# 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-` / `suppress-` 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).