From cc923b5c525909c5b14ce024687d7a58e672f383 Mon Sep 17 00:00:00 2001 From: sudoshi Date: Mon, 27 Jul 2026 19:59:43 -0400 Subject: [PATCH] Record how parallel agent work is isolated, split, and merged MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds .claude/rules/parallel-agents.md and points CLAUDE.md at it as binding on any change bigger than a single-file edit. The rule separates two problems that are easy to conflate: worktrees give isolation, and isolation does nothing for coordination. Layer 2 (decomposition by domain boundary, the single-writer file list, freezing contracts before fanning out) and Layer 3 (sequential rebase-and-merge, the green gate, never letting an agent auto-resolve another agent's conflict) are where the expensive failures actually happen. This session produced the evidence for it. Two efforts independently claimed net.acumenus.nightingale — one renaming the patient app in place, one building a new foundation tree. Both were internally correct, neither could see the other, and the merge reported zero conflicts while producing two applications competing for one App Store record. It was caught by reading diffs by hand, after one had already shipped to TestFlight. Co-Authored-By: Claude Opus 5 (1M context) --- .claude/rules/parallel-agents.md | 220 +++++++++++++++++++++++++++++++ CLAUDE.md | 7 + 2 files changed, 227 insertions(+) create mode 100644 .claude/rules/parallel-agents.md diff --git a/.claude/rules/parallel-agents.md b/.claude/rules/parallel-agents.md new file mode 100644 index 00000000..bae3b1f3 --- /dev/null +++ b/.claude/rules/parallel-agents.md @@ -0,0 +1,220 @@ +# Parallel Agent Work — Isolation, Decomposition, Integration + +How work is planned, split, executed, and merged in this repo — whether one agent +is working or five. **Isolation and coordination are different problems.** Git +worktrees solve isolation. They do nothing for coordination; Layers 2 and 3 are +where the failures actually happen. + +This rule is binding on any change bigger than a single-file edit. Read it +alongside [AGENTS.md](../../AGENTS.md) (build/deploy) and +[CLAUDE.md](../../CLAUDE.md) (token canon). Protected surfaces in +[auth-system.md](./auth-system.md) are never in scope for a worker agent. + +--- + +## Layer 1 — Isolation: worktrees + +One task → one worktree → one branch. The repo already uses this layout; keep it: + +```bash +git worktree add ../Zephyrus- -b codex/ main +``` + +Sibling directory `../Zephyrus-`, branch `codex/` (or `feat/`). +`git worktree list` is the live inventory — check it before adding another. + +Per-worktree setup, in order. `.env`, `/vendor`, and `/node_modules` are +gitignored, so a fresh worktree has **none of them**: + +```bash +cd ../Zephyrus- && cp ../Zephyrus/.env . && composer install && npm install +``` + +### Shared state worktrees do NOT isolate + +Assign each concurrent worktree a **slot number N** (1, 2, 3…) and offset every +port by `+10N`. The orchestrator worktree is slot 0 and keeps the defaults. + +| Service | Slot 0 | Slot N | How to set | +| --- | --- | --- | --- | +| `php artisan serve` | 8001 | `8001+10N` | `php artisan serve --port=8011` | +| Vite dev | 5176 | `5176+10N` | `npm run dev -- --port 5186` | +| compose nginx / pg / redis | 8084 / 5484 / 6384 | `+10N` | `NGINX_PORT` / `POSTGRES_PORT` / `REDIS_PORT` in that worktree's `.env` | +| mailhog | 8029 / 1029 | `+10N` | `MAILHOG_UI_PORT` / `MAILHOG_SMTP_PORT` | +| eddy / arena | 8090 / 8110 | `+10N` | `EDDY_PORT` / `ARENA_PORT` | + +- `./start-dev.sh` hardcodes 8001/5176 and **cannot** be used outside slot 0 — + run `php artisan serve --port` and `npm run dev -- --port` directly. Each + worktree writes its own `public/hot`, so Laravel finds its own Vite. +- **Docker Compose:** always `docker compose -p zephyrus-`. The `eddy` and + `arena` services pin `container_name:` (`zephyrus-eddy`, `zephyrus-arena`), + which collides regardless of project name — **only slot 0 runs the `eddy` or + `arena` profiles.** +- **The canonical DB is shared and live.** `.env` points at + `pgsql.acumenus.net/zephyrus`, which every worktree, the deployed app, and the + scheduled `zephyrus:demo-refresh` all hit. `migrate`, `migrate:fresh`, `db:seed`, + and demo-refresh against it are **orchestrator-only and serialized** — never a + worker action, never concurrent. A worker that needs schema change writes the + migration and stops. +- **PHPUnit is already isolated — do not "fix" it.** `tests/bootstrap.php` → + `IsolatedTestDatabase::provision()` creates `zephyrus_test_<12hex>` per process + (keyed on cwd + pid + random) and drops it at shutdown; browser tests use the + `zephyrus_test_e2e<12hex>` namespace. Concurrent `php artisan test` across + worktrees is safe. Killed runs leave orphans — sweep with + `php scripts/manage-test-database.php list-orphans`. + +--- + +## Layer 2 — Decomposition: where the leverage is + +Isolation prevents file collisions. It does not make two tasks independent. Two +agents both told to "improve the command center" will conflict no matter how many +worktrees they have. + +**Split by domain boundary, never by file list.** The real seams here: +ED · RTDC · Perioperative · Improvement/Process · Patient Flow 4D Navigator · +cockpit · Arena sidecar · Eddy · Hummingbird staff · Nightingale patient · +Home Hospital. Agents discover adjacent files mid-task; file-level partitioning +breaks the moment they do. + +### Single-writer files — the orchestrator owns these + +Highest-churn files in the last 90 days, which is exactly why they collide. A +worker **proposes** a diff for these in its PR description; the orchestrator +applies it. A worker never edits one directly: + +- `routes/api.php`, `routes/web.php` +- `resources/js/config/navigationConfig.ts` (single source for nav — see AGENTS.md) +- `bootstrap/app.php`, `app/Providers/AppServiceProvider.php`, + `app/Providers/AuthServiceProvider.php`, `app/Http/Middleware/HandleInertiaRequests.php` +- `resources/js/types/index.ts`, `resources/js/types/cockpit.ts` +- `.env.example`, `.github/workflows/ci.yml`, `tailwind.config.js` +- `composer.json` / `composer.lock`, `package.json` / `package-lock.json` +- `database/seeders/CommandCenterDemoSeeder.php` +- `docs/hummingbird/api-contract/**`, `capability-registry.lock`, `role-catalog.v1.json` +- `RAW_PALETTE_BASELINE` in `scripts/check-ui-canon.sh` — **at most one branch in + flight may lower it.** Two branches that both lower it merge into a baseline + that neither tree was verified against, and main goes red. + +**No new dependencies without orchestrator approval.** Lockfiles are the single +worst merge surface in this repo. + +### Freeze contracts before fanning out + +The expensive failures are two individually-correct branches with incompatible +assumptions. Before any worker starts, the shared surface is written down and +frozen: Inertia page-prop shapes, `/api/**` request+response shapes, the +Hummingbird BFF OpenAPI, shared TS types, DB schema (`db/schemas/` + +`database/migrations/`), and any new `navigationConfig` entry. Changing a frozen +contract mid-flight is an orchestrator decision that re-briefs every affected +worker — not a unilateral edit. + +### Coordination contract + +1. One task, one branch, one worktree. No worker touches another worker's domain. +2. Draft PR opened **at start**, not at finish — work in flight must be visible. +3. Keep changes small. Conflict rate rises monotonically with diff size. +4. Design work obeys the CLAUDE.md token canon; `/impeccable` for judgment calls. +5. An initiative plan in `docs/plans/` pairs with its `docs/devlog/` execution log + (docs/README.md filing rules). The orchestrator owns the plan; workers append + to the devlog. + +--- + +## Layer 3 — Integration: sequential, small, gated + +**Merge one branch at a time.** Rebase onto current `main`, re-run the gate, merge, +then start the next. Never batch-merge. + +Squash-merge from a PR — the CI verdict-reuse gatekeeper in `ci.yml` depends on +the squash commit reproducing the PR head's tree byte-for-byte, and `deploy.sh` +requires green CI on the *exact* commit. + +### "Green" in this repo — a worker leaves its branch here + +```bash +./vendor/bin/pint && npx tsc --noEmit && npm run test && bash scripts/check-ui-canon.sh && php artisan test +``` + +Plus, when the branch touches that surface: `npm run icons:check` (icons), +`scripts/verify-hummingbird-*.php` (mobile contracts), `npm run test:e2e` +(browser), `scripts/verify-test-isolation.sh` (anything touching test setup). +Note `.husky/pre-commit` calls `npm run build:check`, which is **not defined** in +`package.json` — don't rely on the pre-commit hook as your gate. + +### Conflict resolution + +**Never let an agent auto-resolve a conflict between two other agents' branches.** +This is the failure that doesn't announce itself: the resolver picks a winner +without knowing what the loser was building, a feature silently disappears, and +because no human wrote either side, no human notices. Conflicts between agent +branches go to a human or to a verifier holding the full spec. + +### Verification is a separate agent + +The agent that wrote a branch does not sign off on it. A verifier reviews the +branch against the frozen spec — did it build what was specified, does it respect +the token canon, did it touch a single-writer file, are the tests real. + +### Semantic conflict is unsolved + +No VCS removes it. Type checking and a full post-merge suite are the only +detectors, which makes **test-suite quality load-bearing for parallelism** in a +way it never is for solo work. If a domain's tests are thin, don't fan out into it. + +--- + +## Practical ceiling + +**2–4 concurrent writers.** Coordination cost scales worse than linearly; +parallelism gains scale sub-linearly. Go past 4 only when the work is genuinely +disjoint — independent modules, or a mechanical migration where every edit is the +same transformation. When in doubt, run fewer agents and merge faster. + +--- + +## Prompt templates + +### Orchestrator kickoff + +> Act as orchestrator under `.claude/rules/parallel-agents.md`. Do not write +> feature code yourself. +> +> **Goal:** +> +> 1. Read PRODUCT.md, DESIGN.md, CLAUDE.md, AGENTS.md, and the relevant +> `docs/plans/` entry. Report what already exists before proposing anything. +> 2. Produce a spec: task list split by **domain boundary**, the frozen shared +> contracts (API shapes, Inertia props, TS types, DB schema), and the +> single-writer files you will own. +> 3. Recommend a worker count (default 2–3, hard ceiling 4) and justify it. Name +> the domains whose test coverage is too thin to fan out into. +> 4. Stop and show me the spec before creating any worktree. +> +> Then, on my approval: create worktrees per Layer 1 (slot numbers, port offsets, +> `.env` copy, installs), brief each worker with the template below, and merge +> sequentially per Layer 3. You own all single-writer files and every DB +> operation against the canonical database. + +### Worker brief + +> You own **** only, in worktree `../Zephyrus-` on branch +> `codex/`, slot N (ports +10N). +> +> **Scope:** . **Frozen contracts you must not change:** . +> **Out of scope:** every file in the single-writer list; anything in +> `.claude/rules/auth-system.md`; any other domain. If you need a change to a +> single-writer file, write the exact diff in your PR description and stop. +> +> Never run migrations, seeders, or `zephyrus:demo-refresh` against the canonical +> database. Open a draft PR before you start work. Leave the branch green (Layer 3 +> command list) and record what you built in `docs/devlog/`. + +### Verifier + +> Review branch `codex/` against the spec at . You did not write it. +> Check: does it implement what was specified (and nothing more); did it touch a +> single-writer or protected file; does it hold the CLAUDE.md token canon; is +> `scripts/check-ui-canon.sh` green; are the tests real assertions or theater; +> would it conflict semantically with . Report +> findings — do not fix them. diff --git a/CLAUDE.md b/CLAUDE.md index ef964b05..d7d7fc78 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -3,6 +3,13 @@ Project guidance for Claude Code. Engineering/build/deploy conventions live in [AGENTS.md](./AGENTS.md); this file points at the design context. +## How work is run + +Any change bigger than a single-file edit follows +**[.claude/rules/parallel-agents.md](./.claude/rules/parallel-agents.md)** — +worktree isolation, domain-boundary task splits, the single-writer file list, and +sequential rebase-and-merge. Read it before fanning out agents or opening a branch. + ## Design Context Zephyrus is a **product**-register surface (a hospital operations command center —