Multi-agent & multi-project testing
Reticle is built for the messy real world: several apps running at once, ports that shift between runs, and many agents driving different flows of the same app in parallel — without each one spinning up its own Chromium. This page explains how that works and how to use it.The mental model
- One daemon per machine.
reticle mcpdiscovers a running daemon (via~/.reticle) or starts one. A crashed daemon’s stale pidfile is reclaimed automatically, so you never chase “port already in use” or a zombie server. - Identity is the app, not the port. The build plugin stamps a stable
projectIdthat travels in every connection. If your Next app usually runs on:3000but boots on:3001today, Reticle still knows which app it is — and an agent scoped to project A will never accidentally drive project B’s tab. Origin is only a fallback hint. - One browser, many contexts. When agents need their own headless tabs, the daemon’s browser pool launches a single Chromium and hands out isolated contexts (one per flow) — cheap, and capped so a big fan-out can’t exhaust the machine. Over-cap requests queue.
- Attach-only. Reticle never starts your dev server. It connects to an app you’re already running (or opens a headless tab pointed at it).
Manual testing — ~5 minutes
- Add the plugin (Vite/Next) or one
reticle.connect()call. (See getting-started.) - Start your app as you normally do.
- Open it in a browser — the in-page panel shows Reticle is connected.
- Click around; flag anything that looks wrong with the “Flag a bug” annotator. The agent drains those with
reticle_session {action:"review"}.
Agent testing — ~2 minutes
With the app running and instrumented, an agent drives a flow end to end:reticle_lease {action:"acquire"} opens a fresh isolated headless context against your already-running app, stamps the lease identity into the URL so the app’s own SDK registers under a sessionId you can target, and waits until that tab has connected (ready: true) before returning — so the sessionId is usable immediately. Release when the flow finishes.
10 agents, 10 flows, one dashboard
This is the design target, and it needs no special setup:- Each agent calls
reticle_lease {action:"acquire"}for the same dashboard URL → its own isolated context (own cookies/storage) in the one shared Chromium. - The pool caps simultaneous contexts (
RETICLE_MAX_CONTEXTS, default scales with CPU under a ceiling); extra acquires queue and proceed as slots free. - Flows can’t bleed into each other — contexts are isolated and every session is scoped by
projectId. - A single crashed page is reclaimed on its own, and if an agent crashes or hangs its lease stops being touched and the lease reaper reclaims the context after a TTL, freeing the slot. One dead agent never starves the others.
reticle_sessions lists everything with projectId (group by app) and leased (pool context vs a human tab), so an orchestrator can see the whole fleet at a glance.
Knobs
Why not just open many browsers?
Ten Chromiums is hundreds of MB each and will thrash a laptop. Ten contexts in one browser is a few MB apiece and fully isolated — same correctness, a fraction of the cost. That’s the whole point of the pool. Measured (bench/harness/multi-agent-throughput.mjs): 16 verification flows that take 35.4s one-at-a-time finish in 5.2s across 8 leased contexts on a single Chromium — 6.78× faster, ~30s saved per batch, with all 8 contexts live at peak. The speed-up scales with agent count up to the cap; the win over launching a browser per agent grows with how much per-agent browser startup you avoid.