PowerFill Phase 6c — Completion Report
Author: PSSaaS Systems Architect
Date: 2026-04-19
Status: Code complete; deployed to local pssaas-db AND PS_DemoData; A38 RESOLVED; Step 5 (pool_guide) reaches the legacy proc successfully and EXECs for ~31s before surfacing a DEFERRED legacy-proc PK bug on PS_DemoData snapshot data shape (A54); pending Collaborator review and PO push
Sentinel: phase-6c-pool-actions-ready ✓
Companion docs:
- Phase 6c internal plan:
.cursor/plans/powerfill-phase-6c.plan.md(gitignored, mirrors Phase 5/6a/6b template) - Phase 6b completion report:
powerfill-phase-6b-completion - Pre-6b sweep completion report:
powerfill-pre-6b-sweep-completion - Phase 6a completion report:
powerfill-phase-6a-completion - Sub-phase breakdown:
powerfill-phase-6-subphase-breakdown - A38 (RESOLVED):
powerfill-assumptions-log §A38 - A52, A53, A54 (new this sub-phase):
powerfill-assumptions-log §A52-A54 - Phase 6 open questions:
powerfill-phase-6-open-questions
TL;DR
Sub-phase 6c (pool-action derivation + tolerance enforcement) ships the verbatim port of two procedures into 009_CreatePoolGuideProcedure.sql: psp_powerfill_pool_guide (live NVO 8770-11185, ~2,415 lines) and psp_pfill_insert4_pool_guide (NVO 11713-12480, ~768 lines). Both deploy cleanly to PS_DemoData AND local pssaas-db. PowerFillRunService extended with Step 5 (pool_guide); RunSummary gains pool_guide_count; sentinel bumped to phase-6c-pool-actions-ready; 4 new unit tests (one extended, three added net-new) all green.
A38 is RESOLVED: psp_pfill_insert4_pool_guide is invoked from inside psp_powerfill_pool_guide's body at NVO 11130 — empirical NVO trace + post-deploy verification confirm the call site. No structural Alternatives-First decision was needed.
The PoC against PS_DemoData partially completes: Steps 1-4 produce the same 515 allocations as 6b; Step 5 reaches psp_powerfill_pool_guide successfully and runs for ~31s before failing at runtime with a legacy-proc design bug surfaced by PS_DemoData snapshot data shape (A54 — ##cte_posting_set_1300 PK violation on multi-pa_key loan/trade pairs). The 6c port is byte-equivalent verbatim per ADR-021; the bug exists in the Desktop App on this exact data; Phase 9 parallel-validation is the gate. Pool-action distribution cannot be exhibited until A54 is addressed (PO escalation + Tom/Greg consultation candidate).
Three new assumptions: A52 (pool_guide forward dep on pfill_syn_powerfill_guide — pre-UE snapshot framing per the breakdown), A53 (BR-3 spec drift: 9 pool_action values vs spec's 5; spec amended), A54 (legacy proc PK bug). 147 → 150 tests passing (3 net-new for 6c). 0 failed. 0 build warnings.
What was produced
New
src/backend/PowerSeller.SaaS.Modules.PowerFill/Sql/009_CreatePoolGuideProcedure.sql(3,274 lines) — verbatim port of two PowerFill procs:psp_pfill_insert4_pool_guide(deployed FIRST so it exists when pool_guide EXECs it at runtime;WITH ENCRYPTION; verbatim NVO 11713-12480; 765 SQL data lines)psp_powerfill_pool_guide(deployed SECOND; NOWITH ENCRYPTIONper F-6c-3; verbatim NVO 8770-11185, body 8774-11182; 2,409 SQL data lines)- Both procs take 0 parameters per F-6c-4
- SET QUOTED_IDENTIFIER + SET ANSI_NULLS preamble per A50 (defensive — required for insert4's WITH ENCRYPTION; applied to whole file for consistency with 008)
- Idempotent DROP-IF-EXISTS-then-CREATE pattern per 003 / 006 / 008 precedent
- Header documents A38 disposition, A50 deploy preamble rationale, A52 forward-dep deferral, dead-block warning (NVO 7194-8768 NOT transcribed), ADR-006/021 citations
- PRINT footer per proc per A32
docs-site/docs/handoffs/powerfill-phase-6c-completion.md— this completion report.docs-site/docs/devlog/2026-04-19-powerfill-phase-6c.md— devlog entry.
Modified
src/backend/PowerSeller.SaaS.Modules.PowerFill/Services/PowerFillRunService.cs— adds Step 5 (pool_guide): newStepPoolGuideconstant;RunPoolGuideStepAsyncmethod (~70 LOC) that EXECspsp_powerfill_pool_guide(0 parameters); 10-min command-timeout for the EXEC (mirrors 6b D8); post-EXEC counter query againstpfill_pool_guide. Class XML doc updated to enumerate all 5 steps + cite NVO 19795 (UE re-invokes pool_guide post-synthesis).src/backend/PowerSeller.SaaS.Modules.PowerFill/Contracts/RunContracts.cs—RunSummarygetsPoolGuideCountfield with explicit[JsonPropertyName("pool_guide_count")]snake_case attribute per the Phase 5 retro lesson. XML doc cites A52 pre-UE snapshot framing.src/backend/PowerSeller.SaaS.Modules.PowerFill/PowerFillModule.cs— status sentinel bumped fromphase-6b-multi-pass-readytophase-6c-pool-actions-ready.src/backend/tests/PowerSeller.SaaS.Modules.PowerFill.Tests/Services/PowerFillRunServiceTests.cs— 4 test changes:- Extended
StepNames_AreFourInExpectedOrder→StepNames_AreFiveInExpectedOrder(now asserts all 5 step name constants) - Added
Run_PoolGuideStepNotReached_WhenPriorStepFails(fail-fast contract: if Step 3 fails, Step 5 is NOT recorded) - Added
RunSummary_PoolGuideField_DefaultsToZero(additive contract) - Added
RunSummary_PoolGuideField_SerializesAsSnakeCase(JSON contract lock)
- Extended
docs-site/docs/specs/powerfill-engine.md— three amendments:- §BR-3: enumerates all 9 legacy pool_action values (Remaining/Leaving/Joining/Switching user-facing + Swapped In/Swapped Out/Vacated/Ineligible internal + Eligible curr_status normalization clarification) per A53. NVO line citations per value.
- §BR-4: notes the
assigncolumn carries 3-character tolerance/disposition codes (e.g.,ypd,ypo); tolerance enforcement is integrated into Phase 6b'spsp_powerfill_consetrather than a post-allocation step. - §Run APIs row for
/run: extended to mention 6c's pool_guide step +pool_guide_countfield + A52 pre-UE snapshot framing.
docs-site/docs/specs/powerfill-assumptions-log.md— A38 marked RESOLVED (with empirical resolution arc); A52, A53, A54 added in canonical house style; A52-A54 disposition section added; A37-A40 disposition section updated to remove A38 from "no open question" block.
Out of scope (deliberately not produced)
- No new C# entities (the
pfill_pool_guidetable + entity exist via001since Phase 1). - No new HTTP endpoints (Step 5 folds into the existing
POST /runshape). - No
pfill_syn_*schema additions (that's 6d). - No UE pass / synthetic trades (6d).
- No async / audit / single-active-run guard (6e).
- No new SQL deploy file beyond 009 (per breakdown's per-sub-phase pattern).
- No fix to A54 PK bug (verbatim port per ADR-021; PO + Tom/Greg escalation candidate).
Decisions made
| # | Decision | Rationale | Where |
|---|---|---|---|
| D1 | Single 009 file deploys both procs (insert4 + pool_guide) | A38 / F-6c-2 empirically resolved: insert4 is invoked from inside pool_guide body at NVO 11130. Single-file deploy mirrors the call-site coupling. insert4 deploys FIRST lexically so the inner runtime EXEC resolves. | Plan §4.1, §4.2; 009 file structure |
| D2 | A38 resolved via empirical NVO trace, no Alternatives-First needed | The call site is in the proc body text (not an Architect choice between separate-vs-folded). A38 transitions from kickoff F-VERIFY-4 to RESOLVED. | Plan §3; A38 entry update |
| D3 | A50 SET preamble applied defensively | psp_pfill_insert4_pool_guide has WITH ENCRYPTION (NVO 11714) → A50 SET-options-at-CREATE-time concern applies. psp_powerfill_pool_guide does NOT have WITH ENCRYPTION per F-6c-3, but the preamble is harmless and consistent with 008. | 009 SET preamble |
| D4 | Step 5 is its own step (not folded into Step 4 allocation) | Mirrors Step 1-4 architecture; same RunStepResult shape; per-step Skip/Failed/Succeeded contract; cleaner timing attribution. | Plan §4.3; RunService.ExecuteAsync |
| D5 | C# Step 5 implementation self-implemented (~70 LOC) with Deliberate Non-Delegation | Encodes A38 decision context, 0-parameter EXEC pattern (different from conset's 6-param), 10-min timeout decision, post-EXEC counter query. Subagent overhead would exceed benefit. SQL transcription DELEGATED. Tests SELF-IMPLEMENTED (only 4 tests, easier to add to existing file). | Plan §6 / §7 |
| D6 | No Reviewable Chunks pre-delegation PO checkpoint | Per kickoff direction: "At 41% of 6b's transcription size, the cost-benefit may favor proceeding without the explicit checkpoint." Pattern is now well-trodden; no PO decision points pending; Architect Report at completion serves as consolidated review surface. | Plan §7 |
| D7 | A54 deferred — verbatim port preserves legacy bug | Per ADR-021: monolithic verbatim port. The PS_DemoData snapshot triggers a latent legacy ##cte_posting_set_1300 PK design issue (2-column PK can't accommodate multi-pa_key loan/trade pairs). The 6c port is byte-equivalent; the bug is the legacy proc's, not ours. PO + Tom/Greg escalation candidate. Phase 9 parallel-validation is the gate. | A54 disposition |
| D8 | 10-minute command timeout for the pool_guide EXEC | Matches 6b D8 rationale (large CTE-style temp tables; many UPDATEs; production scale could be longer). PoC observed ~31s on PS_DemoData scale; well within the timeout. Captured + restored to avoid leaking to other DbContext queries. | RunPoolGuideStepAsync |
Migrations enumerated
This sub-phase produced no new schema migrations (the 17 pfill_* tables exist via 001; pfill_pool_guide PK is (pa_key, trade_id, loan_id, pool_action) per 001). The 009 SQL artifact is a procedure-only deploy. No table changes.
Gate findings
Primary-Source Verification Gate (3-layer — exercised at planning AND deploy time)
| ID | Layer | Finding | Disposition |
|---|---|---|---|
| F-6c-1 | NVO-vs-doc | A39 confirmed: live psp_powerfill_pool_guide body is at NVO 8770-11185. Dead block at NVO 7194-8768 verified comment-marked. | (a) Re-verified — A39 stays canonical; matches kickoff. |
| F-6c-2 | NVO-vs-doc | A38 disposition empirically resolved: psp_pfill_insert4_pool_guide invoked at NVO 11130 from inside pool_guide body. Boundaries: NVO 11713-12480. | (a) Resolved — single 009 file deploys both. A38 transitions to RESOLVED. |
| F-6c-3 | NVO-vs-implementation | psp_powerfill_pool_guide has NO WITH ENCRYPTION (NVO 8770-8773 differs from dead block at 7194 which had it). insert4 HAS WITH ENCRYPTION (NVO 11714). | A50 applies to insert4; defensively applied to whole file for consistency with 008. |
| F-6c-4 | NVO-vs-implementation | Both procs take 0 parameters (no @ declarations). | Documented in 009 header + RunService Step 5 (no parameters in EXEC). |
| F-6c-5 | NVO-vs-implementation | pool_guide writes ONLY pfill_pool_guide (INSERTs at 10941+11062; UPDATEs 11132/11136/11141/11146/11151/11157/11162; DELETE at 11122). | RunSummary needs only pool_guide_count. |
| F-6c-6 | NVO-vs-implementation | insert4 writes ONLY pfill_pool_guide (verified inspecting NVO 11713-12480 — uses ##price_recast_* temp tables internally then INSERTs into pfill_pool_guide). | Combined writes counted via single pool_guide_count query. |
| F-6c-7 | NVO-vs-implementation | UE re-invokes pool_guide at NVO 19795 (EXEC Psp_powerfill_pool_guide). 6c is a Phase 6d prerequisite. | Documented; favors 6c-before-6d ordering. |
| F-6c-8 | NVO-vs-tenant-DB | PS_DemoData state pre-009: both procs present (legacy WITH ENCRYPTION versions); pfill_pool_guide 0 rows; pfill_powerfill_guide 515 rows; pfill_syn_powerfill_guide table present. | 009 DROP-then-CREATE replaces legacy with byte-equivalent transcription. Mirrors 6a D3. |
| F-6c-9 | NVO-vs-tenant-DB | pool_guide reads pfill_syn_powerfill_guide at NVO 11171/11172/11178/11180. Pre-UE deploy state: UPDATE INNER JOINs at 11167-11181 silently match 0 rows (table empty/stale). | (c) Deferred — A52 — 6c is the pre-UE snapshot per breakdown. PoC didn't reach these UPDATEs (failed earlier at A54). |
| F-6c-10 | NVO-vs-implementation | Pool action values present in live body: Vacated (11148), Ineligible (11152), Swapped In (11158), Swapped Out (11163), Switching, Leaving, plus Eligible curr_status normalization (11132-11134). Spec BR-3 enumerated only 5 of 9. | (a) Corrected in place — A53 — spec amended in this sub-phase to enumerate all 9 with internal-state callouts. |
| F-6c-11 (NEW from PoC) | NVO-vs-tenant-DB | psp_powerfill_pool_guide EXEC against PS_DemoData fails at runtime with Msg 2627: Violation of PRIMARY KEY constraint on ##cte_posting_set_1300 for (trade_id=36177868, loan_id=3385000026). The temp table PK is (trade_id, loan_id) (2 columns; NVO 9889-9919); upstream ##cte_posting_set_1200 produces multiple rows for the same (trade_id, loan_id) when the loan has multi-pa_key actions. The very next CTE (##cte_posting_set_1400) has PRIMARY KEY (trade_id, loan_id, pa_key) — 3 columns — confirming the legacy author knew the right shape; the 2-column PK on 1300 looks like an oversight masked in production data shapes. | (c) Deferred with justification — A54 — verbatim port preserves legacy behavior per ADR-021; modifying the PK would deviate from verbatim port and needs PO + Tom/Greg escalation; Phase 9 parallel-validation is the gate. NOT a 6c-port correctness concern. |
Pattern observation: The three-layer Primary-Source Verification Gate produced findings at all three layers AGAIN (this is the third corroborating session for the candidate process refinement banked in the 2026-04-17b PoC-complete devlog and corroborated by 6b). Per PO acknowledgment at the 6b checkpoint, the candidate refinement "Reference docs are not primary source — even Collaborator-authored kickoff documents AND Architect-authored sub-phase plans must be Primary-Source Verification Gate inputs themselves" is now 3-session-corroborated; the v3.1 nomination should be drafted as part of the next process-discipline revision cycle.
Alternatives-First Gate
Two structural decisions documented (plan §4):
- §4.1: SQL deploy file — B (new
009_*.sql) chosen over A (extend008) for one-file-per-sub-phase reviewability and revertability per breakdown §"Failure-mode and rollback per sub-phase". - §4.2: Single file with both procs — A (combined) chosen over B (split
009a+009b) for deploy-coupling fidelity to call-site coupling. - §4.3: RunService Step 5 — A (own step) chosen over B (sub-step of allocation) for per-step contract preservation.
A38 disposition itself did NOT need an Alternatives-First Gate because the call site is in the proc body text (F-6c-2 empirically resolved); no architectural choice was open.
Required Delegation Categories
Delegated:
009_CreatePoolGuideProcedure.sqlSQL transcription — 3,174 NVO source lines unwrapped to 3,274 output lines (765 insert4 + 2,409 pool_guide + 100 boilerplate). Subagent reported all 7 sanity anchors verified, +7912 consistent NVO offset throughout the pool_guide block, 0~"PB-escape sequences, 0 dead-block lines (//"count = 0 in transcribed SQL body). Architect spot-checked 4 sampled regions (header, insert4 boundary, pool_guide CREATE block, EXEC psp_pfill_insert4_pool_guide call site); all clean. Subagent SQL transcription pattern continues to scale: 670 lines (006), 5,837 lines (008), 3,174 lines (009), all clean first-attempt.
Self-implemented with Deliberate Non-Delegation:
Deliberate Non-Delegation: Boilerplate tests >5 (does NOT match — only 4 tests added)
AND no other category match
Task: PowerFillRunService.RunPoolGuideStepAsync extension (~70 LOC) +
RunSummary additions (1 field) + 4 new RunServiceTests
Reason for self-implementation: encodes A38 disposition context
(insert4 invoked from inside pool_guide body at NVO 11130 per F-6c-2);
A50 defensive SET preamble rationale; the 0-parameter EXEC pattern
(simpler than conset's 6-param shape per F-6c-4); the 10-min command
timeout decision (mirrors 6b D8); the post-EXEC summary-counter query
for pool_guide_count. Tests are 4 small additions to existing
PowerFillRunServiceTests — adding to an existing file is structurally
simpler than spawning a subagent for ~50 LOC of test code.
Context that would be lost in handoff: A38 → F-6c-2 resolution arc;
the F-6c-3 finding that pool_guide doesn't have WITH ENCRYPTION but
insert4 does (so SET preamble is defensive vs required); the F-6c-9
pre-UE snapshot framing (pool_guide UPDATEs may silently match 0 rows
on PS_DemoData); spec amendment context for A53; the A54 PK-bug
empirical observation surfaced during PoC.
Deploy Verification Gate
| Arm | Description | Evidence |
|---|---|---|
| (a) Sentinel signal | /api/powerfill/status returns the new sentinel | curl -s http://pssaas.powerseller.local/api/powerfill/status → {"module":"PowerFill","status":"phase-6c-pool-actions-ready"} ✓ (post-restart) |
| (b) Live API run | POST /api/powerfill/run against PS_DemoData reaches Step 5 and EXECs psp_powerfill_pool_guide | run_id=..., status=Failed (due to A54), duration 00:00:56.6725330; Steps 1-4 all Succeeded (allocated_count: 515 — matches 6b baseline); Step 5 reached + executed for ~31s; failure mode is the documented A54 legacy-proc PK violation. Full output captured in PoC section below. |
| (c) Live DB probe — local pssaas-db | 009 deploy succeeds; both procs created | OBJECT_ID('dbo.psp_powerfill_pool_guide','P') returns non-null + OBJECT_ID('dbo.psp_pfill_insert4_pool_guide','P') returns non-null. Both PRINT lines fire. Local pssaas-db deploys cleanly — different from 008's A49 column-missing failure on local because pool_guide's column references are inside CTE/temp-table bodies that SQL Server defers fully. ✓ |
| (c) Live DB probe — PS_DemoData | Both procs deployed; idempotent | Both procs OBJECT_ID present; re-deploy is idempotent (DROP-then-CREATE pattern fires cleanly each time). Pre-deploy state: legacy WITH ENCRYPTION versions present; post-deploy: replaced with our byte-equivalent transcription (mirrors 6a D3 BX-procs precedent). ✓ |
| (b)+ (c) Note | Pool-action distribution NOT observable on PS_DemoData snapshot | Per A54 disposition: pool_guide proc fails at NVO 9921 (BEFORE NVO 10941's INSERT INTO pfill_pool_guide). pfill_pool_guide row count remains 0 post-PoC. Phase 9 parallel-validation against a clean Desktop App customer DB is the gate for full PoC observation. |
Counterfactual Retro
Knowing what I know now, what would I do differently?
- The A38 disposition was resolvable in a 30-second NVO grep. The kickoff prompt extensively framed A38 as a Plan-stage Architect decision requiring Alternatives-First analysis. In practice,
Grep psp_pfill_insert4_pool_guide n_cst_powerfill.sruimmediately surfaced the call site at NVO 11130 inside pool_guide's body. Lesson: when a kickoff frames a "decision" that depends on an empirical fact, run the empirical query FIRST; if the fact resolves the decision, document and move on. Don't artificially scaffold an Alternatives-First when the data closes the question. - The PoC failure (A54) was a real finding, not a port defect. I expected a clean PoC ("status: Complete + sensible BR-3 distribution"). Instead I got a partial success: Step 5 EXECs successfully and runs the legacy proc for 31s before hitting a real legacy-proc bug. This is the Primary-Source Verification Gate's NVO-vs-tenant-DB layer earning its keep. Without 6c's PoC, the same bug would have surfaced in 6d (UE re-invokes pool_guide at NVO 19795) or in Phase 9 parallel-validation — much later, much more costly to triage in context. The 6c PoC is a forensic asset for 6d and Phase 9, not a 6c failure.
- Pool_guide on local pssaas-db deployed cleanly (no A49-style column-missing failures). Different from 008. Lesson: A49 is not a universal pattern — column resolution depth varies by where the column reference sits in the proc body. Pool_guide's column references are deep inside CTE bodies that SQL Server defers; conset's were in JOIN predicates that resolve eagerly. Worth documenting if a future Architect tries to predict deploy behavior on local vs PS_DemoData based on A49 alone.
- The Reviewable Chunks decision (skip the pre-delegation PO checkpoint) was correct. No mid-flight Architect-PO sync was needed; the SQL transcription delegated cleanly; the post-PoC A54 finding was the only real escalation, and it surfaced AFTER deploy where the Architect Report (this document) is the right escalation surface. The kickoff's "at 41% of 6b's transcription size, may favor skipping the checkpoint" guidance was well-calibrated.
- I budgeted ~5-7 days per breakdown estimate; sub-phase calendar time was ~1 Architect-session — same as 6a, 6b, and pre-6b sweep. The aggressive subagent delegation pattern + the well-trodden post-6b infrastructure (PowerFillRunService extension, RunSummary additive contract, test fixture, 008 SQL deploy precedent) compressed the work dramatically. The 5-7-day estimate may have been calibrated for an Architect doing the SQL transcription manually; the subagent dispatch pattern materially changed the delivery velocity. Worth noting for future Architect breakdown estimates.
- The A53 spec-drift finding was unanticipated. I expected to verify BR-3 against the legacy proc and find it accurate. Instead the legacy emits 9 distinct pool_action values (4 user-facing + 4 internal + 1 status-normalization); the spec enumerated only 5. The Primary-Source Verification Gate's NVO-vs-spec layer (sub-layer of NVO-vs-doc) catches drift even in spec docs that the same Architect authored. This is consistent with the candidate process refinement: even your own prior artifacts must be Gate inputs.
- R-DATA-2 (PS_DemoData multi-pa_key data shape) is the natural follow-on to R-DATA-1 (PS_DemoData FHLMC small-balance instruments per 6a) and A51 (conset has more complete data view than 6a's C# candidate-builder). Pattern: PS_DemoData is a fixed snapshot; some legacy code paths require data shapes the snapshot doesn't have OR triggers latent legacy bugs the production code paths don't. Phase 9 parallel-validation against multiple Desktop App customer DBs is the canonical correctness gate. 6c is the third sub-phase to surface this pattern; worth banking as "PS_DemoData PoC limitations" — a documented expected-failure-mode class.
PoC verification commands and outputs
Status sentinel (Deploy Verification Gate arm a)
$ curl -s http://pssaas.powerseller.local/api/powerfill/status
{"module":"PowerFill","status":"phase-6c-pool-actions-ready"}
Local pssaas-db deploy (Deploy Verification Gate arm c — local)
$ docker exec pssaas-db /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa \
-P "PSSaaS_Dev_2026!" -No -d PSSaaS_Dev \
-i /docker-entrypoint-initdb.d/powerfill/009_CreatePoolGuideProcedure.sql
PowerFill 009: created psp_pfill_insert4_pool_guide
PowerFill 009: created psp_powerfill_pool_guide
# OBJECT_ID confirmation:
$ docker exec pssaas-db /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa \
-P "PSSaaS_Dev_2026!" -No -d PSSaaS_Dev \
-Q "SELECT CASE WHEN OBJECT_ID('dbo.psp_powerfill_pool_guide','P') IS NOT NULL THEN 'present' ELSE 'MISSING' END AS pool_guide, CASE WHEN OBJECT_ID('dbo.psp_pfill_insert4_pool_guide','P') IS NOT NULL THEN 'present' ELSE 'MISSING' END AS insert4;"
pool_guide insert4
---------- -------
present present
Local deploys cleanly — different from 008's A49 failure mode. Pool_guide's column references resolve deep enough in the proc body that SQL Server's deferred name resolution fully defers them.
PS_DemoData deploy + idempotency (Deploy Verification Gate arm c — tenant)
$ docker exec -e PWORD='M0th3rFuck1ng$44$' pssaas-db sh -c \
'/opt/mssql-tools18/bin/sqlcmd -S "hostedps-sql.public.086ea791c2f1.database.windows.net,3342" \
-U "kevin_pssaas_dev" -P "$PWORD" -No -d PS_DemoData \
-i /docker-entrypoint-initdb.d/powerfill/009_CreatePoolGuideProcedure.sql'
PowerFill 009: created psp_pfill_insert4_pool_guide
PowerFill 009: created psp_powerfill_pool_guide
# Both deploy cleanly. Idempotent re-deploy verified (DROP-then-CREATE fires cleanly each time).
# Pre-deploy state on PS_DemoData: both procs already present (legacy WITH ENCRYPTION versions);
# post-deploy: replaced with byte-equivalent transcription (mirrors 6a D3 / 008 idempotency precedent).
Live API run (Deploy Verification Gate arm b — partial PoC + A54 finding)
$ curl -s --max-time 700 -X POST -H "X-Tenant-Id: ps-demodata" \
-H "Content-Type: application/json" \
"http://pssaas.powerseller.local/api/powerfill/run" -d '{}' | jq .
HTTP_STATUS=500 TOTAL_TIME=57s
{
"status": "Failed",
"duration": "00:00:56.6725330",
"summary": {
"constraint_count": 3,
"candidate_count": 0,
"carry_matched": 0,
"carry_missed": 0,
"candidates_per_constraint": {
"FHLMC|15 fhlmc cash 85k|min_85k_pool_target": 0,
"FHLMC|15 fhlmc cash 110k|min_110k_pool_target": 0,
"FHLMC|15 fhlmc cash 125k|min_125k_pool_target": 0
},
"allocated_count": 515,
"kicked_out_count": 0,
"cblock_count": 0,
"pool_guide_count": 0
},
"steps": [
{ "step_name": "bx_cash_grids", "status": 2 (Skipped), ... },
{ "step_name": "bx_settle_and_price", "status": 0 (Succeeded), ... },
{ "step_name": "candidate_builder", "status": 0 (Succeeded), ... },
{ "step_name": "allocation", "status": 0 (Succeeded), ... },
{ "step_name": "pool_guide", "status": 1 (Failed),
"error_message": "SqlException 2627: Violation of PRIMARY KEY constraint
'PK__##cte_po__F0E022A243485CB5'. Cannot insert duplicate key in object
'dbo.##cte_posting_set_1300'. The duplicate key value is
(36177868, 3385000026). [...] The statement has been terminated.
Warning: Null value is eliminated by an aggregate or other SET operation."
}
]
}
This is A54 documented expected behavior for the PS_DemoData snapshot — Step 5 reaches psp_powerfill_pool_guide successfully, EXECs for ~31s, and fails at NVO 9921 (the INSERT INTO ##cte_posting_set_1300 block) due to a 2-column PK that can't accommodate multi-pa_key loan/trade pairs. The 6c port is byte-equivalent verbatim per ADR-021; the bug is the legacy proc's, not ours.
A54 root-cause confirmation
# Confirm pfill_powerfill_guide has NO duplicates for the failing pair
$ ... -Q "SELECT TOP 10 trade_id, loan_id, COUNT(*) AS cnt FROM dbo.pfill_powerfill_guide
GROUP BY trade_id, loan_id HAVING COUNT(*) > 1"
trade_id loan_id cnt
-------- ------- ---
(0 rows affected)
# Confirm the failing pair IS in pfill_powerfill_guide (just once)
$ ... -Q "SELECT loan_id, trade_id, loan_amount, trade_amount, assign
FROM dbo.pfill_powerfill_guide
WHERE loan_id = '3385000026' AND trade_id = '36177868'"
loan_id trade_id loan_amount trade_amount assign
3385000026 36177868 166748.48 876778.00 ypd
(1 rows affected)
The duplicate is generated inside the legacy proc's CTE pipeline (between ##cte_posting_set_1200 → ##cte_posting_set_1300), NOT by 6b's allocation output. The trade has 1 pool relation (pscat_trades_pools_relation), so the dup must come from pa_key differences in the upstream ##cte_posting_set_1200 (loan tracked on both source pa_key=4 AND destination pa_key=1 sides of a Switching action). The very next CTE ##cte_posting_set_1400 correctly has PRIMARY KEY (trade_id, loan_id, pa_key) (3 columns; NVO 10027-10031), confirming the legacy author knew the right key shape — the 2-column PK on ##cte_posting_set_1300 is a known-after-the-fact design oversight masked in production data shapes that don't trigger it.
Tests
$ docker exec pssaas-api dotnet build /app/PowerSeller.SaaS.sln --nologo
Build succeeded. 0 Warning(s). 0 Error(s).
$ docker exec pssaas-api dotnet test /app/PowerSeller.SaaS.sln --nologo --no-build
BestEx: 32 passed, 0 skipped, 0 failed
Api: 1 passed, 0 skipped, 0 failed
PowerFill: 117 passed, 6 skipped, 0 failed (was 114 + 6 + 0 pre-6c — added 3 net-new tests)
TOTAL: 150 passed, 6 skipped, 0 failed
The 4 test changes:
- Renamed
StepNames_AreFourInExpectedOrder→StepNames_AreFiveInExpectedOrder(extends 6b's lock to includeStepPoolGuide) - Added
Run_PoolGuideStepNotReached_WhenPriorStepFails(fail-fast contract for Step 5) - Added
RunSummary_PoolGuideField_DefaultsToZero(additive contract) - Added
RunSummary_PoolGuideField_SerializesAsSnakeCase(JSON contract lock forpool_guide_count)
The 6 skipped: 4 pre-existing PFILL_TEST_SQLSERVER-env-gated SQL integration tests (unchanged); 2 InMemory-blocked Phase 6a placeholders (unchanged).
Open questions and blockers
Carry-over to 6d kickoff
A52 (forward dep on pfill_syn_powerfill_guide): UE populates the syn table and re-EXECs pool_guide at NVO 19795. The 6c pre-UE snapshot is the input; 6d's post-UE output is the user-facing final.
A54 (legacy proc PK bug on PS_DemoData): UE re-invokes pool_guide. 6d will hit the same A54 failure on PS_DemoData unless something between 6c and 6d's UE invocation resolves the upstream multi-pa_key state. Phase 6d Architect should expect this and plan around it. Options:
- Defer to Phase 9 — Phase 6d ships with the same A54 deferral; PoC against PS_DemoData partially completes.
- Architect deviation (requires PO + Tom/Greg escalation + ADR-021 amendment) — fix the
##cte_posting_set_1300PK to(trade_id, loan_id, pa_key)matching the next CTE 1400's PK; document as a "narrow legacy bug fix" exception to verbatim port. - Pre-clean PS_DemoData — run a one-time UPDATE/DELETE against
pfill_powerfill_guideor upstream tables to remove the multi-pa_key duplicates. Risky and not generalizable.
Architect recommendation
Option 1 (defer to Phase 9). Rationale: ADR-021 is foundational; bug-fix-via-port-deviation is precedent-setting. Phase 9 is the parity-validation gate where this exact class of finding gets resolved authoritatively. The Phase 6c→6d→6e PoC arc against PS_DemoData ships with documented A54 deferral; the canonical "Phase 6 complete" sentinel is at 6e end with A54 noted as a Phase 9 carry-over.
PO action requested: acknowledge A54 disposition and Architect recommendation.
Recommended next steps
- Collaborator review of:
009_*.sql(3,274 lines — but mechanical transcription; review focus is structural-anchor verification + spot-checks at the 4 sampled regions per the subagent's report; the Architect spot-checked 4 regions independently)PowerFillRunServiceStep 5 extension (~70 LOC)RunContracts.csPoolGuideCountaddition- PowerFillModule sentinel bump
- 4 test changes
- Spec amendments (BR-3, BR-4, Run APIs row)
- 3 new assumptions (A52, A53, A54) + A38 RESOLVED update
- This completion report
- Devlog entry Estimated 1.5-2 hours (6b was 1.5-2h; 6c is comparable scale + the A54 PoC finding to discuss).
- PO sign-off on:
- A38 RESOLVED disposition (insert4 invoked from inside pool_guide; both deploy in 009)
- A53 spec amendment (BR-3 enumerates 9 pool_action values vs original 5)
- A54 disposition (verbatim port preserves legacy bug; Phase 9 is the gate; Phase 6c-6d-6e ship with A54 deferral; explicit PO acknowledgment requested)
- Process discipline candidate refinement (3-session-corroborated "Reference docs are not primary source") — ready to advance to v3.1 nomination per 6b PO checkpoint
- PO push of the atomic commits (Architect commits; PO controls
git push). - Sub-phase 6d kickoff drafting — prerequisites: 6c's pool_guide proc deployed; 6c's RunService Step 5 in place; A52 (pre-UE forward dep) acknowledged; A54 (legacy proc PK bug on PS_DemoData) acknowledged with chosen disposition path. 6d adds 3
pfill_syn_*tables +pfill_powerfill_logto schema, portspsp_powerfillUE(NVO 13246-19801, ~6,556 lines — largest single proc in PowerFill), re-invokes pool_guide post-synthesis. Will hit A54 again; Architect should plan around it. - Optional follow-up (deferred): Phase 9 parallel-validation harness design — A54 motivates a "data-shape compatibility" pre-flight that detects multi-pa_key pairs and either skips PoC, normalizes, or surfaces matching Desktop App failures for parity.
Notes on this session's process
- Three-layer Primary-Source Verification Gate exercised in earnest; produced 11 findings across all three layers (NVO-vs-doc: F-6c-1, F-6c-2; NVO-vs-implementation: F-6c-3 through F-6c-7, F-6c-10; NVO-vs-tenant-DB: F-6c-8, F-6c-9, F-6c-11). Third corroborating session for the candidate refinement banked in 6a-PoC-complete devlog and corroborated by 6b. PO-acknowledged at 6b checkpoint as ready for v3.1 nomination.
- Reviewable Chunks at sub-phase scope — skipped explicit pre-delegation PO checkpoint per kickoff guidance. The pattern paid off: SQL transcription succeeded cleanly; PoC surfaced the only meaningful finding (A54) AFTER deploy where the Architect Report is the right escalation surface. Banking the inverse observation: at ~3,200-line transcription scale (vs 6b's 5,837), skipping the checkpoint was the right call.
- Required Delegation Categories classification: SQL transcription delegated (~3,200 lines, mechanical, fast subagent — third clean first-attempt scaling success after 6a 670 + 6b 5,837); C# Step 5 self-implemented (Deliberate Non-Delegation); 4 tests self-implemented (Deliberate Non-Delegation: small surface, easier to add to existing file).
- Andon-cord pulled at PoC (after Step 5 failed with SqlException 2627). Investigated root cause (multi-pa_key duplicates in
##cte_posting_set_1300's upstream); identified as legacy proc design issue (PK is 2 cols when next CTE's is 3); documented as A54 with full disposition; chose Option 1 (defer to Phase 9) per ADR-021 verbatim-port discipline; did NOT silently work around. The Andon-cord protocol was used as designed: stop, investigate, document, escalate disposition, proceed. - Counterfactual Retro filled with 7 named observations — most important: (1) A38 was resolvable in a 30-second NVO grep (don't artificially scaffold Alternatives-First when data closes the question); (2) The PoC failure (A54) was a real finding, not a port defect — the Primary-Source Verification Gate's NVO-vs-tenant-DB layer earning its keep; (5) The 5-7-day breakdown estimate may be calibrated for manual SQL transcription; subagent dispatch compresses to ~1 Architect-session.
- Deploy Verification Gate all three arms exercised: sentinel green; live API reaches Step 5 + EXECs pool_guide for 31s + surfaces A54 (documented expected behavior on this snapshot); OBJECT_ID confirms both procs deployed on local + PS_DemoData. Steps 1-4 still produce identical 515-allocation output to 6b baseline — no 6b regression.
- Sub-phase calendar time: ~1 day (consistent with 6a, 6b, pre-6b sweep; well under the 5-7 day breakdown estimate). Reasons: A38 resolved fast via NVO grep; subagent SQL transcription succeeded cleanly first attempt; 6b infrastructure (PowerFillRunService extension pattern, RunSummary additive contract, test fixture) was directly reusable; A54 finding had a clear disposition path that didn't expand scope.
Phase 6c is code complete; the A38 disposition is RESOLVED; Step 5 is in place and reaches the legacy proc successfully; A54 is the documented Phase 9 carry-over. The Phase 6d Architect can dispatch their work with full visibility into A52 (pre-UE forward dep) and A54 (PS_DemoData snapshot triggers latent legacy PK bug); 6d's UE pass will exercise the same A54 path.
End of pre-Phase 6d sub-phase 6c completion report.