
Changelog Review
- 62 installs
- 49 repo stars
- Updated August 4, 2026
- laurigates/claude-plugins
Helps with ai & agent building tasks.
About
changelog-review is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- changelog-review
- AI & Agent Building
- AI-coding skill
Changelog Review by the numbers
- 62 all-time installs (skills.sh)
- +1 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #6,310 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/laurigates/claude-plugins --skill changelog-reviewAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 62 |
|---|---|
| repo stars | ★ 49 |
| Last updated | August 4, 2026 |
| Repository | laurigates/claude-plugins ↗ |
What it does
Helps with ai & agent building tasks.
Files
Claude Code Changelog Review
When to Use This Skill
| Use this skill when... | Use project-distill instead when... |
|---|---|
| Reviewing Claude Code releases for breaking changes that affect plugins | Distilling project session learnings into rules and recipes |
| Discovering new Claude Code features that plugins can leverage | Use project-skill-scripts instead when auditing skills for script-extraction wins |
| Tracking deprecations before they break installed plugins | Use project-continue instead when resuming feature work, not platform review |
Expertise for analyzing Claude Code changelog and identifying impacts on plugin development.
Core Purpose
Review Claude Code releases to:
- Identify breaking changes requiring plugin updates
- Discover new features plugins can leverage
- Track deprecations before they become problems
- Ensure plugins follow current best practices
Change Categories
High Impact (Action Required)
| Category | Example Changes | Action |
|---|---|---|
| Breaking changes | API changes, renamed tools | Update affected plugins immediately |
| Security fixes | Permission vulnerabilities | Review and update permission rules |
| Deprecations | Removed features/fields | Remove deprecated usage |
| Hook changes | New events, schema changes | Update hooks-plugin |
Medium Impact (Review Recommended)
| Category | Example Changes | Action |
|---|---|---|
| New features | New tools, frontmatter fields | Consider plugin enhancements |
| Permission updates | New wildcard patterns | Update permission documentation |
| SDK changes | New callbacks, streaming | Update SDK-related skills |
| MCP improvements | OAuth, server configs | Update agent-patterns-plugin |
Low Impact (Information Only)
| Category | Example Changes | Action |
|---|---|---|
| Bug fixes | UI improvements | Note in changelog |
| Performance | Startup optimizations | No action needed |
| IDE features | VS Code updates | No action needed |
Plugin Impact Matrix
Map changelog categories to plugins:
| Claude Code Area | Affected Plugins |
|---|---|
| Hooks | hooks-plugin, configure-plugin |
| Skills/Commands | All plugins with skills/commands |
| Agents | agents-plugin, agent-patterns-plugin |
| MCP servers | agent-patterns-plugin |
| Permissions | configure-plugin, hooks-plugin |
| Git operations | git-plugin |
| Testing tools | testing-plugin |
| SDK changes | agent-patterns-plugin |
Version Tracking
Version state stored in .claude-code-version-check.json:
{
"lastCheckedVersion": "2.1.7",
"lastCheckedDate": "2026-01-14",
"changelogUrl": "https://raw.githubusercontent.com/anthropics/claude-code/main/CHANGELOG.md",
"reviewedChanges": [
{
"version": "2.1.7",
"date": "2026-01-14",
"relevantChanges": [],
"actionsRequired": []
}
]
}Analysis Process
Step 1: Fetch Current State
# Read last checked version
cat .claude-code-version-check.json | jq -r '.lastCheckedVersion'Step 2: Fetch Changelog
Use WebFetch to get the current changelog from: https://raw.githubusercontent.com/anthropics/claude-code/main/CHANGELOG.md
Step 3: Identify New Versions
Compare fetched changelog versions against lastCheckedVersion. Extract all changes since that version.
Step 4: Categorize Changes
For each change, determine: 1. Impact level (high/medium/low) 2. Affected plugins 3. Required action
Step 5: Generate Report
Produce a report with:
- New versions found
- High-impact changes requiring immediate action
- Medium-impact changes worth reviewing
- Suggestions for plugin improvements
Step 6: Update Version Tracking
Update .claude-code-version-check.json with:
- New lastCheckedVersion
- New lastCheckedDate
- Summary of reviewed changes
Change Detection Patterns
Breaking Changes
Look for:
- "BREAKING" or "Breaking Change" headers
- "Removed" sections
- "Migrat" keywords
- "deprecated" becoming "removed"
Hook System Changes
Look for:
- "hook" mentions in features
- New hook events (SessionStart, SessionEnd, SubagentStart, etc.)
- Schema changes for hook input/output
- Permission decision updates
Skill/Command Changes
Look for:
- "skill" or "slash command" mentions
- Frontmatter field changes
- Discovery mechanism updates
- New fields like "context: fork"
Agent/Subagent Changes
Look for:
- "agent" or "subagent" mentions
- Task tool changes
- Agent configuration options
- New agent types
Permission Changes
Look for:
- "permission" mentions
- Wildcard pattern updates
- Security fixes
- Bash permission changes
Report Format
For the human-readable review report template (Summary / High-Impact / Medium-Impact / Action Items / Next Steps), see REFERENCE.md. The CI path opens a triage issue instead — see Automation Integration below.
Automation Integration
The .github/workflows/changelog-review.yml workflow runs this review weekly. Its analysis is delegated to a unit-tested script so the logic is reviewable outside the YAML:
bash scripts/analyze-changelog.sh --excerpt <slice> --repo-dir . \
--tracked <ver> --latest <ver>The script (scripts/analyze-changelog.sh, tested by scripts/tests/test-analyze-changelog.sh) emits structured KEY=VALUE output and, beyond keyword counts, carries a deprecation → plugin-code bridge: for any tool/setting/command named in a deprecat|removed|renamed|unshipped line, it greps this repo's skills/hooks/agents/rules and surfaces the files that still reference it as triage candidates. This is the miss class from #1638 — a deprecated identifier (e.g. TaskOutput) that stayed referenced in a hook script because the old keyword map only ever pointed at .claude/rules/*.md.
Workflow flow:
1. Run weekly on schedule 2. Fetch changelog and compare versions (skip-if-exists is drift-aware: an open but unactioned tracking issue no longer suppresses newer versions) 3. If new versions found: run the analyzer, open ONE tracking issue (highest priority = deprecated identifiers still referenced in our code), ratchet the version JSON via a tiny PR 4. Label issues appropriately
Quick Reference
Relevant Claude Code URLs
| Resource | URL |
|---|---|
| Changelog | https://raw.githubusercontent.com/anthropics/claude-code/main/CHANGELOG.md |
| Documentation | https://docs.anthropic.com/en/docs/claude-code |
| GitHub | https://github.com/anthropics/claude-code |
Version Number Format
Claude Code uses semantic versioning: MAJOR.MINOR.PATCH
- MAJOR: Breaking changes
- MINOR: New features
- PATCH: Bug fixes
Issue Labels
| Label | Use For |
|---|---|
changelog-review | All changelog-related issues |
breaking-change | Breaking changes requiring updates |
enhancement | New feature opportunities |
maintenance | General housekeeping |
Changelog Review — Reference
Supporting material for the changelog-review skill, loaded on demand.
Report Format
When producing a human-readable review (rather than the CI triage issue), use this structure:
# Claude Code Changelog Review
**Review Date**: YYYY-MM-DD
**Versions Reviewed**: X.X.X to Y.Y.Y
**Previous Check**: YYYY-MM-DD (vX.X.X)
## Summary
- **New versions**: N
- **High-impact changes**: N
- **Medium-impact changes**: N
- **Action items**: N
## High-Impact Changes
### [Version] - Change Title
**Impact**: Breaking/Security/Deprecation
**Affected plugins**: plugin1, plugin2
**Required action**: Description of what needs to be done
## Medium-Impact Changes
### [Version] - Change Title
**Impact**: New feature/Enhancement
**Opportunity**: How plugins could benefit
**Suggested plugins**: plugin1, plugin2
## Action Items
- [ ] Item 1 (high priority)
- [ ] Item 2 (medium priority)
## Next Steps
1. Create issues for high-priority items
2. Schedule review of medium-impact changes
3. Update version tracking file#!/usr/bin/env bash
# Analyze a Claude Code changelog excerpt for plugin impact.
#
# Emits structured KEY=VALUE output (see .claude/rules/structured-script-output.md)
# that the changelog-review workflow parses to build a triage issue. Extracted
# from the inline workflow bash so it is unit-testable (issue #1638).
#
# What it does beyond raw keyword counting:
# - Adds a DEPRECATION dimension (deprecat|removed|renamed|unshipped|no longer)
# — the class that silently slipped through before (TaskOutput, 2.1.83).
# - For each deprecated/removed *identifier* named in the excerpt, greps the
# repo's plugin content (skills/hooks/agents/rules) and surfaces the files
# that reference it as triage candidates — the missing "changelog → plugin
# code" bridge. A rule-doc-only candidate map would never have routed the
# TaskOutput deprecation to hooks-plugin/hooks/bash-antipatterns.sh.
# - Flags an oversized excerpt (a review stall) as STATUS=WARN so a 60-version
# mega-batch is visible rather than silently lossy.
#
# Usage:
# analyze-changelog.sh --excerpt <file> [--repo-dir <dir>] \
# [--tracked <ver>] [--latest <ver>] [--max-versions <n>]
#
# Output: one `=== CHANGELOG ANALYSIS ===` section on stdout. Candidate files
# are listed one-per-line between `CANDIDATES:` and the section footer.
set -uo pipefail
excerpt=""
repo_dir="."
tracked="unknown"
latest="unknown"
max_versions=25
while [ $# -gt 0 ]; do
case "$1" in
--excerpt) excerpt="$2"; shift 2 ;;
--repo-dir) repo_dir="$2"; shift 2 ;;
--tracked) tracked="$2"; shift 2 ;;
--latest) latest="$2"; shift 2 ;;
--max-versions) max_versions="$2"; shift 2 ;;
*) echo "Unknown argument: $1" >&2; exit 2 ;;
esac
done
if [ -z "$excerpt" ] || [ ! -f "$excerpt" ]; then
echo "ERROR: --excerpt <file> is required and must exist" >&2
exit 2
fi
# grep -c exits 1 on zero matches but still prints "0"; `|| true` keeps the
# count while normalising the exit code (see .claude/rules/parallel-safe-queries.md).
count() { grep -ciE "$1" "$excerpt" 2>/dev/null || true; }
HOOK=$(count 'hook')
SKILL=$(count 'skill')
AGENT=$(count 'agent|subagent')
PERM=$(count 'permission')
PLUGIN=$(count 'plugin')
MCP=$(count 'mcp')
SANDBOX=$(count 'sandbox')
BREAKING=$(count 'breaking')
# New dimension (#1638): deprecations/removals/renames that previously scored
# zero on every tracked keyword and sorted as Low / no-candidates.
DEPRECATION=$(count 'deprecat|removed|renamed|unshipped|no longer')
# Number of version headings in the excerpt = how many new versions this batch
# covers. A large batch signals a review stall and a higher drop risk.
VERSION_COUNT=$(grep -cE '^## \[?[0-9]+\.[0-9]+\.[0-9]+' "$excerpt" 2>/dev/null || true)
# --- Candidate rule-doc files from keyword hits (the original mapping) ---------
candidates=()
add() { candidates+=("$@"); }
[ "$HOOK" -gt 0 ] && add ".claude/rules/hooks-reference.md" ".claude/rules/prompt-agent-hooks.md"
[ "$SKILL" -gt 0 ] && add ".claude/rules/skill-development.md"
[ "$AGENT" -gt 0 ] && add ".claude/rules/agent-development.md"
[ "$PERM" -gt 0 ] && add ".claude/rules/agentic-permissions.md"
[ "$PLUGIN" -gt 0 ] && add ".claude/rules/plugin-structure.md"
[ "$MCP" -gt 0 ] && add ".claude/rules/hooks-reference.md" ".claude/rules/prompt-agent-hooks.md"
[ "$SANDBOX" -gt 0 ] && add ".claude/rules/sandbox-guidance.md"
[ "$BREAKING" -gt 0 ] && add "CLAUDE.md" ".claude/rules/plugin-structure.md"
# --- Deprecation → plugin-code bridge (#1638) ---------------------------------
# Extract identifiers named in deprecation/removal lines, then grep the repo's
# plugin content for any skill/hook/agent/rule that still references them.
DEPRECATED_TOKENS=""
ACTIONABLE_DEPRECATION=0
if [ "$DEPRECATION" -gt 0 ]; then
# Search roots that actually exist (keeps the script runnable in test fixtures
# that only stand up a subset of the tree).
roots=()
for r in "$repo_dir"/*-plugin "$repo_dir/.claude/rules" "$repo_dir/CLAUDE.md"; do
[ -e "$r" ] && roots+=("$r")
done
# Tokens: backtick-delimited names on deprecation-context lines that look like
# tool/setting identifiers (contain an uppercase letter — TaskOutput,
# AgentOutputTool). This deliberately skips lowercase prose words and
# command-style `/output-style` tokens; the DEPRECATION count still surfaces
# those for human review in the issue body.
# Tokens: identifiers that are the *subject* of a deprecation, not merely
# co-located with a deprecation keyword. Anchoring to the verb is what keeps
# the bridge precise — the looser "any backtick token on a line containing
# 'removed'/'no longer'" form produced false positives like `SendMessage`
# ("messages relayed via `SendMessage` … no longer carry authority" — a
# hardening, not a deprecation), `enabledPlugins` ("stale `enabledPlugins`
# entries referencing removed marketplaces"), and `mcpServers` ("no longer
# strips inline `mcpServers`"). Two grammatical shapes are matched:
# verb-first — "Deprecated/Removed/Renamed/Unshipped … `Token`"
# token-first — "`Token` … is/are/now/been deprecated/removed/renamed"
# `[^`]{0,40}` can't cross a backtick, so verb-first always grabs the first
# backticked identifier after the verb (the subject), never a replacement
# named later in an "in favor of `Other`" clause.
mapfile -t tokens < <(
{
grep -oE '(Deprecated|Removed|Renamed|Unshipped|Un-shipped)[^`]{0,40}`[^`]+`' "$excerpt" 2>/dev/null \
| grep -oE '`[^`]+`$'
grep -oE '`[^`]+`[^`]{0,40}(is|are|now|been)[[:space:]]+(deprecated|removed|renamed)' "$excerpt" 2>/dev/null \
| grep -oE '^`[^`]+`'
} 2>/dev/null \
| tr -d '`' \
| grep -E '[A-Z]' 2>/dev/null \
| grep -E '^[A-Za-z][A-Za-z0-9_]{2,}$' 2>/dev/null \
| sort -u || true
)
found_tokens=()
if [ "${#roots[@]}" -gt 0 ]; then
for tok in "${tokens[@]}"; do
[ -z "$tok" ] && continue
# -F literal, -w whole-word, -l files-with-matches. Skip the changelog
# tracking JSON so a prior recording of the token doesn't self-match.
mapfile -t hits < <(grep -rlwF "$tok" "${roots[@]}" 2>/dev/null \
| grep -v '.claude-code-version-check.json' || true)
if [ "${#hits[@]}" -gt 0 ]; then
found_tokens+=("$tok")
for h in "${hits[@]}"; do
# Normalise to a repo-relative path for the issue body.
add "${h#"$repo_dir"/}"
done
fi
done
fi
if [ "${#found_tokens[@]}" -gt 0 ]; then
ACTIONABLE_DEPRECATION=1
DEPRECATED_TOKENS=$(printf '%s ' "${found_tokens[@]}")
DEPRECATED_TOKENS=${DEPRECATED_TOKENS% }
fi
fi
# Dedupe candidates, preserving first-seen order.
mapfile -t uniq_candidates < <(printf '%s\n' "${candidates[@]:-}" | awk 'NF && !seen[$0]++')
# --- Status roll-up ------------------------------------------------------------
# WARN when the batch is oversized (stall) or when a deprecation references live
# plugin code that needs follow-up. Neither is an error — the workflow still
# triages — but both deserve a visible flag.
issue_count=0
issues=()
if [ "${VERSION_COUNT:-0}" -gt "$max_versions" ]; then
issues+=("SEVERITY=WARN TYPE=oversized_batch MSG=excerpt spans ${VERSION_COUNT} versions (>${max_versions}); review-stall, higher drop risk")
issue_count=$((issue_count + 1))
fi
if [ "$ACTIONABLE_DEPRECATION" -eq 1 ]; then
issues+=("SEVERITY=WARN TYPE=actionable_deprecation MSG=deprecated identifiers referenced in plugin code: ${DEPRECATED_TOKENS}")
issue_count=$((issue_count + 1))
fi
STATUS=OK
[ "$issue_count" -gt 0 ] && STATUS=WARN
SUMMARY="Hook:$HOOK Skill:$SKILL Agent:$AGENT Permission:$PERM Plugin:$PLUGIN MCP:$MCP Sandbox:$SANDBOX Breaking:$BREAKING Deprecation:$DEPRECATION"
# --- Emit ----------------------------------------------------------------------
echo "=== CHANGELOG ANALYSIS ==="
echo "TRACKED=$tracked"
echo "LATEST=$latest"
echo "VERSION_COUNT=${VERSION_COUNT:-0}"
echo "HOOK=$HOOK"
echo "SKILL=$SKILL"
echo "AGENT=$AGENT"
echo "PERMISSION=$PERM"
echo "PLUGIN=$PLUGIN"
echo "MCP=$MCP"
echo "SANDBOX=$SANDBOX"
echo "BREAKING=$BREAKING"
echo "DEPRECATION=$DEPRECATION"
echo "ACTIONABLE_DEPRECATION=$ACTIONABLE_DEPRECATION"
echo "DEPRECATED_TOKENS=$DEPRECATED_TOKENS"
echo "SUMMARY=$SUMMARY"
echo "STATUS=$STATUS"
echo "ISSUE_COUNT=$issue_count"
if [ "$issue_count" -gt 0 ]; then
echo "ISSUES:"
for i in "${issues[@]}"; do
echo " - $i"
done
fi
echo "CANDIDATES:"
for c in "${uniq_candidates[@]:-}"; do
[ -n "$c" ] && echo " $c"
done
echo "=== END CHANGELOG ANALYSIS ==="
exit 0
#!/usr/bin/env bash
# Regression tests for analyze-changelog.sh
#
# Run: bash project-plugin/skills/changelog-review/scripts/tests/test-analyze-changelog.sh
# Exit 0 = all tests pass, Exit 1 = failures
#
# Covers issue #1638 (changelog-review blind to tool deprecations):
# - A "Deprecated `TaskOutput` tool" line is counted (DEPRECATION dimension).
# - A deprecated identifier referenced in plugin code is surfaced as a
# candidate file — the "changelog → plugin code" bridge that the rule-doc-
# only map lacked. This is the exact miss that left bash-antipatterns.sh
# recommending the dead TaskOutput tool.
# - A deprecation token NOT referenced anywhere does not raise the actionable
# flag (no false positives).
# - An oversized excerpt (review stall) flags STATUS=WARN.
# - A pure feature excerpt stays STATUS=OK with no deprecation.
set -uo pipefail
SCRIPT="$(cd "$(dirname "$0")/.." && pwd)/analyze-changelog.sh"
PASS=0
FAIL=0
# Throwaway fake repo: one plugin hook that references the deprecated tool, plus
# a rules dir so keyword→rule-doc mapping has somewhere to point.
REPO=$(mktemp -d)
trap 'rm -rf "$REPO"' EXIT
mkdir -p "$REPO/fake-plugin/hooks" "$REPO/.claude/rules"
cat > "$REPO/fake-plugin/hooks/bash-antipatterns.sh" <<'EOF'
# REMINDER: Use the Read tool on the task-output file path.
# (The TaskOutput tool is deprecated.)
EOF
# Identifiers that appear NEAR deprecation keywords in the changelog but are not
# themselves being deprecated (the #1638 sweep false positives). Referenced here
# so that a too-loose extractor WOULD surface them — making the negative
# assertions below meaningful.
cat > "$REPO/fake-plugin/hooks/coexist.sh" <<'EOF'
# uses SendMessage between agents; reads enabledPlugins and mcpServers config
EOF
: > "$REPO/.claude/rules/skill-development.md"
run() {
# run <excerpt-content> [extra-args...] -> sets OUT to the script's stdout
local content="$1"; shift
local exc; exc=$(mktemp)
printf '%s\n' "$content" > "$exc"
OUT=$(bash "$SCRIPT" --excerpt "$exc" --repo-dir "$REPO" --tracked 2.1.76 --latest 2.1.138 "$@")
rm -f "$exc"
}
field() { echo "$OUT" | grep -E "^$1=" | head -1 | cut -d= -f2-; }
assert_eq() {
local desc="$1" want="$2" got="$3"
if [ "$got" = "$want" ]; then
printf " PASS: %s\n" "$desc"; PASS=$((PASS + 1))
else
printf " FAIL: %s (want '%s', got '%s')\n" "$desc" "$want" "$got"; FAIL=$((FAIL + 1))
fi
}
assert_contains() {
local desc="$1" needle="$2"
if echo "$OUT" | grep -qF "$needle"; then
printf " PASS: %s\n" "$desc"; PASS=$((PASS + 1))
else
printf " FAIL: %s (output missing: %s)\n" "$desc" "$needle"; FAIL=$((FAIL + 1))
fi
}
assert_absent() {
local desc="$1" needle="$2"
if echo "$OUT" | grep -qF "$needle"; then
printf " FAIL: %s (output unexpectedly contains: %s)\n" "$desc" "$needle"; FAIL=$((FAIL + 1))
else
printf " PASS: %s\n" "$desc"; PASS=$((PASS + 1))
fi
}
echo "=== analyze-changelog.sh tests ==="
# ── deprecation referenced in plugin code (the #1638 miss) ───────────────────
echo ""
echo "deprecated tool referenced in plugin code is surfaced as a candidate:"
run "## 2.1.83
- Deprecated \`TaskOutput\` tool in favor of using \`Read\` on the background task's output file path"
assert_eq "DEPRECATION dimension counts the line" "1" "$(field DEPRECATION)"
assert_eq "ACTIONABLE_DEPRECATION raised" "1" "$(field ACTIONABLE_DEPRECATION)"
assert_contains "DEPRECATED_TOKENS names TaskOutput" "DEPRECATED_TOKENS=TaskOutput"
assert_contains "candidate surfaces the referencing hook file" "fake-plugin/hooks/bash-antipatterns.sh"
assert_eq "STATUS is WARN for an actionable deprecation" "WARN" "$(field STATUS)"
assert_contains "issue row explains the actionable deprecation" "TYPE=actionable_deprecation"
# ── deprecation of an unreferenced identifier → no false positive ────────────
echo ""
echo "deprecation of an identifier absent from the repo does not raise the flag:"
run "## 2.1.99
- Deprecated \`SomeNonexistentTool\` in favor of the new flow"
assert_eq "DEPRECATION still counted" "1" "$(field DEPRECATION)"
assert_eq "ACTIONABLE_DEPRECATION stays 0 (token not in repo)" "0" "$(field ACTIONABLE_DEPRECATION)"
assert_absent "no fake-plugin candidate surfaced" "fake-plugin/hooks"
# ── verb-anchored extraction rejects co-located non-deprecations (#1638 sweep) ─
# Real shapes from the 2.1.138→2.1.176 sweep where a deprecation keyword sat on
# the same line as a live identifier that was NOT being deprecated.
echo ""
echo "co-located identifiers (not the deprecation subject) do not raise the flag:"
run "## 2.1.166
- Hardened cross-session messaging: messages relayed via \`SendMessage\` from other Claude sessions no longer carry user authority
## 2.1.153
- \`--strict-mcp-config\` no longer strips inline \`mcpServers\` from explicitly-passed agent definitions
## 2.1.152
- Fixed /doctor reporting for stale \`enabledPlugins\` entries referencing removed marketplaces or dropped plugins"
assert_eq "DEPRECATION count still fires on the coarse keywords" "3" "$(field DEPRECATION)"
assert_eq "ACTIONABLE_DEPRECATION stays 0 (none is the deprecation subject)" "0" "$(field ACTIONABLE_DEPRECATION)"
assert_absent "SendMessage not surfaced as a candidate" "coexist.sh"
# Counterpart true positive: the same verb-first shape that the sweep correctly
# caught (an env var that was genuinely removed) still surfaces.
run "## 2.1.160
- Removed \`TaskOutput\`; it is now a no-op"
assert_eq "verb-first 'Removed \`X\`' still raises the flag" "1" "$(field ACTIONABLE_DEPRECATION)"
assert_contains "the removed identifier is surfaced" "DEPRECATED_TOKENS=TaskOutput"
# ── oversized batch (review stall) flags WARN ────────────────────────────────
echo ""
echo "oversized excerpt (review stall) flags STATUS=WARN:"
run "## 2.1.05
- New feature
## 2.1.04
- New feature
## 2.1.03
- New feature" --max-versions 2
assert_eq "VERSION_COUNT counts the headings" "3" "$(field VERSION_COUNT)"
assert_eq "STATUS WARN on oversized batch" "WARN" "$(field STATUS)"
assert_contains "issue row explains the oversized batch" "TYPE=oversized_batch"
# ── pure feature excerpt stays clean ─────────────────────────────────────────
echo ""
echo "a pure feature excerpt has no deprecation and stays OK:"
run "## 2.1.90
- Added a new /color command for per-session prompt bar color"
assert_eq "DEPRECATION is 0" "0" "$(field DEPRECATION)"
assert_eq "ACTIONABLE_DEPRECATION is 0" "0" "$(field ACTIONABLE_DEPRECATION)"
assert_eq "STATUS OK" "OK" "$(field STATUS)"
# ── keyword → rule-doc mapping preserved ─────────────────────────────────────
echo ""
echo "keyword hits still map to rule-doc candidates:"
run "## 2.1.50
- New hook events: WorktreeCreate, TaskCompleted
- New permission model for subagents"
assert_contains "hook keyword maps to hooks-reference.md" ".claude/rules/hooks-reference.md"
assert_contains "agent keyword maps to agent-development.md" ".claude/rules/agent-development.md"
assert_contains "permission keyword maps to agentic-permissions.md" ".claude/rules/agentic-permissions.md"
# ── Summary ──────────────────────────────────────────────────────────────────
echo ""
echo "Results: $PASS passed, $FAIL failed"
[ "$FAIL" -eq 0 ] && exit 0 || exit 1