Skip to main content

PowerFill Phase 6 — Sub-Phase Breakdown

Author: PSSaaS Systems Architect Date: 2026-04-17 Status: Draft — pending Collaborator review and PO approval before sub-phase 6b planning begins. Companion docs:


Why a sub-phase breakdown

The Phase 6 kickoff (line 49-62) explicitly says:

Phase 6 is the biggest phase of the module (3-4 weeks per spec estimate). Substantially more substantive than Phases 1-5 combined. … This is NOT a single-PR phase; the planning output should explicitly propose sub-phases (6a / 6b / 6c …) so each sub-phase is independently reviewable and shippable.

Phase 6 ports two of the largest legacy procedures in the codebase (psp_powerfill_conset ≈ 5,887 lines of T-SQL string-concat at NVO 50-5886, plus psp_powerfillUE ≈ 6,556 lines split across two PB functions at NVO 13246-19801). A monolithic plan would defer review until the work is unreviewable; a single PR would block on every detail. The sub-phase breakdown is the Reviewable Chunks practice (process-discipline §6) applied at phase scale.

Each sub-phase is:

  • Independently reviewable — produces an artifact a Collaborator can verify against the NVO and the PS_DemoData live arm in one pass
  • Independently shippable — bumps the canonical /api/powerfill/status sentinel (status field) so deploy-verification arms (a) sentinel and (c) live probe are both satisfied per sub-phase
  • Dependency-ordered — each sub-phase's preconditions are satisfied by the prior sub-phase's outputs, so a halt at any boundary leaves the system in a coherent state

The 7 cross-sub-phase open questions in powerfill-phase-6-open-questions.md are PO-input gates before sub-phase 6b planning begins. Sub-phase 6a can proceed in parallel with the open-questions discussion because 6a's scope (candidate-builder + carry-cost integration + BX cash-grid pre-step) is upstream of every open question except #6 (calculator integration pattern), and #6 is internal to 6a's design.


At-a-glance summary

Sub-phaseScope (one line)Shippable artifactStaging sentinelEstimated effort
6aRun-orchestration scaffold + BX pre-steps + candidate-builder + carry-cost integrationPOST /api/powerfill/run returns run_id (synchronous for now); pfill_loan2trade_candy_level_01 populated; BX cash-grid + settle-and-price procs deployed and invoked; preview at POST /api/powerfill/candidates/previewphase-6a-candidate-builder-ready5-7 days
6bMulti-pass allocation engine (exact_fit / best_fit / fill_remaining / orphan_handling)pfill_powerfill_guide populated by psp_powerfill_conset port; pfill_kickout_guide_01 populated; allocation passes run synchronouslyphase-6b-multi-pass-ready7-10 days
6cPool-action derivation (BR-3 state machine: Remaining / Leaving / Joining / Switching / Swapped In) + tolerance enforcement (BR-4)pfill_pool_guide populated; psp_powerfill_pool_guide port complete (live NVO 8770-11185 range)phase-6c-pool-actions-ready5-7 days
6dUE pass: psp_powerfillUE port + 3 pfill_syn_* synthetic-trades tables added to schema + populated by UEpfill_syn_trade_base, pfill_syn_powerfill_guide_all_rank, pfill_syn_powerfill_guide populated; pfill_powerfill_log auto-create included; A28 closedphase-6d-ue-syn-trades-ready7-10 days
6eAsync run orchestration + pfill_run_history audit + BR-8 single-active-run guard + BR-9 failure cleanup + PoC sign-offPOST /api/powerfill/run is asynchronous; pfill_run_history row per run; GET /runs, GET /runs/{id}, POST /runs/{id}/cancel; PS_DemoData PoC: a full run produces sensible pool_action distributionphase-6e-async-runs-ready5-7 days

Total estimated effort: 29-41 days = 4-6 work-weeks. Within the spec's "3-4 weeks" estimate at the optimistic end, slightly over at the realistic end. The estimate is ranges, not points; Phase 4 ran ~1.5x the initial estimate due to F3 escalation, and Phase 6 has 7 known open questions.


Sub-phase 6a — Run-orchestration scaffold + candidate-builder + BX pre-steps

