For people working on Reticle — and for an agent asked to explain why a flow behaved the way it did. If you are debugging your app with Reticle, you want usage.md instead.Reticle produces four different signals and they answer four different questions. Reaching for the wrong one is why investigations here have historically started by reading source.
The daemon log
The daemon writes newline-delimited JSON to:reticle doctor prints the exact path for your port. Tail it while you work:
Verbose flow tracing
Off by default, because a trace on every tool call is a cost on the hot path — a verification loop is 50–200 calls. Measured, so the claim is checkable: a disabled span costs 126ns per site (against ~9ns for a bare call), which is under a microsecond per tool call and not the literal zero it is tempting to write. Turn it on for the daemon (the flag is read per call, but the daemon is the process doing the work, so it has to be set where the daemon starts):callIdgroups every stage of one tool call, and is prefixed with the daemon’s pid so two daemons (or one that restarted) can never claim the same id. Several agents can be inside the daemon at once, so lines interleave — the id is the only thing that makes the output a tree instead of a pile.depthis the nesting level.0is the tool handler; anything deeper ran inside it.msis that stage’s own wall-clock. A parent’smsincludes its children’s.ok:falsecarries anerrorfield. A stage that threw is still traced — otherwise the trace would show a call that entered a stage and never left, which reads as a hang.
Where the time goes
The spans that ship today:
The first two answer “is this slow because of us or because of the app under test?” —
browser.command close to tool.handler means the app is taking the time. A large gap between them is Reticle’s own overhead, and that is a performance bug of ours.
Filter to one call:
The init flow
reticle init is synchronous end to end, so it uses spanSync — same line, same tree:
claude mcp add when MCP registration is on. And when the pinned install is refused, the unpinned retry is a second full package-manager run — init legitimately takes about twice as long on that path. Every --local fixture run takes it, because the version being installed is not published yet.
Adding a span
AsyncLocalStorage, so a span added five frames down still lands in the right tree without changing anyone’s signature.