Skip to content

docs: apply the unapplied extract-ssot deduplication lane, and close its declared recall gap - #3460

Merged
claude[bot] merged 6 commits into
mainfrom
claude/docs-hygiene-ssot-extraction-jft8sf
Aug 28, 2026
Merged

docs: apply the unapplied extract-ssot deduplication lane, and close its declared recall gap#3460
claude[bot] merged 6 commits into
mainfrom
claude/docs-hygiene-ssot-extraction-jft8sf

Conversation

@kyle-sexton

@kyle-sexton kyle-sexton commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

No linked issue

Summary

Applies the one lane of the repo-wide docs-hygiene sweep that was never applied. #3362 ran eight lanes and graduated its remainder into docs/specs/docs-hygiene-sweep-unapplied-remediations.md; #3380 then closed out L2, L4, L5, L6 and L7 and left L3, the extract-ssot deduplication lane, untouched. An adversarial verifier re-tested that premise cluster by cluster before anything was edited: all 13 remediated clusters were still open, no later commit had applied them, and no open pull request covered them.

That lane also declared its own recall limit precisely, and named the fix. This change carries the fix out: a shingled n-gram pass over whitespace-normalized markdown with line breaks removed, plus the reading-driven semantic pass the earlier lane had no subagent tool to attempt. Against a control paragraph re-wrapped at three widths, the old line-anchored method shares zero lines and the new pass recovers 100%.

Four of the findings are factual defects rather than prose drift.

Fix

A fallback that could never fire, at 25 sites — and a 26th that fired and said nothing. git status --porcelain 2>/dev/null | head -N || echo "clean" never runs its fallback: || binds to the pipeline, a pipeline's status is its last command's, and head exits 0 on empty input. A failed probe rendered an empty string under a label reading Working tree status:, which a reader takes for a clean tree. Five different fallback strings had drifted across the sites without one of them ever being reachable. plugins/verification/skills/confirm/SKILL.md is the fleet's one uncapped site, so its || already bound to the probe and its fallback did run — and emitted an empty string, which under that label renders exactly the same as a clean tree. Same misreading, different mechanism. The 25 capped sites now put the fallback in a brace group with the probe and apply the cap outside it; verification:confirm keeps its uncapped form and gains a fallback string that says something. All 26 labels now say what empty means. Proven by execution rather than argument: in a non-repository the old form prints nothing and the new one prints the fallback. The reasoning is recorded once, at the playbooks convention that owns precompute context. One site's allowed-tools grant was a byte copy of the injection it authorizes; normalizing only the injection would have orphaned the grant.

Four further sites carry the identical unreachable shape and are deliberately not normalized: docs-hygiene:audit-noise, docs-hygiene:audit-progressive-disclosure, code-tidying:dissolve-comments, and code-tidying:audit-comment-residue. Those are filtered probes whose fallback reads none, which for a filtered list is the correct answer when the filter matches nothing — so an empty render there is not the misreading it is for a working-tree status. Making the fallback reachable would collapse "no matching files" and "git did not run" into one string, which is the ambiguity this fix exists to remove. Each needs a distinct failure token and a re-decided label, per skill. Recorded open in the resumable spec rather than done badly.

An inline floor whose carriers had drifted apart. docs/conventions/loop-lane/ §6 requires each of the three consuming lane bodies to inline the floor, and "records the inline-floor rule so the values stay byte-identical across lanes". It binds three lanes, on the values, and names the reader contract as their provenance rather than as a fourth carrier — an earlier draft of this PR overstated that as "byte-identical across its carriers", which round three caught. Under §6's literal scope the three lanes were already mutually identical and the values never drifted; what drifted was the surrounding prose, between the lanes and the contract they cite. Hashing the three lanes plus the contract plus extract-ssot's orchestrated-mode consumer found two distinct texts, not three: git log -S returns two de-slop shards, and both made the same two substitutions, so between them they produced one drifted form rather than two. One of those substitutions replaced a clause-joining dash with a comma and left a splice in a sentence whose whole job is to state a causal link. All five now hash identically on a form that is both em-dash-free and grammatical. Extending identity to the two cited files is a choice this change makes, not one §6 compels, and the spec says so.

A third em-dash-purge regression in the same class, in the three telemetry-upsert references: one purged copy had lost a clause explaining what the creation-race reconcile does to a sibling instance's comment, and the two unpurged copies disagreed about it. Only one of the two wordings is true, and it is now in all three. No executable block was edited; the worker refuted the brief's claim that the bash blocks were identical and verified each against its own HEAD rather than editing code to fit a false premise.

A dropped-allow-rule roster missing a class. claude-config:audit-permission-state recapped upstream's auto-mode drop classes with Monitor elided, then counted "four documented classes" from the elision. Upstream added the category in v2.1.236. A dropped Monitor allow rule was reported under none of the audit's classes. The prose, the diff driver and its test suite now carry the fifth class, and the skill states plainly that the sibling audit-permission-grants check has no Monitor equivalent, so a clean run there is not evidence that such a grant survives.

Owner clauses that did not exist. docs/PLUGIN-PHILOSOPHY.md gains the runtime-grounded clause 20 setup skills were asserting with nothing to point at, and four Convention registry rows: three owner docs that declare themselves owners and were missing from the registry that indexes them, plus the precompute convention. The registry's own text makes that load-bearing, since fleet audits check conformance per row.

Detector-findings preamble, five sites not four. The four persist-findings.md preambles this sweep touched are byte-identical apart from their run-name slot. testing:audit states the same contract as a numbered apply step instead, keeps that form on purpose, and had silently dropped three load-bearing clauses from it — the shape's authority, the self-ignore guard, and "where the contract and this file disagree, the contract wins". It carries all three again in its own form. provenance:audit's preamble arrived with the main merge and is a sixth surface outside that set, not a fifth copy: it opens on "this plugin's read" rather than "this skill's" and carries a resolution list the four do not. On the owner side the contract still said "both current adopters" with four rows in a table its own rule says is added to by the commit that makes it true; a fifth producer had conformed since 2.5.0.

A line-number citation that asserts the opposite of the line it names. Four surfaces cited plugins/skill-quality/scripts/check-skill.sh:414 as the hard FAIL for a dropped trigger phrase — the fence that keeps a detector's remediation out of frontmatter. Line 414 sits inside a comment stating that a trigger move WARNs and never blocks; the err is at 462. This was written up as an open cluster in the resumable spec, and between that writing and this merge main fixed one of the four in detector-findings 2.7.1, independently and by the same reasoning, leaving the roster wrong within hours. The other three now carry main's remedy: name the trigger-phrase drop check, never a line. The spec keeps the episode as a worked example of its own decay rule rather than deleting it.

A taxonomy attributed to a living author who did not write it. Four songwriting sites credit a seven-title-type taxonomy to Pat Pattison; the owner file's own heading says it is unaudited and that "the count 'seven' is ours, not Pat's". One site carried that warning and contradicted it five lines later. A shipped prompt template asserted it with no caveat. Alongside it, three skills stated a filter's mandatory load list as two files where the filter names seven, and nine skills had dropped the canonical's first match qualifier from the template-override rule.

Verification

