@@ -982,7 +982,17 @@ describe("createPruningCompactor — consolidated handoff (CL-7521)", () => {
982982 mockStrategyCtx ,
983983 ) ;
984984 expect ( compactedTurns ( result2 . output ) ) . toHaveLength ( 1 ) ;
985- expect ( allText ( result2 . output ) ) . toContain ( "UNIQUE_SUCCESS_SUMMARY" ) ;
985+ // CL-8744: the narrative lives in the fat handoff file, not the prompt.
986+ // The live output carries only the thin spine plus its pointer.
987+ expect ( allText ( result2 . output ) ) . not . toContain ( "UNIQUE_SUCCESS_SUMMARY" ) ;
988+ const handoffBlob = defined ( defined ( result2 . blobs ) [ 0 ] ) ;
989+ expect ( handoffBlob . contentType ) . toBe ( "text/markdown" ) ;
990+ expect ( new TextDecoder ( ) . decode ( handoffBlob . bytes ) ) . toContain (
991+ "UNIQUE_SUCCESS_SUMMARY" ,
992+ ) ;
993+ expect ( allText ( result2 . output ) ) . toContain (
994+ `Handoff: tool-output:///${ handoffBlob . key } ` ,
995+ ) ;
986996 expect ( hasConsecutiveSameRole ( result2 . output ) ) . toBe ( false ) ;
987997 } ) ;
988998} ) ;
@@ -1204,11 +1214,24 @@ describe("buildTurnSummary via createPruningCompactor", () => {
12041214 ] ;
12051215
12061216 const result = await compactor . apply ( turns , mockStrategyCtx ) ;
1207- const summaryText = (
1217+ const spineText = (
12081218 defined ( defined ( result . output [ 0 ] ) . content [ 0 ] ) as { text : string }
12091219 ) . text ;
1210- expect ( summaryText ) . toContain ( "read_file" ) ;
1211- expect ( summaryText ) . toContain ( "Total tool calls: 1" ) ;
1220+ // CL-8744: the live output carries only the thin spine (goal one-liner,
1221+ // evidence echo, explicit pointer) — file lists and counts stay in the
1222+ // fat handoff file, where they cannot make the next spine novel.
1223+ expect ( spineText ) . toContain ( "[Compacted prior context]" ) ;
1224+ expect ( spineText ) . toContain ( "Handoff: tool-output:///" ) ;
1225+ expect ( spineText ) . not . toContain ( "src/foo.ts" ) ;
1226+ // The structured tool memory lives in the fat handoff file.
1227+ const file = new TextDecoder ( ) . decode (
1228+ defined ( defined ( result . blobs ) [ 0 ] ) . bytes ,
1229+ ) ;
1230+ expect ( file ) . toContain ( "src/foo.ts" ) ;
1231+ expect ( file ) . toContain ( "paths: src/foo.ts" ) ;
1232+ expect ( file ) . toContain ( "turns: 2, tool calls: 1" ) ;
1233+ expect ( file ) . toContain ( "read_file" ) ;
1234+ expect ( file ) . toContain ( "Total tool calls: 1" ) ;
12121235 } ) ;
12131236
12141237 test ( "truncates summary when it exceeds maxChars" , async ( ) => {
@@ -1230,17 +1253,17 @@ describe("buildTurnSummary via createPruningCompactor", () => {
12301253 ] ;
12311254
12321255 const result = await compactor . apply ( turns , mockStrategyCtx ) ;
1233- const summaryBlock = defined ( defined ( result . output [ 0 ] ) . content [ 0 ] ) as {
1234- text : string ;
1235- } ;
1236- // The summary portion of the block is extracted from after the header line.
1237- // The header itself is "---..." so we look at the full block text — the
1238- // embedded buildTurnSummary output must end with "..." when truncated.
1239- expect ( summaryBlock . text ) . toContain ( "..." ) ;
1240- // And the truncated summary must not exceed maxChars + 3 (for the "..." suffix)
1241- const summaryStart = summaryBlock . text . indexOf ( "[Compacted prior context]" ) ;
1242- const rawSummary = summaryBlock . text . slice ( summaryStart ) ;
1243- // The raw summary lines are bounded by maxChars
1244- expect ( rawSummary . length ) . toBeLessThan ( maxChars + 200 ) ; // header text + bounded summary
1256+ // CL-8744: the deterministic narrative lives in the fat handoff file's
1257+ // Summary section; the live output carries only the thin spine.
1258+ const file = new TextDecoder ( ) . decode (
1259+ defined ( defined ( result . blobs ) [ 0 ] ) . bytes ,
1260+ ) ;
1261+ const narrative = defined (
1262+ file . split ( "## Summary (this fold — may paraphrase)\n" ) [ 1 ] ,
1263+ ) . split ( "## Exact facts" ) [ 0 ] ;
1264+ // The embedded buildTurnSummary output ends with "..." when truncated...
1265+ expect ( narrative ) . toContain ( "..." ) ;
1266+ // ...and the truncated narrative stays within maxChars + 3 ("..." suffix).
1267+ expect ( defined ( narrative ?. trim ( ) ) . length ) . toBeLessThanOrEqual ( maxChars + 3 ) ;
12451268 } ) ;
12461269} ) ;
0 commit comments