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

Sentinel: The guard parked 29 runs in trevato/forge in a day #2163

Merged opened this

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

Merged

mason/forge:mason/2000-sentinel-the-guard-parked-29-runs-in-tre → main 1 commit fork

  • Merged. mason/2000-sentinel-the-guard-parked-29-runs-in-tre was merged into main as db81167.

Review

Sign in to review this change.
2 files changed +33 −3
src/crew/sentinel.ts +12 −3
@@ -233,15 +233,24 @@ export function ledgerStreaks(db: Database, now: number): Finding[] {
233233 lookFor: "A retry decision that is not stop, or a green check after a stop.",
234234 });
235235 }
236 // A skip is a run the guard saved — nothing new to act on — not a run it
237 // refused; only the decisions that parked work are counted.
236238 const parks = new Map<string, number>();
237 for (const d of decisionsSince(db, "guard", new Date(now - DAY_MS).toISOString()))
238 parks.set(d.repoId, (parks.get(d.repoId) ?? 0) + 1);
239 const skips = new Map<string, number>();
240 for (const d of decisionsSince(db, "guard", new Date(now - DAY_MS).toISOString())) {
241 const into = d.answer === "skipped" ? skips : parks;
242 into.set(d.repoId, (into.get(d.repoId) ?? 0) + 1);
243 }
239244 for (const [repoId, n] of parks) {
240245 if (n < 20) continue;
246 const skipped = skips.get(repoId) ?? 0;
241247 out.push({
242248 key: `parks:${repoId}`,
243249 title: `The guard parked ${n} runs in ${repoSlug(db, repoId)} in a day`,
244 evidence: [`${n} \`guard\` decisions since ${new Date(now - DAY_MS).toISOString()}`],
250 evidence: [
251 `${n} \`guard\` decisions that parked a run since ${new Date(now - DAY_MS).toISOString()}`,
252 ...(skipped > 0 ? [`${skipped} skips of a run with nothing new are not counted`] : []),
253 ],
245254 pillar: "Throughput: the crew spends on work, not on being refused",
246255 lookFor: "Fewer than 20 guard decisions a day for the repository.",
247256 });
test/sentinel.test.ts +21
@@ -274,6 +274,27 @@ describe("the guard", () => {
274274 park();
275275 expect(parks()?.evidence[0]).toContain("20 `guard` decisions");
276276 });
277
278 test("a skip is a run saved, not a park: skips alone are never named", () => {
279 app.db.query("DELETE FROM decision WHERE kind = 'guard'").run();
280 const guard = (answer: string) =>
281 recordDecision(app.db, {
282 kind: "guard",
283 repoId: repo.id,
284 state: { reason: "feedback" },
285 answer,
286 p: 1,
287 by: "rule",
288 ms: 0,
289 });
290 const parks = () => findFaults(app.db, Date.now()).find((x) => x.key === `parks:${repo.id}`);
291 for (let n = 0; n < 30; n++) guard("skipped");
292 for (let n = 0; n < 19; n++) guard("capped");
293 expect(parks()).toBeUndefined();
294 guard("capped");
295 expect(parks()?.title).toContain("parked 20 runs");
296 expect(parks()?.evidence.join("\n")).toContain("30 skips");
297 });
277298 });
278299
279300 describe("the log", () => {

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs