Query Lifetimes and Determinism
What lives for how long, and why the same inputs always produce the same answer. The lifetime classes below come straight from the ratified state-lifetime rows in tests/architecture-health/ARH1/products/dependency-contracts.json; the deterministic-ordering obligations come from the semantic determinism matrix and the scheduler contracts.
Lifetime classes
Five lifetimes appear across the hotspots. Knowing which one a piece of state has tells you when it may change and who may observe it:
| Lifetime | Meaning | Examples |
|---|---|---|
session | lives as long as the host/session | Scheduler.nodes, Scheduler.dag, the SemanticGraphStore memo |
snapshot-epoch | MVCC epoch; readers capture an O(1) view and never see it move | Scheduler.source_root (crates/verter_scheduler/src/source_root.rs) |
content-version | re-planned once per file content version | FunctionFlowGraph, FlowSliceIR, stable keys (crates/verter_type_engine/src/semantic_query/stable_key.rs) |
dispatch-transaction | transient per-obligation-root cold-compute frame | CheckerDispatchTransaction (crates/verter_type_engine/src/project_semantic_dispatch/dispatch_txn.rs) |
request | exists for one query/request only, never cached | parse-snapshot leases, admission budget outcomes, cancellation tokens, scoped cache flights |
Two rules follow. First: request-lifetime state is never a cache entry — a scoped cache flight or a cancelled compute frame publishes nothing. Second: content-version identity is what lets an unrelated edit preserve a narrow slice hash while still producing a distinct graph artifact key; never key a content-version artifact by request identity.
The memo: identity is semantic meaning
crates/verter_type_engine/src/semantic_query.rs defines the query envelope (SemanticQueryKey and friends); the host-owned memo is the SemanticGraphStore in crates/verter_type_engine/src/semantic_query_memo/mod.rs, dispatched through ProjectSemanticDispatch. Its founding rule: memo identity is semantic meaning, not request identity. Two different callers asking the same question share one entry; the same caller asking after an edit gets a different answer because the key's content-free identity is pinned by a versioned value inside the entry, not by who asked.
Cache admission is decided in exactly one place — crates/verter_type_engine/src/semantic_query/admit.rs (admit_decision) — and is typed: Unknown, budget-exhausted, cancelled and superseded results route through ReturnOnly, which never publishes an entry, never registers reverse-index metadata, and never becomes a persistent artifact. A partial result is a truthful partial (ResultCompleteness, PartialReasonSet), not a cacheable approximation.
Scheduler: generations fence everything
The scheduler (crates/verter_scheduler/src/scheduler.rs) keeps per-file FileNode state with atomic generation counters. A generation bump makes the prior source immediately unreachable: stale snapshots are invisible by construction (current_analysis() returns nothing when the generation does not match), and the per-canonical retirement floor only ever advances — everything below it can never be admitted again. Batch admission is atomic: submit_batch_atomic lands one batch submission the driver drains under a single DAG lock acquisition (generation bumps and supersede sweeps for every request in one critical section), so observers can never see a half-admitted batch. Source snapshot publication and Source-stage readiness are distinct — a request arriving in that window joins the still-live Source identity instead of forcing an early stage.
If you drive the scheduler in a test, use the cooperative/test seams the crate itself ships; the test_-prefixed hooks are recorded test-only seams under ratified narrowing (see the architecture contracts page), not a supported API.
Determinism
Determinism is enforced on two bases at once — see crates/verter_session/tests/cases/g_block/semantic_determinism_matrix.rs:
- The completed observation rendered to stable text (kinds, names, literal values, structural order) — never node ids, pointers or allocation ordinals.
- The digest of the generated bytes themselves.
A schedule-dependent answer cannot hide behind an unstable print, and a fixed observation cannot hide drifting bytes. In practice this means:
- Union and intersection normalization produce a deterministic order (not input order); relation results publish only decided outcomes.
- Admission, dedup, generation supersession and capacity accounting all flow through the one readiness authority (
dag.rs), so wakeup order does not affect results. - Docs/tooling receipts follow the same discipline: the reference harness canonicalizes its receipt (sorted pages, links, errors) and requires a perturbed discovery order to yield an identical canonical receipt.
When you add a query or a cache, add the discriminating determinism twin: same inputs, perturbed order, byte-identical canonical result. "It passed once" is not determinism evidence.
Related pages
- Architecture contracts — the hotspot rows these lifetime classes come from.
- Source and map identity — canonical paths, spans and stable hashes.