From 4e93f8f66e60ddab8b8466347818bc017820b2bd Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 24 Aug 2026 21:06:20 +0000 Subject: [PATCH] fix(docs): true two stale skill counts to 380 and bring them under the counter gate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Caught by review on the v2.12.0 promotion PR #985: README's Skills Overview heading still said 370 and CLAUDE.md's footer Status line said 379 while the banner/badges/scope line say the derived 380. Both wordings ('370 skills across', '379 skills deployed across') were invisible to derive_counters.py's claim patterns, which is why they could drift — reworded both into the standardized ' production-ready skills across domains' phrasing, made extract_claims() validate every occurrence of a claim pattern instead of only the first, and run_check() now reads CLAUDE.md's Status footer line alongside Current Scope. Verified: planting 999/998 in the two lines fails the gate naming both; restored values pass. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Qgc6RYXWJPr5oW9DHU7zR4 --- CLAUDE.md | 2 +- README.md | 2 +- scripts/derive_counters.py | 33 +++++++++++++++++++++------------ 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index f868f5c8..4e178753 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -720,4 +720,4 @@ When I correct you, or you catch yourself making a mistake: before continuing ad **Last Updated:** August 24, 2026 **Version:** v2.12.0 (consolidated release — see CHANGELOG.md) -**Status:** 379 skills deployed across 20 domains, 96 marketplace plugins, docs site live (counters derived via `scripts/derive_counters.py`) +**Status:** 380 production-ready skills across 20 domains, 96 marketplace plugins, docs site live (counters derived via `scripts/derive_counters.py`) diff --git a/README.md b/README.md index f363f5d7..d9f4b9fa 100644 --- a/README.md +++ b/README.md @@ -152,7 +152,7 @@ Run `./scripts/convert.sh --tool all` to generate tool-specific outputs locally. ## Skills Overview -**370 skills across 20 domains:** +**380 production-ready skills across 20 domains:** | Domain | Skills | Highlights | Details | |--------|--------|------------|---------| diff --git a/scripts/derive_counters.py b/scripts/derive_counters.py index b1df9561..20730e00 100644 --- a/scripts/derive_counters.py +++ b/scripts/derive_counters.py @@ -149,12 +149,18 @@ CLAIM_PATTERNS = { def extract_claims(text: str) -> dict: - """Return {counter_name: first claimed int} for every pattern found in text.""" + """Return {counter_name: [every claimed int]} for every pattern found in text. + + All occurrences are collected (not just the first) so a stale duplicate of a + headline claim elsewhere in the same file — e.g. a section heading that + repeats the skill count — is gated too (caught live on the v2.12.0 + promotion PR #985, where the README banner said 380 while a section + heading still said 370).""" claims = {} for key, pattern in CLAIM_PATTERNS.items(): - match = pattern.search(text) - if match: - claims[key] = int(match.group(1)) + values = [int(m.group(1)) for m in pattern.finditer(text)] + if values: + claims[key] = values return claims @@ -272,9 +278,11 @@ def run_check(root: Path, derived: dict) -> int: claude_md = root / "CLAUDE.md" if claude_md.is_file(): text = claude_md.read_text(encoding="utf-8") - # Restrict to the "Current Scope" line so history sections don't trip the gate. - scope_lines = [ln for ln in text.splitlines() if ln.startswith("**Current Scope:**")] - sources.append(("CLAUDE.md (Current Scope line)", "\n".join(scope_lines))) + # Restrict to the "Current Scope" and footer "Status:" lines so + # history sections don't trip the gate. + scope_lines = [ln for ln in text.splitlines() + if ln.startswith("**Current Scope:**") or ln.startswith("**Status:**")] + sources.append(("CLAUDE.md (Current Scope / Status lines)", "\n".join(scope_lines))) marketplace = root / ".claude-plugin" / "marketplace.json" if marketplace.is_file(): @@ -292,12 +300,13 @@ def run_check(root: Path, derived: dict) -> int: if not claims: mismatches.append(f"{label}: no recognizable counter claims found") continue - for key, claimed in claims.items(): + for key, values in claims.items(): actual = derived[key] - if claimed != actual: - mismatches.append( - f"{label}: claims {key}={claimed}, derived {key}={actual}" - ) + for claimed in values: + if claimed != actual: + mismatches.append( + f"{label}: claims {key}={claimed}, derived {key}={actual}" + ) mismatches.extend(check_domain_table(root)) mismatches.extend(check_readme_badges(root, derived))