scripts/check-changed-skills.sh origin/main        57 skills checked, 0 failed
node scripts/validate-plugin-contracts.mjs         52 setup skills, 3022 files
scripts/check-changelog-parity.sh --check          clean
scripts/check-changelog-parity.sh --check-bump     every bumped plugin has its entry
scripts/check-changelog-parity.sh --check-order    85 changelogs, no duplicates
scripts/check-purged-em-dashes.sh                  21 paths, 30 files, no em dashes
scripts/check-skill-portability.sh origin/main     70 files, no unexcused tokens
scripts/check-manifest-duplicate-keys.py           72 manifests, no duplicates
scripts/check-detector-findings-crosswalk.sh       29 rule rows, every disposition argued
plugins/claude-config/lib/permission-patterns.test.sh          12/12
.../audit-permission-state/scripts/automode-entry-diff.test.sh 68/68
scripts/check-purged-em-dashes.test.sh             21/21
scripts/check-skill-precompute-compose.test.sh     13/13
npx markdownlint-cli2 <changed>                    0 issues in 115 files
typos <changed>                                    clean
scripts/affected-tests.sh --run                    exit 3 (NOT RUN contract), 135 suites, 0 failed

Every execute worker re-derived its site roster before editing rather than trusting the verdict it was handed, and three reported a delta as a result: one found five sites where the record said four, one found the record's "34 edited" was down to one because 33 had landed three days before the audit that recorded them, and one refuted the brief it was given. Independent post-hoc greps confirm all 26 sites on the canonical probe form, the only remaining instances of the broken shape being the four filtered probes deliberately left out of scope above, and the five byte-audited floor carriers hashing identically. The two prompts/loops/ files carry the floor as a blockquoted restatement, re-wrapped and explicitly outside the byte-audited block; their wording was brought to the same text but they are not among the five under the identity contract.

The detector itself was audited adversarially. It found three real defects, all absorbed by the triage contract's requirement that every row be resolved against the real files before judgment: a prose mention of BEGIN GENERATED opened a region that never closed, the known-refused pre-filter suppressed four recorded-open remediations that the re-verification lane owned separately, and union-find chains blocks transitively. Measured precision is 65% false-positive on a uniform sample and 12% on the in-scope remainder; K=12 was tested against K=8 and K=16 and held. Every figure in that paragraph and in the spec's recall-limits section is unreproducible from this repository: the detector was a session tool, deliberately not committed, because a one-run measurement instrument is not a marketplace artifact. The spec now says so in place rather than letting the numbers read as citable facts.

The change set's own record was then reviewed adversarially and was wrong in eight places — the mechanical work verified sound, the record-keeping not. Six changelog entries described a change their diff did not make: three claimed the probe-don't-recite normalization for plugins whose setup skills never contained that directive (their diffs repaired a comma splice, a broken list item and an orphaned clause, and now say so); three claimed all five producer preambles are byte-identical when four are. The new detector-findings adopter row called itself the third producer to join the crosswalk while the changelog entry directly above it correctly says fifth, and said the detector marks six shapes when lib/noise-shapes.sh marks eight and detect.sh classifies a ninth. The section heading "Three emitters" had outlived its count at six. All are corrected in 3f0f0ed2, and docs/conventions/loop-lane/CHANGELOG.md gains the 9.0.1 entry the §4 citation-form change should have carried.

A second verifier then read that adopter row claim by claim against the scripts and found two more. The row said "selection is a mechanical per-sentence scan with no withholding verdict", which is true of detect.sh and false of the skill: a model judgment lane sits between the scan and the writer and may dismiss a candidate on grounds SKILL.md enumerates. The sibling row for claude-config:audit-instructions states its model-lane carve-outs; this one did not, and this contract's "no evidence, no decline" rule requires a declining rule to state its decline evidence in its crosswalk row. Both are now stated, and rule-negation-hard-guardrail's Auto-applicable cell led with n/a, which is not one of the four lead forms the contract permits. Chasing the shape count to its source found the real defect underneath: audit-noise's own SKILL.md, context/persist-findings.md and emit-findings.sh header all said the scanner marks six shapes and declines five, naming five by name. lib/noise-shapes.sh appends eight and detect.sh drives a ninth. Three shapes had been added without the prose following, so ## Surfaces reports declined counts for shapes the skill's own docs say do not exist. Fixed in all three, each now pointing at the function as the count's source instead of restating it.

A third round re-derived the whole change set's claims and found eight more, all record-keeping, none a code defect. "Three distinct texts" was two — both de-slop shards made the same substitutions. The spec overstated what loop-lane §6 binds. The check-skill.sh:414 cluster was declared closed while audit-noise's own emit-findings.sh still carried it, because the earlier sweep grepped only *.md. "Every site carries one wording" was 18 of 20; context-guard and rate-limit-guard are named as the holdouts. The four-preambles claim went stale when provenance landed. The docs-hygiene shape-count entry misdescribed SKILL.md, which was internally inconsistent rather than wrong. The Pass B row said 15 contradictions where the file records 12. And one roster row quoted "This doc never restates it" against severity when that sentence is about the findings-file shape. Each was re-verified against the repository before being fixed, and this PR body was corrected alongside them.

A sweep whose subject is false claims in versioned surfaces does not get to ship false claims in its own — including in this description.

