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

54 lines
3.4 KiB
Markdown
Raw Permalink Normal View History

# os-adr invariants
Behavioral invariants of the plugin. Changing any of these requires explicit human approval and
a matching update to the tests named beside it. Suite entry points: `ruby tests/all.rb` and
`python3 tests/hook_test.py` (both model-free).
1. **Non-destructiveness (migration).** Migration writes only new files under `docs/adr/`;
existing source content is never deleted, moved, or edited by any tooling path. Old-system
removal is a separate, explicit, user-approved step (skill-mediated, never automatic).
`migrator_test.rb#test_sources_stay_byte_identical`, pilot sha256 checks.
2. **Index regeneration, never accretion.** `docs/adr/README.md`'s table is regenerated in full
from directory contents on every ADR write; it is never appended to or hand-edited, and
self-heals after manual edits. Content outside the markers is preserved.
`index_test.rb` (golden fixture, self-heal, prose preservation, generated-header cases).
3. **IDs come from the directory, never the index.** Next ID = max existing filename ID + 1,
derived by scanning `docs/adr/`; a stale or missing index never influences numbering.
`repository_test.rb#test_next_id_ignores_{missing,stale}_index`.
4. **No LLM in the hook.** The SessionStart hook is a pure function of filesystem state (ADR
dir, index, suppression state, snooze stamp) — no model call, no network, no Ruby spin-up;
always exits 0; emits nothing (and creates no state) outside a git project. Its existence
rule is identical to `Adr::Repository#exists?`.
`hook_test.py` (all cases; `PythonRubyAgreementTest` pins the shared rule).
5. **Single uncertainty-flagging mechanism.** Migration uncertainty surfaces only via
`migration_confidence: low|medium|high` frontmatter plus `docs/adr/migration-report.md`.
No scattered inline uncertainty comments.
`migrator_test.rb#test_report_lists_files_confidence_sources_and_flag_rate`.
6. **Core fields are never invented.** A Decision or Context absent from a migration source is
written as `_not stated in source_` and flags the file low-confidence; LLM fills apply only
to pending-marker fields (enforced structurally by `apply_fills!` — a stray fill key is a
no-op), and an LLM-filled file stays flagged (≤ medium).
`migrator_test.rb#test_missing_core_fields_*`, `#test_apply_fills_only_touches_pending_fields`.
7. **Unrecognized content is reported, never auto-converted.** Content matching no known shape
— including multi-decision files without mechanically splittable unit headings — is listed
in the report for manual handling only.
`detector_test.rb#test_multi_decision_file_without_unit_headings_is_unrecognized`,
`migrator_test.rb#test_unrecognized_content_is_reported_never_converted`.
8. **Deterministic-first retrieval.** `adr-find` narrows by frontmatter path/component match,
then Accepted-only status filter (explicit override), then optional one-hop graph expansion
that degrades gracefully (absent or corrupt graph → layers 12). AI judgment happens only in
the `find` skill, only over the CLI's returned candidates.
`finder_test.rb` (all cases, including both degradation paths).
9. **Skills delegate mechanics to `bin/` CLIs.** Numbering, templating, indexing, supersession
bookkeeping, detection, conversion, and retrieval filtering are never re-implemented in
prompt instructions.
— by construction of `skills/*/SKILL.md`; `cli_test.rb` pins the CLI contracts.