what your agent sees
what counts as a conflict
Two questions, asked of every edit.
Same file? The original test, and still the first one. Someone else's agent wrote this exact path inside the window (default 30 minutes) — you should know before you touch it.
Same symbol? The one that costs a team an afternoon. Alice changes the signature of an
exported function in src/models/user.ts. Bob's agent is three files away in
src/api/handlers.ts, building against the old signature. No path matching can see that,
because there is no shared path. AgentMash sees it because the hooks exchange symbols:
- PostToolUse works out which exported declarations an edit changed. An edit inside a
function body never repeats the
export functionline, so the hook reads the file it just wrote and walks back from the edit to the declaration that encloses it. - PreToolUse works out what the file you are about to edit is bound to — everything it imports, plus everything it exports — and sends that with the check.
- The server matches one against the other, so a change to
createUserreaches every file that importscreateUser, whatever directory it lives in.
Extraction runs entirely on your machine, in the hooks, with no dependencies and no parser:
comments, string bodies and template-literal bodies are blanked out, and then a careful pass
over lines that begin with export reads what is left. The server never sees your code —
only a list of names.
The harness behind every number below is committed, and it prints its per-file rows, every name it missed and every name it claimed wrongly, so you can check the totals against the data rather than trusting them:
npm run bench:symbols # this repo's own 51 git-tracked source files
npm run bench:symbols -- --deps # ...plus everything in node_modulesMeasured against the compiler's parse of the same files: 594 of 594 symbols on this repo's
own source (100%), and 34,000 of 34,594 across 7,506 files including node_modules (98.3%) —
with no false positives in either. Cost is measured cold, in a freshly spawned process per
measurement, because a hook is always a fresh process and extraction is never warm: p50
2.2 ms on this repo's files, 12-14 ms on its largest (108 KB), ~19 ms on a synthetic 400 KB
file, and 0.2 ms for a 9 MB file the size ceiling declines to read. The Node process the hook
runs in takes ~57 ms to start and read its config before any of that (npm run bench:budget,
section B).
Ground truth is the TypeScript compiler already in this repo's devDependencies — bench/ is
not part of what the hooks install, which stay dependency-free.
Every holder the server returns carries a confidence:
| What it means | |
|---|---|
high | Both edits touch the same exported symbol, and it is a name specific enough to mean something. The one you want to read. |
medium | Paths match and at least one side sent no symbols — all v0 ever knew. |
low | Paths match, both sides sent symbols, and nothing they share is evidence: two agents at opposite ends of one file, or an overlap made only of names like config. |
That last row is why AGENTMASH_SCOPE=dir stopped crying wolf: a same-directory neighbour
that provably shares no symbols is no longer reported at all.
When it can't tell, it says nothing extra. A language the extractor doesn't read (.py,
.go, .rs), a file too large to open on the edit path, an edit it can't locate — each of
these sends no symbols, and detection falls back to exactly the path matching described
above. Extraction failures cost you recall, never a false alarm.