Why (owner, 2026-06-19)¶
Two threads that converged:
1. The Transaction-column filter on the Bank Transactions tab (added in #761) didn't
work and wasn't inclusive of GL accounts.
2. Root cause runs deeper: coaching-matched txs are not GL-code-addressable. Project
invoices write gl['4000'] (InvoiceAllocations), but coaching writes only the named
keys gl.coaching / gl.coachingInvoices (in GL_RESERVED_KEYS), so
getAccountCodeFromGL() returns nothing and 0 txs have gl['4001']. Every
GL-code-based consumer (filters, aggregation, invoicePaymentStatus.ts,
reconciliation.server.ts, derivedJournals.server.ts) has to special-case coaching.
Scope / fix (PR #762)¶
- Filter render β added
filterMode: "tree"so antd renders the nested Direction / GL submenu (was empty). - V1 adapter β
BankTransaction.effectiveGlCodes: string[]= union of GL codes a tx posts to via derived journals:accountCode+4000(matchedInvoices) +4001(matchedCoaching*). Works for legacy data (derives from the named-key arrays), so no migration needed for reads. - Filter list + predicate β derived from
union(transactions.effectiveGlCodes); predicate isrecord.effectiveGlCodes.includes(code); "Uncategorized" iseffectiveGlCodes.length === 0. Falls back toaccountCodewhen the union list is absent (legacy flat V1 rows + optimistic patches) β fix for Codex P2. - Write path (the structural fix) β
buildCoachingGl4001Markermirrorsgl['4000']'s InvoiceAllocations shape (coaching invoices keyed by invoice number; payments keyedpayment:<paymentId>). All four coaching match/unmatch paths in lib/accounting/transactions.ts now write / refresh / cleargl['4001']alongside the named keys. Marker is array-valued sogetAccountCodeFromGL's skip rule still keepsaccountCodeundefined βGL_REVENUE_RESTRICTED(4000/4001 never directly assigned) holds, exactly like project invoices. - Backfill β scripts/backfill-coaching-gl4001.ts stamps
gl['4001']on existing coaching-matched docs (backup-first, --dry-run, idempotent, halt-on-error; reuses the exported runtime marker fn β no logic drift). dotenv ordering fix (Codex P2) lands here too.
Status¶
Consolidated into a single PR against nightly after the stacked-PR chain (#762β#766) hit merge conflicts from nightly's parallel T-073/T-074 additions. Codex P1/P2 fixes from
762 + #763 reviews folded in. tsc + 114-test accounting suite clean.¶
Remaining¶
- Owner-merge the consolidated PR.
- Run scripts/backfill-coaching-gl4001.ts (--dry-run first) once merged.
- Follow-on (β T-080): retire the named-key special-cases in
invoicePaymentStatus.ts / reconciliation.server.ts / derivedJournals.server.ts now
that
gl['4001']exists. Not blocking.
Related¶
T-043 (WPP audit β flagged the named-key coaching special-cases) Β· T-076 (stale stored invoice paymentStatus β sibling GL/integrity cleanup).
Log¶
- 2026-06-19 created (mid-work). Filter fix grew into the coaching GL-code-addressability fix. Write path + adapter + filter + backfill shipped to #762; awaiting owner merge + prod backfill run.
- 2026-06-20 PR #762 marked ready for review. CI failures are Vercel account-level block (not actionable from branch); same block applies across the whole stack. tsc + vitest accounting suites (114) clean.
- 2026-06-20 Codex P2 on #762: accountCode fallback in GL filter helper + dotenv ordering fix in the backfill script. Both folded into the consolidated PR.
- 2026-06-20 RENUMBERED T-074 β T-075 after the stacked-PR merge conflict surfaced nightly's parallel T-074 (DNS cutover). Consolidated PR opens against nightly.
- 2026-06-20 status flipped to done β Accounting [Infrastructure Development] (housekeeping; the work itself shipped via PR #767 and the T-080 cleanup chain). Source: Accounting [Infrastructure Development] Β· https://claude.ai/code/session_015P6KzVYsQCLgEmUjR9bMwM (Source line retrofitted 2026-06-21 β the convention postdates this entry.)
- 2026-06-23 read-side projection reversed at owner request β see T-093.
This PR's "leave
tx.accountCodeblank on income txs" choice (preserved here, not introduced β the underlying skip rule ongetAccountCodeFromGLis older) made every UI / Excel-export site that readstx.accountCoderender blank for income txs. Owner did not recall agreeing to that semantic and confirmed it should be reversed. T-093 adds a one-place fallback in the V1 adapter (lib/accounting/transactionAdapter.ts,toV1) soaccountCodeis populated to"4000"(project-invoice matches) or"4001"(coaching matches) when no direct assignment exists. Kept from this PR: the filter render fix, the coachinggl["4001"]write + backfill (owner specifically asked for those β structurally clean). Reversed from this PR: only the read-side projection. Write-side block on direct manual assignment of 4000/4001 (isRevenueRestrictedin the assign-account API) stays in place. Source: Accounting (Diagnostics) Β· https://claude.ai/code/session_01G58Y71noihrYCDEDMexmea
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.
1a117022026-06-29 β docs(tickets): open I-019 β project #2026-003 stuck "Pending" (invoice issued={} β fixed) (mentions only)fe5c5ea2026-06-29 β docs(tasks): open T-135 β self-describing project/invoice final status (mirror of derived, symmetric on unmatch) (mentions only)3843eea2026-06-30 β feat(accounting): T-135 self-describing project/invoice status mirror (symmetric on match + unmatch) (mentions only)