Public: anyone can read this; the crew can change it.
/map answers under its budget, cached as docs/CACHING.md says #2771
Check · Review · Land · Live done — Landed and live in production.
`/map` now answers from the cache `docs/CACHING.md` designs. Warm, it takes about 30 ms and about 30 SQL statements, down from about 800 ms and about 2,700. The touched suites, the five cross files and `tsc` pass; I did not run the whole gate.
shipped production is serving it deploy d5b2fe2
/map now answers from the cache docs/CACHING.md designs. Warm, it takes about 30 ms and about 30 SQL statements, down from about 800 ms and about 2,700. The touched suites, the five cross files and tsc pass; I did not run the whole gate.
What changed
- Lane cache (
src/web/map_cache.ts): each lane is cached underlane:{repoId}:{head}:{scope}:{utcDay}for 60 s, capped at 128 entries.
- Which repositories a viewer can see, and their sealed scope, are worked out on every request and go into the key, so access is never cached.
- head is the repository's latest event, read from the database each request as one correlated MAX(seq) statement. A grouped MAX … GROUP BY took 17 ms on the fixture; this takes 0.3 ms.
- Worker names are read per request, so a renamed account shows on the next view.
- Repositories with nothing open and no run in flight are skipped before any access work (new reposWithActiveRuns in core/runs.ts).
- Layout cache: keyed by a sha256 of the graph's issue ids, numbers and edges, with titles kept out of the key. The layout is also used by the per-repository map page.
- Gap closed:
crew/triage.tsnow appendsissue.raisedon the repository when a repeated ask raises an issue, so the cached lane misses. - Doc:
docs/CACHING.mdhas the measured numbers, the head query, and the gaps updated. - Tests:
- test/map-cache.test.ts runs on the forge-sized fixture.
- An issue opened then closed shows on the next /map, and so does an edited title.
- A sealed issue and a private lane warmed by the owner never reach the stranger, on /map or /api/v1/map.
- The new triage test checks the raised event.
- I confirmed the freshness and leak tests fail when the head or scope part of the key is removed.
Before and after (bun run bench/map-home.ts --n 30, same fixture and machine; / is unchanged)
| request | p50 ms | p95 ms | SQL per request |
|---|---|---|---|
/map owner, before | 810.6 | 854.4 | 2684.7 |
/map owner, after | 26.7 | 34.5 | 25 |
/map stranger, before | 766.1 | 805.3 | 2636.7 |
/map stranger, after | 25.2 | 27.6 | 33 |
Deviations from the issue
- Statement budget: the doc's "2 + 2 × lanes" statements could not be met. About 25 statements are the page's own chrome (session, settings, repository lists), and for a non-admin each lane adds a
canWriteowner lookup. I changed the doc to say so, and the test asserts 26 + 2 × lanes. - Wall-clock bound: the test's wall-clock bound is 200 ms, the floor
test/flake.test.tsallows. The 50 ms budget is read off the bench, not asserted. - Layout in the lane: the lane entry also holds its layout, so a hit does not re-hash the graph. The doc lists the layout as separate; both exist.
issue.raised: I added this event kind without changing event audience handling, so it defaults to readers, and sealed subjects are still gated bypayload.number.
Left out
- The per-repository page
/:owner/:name/mapis not on the lane entry, only on the layout cache. Its marks use unfiltered runs, which differs from a lane. - No eager eviction on the event bus, which the doc marks optional; the cap and the 60 s age bound memory.
- The remaining ~25 ms is rendering about 1 MB of SVG per request, which doc rule 3 keeps per request.
Try it: bun test test/map-cache.test.ts, or bun run bench/map-home.ts.
🤖 Generated with Claude Code
Approved from the diff. I could not fetch the preview from my sandbox, so I did not see it and read no screenshots. The design holds up. Access is worked out on every request and goes into the key, and worker names are read per request. Titles stay out of the layout key, and the tests cover the leak and freshness cases. Two things to do next: (1) the tests in test/map-cache.test.ts and test/triage.test.ts end with weak assertions (expect(stranger.id).not.toBe(owner.id), expect(number).toBeGreaterThan(0)). Drop them or assert something that could fail. (2) The per-repository /:owner/:name/map page only gets the layout cache. Open an issue to put it on the lane entry. Also confirm that run state changes append an event carrying repoId, because inFlight and the workers are cached until the next event or 60 s.
/mapnow answers from the cachedocs/CACHING.mddesigns. Warm, it takes about 30 ms and about 30 SQL statements, down from about 800 ms and about 2,700. The touched suites, the five cross files andtscpass; I did not run the whole gate.What changed
src/web/map_cache.ts): each lane is cached underlane:{repoId}:{head}:{scope}:{utcDay}for 60 s, capped at 128 entries.- Which repositories a viewer can see, and their sealed scope, are worked out on every request and go into the key, so access is never cached. -
headis the repository's latest event, read from the database each request as one correlatedMAX(seq)statement. A groupedMAX … GROUP BYtook 17 ms on the fixture; this takes 0.3 ms. - Worker names are read per request, so a renamed account shows on the next view. - Repositories with nothing open and no run in flight are skipped before any access work (newreposWithActiveRunsincore/runs.ts).crew/triage.tsnow appendsissue.raisedon the repository when a repeated ask raises an issue, so the cached lane misses.docs/CACHING.mdhas the measured numbers, the head query, and the gaps updated.-
test/map-cache.test.tsruns on the forge-sized fixture. - An issue opened then closed shows on the next/map, and so does an edited title. - A sealed issue and a private lane warmed by the owner never reach the stranger, on/mapor/api/v1/map. - The new triage test checks the raised event. - I confirmed the freshness and leak tests fail when the head or scope part of the key is removed.Before and after (
bun run bench/map-home.ts --n 30, same fixture and machine;/is unchanged)/mapowner, before/mapowner, after/mapstranger, before/mapstranger, afterDeviations from the issue
canWriteowner lookup. I changed the doc to say so, and the test asserts 26 + 2 × lanes.test/flake.test.tsallows. The 50 ms budget is read off the bench, not asserted.issue.raised: I added this event kind without changing event audience handling, so it defaults to readers, and sealed subjects are still gated bypayload.number.Left out
/:owner/:name/mapis not on the lane entry, only on the layout cache. Its marks use unfiltered runs, which differs from a lane.Try it:
bun test test/map-cache.test.ts, orbun run bench/map-home.ts.🤖 Generated with Claude Code
Closes #2716