Scope (in)

  • POST /api/powerfill/run — accepts the full Run Options body (spec §Run Options, lines 226-238); returns run_id and a synchronous result; the async version moves to 6e. 6a's run is "synchronous best-effort" — it executes inline and returns 200 OK + body when finished, OR 500 if it fails. This lets 6a be testable end-to-end without taking on the BackgroundService design dependency (Open Q1).
  • BX cash-grid pre-step — port psp_pfill_bx_cash_grids from NVO line 12834-13137 to a new SQL file 006_CreatePowerFillRunProcedures.sql. This is the first proc invoked from w_powerfill.srw::ue_perform_powerfill line 111 with bx_price_floor as ls_proc_parms[1]. Skippable when bx_price_floor is unset per A12.
  • BX settle-and-price pre-step — port psp_pfill_bx_settle_and_price from NVO line 11345-11710 to the same 006 script. Second proc invoked at w_powerfill.srw line 131. Always invoked (not gated).
  • Candidate-builder — populate pfill_loan2trade_candy_level_01 from the eligible-loans × open-trades cartesian, applying:
    • Sec-rule filter (BR-1) via pfill_constraint_sec_rule_relpscat_securitization_rules
    • Loan-stage filter (A15)
    • Lockdown filter (BR-2) via pfill_lockdown_guide
    • Price-floor filter (BR-6) when bx_price_floor is set
    • interest_earning_days = DATEDIFF(D, COALESCE(close_date, lock_expiration_date), settlement_date) per NVO line 1232-1233 / 1442
    • market lookup via pfill_cash_market_map
    • price from BX settle-and-price output
  • Carry-cost integration — invoke PowerFillCarryCostCalculator (Phase 5) over the materialized candidate batch; UPDATE pfill_loan2trade_candy_level_01 with the calculated carry_cost and prx_plus_carry. Calculator integration pattern (Open Q6) resolved here.
  • Diagnostic preview endpointPOST /api/powerfill/candidates/preview returns the candidate set without writing to pfill_loan2trade_candy_level_01 (analogous to Phase 5's POST /carry-cost/preview). Diagnostic-only; lets PO/Collaborator inspect what the candidate-builder produces before allocation runs over it.
  • Run orchestration scaffold (synchronous)PowerFillRunService modeled on PowerFillPreprocessService (Phase 3 pattern); no BackgroundService yet; failure leaves pfill_loan2trade_candy_level_01 in whatever state the failed pass produced (cleanup is Phase 6e BR-9).
  • /api/powerfill/status sentinel bump to phase-6a-candidate-builder-ready.

Scope (out)

  • Multi-pass allocation (6b)
  • Pool-action derivation (6c)
  • UE pass / synthetic trades (6d)
  • Async runs / pfill_run_history / single-active-run guard / failure-state cleanup (6e)
  • Output query APIs (Phase 7)
  • React UI (Phase 8)
  • The 4 read APIs over pfill_loan2trade_candy_level_01 (/runs/{id}/candidates, etc.) — those are Phase 7

Shippable artifact

After 6a:

  1. POST /api/powerfill/run against PS_DemoData with {} body (defaults) returns 200 OK + a run_id and a count of candidates produced, OR returns 500 if a step fails. The candidate count is sensible (non-zero, < cartesian explosion).
  2. pfill_loan2trade_candy_level_01 in PS_DemoData has rows after the call, with all carry-cost columns populated where curve match succeeded.
  3. POST /api/powerfill/candidates/preview returns the same candidate shape without writing.
  4. Both BX procs are deployed to PS_DemoData via 006; verifiable via OBJECT_ID('dbo.psp_pfill_bx_cash_grids') IS NOT NULL.
  5. /api/powerfill/status returns phase-6a-candidate-builder-ready.

Sentinel

phase-6a-candidate-builder-ready on /api/powerfill/status.

Why this sub-phase first

The candidate-builder is the least dependent part of Phase 6: every later sub-phase consumes pfill_loan2trade_candy_level_01. Building it first means 6b/6c/6d/6e can each test their work against a stable upstream. Also, 6a is what proves the run-orchestration plumbing works end-to-end — if 6a can't get a request from POST /run through DI, tenant resolution, and the existing preflight stack, that's a blocker for everything downstream and we want to find it as early as possible.

The BX pre-steps are bundled into 6a (rather than split into 6a-pre) because they have no consumers other than the candidate-builder; splitting them would create a deploy state where the BX procs exist but nothing calls them.


Sub-phase 6b — Multi-pass allocation engine

Scope (in)

  • Port psp_powerfill_conset body (NVO line 50-5886) into the new 006 script (or a new 007_*.sql if 006 grows too large; Architect's call when 6b plans). Specifically the multi-pass allocation core: the iterative blocks scattered through NVO line 7500-12500 per kickoff §"NVO PRIMARY-SOURCE CITATION INDEX".
  • Trace and document the actual pass boundaries — A1 says "four passes" but that's an interpretation. The 6b plan must read the NVO and identify each pass empirically (separate INSERT/UPDATE blocks against pfill_loan2trade_candy_level_01), document them as A1.1 / A1.2 / A1.3 / A1.4 in a 6b-specific addendum to the assumptions log, and resolve A1 to "verified" or "needs PO escalation." This is itself a Primary-Source Verification Gate output.
  • Constraint priority iteration — walk pfill_constraints in constraint_priority ASC order; for each constraint, run the multi-pass allocation against the candidate slice that matches that constraint's (investor_id, investor_instrument_name).
  • Populate pfill_powerfill_guide — one row per allocated loan (PK is loan_id per the 001 schema; matches A8's "allocation output" interpretation). 6b stops here; pool-action derivation goes to 6c.
  • Populate pfill_kickout_guide_01 — loans that fall through every pass.
  • Populate pfill_trade_base with per-trade fulfillment counters (pre_session_assigned, post_session_assigned, pre_session_space, post_session_space, etc.) — the 35-column wide table from 001 line 304-345.
  • Carry over to candidate-builder integration — 6b consumes the carry-cost columns 6a wrote to pfill_loan2trade_candy_level_01; ranking uses prx_plus_carry (or price in PriceOnly mode).
  • /api/powerfill/status sentinel bump to phase-6b-multi-pass-ready.

Scope (out)

  • Pool-action derivation (6c — pfill_pool_guide population)
  • pfill_cblock_guide and pfill_trade_cblock_base (A9 still has Low confidence on what "cblock" means; 6b plan must include a Primary-Source Verification probe of where these are populated and consumed; if the answer is "in psp_powerfill_conset itself" then 6b owns them, if "in psp_powerfillUE" then 6d does, if "neither — they're computed elsewhere" then we have an A9 escalation)
  • UE pass / synthetic trades (6d)
  • Async / audit / failure cleanup (6e)

Shippable artifact

After 6b: POST /api/powerfill/run against PS_DemoData populates pfill_powerfill_guide with allocated loans + pfill_kickout_guide_01 with rejects + pfill_trade_base with fulfillment counts. The output count is sensible (most candidates resolve to either allocated or kicked-out; trade-base counters sum correctly). No pfill_pool_guide yet.

Sentinel

phase-6b-multi-pass-ready.

Why this sub-phase second

The multi-pass allocation is the largest single piece of Phase 6 work and consumes 6a's output. It must come before 6c (pool actions need allocation results) and before 6d (UE rebuilds pfill_powerfill_guide from 6b's output). The decision on whether to port psp_powerfill_conset as a single ~5.8K-line stored procedure or to break it into smaller named procs is a 6b-plan decision — kickoff §"EXPLICIT SCOPE" leaves it open ("hybrid C# orchestration + T-SQL execution per ADR-021"); 6b plan must Alternatives-First it.


Sub-phase 6c — Pool-action derivation + tolerance enforcement

Scope (in)

  • Port the live psp_powerfill_pool_guide body at NVO line 8770-11185 (≈2,415 lines; the commented-out older version at 7194-8769 is dead code — verified during Phase 6 kickoff verification gate, finding F-VERIFY-5). Output is pfill_pool_guide (PK (pa_key, trade_id, loan_id, pool_action) per 001).
  • BR-3 pool-action state machine — derive Remaining / Leaving / Joining / Switching / internal Swapped In per A3 by comparing each loan's prior pool assignment (from pscat_trades_pools_relation) to its post-allocation assignment (from pfill_powerfill_guide).
  • BR-4 trade tolerance enforcement — verify each allocated trade's total assigned amount falls within tolerance_amount band; flag exceptions in pfill_pool_guide.assign field.
  • psp_pfill_insert4_pool_guide (NVO line 11712-12483) — investigate during 6c planning whether this 771-line proc is invoked from psp_powerfill_pool_guide (likely; it has the _insert4_ naming convention suggesting it's a sub-step) or invoked separately. F-VERIFY-4 finding from kickoff verification gate; the kickoff doc omitted this proc entirely. 6c plan owns its disposition.
  • Populate pfill_cblock_guide / pfill_trade_cblock_base if the 6b plan's A9 disposition put them here. Otherwise 6c plan documents why they're elsewhere.
  • /api/powerfill/status sentinel bump to phase-6c-pool-actions-ready.

Scope (out)

  • UE pass (6d): UE re-runs the pool-guide step internally; 6c's output is the pre-UE state. After 6d, pfill_pool_guide will be the UE-modified version.
  • Async / audit / failure cleanup (6e)
  • Output query APIs (Phase 7)

Shippable artifact

After 6c: POST /api/powerfill/run populates pfill_pool_guide with one row per (allocated loan, pool_action) and the BR-3 state machine produces a sensible distribution. PS_DemoData should show a mix of Remaining (most), Joining (some), Switching (a few), Leaving (paired with Switching). No Swapped In yet visible to API consumers — that's an internal state surfaced by the Switching Thumbnail report (Phase 7).

Sentinel

phase-6c-pool-actions-ready.

Why this sub-phase third

Pool-action derivation depends on allocation results being in pfill_powerfill_guide. UE (6d) runs after this and modifies the output, so 6c is the "pre-UE" snapshot — useful for 6d's parity validation (does UE preserve, augment, or override 6c's pool actions?).


Sub-phase 6d — UE pass + synthetic trades subsystem

Scope (in)

  • Add 3 pfill_syn_* tables to schemapfill_syn_trade_base (11 cols), pfill_syn_powerfill_guide_all_rank (40 cols), pfill_syn_powerfill_guide (40 cols). DDL recoverable from PS_DemoData information_schema.columns per syn-trades deep dive §"The three tables". Add to a new 007_CreatePowerFillSynTradesSchema.sql (separate from 006 to keep diff reviewable). Add EF entities + register in PowerFillModule. A28 closed by this sub-phase.
  • Add pfill_powerfill_log table to 007_*.sql. Per F-VERIFY-2 (kickoff verification gate), the legacy psp_powerfillUE auto-creates this table on first invocation (NVO line 13265-13275). PSSaaS deploys it explicitly via 007 to make the dependency visible at DDL deploy time rather than implicit at run time. Closes the syn-trades deep dive Open Question 1.
  • Port psp_powerfillUE body — the two-part syntax via of_get_psp_powerfillue_syntax_a (NVO line 13246-16463) + of_get_psp_powerfillue_syntax_b (NVO line 16465-19801). Total ≈ 6,556 lines of T-SQL string-concat. PowerBuilder concatenates _a + _b; PSSaaS deploys the concatenated SQL as one CREATE PROCEDURE in 007.
  • UE invocation orderpsp_powerfillUE runs immediately after psp_powerfill_conset per w_powerfill.srw line 170-172 (verified in Phase 6 kickoff verification gate). UE receives the same parameter array. Both procs are mandatory; UE is not optional per syn-trades deep dive TL;DR.
  • UE teardown — UE deletes pfill_powerfill_guide, pfill_syn_powerfill_guide_all_rank, pfill_cash_market_map, pfill_syn_powerfill_guide (NVO 13441-13451 verified) before rebuilding. Implication: the post-psp_powerfill_conset state of pfill_powerfill_guide is discarded and rebuilt by UE. 6c's "pre-UE pool actions" output is therefore intermediate — 6d documents that the user-facing pfill_pool_guide is the post-UE state.
  • UE synthetic-trades populate — TRUNCATE pfill_syn_trade_base, rebuild from pscat_instruments × rmcat_todays_prices (NVO line 19019-19082). Then populate pfill_syn_powerfill_guide_all_rank (NVO 19083-19250 + 19450-19790 second pass for unallocated loans) with four parallel rank columns per syn-trades deep dive Phase D. Then populate pfill_syn_powerfill_guide with the recommended synthesis per loan (Phase E).
  • UE Pool Guide downstream consumption — UE re-runs psp_powerfill_pool_guide (or the equivalent inline logic) so pfill_pool_guide reflects synthesis-substituted trade IDs (trade_id_origin = 'pfill_syn_powerfill_guide' per syn-trades deep dive Phase G). 6d may need to re-port pool-guide logic from 6c if UE's version differs structurally.
  • /api/powerfill/status sentinel bump to phase-6d-ue-syn-trades-ready.

Scope (out)

  • The 5 open questions to Tom/Greg in the syn-trades deep dive remain logged. 6d plan should re-survey them and propose answers from PS_DemoData empirical observation where possible.
  • Whether to port UE as a verbatim T-SQL proc (per ADR-021) or to lift the synthesis logic into C# — the syn-trades deep dive flags this as a Phase 6 decision. 6d plan owns the Alternatives-First decision; recommend defaulting to verbatim T-SQL per ADR-021's hybrid rule, but explicitly consider C# lift for the synthesis CTE pipeline if ADR-021 doesn't cleanly extend.
  • Async / audit / failure cleanup (6e)

Shippable artifact

After 6d: a full POST /api/powerfill/run produces a populated pfill_powerfill_guide (UE-rebuilt), pfill_pool_guide (UE-modified), pfill_syn_trade_base, pfill_syn_powerfill_guide_all_rank, pfill_syn_powerfill_guide. Synthetic trades visible in pfill_pool_guide rows where trade_id_origin = 'pfill_syn_powerfill_guide' (column added by UE per syn-trades deep dive Phase G; 6d schema work must include it on pfill_pool_guide).

Sentinel

phase-6d-ue-syn-trades-ready.

Why this sub-phase fourth

UE depends on conset (6b) having produced a candidate set and pool-guide (6c) being live. UE then teardowns and rebuilds pfill_powerfill_guide and pfill_pool_guide, so it's the last data-modifying sub-phase before 6e adds run-management around it.

The syn-trades work is bundled into 6d (rather than split as 6d.1 schema + 6d.2 logic) because the UE proc populates the syn tables; deploying schema without populate logic produces empty tables that look broken to anyone querying them.


Sub-phase 6e — Async runs + audit + concurrency + PoC

Scope (in)

  • Convert POST /api/powerfill/run to async — runs return 202 Accepted + run_id immediately; execution happens in a background worker. Background-job pattern is Open Question #1; 6e plan resolves it.
  • pfill_run_history table — PSSaaS-only audit table per spec §Audit Trail line 251-253. Schema TBD pending Open Question #3 resolution (replay-capable vs summary-only). Phase 6e plan owns this table's schema and the 008 SQL script. Documented in spec under "Phase 6e PSSaaS-only tables" (mirroring spec §"Phase 4 PSSaaS-only tables").
  • GET /runs — paginated list of recent runs for tenant.
  • GET /runs/{run_id} — full status response per spec line 449-474.
  • POST /runs/{run_id}/cancel — graceful cancellation (cancel token wired through to the background worker).
  • BR-8 single-active-run-per-tenant — return 409 Conflict if a run is Pending/PreProcessing/Allocating/PostProcessing for the same tenant. Mechanism is Open Question #2; 6e plan resolves it.
  • BR-9 failure-state cleanup — when a run fails mid-pass, the output tables must be left in a "consistent state" per spec line 246. 6e plan defines what that means: option (i) preserve prior run's output verbatim (rollback); option (ii) clear the output tables with error markers; option (iii) leave the partial output and mark the run as Failed with no cleanup. Open Question #7 is essentially this; 6e plan resolves it.
  • PS_DemoData PoC sign-off — execute one full async run against PS_DemoData; assert pfill_powerfill_guide populated, pool-action distribution sensible, no NULL pool_action values; document the PoC outputs in a 6e completion-report Markdown for PO review. This is the kickoff §"CONSTRAINTS" success criterion.
  • /api/powerfill/status sentinel bump to phase-6e-async-runs-ready.

Scope (out)

  • Output query APIs (Phase 7) — the run is auditable; the rich query surface comes later
  • React UI (Phase 8)
  • Phase 9 parallel-validation against Desktop App
  • Connector Plugin spec/code (separate workstream)

Shippable artifact

After 6e: POST /api/powerfill/run returns 202 Accepted + run_id, run completes in background, GET /runs/{run_id} returns Complete + summary, pfill_run_history has the row, BR-8 enforced (a second concurrent POST /run returns 409), failed runs leave the system in a documented state. PS_DemoData PoC completed and the result documented for PO sign-off.

Sentinel

phase-6e-async-runs-ready — the canonical Phase 6 completion sentinel. After 6e, the /api/powerfill/status endpoint reflects "Phase 6 complete" until Phase 7 ships.

Why this sub-phase fifth (last)

Async execution and audit are cross-cutting concerns that only make sense once the synchronous run produces correct output. Trying to parallelize "build async first, then add allocation" inverts the dependency — you end up with a job framework wrapping nothing. Keeping 6e last also means the BackgroundService design (Open Q1) can be informed by what 6a-6d actually do (estimated CPU/memory shape, transaction boundaries, cancellation points).


Cross-cutting concerns

Schema artifact growth

Sub-phaseNew SQL artifactsWhy
6a006_CreatePowerFillRunProcedures.sql (BX procs)First Phase 6 SQL deploy; PRINT-in-guards per A32
6b(extends 006 OR 007_CreatePowerFillConsetProcedure.sql if 006 grows too large; Architect's call when 6b plans)conset is huge (~5,887 NVO lines); may justify own file for diff reviewability
6c(extends 006/007 OR 008_CreatePowerFillPoolGuideProcedure.sql)pool_guide is ~2,415 NVO lines; may justify own file
6d00x_CreatePowerFillSynTradesSchema.sql (3 syn tables + pfill_powerfill_log) + 00x_CreatePowerFillUeProcedure.sql (UE proc)Schema additions are first PowerFill schema growth since Phase 1; tracking separately matches the per-sub-phase reviewability principle
6e00x_CreatePowerFillRunHistoryTable.sqlLast PSSaaS-only addition before Phase 7

The exact numbering and file-split decisions are deferred to each sub-phase's plan. This breakdown commits only to at least one new SQL artifact per sub-phase, each with PRINT-in-guards (A32 / Deploy Verification Gate arm c).

Status sentinel discipline

Every sub-phase must bump /api/powerfill/status status field to its sentinel before the sub-phase commit ships. Verification (Deploy Verification Gate arm a) is a one-line curl:

curl -s http://pssaas.powerseller.local/api/powerfill/status | jq .status
# expected after each sub-phase: phase-6a-…-ready / phase-6b-…-ready / etc.

After staging deploy:

curl -s https://pssaas.staging.powerseller.com/api/powerfill/status | jq .status

If the sentinel doesn't reflect the new sub-phase, do not interpret subsequent run outputs (per Deploy Verification Gate canonical text).

Required Delegation Categories per sub-phase

The kickoff requires aggressive delegation of boilerplate. Per architect-context.md and process-discipline §9:

ClusterDefault delegationSub-phases affected
T-SQL transcription from NVO (with Truth-Rot trap avoidance)Fast subagent, per Phase 3 protocol that worked6a (BX procs), 6b (conset), 6c (pool_guide), 6d (UE)
Working-table CREATE TABLE + EF entity + registrationFast subagent when ≥3 tables6d (3 syn tables + pfill_powerfill_log)
Boilerplate Minimal API endpoint + DTO + testsFast subagent when ≥5 endpoints6a (preview + run), 6e (run management endpoints)
Boilerplate parameterized unit testsFast subagent when ≥5 testsAll sub-phases

Each sub-phase's plan commits to specific delegation clusters with specific subagent prompts following the Template 2 / Phase 3 SQL-transcription pattern. Deliberate Non-Delegation justifications must accompany any work that matches a category but is self-implemented, per the canonical process-discipline format.

Open questions touch order

Open QSub-phase that needs the answerNotes
1. Background-job pattern6eCan defer until 6e begins; not blocking 6a-6d
2. Single-active-run mechanism6eSame
3. pfill_run_history shape6eSame
4. A1 multi-pass semantics6b6b cannot start without resolution — either Architect verifies from NVO, or PO escalates to Tom/Greg
5. Synthetic-trades scope split6d (currently set in 6d per this breakdown)If PO prefers 6a does the schema and 6d does the logic, breakdown changes here
6. Calculator integration pattern6aResolved in 6a plan — recommendation: per-constraint-iteration batches (small scope, fresh calculator scope, cleaner failure isolation)
7. Failure-state semantics6eConnects to BR-9

So sub-phase 6a can begin after PO answers Open Q6 (or accepts the Architect's recommended default in the 6a plan). Sub-phases 6b through 6e cannot start until at least #4 (for 6b) and #1/#2/#3/#5/#7 (for 6e) are resolved. Recommendation: PO answers all 7 before 6b kickoff is written, even though 6a doesn't need them.

Failure-mode and rollback per sub-phase

Each sub-phase's commit batch should be independently revertable: if 6c's pool-action logic is wrong, reverting 6c should leave 6b's allocation output intact (just not pool-actioned). Specifically:

  • 6a → revert removes POST /run, POST /candidates/preview, 006. System back to Phase 5 state.
  • 6b → revert removes the conset proc + the 6b status sentinel; 6a's candidate-builder still works in isolation via the preview endpoint.
  • 6c → revert removes psp_powerfill_pool_guide invocation; 6b's allocation output survives.
  • 6d → revert removes UE invocation + syn tables (kept in DB for forensics; no DROP TABLE) + the UE-modified pool-guide; 6c's pre-UE pool actions become the user-facing output again.
  • 6e → revert restores synchronous run; pfill_run_history rows preserved (no DROP TABLE).

This rollback discipline is the Fail-Fast Permission practice (process-discipline §7) made concrete at sub-phase scope.


Risks captured at the breakdown level

  • R-BREAKDOWN-1 — Estimate fragility. Each sub-phase estimate is a range; the lower bound assumes no Truth Rot escalations beyond the 5 already found in the verification gate. The upper bound assumes 1-2 escalations per sub-phase. Phase 4's 1.5x slip suggests the upper bound is the realistic case for Phase 6.
  • R-BREAKDOWN-2 — Open Q4 (multi-pass semantics) is on the critical path of 6b. If PO can't get Tom/Greg input quickly, 6b plan must commit to Architect's empirical NVO trace as the answer, with explicit Phase 9 critique flag. The empirical trace is estimated at ~1-2 days of NVO reading; that's already absorbed in 6b's 7-10 day range.
  • R-BREAKDOWN-3 — UE-rebuilds-pool-guide invalidates 6c work. The UE pass deletes and rebuilds pfill_powerfill_guide and modifies pfill_pool_guide per syn-trades deep dive (verified at NVO 13441-13451). 6c's "pre-UE pool actions" output is intermediate. If 6d discovers the UE pool-guide differs structurally from 6c's, 6c may need partial rework. Mitigation: 6c's plan must explicitly call out "the 6c output is overwritten by UE; 6c's parity test compares against the pre-UE legacy state."
  • R-BREAKDOWN-4 — Sub-phase 6e's PoC depends on PS_DemoData stability. Per A35, PS_DemoData carry-cost rates are uniform 0.27 placeholders; per the Phase 4 preflight probe, PS_DemoData has only PRICES_STALE warning (prices from 2023-04-26). The 6e PoC will produce technically valid output but the economic meaningfulness is limited. Phase 9 parallel-validation against a Desktop App customer's real DB is the ultimate parity test; 6e is the "shape correctness" PoC.
  • R-BREAKDOWN-5 — The kickoff-doc itself had Truth Rot. Five findings from the Primary-Source Verification Gate before any code was written: kickoff-listed psp_powerfill_conset parameters wrong (7 vs actual 6); psp_pfill_insert4_pool_guide (771 NVO lines) entirely omitted; psp_powerfill_pool_guide line range stale (kickoff says 7192-11186; live version is 8770-11185); syn-trades deep dive Open Q1 wrong about pfill_powerfill_log (it's auto-created in UE, not orphaned); BX cash-grids takes bx_price_floor as its own parameter, not as a conset parameter. Mitigation: every sub-phase plan must run Primary-Source Verification before drafting; assume more lurks. Kickoff text is not primary source.

Approval gate

This breakdown is itself a deliverable that needs Collaborator review and PO approval before sub-phase 6a planning artifact is treated as committed direction. The 6a plan can be drafted in parallel with breakdown review (since 6a is upstream of every breakdown decision except whether 6e has 5 sub-phases or 4) — but the 6a plan should not be merged until the breakdown is approved.

Approval signal: PO comments "approved" or "approved with edits" on this doc; Architect updates the doc and marks Status: Approved.


Next deliverable from this Architect session: the full sub-phase 6a plan at .cursor/plans/powerfill-phase-6a.plan.md, drafted per the Phase 4/5 12-section template, plus the open-questions document at powerfill-phase-6-open-questions.md.