PowerFill Module — Phase 1 Complete
Date: 2026-04-16
Agent: Systems Architect
Scope: Scaffold the PowerSeller.SaaS.Modules.PowerFill .NET project, define 17 EF Core entities mapping to the legacy pfill_* schema, ship an idempotent T-SQL deployment script, register the module with TenantDbContext, and verify round-trip schema compatibility via an integration test.
Why
Phase 0 produced the spec, three ADRs, and an assumptions log for PowerFill. Phase 1 is the first implementation phase — domain model + schema — and the foundation everything else builds on. It has to be exactly right because every subsequent phase (data access in Phase 2, CRUD in Phase 4, allocation engine in Phase 6) depends on this data model matching the legacy schema faithfully.
What Was Done
Phase 0 corrections (committed in the same commit)
Direct extraction of DDL from n_cst_powerfill.sru line 6004 (of_update_database) revealed the Phase 0 deep dive was materially wrong:
- Actual
pfill_*table count is 17, not 13. - One table is named
pfill_epci_params, notpfill_ect_params. - Three tables missing from the Phase 0 inventory entirely:
pfill_loan_constraint_pool,pfill_loan_ordering,pfill_payups.
Corrections landed in docs-site/docs/legacy/powerfill-deep-dive.md, docs-site/docs/specs/powerfill-engine.md, and Assumption A21 in the assumptions log. The miscount triggered an antipattern nomination (see below).
Code
New project src/backend/PowerSeller.SaaS.Modules.PowerFill/ with:
PowerSeller.SaaS.Modules.PowerFill.csproj— .NET 8, referencesSharedKernelandInfrastructure, copiesSql/**/*.sqlto output for test harness access.PowerFillModule.cs— staticRegisterServices+MapEndpoints, mirrors BestEx's pattern. Registers 17 entities withTenantDbContext. Exposes/powerfill/statusendpoint only (Phase 1 is schema-only).- 17 entity classes in
Domain/— one perpfill_*table, column-by-column transcription of NVO DDL (lines 6094–6638). Attribute-based:[Table],[Column],[Key],[MaxLength],[Precision(p, s)],[Column(TypeName = "numeric(n,0)")]for small integer columns. Domain/PowerFillEntityConfiguration.cs—IEntityTypeConfiguration<T>for each composite-key entity (12 of the 17 entities have composite keys).Sql/001_CreatePowerFillSchema.sql— idempotent deployment script withIF OBJECT_ID(...) IS NULLguards. Column types match legacy (CHAR(n),NUMERIC(p,s),DATETIME). Includes thepfill_carry_costseed block from NVO lines 6660–6687, also guarded.Sql/002_ValidatePS_DemoDataSchema.sql— read-only validation script Kevin can run manually against PS_DemoData.Sql/README.md— deployment procedure for local dev and PS_DemoData.
Module wired into PowerSeller.SaaS.Api/Program.cs (registration + endpoint mapping). Added to PowerSeller.SaaS.sln. Dockerfile.dev updated to COPY the new csproj paths so docker compose build api will still work for Phase 2 onwards.
Tests
New test project tests/PowerSeller.SaaS.Modules.PowerFill.Tests/:
PowerFillModuleTests.RegisterServices_DoesNotThrow— smoke test (mirrors BestEx).EntityConfigurationTests— eight tests covering: entity count (exactly 17), legacy table name mapping, the CLAUDE.md invariant that no entity usesfloat/double, every entity has a configured primary key, composite-key column sets match legacy DDL, single-column keys are correctly declared, everydecimalproperty has explicit precision/scale.Sql/SchemaScriptIntegrationTests.SchemaScript_DeploysCleanly_AndModelMatchesActualSchema— deploys the SQL script against a transient SQL Server database, runs it twice (idempotency check), and compares EF model shape againstINFORMATION_SCHEMA.COLUMNS. UsesSkippableFactso it no-ops when thePFILL_TEST_SQLSERVERenvironment variable is unset. Integration test is the round-trip guard that catches entity↔schema drift.
Test results
Full solution: 42 passing tests, 0 failures. Integration test confirms the schema deploys cleanly against the Docker pssaas-db SQL Server container, is safely re-runnable, and matches the EF model's table/column shape exactly.
Key Decisions
Schema deployment strategy (Plan §2, Option C)
Chose hand-authored entities + idempotent SQL script over EF Core code-first migrations. Rationale:
- ADR-006 requires exact legacy schema preservation for Desktop App coexistence.
- EF's code-first generated DDL uses
NVARCHARfor strings andDECIMAL(18,2)for decimals by default, which would silently drift from legacyCHAR(n)andNUMERIC(p,s). - PS_DemoData already contains some
pfill_*tables with data; an EF migration would conflict. - Idempotent SQL with
IF OBJECT_IDguards mirrors the legacy NVO's deployment idiom and is safe against partially-populated databases.
CHAR→NVARCHAR and DATETIME→datetime2 drift accepted for Phase 1 (Plan Open Question #3)
Entities map to NVARCHAR(n) and datetime2 — EF Core's defaults — while the actual tables use legacy CHAR(n) and DATETIME. SQL Server coerces implicitly, so reads/writes work. The drift is logged as Assumption A26 and flagged for Phase 9 parallel-validation review. Explicit [Column(TypeName = "char(n)")] + value converters are a Phase 9 parity-hardening task, not Phase 1 work.
The integration test surfaces this drift as diagnostic output (118 columns), not failure. A distinct type-family mismatch (e.g., int vs decimal) would still fail the test.
pfill_epci_params class name
Table name preserved exactly per ADR-006. C# class name is PowerFillEpciParams — the Epci abbreviation is retained pending Tom/Greg's confirmation of what EPCI stands for (Assumption A21: best guess "Embedded Pay-up Cash Instrument").
Files Produced / Modified
New
src/backend/PowerSeller.SaaS.Modules.PowerFill/(project, module, 17 entities, configurations, SQL scripts, README)src/backend/tests/PowerSeller.SaaS.Modules.PowerFill.Tests/(test project, 3 test classes, 9 tests)
Modified
src/backend/PowerSeller.SaaS.sln— added the two new projectssrc/backend/PowerSeller.SaaS.Api/Program.cs— registered and mapped the PowerFill modulesrc/backend/PowerSeller.SaaS.Api/PowerSeller.SaaS.Api.csproj— added project referencesrc/backend/Dockerfile.dev— added COPY steps for the new csproj filesdocs-site/docs/legacy/powerfill-deep-dive.md— corrected 13→17 table count and table namingdocs-site/docs/specs/powerfill-engine.md— corrected ECT→EPCI and added Phase 1 notedocs-site/docs/specs/powerfill-assumptions-log.md— rewrote A21 (ECT→EPCI); added A26 (Phase 1 type drift)docs-site/docs/handoffs/pssaas-session-handoff.md— updated backlog item #18 and added Phase 1 summary
What's Next
Phase 2 per the plan in docs-site/docs/specs/powerfill-engine.md: data access + preflight validation. Builds on the Phase 1 entities. Will include:
- Repository classes for the 8 configuration tables
- Preflight readiness check (
POST /api/powerfill/preflight) - The
pfillv_existng_pool_dispositionview - Constraint tree queries (GET) with children materialization
Phase 2 is expected to take 3–5 days of focused work. Specification already complete (docs-site/docs/specs/powerfill-engine.md).
Risks Captured
- A26 (type drift). Phase 9 parallel validation may reveal functional issues from CHAR/NVARCHAR or DATETIME/datetime2 drift. Mitigation path documented (explicit
[Column(TypeName)]+ trim value converters). - A21 (EPCI meaning). Class name stays
PowerFillEpciParamsuntil Tom/Greg clarify what EPCI actually stands for. If they rename, one search-and-replace handles it. - A25 — new (not in this devlog but worth noting): The
pfill_loan_constraint_pooltable's runtime semantics (configuration vs. run-intermediate) are unconfirmed. Phase 2 will resolve this. - Phase-0 Truth Rot antipattern nominated. The miscount (13 vs 17) is the triggering example. The Architect proposes adding a "Primary-Source Verification Gate" to the Gates section of process discipline. Relayed to Collaborator for canonical review.