diff --git a/work/0015-recursive-workflow-guards/0004-recursive-integrity-guard/design.md b/work/0015-recursive-workflow-guards/0004-recursive-integrity-guard/design.md index 62ba1bc..f6bb804 100644 --- a/work/0015-recursive-workflow-guards/0004-recursive-integrity-guard/design.md +++ b/work/0015-recursive-workflow-guards/0004-recursive-integrity-guard/design.md @@ -12,7 +12,7 @@ conflicts-with: "tests/README.md, tests/mutation.sh" ## Summary -Add one new fixture-based agreement area, `tests/checks/89-integrity-guard.sh` +Add one new fixture-based agreement area, `tests/checks/91-integrity-guard.sh` (suite token `AC30`), plus its committed fixture root `tests/fixtures/recursive-integrity/`. The area reimplements, read-only, the merge-integrity invariant evaluation as a genuine recursive scan over the fixture @@ -27,13 +27,16 @@ re-asserts none of them. One line is added to `tests/mutation.sh`'s staged-copy list so the opt-in whole-suite runs stay green, and `tests/checks/80-cycle-fixture.sh`'s AC13 whole-set `roadmap.md` assertion is narrowed to exclude the one documented deliberate drift alias so the committed -migration guard stays green with the dual-spelling fixture present. +migration guard stays green with the dual-spelling fixture present. The area is +renamed off the collided `89-` numeric stem to the free `91-` stem so no two +merged areas share a selector prefix; the `AC30` token and sub-area labels are +unchanged (see "Renaming the area off the collided `89-` stem"). ## Approach ### New agreement area -- **File.** `tests/checks/89-integrity-guard.sh`, sourced by the existing +- **File.** `tests/checks/91-integrity-guard.sh`, sourced by the existing `tests/run.sh` glob (no `run.sh` edit), never calling `exit`, Bash 3.2 compatible (no associative arrays, no `mapfile`, no empty-array expansion under `set -u`). It mirrors the structure of `tests/checks/85-conflict-guards.sh` @@ -189,6 +192,43 @@ registries (`tests/README.md`, `tests/mutation.sh`, `manifest.md:126`), which avoids a self-inflicted `DRIFT-FACT` and keeps the declared set semantically exact: the two registries are where sibling plans actually collide. +### Renaming the area off the collided `89-` stem + +The merged default branch carries `tests/checks/89-recursive-status-guards.sh` +(the `0002` readiness/status guard). Two distinct agreement areas sharing the +`89-` prefix would make every prefix selector ambiguous — `tests/README.md` → +"Area selection" defines a prefix matching two or more areas as a setup error — +so this area takes the free stem `91-integrity-guard.sh` (verified against +`origin/main:tests/checks`: no `91-` area exists, and the merged sibling guards +occupy `81-`, `85-`, and `89-`). The rename keeps the suite token `AC30` and every +sub-area label unchanged, so it is invisible to `0005-guard-registration-sweep`'s +mapping and to any mutation job. This resolves the collision the review found +rather than deferring it; the earlier "only an area-selector ambiguity, not a +suite failure" acceptance was wrong, because the merged tree would leave the +documented `FRAMEWORK_TEST_AREAS=89` selector a setup error. + +The rename sweeps **every** textual reference to the old stem — not only the +three the review named. The complete set, each rewritten `89-integrity-guard` → +`91-integrity-guard`: + +- the new check file's own name (`tests/checks/91-integrity-guard.sh`); +- its `FIG_SELF="$FIG_CHECKS_DIR/91-integrity-guard.sh"` resolver; +- its `read-only-contract` self-reference scan + (`grep -rqF '91-integrity-guard' .opencode/`); +- its header comment naming the area; +- the AC13 cross-reference in `tests/checks/80-cycle-fixture.sh` (the deliberate + dual-spelling exception's owning area, `80-cycle-fixture.sh:66`); and +- the six fixture-prose references under `tests/fixtures/recursive-integrity/` + (`items/9201-clean/manifest.md`, `items/9220-renamed-parent/manifest.md`, + `items/9220-renamed-parent/0002-new/manifest.md`, + `items/9220-renamed-parent/0002-new/0001-leaf/spec.md`, + `items/9220-renamed-parent/0003-follower/design.md`, and + `items/9220-renamed-parent/0003-follower/ship.md`). + +`T4` owns the rename and this sweep; its `Verify:` runs the area under the new +selector and asserts `grep -rF '89-integrity-guard' tests/` prints nothing, so a +leftover reference cannot survive green. + ## Alternatives considered - **A prose-only agreement check (pin the recursive authority phrases, no fixture @@ -218,12 +258,21 @@ the two registries are where sibling plans actually collide. a blind spot for the relocated root. Rejected in favor of the scoped narrowing, which keeps the fixture in the canonical tree and the guard still sensitive to any *third* alias. +- **Defer the area rename to `0005-guard-registration-sweep` instead of renaming + it here.** Pros: leaves this item's stated check filename unchanged and touches + one fewer path. Cons: `0005`'s scope (`manifest.md:127`) is only the + `tests/README.md` Checks row/paragraph and the mutation jobs — it does not + include renaming this area's check — so deferral would leave two distinct areas + sharing the `89-` stem in the merged tree and the documented + `FRAMEWORK_TEST_AREAS=89` selector a setup error, i.e. an unresolved collision, + not a handled one. Rejected: the owning plan renames its own area (see + "Renaming the area off the collided `89-` stem"). ## Interfaces and data model -- **New file** `tests/checks/89-integrity-guard.sh` — sourced, not executed; no +- **New file** `tests/checks/91-integrity-guard.sh` — sourced, not executed; no `exit`; read-only; prints `ok`/`FAIL` lines labelled `AC30 `. Public - surface for `0005`: the area name `89-integrity-guard` and the sub-area labels. + surface for `0005`: the area name `91-integrity-guard` and the sub-area labels. - **New fixture root** `tests/fixtures/recursive-integrity/` — root-local `92xx` entity ids; item references resolve inside `items/`, never against live `work/`. @@ -246,7 +295,7 @@ fixture…"). No other area, fixture, selector, or harness behavior changes, so ## Affected areas -- `tests/checks/89-integrity-guard.sh` (new). +- `tests/checks/91-integrity-guard.sh` (new). - `tests/fixtures/recursive-integrity/**` (new). - `tests/checks/80-cycle-fixture.sh` — the AC13 whole-set `roadmap.md` assertion narrowed to exclude the one documented deliberate drift alias (see "Reconciling @@ -272,11 +321,19 @@ fixture…"). No other area, fixture, selector, or harness behavior changes, so stray alias still fails. T1's `Verify:` reintroduces a scratch third alias under `tests/fixtures/` and confirms `80` fails naming `unexpected roadmap.md fixture set`. -- **Filename/prefix collision with a sibling guard authoring a check in - parallel.** — Likelihood medium / impact low / mitigation: the name - `89-integrity-guard.sh` is distinct; the two append-only registries are - declared conflicts and the registration child reconciles; a duplicate `89` - prefix is only an area-selector ambiguity, not a suite failure. +- **Numeric-prefix collision with the merged sibling area.** — Likelihood low / + impact medium / mitigation: the area is renamed off the collided `89-` stem to + the free `91-integrity-guard.sh`, verified free against + `origin/main:tests/checks` (the sibling guards occupy `81-`, `85-`, and `89-`); + no two areas then share a numeric stem, so every prefix selector resolves + uniquely. The `AC30` token and sub-area labels are unchanged, and the two + append-only registries (`tests/README.md`, `tests/mutation.sh`) stay declared + conflicts reconciled by `0005`. +- **A leftover `89-integrity-guard` reference survives the rename.** — + Likelihood low / impact low / mitigation: `T4` sweeps every enumerated site + (see "Renaming the area off the collided `89-` stem") and its `Verify:` asserts + `grep -rF '89-integrity-guard' tests/` prints nothing, so a stale reference + cannot survive green. - **Fixture/analyzer not Bash 3.2 compatible.** — Likelihood low / impact high / mitigation: no associative arrays, no `mapfile`, no empty-array expansion under `set -u`; mirror the shipped `85`/`87` helpers. diff --git a/work/0015-recursive-workflow-guards/0004-recursive-integrity-guard/tasks.md b/work/0015-recursive-workflow-guards/0004-recursive-integrity-guard/tasks.md index c088ffe..3cd3bc4 100644 --- a/work/0015-recursive-workflow-guards/0004-recursive-integrity-guard/tasks.md +++ b/work/0015-recursive-workflow-guards/0004-recursive-integrity-guard/tasks.md @@ -10,12 +10,17 @@ parent: 0015-recursive-workflow-guards # Tasks — Committed recursive merge-integrity and renumber guards Ordered, dependency-aware. One task ≈ one focused commit. The exact check -filename (`tests/checks/89-integrity-guard.sh`), fixture root +filename (`tests/checks/91-integrity-guard.sh`), fixture root (`tests/fixtures/recursive-integrity/`), sub-area names, the old/new renumber constants, and the `tests/checks/80-cycle-fixture.sh` AC13 relaxation are fixed by -`design.md`; no task below re-decides them. +`design.md`; no task below re-decides them. The area is renamed off the collided +`89-` stem to the free `91-` stem (see `design.md` → "Renaming the area off the +collided `89-` stem"); `T4` owns the rename and the full reference sweep, so +`T5`–`T7` and `T9` (whose `Verify:` names the area) are re-opened, while `T1`–`T3` +and `T8` stay checked — their substantive deliverables are unchanged and `T4` +sweeps the incidental area references in `T1`–`T3`'s files. -- [ ] **T1** — Narrow `tests/checks/80-cycle-fixture.sh`'s AC13 whole-set +- [x] **T1** — Narrow `tests/checks/80-cycle-fixture.sh`'s AC13 whole-set `roadmap.md` assertion (`80-cycle-fixture.sh:61-71`): compute the stray-alias set as `find tests/fixtures -name roadmap.md | grep -vxF 'tests/fixtures/recursive-integrity/items/9203-spelling-both/roadmap.md'` and @@ -27,22 +32,22 @@ constants, and the `tests/checks/80-cycle-fixture.sh` AC13 relaxation are fixed a scratch copy of `tests/` (never the repository) add a third `tests/fixtures/scratch-third/roadmap.md` and confirm `80` fails naming `unexpected roadmap.md fixture set`. -- [ ] **T2** — Author the integrity fixture tree `tests/fixtures/recursive-integrity/items/`: `9201-clean` (clean node + `.gitkeep` leaf), `9202-graph-faults` (row `0002-missing` with no directory, `0003-dangling` with `Depends on 0099-ghost`, directory `0004-withdrawn` recorded as a withdrawal under `## Open issues`, directory `0005-unlisted` with no record), `9203-spelling-both` (`manifest.md` + `roadmap.md`), `9204-phase-mismatch` (`manifest.md` with `phase: roadmap`), the top-level prefix pair `9210-dup-a`/`9210-dup-b`, and the depth-3 collision `9211-deep/0001-mid/{0001-alpha,0001-beta}`. Use the six-column `Children` header and `manifest.md` `phase: manifest` exactly as `docs/artifact-conventions.md` defines. [AC2] [AC3] [AC4] [depends: T1] +- [x] **T2** — Author the integrity fixture tree `tests/fixtures/recursive-integrity/items/`: `9201-clean` (clean node + `.gitkeep` leaf), `9202-graph-faults` (row `0002-missing` with no directory, `0003-dangling` with `Depends on 0099-ghost`, directory `0004-withdrawn` recorded as a withdrawal under `## Open issues`, directory `0005-unlisted` with no record), `9203-spelling-both` (`manifest.md` + `roadmap.md`), `9204-phase-mismatch` (`manifest.md` with `phase: roadmap`), the top-level prefix pair `9210-dup-a`/`9210-dup-b`, and the depth-3 collision `9211-deep/0001-mid/{0001-alpha,0001-beta}`. Use the six-column `Children` header and `manifest.md` `phase: manifest` exactly as `docs/artifact-conventions.md` defines. [AC2] [AC3] [AC4] [depends: T1] Verify: `bash tests/run.sh` exits 0 (the `80` relaxation keeps the migration guard green with the dual-spelling fixture present) and the fixture paths exist (`ls tests/fixtures/recursive-integrity/items/9202-graph-faults/` shows the four child dirs; `grep -c '^| 0' tests/fixtures/recursive-integrity/items/9202-graph-faults/manifest.md` shows the rows). -- [ ] **T3** — Author the renumber fixture `9220-renamed-parent/`: `manifest.md` with a `0002-new` row (Local id + Canonical reference `9220-renamed-parent/0002-new`) and a `0003-follower` row with `Depends on: 0002-new`; the intermediate `0002-new/manifest.md`; a descendant `0001-leaf/spec.md` carrying the new `feature:` and `parent:` values; `0003-follower/design.md` with `conflicts-with: 0002-new`; and `0003-follower/ship.md` recording a handoff path naming the new reference. The old local id `0001-old` and old canonical reference `9220-renamed-parent/0001-old` must appear nowhere in the fixture. [AC7] [depends: T2] +- [x] **T3** — Author the renumber fixture `9220-renamed-parent/`: `manifest.md` with a `0002-new` row (Local id + Canonical reference `9220-renamed-parent/0002-new`) and a `0003-follower` row with `Depends on: 0002-new`; the intermediate `0002-new/manifest.md`; a descendant `0001-leaf/spec.md` carrying the new `feature:` and `parent:` values; `0003-follower/design.md` with `conflicts-with: 0002-new`; and `0003-follower/ship.md` recording a handoff path naming the new reference. The old local id `0001-old` and old canonical reference `9220-renamed-parent/0001-old` must appear nowhere in the fixture. [AC7] [depends: T2] Verify: `bash tests/run.sh` exits 0 and `grep -rF '0001-old' tests/fixtures/recursive-integrity/` prints nothing (exit 1). -- [ ] **T4** — Create `tests/checks/89-integrity-guard.sh`: header (naming the areas deferred to), Bash 3.2-compatible helpers, and the recursive analyzer (`fig_manifest`, `fig_children_rows`, `fig_scan_node`, top-level prefix grouping) that emits normalized `CODE|offender[,offender]` findings into the three category blocks `fixture_duplicates`, `fixture_graph`, and `fixture_drift`; compare each against its reviewed expected block, labelled `AC30 per-node-eval`, `AC30 structural-graph`, and `AC30 spelling-drift`; assert the depth-3 collision is attributed to `9211-deep/0001-mid` and that `9202-graph-faults/0004-withdrawn` yields no `UNLISTED-CHILD` fault. [AC2] [AC3] [AC4] [depends: T2] - Verify: `FRAMEWORK_TEST_AREAS=89-integrity-guard bash tests/run.sh` exits 0 and prints the three `AC30` labelled blocks with no `FAIL`. +- [ ] **T4** — Rename the area to `tests/checks/91-integrity-guard.sh` (off the collided `89-` stem) and sweep every reference: `mv tests/checks/89-integrity-guard.sh tests/checks/91-integrity-guard.sh`; set `FIG_SELF="$FIG_CHECKS_DIR/91-integrity-guard.sh"`; change the `.opencode/` self-reference scan literal to `91-integrity-guard`; update the header comment; and rewrite the remaining `89-integrity-guard` references — the `tests/checks/80-cycle-fixture.sh` AC13 cross-reference (`80-cycle-fixture.sh:66`) and the six fixture-prose references under `tests/fixtures/recursive-integrity/` — enumerated in `design.md` → "Renaming the area off the collided `89-` stem". Keep the header (naming the areas deferred to), the Bash 3.2-compatible helpers, and the recursive analyzer (`fig_manifest`, `fig_children_rows`, `fig_scan_node`, top-level prefix grouping) that emits normalized `CODE|offender[,offender]` findings into the three category blocks `fixture_duplicates`, `fixture_graph`, and `fixture_drift`; compare each against its reviewed expected block, labelled `AC30 per-node-eval`, `AC30 structural-graph`, and `AC30 spelling-drift`; assert the depth-3 collision is attributed to `9211-deep/0001-mid` and that `9202-graph-faults/0004-withdrawn` yields no `UNLISTED-CHILD` fault. [AC2] [AC3] [AC4] [depends: T2] + Verify: `FRAMEWORK_TEST_AREAS=91-integrity-guard bash tests/run.sh` exits 0 and prints the three `AC30` labelled blocks with no `FAIL`; `grep -rF '89-integrity-guard' tests/` prints nothing. - [ ] **T5** — Add the `report-only` sub-area: pin the non-fatal / never-truncated / not-auto-repaired / modifies-no-file contract on `docs/workflow.md` → "Merge-integrity guard" and the operational mirror `.opencode/skill/merge-conflict/SKILL.md`; assert the clean `9201-clean` node yields empty findings and does not error; and pin the recursive authority phrases (the invariant set is evaluated at every node; no node's check depends on another's; every node at every depth). Reference the pinned surfaces by path; restate no sibling literal. [AC5] [AC2] [depends: T4] - Verify: `FRAMEWORK_TEST_AREAS=89-integrity-guard bash tests/run.sh` exits 0 with the `AC30 report-only` lines present and passing. + Verify: `FRAMEWORK_TEST_AREAS=91-integrity-guard bash tests/run.sh` exits 0 with the `AC30 report-only` lines present and passing. - [ ] **T6** — Add the `renumber-sweep` sub-area: assert the fixture's new directory exists and contains the new reference in each enumerated rewrite position (directory, `feature:`, `parent:`, owning manifest `Local id`/`Canonical reference`, intermediate manifest `Canonical reference`, `Depends on`, `conflicts-with`, ship path), and assert by a recursive literal scan that the old local id and old canonical reference appear nowhere under `tests/fixtures/recursive-integrity/`; pin that `docs/artifact-conventions.md` and `.opencode/skill/merge-conflict/SKILL.md` name the intermediate manifest on the changed path, referencing `tests/checks/88-renumber-sweep.sh` by name and restating none of its literals. [AC7] [AC8] [AC9] [depends: T3] [depends: T4] - Verify: `FRAMEWORK_TEST_AREAS=89-integrity-guard bash tests/run.sh` exits 0 with the `AC30 renumber-sweep` lines passing. + Verify: `FRAMEWORK_TEST_AREAS=91-integrity-guard bash tests/run.sh` exits 0 with the `AC30 renumber-sweep` lines passing. - [ ] **T7** — Add the structural assertions: `fixture-existence` fails (never skips) when a required fixture file is missing; `read-only-contract` asserts the fixture references no live `work/` path and adds no committed executable checker; `mutation-staging` asserts `tests/mutation.sh`'s staged-copy list names `tests/fixtures/recursive-integrity`; `no-duplication` asserts the header references `10-readiness`, `20-lifecycle`, `80-cycle-fixture`, `85-conflict-guards`, `87-backtrack-guards`, `88-renumber-sweep`, `90-packaging`, `96-signature-sweep`, and the structure guard `0001-recursive-structure-guard` by name and re-asserts none of their owned literals. Every assertion carries the `AC30` token. [AC1] [AC6] [AC10] [AC11] [depends: T4] - Verify: `FRAMEWORK_TEST_AREAS=89-integrity-guard bash tests/run.sh` exits 0 with the `fixture-existence`, `read-only-contract`, `mutation-staging`, and `no-duplication` lines present and passing. -- [ ] **T8** — Add the new fixture root to `tests/mutation.sh`'s `stage_copy()` as one additive `cp -R "$REPO_ROOT/tests/fixtures/recursive-integrity" "$copy/tests/fixtures/recursive-integrity"` line beside the existing fixture copies. Do not add mutation jobs or change `JOB_COUNT`; those remain `0005-guard-registration-sweep`'s. [AC10] [depends: T2] + Verify: `FRAMEWORK_TEST_AREAS=91-integrity-guard bash tests/run.sh` exits 0 with the `fixture-existence`, `read-only-contract`, `mutation-staging`, and `no-duplication` lines present and passing. +- [x] **T8** — Add the new fixture root to `tests/mutation.sh`'s `stage_copy()` as one additive `cp -R "$REPO_ROOT/tests/fixtures/recursive-integrity" "$copy/tests/fixtures/recursive-integrity"` line beside the existing fixture copies. Do not add mutation jobs or change `JOB_COUNT`; those remain `0005-guard-registration-sweep`'s. [AC10] [depends: T2] Verify: `bash tests/mutation.sh` exits 0 (the whole-suite clean copy passes with the fixture staged) and `grep -F 'recursive-integrity' tests/mutation.sh` matches the new line. - [ ] **T9** — Run the full suite and prove the renumber assertion is sensitive and the scope is clean. [AC8] [AC11] [AC12] [depends: T5] [depends: T6] [depends: T7] [depends: T8] - Verify: `bash tests/run.sh` exits 0 (all areas green, including `80-cycle-fixture`); in a scratch copy of `tests/` (never the repository) reintroduce `9220-renamed-parent/0001-old` into one fixture file and run `FRAMEWORK_TEST_AREAS=89-integrity-guard bash tests/run.sh ` — it must exit non-zero and print a line containing `AC30 renumber-sweep`; and `git diff --name-only` lists only files under `tests/` plus this work item. + Verify: `bash tests/run.sh` exits 0 (all areas green, including `80-cycle-fixture`); in a scratch copy of `tests/` (never the repository) reintroduce `9220-renamed-parent/0001-old` into one fixture file and run `FRAMEWORK_TEST_AREAS=91-integrity-guard bash tests/run.sh ` — it must exit non-zero and print a line containing `AC30 renumber-sweep`; and `git diff --name-only` lists only files under `tests/` plus this work item.