Signed WOPC ERL-WOPC/2025-020 reads as "Pending" with no signed PDF — signature.requestId dangles, all surviving requests voided, WOPC.pdf + revisions empty
Symptom¶
Owner, 2026-06-25 (verbatim): "a signed WOPC (ERL-WOPC/2025-020) is not being shown to the records page, and the status of the WOPC changed to Pending."
The WOPC is signed (the owner signed it), but on the Records WOPCs tab it reads Pending and the signed PDF isn't accessible.
Root cause (proven against live Firestore — tebs-epl + aote-system)¶
The WOPC parent doc (payees/JC/wopc/ERL-WOPC|2025-020) is internally inconsistent — it believes
it's signed but every durable home for the signed PDF is empty or dangling:
| Source the app can read the "signed PDF" from | State |
|---|---|
WOPC.signature on the parent |
present — { at: 2026-06-13T06:46:40Z, requestId: lGUUsWLTqSuobCMTq949 } |
The signing request it points at (lGUUsWLTqSuobCMTq949 in aote-system/wopcSigningRequests) |
DELETED — does not exist (swept) |
WOPC.pdf on the parent (Phase 4's durable signed-PDF pointer) |
ABSENT |
revisions/ subcollection (Phase 4 archives signed attempts here) |
EMPTY (0 docs) |
Any findable signing request for wopcRef = ERL-WOPC/2025-020 |
3 exist, ALL status: voided (T6m8geqGBO325C8tXJKX, dg7d69o8iau8WDJ4zCLB, HUjw90a6rQVHy9V1QLYz) |
So the parent says "signed at 06-13 06:46 via request lGUUsW…" but that request was swept, and the
3 requests that survive are all from earlier sign→void cycles (06-11 → 06-13). The 06-13 06:46 sign
(the current signature) left no resolvable artifact: no live request, no WOPC.pdf, no revision doc.
Why the Records page renders it as Pending¶
pages/api/records/wopcs.ts resolves the signed-PDF Drive id only via the signing-request lookup
(lines 145-151) — it never reads WOPC.pdf:
const direct = directId ? await findRequestById(directId) : null // lGUUsW… → null (deleted)
if (direct?.status === 'signed' && direct.signedDriveFileId) { … } // no
else {
const latest = await findLatestRequestForWopc(ref) // newest by createdAt = HUjw90a6…
if (latest?.status === 'signed' && latest.signedDriveFileId) { … } // latest.status = 'voided' → no
}
// → driveFileId = null
With no resolvable signed request, the row has no signed-PDF link and the lifecycle reads Pending,
even though WOPC.status = active and WOPC.signature.at is set.
How it got here¶
This WOPC went through repeated sign → void cycles on 06-11 → 06-13 (3 voided requests + a 4th,
final sign at 06-13 06:46). The T-089 signing-request sweep (ephemeral-request model: terminal
requests get deleted) removed the final signed request lGUUsWLTqSuobCMTq949, but the migration
never backfilled the parent's WOPC.pdf (or a revisions/1 doc) for it — so the signed artifact
pointer was lost. The voided requests survived (they weren't swept), which is what makes the parent's
dangling signature.requestId so visible.
Two gaps — both T-089 scope¶
- Data: this WOPC (and possibly other re-signed/voided-then-re-signed WOPCs) has a dangling
signature.requestId+ missingWOPC.pdf+ emptyrevisions/. Needs a repair that re-attaches the current signed PDF — i.e. decide which Drive file is the canonical 06-13 06:46 signed copy (the deleted request'ssignedDriveFileIdis gone; the file itself may still be in Drive / the file-archive) and write it toWOPC.pdf(+ arevisions/entry per the Phase 4 model). - Code (reader robustness):
pages/api/records/wopcs.tsshould preferview.WOPC.pdfwhen present (the Phase 4 canonical home) and only fall back to the signing-request lookup. As written it can't surface a signed WOPC whose request was swept, even after aWOPC.pdfbackfill.
Escalated to T-089 (WOPC doc-shape restructure + revisions + signing-request sweeping; owner
Accounting (Diagnostics) — https://claude.ai/code/session_01G58Y71noihrYCDEDMexmea). The repair
needs that migration's model (which Drive file is canonical, how revisions/sweep are meant to
interact), so Records (Infrastructure) is not guess-fixing the prod data — wrong choice could
attach a voided PDF as the "signed" copy.
NOT a T-047 clash¶
T-047 (IR56M, EOP Local Assistance fork) touches only the sign-flow UI + signature/chop
composition — app/wopc/sign/[requestId]/*, components/wopc/*, lib/wopc/signing/composition.server.ts,
lib/wopc/signing/placement.ts (how a signature/chop is drawn and stamped onto the PDF). It does
not touch lib/wopc/signingRequests/* (request lifecycle/sweeping), pages/api/records/wopcs.ts
(the records reader), or lib/wopcDocShape.ts / lib/wopc.server.ts (doc shape/read). The broken
state here is in the signing-request lifecycle + records display = T-089, not T-047. (Confirmed
by diffing the file footprint of the recent T-047 commits.)
Decision log¶
2026-06-25 — opened + escalated to T-089 (diagnosed, handed off)¶
- ✅ Attestation (Records (Infrastructure)): read
AGENTS.md; scope-scanned — the WOPC doc-shape + revisions + signing-request sweep is T-089's scope, so this ticket cross-links there rather than opening a parallel task. The reader-robustness gap (#2) is the same WOPC read-path I touched for the empty-tab fix (I-011/T-110), but the resolution depends on T-089's signed-state model, so it belongs with T-089. - Source: Records (Infrastructure) · https://claude.ai/code/session_018RDB37kCqfouHdygVXTAtD
- Not actioned by me: no prod-data writes, no reader change — the canonical-signed-PDF choice + the revisions/sweep contract are T-089's to define. Full live-data evidence captured above so that agent can verify without re-deriving.
2026-06-25 — owner supplied the canonical signed PDF + extra root-cause finding¶
- ✅ Attestation (Records (Infrastructure)): read
AGENTS.md; appending evidence after the owner identified the canonical signed Drive file. No prod write — the repair remains T-089's call. - Source: Records (Infrastructure) · https://claude.ai/code/session_018RDB37kCqfouHdygVXTAtD
Canonical signed PDF (verified)¶
The owner provided https://drive.google.com/file/d/10F29sJZ6UCSCpncUYvnIemeG5xoL5FSs/view.
Confirmed via Drive v3 metadata + a download with the accounting SA:
| Field | Value |
|---|---|
id |
10F29sJZ6UCSCpncUYvnIemeG5xoL5FSs |
name |
ERL-WOPC/2025-020_20260613.pdf (ref + the 06-13 final-sign date) |
mimeType |
application/pdf |
size |
2437560 bytes (≈ 2.4 MB) |
driveId |
0ADgMmnOYpTQuUk9PVA (ERL shared drive) |
parents |
15EGlDQloITkY_KKLVgf8YpC322AEagyT |
trashed |
false |
The filename matches the convention the WOPC signing flow uses (<ref>_<YYYYMMDD>.pdf) and the
date matches the parent's WOPC.signature.at = 2026-06-13T06:46:40Z. This is the file
markWOPCSigned would have referenced via the deleted request's signedDriveFileId.
Repair specification (now fully defined — for T-089's writer)¶
lib/wopc.server.ts:markWOPCSigned writes the signed pointer as WOPC.pdf.signedFileId
(line 946, NOT WOPC.pdf.fileId). The repair on payees/JC/wopc/ERL-WOPC|2025-020:
parentUpdate = {
'WOPC.pdf.signedFileId': '10F29sJZ6UCSCpncUYvnIemeG5xoL5FSs',
// Optionally also: storagePath: 'drive:10F29sJZ6UCSCpncUYvnIemeG5xoL5FSs' if
// T-089's getPdf accessor enforces a storagePath companion (see below — it does).
}
WOPC.currentRevision = 1 is already correct (1 sign, 0 voids on parent — the prior void cycles
should have produced revisions/0, but none exist; this is a separate data debt the repair may
want to backfill or leave as orphaned audit lost-to-sweep).
Extra finding — Phase 4d safety net has a field mismatch (likely separate bug)¶
lib/wopc/signingRequests/synthesize.ts:51,75 synthesises the swept request's signedDriveFileId
from view.WOPC.pdf?.fileId — but the writer (markWOPCSigned) puts the id in
WOPC.pdf.signedFileId, and WOPCViewPdf (lib/wopcDocShape.ts:86-91) doesn't even declare
signedFileId on the view type (only fileId + storagePath). So even after the prod repair
above lands, the synthesis safety net would still return signedDriveFileId: null and the records
endpoint's findLatestRequestForWopc fallback would still hand back one of the surviving voided
requests → Pending would persist.
Two ways T-089 could reconcile this:
- Reader-side: widen WOPCViewPdf to expose signedFileId (and voidedFileId too, for
symmetry with getVoid) and switch synthesize.ts to read it. Then any WOPC.pdf.signedFileId
repair is enough for badge + download to follow.
- Writer-side: change markWOPCSigned to also (or instead) write WOPC.pdf.fileId. Less
invasive on consumers; more risky on existing prod docs that already use signedFileId.
Either is T-089's call; I'm flagging the inconsistency, not picking.
One more reader gap (separate from the synthesis fix)¶
pages/api/records/wopcs.ts:142-153 resolves the signed Drive id ONLY via the signing-request
lookup chain (findRequestById(directId) → findLatestRequestForWopc(ref)). For this WOPC the
direct id is dangling (request swept) AND findLatestRequestForWopc returns the newest-by-
createdAt request, which here is the 2026-06-13 voided one (HUjw90a6rQVHy9V1QLYz). Even if
synthesis is fixed, the records list query doesn't go through the lookup endpoint — it does its
own resolution and never reads WOPC.pdf. So the records-list fix is one of:
- Read view.WOPC.pdf?.signedFileId first, fall back to the request lookup.
- Or route through getSigningRequest / synthesizeFromWopcView so a single fix in synthesis
covers both surfaces.
Again — T-089's choice; flagged for completeness.
Summary handoff¶
What T-089 needs to do, from the prod-data evidence above:
- Write
WOPC.pdf.signedFileId = 10F29sJZ6UCSCpncUYvnIemeG5xoL5FSs(+storagePathif your accessor requires it) ontebs-epl payees/JC/wopc/ERL-WOPC|2025-020. (The 4 surviving signing requests can stay as-is; they're correctlyvoidedandsigned-but-swept.) - Reconcile the
signedFileId↔fileIdnaming betweenmarkWOPCSigned,WOPCViewPdf/getPdf, andsynthesize.tsso the Phase 4d safety net actually surfaces the signed Drive id after a sweep. - Decide whether the records list (
pages/api/records/wopcs.ts) should readWOPC.pdfdirectly or route through the lookup endpoint for a single resolution path.
No code changes from this session — handoff is complete with the canonical Drive id + the field naming gap on record.
2026-07-01 — fixed (via T-089)¶
Resolved via T-089 (done) — WOPC signed-PDF doc-shape + repair. Closed per owner. WARNING: if ERL-WOPC/2025-020 still reads 'Pending' on Records, reopen (T-089 done doesn't itself prove that one prod-data repair landed). [owner: close I-014] - Source (edit): EOP Local Assistance (fork) · session edb0abc7-6373-449b-8e31-379fd38d391b