Intent: reading what landed is as fast as reading what is open.
Measured 2026-11-02: /trevato/forge/pulls?state=closed app 96 ms with words 71 ms (the open list's words is 5 ms). pullWords in src/web/studio/words.ts batches checks, reviewers and close reasons, then awaits pullWord per row; on merged pulls something in that per-row path is the 66 ms difference (find it with a phase, then batch it).
Done looks like:
[ ] pullWords for a page of 50 closed/merged pulls reads in one batch per kind of fact (checks, runs, reviews), with the same words as today for each row.
[ ] A test seeds 50 merged and closed pulls with checks and reviews, asserts the words equal today's per-pull answer, and that the read is under 15 ms.
[ ] The pull states the before/after words phase measured locally.
Files: src/web/studio/words.ts, test/words-closed-batch.test.ts (new).
Risks: the feed and MCP pulls read words through the same function; their answers must not change.
Intent: reading what landed is as fast as reading what is open.
Measured 2026-11-02:
/trevato/forge/pulls?state=closedapp 96 ms withwords71 ms (the open list'swordsis 5 ms).pullWordsinsrc/web/studio/words.tsbatches checks, reviewers and close reasons, then awaitspullWordper row; on merged pulls something in that per-row path is the 66 ms difference (find it with a phase, then batch it).Done looks like:
pullWordsfor a page of 50 closed/merged pulls reads in one batch per kind of fact (checks, runs, reviews), with the same words as today for each row.wordsphase measured locally.Files: src/web/studio/words.ts, test/words-closed-batch.test.ts (new). Risks: the feed and MCP
pullsread words through the same function; their answers must not change.