Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Invariant Registry — Engineer Guide

Canonical registry:
Lint tool: tools/lint/invariant_refs.py
Introduced: RM-0.6.4.11

AngaraBase Invariant Registry makes key architectural guarantees machine-traceable: each invariant links ID → RFC/spec → tests → metrics → evidence. This prevents drift between documentation, code, and observability.


Current Invariants (seed v0)

IDSubsystemRiskStatus
INV-MVCC-SNAPSHOT-VISIBILITYtx_overlaycriticalactive
INV-WAL-DURABLE-AFTER-FSYNCwalcriticalactive
INV-RECOVERY-REDO-UNDO-CLRrecoverycriticalactive
INV-RESOURCE-FAIL-CLOSEDstorage/backpressurehighactive
INV-SPILL-RECURSION-OVERFLOW-53400query/spillhighactive

When INV-ID Is Required in Code

When changing files in the following paths, include a // INV-<ID> comment or add the file to the scoped allowlist:

PathRequired invariant
crates/angarabase/src/tx_overlay/INV-MVCC-SNAPSHOT-VISIBILITY
crates/angarabase/src/wal/INV-WAL-DURABLE-AFTER-FSYNC
crates/angarabase/src/recovery.rsINV-RECOVERY-REDO-UNDO-CLR
crates/angarabase/src/storage/backpressure/INV-RESOURCE-FAIL-CLOSED
crates/angarabase/src/query/spill/INV-SPILL-RECURSION-OVERFLOW-53400

Example code comment

#![allow(unused)]
fn main() {
// INV-WAL-DURABLE-AFTER-FSYNC: UndoAppend must be WAL-durable before PageDelta is written.
// See: RFC-2026-073 §4.Y — ordering invariant (UNDO-before-heap).
assert!(
    undo_lsn < page_delta_lsn,
    "WAL ordering invariant violated: undo_lsn={} >= page_delta_lsn={}",
    undo_lsn, page_delta_lsn
);
}

How to Add a New Invariant

Step 1 — Add an entry to invariants.yaml

- id: INV-<CATEGORY>-<NAME>
  title: "Human-readable name"
  surface: <module_path>           # e.g. tx_overlay, wal, query/spill
  risk_level: critical             # critical | high | medium
  owner: <team>                    # e.g. storage-team, query-team
  spec_refs:
    - rfcs/RFC-YYYY-NNN-name.md    # path relative to repo root docs/rfcs/
  test_refs:
    - crates/angarabase/src/<path>/tests.rs
  metric_refs:
    - angarabase_<metric_name>     # Prometheus metric name (not a file path)
  evidence_refs: []                # or list of pinned evidence paths
  status: active                   # or pending_evidence if refs TBD

Step 2 — If refs are not implemented yet

Use status: pending_evidence and add target_rm:

  status: pending_evidence
  target_rm: RM-0.6.5.0           # train that will create tests/metrics

Lint does not check file refs for pending_evidence invariants.

Step 3 — Run lint

python3 tools/lint/invariant_refs.py --check invariants.yaml
# → exit 0: OK

Step 4 — Add a comment in code

Find the assertion/guard in source code and add the // INV-<ID> comment nearby.

Step 5 — Commit

tools/dev/git-commit-safe.sh -m "NEW(invariants): add INV-<ID> to registry" \
  -- invariants.yaml

Schema Fields

FieldTypeRequiredDescription
idstringUnique ID, starts with INV-
titlestringShort human-readable name
surfacestringModule/subsystem
risk_levelenumcritical / high / medium
ownerstringOwning team
spec_refslist[path]Paths to RFC/spec (checked by lint)
test_refslist[path]Paths to test files (checked by lint)
metric_refslist[string]Prometheus metrics (names)
evidence_refslist[path]Pinned evidence (checked by lint; [] = none)
statusenumactive / pending_evidence
target_rmstringfor pending_evidenceTrain that closes the gap

Running Lint in CI

Lint runs automatically through docs/validate-docs.sh:

bash docs/validate-docs.sh
# → ✅ Invariant registry valid (5 invariant(s) checked, 0 errors)

Standalone:

python3 tools/lint/invariant_refs.py --check invariants.yaml
# exit 0 → OK
# exit 1 → broken ref or schema error (blocks CI)