refactor(doc-sweep): unify doc classification behind one shared module + exempt categories - #9
Merged
Merged
Conversation
Propose artifacts for collapsing the three disagreeing doc-classification definitions into one shared doc-classify.mjs module + a declarative docPatterns source, fixing the .claude/** divergence bug and retiring docMode. brainstorm/proposal/design/specs/tasks/plan generated via the superpowers-bridge schema. Apply phase (worktree + subagent-driven TDD) to follow. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…fy-doc-classification Adds an exemptPatterns concept to the doc-classification design: a three-way doc / exempt / doc-requiring split. Default exempts common test globs, so a test-only PR passes without a [skip docs] ack while tests + real code still enforce. Distinct from excludeDirs by intent; configurable per project. Threads through brainstorm (Q6), proposal, design (D7), the doc-classification spec (+ docs-staleness-ci test-only scenario), tasks, and plan (classifier code, tests, audience-rules block). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Malformed classifier JSON previously fell through to two ungarded node -e parses and silently exited 0 with no diagnostic; now a single guarded parse fails closed to a warn+fail-open path. Config paths with spaces previously word-split via the unquoted $cfg_arg (SC2086 disabled) and silently fell back to default patterns; now cfg_arg is a bash array so the path stays one argument. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-safe array expansion
The per-commit [skip docs] loop in revise-push-guard.sh used `git diff-tree -r`
for every commit, which prints nothing for merge commits, so a non-doc file
introduced only by a merge (e.g. a conflict resolution) fell through to allow.
Detect merge commits (2+ parents) and use the combined-diff form (-c) instead,
which surfaces only merge-unique content, leaving non-merge commit behavior
unchanged and avoiding over-flagging a clean auto-merge with no merge-unique
change. Also replace "${cfg_arg[@]}" with the bash-3.2-safe
${cfg_arg[@]+"${cfg_arg[@]}"} idiom at all three call-sites (revise-push-guard.sh
x2, docs-ci-check.sh x1) so an empty array doesn't abort under `set -u` on
macOS's stock bash.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…classify.mjs (add go/py test globs) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…terns Both install skills (install-docs-ci, install-revise-hook) now copy the shared doc-classify.mjs classifier alongside the script/hook they scaffold, since the guard scripts resolve it via $here/doc-classify.mjs and would otherwise fail open at runtime. The generated config JSON now writes a docPatterns glob list instead of the retired docMode string, matching the default documented in context/audience-rules.md. Uninstall now also removes the vendored doc-classify.mjs. Evals updated to match.
… custom docPatterns to audience-rules Reconfigure in both install-docs-ci and install-revise-hook now unconditionally re-copies doc-classify.mjs alongside the script/hook, so a missing or stale classifier self-heals even when nothing else about the install changed (previously it silently stayed absent, and docs-ci-check.sh/revise-push-guard.sh fail open on every guarded action without it). Existence-detection steps now call out the classifier as part of what constitutes an install. Also aligns audience-rules.md's docPatterns-persistence claim with actual installer behavior: docPatterns is now persisted to .claude/context/audience-rules.md (and mirrored into the per-install config JSON) only when the user picks a custom doc-file-set; the default choice omits the key entirely so doc-classify.mjs's built-in default applies, instead of transcribing the step-2 parenthetical (which was missing the `**/` prefix the real defaults use). Reconfigure evals for both skills now assert the classifier re-copy so a regression here is caught by the skill gate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…classifier changes Both installers' evals now assert classifier-copy + docPatterns behavior; benchmarks regenerated via real eval runs (install-docs-ci 18/18, install-revise-hook 19/19). Corrected one install-docs-ci assertion that wrongly required docPatterns for the default choice (the skill correctly omits it so the built-in default applies). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ptPatterns Wires doc-classify.test.mjs into validate.yml next to the other node --test steps, and updates doc-sweep's README/CHANGELOG for the classifier unification: docMode retirement in favor of docPatterns (BREAKING for stale configs), the shared doc-classify.mjs backing both guards, the .claude/** default fix, and the new exemptPatterns behavior (test-only changes pass without an ack).
…correct exemptPatterns config docs M1: README/CHANGELOG claimed exemptPatterns was an installer prompt like docPatterns; only docPatterns is prompted. exemptPatterns is config-JSON-only (hand-edit, read via --config). M2: an excludeDirs entry with a trailing slash (e.g. "vendor/") silently failed to exclude paths under it; doc-classify.mjs now strips trailing slashes before comparing. M3: the spec said an empty configured docPatterns/exemptPatterns list "replaces the default" without qualification; code (correctly, for safety) treats empty/omitted as "use built-in default" so an empty docPatterns can't fail every PR. Spec now says so explicitly, with scenarios. Added node --test coverage for all three.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Creates the doc-classification living spec, folds the docs-staleness-ci and revise-docs-push-guard deltas into their living specs, and moves the change to archive/2026-07-04-unify-doc-classification. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Collapses doc-sweep's three disagreeing "what is a doc?" definitions into one shared, dependency-free classifier, and adds an exempt-categories concept so the staleness guards stop nagging on changes that provably don't need docs.
The bug this fixes
The two guards (
docs-ci-check.sh,revise-push-guard.sh) each carried a copy-pastedis_doc, and both diverged from the audience-rules:.claude/**/*.mdwere docs per the rules but non-docs per the scripts — so a PR editing only.claude/context/audience-rules.mdfailed the docs-staleness check. Fixed and regression-locked in all three test suites.Changes
plugins/doc-sweep/hooks/doc-classify.mjs— one node classifier (in-house glob matcher, no deps,node --test). Both guards delegate to it; neither carries inline patterns.doc/exempt/doc-requiring. NewexemptPatterns(default: common test globs) means a test-only change passes without a[skip docs]ack, while tests + real source still enforce. Precedence:excludeDirs → exemptPatterns → docPatterns.docModeretired for adocPatternsglob list (BREAKING for stale configs — re-run the installer or rename the field). Default doc-set now includes.claude/**/*.md.doc-classify.mjsalongside the script, recorddocPatterns, and re-copy on reconfigure / remove on uninstall.How it was built & verified
Planned and implemented via the superpowers-bridge OpenSpec schema (brainstorm → proposal → design → specs → tasks → plan → subagent-driven TDD → verify → retrospective → archive). Every task got an adversarial per-task review plus a final whole-branch adversarial review — which caught three real defects green tests missed, all fixed with new regression tests:
[skip docs]bypass in the push guard,Local gate green:
doc-classifyunit (13/13), both shell suites, marketplace policy, skill-gate (7/7, both install benchmarks 1.0),openspec validate --strict --all(5/5), openspec hygiene. ShellCheck runs in CI.🤖 Generated with Claude Code