@@ -381,6 +381,25 @@ async function extraSegmentNamesAtCommit(dir: string, hash: string): Promise<str
381381 return names ;
382382}
383383
384+ async function restorePublishedTurnFiles ( dir : string ) : Promise < void > {
385+ const listing = await runGit ( dir , [ "ls-tree" , "--name-only" , "HEAD" ] ) ;
386+ const present = new Set ( listing . split ( "\n" ) . filter ( ( line ) => line . length > 0 ) ) ;
387+ const tracked : string [ ] = [ ] ;
388+ if ( present . has ( TURNS_FILE ) ) tracked . push ( TURNS_FILE ) ;
389+ for ( let index = 1 ; ; index ++ ) {
390+ const name = segmentFileName ( TURNS_FILE , index ) ;
391+ if ( ! present . has ( name ) ) break ;
392+ tracked . push ( name ) ;
393+ }
394+ if ( tracked . length > 0 ) {
395+ await runGit ( dir , [ "checkout" , "HEAD" , "--" , ...tracked ] ) ;
396+ }
397+ const disk = await listSegmentFiles ( dir , TURNS_FILE ) ;
398+ for ( const name of disk ) {
399+ if ( ! present . has ( name ) ) await fs . promises . unlink ( path . join ( dir , name ) ) ;
400+ }
401+ }
402+
384403async function describeHead ( dir : string , message : string ) : Promise < ContextCommit > {
385404 const [ hash , seconds , parents ] = ( await runGit ( dir , [ "log" , "-1" , "--format=%H%n%ct%n%P" ] ) ) . split (
386405 "\n" ,
@@ -446,7 +465,7 @@ export async function createOptimizedContextStore(
446465 const base = await createIsogitStore ( dir ) ;
447466 const pendingBlobFilepaths = new Set < string > ( ) ;
448467 const pendingSegmentPaths = new Set < string > ( ) ;
449- const writeTurnsSegmented = createSegmentedJSONLWriter ( dir , TURNS_FILE ) ;
468+ let writeTurnsSegmented = createSegmentedJSONLWriter ( dir , TURNS_FILE ) ;
450469 const writePromptSegmented = createSegmentedJSONLWriter ( dir , PROMPT_FILE ) ;
451470 let liveTurnRefs : readonly ConversationTurn [ ] | null = null ;
452471 let unpublishedRewrite : ConversationTurn [ ] | null = null ;
@@ -620,50 +639,62 @@ export async function createOptimizedContextStore(
620639 pendingBlobFilepaths . add ( `${ TOOL_OUTPUT_DIR } /${ filename } ` ) ;
621640 } ,
622641 async commit ( options , _signal ) {
623- if ( unpublishedRewrite !== null ) {
624- await writeSegmented ( writeTurnsSegmented , unpublishedRewrite ) ;
625- liveTurnRefs = unpublishedRewrite ;
626- unpublishedRewrite = null ;
642+ const stagedRewrite = unpublishedRewrite ;
643+ if ( stagedRewrite !== null ) {
644+ await writeSegmented ( writeTurnsSegmented , stagedRewrite ) ;
627645 }
628646
629- const toAdd : string [ ] = [ ] ;
630- const toRemove : string [ ] = [ ] ;
647+ try {
648+ const toAdd : string [ ] = [ ] ;
649+ const toRemove : string [ ] = [ ] ;
631650
632- const rewrittenEachCycle = [ RESPONSE_FILE , MANIFEST_FILE , METADATA_FILE ] ;
633- for ( const filepath of rewrittenEachCycle ) {
634- if ( await pathExists ( path . join ( dir , filepath ) ) ) toAdd . push ( filepath ) ;
635- }
651+ const rewrittenEachCycle = [ RESPONSE_FILE , MANIFEST_FILE , METADATA_FILE ] ;
652+ for ( const filepath of rewrittenEachCycle ) {
653+ if ( await pathExists ( path . join ( dir , filepath ) ) ) toAdd . push ( filepath ) ;
654+ }
636655
637- for ( const filepath of [ ...pendingSegmentPaths , ...pendingBlobFilepaths ] ) {
638- if ( await pathExists ( path . join ( dir , filepath ) ) ) toAdd . push ( filepath ) ;
639- else toRemove . push ( filepath ) ;
640- }
656+ for ( const filepath of [ ...pendingSegmentPaths , ...pendingBlobFilepaths ] ) {
657+ if ( await pathExists ( path . join ( dir , filepath ) ) ) toAdd . push ( filepath ) ;
658+ else toRemove . push ( filepath ) ;
659+ }
641660
642- if ( await pathExists ( path . join ( dir , EVIDENCE_ARCHIVE_DIR ) ) ) {
643- toAdd . push ( EVIDENCE_ARCHIVE_DIR ) ;
644- }
661+ if ( await pathExists ( path . join ( dir , EVIDENCE_ARCHIVE_DIR ) ) ) {
662+ toAdd . push ( EVIDENCE_ARCHIVE_DIR ) ;
663+ }
645664
646- // Disk is source of truth for which turn/prompt segments should remain
647- // tracked after a rewrite or heal, even if pendingSegmentPaths was lost.
648- await reconcileSegmentStaging ( dir , TURNS_FILE , toAdd , toRemove ) ;
649- await reconcileSegmentStaging ( dir , PROMPT_FILE , toAdd , toRemove ) ;
665+ // Disk is source of truth for which turn/prompt segments should remain
666+ // tracked after a rewrite or heal, even if pendingSegmentPaths was lost.
667+ await reconcileSegmentStaging ( dir , TURNS_FILE , toAdd , toRemove ) ;
668+ await reconcileSegmentStaging ( dir , PROMPT_FILE , toAdd , toRemove ) ;
650669
651- const add = [ ...new Set ( toAdd ) ] ;
652- const remove = [ ...new Set ( toRemove ) ] . filter ( ( p ) => ! add . includes ( p ) ) ;
670+ const add = [ ...new Set ( toAdd ) ] ;
671+ const remove = [ ...new Set ( toRemove ) ] . filter ( ( p ) => ! add . includes ( p ) ) ;
653672
654- if ( add . length > 0 ) await runGit ( dir , [ "add" , "--" , ...add ] ) ;
655- if ( remove . length > 0 ) {
656- await runGit ( dir , [ "rm" , "--cached" , "--ignore-unmatch" , "--" , ...remove ] ) ;
673+ if ( add . length > 0 ) await runGit ( dir , [ "add" , "--" , ...add ] ) ;
674+ if ( remove . length > 0 ) {
675+ await runGit ( dir , [ "rm" , "--cached" , "--ignore-unmatch" , "--" , ...remove ] ) ;
676+ }
677+ await runGit (
678+ dir ,
679+ [ "commit" , "-m" , options . message , `--author=${ author . name } <${ author . email } >` ] ,
680+ author ,
681+ gitEnv ,
682+ ) ;
683+ pendingBlobFilepaths . clear ( ) ;
684+ pendingSegmentPaths . clear ( ) ;
685+ if ( stagedRewrite !== null ) {
686+ liveTurnRefs = stagedRewrite ;
687+ unpublishedRewrite = null ;
688+ }
689+ return describeHead ( dir , options . message ) ;
690+ } catch ( cause ) {
691+ if ( stagedRewrite !== null ) {
692+ await restorePublishedTurnFiles ( dir ) ;
693+ writeTurnsSegmented = createSegmentedJSONLWriter ( dir , TURNS_FILE ) ;
694+ liveTurnRefs = null ;
695+ }
696+ throw cause ;
657697 }
658- await runGit (
659- dir ,
660- [ "commit" , "-m" , options . message , `--author=${ author . name } <${ author . email } >` ] ,
661- author ,
662- gitEnv ,
663- ) ;
664- pendingBlobFilepaths . clear ( ) ;
665- pendingSegmentPaths . clear ( ) ;
666- return describeHead ( dir , options . message ) ;
667698 } ,
668699 } ;
669700}
0 commit comments