Skip to content

Commit a270da0

Browse files
insertion-normalization: a merged standalone matches the join of its pinned blocks — the 587k's shape
CC sometimes migrates ALL of a message's volatile blocks out together, joined into one standalone message (both hook reminders, wrapper-stripped, joined with "\n\n"), rather than one standalone per block. The existing single-block suppression set could never match that shape. Each pinned entry with >=2 volatile blocks now also registers a join-hash — its blocks' unwrapped texts, in wire order, joined with the one observed separator — and findSuppressibleDuplicate checks it as a second pass. No subset-merges, no speculative separators: only the one shape measured live (capture s-633915a8, msg863/864, and independently confirmed on a second real occurrence at msg640/641 the same session). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> (cherry picked from commit 78940a0)
1 parent 1ca82f0 commit a270da0

2 files changed

Lines changed: 275 additions & 12 deletions

File tree

proxy/extensions/insertion-normalization.mjs

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -656,22 +656,54 @@ function pinnedBlockHashes(priorCanonical) {
656656
return hashes;
657657
}
658658

659-
// Is `msg` a suppressible duplicate of a currently-pinned block? Narrow by
660-
// definition (BACKLOG #76606 part (c)): STANDALONE only (single block after
661-
// the same string->one-block fold canonicalMessageShape already applies
662-
// elsewhere in this file), and its wrapper-stripped bytes must exactly
663-
// equal a hash in `pinnedHashes` — never a positional or role heuristic.
664-
// Returns the matched hash (for telemetry) or null (genuine content: no
665-
// suppression, existing rules apply unchanged).
666-
export function findSuppressibleDuplicate(msg, pinnedHashes) {
659+
// The merged-standalone shape (measured 2026-07-30, capture s-633915a8,
660+
// msg864, the 587k window): CC sometimes migrates ALL of a message's
661+
// volatile blocks out TOGETHER, joined into one standalone message,
662+
// rather than one standalone per block. pinnedBlockHashes above can never
663+
// match that — it hashes one block at a time, and a merged message is one
664+
// block whose text spans two reminders. A second set covers exactly the
665+
// observed join: for each pinned entry with >=2 volatile blocks, hash the
666+
// concatenation of ALL its volatile blocks' wrapper-stripped texts, in
667+
// WIRE order, joined with "\n\n" — the exact separator measured on the
668+
// real merged standalone (both hook reminders, 627 chars). No
669+
// subset-merges: partial joins were never observed and would only invite
670+
// false suppression on coincidental partial matches. "\n\n" is hardcoded
671+
// to the one observed instance, not a general N-ary merge grammar — other
672+
// separators are unobserved, and the census keeps watching for them.
673+
function pinnedJoinHashes(priorCanonical) {
674+
const hashes = new Set();
675+
if (!Array.isArray(priorCanonical)) return hashes;
676+
for (const entry of priorCanonical) {
677+
if (entry.d || !entry.m || !Array.isArray(entry.m.content)) continue;
678+
const volatileTexts = entry.m.content.filter(isVolatileBlock).map((b) => unwrapVolatileText(b).text);
679+
if (volatileTexts.length < 2) continue;
680+
const h = hashMessageContent({ content: [{ type: "text", text: volatileTexts.join("\n\n") }] });
681+
if (h !== null) hashes.add(h);
682+
}
683+
return hashes;
684+
}
685+
686+
// Is `msg` a suppressible duplicate of a currently-pinned block (or, since
687+
// 2026-07-30, a currently-pinned entry's FULL joined volatile-block set)?
688+
// Narrow by definition (BACKLOG #76606 part (c)): STANDALONE only (single
689+
// block after the same string->one-block fold canonicalMessageShape
690+
// already applies elsewhere in this file), and its wrapper-stripped bytes
691+
// must exactly equal a hash in `pinnedHashes` or `joinHashes` — never a
692+
// positional or role heuristic. `joinHashes` is optional (existing callers
693+
// checking single-block duplicates only are unaffected). Returns the
694+
// matched hash (for telemetry) or null (genuine content: no suppression,
695+
// existing rules apply unchanged).
696+
export function findSuppressibleDuplicate(msg, pinnedHashes, joinHashes) {
667697
const shaped = canonicalMessageShape(msg);
668698
if (!Array.isArray(shaped.content) || shaped.content.length !== 1) return null;
669699
const h = hashMessageContent({ content: [unwrapVolatileText(shaped.content[0])] });
670-
if (h === null || !pinnedHashes.has(h)) return null;
671-
return h;
700+
if (h === null) return null;
701+
if (pinnedHashes.has(h)) return h;
702+
if (joinHashes && joinHashes.has(h)) return h;
703+
return null;
672704
}
673705

674-
export { pinnedBlockHashes };
706+
export { pinnedBlockHashes, pinnedJoinHashes };
675707

676708
// Pin-mode classification. Differences from classifyInsertion:
677709
// - identities exclude volatile blocks (flip absorption);
@@ -823,10 +855,11 @@ export function classifyPinned(messages, priorCanonical) {
823855
// and whatever the existing rules above already decided (append/splice/
824856
// edit-shaped reset) stands — no new reset path is introduced.
825857
const pinnedHashes = pinnedBlockHashes(priorCanonical);
858+
const pinnedJoin = pinnedJoinHashes(priorCanonical);
826859
const suppressions = [];
827860
for (const e of newEntries) {
828861
if (e.r === "assistant") continue;
829-
const h = findSuppressibleDuplicate(messages[e.index], pinnedHashes);
862+
const h = findSuppressibleDuplicate(messages[e.index], pinnedHashes, pinnedJoin);
830863
if (h !== null) suppressions.push({ index: e.index, hash: h });
831864
}
832865
const suppressedIdx = new Set(suppressions.map((s) => s.index));
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
// insertion-merge-suppression — the merged-standalone shape (587k window,
2+
// capture s-633915a8, msg864). Sibling to insertion-suppression.test.mjs's
3+
// single-block case, but here CC migrates ALL of a message's volatile
4+
// blocks out TOGETHER, joined into one standalone message, rather than one
5+
// standalone per block. The single-block pinnedHashes set can never match
6+
// that shape (it hashes one block at a time); this file exercises the
7+
// join-hash set added alongside it (pinnedJoinHashes / findSuppressibleDuplicate's
8+
// third argument).
9+
//
10+
// Design settled by the dispatcher after the 587k premise was corrected
11+
// (BACKLOG.md, "merged-reminder standalone, join-hash design settled"): for
12+
// each pinned entry with >=2 volatile blocks, also hash the concatenation of
13+
// ALL its volatile blocks' wrapper-stripped texts, in WIRE order, joined
14+
// with "\n\n" — the exact separator measured on the real merged standalone.
15+
// No subset-merges, no other separators — this is the one observed shape,
16+
// not a general N-ary merge grammar.
17+
18+
import { test } from "node:test";
19+
import assert from "node:assert/strict";
20+
import { readFileSync } from "node:fs";
21+
import { dirname, join } from "node:path";
22+
import { fileURLToPath } from "node:url";
23+
24+
import {
25+
classifyPinned,
26+
pinnedBlockHashes,
27+
pinnedJoinHashes,
28+
findSuppressibleDuplicate,
29+
} from "../proxy/extensions/insertion-normalization.mjs";
30+
31+
const __dirname = dirname(fileURLToPath(import.meta.url));
32+
const FIXTURE_PATH = join(__dirname, "fixtures", "harvested", "oscillation-s-633915a8-863.json");
33+
const fixture = JSON.parse(readFileSync(FIXTURE_PATH, "utf-8"));
34+
35+
// The real msg863 form (1243B-shaped: tool_result + two <system-reminder>
36+
// blocks, PreToolUse and PostToolUse) and the real msg864 merged standalone
37+
// (627 chars, both reminders wrapper-stripped and joined with "\n\n") —
38+
// pulled from the fixture rather than retyped, so the bite is the actual
39+
// measured bytes, not a paraphrase of them.
40+
const REAL_MSG863 = fixture.requests[0].msg863;
41+
const REAL_MERGED_STANDALONE = fixture.requests_864.find((r) => r.msg864.role === "system").msg864;
42+
43+
// --- Helpers (mirrors test/insertion-suppression.test.mjs's idiom) ---
44+
45+
function assistantToolUse(id) {
46+
return { role: "assistant", content: [{ type: "tool_use", id, name: "Agent", input: {} }] };
47+
}
48+
49+
function userMsg(text) {
50+
return { role: "user", content: [{ type: "text", text }] };
51+
}
52+
53+
const REMINDER_PRE = "<system-reminder>\nPreToolUse: first reminder\n</system-reminder>";
54+
const REMINDER_POST = "<system-reminder>\nPostToolUse: second reminder\n</system-reminder>";
55+
56+
function withTwoReminders(text) {
57+
return {
58+
role: "user",
59+
content: [
60+
{ type: "text", text },
61+
{ type: "text", text: REMINDER_PRE },
62+
{ type: "text", text: REMINDER_POST },
63+
],
64+
};
65+
}
66+
67+
function pinCanon(messages) {
68+
return classifyPinned(messages, null).canonicalEntries;
69+
}
70+
71+
// =====================================================================
72+
// (a) Bite from the REAL fixture bytes
73+
// =====================================================================
74+
75+
test("RED against the old (2-arg) call: the real merged standalone does not match single-block hashes alone", () => {
76+
// The tool_use id in the real fixture's msg863 pairs it with an
77+
// assistant Agent-spawn — reproduced here only so classifyPinned's
78+
// adjacency check accepts the array; the message content itself is the
79+
// fixture's own, unmodified.
80+
const toolUseId = REAL_MSG863.content[0].tool_use_id;
81+
const canon = pinCanon([assistantToolUse(toolUseId), REAL_MSG863]);
82+
const pinnedHashes = pinnedBlockHashes(canon);
83+
84+
// Old call shape (no third argument) — this is exactly what production
85+
// ran before the join-hash set existed, and it is what left
86+
// suppressed:0 across all 560 events of the real session.
87+
const h = findSuppressibleDuplicate(REAL_MERGED_STANDALONE, pinnedHashes);
88+
assert.equal(h, null, "single-block hashes alone must not match a merged standalone — this IS the observed gap");
89+
});
90+
91+
test("GREEN: the real merged standalone matches the join-hash of its pinned entry", () => {
92+
const toolUseId = REAL_MSG863.content[0].tool_use_id;
93+
const canon = pinCanon([assistantToolUse(toolUseId), REAL_MSG863]);
94+
const pinnedHashes = pinnedBlockHashes(canon);
95+
const joinHashes = pinnedJoinHashes(canon);
96+
97+
assert.equal(joinHashes.size, 1, "msg863's entry has exactly 2 volatile blocks -> exactly one join hash");
98+
99+
const h = findSuppressibleDuplicate(REAL_MERGED_STANDALONE, pinnedHashes, joinHashes);
100+
assert.notEqual(h, null, "the real merged standalone must be recognized as a suppressible duplicate");
101+
});
102+
103+
test("classifyPinned end-to-end: the real merged standalone is suppressed as a new entry, not forwarded twice", () => {
104+
const toolUseId = REAL_MSG863.content[0].tool_use_id;
105+
const canon = pinCanon([assistantToolUse(toolUseId), REAL_MSG863]);
106+
107+
const messages = [assistantToolUse(toolUseId), REAL_MSG863, { ...REAL_MERGED_STANDALONE }];
108+
const result = classifyPinned(messages, canon);
109+
110+
assert.equal(result.suppressed, 1, "the merged standalone must be counted as a suppression");
111+
assert.equal(result.suppressions.length, 1);
112+
assert.equal(result.suppressions[0].index, 2);
113+
// The pinned inline form (index 1) already carries both reminders; the
114+
// standalone must not also appear in the forwarded array.
115+
assert.equal(result.messages.length, 2, "the standalone must not be forwarded alongside the pinned inline form");
116+
});
117+
118+
// =====================================================================
119+
// (b) Regression: single-reminder standalone still matches (unchanged path)
120+
// =====================================================================
121+
122+
test("REGRESSION: a single-reminder standalone still matches via pinnedHashes even though joinHashes is now also passed", () => {
123+
const REMINDER_INNER = "PreToolUse:Edit hook additional context: file changed";
124+
const REMINDER = `<system-reminder>\n${REMINDER_INNER}\n</system-reminder>`;
125+
const singleReminderMsg = {
126+
role: "user",
127+
content: [
128+
{ type: "text", text: "tool result" },
129+
{ type: "text", text: REMINDER },
130+
],
131+
};
132+
const canon = pinCanon([singleReminderMsg, { role: "assistant", content: [{ type: "text", text: "a1" }] }]);
133+
const pinnedHashes = pinnedBlockHashes(canon);
134+
const joinHashes = pinnedJoinHashes(canon);
135+
136+
assert.equal(joinHashes.size, 0, "a single volatile block never produces a join hash");
137+
138+
const standalone = { role: "system", content: [{ type: "text", text: REMINDER_INNER }] };
139+
const h = findSuppressibleDuplicate(standalone, pinnedHashes, joinHashes);
140+
assert.notEqual(h, null, "the existing single-block suppression path must be unaffected");
141+
});
142+
143+
// =====================================================================
144+
// (c) Guard: a join of blocks from TWO DIFFERENT entries is NOT suppressed
145+
// =====================================================================
146+
147+
test("GUARD: concatenating volatile blocks from two DIFFERENT pinned entries does not suppress — identity is per-entry", () => {
148+
// Two separate messages, each carrying exactly ONE of the two reminders
149+
// (as opposed to withTwoReminders, which puts both on the SAME entry).
150+
const entryA = {
151+
role: "user",
152+
content: [{ type: "text", text: "result A" }, { type: "text", text: REMINDER_PRE }],
153+
};
154+
const entryB = {
155+
role: "user",
156+
content: [{ type: "text", text: "result B" }, { type: "text", text: REMINDER_POST }],
157+
};
158+
const canon = pinCanon([
159+
entryA,
160+
{ role: "assistant", content: [{ type: "text", text: "a1" }] },
161+
entryB,
162+
{ role: "assistant", content: [{ type: "text", text: "a2" }] },
163+
]);
164+
const pinnedHashes = pinnedBlockHashes(canon);
165+
const joinHashes = pinnedJoinHashes(canon);
166+
167+
assert.equal(joinHashes.size, 0, "neither entry has >=2 volatile blocks of its own -> no join hash from either");
168+
169+
// A candidate that tries to forge the join by pasting bytes from BOTH
170+
// entries together.
171+
const forged = {
172+
role: "system",
173+
content: "PreToolUse: first reminder\n\nPostToolUse: second reminder",
174+
};
175+
const h = findSuppressibleDuplicate(forged, pinnedHashes, joinHashes);
176+
assert.equal(h, null, "a cross-entry concatenation must never be treated as a suppressible duplicate");
177+
});
178+
179+
// =====================================================================
180+
// (d) Guard: a genuinely different concatenation is NOT suppressed
181+
// =====================================================================
182+
183+
test("GUARD: wrong order, wrong separator, or extra content — none of them suppress", () => {
184+
const canon = pinCanon([withTwoReminders("tool result"), { role: "assistant", content: [{ type: "text", text: "a1" }] }]);
185+
const pinnedHashes = pinnedBlockHashes(canon);
186+
const joinHashes = pinnedJoinHashes(canon);
187+
assert.equal(joinHashes.size, 1);
188+
189+
const reversedOrder = {
190+
role: "system",
191+
content: "PostToolUse: second reminder\n\nPreToolUse: first reminder",
192+
};
193+
assert.equal(findSuppressibleDuplicate(reversedOrder, pinnedHashes, joinHashes), null, "reversed order must not match");
194+
195+
const wrongSeparator = {
196+
role: "system",
197+
content: "PreToolUse: first reminder\nPostToolUse: second reminder",
198+
};
199+
assert.equal(
200+
findSuppressibleDuplicate(wrongSeparator, pinnedHashes, joinHashes),
201+
null,
202+
"a single-newline join (unobserved separator) must not match",
203+
);
204+
205+
const extraContent = {
206+
role: "system",
207+
content: "PreToolUse: first reminder\n\nPostToolUse: second reminder\n\nextra",
208+
};
209+
assert.equal(findSuppressibleDuplicate(extraContent, pinnedHashes, joinHashes), null, "extra trailing content must not match");
210+
});
211+
212+
// =====================================================================
213+
// pinnedJoinHashes unit bites (mirrors pinnedBlockHashes's own tests)
214+
// =====================================================================
215+
216+
test("pinnedJoinHashes: a dropped entry's join is excluded — its content is not being served anywhere", () => {
217+
const canon1 = pinCanon([
218+
withTwoReminders("tool result"),
219+
{ role: "assistant", content: [{ type: "text", text: "a1" }] },
220+
userMsg("u2"),
221+
{ role: "assistant", content: [{ type: "text", text: "a3" }] },
222+
]);
223+
const pruned = classifyPinned(
224+
[{ role: "assistant", content: [{ type: "text", text: "a1" }] }, userMsg("u2"), { role: "assistant", content: [{ type: "text", text: "a3" }] }, userMsg("tail")],
225+
canon1,
226+
);
227+
assert.equal(pruned.dropped, 1);
228+
const joinHashes = pinnedJoinHashes(pruned.canonicalEntries);
229+
assert.equal(joinHashes.size, 0, "a dropped pin's join must not be treated as currently live");
230+
});

0 commit comments

Comments
 (0)