Skip to content

Commit 439dfc8

Browse files
Bill LeoutsakosBill Leoutsakos
authored andcommitted
fix(pi): retain post-push check state
1 parent dbf510f commit 439dfc8

2 files changed

Lines changed: 64 additions & 31 deletions

File tree

apps/sim/executor/handlers/pi/babysit-backend.test.ts

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,11 @@ const greenChecks = {
130130
blockingFailing: [],
131131
checksGreen: true,
132132
}
133+
const noChecksGreen = {
134+
...greenChecks,
135+
checks: [],
136+
contextRequirements: new Map<string, boolean>(),
137+
}
133138

134139
function params(overrides: Partial<PiBabysitRunParams> = {}): PiBabysitRunParams {
135140
return {
@@ -648,11 +653,6 @@ describe('runBabysitPiWithOptions', () => {
648653
})
649654

650655
it('preserves known clean flags when the pin moves after successful writes', async () => {
651-
const noChecksGreen = {
652-
...greenChecks,
653-
checks: [],
654-
contextRequirements: new Map<string, boolean>(),
655-
}
656656
mockFetchSnapshot
657657
.mockResolvedValueOnce(snapshot)
658658
.mockResolvedValueOnce(snapshot)
@@ -695,7 +695,7 @@ describe('runBabysitPiWithOptions', () => {
695695
totalUnresolved: 1,
696696
latestReview: null,
697697
})
698-
mockFetchChecks.mockResolvedValue(greenChecks)
698+
mockFetchChecks.mockResolvedValue(noChecksGreen)
699699
const { runner } = makeRunner({})
700700
mockWithPiSandbox.mockImplementation(async (callback) => callback(runner))
701701

@@ -710,10 +710,40 @@ describe('runBabysitPiWithOptions', () => {
710710
rounds: 1,
711711
commitsPushed: 1,
712712
threadsResolved: 0,
713+
checksGreen: true,
713714
})
714715
expect(result.totals.finalText).toContain('temporary GitHub read failure')
715716
})
716717

718+
it('marks required checks non-green when a pushed round file is invalid', async () => {
719+
mockFetchSnapshot
720+
.mockResolvedValueOnce(snapshot)
721+
.mockResolvedValueOnce(snapshot)
722+
.mockResolvedValue({ ...snapshot, headSha: NEW_SHA })
723+
mockFetchThreads.mockResolvedValue({
724+
actionable: [trustedThread],
725+
skipped: [],
726+
totalUnresolved: 1,
727+
latestReview: null,
728+
})
729+
mockFetchChecks.mockResolvedValue(greenChecks)
730+
const { runner } = makeRunner({ roundFile: 'not json' })
731+
mockWithPiSandbox.mockImplementation(async (callback) => callback(runner))
732+
733+
const result = await runBabysitPiWithOptions(
734+
params(),
735+
{ onEvent: vi.fn() },
736+
{ convergenceWaitMs: 0, roundWaitMs: 0 }
737+
)
738+
739+
expect(result).toMatchObject({
740+
stopReason: 'agent_failure',
741+
rounds: 1,
742+
commitsPushed: 1,
743+
checksGreen: false,
744+
})
745+
})
746+
717747
it('keeps an agent summary and does not await re-review when every request failed', async () => {
718748
mockFetchSnapshot
719749
.mockResolvedValueOnce(snapshot)

apps/sim/executor/handlers/pi/babysit-backend.ts

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,10 @@ function outstandingReason(
556556
return fallback
557557
}
558558

559+
function threadsAreClean(threads: BabysitThreadsState | undefined): boolean {
560+
return !!threads && threads.actionable.length === 0 && threads.skipped.length === 0
561+
}
562+
559563
/** Resolves the host-side run budget without imposing E2B's lifetime on other providers. */
560564
export function resolveBabysitExecutionBudgetMs(executionBudgetMs?: number): number {
561565
return executionBudgetMs ?? resolvePiSandboxLifetimeMs() ?? getMaxExecutionTimeout()
@@ -874,11 +878,23 @@ export async function runBabysitPiWithOptions(
874878
secrets
875879
)}`
876880
)
877-
return resultFor(totals, 'pushed_awaiting_confirmation', progress, threadsClean, false)
881+
return resultFor(
882+
totals,
883+
'pushed_awaiting_confirmation',
884+
progress,
885+
threadsClean,
886+
lastKnownChecksGreen
887+
)
878888
}
879889
if (convergence === 'third_party') {
880890
progress.notes.push('A third-party head SHA appeared after the Babysit push.')
881-
return resultFor(totals, 'pushed_awaiting_confirmation', progress, threadsClean, false)
891+
return resultFor(
892+
totals,
893+
'pushed_awaiting_confirmation',
894+
progress,
895+
threadsClean,
896+
lastKnownChecksGreen
897+
)
882898
}
883899
if (convergence === 'lagging') {
884900
progress.notes.push('The push succeeded, but GitHub had not yet converged on its SHA.')
@@ -899,13 +915,7 @@ export async function runBabysitPiWithOptions(
899915
roundRaw = await runner.readFile(BABYSIT_ROUND_PATH)
900916
} catch (error) {
901917
progress.notes.push(scrubPiSecrets(getErrorMessage(error), secrets))
902-
return resultFor(
903-
totals,
904-
'agent_failure',
905-
progress,
906-
threadsClean,
907-
latestChecks!.checksGreen
908-
)
918+
return resultFor(totals, 'agent_failure', progress, threadsClean, lastKnownChecksGreen)
909919
}
910920
let decisions
911921
try {
@@ -918,13 +928,7 @@ export async function runBabysitPiWithOptions(
918928
})
919929
} catch (error) {
920930
progress.notes.push(scrubPiSecrets(getErrorMessage(error), secrets))
921-
return resultFor(
922-
totals,
923-
'agent_failure',
924-
progress,
925-
threadsClean,
926-
latestChecks!.checksGreen
927-
)
931+
return resultFor(totals, 'agent_failure', progress, threadsClean, lastKnownChecksGreen)
928932
}
929933
progress.notes.push(...decisions.contractViolations)
930934
if (decisions.summary) progress.notes.push(decisions.summary)
@@ -944,12 +948,13 @@ export async function runBabysitPiWithOptions(
944948
progress.threadsResolved += writeResult.threadsResolved
945949
const resolvedThreadIds = new Set(writeResult.resolvedThreadIds ?? [])
946950
if (resolvedThreadIds.size > 0) {
951+
const knownThreads = latestThreads!
947952
latestThreads = {
948-
...latestThreads,
949-
actionable: latestThreads.actionable.filter(
953+
...knownThreads,
954+
actionable: knownThreads.actionable.filter(
950955
(thread) => !resolvedThreadIds.has(thread.id)
951956
),
952-
totalUnresolved: Math.max(0, latestThreads.totalUnresolved - resolvedThreadIds.size),
957+
totalUnresolved: Math.max(0, knownThreads.totalUnresolved - resolvedThreadIds.size),
953958
}
954959
}
955960
githubWriteOccurred ||= writeResult.repliesPosted > 0 || writeResult.threadsResolved > 0
@@ -965,13 +970,11 @@ export async function runBabysitPiWithOptions(
965970
)
966971
}
967972
if (writeResult.stopReason) {
968-
const knownThreadsClean =
969-
latestThreads.actionable.length === 0 && latestThreads.skipped.length === 0
970973
return resultFor(
971974
totals,
972975
writeResult.stopReason,
973976
progress,
974-
knownThreadsClean,
977+
threadsAreClean(latestThreads),
975978
lastKnownChecksGreen
976979
)
977980
}
@@ -986,7 +989,7 @@ export async function runBabysitPiWithOptions(
986989
totals,
987990
finalized.commitPushed ? 'pushed_awaiting_confirmation' : 'agent_failure',
988991
progress,
989-
latestThreads.actionable.length === 0 && latestThreads.skipped.length === 0,
992+
threadsAreClean(latestThreads),
990993
lastKnownChecksGreen
991994
)
992995
}
@@ -995,7 +998,7 @@ export async function runBabysitPiWithOptions(
995998
totals,
996999
'head_moved',
9971000
progress,
998-
latestThreads.actionable.length === 0 && latestThreads.skipped.length === 0,
1001+
threadsAreClean(latestThreads),
9991002
lastKnownChecksGreen
10001003
)
10011004
}
@@ -1004,7 +1007,7 @@ export async function runBabysitPiWithOptions(
10041007
totals,
10051008
'pushed_awaiting_confirmation',
10061009
progress,
1007-
latestThreads.actionable.length === 0 && latestThreads.skipped.length === 0,
1010+
threadsAreClean(latestThreads),
10081011
lastKnownChecksGreen
10091012
)
10101013
}

0 commit comments

Comments
 (0)