docs: close out the docs-hygiene sweep's unapplied remediations, and give the findings-file contract a legal home - #3380
Conversation
…the findings-file contract a legal home The docs-hygiene sweep's L4 encapsulation lane, plus the three open L5 noise findings and the one L7 doctrine edit that were still outstanding after #3362. Twenty-two of the 24 cross-plugin violations were citations reaching into another plugin's skill-private tree. Each now names the owning skill's public invocation instead: /review:fanout fix, /skill-quality:check, /ai-slop:audit, /autonomy:setup, /claude-ops:lanes, /code-tidying:tidy, /source-control:setup, /session-flow:workflow, /docs-hygiene:write-for-humans. Plugins install independently, so a path into another plugin's private surface can be genuinely absent at read time; a slash command cannot be. The remaining two could not be fixed that way, and the reason is worth recording. Both cite the findings-file shape, which is not behavior a reader can route to but a spec a third-party detector must read to produce a file the fix action will consume. Naming a command there hands a producer a command instead of the contract it needs. So the contract moved rather than the citation: it now lives at plugins/review/reference/findings-file-shape.md, at plugin level and outside every skill directory, where the repo-level convention can cite it legally. It stays inside the plugin rather than moving under docs/ because plugins ship without this repository. The section moved byte-for-byte, verified by round-trip diff; only its relative links to fix-pass-mode.md were re-based, and default-mode.md keeps a pointer. The confidence rank order (high > medium > unscored > low) had been stated in two uncoupled places. It now has one owner in review's plugin-level severity.md, and both the normalization stage and the convention read it from there. Eight unresolvable citations are also fixed. They were written in a form whose implied base is the plugin root while the real base is reference/, so none of them resolved for any reader; they now use the anchored ${CLAUDE_PLUGIN_ROOT} form that parent-contract.md already used correctly for the same targets. Two heading anchors are dropped in favour of file-level links, because renaming a heading is exactly the refactor the encapsulation contract exists to survive. Three babysit-prs rules are restated in the positive, saying what to do rather than only what not to, and one stops narrating the policy it replaced. write-for-agents' glossary prohibition is narrowed to the consuming project's domain, so the six skills that define their own working vocabulary are no longer caught by a rule whose routing target would refuse them. One defect the sweep itself missed: coupling-model.md carried a Contents section of six plain bullets with no links at all. Fixed, keeping the descriptive glosses as when-to-read cues. The discovery no-split record is corrected to name the test that actually goes red. It cited agents/tool-honesty.test.sh, which reads each agent body for that agent's own claims and therefore goes quiet when the prose moves. The hard pin is scripts/contract.test.sh, which asserts the file contains "single write boundary". A record that names only the quiet suite invites the re-proposal it exists to prevent. Verified: markdownlint clean over 1359 files, changelog parity and bump and order gates clean, plugin contracts validated, detector-findings crosswalk clean, and every relative link in the changed set resolves. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q
…okes, and settle the pointer batch The docs-hygiene sweep's L2 deep-nesting and orphan-spoke findings, plus the adjudication of its largest open L7 row. Anthropic's guidance caps reference nesting at one level from the hub, and four chains exceeded it: audit-pass reached terms.md and finding-identity.md only through run-contract.md, architecture:improve reached its five research/deepening files only through actions/deepening.md, retro reached its catalog only through context/session.md, and course-digest reached screenshot-strategy.md only through context/workflow.md. Each hub now links the leaf directly, with a read condition per row, in the Reference index shape plugin-quality:audit and source-control:commit already use. Every intermediate keeps its own pointer, so a reader arriving mid-chain loses nothing. The audit-pass case was the worst of the four: the file every other leaf opens by assuming was the one furthest from the hub. Two spokes had no inbound pointer at all. implement's context/gotchas.md was dead within its own skill while SKILL.md carried a Gotchas heading inline instead; the eight bullets moved byte-identical and the hub now points at them. One of the eight did not move down. "Config/docs changes still need verification" restated no owning step, and Config is the one mode whose dispatch row loads no context file, so demoting it to on-demand would have weakened exactly the path that needs it. It folded up into the Step 0 Config row instead, where the Config path necessarily reads it. That correction came from a fresh-context verifier, not from the applying agent. suno's drift audit ledger is reachable under a new maintenance section. It stays inside the skill rather than moving to plugin scope because every row in it cites a file inside that skill, and nothing else in the plugin consumes it. The L7 I-1 pointer batch is adjudicated and declined, recorded in the spec so it is not re-derived. The recorded count was wrong: roughly 104 sites across 26 files, not 31 across 16, and the first census was itself wrong until a verifier refuted it, because the detector required the link on the same physical line and about a quarter of these pointers wrap. Half the population is bare pointers whose link text is already the domain term, where the prescribed rewrite yields a tautology, so "apply all in one edit" is not an available option and a partial pass would leave two competing styles in one densely cross-referenced reading path. Two sites are exempt from that decline and are fixed here: both named their target contentlessly, which is the shape write-for-agents gives as its own counter-example. One defect the audit missed, found while validating the table-of-contents pass: coupling-model.md listed all six of its headings as plain bullets with no links, so a reader could see the sections but not jump to one. Verified: orphan count 1 to 0 in both skills by the repo's own detector, with the pre-existing unresolved-pointer count unchanged at 3; content preservation checked bullet-for-bullet on the gotchas move; markdownlint clean; no em dashes added; and a whole-plugin grep for prose-pinning tests before every edit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q
…ise flagging the shape it prescribes The docs-hygiene sweep's L2 blind-pointer findings: 18 of 22 applied, three declined as no longer holding, one left standing on purpose. A blind pointer names a spoke and says what it holds, never when to open it, so a reading agent cannot decide whether to spend the context without following the link. Ten trailing index sections become the shape the repository already used in three places: a Reference index. Load on demand heading over a two-column table whose second column is the load condition. Eight more sites keep their heading and gain an inline when-clause, because they are not spoke indexes at all: one spoke among sibling-skill boundary notes, or a single pointer, where renaming the heading would have misrepresented the section. Two of those clauses are worth naming because getting them wrong would have been invisible. babysit-prs' stuck-checks row states both entry conditions, a non-empty checks.stuck array or a conflicting branch with a short check list. The second limb is the one 0.54.13 added, for the case where checks were never scheduled at all and the array is empty by construction. A row gating on the array alone reads as exemplary and silently reverts that fix. And the six audit skills' shared clean-tree pointer splits by whether each skill states its own unattended branch locally, so a headless run is not steered away from its only guidance. Applying this shape had a cost nobody had noticed: audit-noise exempts ## Cross-references from its enum-list rule but not the headings its own sibling lane rewrites blind pointers INTO. So one lane's fix became another lane's finding, and the exemplar the doctrine points at, plugin-quality:audit's SKILL.md, was already reporting enum-list against its own reference index. The three prescribed headings are now exempt: a two-column path-and-condition table is a routing table, not the consumer roster enum-list looks for. Three sites are declined because the finding no longer holds. claude-ops morning-brief ships no context/ or reference/ directory, so there is no spoke to attach a condition to; observability's real spoke index is the Context ladder section this remediation copies as a model. The spec's cross-lane note about audit-install-state is corrected: it points at a file the blind-pointer table never listed, its two halves disagree on the count, and all four of the P3 replacements it defers to are already present. planning:interview is left standing, and the spec now records why. The blind pointer is real, but it sits inside the section interview-defenses.test.sh pins by content digest to make an unreviewed edit to a safety prohibition fail loudly. Every fix re-baselines that digest, the structurally better one included, since removing the misfiled line changes the digest too. Spending a standing defense on a Tier 3 prose improvement is the wrong trade; whether to pay it is a human's call. Verified: the repo's own detector confirms no enum-list finding falls inside any new index section and audit-noise still passes all 199 of its own checks; 108 links across 16 files resolve with anchors slug-matched against real headings; no inbound anchor pointed at a renamed heading; markdownlint clean over 1359 files; changelog parity, order and bump gates clean; 28 changed skills checked, 0 failed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q
The docs-hygiene sweep's missing-toc finding, applied as one pass rather than 92 edits, because the predicate is mechanical and the treatment is generated. Scope came from the doctrine, and the first attempt at it was wrong in a way worth recording. The rule reads "Reference file >300 lines with no TOC", and an obvious reading of that is "any markdown file over 300 lines without a Contents section". That predicate returns 111 files and adds a table of contents to fifteen skill hubs, which is why no SKILL.md in this repository has ever carried one: the doctrine never asked it to. The detector fixes every SKILL.md at invocation tier, and on-demand IS the reference tier the rule names, so scope here is the detector's own tier classification. That excludes skill hubs by construction instead of by a list someone has to maintain. Seven files are held back rather than treated. The doctrine's treatment reads "Add a TOC at top (or a grep recipe for lookup-shaped content)", and that alternative is load-bearing: a 1,742-line file with three H2s gets nothing from a three-row index. Those seven are listed in the run output and left for a judgment pass. The 305 files in the 100-to-300-line band are not findings, per the two-band treatment that resolves the officially conflicting 100-vs-300 guidance. Anchors follow GitHub's slugger rather than an approximation of it, including the case where an em dash between spaces collapses to two hyphens. The verifier re-derives each file's heading slugs independently and compares, so a slugger bug surfaces as a dangling anchor instead of shipping; it was validated first against all 31 pre-existing hand-written tables of contents in the repository, which it passes clean. That verifier reported one dangling anchor, and it was the verifier that was wrong. sources.md documents `[depth limit](#anchor)` as an example of a quoting normalization, in prose that sits above the first H2 and therefore inside the heading-to-heading window the check was scanning. The fix is in the checker, which now reads only list rows: the generated table was correct. The pass is purely additive: 1,571 lines added and zero deleted across 92 files, so no content can have been lost. The detector confirms the finding count fell from 99 to exactly the 7 held files. Every affected file's plugin is bumped, with the entry naming that plugin's own file count. Before applying, every target was checked against the repository's scripts for a prose pin. Note for anyone repeating that check: this sandbox's grep silently ignores --include, so the obvious command returns the markdown files themselves and reads as a false negative. Use rg with -g. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q
main landed three commits after this branch was cut, and one of them collided: ai-slop released 0.4.1 for the Windows jq carriage-return fix while this branch had already used 0.4.1 for its table-of-contents entry. This conflict is why CI had gone quiet rather than red. GitHub schedules pull_request workflows against the computed merge commit, and with a conflict it cannot compute one, so ci and pr-title were never created for this head while the pull_request_target workflows (do-not-merge, pr-issue-linkage) kept reporting green against the base. Three green checks on a PR that should run a dozen is the tell; a missing run reads exactly like a slow one. Same resolution rule as the earlier collision on this branch: main's entry is released and keeps its number, this branch's entry is unreleased and renumbers above it, and the manifest ends at the highest. So the table-of-contents entry becomes 0.4.2, main's 0.4.1 stays where it is with its body verbatim, and the manifest reads 0.4.2. One thing in main's other commit needed checking rather than assuming. #3344 added scripts/check-purged-em-dashes.sh, a ratchet enforcing zero em dashes on an allowlist of already-purged surfaces, and that allowlist covers plugins/implementation/skills/*/SKILL.md and plugins/overengineering/skills/*/SKILL.md, both of which this branch edits. The table-of-contents pass copies heading text verbatim into its rows, em dashes included, so the two changes could have collided by construction. They do not: the gate passes, 21 declared paths and 30 files scanned. Verified on the merged tree: no conflict markers anywhere, changelog parity, order and bump gates clean, the new em-dash gate clean, plugin contracts across 3023 files, and markdownlint clean at 1359 files. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q
|
Claude finished @kyle-sexton's task in 5m 54s —— View job CI Code Review — in progress
|
|
Claude finished @kyle-sexton's task in 52s —— View job Security review complete ✅
Skip gate triggered — no security-relevant surface. Reviewed the full diff (
No injection surfaces, authorization/access-control code, token/secret/credential handling, or GitHub Actions workflow files are touched. No new command execution, no untrusted-input handling, no permission changes. Per |
|
Last security-reviewed head: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d909d48722
ℹ️ 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".
…postrophe CI's hygiene job went red on typos, and the finding is real rather than a false positive worth arguing with: the table-of-contents row this branch generated for design-level.md carries the anchor #domain-events-in-depth-why-theyre-special, and "theyre" is a word the checker corrects to "their" or "there". The anchor cannot be spelled any other way. GitHub's slugger strips apostrophes, so a heading containing a contraction produces exactly that token, and changing it breaks the link. The heading itself, "Domain Events in Depth (Why They're Special)", is pre-existing and out of this branch's scope to reword. So the generated block carries the repo's markdown spellchecker directive, and the suppression costs no coverage: every row in a table of contents echoes a heading that is still spell-checked where it is actually written, further down the file. A genuine typo in that heading would still be caught there. Scoped to the one block rather than added to _typos.toml, which is the constellation-wide synced policy and says plainly that a repo-specific exception does not belong in it. Verified: typos exits 0 over the tree, markdownlint clean on the file, and all 124 tables of contents still resolve with zero dangling anchors. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q
|
Claude has reviewed this PR 2 times. The lane skips further automatic reviews after 5; deleting this comment resets the count. |
|
Claude finished @kyle-sexton's task in 45s —— View job Security review complete ✅
Skip gate triggered — no security-relevant surface. This lane's relevance gate compares only the commits landed since the last reviewed head (
That's the entire delta: two markdown comments wrapping existing link text, no code, no config, no workflow file, no logic change. No injection surface, no auth/access-control code, no secret/credential handling, no permission changes. Per (For the full PR: the prior review on this thread already covered the base diff through |
Code review in progress
|
…hat contradicted its own commit Two review findings, both correct, both fixed here. Codex, P2. Moving the findings-file contract to plugin scope left ADR 0010 citing `default-mode.md:48` for the shape and `:77` for the coverage fields. Neither line carries that content any more, so a reader following either one lands on unrelated text, and the ADR names a file that is no longer the owner. An earlier pass on this branch reasoned that ADR citations are historical and should be left alone. That was wrong here, and the distinction is worth stating: the repository excludes `docs/adr/**` from its em-dash purge because a decision record is a historical account rather than prose to restyle, which is a rule about STYLE. These two are load-bearing evidence citations inside Context and Consequences, the kind a reader is meant to follow to check the claim being made. A dead evidence link degrades the record; retargeting it preserves exactly what the record was asserting. Both claims were verified present at the new location before the cites were changed. The Claude code reviewer, on the spec. Its "residual" note said the two `troubleshoot.md` pointer sites stay open and "carry forward as their own two-site finding" — while the same commit that shipped the note also fixed both sites. The note also quoted a prescribed replacement that differs from what actually shipped: it says `see [SKILL.md](../SKILL.md)`, the code says `see [Confidence flags](../SKILL.md)`, and the second is the better fix, since naming the target section rather than the file is the half the doctrine's counter-example is actually about. That contradiction is mine. The adjudicating pass wrote the note before the sites were fixed, and both landed in one commit without being reconciled. It matters more than an ordinary stale line, because this document's whole claim on a future reader is that it is the resumable record: a row asserting open work that is already closed would cost that reader a PR to discover, and it undercuts the decay rule the same document leads with. The row now records the applied state, quotes the shipped text, and keeps the reasoning for why these two were never part of the declined batch. Verified: no `default-mode.md:<line>` citation remains anywhere in the tree, the spec's quoted text now matches the file byte-for-byte, typos exits 0, and markdownlint is clean on both files. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q
|
Claude finished @kyle-sexton's task in 2m 51s —— View job Code review in progress
|
Review coverage on the current head, given the lane's infra failureThe automated code-review lane failed with an infrastructure error on Completed reviews, and what they found. Both ran on earlier heads of this branch and both produced a finding, each of which is fixed:
The one item the failed lane left unfinished. Its last progress update showed a background sweep of the remaining ~180 files for broken links, anchor mismatches, and version drift still running when the job died. I ran that sweep independently rather than assume it would have come back clean: That single hit is an illustrative example inside the doctrine's own prose, demonstrating the correct citation form; its target was never meant to exist, it is present identically on Three details in that checker mattered, since a naive version reports noise instead of an answer: fenced blocks and inline code spans are stripped first, because the spec quotes other files' pointer text verbatim and those quotes resolve against the quoted file rather than the quoting one; CI on Generated by Claude Code |
…its declared recall gap (#3460) 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%. Five of the findings are factual defects rather than prose drift: - A fallback that could never fire, at 25 sites, and a 26th that fired and said nothing. 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. verification:confirm is the fleet's one uncapped site, so its fallback did run and emitted an empty string, which under that label is the same misreading by a different mechanism. Proven by execution. - An inline floor whose carriers had drifted into two distinct texts. loop-lane §6 binds three lane bodies on the values; under that scope the values never drifted and the surrounding prose did. Both de-slop shards made the same substitutions, one of which left 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. Bounded to that version, since on an older install the verdict inverts in the less safe direction. - A line-number citation asserting the opposite of the line it named: four surfaces cited check-skill.sh:414 as the hard FAIL for a dropped trigger phrase, where 414 says a trigger move WARNs and never blocks and the err is at 462. main fixed one of the four independently while this branch was in review; the other three now name the check rather than a line. - 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. Three adversarial review rounds were run against the change set itself, and each found real defects in its record-keeping rather than its mechanics. Six changelog entries described a change their own diff did not make. The new detector-findings adopter row claimed a mechanical selection the producer does not have, and its shape count was wrong at the source: audit-noise's context/persist-findings.md and emit-findings.sh header both said six shapes and five declined, where lib/noise-shapes.sh appends eight and detect.sh drives a ninth. Round three re-derived every remaining claim and found eight more: "three distinct texts" was two, the spec overstated what loop-lane 6 binds, the check-skill.sh:414 cluster was declared closed while audit-noise's own script still carried it (the earlier sweep grepped only *.md), "every site carries one wording" was 18 of 20, the four-preambles claim went stale when provenance landed with the merge, one entry misdescribed a file that was internally inconsistent rather than wrong, a contradiction count read 15 where the file records 12, and one roster row attached a quote to the wrong subject. All corrected, each re-verified against the repository first, and each count now points at the artifact that is its source. The full roster, the twelve contradictions the semantic pass surfaced, the four filtered-probe sites deliberately left unnormalized and why, 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. That file states plainly that its recall figures are unreproducible from this repository, because the detector was a session tool and was deliberately not committed. Refs #3362, refs #3380, refs #3178.
…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.
…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.
…reopened it three times (#3478) The L4 roster's Group 2, eight intra-plugin path-form citations, was the last block anyone believed open. All eight were already closed, by 6c7a103 (#3380) on 2026-08-26 -- the same commit that wrote the roster. Its own message names what it did: eight citations written with an implied base of the plugin root while the real base was reference/, none of which resolved for any reader. Verified one at a time against the roster's own text test. All eight now read ${CLAUDE_PLUGIN_ROOT}/skills/<skill>/<path> and all seven distinct targets exist on disk. Zero plugins/** files edited, so no bump and no plugin changelog entry: nothing needed changing. The reason four separate passes each re-derived this roster from scratch is one sentence. #3380 fixed 32 of 34 rows and left its summary asserting that all 34 path:line citations were re-verified on 2026-08-26 and every one resolves -- standing over citations it had just deleted. A fifth-round audit caught that for Group 1, where 22 of 32 were already closed and twelve of those by #3380 itself. This closes the same hole for Group 2. The generalizable rule is now in the sweep spec: a record that fixes findings and updates its own summary in the same commit must update the summary, or the summary outranks the fix for every later reader. The roster's inventory rows are left verbatim, per its own decay rule that the inventory is the part that cannot be re-derived, and per the precedent that #3474 and #3475 both closed rows without touching the file. Only an additive closure stamp is appended. On whether ADR 0018 reaches this class, the honest answer is that its encapsulation half does not. Clause 1 legalises intra-plugin citations and names this exact citing surface. Clause 2 cannot apply, because both files ship inside one plugin so the runtime absence motivating it cannot occur, and the 2026-08-28 amendment's fix-an-address / keep-evidence test divides clause 2 applications only. Only clause 3 reaches Group 2, and clause 3 is a resolvability rule rather than an encapsulation one: had these been open, the remedy would have been path form and nothing else. The clause that earned its keep is the ADR's own observation that proximity did not prevent them. A second derivation making no reference to the roster resolved every citation token in every plugin-level reference/, context/ and agents/ tree plus every plugin README against the base its own form implies: 52 tokens, 0 clause-3 failures. Group 3's two anchors were also closed by #3380. 34 closed, 0 open. No fresh-context verifier reviewed this diff. Nested spawning is unavailable at this depth: the Agent tool is withheld pre-launch, so there is no call to refuse and no refusal string. A second mechanical derivation was substituted and every line reference re-checked against the live tree, which is weaker than a fresh context and is recorded as such. Refs #3477, refs #3476, refs #3475, refs #3474, refs #3469, refs #3468.
…he citations its derivation could not see A fresh-context verifier reproduced the F3 derivation independently (119 tokens, 3 failures pre-fix, 0 post-fix, same three sites) and found three new false statements in the round that produced it. - "#3380 closed 32 of 34 rows in three plugins' files" is refuted by `git show --stat 6c7a103`: the rows span a `.claude/rules/` file, `docs/PLUGIN-PHILOSOPHY.md`, four convention READMEs and several plugins' trees, and the majority are not plugin files at all. - The `config-cascade` sentences were pinned at `:13-14` and `:11-13`; the quoted forms are on 12, 13 and 14 and the assertion runs 14 to 16. A line pin written into the file whose own decay rule says the check is the text. Both re-anchored on text rather than corrected. - "names no invocation at all" is contradicted by the bullet's own text, which names two. It names no invocation as the replacement, which is the claim the ruling actually rests on. The claim that both earlier derivations shared one blind spot is downgraded to what the evidence supports: derivation 1's expression is recorded and does have it, derivation 2's was never written down. An unrecorded expression cannot be audited, only believed, and that is the defect worth carrying forward. The verifier also found three citations the third derivation could not see, because it was `skills/`-scoped and these name no skill. They sit three lines above V-disc-04 through V-disc-06, which this sweep closed: a pass corrected the citations below them and left these standing. Fixed to the anchored form, which is right here because the file is not fetched across the plugin boundary.
…citations no reader could resolve (#3480) Three rounds of correction, each one fixing the round before it. The record asserted that #3380 wrote the L4 roster and fixed 32 of its 34 rows in the same commit, and drew a generalizable rule from it. `--diff-filter=A` returns #3362 and #3380's hunks miss the L4 range entirely, so the rule described an event that did not happen. Replaced with its near-inverse: a commit that closes findings inventoried in a record it did not author still owes that record's summary an update. Six citations resolved against nothing. Three name a SKILL.md directly and three name agent definitions; each earlier derivation was blind to exactly the rows the next one found, because each was built from a token shape rather than from the defect class. The three in discovery sat three lines above rows this sweep closed. A fourth derivation is owed and recorded as owed. The first remedy was wrong for one of them: config-resolution.md is fetched across the plugin boundary, so ${CLAUDE_PLUGIN_ROOT} names the fetching plugin and the anchor was unfollowable for exactly the reader that needed it. It now uses the public invocation. Every later fix checks who fetches the file before choosing the remedy. A fresh-context verifier reproduced the disputed 119-token derivation independently, delta zero, and found three false statements in the correction itself: a miscounted file spread, two line pins in the file whose own rule forbids line pins, and an absolute its own source text contradicts. All fixed. A claim that two derivations shared one blind spot is downgraded to what the evidence supports, since the second derivation's expression was never recorded anywhere and cannot be audited, only believed. discovery 0.16.17, source-control 0.55.30, work-items 0.39.39, songwriting 1.4.18.

No linked issue
Summary
Closes out every remediation the repo-wide
docs-hygienesweep left unapplied in #3362, working from the resumable record that PR graduated intodocs/specs/docs-hygiene-sweep-unapplied-remediations.md. Four lanes, 196 files, 28 plugin bumps.The spec's own decay rule says the status column is its weakest part, and re-measuring proved that twice over. The L2 split lane had nothing left to do — the spec listed 13 outstanding, and every one had already landed in #3362; no
SKILL.mdor agent file in the repository now exceeds the 500-line body cap. And the largest open row, L7'sI-1, turned out to be 104 sites across 26 files rather than the recorded 31 across 16 — a batch that was then declined on its merits.Fix
L4 encapsulation, 32 of 34. Twenty-two cross-plugin citations reaching into another plugin's skill-private tree now name the owning skill's public invocation (
/review:fanout fix,/skill-quality:check,/ai-slop:audit,/autonomy:setup,/claude-ops:lanes,/code-tidying:tidy,/source-control:setup,/session-flow:workflow,/docs-hygiene:write-for-humans). Plugins install independently, so a path into another plugin's private surface can be genuinely absent at read time; a slash command cannot be.Two of the 34 could not be fixed that way, and the reason drove the one structural change here. Both cite the findings-file shape, which is not behavior a reader can be routed to but a spec a third-party detector must read to produce a file the fix action will consume. Naming a command hands a producer a command instead of the contract. So the contract moved rather than the citation:
plugins/review/reference/findings-file-shape.md, at plugin level and outside every skill directory, where the repo-level convention can cite it legally. It stays inside the plugin rather than moving underdocs/because plugins ship without this repository. The section moved byte-for-byte, verified by round-trip diff.Also eight citations written with an implied base of the plugin root while the real base was
reference/— none of them resolved for any reader — and two heading anchors dropped to file-level links, since renaming a heading is exactly the refactor the encapsulation contract exists to survive.L2 structure. Four spoke chains flattened to one hop from their hub (
audit-pass,architecture:improve,retro,course-digest), each intermediate keeping its own pointer so a reader arriving mid-chain loses nothing. Two orphaned spokes reconnected. Eighteen of 22 blind pointers now carry a when-to-read clause, ten as theReference index. Load on demandtable this repo already used in three places. And 92 long reference files gained a## Contentsindex.L5 / L6 / L7. Three
babysit-prsrules restated in the positive; thewrite-for-agentsglossary prohibition narrowed to the consuming project's domain, so the six skills defining their own working vocabulary are no longer caught by a rule whose routing target would refuse them.Two detector-level defects found while applying, not by the audit.
audit-noiseexempted## Cross-referencesfrom itsenum-listrule but not the headings its own sibling lane rewrites blind pointers into, so applying one lane's fix converted it into another lane's finding — and the exemplar the doctrine points at was already self-flagging. Andcoupling-model.mdcarried a## Contentssection of six plain bullets with no links at all.Verification
Fresh-context verifiers changed the outcome four times rather than rubber-stamping:
stuck-checks.md's new row gated on a non-emptychecks.stuckarray alone. The second limb, added in 0.54.13, exists for the case where checks were never scheduled and that array is empty by construction. Both limbs are now stated.I-1count was rebuilt paragraph-first after a verifier refuted the first pass, which had required the link on the same physical line while about a quarter of those pointers wrap.Independent re-measurement of every agent's claims: orphan count
1 → 0in both skills with the pre-existing unresolved-pointer count unchanged at 3; the missing-toc finding99 → 7, the 7 being exactly the held-back set; the TOC pass +1,571 lines and 0 deleted, so content loss is not possible;audit-noisestill passing all 199 of its own checks with noenum-listfinding falling inside any new index section.Three suite failures, all environmental and none attributable to this branch, each root-caused rather than waved off as flake:
discovery/check-coverage-complete.test.shandsource-control/worktree-claim.test.shboth assert a permission denial, and this sandbox runs as uid 0, which bypasses it. Neither suite nor its target differs fromorigin/mainin this diff, so running them here is running them at main.repo-fleet-hygiene/apply-plan.test.shfailed onfatal: failed to write commit objectafter the commit-signing server returned 503 mid-run. It passes in isolation, and this branch touches no file in that plugin.Related
One finding is deliberately left standing, recorded in the spec with its reasoning: the blind pointer in
plugins/planning/skills/interview/SKILL.md. It sits inside the sectioninterview-defenses.test.shpins by content digest to make an unreviewed edit to a safety prohibition fail loudly. Every fix re-baselines that digest — including the structurally better one of moving the misfiled reference row out of a prohibitions section, since removing the line changes the digest too. Spending a standing defense on a Tier 3 prose improvement is the wrong trade, so whether to pay it is left as a human's call.Two more declines are recorded rather than silently dropped. L6's single compression finding fails its own skill's ship rule (
compressreverts at<3% AND 0 semantic-loss, and dropping one word is ~0.1%), andai-slop's stacked-hedging detector returns zero on that file, so it has no justification outside the skill that would revert it. L7'sI-1batch is declined because roughly half its 104 sites are bare pointers whose link text is already the domain term, where the prescribed rewrite yields a tautology (Hook: see [hook](hook.md).); "apply all in one edit" is therefore unavailable, and a partial pass leaves two competing styles in one densely cross-referenced reading path.The spec's cross-lane note about
audit-install-stateis corrected: it pointed at a file the blind-pointer table never listed, its two halves disagreed on the count, and all four P3 replacements it deferred to are already present.Refs #3362,
docs/specs/docs-hygiene-sweep-unapplied-remediations.md,docs/specs/docs-hygiene-sweep-yield-measurement.md,docs/adr/0018-treat-the-plugin-as-the-encapsulation-boundary-for-skill-citation.md,.claude/rules/vendor-docs-are-not-style.md.🤖 Generated with Claude Code
https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q
Generated by Claude Code