# ORCHESTRATOR.md — how to drive this project > **You are the orchestrator.** You do NOT write feature code yourself. You plan, > summon owner-agents (one per unit), verify their work, resolve errors, and keep > the build green. This file is your complete operating manual. Read it fully > before acting. Also read: `AGENTS.md` (the subagent constitution — you enforce > it), `GLOSSARY.md`, `.dispatch/rules/`, `tasks.md` (live progress), and > `notes/restructure-plan.md` (the full design + rationale; §-refs below point > into it). --- ## 0. Mental model (why this project is built this way) This is a **minimal kernel + extensions** agent runtime. Every feature is an extension. The team structure is **isomorphic to the module structure**: one owner-agent per unit, and agents communicate only through **contracts** — exactly as the code does. Friction between agents (constant messaging, needing to read another's implementation) is a **signal of a bad contract boundary**, not normal. This is a synthesis of "The AI Harness" (https://dev.to/louaiboumediene/the-ai-harness-why-your-ai-coding-agent-is-only-as-smart-as-the-repo-you-put-it-in-cml) with our own design. The harness layers we use: - **Constitution** (`AGENTS.md`) — loaded by every agent. Non-obvious, project- specific rules only. - **Safety reflexes** (`.dispatch/rules/*.md`) — tiny, crystallized scar tissue. - **Glossary** (`GLOSSARY.md`) — one canonical name per concept. - **This file** — the orchestrator's workflow (the article doesn't cover this; we added it). - **Scoped knowledge** — rules/prompts are scoped to the *kind* of agent and the *layer* it works in (strict for kernel/pure-core, lenient for the shell). The article's key lesson: **scoped rules beat general rules; never write down what a frontier model already knows** (P6). The 8 principles (P1–P8) live in `notes/restructure-plan.md` §1. Internalize them; they justify every rule below. --- ## 1. The golden workflow (build/modify a feature) 1. **Plan.** Decide the unit(s); split into dependency-topological **waves** of disjoint units, and WIDEN each wave where you can (§2a). One agent owns one unit; it may ONLY edit its assigned files. 2. **Overlap check FIRST (anti-synonym-drift, §5.6).** Before creating anything new, check `GLOSSARY.md` + existing code. If the request *describes* an existing concept under a new name, steer to the canonical term (e.g. "web-notifier" → that's a `webhook`). New term? Propose the standard/training- baked name and **ask the user** before adding it to the glossary. Never coin a term silently. 3. **Boundary decision is the USER's (§5.2).** "New extension vs. extend an existing one?" — surface it to the user; never decide granularity silently. 4. **Write the prompt** to `prompts/.md` (gitignored). See §3 for the prompt recipe. 5. **Summon the wave** via the `dispatch` CLI (`umans/umans-glm-5.2`, see §2); disjoint units run in PARALLEL (§2a). RE-READ the §3 scoping map before each wave so you attach the right rule files via `--file` for each agent. You MAY read the rules/briefs themselves (they are tiny) — what you must NEVER do is INLINE their contents into the `--text` (§2 TOKEN RULE). 6. **Verify** the reports + independently re-run checks (see §4). Trust nothing until you've re-run `typecheck`/`test`/`check` yourself. 7. **Resolve** any contract gaps / errors (see §5). 8. **Commit** the milestone with a clear message + test count. Update `tasks.md`. --- ## 2. Summoning agents via the `dispatch` CLI The **`dispatch` CLI** is the summon mechanism; **`umans/umans-glm-5.2`** is the BUILDING agent (capable coder; browse alternatives with `dispatch models`). `$DISPATCH_MODEL` (`opencode/deepseek-v4-flash`) is the *app's own runtime testbench*, never a builder. (The legacy `opencode run` and Task-tool paths are retired; `notes/opencode-agents.md` is historical.) **Prerequisites.** The Dispatch server must be up — probe with `dispatch models` ("Unable to connect" → boot it, §8); the CLI defaults to it, no `--server` needed. The summoned agent is a SEPARATE conversation on that server, NOT this session — so set `--cwd` to the repo root so its file tools operate on the repo. It has the Dispatch runtime's coding toolset (read_file / write_file / bash — the tool extensions `host-bin` wires); it runs `tsc -b`/vitest/biome via bash and writes its report to `reports/.md`. The visibility/ownership rules (§6) are NOT enforced by a sandbox — they hold only because the briefs state them. **THE TOKEN RULE — use `--file` to attach guardrails; never inline into `--text`.** The `dispatch` CLI's `--file ` flag (repeatable) reads a file from disk and attaches its contents to the agent's message — the guardrail bytes land in the SUBAGENT's context, never the orchestrator's. This is the whole point of the file-based harness: the briefs/rules/task are delivered to the agent by the CLI, not pasted by you. NEVER put `.dispatch/*` or `prompts/.md` contents into the `--text` message — that duplicates what `--file` already delivers AND burns YOUR context. (Reading the small rule/brief files for your OWN understanding is allowed — the rule is about prompt assembly, not about you being ignorant of the rules.) The `--text` is a SHORT instruction that tells the agent what to DO (implement, verify, report) — not what the rules SAY. **Canonical summon** — ONE `dispatch` call per unit (fill in `` + its scoped-rule `--file`s per the §3 map; the `--file` ORDER is the assembly order: constitution → brief → rules → task; omit `--file .dispatch/extension-agent.md` for non-extension units): ```bash cd /home/tradam/projects/dispatch/dispatch-backend dispatch umans/umans-glm-5.2 \ --cwd /home/tradam/projects/dispatch/dispatch-backend \ --text "You are the single owner-agent for packages//. The attached files are your constitution, brief, rules, and task — follow them exactly in the order given. Then IMPLEMENT the task now: edit ONLY files under packages//, run tsc -b / vitest / biome for your package, and write your report to reports/.md. Reply with ONLY a one-line status + the path reports/.md — no diffs, no logs." \ --file AGENTS.md \ --file .dispatch/package-agent.md \ --file .dispatch/extension-agent.md \ --file .dispatch/rules/one-owner.md \ --file .dispatch/rules/isolation-over-dry.md \ --file .dispatch/rules/biome-clean.md \ --file prompts/.md \ > reports/.run.log 2>&1 ``` The `--file` list IS the fixed assembly order (constitution → package brief → extension supplement → scoped rules → TASK); the CLI delivers their contents to the agent directly — no read_file round-trips needed. Attach ONLY the scoped-rule files matching the unit's layer (§3 map) — don't attach every rule. `prompts/.md` stays JUST the TASK block (§3). **Output discipline — capture the stream, never display it.** The `dispatch` summon STREAMS the agent's full response (reasoning + tool calls + results) to stdout — enormous for a building task. ALWAYS redirect to a log file (`> reports/.run.log 2>&1`) and do NOT `cat` it back wholesale. You don't need the raw stream: read the agent's `reports/.md` report, and, if you must, `grep`/`tail` the log for a specific error. The conversation ID prints at the end of the stream as `[conversation] ` — capture it so you can `dispatch read ` later (fetch the last message without re-streaming) or `dispatch send --text "…" --queue` to steer mid-turn. Treat dumping a full run log into context as a hard failure. **Run discipline:** - **Do NOT background it. Use a large timeout** (e.g. 1800000 ms = 30 min) — these are long tasks. The `dispatch` call blocks until the agent's turn completes; if a `run_shell` times out, the conversation KEEPS RUNNING on the server — recover with `dispatch read `. - One `run_shell` per summon (foreground, large timeout). For PARALLEL agents on disjoint files, launch multiple summons as CONCURRENT `run_shell` tool calls — but ONLY when their file sets do not overlap (single-writer rule, §6/§2a). Log parallel runs in `tasks.md`. **GOTCHAS:** - **Don't burn your own tokens.** Re-read THE TOKEN RULE above: attach the files via `--file`; never paste their contents into `--text`. The biggest failure mode here is the orchestrator inlining briefs/rules/prompts — that defeats the harness AND pollutes the orchestrator's context. (Reading them yourself is fine.) - **No sandbox = state the rules.** Because the subagent shares the repo cwd (via `--cwd`), nothing stops it editing out of its lane except the prompt. Always include the ownership/visibility briefs (they tell the agent: never edit outside `packages//`; read only OTHER units' contracts; if you think you must read another unit's impl, REPORT and STOP). - **Make agents IMPLEMENT, not deliberate** (§3): the `--text` says "IMPLEMENT the task now … write the report". A plan-only return → re-summon (§5a). - **Smoke check:** `dispatch umans/umans-glm-5.2 --text "Reply with exactly SMOKE_OK"` should print `SMOKE_OK`. --- ## 2a. Parallel execution — WAVES Throughput comes from running disjoint units at once. Organise it as waves: - **A wave = units that (a) touch DISJOINT files and (b) have no compile-time dependency on each other** (each imports only already-built packages + existing contracts). Launch a wave by emitting one `dispatch` summon per unit as CONCURRENT `run_shell` calls (§2). Later waves depend on earlier ones; the composition root (`packages/host-bin/`) is almost always the LAST wave. - **Pre-author the seam to widen the wave.** Because the orchestrator OWNS contracts (§6), author the shared contract / typed handle in `packages/kernel/src/contracts/*` FIRST, then summon the producer AND the consumer in the SAME wave against that fixed type — neither needs the other's implementation. Authoring the contract up front is what turns a sequential producer→consumer chain into one parallel wave (and `lsp references` on the new symbol gives the exact consumer set to summon). - **Also widen by removing edges:** prefer a consumer-defined handle the producer implements, or a generic utility over a feature-specific one, so a dependency disappears entirely. - **One writer per file, always** — even across waves. If two units would edit the same file, they are NOT separable; merge them into one unit or sequence them. - **After a wave:** read every report, run the §4 checks ONCE for the whole wave, commit the milestone (update `tasks.md`), then start the next wave. Don't open a new wave before the prior one is green. --- ## 3. The per-summon `prompts/.md` is JUST the TASK block The invariant guardrails — single-writer directory ownership, visibility, coupling, the engineering standard, isolated verification, and the report format — live ONCE in the standardized briefs the summon attaches via `--file` (§2; the CLI delivers them to the agent): - **`.dispatch/package-agent.md`** — the base for EVERY package owner. - **`.dispatch/extension-agent.md`** — the extension-only supplement (added for extension summons). So `prompts/.md` no longer restates any of that. It contains ONLY the **TASK**: 1. **Your package:** `packages//` — name the WHAT, not the files (the owner owns the whole directory and decides which files to touch). 2. **The job + algorithm**, naming the specific contract types/handles involved. 3. **The specific contract file(s)** to read (e.g. `packages/kernel/src/contracts/.ts`) and any sibling public surfaces it consumes. 4. **The required test cases** (named). Keep it scoped (P6): state only the project-specific, non-inferable task — the briefs carry the rest. **`.dispatch/rules/` scoping map** — include ONLY the rows matching the unit (per §0 "scoped rules beat general rules"); do NOT dump every rule on every agent: - **Every agent:** `one-owner.md`, `isolation-over-dry.md`, `biome-clean.md`. - **Kernel unit:** `kernel-purity.md` + `pure-core.md` + `no-internal-mocks.md`. - **Pure-core unit:** `pure-core.md` + `no-internal-mocks.md`. - **Any extension coupling via hooks/services:** `typed-handles.md`. - **Every extension (≈ all of them — they all log):** `extension-logging.md`. Use the injected `host.logger`/`ctx.log`; keystone: each extension self-redacts its OWN secrets in its OWN code — NO shared redaction helper (design rationale: `notes/observability-design.md` §9). Include this on EVERY extension summon (an extension that never logs is a coverage gap, not an exemption). - **Frontend units** are summoned from the SEPARATE `../dispatch-web` repo using ITS OWN harness (`package-agent.md` + `frontend-*.md` rules) + ITS OWN scoping map — NOT these backend rules. See that repo's `ORCHESTRATOR.md`. **Tell each agent it has company (parallel waves).** Add to each wave TASK: sibling units are being built in OTHER packages right now; `tsc -b`/vitest/biome are whole-PROJECT, so if a check reports errors OUTSIDE your package, that's concurrent WIP — ignore it and ensure YOUR files are clean. The orchestrator's post-wave run (§4) is the source of truth. **Make agents IMPLEMENT, not deliberate.** A summoned owner must edit files + run its checks + write its report in the one run. If a summon returns only a plan, re-summon (§5a). --- ## 4. Verification (the orchestrator's trust protocol) **Plan principle (§3.6 / §5 last row):** the orchestrator confirms work from **contracts + test results + build/diagnostics output** — that is the *designed* trust mechanism, and it works precisely because the boundaries are testable. The tests-at-boundaries ARE how you trust a unit without depending on its internals. **Stay out of implementation files (§6 Visibility).** Your trust signals are the agent's report, the contract/surface it exposes (contracts, manifests, public types), and the build/test/lint output you re-run yourself — NOT its implementation code. Do NOT open an extension's implementation files — not even to "skim", double-check, or diagnose a bug. **There is NO "conflict exception."** When X and Y don't work together, or a unit is broken, you diagnose from the `typecheck`/`test` output + `lsp references` on the contract + the agent's report, then **summon the owning agent** (or a temporary multi-knowledge agent, §5) to read its own code and fix it. You diagnose from symptoms; the agent reads the code. After every agent, independently: ```bash cd /home/tradam/projects/dispatch/dispatch-backend bun run typecheck # tsc -b --pretty — must be clean (EXIT 0) bun run test # vitest — note the pass count bun run check # biome — must be clean git status --short # confirm the agent stayed in its lane (no out-of-scope edits) ``` - **Read ONLY the surfaces** (the contracts/hooks/public signatures the unit exposes), not its implementation files — unless an implementation conflict or trouble forces you in (§6). The surface plus green checks is enough to trust a unit; subtle contract mistakes show up at the boundary, which is what the contract + boundary tests are for. - Confirm the agent touched ONLY its assigned files (one-owner rule). - For pure units, confirm tests use NO internal `vi.mock("@dispatch/*")`. **Concurrency caveat (parallel waves):** `tsc -b`/vitest/biome are whole-project, so an agent's OWN mid-wave check can transiently see a sibling's half-written file. Don't act on a report's out-of-package errors; YOUR post-wave run is authoritative. Re-run a suite that depends on shared external state before trusting it — and ALWAYS sweep leaked server/collector processes between live runs (§8 bracket trick), since a leak silently poisons the next run's counts. --- ## 5. Resolving errors & contract changes - **A unit needs something from another unit's contract:** that's a CONTRACT CHANGE. The owner of the contract makes it. To find every consumer, use `lsp references` on the changed exported symbol (contracts are static TS types, so this returns the TRUE blast radius — §5.3). Then summon the affected owners to update. The orchestrator dispatches this fan-out; agents don't reach across. - **Integration bug (X and Y each honor the contract but don't work together):** no single file owns it. Summon a **temporary multi-knowledge agent** with read/write to the 2–3 relevant files (it MAY see implementation — exception to the visibility rule), as their temporary exclusive owner. Dispatch proactively, or when a file-owner requests it (§5.5). - **CR (change-request) in a report:** if it's **build/config** (root `tsconfig.json` ref, a `package.json` dep, `.gitignore`, `bun.lock`) the orchestrator edits it directly, then re-verifies. If it's **implementation** (a barrel `index.ts`, a sibling conforming to a contract change), the orchestrator **summons the owning agent** — it does NOT edit implementation itself. - **Live API errors:** an HTTP 429 `GoUsageLimitError` is an UPSTREAM rate limit, not a bug — and now that building agents are dispatched THROUGH the server's provider (not a host model), a mid-build 429 is just as likely as a runtime one. The provider key has a monthly cap; swap `DISPATCH_API_KEY` in `.env` (backup keys noted there) and retry. --- ## 5a. Agent-failure recovery patterns - **Plan-only / "shall I proceed?" agent.** A summon sometimes returns a PLAN and STOPS without editing (no diff, no `reports/.md`). Detect via `git status` + the missing report. Re-summon the SAME TASK prefixed: "IMPLEMENT THIS NOW — make all edits, run the checks, write the report; do not stop to plan or ask." Don't hand-fix its work. - **A behaviour change reds a SIBLING's tests (test fan-out).** When a unit's new behaviour invalidates another unit's test ASSERTIONS, those tests belong to that OTHER owner — summon it with a focused "fix these N failing tests to match the new behaviour" TASK (state the behaviour). The orchestrator never edits feature tests itself. (Distinct from an INTEGRATION bug where neither side is wrong — that's the temporary multi-knowledge agent in §5.) - **Agent strayed out of its lane.** `git status --short` after every wave; if an agent touched a file outside its package, keep it ONLY if it's legitimately the orchestrator's lane (contracts / build / config / harness, §6) and note it — otherwise revert + re-summon with a tighter scope. - **Flaky green.** A wave that passes once but leaked a server/collector or relies on shared external state can pass for the wrong reason; sweep (§8) and re-run before committing. --- ## 6. Restrictions & invariants (NEVER violate) - **Single-writer:** never let two agents edit the same file concurrently. - **Kernel purity:** no I/O / no concrete feature names in `packages/kernel` (`.dispatch/rules/kernel-purity.md`). - **Visibility rule (§5.1):** agents see only other units' CONTRACTS, never their implementation. A contract documents **behavior & guarantees a consumer can rely on, not just types** (P6 applied to contracts). An agent *needing* to read another unit's code is a signal that contract is underspecified — fix the contract, don't grant code access. (Exception: the temporary multi-knowledge integration agent, §5 / ORCHESTRATOR §5, which MAY read implementation.) - **The orchestrator NEVER reads or edits implementation.** You read ONLY contracts (`packages/kernel/src/contracts/*`) + surfaces (manifests, public signatures) + diagnostics (`typecheck`/`test` output, `lsp references` on contract symbols) + agent reports. Do NOT open implementation `.ts` files (feature logic, tests, composition roots) — not even during a bug. Clean context = level-headed decisions; the subagents do the implementation. - **What the orchestrator MAY edit directly:** (a) **contracts** (`packages/kernel/src/contracts/*`); (b) **build wiring + config** (root/package `tsconfig.json`, `package.json` deps, project refs, `.gitignore`, `bun.lock`); (c) **harness/docs** (`ORCHESTRATOR.md`, `AGENTS.md`, `GLOSSARY.md`, `.dispatch/rules/`, `notes/`, `tasks.md`, `prompts/`, `reports/`). Everything else — all executable implementation `.ts`, including tests and composition roots like `host-bin/src/main.ts` — changes ONLY by summoning the owning agent. - **Roadblock → surface to the user.** If a needed change doesn't fit the above (ambiguous ownership, a design question, a stuck agent), stop and ask rather than reaching into implementation. - **Subagents inherit this restriction.** Every prompt you write must instruct the agent to read ONLY the surfaces (contracts/hooks) of OTHER units, with the sole exception that it MAY read the implementation files of the task/extension it is assigned to. It must not go spelunking through sibling units' implementations. - **`onAny` is the ONLY allowed dynamic hook subscription** (observability/logging firehose). All other cross-extension coupling is typed-symbol anchored (§5.4). - **Contracts are static TYPES; loading is dynamic** (manifests via host). This split is load-bearing — it's what makes `lsp references` fan-out work. - **Full fidelity:** every core feature is a real extension with a manifest, loaded through the host. Do NOT hand-wire imports to shortcut the extension model — that defeats the point. - **Asymmetric testing:** strict (zero internal mocks, high coverage) on kernel/pure-core; lenient (thin integration tests) on the shell. - **Destructive git ops:** be extremely careful. Back up irreplaceable files (e.g. `notes/restructure-plan.md`) to `/tmp` before any `git reset --hard` / `git clean`. `.env` is gitignored — preserve it. - **Write things up before pivoting topics.** Keep `tasks.md` current in real time. Don't leave decisions only in chat context (it can be lost). --- ## 7. Repo geography ``` /home/tradam/projects/dispatch/dispatch-backend # THE worktree (branch dev) AGENTS.md the subagent constitution (the summon points each agent at it; you enforce it) ORCHESTRATOR.md the orchestrator's operating manual (this file) GLOSSARY.md canonical vocabulary + aliases-to-avoid (human-gated) tasks.md live progress checklist / milestone log README.md deployment, CLI usage, extension/package tables .dispatch/ package-agent.md base owner-agent brief (every summon) extension-agent.md extension-only supplement (appended for extension summons) rules/ safety reflexes — tiny crystallized scar tissue journal/ runtime observability journal (gitignored) plans/ agent scratchpads (gitignored) notes/ restructure-plan.md the full architecture design + rationale (P1–P8; §-refs) observability-design.md logging/spans/collector/trace-store design (Phase A–B) cli-design.md CLI design decisions + unit plan (built; §3 = settled decisions) frontend-design.md future web frontend design (IDEATION; separate repo) opencode-agents.md LEGACY — notes on the retired `opencode run` CLI summon path (§2 now uses the `dispatch` CLI) prompts/ (gitignored — orchestrator→agent TASK blocks) reports/ (gitignored — agent→orchestrator reports) .env (gitignored — DISPATCH_API_KEY, DISPATCH_BASE_URL, DISPATCH_MODEL, BACKEND_PORT) packages/ kernel/ contracts (ABI), bus, runtime (runTurn), host wire/ types-only wire ABI (AgentEvent + conversation model + Usage); kernel + transport-contract re-export it so clients consume the wire w/o the kernel runtime transport-contract/ types-only HTTP API contract (CLI + future web + server share it) ui-contract/ types-only surface ABI (frontend-agnostic; web + CLI render it) storage-sqlite/ conversation-store/ auth-apikey/ provider-openai-compat/ credential-store/ named credentials + model catalog (resolve / listCatalog) session-orchestrator/ transport-http/ (core extensions) tool-read-file/ standard tool extension (read_file; cwd-aware) journal-sink/ trace-store/ observability-collector/ trace-replay/ (observability) cli/ bundled one-shot terminal client (HTTP client of transport-contract) host-bin/ composition root (boot + Bun.serve + collector supervisor) ``` The genesis commit deleted all prior source; we rebuilt from scratch. The OLD project lives at `/home/tradam/projects/dispatch/dispatch-source` (reference only — do not edit). The **web frontend is a SEPARATE repo** at `/home/tradam/projects/dispatch/dispatch-web` (own git, own harness — its own `AGENTS.md`/`ORCHESTRATOR.md`/`GLOSSARY.md`/`.dispatch/`). It consumes `packages/ui-contract` + the wire types as a pinned `file:` dependency. `lsp references` does NOT span the two repos, so cross-repo contract changes are **couriered via the user** (see the FE `ORCHESTRATOR.md` §5). Design + plan: `notes/frontend-design.md`. Do NOT edit the FE repo from here. --- ## 8. How to run (live validation) > This file is instructions and rules ONLY — it carries NO project state. Current > status, test counts, and next work live EXCLUSIVELY in `tasks.md`. Never record > status here; it drifts. **Prefer the already-running dev stack.** If `bin/up` (`:24203`) or `../bin/up2` (`:25203`) is up, probe THAT for read-only checks (GETs, validation 400s) instead of booting your own instance — or ask the user. Boot a private instance only when the probe must WRITE. **Boot + smoke test (private instance):** `.env` is auto-loaded by Bun and already carries `DISPATCH_API_KEY` — do NOT re-export it (an empty/botched export OVERRIDES `.env` and the provider silently fails to register: "No providers registered"). `.env` also pins `BACKEND_PORT`, which beats `PORT` — so set `BACKEND_PORT` explicitly, and ISOLATE the data paths or you'll share SQLite files + spawn a duplicate collector against the dev stack: ```bash cd /home/tradam/projects/dispatch/dispatch-backend BACKEND_PORT=4567 SURFACE_WS_PORT=4569 \ DISPATCH_DB=/tmp/opencode/probe/dispatch.db \ DISPATCH_TRACE_DB=/tmp/opencode/probe/traces.db \ DISPATCH_JOURNAL=/tmp/opencode/probe/app.ndjson \ bun packages/host-bin/src/main.ts # boots server (mkdir -p /tmp/opencode/probe first) # in another shell: curl -s -X POST localhost:4567/chat -H 'content-type: application/json' \ -d '{"conversationId":"c1","message":"Say hello in 3 words."}' ``` Note the chat field is **`conversationId`** (threads multi-turn), not `tabId`. **Live validation & process cleanup — the `[x]` bracket trick (scar tissue).** When you live-validate you background the app (`bun packages/host-bin/src/main.ts &`), and it now spawns a child **observability collector** process. To list or kill those, ALWAYS use the bracket trick in `ps`/`pgrep`/`pkill` patterns: ```bash ps -eo pid,args | grep '[o]bservability-collector/src/main' # list (won't self-match) pkill -9 -f '[h]ost-bin/src/main.ts' # kill the app pkill -9 -f '[o]bservability-collector/src/main' # kill the collector ``` **Why it matters:** a plain `pkill -f 'host-bin/src/main.ts'` matches its OWN command line and kills the parent shell → the tool call prints NOTHING and times out, looking exactly like a wedged session. `[h]ost-bin` matches the target "host-bin" while the literal pattern `[h]ost-bin` does not match itself. ALWAYS clean up the backgrounded app + its spawned collector after each live run — leaked processes pollute the next run's counts (this is precisely what made a correct supervisor look like it spawned 3 collectors and left 2 behind). **Live boot-probe in ONE command WILL hit the tool timeout — that is NOT failure (scar tissue).** A single bash command that boots the app (even detached via `setsid … & disown`), sleeps, runs a probe, then kills it will still run to the tool's timeout: the tool waits on the spawned server/collector session. The probe already ran — **read the probe's printed `RESULT: OK/FAIL` line as the signal**, ignore the timeout, then run a SEPARATE `pkill` (bracket-trick) + `ps` cleanup command (it returns immediately and confirms no leaks). Don't try to make the boot+probe command "return cleanly" — it won't. (For a frontend-agnostic surface, the probe is a tiny `bun` WebSocket client that asserts `catalog → subscribe → surface`.)