claudemd-cli 0.64.0 → 0.65.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.
Files changed (2) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -8,6 +8,35 @@ 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.65.0] - 2026-07-28
12
+
13
+ The fail-open instrumentation could not report the failure it was built for. Fixes from the 2026-07-28 four-dimension audit (`docs/comprehensive-audit-2026-07-28-v0.64.1.md`), whose starting taxonomy was deliberately moved outside the detection-coverage family the previous five rounds sampled — lifecycle, entropy, dependency degradation, adoption.
14
+
15
+ **Upgrade note.** No command shape changes verdict; the `§8` corpus passes 694/694 unchanged. Two behaviors do change, both advisory: `residue-audit` now counts loose *files* in `~/.claude/tmp` (not just directories) and silently re-baselines once on upgrade, and `/claudemd-clean-residue` now reaps three classes of orphan from `~/.claude/.claudemd-state/`.
16
+
17
+ - **fix: `jq-missing` and `jq-broken` fail-open rows were never written.** `rule_hits_append` built every row with `jq -cn`, so the two reasons that *mean jq is unusable* could not be recorded — the hook set its 60s rate-limit marker (`failopen-*.ts`) and returned, which looks exactly like a row that was written. `hook_record_failopen` hand-builds its `extra` payload with a comment saying "jq may be the very thing missing", and the callee then reintroduced the dependency it had just avoided. Measured: with jq off `PATH`, `banned-vocab` wrote a marker and **zero** rows. `rule-hits.sh` now has a jq-free fallback row builder; T12 parses that row back with a real jq and asserts all nine schema fields survive.
18
+ - The row is also built into a variable before being appended. The previous form redirected `jq` straight at the log, so a jq failing mid-write could leave a torn line in a file whose entire contract is one valid JSON object per line.
19
+ - **fix: a jq that is present but broken silently disabled the safety gates.** `hook_require_jq` tests presence (`command -v`), which a stub earlier on `PATH`, a corrupt binary, a missing shared library or a resource-limit kill all pass. Every parse then used `jq -r … 2>/dev/null`, so the field came back empty and the hook took its ordinary "not my tool/event" early exit — indistinguishable in telemetry from "rule not applicable". New `hook_jq_field` routes the first parse and attributes the failure (`bad-event` vs `jq-broken`); the disambiguating `jq -n .` runs **only** on the failure path, so the success path adds no process. Measured cost on the PreToolUse hot path: 18ms → 19ms per invocation.
20
+ - **fix: five of ten `hook_require_jq` consumers had no fail-open instrumentation at all.** `hook_record_failopen` was wired into the hooks its tests named — three safety hooks plus `banned-vocab` — and the rest were left silent. `session-extended-read` enforces the HARD rule `§13.1-extended-read` and recorded nothing; `transcript-vocab-scan` carries `§10-V` Path 2 and recorded nothing. Now instrumented, with `memory-prompt-hint` and `transcript-structure-scan`.
21
+ - `session-summary` stays a deliberate non-emitter. It enforces no spec rule, and two shipped documents declare it the one hook that never writes to the log; contradicting both to satisfy a gate would be backwards. The exemption is recorded with its reason in the gate itself.
22
+ - **feat: `tests/scripts/jq-guard-consumers.test.js` derives the consumer set from source.** The recurring root behind this release — and behind the 0.62.x hotfixes — is a shared helper whose consumer set nothing enumerates. The gate greps `hook_require_jq` callers, asserts a floor so it can never validate an empty set, and requires each caller to be instrumented or explicitly exempt with a stated reason. Verified against unfixed source: 3/3 fail.
23
+ - **feat: the plugin's own state dir is reachable by its own tooling.** `~/.claude/.claudemd-state/` sat outside every cleaner and every health check — `clean-residue.js` had zero references to it, `doctor.js` had zero, and `residue-audit.sh` only ever watched `~/.claude/tmp`. Measured on the maintainer's machine: 39 orphans, oldest 79 days — 27 `ext-read-*` (reaped only for the hook's own session, so any crash leaks one), 11 `mem-coverage-*` written by a hook deleted in v0.23.12 with no migration, and residue from a test-hermeticity bug that was itself fixed but whose leftovers nothing could reach.
24
+ - Deletion is **allowlist-by-pattern**, not "everything old". The same directory holds live singleton state whose age says nothing about whether it is in use; a test pins all eleven such names surviving a reap at 300 days. A pattern the list does not name is never touched, so a future state file is safe by default rather than by memory.
25
+ - **change: `residue-audit` counts every depth-1 entry, files included.** `-type d` made a leaked scratch *file* invisible, and spec §7's evidence table specifies a path-scoped residue count, not a directory count. The baseline file gains a `v2:` format tag: a v1 baseline counted directories only, so comparing across the change would emit a one-time advisory whose delta is just the file count — a false alarm indistinguishable from real growth. v1 re-baselines silently instead.
26
+ - **feat: `doctor` reports `state-dir-orphans`** so the count is seen rather than merely cleanable. Reporting only; deletion stays behind the AUTH'd `/claudemd-clean-residue` path.
27
+ - **fix: `docs/ARCHITECTURE.md` "State locations" documented 6 of the 14 kinds on disk**, and is now gated by `architecture-drift.test.js`, which extracts state paths from `hooks/**/*.sh` and `scripts/**/*.js`. That file exists *because* a doc section claimed source-of-truth with nothing behind it — and the sibling section in the same file had exactly that problem. Verified against the unfixed doc: fails, naming the missing entries.
28
+ - **Measured and closed with a negative result**: the "jq 1.6 dialect" risk carried since the 2026-07-27 deferred list. Extracting the jq programs from the hooks and grepping for jq-1.6+-only builtins (`@base64d`, `splits`, `ltrimstr`, `abs`, `pick`, `toarray`, `getpath`, `$__loc__`, `halt_error`) finds none — everything in use (`IN`, `sub`, `gsub`, `select`, `fromjson`, `test`, `to_entries`, `reduce`, `any`, `startswith`, `join`, `map`, `try`, …) has been present since jq 1.5. No CI matrix leg was added, because there is nothing for it to catch. (A file-wide grep suggesting `abs`/`trim`/`scan` was substring noise from filenames and prose.)
29
+ - `residue-audit.test.sh` swaps its hand-maintained `8` total for a runtime counter — the literal drifted the moment a case was added, the same class recorded in `tasks/audit-2026-07-27-deferred.md §0`.
30
+
31
+ ## [0.64.1] - 2026-07-28
32
+
33
+ Hotfix for a red CI on the v0.64.0 tag. No hook, script or spec behavior changes — the failure was the test harness killing a suite, not a test failing.
34
+
35
+ - **fix: the shell hook suites get the same 300s wall-clock cap the integration suites have.** `pre-bash-safety.test.sh` drives one hook process per corpus row, and v0.64.0 took that corpus 500 → 598 rows: 65s on Linux, and macOS runners are roughly 4× slower at process creation, so the leg blew the 120s guard. The tag went red with **every assertion passing** — `# fail 0`, every suite printing `N/N`, shellcheck clean, both static gates clean — and one line explaining it: `TIMEOUT: pre-bash-safety.test.sh exceeded 120s (killed)`.
36
+ - Same reasoning the node cap in the same file already documents: a real hang is *infinite*, so 300s catches it exactly as well as 120s, and the only cost is minutes spent reporting a hang someone is already debugging.
37
+ - **The root cause is not fixed**: ~600 process spawns per run, and every corpus round moves closer to the new cap. Both candidate fixes (a batch stdin entry point, or making the hook sourceable) mean changing the hook under test for the test's convenience, so it is filed with its reopen condition rather than rushed — `tasks/audit-2026-07-27-deferred.md §F`.
38
+ - Worth stating plainly: npm published 0.64.0 before ci went red, the same channel asymmetry recorded as H3. The published artifact is not broken — no test failed — so this is a forward fix, not a deprecate.
39
+
11
40
  ## [0.64.0] - 2026-07-28
12
41
 
13
42
  The fetch-execute gate stops being about `curl`. It was two word lists spelled inline — SOURCE `(curl|wget)`, SINK `(sh|bash|zsh|dash|ksh|ash)` — and a full measurement of the class found five families it could not see. They were not five defects; they were two tables that were too narrow, plus three delivery shapes that cannot be written as SOURCE-pipe-SINK at all.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudemd-cli",
3
- "version": "0.64.0",
3
+ "version": "0.65.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": {