summaryrefslogtreecommitdiffhomepage
path: root/ORCHESTRATOR.md
blob: 4a41f681447e702705ded1a6c1477963756e8418 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
# 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*`** (or `UIState*` for
   UI-only state) — 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.
7. **`font_data.h` is a definition, not a declaration** — include it in exactly
   ONE `.c` file (currently `ui.c`). Including it from multiple translation
   units causes multiple-definition link errors.

---

## 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.
- **Multiple-definition link error on `embedded_font_data`:** `font_data.h` is
  an `xxd`-generated array definition, not a declaration. It must be included
  in exactly ONE `.c` file (currently `ui.c`). If a new module needs the font
  data, either route it through `ui.h` functions or move the include to a
  single owner and expose `extern` declarations.
- **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). It uses `$(wildcard src/*.c)` so new modules auto-compile. Structural
  changes (new targets, new platforms) are orchestrator-owned.

---

## 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*`
  (or `UIState*` for UI-only state). File-scope statics are for module-private
  data only.
- **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 module 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

  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, SilenceRegion, UILayout)
    player.h         CONTRACT — audio playback: load, seek, play, pause, update, format_time
    player.c         IMPL
    study.h          CONTRACT — study mode: detect_silence, portion navigation, auto-pause
    study.c          IMPL
    ui.h             CONTRACT — rendering + input: init, destroy, handle_input, render_player, render_empty
    ui.c             IMPL (owns font_data.h include)
    config.h         CONTRACT — UILayout persistence: config_load, config_save
    config.c         IMPL
    layout_editor.h  CONTRACT — layout editor tab: init, draw
    layout_editor.c  IMPL (owns raygui implementation)
    main.c           COMPOSITION ROOT — entry point + main loop + platform glue

  deps/
    raylib/          raylib library (built as static lib)
    raygui/          raygui library (single-header, implemented in layout_editor.c)

  bin/
    build            build script for desktop (Linux native / 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 module split is **complete** — the
single-file `src/main.c` (778 lines) has been decomposed into 7 modules:
`types.h`, `player`, `study`, `ui`, `config`, `layout_editor`, `main`.

**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/
```

The font header generation is a make prerequisite — `build/font_data.h` is
generated by `xxd -i` from the first `.otf`/`.ttf` in `resources/`.