Spec: Covering Temporal Indexes & Global Scan Semantics¶
File: .ai/specs/core-engine/2026-07-02-global-temporal-scan.md Status: Draft Owners: Storage lane Related: PR-016; composite-key-layout spec §5.2 (valid_time_index / tx_time_index layouts); tombstone-cascade spec
1. Overview¶
Defines how the two covering secondary indexes are maintained, the exact algorithm for global cross-cutting temporal queries ("everything valid at T within tenant"), horizon-boundary termination, and the consistency doctrine between primary and index CFs.
2. Problem Statement¶
Entity-scoped reads are cheap prefix scans, but "show me the whole tenant's world at time T" cannot enumerate every logicalId. Without covering indexes it degenerates to a full-tenant scan; without defined maintenance rules the indexes silently drift from the primary CFs; without a termination rule the scan is unbounded; and without a validation step, index rows for versions whose validity has since been capped produce false positives.
3. Proposed Solution¶
Every version write (create, update-closure, tombstone — no exceptions) emits its valid_time_index and tx_time_index entries in the same WriteBatch as the primary record; index values carry the full primary key (covering). The global scan iterates the index forward from [scope][inv(T)], and for each candidate performs a primary-CF point lookup to validate the record's current end boundaries at the query's bitemporal coordinate. Index rows are never deleted (append-only doctrine); staleness is impossible because validation always consults the primary record.
4. Architecture¶
query "valid at (vT, tT)"
→ ScanRange from [scope][inv(vT)] (valid_time_index)
→ forward iterate: candidates have validTimeStart <= vT (by inversion)
→ per candidate: point-lookup primary key (carried in index value)
keep iff validTime.start <= vT < validTime.end
AND txTime.start <= tT < txTime.end
AND !tombstone
→ dedupe by (kind, logicalId): first hit wins (latest version sorts first)
→ stop at: result limit | horizon boundary | scan ceiling
5. Data Models¶
Key layouts per composite-key-layout spec §5.2 (65B, kind byte disambiguating node/edge). Index value = the exact primary-CF key bytes (64B), making every validation a point lookup. Maintenance rule (normative): the set of index entries is exactly the set of version records × 2 — verified by the consistency checker (§12). Horizon boundary: iteration stops when validTimeStart drops below vT − horizon (config, default 10 years): any record starting earlier that is still valid at vT is a long-lived record and horizon must be sized so none are missed — see OQ-1 for the pathological case and its mitigation.
6. API Contracts¶
fn scan_valid_at(scope, valid_t, tx_t, filter: Option<LabelFilter>, limit, cursor) -> Page<VersionRef>;
fn scan_tx_at(...) // mirror on tx_time_index
Cursor = last index key (opaque, tenant-checked on resume). Telemetry per scan: rows_scanned, rows_validated, rows_returned — exported as metrics; selectivity = returned/scanned drives planner cost decisions and the PR-022b bitmap trigger.
7. UI/UX¶
Internal. Forensics: telha debug index-check <tenant> runs the consistency checker over one tenant and reports orphan/missing entries (should always be zero).
8. Configuration¶
global_scan.horizon (default 10y), global_scan.max_rows_scanned (default 1,000,000 — exceeding returns partial-with-warning), page size caps.
9. Alternatives Considered¶
Deleting index entries when versions close (rejected: violates append-only; breaks backward snapshots; F3 doctrine). Interval trees / segment indexes (rejected for v1: powerful but complex; the covering-index + validation pattern is simple, correct, and measurable — revisit only if selectivity telemetry shows chronic waste). Storing end-times in index keys (rejected: end-times change when versions close, and keys must be immutable; validation-via-primary keeps keys write-once).
10. Implementation Approach¶
PR-016 commits: this spec → index maintenance in write paths → scan operator → telemetry → consistency checker (tool + test) → brute-force-reference correctness tests on randomized datasets.
11. Migration Path¶
Greenfield. If maintenance rules ever change, indexes are rebuildable from primary CFs by full scan (derived-data doctrine, same as F4) — a telha index rebuild-temporal subcommand accompanies any such change.
12. Success Metrics¶
Consistency checker: zero orphans/missing across randomized write/tombstone workloads. Correctness: results byte-equal to a brute-force full-scan reference on 10k randomized histories. Performance (F6 gate, PR-029): global valid-at-T scan p50 within budget on the 10M-node dataset at realistic selectivity.
13. Open Questions¶
- OQ-1: Horizon vs immortal records — a record created 30 years ago and still valid would be missed by a 10y horizon. Stance: v1 mitigates by exempting open-ended (
end==MAX) records via a small per-tenant "long-lived set" side list consulted at scan start; measure its size in practice and revisit if it grows past ~10k entries per tenant.
14. Changelog¶
- 2026-07-02 — v0.1: initial draft for peer review.