@@ -3,11 +3,7 @@ import fs from "node:fs";
33import os from "node:os" ;
44import path from "node:path" ;
55import type { ConversationTurn } from "@intx/types/runtime" ;
6- import {
7- createOptimizedContextStore ,
8- loadRecentTurns ,
9- resolveCheckpointAuthor ,
10- } from "./optimized-context-store.js" ;
6+ import { createOptimizedContextStore , loadRecentTurns } from "./optimized-context-store.js" ;
117import { segmentFileName , listSegmentFiles } from "./incremental-jsonl.js" ;
128
139const TURNS_FILE = "turns.jsonl" ;
@@ -26,11 +22,17 @@ function isolatedGitEnv(gitconfig: string): NodeJS.ProcessEnv {
2622 GIT_CONFIG_SYSTEM : "/dev/null" ,
2723 GIT_CONFIG_NOSYSTEM : "1" ,
2824 HOME : dir ,
25+ XDG_CONFIG_HOME : dir ,
2926 } ;
3027}
3128
32- async function headAuthor ( dir : string ) : Promise < { name : string ; email : string } > {
33- const proc = Bun . spawn ( [ "git" , "-C" , dir , "log" , "-1" , "--format=%an%n%ae" ] , {
29+ async function headIdent ( dir : string ) : Promise < {
30+ authorName : string ;
31+ authorEmail : string ;
32+ committerName : string ;
33+ committerEmail : string ;
34+ } > {
35+ const proc = Bun . spawn ( [ "git" , "-C" , dir , "log" , "-1" , "--format=%an%n%ae%n%cn%n%ce" ] , {
3436 stdout : "pipe" ,
3537 stderr : "pipe" ,
3638 } ) ;
@@ -42,13 +44,39 @@ async function headAuthor(dir: string): Promise<{ name: string; email: string }>
4244 if ( exitCode !== 0 ) {
4345 throw new Error ( `git log failed: ${ stderr . trim ( ) || stdout . trim ( ) } ` ) ;
4446 }
45- const [ name , email ] = stdout . trimEnd ( ) . split ( "\n" ) ;
46- if ( name === undefined || email === undefined ) {
47- throw new Error ( `unexpected git log author output: ${ JSON . stringify ( stdout ) } ` ) ;
47+ const [ authorName , authorEmail , committerName , committerEmail ] = stdout . trimEnd ( ) . split ( "\n" ) ;
48+ if (
49+ authorName === undefined ||
50+ authorEmail === undefined ||
51+ committerName === undefined ||
52+ committerEmail === undefined
53+ ) {
54+ throw new Error ( `unexpected git log identity output: ${ JSON . stringify ( stdout ) } ` ) ;
4855 }
49- return { name , email } ;
56+ return { authorName , authorEmail , committerName , committerEmail } ;
5057}
5158
59+ const EMPTY_CHECKPOINT_METADATA = {
60+ pendingOperations : [ ] ,
61+ tokenUsage : { input : 0 , output : 0 , cacheRead : 0 , cacheWrite : 0 , thinking : 0 } ,
62+ } ;
63+
64+ async function commitEmptyCheckpoint (
65+ dir : string ,
66+ opts ?: Parameters < typeof createOptimizedContextStore > [ 1 ] ,
67+ ) : Promise < void > {
68+ const store = await createOptimizedContextStore ( dir , opts ) ;
69+ await store . writeMetadata ( EMPTY_CHECKPOINT_METADATA ) ;
70+ await store . commit ( { message : "checkpoint: tool-execution" } ) ;
71+ }
72+
73+ const HARNESS_IDENT = {
74+ authorName : "interchange-harness" ,
75+ authorEmail : "harness@interchange.local" ,
76+ committerName : "interchange-harness" ,
77+ committerEmail : "harness@interchange.local" ,
78+ } ;
79+
5280function turn ( text : string ) : ConversationTurn {
5381 return { role : "user" , content : [ { type : "text" , text } ] , timestamp : 1 } ;
5482}
@@ -510,51 +538,71 @@ describe("createOptimizedContextStore checkpoint", () => {
510538 expect ( atHead ) . toHaveLength ( total ) ;
511539 } , 20_000 ) ;
512540
513- test ( "records the operator identity on the cycle commit " , async ( ) => {
541+ test ( "records the operator identity as author and committer from global git config " , async ( ) => {
514542 const dir = tempDir ( ) ;
515- const store = await createOptimizedContextStore ( dir , {
516- author : { name : " Sawyer" , email : " sawyer@dirtroad.dev" } ,
543+ await commitEmptyCheckpoint ( dir , {
544+ env : isolatedGitEnv ( `[user]\n\tname = Sawyer\n\temail = sawyer@dirtroad.dev\n` ) ,
517545 } ) ;
518- await store . writeMetadata ( {
519- pendingOperations : [ ] ,
520- tokenUsage : { input : 0 , output : 0 , cacheRead : 0 , cacheWrite : 0 , thinking : 0 } ,
521- } ) ;
522- await store . commit ( { message : "checkpoint: tool-execution" } ) ;
523546
524- expect ( await headAuthor ( dir ) ) . toEqual ( {
525- name : "Sawyer" ,
526- email : "sawyer@dirtroad.dev" ,
547+ expect ( await headIdent ( dir ) ) . toEqual ( {
548+ authorName : "Sawyer" ,
549+ authorEmail : "sawyer@dirtroad.dev" ,
550+ committerName : "Sawyer" ,
551+ committerEmail : "sawyer@dirtroad.dev" ,
527552 } ) ;
528553 } ) ;
529- } ) ;
530554
531- describe ( "resolveCheckpointAuthor" , ( ) => {
532- test ( "uses global user.name and user.email when both are set" , async ( ) => {
533- const env = isolatedGitEnv ( `[user]\n\tname = Sawyer\n\temail = sawyer@dirtroad.dev\n` ) ;
534- await expect ( resolveCheckpointAuthor ( env ) ) . resolves . toEqual ( {
535- name : "Sawyer" ,
536- email : "sawyer@dirtroad.dev" ,
555+ test ( "records an injected author as both author and committer" , async ( ) => {
556+ const dir = tempDir ( ) ;
557+ await commitEmptyCheckpoint ( dir , {
558+ author : { name : "Sawyer" , email : "sawyer@dirtroad.dev" } ,
559+ } ) ;
560+
561+ expect ( await headIdent ( dir ) ) . toEqual ( {
562+ authorName : "Sawyer" ,
563+ authorEmail : "sawyer@dirtroad.dev" ,
564+ committerName : "Sawyer" ,
565+ committerEmail : "sawyer@dirtroad.dev" ,
537566 } ) ;
538567 } ) ;
539568
540569 test ( "falls back to the harness identity when global config is missing" , async ( ) => {
541- const env = isolatedGitEnv ( "" ) ;
542- await expect ( resolveCheckpointAuthor ( env ) ) . resolves . toEqual ( {
543- name : "interchange-harness" ,
544- email : "harness@interchange.local" ,
545- } ) ;
570+ const dir = tempDir ( ) ;
571+ await commitEmptyCheckpoint ( dir , { env : isolatedGitEnv ( "" ) } ) ;
572+ expect ( await headIdent ( dir ) ) . toEqual ( HARNESS_IDENT ) ;
546573 } ) ;
547574
548575 test ( "falls back when only one of name or email is set" , async ( ) => {
549- const nameOnly = isolatedGitEnv ( `[user]\n\tname = Sawyer\n` ) ;
550- const emailOnly = isolatedGitEnv ( `[user]\n\temail = sawyer@dirtroad.dev\n` ) ;
551- await expect ( resolveCheckpointAuthor ( nameOnly ) ) . resolves . toEqual ( {
552- name : "interchange-harness" ,
553- email : "harness@interchange.local" ,
576+ const nameOnly = tempDir ( ) ;
577+ await commitEmptyCheckpoint ( nameOnly , {
578+ env : isolatedGitEnv ( `[user]\n\tname = Sawyer\n` ) ,
554579 } ) ;
555- await expect ( resolveCheckpointAuthor ( emailOnly ) ) . resolves . toEqual ( {
556- name : "interchange-harness" ,
557- email : "harness@interchange.local" ,
580+ expect ( await headIdent ( nameOnly ) ) . toEqual ( HARNESS_IDENT ) ;
581+
582+ const emailOnly = tempDir ( ) ;
583+ await commitEmptyCheckpoint ( emailOnly , {
584+ env : isolatedGitEnv ( `[user]\n\temail = sawyer@dirtroad.dev\n` ) ,
585+ } ) ;
586+ expect ( await headIdent ( emailOnly ) ) . toEqual ( HARNESS_IDENT ) ;
587+ } ) ;
588+
589+ test ( "falls back when global name and email are empty or whitespace" , async ( ) => {
590+ const bothEmpty = tempDir ( ) ;
591+ await commitEmptyCheckpoint ( bothEmpty , {
592+ env : isolatedGitEnv ( `[user]\n\tname =\n\temail =\n` ) ,
593+ } ) ;
594+ expect ( await headIdent ( bothEmpty ) ) . toEqual ( HARNESS_IDENT ) ;
595+
596+ const bothWhitespace = tempDir ( ) ;
597+ await commitEmptyCheckpoint ( bothWhitespace , {
598+ env : isolatedGitEnv ( `[user]\n\tname = \n\temail = \n` ) ,
599+ } ) ;
600+ expect ( await headIdent ( bothWhitespace ) ) . toEqual ( HARNESS_IDENT ) ;
601+
602+ const nameOnlyWhitespaceEmail = tempDir ( ) ;
603+ await commitEmptyCheckpoint ( nameOnlyWhitespaceEmail , {
604+ env : isolatedGitEnv ( `[user]\n\tname = Sawyer\n\temail = \n` ) ,
558605 } ) ;
606+ expect ( await headIdent ( nameOnlyWhitespaceEmail ) ) . toEqual ( HARNESS_IDENT ) ;
559607 } ) ;
560608} ) ;
0 commit comments