Skip to content

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. swapAbbreviation deliberately 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 individualId to WOPC docs + transactions' WOPCReference (ADDITIVE; keep the existing payees/{abbr} anchor).
  • Do NOT change the ERL-WOPC/{abbr}-{YYYY}-{NNN} reference-number scheme. If ever re-keying the frozen payees/{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 stableId alongside counterpartyDirectoryId (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.ts into a synchronous CACHED VIEW over individuals.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 (checkDirectorsConsistent already 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.