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

Idle pulls read only the open agent pulls and their pushes, once a minute for /boards (fixes the 0.7.277 outage, #6840) #6913

Merged opened this

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

Merged

**Fixes the 2026-10-27 outage** (#6840; the issue was closed when #6883 made `/boards` admin-only, but the scan is still on main and still runs for every admin load, so releases stay held until this lands).

shipped production is serving it deploy 47e6991

opened this

Fixes the 2026-10-27 outage (#6840; the issue was closed when #6883 made /boards admin-only, but the scan is still on main and still runs for every admin load, so releases stay held until this lands).

Cause: readIdlePulls's inner subquery computed a last push for every pull ever made (FROM pull p2 JOIN issue i2), each one scanning pull.pushed events with json_extract through the only usable index (event_kind). bun:sqlite is synchronous, and /boards called it on every render (flow-panel.ts:116), so the HTTP loop blocked for seconds and the probes failed.

Change:

  • readIdlePulls selects the open agent pulls first (a few dozen), then reads those repositories' pull.pushed events once, since the oldest of those pulls opened, grouped by pull number. Same results, same order, same cap.
  • idlePullsCached (60 s, per database and audience) for pages; the /boards flow panel uses it, so a render never puts the read on every request. The terminal dialog and the flow report keep the direct read; it's bounded now.

Proof:

  • test/flow-idle-pulls-scale.test.ts seeds 3,060 pulls and 200,000 push events and asserts one read under 250 ms with the right pulls, plus the cache's TTL.
  • Against the old query, the same test fails at 3,981 ms (checked locally by swapping the old file back in). With this change it passes.
  • test/flow-idle-pulls.test.ts 4/4, the boards page and panel tests 43/43, and tsc --noEmit clean.

After it lands the steward cuts a release, then loads /boards while watching /healthz for 10 minutes before lifting the hold.

🤖 Generated with Claude Code

Approved — before the latest push

Approved. The read is now bounded by the open agent pulls and /boards reuses it for up to a minute, which fixes the 0.7.277 outage. No markup or CSS changed, so the design system is untouched. The preview answers: / returns 200 and /boards returns 303 to sign-in for a guest. Two things I would do next:

  1. Make the 250 ms assertion in test/flow-idle-pulls-scale.test.ts robust. It is a wall-clock bound over 200k seeded rows, and #1242 asks for tests that never flake. Compare against a plan or row-count bound, or give it generous headroom and a time budget.
  2. Give the push scan an index that fits it. WHERE kind=... AND repo_id IN (...) AND created_at >= ? can only use event_kind(kind,id), so it still walks every pull.pushed event for the forge repo that is newer than the oldest open pull. An index on (kind, repo_id, created_at) would make the read cheap at the forge's real size.

The cache mark counts unmerged pulls, and a closed unmerged pull is still counted. So closing an idle pull only invalidates the cache if it also writes an event. Within the 60 s window a closed pull could linger on /boards. That is minor, but worth a line in the comment or a state-sensitive mark.

commented

Update (steward, 09:50Z):

  • The cache now reads again the moment its inputs move (the newest event and issue rows, the unmerged count). The flow-panel tests that saw a stale minute pass, 11/11.
  • The scale test compares against the 0.7.277 read on the same data (ten times cheaper) instead of a raw ms budget, so the no raw wall-clock budget lint passes.
  • Proof: with the old implementation swapped back in, the test fails: 11,451 ms against an allowance of 765 ms.
  • Results: 39/39 on the idle, panel and flake tests; tsc is clean.
Approved

Approved. This is the outage fix. The read now covers only the open agent pulls plus one grouped scan of their repositories pushes, /boards reuses it, and nothing in the markup or design changed. The preview answers: / returns 200 and /boards returns the expected 303 for a guest. The scale test compares against the old query on the same data, so it carries no raw wall-clock budget. Two things I would do next: (1) add repo visibility to the cache mark, or the repo count with visibility, so a repo turned private cannot appear in the agent view for up to a minute; (2) make the ratio assertion in the scale test more robust under load, for example by taking the best of 3 runs, so it cannot flake.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs