@@ -960,3 +960,162 @@ describe("createOptimizedContextStore unpublished rewrite", () => {
960960 expect ( turnTexts ( ( await store . load ( ) ) . turns ) ) . toEqual ( [ "era-2" ] ) ;
961961 } ) ;
962962} ) ;
963+
964+ describe ( "createOptimizedContextStore prompt dedupe (CL-9026)" , ( ) => {
965+ const PROMPT_FILE = "prompt.jsonl" ;
966+
967+ function toolResultTurn ( body : string ) : ConversationTurn {
968+ return {
969+ role : "user" ,
970+ content : [
971+ {
972+ type : "tool_result" ,
973+ callId : "call-1" ,
974+ content : [ { type : "text" , text : body } ] ,
975+ } ,
976+ ] ,
977+ timestamp : 1 ,
978+ } ;
979+ }
980+
981+ function cloneTurns ( turns : ConversationTurn [ ] ) : ConversationTurn [ ] {
982+ return turns . map ( ( t ) => JSON . parse ( JSON . stringify ( t ) ) as ConversationTurn ) ;
983+ }
984+
985+ test ( "identical writePrompt writes no prompt segment" , async ( ) => {
986+ const dir = tempDir ( ) ;
987+ const store = await createOptimizedContextStore ( dir ) ;
988+ const turns = [ turn ( "a" ) , turn ( "b" ) ] ;
989+ await store . writeTurns ( [ ...turns ] ) ;
990+ await store . writePrompt ( [ ...turns ] ) ;
991+ await store . writePrompt ( cloneTurns ( turns ) ) ;
992+
993+ expect ( fs . existsSync ( path . join ( dir , PROMPT_FILE ) ) ) . toBe ( false ) ;
994+ expect ( await listSegmentFiles ( dir , PROMPT_FILE ) ) . toEqual ( [ ] ) ;
995+
996+ await store . writeMetadata ( EMPTY_CHECKPOINT_METADATA ) ;
997+ await store . commit ( { message : "identical prompt is a no-op" } ) ;
998+ expect ( await gitLsTree ( dir ) ) . not . toContain ( PROMPT_FILE ) ;
999+
1000+ // A fresh instance compares against on-disk live turns and also skips.
1001+ const fresh = await createOptimizedContextStore ( dir ) ;
1002+ await fresh . writePrompt ( cloneTurns ( turns ) ) ;
1003+ expect ( fs . existsSync ( path . join ( dir , PROMPT_FILE ) ) ) . toBe ( false ) ;
1004+ } ) ;
1005+
1006+ test ( "differing writePrompt still writes the prompt snapshot" , async ( ) => {
1007+ const dir = tempDir ( ) ;
1008+ const store = await createOptimizedContextStore ( dir ) ;
1009+ const live = [ turn ( "a" ) , toolResultTurn ( "x" . repeat ( 1000 ) ) ] ;
1010+ await store . writeTurns ( [ ...live ] ) ;
1011+
1012+ const shrunk = [ turn ( "a" ) , toolResultTurn ( "summarized" ) ] ;
1013+ await store . writePrompt ( shrunk ) ;
1014+ expect ( fs . existsSync ( path . join ( dir , PROMPT_FILE ) ) ) . toBe ( true ) ;
1015+ expect ( fs . readFileSync ( path . join ( dir , PROMPT_FILE ) , "utf-8" ) ) . toBe (
1016+ jsonl ( shrunk ) ,
1017+ ) ;
1018+
1019+ // An appended ephemeral turn also changes the prompt and still writes.
1020+ const ephemeralDir = tempDir ( ) ;
1021+ const ephemeralStore = await createOptimizedContextStore ( ephemeralDir ) ;
1022+ await ephemeralStore . writeTurns ( [ ...live ] ) ;
1023+ const withEphemeral = [ ...cloneTurns ( live ) , turn ( "ephemeral" ) ] ;
1024+ await ephemeralStore . writePrompt ( withEphemeral ) ;
1025+ expect ( fs . readFileSync ( path . join ( ephemeralDir , PROMPT_FILE ) , "utf-8" ) ) . toBe (
1026+ jsonl ( withEphemeral ) ,
1027+ ) ;
1028+ } ) ;
1029+
1030+ test ( "turns-only store loads live turns and commits no prompt file" , async ( ) => {
1031+ const dir = tempDir ( ) ;
1032+ const store = await createOptimizedContextStore ( dir ) ;
1033+ await store . writeTurns ( [ turn ( "a" ) , turn ( "b" ) ] ) ;
1034+ await store . writeMetadata ( EMPTY_CHECKPOINT_METADATA ) ;
1035+ const first = await store . commit ( { message : "turns only" } ) ;
1036+
1037+ expect ( turnTexts ( ( await store . load ( ) ) . turns ) ) . toEqual ( [ "a" , "b" ] ) ;
1038+
1039+ const fresh = await createOptimizedContextStore ( dir ) ;
1040+ expect ( turnTexts ( ( await fresh . load ( ) ) . turns ) ) . toEqual ( [ "a" , "b" ] ) ;
1041+ const second = await fresh . commit ( { message : "turns-only no-op" } ) ;
1042+ expect ( second . hash ) . toBe ( first . hash ) ;
1043+ expect ( fs . existsSync ( path . join ( dir , PROMPT_FILE ) ) ) . toBe ( false ) ;
1044+ expect ( await gitLsTree ( dir ) ) . not . toContain ( PROMPT_FILE ) ;
1045+ } ) ;
1046+
1047+ test ( "identical writePrompt after a differing one removes stale prompt segments" , async ( ) => {
1048+ const dir = tempDir ( ) ;
1049+ const store = await createOptimizedContextStore ( dir ) ;
1050+ const live = [ turn ( "a" ) , turn ( "b" ) ] ;
1051+ await store . writeTurns ( [ ...live ] ) ;
1052+ await store . writePrompt ( [ turn ( "a" ) ] ) ;
1053+ expect ( fs . existsSync ( path . join ( dir , PROMPT_FILE ) ) ) . toBe ( true ) ;
1054+
1055+ await store . writePrompt ( cloneTurns ( live ) ) ;
1056+ expect ( fs . existsSync ( path . join ( dir , PROMPT_FILE ) ) ) . toBe ( false ) ;
1057+ expect ( await listSegmentFiles ( dir , PROMPT_FILE ) ) . toEqual ( [ ] ) ;
1058+
1059+ await store . writeMetadata ( EMPTY_CHECKPOINT_METADATA ) ;
1060+ await store . commit ( { message : "stale prompt removed" } ) ;
1061+ expect ( await gitLsTree ( dir ) ) . not . toContain ( PROMPT_FILE ) ;
1062+ expect ( turnTexts ( ( await store . load ( ) ) . turns ) ) . toEqual ( [ "a" , "b" ] ) ;
1063+ } ) ;
1064+
1065+ test ( "committed then identical writePrompt drops prompt.jsonl from HEAD" , async ( ) => {
1066+ const dir = tempDir ( ) ;
1067+ const store = await createOptimizedContextStore ( dir ) ;
1068+ const live = [ turn ( "a" ) , turn ( "b" ) ] ;
1069+ await store . writeTurns ( [ ...live ] ) ;
1070+ await store . writePrompt ( [ turn ( "a" ) ] ) ;
1071+ await store . writeMetadata ( EMPTY_CHECKPOINT_METADATA ) ;
1072+ await store . commit ( { message : "differing prompt" } ) ;
1073+ expect ( await gitLsTree ( dir ) ) . toContain ( PROMPT_FILE ) ;
1074+
1075+ await store . writePrompt ( cloneTurns ( live ) ) ;
1076+ await store . writeMetadata ( EMPTY_CHECKPOINT_METADATA ) ;
1077+ const afterIdentical = await store . commit ( { message : "identical prompt" } ) ;
1078+ expect ( await gitLsTree ( dir ) ) . not . toContain ( PROMPT_FILE ) ;
1079+ expect ( turnTexts ( ( await store . load ( ) ) . turns ) ) . toEqual ( [ "a" , "b" ] ) ;
1080+
1081+ await store . writeMetadata ( EMPTY_CHECKPOINT_METADATA ) ;
1082+ const empty = await store . commit ( { message : "empty checkpoint" } ) ;
1083+ expect ( empty . hash ) . toBe ( afterIdentical . hash ) ;
1084+ } ) ;
1085+
1086+ test ( "identical writePrompt after multi-segment differing prompt drops all prompt files from HEAD" , async ( ) => {
1087+ const dir = tempDir ( ) ;
1088+ const store = await createOptimizedContextStore ( dir ) ;
1089+ const live : ConversationTurn [ ] = [ ] ;
1090+ const big = "x" . repeat ( 20_000 ) ;
1091+ for ( let i = 0 ; i < 18 ; i ++ ) {
1092+ live . push ( turn ( `${ i } -${ big } ` ) ) ;
1093+ }
1094+ await store . writeTurns ( [ ...live ] ) ;
1095+
1096+ const differing : ConversationTurn [ ] = [ ] ;
1097+ for ( let i = 0 ; i < 18 ; i ++ ) {
1098+ differing . push ( turn ( `p-${ i } -${ big } ` ) ) ;
1099+ }
1100+ await store . writePrompt ( differing ) ;
1101+ const extraPrompt = segmentFileName ( PROMPT_FILE , 1 ) ;
1102+ expect ( fs . existsSync ( path . join ( dir , PROMPT_FILE ) ) ) . toBe ( true ) ;
1103+ expect ( fs . existsSync ( path . join ( dir , extraPrompt ) ) ) . toBe ( true ) ;
1104+
1105+ await store . writeMetadata ( EMPTY_CHECKPOINT_METADATA ) ;
1106+ await store . commit ( { message : "multi-segment prompt" } ) ;
1107+ const treeAfterDiffering = await gitLsTree ( dir ) ;
1108+ expect ( treeAfterDiffering ) . toContain ( PROMPT_FILE ) ;
1109+ expect ( treeAfterDiffering ) . toContain ( extraPrompt ) ;
1110+
1111+ await store . writePrompt ( cloneTurns ( live ) ) ;
1112+ await store . writeMetadata ( EMPTY_CHECKPOINT_METADATA ) ;
1113+ await store . commit ( { message : "identical drops prompt segments" } ) ;
1114+ const tree = await gitLsTree ( dir ) ;
1115+ expect ( tree ) . not . toContain ( PROMPT_FILE ) ;
1116+ expect ( tree ) . not . toContain ( extraPrompt ) ;
1117+ expect ( turnTexts ( ( await store . load ( ) ) . turns ) ) . toEqual (
1118+ live . map ( ( t ) => ( t . content [ 0 ] as { text : string } ) . text ) ,
1119+ ) ;
1120+ } ) ;
1121+ } ) ;
0 commit comments