100 lines
5.1 KiB
Markdown
100 lines
5.1 KiB
Markdown
|
|
# Delta: lifecycle-rulebook (calibrate-assessment-inventory)
|
||
|
|
|
||
|
|
## MODIFIED Requirements
|
||
|
|
|
||
|
|
### Requirement: Rulebook Locations and Envelope
|
||
|
|
|
||
|
|
The plugin SHALL ship a global rulebook at
|
||
|
|
`plugins/os-doc-hygiene/rulebook.json`, resolved relative to plugin scripts,
|
||
|
|
present in every project. A project MAY additionally provide a committed
|
||
|
|
repo-root `.dochygiene-rules.json` override. Both files SHALL use the
|
||
|
|
envelope `{"schema_version": 1, "rules": [...]}`. The project file MAY
|
||
|
|
additionally carry a top-level `nominations` key (see the Nominations Memory
|
||
|
|
requirement); the loader SHALL ignore unknown top-level keys, so the key is
|
||
|
|
additive to `schema_version` 1. The per-project override SHALL NOT live under
|
||
|
|
gitignored `.cc-os/` — it SHALL be a committed, reviewable dotfile.
|
||
|
|
|
||
|
|
#### Scenario: Global rulebook is always present
|
||
|
|
|
||
|
|
- **WHEN** the rulebook loader runs in any project
|
||
|
|
- **THEN** it loads `plugins/os-doc-hygiene/rulebook.json` resolved relative to the plugin scripts directory
|
||
|
|
|
||
|
|
#### Scenario: Per-project override is optional and committed
|
||
|
|
|
||
|
|
- **WHEN** a project has no `.dochygiene-rules.json` at its repo root
|
||
|
|
- **THEN** the loader proceeds using only the global rulebook, and when the file is present it is read as a committed, reviewable file, never from `.cc-os/`
|
||
|
|
|
||
|
|
#### Scenario: Both files share one envelope shape
|
||
|
|
|
||
|
|
- **WHEN** either the global rulebook or a project override is loaded
|
||
|
|
- **THEN** it is validated against the envelope `{"schema_version": 1, "rules": [...]}`
|
||
|
|
|
||
|
|
#### Scenario: A nominations key does not disturb the v1 loader
|
||
|
|
|
||
|
|
- **WHEN** a project `.dochygiene-rules.json` carries a top-level `nominations` key alongside `rules`
|
||
|
|
- **THEN** `rulebook.py` loads the `rules` array exactly as before, ignoring the unknown top-level key without warning or error
|
||
|
|
|
||
|
|
## ADDED Requirements
|
||
|
|
|
||
|
|
### Requirement: Nominations Memory Lives in the Project Rules File
|
||
|
|
|
||
|
|
The project `.dochygiene-rules.json` MAY carry a top-level `nominations` key
|
||
|
|
holding exactly two lists: `consults` (open questions — entries with `glob`,
|
||
|
|
`question`, `evidence`, `cluster_key`, `asked_on`, and deliberately NO
|
||
|
|
lifetime) and `rejected` (settled "no" answers — entries with `glob`,
|
||
|
|
`lifetime`, `why`, optional `consider_instead`, `rejected_by` (`"judge"` or
|
||
|
|
`"human"`), `judged_on`). The `nominations` key SHALL never affect which
|
||
|
|
files the rulebook governs — only entries in `rules` decide that.
|
||
|
|
`rulebook.py` SHALL remain nomination-unaware; only the calibrate helpers
|
||
|
|
read the key, and the calibrate reader SHALL warn on unrecognized nomination
|
||
|
|
fields, mirroring the rules array's unknown-field discipline. Rejected
|
||
|
|
entries and exact-path singleton keep rules SHALL exit only by hand-deletion
|
||
|
|
(removals stay HITL with recorded reasoning); no automated revisit path
|
||
|
|
SHALL exist.
|
||
|
|
|
||
|
|
#### Scenario: Nominations never filter files
|
||
|
|
|
||
|
|
- **WHEN** the scanner or rulebook resolves the governing rule for a path that only a `nominations` entry's glob matches
|
||
|
|
- **THEN** the path is treated as unmatched/unmanaged — nominations carry no lifecycle authority
|
||
|
|
|
||
|
|
#### Scenario: Consult entries carry no lifetime
|
||
|
|
|
||
|
|
- **WHEN** a consult entry is written to `nominations.consults`
|
||
|
|
- **THEN** it records `glob`, `question`, `evidence`, `cluster_key`, `asked_on` and no lifetime field — presence in the list means open, with no status field
|
||
|
|
|
||
|
|
#### Scenario: Unrecognized nomination fields warn in the calibrate reader
|
||
|
|
|
||
|
|
- **WHEN** the calibrate helpers read a nominations entry containing an unknown field
|
||
|
|
- **THEN** a warning is emitted and the field is preserved, never silently dropped
|
||
|
|
|
||
|
|
#### Scenario: A rejection leaves only by hand-deletion
|
||
|
|
|
||
|
|
- **WHEN** a calibration pass runs against a rules file containing a stale rejection
|
||
|
|
- **THEN** no automated path removes or expires the entry; it is removed only by explicit human edit
|
||
|
|
|
||
|
|
### Requirement: Canonical Writer-Enforced Ordering
|
||
|
|
|
||
|
|
Every code path that serializes `.dochygiene-rules.json` SHALL write through
|
||
|
|
one canonical writer that emits: `rules` grouped by lifetime tier in the
|
||
|
|
order delete-once-served, temporary, keep, glob-sorted within each group;
|
||
|
|
`nominations` after `rules`; `consults` before `rejected`, each glob-sorted.
|
||
|
|
The writer SHALL be idempotent (canonicalizing an already-canonical file is a
|
||
|
|
no-op) and SHALL round-trip unknown fields with a warning rather than
|
||
|
|
dropping them. Ordering SHALL NOT be enforced by any hook; hand edits
|
||
|
|
re-canonicalize on the next write.
|
||
|
|
|
||
|
|
#### Scenario: Writes are grouped and sorted canonically
|
||
|
|
|
||
|
|
- **WHEN** the writer serializes a rules file containing rules of all three tiers plus nominations
|
||
|
|
- **THEN** the output orders rules delete-once-served → temporary → keep with globs sorted within each group, and nominations follows rules with consults before rejected, each list glob-sorted
|
||
|
|
|
||
|
|
#### Scenario: Canonicalization is idempotent
|
||
|
|
|
||
|
|
- **WHEN** the writer serializes a file it previously wrote, unchanged
|
||
|
|
- **THEN** the output is byte-identical
|
||
|
|
|
||
|
|
#### Scenario: A hand-edited file re-canonicalizes on the next write
|
||
|
|
|
||
|
|
- **WHEN** a human appends a rule out of tier order and a later calibrate run persists a new entry
|
||
|
|
- **THEN** the whole file is rewritten in canonical order in that write, with no hook involved in the interim
|