diff --git a/infra/firestore/firestore.indexes.json b/infra/firestore/firestore.indexes.json index 4c44a38..3a8216c 100644 --- a/infra/firestore/firestore.indexes.json +++ b/infra/firestore/firestore.indexes.json @@ -1,7 +1,10 @@ { - "//": "Composite indexes required once drb-frontend's lib/use*.ts hooks add a where(\"org_id\",\"==\",orgId) equality filter alongside an existing range/orderBy/array-contains clause. Firestore auto-indexes single-field lookups and equality-only compound queries, but org_id==X combined with an inequality, orderBy on a different field, or array-contains needs an explicit composite index or the query fails at runtime with a FAILED_PRECONDITION 'index required' error (see SAAS_PLAN.md 2.8/B2 and the URL Firestore prints in that error, which is the fastest way to double-check this list against the live query shapes). Deploy with: firebase deploy --only firestore:indexes --project ", + "//": "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 (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": [ @@ -10,6 +13,7 @@ ] }, { + "//": "drb-frontend lib/useCalls.ts — the calls attached to one incident.", "collectionGroup": "calls", "queryScope": "COLLECTION", "fields": [ @@ -18,6 +22,25 @@ ] }, { + "//": "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/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": [ @@ -26,6 +49,7 @@ ] }, { + "//": "drb-frontend lib/useAlerts.ts — org-scoped alert feed, orderBy triggered_at desc.", "collectionGroup": "alert_events", "queryScope": "COLLECTION", "fields": [ @@ -34,6 +58,7 @@ ] }, { + "//": "drb-frontend lib/useAlerts.ts useUnacknowledgedAlerts — the nav badge.", "collectionGroup": "alert_events", "queryScope": "COLLECTION", "fields": [