WOPC reference-number module — "Generating…" spinner never clears (T-081 numbering format mismatch)
Symptom (owner, 2026-06-24, verbatim)¶
"When matching a tx to 5050 on the match transaction modal, a 'Generating…' text keeps showing up under the proposed WOPC number. Even though a WOPC with the correct reference number still got successfully generated, but the 'Generating…' text that won't go away might be worth looking into."
So: WOPC ref is correctly minted in Firestore and shown in the field; only the inline "Generating…"
help text + spinner under the field never disappear.
Root cause (diagnosed from code, no live capture yet)¶
components/accounting/transactions/modules/ReferenceNumberModule.tsx:96 guards its
useEffect against re-generation with:
That format predates the T-081 WOPC-numbering switch. The check is looking for an
abbreviation-in-the-middle like /JC-2025-…, but the actual format minted by
generateNextWOPCNumber today (verified live against the 34 WOPCs in tebs-epl) is
ERL-WOPC/2025-NNN — no abbreviation in the middle. The substring never matches, so the
"already-generated" early-return never fires.
Net effect, frame by frame:
- Modal opens →
useEffectdeps satisfied →setGenerating(true)→ fetch API → returns refERL-WOPC/2025-NNN→onChange({referenceNumber})→setGenerating(false)infinally. - The parent's
onChangere-renders the module (line-items array's reference identity changes on most re-renders, andlineItemsData?.lineItemsis in the effect's deps on line 102). - Effect re-fires. Guard on line 96 checks for
/${upper}-${year}-against the value the API just returned. Substring not found → guard doesn't return. - Effect calls
generateReferenceNumberagain →setGenerating(true)→ fetch returns the same ref (the server-side allocator is idempotent for {abbr, year}) →onChangewith the same value → next render → guard fails again → loop.
The spinner gets stuck because each loop sets generating=true synchronously and the in-flight
fetch keeps a new pending request alive between the finally of one cycle and the start of the
next.
Why this is ticket-only (no T-NNN escalation)¶
Single-file UI fix in ReferenceNumberModule.tsx. Not structural (no schema, no data, no
cross-cutting wire change). The guard just needs to either:
- (a) match the actual format —
value?.referenceNumber?.includes(\/${year}-`)`, or - (b) be tightened to "ref already set" —
if (value?.referenceNumber) return, or - (c) stabilise the effect deps so the loop doesn't restart in the first place (memoise
lineItemsData?.lineItemsupstream, or depend on a stable key derived from it).
Per AGENTS.md "Tickets" rule: structural / cross-cutting → escalate to a T-NNN; this isn't.
Suggested fix scope¶
Smallest correct change is (a) + a defensive (b): match the real format AND short-circuit on any
already-set ref. Trades the wrong (abbr,year)-equality check (which the previous numbering scheme
needed) for "I already have a ref — don't ask for another one"; safe because the form only ever
generates within one WOPC session, so any previously-set ref is the one we want.
Decision log¶
2026-06-24 — opened (investigating; ticket-only)¶
- ✅ Attestation (Accounting (Diagnostics)): read
AGENTS.md— confirmed Tickets procedure (symptoms live asI-NNN; escalate toT-NNNonly when structural). Scope-scanned the board: no existing ticket / task covers this surface. - Source: Accounting (Diagnostics) · https://claude.ai/code/session_01G58Y71noihrYCDEDMexmea
- Owner directive (verbatim, 2026-06-24):
"this might be a new task that needs to be assigned with a new UID. Please read AGENTS.MD for procedural confirmation"
- Why no T-NNN: root cause is local to one React component's effect guard; no data, schema, or wire change. AGENTS.md → Tickets: "A one-off data fix can stay ticket-only."
- Not yet done in this ticket: the actual code fix. Capturing the diagnosis here so any agent (or the owner) can apply the one-liner without re-deriving it.
2026-06-24 — fixed¶
- ✅ Attestation (Accounting (Diagnostics)): read
AGENTS.md; closing this ticket per owner "Proceed to I-010 first" (2026-06-24). - Source: Accounting (Diagnostics) · https://claude.ai/code/session_01G58Y71noihrYCDEDMexmea
- What changed:
components/accounting/transactions/modules/ReferenceNumberModule.tsx:96— guard's substring check switched from`/${upper}-${year}-`(the pre-T-081 abbreviation-in-the-middle format) to`/${year}-`(the actualERL-WOPC/YYYY-NNNformatgenerateNextWOPCNumberemits perlib/wopc.server.ts:273-278). Inline comment added so a future agent doesn't re-do the format-mismatch sleuthing. - Verification: the in-flight match-flow's effect now skips on a repeat render once the API has returned a ref for the current
year. No more infinite loop; spinner clears as soon as the response lands.npx tsc --noEmitclean. - Blast radius: single component's
useEffectguard. No data, schema, or server change. Other modules in the workflow wizard are untouched. - Status: investigating → fixed.