Skip to content

Commit 845c834

Browse files
authored
fix(worker): stop running pre-PR repair cycles by default (#320)
* fix(worker): stop running pre-PR repair cycles by default * docs: plan the auto-fix loop on failing PR checks
1 parent e94f5d7 commit 845c834

7 files changed

Lines changed: 390 additions & 9 deletions

File tree

apps/worker/src/pre-pr-checks/runner.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,13 @@ import type {
2222
RunBudgetState,
2323
} from "../workflows/run-budget.js";
2424

25-
export const MAX_PRE_PR_FIX_CYCLES = 3;
25+
/** Repair cycles the Pre-PR gate runs when a graph does not author its own:
26+
* none. Every cycle re-ran a tenant's entire check batch, so three cycles of an
27+
* 810s batch burned 54 minutes of a 100 minute run budget, and the loop could
28+
* not tell a broken environment from broken code. Remediation belongs after the
29+
* pull request is open, where provider CI has already reported what failed. A
30+
* graph can still opt in per node. */
31+
export const MAX_PRE_PR_FIX_CYCLES = 0;
2632

2733
/** Longest launch cause carried into the Pre-PR repair failure detail. Same
2834
* bound the workspace gate puts on a carried inspection reason (AIW-223): long

apps/worker/src/workflow-definition/block-registry.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -774,7 +774,7 @@ const definitions: Record<WorkflowBlockType, ContractDefinition> = {
774774
"Runs the product's configured pre-publication validation and fix cycle.",
775775
"✓",
776776
),
777-
defaults: { maxFixCycles: 3 },
777+
defaults: { maxFixCycles: 0 },
778778
inputs: {},
779779
output: statusOutput({
780780
ok: booleanType(),

apps/worker/src/workflows/agent.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ import {
152152
} from "./blocks/call-llm.js";
153153
import { pollPhaseUntilDone } from "./blocks/poll-phase.js";
154154
import {
155+
MAX_PRE_PR_FIX_CYCLES,
155156
loadPrePrCheckConfigStep,
156157
runPrePrChecksWithFixes,
157158
} from "./blocks/pre-pr-checks.js";
@@ -5267,7 +5268,7 @@ async function agentWorkflowBody(
52675268
: undefined);
52685269
if (
52695270
ctx.schemaVersion === 2 &&
5270-
(maxFixCycles ?? 3) > 0 &&
5271+
(maxFixCycles ?? MAX_PRE_PR_FIX_CYCLES) > 0 &&
52715272
!repairRuntime
52725273
) {
52735274
return executionError(

apps/worker/src/workflows/blocks/pre-pr-checks.test.ts

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,11 @@ function collected(overrides: {
122122

123123
describe("runPrePrChecksWithFixes", () => {
124124
beforeEach(() => {
125-
vi.clearAllMocks();
125+
// resetAllMocks, not clearAllMocks: clear leaves queued mockImplementationOnce
126+
// entries behind, so a test that consumes fewer of them than it queued leaks
127+
// the rest into whatever runs next. That made five unrelated tests fail the
128+
// moment the repair loop stopped running by default.
129+
vi.resetAllMocks();
126130
mocks.pollPhaseUntilDone.mockImplementation(pollEnds("finished", 30_000));
127131
mocks.startRepoCheckBatchStep.mockImplementation(async (
128132
_sandboxId: string,
@@ -446,7 +450,7 @@ describe("runPrePrChecksWithFixes", () => {
446450
});
447451
mocks.collectPrePrRepairStep.mockResolvedValue({ usage: null });
448452

449-
const result = await runPrePrChecksWithFixes(options());
453+
const result = await runPrePrChecksWithFixes(options({ maxFixCycles: 3 }));
450454

451455
expect(result.passed).toBe(true);
452456
expect(result.fixCycles).toBe(1);
@@ -619,7 +623,7 @@ describe("runPrePrChecksWithFixes", () => {
619623
});
620624
mocks.collectPrePrRepairStep.mockResolvedValue({ usage: null });
621625

622-
const result = await runPrePrChecksWithFixes(options({ config }));
626+
const result = await runPrePrChecksWithFixes(options({ maxFixCycles: 3, config }));
623627

624628
expect(result.setupFailed).toBe(false);
625629
expect(mocks.startPrePrRepairStep).toHaveBeenCalledTimes(1);
@@ -735,6 +739,32 @@ describe("runPrePrChecksWithFixes", () => {
735739
expect(mocks.startPrePrRepairStep).not.toHaveBeenCalled();
736740
});
737741

742+
it("runs no fix cycles when the graph does not author maxFixCycles", async () => {
743+
// Pins the shipped default. Repair before the pull request re-ran the whole
744+
// batch on every cycle and could not tell a broken environment from broken
745+
// code, so remediation moved behind the pull request. A graph that wants the
746+
// old behaviour has to ask for it by number.
747+
mocks.collectRepoCheckBatchStep.mockResolvedValue(
748+
collected({
749+
results: [{ provider: "github", repoPath: "acme/web", command: "pnpm typecheck", exitCode: 1 }],
750+
failures: [{
751+
provider: "github",
752+
repoPath: "acme/web",
753+
command: "pnpm typecheck",
754+
exitCode: 1,
755+
stdout: "",
756+
stderr: "still failing",
757+
}],
758+
}),
759+
);
760+
761+
const result = await runPrePrChecksWithFixes(options());
762+
763+
expect(result.fixCycles).toBe(0);
764+
expect(result.passed).toBe(false);
765+
expect(mocks.startPrePrRepairStep).not.toHaveBeenCalled();
766+
});
767+
738768
it("returns a repair agent failure without starting another cycle", async () => {
739769
mocks.collectRepoCheckBatchStep.mockResolvedValue(
740770
collected({
@@ -754,7 +784,7 @@ describe("runPrePrChecksWithFixes", () => {
754784
failure: { ok: false, category: "provider", diagnostic: { failureKind: "setup_failed" } },
755785
});
756786

757-
const result = await runPrePrChecksWithFixes(options());
787+
const result = await runPrePrChecksWithFixes(options({ maxFixCycles: 3 }));
758788

759789
expect(result.fixCycles).toBe(1);
760790
expect(result.agentFailure).toMatchObject({
@@ -843,6 +873,7 @@ describe("runPrePrChecksWithFixes", () => {
843873

844874
const result = await runPrePrChecksWithFixes(
845875
options({
876+
maxFixCycles: 3,
846877
budget: {
847878
state: createRunBudgetState(),
848879
limits: { maxDurationMs: 60_000, maxTokens: 12 },

apps/worker/src/workflows/blocks/pre-pr-checks.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { PrePrCheckConfig } from "../../pre-pr-checks/config.js";
2+
export { MAX_PRE_PR_FIX_CYCLES } from "../../pre-pr-checks/runner.js";
23
import {
34
MAX_PRE_PR_FIX_CYCLES,
45
PRE_PR_CHECK_BATCH_MAX_MINUTES,

0 commit comments

Comments
 (0)