Skip to content

Commit 096895c

Browse files
committed
fix(session): keep complete handoff facts over spine cuts
Cuts are 83 chars; longer-string-wins replaced 81- and 82-char complete facts with the truncated spine fragment and dropped the last characters.
1 parent 8075e0a commit 096895c

2 files changed

Lines changed: 73 additions & 1 deletion

File tree

‎src/session/compaction-handoff.test.ts‎

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,28 @@ describe("renderHandoffSpine", () => {
389389
expect(rendered.length).toBeLessThan(file.length / 3);
390390
expect(rendered.split("\n").length).toBeLessThanOrEqual(10);
391391
});
392+
393+
test("leaves 79- and 80-char items unmarked and marks 81 with an ellipsis", () => {
394+
const unmarked79 = `Must never write to ${"a".repeat(59)}`;
395+
const unmarked80 = `Must never write to ${"a".repeat(60)}`;
396+
const marked81 = `Must never write to ${"a".repeat(61)}`;
397+
expect(unmarked79.length).toBe(79);
398+
expect(unmarked80.length).toBe(80);
399+
expect(marked81.length).toBe(81);
400+
const rendered = renderHandoffSpine(
401+
{
402+
goal: "Ship it.",
403+
constraints: [unmarked79, unmarked80, marked81],
404+
decisions: [],
405+
evidenceMarkers: [],
406+
activatedTools: [],
407+
},
408+
handoffBlobUri(HANDOFF_LATEST_KEY),
409+
);
410+
expect(rendered).toContain(
411+
`Constraints: ${unmarked79} | ${unmarked80} | ${unmarked80}...`,
412+
);
413+
});
392414
});
393415

394416
describe("iterative folding", () => {
@@ -593,6 +615,9 @@ describe("iterative folding", () => {
593615
expect(first.artifact.constraints).toEqual(
594616
expect.arrayContaining([complete, sibling]),
595617
);
618+
expect(first.spineText).toContain(
619+
`Constraints: ${complete} | ${complete}...`,
620+
);
596621

597622
const second = buildHandoffFold(
598623
[spineTurn(first.spineText), userTurn("Continue.")],
@@ -620,6 +645,51 @@ describe("iterative folding", () => {
620645
expect(second.artifact.constraints).not.toContain(cut);
621646
});
622647

648+
test("81- and 82-char complete constraints survive a fold against their 83-char cuts", () => {
649+
for (const length of [81, 82]) {
650+
const complete = `Must never write to ${"a".repeat(length - 20)}`;
651+
expect(complete.length).toBe(length);
652+
const first = buildHandoffFold([userTurn(complete)], "narrative");
653+
expect(first.artifact.constraints).toEqual([complete]);
654+
const cut = `${complete.slice(0, 80)}...`;
655+
expect(cut.length).toBe(83);
656+
expect(first.spineText).toContain(`Constraints: ${cut}`);
657+
658+
const second = buildHandoffFold(
659+
[spineTurn(first.spineText), userTurn("Continue.")],
660+
"narrative",
661+
{ priorFileText: new TextDecoder().decode(first.blob.bytes) },
662+
);
663+
expect(second.artifact.constraints).toEqual([complete]);
664+
expect(second.artifact.constraints).not.toContain(cut);
665+
}
666+
});
667+
668+
test("an 81-char complete decision survives a fold against its 83-char cut", () => {
669+
const complete = `Use the session table ${"x".repeat(59)}`;
670+
expect(complete.length).toBe(81);
671+
const first = buildHandoffFold(
672+
[
673+
userTurn("Ship the widget."),
674+
userTurn(complete),
675+
userTurn("Keep going."),
676+
],
677+
"narrative",
678+
);
679+
expect(first.artifact.decisions).toEqual([complete]);
680+
const cut = `${complete.slice(0, 80)}...`;
681+
expect(cut.length).toBe(83);
682+
expect(first.spineText).toContain(`Decisions: ${cut}`);
683+
684+
const second = buildHandoffFold(
685+
[spineTurn(first.spineText), userTurn("Continue.")],
686+
"narrative",
687+
{ priorFileText: new TextDecoder().decode(first.blob.bytes) },
688+
);
689+
expect(second.artifact.decisions).toEqual([complete]);
690+
expect(second.artifact.decisions).not.toContain(cut);
691+
});
692+
623693
test("iterative union keeps prefix-sharing constraints distinct", () => {
624694
const first = buildHandoffFold(
625695
[

‎src/session/compaction-handoff.ts‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,9 @@ function mergeUnique(
366366
);
367367
if (related >= 0) {
368368
const existing = merged[related];
369-
if (existing !== undefined && item.length > existing.length)
369+
// Cuts are 83 chars (80 + "..."). Longer-string-wins would replace an
370+
// 81- or 82-char complete fact with that cut and drop the last chars.
371+
if (existing !== undefined && isSpineTruncationOf(existing, item))
370372
merged[related] = item;
371373
return;
372374
}

0 commit comments

Comments
 (0)