* check-pattern-count: assert CLAUDE.md's copy of the category count on every push
CLAUDE.md's don't-restate-it sentence quotes the README bullet, and its copy
is tracked only by .ssot.yaml, which runs on release and weekly cron - so a
same-repo edit could sit stale for up to a week (how #95's 46-vs-61 lived
unnoticed). The sed pattern mirrors .ssot.yaml's regex for the copy, so the
two guards cannot read different sentences.
Controls: clean tree passes; 61->62 mutation fails with the drift message;
a reworded sentence fails with its own message pointing at both guards.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EKwz2XfwaMD6uR5ABmVaKq
* count guard: actually trigger on CLAUDE.md edits, survive a missing file, take the first match
All three findings from the adversarial review of #111:
- HIGH: plugin-skill-sync.yml's paths filter omitted CLAUDE.md, so the
guard never ran on the edits it exists to catch (PR #106: zero check
runs). Added to both triggers.
- MEDIUM: under set -euo pipefail a missing CLAUDE.md killed the script
with sed's raw error and exit 2 before the friendly branch. Explicit -f
check with its own message and exit 1.
- LOW: greedy sed took the LAST match on a line where a first-match regex
engine takes the first; extraction is now grep -o | head -n1 (file-order
first match) and the mirror-claim comment states the semantics honestly.
Controls, exit codes checked unpiped: clean 0; mutation 1; reworded 1;
missing file 1; same-line double-mention resolves to the first match.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EKwz2XfwaMD6uR5ABmVaKq
---------
Co-authored-by: Claude <noreply@anthropic.com>
Adds a preservation validator, a self-scan with PROOF.md, over-correction guardrails, a false-positive intake, and a two-class control corpus with the project's first FPR, TPR, and AUC.
Splits Tier 1 into 1A frequency markers and 1B clarity edits, and fixes two detector defects the measurement found (#67, #69).
Findings and open decisions: #70, #71, #72, #73.
The README advertised a "109-entry word replacement table". The real count is 111 (59 Tier 1 + 40 Tier 2 + 12 Tier 3). Stale since 176b5a0 because nothing checked it.
Extends check-pattern-count.sh to derive the word count from the Tier 1/2/3 tables and assert it against the README bullet. Already wired into CI via plugin-skill-sync.yml.
Headers are detected positionally (the row before the |---|---| separator) rather than by label: Tier 1/2 use "| Replace | With |" while Tier 3 uses "| Word | What to do |", so label-matching silently counted two headers as data — which is how the first pass of this PR arrived at 112. Separator matching tolerates alignment colons. A per-tier non-zero assertion fails with a distinct "fix the counter, not the README" message when a heading stops matching, so a broken counter cannot enshrine a wrong number.
Comprehensive doc review after the 3.4->3.7.2 churn. The pattern count
had rotted across files (README said 42, CLAUDE.md said 36, reality is
46 detection categories in SKILL.md).
- Fix the count to 46 and keep it in ONE user-facing place (the README
"46 pattern categories" bullet). README heading reframed to "Pattern
reference" (representative examples, not an exhaustive census), and the
table extended with the 7 missing categories (placeholders, citation
markup, UTM, gap-filling, hyphenated-pair, infomercial, low-TTR).
- De-duplicate the count: CLAUDE.md and detector/CATEGORIES.md now
reference SKILL.md + CI instead of restating their own numbers.
- Add scripts/check-pattern-count.sh + wire into the CI workflow: it
derives the count from SKILL.md's detection ### entries (excluding the
3 writer-side tests) and fails if the README number drifts. Drift is
now a red check, not silent rot.
- Fix stale detector/patterns.js version comment and the marketplace.json
description (was "detection-only mode").
No SKILL.md / version change — docs + CI only.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>