claudemd-cli 0.14.0 → 0.16.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.
package/CHANGELOG.md CHANGED
@@ -8,6 +8,85 @@ 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.16.0] - 2026-05-11
12
+
13
+ **Minor — fix: `sandbox-disposal-check` no longer false-positive-flags `version-sync.sh` sentinel files; cuts 95% of 30d warn volume.**
14
+
15
+ ### Background
16
+
17
+ 30-day rule-hits telemetry: `sandbox-disposal` accounted for **47% of all hook log lines** (853/1819), with **70% concentration in the last 3 days** (596/853 since 2026-05-08). Diagnostic sampling across 3 sessions (claudemd ship session 7930b8b6, code-graph-mcp Rust session 892392d9) showed 100% FP rate on the surveyed fires — the hook warned during pure read-only investigation, AskUserQuestion-only turns, and `git tag` / `gh release create` ship sequences with no `mkdtemp` activity.
18
+
19
+ ### Root cause
20
+
21
+ Two of this plugin's own hooks stepping on each other:
22
+
23
+ - `hooks/version-sync.sh:27` creates per-session sentinel **files** via `touch "$TMPDIR/claudemd-sync-<sid>"` (intentionally persisted 24h for session-scoped early-exit).
24
+ - `hooks/sandbox-disposal-check.sh:54` scanned the same `~/.claude/tmp/` directory via `platform_find_newer`, filtering by name prefix (`^claudemd-` / `^tmp\.`) but **not by inode type**. Result: `version-sync`'s session-scoping touch-files were detected as "fresh mkdtemp directories from this session."
25
+
26
+ Live FS state at fix time: `~/.claude/tmp` held **704 matching entries — 668 files (95%), 36 directories (5%)**. Spec §8.V4 explicitly scopes the rule to "`mkdtempSync` / scratch fixtures / HACK `tmp/`+`scripts/` output" — i.e. directories. Hook header (`sandbox-disposal-check.sh:3`) already documented intent: "Warns if `tmp.XXXXXX`-style mkdtemp **directories** were created this session." Implementation drift, not spec misalignment.
27
+
28
+ ### What changed
29
+
30
+ - **`hooks/sandbox-disposal-check.sh`**: 1 LOC fix — caller-side `[[ -d "$path" ]] || continue` filter (kept the `-type d` semantic local to this hook rather than baking it into `platform_find_newer`, which `tests/hooks/platform.test.sh` exercises with file fixtures).
31
+ - **`tests/hooks/sandbox-disposal.test.sh`**: new Case 9 — `touch claudemd-sync-fake-session-id` + `touch tmp.fake-mktemp-file` must NOT trigger a warn. Regression guard for cross-hook collision.
32
+
33
+ ### Why not also touch `~/.claude/tmp` residue
34
+
35
+ The 668 stale sentinels are <24h old (high-volume CC session user — 668 sessions / 24h). `version-sync.sh:34`'s `-mmin +1440 -delete` GC will sweep them on next first-prompt-per-session. `/claudemd-clean-residue` already handles manual cleanup if wanted. Fix is the durable solution; cleanup is mechanical recovery from past noise.
36
+
37
+ ### Why not `(b) + (c)` (per-session first-fire sentinel / cwd-`/tmp` skip)
38
+
39
+ Original 30d telemetry triage proposed 5 cuts (demote / sentinel / cwd-skip / per-session-create tracking / test-trap). Root-cause investigation collapsed them to 1: 95% of the noise pool was version-sync sentinels misclassified. The remaining 5% is real `mkdtemp` residue the hook *should* catch. Adding per-session sentinel or cwd-skip filters on top would now bias toward FN (silencing legitimate residue) without proportional FP reduction.
40
+
41
+ ### Tests
42
+
43
+ - New `tests/hooks/sandbox-disposal.test.sh` Case 9 (file-shaped match not flagged). 9/9 passed.
44
+ - Full hook suite: 23 files, all green. JS suite: 405 unchanged. Integration: full-lifecycle + upgrade-lifecycle PASS.
45
+ - `platform.test.sh` unchanged — keeping the type-filter at caller rather than helper preserves the generic file-or-dir contract that platform-lib tests depend on.
46
+
47
+ ### Operator notes
48
+
49
+ Existing `~/.claude/tmp/claudemd-sync-*` residue (any amount) will be silently ignored by sandbox-disposal from this version forward; `version-sync.sh`'s own 24h GC continues to age them out. Warn-noise reduction is immediate on next CC session reload.
50
+
51
+ ## [0.15.0] - 2026-05-11
52
+
53
+ **Minor — feat: `mid-spine-yield-scan` Stop hook closes the §11-mid-spine-yield observation gap.**
54
+
55
+ P2 #1 (a-mini) from the P2/P3 phase plan. Highest-confidence detector in the 5-rule transcript-scan extension queue, shipped first so its FP profile can be measured before layering medium-confidence siblings (iron-law-1 / parallel-path / session-exit / author-not-reviewer).
56
+
57
+ ### Background
58
+
59
+ §11 Mid-SPINE turn-yield (HARD): once a turn has executed ≥1 tool call inside an active SPINE cycle, the agent must continue planned steps through VALIDATE. Yield only on `[AUTH REQUIRED]`, real ambiguity, or context pressure. Spec gives the tell: "next user message is `继续 / next / 怎么停了 / why did you stop` → confirmed prior yield." Pre-fix, the rule was self-discipline only — no hook observed it, no `rule_hits.jsonl` row carried `§11-mid-spine-yield`, and `/claudemd-rules` staleReviews permanently listed it as un-reviewed. The 4 sibling self-rules (§iron-law-2 / §10-four-section-order / §10-honesty / §10-V) already had hook-side observers (`transcript-structure-scan` / `transcript-vocab-scan`); mid-spine-yield was the only §11 rule still observability-dark.
60
+
61
+ ### What changed
62
+
63
+ - **New hook** `hooks/mid-spine-yield-scan.sh` (~125 LOC). Stop event; opt-in `MID_SPINE_YIELD_SCAN=1` (default OFF per behavior-layer hook convention — same as `transcript-vocab-scan` / `transcript-structure-scan` / `memory-coverage-scan`).
64
+ - **Detection**: walks the session transcript in order; tracks each assistant turn's text + tool_use count. For each user message matching the continuation tell (`继续 / next / continue / proceed / 怎么停了 / 为什么停了 / 还有吗 / why (did) you stop / why stop / keep going / again`, body ≤ 30 chars after whitespace), evaluates the immediately-prior assistant turn. Suspect mid-SPINE yield when the prior turn (a) contained ≥1 tool_use AND (b) text body lacked four-section report anchor (`^Done:` / `^## Done` / `^Done —`) AND (c) text body lacked `[AUTH REQUIRED` (legitimate yield) AND (d) text body lacked `[PARTIAL:` (legitimate partial-completion signal).
65
+ - **Aggregation**: per-session count emitted as one advisory row with `extra.count`. Per-session dedup via state sentinel `~/.claude/.claudemd-state/mid-spine-yield-<sid>.ts` — at most one row per `session_id`.
66
+ - **Schema additive**: new event `mid-spine-advisory`; new section-mapping row `§11-mid-spine-yield`. `tests/hooks/contract.test.sh` DOCUMENTED array extended.
67
+ - **Registry sync**: 16 → 17 hooks. `scripts/lib/hook-registry.js` adds entry; `tests/scripts/{install,hook-registry}.test.js` count pins moved (real-plugin count: 16 → 17; fixture array stays at 12 — fixture is intentionally minimal); `tests/integration/full-lifecycle.test.sh` MCOUNT + regex group moved; `README.md` shell-hook count + name list updated; `commands/claudemd-toggle.md` valid-name list extended; `README.md` kill-switch enumeration extended with `DISABLE_MID_SPINE_YIELD_HOOK`.
68
+ - **Kill-switch**: `DISABLE_MID_SPINE_YIELD_HOOK=1` (and the global `DISABLE_CLAUDEMD_HOOKS=1`).
69
+
70
+ ### Why minor (not patch)
71
+
72
+ New hook = new contract surface (rule-hits emits `mid-spine-advisory` rows, schema documents `§11-mid-spine-yield`). Per `feedback_claudemd_spec_single_source_of_truth.md`: plugin semver vs spec semver are independent — plugin minor for additive observability instrumentation; spec stays at v6.11.15 (rule itself unchanged, only the observation surface added). §13.2 budget cost: 0 (no rule add/remove/downgrade).
73
+
74
+ ### Tests
75
+
76
+ - New `tests/hooks/mid-spine-yield-scan.test.sh`: 12 cases — default OFF / missing-transcript fail-open / four-section-prev silence / tool-call-prev TP / `[AUTH REQUIRED]` legit-yield / `[PARTIAL:]` legit-partial / long-form-message FP filter / kill-switch / per-session dedup / multi-yield count aggregation / tool+report combined silence / EN `next` continuation parity.
77
+ - Updated `tests/hooks/contract.test.sh`: `mid-spine-advisory:mid-spine-yield-scan` in DOCUMENTED array; B/C invariants auto-verify via grep.
78
+ - Updated `tests/scripts/install.test.js` + `tests/scripts/hook-registry.test.js`: count pin 16 → 17.
79
+ - Updated `tests/integration/full-lifecycle.test.sh`: MCOUNT + settings-eviction regex group.
80
+ - Full JS suite: 405 unchanged (count pins only). Hook suite 12/12 new + 8/8 existing transcript-vocab unchanged. Integration: PASS.
81
+
82
+ ### Operator notes
83
+
84
+ Hook ships default-OFF for ≥30 days FP signal collection before flipping default-ON, mirroring the `transcript-*-scan` + `memory-coverage-scan` precedent. Enable per project via:
85
+ ```
86
+ export MID_SPINE_YIELD_SCAN=1
87
+ ```
88
+ Once FP rate is measurable, the next 4 sibling detectors (iron-law-1 / parallel-path / session-exit / author-not-reviewer) ship as v0.16.0 / v0.17.0 per the P2 #1 (a/b) split — calibrated against this hook's signal-to-noise baseline.
89
+
11
90
  ## [0.14.0] - 2026-05-11
