I-031 — GCP charge shown in both sub-tabs + broken date sorting¶
Symptoms (owner, 2026-07-01)¶
- Service Invoices sub-tab: the Invoice Date column (MMM DD, YYYY) sorts in alphabetical (month-name) order, not by date.
- GCP charge
5626347939(a Google Cloud Platform charge; should be a Service Invoice) shows in both the Expense Receipt sub-tab AND the Service Invoices sub-tab. - Expense Receipt sub-tab sorts rows unpredictably when sorting by date.
Investigation (live tebs-erl / aote-system)¶
- All date fields are ISO (
YYYY-MM-DD): 86 receipts, the GCP billing invoices, the 3 manual + 46 workspace invoices — none non-ISO. So a rawdate.localeCompare()would sort chronologically; the values aren't the problem. - No GCP receipt exists — 0 of 86 receipts match by amount (879.56 / 301.02), date (2026-06-30) or a Google/GCP/Cloud merchant. Not literally duplicated into the receipt store.
- Confirmed root cause — duplicate React key: the GCP billing
invoicescollection holds two docs for June 2026, bothinvoiceMonth: "202606"— the Cost Table invoice5626347939($879.56, 2026-06-30) and a BigQuery-only aggregate202606($301.02, no invoice #).buildGcpRowskeys each row byinvoiceMonth, so both map torecordId 202606→ExpenseRecordRow.id = "invoice:gcp:202606"for both. Duplicate React key → month rendered twice + AntD sort/render erratic (row bleeds into the other sub-tab's view = the "both tabs").
Fix (commit below)¶
lib/accounting/vendorInvoiceFeed.server.ts(buildGcpRows) — collapse GCP rows to one per billing month, preferring the Cost Table invoice over the aggregate. Kills the duplicate June row + the key collision.components/records/ExpenseRecordsTab.tsx— the Date column sorts by the parsed date (new Date(x).getTime(), undated last), not stringlocaleCompare.
Verification¶
Replicated listStoredInvoices + buildGcpRows on live data: 2026-06 went from 2
rows (both recordId 202606) → 1 (5626347939, $879.56). Project-pinned tsc clean.
Owner to re-verify on deploy (if the Receipt sub-tab still mis-sorts with no GCP row
present, a screenshot would help — no receipt-side dup was found).
Decision log¶
2026-07-01 — ✅ Read AGENTS.md before working this ticket. Source: Records (Infrastructure) · https://claude.ai/code/session_018RDB37kCqfouHdygVXTAtD
Renumbered I-024 → I-029 → I-031 across successive merge collisions (I-024/I-026 are the fork's NAS GCP-scraper tickets that fix the ingest side; I-029 was taken by the coaching-links recovery; I-030 by the payee-block hot-fix). This ticket is the read-time Records-feed tolerance for the duplicate June docs — complementary, not overlapping. Ticket-only. Next free I-032.