Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"name": "sdlc-wizard",
"source": ".",
"description": "SDLC enforcement for AI agents — TDD, planning, self-review, CI shepherd",
"version": "1.41.0",
"version": "1.41.1",
"author": {
"name": "Stefan Ayala"
},
Expand Down
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "sdlc-wizard",
"version": "1.41.0",
"version": "1.41.1",
"description": "SDLC enforcement for AI agents — TDD, planning, self-review, CI shepherd",
"author": {
"name": "Stefan Ayala",
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,9 @@ jobs:
- name: Run postmortem lessons tests (#221)
run: ./tests/test-postmortem-lessons.sh

- name: Run MCP-tool hook audit tests (#218)
run: ./tests/test-mcp-hook-audit.sh

# Clean up old bot comments on PR push (keeps PRs tidy)
# Also runs on workflow_dispatch (no-op) so branch protection doesn't block auto-merge.
cleanup-old-comments:
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to the SDLC Wizard.

> **Note:** This changelog is for humans to read. Don't manually apply these changes - just run the wizard ("Check for SDLC wizard updates") and it handles everything automatically.

## [1.41.1] - 2026-04-26

### Added

- **MCP-tool hooks audit documented** (ROADMAP #218). CC 2.1.118 introduced `type: "mcp_tool"` for hooks. Audited all 5 wizard hooks (sdlc-prompt-check, instructions-loaded-check, tdd-pretool-check, model-effort-check, precompact-seam-check) against MCP-tool migration criteria: portability, gating semantics, cross-tool state. Conclusion: all 5 stay bash. Per-hook rationale documented in CLAUDE_CODE_SDLC_WIZARD.md → "Known CC Gotchas → MCP-tool hooks audit". New `tests/test-mcp-hook-audit.sh` (7 tests) ensures the audit doesn't get re-litigated by future maintainers; if a hook DOES migrate later, the test is the natural place to update with new rationale.

## [1.41.0] - 2026-04-26

### Added
Expand Down
30 changes: 28 additions & 2 deletions CLAUDE_CODE_SDLC_WIZARD.md
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,32 @@ The same post-mortem documented that a length-limit prompt change (one of severa

See the dedicated subsection under [Tasks System](#tasks-system-v2116) (above, in Claude Code Feature Updates) for the full breakdown. Short version: pin `cleanupPeriodDays: 30` or higher in `.claude/settings.json` if you ever pause SDLC work for more than a week. The wizard ships this default in `cli/templates/settings.json` and the CLI's smart-merge preserves user overrides on `init --force`.

### MCP-tool hooks audit (ROADMAP #218, CC 2.1.118)

CC 2.1.118 introduced `type: "mcp_tool"` for hooks — a hook can now directly invoke an MCP tool instead of running a bash script. **Audit (2026-04-26) of all 5 wizard hooks concluded: none migrate, all stay bash.** This subsection documents the per-hook reasoning so future audits don't redo the work; if a future PR migrates a hook to MCP, update this entry with the new rationale rather than deleting it.

**Decision criteria applied** (any one rules out MCP):

1. **Portability** — bash hooks port to the **shipped** Codex sibling (`~/codex-sdlc-wizard`) and to a **planned** OpenCode sibling (ROADMAP #91, not yet shipped) without rewrite. MCP hooks are CC-specific. Cross-host portability is an XDLC requirement.
2. **Fail-closed gating** — hooks that block an action (exit 2 from PreCompact) need a fail-closed contract: any error in the hook MUST keep the block in place. CC docs ([code.claude.com/docs/en/hooks](https://code.claude.com/docs/en/hooks)) confirm `mcp_tool` hooks CAN gate via `decision: "block"` JSON output, but **MCP server errors are non-blocking by design** — if the MCP server is down/slow/buggy, the action proceeds. That breaks the fail-closed contract. Bash exit 2 fails closed.
3. **Local-only state** — hooks that read/write `~/.cache/sdlc-wizard/` or `.reviews/handoff.json` don't surface state across tool boundaries. MCP adds a wire format without consumers.

**Per-hook decision** (each row applies at least one criterion explicitly):

- **`sdlc-prompt-check.sh`** (UserPromptSubmit, ~132 lines) — emits the SDLC BASELINE text on every prompt; writes effort-bump signals to `~/.cache/sdlc-wizard/effort-signals.log` for self-consumption on next invocation. Decision: **Stay bash.** Portability criterion: same script ships to Codex sibling unchanged. Local-state criterion: signal log is local-only.
- **`instructions-loaded-check.sh`** (~202 lines) — InstructionsLoaded event; validates SDLC files exist, fetches npm `latest` with daily file cache (`~/.cache/sdlc-wizard/npm-latest.json`), emits staleness warnings. Decision: **Stay bash.** Portability criterion: Codex sibling has its own equivalent of session-start validation; bash port is direct. Local-state criterion: cache file is local.
- **`tdd-pretool-check.sh`** (~29 lines) — PreToolUse on Write/Edit/MultiEdit; emits the TDD reminder text. Decision: **Stay bash.** Portability criterion: trivially portable (one-screen text emit). Gating criterion: not applicable (this hook does not block, it advises). MCP would add a runtime dependency for zero functional gain.
- **`model-effort-check.sh`** (~69 lines) — SessionStart event; reads `CLAUDE_CODE_EFFORT` env var, emits silent/soft/loud nudge per-tier. Decision: **Stay bash.** Portability criterion: env-var read maps 1:1 to any agent runtime. Local-state criterion: not applicable, hook is stateless.
- **`precompact-seam-check.sh`** (~125 lines) — PreCompact event (matcher: `manual`); reads `.reviews/handoff.json` via jq, blocks manual `/compact` with exit 2 + stderr message when status is `PENDING_*` and the linked PR (if any) isn't merged. Decision: **Stay bash.** Fail-closed gating criterion: bash exit 2 fails closed by definition; an MCP `mcp_tool` hook returning `decision: "block"` works on the happy path, but if the MCP server crashes/times out the action proceeds — that flips the safety property from fail-closed to fail-open. For a hook whose entire job is to prevent context loss at bad seams, fail-open is the wrong default.

**When to revisit this audit:**

- A future hook genuinely needs cross-tool structured state surfacing (e.g., a "score history reader" that an MCP-aware skill consumes directly).
- Anthropic deprecates bash hooks in favor of `mcp_tool` (currently both are first-class).
- Codex / OpenCode siblings gain native MCP-tool hook support (then portability is no longer an MCP-rules-out).

Until then, default answer for new hooks is bash.

---

## Prove It's Better
Expand Down Expand Up @@ -2874,7 +2900,7 @@ If deployment fails or post-deploy verification catches issues:

**SDLC.md:**
```markdown
<!-- SDLC Wizard Version: 1.41.0 -->
<!-- SDLC Wizard Version: 1.41.1 -->
<!-- Setup Date: [DATE] -->
<!-- Completed Steps: step-0.1, step-0.2, step-0.4, step-1, step-2, step-3, step-4, step-5, step-6, step-7, step-8, step-9 -->
<!-- Git Workflow: [PRs or Solo] -->
Expand Down Expand Up @@ -3936,7 +3962,7 @@ Walk through updates? (y/n)
Store wizard state in `SDLC.md` as metadata comments (invisible to readers, parseable by Claude):

```markdown
<!-- SDLC Wizard Version: 1.41.0 -->
<!-- SDLC Wizard Version: 1.41.1 -->
<!-- Setup Date: 2026-01-24 -->
<!-- Completed Steps: step-0.1, step-0.2, step-1, step-2, step-3, step-4, step-5, step-6, step-7, step-8, step-9 -->
<!-- Git Workflow: PRs -->
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Thank you for your interest in improving the SDLC Wizard!
./tests/test-update-skill-cli-version.sh && \
./tests/test-cleanup-period-guidance.sh && \
./tests/test-postmortem-lessons.sh && \
./tests/test-mcp-hook-audit.sh && \
./tests/e2e/run-simulation.sh && \
./tests/e2e/test-deterministic-checks.sh && \
./tests/e2e/test-scenario-rotation.sh && \
Expand Down
4 changes: 2 additions & 2 deletions SDLC.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!-- SDLC Wizard Version: 1.41.0 -->
<!-- SDLC Wizard Version: 1.41.1 -->
<!-- Setup Date: 2026-01-24 -->
<!-- Completed Steps: step-0.1, step-0.2, step-1, step-2, step-3, step-4, step-5, step-6, step-7, step-8, step-9 -->
# SDLC Configuration
Expand All @@ -7,7 +7,7 @@

| Property | Value |
|----------|-------|
| Wizard Version | 1.41.0 |
| Wizard Version | 1.41.1 |
| Last Updated | 2026-04-24 |
| Claude Code Baseline | v2.1.111+ (required for Opus 4.7 / `opus[1m]`) |
| Recommended Model | `opus[1m]` (Opus 4.7, 1M context) — run `/model opus[1m]` |
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "agentic-sdlc-wizard",
"version": "1.41.0",
"version": "1.41.1",
"description": "SDLC enforcement for Claude Code — hooks, skills, and wizard setup in one command",
"bin": {
"sdlc-wizard": "cli/bin/sdlc-wizard.js"
Expand Down
3 changes: 2 additions & 1 deletion skills/update/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,10 @@ Parse all CHANGELOG entries between the user's installed version and the latest.

```
Installed: 1.24.0
Latest: 1.41.0
Latest: 1.41.1

What changed:
- [1.41.1] MCP-tool hooks audit — ROADMAP #218. Audited all 5 wizard hooks against CC 2.1.118's `type: "mcp_tool"` migration option; conclusion: all stay bash (portability to Codex/OpenCode siblings + exit-code gating semantics rule out MCP). Per-hook rationale documented under "Known CC Gotchas → MCP-tool hooks audit". 7 quality tests.
- [1.41.0] Post-mortem 2026-04-23 lessons folded into wizard — ROADMAP #221. New "Known CC Gotchas" section documents extended-thinking + caching + idle-session failure mode. Recommended Effort section cites the post-mortem as third-party evidence ("don't rely on CC default — set effort yourself"). Brevity-cap audit clean, regression guard added. 7 quality tests.
- [1.40.1] cleanupPeriodDays: 30 pinned in template — ROADMAP #225. CC 2.1.117 expanded `cleanupPeriodDays` to also cover `~/.claude/tasks/`. Aggressive defaults could prune in-progress TodoWrite checklists for paused long-running features. Wizard now ships a 30-day floor + documented gotcha. 7 quality tests.
- [1.40.0] CLI version detection in /update-wizard — ROADMAP #232. New Step 1.5 detects locally installed `agentic-sdlc-wizard` CLI version (npm ls + npx cache inspection, both with semver-aware ordering), compares to `registry.npmjs.org/agentic-sdlc-wizard/latest`, and surfaces a 3-way upgrade choice BEFORE drift detection: A) refresh CLI cache only (default, safest), B) `init --force` re-init with explicit non-settings overwrite warning, C) skip. Closes the gap where in-session file updates landed but the user's stale npx cache kept running an old CLI. Mirrors `claude update` UX. 8 quality tests, mutation-verified.
Expand Down
120 changes: 120 additions & 0 deletions tests/test-mcp-hook-audit.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#!/bin/bash
# Quality test: ROADMAP #218 MCP-tool hook audit conclusion documented in wizard.
#
# CC 2.1.118 introduced `type: "mcp_tool"` for hooks. Question: should any of our
# 5 bash hooks migrate? Audit (2026-04-26) concluded NO — all 5 hooks stay bash:
# (1) bash hooks port to Codex/OpenCode siblings, MCP hooks don't, (2) none of
# our hooks needs cross-tool structured-state surfacing, (3) precompact-seam-check
# needs to BLOCK on exit 2, MCP is for invocation not gating.
#
# This test asserts the audit conclusion is documented in CLAUDE_CODE_SDLC_WIZARD.md
# so future maintainers don't redo the audit. If a future PR migrates a hook to MCP,
# this test should be UPDATED with new rationale, not deleted.

set -e

WIZARD="${WIZARD:-CLAUDE_CODE_SDLC_WIZARD.md}"
PASSED=0
FAILED=0

pass() { echo "PASS: $1"; PASSED=$((PASSED + 1)); }
fail() { echo "FAIL: $1"; FAILED=$((FAILED + 1)); }

if [ ! -f "$WIZARD" ]; then
echo "FAIL: $WIZARD not found"
exit 1
fi

# Extract the MCP-tool hook audit section
audit_section=$(awk '
/^### MCP-tool hooks audit/ { flag=1; next }
/^### / && flag { flag=0 }
/^## / && flag { flag=0 }
flag { print }
' "$WIZARD")

# Test 1: audit section exists
if [ -n "$audit_section" ]; then
pass "MCP-tool hooks audit section exists in wizard"
else
fail "Wizard must have an 'MCP-tool hooks audit' subsection documenting the #218 conclusion"
fi

# Test 2: section cites CC 2.1.118 as the trigger
if echo "$audit_section" | grep -qE "2\.1\.118|mcp_tool"; then
pass "Audit cites CC 2.1.118 / mcp_tool as the trigger feature"
else
fail "Audit must cite the CC 2.1.118 mcp_tool feature as context"
fi

# Test 3: each hook has a SUBSTANTIVE bullet (not just name + "Stay bash" filler).
# Extract each hook's bullet by line: the line containing the hook name with backticks.
# Each bullet must have BOTH:
# (i) the hook name in backticks (identifies the bullet)
# (ii) ≥120 chars of rationale on that bullet line (not filler)
# (iii) at least one criterion keyword (Portability|gating|local-state|fail-closed|criterion)
all_hooks=true
for h in sdlc-prompt-check instructions-loaded-check tdd-pretool-check model-effort-check precompact-seam-check; do
bullet=$(echo "$audit_section" | grep "\`$h\.sh\`" || true)
if [ -z "$bullet" ]; then
all_hooks=false
echo " Missing hook bullet: $h.sh"
continue
fi
bullet_len=${#bullet}
if [ "$bullet_len" -lt 200 ]; then
all_hooks=false
echo " Bullet too short for $h.sh (${bullet_len} chars, need ≥200) — likely filler"
continue
fi
if ! echo "$bullet" | grep -qiE "Portability|gating|local-state|fail-closed|criterion"; then
all_hooks=false
echo " Bullet for $h.sh lacks any decision-criterion keyword — appears to be filler"
fi
done
if [ "$all_hooks" = true ]; then
pass "Each of 5 hooks has substantive per-hook rationale (≥200 chars + criterion keyword)"
else
fail "Audit must give each hook a SUBSTANTIVE bullet (not 'Stay bash' filler) — see lines above"
fi

# Test 4: conclusion is "stay bash" / "no migration"
if echo "$audit_section" | grep -qiE "stay.*bash|no migration|no MCP migration|remain.*bash|none.*benefit|default.*leave"; then
pass "Audit conclusion: hooks stay bash"
else
fail "Audit must explicitly conclude 'all hooks stay bash, no MCP migration'"
fi

# Test 5: portability rationale (bash → Codex/OpenCode siblings)
if echo "$audit_section" | grep -qiE "Codex|OpenCode|portab|sibling"; then
pass "Audit cites portability to other agent runtimes"
else
fail "Audit must cite portability — bash hooks port to Codex/OpenCode siblings; MCP hooks don't"
fi

# Test 6: precompact gating rationale must live INSIDE the precompact bullet.
# Just having "block" anywhere in the section isn't enough — the precompact bullet
# itself must explain the fail-closed gating concern.
precompact_bullet=$(echo "$audit_section" | grep '`precompact-seam-check\.sh`' || true)
if [ -n "$precompact_bullet" ] \
&& echo "$precompact_bullet" | grep -qiE "fail-closed|fail-open|exit 2|MCP.*server.*(error|crash|down|timeout|fail)"; then
pass "Precompact bullet explains fail-closed gating concern with MCP servers"
else
fail "Precompact bullet must explain exit-2 fail-closed contract vs MCP server fail-open behavior"
fi

# Test 7: CHANGELOG [1.41.1] entry exists and mentions #218
if grep -qE "^## \[1\.41\.1\]" CHANGELOG.md; then
cl_entry=$(awk '/^## \[1\.41\.1\]/,/^## \[1\.41\.0\]/' CHANGELOG.md | sed '$d')
if echo "$cl_entry" | grep -qE "#218|MCP|mcp_tool"; then
pass "CHANGELOG [1.41.1] mentions #218 / MCP audit"
else
fail "CHANGELOG [1.41.1] must reference #218 or MCP audit"
fi
else
fail "CHANGELOG [1.41.1] entry missing"
fi

echo ""
echo "Results: $PASSED passed, $FAILED failed"
[ "$FAILED" -eq 0 ]
Loading