@pmelab/gtd 8.0.0 → 8.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,994 +0,0 @@
1
- # Configuration
2
-
3
- gtd reads an optional `.gtdrc` config file via
4
- [cosmiconfig](https://github.com/cosmiconfig/cosmiconfig). gtd ships the bundled
5
- **unified** workflow as its **built-in default**: with no `workflow:` configured
6
- anywhere in the cwd→home config chain, that default is used automatically, so a
7
- state command works out of the box with no config at all.
8
- [`gtd init`](#gtd-init) is optional — it seeds a minimal `.gtdrc.json` with the
9
- two things most projects tune (a `testCommand` var and a Prettier formatting
10
- suggestion), not the workflow. To CUSTOMIZE the machine itself, add your own
11
- [`workflow:`](#the-workflow-key) key. Supported filenames (searched in this
12
- order):
13
-
14
- - `.gtdrc`
15
- - `.gtdrc.json`
16
- - `.gtdrc.yaml`
17
- - `.gtdrc.yml`
18
- - `gtd.config.json`
19
- - `gtd.config.yaml`
20
-
21
- ## Schema
22
-
23
- v3's `.gtdrc` has exactly three blessed top-level keys:
24
-
25
- - **`workflow`** (object, optional) — the whole machine definition (its states,
26
- plus its own `vars:` defaults and `modes:`), compiled by
27
- `src/PatternConfig.ts`. Absent = gtd's built-in default (the bundled unified
28
- workflow) is used. Declare it to fully REPLACE that default with your own
29
- machine (there is no `extends`/merge). See
30
- ["The `workflow:` key" below](#the-workflow-key) for its schema.
31
- - **`vars`** (object, optional) — a flat `name -> scalar` map, one layer of the
32
- merged `it.vars` every template sees — see ["Variables"](#variables) below.
33
- - **`modes`** (object, optional) — steering-file modes (`format:`/`validate:`
34
- shell commands), layered over the active workflow's own `modes:` and gtd's
35
- built-in validators, so a project can plug its formatter or linter into the
36
- configured workflow without re-declaring that mode on the workflow itself. See
37
- ["`modes:`"](#modes--pluggable-steering-file-modes) below.
38
- - **`$schema`** (string, optional) — stripped before validation, so it never
39
- counts as an unknown key. Point it at the published schema for editor-backed
40
- autocompletion. A `schema.json` is generated from `src/ConfigSchema.ts` at
41
- build time and ships with the package.
42
-
43
- Any other top-level key is **rejected** (`onExcessProperty: "error"`) — v3 has
44
- no `testCommand`, `fixAttemptCap`, `reviewThreshold`, `agenticReview`, `squash`,
45
- `learning`, `decisionLog`, or `models` keys; all of that machinery is gone (see
46
- [Upgrading](upgrading.md)). The engine blesses no VARIABLE NAMES either —
47
- `testCommand` (the bundled template's own var, see ["Variables"](#variables)) is
48
- workflow-authored data like any other `it.vars` entry, not a special key gtd
49
- interprets.
50
-
51
- ## The `workflow:` key
52
-
53
- > Authoring or editing a workflow with a coding agent?
54
- > `skills/authoring/SKILL.md` is the agent-facing contract for producing a valid
55
- > `workflow:` — the state model, pattern grammar, load-time rules, and how to
56
- > verify a change compiles. This section is the exhaustive schema reference
57
- > behind it.
58
-
59
- A declared `workflow:` key fully REPLACES gtd's built-in default — there is no
60
- `extends`, no merge-over-a-built-in. The built-in default is itself a YAML asset
61
- (`src/workflows/unified.yaml`) compiled through the exact same compiler
62
- (`compileWorkflowConfig`) your own `workflow:` value goes through — no
63
- privileged code path — so you can materialize it into config form as a starting
64
- point to edit (`renderInitConfig` in `src/workflows/templates.ts`). Its shape:
65
-
66
- ```yaml
67
- workflow:
68
- vars: # optional — the workflow's own declared `it.vars` defaults (see "Variables" below)
69
- anyKey: anyScalarValue
70
- modes: # optional — steering-file modes a state's `mode:` may name (see "modes:" below)
71
- <name>:
72
- format: <shell command> # at least one of format/validate
73
- validate: <shell command>
74
- states:
75
- <name>:
76
- actor: <string> # forbidden on a commit state, required otherwise
77
- script: <string> # exactly one of script/prompt/message/commit
78
- prompt: <string>
79
- message: <string>
80
- commit: <string>
81
- on: # a mapping, DECLARATION ORDER PRESERVED
82
- "<pattern>": <targetState> # short form
83
- "<pattern>": { to: <targetState>, describe: <sentence> } # with a human-readable route description
84
- initial: true # exactly one state across the whole workflow
85
- retry:
86
- max: <number>
87
- otherwise: <targetState>
88
- model: <string> # optional, opaque harness hint — forbidden on a commit state
89
- memory: <string> # optional, opaque memory-scope label — forbidden on a commit state
90
- file: <string> # optional, an Eta template naming the state's steering file — forbidden on a commit state
91
- mode: <modeName> # optional, requires "file" — a built-in (qa/review/prose) or a `modes:` entry; forbidden on a commit state
92
- ```
93
-
94
- See [STATES.md](../STATES.md#1-the-model) for what each field means to the
95
- engine, and [§3](../STATES.md#3-pattern-grammar) for the pattern grammar.
96
-
97
- ### Content values: inline or a file reference
98
-
99
- A `script`/`prompt`/`message`/`commit` value starting with `./` or `../` is a
100
- **file reference** — resolved relative to **the directory of the `.gtdrc` that
101
- declared it** (not the directory `gtd` runs from) and read at load time. A
102
- missing or unreadable file reference is a **load error**, collected and thrown
103
- with every other config problem — never silently treated as inline text. Any
104
- other string (including one that merely contains a `/`, or an absolute path) is
105
- inline Eta template source, used verbatim.
106
-
107
- Because references anchor to the declaring file, a `.gtdrc` and its
108
- `gtd-prompts/` (or `prompts/`) directory can live **outside** the repo — e.g. in
109
- a parent directory shared across several repos. gtd still runs from the repo
110
- root, discovers the parent `.gtdrc` by walking up the directory chain (see
111
- ["Lookup and precedence"](#lookup-and-precedence) — cwd→home), and inlines each
112
- level's `./`-relative references against that level's own directory. When
113
- workflows are layered across levels, every reference resolves against the file
114
- that wrote it, so a parent's `./gtd-prompts/x.md` and a child's never collide.
115
- (This is a capability of a `.gtdrc` you write yourself — `gtd init` seeds only
116
- `vars:`/`modes:`, not a `workflow:` with file references.)
117
-
118
- ```yaml
119
- workflow:
120
- states:
121
- working:
122
- actor: agent
123
- prompt: ./prompts/working.md # read from alongside this .gtdrc
124
- on:
125
- "* **": done
126
- ```
127
-
128
- A `vars:` key sibling to `states:` inside `workflow:` declares the workflow's
129
- own defaults for `it.vars` — see ["Variables"](#variables) below for the full
130
- three-layer picture.
131
-
132
- ### `on:` values — a target, or a `{ to, describe }` route description
133
-
134
- An `on` row's value is normally the target state name. It may instead be an
135
- object `{ to: <target>, describe: <sentence> }`, attaching a human-readable
136
- `describe` — a plain sentence explaining what making that kind of change does
137
- next. `describe` is **inert to the engine**: it plays no part in matching, is
138
- never Eta-rendered — UNLIKE the pattern key itself, which IS an Eta template
139
- (see below) — and never affects a decision. It exists only to be surfaced. A
140
- state's own edges are handed to its content template as `it.edges` (an array of
141
- `{ pattern, target, describe? }`, `pattern` already rendered, in declaration
142
- order), so a human gate's `message:` can render a "what each change does next"
143
- list from the same routing the engine uses — one source of truth:
144
-
145
- ```yaml
146
- workflow:
147
- states:
148
- await-review:
149
- actor: human
150
- message: |
151
- Review the diff, then run `gtd step human`.
152
-
153
- What each change does next:
154
- <% it.edges.forEach(function (e) { if (e.describe) { %>
155
- <%~ "- " + e.describe + "\n" %>
156
- <% } }) %>
157
- on:
158
- "D .gtd/REVIEW.md":
159
- to: idle
160
- describe:
161
- "Delete `.gtd/REVIEW.md` to approve the whole cycle and rest at
162
- idle."
163
- "* **":
164
- to: planning
165
- describe:
166
- "Change any source file to leave feedback and start another round."
167
- ```
168
-
169
- The `<%~ "- " + e.describe + "\n" %>` idiom keeps each bullet's own newline: Eta
170
- strips the template's line breaks around `<% %>` control tags (its `autoTrim`
171
- default), not text inside an interpolation. Edges without a `describe` are
172
- skipped by the `if (e.describe)` guard. `gtd next --json` and
173
- `gtd status --json` also emit the `edges` array; both omit it when the state has
174
- no `on` (a commit state), and omit a per-edge `describe` when that edge declares
175
- none.
176
-
177
- **The pattern key (everything before the value) is an Eta template, rendered
178
- against `it.vars` alone** — the same vars-only slice `on` templates could ever
179
- need, since a pattern only ever names a path, never a diff or commit hash. A
180
- literal path (`"A .gtd/FEEDBACK.md": fix`) still works unchanged; naming a var
181
- instead (`"A <%= it.vars.feedbackFile %>": fix`) makes that row track a
182
- repointed `feedbackFile` the same way `file:` does — see
183
- ["Variables"](#variables).
184
-
185
- ### `model:` — the opaque harness hint, template-rendered
186
-
187
- A state may declare `model: <string>` — an OPAQUE label (e.g. `smart`, `fast`,
188
- or a concrete model id) gtd never interprets; it is only passed through so the
189
- driving loop can map it onto whatever models its agent harness provides. Unset
190
- means "use the harness's default." Forbidden on a commit state (never at rest,
191
- emits nothing):
192
-
193
- ```yaml
194
- workflow:
195
- states:
196
- working:
197
- actor: agent
198
- model: smart
199
- prompt: do the thing
200
- on:
201
- "* **": done
202
- ```
203
-
204
- Like every content string, `model:` is rendered as an Eta template through the
205
- exact same context as the state's content — a plain string with no Eta tags
206
- (`smart` above) passes through unchanged, but
207
- `model: "<%= it.vars.reviewModel %>"` resolves against the merged `it.vars` (see
208
- ["Variables"](#variables)). A render failure behaves exactly like a content
209
- render failure at the same call site: `gtd next`/`gtd status` error out, nothing
210
- committed.
211
-
212
- `gtd next --json` and `gtd status --json` include a `"model"` key (the RENDERED
213
- value) only when the resolved state declares one — it is **omitted entirely**,
214
- never emitted as `null`, when unset.
215
-
216
- ### `memory:` — the memory-scope label, template-rendered
217
-
218
- A state may declare `memory: <string>` — an OPAQUE label gtd never interprets;
219
- it is passed through so a memory-aware driving loop can decide when an agent
220
- turn continues from the previous turn's memory and when it starts fresh. The
221
- contract is a **comparison, not a command**: two agent turns that emit the SAME
222
- `memory` value belong to the same memory scope (the driver retains the agent's
223
- memory across them), and a change in value — or the very first agent turn — is
224
- where the driver starts fresh. Unset means "use the harness's default."
225
- Forbidden on a commit state (never at rest):
226
-
227
- ```yaml
228
- workflow:
229
- states:
230
- planning:
231
- actor: agent
232
- memory: plan
233
- prompt: develop the sketch into a concrete plan
234
- on:
235
- "* **": plan-review
236
- plan-review:
237
- actor: human
238
- message: accept the plan, or edit it to iterate
239
- on:
240
- "C": building
241
- "* **": planning
242
- building:
243
- actor: agent
244
- memory: build # a different scope — implementation starts fresh
245
- prompt: implement the settled plan
246
- on:
247
- "* **": done
248
- done:
249
- commit: "feat: done"
250
- ```
251
-
252
- Because `planning` re-enters itself around the review loop, every lap emits
253
- `memory: plan` and the planner keeps its accumulated exploration; the move to
254
- `building` emits a different label (`build`), which is where the driver clears
255
- memory for a fresh implementation turn. A loop of same-labelled turns retains; a
256
- differently-labelled state at a phase boundary clears. This is exactly how the
257
- unified template scopes its agent states across the four `plan`/`build`/`fix`/
258
- `review` memory tiers (see
259
- [STATES.md §10](../STATES.md#10-the-bundled-workflow-templates)), and how the
260
- loop driver reads it is spelled out in `skills/loop/SKILL.md`.
261
-
262
- Like `model`, `memory:` is rendered as an Eta template through the same context
263
- as the state's content — a plain label (`plan`) passes through unchanged, while
264
- `memory: "<%= it.vars.planScope %>"` resolves against the merged `it.vars`. A
265
- render failure behaves exactly like a content render failure:
266
- `gtd next`/`gtd status` error out, nothing committed.
267
- `gtd next --json`/`gtd status --json` include a `"memory"` key (the RENDERED
268
- value) only when the resolved state declares one — **omitted entirely**, never
269
- `null`, when unset. Plain `gtd status` prints a `Memory:` line (right after
270
- `Model:`, when present).
271
-
272
- ### `label:` — the opaque display name, template-rendered
273
-
274
- A state may declare `label: <string>` — an OPAQUE display name gtd never
275
- interprets; it is only passed through so a driver/viewer can show something
276
- friendlier than the raw state name. Unset means the state has no display name.
277
- Forbidden on a commit state (never at rest, emits nothing):
278
-
279
- ```yaml
280
- workflow:
281
- states:
282
- working:
283
- actor: agent
284
- label: Implement the feature
285
- prompt: do the thing
286
- on:
287
- "* **": done
288
- ```
289
-
290
- Like `model`/`memory`, `label:` is rendered as an Eta template through the exact
291
- same context as the state's content — a plain string with no Eta tags
292
- (`Implement the feature` above) passes through unchanged, but
293
- `label: "<%= it.vars.stateLabel %>"` resolves against the merged `it.vars` (see
294
- ["Variables"](#variables)). A render failure behaves exactly like a content
295
- render failure at the same call site: `gtd next`/`gtd status` error out, nothing
296
- committed.
297
-
298
- `gtd next --json` and `gtd status --json` include a `"label"` key (the RENDERED
299
- value) only when the resolved state declares one — it is **omitted entirely**,
300
- never emitted as `null`, when unset. A driver/viewer choosing to fall back to
301
- the raw state name when `label` is absent is that consumer's own fallback, not
302
- something gtd itself does.
303
-
304
- ### `file:`/`mode:` — the steering-file association
305
-
306
- A state may additionally declare `file:` — an Eta template naming THE steering
307
- file this state is about: the file a human/editor should look at while the
308
- machine rests here (rendered through the same `it.vars`-carrying context as
309
- content/`model`; must render non-empty). Forbidden on a commit state (never at
310
- rest). Multiple states may share one `file:` (and, in the unified template, do):
311
-
312
- ```yaml
313
- workflow:
314
- vars:
315
- planFile: .gtd/PLAN.md
316
- states:
317
- working:
318
- actor: agent
319
- file: <%= it.vars.planFile %>
320
- mode: qa
321
- prompt: develop the plan
322
- on:
323
- "* **": done
324
- ```
325
-
326
- `mode:` requires a sibling `file:` and names the file's FORMAT — a
327
- **steering-file mode**. Two names are BUILT IN, each carrying a VALIDATOR gtd
328
- implements itself:
329
-
330
- | `mode` | Format |
331
- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
332
- | `qa` | The open-questions format (`## Open Questions` near the top and `## Answered Questions` at the bottom, one `###` sub-heading per question with a free-form body; status is positional, no marker line). The bundled advanced flow additionally writes each open question as a checkbox list (candidate answers + a `- [ ] _your answer_` slot) and gates the human's answer step with `answerGate` (exactly one tick per question) — but that convention + gate live in the workflow/edge, not in this validator, which only checks the structure above. |
333
- | `review` | The checkbox review format (`# Review: <hash>` header, `<!-- base: <hash> -->` comment, `##` chunks, `- [ ]` pointers). |
334
-
335
- A THIRD name, `prose`, is also built in but ships **no validator at all** — a
336
- known mode name a state's `mode:` may use with no `modes:` declaration, that
337
- formats (once a `modes:` layer gives it a `format:` command) but never
338
- validates. It is for a curated, free-form document with no gtd-side schema — the
339
- bundled template's simple-flow plan file (`.gtd/TODO.md` at
340
- `planning`/`plan-review`) uses it.
341
-
342
- Any other name must be declared in a `modes:` map (next section) — as must a
343
- `format:` command for any of the three built-ins, since gtd ships no formatter.
344
- A `mode:` naming neither a built-in nor a declared mode is a load error listing
345
- both sets (typos must not silently disable the format gate or editor support).
346
-
347
- What a mode buys the state: before capturing a turn out of it, `gtd step`
348
- formats that file in place and validates it, refusing the step when it is
349
- invalid (`gtd validate` runs the same check on demand — see
350
- [STATES.md §12](../STATES.md) and [docs/cli.md](cli.md#gtd-validate)). The
351
- ENGINE itself never branches on `mode`; the edge does, and `gtd lsp` (see
352
- [docs/design/state-file-association.md](design/state-file-association.md)) reads
353
- it to decide which document symbols/code actions/diagnostics a file gets — for
354
- the built-in names only.
355
-
356
- `gtd next --json`/`gtd status --json` gain `"file"` (the RENDERED path) and
357
- `"mode"` (verbatim) keys, each **omitted entirely** (never `null`) when the
358
- resolved state declares none — exactly like `model`. Plain `gtd status` prints
359
- `File:`/`Mode:` lines (right after `Model:`, when present) when set.
360
-
361
- **`on` pattern keys are Eta templates too.** A workflow's `on` patterns may name
362
- a filename var the same way `file:`/content does — e.g.
363
- `"A <%= it.vars.feedbackFile %>": fix` instead of a literal
364
- `"A .gtd/FEEDBACK.md": fix`. The edge (`src/Edge.ts`'s `renderOnEdges`) renders
365
- every pattern's key against `it.vars` ALONE (no diffs/commit hashes — a pattern
366
- only ever needs to name a path) before handing the edges to the pure engine,
367
- which only ever matches plain, already-rendered strings. This is what lets a
368
- repointed filename var (`.gtdrc`'s top-level `vars:`, or a `GTD_` override)
369
- reroute the machine consistently: `file:` (and any template reading/ writing
370
- that path) and the `on` map that decides what a change to it MEANS both follow
371
- the same var. A pattern written with a literal `.gtd/…` path instead of the var
372
- still works exactly as before — only a workflow that templates its patterns
373
- benefits from the repoint.
374
-
375
- ### `modes:` — pluggable steering-file modes
376
-
377
- A mode is a pair of SHELL COMMANDS over one file: `format:` rewrites it in
378
- place, `validate:` decides whether it is well-formed. Declare them beside
379
- `states:` (the workflow's own modes) and/or under a top-level `modes:` key (the
380
- project's layer over them), and point any state's `mode:` at the name:
381
-
382
- ```yaml
383
- workflow:
384
- modes:
385
- adr:
386
- format: "npx prettier --write <%= it.file %>"
387
- validate: "./scripts/check-adr.sh <%= it.file %>"
388
- states:
389
- drafting:
390
- actor: agent
391
- prompt: "Write the ADR."
392
- file: docs/adr/0001.md
393
- mode: adr
394
- on:
395
- "* **": reviewing
396
- # ...
397
- ```
398
-
399
- The contract:
400
-
401
- - **`it.file`** is the already-rendered `file:` path. A command is an Eta
402
- template over the same context every other template sees (so `it.vars` works
403
- too) plus that one extra key. A `./`-prefixed command is a COMMAND, never a
404
- `./`-relative file reference the way a content string is.
405
- - **`validate:` exits 0 for valid.** A non-zero exit means invalid, and its
406
- output (stdout then stderr) becomes the findings, one per line — that is what
407
- `gtd validate` prints and what the `gtd step` gate refuses with. A failing
408
- command that prints nothing still refuses, with a synthesized finding.
409
- - **`format:` rewrites the file in place** and runs FIRST, so validation always
410
- judges the formatted file. A non-zero exit is a HARD error (broken tooling,
411
- not a malformed file): nothing is validated and nothing is committed.
412
- - **At least one of the two is required** in any single entry; the half it
413
- leaves out falls through to the layer beneath (below), or is a no-op.
414
- - Commands run via `bash -c` from the repository root — the only place gtd
415
- itself spawns a subprocess (a `script:` state is run by the loop driver, not
416
- by gtd). A `.gtdrc` is project code: only add commands you would run yourself.
417
-
418
- #### The layers, half by half
419
-
420
- `format:` and `validate:` resolve INDEPENDENTLY, each from the first layer that
421
- declares it:
422
-
423
- 1. the top-level `.gtdrc` `modes:` key (the project's own layer, cwd→home
424
- deep-merged like any other config key);
425
- 2. the active workflow's own `modes:` map;
426
- 3. for the two VALIDATOR built-in names only, gtd's in-process validator — `qa`
427
- → `src/OpenQuestions.ts`, `review` → `src/ReviewDoc.ts`. The third built-in,
428
- `prose`, has no in-process validator to fall back to, so it validates nothing
429
- unless a `modes:` layer above declares its own `validate:`. There is no
430
- built-in formatter at this layer for any of the three: **gtd ships no
431
- formatter**, so bring your own.
432
-
433
- So for a state declaring `mode: qa`:
434
-
435
- | `modes: { qa: … }` | formats with | validates with |
436
- | -------------------------------- | ------------- | --------------------------- |
437
- | (nothing declared) | nothing | gtd's open-questions parser |
438
- | `format: npx prettier --write …` | that command | gtd's open-questions parser |
439
- | `validate: my-linter …` | nothing | that command |
440
- | both | its `format:` | its `validate:` |
441
-
442
- That is what makes the built-in modes extensible rather than all-or-nothing —
443
- and the top-level key means a project can plug its formatter into the built-in
444
- default workflow (or its own) without re-declaring a `modes:` block on the
445
- workflow itself. **`gtd init` seeds exactly this block by default** (the
446
- Prettier suggestion below), so a fresh repo already formats its steering files —
447
- edit or drop it to taste. It is what a scaffolded `.gtdrc.json` contains
448
- (`gtd init` writes no `workflow:` key — the built-in default is used):
449
-
450
- ```jsonc
451
- // .gtdrc.json — as scaffolded by `gtd init`: a testCommand var and the markdown formatter
452
- {
453
- "vars": { "testCommand": "npm test" },
454
- "modes": {
455
- "qa": { "format": "npx prettier --write <%= it.file %>" },
456
- "review": { "format": "npx prettier --write <%= it.file %>" },
457
- "prose": { "format": "npx prettier --write <%= it.file %>" },
458
- },
459
- }
460
- ```
461
-
462
- **Known limitation — no live editor support for a custom mode.** `gtd lsp`
463
- publishes diagnostics, document symbols and code actions for the built-in
464
- `qa`/`review` formats only (gtd never runs a mode command per keystroke over an
465
- unsaved buffer). A file whose validation is a command — and a `prose` file,
466
- which has no validator to publish diagnostics for — is still formatted and
467
- validated by `gtd validate` and the `gtd step` capture gate.
468
-
469
- ### `reviewWindow:`/`reviewBase:` — the review checkout window
470
-
471
- A state may declare `reviewWindow: true`. While the machine RESTS there, gtd
472
- opens a **review checkout window**: it rewinds HEAD and the index to the review
473
- base (`git reset --mixed`) with the working tree untouched, so the whole
474
- `base..HEAD` diff surfaces as ordinary uncommitted changes in the editor's git
475
- integration (SCM panel, gutters, per-file diff, discard-hunk). The window closes
476
- automatically on the next invocation, once the machine rests anywhere else — and
477
- a reviewer's own edits, made while it was open, become the resting state's
478
- ordinary pending changes, captured by its `on` patterns like any other diff.
479
- Forbidden on a commit state (never at rest).
480
-
481
- The diff base defaults to the current process's start. To narrow it, mark an
482
- earlier state `reviewBase: true`: the most-recent in-process commit that entered
483
- such a state becomes the base, so only work committed after that milestone
484
- surfaces.
485
-
486
- ```yaml
487
- workflow:
488
- states:
489
- # …
490
- building:
491
- actor: agent
492
- reviewBase: true # the diff shown at review starts here…
493
- prompt: implement the plan
494
- on:
495
- "* **": review
496
- review:
497
- actor: human
498
- reviewWindow: true # …and is surfaced while resting here
499
- message: review the diff in your editor, then `gtd step human`
500
- on:
501
- "C": done
502
- "* **": building
503
- ```
504
-
505
- The unified template enables `reviewWindow: true` on `await-review` and marks
506
- `review-deciding` `reviewBase: true`, so the first review covers the whole cycle
507
- but each subsequent feedback round's window narrows to `last-review..HEAD`. The
508
- pure engine never observes an open window — it is opened/closed entirely at the
509
- edge; the real head is preserved under `refs/worktree/gtd/review-head` (the base
510
- under `refs/worktree/gtd/review-base`) for the window's lifetime — git's
511
- per-worktree ref namespace, so linked worktrees of one repository each get their
512
- own window. See [STATES.md §11](../STATES.md) for the full lifecycle.
513
-
514
- ### `reviewEntry:` — the review entry point (`gtd review <commitish>`)
515
-
516
- A state may separately declare `reviewEntry: true` (at most one across the whole
517
- workflow; forbidden on a commit state and on the initial state). It marks the
518
- state [`gtd review <commitish>`](cli.md#gtd-review-commitish---json) enters to
519
- start a BRAND NEW process reviewing `<commitish>..HEAD` — e.g. a colleague's PR
520
- branch with no gtd process of its own — reusing whatever `on`/
521
- `reviewWindow`/`reviewBase` machinery that state and its downstream states
522
- already declare, with zero duplicated logic.
523
-
524
- ```yaml
525
- workflow:
526
- states:
527
- # …
528
- review:
529
- actor: agent
530
- reviewEntry: true # `gtd review <commitish>` starts a new process here
531
- prompt: summarize <%~ it.processDiff %> for a human to check
532
- on:
533
- "* **": await-review
534
- ```
535
-
536
- `gtd review <commitish>` requires resting at the workflow's initial state with a
537
- clean tree, and requires the active workflow to declare a `reviewEntry` state.
538
- It resolves `<commitish>` (which must name an ancestor of HEAD other than HEAD
539
- itself) and writes one empty `gtd(human): <review-entry-state>` turn commit
540
- carrying the resolved hash as a `Gtd-Review-Base:` trailer — read back by
541
- `computeProcessRun` to override the new process's diff base (`it.startCommit`
542
- and `it.processDiff` then cover `<commitish>..HEAD`, and a downstream
543
- `reviewWindow: true` state's checkout window opens over that same range). The
544
- process's `retry`/squash trace boundary is untouched: the entry commit's own
545
- parent is a non-workflow commit, so the ordinary boundary rule already stops
546
- there. See [STATES.md §11](../STATES.md#11-the-review-checkout-window) for the
547
- full mechanism.
548
-
549
- The bundled default enables `reviewEntry: true` on `review-start-check` — the
550
- green-baseline gate in front of `reviewing`.
551
-
552
- ### `fixEntry:` — the fix entry point (`gtd fix`)
553
-
554
- A state may declare `fixEntry: true` (at most one across the whole workflow;
555
- forbidden on a commit state and on the initial state — the same rule family as
556
- `reviewEntry`). It marks the state [`gtd fix`](cli.md#gtd-fix---json) enters to
557
- start a BRAND NEW process that goes straight into repairing the current failing
558
- tests.
559
-
560
- ```yaml
561
- workflow:
562
- states:
563
- # …
564
- fix-check:
565
- actor: check
566
- fixEntry: true # `gtd fix` starts a new process here
567
- script: <%~ it.vars.testCommand %> || echo fail > .gtd/FEEDBACK.md
568
- on:
569
- "A .gtd/FEEDBACK.md": fixing # red -> repair loop
570
- "C": idle # green -> nothing to fix
571
- ```
572
-
573
- `gtd fix` requires resting at the workflow's initial state with a clean tree,
574
- and requires the active workflow to declare a `fixEntry` state. Unlike
575
- `gtd review`, it writes NO `Gtd-Review-Base:` trailer — a fix reviews its own
576
- fixes from the ordinary process start — so it simply writes one empty
577
- `gtd(human): <fix-entry-state>` turn commit and lets the workflow's own `on`
578
- routing take over. The bundled default enables `fixEntry: true` on `fix-check`:
579
- a red suite drops into the shared `fixing` loop and out through the review +
580
- squash tail; a green suite is a no-op back to `idle`. See
581
- [STATES.md §10](../STATES.md#10-the-bundled-workflow-template).
582
-
583
- ### Template variables
584
-
585
- Every `script`/`prompt`/`message`/`commit`/`model`/`memory`/`file` template is
586
- rendered as an Eta template (`it.<name>`) with:
587
-
588
- | Variable | Meaning |
589
- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
590
- | `startCommit` | The current process's diff base — normally the process start, but re-pointed to `<commitish>`'s hash for a process `gtd review <commitish>` started (see ["`reviewEntry:`"](#reviewentry--the-review-entry-point-gtd-review-commitish)). |
591
- | `currentCommit` | HEAD's hash at render time. |
592
- | `previousCommit` | The hash before the last transition (HEAD's parent, in-process). |
593
- | `state` | The state whose content is being rendered. |
594
- | `actor` | The actor this render is for. |
595
- | `processDiff` | `startCommit..HEAD` plus the pending working-tree diff. |
596
- | `lastDiff` | The diff of the last transition alone. |
597
- | `processCost` | The accumulated token cost of the current process — the sum of every `Gtd-Cost:` trailer recorded by `gtd step <actor> --cost=<n>` across its turn commits, plus the in-flight step's own `--cost` (so a `commit:` squash template sees the whole-process total). A number, `0` when none recorded. See ["Token cost"](#token-cost). |
598
- | `processCostByModel` | The same accumulated cost broken down per model: an array of `{ model, cost }`, highest-cost first, one entry per distinct `--model` tag (costs recorded with no `--model` group under `"unspecified"`). A squash template iterates it to itemize the feature's cost per model. Empty when none recorded. See ["Token cost"](#token-cost). |
599
- | `read(path)` | Reads a working-tree file (pending contents, not HEAD's) by repo-relative path. Throws for a missing/unreadable path — for a `commit:` template, that throw refuses the step (see [STATES.md §8](../STATES.md#8-the-squash-lifecycle)). |
600
- | `vars` | The merged three-layer variable map, always a flat `Record<string, string>` — see ["Variables"](#variables) below. |
601
-
602
- ### Token cost
603
-
604
- A loop driver that knows how many tokens the invocation it just drove cost — and
605
- on which model — can record both with
606
- `gtd step <actor> --cost=<n> [--model=<name>]` (`<n>` a non-negative number).
607
- gtd appends them to that turn's commit as a `Gtd-Cost: <n> <model>` trailer — a
608
- blank line then the trailer, below the untouched `gtd(<actor>): <from> → <to>`
609
- subject — so the per-turn cost and model are persisted in the git log:
610
-
611
- ```
612
- gtd(agent): building → reviewing
613
-
614
- Gtd-Cost: 1450 claude-opus-4-8
615
- ```
616
-
617
- `--model` is optional and requires `--cost` (a model tag with no token count
618
- records nothing to sum); a cost recorded without a model is grouped under
619
- `"unspecified"`. `gtd status` shows the process's running total and, when the
620
- breakdown adds information, a per-model split (a `Cost:` line with indented
621
- `<model>: <cost>` lines / a `cost` number and a `costByModel` array, omitted
622
- when nothing has been recorded). `gtd step --json` echoes what it recorded
623
- (`{ "state": …, "subject": …, "cost": 1450, "model": "claude-opus-4-8" }`).
624
-
625
- Every template sees the running total as `it.processCost` and the per-model
626
- breakdown as `it.processCostByModel` (the table above): the current process's
627
- turn-commit entries, plus the in-flight step's own `--cost`/`--model`. Their
628
- intended use is a `commit:` squash template, which renders against the pending
629
- tree as the process collapses — so the whole feature's total AND its per-model
630
- itemization (including the squashing step itself) land in the squash message
631
- even though the intermediate per-turn trailers are discarded with the turns they
632
- rode on:
633
-
634
- ```yaml
635
- done:
636
- commit: |
637
- feat: ship it
638
-
639
- Total token cost: <%= it.processCost %>
640
- <% it.processCostByModel.forEach(function(m){ %>
641
- - <%= m.model %>: <%= m.cost %>
642
- <% }) %>
643
- ```
644
-
645
- `--cost`/`--model` are accepted only by `gtd step` (a usage error on any other
646
- command), and gtd never interprets the number's unit (tokens, cents, whatever
647
- the driver records) or the model name — it only records, sums, and groups them.
648
-
649
- ## Sub-machines (`submachines:` / `use:`)
650
-
651
- Two OPTIONAL top-level keys let you factor repeated or complex clusters of
652
- states into reusable **sub-machines** that are expanded into ordinary concrete
653
- states at load time (see [STATES.md §13](../STATES.md) and
654
- `src/Submachines.ts`). They add NO runtime behavior — the engine only ever sees
655
- the expanded states — so a workflow authored with them is exactly a workflow
656
- written flat.
657
-
658
- ```yaml
659
- workflow:
660
- submachines:
661
- gate: # authored once
662
- params: [onGreen] # names referenced as `$onGreen` below
663
- states:
664
- check:
665
- actor: check
666
- script: <%~ it.vars.testCommand %>
667
- on:
668
- "A .gtd/FEEDBACK.md": blocked # a local state -> renamed by `as:`
669
- "C": "$onGreen" # a param -> bound by `with:`
670
- blocked:
671
- actor: human
672
- file: <%= it.vars.feedbackFile %>
673
- message: "fix the baseline"
674
- on: { "* **": check }
675
- use:
676
- - submachine: gate
677
- as: { check: start-check, blocked: start-blocked } # local -> concrete (omit for identity)
678
- with: { onGreen: planning } # bind each `$param`
679
- set: { check: { reviewEntry: true } } # optional extra fields on a renamed state
680
- states:
681
- idle:
682
- { actor: human, initial: true, message: "…", on: { "* **": start-check } }
683
- planning: { actor: agent, prompt: "…", on: { "* **": start-check } }
684
- ```
685
-
686
- Rules:
687
-
688
- - **`submachines: { <name>: { params, states } }`** — `states` are ordinary
689
- state definitions, except an `on`/`retry.otherwise` target or any whole field
690
- value may be a `$param` placeholder.
691
- - **`use:`** is an ordered list. Each entry names a `submachine:` and may set:
692
- - **`as:`** — a `local -> concreteName` rename map. Omit it (or a given local)
693
- for identity. Alias to the names a flat workflow would use to keep history
694
- and `git log` unchanged.
695
- - **`with:`** — bindings for the sub-machine's `$param`s.
696
- - **`set:`** — extra fields merged onto a renamed state (e.g. `reviewEntry`).
697
- - **Substitution is WHOLE-VALUE only.** `$name` resolves only when it is the
698
- entire value of a field or target — never a substring — so bash `$x`/`${x}`
699
- and Eta `<%= it.vars.x %>` inside content are untouched. Pass per-instance
700
- text (a whole message/prompt/`describe`) as a whole `$name` binding.
701
- - A target naming one of the sub-machine's own locals is rewritten via `as:`;
702
- any other target passes through verbatim (a concrete top-level state). `on`
703
- PATTERN keys are never substituted.
704
- - Expansion strips `submachines:`/`use:`; the merged `states` are then compiled
705
- and `validateDefinition`'d normally. Unknown sub-machine, unbound `$param`,
706
- and generated-name collisions are load-time errors, aggregated with the rest.
707
-
708
- `gtd init` scaffolds the EXPANDED flat config, so a fresh project never sees
709
- `submachines:`/`use:` unless it adds them itself. The bundled template is
710
- authored with them (dedup: `assertGreen`/`makeGreen`/`qaLoop`; comprehension:
711
- `planLoop`/`humanReview`/`specGate`/`packageLoop`) and its golden test pins that
712
- expansion is byte-identical to the flat form.
713
-
714
- ## Variables
715
-
716
- Every template — `script`/`prompt`/`message`/`commit`, and `model`/`memory`/
717
- `file` — sees `it.vars`: a flat `Record<string, string>` assembled from three
718
- layers, **later wins**:
719
-
720
- 1. **The workflow's own `vars:` key** (sibling to `states:`, shown above) — the
721
- workflow author's declared defaults. The unified template declares
722
- `vars: { testCommand: "npm test" }`, read by `checking`'s script as
723
- `<%~ it.vars.testCommand %>` (see
724
- [STATES.md §10](../STATES.md#10-the-bundled-workflow-templates)). The bundled
725
- template also declares two model-tier vars — `plannerModel` (default `smart`)
726
- and `coderModel` (default `base`) — that every agent state's `model:`
727
- resolves through (`model: <%= it.vars.plannerModel %>`), so repointing one
728
- var here (or via layer 2/3 below) changes every state on that tier at once.
729
- 2. **A top-level `.gtdrc` `vars:` key** (a sibling of `workflow:`, NOT nested
730
- inside it) — per-repo tuning without redefining the whole workflow. Subject
731
- to the same cwd→home deep merge as everything else in `.gtdrc` (innermost
732
- wins per-key).
733
- 3. **`GTD_<UPPERCASE-name>` environment variables** — highest precedence,
734
- checked at every invocation, case-insensitively against each name already
735
- declared by layer 1 or 2: `GTD_TESTCOMMAND` overrides `testCommand`. Unlike
736
- the other two layers, the environment can only OVERRIDE a name a config layer
737
- already declared — an uppercased env key can't round-trip back to an
738
- arbitrary camelCase name, so a `GTD_*` var matching no declared name is
739
- silently ignored.
740
-
741
- Values in layers 1–2 must be YAML scalars (string/number/boolean) — coerced to
742
- strings at load time; an object or array value is a load error, collected
743
- alongside every other config-shape finding (see
744
- ["Validation and errors"](#validation-and-errors)). Environment values are
745
- already strings.
746
-
747
- The bundled template also declares `stateDir: .gtd` — where its check scripts
748
- keep their own scratch/bookkeeping (the `.check-output` temp file,
749
- `review-deciding`'s exclusion of gtd's other state files when deciding whether
750
- the human hand-edited code this round). It is independent of the per-file vars
751
- (`todoFile`, `feedbackFile`, `reviewFile`, …): relocating one steering file
752
- outside `stateDir` needs no `stateDir` change (each check script creates that
753
- file's own parent directory as needed), and a project relocating its state
754
- wholesale sets both `stateDir` and the file vars it cares about.
755
-
756
- ```yaml
757
- # .gtdrc — overriding the unified template's testCommand
758
- vars:
759
- testCommand: npm run test:ci
760
- ```
761
-
762
- ```bash
763
- # highest precedence — beats both the workflow default and the .gtdrc value above.
764
- # The var is read at render time, so it shows up in the script `gtd next` prints
765
- # (and in what the loop driver then executes).
766
- GTD_TESTCOMMAND="npm run test -- --bail" gtd next
767
- ```
768
-
769
- A template reads any of it as `it.vars.<name>`:
770
-
771
- ```yaml
772
- workflow:
773
- vars:
774
- reviewer: alice
775
- states:
776
- working:
777
- actor: agent
778
- prompt: "Assigned reviewer: <%= it.vars.reviewer %>"
779
- model: "<%= it.vars.reviewModel %>" # a GTD_REVIEWMODEL override, or a .gtdrc vars: entry
780
- on:
781
- "* **": done
782
- ```
783
-
784
- No variable name is blessed by the engine — `testCommand` is workflow-authored
785
- data like any other `it.vars` entry, not a special key gtd interprets; a custom
786
- workflow is free to declare and read any names it likes.
787
-
788
- ## Validation and errors
789
-
790
- Config-shape problems (unknown keys, wrong types, unreadable file references)
791
- are collected together; if the shape is clean, the assembled definition is
792
- additionally run through the engine's own `validateDefinition` (see
793
- [STATES.md §9](../STATES.md#9-validation)). A bad config throws **one** error
794
- listing every finding, at load time — before anything touches the repository —
795
- never partially, and never deferred to step time:
796
-
797
- ```
798
- workflow config:
799
- - state "idle": must declare exactly one of script/prompt/message/commit (found 2)
800
- - state "idle": "on" target "nowhere" is not a defined state
801
- ```
802
-
803
- `validateDefinition`'s findings include the **semantic graph checks**: every
804
- `on` target and `retry.otherwise` must name a defined state, and every state
805
- must be **reachable** from the initial state by walking `on` targets and
806
- `retry.otherwise` redirects — an unreachable state (typically a typo'd rename or
807
- a leftover) is a load error like any other, since a workflow is bound to a
808
- project and edited as a project-wide change.
809
-
810
- Many of these problems never reach gtd at all if your editor validates against
811
- the published schema: `schema.json` fully types the `workflow:` key (state
812
- shape, content kinds, `on`/`retry` structure, the `modes:` map), so a
813
- yaml-language-server-style editor flags unknown keys and wrong types as you
814
- type. The rules JSON Schema cannot express — exactly one content kind, exactly
815
- one `initial: true`, targets naming defined states, reachability — remain the
816
- compiler's job at load time.
817
-
818
- Other load failures:
819
-
820
- - **Parse errors** (malformed YAML/JSON) — message includes the offending
821
- filename.
822
- - **Non-object top-level** — a YAML list or `null` at the root is rejected with
823
- the filename in the message.
824
- - **Unknown top-level key** — anything besides
825
- `workflow`/`vars`/`modes`/`$schema` emits
826
- `Invalid gtd config: <field>: <reason>`.
827
- - **A bad top-level `vars:` entry** — an object/array value fails the same way
828
- as a bad workflow-level `vars:` entry (see ["Variables"](#variables)), one
829
- aggregated error:
830
- `gtd config:\n - "vars.<name>" must be a string, number, or boolean, got <type>`.
831
-
832
- All of these exit **1** and write to **stderr**, never stdout.
833
-
834
- ## Lookup and precedence
835
-
836
- gtd walks from the current working directory **up to your home directory** (or
837
- to the filesystem root when cwd is outside home), collecting every `.gtdrc` it
838
- finds along the way. All found levels are **deep-merged**, with the **innermost
839
- (cwd) config winning** on conflicts — so a shared `.gtdrc` in a worktree-parent
840
- directory cascades to every checkout beneath it, while any individual checkout
841
- can still override with its own `.gtdrc`.
842
-
843
- ## `gtd init`
844
-
845
- gtd ships the bundled unified workflow as its **built-in default**, so a state
846
- command works out of the box with **no** config and no init at all. `gtd init`
847
- is therefore optional — it just seeds the two things most projects tune:
848
-
849
- ```bash
850
- gtd init # takes no argument
851
- ```
852
-
853
- `gtd init` writes a **minimal** `.gtdrc.json` at the repository root — a
854
- `$schema` link (so editors pick up completion/validation), a
855
- `vars: { "testCommand": "npm test" }` entry (the var the built-in workflow's
856
- check script reads), and a ready-to-edit `modes:` formatting suggestion (below).
857
- It writes **no** `workflow:` key — the built-in default is used until you
858
- declare your own. It takes **no argument** — passing one is a usage error
859
- (`gtd init: too many arguments — init takes no argument`).
860
-
861
- `gtd init` writes only config — it derives no git state — so it need not run in
862
- a git repository. It runs at a **repository root** or in a directory **outside
863
- any repository** (scaffolding a shared config a nested repo picks up by walking
864
- up — see the parent-dir note under
865
- [file references](#content-values-inline-or-a-file-reference)); its guidance
866
- adjusts to say there is nothing to commit when it is not in a repo. It refuses
867
- only to write into a repository **subdirectory**, where the upward config walk
868
- would never find the file. Either way, it will not overwrite an existing config
869
- at that directory (remove it first to re-scaffold).
870
-
871
- The [`modes:`](#modes--pluggable-steering-file-modes) block it seeds is a
872
- ready-to-edit suggestion — a Prettier `format:` for each built-in steering-file
873
- mode the default workflow uses:
874
-
875
- ```jsonc
876
- "modes": {
877
- "qa": { "format": "npx prettier --write <%= it.file %>" },
878
- "review": { "format": "npx prettier --write <%= it.file %>" },
879
- "prose": { "format": "npx prettier --write <%= it.file %>" }
880
- }
881
- ```
882
-
883
- Only `format:` is declared, so gtd's built-in `qa`/`review` validators still do
884
- the validating, and `prose` (a format-only built-in with no validator of its own
885
- — see the `mode:` section above) still validates nothing (the two halves layer
886
- independently). gtd ships no formatter, so this is the one default it suggests:
887
- a fresh repo auto-formats its steering files out of the box, and you edit or
888
- drop the block freely (swap Prettier for dprint, point at a script, or delete
889
- the key).
890
-
891
- The built-in default workflow has three entry points into a shared review/squash
892
- tail (all walked through in
893
- [STATES.md §10](../STATES.md#10-the-bundled-workflow-templates)):
894
-
895
- - **the simple flow** — creating `.gtd/TODO.md` starts idle → planning ⇄
896
- plan-review → building → checking ⇄ fixing (→ escalate) → reviewing →
897
- await-review.
898
- - **the advanced flow** — creating `.gtd/REQUIREMENTS.md` starts the fuller
899
- machine: two-phase Q&A planning (product then architecture), package
900
- decomposition, a per-package parallel build queue with an agentic spec-review
901
- gate, then the same shared tail.
902
- - **`gtd review <commitish>`** — enters the shared `reviewing` state directly to
903
- review a foreign branch.
904
-
905
- Both file-created flows converge on `reviewing` → `await-review`, and a full
906
- sign-off squashes the whole cycle into one commit. To CUSTOMIZE any of this, add
907
- a [`workflow:`](#the-workflow-key) key that fully replaces the default.
908
-
909
- The file is written **uncommitted** — review it, then commit `.gtdrc.json`
910
- before your first `gtd step` (an uncommitted file is a pending change the
911
- initial state's `* **` edge would otherwise capture). `gtd init` refuses if the
912
- repo root already has its OWN gtd config (remove it first to re-init; a
913
- global/ancestor config such as `~/.gtdrc` does not count) and requires the
914
- repository root (or a directory outside any repo).
915
-
916
- ## A complete example
917
-
918
- A three-state note-taking machine: draft, revise-or-accept, and a squashed
919
- commit — one prompt/message pair reading a workflow-declared `it.vars` entry:
920
-
921
- ```yaml
922
- # .gtdrc.yaml
923
- workflow:
924
- vars:
925
- reviewer: alice
926
- states:
927
- idle:
928
- actor: human
929
- initial: true
930
- message: |
931
- No active note. Write NOTE.md with what you want written up, then run
932
- `gtd step human`.
933
- on:
934
- "* **": drafting
935
-
936
- drafting:
937
- actor: agent
938
- prompt: |
939
- Read NOTE.md and draft a short write-up in DRAFT.md.
940
- Reviewer of record: <%= it.vars.reviewer %>.
941
- Leave DRAFT.md uncommitted and finish your turn.
942
- on:
943
- "* **": revising
944
-
945
- revising:
946
- actor: human
947
- message: |
948
- DRAFT.md holds the draft. Edit it for another round, or run
949
- `gtd step human` with no edits to accept it and write the commit
950
- message.
951
- on:
952
- "C": working
953
- "* **": drafting
954
-
955
- working:
956
- actor: agent
957
- prompt: |
958
- The draft is accepted. Write COMMIT_MSG.md with one conventional-
959
- commits message for the whole cycle, then finish your turn.
960
- on:
961
- "A COMMIT_MSG.md": done
962
- "M COMMIT_MSG.md": done
963
-
964
- done:
965
- commit: '<%~ it.read("COMMIT_MSG.md") %>'
966
- ```
967
-
968
- A clean `gtd step human` at `revising` (the `C` event) accepts the draft and
969
- moves to `working`; any other pending change loops back to `drafting`. Writing
970
- `COMMIT_MSG.md` at `working` enters `done`, which squashes every commit since
971
- `idle` into one, using `COMMIT_MSG.md`'s content as the message, and discards
972
- the file.
973
-
974
- ## Recipe: a per-task builder loop without a script state
975
-
976
- A `picking`-style queue arbiter (the bundled unified template's advanced flow
977
- carries one — see
978
- [STATES.md §10](../STATES.md#10-the-bundled-workflow-templates)) is a `script`
979
- state that turns a task-queue glob into a diff patterns can see. A driver that
980
- can't run scripts can get the same per-task loop out of pure prompt/`on`
981
- declarations instead, at the cost of trusting the agent to honor a marker
982
- protocol rather than a deterministic `ls`: see
983
- [docs/design/work-packages.md §3](design/work-packages.md) ("Option B —
984
- agent-encoded verdict") for the worked example.
985
-
986
- ## A fuller example: two-phase planning, task decomposition, agent-prepared review
987
-
988
- The example above is deliberately small (it is close to the bundled unified
989
- template's simple flow). For a heavier machine — Q&A planning loops, an
990
- architecture phase, task decomposition, the deterministic per-task `picking`
991
- arbiter, and agent-prepared `.gtd/REVIEW.md` review — the unified template's
992
- **advanced flow** (started by creating `.gtd/REQUIREMENTS.md`) is exactly that,
993
- walked through in
994
- [STATES.md §10](../STATES.md#10-the-bundled-workflow-templates).