@@ -114,6 +114,11 @@ const tail = (s, n = RUN_OUTPUT_BYTES) => {
114114 return bytes . length > n ? `[... earlier output truncated ...]\n${ bytes . subarray ( - n ) . toString ( 'utf8' ) } ` : text ;
115115} ;
116116const lastLine = ( s ) => tail ( String ( s ?? '' ) . trim ( ) . split ( / \r ? \n / ) . filter ( Boolean ) . pop ( ) ?? '' , 1000 ) ;
117+ // Git puts the cause before multi-line advice; kit run summaries still use lastLine.
118+ const gitReason = ( output ) => {
119+ const cause = String ( output ?? '' ) . split ( / \r ? \n / ) . find ( ( line ) => / ^ ( f a t a l | e r r o r ) : / . test ( line ) ) ;
120+ return cause === undefined ? lastLine ( output ) : tail ( cause , 1000 ) ;
121+ } ;
117122const posix = ( p ) => p . split ( path . sep ) . join ( '/' ) ;
118123
119124/** Legacy diagnostic parser. Collection accounting never trusts human-readable logs. */
@@ -182,7 +187,7 @@ export function createAutoCollect({
182187 const ahead = await git ( [ 'rev-list' , '--count' , '@{u}..HEAD' ] , top ) ;
183188 if ( ahead . truncated ) return 'could not inspect local commits: Git output was truncated; deliver manually' ;
184189 const count = Number ( ahead . output . trim ( ) ) ;
185- if ( ahead . code !== 0 ) return `could not inspect local commits: ${ lastLine ( ahead . output ) } ` ;
190+ if ( ahead . code !== 0 ) return `could not inspect local commits: ${ gitReason ( ahead . output ) } ` ;
186191 if ( ! / ^ \d + $ / . test ( ahead . output . trim ( ) ) || ! Number . isSafeInteger ( count ) ) {
187192 return 'could not inspect local commits: invalid Git ahead count; deliver manually' ;
188193 }
@@ -195,47 +200,47 @@ export function createAutoCollect({
195200 }
196201 const changed = await git ( [ 'diff-tree' , '--no-commit-id' , '--name-only' , '-r' , '-z' , 'HEAD' ] , top ) ;
197202 if ( changed . truncated ) return 'could not inspect pending commit paths: Git output was truncated; deliver manually' ;
198- if ( changed . code !== 0 ) return `could not inspect pending delivery commit: ${ lastLine ( changed . output ) } ` ;
203+ if ( changed . code !== 0 ) return `could not inspect pending delivery commit: ${ gitReason ( changed . output ) } ` ;
199204 const allowed = changed . output . split ( '\0' ) . filter ( Boolean ) . every ( ( file ) => paths . some ( ( base ) => (
200205 file === base || file . startsWith ( `${ base . replace ( / \/ $ / , '' ) } /` )
201206 ) ) ) ;
202207 if ( ! allowed ) return 'the pending auto-collect commit contains paths outside this request; push it manually' ;
203208 const pushed = await git ( [ 'push' ] , top ) ;
204- return pushed . code === 0 ? 'pushed' : `push failed: ${ lastLine ( pushed . output ) } ` ;
209+ return pushed . code === 0 ? 'pushed' : `push failed: ${ gitReason ( pushed . output ) } ` ;
205210 }
206211 }
207212 const scratch = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , 'research-kit-autocollect-index-' ) ) ;
208213 const indexEnv = { ...env , GIT_INDEX_FILE : path . join ( scratch , 'index' ) } ;
209214 const isolatedGit = ( args ) => exec ( 'git' , args , { cwd : top , env : indexEnv } ) ;
210215 try {
211216 const initialized = await isolatedGit ( [ 'read-tree' , 'HEAD' ] ) ;
212- if ( initialized . code !== 0 ) return `could not initialize isolated git index: ${ lastLine ( initialized . output ) } ` ;
217+ if ( initialized . code !== 0 ) return `could not initialize isolated git index: ${ gitReason ( initialized . output ) } ` ;
213218 for ( const p of paths ) {
214219 const r = await isolatedGit ( [ 'add' , '--' , p ] ) ;
215- if ( r . code !== 0 ) return `git add ${ p } failed: ${ lastLine ( r . output ) } ` ;
220+ if ( r . code !== 0 ) return `git add ${ p } failed: ${ gitReason ( r . output ) } ` ;
216221 }
217222 for ( const p of paths ) {
218223 if ( ! ( p === 'research' || p . endsWith ( '/research' ) ) ) continue ;
219224 const ledger = path . posix . join ( p . slice ( 0 , - 'research' . length ) || '.' , LEDGER ) ;
220225 if ( fs . existsSync ( path . join ( top , ledger ) ) ) {
221226 const r = await isolatedGit ( [ 'add' , '-f' , '--' , ledger ] ) ;
222- if ( r . code !== 0 ) return `git add -f ${ ledger } failed: ${ lastLine ( r . output ) } ` ;
227+ if ( r . code !== 0 ) return `git add -f ${ ledger } failed: ${ gitReason ( r . output ) } ` ;
223228 }
224229 }
225230 const staged = await isolatedGit ( [ 'diff' , '--cached' , '--quiet' ] ) ;
226- if ( staged . code !== 0 && staged . code !== 1 ) return `could not inspect isolated git index: ${ lastLine ( staged . output ) } ` ;
231+ if ( staged . code !== 0 && staged . code !== 1 ) return `could not inspect isolated git index: ${ gitReason ( staged . output ) } ` ;
227232 const hasChanges = staged . code !== 0 ;
228233 if ( hasChanges ) {
229234 const c = await isolatedGit ( [ 'commit' , '-m' , report . subject , '-m' , report . body ] ) ;
230- if ( c . code !== 0 ) return `commit refused: ${ lastLine ( c . output ) } ` ;
235+ if ( c . code !== 0 ) return `commit refused: ${ gitReason ( c . output ) } ` ;
231236 const reset = await git ( [ 'reset' , '--quiet' , 'HEAD' , '--' , ...paths ] , top ) ;
232- if ( reset . code !== 0 ) return `committed; could not refresh staged paths: ${ lastLine ( reset . output ) } ` ;
237+ if ( reset . code !== 0 ) return `committed; could not refresh staged paths: ${ gitReason ( reset . output ) } ` ;
233238 }
234239 const upstream = await git ( [ 'rev-parse' , '--abbrev-ref' , '--symbolic-full-name' , '@{u}' ] , top ) ;
235240 if ( upstream . truncated ) return `${ hasChanges ? 'committed; ' : '' } could not inspect upstream: Git output was truncated; deliver manually` ;
236241 if ( upstream . code !== 0 ) return hasChanges ? 'committed; no upstream to push to' : 'no upstream to push to' ;
237242 const pushed = await git ( [ 'push' ] , top ) ;
238- if ( pushed . code !== 0 ) return hasChanges ? `committed; push failed: ${ lastLine ( pushed . output ) } ` : `push failed: ${ lastLine ( pushed . output ) } ` ;
243+ if ( pushed . code !== 0 ) return hasChanges ? `committed; push failed: ${ gitReason ( pushed . output ) } ` : `push failed: ${ gitReason ( pushed . output ) } ` ;
239244 return hasChanges ? 'committed and pushed' : 'pushed' ;
240245 } finally {
241246 fs . rmSync ( scratch , { recursive : true , force : true } ) ;
@@ -290,7 +295,7 @@ export function createAutoCollect({
290295 if ( ! inside ( canonical ( top ) , physical ) ) return { problem : 'the approved topics folder resolves outside the repository' } ;
291296 const gitDir = await git ( [ 'rev-parse' , '--absolute-git-dir' ] , top ) ;
292297 if ( gitDir . truncated ) return { problem : 'could not inspect Git metadata: Git output was truncated; inspect the target manually' } ;
293- if ( gitDir . code !== 0 ) return { problem : `could not inspect Git metadata: ${ lastLine ( gitDir . output ) } ` } ;
298+ if ( gitDir . code !== 0 ) return { problem : `could not inspect Git metadata: ${ gitReason ( gitDir . output ) } ` } ;
294299 if ( gitDir . code === 0 && inside ( canonical ( gitDir . output . trim ( ) ) , physical ) ) {
295300 return { problem : 'new topics cannot be created in Git metadata' } ;
296301 }
@@ -327,12 +332,12 @@ export function createAutoCollect({
327332 const pathspecs = [ ...paths , `:(exclude)${ excludedPath } ` ] ;
328333 const status = await git ( [ 'status' , '--porcelain=v1' , '-z' , '--untracked-files=all' , '--' , ...pathspecs ] , top ) ;
329334 if ( status . truncated ) throw new Error ( 'could not inspect pending delivery: Git output was truncated; deliver manually' ) ;
330- if ( status . code !== 0 ) throw new Error ( `could not inspect pending delivery: ${ lastLine ( status . output ) } ` ) ;
335+ if ( status . code !== 0 ) throw new Error ( `could not inspect pending delivery: ${ gitReason ( status . output ) } ` ) ;
331336 const hash = crypto . createHash ( 'sha256' ) ;
332337 hash . update ( status . output ) ;
333338 const diff = await git ( [ 'diff' , '--binary' , 'HEAD' , '--' , ...pathspecs ] , top ) ;
334339 if ( diff . truncated ) throw new Error ( 'could not fingerprint pending delivery: Git output was truncated; deliver manually' ) ;
335- if ( diff . code !== 0 ) throw new Error ( `could not fingerprint pending delivery: ${ lastLine ( diff . output ) } ` ) ;
340+ if ( diff . code !== 0 ) throw new Error ( `could not fingerprint pending delivery: ${ gitReason ( diff . output ) } ` ) ;
336341 hash . update ( diff . output ) ;
337342 for ( const record of status . output . split ( '\0' ) ) {
338343 if ( ! record . startsWith ( '?? ' ) ) continue ;
@@ -563,7 +568,7 @@ export function createAutoCollect({
563568 const top = await repoTop ( dir ) ;
564569 if ( ! top ) { note ( `${ dir } is not in a git repository; requests travel by git` ) ; return status ( ) ; }
565570 const pulled = await git ( [ 'pull' , '--ff-only' ] , top ) ;
566- if ( pulled . code !== 0 ) note ( `git pull did not run cleanly (${ lastLine ( pulled . output ) } ); collection blocked until synchronization succeeds` ) ;
571+ if ( pulled . code !== 0 ) note ( `git pull did not run cleanly (${ gitReason ( pulled . output ) } ); collection blocked until synchronization succeeds` ) ;
567572 const requests = pulled . code === 0 ? listRequests ( dir ) : [ ] ;
568573
569574 for ( const item of requests ) {
0 commit comments