claudemd-cli 0.21.6 → 0.21.8

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.
Files changed (2) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -8,6 +8,59 @@ All notable changes to the `claudemd` plugin. This changelog tracks plugin artif
8
8
  - **Canonical spec version source**: `spec/CLAUDE.md` top-line title (`# AI-CODING-SPEC vX.Y.Z — Core`) + `spec/CLAUDE-changelog.md` top `##` entry.
9
9
  - **Plugin semver vs spec semver** are independent: plugin patch (0.2.0 → 0.2.1) may ship when spec is unchanged (this release); plugin minor (0.1.9 → 0.2.0) ships when spec minor updates (v0.2.0 shipped spec v6.10.0).
10
10
 
11
+ ## [0.21.8] - 2026-05-24
12
+
13
+ **Patch — fix: §8 SAFETY indirect-exec coverage default-ON (`BASH_SAFETY_INDIRECT_CALL` flip from opt-in to opt-out). Spec unchanged at v6.13.2.**
14
+
15
+ ### Why this patch
16
+
17
+ `hooks/pre-bash-safety-check.sh` has shipped indirect-exec unwrap (`bash -c '<inner>'`, `sh -c`, `zsh -c`, `eval`) behind `BASH_SAFETY_INDIRECT_CALL=1` opt-in since v0.6.0 to gather FP signal. The flag stayed default-OFF for 21 minor releases — meaning `bash -c "rm -rf $UNSAFE"` and `eval "rm -rf $X"` silently bypassed §8 SAFETY rm-detection in steady-state, inside the §5.1 Never-downgrade SAFETY family. v0.21.4 closed the direct-path silent bypass (sanitize / per-segment iteration); this patch closes the matching indirect path. Audit-driven (2026-05-24 full-project review surfaced it as the only §8 silent-bypass remaining after v0.21.4).
18
+
19
+ ### What changed
20
+
21
+ - `hooks/pre-bash-safety-check.sh`: `${BASH_SAFETY_INDIRECT_CALL:-0}` → `${BASH_SAFETY_INDIRECT_CALL:-1}` (line 200; matches `BASH_READONLY_FAST_PATH` default-ON style from v0.20.0). Header comment block updated; `BASH_SAFETY_INDIRECT_CALL=0` remains as the documented opt-out escape hatch for users who hit FP from the heuristic unwrap.
22
+ - `tests/fixtures/bash-safety/corpus.tsv`: three "default OFF allows" pass cases (rm-rf via `bash -c` / `eval` / `sh -c`) flipped to **deny** under default behavior; nine pre-existing `BASH_SAFETY_INDIRECT_CALL=1` deny cases simplified to test default (env-prefix removed since redundant); three new `BASH_SAFETY_INDIRECT_CALL=0` opt-out **pass** cases verify the escape hatch; four FP-guard pass cases (`$HOME/cache` whitelist, `prettier@3.0.0` pin, `[allow-rm-rf-var]` token, `echo "bash -c ..."` string-literal) simplified to test default.
23
+ - `tests/hooks/pre-bash-safety.test.sh`: `unset BASH_SAFETY_INDIRECT_CALL` at suite entry per `feedback_hook_env_test_hermeticity` — user can set the flag in `~/.claude/settings.json` env block and silently flip deny cases to pass under `npm test`, same v0.21.2 trap as `CLAUDEMD_PATH2_DRY_RUN`.
24
+
25
+ ### Heuristic limits (unchanged)
26
+
27
+ `unwrap_indirect` is regex-based — escaped quotes, nested heredocs, and substitution forms (`bash -c "$(cat <<X ... X)"`) can still defeat it. `[allow-rm-rf-var]` / `[allow-npx-unpinned]` bypass tokens survive the unwrap and remain the authorized escape for legitimate indirect calls.
28
+
29
+ Lesson sources: `feedback_demote_needs_data_not_intuition.md` (today's audit surfaced this as the highest-leverage §8 close after demote-review was data-rejected), `feedback_hook_env_test_hermeticity.md` (env teardown pattern).
30
+
31
+ ## [0.21.7] - 2026-05-24
32
+
33
+ **Patch — fix: audit `uniqueInvocations.duplicate_rows` was misleading without the "non-null tool_use_id" guard. Now split into `_real` / `_legacy`. Spec unchanged at v6.13.2.**
34
+
35
+ ### Why this patch
36
+
37
+ The v0.21.5 audit incorrectly flagged "PreToolUse double-fire bug" against `banned-vocab` (90 dupes) and `pre-bash-safety` (59 dupes). Phase-1 reproduction against `~/.claude/logs/claudemd.jsonl` found:
38
+
39
+ - banned-vocab: 6 real-session rows with non-null `tool_use_id`, **0 collisions**
40
+ - pre-bash-safety: 48 real-session rows with non-null `tool_use_id`, **0 collisions**
41
+ - All-hook global check: 0 true double-fires
42
+
43
+ The 90 / 59 reported "dupes" were all from pre-v0.9.34 legacy rows (null `tool_use_id`) where two distinct hook invocations within the same wall-clock second collided on the `(ts, hook, session, tool_use_id)` quadruple — an artifact of seconds-precision `date -u +%Y-%m-%dT%H:%M:%SZ` timestamps in `rule_hits_append`, NOT a registration / lib bug.
44
+
45
+ The audit docs (`rule-hits-parse.js:114-117` and `commands/claudemd-audit.md` field table) DID name the "non-null tool_use_id" guard, but the headline metric (`duplicate_rows`) was a bare sum across both legacy noise and real signal. Easy to misread under reading-fatigue.
46
+
47
+ ### What changed
48
+
49
+ - `scripts/lib/rule-hits-parse.js#uniqueInvocations`: per-hook output gains `duplicate_rows_real` (collision row has non-null `tool_use_id` → true double-fire bug signal) and `duplicate_rows_legacy` (collision row has null `tool_use_id` → expected noise from pre-v0.9.34 legacy rows OR Stop/SessionStart-class hooks). `duplicate_rows = _real + _legacy` retained for backward compat. Docstring rewritten with explicit reading guide.
50
+ - `commands/claudemd-audit.md`: field-table entry for `uniqueInvocations` rewritten with the split; the operator-format paragraph changed from "Surface `duplicate_rows > 0`..." to "Surface `duplicate_rows_real > 0`... **Do NOT report bare `duplicate_rows`**". The bare-sum is now explicitly called out as a misread trap.
51
+ - `tests/scripts/audit.test.js`: existing v0.9.34 test extended with `duplicate_rows_real=1, duplicate_rows_legacy=0` assertion. New v0.21.7 test covers three collision shapes (real bug / Stop-class noise / pre-v0.9.34 legacy noise) and asserts each routes to the correct counter.
52
+ - `feedback_audit_no_reverify.md`: trap-rule bullet added — "Read field-guards in full, not just headline metrics". Cites today's misread as the origin.
53
+
54
+ ### Backward compatibility
55
+
56
+ - `duplicate_rows` field unchanged (sum). Consumers reading the bare sum keep working but will under-report or over-report depending on interpretation; new readers should switch to `_real` / `_legacy`.
57
+ - No change to any hook code; no spec change; no migration. Pure reporting-layer fix.
58
+
59
+ ### Verification
60
+
61
+ - 448 unit + 2 integration tests pass (was 447 unit; +1 new test).
62
+ - Functional re-check against current `~/.claude/logs/claudemd.jsonl` (30d window): all hooks `duplicate_rows_real = 0`. The original audit's "bug" disappears once the gate is applied correctly.
63
+
11
64
  ## [0.21.6] - 2026-05-24
12
65
 
13
66
  **Patch — feat: `runSpecSizingCheck` now emits copy-paste-ready OLD/NEW edits on drift. Spec unchanged at v6.13.2.**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudemd-cli",
3
- "version": "0.21.6",
3
+ "version": "0.21.8",
4
4
  "description": "Standalone CLI for §10-V banned-vocab + transcript scanning. Companion to the claudemd Claude Code plugin (github.com/sdsrss/claudemd) for use in git pre-commit hooks, GitHub Actions, and other agents.",
5
5
  "type": "module",
6
6
  "bin": {