Skip to content

perf(webapp): a teammate's write should not cost everyone the project - #239

Merged
ssowonny merged 1 commit into
mainfrom
perf/narrow-fanout
Sep 20, 2026
Merged

ssowonny merged 1 commit into
mainfrom
perf/narrow-fanout

Conversation

@ssowonny

Copy link
Copy Markdown
Contributor

TL;DR

  • One teammate writing one file made every other client refetch the whole tree, the whole heat map, and every cached file body in every project open in the tab — twice.
  • Now: zero heat, at most one tree refresh, at most two requests total.
  • A ten-file agent run costs one tree refresh instead of ten.
  • Full hub suite: 260 passed, zero failures — the first entirely green run today.

What a frame used to cost

~1.8 MB of refetching to say that one file moved.

before after
heat (133 KB) every frame never — a write can't move read telemetry
["text"] bare prefix, twice per frame — every body, every project the named path only
tree (1.65 MB raw) every frame coalesced, ≤1 per 2 s
history every frame coalesced (free when unmounted)

Heat is the one worth dwelling on: it's read telemetry. It moves when somebody opens a file, which no write can tell us — so refreshing it on every write re-sent the whole map to say exactly what it said before.

What I deliberately did not do

The PRD asks for the tree cache to be patched from the frame rather than refetched. A frame carries paths, not the new size/time/author, so patching needs the hub to enrich the frame — a server change with a permissions question attached, since a frame must not name a path its reader cannot see.

The measurable criterion is met without it, so it's recorded in the PRD as deferred with that reasoning, not quietly dropped.

Also worth noting: history needs no "only when mounted" gate. invalidateQueries refetches active queries and only marks inactive ones stale, so a history view nobody has open already costs nothing.

Verification

  • e2e: a second account writes one file; the first client's request count for that frame is asserted — 0 heat, ≤1 tree, ≤2 total. Verified failing without the change (heat refetched: …/heat?days=30).
  • Full hub suite: 260 passed, 0 failed.

One honest note: an earlier run showed home.spec failures and I suspected my own coalescing window. It was the documented admin/hub flake cascade — a clean re-run is fully green. I'd truncated the first run's log to tail -12, so I had failure names without reasons and guessed twice before capturing the log properly.

🤖 Generated with Claude Code

A one-path change invalidated the whole tree, the whole heat map, and every
cached file body in every project open in the tab — the last one twice per
frame, because the bare ["text"] prefix was invalidated in two places. On a
real project that is ~1.8 MB of refetching to say that one file moved, and a
ten-file agent run said it ten times.

The frame names the path. This makes the client act like it.

  - Heat is out of the fan-out entirely. It is READ telemetry: it moves when
    somebody opens a file, which no write can tell us, so refreshing it on
    every write re-sent 133 KB to say exactly what it said before. The
    surfaces that show it refresh it when they open, and it carries its own
    staleTime.
  - ["text"] is scoped to the named path. The live URL is a pure function of
    the path (fileURLFor), so the key is knowable at the frame; a ?v= URL is
    content-addressed and cannot go stale at all.
  - The tree and history coalesce behind a 2s window, so a sync push or a
    multi-file agent run costs one refresh instead of N. The tree is the
    single biggest response the hub serves.
  - Per-path bodies are deliberately NOT delayed. An open file updating is
    the thing a reader actually notices, and one body is small.

History needs no "only when mounted" gate: invalidateQueries refetches ACTIVE
queries and only marks inactive ones stale, so a history view nobody has open
already costs nothing.

Not done, and recorded as deferred rather than skipped: patching the tree
cache instead of refetching it. A frame carries paths, not the new
size/time/author, so patching needs the hub to enrich the frame — a server
change with a permissions question attached, since a frame must not name a
path its reader cannot see. The criterion is met without it.

e2e: a second account writes one file and the first client's request count for
that frame is asserted — zero heat, at most one tree, at most two requests
total. Verified failing without the change. Full hub suite: 260 passed, zero
failures, the first entirely green run of the day.

Stage 3 of docs/network-efficiency-prd.md.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ssowonny
ssowonny merged commit d54e9fb into main Sep 20, 2026
3 checks passed
@ssowonny
ssowonny deleted the perf/narrow-fanout branch September 20, 2026 06:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant