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

Throughput: 5 open crew pulls have a red check. #12258

Merged opened this in answer to

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

Merged

scout/forge:scout/12120-throughput-5-open-crew-pulls-have-a-red → main 1 commit fork 1 thread · 1 unresolved

  • Merged. scout/12120-throughput-5-open-crew-pulls-have-a-red was merged into main as 9e5d84b.

Review

Sign in to review this change.
4 files changed +46 −2
docs/features/keeper.md +2
@@ -9,6 +9,8 @@ The forge watches its own landing rate, names the one thing binding it, and acts
99
1010 Where it shows: one line on home and on the boards panel, `keeper` in the MCP `flow` report, the terminal's Flow dialog (the line, the question, and `l` for its one button after a confirm) and its crew scorecard ("Forge per day" rows), and "Forge per day" (hours running, landed) on the crew scorecard and in `scorecard` (`keeperDays`). Every attempt is a row in `keeper_attempt`.
1111
12 A `red_checks` issue lists the red pulls, oldest first, so whoever picks it up knows which to rework.
13
1214 An `empty_pillar` issue lists the ready issues and the hold on each (`landed`, `open_pull`, `stalled`...), so a person sees why nothing can start.
1315
1416 Not done: DORA change-fail rate, revert rate, queue-wait p95 and rework per agent from the issue discussion are not measured yet.
src/flow/keeper.ts +4 −2
@@ -18,7 +18,7 @@ import { ownRepoPath } from "../kernel/own_repo.ts";
1818 import { KEEPER_CAUSES, pickCause, readKeeperFacts, type KeeperCause, type KeeperMove } from "./keeper_cause.ts";
1919 import { isLow, landedBetween, readRate, type Rate } from "./keeper_rate.ts";
2020 import { HOLDS, heldNow, heldWords } from "../crew/held.ts";
21 import { oldestWaitingOn } from "./pile.ts";
21 import { oldestWaitingOn, redPulls } from "./pile.ts";
2222 import { during } from "../perf/jobs.ts";
2323
2424 const log = createLog("keeper");
@@ -120,6 +120,8 @@ export function fileOnce(app: App, found: { cause: KeeperCause; says: string },
120120 if (!repo) return log.warn("keeper cannot file: the forge's own repository is missing", { path: ownRepoPath() });
121121 const oldest = found.cause === "stale_pile" ? oldestWaitingOn(app.db, 10, repo.visibility !== "private") : [];
122122 const list = oldest.length === 0 ? "" : `\n\nOldest waiting, start here:\n${oldest.map((s) => `- ${s.ref} (waits on ${s.on})`).join("\n")}`;
123 const reds = found.cause === "red_checks" ? redPulls(app.db, 10, repo.visibility !== "private") : [];
124 const redList = reds.length === 0 ? "" : `\n\nRed, oldest first, start here:\n${reds.map((r) => `- ${r}`).join("\n")}`;
123125 const held = found.cause === "empty_pillar" ? heldNow(app, repo) : undefined;
124126 const heldList = held
125127 ? `\n\nReady issues and what holds them (${heldWords(held)}):\n${HOLDS.flatMap((h) => held.by[h].slice(0, 5).map((i) => `- #${i.number} (${h})`)).slice(0, 10).join("\n")}`
@@ -128,7 +130,7 @@ export function fileOnce(app: App, found: { cause: KeeperCause; says: string },
128130 repoId: repo.id,
129131 authorId: repo.ownerId,
130132 title: `Throughput: ${found.says}`.slice(0, 200),
131 body: `The throughput keeper found this binding landings (${rate.recent.toFixed(1)} an hour against a best of ${rate.best.toFixed(1)}) and cannot act on it itself.\n\n${found.says}${list}${heldList}\n\nFiles: docs/features/keeper.md`,
133 body: `The throughput keeper found this binding landings (${rate.recent.toFixed(1)} an hour against a best of ${rate.best.toFixed(1)}) and cannot act on it itself.\n\n${found.says}${list}${redList}${heldList}\n\nFiles: docs/features/keeper.md`,
132134 });
133135 if (!made.ok) return;
134136 app.bus.append({ kind: "issue.opened", repoId: repo.id, payload: { number: made.value.number } });
src/flow/pile.ts +25
@@ -107,3 +107,28 @@ export function oldestWaitingOn(db: Database, limit: number, publicOnly = true):
107107 }
108108 return out;
109109 }
110
111 /**
112 * The open crew pulls whose latest check on the current head failed, oldest
113 * first, each as `owner/repo#n`. A filed "red check" issue names them so the
114 * one who picks it up knows which pulls to rework. Private repositories are
115 * left out unless `publicOnly` is false.
116 */
117 export function redPulls(db: Database, limit: number, publicOnly = true): string[] {
118 const cap = Math.max(0, Math.floor(limit));
119 if (cap === 0) return [];
120 return db
121 .query<{ ref: string }, [number, number]>(
122 `SELECT o.username || '/' || r.name || '#' || i.number AS ref
123 FROM issue i JOIN pull p ON p.issue_id = i.id JOIN repo r ON r.id = i.repo_id
124 JOIN user o ON o.id = r.owner_id JOIN user a ON a.id = i.author_id
125 WHERE i.state = 'open' AND p.merged_at IS NULL AND (?1 = 0 OR r.visibility = 'public')
126 AND a.is_agent = 1 AND COALESCE(i.origin_author, '') = ''
127 AND EXISTS (SELECT 1 FROM repo_crew c WHERE c.repo_id = r.id AND c.user_id = a.id)
128 AND COALESCE((SELECT c.state FROM run c WHERE c.kind = 'check' AND c.issue_id = i.id
129 AND c.commit_oid = p.head_oid ORDER BY c.id DESC LIMIT 1) = 'failed', 0)
130 ORDER BY i.created_at ASC, i.id ASC LIMIT ?2`,
131 )
132 .all(publicOnly ? 1 : 0, cap)
133 .map((r) => r.ref);
134 }
test/flow-pile-oldest.test.ts +15
@@ -129,3 +129,18 @@ test("the filed empty_pillar issue says what holds the ready issues", () => {
129129 else process.env.FORGE_SENTINEL_REPO = was;
130130 }
131131 });
132
133 test("the filed red_checks issue names the red pulls, oldest first", () => {
134 const was = process.env.FORGE_SENTINEL_REPO;
135 process.env.FORGE_SENTINEL_REPO = "trevato/open";
136 try {
137 const rate = { lastHour: 0, recent: 0.5, best: 3, ranShare: 1 };
138 fileOnce(app, { cause: "red_checks", says: "2 open crew pulls have a red check." }, rate, Date.now());
139 const b = app.db.query<{ body: string }, []>("SELECT body FROM issue WHERE title LIKE 'Throughput:%' ORDER BY created_at DESC, rowid DESC LIMIT 1").get()!.body;
140 expect(b).toContain("Red, oldest first, start here:\n- trevato/open#2");
141 expect(b).not.toContain("Oldest waiting");

This asserts only one entry, so the ordering promised by the test name is not proven. Seed a second red pull with a later created_at and assert the order.

142 } finally {
143 if (was === undefined) delete process.env.FORGE_SENTINEL_REPO;
144 else process.env.FORGE_SENTINEL_REPO = was;
145 }
146 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs