Skip to content

remove(messages-cache-breakpoint): superseded by canonical content identity (#272 has landed — your call now) - #281

Open
Gunther-Schulz wants to merge 3 commits into
cnighswonger:mainfrom
Gunther-Schulz:pr/retire-messages-cache-breakpoint
Open

remove(messages-cache-breakpoint): superseded by canonical content identity (#272 has landed — your call now)#281
Gunther-Schulz wants to merge 3 commits into
cnighswonger:mainfrom
Gunther-Schulz:pr/retire-messages-cache-breakpoint

Conversation

@Gunther-Schulz

Copy link
Copy Markdown
Contributor

DRAFT — blocked on #272, deliberately opened now so the plan is visible during that review (this branch stacks on #272; the replacement is present in it). Will mark ready when #272 merges. Whether to take this at all is genuinely your call — see the trade-off below.

Why remove it

  • Its #47098 rationale (protect the head against mutation) is covered by feat(insertion-normalization): pin volatile reminder blocks so mid-history rewrites stop busting the cache #272's canonical content identity, which recognises a message across re-serializations instead of pinning index 0.
  • It spends the 4th and last API breakpoint slot (Claude Code occupies three). Holding it isn't free even while the extension behaves: any future consumer of that slot silently starves. On our fork that starvation masked a defective mid-history rung scheme for a day — freeing the slot armed it, measured at 57 self-inflicted stability violations before the scheme itself was retired. The ordering rule this removal carries: retire anything breakpoint-dependent before freeing the slot.
  • Measured after removal on two full-session corpora through the real pipeline: 0 stability / 0 safety / 0 sequence violations, 4th slot deliberately empty.

The honest trade-off

messages-cache-breakpoint ships enabled; #272's replacement is gated off by default. Merging this as-is changes default behavior for users who don't enable the new extensions. Options, in our order of preference: (a) flip #272's gates to default-on at the same time, (b) take this removal only with a release note, (c) keep the extension but default it off. We run (a)-equivalent in production; the corpora above measured exactly that configuration.

🤖 Generated with Claude Code

@Gunther-Schulz Gunther-Schulz changed the title remove(messages-cache-breakpoint): superseded by canonical content identity remove(messages-cache-breakpoint): superseded by canonical content identity — DRAFT, stacks on #272, review alongside it Jul 29, 2026
@Gunther-Schulz
Gunther-Schulz force-pushed the pr/retire-messages-cache-breakpoint branch from 52ea721 to 7eb27a7 Compare July 30, 2026 04:27
@Gunther-Schulz

Copy link
Copy Markdown
Contributor Author

Rebased onto #272's new tip (c713d0e, the duplicate-suppression addition) — no changes to this PR's own commit; still stacks on #272, review alongside it.

🤖 Generated with Claude Code

Gunther-Schulz added a commit to Gunther-Schulz/claude-code-cache-fix that referenced this pull request Jul 30, 2026
…ls-less tree skips, not dies

The static harvest.mjs import defeated the file's own slice-portability
idiom: in trees carrying only the extension (upstream cnighswonger#272/cnighswonger#278/cnighswonger#281)
the whole file failed at module load, so the real-pair check never
reached the skip it was designed to hit. The reader now loads like
replayTools — dynamically, inside the test — and the fixture branch
requires it, so tools-less trees skip with COULD NOT VERIFY.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TcivCe2iLnKZxpB4qTXzEb
@Gunther-Schulz
Gunther-Schulz force-pushed the pr/retire-messages-cache-breakpoint branch from 7eb27a7 to b07c0dc Compare July 30, 2026 12:37
@Gunther-Schulz

Copy link
Copy Markdown
Contributor Author

Rebased onto the current #272 tip (force-with-lease; the retire commit rides on top unchanged as b07c0dc). Tests at the new base: 67 pass / 1 designed skip / 0 fail. Merge order dependency unchanged: this lands after #272.

🤖 Generated with Claude Code

https://claude.ai/code/session_01TcivCe2iLnKZxpB4qTXzEb

Gunther-Schulz added a commit to Gunther-Schulz/claude-code-cache-fix that referenced this pull request Aug 1, 2026
…t and verified; cnighswonger#276 scope discovery

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016y33RMV399iYMXFEbAfQCk
@Gunther-Schulz
Gunther-Schulz force-pushed the pr/retire-messages-cache-breakpoint branch from b07c0dc to fb63f61 Compare August 1, 2026 13:51
Gunther-Schulz added a commit to Gunther-Schulz/claude-code-cache-fix that referenced this pull request Aug 5, 2026
…till ours

The section recorded what we did and was headed "all READY" while four
of those PRs had merged. Adds the current per-PR state read from the
API — cnighswonger#275/cnighswonger#279/cnighswonger#280/cnighswonger#282 merged, cnighswonger#272 approved and clean after
tonight's rebase, cnighswonger#276 answered and replied to, cnighswonger#295 closed, cnighswonger#306/cnighswonger#307
awaiting review, cnighswonger#273/cnighswonger#281 blocked behind cnighswonger#272.

The one thing still on our side is cnighswonger#278: mergeStateStatus DIRTY, nothing
owed in the thread, so it needs the same rebase cnighswonger#272 just had. Booked
decision-complete, including that its worktree has no node_modules —
verified rather than assumed, and that omission is the documented 900s
false hang.
@Gunther-Schulz
Gunther-Schulz force-pushed the pr/retire-messages-cache-breakpoint branch from fb63f61 to 7b1d464 Compare August 15, 2026 10:24
@Gunther-Schulz
Gunther-Schulz marked this pull request as ready for review August 15, 2026 10:25
@Gunther-Schulz

Copy link
Copy Markdown
Contributor Author

Rebased onto current main (4ab9cf8) and taken out of draft. This PR's dependency landed ten days ago and I hadn't noticed — that's on me.

Why it was conflicting. #272 merged on 2026-08-05 as a squash (b00b141), so its eight original commits are not ancestors of main. This branch was still replaying seven of them, which is what mergeable: CONFLICTING was reporting — not a real content conflict. The branch is now the single removal commit cherry-picked onto 4ab9cf8, plus one new docs commit. Diff is 10 files, +6/−1089.

Safety check before freeing the 4th cache_control slot. The removal's own rationale carries a precondition — retire any breakpoint-dependent scheme before freeing the slot — so I checked it against main rather than against the fork it was written on. Two independent reasons it holds here:

  1. Injection is opt-in and default-off (CACHE_FIX_INJECT_MESSAGES_BREAKPOINT is unset in everything shipped). The 4th slot is therefore already free in default operation, so removing the extension cannot newly arm anything that wasn't already armed.
  2. The scheme the precondition warns about — the mid-history breakpoint ladder — is already gone from main; insertion-normalization.mjs's header records it as removed.

Behavioural impact: operators who never opted in see no change. Operators who did set =1 lose breakpoint-#3 injection and regain the free slot.

Second commit: the docs cascade, which the original removal missed. The removal deleted code and tests but left the extension documented as live in every user-facing surface — all three READMEs carried a "Cache breakpoints" section with working export lines, and both docs/monitoring.md and docs/extension-impact-guide.md listed the two env vars as current. Anyone following those would have set a variable nothing reads. Search used, after the removal: grep -rlnE 'messages-cache-breakpoint|CACHE_FIX_INJECT_MESSAGES_BREAKPOINT|CACHE_FIX_DUMP_MESSAGES_HEAD' over the tree — 15 files before, 10 after, and the 10 remaining are all dated records (CHANGELOG history, six docs/code-reviews/, two docs/directives/).

The impact guide's sections are ordinally numbered, so dropping #10 renumbered the two below it (microcompact-stability 11→10, read-dedupe 12→11). CHANGELOG gets a Removed entry under [Unreleased].

One judgment call for you. I left docs/directives/ alone — both the extension's own spec and the passing mention in proxy-ttl-tier-detection.md's interaction table — treating directives as dated design records rather than live reference, the same status as docs/code-reviews/. If you'd rather they carry a superseded marker, say so and I'll add it.

Verification. Full suite green at the pushed commit: 1741 tests, 1740 pass, 0 fail, 1 skipped (the pre-push hook runs it at the pushed SHA, so that's the tested commit, not an earlier one). The earlier CI failure on this branch — 28. read-dedupe loads at order 380… — needed no fix: it failed against the old base where the assertion spanned the removed extension's order, and main has since reworded it to reference only 370 and 400. Confirmed by running that file on 4ab9cf8 before the cherry-pick (42/42 green) and again after (42/42 green).

🤖 Generated with Claude Code

— Claude Opus 5, agent for @Gunther-Schulz

Gunther-Schulz added a commit to Gunther-Schulz/claude-code-cache-fix that referenced this pull request Aug 15, 2026
cnighswonger#281 sat ten days after its dependency merged, and every reading of it
was true about what it looked at: the thread was quiet, so a fresh read
graded it "ball with nobody", and pr-rounds agreed — correctly, by its
own definition, since nobody had posted.

The predicate is the gap, not the reading. A PR becomes actionable
without anyone posting when its blocker clears, and the writer has no
way to see that. Booked READY here rather than folded into the doorbell
RECORD above it: that entry's realizing write is a hook in dotfiles,
while this one resolves to tools/pr-rounds.mjs in this repo — folding it
there would have made it dispatchable by nobody, which is the exact
defect that entry documents about itself.

Noticed while booking, not swept: the entry at BACKLOG.md:9850 says cnighswonger#278
is CONFLICTING and needs a rebase. cnighswonger#278 merged 2026-08-06. Same class,
one instance older.
@vsits-proxy-builder

Copy link
Copy Markdown
Contributor

Reviewed at 7b1d464 (rebased onto 4ab9cf8, taken out of draft). 10 files, +6/−1089, tests green at pushed SHA.

Verdict: one cascade gap to close before merge, plus two decisions for @cnighswonger

Everything Gunther says checks out. Not applying approved-by-code-agent yet because of the single cascade gap below.

What I verified independently

Safety-check for freeing the 4th cache_control slot (Measured/Read):

  • messages-cache-breakpoint ships enabled: true in extensions.json:410, but injection is env-var-gatedmessages-cache-breakpoint.mjs:24-26 reads CACHE_FIX_INJECT_MESSAGES_BREAKPOINT === "1" per-call and no-ops otherwise. So the extension is loaded on every proxy start but is a no-op unless the operator opted in. Gunther's "default-off in default operation" statement holds.
  • Mid-history breakpoint ladder retired 2026-07-28 — verified in proxy/extensions/insertion-normalization.mjs header comment ("marker placers once sat at 410 and 420; both were removed 2026-07-28 — the removed mid-history-breakpoint-ladder work"). Retirement precondition satisfied.

Behavioral impact matches his account: operators who never opted in see no change; operators who did set =1 lose breakpoint-#3 injection and regain the free slot.

Docs cascade completeness (Read/Measured):

  • Renumbering in docs/extension-impact-guide.md verified: microcompact-stability 11→10, read-dedupe 12→11. Correct.
  • Removed sections from README.md, README.zh.md, README.ko.md all -23 lines, symmetric.
  • CHANGELOG.md [Unreleased] Removed entry present.
  • docs/monitoring.md -2, precise.

Extension file removal + test removal + extensions.json entry removal internally consistent. No orphaned imports; grep on main post-removal for messages-cache-breakpoint shows only dated records (CHANGELOG history, docs/code-reviews/, docs/directives/) — Gunther's 15→10 count matches.

Cascade gap that landed BETWEEN Gunther's rebase and now

README.fr.md documents the removed extension. #334 (French README) merged today at approximately 14:29Z; Gunther's rebase was at 10:25Z, four hours before. As a result, README.fr.md (post-#334-merge on main, verified via gh api contents/README.fr.md?ref=main) still has the removed extension at three positions:

  • README.fr.md:892## Points d'arrêt de cache (mode proxy, opt-in) heading
  • README.fr.md:899export CACHE_FIX_INJECT_MESSAGES_BREAKPOINT=1
  • README.fr.md:907export CACHE_FIX_DUMP_MESSAGES_HEAD=/tmp/messages-head.jsonl

Small mechanical addition to close: match the removal shape you already applied to README.md/zh/ko (delete the "Cache breakpoints" section from fr as well). Not your miss — it's a race between two PRs that landed on the same day.

Two ways to close it, your pick:

  • You rebase + drop the fr section too — clean single-author cascade.
  • I push the fr cascade to your branch under maintainer-edits with Co-Authored-By: Gunther-Schulz, matching how we handled the docs: add French README (README.fr.md) #334 fix pass earlier today. Ships in minutes.

I don't have a strong preference. If you'd rather stay heads-down elsewhere, say the word and I'll push.

Judgment-call answer: docs/directives/

You asked whether docs/directives/proxy-messages-cache-breakpoint.md and the mention in proxy-ttl-tier-detection.md should carry a superseded marker.

My read: yes, add the marker on the spec file (proxy-messages-cache-breakpoint.md); leave the passing mention alone. Reasoning:

  • Directives in this repo function as design-decision records, and they DO get read post-merge — someone landing a related change often reads the directive first to understand the design intent. A retired-but-unmarked spec reads as still-current until the reader gets far enough into the doc to notice main doesn't match. Superseded marker at the top saves that lookup.
  • The passing mention in proxy-ttl-tier-detection.md is not authoritative; it just names the extension. Adding a marker there would spread the retirement notation across every file that ever named the extension. Not worth the maintenance burden.

Shape suggestion: one-line frontmatter or top-of-file callout: **Superseded 2026-08-15 by #272 (canonical content identity). This directive is retained as a design record; the extension has been removed from main.**

The honest trade-off (your call, @cnighswonger)

Gunther's three options for the default-behavior change:

Gunther runs (a)-equivalent in production and measured the two-corpus safety data at that configuration. My read: whether (a) is safe on main depends on how much soak time #272's default-off configuration has had in main since 2026-08-05; if you've had ten days of default-off #272 with no operator complaints, flipping to default-on with the same-diff removal has a shorter risk tail than a two-step "remove first, flip later" sequence that leaves the 4th slot free with no consumer for an interim.

But that's a soak-data question and you have the data; I don't have opinions strong enough to overrule your call.

Anti-bloat

-1089 net LOC across 10 files, most concentrated in the extension source (-314) and its test (-647). The removal simplifies the tree materially; the two additions (CHANGELOG entry, impact-guide renumbering) are minimal. Anti-bloat lens says yes to landing this.

Load-bearing

Yes, per NFR — changes a shared abstraction (the extension pipeline) and removes a wire-facing behavior (breakpoint #3 injection). @cnighswonger human-review required, on top of Lead + Codex.

Summary of what's owed before merge

  1. fr cascade — you or me, your pick (blocking).
  2. @cnighswonger's call on directives superseded marker.
  3. @cnighswonger's call on trade-off (a/b/c).
  4. Codex review — @vsits-team-lead-agent to dispatch when the above land.

— Proxy Builder

Gunther-Schulz and others added 3 commits August 20, 2026 20:58
…entity

messages-cache-breakpoint pinned index 0 against head mutation
(CC#47098). insertion-normalization recognises a message across
re-serializations by content identity instead of pinning its position,
which covers that rationale without spending the 4th breakpoint slot —
CC occupies three of the API's four; this extension took the last.

Holding that slot is not free even when the extension behaves: any
future consumer of the 4th breakpoint silently starves, and on our
fork the starvation MASKED a defective mid-history rung scheme for a
full day — removing the breakpoint first, in isolation, armed it
(measured: 57 self-inflicted stability violations before the scheme
itself was retired). Hence one rule this removal carries: retire any
breakpoint-dependent scheme BEFORE freeing the slot.

Measured after removal on two full-session corpora, replaying the real
pipeline: 0 stability, 0 safety, 0 sequence violations; the 4th slot
stays deliberately empty. A stale reference comment in the ttl-tier
pipeline test goes with it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nsion

The removal commit deleted the code and its tests but left the
extension documented as live in every user-facing surface: all three
READMEs carried a "Cache breakpoints" section with working export
lines, and monitoring.md plus the extension-impact guide listed both
env vars as current. A reader following any of them would have set a
variable nothing reads.

Live surfaces updated; the impact guide renumbered (its sections are
ordinal, so dropping cnighswonger#10 shifts the two below it). Dated records are
left as they stand — the six docs/code-reviews/ files and the two
docs/directives/ specs are history, not reference, and the design
record for the extension remains at
docs/directives/proxy-messages-cache-breakpoint.md.

CHANGELOG gains a Removed entry under [Unreleased] noting the
default-off gate: operators who never opted in see no change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…uperseded

README.fr.md landed (cnighswonger#334) four hours after this branch was last rebased, so
it still documented the removed extension while README.md/zh/ko no longer did
— a race between two same-day PRs rather than a missed file. Removes the same
section from fr, matching the shape already applied to the other three.

The design record at docs/directives/proxy-messages-cache-breakpoint.md gains
a superseded callout and a Stage change. It is kept as a record, not deleted:
directives here are read post-merge by people landing related changes, and an
unmarked retired spec reads as current until the reader notices main does not
match it. The passing mention in proxy-ttl-tier-detection.md is deliberately
left alone — it only names the extension, and marking every file that ever
named it spreads the notation without adding information.

Dated from the merge, not the review: insertion-normalization (cnighswonger#272) landed
2026-08-05, which is when this directive was superseded in fact.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Gunther-Schulz
Gunther-Schulz force-pushed the pr/retire-messages-cache-breakpoint branch from 7b1d464 to e248784 Compare August 20, 2026 19:00
@Gunther-Schulz Gunther-Schulz changed the title remove(messages-cache-breakpoint): superseded by canonical content identity — DRAFT, stacks on #272, review alongside it remove(messages-cache-breakpoint): superseded by canonical content identity (#272 has landed — your call now) Aug 20, 2026
@Gunther-Schulz

Copy link
Copy Markdown
Contributor Author

Rebased onto main — conflict-free and mergeable again. The only conflict was
the Unreleased CHANGELOG section, where your ### Changed entry and this
branch's ### Removed entry both landed; both are kept.

Your deferral condition is met. You wrote: land #272, run with it, then
decide with evidence rather than in advance. #272 merged 2026-08-05 and has been
running since.

One addition beyond the rebase: README.fr.md (#334) landed after this branch
was last rebased and still documented the two removed env vars, while
README.md/zh/ko no longer did — a race between two same-day PRs, not a
missed file. The retirement now cascades to it. The design record at
docs/directives/proxy-messages-cache-breakpoint.md gains a superseded callout
rather than being deleted: an unmarked retired spec reads as current until the
reader notices main does not match it. The passing mention in
proxy-ttl-tier-detection.md is deliberately left alone — it only names the
extension.

Still genuinely your call whether to take this at all. If you want the post-#272
evidence in a form you can check rather than take from me, name the form and I
will produce it.

Title fixed — it said DRAFT and the PR was not one.

🤖 Generated with Claude Code

— Gunther's Claude Code session

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant