Invariant Registry — руководство инженера
Canonical registry:
Lint tool:tools/lint/invariant_refs.py
Introduced: RM-0.6.4.11
AngaraBase Invariant Registry делает ключевые архитектурные гарантии машинно-трассируемыми: каждый инвариант связывает ID → RFC/spec → тесты → метрики → evidence. Это предотвращает drift между документацией, кодом и наблюдаемостью.
Текущие инварианты (seed v0)
| ID | Подсистема | Risk | Статус |
|---|---|---|---|
INV-MVCC-SNAPSHOT-VISIBILITY | tx_overlay | critical | active |
INV-WAL-DURABLE-AFTER-FSYNC | wal | critical | active |
INV-RECOVERY-REDO-UNDO-CLR | recovery | critical | active |
INV-RESOURCE-FAIL-CLOSED | storage/backpressure | high | active |
INV-SPILL-RECURSION-OVERFLOW-53400 | query/spill | high | active |
Когда обязательно указывать INV-ID в коде
При изменении файлов в следующих путях необходимо включить // INV-<ID> comment
или добавить файл в scoped allowlist:
| Путь | Обязательный инвариант |
|---|---|
crates/angarabase/src/tx_overlay/ | INV-MVCC-SNAPSHOT-VISIBILITY |
crates/angarabase/src/wal/ | INV-WAL-DURABLE-AFTER-FSYNC |
crates/angarabase/src/recovery.rs | INV-RECOVERY-REDO-UNDO-CLR |
crates/angarabase/src/storage/backpressure/ | INV-RESOURCE-FAIL-CLOSED |
crates/angarabase/src/query/spill/ | INV-SPILL-RECURSION-OVERFLOW-53400 |
Пример 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
);
}
Как добавить новый инвариант
Шаг 1 — Добавить запись в 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
Шаг 2 — Если refs ещё не реализованы
Используй status: pending_evidence и добавь target_rm:
status: pending_evidence
target_rm: RM-0.6.5.0 # train, который создаст тесты/метрики
Lint не проверяет file refs для pending_evidence инвариантов.
Шаг 3 — Проверить lint
python3 tools/lint/invariant_refs.py --check invariants.yaml
# → exit 0: OK
Шаг 4 — Добавить комментарий в код
Найди assertion/guard в исходном коде и добавь // INV-<ID> comment рядом.
Шаг 5 — Закоммитить
tools/dev/git-commit-safe.sh -m "NEW(invariants): add INV-<ID> to registry" \
-- invariants.yaml
Поля схемы
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
id | string | ✓ | Уникальный ID, начинается с INV- |
title | string | ✓ | Короткое человекочитаемое название |
surface | string | ✓ | Модуль/подсистема |
risk_level | enum | ✓ | critical / high / medium |
owner | string | ✓ | Команда-owner |
spec_refs | list[path] | ✓ | Пути к RFC/spec (проверяются lint-ом) |
test_refs | list[path] | ✓ | Пути к тест-файлам (проверяются lint-ом) |
metric_refs | list[string] | ✓ | Prometheus-метрики (имена) |
evidence_refs | list[path] | ✓ | Pinned evidence (проверяются lint-ом; [] = нет) |
status | enum | ✓ | active / pending_evidence |
target_rm | string | для pending_evidence | Train, закрывающий gap |
Запуск lint в CI
Lint запускается автоматически через 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 или schema error (блокирует CI)