Skip to content

231 coaching-invoice share links broke — 5 Nan session invoices dropped by the T-146 orphan re-home (restored)

Symptom

Short links in the Bank-Transaction XLSX attached to coaching invoices rendered "Document not found / The referenced document no longer exists." The viewerLinks records were valid; the failure was downstream — the linked sessions had no invoice anymore.

200 OK · 231 broken · 0 expired. All 231 broken links collapse onto exactly 5 sessions, all Nancy Kwai (Nan) — the I-025/T-146 orphan cohort: m29fu35… (Nan-016), 7r7kal6… (Nan-017), 0bsl1jp… (Nan-018), lche21l… (Nan-020), lkact6v… (Nan-021).

Root cause

The T-146 orphan re-home (2026-07-01) moved these sessions to Students/Nan/Sessions/{id} but their invoice did not survive in either shape — no invoice map field on the re-homed doc, and zero invoice subcollection docs anywhere in tebs-mel (collection-group scan, phantoms included). The re-home script's doc-copy spreads the full body, so the top-level docs already lacked the map field at re-home time; whatever shape held these invoices was dropped in the move. Unrecoverable via Firestore infra: PITR disabled (1h retention), no managed backups; the re-home's own JSON backup (gitignored) could not be located.

Fix — evidence-based reconstruction (no re-minting)

scripts/restore-nan-coaching-invoices.ts (dry-run default · backup-first · never overwrites a populated invoice) rebuilt each invoice map from surviving data: - Original invoice number, amount, payment tx ref ← the session's own payment subcollection (written by accounting-match) — numbers preserved, nothing re-minted; - ordinal ← parsed from the number (rule verified on a healthy sibling); sessionDate ← origStartTimestamp; issuedAt/issuedBy ← the invoiceUpdateLogs "created" entry (Nan-020: paymentMade fallback); - detail.payment.tx = [paymentId] — equivalence verified on the healthy sibling; - payee/billing/student constants ← the healthy sibling template (same student, same billing company). Provenance logged to each session's invoiceUpdateLogs (system:i-029-restore); rollback JSON in scripts/migration-backups/ (local).

Verification

Executed 5/5; live checks: the owner's link + one link per session all render their original invoice numbers; "Document not found" gone. Owner authorization (2026-07-03, verbatim): "please do scan every coaching-invoice share link and check which sessions now lack an invoice, while Firestore should have backup logging moves made to the databases, you can also check there for issues" (fix directive from the prior message: "Please figure out the cause and fix it").

For the Coaching agent (T-146)

The re-home dropped invoice payloads for these 5 orphans — if any future orphan re-home runs, carry the invoice map field explicitly and verify invoice presence post-move. Recommend enabling PITR on tebs-mel (and the other DBs) so the next data incident has a 7-day time-travel window.

  • Source: EOP Local Assistance (fork) · session edb0abc7-6373-449b-8e31-379fd38d391b

2026-07-03 — full-database sweep: NO further losses (recovery complete)

  • ✅ Read AGENTS.md. Owner asked to keep working the missing-coaching-invoice issue, so the scan was widened beyond link-referenced sessions to all 113 sessions in tebs-mel, using three independent evidence sources:
  • Loss signature (payment doc carries an invoiceNumber but the session has no invoice map): 0 sessions match (was 5 before the restore). 0 number mismatches between payments and invoice maps.
  • Share-link evidence (a link minted ⇒ an invoice existed at mint time): 15 distinct sessions were ever linked — all 15 currently carry their invoice map (13 Nan + 2 MT). Nothing ever-linked is missing.
  • updateLog evidence: every session with invoiceUpdateLogs now has an invoice map.
  • Billing classification of the other 98 sessions (why they have no invoice doc — by design, not loss): 68 payment-only (paid/matched before the per-session invoice feature existed, ~Apr 2026 — their payment docs carry no invoiceNumber), 28 no billing artifacts (unbilled / retainer-covered — e.g. 22 of Kiri T's 50; retainers live on the Student, not the session), 2 voucher-covered.
  • Verdict: every coaching invoice that provably ever existed is present. The apparent per-student "holes" (e.g. Nan ordinals 1–5, 9–10, 19) are pre-feature payment-only or voucher/unbilled sessions — invoice numbers derive from the session ordinal, so a hole is a never-invoiced session, not a lost doc.
  • Source: EOP Local Assistance (fork) · session edb0abc7-6373-449b-8e31-379fd38d391b

2026-07-03 — follow-up executed: PITR enabled on ALL Firestore databases

  • ✅ Read AGENTS.md. Owner approved the ticket's recommendation (verbatim: "Can you enable PITR for me?"). Enabled Point-in-Time Recovery on all 5 databases (tebs-erl, tebs-mel, tebs-epl, aote-system, jefferochan) — verified POINT_IN_TIME_RECOVERY_ENABLED, retention 604800s (7 days, was 1h). The window accrues from 2026-07-03 forward (no retroactive coverage). Next data incident: read/export at a pre-incident timestamp instead of evidence-based reconstruction. Cost: version-storage only — trivial at current volumes.
  • Source: EOP Local Assistance (fork) · session edb0abc7-6373-449b-8e31-379fd38d391b