fix(docs): true two stale skill counts to 380 and bring them under the counter gate

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 '<N> production-ready skills across <D> 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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qgc6RYXWJPr5oW9DHU7zR4
This commit is contained in:
Claude 2026-08-24 21:06:20 +00:00
parent 8142fd7610
commit 4e93f8f66e
No known key found for this signature in database
3 changed files with 23 additions and 14 deletions

View file

@ -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`)

View file

@ -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 |
|--------|--------|------------|---------|

View file

@ -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))