Skip to main content

PowerFill Pre-6b Sweep — Completion Report

Author: PSSaaS Systems Architect Date: 2026-04-18 Status: Code complete; deployed to local pssaas-db AND PS_DemoData; regression baseline preserved; pending Collaborator review and PO push Sentinel: Unchanged at phase-6a-candidate-builder-ready (this sweep is a 6a follow-up, not a sub-phase bump) Companion docs:


TL;DR

Enumerated every legacy-plugin schema migration in n_cst_powerfill.sru::of_update_database (NVO 6004-6756 — function boundary larger than the kickoff's ~6500-6700 estimate). 23 distinct operations identified; 21 confirmed out-of-scope (PSSaaS already owns the 17 pfill_* tables via 001, the 2 views via 002, the carry-cost auto-seed is operator-driven per A35, the pxcat_site_plugins.current_version registry column is Desktop-App-only, and the rmusr_payups table create is a Risk-Manager-owned base table); 2 in-scope — M1 rmusr_payups.div_by (already shipped in 007 from the 6a discovery arc) and M2 mkt_shipped_date DATETIME NULL on 8 base pipeline tables (newly shipped in 007).

Empirical verification on PS_DemoData: every column the sweep would touch is already present — M2 ships as defensive insurance for future tenants whose 8.x DB hasn't been opened by the legacy PowerFill plugin window. The local pssaas-db state (only 2 of 8 base tables exist, both already migrated) exercises the skip-safe paths cleanly.

Regression baseline preserved: POST /api/powerfill/run against PS_DemoData returned status: Complete in 1.04s (within noise of the 1.91s pre-sweep baseline) with all 3 steps succeeded.

This sweep closes the latent class of Invalid column name failures before sub-phase 6b's allocation work surfaces another mid-pass.


What was produced

Modified

  • src/backend/PowerSeller.SaaS.Modules.PowerFill/Sql/007_BackfillLegacyPluginMigrations.sql — extended with:
    • M2 cursor-driven 8-table mkt_shipped_date backfill (NVO 6640-6653 source-line cite). For each of loan, loan_shipped, loan_modifications, loan_notional, loan_inquiry, psarc_loan_shipped, psarc_loan_modifications, pscat_loan_essential: OBJECT_ID guard for table-missing → PRINT skip; COL_LENGTH guard for column-already-present → PRINT skip; otherwise ALTER TABLE ... ADD mkt_shipped_date DATETIME NULL + PRINT success.
    • Header comment updated to record the verified function boundary (NVO 6004-6756, not the kickoff's "~6500-6700") and the sweep result.
    • Trailing out-of-scope inventory documenting each of the 6 categories of operations enumerated but NOT ported, with NVO line citations and reasoning, so a future reader doesn't re-derive the classification.
  • docs-site/docs/specs/powerfill-assumptions-log.md:
    • A44 extended with the 2026-04-18 sweep result (function boundary correction, in-scope vs out-of-scope counts, defensive-insurance disposition).
    • A45 added — full entry for the M2 mkt_shipped_date finding in the A44 house style: NVO citation, source proc references, comment-vs-code drift in NVO 6640, empirical PS_DemoData state verification, local pssaas-db state verification, sub-phase implications, out-of-scope items table.
    • Open Questions summary updated: A43 marked RESOLVED (GRANT EXECUTE applied 2026-04-17); new A41-A45 disposition section added (all VERIFIED + SHIPPED; none require Tom/Greg input).

New

  • docs-site/docs/handoffs/powerfill-pre-6b-sweep-completion.md — this completion report.
  • docs-site/docs/devlog/2026-04-18-pre-6b-sweep.md — devlog entry.

Out of scope (deliberately not produced)

  • No C# changes. No spec amendments to powerfill-engine.md (the sweep findings don't affect any spec API contract).
  • No status sentinel bump (per kickoff direction — this is a 6a follow-up, not a sub-phase ship).
  • No new test file (no C# code path changes; the SQL is verified by Deploy Verification Gate arms a/b/c).

Decisions made

#DecisionRationaleWhere
D1Extend 007 rather than create 008The new M2 block is small (~50 lines including comments). 007's "Future M-rows" section was authored exactly for this kind of follow-up. Single file is easier to onboard against than two near-identical migration files.007_BackfillLegacyPluginMigrations.sql
D2Cursor-driven loop (vs 8 hand-rolled IF blocks)The 8 tables share identical structure; a single cursor is more reviewable and trivially extensible if a future plugin sweep adds a 9th table. SQL Server LOCAL FAST_FORWARD cursors over a tiny in-memory @table_var are negligible cost.007 lines 124-170
D3Ship M2 even though PS_DemoData already has the columnA44's whole point is defensive insurance for future tenant DBs. PS_DemoData was empirically already migrated, but a future tenant onboarding from a fresh 8.x install or a different upgrade path would surface the same failure that surfaced 6a-DATA-1 for div_by. The script is a no-op when already-applied; cost of shipping is zero.007 M2 block
D4Document out-of-scope items in trailing comment blockA future reader inheriting this sweep should not have to re-do the 23-operation classification. The trailing comment block records each NVO range, the operation kind, and the reason it's not ported.007 lines 172-201
D5Deliberate Non-Delegation of the SQL writingThe interpretation work (sweep, classification, verification gate) was the bulk of the task and was correctly self-implemented per Required Delegation Categories. The remaining SQL is a single migration block (~50 LOC) with rich decision context (D2, D3, D4 above + the comment-vs-code drift discovery in NVO 6640). Subagent handoff would have cost more than self-implementation. See §"Required Delegation Categories" below.This report §"Required Delegation Categories" + commit message
D6Do NOT port pfill_carry_cost auto-seed (NVO 6660-6687)Phase 4 ConfigurationService manages carry-cost CRUD per A35 (operator-driven). Auto-seeding defaults would conflict with the PSSaaS UX intent (operators configure their own curves) and would create a one-shot side-effect that's confusing on idempotent re-deploys.Documented in 007 trailing comment + this report §"Out-of-scope items"
D7Do NOT port pxcat_site_plugins.current_version migration (NVO 6037-6042)Desktop-App plugin-version registry; never consumed by PSSaaS. PSSaaS has its own status sentinel for version reporting (/api/powerfill/status).Documented in 007 trailing comment + A45

Migrations enumerated (full inventory of of_update_database NVO 6004-6756)

#NVO lineOperationTargetDispositionPS_DemoData state
16037-6042_set_addcolumn current_version CHAR(20) NULL (if missing)pxcat_site_pluginsOUT-OF-SCOPE — Desktop App plugin-version registry; never used by PSSaaSpresent
26094-6109_create_table if missingpfill_epci_paramsOUT-OF-SCOPE — PSSaaS owns via 001n/a
36119-6130_create_table if missingpfill_constraintsOUT-OF-SCOPE — 001n/a
46142-6150_create_table if missingpfill_constraint_sec_rule_relOUT-OF-SCOPE — 001n/a
56163-6183_create_table if missingpfill_loan_constraint_poolOUT-OF-SCOPE — 001n/a
66194-6213_create_table if missing + settlement_date fixuppfill_lockdown_guideOUT-OF-SCOPE — 001 (settlement_date is in PSSaaS DDL from day 1)n/a
76226-6243_create_table if missingpfill_cblock_guideOUT-OF-SCOPE — 001n/a
86253-6297_create_table if missingpfill_trade_cblock_baseOUT-OF-SCOPE — 001n/a
96309-6322_create_table if missingpfill_trade_paramsOUT-OF-SCOPE — 001n/a
106332-6339_create_table if missingpfill_carry_costOUT-OF-SCOPE — 001n/a
116350-6356_create_table if missingpfill_cash_market_mapOUT-OF-SCOPE — 001n/a
126367-6382_create_table if missingpfill_kickout_guide_01OUT-OF-SCOPE — 001n/a
136393-6418_create_table if missingpfill_loan_orderingOUT-OF-SCOPE — 001n/a
146426-6458_create_table + 2-col PK if missingpfill_loan2trade_candy_level_01OUT-OF-SCOPE — 001n/a
156462-6484_create_table + PK if missingpfill_powerfill_guideOUT-OF-SCOPE — 001n/a
166488-6526_create_table if missingpfill_trade_baseOUT-OF-SCOPE — 001n/a
176537-6551_create_table if missingpfill_payupsOUT-OF-SCOPE — 001n/a
18a6564-6579_create_table if missingrmusr_payupsOUT-OF-SCOPE — Risk-Manager base table; PSSaaS doesn't own. When missing, Phase 6a fails fast with SqlException 208 (documented A42 behavior).present
18b6577 / 6591-6592_set_addcolumn div_by NUMERIC(5,0) NULLrmusr_payupsIN-SCOPE — M1 (already shipped in 007, 2026-04-17)present (007 M1 already applied)
196599-6628_create_table + 4-col PK if missingpfill_pool_guideOUT-OF-SCOPE — 001n/a
206640-66538-table FOR loop: _set_addcolumn mkt_shipped_date DATETIME NULL if missingloan, loan_shipped, loan_modifications, loan_notional, loan_inquiry, psarc_loan_shipped, psarc_loan_modifications, pscat_loan_essentialIN-SCOPE — M2 (newly shipped in 007, 2026-04-18)all 8 present
216660-6687INSERT default carry-cost rows when pfill_carry_cost emptypfill_carry_cost dataOUT-OF-SCOPE — Phase 4 ConfigurationService manages CRUD per A35 (operator-driven)295 rows (per A35, uniform 0.27 placeholder rates)
226705-6728DROP/CREATE pfillv_pf_forensics_tradeside viewviewOUT-OF-SCOPE — PSSaaS owns via 002view present (deployed by 002)
236730-6753DROP/CREATE pfillv_existng_pool_disposition viewviewOUT-OF-SCOPE — PSSaaS owns via 002view present (deployed by 002)

Score: 23 operations enumerated, 21 out-of-scope (with documented reason), 2 in-scope (M1 + M2; both shipped via 007).

Comment-vs-code drift discovered in NVO 6640

The legacy comment at NVO 6640 reads:

// [TMorgan - 02/07/17] - Add mkt_purchase_date to pipeline tables

But the executable code immediately below (NVO 6648) adds mkt_shipped_date, NOT mkt_purchase_date. PS_DemoData also has mkt_purchase_date present on all 8 tables — confirming that mkt_purchase_date is added by some OTHER plugin (likely BestEx or Risk Manager — out of scope for this sweep, which is PowerFill-only).

This is a documented finding (A45 §"NVO comment-vs-code drift") but does not affect M2's correctness: the migration we ship adds mkt_shipped_date exactly as the executable code does, not as the misleading comment suggests. If a future BestEx/RM plugin sweep finds the mkt_purchase_date migration source, that's a separate workstream's deliverable.


Gate findings

Primary-Source Verification Gate (NVO-vs-actual)

Three layers exercised per the candidate refinement banked in the 2026-04-17b devlog §"Process discipline observations":

LayerWhat was verifiedOutput
NVO-vs-docThe kickoff's "NVO ~6500-6700" estimate vs the actual function boundaryFunction actually spans NVO 6004-6756 (752 lines). The kickoff's range was too narrow at both ends. Recorded in 007 header comment + this report.
NVO-vs-implementationComment-vs-code drift at NVO 6640 ("Add mkt_purchase_date" comment, _set_addcolumn mkt_shipped_date code)Drift confirmed. Documented in A45. The migration we ship matches the code, not the comment.
NVO-vs-tenant-DBFor each in-scope migration, query sys.columns on PS_DemoData to determine already-present vs needs-migrationAll M2 targets already present on PS_DemoData; M1 already present (007 M1 applied 2026-04-17). M2 ships as defensive insurance. Empirical sqlcmd output captured below.

Required Delegation Categories

This sweep is interpretation-heavy (sweep enumeration + classification + verification) — those parts were correctly self-implemented. The actual SQL writing was small enough that delegation overhead would have exceeded benefit.

Deliberate Non-Delegation: SQL script generation from a documented schema (>5 tables)
— partially matches because M2 touches 8 tables
Task: Add a single cursor-driven 8-table mkt_shipped_date backfill block to
007_BackfillLegacyPluginMigrations.sql, plus extend the file's header
comment and add a trailing out-of-scope inventory.
Reason for self-implementation: the SQL writing is a single ~50-LOC block
with rich decision context that would need to be relayed:
- the cursor pattern (D2) vs 8 hand-rolled IF blocks
- the defensive-insurance shipping disposition (D3) when PS_DemoData is
empirically already migrated
- the trailing-comment classification (D4) for the 21 out-of-scope items
- the comment-vs-code drift discovery in NVO 6640
Subagent handoff would require a Template-2 prompt that's longer than the
resulting SQL. The Phase 6a 006 transcription was cleanly delegated because
the work was 670 lines of mechanical literal text; this sweep's output is
~50 lines of carefully-decided structure.
Context that would be lost in handoff: the 23-operation classification, the
per-target empirical PS_DemoData verification, the comment-vs-code drift
finding, the M1+M2 file structure decisions, A44/A45 cross-references.

The 006 SQL transcription delegation in Phase 6a established that fast-subagent SQL work is the right call when the work is mechanical literal transcription of large NVO bodies. This sweep's M2 block is structurally different — small surface, high decision density. Self-implementation is correct.

Deploy Verification Gate

ArmDescriptionEvidence
(a) Sentinel signal/api/powerfill/status unchanged at phase-6a-candidate-builder-readycurl -s http://pssaas.powerseller.local/api/powerfill/status{"module":"PowerFill","status":"phase-6a-candidate-builder-ready"} ✓ (correctly unchanged — this is a 6a follow-up, not a sub-phase bump)
(b) Live API regressionPOST /api/powerfill/run against PS_DemoData still returns status: Complete after the sweeprun_id=4c4d0ce7-a9f2-425d-ba1a-8e78e75a8205, status=Complete, duration 00:00:01.0420433 (well within noise of pre-sweep baseline 00:00:01.9113199); 3 FHLMC constraints iterated; all 3 steps succeeded; same R-DATA-1 (0 candidates per E13). Full output captured below.
(c) Live DB probe — localEach migration verifiable via sys.columns on pssaas-dbAll deploy-script PRINT lines fire as expected: loan + loan_shipped show "already exists (skipped ALTER)" branch (those tables exist with the column already); 6 of 8 tables show "TABLE MISSING — skipping" branch (skip-safe path); rmusr_payups shows "TABLE MISSING — skipping div_by backfill" (M1 skip).
(c) Live DB probe — PS_DemoDataEach migration verifiable via sys.columns on PS_DemoDataAll 9 PRINT lines (M1 div_by + M2 ×8 mkt_shipped_date) show "already exists (skipped ALTER)" — confirming PS_DemoData was already fully migrated AND that 007's idempotency is correct on already-applied tenants.

PoC verification commands and outputs

Deploy to local pssaas-db (skip-safe paths)

$ docker exec pssaas-db /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa \
-P "***" -No -d PSSaaS_Dev \
-i /docker-entrypoint-initdb.d/powerfill/007_BackfillLegacyPluginMigrations.sql

PowerFill 007: dbo.rmusr_payups does not exist (local-dev / unseeded tenant) — skipping div_by backfill
PowerFill 007: dbo.[loan].mkt_shipped_date already exists (skipped ALTER)
PowerFill 007: dbo.[loan_inquiry] does not exist (local-dev / unseeded tenant) — skipping mkt_shipped_date backfill
PowerFill 007: dbo.[loan_modifications] does not exist (local-dev / unseeded tenant) — skipping mkt_shipped_date backfill
PowerFill 007: dbo.[loan_notional] does not exist (local-dev / unseeded tenant) — skipping mkt_shipped_date backfill
PowerFill 007: dbo.[loan_shipped].mkt_shipped_date already exists (skipped ALTER)
PowerFill 007: dbo.[psarc_loan_modifications] does not exist (local-dev / unseeded tenant) — skipping mkt_shipped_date backfill
PowerFill 007: dbo.[psarc_loan_shipped] does not exist (local-dev / unseeded tenant) — skipping mkt_shipped_date backfill
PowerFill 007: dbo.[pscat_loan_essential] does not exist (local-dev / unseeded tenant) — skipping mkt_shipped_date backfill
PowerFill 007: backfill legacy plugin migrations complete

Deploy to PS_DemoData (idempotent skip — already-present)

$ docker exec pssaas-db /opt/mssql-tools18/bin/sqlcmd \
-S "hostedps-sql.public.086ea791c2f1.database.windows.net,3342" \
-U "kevin_pssaas_dev" -P 'M0th3rFuck1ng$44$' -No -d PS_DemoData \
-i /docker-entrypoint-initdb.d/powerfill/007_BackfillLegacyPluginMigrations.sql

PowerFill 007: dbo.rmusr_payups.div_by already exists (skipped ALTER)
PowerFill 007: dbo.[loan].mkt_shipped_date already exists (skipped ALTER)
PowerFill 007: dbo.[loan_inquiry].mkt_shipped_date already exists (skipped ALTER)
PowerFill 007: dbo.[loan_modifications].mkt_shipped_date already exists (skipped ALTER)
PowerFill 007: dbo.[loan_notional].mkt_shipped_date already exists (skipped ALTER)
PowerFill 007: dbo.[loan_shipped].mkt_shipped_date already exists (skipped ALTER)
PowerFill 007: dbo.[psarc_loan_modifications].mkt_shipped_date already exists (skipped ALTER)
PowerFill 007: dbo.[psarc_loan_shipped].mkt_shipped_date already exists (skipped ALTER)
PowerFill 007: dbo.[pscat_loan_essential].mkt_shipped_date already exists (skipped ALTER)
PowerFill 007: backfill legacy plugin migrations complete

Pre-sweep PS_DemoData state probe

$ sqlcmd ... -d PS_DemoData -Q "<8-table mkt_shipped_date check via sys.columns>"
table_name mkt_shipped_date
--------------------------- ----------------
loan present
loan_inquiry present
loan_modifications present
loan_notional present
loan_shipped present
psarc_loan_modifications present
psarc_loan_shipped present
pscat_loan_essential present

(8 rows affected)

# Bonus probes (not in original scope, captured for completeness):
table_name mkt_shipped_date mkt_purchase_date
loan present present (mkt_purchase_date is added by another plugin)
... (same shape on the other 7) ...

table_name current_version_status
pxcat_site_plugins present

table_name div_by_status
rmusr_payups present (007 M1 already applied 2026-04-17)

Pre-sweep local pssaas-db state probe

$ sqlcmd ... -d PSSaaS_Dev -Q "<8-table mkt_shipped_date check via sys.columns>"
table_name mkt_shipped_date_status
------------------------- -----------------------------
loan present
loan_inquiry TABLE MISSING
loan_modifications TABLE MISSING
loan_notional TABLE MISSING
loan_shipped present
psarc_loan_modifications TABLE MISSING
psarc_loan_shipped TABLE MISSING
pscat_loan_essential TABLE MISSING

(8 rows affected)

Regression baseline: POST /api/powerfill/run against PS_DemoData

Pre-sweep (baseline captured at session start):

run_id=db72d930-6baf-4aef-aa1a-f1119eb1a57d
status=Complete
duration=00:00:01.9113199
constraint_count=3, candidate_count=0 (R-DATA-1)
all 3 steps Succeeded

Post-sweep (after 007 M2 deploy):

run_id=4c4d0ce7-a9f2-425d-ba1a-8e78e75a8205
status=Complete
duration=00:00:01.0420433 (faster than baseline, well within noise)
constraint_count=3, candidate_count=0 (R-DATA-1, expected — same data state)
all 3 steps Succeeded

Regression baseline preserved ✓.


Counterfactual retro

Knowing what I know now, what would I do differently?

  1. The kickoff's NVO line range estimate ("~6500-6700") was off at both ends. The actual function boundary is NVO 6004-6756 (752 lines, vs the kickoff's ~200-line estimate). The earlier portion (6004-6500) is mostly pfill_* table CREATE-IF-MISSING blocks that PSSaaS already owns via 001, so the kickoff's narrower range happened to bracket the most likely-relevant portion — but it would have missed M2 entirely if I had trusted the range. Lesson: always verify the function boundary by Grepping ^public function|^end function rather than trusting a passed-along range. Kickoff's anti-pattern note ("trust the function boundary, not the rough line range") was the right preempt.

  2. The "find every migration" framing turned out to be 21:2 out-of-scope-to-in-scope. Most of of_update_database is PSSaaS-already-owns-this — the legacy plugin was responsible for creating pfill_* tables that PSSaaS now owns via 001, plus 2 views that are in 002, plus a carry-cost auto-seed that conflicts with our operator-driven CRUD. The ratio is reassuring: PSSaaS's existing schema deploy scripts already cover the bulk of legacy migration responsibility. Net new work for PSSaaS = the columns added to non-PSSaaS-owned base tables, which is a much smaller surface than I initially feared.

  3. PS_DemoData was already fully migrated. Both M1 (div_by, applied via 007 on 2026-04-17) AND M2 (mkt_shipped_date, applied at some point before Joe's snapshot OR included in 7.x base schema) were no-ops. The original 6a-DATA-1 finding was an outlier in that div_by happened to be a column the legacy plugin added in a more recent version than PS_DemoData's snapshot baseline. This is good news: PS_DemoData is more representative of "fully-migrated tenant" than I'd assumed. The defensive-insurance ship is correct discipline anyway — a future tenant onboarding from a different snapshot baseline could surface the same issue.

  4. What I'm not doing next time: I considered writing a generalized "for each _set_addcolumn discovered, generate a deploy script entry" code generator. Decided against it — the per-finding judgment calls (in-scope vs out-of-scope, what guards to use, whether to skip-safe on table-missing vs error, whether to ship even when PS_DemoData has it) require Architect interpretation that a generator would replicate poorly. The 23-operation manual classification is the right tool for the job at this scale. If a future plugin (BestEx, RM, etc.) has 100s of migrations, then the generator becomes worth building. For PowerFill's 23, manual is faster and more accurate.

  5. Process pattern observation: The 6a-DATA-1 → A44 pattern → pre-6b sweep arc is exactly the Counterfactual Retro working as designed. 6a discovered an instance, codified the pattern (A44), and the next session enumerated the class. Without A44's "Recommended pre-6b sweep" §, the 2-3 hour cost of this sweep would have been borne by 6b mid-allocation as a serial drip of Invalid column name findings — much more expensive in context and morale. The act of writing A44 paid for itself today.

  6. Three-layer Primary-Source Verification Gate refinement (banked in the 6a-PoC-complete devlog): this sweep exercised all three layers (NVO-vs-doc, NVO-vs-implementation, NVO-vs-tenant-DB) and produced findings at each. Specifically: NVO-vs-doc caught the function-boundary range correction; NVO-vs-implementation caught the comment-vs-code drift at NVO 6640; NVO-vs-tenant-DB caught the "already-present everywhere" defensive-insurance disposition. The three layers ARE distinct categories, with distinct findings. Worth promoting from "banked refinement" to "v3.1 nomination" after one more session corroborates.


  1. Collaborator review of the modified 007_*.sql, the assumption-log A44 extension + A45 addition, and this completion report. Phase 6a review pattern; estimated 30 minutes (the kickoff's target scaling — sweep work is mechanical enough that the review is fast).
  2. PO push of the 3-4 atomic commits (Architect commits; PO controls git push).
  3. Sub-phase 6b kickoff drafting — prerequisites: PO answers Q4 (multi-pass semantics; Architect default Option A: empirical NVO trace). The 6a-PERM-1 GRANT is already in place; this sweep eliminated the M2 latent-finding class.
  4. Optional follow-up (deferred): sweep BestEx and Risk Manager plugin sources for analogous of_update_database migrations. The mkt_purchase_date column added by one of those plugins is a candidate for a similar pre-6b-style sweep specific to those workstreams. NOT in this sweep's scope; flagged for future Architect dispatch when those modules' data integrity becomes runtime-critical.

Notes on this session's process

  • Session-start checklist completed before any action (CLAUDE.md, AGENTS.md, architect-context, process-discipline, handoff-prompts, A44, 6a-poc-complete devlog, 007 prototype). Local environment verified: sentinel green, baseline POST /run Complete in 1.91s.
  • Three-layer Primary-Source Verification Gate exercised; produced findings at each layer (function-boundary correction, comment-vs-code drift, already-present empirical state).
  • Required Delegation Categories classification: SQL writing (~50 LOC, single block, high decision density) self-implemented with Deliberate Non-Delegation justification logged here AND in the commit message. The Phase 6a 006 SQL transcription was cleanly delegated because that work was 670 lines of mechanical literal NVO transcription; this sweep's M2 block is structurally different and self-implementation was the right call.
  • Andon-cord NOT pulled — no findings warranted scope expansion or PO escalation. The kickoff explicitly said "if a finding turns out to be larger than expected … STOP and report" — none did. The 21:2 out-of-scope-to-in-scope ratio meant the sweep was smaller than expected, not larger.
  • WSL Ubuntu was attempted for shell work but the existing docker exec pssaas-db /opt/mssql-tools18/bin/sqlcmd pattern (used in Phase 6a) worked fine via PowerShell — the sqlcmd-in-container path bypasses the password-escape issue entirely (single-quoted PowerShell strings preserve $ literally). No new shell tooling was needed; future tenant-DDL work can continue with the existing pattern.
  • Counterfactual Retro filled with 6 named observations. One process-discipline candidate: promote the three-layer Primary-Source Verification Gate refinement from "banked" to "v3.1 nomination" after one more corroborating session.

Pre-6b sweep is complete and PoC-verified. The Phase 6b Architect can dispatch their work without fear of mid-allocation Invalid column name failures from the legacy-plugin-migration class.


End of pre-6b sweep completion report.