Repository navigation
Cut trail's peak memory on big repos by a third - #737
anirudhkumar-nanonets wants to merge 1 commit into
Conversation
🌱 graft blast radius2 areas changed → 4 areas can be affected. 7 dependent symbols, depth 2. flowchart TB
A0(("CLI Graph Refresh<br/>3 symbols"))
A1(("MCP Server Integration<br/>2 symbols"))
A2(("Graph Building<br/>1 symbol"))
A3(("Pull Request Review<br/>1 symbol"))
classDef reached fill:#D9EDF3,stroke:#3AA7C9,stroke-width:1.5px,color:#0E313C;
class A0,A1,A2,A3 reached;
Who knows this code — 3 people across 6 areas
Ownership is git history over each area's own files, weighted towards recent work (120-day half-life). Merge commits and bots are dropped, and you are dropped from your own PR. A name with no All 7 dependent symbols, grouped by areaCLI Graph Refresh — 3 symbols in 2 files
MCP Server Integration — 2 symbols in 2 files
Graph Building — 1 symbol in 1 file
Pull Request Review — 1 symbol in 1 file
Test signal per changed area — 1 ✓ · 1 ⚠Reached = a node under a test path has a resolved edge into the changed symbol. It undercounts anything called indirectly — through a CLI, a spawned process or a dynamic import — so read a low ratio as “look here”, never as a coverage gate.
42 test suites also reference this code45 symbols, kept out of the diagram and the table so they cannot crowd out the areas a reviewer has to look at.
Open the interactive graph → — click an area to see its dependent symbols at file:line. |
Issue: In a Claude Code session on assign, trail's processes used up to 4.2 GB at once, enough to make a laptop hang while trail syncs.
Why: The MCP server rebuilt the code map inside itself after edits and kept about 2 GB for the rest of the session. Every query also copied the whole search index, and one non-ASCII character made each map file twice as big in memory.
Fix: Rebuilds run in a short-lived process that gives its memory back when it ends, queries keep only the words they look up, and the map files are written as plain ASCII. One turn's peak drops to 2.9 GB, with the same answers.
Moves: active users, people with
session_summaryin the last 7 days (PostHog db 11,posthog_event_full_view): 7,214 on 2026-10-29, read 13:10 IST (996 of them from the trail CLI).Second PR for the reported hang, after #736 (lock takeover, which can't happen on the reporter's 0.26.0). This one is the memory, and every path it changes is also in 0.26.0.
Measured: one turn on assign, median of 3
The MCP server answers a query,
trail askruns as the prompt hook runs it, the agent edits a file and queries again, thentrail buildruns as the end-of-turn sync does. Copy of assign (88k symbols: 94 MB map, 70 MB search index, 255 MB extract cache), 12-core Mac, 24 GB. Before isorigin/main(19865ff4).trail ask(prompt hook's child)Same answers:
trail ask --jsonis byte-identical on 8 queries,ask-index.jsonis byte-identical, andwiring.jsonand the extract cache read back the same values.What changed
src/graph/refresh.ts+ newsrc/graph/refresh-run.ts:ensureFreshGraph's drift rebuild runs in a child process when the compiled script ships next to it (from source, i.e. the tests, it still builds in process). The query waits for it as it waited for the in-process build. A signal that ends the query kills the child before releasing the lock.src/ask/ask.ts: a sidecar doc keeps only the query's terms and the plural formshasTermchecks, plus its body length.--inkeeps whole bags, since its idf comes from them.src/graph/write.ts(asciiJson),src/graph/extract-cache.ts: non-ASCII written as\uXXXX. One such character made V8 hold the whole file as a two-byte string: ~100 MB more per map read, ~300 MB per extract-cache read. The extract cache is also written one file's entry at a time instead of as one 255 MB string.src/ask/index-file.ts: the search index is written entry by entry, with the same bytes.Kept away from
src/cli.ts,src/telemetry/contract.ts,src/claude/format.ts,src/notes/notes.tsand #736's files.Tests
npm testwith a fresh scratch HOME: 4129 pass, 9 skipped, 1 fail. The one failure isupkeep-hooks"a repo graft set up, on a machine without trail", which fails on clean main the same way.trail checkflags 2 conflicts with the 27 Oct learning that ruled out "building synchronously inside the query". That learning is about the first build of a missing map, which is unchanged (still detached via sync-run). The drift rebuild was already awaited by the query before this PR; it now runs in a child, and a hook timeout still ends it and frees the lock, as before.Follow-ups
[word, count]pairs); a word-to-documents format would be about a tenth.build_completedandquery.🤖 Generated with Claude Code