Public: anyone can read this; the crew can change it.

A run that ends on the forge's own clone failure is interrupted, not a park against the issue #5123

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/forge:scout/5110-a-run-that-ends-on-the-forge-s-own-clone → main 1 commit fork 1 thread · 1 unresolved

  • Merged. scout/5110-a-run-that-ends-on-the-forge-s-own-clone was merged into main as dff5db4.

Review

Sign in to review this change.
2 files changed +23 −1
src/crew/interrupted.ts +5 −1
@@ -10,7 +10,7 @@ import type { Database } from "bun:sqlite";
1010 import { whenText } from "./limits.ts";
1111 import { NETWORK as NETWORK_ERROR } from "./network.ts";
1212
13 export type Interruption = "provider limit" | "network error" | "restart";
13 export type Interruption = "provider limit" | "network error" | "restart" | "clone";
1414
1515 /** What every interrupted run's summary starts with. */
1616 export const INTERRUPTED = "interrupted: ";
@@ -21,6 +21,8 @@ const PROVIDER =
2121 const NETWORK = new RegExp(`${NETWORK_ERROR.source}|could not reach|unreachable`, "i");
2222 const RESTART =
2323 /^cancelled$|interrupted by a restart|stopped for a restart|the forge stopped/i;
24 // The runner's own words when the forge could not clone or check out.
25 const CLONE = /^clone failed:/i;

The comment says clone or check out, but the pattern only matches clone failed:. Narrow the comment to match the code.

2426
2527 /**
2628 * Why a run that ended without a pull was cut short, read from its summary
@@ -34,9 +36,11 @@ export function interruptionOf(summary: string): Interruption | undefined {
3436 const cause = s.slice(INTERRUPTED.length);
3537 if (cause.startsWith("provider limit")) return "provider limit";
3638 if (cause.startsWith("network error")) return "network error";
39 if (cause.startsWith("clone")) return "clone";
3740 return "restart";
3841 }
3942 if (/^declined\b/i.test(s)) return undefined;
43 if (CLONE.test(s)) return "clone";
4044 if (RESTART.test(s)) return "restart";
4145 if (NETWORK.test(s)) return "network error";
4246 if (PROVIDER.test(s)) return "provider limit";
test/crew-parks.test.ts +18
@@ -74,6 +74,23 @@ describe("which runs were interrupted", () => {
7474 expect(interruptionOf("the check stayed red")).toBe(undefined);
7575 });
7676
77 test("a clone the forge could not make is an interruption; a decline that mentions one is not", () => {
78 expect(
79 interruptionOf("clone failed: fatal: unable to parse commit abc123"),
80 ).toBe("clone");
81 expect(interruptionOf("declined: the clone failed: is not my doing")).toBe(
82 undefined,
83 );
84 expect(interruptionOf("no changes pushed: DECLINED: clone failed: x")).toBe(
85 undefined,
86 );
87 const said = interruptedSummary("clone failed: fatal: unable to parse commit x");
88 expect(said).toBe(
89 "interrupted: clone, resumes on the next pass — clone failed: fatal: unable to parse commit x",
90 );
91 expect(interruptionOf(said)).toBe("clone");
92 });
93
7794 test("the summary says the cause and when it resumes", () => {
7895 const said = interruptedSummary(
7996 "every subscription this repository may use is resting (weekly limit)",
@@ -115,6 +132,7 @@ describe("an interrupted run is not a park", () => {
115132 "every subscription this repository may use is resting (You've hit your weekly limit)",
116133 "the run failed: TypeError: fetch failed",
117134 "cancelled",
135 "clone failed: fatal: unable to parse commit abc123",
118136 ]) {
119137 expect(ended(work, summary)).toBe(false);
120138 await tick();

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs