summaryrefslogtreecommitdiffhomepage
path: root/ORCHESTRATOR.md
diff options
context:
space:
mode:
Diffstat (limited to 'ORCHESTRATOR.md')
-rw-r--r--ORCHESTRATOR.md403
1 files changed, 403 insertions, 0 deletions
diff --git a/ORCHESTRATOR.md b/ORCHESTRATOR.md
new file mode 100644
index 0000000..b5d5319
--- /dev/null
+++ b/ORCHESTRATOR.md
@@ -0,0 +1,403 @@
+# 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 module), 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 module design + rationale).
+
+---
+
+## 0. Mental model (why this project is built this way)
+
+This is a **C/Raylib desktop application** built from composable modules. Each
+module is a `.h` (contract) + `.c` (implementation) pair. The team structure is
+**isomorphic to the module structure**: one owner-agent per module, and agents
+communicate only through **header-file contracts** — exactly as the code does.
+If an agent needs to read another module's `.c` file to understand its behavior,
+the `.h` contract is underspecified — that is a bug, not normal.
+
+### The harness layers
+
+- **Constitution** (`AGENTS.md`) — loaded by every agent. C99 rules, raylib
+ conventions, zero-warning policy.
+- **Safety reflexes** (`.dispatch/rules/*.md`) — tiny, crystallized scar tissue.
+- **Glossary** (`GLOSSARY.md`) — one canonical name per concept. Prevents
+ synonym drift across modules.
+- **This file** — the orchestrator's workflow (plan → summon → verify → commit).
+- **Contracts** — `.h` files are the ONLY interface between modules. They
+ declare types, constants, and function signatures. `.c` files are private
+ implementation — never read by anyone but the owning agent.
+
+### C/Raylib-specific principles
+
+1. **Contracts are headers** — a `.h` file IS the boundary. It must be
+ self-contained (all types it uses are included within it). Prefer forward
+ declarations over pulling in heavy headers.
+2. **No cross-module `.c` includes** — ever. If module A needs module B, A
+ includes `B.h`, never `B.c`.
+3. **All shared mutable state goes through `PlayerState*`** — no global
+ variables. File-scope statics are only for module-private state (e.g. fonts
+ in the UI module).
+4. **One `.o` per module** — each `.c` compiles independently. The linker
+ resolves dependencies. This is what makes parallel-agent waves possible.
+5. **Zero warnings on `-Wall -Wextra`** — the build is the trust signal. If
+ `make` barks, the wave is not green.
+6. **Raylib is the only external dependency** — no pulling in new libraries
+ without a design decision.
+
+---
+
+## 1. The golden workflow (build/modify a feature)
+
+1. **Plan.** Decide the module(s); split into dependency-topological **waves** of
+ disjoint modules, and WIDEN each wave where you can (§2a).
+2. **Overlap check FIRST.** Before creating anything new, check `GLOSSARY.md` +
+ existing `*.h` files. If the request *describes* an existing concept under a
+ new name, steer to the canonical term. New term? Propose the
+ standard/training-baked name and **ask the user** before adding it to the
+ glossary.
+3. **Boundary decision is the USER's.** "New module vs. extend an existing one?"
+ — surface it; never decide granularity silently.
+4. **Write the prompt** to `prompts/<module>.md` (gitignored). See §3 for the
+ prompt recipe.
+5. **Summon the wave** via `opencode run` (see §2); disjoint modules run in
+ PARALLEL (§2a). RE-READ `.dispatch/rules/` + the §3 scoping map before each
+ wave — assemble from the files, not from memory.
+6. **Verify** the reports + independently re-run checks (see §4). Trust nothing
+ until you've re-run `make` yourself and it exits 0 with zero warnings.
+7. **Resolve** any contract gaps / errors (see §5).
+8. **Commit** the milestone with a clear message. Update `tasks.md`.
+
+---
+
+## 2. Summoning agents via `opencode run` (the harness)
+
+OpenCode CLI is the summon mechanism. The orchestrator assembles each agent's
+prompt by concatenating standardized briefs + scoped rules + the TASK block.
+
+**Working dir:** always the repo root, `/home/tradam/projects/study-player`.
+
+**Two agent types:**
+
+| Agent type | Brief | Reads | Writes |
+|---|---|---|---|
+| **Module agent** | `.dispatch/package-agent.md` | Only other `.h` files | Own `.h` + `.c` pair |
+| **Build system agent** | `.dispatch/build-agent.md` | ANY file | `Makefile`, `bin/*` only |
+
+**Module agent canonical invocation** — the invariant guardrails live ONCE in
+the brief, so `prompts/<module>.md` is JUST the TASK block (§3). Do NOT use
+`-f` (see gotcha); ALWAYS redirect output to a file.
+
+```bash
+cd /home/tradam/projects/study-player && \
+opencode run --dir /home/tradam/projects/study-player \
+ "$(cat .dispatch/package-agent.md)
+$(cat .dispatch/rules/one-owner.md .dispatch/rules/zero-warnings.md .dispatch/rules/contracts-are-h.md)
+
+## TASK
+$(cat prompts/<module>.md)" \
+ > reports/<module>.run.log 2>&1
+```
+
+**Build system agent canonical invocation:**
+
+```bash
+cd /home/tradam/projects/study-player && \
+opencode run --dir /home/tradam/projects/study-player \
+ "$(cat .dispatch/build-agent.md)
+$(cat .dispatch/rules/one-owner.md .dispatch/rules/zero-warnings.md)
+
+## TASK
+$(cat prompts/build-system.md)" \
+ > reports/build-system.run.log 2>&1
+```
+
+**Assembly order is fixed: agent brief → scoped rules → TASK.**
+
+**Scoping map** — include ONLY the rules matching the agent type:
+- **Every module agent:** `one-owner.md`, `zero-warnings.md`, `contracts-are-h.md`.
+- **Build system agent:** `one-owner.md`, `zero-warnings.md` (it reads any file
+ so `contracts-are-h.md` doesn't apply).
+
+`AGENTS.md` is auto-loaded by opencode — never `cat` it.
+
+**MANDATORY — capture output to a file, never display it.** The agent's streamed
+output is enormous and will overwhelm context if it lands in your terminal.
+ALWAYS redirect the summon's stdout+stderr to a log file (e.g.
+`> reports/<module>.run.log 2>&1`) and do NOT echo/`cat` that log back. Read
+the agent's `reports/<module>.md` report (and, if necessary, `grep`/`tail` the
+log for a specific error). Dumping a full run log into context is a hard
+failure.
+
+**Run discipline:**
+- **Do NOT background it. Use a large timeout** (e.g. 1800000 ms = 30 min).
+- One summon per tool call. For PARALLEL agents on disjoint files, launch
+ multiple summons as concurrent tool calls — but ONLY when their file sets do
+ not overlap (single-writer rule).
+- Log parallel runs in `tasks.md`.
+
+**GOTCHAS:**
+- `-f/--file` is an ARRAY flag and greedily eats your trailing message as
+ another filename → "File not found". **Inline with `"$(cat prompts/X.md)"`
+ instead.**
+- A quick smoke test: `opencode run "Reply with exactly SMOKE_OK"` should print
+ `SMOKE_OK`.
+- `opencode agent list` lists agent profiles; `opencode run --help` for flags.
+
+---
+
+## 2a. Parallel execution — WAVES
+
+Throughput comes from running disjoint modules at once. Organise it as waves:
+- **A wave = modules that (a) touch DISJOINT files and (b) have no dependency
+ on each other's `.c` files** (each includes only already-authored `.h`
+ contracts). Launch a wave by emitting one summon per module as CONCURRENT tool
+ calls. The composition root (`main.c`) is almost always the LAST wave.
+- **Pre-author the seam to widen the wave.** Because the orchestrator OWNS
+ contracts (§6), write ALL `.h` contracts FIRST (WAVE 0), then summon the
+ implementors in the SAME wave against those fixed contracts — no module needs
+ another's implementation. Authoring the contracts up front turns a sequential
+ chain into one parallel wave.
+- **One writer per file, always** — even across waves. If two units would edit
+ the same file, they are NOT separable; merge them into one module or sequence
+ them.
+- **After a wave:** read every report, run `make` 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/<module>.md` is JUST the TASK block
+
+The invariant guardrails — single-writer ownership, visibility, zero warnings,
+contract discipline, and the report format — live ONCE in the standardized
+briefs the summon concatenates (§2). `prompts/<module>.md` contains ONLY:
+
+1. **Your module files:** e.g. `src/player.h` and `src/player.c` — name the
+ FILES the agent may edit (it owns them exclusively).
+2. **The job + algorithm**, naming specific functions and their signatures from
+ the pre-authored `.h` contract.
+3. **The specific `.h` contract file(s)** to read (e.g. `src/types.h`,
+ `src/study.h`) — the agent reads ONLY these headers, never `.c` files.
+4. **Any build instructions** (e.g. "run `make` from repo root").
+
+Keep it scoped: state only the project-specific, non-inferable task — the briefs
+carry the rest.
+
+**Make agents IMPLEMENT, not deliberate.** A summoned owner must edit files +
+run `make` + write its report in one run. If a summon returns only a plan,
+re-summon (§5a).
+
+---
+
+## 4. Verification (the orchestrator's trust protocol)
+
+The orchestrator confirms work from **contracts (.h files) + build output** —
+that is the designed trust mechanism. The header files ARE how you trust a
+module without depending on its internals.
+
+**Stay out of implementation files (§6 Visibility).** Your trust signals are the
+agent's report, the `.h` contract/surface it exposes, and the `make` output you
+re-run yourself — NOT its `.c` implementation. Do NOT open a module's `.c` file
+— not even to "skim", double-check, or diagnose a bug. You diagnose from the
+`make` output + the `.h` 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.
+
+After every agent, independently:
+```bash
+cd /home/tradam/projects/study-player
+make clean && make -j$(nproc) 2>&1 # must exit 0 with zero warnings
+git status --short # confirm agent stayed in its lane
+```
+
+- **Read ONLY the `.h` files** the unit exposes (its contract), not its `.c`
+ file. The contract plus a green build is enough to trust a module; subtle
+ mistakes show up as link errors or undefined symbols, which `make` catches.
+- Confirm the agent touched ONLY its assigned files (one-owner rule).
+
+**Concurrency caveat (parallel waves):** `make` is whole-project, so an agent's
+OWN mid-wave check can transiently see a sibling's half-written `.c` file. Don't
+act on a report's out-of-module compile errors; YOUR post-wave `make` run is
+authoritative.
+
+---
+
+## 5. Resolving errors & contract changes
+
+- **A module needs something from another module's contract:** that's a CONTRACT
+ CHANGE. The owner of the `.h` makes it. To find every consumer, grep for
+ `#include "<header.h>"` across `src/`. Then summon the affected module owners
+ to update. The orchestrator dispatches this fan-out; agents don't reach
+ across.
+- **Link error or undefined symbol (X and Y each compile but don't link):** no
+ single file owns it. Summon a **temporary multi-knowledge agent** with
+ read/write to the 2–3 relevant files (it MAY see `.c` files — exception to
+ the visibility rule), as their temporary exclusive owner.
+- **CR (change-request) in a report:** if it's **build/config** (`Makefile`,
+ `.gitignore`, `deps/` reference) the orchestrator edits it directly, then
+ re-verifies with `make`. If it's **implementation** (a `.c` file), the
+ orchestrator **summons the owning agent** — it does NOT edit `.c` files
+ itself.
+- **Makefile changes:** the Makefile is orchestrator-owned (it's build wiring,
+ §6). If a module addition requires updating `SRCS`, the orchestrator does it.
+
+---
+
+## 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/<module>.md`). Detect via
+ `git status` + the missing report. Re-summon the SAME TASK prefixed:
+ "IMPLEMENT THIS NOW — make all edits, run `make`, write the report; do not
+ stop to plan or ask."
+- **Agent strayed out of its lane.** `git status --short` after every wave; if
+ an agent touched a file outside its assigned set, keep it ONLY if it's
+ legitimately the orchestrator's lane (contracts / Makefile / harness / docs,
+ §6) — otherwise revert + re-summon with a tighter scope.
+- **Flaky green.** A module that compiles once but relies on stale `.o` files
+ might pass for the wrong reason; always `make clean && make` before
+ committing.
+
+---
+
+## 6. Restrictions & invariants (NEVER violate)
+
+- **Single-writer:** never let two agents edit the same file concurrently.
+- **Visibility rule:** agents see only other modules' `.h` contracts, NEVER
+ their `.c` implementation. An agent *needing* to read another module's `.c`
+ code is a signal that the `.h` contract is underspecified — fix the contract,
+ don't grant code access. (Exception: the temporary multi-knowledge integration
+ agent, §5.)
+- **The orchestrator NEVER reads or edits `.c` implementation files.** You read
+ ONLY `.h` files (contracts) + `make` output + agent reports. Do NOT open
+ `.c` files — not even during a bug. Clean context = level-headed decisions;
+ the subagents do the implementation.
+- **What the orchestrator MAY edit directly:**
+ (a) **Contracts** — any `.h` header file, especially `types.h` (pure shared
+ types with no .c file) and other `.h` files when pre-authoring contracts
+ or resolving gaps.
+ (b) **Build wiring + config** — `Makefile`, `.gitignore`, `deps/`
+ structure. (Note: the build system agent also owns `Makefile` and
+ `bin/*` — coordinate, don't conflict.)
+ (c) **Harness/docs** — `ORCHESTRATOR.md`, `AGENTS.md`, `GLOSSARY.md`,
+ `.dispatch/`, `notes/`, `tasks.md`, `prompts/`, `reports/`.
+ Everything else — all `.c` implementation files — 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 `.h` files of OTHER modules, with the sole
+ exception that it MAY read the `.c` files of the module it is assigned to.
+- **Linux native + Windows cross-compile** — `make` builds for Linux; `make
+ windows` cross-compiles for Windows via MinGW. Both platforms must work.
+ Use `#ifdef PLATFORM_LINUX` / `_GLFW_X11` vs `_GLFW_WIN32` guards where
+ platform differences exist.
+- **No global mutable state.** All shared state passes through `PlayerState*`.
+ File-scope statics are for module-private data only (e.g. cached fonts in the
+ UI module).
+- **C99 only.** No C11/C17 features the compiler doesn't support. No C++
+ in `.c` files.
+- **Raylib is the only external library.** No SDL, no GLFW standalone, no
+ third-party UI — everything goes through raylib's API.
+
+---
+
+## 7. Repo geography
+
+```
+/home/tradam/projects/study-player
+
+ AGENTS.md the subagent constitution (auto-loaded by opencode; you enforce it)
+ ORCHESTRATOR.md the orchestrator's operating manual (this file)
+ GLOSSARY.md canonical vocabulary + aliases-to-avoid
+ tasks.md live progress checklist / milestone log
+ Makefile build — orchestrator-owned, never touched by agents
+ README.md project overview, build instructions, usage guide
+
+ .dispatch/
+ package-agent.md base owner-agent brief (module agents)
+ build-agent.md build system agent brief (Makefile, bin/*)
+ rules/ safety reflexes — tiny crystallized scar tissue
+ one-owner.md
+ zero-warnings.md
+ contracts-are-h.md
+
+ .rules/ original design plans (reference only)
+ plan/
+ plan.md
+ phase1.md
+ phase2.md
+ phase3.md
+ ideas/
+
+ notes/
+ restructure-plan.md the full module split design + rationale + wave plan
+
+ prompts/ (gitignored — orchestrator→agent TASK blocks)
+ reports/ (gitignored — agent→orchestrator reports)
+
+ src/
+ types.h CONTRACT — shared types, enums, constants (PlayerState, etc.)
+ player.h CONTRACT — audio playback: load, seek, play, pause, format_time
+ player.c IMPL
+ study.h CONTRACT — study mode: detect_silence, portion navigation
+ study.c IMPL
+ ui.h CONTRACT — rendering: init, render_frame, destroy
+ ui.c IMPL
+ main.c COMPOSITION ROOT — entry point + main loop
+
+ deps/
+ raylib/ raylib library (built as static lib)
+ raygui/ raygui library (reserved, not yet used)
+
+ bin/
+ build build script for desktop (Windows cross-compile)
+ build-web build script for WASM/web
+ clean clean build artifacts
+ serve serve web build locally
+
+ resources/ font files, assets (gitignored)
+ build/ desktop build artifacts (gitignored)
+ build-web/ web build artifacts (gitignored)
+ web/ shell.html for emscripten
+```
+
+---
+
+## 8. Current status & how to run
+
+See `tasks.md` for the live checklist. The project is a working single-file
+`src/main.c` (778 lines) that needs to be split into modules as described in
+`notes/restructure-plan.md`.
+
+**Desktop build:**
+```bash
+cd /home/tradam/projects/study-player
+make -j$(nproc) # native Linux build → build/study-player
+# or for cross-compile:
+make windows -j$(nproc) # Windows cross-compile → build/study-player.exe
+```
+
+**Web build:**
+```bash
+bin/build-web # emscripten → build-web/index.html
+bin/serve # serve on port 8080
+```
+
+**Manual make:**
+```bash
+make clean && make -j$(nproc)
+```
+
+**Clean:**
+```bash
+bin/clean # removes build/ and build-web/
+```
+
+When the module restructure is complete, `make` builds for the host platform.
+`make windows` cross-compiles for Windows via MinGW. The font header generation
+is a make prerequisite.