Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
```

Expand All @@ -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
```

Expand Down Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
@@ -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
41 changes: 41 additions & 0 deletions docs/code-reviews/release-v4.0.0-codex-review-2026-06-07.md
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand Down
Loading