docs: run all eight docs-hygiene audits repo-wide, and fix the four detector defects that blocked them - #3362
Conversation
Inventory every tracked markdown file outside the vendor tree (1302 files, ~17.0 MB), classify each by load tier and audience, and partition into 13 cohesion-first groups. Records the wave structure the sweep runs under: all eight docs-hygiene lanes audit read-only in parallel, findings reconcile into one per-file edit plan, then remediations apply in dependency order so deletions precede splits, splits precede SSOT extraction, and citation rewrites land on final paths. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q
…0.21.12) detect.sh matched the fence delimiter, called flush_negation, and only then read BASH_REMATCH[1]. flush_negation runs its own [[ =~ ]] matches, which overwrite BASH_REMATCH, so under set -u the stale read aborted the entire invocation. Any file where a fenced block opens directly after paragraph text triggered it. A repo-wide scan died on the first such file and reported the files it had reached as if that were full coverage, which is the worse half of the bug: the partial result looks complete. Capture the delimiter before the flush. Regression test asserts the run exits 0 and still summarises the file; it fails against the unfixed script. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q
…ise headings (0.21.13) The fence fix in 0.21.12 left the ATX-heading branch with the identical shape: match, call flush_negation, then read BASH_REMATCH[2]. The full-corpus scan got 1024 files further before dying on a multi-line HTML comment followed by a heading. The heading branch has the worse failure mode of the two. It aborts only when the clobbering match leaves fewer than two groups bound; otherwise it reads a different match's text and hands it to the section-exemption check, so an exempt section silently stops being exempt and the run still exits 0. Capture before the flush. The regression test is built from the file that actually crashed rather than a guessed shape, because a hand-written fixture reproduced neither failure mode. Also swept every remaining BASH_REMATCH read in the plugin's detectors and the shared lib. All read consecutively with no intervening call; nothing else needs changing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q
Full-corpus detect.sh run, now that it completes. 1218 files scanned, 84 CHANGELOGs skipped by design, 5858 raw candidates. Notes what the two dominant shapes actually mean: enum-list at 3.7 per file and negation at 1229 in a repo about guardrails are recall-biased cues, not 5858 defects. The lane triages them; it does not copy them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q
…spokes (0.21.14)
md_links() opens with a grep that exits 1 when a file has no markdown links,
which is the normal case rather than an error. Under set -euo pipefail that
status propagated out of ref_candidates() before it reached its backtick
branch, so every hub that cites its spokes only in backticks had all of them
reported as unreachable orphans.
Measured over all 243 SKILL.md files, orphans drop from 133 to 51. The
remaining 51 are the detector's other limits, it cannot resolve
${CLAUDE_PLUGIN_ROOT}, @./, or directory pointers, and those need judgment
rather than a shell fix.
The suite already covered backtick citation, but its fixture hub also carried
markdown links, so md_links succeeded and the broken path never executed. That
is why a real defect sat under a green test. The new case gives the hub
backtick citations and nothing else.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q
L1-derivability, L2-progressive-disclosure, L3-ssot and L4-encapsulation each covered all 1302 files and wrote remediation specs. Read-only; no source file was touched. Headline counts: L1 3 actionable of 1302, L2 200 findings, L3 26 duplication clusters proposing zero new artifacts, L4 89 encapsulation violations. Three findings reframe the sweep rather than feed it: - The repo already swept derivability and formally recorded that this judgment class is unrulable (docs/specs/d1-model-already-knows-measurement.md). L1's near-empty result is the expected one, not a failed pass. - Plugin contracts are carried inline at every adopting site on purpose, because plugins ship without the marketplace repo. Repetition that reads as duplication is often load-bearing portability, which is why L3 mints nothing. - PLUGIN-PHILOSOPHY.md prescribes the cross-skill citation form the public-surface contract forbids, and that doctrine conflict produced 30 of L4's 89 violations. Recorded as escalation E1; those 30 are not remediable as call-site defects. Adds remediation/escalations.md for the three decisions that block specific wave 3 edits, and corrects PLAN.md: lane leads cannot spawn subagents in this environment, so the tree is two levels and every lane substituted corpus-wide mechanical detection for fan-out. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q
Partial. Both lanes are still running; this commit exists so the work survives the container rather than because either lane is done. A later commit supersedes it with their complete findings. L6-compress has its full per-file classification down: scan-raw.md carries one adjudicated row per corpus file with the six signals and the reason, and classification.md is the summary over it. Early reading is that the corpus is mostly SKIP, which matches the skill's own empirical 2 to 3 percent bound on author-time-disciplined instruction surfaces. L7-write-for-agents has its predicates.md down, the doctrine rendered as testable predicates traced to the passages they come from, which is the part that had to exist before any conformance finding could be verified. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q
Still partial; both lanes continue to write. Same reason as the previous checkpoint, the container is ephemeral and uncommitted lane output is lost with it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q
…-tests The derivability rubric requires a blind re-derivation test before any delete, and no lane can spawn one. Ran both from the orchestrator. They split. repo-hygiene/skills/clean/reference/ecosystems.md passes. The agent rebuilt tier membership, protected classes and sweep semantics from cleanup-config.md and the scripts, cited to lines, and independently surfaced CHANGELOG v0.10.4 recording that a prior fresh-context agent did the same. The delete stands and finishes a conversion the repo already started. ai-briefing/skills/generate/context/execution-flow.md fails. The agent answered "partially" and named what it could not recover; four of those rules turn out to live only in that file, with zero hits across SKILL.md and references/: re-run idempotency, outbound timeout and partial-failure visibility, explicit empty-bucket emission, and registry-write ordering. Deleting it drops four behavioral rules. Verdict downgraded to salvage-then-remove. The gate earned itself on the first use. Two live defects fell out and are recorded rather than fixed here: references/providers.md is cited twice and does not exist, and the S4 / Step 4.5 / Step 5 stage vocabulary used across the references is defined in no file at all, including the one suspected of holding it. Also records an error in the orchestrator's own brief: the first spot-test excluded the subject file but not this sweep's findings directory, so that agent read the verdict it was meant to test blind. Its conclusion survives on independent primary sourcing, and future briefs must exclude both. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q
E1, the PLUGIN-PHILOSOPHY versus public-surface-contract conflict. Neither document is wrong; they assume different units. The contract protects a skill directory moved on its own, but nothing here ships that way: the plugin carries the version, is what a consumer enables, and its skills always travel together. The contract itself says a consuming repo may layer its own conventions on top, and PLUGIN-PHILOSOPHY is that layer. So intra-plugin sibling citation in the anchored form is legal here, cross-plugin citation into skill privates stays a violation, a bare relative cross-skill path stays a defect on the philosophy doc's own reasoning, and heading anchors stay private even intra-plugin. Both documents need an edit in wave 3 so the next audit does not re-raise the same findings. E4, write-for-agents disclaiming SKILL.md. The disclaimer is about authorship, not prose. Reading it to cover sentence quality would leave 250 files, the repo's largest agent-facing surface, governed by no authoring doctrine at all. Prose inside an existing skill body is in scope; frontmatter, actions and structure are not. The INTRA versus CROSS split of all 89 violations is being measured rather than assumed before either ruling is applied. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q
…dings The phrase survived the repo-wide style pass in 34 plugin READMEs because it is generated, not written. Every occurrence sits inside a BEGIN GENERATED block emitted by sync-plugin-options-docs.py, and ci.yml's plugin-options-docs-gate runs that script with --check, so hand-editing the READMEs fails CI and reverts on the next sync. Fixed at the two template string literals and regenerated. Verified: the gate command passes, exactly 34 READMEs changed, every changed line falls between a BEGIN and its matching END marker, and the word-diff across all 34 is the single token "in order" removed, nothing added. One further occurrence at plugins/ai-slop/README.md:75 is left alone; it sits outside any generated block and quotes the phrase as an example of the tell. The generator diff also carries four line-wrapping hunks from the repo's own ruff-format hook, which re-applies them on any edit of the file. No token content changed and no workflow runs ruff format, so they are inert; they are in the diff because the hook makes them unavoidable, not because they were wanted. L5-noise: 5858 mechanical candidates reduced to 10 findings. enum-list, 4568 of those candidates, yields zero: a census showed the shape's canonical form has no instances in this corpus, so the defect is absent rather than unmatched. The lane declined to extrapolate its negation sample to the unread population, citing the d1 measurement on how this repo's house style writes load-bearing rules as bare negative imperatives. Also repairs 33 code spans across the L3 and L4 findings where verbatim source indentation sat inside the span and tripped MD038. Repo-wide markdownlint is clean at 1425 files. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q
…laimer Three file-level changes, the whole actionable output of a lane that audited all 1302 files. ai-briefing: context/execution-flow.md carried four behavioural rules found nowhere else in the skill, so they move into SKILL.md at the steps they govern before the file goes. The applier caught a contradiction the audit missed: references/slide-generation.md:89 states the opposite empty-bucket convention for slide output, so the salvaged rule is scoped to the markdown briefing rather than stated globally. repo-hygiene: reference/ecosystems.md removed. A fresh-context agent rebuilt its whole content from cleanup-config.md and the scripts, and the plugin's own v0.10.4 changelog records a prior agent doing the same. Its one live inbound citation is repaired; the changelog mention is history and stays. claude-ops: issue-templates.md was a 5.6 KB snapshot of external content, dated, with no regeneration path, whose only consumer is told twice not to trust it. Reduced to a pointer, keeping the one fact that was local rather than copied. Both consumer lines reworded, since they described it as a snapshot. Separately, write-for-agents disclaimed SKILL.md entirely, which would leave 250 files, the largest agent-facing surface here, governed by no authoring doctrine. The disclaimer now separates authorship, which stays with skill-authoring and skill-quality, from prose inside an already-authored body, which is exactly what this skill governs. A fresh-context verifier is checking this work independently. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q
Partial. The lane is still running; groups L and M and the roll-up are not down yet. Committed because the container is ephemeral, not because the lane is done. Carries the two files that had to exist before any finding could be trusted: standard-resolution.md, recording that the lane resolved to this repo's own rewrite-guide rather than the skill's named defaults, and predicates.md turning the doctrine into testable predicates traced to their passages. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q
Records the dependency chain and why each step precedes the next, the 157 files carrying findings from more than one lane, and the five hazards that cross every lane, including the generated blocks that silently revert hand-edits and the 62 fixture rows that are deliberately defective test data. Merges the four in-file prose lanes into a single pass. They all edit prose in files that survive the structural steps, so running them separately would mean four passes over one file with three chances to collide. Their combined volume is under a hundred findings, so one editor per file costs nothing. Also records what deliberately does not get applied, so the next sweep does not re-derive it: no new SSOT artifact at any multiplicity, no article drops against a measured 9-of-9 revert record, and no extrapolation of the noise lane's negation sample, whose 11.7 percent precision would mean roughly 910 spurious edits to instruction surfaces. The sweep's main empirical result is the rejection rate, not the findings. One lane rejected 5847 of 5858 candidates and its largest shape yielded zero. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q
The classification pass was told to report evidence contradicting the ruling. It found three real problems and all three stand. The clause rejecting bare relative cross-skill paths is withdrawn. It leaned on PLUGIN-PHILOSOPHY condemning a bare context/... path written with no ../ prefix. Zero of the 89 citations use that shape; the 33 relative intra-plugin citations all compute a correct ../ path and all 33 resolve. The clause was keeping 49 findings alive on a misreading. Replaced with the defect that is real: a citation that does not resolve from its own base, whatever its form. The ruling narrows the contract rather than reading it. The licensing sentence was quoted at half length; its second clause limits what a layered convention may do, and the portability guarantee is stated at the skill-directory level, not the plugin level. The ruling still stands on the repo's actual distribution shape, but it is recorded as a deliberate narrowing. The blast radius is 65, not the 30 first claimed. Intra-plugin as defined also legalises 16 plugin-README citations, which the contract names explicitly as external consumers. Accepted deliberately now rather than absorbed unnoticed. Two findings nobody predicted. Not one cited path is missing from disk, so the silent breakage the contract guards against has not happened here at all. And ten citations point at a target that exists via an address that does not reach it, including one plugin citing the same three targets both correctly and unresolvably in two of its own files. Settled: 55 dissolve, 34 remain. The convention goes in docs/conventions/, not into the contract file, which ships inside a plugin to other repositories and would export this repo's relaxation to every consumer. Also adds the wave 2 reconciliation plan: the dependency chain, the 157 multi-lane files, the cross-lane hazards, and what deliberately does not ship. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q
…diations The verifier passed all five acceptance criteria, then read the diff for damage the criteria would not catch and found four things. Three were introduced by the remediation itself. action-create.md:38 claimed issue-templates.md "names the live source only". False, and it contradicted line 141 of the same file. Worse, it gave a reader a reason never to open the one file that now holds the regression-field rule, which is the single fact the pointer conversion had to preserve. SKILL.md gained two names for one concept: step 5 deduplicates on "normalized event identity" while the salvaged sentence at step 7 said "canonical event identity". The salvage imported the deleted file's vocabulary instead of the vocabulary already six lines above it. Step 7 now points at step 5's key explicitly. issue-templates.md disclaimed ownership of auto-detection in one paragraph and then supplied an auto-detection rule in the next. The disclaimer now scopes to the fields the session reads from its own context, and says why the regression field is not among them. The changelog said all four salvaged rules existed only in the deleted file. Three did; the fourth was half-present, since SKILL.md already stated that partial collection stays visible but not that requests carry a timeout. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q
… findings Nine of the progressive-disclosure audit's 33 split findings, across claude-config, context-guard, disk-hygiene and source-control. Each body sat near the 500-line ceiling with on-demand material inlined, which a SKILL.md pays for across the rest of a session once it triggers. Every pointer states the condition that sends a reader to the spoke rather than only that the spoke exists, so an oversize body is not traded for a blind pointer, which is a defect the same audit files against other files. Content preservation was checked line by line rather than by eye: for each pair, every non-blank line removed from the hub was matched against the spoke. The lines that did not match are relative-link targets that had to change depth and prose reworded to read in its new home. One real defect surfaced that way and is fixed here: context-guard's spoke carried a table of contents pointing at ../../SKILL.md anchors for five sections that had just moved into the spoke itself, by a path resolving to no file at all. They are in-page anchors now. All nine hubs and all nine spokes were then checked for dangling in-page anchors and unresolvable relative links. Zero of either. L8-write-for-humans completes the audit wave. It resolved to the repo's own style guide over the skill's named defaults and recorded eleven overrides, notably declining to touch 104 consistent Latin abbreviations. Its find is 18 sentences ended inside a parenthetical, all in plugin READMEs and none in docs/. It judged all 84 changelogs once as a class rather than file by file, and reported that three of its predicates fired 102 times and adjudicated to zero. The remaining 24 split findings are not applied. The applier stopped on a rate limit partway through and this commit completes only what it had finished. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q
… doctrines The classification pass was asked to say if the evidence contradicted the ruling. It did, on four counts, three of which are errors in the ruling's reasoning rather than its conclusion. The conclusion survives and is now evidenced rather than asserted: 71 plugins, 71 manifests carrying one version each, 71 marketplace entries, no per-skill manifest, and no .claude/skills directory at all. Skills here do not ship independently. What was wrong: The ruling claimed a bare relative cross-skill path is broken on the philosophy doc's own reasoning. That was a category error. The doctrine condemns one shape, a bare context/x.md with no ../ prefix, and zero of the 89 violations use it. All 33 relative intra-plugin citations compute ../ correctly and every one resolves. They are not defects. What is actually broken is a shape the ruling never named: 8 citations in skills/<s>/<path> form written from a plugin-level reference directory, where the implied base is the plugin root and the real base is the citing file's directory. The ruling quoted the contract's "may layer its own conventions on top" as authorizing it, omitting the rest of the sentence, which reasserts the detector's surfaces against whatever is layered. That sentence permits the ruling; it does not authorize it. The ruling said it blocked 30 violations and left 59 alone. The real split is 65 INTRA and 24 CROSS, because every plugin README and plugin-level doc citing its own plugin's skills is intra-plugin. Sixteen plugin README citations become legal in one move, and the contract names READMEs explicitly as external consumers. That is the substantive consequence and the ruling never mentioned it. Recorded and not argued from: across 89 violations and 35 leaked skills, no cited path is missing. The contract's failure mode is real in principle and unobserved here, which cuts against urgency in both directions. Eight of the ten unresolvable citations are intra-plugin, so proximity prevented nothing. Both documents are edited so the next audit does not re-raise the dissolved 65. PLUGIN-PHILOSOPHY gains the cross-plugin limit it omitted, which is how 30 call sites came to exist, plus the heading-anchor exclusion. The contract gains the bounded plugin-monorepo carve-out and says plainly that an undeclared repo gets the unrelaxed rule. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q
|
Claude finished @kyle-sexton's task in 3m 20s —— View job Code review complete
No blocking findings. This is the What I verified directly (not just author-claimed):
I did not execute the shell test suites or the markdownlint/typos/link gates myself (sandboxed Bash here rejected the multi-part invocations); the PR body's gate output is author-claimed and unverified by me. |
|
Claude finished @kyle-sexton's task in 2m 21s —— View job Security review complete
No security issues found. This PR is almost entirely markdown documentation (audit findings, remediation records, plan docs) with a small tail of narrowly-scoped code changes. I reviewed every non-markdown change in the diff:
Nothing here touches authn/authz, injection surfaces, secret handling, or CI trigger/permission configuration, so there's nothing to flag under this lane's mandate. |
|
Last security-reviewed head: |
|
Claude has reviewed this PR 5 times. The lane skips further automatic reviews after 5; deleting this comment resets the count. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0e5dddefe9
ℹ️ 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".
…afety contract Two of the three red gates on this PR. changelog-parity-gate --check-bump: 28 plugins had a modified file with no version bump. All 28 changed exactly one line, inside the generated options block, when the generator stopped emitting "in order to". Each gets a patch bump and an entry saying so. The repo's rule is that any change to shipped files under plugins/<p>/ needs both, and the local --check run I had been using does not cover it; --check-bump does. skill-quality-gate: the babysit-prs split broke three assertions in that skill's own scripts/tests/test_skill_contract.py. They require the merge-readiness paragraph to be in SKILL.md, and the test comments cite #601 for the reason: the body must name the merge gate's `ready` field as the sole authority for a merge-ready claim, never the finding-classification gate's READINESS_OK. The split is reverted rather than the test relaxed. The content it moved is the guarded-mutation gate catalog, so an agent that never opens the spoke could call a PR merge-ready on the wrong signal, which is the exact failure #601 closed. Line count is the weaker consideration when the inlined content is the safety contract. The other four source-control splits stand, and the changelog now records babysit-prs as audited and deliberately not split, with the reason, so the next sweep does not re-propose it. 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 23s —— View job Code review complete
No blocking findings. This is the What I checked directly, not just author-claimed:
I did not execute the shell gates myself ( |
|
Claude finished @kyle-sexton's task in 48s —— View job Security review complete
Skip gate: no security-relevant surface in the new commits. Per the lane's own tracking comment, this PR was already security-reviewed through
No workflow files, permissions, secrets handling, or injection-relevant code paths are touched by this commit. Nothing here changes the "no security issues found" conclusion from the prior review pass, and I'm not re-flagging anything already covered in that pass. |
…the slice The rest of wave 3, plus the graduation this repo's own topic-docs convention requires before merge. Splits. 24 new spokes across 16 skill bodies and two long docs, each pointer carrying the condition that sends a reader to the spoke. Content preservation was checked line by line: 2415 lines left their hubs, 2347 matched verbatim, 264 after re-indent, and the 79 that differ are heading promotions, relative-link re-bases for the extra directory level, and pointer rewordings. Four splits were audited and deliberately not applied, which is the more useful half of this change. Three would have moved statements that a test pins to SKILL.md by path, and one would have put three prohibitions behind a pointer. source-control/setup is reverted here for that reason, the third revert in that plugin after babysit-prs and babysit-loop, so its changelog now says plainly that this plugin pins body prose by test on purpose and that a splitter must grep scripts/tests/ before proposing one. A line-count audit cannot see a pin. The babysit-loop split committed earlier had already broken four assertions in babysit-prs's contract test. Its promotion-evidence gate is back in the body. Prose. 65 of the 83 in-file findings applied across L5, L7 and L8: 18 sentences that ended inside a parenthetical, 15 generated blocks filed under a findable heading, 14 backtracking rewrites, and the rest. What was not applied matters as much: one finding was void because its quote no longer existed, three were dropped when the flagged claims failed verification (two invented table glosses were checked against the skill and found wrong), one was rejected for sitting inside a CI-checked generated block, and 31 songwriting pointers were left alone as quoted third-party teaching material. Graduation and prune. docs/topics/<slug>/ is Contract tier here, committed on a task branch and pruned before merge, so all 114 files go and three documents carry what was durable: ADR 0018 for the encapsulation boundary, including the four corrections the measurement forced rather than only the conclusion; a spec recording the yield measurement so a future sweep does not repeat it; and a spec holding the unapplied remediation set with its path:line citations intact. Six further ADRs, 0019 to 0024, come out of MIGRATION-PLAYBOOK.md the same way. All 155 citations across the three documents were checked to resolve, which caught 12 unresolvable ones of their own. Also closes the gap Codex found: the doctrine edit had reached the supporting contract but not the live filter taxonomy in audit-encapsulation/SKILL.md, so the audit would still have re-reported all 65 dissolved citations. The taxonomy now carries the intra-plugin filter, gated on the consuming repo having actually declared the convention. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q
|
Claude finished @kyle-sexton's task in 7m 54s —— View job Code review complete
No blocking findings. This is the Given the size, I split verification across two independent passes rather than spot-checking at random:
One minor, non-blocking finding posted inline: I did not execute the shell test suites, markdownlint, or other CI gates myself (sandboxed Bash here rejects most multi-part invocations); I verified the claims above by reading source/diffs directly rather than trusting the PR body's gate output. |
|
Claude finished @kyle-sexton's task in 1m 9s —— View job Security review complete
Skip gate: no security-relevant surface in the new commit. Per the lane's tracking comment, this PR was already security-reviewed through
This is a pure content-restructuring commit (markdown splits, doctrine edits, changelog/version bookkeeping) with zero code, workflow, or permission surface. Nothing here changes the "no security issues found" conclusion from the prior review passes on this PR, and there's nothing new to flag under this lane's mandate (logic flaws, authz/access-control gaps, injection surfaces, secret handling, or Actions security patterns). |
…ing it (0.39.27) Review catch on the new re-decompose spoke. It cited the container-lifecycle drift doctrine by name with no link, while every other cross-file reference in the same batch of spokes uses markdown link syntax. The target sits in the same context/ directory and was split out alongside it in the same commit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q
|
Claude finished @kyle-sexton's task in 58s —— View job Code review complete
No blocking findings. This is the This commit directly implements the finding from this lane's own prior review round (and Kyle's reply confirming the plan): Verified directly, not just author-claimed:
Nothing else changed since the last reviewed head, so there's nothing further to flag. |
|
Claude finished @kyle-sexton's task in 40s —— View job Security review complete
Skip gate: no security-relevant surface in the new commit. Per the lane's tracking comment, this PR was already security-reviewed through The commit touches exactly three files:
No workflow files, scripts, or manifest permission scopes are touched. Nothing here changes the "no security issues found" conclusion from the prior review passes on this PR, and there is nothing new to flag under this lane's mandate (logic flaws, authz/access-control gaps, injection surfaces, secret handling, or Actions security patterns). |
… prose plugin-gate was red on this. worktree's context/nesting-invariant.md extraction carried the upstream-drift verification stamp out of SKILL.md, and skills/worktree/nesting-invariant-ssot.test.sh declares OWNER_REL="skills/worktree/SKILL.md" then asserts that body is the single owner of the measured claim. Its header gives the reason: the defect it was written against was the same statement drifting across thirteen sites, and one owner with twelve pointers is the fix. Splitting the claim into a spoke recreates exactly the drift the test exists to stop, so the test is right and the split was wrong. Fourth revert in this plugin for the same underlying reason. Rather than fix this one and wait for CI to find the next, I swept every split on this branch: for each of the 22 split skills, searched its whole plugin for a test that reads the hub SKILL.md. Five came back flagged; four ran clean (claude-config audit-instructions, disk-hygiene clean, planning plan, plugin-quality audit, 119 + 317 + 9 + 23 checks) and only worktree was real. The plugin-wide search is the point. The worktree pin sits in a *.test.sh beside the skill; the setup pin sits under a different skill entirely, in babysit-prs/scripts/tests/. A skill-local grep would have missed one of them. The changelog now says to search the plugin, not the skill directory. Two suites fail locally and are not this PR's: check-coverage-complete and worktree-claim both assert a permission or lock failure that root does not experience, so they fail in this container and pass on a non-root runner. Both fail identically with this branch's changes stashed, and this branch touches neither the tests nor their subjects. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q
The repo ships many pytest suites, and running them drops a .pytest_cache directory carrying its own README.md. Neither ignore list covered it, so after running the repo's own Python tests, the repo's own markdown lint fails on a file no one authored: .pytest_cache/README.md:1 MD003/heading-style [Expected: atx; Actual: atx_closed] Both lists already had the neighbours. .gitignore carries __pycache__/ and .venv/; the linter's ignores carry **/.venv/** under a comment reading "Build, dependency, and cache trees, not authored markdown". A pytest cache is exactly that class and was simply missed. Two entries are needed rather than one, because the linter globs the filesystem rather than reading git: with only the .gitignore entry, git stops seeing the directory but markdownlint still lints it. Verified by reproducing: run a pytest suite, confirm the cache appears, confirm git reports zero untracked entries for it, and confirm markdownlint reports 0 issues across 1358 files where it previously failed. This also re-triggers CI. The ci.yml run for 46a258a was never scheduled by GitHub, fifteen minutes after the push and with the other workflows on that same push having fired normally, so there was no run to re-run. This is a real fix carried on its own merits, not an empty commit to kick the pipeline. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q
main landed a repo-wide behavior-preserving simplification sweep (#3379) across 203 files, which collided with this branch on 43 of them: one plugin.json and one CHANGELOG.md for each of 23 plugins. This conflict is why CI went quiet rather than red. GitHub schedules pull_request workflows against the computed merge commit, and it could not compute one, so ci, pr-title, claude-review and claude-security-review stopped being created for the last two heads while the pull_request_target workflows (do-not-merge, pr-issue-linkage) kept firing normally against the base. A missing run reads exactly like a slow one, which is what made it worth chasing. Every collision is the same shape. main took the next patch after the base version for its sweep entry, and this branch had already used that same number for a different change. Resolution rule: main's entry is released and keeps its number; this branch's entries are unreleased and renumber above it, with the manifest ending at the highest. So docs-hygiene's 0.21.12 through 0.21.16 become 0.21.13 through 0.21.17, main's 0.21.12 stays, and the manifest reads 0.21.17. Resolved by script rather than by hand across 43 files, and the script refuses rather than guesses: it requires main's new entries to be contiguous patches sitting directly on the base version, and reports anything else for a human. It refused three plugins on the first pass, correctly, because ai-briefing, repo-fleet-hygiene and testing had both sides land on the same version number, so their manifests never conflicted and the slice boundary cut inside our own block. Anchoring that boundary on the base entry rather than on main's newest fixed it, and all 23 then resolved. Verified: no conflict markers anywhere in the tree, every one of the 23 manifests agrees with its changelog's top entry, both changelog gates pass including the duplicate-version check that this collision would have tripped, markdownlint clean at 1358 files, typos, editorconfig, the plugin-options gate, plugin contracts across 3018 files, and the contract-slice prune gate. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q
|
Claude finished @kyle-sexton's task in 4m 25s —— View job Code review complete
No blocking findings. This is the What I checked directly, not just author-claimed:
Nothing else changed since the last reviewed head that needs re-review. |
|
Claude finished @kyle-sexton's task in 5m 29s —— View job Security review in progress
|
The merge resolution renumbered ai-briefing, repo-fleet-hygiene and testing to 0.7.10, 0.23.13 and 0.7.8 in both their changelog and their manifest, but only the changelogs reached the commit. Those three were the plugins where both sides picked the same version number, so their manifests never conflicted, and the staging step added the unmerged paths only. The result was a commit whose changelog documents a version above its manifest, which is exactly what check-changelog-parity --check rejects. My own verification missed it because it read the worktree rather than HEAD, so it inspected the corrected files instead of the committed ones and reported zero mismatches. Verifying a commit means asking git what the commit contains, not asking the filesystem what is lying around next to it. The check below is re-run against HEAD. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q
|
Claude finished @kyle-sexton's task in 1m 46s —— View job Security review complete
Skip gate: no security-relevant surface in the new commits. Per this lane's tracking history on the PR, this branch was already security-reviewed through commit
None of these touch workflow files, scripts with executable logic, authn/authz, injection surfaces, or secret handling. This doesn't change the "no security issues found" conclusion from the prior review passes on this PR, and there is nothing new to flag under this lane's mandate. |
…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
…give the findings-file contract a legal home (#3380) No linked issue ## Summary Closes out every remediation the repo-wide `docs-hygiene` sweep left unapplied in #3362, working from the resumable record that PR graduated into `docs/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.md` or agent file in the repository now exceeds the 500-line body cap. And **the largest open row, L7's `I-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 under `docs/` 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 the `Reference index. Load on demand` table this repo already used in three places. And 92 long reference files gained a `## Contents` index. **L5 / L6 / L7.** Three `babysit-prs` rules restated in the positive; the `write-for-agents` glossary 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-noise` exempted `## Cross-references` from its `enum-list` rule 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. And `coupling-model.md` carried a `## Contents` section of six plain bullets with **no links at all**. ## Verification Fresh-context verifiers changed the outcome four times rather than rubber-stamping: - **A rule was about to be demoted out of the only path that reads it.** Moving eight gotcha bullets to an on-demand spoke was safe for seven, which each restated a rule an owning step already carried. "Config/docs changes still need verification" restated nothing, and Config is the one mode whose dispatch row loads no context file. It folded up into the Step 0 Config row instead, staying invocation-loaded. - **A pointer row that looked exemplary silently reverted a shipped fix.** `stuck-checks.md`'s new row gated on a non-empty `checks.stuck` array 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. - **A census was wrong by ~30%.** The `I-1` count 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. - **A safety pin was about to be spent on cosmetic prose.** See the declined site below. Independent re-measurement of every agent's claims: orphan count `1 → 0` in both skills with the pre-existing unresolved-pointer count unchanged at 3; the missing-toc finding `99 → 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-noise` still passing all 199 of its own checks with no `enum-list` finding falling inside any new index section. ``` npx markdownlint-cli2 "**/*.md" 0 issues in 1359 files check-changelog-parity.sh --check/--check-order/--check-bump pass node scripts/validate-plugin-contracts.mjs 51 setup skills, 3023 files scripts/check-changed-skills.sh origin/main 28 skills, 0 failed scripts/check-detector-findings-crosswalk.sh --check 28 rule rows, clean audit-noise detect.test.sh 199 checks, 0 failed scripts/run-plugin-tests.sh 7365 passing ``` 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.sh` and `source-control/worktree-claim.test.sh` both assert a **permission denial**, and this sandbox runs as uid 0, which bypasses it. Neither suite nor its target differs from `origin/main` in this diff, so running them here is running them at main. - `repo-fleet-hygiene/apply-plan.test.sh` failed on `fatal: failed to write commit object` after 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 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 — 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 (`compress` reverts at `<3% AND 0 semantic-loss`, and dropping one word is ~0.1%), and `ai-slop`'s stacked-hedging detector returns zero on that file, so it has no justification outside the skill that would revert it. **L7's `I-1` batch** 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-state` is 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.com/claude-code) https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q --- _Generated by [Claude Code](https://claude.ai/code/session_01FTVH5ZRVph75kvxzUAnb6q)_ --------- Co-authored-by: Claude <noreply@anthropic.com>

No linked issue
Summary
Runs all eight
docs-hygieneskills against the whole tracked markdown corpus (1302 files, 17 MB, vendor tree excluded), reconciles the findings, applies the remediations that survived verification, and graduates the durable outcomes intodocs/adr/anddocs/specs/so the working slice can be pruned before merge.The headline is not the edit count. Four of the eight lanes independently measured that this corpus is already clean on their axis, and the sweep's most valuable output is that evidence plus the four detector defects it had to fix before it could run at all.
enum-list, 4568 of those candidates, yields zero: a census showed the shape's canonical form has no instances here, so the defect is absent rather than unmatched.docs/specs/d1-model-already-knows-measurement.md.Three lanes cited that same
d1measurement for why cue-based verdicts do not survive contact with this house style, and declined to extrapolate samples to unread populations. That result is now itself a spec.Fix
Four detector defects, each with a regression test that fails against the unfixed script. All four corrupted results silently rather than failing loudly, which is why they had survived.
audit-noisereadBASH_REMATCHafter a call that overwrites it, twice (fence branch, heading branch). A repo-wide scan died at file 213 of 1302 and reported partial coverage as complete. The heading branch has the worse mode: when the clobbering match happens to leave two groups bound it does not abort at all, it silently applies the wrong heading text to the section-exemption check.audit-progressive-disclosure'smd_links()opens with agrepthat exits 1 on a file with no markdown links, which underset -euo pipefailkilled the caller before its backtick branch ran. Every hub citing spokes only in backticks had all of them reported unreachable. Measured over all 243SKILL.md: orphans drop 133 to 51. The suite already asserted backtick citation worked, but its fixture hub also had markdown links, so the broken path never executed.in order tofixed at its generator. The phrase survived the repo-wide style pass in 34 plugin READMEs because it is generated, insideBEGIN GENERATEDblocks thatplugin-options-docs-gatechecks for drift. Hand-editing them fails CI and reverts on the next sync.Applied remediations. L1's three files (four behavioural rules salvaged into
SKILL.mdbefore the file carrying them was deleted, one file removed, one converted to a pointer). 24 spokes split out of 16 skill bodies and two long documents, each pointer stating the condition that sends a reader there. 65 of the 83 in-file prose findings: 18 sentences that ended inside a parenthetical, 15 generated blocks filed under a findable heading, 14 backtracking rewrites, and the rest.Two doctrine conflicts settled, both found by lanes and neither remediable as a call-site defect:
PLUGIN-PHILOSOPHY.mdprescribed the cross-skill citation form the public-surface contract forbids, which produced 30 findings. Ruling: the plugin, not the skill, is this repo's shipping unit (71 plugins, 71 manifests with one version each, no per-skill manifest, no.claude/skills/), so intra-plugin citation is legal here and cross-plugin is not. Recorded as ADR 0018, with both doctrine documents and the live filter taxonomy edited so the next audit does not re-raise the dissolved 65.write-for-agentsdisclaimedSKILL.mdoutright, which would leave 250 files, the largest agent-facing surface here, governed by no authoring doctrine. Narrowed to separate authorship from prose.Verification
Every applied change was checked by a fresh context that was not told the applier's reasoning. Three of those verifications changed the outcome rather than rubber-stamping it.
ai-briefing'sexecution-flow.mddeletable; an agent forbidden from reading it could not reconstruct four rules, so those were salvaged intoSKILL.mdfirst. The applier then caught a contradiction the audit missed:references/slide-generation.md:89states the opposite empty-bucket convention, so the salvaged rule is scoped to markdown output.SKILL.md.babysit-prs,babysit-loopandsource-control/setup. The tests cite issue source-control:babysit-prs: docs don't clearly distinguish babysit-readiness-gate.sh (classification count) from babysit_merge.py (actual merge readiness) — caused a false MERGE-READY report #601: the body must name the merge gate'sreadyfield as the sole authority for a merge-ready claim. An agent that never opens a spoke could call a PR merge-ready on the wrong signal. Line count is the weaker consideration when the inlined content is the safety contract, andsource-control's changelog now says so as a standing pre-check. The splits agent then applied that rule itself and skipped four more candidates.context-guard's spoke shipped a table of contents pointing at../../SKILL.mdanchors for five sections that had just moved into the spoke itself, by a path resolving to no file.Gates, all green locally on the current head:
Related
The working slice under
docs/topics/docs-hygiene-repo-sweep/is pruned, asdocs/conventions/topic-docs/README.mdrequires of Contract-tier documents before merge. Its durable content graduated to:docs/adr/0018-treat-the-plugin-as-the-encapsulation-boundary-for-skill-citation.md, carrying the ruling and its four corrections.docs/specs/docs-hygiene-sweep-yield-measurement.md, so a future sweep does not repeat the lanes that measured near-zero.docs/specs/docs-hygiene-sweep-unapplied-remediations.md, holding what is deliberately not applied, resumable without re-auditing: 153path:linecitations, per-lane recall limits, and a decay rule.docs/adr/0019through0024, extracted fromMIGRATION-PLAYBOOK.mdthe same way.What that spec records as not applied, with reasons: L2's 167 structure findings (TOCs must follow splits or they describe a file about to be restructured), 24 cross-plugin encapsulation violations and 8 unresolvable
plugin-root-form citations, L6's 312-byte compression set (compressmakes a semantic-diff subagent a mandatory gate and requires the compressing context not be the verifying one), and 18 prose findings that were void, failed verification, or sat inside a CI-checked generated block.Refs
docs/specs/d1-model-already-knows-measurement.md,docs/conventions/topic-docs/README.md,docs/conventions/untrusted-content/README.md,.claude/rules/vendor-docs-are-not-style.md.