Post-migration dead-code cleanup — drop legacy-shape fallbacks once migrations are applied
Why¶
Each shape migration in the chain (T-074 / T-075 / T-077 / T-078) ships with read-side dual-read code — new shape preferred, legacy fallback kept — so the web app keeps working before / during / after the migration script runs. Once a migration has been applied to prod (and re-running the script reports "nothing to migrate"), the fallback branches become dead code. This task is a sequence of small cleanup PRs, one per migration that's landed, to delete that dead code.
Cleanup checklist (one PR per migration applied)¶
After T-075's gl[4001] backfill (scripts/backfill-coaching-gl4001.ts)¶
- Nothing to clean up. The runtime read path uses
effectiveGlCodesderived from existing V1 fields (matchedCoachingPayments/matchedCoachingInvoices); the backfill is for data hygiene only. No fallback code exists to drop.
After T-078's invoice doc shape migration (scripts/migrate-invoice-doc-shape.ts)¶
In lib/invoiceDocShape.ts — single point of cleanup, called out in the file's
docstring:
- Drop the tier-2 (invoiceDetail / paymentDetail / lineItems / top-level
invoiceTotal / flat email*) accessor fallbacks.
- Drop the tier-3 (flat top-level paymentStatus / invoiceIssued / invoiceCleared
/ invoiceDrafted / invoiceCreated / invoiceNumber / payTo-and-aliases /
item{N}) accessor fallbacks.
- Drop the hoistPriorTierToFlat pre-pass in transformPayloadToNestedShape.
- Drop PRIOR_TIER_TOP_LEVEL_MAPS + LEGACY_VESTIGIAL_KEYS delete arrays in the
UPDATE-path delete-emission (no docs left holding those keys).
After T-078 Phase A's coaching invoice migration (scripts/migrate-coaching-invoice-shape.ts)¶
- In
lib/accounting/coachingInvoices.ts: ·getCoachingInvoiceForSession— drop the legacySessions/{id}/invoicesubcollection fallback after the map-field read. ·findCoachingInvoiceByNumber— drop the legacycollectionGroup('invoice')fallback. - In
lib/accounting/derivedJournals.server.ts(COACHING_ISSUED) — drop theif (sessionsWithLegacy.length > 0) { collectionGroup('invoice') … }branch. - In
lib/accounting/reports.server.ts(AR aging coaching loop) — drop the same. - In
pages/api/accounting/matchable-coaching-invoices.ts— drop theelse { … sessionDoc.ref.collection('invoice').get() }branch. - In
pages/api/coaching/invoices/[sessionId]/saved.ts— drop the legacy subcollection fallback (inline; predates the helper). - In
components/StudentDialog/SessionDetail.tsx— drop the legacysetInvoiceExistsfallback after the map-field check. - In
lib/accounting/transactions.ts:syncCoachingInvoiceTxTrail— drop the subcollection-write branch. - Delete
scripts/migrate-coaching-invoice-shape.ts(one-off, served its purpose).
After T-078 Phase B's session migration (scripts/migrate-sessions-under-students.ts)¶
TBD when Phase B lands. Will include collection vs collectionGroup branches.
After T-076's workStatus drop has soaked¶
- Remove
/api/accounting/sync-project-statuses.tsroute (syncProjectWorkStatusesis already a no-op stub). - Remove the
syncProjectWorkStatusesexport fromlib/accounting/transactions.tsandlib/accounting/index.ts.
Sequencing¶
Each cleanup PR is independent and gated on its migration script having been applied to prod cleanly (re-run reports "nothing to migrate"). Don't bundle multiple cleanups into one PR — keeps each easy to revert if a stale-doc edge case is found.
Related¶
T-075 · T-076 · T-078 · T-079 (the migrations whose fallbacks this task cleans up).
Log¶
- 2026-06-20 created. Logged proactively so the fallback cleanup doesn't get lost after the migration scripts run.
- 2026-06-20 prod migrations applied (T-078: invoice doc shape, T-079 Phase A: coaching invoice → map field, T-079 Phase B: sessions under students). gl[4001] backfill (T-075 data hygiene) applied too. Each script idempotent on re-run.
- 2026-06-20 PR 1/3 SHIPPED in #770 (claude/t080-invoice-shape-fallback-cleanup-yxMLM). lib/invoiceDocShape.ts shrinks by 321 lines — all tier-2 / tier-3 dual-read fallbacks gone, hoistPriorTierToFlat pre-pass gone, UPDATE-path legacy-key deletes gone. tsc + 114-test accounting suite clean.
- 2026-06-20 PR 2/3 SHIPPED in #771 (claude/t080-coaching-schema-fallback-cleanup-yxMLM).
Coaching schema dual-read fallbacks removed: getCoachingInvoiceForSession +
findCoachingInvoiceByNumber now read the canonical shape only; findSessionRef
keeps a no-abbr collection-group lookup for the 46 legacy stubs; the
if (sessionsWithLegacy.length > 0)branches in derivedJournals.server.ts + reports.server.ts (+ unused parseCoachingInvoiceDocPath) gone; matchable-coaching-invoices.ts subcollection branch gone; SessionDetail.tsx existence check single-path; syncCoachingInvoiceTxTrail subcollection-write branch gone; writer in client.tsx writes directly to Students/{abbr}/Sessions/{id}. Net: 286 lines deleted, 137 added. tsc + 114-test suite clean. - 2026-06-20 PR 3/3 SHIPPED — workStatus stub + sync-project-statuses endpoint. Deleted pages/api/accounting/sync-project-statuses.ts; removed the syncProjectWorkStatuses no-op stub from lib/accounting/transactions.ts + its export from lib/accounting/index.ts. tsc + 114-test suite clean. T-080 done.
2026-06-24 — appendix: T-089 (WOPC) dual-read cleanup¶
- ✅ Attestation (Accounting (Diagnostics)): read
AGENTS.md; appendix to the T-080 cleanup chain — T-089's read-side fallbacks were the WOPC counterpart to the invoice / coaching cleanups already in this task. - Source: Accounting (Diagnostics) · https://claude.ai/code/session_01G58Y71noihrYCDEDMexmea
- What changed:
lib/wopcDocShape.tsgetters now read the canonical bucketed shape only (?? data.legacyXtails removed; legacyif (m) … else { legacy }branches collapsed). Writers inlib/wopc.server.tsstop emitting legacy flat keys (createWOPC,updateWOPCMatched,updateWOPCClosingDirector); three dead writers deleted (updateWOPCCompleted,updateWOPCFailed,updateWOPCPdfReference— 0 callers each). Test suite__tests__/lib/wopcDocShape.test.tsrewritten to the canonical-only contract (26/26 passing). - Migration prerequisite met: the Phase 5 migration script + the line-item rule had already run; this cleanup also shipped
scripts/backfill-wopc-notes.tsto catch the one field the migration missed (3 docs). Live: 34/34 WOPCs in new shape, no fallback-dependent fields. - Cross-link: see T-089's "2026-06-24 — T-080 dual-read cleanup + DONE" entry for the full close-the-loop record (verification, blast radius, remaining follow-ups).
Commit index (backfilled 2026-07-01, best-effort · Coaching (Diagnostic))¶
Candidate related commits, auto-backfilled from git on main: commits whose message references this task's UID or a PR number it cites. Not verified — this squash-merged history can't yield a precise per-task list, so rows tagged (mentions only) name the task in passing (may be tangential) and untagged work commits may be missing. Treat as a starting point: verify, prune tangential rows, and append any real ones per the AGENTS.md "record every related SHA" policy.
ba2025c2026-06-24 — feat(t-089): createWOPC writes full bucketed shape + migrate 2 stragglers; close T-074 (mentions only)e035e642026-06-24 — feat(t-089): T-080 dual-read cleanup + DONE — drop WOPC legacy-shape fallbacksca5f0482026-06-24 — Merge pull request #800 from girafeev1/claude/busy-dirac-QmUdM (mentions only)d7b4e0c2026-06-25 — fix(records): WOPC drawer iframe + transaction-linked preview shape (T-126) (mentions only)d7908e02026-06-29 — docs(tickets): I-019 — fix sibling ERL-2026-004-0508 drafted={} + sharpen root cause (mentions only)
2026-07-03 — correction: invoice fallbacks restored for live split-shape survivors¶
- ✅ Attestation (Codex local session): read
AGENTS.md; checked the board by scope, not UID — this is a correction to T-080's invoice fallback cleanup premise. Source: Codex local session ·/Users/gutchumi/dev/ArtifactoftheEstablisher-codex-invoice-hotfix - Why: The T-080 invoice cleanup assumed every project invoice had been fully migrated to
detail.*. Live Firestore disproved that:ERL-2026-004-0508still hadpaymentDetail.payTobut notdetail.payment.payTo, and new writes could still preservepaymentDetailbecause the transformer did not fold partial-nested maps. - Correction:
lib/invoiceDocShape.tsrestored narrowly-scoped read tolerance forinvoiceDetail/paymentDetail/ top-levellineItemssurvivors and now folds those maps into canonicaldetail.invoice/detail.payment/detail.lineItemsduring writes. This is not a general return to flat-field support as the preferred model; it is a safety rail around the exact split-shape tier T-078 introduced. - 2026-07-03 follow-up: owner testing showed previews still had
items: []/total: 0after the first correction. The remaining edge was an emptydetail.lineItemsmap masking valid top-levellineItems.itemNmaps. Accessors now treat a canonical lineItems map with noitemNentries as missing and fall back to the survivor map. - Cross-link: full owner report, live findings, verification, and blast radius are logged under T-078's 2026-07-03 hotfix entry.