cc-os/plugins/os-status/invariants.md

34 lines
2.4 KiB
Markdown

# os-status behavioral invariants
Reversion protection for the SessionStart status hook. Any change that breaks one of these
is a regression, not a refactor. Enforced by `tests/hook_test.py` (model-free;
`python3 tests/hook_test.py`).
1. **Single banner.** At most one `systemMessage` warning banner per session, containing one
short line per surviving `warn`. Multiple warns never produce multiple banners.
2. **Notes are never suppressed.** `note` results bypass snooze and suppress entirely and are
emitted as `additionalContext`, not inside the banner. This exists so os-adr's
Eval-B-tuned usage note survives conversion unchanged (design D2).
3. **Exit 0, always.** The hook exits 0 on every path — checks warning, checks raising, the
checks module failing to import, malformed settings.json, no git project. A session is
never blocked by os-status.
4. **Failure isolation.** A check that raises or blows its time budget becomes a generic
`warn` naming the check; it never hides other checks' results and never leaks exception
text into the banner.
5. **State only in `.cc-os/`.** The hook writes nothing outside the project's `.cc-os/`
directory, and writes nothing at all outside a git project. `.cc-os/` holds state,
never code: the shared `config` file at `.cc-os/config` (ADR-026), and os-status's own
`snooze-<check>` / `suppress-<check>` markers under `.cc-os/status/` (ADR-027). Reads
fall back to the pre-ADR-027 top-level location; the first write migrates and removes
the legacy files. Add `.cc-os/` to the project's `.gitignore` (one entry covers every
cc-os plugin's state).
6. **PRESENT_NOTE / ABSENT_NOTE byte-identical to os-adr.** `checks.py` carries verbatim
copies of the wording in `plugins/os-adr/hooks/session_start.py` (the wording source of
record, tuned by the Eval B campaign and validated by Eval C). Never paraphrase; if the
wording changes there, copy it here byte-for-byte (the test compares the two files).
7. **Legacy suppress honored.** The `adr-system-present` check returns `ok` when
`.os-adr/suppress` exists, so projects silenced under the old os-adr hook stay silent.
8. **Convention-shaped seam.** Every check keeps the uniform signature
`check(ctx) -> CheckResult(status, message)` with registration in `checks.REGISTRY`
only — so later extraction to per-plugin subprocess checks stays mechanical (design D1).