diff --git a/CHANGELOG.md b/CHANGELOG.md index b4ed7849..e30efa8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,21 @@ ## [Unreleased] -### Behavior changes +## [4.0.0] - 2026-06-07 -- **`thinking-block-sanitize` v1 is now on by default (#162, #63147).** Was opt-in via `CACHE_FIX_THINKING_SANITIZE=on` in v3.8.0–v3.9.x. Seven days of prod dogfood (2026-05-29 → 2026-06-05) across 37 sessions: zero `cannot be modified` 400s, cache hit-rate aggregate 94.66% vs. 92.44% baseline (no prefix degradation), sanitize fired on ~35% of sessions with ~800 blocks dropped per day, max 938K context healthy. Set `CACHE_FIX_THINKING_SANITIZE=off` to explicitly disable. v2 (additional tools-hash-mismatch drop) stays opt-in via `=v2` pending its own prod-dogfood window after #196 closes the silent-load failure mode. -- **In-process extension hot-reload is now off by default (#196, #198).** Was on in v3.x. Set `CACHE_FIX_HOT_RELOAD=on` in the proxy's runtime environment (or in the install-service environment if using `cache-fix-proxy install-service`) to restore the prior behavior. Off-by-default eliminates the Node ESM stale-import race that silently broke `thinking-block-sanitize v2` for 17 hours after PR #192's merge — the watcher re-imports an extension whose transitive dependencies are already cached by Node's loader, and Node cannot evict cached transitive modules in-process. Cold starts are unaffected. -- **A supervisor-level proxy restart is now required after `npm install -g cache-fix-proxy@4`** to pick up extension changes. See [Upgrading from v3.x](README.md#upgrading-from-v3x) for per-platform restart commands. +A major release because two long-standing defaults change. Both flips are backed by empirical data; both have explicit opt-out paths. + +### Changed + +- **`thinking-block-sanitize` v1 is now on by default (#162, #63147, #201).** Was opt-in via `CACHE_FIX_THINKING_SANITIZE=on` in v3.8.0–v3.9.x. Seven days of prod dogfood (2026-05-29 → 2026-06-05) across 37 sessions: zero `cannot be modified` 400s, cache hit-rate aggregate 94.66% vs. 92.44% baseline (no prefix degradation), sanitize fired on ~35% of sessions with ~800 blocks dropped per day, max 938K context healthy. Set `CACHE_FIX_THINKING_SANITIZE=off` to explicitly disable. Credit to [@yurukusa](https://github.com/yurukusa) for the [13E cluster taxonomy](https://yurukusa.github.io/cc-safe-setup/cluster-tracker.html#cluster-extended-thinking-wedge) and the [22:32 UTC 2026-05-29 synthesis comment](https://github.com/anthropics/claude-code/issues/63147#issuecomment-4580358273) on #63147 that made the v2 predicate (cache-fix #171) tractable. +- **In-process extension hot-reload is now off by default (#196, #198, #200).** Was on in v3.x. Set `CACHE_FIX_HOT_RELOAD=on` in the proxy's runtime environment (or in the install-service environment if using `cache-fix-proxy install-service`) to restore the prior behavior. Off-by-default eliminates the Node ESM stale-import race that silently broke `thinking-block-sanitize v2` for 17 hours after PR #192's merge — the watcher re-imports an extension whose transitive dependencies are already cached by Node's loader, and Node cannot evict cached transitive modules in-process. Cold starts are unaffected. +- **A supervisor-level proxy restart is now required after `npm install -g claude-code-cache-fix@4`** to pick up extension changes. See [Upgrading from v3.x](README.md#upgrading-from-v3x) for per-platform restart commands. +- **Embedder note (Bun hosts, DAP-style integrations using `createProxyServer()` / `startProxy()`).** v4.0.0 flips `CACHE_FIX_THINKING_SANITIZE` from default-off to default-on. The v1 omitted-text drop will run on every request body passing through the embedded proxy. If your host depends on the prior no-sanitization behavior (e.g., your downstream code expects empty `thinking` blocks to survive the proxy round-trip), set `CACHE_FIX_THINKING_SANITIZE=off` in the host environment, or `process.env.CACHE_FIX_THINKING_SANITIZE = "off"` in your code at any point before request handling (the mode is read per-request via `modeFromEnv()`, not cached at module load). The flip is backed by the same 7-day dogfood data above. See [PR #201](https://github.com/cnighswonger/claude-code-cache-fix/pull/201) and [#63147](https://github.com/anthropics/claude-code/issues/63147). + +### Added + +- **`thinking-block-sanitize` v2 — tools-hash-mismatch drop (opt-in, #171, #192).** A new mode of the sanitize extension that detects cross-request tools-surface change via a per-session tools-hash baseline and strips ALL prior-turn signed thinking (both `thinking` blocks with non-empty text AND `redacted_thinking` blocks) when the hash flips. Targets yurukusa's [13E (ToolSearch) sub-pattern](https://yurukusa.github.io/cc-safe-setup/cluster-tracker.html#cluster-extended-thinking-wedge) of [anthropics/claude-code#63147](https://github.com/anthropics/claude-code/issues/63147), where dynamically-loaded tools mid-conversation invalidate the prior assistant turn's thinking signature and produce a per-turn 400 + retry tax. **Opt-in via `CACHE_FIX_THINKING_SANITIZE=v2`** (a strict superset of `=on` — v2 mode also runs v1's omitted-text drop). Stays opt-in pending its own prod-dogfood window, now that #196 has closed the silent-load failure mode that prevented v2 from running in prior testing. New `proxy/extensions/signature-surface-hash.mjs` helper computes the deterministic 16-char sha256 hash over the canonicalized tools surface. +- **`/health` extension-load observability (#196, #197).** When an extension fails to import — including the Node ESM stale-import race that originally surfaced in #196 — every failure is recorded on the pipeline module and surfaced via `/health` as `503 + {status:"degraded", failed_extensions:[...], hint:"restart the proxy via your supervisor to recover..."}`. Healthy proxies still return `200 + {status:"ok"}`. Catches load failures within seconds of the bad import instead of leaving the operator to grep the journal. New `getFailedExtensions()` export on `proxy/pipeline.mjs` for any other operator-facing tool that wants to surface the same state. ## [3.9.0] - 2026-06-03 diff --git a/README.md b/README.md index 74449019..38f1936e 100644 --- a/README.md +++ b/README.md @@ -218,6 +218,15 @@ Options (all optional; all fall back to the same env vars used by the CLI): - **`thinking-block-sanitize` v1 is now on by default.** Was opt-in via `CACHE_FIX_THINKING_SANITIZE=on` in v3.8.0–v3.9.x. After seven days of prod dogfood across 37 sessions (zero `cannot be modified` 400s, cache hit-rate aggregate 94.66% vs. 92.44% baseline, sanitize firing on ~35% of sessions with ~800 blocks dropped per day) the v1 mitigation is the new default. Set `CACHE_FIX_THINKING_SANITIZE=off` to explicitly disable. v2 (additional tools-hash-mismatch drop) stays opt-in via `=v2`. See [#63147](https://github.com/anthropics/claude-code/issues/63147) and [#162](https://github.com/cnighswonger/claude-code-cache-fix/issues/162). - **In-process extension hot-reload is now off by default.** Was on in v3.x. Set `CACHE_FIX_HOT_RELOAD=on` to restore the prior behavior. Off-by-default eliminates the Node ESM stale-import race documented in [#196](https://github.com/cnighswonger/claude-code-cache-fix/issues/196), where the watcher silently failed to load a newly-merged extension for 17 hours after a hot-reload trigger. The race fires when the file watcher re-imports an extension whose transitive dependencies are already cached by Node's loader; cold starts are unaffected. +### Embedder note (Bun hosts, DAP-style integrations using `createProxyServer()` / `startProxy()`) + +v4.0.0 flips `CACHE_FIX_THINKING_SANITIZE` from default-off to default-on. The v1 omitted-text drop will run on every request body passing through the embedded proxy. If your host depends on the prior no-sanitization behavior (e.g., your downstream code expects empty `thinking` blocks to survive the proxy round-trip), preserve it by either: + +- Setting `CACHE_FIX_THINKING_SANITIZE=off` in your host's environment, OR +- Setting `process.env.CACHE_FIX_THINKING_SANITIZE = "off"` in your code at any point before request handling — the mode is read per-request via `modeFromEnv()`, not cached at module load. + +The flip is backed by 7 days of prod dogfood (37 sessions, zero `cannot be modified` 400s, cache hit-rate aggregate 94.66% vs 92.44% baseline). See [PR #201](https://github.com/cnighswonger/claude-code-cache-fix/pull/201) for the validation data and [#63147](https://github.com/anthropics/claude-code/issues/63147) for upstream context. + Picking up a new extension or a code change to an existing one in v4.0.0 requires a supervisor-level proxy restart. There are two upgrade flows depending on whether you also want to opt back into hot-reload. ### Flow 1 — code-only npm upgrade (recommended default) @@ -227,7 +236,7 @@ Your existing systemd unit / launchd plist is unchanged; only the proxy code on **Linux (systemd user unit):** ``` -npm install -g cache-fix-proxy@4 +npm install -g claude-code-cache-fix@4 systemctl --user restart cache-fix-proxy ``` @@ -236,7 +245,7 @@ No `daemon-reload` required — the unit file content is unchanged. **macOS (launchd user agent):** ``` -npm install -g cache-fix-proxy@4 +npm install -g claude-code-cache-fix@4 launchctl kickstart gui/$(id -u)/com.cnighswonger.cache-fix-proxy ``` @@ -886,6 +895,7 @@ We monitor 30+ upstream Claude Code issues related to cache, quota, and context - **[@deafsquad](https://github.com/deafsquad)** — Universal smoosh_split un-smoosh fix (PR #26), source-level function attribution of resume scatter bug (anthropics/claude-code#43657), OTEL telemetry discovery, proposed and built proxy architecture for v3.0.0 - **[@vmfarms](https://github.com/vmfarms)** — Concurrent multi-runner production validation, surfaced proxy-mode resume-marker regex no-op (#96), TTL tier detection gap (#97), and image-strip stderr leak (#98) - **[@ojura](https://github.com/ojura)** — Opus 4.7 thinking-summaries root-cause analysis: filed [anthropics/claude-code#59844](https://github.com/anthropics/claude-code/issues/59844) with the CLI-binary decode (`!getIsNonInteractiveSession()` gate at offset 230510599 in v2.1.142) and the two-stacked-special-cases framing, which made the `thinking-display` extension (v3.6.1) a clean proxy-side complement to the proposed upstream fix +- **[@yurukusa](https://github.com/yurukusa)** — [Cluster taxonomy](https://yurukusa.github.io/cc-safe-setup/cluster-tracker.html#cluster-extended-thinking-wedge) for [anthropics/claude-code#63147](https://github.com/anthropics/claude-code/issues/63147) thinking-desync wedge; the 13E (ToolSearch) sub-pattern synthesis that made the `thinking-block-sanitize` v2 directive predicate tractable (cache-fix #171, shipped behind `=v2` opt-in in v4.0.0) - **[@schuay](https://github.com/schuay)** — `quota-statusline.sh` enhancements: 10-cell quota bar with elapsed-time tick and exhaust-vs-reset projection replacing the prior `%/min` burn-rate display (PR #140, v3.6.2), and d/h vs h/m time-format autoselect plus named time-unit and burn-warmup constants (PR #143, v3.7.0) If you contributed to the community effort on these issues and aren't listed here, please open an issue or PR — we want to credit everyone properly. diff --git a/docs/code-reviews/release-v4.0.0-codex-review-2026-06-07-round-2.md b/docs/code-reviews/release-v4.0.0-codex-review-2026-06-07-round-2.md new file mode 100644 index 00000000..90347138 --- /dev/null +++ b/docs/code-reviews/release-v4.0.0-codex-review-2026-06-07-round-2.md @@ -0,0 +1,37 @@ +# Review: release v4.0.0 + +Date: 2026-06-06 +Reviewed: PR #204 release payload (`release/v4.0.0-prep`) at `014c670` +Round: 2 +Label applied: `approved-by-codex-agent` + +## What Is Correct + +- The release payload still has the correct major-version bump: `package.json` is `4.0.0`, the npm package name is `claude-code-cache-fix`, and `cache-fix-proxy` remains only the installed bin name (`package.json:2-3`, `package.json:11-12`). +- The install-command blocker is closed in every release-facing location reviewed. The v4.0.0 changelog now uses `npm install -g claude-code-cache-fix@4`, and the README upgrade flow uses the same package name in both Linux and macOS examples (`CHANGELOG.md:13`, `README.md:239-249`). +- The changelog structure now matches the canonical release workflow: empty `## [Unreleased]` at the top, followed by `## [4.0.0] - 2026-06-07`, with conventional `### Changed` and `### Added` sections (`CHANGELOG.md:3-18`, `docs/release-workflow.md:51-57`). +- The v2 sanitize landing is now explicit and discoverable instead of buried as a parenthetical. It has its own `### Added` bullet, including the `CACHE_FIX_THINKING_SANITIZE=v2` activation path and the new `proxy/extensions/signature-surface-hash.mjs` helper (`CHANGELOG.md:16-19`). +- The round-2 fixup is scoped correctly: `git diff --name-only c0ae54f..014c670` shows only `CHANGELOG.md` and `README.md`, and the branch remains the expected release surface plus the prior round-1 review artifact. +- Local verification is clean: `npm test` passed with `1004` tests and `0` failures. + +## Blockers + +- None. + +## What Needs Attention + +- None. + +## Bloat / Non-Functional + +- None. + +## Recommendations + +- Proceed with tag, npm publish, and GitHub Release when the release owner is ready. + +## Bottom Line + +Approve. The three round-1 blockers are closed exactly as requested: the install package name is corrected everywhere reviewed, the changelog is back in canonical release-workflow shape, and the shipped `thinking-block-sanitize` v2 surface now has a dedicated `### Added` entry. I did not find any new regressions in the round-2 delta, and the local test suite still passes. + +— Codex review diff --git a/docs/code-reviews/release-v4.0.0-codex-review-2026-06-07.md b/docs/code-reviews/release-v4.0.0-codex-review-2026-06-07.md new file mode 100644 index 00000000..dd9ed871 --- /dev/null +++ b/docs/code-reviews/release-v4.0.0-codex-review-2026-06-07.md @@ -0,0 +1,41 @@ +# Review: release v4.0.0 + +Date: 2026-06-06 +Reviewed: PR #204 release payload (`release/v4.0.0-prep`) at `c0ae54f` +Round: 1 +Label applied: `changes-requested` + +## What Is Correct + +- The semver bump to `4.0.0` is correct. This release flips two defaults (`CACHE_FIX_THINKING_SANITIZE` to default-on and `CACHE_FIX_HOT_RELOAD` to default-off), which is a major-release trigger under the canonical workflow (`docs/release-workflow.md:13-15`, `CHANGELOG.md:9-12`, `package.json:2-3`). +- The release commit itself is clean and minimal: `git diff --name-only c0ae54f^ c0ae54f` contains exactly `CHANGELOG.md`, `README.md`, and `package.json`, with no debug code or secrets in the added lines. +- The embedder note is technically accurate. It does not invent a `startProxy()` option for sanitize control; instead it documents the real behavior that sanitize mode is read from `process.env` per request via `modeFromEnv()`, while `startProxy()` only exposes `watch` control for hot-reload (`README.md:221-228`, `proxy/extensions/thinking-block-sanitize.mjs:202-206`, `proxy/server.mjs:303-318`). +- The `@yurukusa` contributor credit is formatted consistently with the existing Contributors section and the changelog narrative is aligned with the underlying v2 implementation history (`README.md:898`, `CHANGELOG.md:9`). +- `git status --short` is clean, and the workflow's local-operations check also passes: the `usage-log` local mod is present in the working copy (`docs/release-workflow.md:46-47,91`, `proxy/extensions.json:74-76`). + +## Blockers + +- The new v4.0.0 release note tells users to run `npm install -g cache-fix-proxy@4`, but `cache-fix-proxy` is the installed bin name, not the npm package name. The package name is `claude-code-cache-fix` (`package.json:2,11-12`), the canonical release workflow uses `npm install -g claude-code-cache-fix@X.Y.Z` (`docs/release-workflow.md:138`), and `npm view cache-fix-proxy version` currently returns `E404`. This breaks the upgrade instruction in both the new changelog bullet and the README flow users are sent to next (`CHANGELOG.md:11`, `README.md:239-249`). +- `CHANGELOG.md` does not follow the repo's canonical release-cut format. The workflow requires a new empty top-level `## [Unreleased]` heading and conventional subsection names such as `### Added` / `### Changed` / `### Fixed` / `### Removed` / `### Security` (`docs/release-workflow.md:51-57`), but the current file starts directly at `## [4.0.0] - 2026-06-07` and groups the breaking items under `### Behavior changes` instead (`CHANGELOG.md:3-16`). This is a release-process regression, not just style. +- The changelog still undercounts shipped user-facing surface between `v3.9.0` and `c0ae54f`. `8a1b4bc` added the new `CACHE_FIX_THINKING_SANITIZE=v2` opt-in mode plus the new `proxy/extensions/signature-surface-hash.mjs` helper that makes it work (`proxy/extensions/thinking-block-sanitize.mjs:13-22,48-54,202-206`, `proxy/extensions/signature-surface-hash.mjs:1-9`). In the release notes, that landing is only a parenthetical inside the v1-default-on bullet (`CHANGELOG.md:9`), which is too easy to miss for users upgrading from `v3.9.0` who are seeing v2 for the first time. Per the workflow, the release note needs to accurately reflect every user-facing commit since the last tag (`docs/release-workflow.md:88-89`); v2 should get its own explicit bullet, likely under `### Added`. + +## What Needs Attention + +- `package-lock.json` is not a blocker for this PR. It is gitignored in this repo, and the on-disk lockfile already resolves to `4.0.0`, so there is no stale local publish input to correct before tag time. +- After the release-note fixes land, re-run the final gate and then separately record Chris's explicit go before step 7, since this is a major release (`docs/release-workflow.md:15,98-103`). + +## Bloat / Non-Functional + +- None in the release cut itself. The payload is intentionally small; the problems are release-note correctness and workflow compliance. + +## Recommendations + +- Replace every `npm install -g cache-fix-proxy@4` release-upgrade instruction with `npm install -g claude-code-cache-fix@4` (or `@4.0.0` where the full pin is preferred), keeping `cache-fix-proxy` only as the runtime command name. +- Restore the top-level empty `## [Unreleased]` heading and rename `### Behavior changes` to `### Changed` so the changelog matches the canonical release workflow. +- Add an explicit v4.0.0 bullet for the shipped `thinking-block-sanitize v2` opt-in mode and its `CACHE_FIX_THINKING_SANITIZE=v2` activation path, instead of leaving it buried as a parenthetical inside the v1-default-on entry. + +## Bottom Line + +Revise, then re-review. The major-version bump is correct, the release commit is clean, the embedder note is technically sound, and the local usage-log safeguard is still in place. But this is not ready to tag: the upgrade command currently points at a nonexistent npm package, the changelog structure regressed from the canonical release workflow, and the shipped v2 sanitize mode is still under-documented for downstream users upgrading from `v3.9.0`. + +— Codex review diff --git a/package.json b/package.json index cb8dba53..e90eb2b4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "claude-code-cache-fix", - "version": "3.9.0", + "version": "4.0.0", "description": "Cache optimization proxy and interceptor for Claude Code. Fixes prompt cache bugs, stabilizes prefix, reduces quota burn.", "type": "module", "exports": {