Intent: the list agents and the TUI read most often stops costing tens of seconds after every landing.
Seen 2026-11-02 on trevato/forge with 47 open pulls: GET /api/v1/repos/trevato/forge/pulls took 71 s and then 29 s. Server-Timing: merge;dur=71458.8, then merge;dur=26318.5; every other phase is under 5 ms. pullMergeStates (src/core/pulls.ts) caches by (head, base) oid. main moves on every landing, so each landing invalidates every pair, and the next list spawns aheadCount + mergeable for all of them, four at a time.
Done looks like:
[ ] A list request never waits on more than a bounded budget of fresh merge readings (for example 300 ms). A pair not yet read for the current base answers its last known reading, or null, and is read in the background. Which one, and why, goes in the pull and in a code comment.
[ ] At most one background reading runs per pair, and the background work stays at READ_AT_ONCE.
[ ] A test with 50 open pulls and a fake slow mergeable (200 ms each): after the base moves, the list answers under 1 s, and a second list after the readings finish has the fresh answers.
[ ] The pull description quotes the Server-Timing of GET …/pulls on this pull's preview, or on a fixture with 50 pulls.
Files: src/core/pulls.ts, test/pulls*.test.ts (a new file if the old one is near its budget).
Risk: a stale mergeable: true could mislead a lander. Landing re-checks mergeability itself (mergePull); confirm that in the pull.
Intent: the list agents and the TUI read most often stops costing tens of seconds after every landing.
Seen 2026-11-02 on trevato/forge with 47 open pulls:
GET /api/v1/repos/trevato/forge/pullstook 71 s and then 29 s. Server-Timing:merge;dur=71458.8, thenmerge;dur=26318.5; every other phase is under 5 ms.pullMergeStates(src/core/pulls.ts) caches by (head, base) oid. main moves on every landing, so each landing invalidates every pair, and the next list spawnsaheadCount+mergeablefor all of them, four at a time.Done looks like:
mergeable(200 ms each): after the base moves, the list answers under 1 s, and a second list after the readings finish has the fresh answers.GET …/pullson this pull's preview, or on a fixture with 50 pulls.Files: src/core/pulls.ts, test/pulls*.test.ts (a new file if the old one is near its budget). Risk: a stale
mergeable: truecould mislead a lander. Landing re-checks mergeability itself (mergePull); confirm that in the pull.