Skip to main content

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, not pfill_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, references SharedKernel and Infrastructure, copies Sql/**/*.sql to output for test harness access.
  • PowerFillModule.cs — static RegisterServices + MapEndpoints, mirrors BestEx's pattern. Registers 17 entities with TenantDbContext. Exposes /powerfill/status endpoint only (Phase 1 is schema-only).
  • 17 entity classes in Domain/ — one per pfill_* 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.csIEntityTypeConfiguration<T> for each composite-key entity (12 of the 17 entities have composite keys).
  • Sql/001_CreatePowerFillSchema.sql — idempotent deployment script with IF OBJECT_ID(...) IS NULL guards. Column types match legacy (CHAR(n), NUMERIC(p,s), DATETIME). Includes the pfill_carry_cost seed 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 uses float/double, every entity has a configured primary key, composite-key column sets match legacy DDL, single-column keys are correctly declared, every decimal property 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 against INFORMATION_SCHEMA.COLUMNS. Uses SkippableFact so it no-ops when the PFILL_TEST_SQLSERVER environment 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 NVARCHAR for strings and DECIMAL(18,2) for decimals by default, which would silently drift from legacy CHAR(n) and NUMERIC(p,s).
  • PS_DemoData already contains some pfill_* tables with data; an EF migration would conflict.
  • Idempotent SQL with IF OBJECT_ID guards 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 projects
  • src/backend/PowerSeller.SaaS.Api/Program.cs — registered and mapped the PowerFill module
  • src/backend/PowerSeller.SaaS.Api/PowerSeller.SaaS.Api.csproj — added project reference
  • src/backend/Dockerfile.dev — added COPY steps for the new csproj files
  • docs-site/docs/legacy/powerfill-deep-dive.md — corrected 13→17 table count and table naming
  • docs-site/docs/specs/powerfill-engine.md — corrected ECT→EPCI and added Phase 1 note
  • docs-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_disposition view
  • 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

  1. 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).
  2. A21 (EPCI meaning). Class name stays PowerFillEpciParams until Tom/Greg clarify what EPCI actually stands for. If they rename, one search-and-replace handles it.
  3. A25 — new (not in this devlog but worth noting): The pfill_loan_constraint_pool table's runtime semantics (configuration vs. run-intermediate) are unconfirmed. Phase 2 will resolve this.
  4. 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.