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]/deletewith{ mode: 'permanent' }, then refreshes the list.- The per-row kebab shows "Delete permanently" ONLY on hidden/tombstoned rows (
row.deletedAtset β i.e. under "Show hidden"), and suppresses the "Request signature" item there (a deleted WOPC can't be re-requested).WopcColumnsContextgainsonPermanentDeleteWopc. - 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 unfilteredtsc --noEmitclean. - 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 tombstonedERL-WOPC/2025-025row'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]/deletereturns 403 formode: 'permanent'unlessisAdminLikeRole(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.tscclean.