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.
|