Skip to content

fix(hooks): widen PostToolUse gate matchers to MultiEdit + @file-content-gate/@matcher-parity preventer - #1054

Merged
artyhoo merged 13 commits into
stagingfrom
feat/matcher-widening-multiedit
Jul 21, 2026
Merged

fix(hooks): widen PostToolUse gate matchers to MultiEdit + @file-content-gate/@matcher-parity preventer#1054
artyhoo merged 13 commits into
stagingfrom
feat/matcher-widening-multiedit

Conversation

@artyhoo

@artyhoo artyhoo commented Jul 21, 2026

Copy link
Copy Markdown
Owner

What

Widens 6 PostToolUse gate matchers Edit|WriteEdit|Write|MultiEdit (a MultiEdit edit was silently bypassing them), then makes that class of drift executable-preventable at two channels:

  • Layer 1 (edit-time, .claude/hooks/check-hook-marker.sh): two new self-declared invariants.
    • @file-content-gate marker → the hook's registered matcher must include Edit, Write and MultiEdit (for the 3 path-only content-gates whose only tool-filter is the matcher).
    • @matcher-parity → any hook with an internal case "$TOOL" in <tools>) must have its matcher ⊇ that case-arm set (covers the 3 case-TOOL gates that carry no marker, incl. check-hook-marker itself). Self-calibrating: inject-memory-codification (A5 Write-only) stays green with no hardcoded MultiEdit demand.
  • Layer 2 (CI backstop, packages/core/hooks/check-hook-marker.test.ts): a population sweep that runs the real edit-time gate against every tracked hook against the live settings.json — catches a matcher narrowed directly in the SSOT/settings.json even when no hook .sh is edited (the vector Layer 1 can't see). Placed in the CC-config bucket, NOT tests/agnosticism/MultiEdit is CC-only (inert on other harnesses), so the check must not contaminate the harness-agnostic channel-coverage probe's PORTABLE verdict.

Full design: 2026-07-17-matcher-widening-design.md (4 review rounds). Session forks/findings: 2026-07-17-matcher-widening-decisions.md (D1–D5, F1–F2).

§1.7 Forward-check applied

Class of this change = a new edit-time discipline gate enforcing matcher-completeness for file-edit hooks (recursive-self-application target: the gate is itself a .claude/hooks/*.sh subject to its own invariants).

  • Provenance, not invention. The two-rule Layer-1 design is the original intent of 2026-07-17-matcher-widening-design.md:65 («for the case-TOOL hooks the parity rule applies, matcher ⊇ case-arm set»); the kickoff's Layer-1-marker-only deferral was under-specified and is corrected here (decisions D4). The bucket relocation off the agnosticism probe is decisions D3.
  • Falsifier is executed, not asserted. packages/core/hooks/check-hook-marker.test.ts ships RED fixtures that must exit 1: the @file-content-gate PAIRED-NEGATIVE (check-hook-marker.test.ts:249), the @matcher-parity PARITY-NEGATIVE (check-hook-marker.test.ts:297), and the Layer-2 backstop RED fixtures (check-hook-marker.test.ts:433). A vacuous gate would let them exit 0 and the suite would fail. Suite = 18/18 edit-time + a real-tree population arm asserting all live hooks pass.
  • Self-application. check-hook-marker.sh is itself a case-TOOL hook (case "$TOOL" in Edit | Write | MultiEdit) at .claude/hooks/check-hook-marker.sh:52); the parity rule applies to it and it passes (matcher EWM ⊇ case-arm EWM). Its extraction strips comment lines (check-hook-marker.sh:112) so its own @matcher-parity doc prose is not mis-read as a code arm — a comment-immunity test arm proves it (check-hook-marker.test.ts:332).

§1.7 Backward-check applied

Class = «a PostToolUse hook that gates file edits must have a matcher covering every edit-tool it handles». Swept every sibling surface where the class must hold:

  • All 6 PostToolUse gate hooks — SWEPT-CLEAN. 5 with a case "$TOOL" arm verified matcher ⊇ case-arm: check-hook-marker, check-kickoff-traps (.claude/hooks/check-kickoff-traps.sh:35), check-worker-dispatch-channel (.claude/hooks/check-worker-dispatch-channel.sh:51), inject-matching-rule (EWM/EWM), inject-memory-codification (Write/Write). 3 path-only carry @file-content-gate: validate-prompt, check-doc-authority, check-doc-authority-header.
  • A5 Write-only invariant (inject-memory-codification) — SWEPT-CLEAN: parity {Write} ⊆ {Write} green; the rule does NOT force MultiEdit onto it, so A5 is preserved, not superseded.
  • Three delivery channels (SSOT .ai-factory/harness-model.json, CC .claude/settings.json, plugin plugin/hooks/hooks.json) — SWEPT-CLEAN: narrow=0 / wide=6 in each; plugin twins (warn-subagent-report-zcode etc.) preserved through the rebase conflict resolution (decisions D5).
  • Shipped-only consumer hook (check-doc-authority-header, absent from framework settings.json) — SWEPT: tolerated by Layer 1 (no framework matcher to read), enforced by tests/install-sh/gh-934-ship-doc-authority-hook.test.sh:56 + Layer 2.
  • Supersedes: no existing rule or principle is removed. The kickoff Task-C channel-coverage.sh plan is replaced (agnosticism correctness, D3); the channel-coverage probe itself is untouched and still owns @dual-pair/@cc-only-rationale sweeping.

Intentional-skip list (frozen / out-of-scope files NOT edited)

tests/fixtures/plugin-broken-manifest/plugin/hooks/hooks.json (intentional broken fixture) · packages/core/hooks/apply-doc-fixes.test.ts:32,36 + scripts/apply-doc-fixes.sh:61,66 (frozen historical prose) · packages/core/hooks/validate-prompt.test.ts:4 (doc comment) · setup.d/lib.sh:871 (doc example) · scripts/setup-cc-adoptions.sh:62 (warning prose) · .claude/orchestrator-prompts/launch-preannounce-track/s5-probes/probe-zcode-hooks.sh:52,59 (frozen probe data) · validate-prompt.sh:52 pre-existing SC2295 (not CI-scope, not this PR's concern).

Verification

  • cd packages/core && npx vitest run principles/ hooks/1147/1147 green (rebased onto staging trunk-restore 62d90304d; the 8 formerly-inherited reds were staging's, verified on a pristine origin/staging worktree — this PR adds zero red).
  • byte-identical, render --check, shellcheck (setup.d + install.sh), lib-helpers/meta-all-wired/layer-units/refresh-covers-full-delivery/gh-934-ship-* → all green.

Prior-art: skipped — extends the existing check-hook-marker.sh gate + adds test arms; no new dependency, no new code module ≥50/80 LOC, no new subdirectory.

test22345 added 11 commits July 21, 2026 12:40
…(3-round review)

Captures the design after 3 rounds of dual top-down + bottom-up review.
Scope: widen 6 PostToolUse gate hooks Edit|Write -> Edit|Write|MultiEdit
(incl. check-doc-authority-header from merged #1009, discovered when the
worktree base was refreshed off stale de7fc9d -> 4a9b508). Single
atomic PR: the fix + a ~15-LOC grep probe (parity for case-TOOL hooks,
@file-content-gate marker for path-only hooks) + a live RED fixture.
Full principle No.33 deferred to incidence N>=2.
…sed edit-site

Round-4 review (fresh base 4a9b508) found:
- R1: reuse-first path — extend check-hook-marker.sh (edit-time) +
  channel-coverage.sh (CI) instead of a new standalone probe.
  Lands preventer at earliest reachable channel (rule-enforcement-channel-
  selection §4), avoids consumer dead-token (R2).
- R5: missed edit-site tests/install-sh/gh-934-ship-doc-authority-hook.test.sh:56
  (hardcoded Edit|Write equality from #1009) — added to §2 inventory.
- R4 nit: citation BFR §5 -> dual-implementation-discipline §9 (probe != principle slot).

Decision (option B): DUAL-LAYER gate — edit-time catches early, CI catches
reliably (the original bug was itself an edit-time-hook bypass; single-layer
trust in the same mechanism class is not robust).
…e|MultiEdit

MultiEdit emits a SEPARATE PostToolUse event (tool_name:MultiEdit) that
an Edit|Write matcher does NOT catch (falsifier: docs/2026-07-17-matcher-
widening-design.md §1). A MultiEdit that violates a gate's rule slipped
past silently. The real bug shape is body<->registration drift: 3 of 6
hooks already had 'case "$TOOL" in Edit|Write|MultiEdit)' in their body
but were registered Edit|Write.

SSOT-first: harness-model.json (5) -> render --write -> setup.d/10-skills.sh
(206,246) + install.sh (469,493) (inject-matching-rule + check-doc-authority-
header shipped) + gh-934 tests (session-ux 56/57/136, doc-authority-hook 56)
+ re-capture 4 greenfield npm baselines (brownfield stable, 3 python stable).

inject-memory-codification stays Write-only (A5 — creation moment).
check-doc-authority-header from merged #1009 (was invisible until the
worktree base was refreshed off stale de7fc9d -> 4a9b508).

Gates GREEN: render --check, byte-identical 11/11, refresh-covers-full-
delivery, both gh-934 tests (11/11 + 14/14), shellcheck clean.

STEP 2 (dual-layer preventer: extend check-hook-marker.sh + channel-
coverage.sh + RED fixtures) follows in this same PR.
Self-contained SSOT for an unattended resume session. Includes the
STEP 0 hypothesis-verification block (verify state on disk BEFORE work),
the resume point (STEP 1 done at f707d68d1; STEP 2 Layer 1 in-progress
uncommitted), the strict task order A-G, the autonomy/fork policy for
an operator-away run, critical nuances (shipped-only tolerance, A5,
frozen intentional-skip list), and the policy reminders (Decision B
dual-layer APPROVED, do not re-litigate).
STEP 1 widened 5 PostToolUse gate matchers Edit|Write -> Edit|Write|MultiEdit
in the CC channel (.claude/settings.json) + SSOT (.ai-factory/harness-model.json).
At STEP 1's base the plugin channel (plugin/hooks/hooks.json, added by staging
zcode-parity #1030/#1036/#1046) did not exist; post-rebase it carried the same 5
gate matchers still narrow. Widened them surgically (exact-string sed, 5 lines)
rather than via `render-harness-config.mjs --write`, which would clobber the
hand-maintained ZCode-parity twins (inject-project-digest, inject-output-language,
warn-subagent-report-zcode) that emitPlugin does not reproduce from the SSOT.

Diff = 5 matcher lines only; twins intact (5 refs); valid JSON. See
docs/superpowers/specs/2026-07-17-matcher-widening-decisions.md D2 + finding F1
(pre-existing, ungated staging SSOT/render drift — out of scope, surfaced only).

Prior-art: skipped — rendered-output matcher widening + session decisions doc, no new capability (no dependency, no new code module).
…dening preventer)

Prevents the body<->registration matcher-drift class that STEP 1 fixed from
recurring. A hook that validates a file's *content* (path-only — no internal
tool_name filter) MUST be registered with matcher Edit|Write|MultiEdit, else a
MultiEdit edit that violates the rule slips past silently (the registration matcher
is the gate's ONLY tool-filter). 3 of 6 hooks had drifted this way.

Mechanism (edit-time, earliest reachable channel per README goal):
- New `# @file-content-gate:` marker declared on the 4 path-only content-gate hooks
  (validate-prompt, check-doc-authority, check-doc-authority-header, and
  check-hook-marker documents its own extension).
- check-hook-marker.sh extended: when a hook declares @file-content-gate AND is
  registered in the framework-self .claude/settings.json, assert its matcher
  contains Edit, Write AND MultiEdit; else _adv_violation (exit 1 on CC).
- Tolerance: a shipped-only hook (no framework-self settings.json entry, e.g.
  check-doc-authority-header) is SKIPPED — the gate has no framework-side matcher to
  read for it; its matcher is enforced by the install-sh firing tests + Layer 2.

Tests: 3 new arms in check-hook-marker.test.ts (paired-negative Edit|Write -> exit 1;
paired-positive Edit|Write|MultiEdit -> exit 0; shipped-only absent-from-settings ->
exit 0). Full suite 11/11 green on the rebased base (staging #1030 runHook object
shape + ZCODE arm reconciled).

CI-backstop (population sweep) follows in Layer 2. Design: 4-round-reviewed spec
docs/superpowers/specs/2026-07-17-matcher-widening-design.md.

Prior-art: skipped — edit-time gate extension to an existing hook + marker comments on existing hooks, no new capability (no dependency, no new code module >=50/80 LOC, no new subdir).
…stop

Closes the case-TOOL coverage gap (cold-review MAJOR) and places the CI backstop in
the CC-config bucket, NOT the harness-agnostic channel-coverage probe (operator-flagged).

Layer 1 (edit-time, check-hook-marker.sh):
- New @matcher-parity invariant: a hook with `case "$TOOL" in <tools>)` must have its
  registered matcher ⊇ that case-arm set. Covers the 3 case-TOOL gates that carry no
  @file-content-gate marker (check-kickoff-traps, check-worker-dispatch-channel,
  check-hook-marker itself). Uses each hook's existing `case` author-declaration — no new
  marker, no allowlist. Self-calibrating: inject-memory-codification (A5, Write-only) stays
  GREEN (matcher Write ⊇ case-arm Write) with no hardcoded MultiEdit demand.
- Refactored the settings.json matcher lookup into _reg_matcher(), anchored on the closing
  `"` so check-doc-authority no longer substring-matches check-doc-authority-header.

Layer 2 (CI backstop, packages/core/hooks/check-hook-marker.test.ts):
- A vitest describe block runs the REAL edit-time gate against EVERY tracked hook against the
  live settings.json — catches a matcher narrowed directly in harness-model.json/settings.json
  even when no hook .sh is edited (the vector Layer 1 cannot see). Reuses the gate verbatim
  (no parallel population reader). 2 RED fixtures prove non-vacuousness; ≥5-hook sentinel.
- Bucket: CC-config (hooks), NOT tests/agnosticism/ — the MultiEdit requirement is CC-specific
  (inert on other harnesses), so it must not contaminate the harness-agnostic PORTABLE verdict.

Supersedes the kickoff Task-C plan (channel-coverage.sh extension + tests/fixtures/matcher-drift/).
Suite 17/17 green. See decisions doc D3 (bucket) + D4 (parity rule).

Prior-art: skipped — edit-time gate extension + test arms for the existing hook capability, no new capability (no dependency, no new code module >=50/80 LOC, no new subdir).
… staging inject-* staleness)

The @file-content-gate marker added to the shipped check-doc-authority-header.sh shifts its
install fingerprint. Re-capturing (snapshot.sh, SNAPSHOT_MODE=capture) also corrects three
baselines that origin/staging left STALE — inject-matching-rule / inject-output-language /
inject-project-digest — hooks staging changed via zcode-parity but never re-captured (my
source for them is byte-identical to staging's; only staging's recorded baseline was old).

byte-identical is an all-or-nothing gate → a full re-capture is mandatory to green it; there
is no way to refresh only the marker-touched hook. Captured hashes match the live source
(verified via shasum). See decisions doc F2. Framework-internal hooks (check-hook-marker,
check-doc-authority, validate-prompt) ship to 0 consumers → untouched here.

Prior-art: skipped — regenerated install snapshot baselines after a shipped-hook marker edit, no new capability.
…iew MINOR)

The case-arm extraction matched `case "$TOOL" in …` on ANY line, including comments — and
this hook's own @matcher-parity doc block now contains that pattern in prose. Current
population was safe only because the real code arm precedes the comment mentions and head -1
takes the first; one reorder would mis-extract the prose arm → false parity verdict. Strip
comment lines (^[[:space:]]*#) before extraction, mirroring the ^#-anchored marker check.

New test arm PARITY comment-immunity: a prose `case "$TOOL" in Read)` above the real
`case "$TOOL" in Write)` with matcher Write → exit 0 (reads the real {Write}, not {Read}).
Suite 18/18 green.

Prior-art: skipped — hardening the edit-time gate's parser + one test arm, no new capability.
…oEmit)

vitest passed but tsc --noEmit (all-workspaces CI job) rejected `delete fullEnv.ZCODE_PROJECT_DIR`
— the {...process.env, CLAUDE_PROJECT_DIR} literal lost ProcessEnv's index signature. Explicit
annotation restores it, mirroring the sibling runHook helper.

Prior-art: skipped — type annotation on a test helper, no new capability.
@artyhoo
artyhoo merged commit e86395d into staging Jul 21, 2026
37 checks passed
artyhoo added a commit that referenced this pull request Aug 17, 2026
…sts nobody ran (#1441)

## Summary

`tests/install-sh/meta-all-wired.test.sh` has gated exactly ONE shell-test directory since it shipped. Nothing gated the other six, so the `#armed-but-not-fired` false-green that principle 38 measured for vitest was live for shell tests too. Enumerated over `git ls-files`: **130 tracked `*.test.sh`, 12 invoked by no workflow step** — `tests/plugin/` (9 of its 10), `tests/aif-doctor/` (2), `scripts/probe-channels.test.sh` (1). Principle 41 closes the population; eleven of the twelve are wired in the same commit.

**The finding is worth more than the gate.** One of the twelve is RED, and had been silently so across five hook revisions.

## The red test — traced, not guessed

`tests/plugin/end-of-turn-reminder-zcode.test.sh` case (1) asserts the Stop hook suppresses a repeated question through its ZCode-rollout arm. The hook answers `decision: block` instead.

`ZCODE_ROLLOUT_DIR` appears in that test file and **nowhere else in the tree**. The arm shipped in #1044 into the *twin* `plugin/hooks/end-of-turn-reminder`, never into the source `.claude/hooks/end-of-turn-reminder.sh`. That twin now carries no `@plugin-transform` marker and is byte-identical to its source — i.e. identity-generated — so regeneration overwrote the arm. The test that would have caught it ran at no channel, so it could not say so, through #1054, #1137, #1142, #1349 and #1409.

That is the argument for this gate in one sentence: a directory nobody wired hid a real capability loss for five revisions of the file it guarded.

**It is allowlisted — not wired, not deleted.** Wiring it would paint CI red on a defect this PR did not cause; deleting it would destroy the only evidence. «Restore the arm or retire the test» is a behaviour decision this gate does not get to make. The `COVERAGE_ALLOWLIST` entry carries the full diagnosis and instructs removal in the commit that settles it. **This needs a decision from you** — see Parked questions.

## Changes

- **`packages/core/principles/41-shell-test-ci-coverage.test.ts`** (new) — population = git-tracked `*.test.sh`; registry = `.github/workflows/` step text. Seven arms reusing 38's structure: real-tree, non-vacuity, two seeded negatives (step removal · population addition), fail-closed on an unreadable workflow dir, comment-only mentions, allowlist hygiene.
- **`.github/workflows/audit-self.yml`** — 11 previously-unrun tests wired as steps. The `tests/aif-doctor/` pair is safe on a clean runner by construction, not by hope: both stub `docker`/`gh` explicitly and carry an in-file «ENVIRONMENT-INDEPENDENCE PROOF (must pass with real docker on PATH)» — verified locally at 9/9 and 5/5.
- **`scripts/run-local-ci-sweep.sh` + `run-local-ci-sweep-coverage.test.sh`** — `tests/plugin/` and `tests/aif-doctor/` registered as battery families, covered by one row **derived from the workflow** (the `script-selftests` pattern) rather than a `tests/plugin/*` loop. This is the load-bearing choice: a derived row cannot pick up a test wired to no CI step, so the known-red test stays out of the local sweep. `probe-channels` needed nothing — the derived `scripts/*.test.sh` family already covered it. My previous literal `plugin-twin-tests` row is superseded by this one.
- **`docs/meta-factory/prior-art-evaluations.md`** — `Last reviewed` on #248 bumped to 2026-08-17 in the same commit, per the PR-template requirement for a matched entry.

## Prior-art consult

- [x] `Prior-art: prior-art-evaluations.md#248` (**ADAPT**). #248 states our problem class verbatim — «which files does the CI *invocation list* never select?», a completeness claim over an external runner's hand-written argv against a declared population. Its DeepWiki + context7 + WebSearch pass established that nothing upstream reconciles the two, and that finding transfers unchanged.
- [x] No new SSOT entry: the matched entry covers this class, and this commit only widens the population (vitest `include` → every tracked `*.test.sh`) while reusing in-repo arm structure from `38-vitest-include-ci-coverage.test.ts` and `meta-all-wired.test.sh`. No new engine, no new dependency, no new channel.
- [x] `Last reviewed` updated on the matched entry in the same commit (`git log -p` on the SSOT confirms the touch).

## Test plan

- [x] Principle 41 — **7/7**, with both seeded negatives confirmed firing (not merely present): deleting a real wiring step reports that test as unwired; a synthetic population member is reported.
- [x] Full principles — **44 files / 419 passed, 1 skipped** under `--no-file-parallelism`, which dodges the pre-existing inter-file race diagnosed and controlled in #1437 (clean `origin/staging` reproduces that race; it is not from this branch).
- [x] All 11 newly-wired tests green **under bash**. Method note worth recording: an earlier check reported RED because I ran the `for t in $ts` loop under zsh, which does no word-splitting — the sweep row is bash by shebang and green there. The defect was in my probe, not the row.
- [x] Sweep coverage metatest **9 pass / 0 fail**; the derived list contains exactly the 11 wired tests and **excludes** the known-red one.
- [x] `shellcheck` clean on both edited scripts — the single `SC2086` it reports is pre-existing (`SWEEP_DIFF_OVERRIDE`, 2 hits on untouched `staging`), deliberately not fixed here.
- [x] YAML parses; `typecheck` and `prettier` clean; principle 08 (SSOT citation integrity) 9/9 after the `Last reviewed` edit.
- [x] **Comment-stripping measured, not assumed.** 130 tracked tests are named in the raw workflow text but only 129 inside real steps — because audit-self.yml mentions the allowlisted red test in a `#` comment. Counting comments would mark it «wired» and the derived sweep row would run a knowingly-red test locally. Arm (f) pins this against the real file rather than a synthetic string, and I corrected the docblock that had claimed the two readings agree — they no longer do, precisely because of a comment I added in this PR.

## Provenance

n/a — non-stage follow-up to observation (2) parked in #1437; no kickoff, no aif substrate, in-session.

## Review findings

n/a — non-stage PR. Own cold read caught two defects in my own work before commit: a dead `workflowRegistry.length >= 0` condition in arm (f), replaced with the real comment-case assertion; and a docblock that had gone false the moment I added the comment it described.

## Fidelity verdict

FIDELITY: skipped — non-stage infrastructure PR, no kickoff or stage acceptance contour applies to it.

## Parked questions

**(1) Needs your decision — the ZCode rollout arm.** Two options, and I am deliberately not picking: **restore** the arm (it was real capability — multi-turn repeat-question suppression via the ZCode rollout — and would have to land in `.claude/hooks/end-of-turn-reminder.sh`, the source, so regeneration cannot eat it again), or **retire** the test (if the ZCode multi-turn channel is no longer wanted). Either way the `COVERAGE_ALLOWLIST` entry in principle 41 must be removed in the same commit; the gate then holds the whole population with no exemptions.

**(2) Observation, not fixed here:** this is the second capability lost to twin regeneration in two PRs (#1437 was the agents twin, this is the ZCode arm). The pattern is «logic lives only in `plugin/`, source has none, twin becomes identity-generated, logic vanishes». A gate could plausibly catch it — e.g. asserting a twin's declared `@plugin-transform` mode accounts for every line the source lacks — but I have one measured shape, not three, so I am recording it rather than designing on a sample of one.

**(3) Observation:** `meta-all-wired.test.sh` is now a strict subset of principle 41. Retiring another owner's gate is a separate decision; I left it in place.

## §1.7 Self-discipline check (REQUIRED if PR touches discipline-bearing files)

### §1.7 Forward-check applied

**`.claude/rules/attention-is-not-a-mechanism.md:29`** is the rule this PR enforces: a test invoked by no channel is `#warning-nobody-reads` in its strongest form — its failure mode is that nobody ever looked, and the ZCode defect proves the cost concretely. §1 clause (a) is satisfied by a deterministic gate at the earliest reachable channel. **`.claude/rules/rule-enforcement-channel-selection.md:45`** — «Detectable? Mechanically yes → design the narrowest gate on the earliest reachable channel» (§3 step 1); breadth is repo-wide because the population is, and per that rule's own channel table (`:59`, principle test = gate, repo-wide, zero standing cost) `principles/` is the channel that fires at both pre-push and CI. **`README.md#why-this-exists`** invariant 4 is served rather than claimed: the gate runs at pre-push first, CI as backstop. **`.claude/rules/no-paid-llm-in-ci.md`** — file reads plus `git ls-files`; zero API-billed calls. **`.claude/rules/build-first-reuse-default.md:57`** — ADAPT per #248, and cheap by that section's own cost gate: no dependency, no standing infra. **`.claude/rules/ci-tool-pinning.md`** — the 11 new steps install nothing, so §1 Rule A does not fire.

### §1.7 Backward-check applied

Class of this change = **populations of test artifacts reconciled against an invocation registry**. Enumerated by grep over `packages/core/principles/*.ts` and `tests/**`, not from the diff — four members, and three are surfaces this PR does not touch. `38-vitest-include-ci-coverage.test.ts` — SWEPT-CLEAN, the vitest half, untouched and re-run green. `tests/install-sh/meta-all-wired.test.sh:22-43` — SWEPT-CLEAN, still passes; now a strict subset of principle 41, recorded as parked question (3) rather than retired. `36-ci-needs-completeness.test.ts` — SWEPT-CLEAN, different axis (jobs ⊆ `ci-success.needs`), re-run green after 11 steps were added to an existing job. `scripts/run-local-ci-sweep-coverage.test.sh` — the *reverse* direction (a CI command the sweep never runs); GAP-FOUND by construction, since adding 11 CI steps would have made it red, and closed here with two family registrations. Adjacent surface checked and excluded: `27-prepush-copylist-complete.test.ts` reconciles the install copy-list, a different population with its own registry. No rule superseded, no owner's gate retired.

---

Prior-art: prior-art-evaluations.md#248 (verdict ADAPT — #248 states this problem class verbatim: «which files does the CI *invocation list* never select?», a completeness claim over an external runner's hand-written argv against a declared population. Its DeepWiki + context7 + WebSearch pass established that nothing upstream reconciles the two, and that finding transfers unchanged. This commit widens the population from vitest `include` to every git-tracked `*.test.sh` and reuses the in-repo arm structure of 38-vitest-include-ci-coverage.test.ts plus tests/install-sh/meta-all-wired.test.sh. Last reviewed bumped to 2026-08-17 on the matched entry in the same commit. No new engine, no new dependency, no new channel.)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants