Public: anyone can read this; the crew can change it.
A pull whose green was checked on an older main reads "checked against an older main" on its page, in the API and in the MCP #2240
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/2237-a-pull-whose-green-was-checked-on-an-old → main 4 commits fork 1 thread · 1 unresolved
2 files
changed +65
−0
test/checks.test.ts +7
| @@ -475,6 +475,13 @@ describe("checks", () => { | ||
| 475 | 475 | // The terminal says the same words (#1810). |
| 476 | 476 | expect(checksText(seen, false, true, "main")).toContain(` · ${said}`); |
| 477 | 477 | expect(checksText(seen, false, true)).not.toContain("checked against"); |
| 478 | // A green on an older base is not an unqualified pass in the terminal (#2237). | |
| 479 | const green = { ...seen, state: "ok" as const }; | |
| 480 | expect(checksText(green, false, true, "main", true)).toContain( | |
| 481 | "checks: passed against an older main", | |
| 482 | ); | |
| 483 | expect(checksText(green, false, true, "main", false)).toContain("checks: passed"); | |
| 484 | expect(checksText(green, false, true, "main", false)).not.toContain("older"); | |
| 478 | 485 | expect( |
| 479 | 486 | checksForHead(app.db, pull.id, branched.value.commitOid)[0]?.id, |
| 480 | 487 | ).toBe(failed.id); |
test/mcp.test.ts +58
| @@ -500,6 +500,64 @@ describe("the forge over MCP", () => { | ||
| 500 | 500 | ).toBe(false); |
| 501 | 501 | }); |
| 502 | 502 | |
| 503 | test("pull says checkStale in the MCP and the API when its check was on an older base (#2237)", async () => { | |
| 504 | const branched = await commitToBranch(app, alice, game, { | |
| 505 | branch: "stale-feat", | |
| 506 | message: "another change", | |
| 507 | files: [{ path: "c.txt", content: "c\n" }], | |
| 508 | }); | |
| 509 | if (!branched.ok) throw new Error(JSON.stringify(branched.error)); | |
| 510 | const opened = await openPull(app, alice, game, { | |
| 511 | title: "Stale", | |
| 512 | baseBranch: "main", | |
| 513 | headBranch: "stale-feat", | |
| 514 | }); | |
| 515 | if (!opened.ok) throw new Error(String(opened.error)); | |
| 516 | const pull = opened.value; | |
| 517 | const viaApi = async () => | |
| 518 | (await ( | |
| 519 | await fetch(`${base}/api/v1/repos/alice/game/pulls/${pull.number}`, { | |
| 520 | headers: { authorization: `Bearer ${ro}` }, | |
| 521 | }) | |
| 522 | ).json()) as any; | |
| 523 | const viaMcp = async () => | |
| 524 | (await tool(ro, "pull", { repo: "alice/game", number: pull.number })).value; | |
| 525 | // No check, or one that recorded no base: not stale. | |
| 526 | expect((await viaMcp()).checkStale).toBe(false); | |
| 527 | expect((await viaApi()).checkStale).toBe(false); | |
| 528 | const run = createRun(app.db, { | |
| 529 | repoId: game.id, | |
| 530 | kind: "check", | |
| 531 | issueId: pull.id, | |
| 532 | env: `pull-${pull.number}`, | |
| 533 | commitOid: branched.value.commitOid, | |
| 534 | }); | |
| 535 | expect((await viaMcp()).checkStale).toBe(false); | |
| 536 | // Checked on the base's tip: not stale. | |
| 537 | const tip: string = (await viaApi()).status.baseOid; | |
| 538 | setRunBase(app.db, run.id, tip); | |
| 539 | expect((await viaMcp()).checkStale).toBe(false); | |
| 540 | expect((await viaApi()).checkStale).toBe(false); | |
| 541 | // The base moves on: the green is from an older main. | |
| 542 | const moved = await commitToBranch(app, alice, game, { | |
| 543 | message: "main moves", | |
| 544 | files: [{ path: "d.txt", content: "d\n" }], | |
| 545 | }); | |
| 546 | if (!moved.ok) throw new Error(JSON.stringify(moved.error)); | |
| 547 | expect((await viaMcp()).checkStale).toBe(true); | |
| 548 | expect((await viaApi()).checkStale).toBe(true); | |
| 549 | // A new check on the new tip is current again. | |
| 550 | setRunBase(app.db, run.id, moved.value.commitOid); | |
| 551 | expect((await viaMcp()).checkStale).toBe(false); | |
| 552 | expect((await viaApi()).checkStale).toBe(false); | |
| 553 | // Stale again, then closed: a pull that cannot land reads false, not noise. | |
| 554 | setRunBase(app.db, run.id, tip); | |
| 555 | expect((await viaApi()).checkStale).toBe(true); | |
| 556 | app.db.run("UPDATE issue SET state = 'closed' WHERE id = ?", [pull.id]); | |
| 557 | expect((await viaMcp()).checkStale).toBe(false); | |
| 558 | expect((await viaApi()).checkStale).toBe(false); | |
| 559 | }); | |
| 560 | ||
| 503 | 561 | test("stop takes a run id from now, and refuses a stale or foreign one", async () => { |
| 504 | 562 | const run = createRun(app.db, { |
| 505 | 563 | repoId: game.id, |
new above
offline