Skip to content

T 163


uid: T-163 title: Records WOPC β€” "Delete permanently" kebab action for hidden/tombstoned WOPCs status: done area: wopc-records created: 2026-07-09 updated: 2026-07-09 owner: girafeev1 assignee: Records (Infrastructure) related: T-089


Goal (owner, 2026-07-09)

Follow-on to T-089 (WOPC soft-delete / tombstone + "Show hidden"). A soft-deleted WOPC stays in Firestore as a hidden tombstone (surfaced under "Show hidden") awaiting an eventual PERMANENT erase β€” but there was no UI to do it, so the owner had to ask an agent to delete it from the database. The delete endpoint already supports mode: 'permanent' (β†’ removeWOPC, a true hard-delete); this wires a "Delete permanently" affordance on the row so the owner can erase a hidden WOPC themselves.

Prompted by ERL-WOPC/2025-025 (owner: "create a Delete Permanently option so I can erase it"). Its lifecycle, from the audit trail: created (payee Jeffero Chan) β†’ matched to a tx β†’ signing-request created + sent to Ngai on 2026-06-29 07:58 β†’ withdrawn 2 min later (never signed, never sealed, never voided) β†’ soft-deleted (tombstone, "manually-cancelled") 08:26 β†’ sat as a hidden tombstone. A clean, never-signed orphan safe to hard-delete.

What shipped

  • components/records/RecordsApp.tsx: handlePermanentDeleteWopc + a type-to-confirm Modal (type the exact reference to enable the danger button) that POSTs the existing /api/accounting/wopc/[ref]/delete with { mode: 'permanent' }, then refreshes the list.
  • The per-row kebab shows "Delete permanently" ONLY on hidden/tombstoned rows (row.deletedAt set β€” i.e. under "Show hidden"), and suppresses the "Request signature" item there (a deleted WOPC can't be re-requested). WopcColumnsContext gains onPermanentDeleteWopc.
  • Backend unchanged β€” the endpoint's mode: 'permanent' path already existed (T-089). A signed WOPC is refused server-side (must be voided first), so permanent-delete only completes for never-signed tombstones; the signing-request history + any Drive PDF are retained.

Log

  • 2026-07-09 β€” created + done (Records Infrastructure; owner request, 2026-07-09). βœ… Read AGENTS.md Β· checked the board by scope (T-089 is done; this is the net-new permanent-delete UI β€” no covering task). Source: Records (Infrastructure) Β· https://claude.ai/code/session_018RDB37kCqfouHdygVXTAtD
  • Verdict: shipped as scoped β€” UI-only (kebab item + type-to-confirm modal) over the existing mode: 'permanent' endpoint; no new backend. Full unfiltered tsc --noEmit clean.
  • Merged to main, no deploy (owner: commit + merge every change, don't deploy; main 🟑🟑 = manual). Blast-radius: components/records/RecordsApp.tsx (WOPC tab kebab + a new confirm modal). No API/data-model change. Owner runtime-verify: under "Show hidden", the tombstoned ERL-WOPC/2025-025 row's kebab β†’ "Delete permanently" β†’ type the ref β†’ confirm β†’ row erased.
  • 2026-07-09 β€” review hardening (Codex P2 on PR #880, accepted β€” the finding is correct): permanent (hard) delete is now admin-gated on BOTH layers. UI: the kebab item shows only when isAdmin. API: /api/accounting/wopc/[ref]/delete returns 403 for mode: 'permanent' unless isAdminLikeRole(session.user.role). Soft-delete (tombstone) is unchanged β€” any Records user with subsidiary access may hide a WOPC (recoverable via "Show hidden"); only the irreversible erase is admin-only. Rationale: an irreversible Firestore delete shouldn't be reachable by every ERL-access viewer, and this PR is what surfaced that path in the UI. tsc clean.