54 lines
3.4 KiB
Markdown
54 lines
3.4 KiB
Markdown
# 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 1–2). 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.
|