Origin: Security review of PR #482, 2026-08-20 (finding 5, "hardening idea"). Deliberately deferred: the heuristic is correct against the tree as it stands today, and replacing it is a change to the checking mechanism rather than to any rule it checks.
Problem
Scripts/tests/test_deliver_selection_prose.py's ReflexiveSetTests._globs() identifies each file's canonical reflexive glob list as the largest set of globs found in any single blank-line paragraph mentioning "reflexive".
That heuristic exists because the obvious approach — union every match in every paragraph mentioning "reflexive" — is provably blind. All three files carrying the set (.claude/skills/deliver/SKILL.md, references/next-mode.md, references/worktree-lifecycle.md) also carry narrative paragraphs describing the 2026-08-20 drift, and each of those names .claude/workflows/** in passing. Under a union, deleting that glob from a canonical list leaves the narrative supplying it, and the test stays green. PR #482 fixed exactly that, and had to split one paragraph in worktree-lifecycle.md where the canonical list and its narrative shared a block.
"Largest paragraph wins" is a proxy for "the canonical one", and it holds only while no narrative paragraph grows to name as many globs as the canonical list. Nothing enforces that.
Failure scenario
Someone documents the drift more fully — a paragraph that walks through all four globs and explains which one was missing, which is a natural thing to write in a file whose whole subject is this class of bug. That paragraph now ties or exceeds the canonical list's count.
From then on, _globs() may return the narrative's set for that file. A glob deleted from the canonical list is masked, test_all_three_copies_list_the_same_globs and test_the_workflows_glob_is_present_everywhere both stay green, and the reflexive set silently loses .claude/workflows/** — the glob whose absence lets a rewrite of deliver-panel.js, the script defining the panel that authorises unattended merges, be auto-merged unread.
The failure is silent and the test reports success, which is the False green family (knowledge/gotchas.md).
Fix sketch
Mark the canonical block explicitly instead of inferring it. An HTML comment sentinel is invisible in rendered Markdown and exact to parse:
<!-- reflexive-set:start -->
… `.claude/skills/**`, `.claude/agents/**`, `.claude/workflows/**` or `.github/CODE_REVIEW.md` …
<!-- reflexive-set:end -->
_globs() then extracts only what lies between the sentinels, and fails loudly when a file has no sentinel pair — a missing marker must not degrade to "no globs found", which would compare equal to another empty set. Precedent for a machine-read marker in prose: the <!-- run-list: … --> line, whose grammar is owned by Scripts/build_run_list.py and asserted by test_build_run_list.py.
Worth doing at the same time: the same "largest paragraph" reasoning is not used anywhere else in that file, so this is a single-function change plus three sentinel pairs.
Related: PR #482, ADR-0028, knowledge/gotchas.md → A rule written in two files drifts and A test that asserts on prose can be blind.
Breaking class: none (test-only)
Origin: Security review of PR #482, 2026-08-20 (finding 5, "hardening idea"). Deliberately deferred: the heuristic is correct against the tree as it stands today, and replacing it is a change to the checking mechanism rather than to any rule it checks.
Problem
Scripts/tests/test_deliver_selection_prose.py'sReflexiveSetTests._globs()identifies each file's canonical reflexive glob list as the largest set of globs found in any single blank-line paragraph mentioning "reflexive".That heuristic exists because the obvious approach — union every match in every paragraph mentioning "reflexive" — is provably blind. All three files carrying the set (
.claude/skills/deliver/SKILL.md,references/next-mode.md,references/worktree-lifecycle.md) also carry narrative paragraphs describing the 2026-08-20 drift, and each of those names.claude/workflows/**in passing. Under a union, deleting that glob from a canonical list leaves the narrative supplying it, and the test stays green. PR #482 fixed exactly that, and had to split one paragraph inworktree-lifecycle.mdwhere the canonical list and its narrative shared a block."Largest paragraph wins" is a proxy for "the canonical one", and it holds only while no narrative paragraph grows to name as many globs as the canonical list. Nothing enforces that.
Failure scenario
Someone documents the drift more fully — a paragraph that walks through all four globs and explains which one was missing, which is a natural thing to write in a file whose whole subject is this class of bug. That paragraph now ties or exceeds the canonical list's count.
From then on,
_globs()may return the narrative's set for that file. A glob deleted from the canonical list is masked,test_all_three_copies_list_the_same_globsandtest_the_workflows_glob_is_present_everywhereboth stay green, and the reflexive set silently loses.claude/workflows/**— the glob whose absence lets a rewrite ofdeliver-panel.js, the script defining the panel that authorises unattended merges, be auto-merged unread.The failure is silent and the test reports success, which is the False green family (
knowledge/gotchas.md).Fix sketch
Mark the canonical block explicitly instead of inferring it. An HTML comment sentinel is invisible in rendered Markdown and exact to parse:
_globs()then extracts only what lies between the sentinels, and fails loudly when a file has no sentinel pair — a missing marker must not degrade to "no globs found", which would compare equal to another empty set. Precedent for a machine-read marker in prose: the<!-- run-list: … -->line, whose grammar is owned byScripts/build_run_list.pyand asserted bytest_build_run_list.py.Worth doing at the same time: the same "largest paragraph" reasoning is not used anywhere else in that file, so this is a single-function change plus three sentinel pairs.
Related: PR #482, ADR-0028,
knowledge/gotchas.md→ A rule written in two files drifts and A test that asserts on prose can be blind.Breaking class: none (test-only)