31 lines
2.1 KiB
Markdown
31 lines
2.1 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
|
|
(config, `snooze-<check>`, `suppress-<check>`), never code. Add `.cc-os/` to the
|
|
project's `.gitignore`.
|
|
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).
|