* 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>
The Cursor port was a hand-made copy stranded at v3.16.0 while
SKILL.md reached v3.22.3 — its own README said 'no automated sync
today, open an issue if drift becomes a problem.' It did.
scripts/sync-cursor-rules.sh now generates the .mdc from SKILL.md:
Cursor frontmatter with the version derived from the SKILL.md
frontmatter, plus the three portability rewrites a copied-out rule
needs (no repo self-reference, no CATEGORIES.md citation, manual
preservation check instead of node detector/validate.js). Each
rewrite is anchored on exact upstream text and exits non-zero if the
anchor stops matching once, so an upstream edit to a ported span
breaks CI here instead of silently shipping a wrong rule.
plugin-skill-sync workflow regenerates the rule on every SKILL.md
change and fails on drift; cursor-rules/** added to its path filters
so a hand-edit to the .mdc alone can't dodge the check.
Negative control run: mutating a ported span in SKILL.md makes the
script exit 1 with an actionable message; restoring it regenerates
byte-identically (second run idempotent).
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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>
* feat: distribute as a Claude Code / Cowork plugin
Package the skill as a single-plugin marketplace so it can be installed
via /plugin instead of a bare clone. This is the only path that works in
Claude Cowork, which loads skills exclusively from installed plugins
(see #12).
Root SKILL.md stays the single source of truth; the plugin's bundled
copy at plugins/avoid-ai-writing/skills/avoid-ai-writing/SKILL.md is
generated by scripts/sync-plugin-skill.sh. A CI workflow regenerates it
and fails on drift or a plugin.json/SKILL.md version mismatch.
Validated with `claude plugin validate` and a full local
add-marketplace / install / inspect / uninstall cycle.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: parse plugin version safely in sync script
* fix: harden plugin version parsing in sync script
* harden skill-version parsing + plugin install docs
Builds on Copilot's plugin.json JSON-parser fix:
- sync-plugin-skill.sh: read the version only from the SKILL.md
frontmatter block (not anywhere in the file), strip CR, and fail with
a clear message if it's unparseable — closes the loose/false-green
side of the version check that the JSON parser already closed for
plugin.json.
- .gitattributes: pin *.md to LF so a CRLF checkout can't forge a
version mismatch or mask skill drift.
- README: add /reload-plugins to the install steps (skills don't
activate until reload/restart) and note updates are version-gated.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>