Repository navigation
Find why trail sync crashes a laptop, and fix it - #736
anirudhkumar-nanonets wants to merge 1 commit into
Conversation
🌱 graft blast radius4 areas changed → 9 areas can be affected. 29 dependent symbols, depth 2. flowchart TB
A0(("Hook System<br/>13 symbols"))
A1(("Graph Freshness<br/>4 symbols"))
A2(("Cloud Pairing<br/>3 symbols"))
A3(("Note Storage<br/>2 symbols"))
A4(("Initialization<br/>2 symbols"))
AX(("4 smaller areas<br/>5 symbols"))
classDef reached fill:#D9EDF3,stroke:#3AA7C9,stroke-width:1.5px,color:#0E313C;
class A0,A1,A2,A3,A4 reached;
classDef tail fill:#EEF2F3,stroke:#9AA4A9,stroke-width:1px,color:#3A4247;
class AX tail;
Who knows this code — 5 people across 13 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 29 dependent symbols, grouped by areaHook System — 13 symbols in 12 files
Graph Freshness — 4 symbols in 1 file
Cloud Pairing — 3 symbols in 3 files
Note Storage — 2 symbols in 2 files
Initialization — 2 symbols in 2 files
Session Telemetry — 2 symbols in 2 files
CLI Skills — 1 symbol in 1 file
Learning Sessions — 1 symbol in 1 file
MCP Tools — 1 symbol in 1 file
Test signal per changed area — 2 ✓ · 2 –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.
50 test suites also reference this code50 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: On a big repo like assign, trail could run two or three full code-map builds at once in the background, each peaking above 2 GB of memory and using a CPU core or more.
Why: The build lock counted as abandoned after 5 minutes, even with its build still running, so on a slow laptop the next trail query started another build beside it.
Fix: The lock now names the process doing the build and is taken over only once that process is gone. Background builds run at the lowest CPU priority, and a build now reports when another was already running.
Moves: active users, people with
session_summaryin the last 7 days (PostHog db 11,posthog_event_full_view): 7,214 on 2026-10-29 (996 of them from the trail CLI).What changed
src/util/state.ts: a lock is reclaimed only when its owner pid is gone and it's pastLOCK_STALE_MS(5 min), or when it's past the newLOCK_MAX_MS(15 min) whoever holds it. 15 min is longer than any build's own limit (a first build gets 10), and it covers a reused pid.lockHeldInuses the same rule.src/claude/sync-run.ts: writes its own pid into the lock before building (claimLock). Whoever took the lock (a query, the Stop hook) exits at once, so its pid said nothing about whether a build was running. It also setsPRIORITY_LOW; thebuildchild inherits it.build_completedgetsoverlap(src/cli.ts,src/telemetry/contract.ts,TELEMETRY.md): true when another live process held this map's lock as the build started. Nothing recorded overlaps before. It can't see two worktrees building at once, since each worktree has its own lock..trail/learnings/2026-10-29-on-a-big-repo-every-trail-anirudhkumar.md.Measured (copy of assign, 12,281 files, 12-core Mac, 24 GB)
19865ff4)Three
trail askcalls 4 s apart on a repo with no map, run the way the prompt hook runs them (GRAFT_FIRST_BUILD_WAIT_MS=0). A build here takes 23 s, so the lock's mtime was set 6 minutes back before the 2nd and 3rd query, which is what a laptop's lock looks like once its build passes 5 minutes. At nice 19 an idle-machine build took 22 s vs 23 s before.What this does and doesn't explain
The report (Slack DM, 29 Oct 12:43 IST) came from an install on 0.26.0, working in assign: a first build at
trail init, 14 end-of-turn builds of 5–30 s on 27–28 Oct, no failures, last event 28 Oct 13:53 IST. 0.26.0 predates the first-query build (#704), and on it every background build is killed at 2 minutes, so its lock never reaches 5 minutes. This bug can't be what hung that laptop. It protects 0.27+.What more likely hung it is memory. assign's map is a 98.6 MB
wiring.json, and every process that loads it holds about 1.2 GB:trail askfrom the prompt hook after an edit: 1.29 GB peak, 2.3 sEstimate: three sessions open on assign hold ~3.7 GB, and each turn end adds ~2.2 GB. This PR makes that build yield the CPU but doesn't lower memory (follow-up 1).
Follow-ups
build_completedandquery.Tests
npm testwith a scratch HOME: 4125 pass, 9 skipped, 4 fail. Cleanmainthe same way: 4123 pass, 3 fail. None of the failures come from this change: theupkeep-hooks"machine without trail" test fails on main too, and the 3trail-scenario-hookstests pass 9/9 in a fresh HOME but fail on a second run in the same HOME (they leave state there), which is how they ran here. Main's other 2 failures pass on this branch. New tests cover a live owner keeping an old lock, a gone owner's lock being reclaimed, the 15-minute cap,lockHeldByOther, and sync-run owning the lock during its build.trail checkflags 3 conflicts with the 27 Oct learning's "Reusing sync-run's 120s limit for a first build". That doesn't happen here: the first build keeps its 10-minute limit. The check matches on the words "sync-run", "first build" and "limit".🤖 Generated with Claude Code