summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-07 13:56:38 +0900
committerAdam Malczewski <[email protected]>2026-06-07 13:56:38 +0900
commitb3f0d4101e4335f5fb1da6f053f771bd5d073530 (patch)
treed7d0105ea8b69430d4f6abf50cfefe78718cfa34
parent1144a8027a3d0446e407f98c5cddc3a8c78831d5 (diff)
downloaddispatch-web-b3f0d4101e4335f5fb1da6f053f771bd5d073530.tar.gz
dispatch-web-b3f0d4101e4335f5fb1da6f053f771bd5d073530.zip
ORCHESTRATOR.md: document parallel-execution craft + recovery patterns
Fold in the working practices that were implicit: - §2a Waves: disjoint+no-compile-dep batching; widen waves by removing edges; parallel = N summon calls in ONE message (resolves the no-background tension) - §3: pre-author the cross-unit seam; tell agents about concurrent siblings; make agents implement (not deliberate) - §4: whole-project-check concurrency caveat + double-run for shared-global flakiness - §5a Recovery: plan-only agent re-summon; sibling test fan-out; lane-stray handling; flaky green - §9 Live integration probe (gated scripts/live-probe.ts; backend is user's process) - §10 Living backend-handoff.md as the cross-repo courier channel - generalize contract mirroring to all .dispatch/*.reference.md; re-read rules before each wave (a miss this session)
-rw-r--r--ORCHESTRATOR.md121
1 files changed, 102 insertions, 19 deletions
diff --git a/ORCHESTRATOR.md b/ORCHESTRATOR.md
index 5da54fb..eb5e381 100644
--- a/ORCHESTRATOR.md
+++ b/ORCHESTRATOR.md
@@ -18,14 +18,17 @@ mechanism — see `frontend-design.md` §4. No feature (not even chat) is the
mandatory structural root.
## 1. The golden workflow
-1. Plan the unit(s); respect dependency-topological order; one agent owns one unit.
+1. Plan the unit(s); split into dependency-topological **waves** of disjoint units, and
+ WIDEN each wave where you can (§2a); one agent owns one unit.
2. Overlap/vocab check vs `GLOSSARY.md` before naming anything new (§5.6 — ask the
user before coining a term).
-3. Write the per-summon TASK to `prompts/<unit>.md` (gitignored).
-4. Summon via `opencode run` (§2). Parallelize disjoint units only.
-5. Verify the report + independently re-run typecheck/test/check/build (§4).
-6. Resolve contract gaps / CRs (§5).
-7. Commit the milestone; update progress (`frontend-design.md` / a tasks log).
+3. Pre-author the cross-unit seam (§3) + write the per-summon TASK to
+ `prompts/<unit>.md` (gitignored). RE-READ `.dispatch/rules/` + the §2 scoping map
+ before each wave — summon from the files, NOT from memory.
+4. Summon a wave via `opencode run` (§2); disjoint units run in PARALLEL (§2a).
+5. Verify the reports + independently re-run typecheck/test/check/build (§4).
+6. Resolve contract gaps / CRs / agent failures (§5, §5a).
+7. Commit the milestone; update progress + the living handoff (§10).
## 2. Summoning agents (`opencode run`)
**Working dir:** the repo root `/home/tradam/projects/dispatch/dispatch-web` (so the
@@ -47,28 +50,63 @@ $(cat prompts/<unit>.md)" \
**MANDATORY — capture output to a file, never display it** (the stream is huge and
will crash the harness). Read the agent's `reports/<unit>.md`; `grep`/`tail` the log
only for a specific error.
-**Run discipline:** do NOT background; large timeout (e.g. 1800000 ms). One
-non-backgrounded run per summon; parallel summons ONLY for disjoint file sets
-(single-writer). `AGENTS.md` is auto-loaded by opencode — never `cat` it.
+**Run discipline:** do NOT background (no shell `&`); large timeout (e.g. 1800000 ms).
+Each summon is its own FOREGROUND `opencode run`. **Parallelism = emit several summon
+calls in ONE assistant message (concurrent tool calls), one per unit — NOT shell
+backgrounding and NOT `&`.** Parallel summons ONLY for disjoint file sets (single-writer);
+see §2a. `AGENTS.md` is auto-loaded by opencode — never `cat` it.
**GOTCHA — headless cross-repo read = HANG.** An agent's Read of any file OUTSIDE `--dir`
(here `dispatch-web/`) triggers a permission prompt that CANNOT be answered headlessly → the
run wedges until aborted. The `@dispatch/ui-contract` `file:` dep symlinks OUT of this repo, so
reading `node_modules/@dispatch/*` hangs. Mitigation (in place): the contract is mirrored in-repo
-at `.dispatch/ui-contract.reference.md` — agents read THAT; the brief forbids `node_modules/
-@dispatch/*` reads. **Regenerate that snapshot whenever `ui-contract` changes.** Agents are told:
-if you'd need a file outside your scope, report it and STOP — never attempt the read.
+under `.dispatch/*.reference.md` — one per consumed backend contract (`ui-contract`, `wire`,
+`transport-contract`) — agents read THOSE; the brief forbids `node_modules/@dispatch/*` reads.
+**Regenerate the relevant snapshot whenever that contract changes** (re-pin → re-mirror → fan out
+consumers), and point `package-agent.md` at every mirror. Agents are told: if you'd need a file
+outside your scope, report it and STOP — never attempt the read.
### `.dispatch/rules/` scoping map (inline ONLY the matching rows)
- **Every FE agent:** `frontend-pure-core.md`, `frontend-no-ambient-state.md`.
- **Surface interpreter / renderer / field-component unit:** + `frontend-interpreter-generic.md`.
- **Transport / protocol / WS-client unit:** + `frontend-inject-transport.md`.
+- **Any unit that builds `.svelte` UI (app shell, chat view/composer, surface field components):** + `frontend-styling.md`.
+
+## 2a. Parallel execution — WAVES
+Throughput comes from running disjoint units at once. Organise it as waves:
+- **A wave = a set of units that (a) touch DISJOINT files and (b) have no compile-time
+ dependency on each other** (each imports only already-built units + the pinned contracts).
+ Launch a wave by emitting one summon `Bash` call per unit IN A SINGLE MESSAGE (§2). Later
+ waves depend on earlier ones; the composition root (`src/app/`) is almost always the LAST wave.
+- **Widen waves deliberately.** Before summoning, look for dependency edges you can remove so a
+ unit moves into an earlier (wider) wave — e.g. make an adapter GENERIC so it doesn't import a
+ feature's port (a `LocalStore<T>` instead of a tabs-specific store), or have the consumer define
+ the port it needs so the producer/consumer split disappears. Fewer edges ⇒ wider waves ⇒ faster.
+- **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 (not per unit),
+ commit the milestone, then start the next wave. Don't interleave a new wave before the prior one
+ is green.
## 3. The per-summon `prompts/<unit>.md` is JUST the TASK
The invariant guardrails live in `package-agent.md` + the inlined rules. The TASK
states only the non-inferable, project-specific job: your unit's directory; the job
+ algorithm naming the contract types involved; the contract file(s) to read
-(`@dispatch/ui-contract`, a sibling's `index.ts`); the required named test cases.
+(`.dispatch/*.reference.md`, a sibling's `index.ts`); the required named test cases.
+
+**Pre-author the cross-unit seam.** When two units in the SAME wave must interoperate (a producer
++ a consumer that never see each other's internals), the orchestrator pins the shared interface in
+BOTH prompts before summoning: name the exact port/type, say who DEFINES it and who IMPORTS it
+(consumer-defines-port is the default — the adapter implements it), and which `index.ts` to import
+from. That precise seam is what lets disjoint, blind units compose on the first try.
+
+**Tell each agent it has company.** Add the concurrency note to every wave prompt: sibling units are
+being built in OTHER dirs right now; `svelte-check`/biome are whole-project, so if a check reports
+errors OUTSIDE your unit's dir, 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. Prompts should say so explicitly when needed (see §5a).
## 4. Verification (re-run yourself — trust nothing)
```bash
@@ -82,6 +120,12 @@ git status --short # confirm the agent stayed in its lane
Trust = contracts + public surfaces + green checks + the report — NOT reading impl.
For pure units, confirm tests use NO internal `vi.mock` of our modules.
+**Concurrency caveat:** because `svelte-check`/biome/`vitest` are whole-project, an agent's OWN
+verification (mid-wave) can transiently see a sibling's half-written file. Don't act on an agent
+report's out-of-dir errors; YOUR post-wave run is authoritative. **Run the suite TWICE** when a wave
+touched effects backed by a shared global (e.g. `fake-indexeddb`, `localStorage`) — to catch
+cross-test pollution / flakiness before committing.
+
## 5. Errors, CRs, cross-repo
- **FE contract change** (a shared FE type / service handle): the owner edits it,
runs `lsp references`, reports the consumer list; the orchestrator dispatches the
@@ -92,8 +136,25 @@ For pure units, confirm tests use NO internal `vi.mock` of our modules.
- **BACKEND contract change (cross-repo):** `lsp references` does NOT span the two
repos. The FE pins `@dispatch/ui-contract` + wire types as a dependency. A needed
backend change is reported UP and **couriered by the user** to the backend
- orchestrator; on the new version, re-pin + fan out FE consumers. NEVER edit the
- backend repo.
+ orchestrator (via the living handoff, §10); on the new version, re-pin + re-mirror +
+ fan out FE consumers. NEVER edit the backend repo.
+
+## 5a. Agent-failure recovery patterns
+- **Plan-only / "shall I proceed?" agent.** A summon sometimes returns a PLAN and stops without
+ editing (no diff, no report). Detected via `git status` + a missing `reports/<unit>.md`. Re-summon
+ the SAME TASK prefixed with: "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.
+- **Behaviour change reds a SIBLING's tests (test fan-out).** When a unit's new behaviour invalidates
+ another unit's test assertions, the failing 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 does not edit feature tests itself.
+- **Agent strayed out of its lane.** `git status` after every wave; if an agent touched a file
+ outside its dir, decide per §5/§6: keep it if it's legitimately the orchestrator's lane (build/
+ config/harness) and note it; otherwise revert + re-summon with a tighter scope. (Seen: an `app`
+ agent edited root `vitest-setup.ts` instead of filing a CR — adopted because it's config, but it
+ should have been a CR.)
+- **Flaky green.** If a wave's suite passes once but the units use shared-global effects, re-run
+ before trusting (see §4).
## 6. Restrictions (NEVER violate)
- Single-writer: never two agents on one file.
@@ -120,7 +181,29 @@ Backend (SEPARATE repo, contracts only): `/home/tradam/projects/dispatch/arch-re
— consume `@dispatch/ui-contract` (`file:` dep) + the wire types. Do NOT edit it.
## 8. Status
-Slice 1 (surface system + WS) in progress — plan in
-`../arch-rewrite/notes/frontend-design.md` §10. Scaffold verified (svelte-check +
-biome + `vite build` green; `@dispatch/ui-contract` linked). Dev server:
-`bun run dev` (port 24204).
+Slices 1–3 DONE + committed (surface system + WS; conversation transcript cache + delta streaming,
+live-verified 9/9; tabs + model selector + DaisyUI/dracula). Plan in
+`../arch-rewrite/notes/frontend-design.md` §10. Dev server: `bun run dev` (port 24204); full stack:
+backend `bin/up` (HTTP :24203, surface WS :24205) + FE Vite :24204.
+
+## 9. Live integration probe (effectful seams hide bugs unit tests can't)
+Pure/unit green is necessary, NOT sufficient: a transport/storage SHELL only fails for real against a
+running backend (the slice-1 WS-upgrade bug, and the secure-context `crypto.randomUUID` blank page,
+only surfaced live). After a slice that touches the wire or browser effects, run a LIVE probe:
+- A gated `scripts/live-probe.ts` (run with `bun scripts/live-probe.ts`) that drives the FE's REAL
+ network-facing modules (`adapters/ws` + `core/chunks` + cache + HTTP sync) against `bin/up`. Keep it
+ OUT of `bun run test` so a down backend never reds CI. It runs in Bun (global `WebSocket`/`fetch`);
+ use `fake-indexeddb/auto` for IndexedDB.
+- The backend is the USER's process (never boot it yourself — headless boot+probe HANGS + leaks
+ servers, EADDRINUSE). Just CONNECT to the open ports; confirm reachability first (`curl :24203/
+ health`). Probe with the REAL env (never an overridden empty key).
+- Browser-only bugs (secure-context APIs, theme, layout) still need a human at the page — list the
+ exact things to click and ask the user to confirm.
+
+## 10. Living cross-repo handoff — `backend-handoff.md`
+`lsp` can't span repos, so the FE↔backend seam flows through ONE rolling doc, `backend-handoff.md`
+(repo root, tracked), kept current so the user can courier the whole seam at any time. It records: FE
+slice status; the pinned contract versions + mirrors; OPEN asks / roadblocks for the backend;
+findings (e.g. "model is per-turn, not persisted per conversation"); and likely NEXT asks. Update it
+at every milestone and whenever a backend need arises; the user carries it across and brings the
+reply back (e.g. `backend-handoff-reply.md`).