main moved under this branch during the review rounds (#3461, #3456, #3457), conflicting 36 files: every plugin this change bumps had also been bumped on main, and main's version had shipped. Resolved by taking main's side of every manifest and changelog and re-landing this branch's entry one patch above it, so no shipped version number is reused and no main entry is lost. All 26 verified centrally: manifest version equals topmost changelog version, no duplicates, no conflict markers, --check-order clean at 85 changelogs. main's daa9e148 was an authoring-doctrine and em-dash-grammar pass over the same surfaces; it does not overlap the three grammar repairs here, checked one by one against origin/main rather than assumed.

The sweep's full roster, including everything verified and deliberately not applied, the twelve contradictions the semantic pass surfaced, and those measured recall limits, is recorded in docs/specs/extract-ssot-sweep-2026-08-28.md so the remainder is resumable without re-auditing.

Related

Refs #3362, refs #3380, refs #3178. Extends docs/specs/docs-hygiene-sweep-unapplied-remediations.md L3.


Generated by Claude Code


Generated by Claude Code

…its declared recall gap

The repo-wide docs-hygiene sweep (#3362) ran eight lanes and graduated its
remainder into docs/specs/docs-hygiene-sweep-unapplied-remediations.md. #3380
closed out L2, L4, L5, L6 and L7 and left L3, the extract-ssot deduplication
lane, untouched. An adversarial verifier re-tested that premise cluster by
cluster before anything was edited: all 13 remediated clusters were still open,
no later commit had applied them, and no open pull request covered them.

That lane also declared its own recall limit and named the fix. This carries it
out: a shingled n-gram pass over whitespace-normalized markdown with line breaks
removed, plus the reading-driven semantic pass it had no subagent tool to
attempt. Against a control paragraph re-wrapped at three widths, the old
line-anchored method shares zero lines and the new pass recovers 100%.

Four of the findings are factual defects rather than prose drift:

- A fallback that could never fire, at 26 sites. Piping a probe into head
  before || makes the fallback unreachable, so a failed git status rendered an
  empty string under a label that reads as a clean tree. Proven by execution.
- An inline floor the loop-lane convention requires byte-identical, which was
  three distinct texts. The drift traces to two de-slop shards, one of which
  also introduced a comma splice.
- A dropped-allow-rule roster missing the Monitor class upstream added in
  v2.1.236, so a dropped Monitor grant was reported under none of the classes.
- A title taxonomy credited to a living author that the owner file records as
  unaudited and not his, including in a shipped prompt template.

docs/PLUGIN-PHILOSOPHY.md gains the runtime-grounded clause 20 setup skills were
asserting with nothing to point at, and four Convention registry rows: three
owner docs that declare themselves owners and were absent from the registry that
indexes them, plus the precompute convention.

The full roster, the twelve contradictions the semantic pass surfaced, and the
measured recall limits of the detector itself are recorded in
docs/specs/extract-ssot-sweep-2026-08-28.md so the remainder is resumable.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UmrZGdp1dgbZuPCy7tcRJo
@github-actions github-actions Bot deleted a comment from claude Bot Aug 28, 2026
@github-actions github-actions Bot deleted a comment from claude Bot Aug 28, 2026
@github-actions github-actions Bot deleted a comment from claude Bot Aug 28, 2026
@github-actions github-actions Bot deleted a comment from claude Bot Aug 28, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 723559280c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@claude

claude Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Claude encountered an error after 1s —— View job


I'll analyze this and get back to you.

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown

Warning

Automated review did not complete — this is an infrastructure failure, not a review verdict.

Treat any Claude comment on this PR (including a placeholder like "I'll analyze this and get back to you") as incomplete, not "no findings."

Re-run the job, or workflow_dispatch this workflow with the PR number, to retry the review. A new push re-triggers this lane only if the caller's pull_request triggers include synchronize (the canonical caller omits it).
An automatic retry may already have run — it is skipped when a partial review could duplicate comments, or when the failure class needs an operator (auth).

@claude

claude Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Claude encountered an error after 0s —— View job


I'll analyze this and get back to you.

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown

Warning

Automated security review did not complete — this is an infrastructure failure, not a review verdict.

Treat any Claude comment on this PR (including a placeholder like "I'll analyze this and get back to you") as incomplete, not "no findings."

The check is green on purpose, and it is not evidence. It certifies that a security pass ran, and this one did not complete — but the cause is outside this PR's control, so merging is deliberately left unblocked rather than locking every merge for the length of the outage. Nothing was reviewed at this head. Where this check is required, it is satisfied without that evidence; a human should review security-sensitive changes here before merging.

Re-run the job, or workflow_dispatch this workflow with the PR number, to retry the review (ci-workflows#227). A new push also retries it only if the caller's pull_request triggers include synchronize (the canonical security caller keeps it). An automatic retry may already have run — it is skipped when a partial review could duplicate comments, or when the failure class needs an operator.

Re-running does NOT help for every class:

  • rate-limit that persists across re-runs, or auth — the credential or usage budget needs an operator; retrying will not clear it.
  • a run that exhausted its turn budget ("subtype":"error_max_turns" above) will exhaust it again. As the PR author, split the change into smaller PRs; raising --max-turns is a change to the caller workflow, not something you can set on this PR.

…t introduced it

Codex review P2 on #3460. The new Monitor branch classified every Monitor allow
rule as dropped unconditionally, but the category arrived in Claude Code
v2.1.236 and the reference's own source note records that earlier versions leave
Monitor rules in effect. The plugin declares no minimum Claude Code version, so
on an older install the verdict was inverted in the direction that matters: it
told an operator a live grant was already suspended.

The script cannot read the running version, so it now emits a DIFF-NOTE naming
the v2.1.236 bound whenever it classifies a Monitor rule, following the same
idiom the classifyAllShell branch already uses for its own version requirement.
The reference documents the note, and two regression tests pin it present when a
Monitor rule is classified and absent when none is.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UmrZGdp1dgbZuPCy7tcRJo
@github-actions github-actions Bot deleted a comment from claude Bot Aug 28, 2026
@github-actions github-actions Bot deleted a comment from claude Bot Aug 28, 2026

Copy link
Copy Markdown
Contributor Author

Standing down on the automated review lanes, with the evidence they would have produced

Both Claude lanes (review and security-review) have failed on every attempt at both heads, all with api_error_status: 429, class rate-limit:

Attempt Run Result
security-review, initial 33148240880 error after 0s, 429
security-review, auto-retry 33148240880 error after 1s, 429
security-review, my manual re-run 33148240880 error after 0s, 429
review, initial and retry 33148240731 error after 1s, 429
both lanes, re-triggered by the 63d48450 push 33148778007 / 33148778106 error after 0s, 429

Per the workflow's own notice this is the class that "needs an operator; retrying will not clear it," so I have spent my one re-run and am not retrying further. This is an account-wide budget outage, not a property of this change. Nothing was reviewed by those lanes at either head.

Because the notice also says a human should review security-sensitive changes before merging, here is the verification I ran in their place, so that review starts from evidence rather than from scratch.

Executable surface of this PR

Three files, all in claude-config, plus 26 markdown injections and one frontmatter grant.

The 26 precompute injections. Every added injection, deduplicated, is one of exactly two shapes:

!`{ git status --porcelain 2>/dev/null || echo "(git status unavailable)"; } | head -N`
!`git status --porcelain 2>/dev/null || echo "(git status unavailable)"`

Audited across all added injection lines:

  • $ expansion, ${...}, $(...) or backtick substitution: none
  • file-writing redirect (excluding 2>/dev/null): none
  • rm, curl, wget, eval, chmod, chown, mv, cp, dd, nc, ssh, sudo, sh, bash, python: none
  • distinct commands invoked: git, echo, head only

git status --porcelain is read-only, echo emits a literal, head truncates. The change narrows nothing and widens nothing; it only moves where || binds.

The one allowed-tools grant. plugins/review/skills/quality-gate/SKILL.md carried an exact-match grant that was a byte copy of its own injection. It was updated in the same commit so the grant still matches; leaving it stale would have silently denied the command. Verified as the only such grant in the fleet.

automode-entry-diff.sh. Adds one branch classifying Monitor allow rules as dropped, plus (after the Codex finding) a DIFF-NOTE bounding that verdict to v2.1.236. For a permission audit the direction matters: the branch can only report more grants as suspended, never fewer, and the note prevents the inverse error on older versions.

lib/permission-patterns.sh. Comment only. No behavior change; its 12-check suite passes unmodified.

Tests. Net +7 assertions in automode-entry-diff.test.sh (66 to 68 checks after the Codex fix). The two assertions that changed went from 4 classes to 5 and from 5 rules to 6, i.e. strictly stronger. Nothing was skipped, weakened, or quarantined.

What a human should still look at

The lanes never ran, so this is my verification and not an independent one. The narrowest thing worth a second pair of eyes is the claude-config script change, since it is the only behavior change in a 144-file diff that is otherwise documentation. Everything else is prose normalization whose conformance is asserted by the greps in the PR body.


Generated by Claude Code

@claude

claude Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Claude encountered an error after 0s —— View job


I'll analyze this and get back to you.

@claude

claude Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Claude encountered an error after 0s —— View job


I'll analyze this and get back to you.

claude added 3 commits August 28, 2026 07:14
An adversarial review of the change set found the mechanical work sound and the
record wrong in eight places. A sweep whose subject is false claims in versioned
surfaces cannot ship false claims in its own versioned surfaces.

Changelog entries that described a different change than the diff made:

- ai-slop, mutation-testing and testing said all five producer preambles are now
  byte-identical. Four are; testing:audit keeps a numbered-step form by design
  and regained the three clauses it had dropped. Each entry now says which.
- claude-ops, knowledge and rate-limit-guard each carried the probe-don't-recite
  entry, which belongs to the twenty setup skills that actually contained that
  directive. These three did not. Each now reports the defect its own diff
  repaired: a comma splice, a broken list item, an orphaned clause.
- verification 0.5.8 called its fallback unreachable. That site is the fleet's
  one uncapped injection, so its fallback ran and emitted an empty string, which
  under the label is still indistinguishable from a clean tree. Twenty-five sites
  had the unreachable shape; this one had a reachable but uninformative one.
- songwriting 1.4.15 reported the skill-wide clusters and omitted the suno files
  and ledger row S20 that the same commit moved, which the ledger's own rule
  requires be reported here.

detector-findings, the convention this sweep also edited:

- The new audit-noise adopter row called itself the third producer to join the
  crosswalk; the changelog entry directly above it correctly says fifth. It said
  the detector marks six shapes with five declined; lib/noise-shapes.sh marks
  eight and detect.sh classifies negation as a ninth, so eight are declined.
- The section heading "Three emitters" outlived its count at six. A heading that
  names a number decays every time the table it heads grows.

Anti-regrowth lines restored in songwriting/suno context files, whose inline
"deliberately retired" note an earlier release records as the defense against
the advice returning. Both prompts/loops rate-limit floor carriers brought to
the canonical wording; both are blockquoted restatements outside the
byte-audited block, and the spec now says so rather than implying seven
carriers are under the identity contract.

The resumable spec gains the four filtered-probe sites deliberately left
unnormalized and why, and declares that every figure in its recall-limits
section is unreproducible because the Pass A detector was a session tool that
was deliberately not committed.

loop-lane 9.0.1 records the §4 citation-form change, which had edited a
versioned contract with no entry in its own changelog.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UmrZGdp1dgbZuPCy7tcRJo
…source

A verifier read the detector-findings adopter row for docs-hygiene:audit-noise
claim by claim against the scripts. Nine of eleven claims held with direct code
evidence. Two did not, and chasing one of them found the defect underneath.

The row said "selection is a mechanical per-sentence scan with no withholding
verdict". True of detect.sh, false of the skill: a model judgment lane sits
between the scan and the writer and may dismiss a candidate on the grounds
SKILL.md enumerates. Every dismissal is counted in ## Surfaces as
reason=judgment-lane-dismissal, so nothing vanishes silently, but the contract's
"no evidence, no decline" rule requires a declining rule to state its decline
evidence in its crosswalk row, and this one stated only two of its three
classes. The sibling row for claude-config:audit-instructions gets this right.
Both the adopter row and the crosswalk row now name the lane.

rule-negation-hard-guardrail's Auto-applicable cell led with `n/a`, which is not
one of the four lead forms this contract permits; its three sibling non-emitting
rows all use "Not applicable — no row". Corrected, argument unchanged.

The shape count was wrong at its source, not just in the row. SKILL.md,
context/persist-findings.md and emit-findings.sh's header each said the scanner
marks six shapes and declines five, naming citation, ghost-ref, preamble,
enum-list and scope-meta. lib/noise-shapes.sh appends eight -- those five plus
plan-reference, conversational-antecedent and ticket-pr-residue -- and detect.sh
drives a ninth, negation, over an accumulated paragraph. Three shapes were added
without the prose following, so a reader of ## Surfaces would find declined
counts for shapes the skill's own docs say do not exist. All three now say nine
and eight, name all eight, and point at audit_noise_detect_shapes_into as the
count's source rather than restating it as a fact. No behavior changes;
emit-findings 35/35 and detect 199/199 still pass.

The adopter row's lead also said nothing distinguishing -- three other adopters
are equally read-only and reach the relay. Its real novelty is that it is the
first producer selecting per sentence across soft-wrapped lines rather than per
line, which is what the lead now says.

Separately: a fifth site was checked against the unreachable-fallback shape and
cleared by execution. docs-hygiene:audit-derivability captures the probe into a
variable first, so the probe's own exit status heads an && list and the ||
fires on failure. That is the form the four deliberately-skipped filtered probes
should adopt, and the spec now records it as a candidate rather than a decision.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UmrZGdp1dgbZuPCy7tcRJo
…sot-extraction-jft8sf

# Conflicts:
#	docs/conventions/detector-findings/CHANGELOG.md
#	plugins/ai-slop/CHANGELOG.md
#	plugins/claude-config/.claude-plugin/plugin.json
#	plugins/claude-config/CHANGELOG.md
#	plugins/claude-ops/CHANGELOG.md
#	plugins/code-tidying/CHANGELOG.md
#	plugins/codebase-health/CHANGELOG.md
#	plugins/discovery/CHANGELOG.md
#	plugins/disk-hygiene/.claude-plugin/plugin.json
#	plugins/disk-hygiene/CHANGELOG.md
#	plugins/docs-hygiene/CHANGELOG.md
#	plugins/firecrawl/CHANGELOG.md
#	plugins/implementation/CHANGELOG.md
#	plugins/improvement/CHANGELOG.md
#	plugins/knowledge/CHANGELOG.md
#	plugins/mutation-testing/.claude-plugin/plugin.json
#	plugins/mutation-testing/CHANGELOG.md
#	plugins/planning/.claude-plugin/plugin.json
#	plugins/planning/CHANGELOG.md
#	plugins/playbooks/CHANGELOG.md
#	plugins/playwright/CHANGELOG.md
#	plugins/rate-limit-guard/CHANGELOG.md
#	plugins/repo-hygiene/CHANGELOG.md
#	plugins/review/.claude-plugin/plugin.json
#	plugins/review/CHANGELOG.md
#	plugins/session-flow/.claude-plugin/plugin.json
#	plugins/session-flow/CHANGELOG.md
#	plugins/songwriting/CHANGELOG.md
#	plugins/source-control/.claude-plugin/plugin.json
#	plugins/source-control/CHANGELOG.md
#	plugins/testing/.claude-plugin/plugin.json
#	plugins/testing/CHANGELOG.md
#	plugins/toolchain/CHANGELOG.md
#	plugins/verification/.claude-plugin/plugin.json
#	plugins/verification/CHANGELOG.md
#	plugins/work-items/CHANGELOG.md
@github-actions github-actions Bot deleted a comment from claude Bot Aug 28, 2026
@github-actions github-actions Bot deleted a comment from claude Bot Aug 28, 2026
@claude

claude Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Claude encountered an error after 0s —— View job


I'll analyze this and get back to you.

@claude

claude Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Claude encountered an error after 0s —— View job


I'll analyze this and get back to you.

@claude

claude Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Both automated review lanes returned api_error_status: 429 (class: rate-limit) at this head and
at the previous one, so nothing was reviewed by them. Their checks are green by design and are
not evidence. Re-running is not available to this session (the token gets 403 on
rerun-failed-jobs), and the repo's own infra notice says a persistent rate-limit needs an
operator rather than a retry. Some of the contention is this session's own subagent fan-out.

Standing in for the lanes, here is the verification, re-derived against b2027f59 (merged
origin/main), with the commands so it can be re-run rather than trusted.

Security surface

Dynamic-context injections. The diff adds exactly four distinct ! forms across 26 sites:

!`git status --porcelain 2>/dev/null || echo "(git status unavailable)"`
!`{ git status --porcelain 2>/dev/null || echo "(git status unavailable)"; } | head -5`
!`{ git status --porcelain 2>/dev/null || echo "(git status unavailable)"; } | head -10`
!`{ git status --porcelain 2>/dev/null || echo "(git status unavailable)"; } | head -20`

Every one invokes only git status --porcelain, echo, and head. Measured over the added lines:

Probe Count
literal $ inside an added injection (expansion or command substitution) 0
output redirect or tee (after stripping 2>/dev/null) 0
rm/curl/wget/eval/chmod/mv/cp/ssh/nc/base64/sudo/destructive git verbs 0

The absence of $ is deliberate and load-bearing beyond style: an expansion other than bare
$HOME leaves the composed pre-compute block unverifiable to the worktree-isolation guard, which
then refuses to load the skill from an isolated agent. That reasoning is recorded once, at the
playbooks precompute convention.

The one allowed-tools grant that byte-copies its injection. review:quality-gate grants the
injection verbatim, so normalizing only the injection would have orphaned the grant and broken the
skill. Both sides were changed together and still match exactly: a grep -c for the injection and
a grep -c for the escaped grant each return 1.

The grant did not widen. The old rule was
Bash(git status --porcelain 2>/dev/null | head -20 || echo "unavailable") and the new one is
Bash({ git status --porcelain 2>/dev/null || echo "(git status unavailable)"; } | head -20);
both authorize exactly one command shape.

Executable changes

Four scripts are touched. Three are comment-only:

  • plugins/claude-config/lib/permission-patterns.sh — header comment records that Agent and
    Monitor are whole-tool classes with no shell shape to share. No code.
  • plugins/docs-hygiene/skills/audit-noise/scripts/emit-findings.sh — header comment corrects a
    five-shape list to eight and points at the function that is the source. No code.
  • .../automode-entry-diff.test.sh — two added regression cases, none weakened (66 to 68).

The one substantive change is .../automode-entry-diff.sh, which adds a monitor class and errs
in the safe direction:

  • The new branch matches only Monitor / Monitor(...) and returns dropped class=monitor. No
    existing branch's behavior changes; nothing is reclassified out of kept except an exact
    Monitor token.
  • The verdict is version-dependent (upstream added Monitor to the dropped list in v2.1.236) and
    the script cannot read the running version, so it emits a DIFF-NOTE naming the version and
    stating that on an older install the verdict inverts. That was a real review finding on an
    earlier head, and it is now bounded rather than asserted.
  • shellcheck exit 0; automode-entry-diff.test.sh 68/68.

Repo gates on this head

scripts/check-changed-skills.sh origin/main        57 skills checked, 0 failed
node scripts/validate-plugin-contracts.mjs         52 setup skills, 3022 files
scripts/check-changelog-parity.sh --check          clean
scripts/check-changelog-parity.sh --check-order    85 changelogs, no duplicates
scripts/check-changelog-parity.sh --check-bump     every bumped plugin has its entry
scripts/check-skill-portability.sh origin/main     70 files, no unexcused tokens
scripts/check-purged-em-dashes.sh                  21 paths, 30 files, no em dashes
scripts/check-detector-findings-crosswalk.sh       29 rule rows, every disposition argued
scripts/affected-tests.sh --run                    134 shell suites, 0 failed
markdownlint-cli2 <changed>                        0 issues in 115 files
typos <changed>                                    clean

What this does not cover

This is the author verifying their own change, which is weaker than an independent lane and should
not be read as a substitute for one. A human reviewing the security-sensitive part has one file to
read: plugins/claude-config/skills/audit-permission-state/scripts/automode-entry-diff.sh.
Everything else in the diff is prose, changelog entries, and comments.


Generated by Claude Code

A third adversarial review re-derived the change set's own claims against the
repository. Every finding is record-keeping; none is a code defect. All eight
were re-verified here before fixing.

"Three distinct texts" was two. Hashing the floor block across the five carriers
at origin/main returns exactly two md5 values, not three. Both de-slop shards
made the same two substitutions, so between them they produced one drifted form
rather than two. Corrected in the spec and in the four plugin changelogs that
repeated it, with "one of the two edits" disambiguated to name the substitution
rather than the commit.

The spec also overstated what loop-lane §6 requires. §6 binds the three
consuming lane bodies, on the values, and names the reader contract as their
provenance rather than as a carrier; it says nothing about extract-ssot's
orchestrated-mode consumer. Under that literal scope the three lanes were
already identical and the values never drifted at all -- what drifted was the
surrounding prose. Extending identity to the two cited files is a choice this
record makes, and now says so instead of claiming the convention compelled it.

The check-skill.sh:414 cluster was declared closed while audit-noise's own
emit-findings.sh still carried the citation. The earlier sweep grepped only
*.md and missed the .sh site. Fixed there too, so the claim is now true.

"Every site carries one wording" was 18 of 20. context-guard and
rate-limit-guard state the same rule about their own scripts in their own words
and were not touched. Corrected in all eight changelogs that carried the claim,
naming the two holdouts and the separate pass they need.

The four-preambles claim went stale when provenance landed with the merge. Its
persist-findings.md is a fifth file and is not byte-identical -- "this plugin's
read" rather than "this skill's", plus a resolution list the four do not carry.
Re-worded in ai-slop, mutation-testing and testing.

The docs-hygiene shape-count entry misdescribed SKILL.md on both sides. That
file was internally inconsistent, not wrong: it says nine throughout and
documents all three newer shapes, but one sentence said "the other five". The
entry now says that, and SKILL.md's sentence points at
audit_noise_detect_shapes_into instead of restating the number.

Two smaller ones: the spec's Pass B row said 15 recorded contradictions where
the file records 12, and one roster row quoted "This doc never restates it"
against severity when that sentence is about the findings-file shape. The
underlying observation held; the quote did not.

Folded in from the same review: the encapsulation roster is a floor, with three
more path-citations found on no roster; a fifth filtered probe in
docs-hygiene:compress reaches the same ambiguity from the other direction; the
1,536 cap count moved from 14 to 16 during review, which is the decay rule
rather than an exception to it; and review 0.26.13 now says its diff rewrote an
allowed-tools grant, not only an injection.

emit-findings 35/35, detect 199/199, shellcheck clean, all repo gates green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UmrZGdp1dgbZuPCy7tcRJo
@github-actions github-actions Bot deleted a comment from claude Bot Aug 28, 2026
@github-actions github-actions Bot deleted a comment from claude Bot Aug 28, 2026
@claude

claude Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Claude encountered an error after 0s —— View job


I'll analyze this and get back to you.

@claude

claude Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Claude encountered an error after 0s —— View job


I'll analyze this and get back to you.

@claude

claude Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Addendum — the head moved from b2027f59 to 557f5321, and both review lanes hit 429 again at
the new head, including the automatic retry. Per the infra notice this class needs an operator, and
this session's token gets 403 on rerun-failed-jobs, so I cannot clear it.

The verification above was performed at b2027f59. Here is why it still holds at 557f5321, rather
than asking anyone to assume it:

git diff --name-only b2027f59 557f5321   ->  20 .md files, 1 .sh file
  • The one .sh file is plugins/docs-hygiene/skills/audit-noise/scripts/emit-findings.sh, and its
    diff has zero non-comment changed lines: git diff … -- '*.sh' | grep '^[+-]' | grep -v '^[+-][+-]' | grep -vE '^[+-]#' returns nothing. The edit replaced a rotted
    check-skill.sh:414 line-pin in a comment with the name of the check.
  • No injection and no allowed-tools grant changed. Two added lines match
    !\`` or allowed-tools, and both are changelog prose describing the grant edit that already landed in b2027f5` — not grants themselves.
  • Everything else is changelog and spec prose: eight record-keeping corrections a third adversarial
    review found and I re-derived against the repository before applying.

So the security surface at 557f5321 is byte-identical to the surface reviewed above. shellcheck
clean, emit-findings.test.sh 35/35, detect.test.sh 199/199, and the repo gates re-run green on
this head.

Still the same caveat: this is the author checking their own work, and no independent lane has run
at any head on this PR.


Generated by Claude Code

@claude
claude Bot merged commit 908902d into main Aug 28, 2026
58 checks passed
@claude
claude Bot deleted the claude/docs-hygiene-ssot-extraction-jft8sf branch August 28, 2026 08:08
claude Bot added a commit that referenced this pull request Aug 28, 2026
…closed gate that failed open (#3468)

#3460 merged a whole-repo extract-ssot sweep and recorded, in its own resumable
spec, three things it deliberately left open. This closes them, and closes a
fourth the sweep did not know it had.

A post-merge security audit found the defect #3460 fixed for git-status probes
still live in the rest of its class. video-digest/SKILL.md states "STOP if the
pre-computed context above shows MISSING for yt-dlp, ffmpeg, or ImageMagick.
Cloud agents without the media toolchain fail closed." Its probes were
`yt-dlp --version 2>/dev/null | head -1 || echo "MISSING ..."`. Because || binds
to the pipeline and head exits 0 regardless, an absent yt-dlp rendered an empty
string, never MISSING. The gate could not fire: a skill documenting itself as
failing closed failed open. Fixed there and at course-digest, ai-slop:audit,
provenance:audit, firecrawl:update and claude-ops:observability, using the
guard-first shape firecrawl:firecrawl already shipped, so the fleet gains no new
idiom. Proven by execution both ways.

Also fixes a grant #3460 left behind: it rewrote the two prototype skills'
injections to begin with { while their grant stayed the prefix rule
Bash(git status:*), whose leading token no longer matches.

The five filtered probes #3460 deferred are bound to their probes, each keeping
its own filter, cap and label noun so "the filter matched nothing" and "git did
not run" stay distinguishable.

The ADR 0018 encapsulation floor is closed. The spec recorded two sites and
declared the roster a floor; re-derivation across all of docs/** with five
independent search shapes found 16 citations at 12 sites in 8 files, among them
V-review-13 and V-review-14, open on the predecessor's roster since 2026-08-26
(34 to 32). The sharpest: docs/PLUGIN-PHILOSOPHY.md's Convention registry row,
which #3460 itself added, was written in the exact bare unresolvable form ADR
0018 names as its real defect class. 23 citations are kept on a stated test and
recorded as a judgment set so a later pass can overturn them.

A fourth review round found five more false claims in the merged record, each
re-derived before fixing. Two are structural: a cross-reference pointing at a
section that names none of its four clusters, and a quote attributed to two
files that neither makes -- the identical wrong-subject defect round three
fixed, recurring one paragraph away.

Refs #3460, refs #3362, refs #3380.
claude Bot added a commit that referenced this pull request Aug 28, 2026
…l-proof (#3469)

#3468 fixed five filtered-probe injections so a failed probe could be told
apart from a filter that matched nothing. The form it shipped is wrong under
set -o pipefail, and wrong in the more damaging direction: it asserts a failure
that did not happen.

  probe >/dev/null 2>&1 && probe | filter | head -10 || echo "(git status unavailable)"

The && list takes the pipeline's exit status. Without pipefail that is head's,
which is 0, and the guard works. With pipefail it is the last non-zero status in
the pipeline, which two ordinary situations produce: grep matching nothing, and
git taking SIGPIPE when head closes the pipe at the cap. Either fires the
failure token on a healthy probe. That is worse than the defect #3468 removed:
the shape it replaced only ever said none, while this one positively asserts
git status was unavailable when it ran fine.

Both modes reproduced against the shipped lines, then shown fixed. On a healthy
repository whose dirty files do not match, and again on one with 3,000 dirty
files, the 0.21.23 form prints (git status unavailable) and this one does not.
Outside a repository this one still prints it, which is the case the token is
for.

The filter pipeline now sits in a brace group closed by :, a command that cannot
fail, so the || is reachable only by the guard short-circuiting. No $ expansion
is introduced; the composed pre-compute block must stay verifiable to the
worktree-isolation guard. audit-comment-residue's parity test anchors its
porcelain capture past '&& {' so it evaluates the data run rather than an
unterminated brace group, and its capture was checked non-empty so the assertion
still bites.

Open and undocumented: whether Claude Code executes ! injections under pipefail.
Neither the precompute convention nor the permissions docs say. The brace-group
form is correct under both settings and the shipped form under only one, so this
is the fail-safe direction regardless.

Refs #3468, refs #3460.
kyle-sexton pushed a commit that referenced this pull request Aug 28, 2026
…idiom

Two things land here: the merge resolution, and the exemption rework that had to
happen on the merged tree.

## Merge resolution, per conflict

Main reconciled the floor block independently while this branch was in review,
so seven files conflicted.

- reader-contract.md, orchestrated-mode.md, loop-lane-prompts.md and
  loop-lane-profile-claude-code-plugins.md: all four conflicts are the same one
  line, this branch's comma against main's colon in the staleness bullet. Main's
  side taken in all four. Main's #3460 hashed the carriers, found the same
  divergence this branch found, and repaired it first; its form is grammatical
  where the comma left a splice. Running this branch's gate against origin/main
  unmodified reports 6 consumers matching and no unregistered copy, so main is
  already consistent across the owner and all six registered consumers. There is
  no live drift event to reconcile here, and re-imposing a second correct answer
  would be a third uncoordinated edit to the block.

- rate-limit-guard/CHANGELOG.md: main claimed 0.7.18. This branch's entry moves
  to 0.7.19 and drops its reconciliation bullet, which main's 0.7.18 now owns;
  what remains is the gate, the discovery scan, and the setup-probe change. That
  also removes the "other three copies matched byte for byte" line, which was
  wrong: two of those three are blockquoted templates matching under values-mode
  normalization, not byte for byte.

- docs-hygiene/CHANGELOG.md: same, 0.21.22 taken, this branch's entry moves to
  0.21.23 and now records only that the copy is gated rather than that it was
  reconciled here.

- loop-lane/CHANGELOG.md: main added 9.0.1, this branch 9.1.0. Both kept, in
  order; no content overlap.

Manifests follow: rate-limit-guard 0.7.19, docs-hygiene 0.21.23.

## The exemption idiom could silence a genuine consumer

The in-file `loop-lane-floor-carrier-ok:` annotation matched anywhere on any
line, in any context, so a real lane body could inline the floor, apply it,
mention the token in a sentence, and never be compared again. Demonstrated end
to end by review: an annotated lane passed at rc=0, then had its 90 changed to
80 and still passed. That is materially weaker than the two idioms it was
modelled on, for a structural reason rather than an implementation one:
`# lane-coverage-ok:` and `# silent-skip-ok:` anchor to a syntactic site, a job
key or a guard line, which bounds what they excuse, and a file has no such site.

The exception list moves into the gate as DATA_CARRIERS, path plus reason. No
file can now exempt itself by writing anything into itself; a new data carrier
costs an edit to the gate, which is the review the exception needs. Entries are
stale-guarded the way the consumer registry is, failing when the path is gone or
no longer carries the floor, and they are printed on the PASSING path of
--check, not only under --list: CI runs --check and nothing else, so an
exemption visible only under a listing flag would be invisible in the log of the
run that went green over it.

Corrects the claim this defect falsified. The gate header and section 6 said the
checked set was "provably equal" to the set of files carrying the floor, untrue
while an unbounded self-declared exemption existed two sentences away. Both now
state the guarantee exactly: the carried set minus a finite, enumerated,
reviewed exception list. In a change whose premise is that an unbacked doc claim
is a defect, that had to be fixed rather than narrowed.

Tests, now 28: a file naming the old token cannot excuse its own copy; the same
file still fails once drifted; --check names every excused carrier on the
passing path; every listed data carrier exists in the checkout. The fixture tree
seeds the data-carrier paths it reads back out of the gate, so both registries
are stale-guarded against the same tree.

Adds the missing test for the discovery self-proof. Gutting that guard
previously left the suite green while the gate reported a clean corpus over a
scan that never ran, the exact property the liveness-assertion Adopters row
advertises by name. Staging every fixture file EXCEPT the source reproduces a
search that returns nothing, and the gate must exit 2. Verified as a mutation:
with the self-proof condition forced false the suite reports PASS=27 FAIL=1, and
the one failure is that test.

Documents the shared anchored-marker limit: a copy indented under an outer list
or nested two blockquote levels deep is invisible to discovery AND to
comparison. The second half bounds the first, since such a path registered as a
consumer fails as MISSING FLOOR rather than passing silently.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QkMx972GYEQ3vCb98pLw8m
claude Bot added a commit that referenced this pull request Aug 28, 2026
…a spaced skill dir (#3474)

#3469 fixed five filtered probes whose || fallback fires spuriously under
set -o pipefail, and stopped there. Two independent verifiers found the same
shape still live at eleven more sites across six plugins, four of them
introduced by #3468 -- the commit immediately before the one that diagnosed the
bug class.

The mechanism is unchanged. guard >/dev/null 2>&1 && probe | cap || echo TOKEN
takes the pipeline's exit status. Without pipefail that is the cap's, always 0.
With pipefail it is the last non-zero status, which two ordinary situations
produce: a filter matching nothing, and the writer taking SIGPIPE when the cap
closes the pipe. Either fires the failure token on a healthy probe.

ai-slop:audit was live and unconditional. detect.sh --show-config emits 10 lines
against a head -8 cap, writing them one at a time, so head closes the pipe
mid-run every time; PIPESTATUS reads 141 0. The probe rendered the full correct
config and then appended 'detector unavailable' beneath it, reproducible from
any directory with no configuration at all.

knowledge had a consequence beyond a wrong string. video-digest states 'STOP if
the pre-computed context above shows MISSING for yt-dlp, ffmpeg, or ImageMagick.
Cloud agents without the media toolchain fail closed.' #3468 fixed that gate's
fail-open defect and shipped this inversion in its place, so the gate would
refuse to run on a machine that HAS the toolchain. Measured over 30 runs each
with the tool installed and working: ffmpeg 29/30 (41 lines), magick 19/30 (5
lines, a race), yt-dlp 0/30 (one line, never reaches the cap). After the fix,
0/30 at every size.

Also fixed: course-digest's two probes, firecrawl:firecrawl (pre-existing, and
the idiom the fleet copied), provenance:audit's two detector probes (latent by
shape only, and its changelog says so rather than claiming a live fix),
docs-hygiene:audit-derivability whose capture-first form protects only the git
call, and code-tidying:tidy where a bare third 'unknown' was appended to
well-formed output.

One grant moved, and an earlier draft of this message denied it. Quoting
CLAUDE_SKILL_DIR changes the literal command string, and Bash permission rules
are globs over that string, so the unquoted rule stops matching the quoted
command. Three companion quoted rules were added with the unquoted ones
retained; same script, same anchor, same :* scope, nothing widened. The quoting
itself fixes a real defect: installed under a path containing a space, the
unquoted expansion made a working detector report itself unavailable.

Roster completeness was re-derived by a method unrelated to the first grep, a
parser tracking brace depth and quote state, which found zero of these eleven
and nine survivors, all correctly-drawn declines or structurally immune.

Open and undocumented: whether Claude Code runs ! injections under pipefail.
The brace-group form is correct under both settings, so the exposure is
avoidable regardless.

Refs #3469, refs #3468, refs #3460.
claude Bot added a commit that referenced this pull request Aug 28, 2026
…five wrong keeps (#3475)

#3468 closed 16 cross-plugin path citations under ADR 0018 and kept 23 on a
test it never wrote down. Its own verifier reported '3 keeps refuted; 4 cannot
verify' and that reasoning was unreadable, because the verifier ran as a remote
session the agent could not read back. An independent verifier settled it: five
are wrong, not three, and it found why this recurred.

The dividing test #3468 applied -- fix a citation that is an ADDRESS for an
obligation, keep one that is EVIDENCE about this tree at a moment in time --
appears nowhere in ADR 0018. Clause 2 reads unconditionally. But ADR 0018 is
itself a docs/** file and path-cites plugin skill privates five times, quoting
their contents inline. Either it violates itself on its own first page, or
clause 2 is scoped to citations that function as an address. Only the second
reading is coherent, and an unwritten rule is what let one commit rule two ways
in two files. This writes it down, as an Amendment following ADR 0011's
convention: the evidence/address test, scripts/lib being outside the
entry-surface carve-out, and how plugin-level non-skill trees generalize.

The five wrong keeps. shell-test-helpers cited a private scripts/lib/ path --
and that is a self-contradiction inside c66f26c itself, which added
detector-findings 2.8.1 fixing the byte-identical shape and stating the ground,
then kept it one file over. Three line and step pins are dropped as the same
class 2.7.1 removed a :414 pin for; each citation already quotes its content
inline, so the drop is lossless. records.json:3's keep-ground was false: the
sweep kept it because generation overwrites hand-edits, but that store's own
note says 'Hand-editable and human-gated'.

Recorded as open remainder rather than swept: twelve citations c66f26c created
itself, every fix having written a changelog entry quoting the path it removed,
so the population is 16 fixed and 35 kept rather than 16 and 23; three trees
excluded by fiat with no carve-out authorizing it; 51 citations into
plugin-level non-skill trees; and a stale dometrain security record whose own
version-bump re-trigger has fired unnoticed.

A fifth review round found two more false claims, both stale statuses. '34 to
32' was wrong: re-deriving all 34 rows against the roster's own text test finds
22 already closed, twelve of them by #3380, the same commit that last wrote the
roster and left its own 'all 34 re-verified' line standing over twelve citations
it had just deleted. This change set republished that status in a file whose
line 18 reads 'the check is the text, never the status and never the line
number.' And the spec still shipped the probe shape #3469 removed, presented as
verified in three states, two of which print the failure token under pipefail.

Refs #3468, refs #3469, refs #3460. Sibling #3474.
claude Bot added a commit that referenced this pull request Aug 28, 2026
…test, and record the pipefail question (#3476)

The last five findings from an independent security review of #3460/#3468/
#3469. #3474 and #3475 closed the probe-shape and citation halves; this closes
the filter half and writes the open question into the convention.

Four probes silently under-reported any filename git status --porcelain
C-quotes. A plain space is enough to defeat grep '\.md$' and awk '{print $NF}'.
Executed against a repo holding seven files per extension (plain, space, single
quote, double quote, semicolon, pipe, embedded newline), each of the four went
from catching 4 of 7 to 7 of 7. Two correct forms already shipped in the same
two plugins, so this was drift rather than a missing idea: the .md sites took
audit-noise's grep form, which keeps their leading token and so keeps their
grant valid, and dissolve-comments took audit-comment-residue's -z NUL parse.
The residual is stated in the changelogs rather than hidden.

audit-comment-residue's parity test could not see an over-reporting preview.
Its loop only checked one direction, so mutating the SKILL.md awk to drop its
rename skip made the preview emit a phantom path while detect.sh audited five
real ones, and the suite still passed 53/53. A reverse loop now asserts every
previewed path is one detect.sh audited: the same mutation fails 1/54 naming the
phantom, and reverted it passes 54/54. The forward loop is byte-identical.

claude-ops:observability rendered 'unknown' both outside a repo and inside one
whose toplevel directory is literally named unknown. The failure case now
renders a distinct token.

Six labels asserted 'empty = none' in two states the probe does not establish:
the brace group's : makes the outer || unreachable, which also masks a
filter-stage failure and a guard/data double invocation. The labels now say
'empty = none matched or the probe returned nothing'. No plumbing restructured;
the ~25 unfiltered probes were left alone because their claim is already true.

The durable fix is in precompute-context.md: whether Claude Code runs !
injections under pipefail is undocumented, the brace group is correct under both
settings while guard && pipeline || token is correct under only one, and the
brace group's own price is that an internal failure renders empty, which is why
the label must carry it. The recheck trigger now covers shell options.

Two derivations unrelated to the review's table both return the same six
filtered probes: the true set is six, four defective, not five and three.
#3474 fixed audit-derivability's SIGPIPE shape hours earlier and left its filter
untouched, so both defects landed on one line; this branch was rebuilt onto
current main and carries both, since committing either alone would have
reverted the other.

70 pipefail cells all exit 0; affected-tests real exit 0 with 17/17 suites;
changelog parity including --check-preserved across 306 headings; 3061 plugin
files validated; markdownlint clean over 173 files. No grant widened or broken.

Refs #3474, refs #3475, refs #3469, refs #3468, refs #3460.
kyle-sexton pushed a commit that referenced this pull request Aug 28, 2026
…, and close the dometrain staleness

#3468 closed the ADR 0018 encapsulation floor by writing a convention CHANGELOG entry
for each fix, and every entry quotes the path it removed. That manufactured twelve new
instances of the shape the pass was sweeping. The sweep record listed them and stated
they were fine; nothing ruled on them one at a time, so the arithmetic (16 fixed and 35
kept, not 16 and 23) rested on an assumption.

Ruled individually against the test #3475 wrote into ADR 0018's amendment. All twelve are
keep-correct and none is edited. Each sits in a dated entry whose claim is what a named
file contained on that date, quoted as the string the entry removed, with the replacing
invocation named in the same sentence, so no reader is sent to any of them for a rule.

Three sub-rulings the class needed are recorded, because a bare "all twelve are evidence"
is the assumption this was meant to replace. The three config-cascade rows quote
plugin-relative forms that resolve against nothing, and the entry says so: clause 3 does
not fire where the entry asserts non-resolution, since making them resolve would delete
the finding. The other nine resolve on disk from their own implied base, checked one at a
time. None carries a line or step pin, the part the amendment says rots first.

Re-derived with a second expression over the fix commit's added lines rather than a scan
of the files at rest. Same twelve, plus one the roster excludes on purpose:
plugins/review/reference/topic-docs.md, a plugin-level non-skill tree that belongs to the
pass the amendment routes it to, and a declared keep rather than a quoted removal.

Separately, MIGRATION-PLAYBOOK's dometrain record was reviewed at 0.1.0 and the manifest
reads 0.2.7, eleven releases later. Whether any of them added a trust surface is what the
re-trigger clause turns on and no record answers it, which takes a review rather than a
reading. The staleness is now stated in place with the re-review logged as owed. The
review itself is deliberately not performed here.

Refs #3468, refs #3475, refs #3460.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UmrZGdp1dgbZuPCy7tcRJo
claude Bot added a commit that referenced this pull request Aug 28, 2026
…, and flag a fired security re-trigger (#3477)

Every one of #3468's ADR-0018 citation fixes wrote a CHANGELOG entry quoting
the path it removed, creating twelve new instances of the shape it was closing.
The population at main is 16 fixed and 35 kept, not 16 and 23. #3475 wrote the
governing test into ADR 0018; this applies it to those twelve so a later
re-derivation does not re-open them.

All twelve are keep-correct, established row by row and anchored on text rather
than line numbers. A dated changelog entry quoting a citation it removed is the
evidence case exactly. No CHANGELOG was edited, so no convention bump is owed.

Three sub-rulings the class needed, now written into the spec rather than left
implicit. config-cascade's three rows are the only clause-3 question in the set:
they are plugin-relative and resolve against nothing, and the entry says so in
the same sentence. Clause 3 governs addresses a doc offers, not strings it
quotes in order to report them broken, and forcing resolution would delete the
finding. The other nine resolve on disk today, verified one path at a time
across eleven distinct targets. No row carries a line or step pin, the part the
amendment says rots first.

One excluded on purpose and recorded so it is not re-opened:
plugins/review/reference/topic-docs.md, a plugin-level non-skill tree the
amendment routes to its own pass, which the entry declares a keep rather than
quoting as removed.

Separately, MIGRATION-PLAYBOOK.md records a dometrain security review performed
at 0.1.0 and states that a version bump adding a new trust surface re-triggers
it. The manifest now reads 0.2.7, eleven releases later. The note added states
the reviewed version, the shipping version, that the ACCEPT below it describes
0.1.0 only, and that the re-review is owed and deliberately not performed here.
It stops short of asserting a trust surface was added: that is the condition the
clause turns on and cannot be settled by reading the page. Two checks were run
to avoid claiming otherwise and neither settles it, which the note says.

The spec's dometrain entry pinned MIGRATION-PLAYBOOK.md:943, a line this
change's own edit would have invalidated. Re-anchored on the record's heading
text. That decay rule has been violated three times across this sweep; this is
the first time it was caught before landing.

Recorded because two earlier claims about it were wrong in opposite directions:
this worker's definition declares the Agent tool and the harness withheld it at
spawn, so nesting is gated by depth realized as pre-launch tool omission. There
is no refusal string because there is no call to refuse.

Refs #3476, refs #3475, refs #3474, refs #3469, refs #3468, refs #3460.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants