77 classifyTaskBoundary ,
88 buildLLMTurnSummary ,
99 COMPACTED_PREFIX ,
10- COMPACT_SPACER_TEXT ,
1110 type SessionMetadata ,
1211} from "./session/compactor.js" ;
1312import { createModelSummarizer } from "./session/summarizer.js" ;
@@ -585,7 +584,7 @@ describe("createPruningCompactor — summarize receives the workflow context (CL
585584 } ) ;
586585} ) ;
587586
588- describe ( "createPruningCompactor — prefix-stable summaries (CL-6914 )" , ( ) => {
587+ describe ( "createPruningCompactor — consolidated handoff (CL-7521 )" , ( ) => {
589588 function firstText ( turn : ConversationTurn ) : string {
590589 const block = turn . content . find ( ( b ) => b . type === "text" ) ;
591590 return block !== undefined && block . type === "text" ? block . text : "" ;
@@ -608,23 +607,55 @@ describe("createPruningCompactor — prefix-stable summaries (CL-6914)", () => {
608607 return [ ...base , ...extra ] ;
609608 }
610609
611- test ( "second apply leaves output[0] bytes identical and appends a later summary " , async ( ) => {
610+ test ( "second apply replaces the prior summary instead of accumulating " , async ( ) => {
612611 const compactor = createPruningCompactor ( { keepRecentTurns : 2 , summaryMaxChars : 500 } ) ;
613612 const turns = grow ( [ ] , 16 , "round1" ) ;
614613 const output1 = ( await compactor . apply ( turns , mockStrategyCtx ) ) . output ;
615614 expect ( firstText ( output1 [ 0 ] ! ) ) . toContain ( COMPACTED_PREFIX ) ;
616615
617616 const output2 = ( await compactor . apply ( grow ( output1 , 16 , "round2" ) , mockStrategyCtx ) ) . output ;
618617
619- expect ( firstText ( output2 [ 0 ] ! ) ) . toBe ( firstText ( output1 [ 0 ] ! ) ) ;
620- expect ( output2 [ 0 ] ) . toBe ( output1 [ 0 ] ) ;
621- const summaries = compactedTurns ( output2 ) ;
622- expect ( summaries . length ) . toBeGreaterThanOrEqual ( 2 ) ;
623- expect ( output2 . indexOf ( summaries [ 1 ] ! ) ) . toBeGreaterThan ( 0 ) ;
618+ expect ( output2 [ 0 ] ) . not . toBe ( output1 [ 0 ] ) ;
619+ expect ( compactedTurns ( output2 ) ) . toHaveLength ( 1 ) ;
620+ expect ( firstText ( output2 [ 0 ] ! ) ) . toContain ( COMPACTED_PREFIX ) ;
624621 expect ( hasConsecutiveSameRole ( output2 ) ) . toBe ( false ) ;
622+ expect ( allText ( output2 ) ) . toContain ( "round1 0" ) ;
623+ } ) ;
624+
625+ test ( "second apply keeps the initiating task as its own user turn" , async ( ) => {
626+ const compactor = createPruningCompactor ( {
627+ keepRecentTurns : 2 ,
628+ maxAnchorTurns : 1 ,
629+ summaryMaxChars : 500 ,
630+ } ) ;
631+ const goal = "GOAL: migrate the auth module to opaque tokens" ;
632+ const turns : ConversationTurn [ ] = [
633+ makeTurn ( { role : "user" , content : [ { type : "text" , text : goal } ] } ) ,
634+ ] ;
635+ for ( let i = 0 ; i < 8 ; i ++ ) {
636+ turns . push ( makeTurn ( { role : "assistant" , content : [ { type : "text" , text : `step ${ i } ` } ] } ) ) ;
637+ }
638+ turns . push (
639+ makeTurn ( { role : "user" , content : [ { type : "text" , text : "also handle refresh" } ] } ) ,
640+ ) ;
641+ turns . push ( makeTurn ( { role : "assistant" , content : [ { type : "text" , text : "recent reply" } ] } ) ) ;
642+ turns . push ( makeTurn ( { role : "user" , content : [ { type : "text" , text : "recent ask" } ] } ) ) ;
643+
644+ const output1 = ( await compactor . apply ( turns , mockStrategyCtx ) ) . output ;
625645 expect (
626- output2 . some ( ( t ) => t . role === "assistant" && firstText ( t ) === COMPACT_SPACER_TEXT ) ,
646+ output1 . some (
647+ ( t ) => t . role === "user" && t . content . some ( ( b ) => b . type === "text" && b . text === goal ) ,
648+ ) ,
627649 ) . toBe ( true ) ;
650+
651+ const output2 = ( await compactor . apply ( grow ( output1 , 16 , "round2" ) , mockStrategyCtx ) ) . output ;
652+ expect ( compactedTurns ( output2 ) ) . toHaveLength ( 1 ) ;
653+ expect (
654+ output2 . some (
655+ ( t ) => t . role === "user" && t . content . some ( ( b ) => b . type === "text" && b . text === goal ) ,
656+ ) ,
657+ ) . toBe ( true ) ;
658+ expect ( hasConsecutiveSameRole ( output2 ) ) . toBe ( false ) ;
628659 } ) ;
629660
630661 test ( "empty-fold keep-set returns the input unchanged" , async ( ) => {
@@ -648,7 +679,7 @@ describe("createPruningCompactor — prefix-stable summaries (CL-6914)", () => {
648679 expect ( result . record . reason ) . toBe ( "no compaction needed" ) ;
649680 } ) ;
650681
651- test ( "failing then succeeding summarizer does not rewrite output[0] " , async ( ) => {
682+ test ( "failing summarizer keeps prior context; a later success writes one handoff " , async ( ) => {
652683 const source : InferenceSource = {
653684 id : "test" ,
654685 provider : "openai" ,
@@ -671,15 +702,15 @@ describe("createPruningCompactor — prefix-stable summaries (CL-6914)", () => {
671702 summarize,
672703 } ) ;
673704 const turns = grow ( [ ] , 16 , "fail" ) ;
674- const output1 = ( await compactor . apply ( turns , mockStrategyCtx ) ) . output ;
675- expect ( firstText ( output1 [ 0 ] ! ) ) . toContain ( "Turns compacted:" ) ;
676- expect ( firstText ( output1 [ 0 ] ! ) ) . not . toContain ( "UNIQUE_SUCCESS_SUMMARY ") ;
677- expect ( firstText ( output1 [ 0 ] ! ) ) . toContain ( "Model summary unavailable" ) ;
678-
679- const output2 = ( await compactor . apply ( grow ( output1 , 16 , "ok" ) , mockStrategyCtx ) ) . output ;
680- expect ( firstText ( output2 [ 0 ] ! ) ) . toBe ( firstText ( output1 [ 0 ] ! ) ) ;
681- expect ( allText ( output2 ) ) . toContain ( "UNIQUE_SUCCESS_SUMMARY" ) ;
682- expect ( hasConsecutiveSameRole ( output2 ) ) . toBe ( false ) ;
705+ const result1 = await compactor . apply ( turns , mockStrategyCtx ) ;
706+ expect ( result1 . output ) . toBe ( turns ) ;
707+ expect ( result1 . record . reason ) . toBe ( "summarize failed ") ;
708+ expect ( firstText ( result1 . output [ 0 ] ! ) ) . not . toContain ( COMPACTED_PREFIX ) ;
709+
710+ const result2 = await compactor . apply ( grow ( result1 . output , 16 , "ok" ) , mockStrategyCtx ) ;
711+ expect ( compactedTurns ( result2 . output ) ) . toHaveLength ( 1 ) ;
712+ expect ( allText ( result2 . output ) ) . toContain ( "UNIQUE_SUCCESS_SUMMARY" ) ;
713+ expect ( hasConsecutiveSameRole ( result2 . output ) ) . toBe ( false ) ;
683714 } ) ;
684715} ) ;
685716
0 commit comments