12
91
 
13
92
  **Minor — feat: `/claudemd-sampling-audit` retrospective batch scanner for 4 self-enforced HARD rules.**
package/README.md CHANGED
@@ -58,8 +58,8 @@ Verify in one command (Linux): `node --version && jq --version && gh --version &
58
58
 
59
59
  | Layer | Contents |
60
60
  |---|---|
61
- | 16 shell hooks | `banned-vocab-check` · `pre-bash-safety-check` · `ship-baseline-check` · `residue-audit` · `memory-read-check` · `memory-prompt-hint` · `memory-coverage-scan` · `sandbox-disposal-check` · `session-start-check` · `session-extended-read` · `session-summary` · `session-end-check` · `transcript-vocab-scan` · `transcript-structure-scan` · `version-sync` · `mem-audit` |
62
- | 10 slash commands | `/claudemd-status` · `/claudemd-update` · `/claudemd-audit` · `/claudemd-toggle` · `/claudemd-doctor` · `/claudemd-analyze` · `/claudemd-uninstall` · `/claudemd-rules` · `/claudemd-clean-residue` · `/claudemd-sparkline` |
61
+ | 17 shell hooks | `banned-vocab-check` · `pre-bash-safety-check` · `ship-baseline-check` · `residue-audit` · `memory-read-check` · `memory-prompt-hint` · `memory-coverage-scan` · `mid-spine-yield-scan` · `sandbox-disposal-check` · `session-start-check` · `session-extended-read` · `session-summary` · `session-end-check` · `transcript-vocab-scan` · `transcript-structure-scan` · `version-sync` · `mem-audit` |
62
+ | 11 slash commands | `/claudemd-status` · `/claudemd-update` · `/claudemd-audit` · `/claudemd-toggle` · `/claudemd-doctor` · `/claudemd-analyze` · `/claudemd-uninstall` · `/claudemd-rules` · `/claudemd-clean-residue` · `/claudemd-sparkline` · `/claudemd-sampling-audit` |
63
63
  | 1 standalone CLI | `claudemd-cli lint` · `claudemd-cli audit` ([npm: `claudemd-cli`](https://www.npmjs.com/package/claudemd-cli)) |
64
64
  | Spec v6.11.15 | `~/.claude/CLAUDE.md` · `CLAUDE-extended.md` · `CLAUDE-changelog.md` (backup-before-overwrite) |
65
65
 
@@ -180,6 +180,7 @@ export DISABLE_MEM_AUDIT_HOOK=1 # v0.9.4+ — Stop Why:-less ci
180
180
  export DISABLE_SESSION_END_CHECK_HOOK=1 # v0.9.27+ — SessionEnd §11-session-exit mid-SPINE check
181
181
  export DISABLE_SESSION_EXTENDED_READ_HOOK=1 # v0.10.1+ — PreToolUse:Read §13.1-extended-read denominator signal
182
182
  export DISABLE_MEMORY_COVERAGE_HOOK=1 # v0.13.0+ — Stop §11-mem-coverage advisory (opt-in via MEMORY_COVERAGE_SCAN=1)
183
+ export DISABLE_MID_SPINE_YIELD_HOOK=1 # v0.15.0+ — Stop §11-mid-spine-yield advisory (opt-in via MID_SPINE_YIELD_SCAN=1)
183
184
  ```
184
185
 
185
186
  **2a. Per-sub-feature** (v0.4.0+). Sub-flags inside an enabled hook, named without the `_HOOK` suffix:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudemd-cli",
3
- "version": "0.14.0",
3
+ "version": "0.16.0",
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": {