@@ -2127,7 +2127,10 @@ async function maybeRunRepair(config: AdapterConfig, taskId: string, inputTask?:
21272127 } else if ( live . headSha !== preparedCommit || live . baseSha !== evidenceRevision . baseSha ) {
21282128 return repairStop ( path , task , "repair_stale_head_during_push_recovery" ) ;
21292129 }
2130- const liveAfter = await currentPullRevision ( String ( task . repository ) , prNumber , repairToken ) ;
2130+ const liveAfter = await waitForExpectedPullRevision (
2131+ ( ) => currentPullRevision ( String ( task . repository ) , prNumber , repairToken ) ,
2132+ { headSha : preparedCommit , baseSha : evidenceRevision . baseSha } ,
2133+ ) ;
21312134 if ( liveAfter . headSha !== preparedCommit || liveAfter . baseSha !== evidenceRevision . baseSha ) return repairStop ( path , task , "repair_pushed_revision_unverified" ) ;
21322135 const followupTaskId = await createFollowupReviewTask ( config , task , currentPolicy , preparedCommit , liveAfter . baseSha , String ( task . repair_finding_signature || signature ) , preparedCommit ) ;
21332136 task . repair_state = "repair_committed" ;
@@ -2260,7 +2263,10 @@ async function maybeRunRepair(config: AdapterConfig, taskId: string, inputTask?:
22602263 const push = runCommand ( [ config . hostGitBin , "-c" , "core.hooksPath=/dev/null" , "push" , "origin" , `HEAD:refs/heads/${ headRef } ` ] , repairWorkspace , gitEnv , 180 ) ;
22612264 writeJsonAtomic ( join ( artifactDir , "repair-push.json" ) , redactedCommandResult ( push ) ) ;
22622265 if ( push . returncode !== 0 ) return repairStop ( path , task , "repair_push_failed" , push . stderr ) ;
2263- const liveAfterPush = await currentPullRevision ( String ( task . repository ) , prNumber , repairToken ) ;
2266+ const liveAfterPush = await waitForExpectedPullRevision (
2267+ ( ) => currentPullRevision ( String ( task . repository ) , prNumber , repairToken ) ,
2268+ { headSha : commitSha , baseSha : evidenceRevision . baseSha } ,
2269+ ) ;
22642270 if ( liveAfterPush . headSha !== commitSha || liveAfterPush . baseSha !== evidenceRevision . baseSha ) return repairStop ( path , task , "repair_pushed_revision_unverified" ) ;
22652271 const followupTaskId = await createFollowupReviewTask ( config , task , refreshedPolicy , commitSha , liveAfterPush . baseSha , signature , commitSha ) ;
22662272 task . repair_state = "repair_committed" ;
@@ -3658,7 +3664,7 @@ interface SubmittedReview {
36583664 staleEvidence : boolean ;
36593665}
36603666
3661- interface PullRevision {
3667+ export interface PullRevision {
36623668 headSha : string ;
36633669 baseSha : string ;
36643670}
@@ -3672,6 +3678,24 @@ function samePullRevision(left: PullRevision, right: PullRevision): boolean {
36723678 return Boolean ( left . headSha && left . baseSha && left . headSha === right . headSha && left . baseSha === right . baseSha ) ;
36733679}
36743680
3681+ export async function waitForExpectedPullRevision (
3682+ readRevision : ( ) => Promise < PullRevision > ,
3683+ expected : PullRevision ,
3684+ attempts = 6 ,
3685+ delayMs = 500 ,
3686+ ) : Promise < PullRevision > {
3687+ const boundedAttempts = Math . max ( 1 , Math . min ( 20 , Math . trunc ( attempts ) ) ) ;
3688+ let latest : PullRevision = { headSha : "" , baseSha : "" } ;
3689+ for ( let attempt = 0 ; attempt < boundedAttempts ; attempt += 1 ) {
3690+ latest = await readRevision ( ) ;
3691+ if ( samePullRevision ( latest , expected ) ) return latest ;
3692+ if ( attempt + 1 < boundedAttempts && delayMs > 0 ) {
3693+ await new Promise ( ( resolveDelay ) => setTimeout ( resolveDelay , delayMs ) ) ;
3694+ }
3695+ }
3696+ return latest ;
3697+ }
3698+
36753699async function currentPullRevision ( repo : string , prNumber : number , token : string ) : Promise < PullRevision > {
36763700 const pr = ( await githubRequest ( "GET" , `https://api.github.com/repos/${ repo } /pulls/${ prNumber } ` , token ) ) as JsonObject ;
36773701 return {
@@ -4562,7 +4586,7 @@ export function runtimeDiagnostic(result: CommandResult): string | null {
45624586 return `Coven Code exited ${ result . returncode } without a diagnostic.` ;
45634587 }
45644588 const safe = redactTokenish ( raw )
4565- . replace ( / \b [ A - Z 0 - 9 . _ % + - ] + @ [ A - Z 0 - 9 . - ] + \. [ A - Z ] { 2 , } \b / gi, "[redacted email]" )
4589+ . replace ( / [ A - Z 0 - 9 . _ % + - ] + (?: \[ [ A - Z 0 - 9 _ - ] + \] ) ? @ [ A - Z 0 - 9 . - ] + \. [ A - Z ] { 2 , } / gi, "[redacted email]" )
45664590 . replace ( / \b o n a c c o u n t \s + [ ^ \n . ] + / gi, "on the configured account" )
45674591 . replace ( / \s + / g, " " )
45684592 . trim ( ) ;
@@ -4604,7 +4628,7 @@ export function redactTokenish(text: string): string {
46044628 . replace ( / \b (?: g h [ p o u s r ] _ | g i t h u b _ p a t _ ) [ A - Z a - z 0 - 9 _ - ] { 6 , } / g, "[redacted github token]" )
46054629 . replace ( / \b s k - (?: p r o j - ) ? [ A - Z a - z 0 - 9 _ - ] { 8 , } / g, "[redacted OpenAI token]" )
46064630 . replace ( / \b B e a r e r \s + [ ^ \s ' \" ] + / gi, "Bearer [redacted]" )
4607- . replace ( / \b [ A - Z 0 - 9 . _ % + - ] + @ [ A - Z 0 - 9 . - ] + \. [ A - Z ] { 2 , } \b / gi, "[redacted email]" )
4631+ . replace ( / [ A - Z 0 - 9 . _ % + - ] + (?: \[ [ A - Z 0 - 9 _ - ] + \] ) ? @ [ A - Z 0 - 9 . - ] + \. [ A - Z ] { 2 , } / gi, "[redacted email]" )
46084632 . replace ( / \b o n a c c o u n t \s + ` ? [ ^ ` \n . ] + ` ? / gi, "on the configured account" )
46094633 . replace ( / \b e y J [ A - Z a - z 0 - 9 _ - ] + \. [ A - Z a - z 0 - 9 _ - ] + \. [ A - Z a - z 0 - 9 _ - ] + \b / g, "[redacted JWT]" ) ;
46104634}
0 commit comments