feat(docs-hygiene): bucket extract-ssot candidates by multiplicity, gate artifact creation not reporting (0.19.0) - #3142
Conversation
…ate artifact creation not reporting (0.19.0) The Rule of Three was doing two jobs in extract-ssot: gating creation of a new SSOT artifact (correct, and what the cited failure-rate evidence is about) and gating whether duplication was reported to the user at all (wrong). Two real defect classes were discarded in silence — a consumer inlining a recap of an SSOT that already exists (N=1), and two files asserting the same contract with no declared owner, drifting (N=2). identify now rosters candidates in three labelled buckets (N=1, N=2, N>=3) with the instance count per candidate, and admits an N=1 candidate only when an existing canonical home is what the site recaps instead of cites. verify Gate 1 assigns that bucket and emits it in a new bucket: output field; REFUSE-rule-of-three-fails is retained as the reason code but now fires only against an artifact-creating remedy below three. Gate 4 splits intentional two-audience bifurcation (still refuses) from accidental bifurcation with no declared owner (the N=2 bucket's own defect). Adds two non-abstracting remedies, normalize-wording and name-an-owner, and five flags on the identify/batch surfaces: --min-instances (default 1; =3 is the regression guard reproducing the pre-bucket behavior), --buckets, --fix, --dry-run, --yes. Bare invocation stays read-only. The 6-test gate is untouched and still governs every N>=3 extraction. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FdD9bt2ckihbEiS3K4nEgP
|
Claude finished @kyle-sexton's task in 1m 6s —— View job Security review (
|
|
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: b442322c98
ℹ️ 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".
…ds, and semantic-cluster counting (0.19.0) Three defects in the multiplicity-bucket design shipped in b442322, each surfaced by automated review of the PR. 1. `trim-to-citation` was missing from the N=2 permitted-remedy set in the bucket contract and in verify's permitted-remedies schema, even though the routing rules already prescribed it for "N=2, home exists". A candidate was therefore constrained to remedies that cannot remove two redundant recaps of an already-complete canonical home. The N=2 description now covers both of its shapes — two consumers recapping an existing home (trim both to citations) and two files asserting one contract with no declared owner (name one). `REFUSE-rule-of-three-fails` is restated positively (fires only against rule-file / new-skill / new-action below N>=3) instead of enumerating the remedies it spares, which is what let the set drift incomplete. 2. The sibling boundaries in compress, audit-noise, audit-derivability, and write-for-agents still routed cross-file duplication to extract-ssot only at 3+ files, leaving the sub-three buckets unreachable from the flows that feed them. They now route repeated content at any multiplicity; creating a NEW artifact still waits for the third instance. 3. verify Gate 1 counted every cluster by discriminating-phrase grep. A semantic cluster (identify forms c2/i) shares no verbatim >=8-word phrase, so the grep found only the file the phrase came from — assigning a real N=2/N>=3 cluster to N=1 and, with no prior canonical, returning REFUSE-not-found, after which batch's mandatory verify filter dropped it. Gate 1 now counts by evidence shape and gained a semantic Tier 0 evidence form; Gate 0's REFUSE-not-found fires only when neither grep nor reading resolves an instance. The core invariant is unchanged: no artifact-creating output below N>=3, and trim-to-citation / normalize-wording / name-an-owner / edit-existing-rule never create a file. Version stays 0.19.0; the CHANGELOG entry is extended. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FdD9bt2ckihbEiS3K4nEgP
|
Claude finished @kyle-sexton's task in 47s —— View job Security review (
|
…r-humans and compress integration note (0.19.0) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FdD9bt2ckihbEiS3K4nEgP
|
Claude finished @kyle-sexton's task in 50s —— View job Security review (
|
|
Claude finished @kyle-sexton's task in 2m 45s —— View job Code review (
|
|
Claude has reviewed this PR 3 times. The lane skips further automatic reviews after 5; deleting this comment resets the count. |
…uting-threshold sweep (0.19.0) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FdD9bt2ckihbEiS3K4nEgP
|
Claude finished @kyle-sexton's task in 2m 1s —— View job Code review (
|
…g remedies (0.19.0) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FdD9bt2ckihbEiS3K4nEgP
|
Claude finished @kyle-sexton's task in 49s —— View job Security review (
|
|
Claude finished @kyle-sexton's task in 1m 17s —— View job Code review (
|
Resolves two conflicts in docs-hygiene, both from #3142 bumping the plugin to 0.19.0 while this branch carried 0.18.4: - plugin.json: version becomes 0.19.1, this branch's fix on top of 0.19.0. - CHANGELOG.md: this branch's entry re-headed [0.19.1] and ordered above [0.19.0]; both entries kept in full. #3140 also landed on main during this branch's life, so the sibling `code-tidying/audit-comment-residue` parse it ports from is now present. The gate and the two-step unescape here are identical to it, so the two porcelain parsers converge rather than failing in opposite directions on renames. Noted in the changelog entry. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXYVz59XVYS7NEjMB5UyHq
….19.1) (#3171) Closes #3143 ## Summary `audit-noise`'s `git status --porcelain` parse dropped files whose paths git treats specially. Both defects are the silent-false-negative class: the file did not error, it simply disappeared from the target list, so a run over a tree containing one reported clean. ## Fix **Rename split is gated on the status letter, not the path text.** It previously fired on any record whose path contained `" -> "`, so a file literally named `notes -> draft.md` was reduced to `draft.md` — a name that resolves to nothing. It now gates on `[RC]` in *either* column, which is narrower and still catches a rename recorded in the index or the worktree. **`\\` is now unescaped as well as `\"`.** Git C-quotes a path for an embedded backslash too, so `back\-slash.md` stayed escaped and resolved to nothing. Both escapes are undone, `\"` before `\\`. #3140 landed on `main` while this branch was in flight. The gate and the two-step unescape here are **identical** to the ones it gave `code-tidying/audit-comment-residue`, so the two porcelain parsers now converge rather than failing in opposite directions on renames — which is what #3143 asked for. `audit-noise`'s `SKILL.md` does not mirror this parse — it uses a plain `grep '\.md$'` pipeline — so the conditional "mirrored parser" half of the issue does not apply here. `docs-hygiene` 0.19.1. ## Verification - `detect.test.sh`: **all 66 checks pass**, including 5 new cases. - **Discriminator check**: reverting only `detect.sh` to the old parse fails exactly the two new defect cases (`' -> '` path, backslash path) and nothing else — so neither case passes vacuously. The rename cases pass under both implementations by design; they are regression guards showing the new gate does not cost the `old -> new` handling it narrows. - All 7 `docs-hygiene` suites pass, re-run after merging `main`; nothing outside this skill references `audit-noise/scripts/detect.sh`. - `shellcheck`, `shfmt -d`, `markdownlint-cli2`, `typos`, `editorconfig-checker`: clean. - `check-fixture-git-isolation.sh --check`, `check-plugin-manifest-presence.sh`, `check-changelog-parity.sh --check` / `--check-order` / `--check-bump` / `--check-preserved` against `origin/main`: pass. The suite also picks up the `unset GIT_DIR GIT_WORK_TREE GIT_CONFIG` isolation line that 0.18.3's sweep missed on this file. **Known residual, recorded at the parse site rather than left implicit:** git's octal escapes (`\NNN`) for control and non-ASCII bytes are still not decoded, so those paths continue to miss. Converging on `git status --porcelain -z` would close the class outright rather than extending the string parse a third time. ## Related - Refs #3140 — the sibling `code-tidying/audit-comment-residue` parse fix this issue was deferred out of; now landed, and this change converges with it. - Refs #3126 — the original `audit-comment-residue` defect report. - Refs #2872 — the fixture git-isolation convention this suite now satisfies. - Refs #3142 — bumped `docs-hygiene` to 0.19.0 mid-flight; merged in, so this PR ships 0.19.1. Co-authored-by: Claude <noreply@anthropic.com>
Closes #3114
Summary
The Rule of Three was doing two jobs in
/docs-hygiene:extract-ssot: gating creation of a new SSOT artifact (correct — the failure mode the cited ~19%/~50% extraction-failure evidence is about) and gating whether duplication is reported at all (wrong). Two real defect classes never reached the user: a consumer inlining a recap of an SSOT that already exists (N=1), and two files asserting the same contract with no declared owner, drifting independently (N=2). This PR separates the two gates: reporting happens at every multiplicity, artifact creation still refuses below three.Fix
identifynow rosters candidates in three labelled buckets — N=1 (inline recap of an existing SSOT; admitted only when a canonical home exists), N=2 (source-of-truth bifurcation risk), N≥3 — showing bucket membership and instance count per candidate. Sub-three buckets offer only non-abstracting remedies;rule-file/new-skill/new-actionstay reserved for N≥3 behind the unchanged 6-test gate.normalize-wording(align divergent phrasings in place) andname-an-owner(declare one existing file canonical, make the other cite it). Neither creates a file.verifyGate 1 assigns the bucket (newbucket:+permitted-remedies:output fields) instead of refusing on count;REFUSE-rule-of-three-failsfires only against an artifact-creating remedy below three. Gate 4 splits intentional two-audience bifurcation (still refuses) from accidental bifurcation (the N=2 bucket's own defect), making anti-pattern fix(markdown-formatter): harden hook + simplify plumbing #11 reachable in practice.argument-hint:--min-instances(default 1;=3is the regression guard reproducing pre-bucket behavior exactly),--buckets,--fix(non-abstracting remedies only, never creates an artifact),--dry-run,--yes. Bare invocation stays read-only.context/decision-framework.mddocuments the reporting-gate vs abstraction-gate split; the frontmatterdescriptionno longer promises refusal below three; evals gain N=1, N=2, and--min-instances=3regression cases plus explicit no-new-artifact-below-three expectations (evals 1, 2, 8, 9).0.18.3→0.19.0with a matching CHANGELOG entry.Verification
npx markdownlint-cli2over the 8 changed markdown files — 0 issues.typosandeditorconfig-checkerover the changed surface — clean.check-jsonschemaagainst the claude-code-plugin-manifest schema forplugin.json— valid; manifest duplicate-key detector — clean.scripts/check-changelog-parity.shin all four modes (--check,--check-bump origin/main,--check-order,--check-preserved origin/main) — all pass.evals.jsonparses (jq/json.tool) and keeps the existing entry shape; ids 1–9 enumerate in file order.actions/verify.mdGate 1 assigns rather than refuses; no surviving text routes a sub-three candidate to an artifact-creating output).Related
/review:code-reviewSkill invocation erroring during this PR's review cycle and fell back to a manual review; filed as a follow-up, not addressed here.--configclaim in three docs, no SSOT for the contract, stale PR-section config #3127 — thepr-issue-linkagefailure this PR hit at open time traces to item 3 there (stalepr_body_required_sectionsconfig vs the CI gate's Summary/Fix/Verification/Related contract); fixed by editing this PR's body, config drift already tracked.🤖 Generated with Claude Code
https://claude.ai/code/session_01FdD9bt2ckihbEiS3K4nEgP
Generated by Claude Code