Dedup was failing on essentially every inbound call in production. dedup.py queries system_id == X with a started_at range; that composite index was neither declared in firestore.indexes.json nor present in the live c2-server database, so the query returned FAILED_PRECONDITION, dedup swallowed it as a warning, and every duplicate check degraded to "not a duplicate". While AI is off that only cost duplicate call documents. With a window open it would have paid Whisper and Gemini twice for every double-heard transmission, and fed Gate B5's cost measurement a figure that is wrong for a reason unrelated to the pipeline being measured. Two documents for one transmission is also the exact input shape that produces a spurious second incident. The index is created on c2-server and building. This declares it in source so the file and the live database agree. Refs server-26#84, #33, #45. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
82 lines
4.5 KiB
JSON
82 lines
4.5 KiB
JSON
{
|
|
"//": "Composite indexes for the c2-server database. Firestore auto-indexes single-field lookups and equality-only compound queries; an equality filter combined with an inequality, an orderBy on a different field, or array-contains needs an explicit composite index or the query fails at runtime with FAILED_PRECONDITION. Deploy with: firebase deploy --only firestore:indexes --project <project-id> (firebase.json pins database c2-server — without that key the CLI targets (default) and changes nothing the app can see).",
|
|
"//direction": "Every index here is declared ASCENDING. Firestore scans an index in either direction, so org_id+started_at ASC serves orderBy(started_at, 'desc') as well — which is what every frontend hook actually asks for. Declaring only the ASC form keeps one index per query shape instead of a matched pair.",
|
|
"//drift-2026-08-23": "Reconciled against `gcloud firestore indexes composite list --database=c2-server` (server-26#33). The file had drifted four indexes behind the live database, and a deploy against the stale file then added ASC copies of indexes that already existed as DESC. The next deploy will offer to delete three live indexes that are deliberately not declared here — answer YES to all three: calls(org_id ASC, started_at DESC) and incidents(org_id ASC, started_at DESC) are duplicates of the ASC entries below, and alert_events(acknowledged ASC, triggered_at DESC) predates tenancy and is superseded by the org-scoped entry below. Nothing else may be deleted.",
|
|
"indexes": [
|
|
{
|
|
"//": "drb-frontend lib/useCalls.ts — org-scoped call list, orderBy started_at desc.",
|
|
"collectionGroup": "calls",
|
|
"queryScope": "COLLECTION",
|
|
"fields": [
|
|
{ "fieldPath": "org_id", "order": "ASCENDING" },
|
|
{ "fieldPath": "started_at", "order": "ASCENDING" }
|
|
]
|
|
},
|
|
{
|
|
"//": "drb-frontend lib/useCalls.ts — the calls attached to one incident.",
|
|
"collectionGroup": "calls",
|
|
"queryScope": "COLLECTION",
|
|
"fields": [
|
|
{ "fieldPath": "org_id", "order": "ASCENDING" },
|
|
{ "fieldPath": "incident_ids", "arrayConfig": "CONTAINS" }
|
|
]
|
|
},
|
|
{
|
|
"//": "c2-core internal/recorrelation_sweep.py:45 — status == 'ended' AND ended_at >= cutoff. Backend only; was live but undeclared until 2026-08-23.",
|
|
"collectionGroup": "calls",
|
|
"queryScope": "COLLECTION",
|
|
"fields": [
|
|
{ "fieldPath": "status", "order": "ASCENDING" },
|
|
{ "fieldPath": "ended_at", "order": "ASCENDING" }
|
|
]
|
|
},
|
|
{
|
|
"//": "c2-core internal/dedup.py:84 — system_id == X AND started_at within a +/- window. Was live-failing on essentially every inbound call (server-26#84): dedup caught the FAILED_PRECONDITION and degraded to \"not a duplicate\", so double-heard transmissions were stored twice and would have been transcribed and correlated twice the moment an AI window opened. Created directly on c2-server 2026-08-28.",
|
|
"collectionGroup": "calls",
|
|
"queryScope": "COLLECTION",
|
|
"fields": [
|
|
{ "fieldPath": "system_id", "order": "ASCENDING" },
|
|
{ "fieldPath": "started_at", "order": "ASCENDING" }
|
|
]
|
|
},
|
|
{
|
|
"//": "c2-core internal/vocabulary_learner.py:290 — system_id == X AND ended_at >= cutoff. Backend only; was live but undeclared until 2026-08-23.",
|
|
"collectionGroup": "calls",
|
|
"queryScope": "COLLECTION",
|
|
"fields": [
|
|
{ "fieldPath": "system_id", "order": "ASCENDING" },
|
|
{ "fieldPath": "ended_at", "order": "ASCENDING" }
|
|
]
|
|
},
|
|
{
|
|
"//": "drb-frontend lib/useIncidents.ts — org-scoped incident browse, orderBy started_at desc.",
|
|
"collectionGroup": "incidents",
|
|
"queryScope": "COLLECTION",
|
|
"fields": [
|
|
{ "fieldPath": "org_id", "order": "ASCENDING" },
|
|
{ "fieldPath": "started_at", "order": "ASCENDING" }
|
|
]
|
|
},
|
|
{
|
|
"//": "drb-frontend lib/useAlerts.ts — org-scoped alert feed, orderBy triggered_at desc.",
|
|
"collectionGroup": "alert_events",
|
|
"queryScope": "COLLECTION",
|
|
"fields": [
|
|
{ "fieldPath": "org_id", "order": "ASCENDING" },
|
|
{ "fieldPath": "triggered_at", "order": "ASCENDING" }
|
|
]
|
|
},
|
|
{
|
|
"//": "drb-frontend lib/useAlerts.ts useUnacknowledgedAlerts — the nav badge.",
|
|
"collectionGroup": "alert_events",
|
|
"queryScope": "COLLECTION",
|
|
"fields": [
|
|
{ "fieldPath": "org_id", "order": "ASCENDING" },
|
|
{ "fieldPath": "acknowledged", "order": "ASCENDING" },
|
|
{ "fieldPath": "triggered_at", "order": "ASCENDING" }
|
|
]
|
|
}
|
|
],
|
|
"fieldOverrides": []
|
|
}
|