agentic-sdlc-wizard 1.42.2 → 1.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,7 +13,7 @@
13
13
  "name": "sdlc-wizard",
14
14
  "source": ".",
15
15
  "description": "SDLC enforcement for AI agents — TDD, planning, self-review, CI shepherd",
16
- "version": "1.42.2",
16
+ "version": "1.44.0",
17
17
  "author": {
18
18
  "name": "Stefan Ayala"
19
19
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sdlc-wizard",
3
- "version": "1.42.2",
3
+ "version": "1.44.0",
4
4
  "description": "SDLC enforcement for AI agents — TDD, planning, self-review, CI shepherd",
5
5
  "author": {
6
6
  "name": "Stefan Ayala",
package/CHANGELOG.md CHANGED
@@ -4,6 +4,39 @@ All notable changes to the SDLC Wizard.
4
4
 
5
5
  > **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.
6
6
 
7
+ ## [1.44.0] - 2026-04-27
8
+
9
+ ### Fixed
10
+
11
+ - **Install-path & cache hygiene** — closes #254 (P1+P2), #239, #238 filed by consumer codeguesser after upgrading 1.32.0 → 1.42.1.
12
+ - **#254 Bug 1 (P1)**: `cli/init.js` FILES list now ships `hooks/_find-sdlc-root.sh`. The helper is sourced by all 5 wizard hooks and provides `find_sdlc_root` + `dedupe_plugin_or_project`, but `npx ... init --force` previously didn't copy it. Result on every consumer: stderr noise (`_find-sdlc-root.sh: No such file or directory`, `dedupe_plugin_or_project: command not found`) and the SDLC walk-up logic (#171), silent-exit-for-non-SDLC-dirs (#173), and plugin/project dedupe were all silently dead.
13
+ - **#254 Bug 2 (P2)**: `init --force` now invalidates `~/.cache/sdlc-wizard/latest-version` (or `$SDLC_WIZARD_CACHE_DIR/latest-version` when overridden). Previously the cache held the pre-upgrade "latest" for up to 24h, producing reverse staleness nudges like `update available 1.42.1 → 1.41.1`. Console now prints a `BUST` line so the cache eviction is visible.
14
+ - **#254 Bug 2 / #239 (semver-direction)**: `instructions-loaded-check.sh` introduces `semver_lt` for numeric major/minor/patch comparison. Nudge gate switched from `LATEST_VERSION != INSTALLED_VERSION` (which fired on equality AND reverse direction) to `semver_lt INSTALLED LATEST` — so the nudge only fires when an actual upgrade is available. Cache reads gain a sanity check: cached "latest" must be >= installed, otherwise force a refetch.
15
+ - **#239 (npm failure surface)**: When `npm view` fails (e.g. EPERM from root-owned `~/.npm/_cacache/`) AND no usable cache exists, the hook now prints `npm view failed — version check unavailable (run 'npm view agentic-sdlc-wizard version' to debug)`. Previously the version-check block produced no output at all and the user had no signal that the staleness nudge was broken.
16
+ - **#238 (dual-channel ack sentinel)**: The "CLI skills + Claude plugin both present" warning gains an opt-in silence mechanism. Once the user runs `mkdir -p $DUAL_CACHE_DIR && touch $DUAL_ACK_FILE` (the exact command is printed inside the nudge), the warning stops firing. Sentinel lives at `$SDLC_WIZARD_CACHE_DIR/dual-channel-acknowledged` (defaults to `~/.cache/sdlc-wizard/dual-channel-acknowledged`). Removes the every-session noise that was training users to ignore all hook output.
17
+ - 8 new tests across `tests/test-cli.sh` (75 total, +3 for #254) and `tests/test-hooks.sh` (134 total, +5 for the four bugs + Codex round-1 cache-dir-absent regression test). Codex CERTIFIED 10/10 round 2.
18
+
19
+ ### Files
20
+
21
+ - `cli/init.js` — `FILES` list adds `_find-sdlc-root.sh`; new `invalidateVersionCache()` helper; `--force` path calls bust + logs `BUST` line
22
+ - `hooks/instructions-loaded-check.sh` — new `semver_lt()`; cache sanity check; npm-fail surface; semver-direction nudge gate; dual-channel ack sentinel
23
+ - `tests/test-cli.sh` — file-count test bumped 11→12; 3 new tests
24
+ - `tests/test-hooks.sh` — 5 new tests covering all 4 bugs + Codex cache-dir-absent regression
25
+
26
+ ## [1.43.0] - 2026-04-27
27
+
28
+ ### Added
29
+
30
+ - **Token-spike anomaly detection** (ROADMAP #220 closure). New SessionStart hook `hooks/token-spike-check.sh` walks the CC transcript dir (`~/.claude/projects/<sanitized-cwd>/*.jsonl`), sums per-session `usage.{input_tokens, output_tokens, cache_creation_input_tokens, cache_read_input_tokens}` from every assistant message with a usage block, and idempotently appends one record per `session_id` to `.metrics/token-history.jsonl`. The hook then warns when the most recent completed session's `costly_tokens` (= `input + cache_creation + output`, excluding the cheap ~$1.50/M `cache_read` tier) exceeds the rolling baseline by more than 2σ. Anthropic's 2026-04-23 post-mortem documented a CC caching bug that "continuously dropped thinking blocks from subsequent requests" — invisible until the invoice arrived; this hook surfaces the same shape of regression the moment it occurs. The `--metric median` mode (default) uses MAD (median absolute deviation) instead of stdev for the spread term, so a single outlier session in the baseline doesn't mask the next genuine spike. Hook is gated on `.metrics/` existing in the project root (opt-in for consumers, on for the wizard repo which already maintains `.metrics/catches.jsonl`). 14 quality tests in `tests/test-token-spike.sh` cover burn calculation against summed transcript fields, idempotent ingest, positive/negative spike detection, the min-baseline floor (no false positives on <5-record windows), the median-vs-mean contrast (both `--metric` modes invoked, asserting median warns and mean does not on an outlier-inflated fixture), flat-baseline minimum-spread floor (1000→1100 suppressed, 1000→50000 still fires), privacy/type-coercion (a malicious transcript with `"USER_SECRET_INPUT"` strings in usage fields cannot leak content into history), concurrent-ingest atomic-lock serialization (parallel ingests produce 1 record per session), and hook gating + warning surface.
31
+
32
+ ### Files
33
+
34
+ - New `hooks/token-spike-check.sh` (SessionStart, opt-in)
35
+ - New `tests/e2e/token-analytics.sh` (writer + checker engine; supports `--ingest`, `--check`, `--report`, `--metric median|mean`, `--window`, `--threshold-sigma`)
36
+ - New `tests/test-token-spike.sh` (14 quality tests)
37
+ - Hook registered in `hooks/hooks.json` and `.claude/settings.json` SessionStart event
38
+ - `SDLC.md` hooks table + file tree updated
39
+
7
40
  ## [1.42.2] - 2026-04-26
8
41
 
9
42
  ### Documented
@@ -2918,7 +2918,7 @@ If deployment fails or post-deploy verification catches issues:
2918
2918
 
2919
2919
  **SDLC.md:**
2920
2920
  ```markdown
2921
- <!-- SDLC Wizard Version: 1.42.2 -->
2921
+ <!-- SDLC Wizard Version: 1.44.0 -->
2922
2922
  <!-- Setup Date: [DATE] -->
2923
2923
  <!-- 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 -->
2924
2924
  <!-- Git Workflow: [PRs or Solo] -->
@@ -3983,7 +3983,7 @@ Walk through updates? (y/n)
3983
3983
  Store wizard state in `SDLC.md` as metadata comments (invisible to readers, parseable by Claude):
3984
3984
 
3985
3985
  ```markdown
3986
- <!-- SDLC Wizard Version: 1.42.2 -->
3986
+ <!-- SDLC Wizard Version: 1.44.0 -->
3987
3987
  <!-- Setup Date: 2026-01-24 -->
3988
3988
  <!-- 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 -->
3989
3989
  <!-- Git Workflow: PRs -->
package/cli/init.js CHANGED
@@ -25,6 +25,10 @@ const FILES = [
25
25
  { src: 'hooks/instructions-loaded-check.sh', dest: '.claude/hooks/instructions-loaded-check.sh', executable: true, base: REPO_ROOT },
26
26
  { src: 'hooks/model-effort-check.sh', dest: '.claude/hooks/model-effort-check.sh', executable: true, base: REPO_ROOT },
27
27
  { src: 'hooks/precompact-seam-check.sh', dest: '.claude/hooks/precompact-seam-check.sh', executable: true, base: REPO_ROOT },
28
+ // #254 Bug 1: shared helper sourced by all hooks above. Must ship — without
29
+ // it, hooks emit "_find-sdlc-root.sh: No such file or directory" + the
30
+ // SDLC root walk-up logic is silently dead.
31
+ { src: 'hooks/_find-sdlc-root.sh', dest: '.claude/hooks/_find-sdlc-root.sh', base: REPO_ROOT },
28
32
  { src: 'skills/sdlc/SKILL.md', dest: '.claude/skills/sdlc/SKILL.md', base: REPO_ROOT },
29
33
  { src: 'skills/setup/SKILL.md', dest: '.claude/skills/setup/SKILL.md', base: REPO_ROOT },
30
34
  { src: 'skills/update/SKILL.md', dest: '.claude/skills/update/SKILL.md', base: REPO_ROOT },
@@ -235,6 +239,21 @@ function printOps(ops) {
235
239
  }
236
240
  }
237
241
 
242
+ // #254 Bug 2: clear the version cache on `init --force` so the
243
+ // instructions-loaded hook re-fetches latest from npm post-upgrade. Without
244
+ // this, the cache holds the pre-upgrade "latest" for up to 24h and the hook
245
+ // prints reverse nudges like "1.42.1 → 1.41.1".
246
+ function invalidateVersionCache({ dryRun }) {
247
+ const cacheDir = process.env.SDLC_WIZARD_CACHE_DIR
248
+ || path.join(os.homedir(), '.cache', 'sdlc-wizard');
249
+ const cacheFile = path.join(cacheDir, 'latest-version');
250
+ if (!fs.existsSync(cacheFile)) return false;
251
+ if (!dryRun) {
252
+ try { fs.rmSync(cacheFile, { force: true }); } catch (_) { /* best-effort */ }
253
+ }
254
+ return true;
255
+ }
256
+
238
257
  function init(targetDir, { force = false, dryRun = false } = {}) {
239
258
  if (!dryRun && !force) {
240
259
  const pluginPaths = detectPluginInstall();
@@ -291,6 +310,13 @@ function init(targetDir, { force = false, dryRun = false } = {}) {
291
310
  console.log(` ${GREEN}APPEND${RESET} .gitignore (${gitignoreAdds.join(', ')})`);
292
311
  }
293
312
 
313
+ // #254 Bug 2: bust the latest-version cache after an upgrade so the
314
+ // staleness nudge re-fetches a fresh value from npm. Only on --force,
315
+ // since that's the upgrade path.
316
+ if (force && invalidateVersionCache({ dryRun: false })) {
317
+ console.log(` ${CYAN}BUST${RESET} version cache (${path.join(process.env.SDLC_WIZARD_CACHE_DIR || path.join(os.homedir(), '.cache', 'sdlc-wizard'), 'latest-version')})`);
318
+ }
319
+
294
320
  console.log(`
295
321
  ${GREEN}SDLC Wizard installed successfully!${RESET}
296
322
 
package/hooks/hooks.json CHANGED
@@ -39,6 +39,10 @@
39
39
  {
40
40
  "type": "command",
41
41
  "command": "${CLAUDE_PLUGIN_ROOT}/hooks/model-effort-check.sh"
42
+ },
43
+ {
44
+ "type": "command",
45
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/token-spike-check.sh"
42
46
  }
43
47
  ]
44
48
  }
@@ -44,14 +44,38 @@ fi
44
44
  SDLC_MD="$PROJECT_DIR/SDLC.md"
45
45
  # Strict x.y.z semver — rejects whitespace, "junk", "1.alpha.0", etc.
46
46
  SEMVER_RE='^[0-9]+\.[0-9]+\.[0-9]+$'
47
+
48
+ # semver_lt A B → exit 0 if A < B, exit 1 otherwise. Both args validated semver.
49
+ # Used so a stale-but-fresh cache from before an upgrade ("latest" < installed)
50
+ # doesn't fire a reverse-direction nudge (#254 Bug 2 / #239).
51
+ semver_lt() {
52
+ local a_major a_minor a_patch b_major b_minor b_patch
53
+ a_major=$(echo "$1" | awk -F. '{print $1+0}')
54
+ a_minor=$(echo "$1" | awk -F. '{print $2+0}')
55
+ a_patch=$(echo "$1" | awk -F. '{print $3+0}')
56
+ b_major=$(echo "$2" | awk -F. '{print $1+0}')
57
+ b_minor=$(echo "$2" | awk -F. '{print $2+0}')
58
+ b_patch=$(echo "$2" | awk -F. '{print $3+0}')
59
+ if [ "$a_major" -lt "$b_major" ]; then return 0; fi
60
+ if [ "$a_major" -gt "$b_major" ]; then return 1; fi
61
+ if [ "$a_minor" -lt "$b_minor" ]; then return 0; fi
62
+ if [ "$a_minor" -gt "$b_minor" ]; then return 1; fi
63
+ if [ "$a_patch" -lt "$b_patch" ]; then return 0; fi
64
+ return 1
65
+ }
66
+
47
67
  if [ -f "$SDLC_MD" ]; then
48
68
  INSTALLED_VERSION=$(grep -o 'SDLC Wizard Version: [0-9.]*' "$SDLC_MD" | head -1 | sed 's/SDLC Wizard Version: //')
49
69
  if [ -n "$INSTALLED_VERSION" ] && [[ "$INSTALLED_VERSION" =~ $SEMVER_RE ]]; then
50
70
  VERSION_CACHE_DIR="${SDLC_WIZARD_CACHE_DIR:-$HOME/.cache/sdlc-wizard}"
51
71
  VERSION_CACHE_FILE="$VERSION_CACHE_DIR/latest-version"
52
72
  LATEST_VERSION=""
73
+ NPM_FAILED=0
53
74
 
54
- # Use cache if present, <24h old, and contents are valid semver
75
+ # Use cache if present, <24h old, contents are valid semver, AND cached
76
+ # version is not strictly older than installed (#239: post-upgrade
77
+ # cache poison sanity check — if cache says "latest=1.41.1" but
78
+ # installed=1.43.0, the cache is poisoned, force a refetch).
55
79
  if [ -f "$VERSION_CACHE_FILE" ]; then
56
80
  if stat -f %m "$VERSION_CACHE_FILE" > /dev/null 2>&1; then
57
81
  CACHE_MTIME=$(stat -f %m "$VERSION_CACHE_FILE")
@@ -62,22 +86,37 @@ if [ -f "$SDLC_MD" ]; then
62
86
  if [ "$CACHE_AGE" -lt 86400 ]; then
63
87
  CACHE_CONTENT=$(cat "$VERSION_CACHE_FILE" 2>/dev/null) || CACHE_CONTENT=""
64
88
  if [[ "$CACHE_CONTENT" =~ $SEMVER_RE ]]; then
65
- LATEST_VERSION="$CACHE_CONTENT"
89
+ # Sanity check: cached "latest" must be >= installed.
90
+ if ! semver_lt "$CACHE_CONTENT" "$INSTALLED_VERSION"; then
91
+ LATEST_VERSION="$CACHE_CONTENT"
92
+ fi
66
93
  fi
67
94
  fi
68
95
  fi
69
96
 
70
- # Fetch from npm if cache miss / stale / malformed
97
+ # Fetch from npm if cache miss / stale / malformed / poisoned
71
98
  if [ -z "$LATEST_VERSION" ] && command -v npm > /dev/null 2>&1; then
72
- NPM_RESULT=$(npm view agentic-sdlc-wizard version 2>/dev/null) || NPM_RESULT=""
73
- if [[ "$NPM_RESULT" =~ $SEMVER_RE ]]; then
99
+ NPM_RESULT=$(npm view agentic-sdlc-wizard version 2>/dev/null)
100
+ NPM_RC=$?
101
+ if [ "$NPM_RC" -ne 0 ] || ! [[ "$NPM_RESULT" =~ $SEMVER_RE ]]; then
102
+ NPM_FAILED=1
103
+ else
74
104
  LATEST_VERSION="$NPM_RESULT"
75
105
  mkdir -p "$VERSION_CACHE_DIR" 2>/dev/null || true
76
106
  printf '%s' "$LATEST_VERSION" > "$VERSION_CACHE_FILE" 2>/dev/null || true
77
107
  fi
78
108
  fi
79
109
 
80
- if [ -n "$LATEST_VERSION" ] && [ "$LATEST_VERSION" != "$INSTALLED_VERSION" ]; then
110
+ # #239: surface npm failure once when cache miss + npm fails. Without
111
+ # this, the version-check block produces no output and the user has
112
+ # no way to know the staleness nudge is broken.
113
+ if [ -z "$LATEST_VERSION" ] && [ "$NPM_FAILED" -eq 1 ]; then
114
+ echo "npm view failed — version check unavailable (run 'npm view agentic-sdlc-wizard version' to debug)"
115
+ fi
116
+
117
+ # #254 Bug 2: only nudge when installed < latest (semver direction).
118
+ # Equality `!=` previously fired reverse nudges post-upgrade.
119
+ if [ -n "$LATEST_VERSION" ] && semver_lt "$INSTALLED_VERSION" "$LATEST_VERSION"; then
81
120
  # Minor-version delta: 1.25.0 vs 1.34.0 → 9
82
121
  INSTALLED_MINOR=$(echo "$INSTALLED_VERSION" | awk -F. '{print $2+0}')
83
122
  LATEST_MINOR=$(echo "$LATEST_VERSION" | awk -F. '{print $2+0}')
@@ -131,8 +170,13 @@ fi
131
170
  # this hook and model-effort-check.sh both fire on SessionStart, so two checks
132
171
  # would double-print the nudge and risk drifting out of sync.
133
172
 
134
- # Dual-channel install check (#181) — nudge when CLI skills + Claude plugin both present
135
- if [ -d "$PROJECT_DIR/.claude/skills/update" ]; then
173
+ # Dual-channel install check (#181) — nudge when CLI skills + Claude plugin both present.
174
+ # #238: silenced once the user opts in via an ack sentinel. Sentinel is per-host
175
+ # (lives under $SDLC_WIZARD_CACHE_DIR/dual-channel-acknowledged) since the dual
176
+ # install is always a $HOME-scoped condition.
177
+ DUAL_CACHE_DIR="${SDLC_WIZARD_CACHE_DIR:-$HOME/.cache/sdlc-wizard}"
178
+ DUAL_ACK_FILE="$DUAL_CACHE_DIR/dual-channel-acknowledged"
179
+ if [ -d "$PROJECT_DIR/.claude/skills/update" ] && [ ! -f "$DUAL_ACK_FILE" ]; then
136
180
  for plugin_path in "$HOME/.claude/plugins-local/sdlc-wizard-wrap" "$HOME/.claude/plugins/cache/sdlc-wizard-local"; do
137
181
  if [ -d "$plugin_path" ]; then
138
182
  echo "WARNING: dual-install detected — CLI skills in .claude/skills/ AND Claude plugin at:"
@@ -140,6 +184,7 @@ if [ -d "$PROJECT_DIR/.claude/skills/update" ]; then
140
184
  echo " Duplicate /update-wizard commands come from running both channels. Pick one:"
141
185
  echo " - Keep plugin: remove .claude/skills/ from this project"
142
186
  echo " - Keep CLI: /plugin uninstall sdlc-wizard (or remove plugin dir)"
187
+ echo " - Keep both: mkdir -p \"$DUAL_CACHE_DIR\" && touch \"$DUAL_ACK_FILE\" (silences this nudge)"
143
188
  break
144
189
  fi
145
190
  done
@@ -0,0 +1,60 @@
1
+ #!/bin/bash
2
+ # SessionStart hook — token-spike anomaly detection (ROADMAP #220).
3
+ #
4
+ # Reads CC transcript history, computes per-session token burn, and warns
5
+ # if the last completed session's burn deviates >2σ above the rolling median.
6
+ # Catches silent CC-side regressions (caching bugs, prompt-inflation defaults)
7
+ # that only otherwise surface on the invoice. Reference: Anthropic 2026-04-23
8
+ # post-mortem on the dropped-thinking-blocks caching bug.
9
+ #
10
+ # Gated on `.metrics/` directory existing in the project root — opt-in for
11
+ # consumers, on-by-default for the wizard repo (which already maintains
12
+ # `.metrics/catches.jsonl` for the effectiveness scoreboard).
13
+ #
14
+ # Non-blocking: always exits 0.
15
+
16
+ # Token-bloat fix: when both project + plugin register this hook, plugin yields.
17
+ HOOK_DIR="${BASH_SOURCE[0]%/*}"
18
+ [ "$HOOK_DIR" = "${BASH_SOURCE[0]}" ] && HOOK_DIR="."
19
+ # shellcheck disable=SC1091
20
+ source "$HOOK_DIR/_find-sdlc-root.sh"
21
+ dedupe_plugin_or_project "${BASH_SOURCE[0]}" || { [ ! -t 0 ] && cat > /dev/null; exit 0; }
22
+
23
+ # Drain stdin (SessionStart sends JSON; we don't need any of it)
24
+ [ ! -t 0 ] && cat > /dev/null
25
+
26
+ ROOT="${CLAUDE_PROJECT_DIR:-$PWD}"
27
+
28
+ # Gate 1: opt-in via .metrics/ directory
29
+ [ -d "$ROOT/.metrics" ] || exit 0
30
+
31
+ # Gate 2: analytics script must exist. Resolve hook-relative first so the
32
+ # wizard repo's hook always finds its own analytics regardless of how
33
+ # CLAUDE_PROJECT_DIR is set (e.g., test fixtures pointing at a tmp dir).
34
+ # Fall back to project-relative for consumer forks that ship the script.
35
+ ANALYTICS=""
36
+ for candidate in \
37
+ "$HOOK_DIR/../tests/e2e/token-analytics.sh" \
38
+ "$ROOT/tests/e2e/token-analytics.sh"; do
39
+ if [ -x "$candidate" ]; then
40
+ ANALYTICS="$candidate"
41
+ break
42
+ fi
43
+ done
44
+ [ -n "$ANALYTICS" ] || exit 0
45
+
46
+ # Gate 3: jq is required by the analytics script
47
+ command -v jq > /dev/null 2>&1 || exit 0
48
+
49
+ ARGS=(--history "$ROOT/.metrics/token-history.jsonl" --ingest --check)
50
+
51
+ # Test override: SDLC_TOKEN_SPIKE_TRANSCRIPT_DIR points the ingest at a
52
+ # fixture directory instead of the real ~/.claude/projects/... path.
53
+ if [ -n "$SDLC_TOKEN_SPIKE_TRANSCRIPT_DIR" ]; then
54
+ ARGS+=(--transcript-dir "$SDLC_TOKEN_SPIKE_TRANSCRIPT_DIR" --no-skip-recent)
55
+ fi
56
+
57
+ OUTPUT=$("$ANALYTICS" "${ARGS[@]}" 2>&1) || true
58
+ [ -n "$OUTPUT" ] && echo "$OUTPUT"
59
+
60
+ exit 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-sdlc-wizard",
3
- "version": "1.42.2",
3
+ "version": "1.44.0",
4
4
  "description": "SDLC enforcement for Claude Code — hooks, skills, and wizard setup in one command",
5
5
  "bin": {
6
6
  "sdlc-wizard": "cli/bin/sdlc-wizard.js"
@@ -131,9 +131,11 @@ Parse all CHANGELOG entries between the user's installed version and the latest.
131
131
 
132
132
  ```
133
133
  Installed: 1.24.0
134
- Latest: 1.42.2
134
+ Latest: 1.44.0
135
135
 
136
136
  What changed:
137
+ - [1.44.0] Install-path & cache hygiene — closes #254, #239, #238 filed by consumer codeguesser after upgrading 1.32.0 → 1.42.1. (1) `cli/init.js` FILES list now ships `hooks/_find-sdlc-root.sh` — the helper sourced by all 5 hooks was missing from npm install path, so every session emitted `_find-sdlc-root.sh: No such file or directory` + `dedupe_plugin_or_project: command not found` and the SDLC walk-up logic was silently dead. (2) `init --force` now invalidates `~/.cache/sdlc-wizard/latest-version` so post-upgrade hooks re-fetch fresh values from npm instead of serving the pre-upgrade cache for 24h (which produced reverse "1.42.1 → 1.41.1" nudges). (3) instructions-loaded-check.sh now uses semver-direction comparison via new `semver_lt` function: nudge only fires when installed < latest, equality is silent, reverse direction is silent. Cache sanity-check rejects poisoned values (cached "latest" < installed → force refetch). (4) When `npm view` fails AND cache empty, hook now surfaces a one-line warning instead of going silent. (5) Dual-channel install nudge gains an opt-in silence sentinel — set via `mkdir -p $SDLC_WIZARD_CACHE_DIR && touch $SDLC_WIZARD_CACHE_DIR/dual-channel-acknowledged` (printed inside the nudge itself for discoverability). 8 new tests across test-cli.sh + test-hooks.sh, Codex CERTIFIED 10/10 round 2.
138
+ - [1.43.0] Token-spike anomaly detection — ROADMAP #220 closure. New `hooks/token-spike-check.sh` (SessionStart, opt-in via `.metrics/`) ingests CC transcript usage (`input_tokens` / `output_tokens` / `cache_creation_input_tokens` / `cache_read_input_tokens`) into `.metrics/token-history.jsonl`, then warns when the last session's `costly_tokens` (input + cache_creation + output, excluding the cheap cache_read tier) exceeds median + 2σ over a rolling baseline. Catches silent CC-side caching regressions (per Anthropic's 2026-04-23 post-mortem) before they surface on the invoice. Uses MAD-based spread for the median metric so a single baseline outlier doesn't mask the next spike. 14 quality tests in `tests/test-token-spike.sh` (incl. malicious-transcript privacy probe, flat-baseline floor, median-vs-mean contrast, concurrent-ingest mkdir lock).
137
139
  - [1.42.2] PreCompact self-heal documented — ROADMAP #209 closure. Added `pr_number` opt-in to all 3 handoff template schemas (skill Step 1; wizard Round 1 + cross-model section). Self-heal logic shipped earlier with #229 but was undocumented, leaving the dead-code path. New `test_handoff_template_documents_pr_number` enforces template/doc parity. Together with #229 (mtime auto-expire) closes the "stuck PENDING handoff blocks /compact forever" footgun from both directions.
138
140
  - [1.42.1] CI hygiene fix — skip Claude PR review on wizard self-PRs. 7 self-PRs (v1.39.0–v1.42.0) had shipped with red `review` job (API canary firing on dead credit balance). Treated as "expected" but red normalizes red. Workflow `if:` now skips review on `BaseInfinity/claude-sdlc-wizard` repo only; consumer projects unaffected. 7 quality tests, mutation-verified (== inversion fails).
139
141
  - [1.42.0] AGENTS.md interop detection — ROADMAP #205 phase (a). Setup wizard auto-scan now lists AGENTS.md (cross-tool agent-instructions standard, CC issue #6235); new Step 4.5 surfaces a 3-way decision (dual-maintain / merge / skip) when AGENTS.md is detected. Phase (b) write-fresh and phase (d) drift-test deferred. 7 quality tests.