Skip to main content
How a tool call gets from a coding agent to your app and back, what each hop can do wrong, and which of those failures are silent. Written for anyone touching the transport, the daemon, the bridge, or a gate — and for contributors trying to work out where their change lives. Companion pages: harness-rules.md (what a gate must do about all this) and telemetry-contract.md (the rules for anything that emits).

1. Topology — 5 processes, 4 hops

Each hop has its own failure vocabulary, its own recovery, and its own way of lying. Nothing stated this end to end until this page, which is why gate results have been contradictory.

2. Connection establishment

Sequencing hazard: steps 6 and 7 race. The product absorbs it internally — the first live tool call blocks briefly for a session to appear rather than failing — but a gate that boots an app and immediately asks whether a session exists is outside that protection and has been burned by it. Poll for the session; never sample once.

3. The tool graph — what produces what

Three different counts, all measured, and the difference between them matters: 68 → 48 is family folding: reticle_baseline, reticle_session, reticle_record, reticle_flow and reticle_lease each absorb their members behind an action parameter. So “every tool is callable” is asserted over 48 surfaces, not 68 behaviours — a family member reachable only through an action value the sweep never passes is not covered by it. Worth closing when the per-tool budget lands (see gate-plan.md, Phase 4). Two structural facts:
  • reticle_run is a second dispatch path. It routes through runTool, so telemetry is intact, but anything decorating results above runTool silently does not apply. The session lease is the instance we know about; the next decorator inherits the same bug.
  • record → save → verify → heal is the only model-free loop, and it is what makes a cheap gate possible at all.

4. Fragility inventory

Silent = fails with no error surfaced to the agent or the user.

Hop 1 — client ↔ proxy

Hop 2 — proxy ↔ daemon

Three distinct populations of unanswered call, all covered: Still fragile:

Hop 3 — daemon lifecycle

Hop 4 — bridge ↔ SDK

Hop 5 — SDK internals

5. Where the gates are

Nine of eleven layers have a gate. The two that do not are exactly the two that need a second repo and a human. The plan for both is in gate-plan.md.

6. Traces already encode the invariants

runTool opens one tool.handler root span per call and every stage underneath inherits it. With RETICLE_TRACE=1 that makes three assertions available from any existing battery run, at the cost of one log parser:
  • exactly one root span at depth: 0 per tool call
  • zero orphan spans — 23 orphans on one healthy run is the documented signature of a hung call
  • settleTiming always fires; it sits in a finally so a thrown call cannot leak a concurrency slot
Today that output is produced and discarded.
Last modified on August 14, 2026