Contacts unification OPTIONAL follow-ups (P6 WOPC re-anchoring · P7 teardown + registry cutover)
Why these are OPTIONAL (read first)¶
T-048 (the Contacts → Individuals + Client Company unification) is functionally complete + archived done. A read-only P6 audit (2026-06-16) showed the remaining phases are not currently needed:
- 25 WOPCs (24 JC + 1 JN) hang off the FROZEN
payees/{abbr}doc-id, which the migration never touches.swapAbbreviationdeliberately never renames that doc-id (it edits the Individual's abbreviation + keeps the old as a resolvable alias), so WOPC paths stay valid even when a person is renamed in the new UI. - 0 transactions carry
WOPCReference.wopcPath→ nothing to backfill. - 27 txns have
counterpartyDirectoryId, all uppercase → the case-mismatch hazard (adversarial finding #1) does not manifest in live data.
So nothing here is required for correctness today. Pick these up only if a concrete need arises (below).
P6 — WOPC re-anchoring on stableId (OPTIONAL — only if a frozen payee key ever needs renaming)¶
- Add
individualIdto WOPC docs + transactions'WOPCReference(ADDITIVE; keep the existingpayees/{abbr}anchor). - Do NOT change the
ERL-WOPC/{abbr}-{YYYY}-{NNN}reference-number scheme. If ever re-keying the frozenpayees/{abbr}doc-id, FREEZE WOPC minting during cutover + assert per-year reference uniqueness before unfreezing. - Trigger to actually do it: a payee's frozen abbreviation/doc-id must change (not just the Individual's display abbr).
P6 tail — transaction-stamp dual-keying (OPTIONAL — statutory robustness)¶
- When a NEW transaction is linked to a payee, stamp the Individual
stableIdalongsidecounterpartyDirectoryId(abbr) so related-party/IR56M/director-current-account schedules can dual-resolve (R4). Lower urgency: schedules resolve via abbr today and abbreviations are stable.
P7 — teardown + registry cutover (OPTIONAL — risk without benefit today)¶
- Registry cutover (BOOT-CRITICAL — do NOT do casually): convert
lib/directors/registry.tsinto a synchronous CACHED VIEW overindividuals.system.director(hardcoded fallback + fail-loud). RISK: an empty cold-boot read = accounting misclassification + legally-wrong signed WOPC PDFs. The hardcoded registry works and is the safe source; only cut over with explicit owner sign-off + the consistency guard green (checkDirectorsConsistentalready covers id/initials/closingName/loginEmails/contactEmail/title). - Legacy-tab teardown: retire the Contacts Clients / People / Staff tabs. NOTE: the new Individuals editor now has full editing parity (incl. bank accounts + address as of e85eb7a5), so the People tab is no longer needed for that; re-confirm client/staff parity before removing those two. Also retire MelClientsContent / ClientAccountsApp / the old payee auto-match once nothing reads them.
- Section-gated picker rewiring (deferred from P4): point ContractorModule / WOPC / contact pickers at the unified
/api/aote-system/individuals?gate=…instead of the (Wave-3-overlaid) payee directory. The overlay already keeps the typeahead reflecting unified data, so this is cleanup, not a fix.
Log¶
- 2026-06-16 created. Split out of T-048 as OPTIONAL follow-ups so T-048 can be archived done. P6 audit shows none are needed now; documented triggers for each. See vault T-048.md + docs/contacts-unification-design.md.
- 2026-06-16 owner chose "registry cutover too" → started, building the SAFE form only. DONE (nightly df8397ee): Registry cutover = fail-loud guard. Kept lib/directors/registry.ts as the SYNCHRONOUS source; the persisted store is now a VALIDATED mirror — sendForSigning calls assertDirectorsConsistent() before dispatch (refuses to send if drift). A true async source-swap was deliberately NOT done (empty cold-boot read = legally-wrong signed PDFs / misclassified director loans). Store verified consistent. Optional hardening: wire the same guard into director-loan posting + period-close. User-mirroring: already satisfied — all 4 RBAC users are bridged to individuals (P3); only FUTURE signups need lazy creation (deferred: touches the auth onboarding path + needs bridge-vs-create to avoid forking an existing contact). Teardown of legacy Clients/People/Staff tabs: STILL BLOCKED — the new Individuals view doesn't replicate the Staff tab's role/status admin display, so removing it would LOSE staff-management info (a regression). Needs the new UI to add a staff/role view first. P6 WOPC re-anchor + picker rewiring remain NO-OP/no-benefit (see table above). NOT promoted to main.