@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.
package/README.md CHANGED
@@ -70,55 +70,27 @@ review and commit the file before your first `gtd step`. `gtd init` takes no
70
70
  argument and refuses to clobber an existing config; it may also run in a plain
71
71
  parent directory (not a git repo) to seed a shared config a nested repo picks
72
72
  up. To customize the machine itself, add a `workflow:` key (there is no default
73
- fallback to merge over — a `workflow:` is the whole definition). See
74
- [Configuration](docs/configuration.md#gtd-init) for the details.
73
+ fallback to merge over — a `workflow:` is the whole definition).
75
74
 
76
75
  ## How it works
77
76
 
78
77
  gtd is a small **pattern machine**: named states, each awaiting one actor and
79
78
  carrying one piece of content (a script, a prompt, a message, or a squash commit
80
- template), with an ordered set of change-patterns routing to the next state. A
81
- handful of commands drive it:
82
-
83
- - **`gtd step <actor>`** — authenticate as `<actor>` and perform the one
84
- transition the pending changes match.
85
- - **`gtd next [--json]`** — print whichever actor is awaited and what they
86
- should do, without mutating anything.
87
- - **`gtd status`** — a dry-run report of the resolved state and which pattern
88
- each pending change matches.
89
- - **`gtd review <commitish>`** — start a brand new review process reviewing
90
- `<commitish>..HEAD` (e.g. a colleague's PR branch), reusing the workflow's
91
- existing review/feedback machinery over that diff.
92
- - **`gtd fix`** — start a brand new process that goes straight into repairing
93
- the current failing tests, then runs the shared review/squash tail. If the
94
- suite is already green there is nothing to fix, and the log is left untouched
95
- — no commit is left behind.
96
- - **`gtd abandon`** — end the process underway without completing it: rewind to
97
- the commit it started from, keeping everything it produced as uncommitted
98
- changes (nothing is discarded).
99
- - **`gtd restore`** — undo the last squash (or `gtd abandon`) by hard-resetting
100
- HEAD back to its retained pre-squash tip, bringing the turn-by-turn history
101
- back. Refuses on a dirty working tree, when there is nothing retained to
102
- restore, or when HEAD has since moved past it with commits that would be lost.
103
- - **`gtd visualize`** — serve an interactive diagram of the active workflow on a
104
- local web server: the main flow (each sub-machine collapsed to one box, with
105
- its own diagram below — pan/zoom with scroll, drag, or the corner controls),
106
- per-state details including the state's own raw prompt/message/ script text,
107
- and a "Current state" panel showing where the active process rests, its
108
- pending changes, and which action leads where. The panel and diagram highlight
109
- refresh live (~every 3s) while the page is open, so advancing the process
110
- elsewhere shows up without a manual refresh. `gtd version` (or
111
- `gtd --version`/`-v`) prints the installed version and exits; `gtd help` (or
112
- `gtd --help`/`-h`) prints the command list. Both short-circuit before any repo
113
- work, so they run anywhere.
79
+ template), with an ordered set of change-patterns routing to the next state.
114
80
 
115
81
  The loop is one beat, repeated: run `gtd next --json` and dispatch on `kind` —
116
82
  `"message"` means it's a human's move (stop and hand off); `"script"` means the
117
83
  driver runs `content` itself, then steps its actor; `"prompt"` means feed
118
84
  `content` to your agent, then run `gtd step <actor>` once it's done. gtd itself
119
- never executes anything — the driver owns running scripts. See
120
- [STATES.md](STATES.md) for the model and [Driving the loop](docs/loop.md) for
121
- the full protocol.
85
+ never executes anything — the driver owns running scripts.
86
+
87
+ An `on` edge may also carry a short imperative `action` (e.g. `Accept plan`)
88
+ alongside its existing `describe` sentence — a human-facing name for the choice
89
+ itself, not just the underlying glob pattern. `gtd status` (plain and `--json`)
90
+ and `gtd visualize`'s diagram and inspector all render the two composed
91
+ together: the `action` leads when the edge declares one, with the raw pattern
92
+ (and `describe`) still shown alongside it, so a human choosing between routes
93
+ reads intent first, glob second.
122
94
 
123
95
  The unified workflow has **entry points behind a green-baseline gate, into one
124
96
  shared tail**. Every entry first runs your test suite and only starts once it's
@@ -154,27 +126,154 @@ dropped — a build turn that addresses nothing is refused). Ticking every box
154
126
  with no comment is the sign-off, which collapses the whole cycle into one commit
155
127
  (a **squash finale** whose message an agent drafts). Stepping with a box still
156
128
  unticked and no comment is refused (finish reviewing first), as is deleting
157
- `.gtd/REVIEW.md`. The same review tail also has a direct entry point —
158
- `gtd review <commitish>` starts a brand new process reviewing
159
- `<commitish>..HEAD` with no cycle of its own, e.g. a colleague's PR branch. Its
160
- squash keeps and describes only the fixes made _during_ the review (not the
161
- reviewed changeset); a clean sign-off with no fixes becomes an empty
162
- `chore: human review` commit. A fourth entry, `gtd fix`, starts from a clean
163
- `idle` and goes straight into repairing a red baseline — repair, review, and
164
- squash into one commit. If the suite is already green there is nothing to fix,
165
- and the log is left untouched — no commit is left behind. See
166
- [STATES.md](STATES.md#10-the-bundled-workflow-template) for the full shape. The
167
- workflow is just `.gtdrc` config — edit it or write your own (see
168
- [Configuration](docs/configuration.md)). Every agent state routes its model
169
- through two `vars` tiers — `plannerModel` (heavier planning and review) and
170
- `coderModel` (the coding turns) — so you can repoint the models globally in one
171
- place (a `vars:` edit or a `GTD_PLANNERMODEL` override) instead of per state.
172
- Steering-file path vars (`feedbackFile`, `reviewFile`, …) work the same way, and
173
- now propagate to the `on` patterns that route on them too — a repointed path var
174
- actually reroutes the machine, not just the templates that read/write the file.
175
- A separate `stateDir` var (default `.gtd`) names only where the check scripts
176
- keep their own scratch/bookkeeping, independent of the per-file vars — relocate
177
- one steering file without touching it.
129
+ `.gtd/REVIEW.md`.
130
+
131
+ The same review tail also has a direct entry point — `gtd review <commitish>`
132
+ starts a brand new process reviewing `<commitish>..HEAD` with no cycle of its
133
+ own, e.g. a colleague's PR branch. Its squash keeps and describes only the fixes
134
+ made _during_ the review (not the reviewed changeset); a clean sign-off with no
135
+ fixes becomes an empty `chore: human review` commit. A fourth entry, `gtd fix`,
136
+ starts from a clean `idle` and goes straight into repairing a red baseline —
137
+ repair, review, and squash into one commit. If the suite is already green there
138
+ is nothing to fix, and the log is left untouched — no commit is left behind.
139
+
140
+ Every agent state routes its model through two `vars` tiers — `plannerModel`
141
+ (heavier planning and review) and `coderModel` (the coding turns) — so you can
142
+ repoint the models globally in one place (a `vars:` edit or a `GTD_PLANNERMODEL`
143
+ override) instead of per state. Steering-file path vars (`feedbackFile`,
144
+ `reviewFile`, …) work the same way, and propagate to the `on` patterns that
145
+ route on them too — a repointed path var actually reroutes the machine, not just
146
+ the templates that read/write the file. A separate `stateDir` var (default
147
+ `.gtd`) names only where the check scripts keep their own scratch/bookkeeping,
148
+ independent of the per-file vars — relocate one steering file without touching
149
+ it.
150
+
151
+ To inspect or change the machine itself, see [Configuration](#configuration) —
152
+ the workflow is just `.gtdrc` config.
153
+
154
+ ## Commands
155
+
156
+ ```
157
+ Usage: gtd [command] [options]
158
+
159
+ Commands:
160
+ (no command), loop
161
+ Launch the loop driver (bin/gtd), which repeatedly drives
162
+ an agent through gtd next/gtd step calls until the
163
+ workflow rests at a human gate (a non-autonomous state)
164
+ or settles. A bare gtd invocation and gtd loop both
165
+ launch it identically
166
+ init Scaffold a minimal .gtdrc.json for this repo, seeding the
167
+ default variables you are most likely to change (the test
168
+ command) and a Prettier formatting suggestion. gtd runs its
169
+ built-in workflow by default, so no workflow is written —
170
+ add a workflow: key only to customize the machine itself.
171
+ Takes no argument. Run once per repo; refuses if a gtd
172
+ config already exists. Leaves the file uncommitted for you
173
+ to review and commit
174
+ step <actor> Authenticate as <actor>, match the resolved rest's
175
+ declared patterns against the pending changes, and commit
176
+ (or squash) the one resulting transition. Pass
177
+ --cost=<n> (optionally --model=<name>) to record the
178
+ just-finished invocation's token cost and model on the
179
+ turn commit (summed into it.processCost/processCostByModel)
180
+ review <commitish>
181
+ Start a NEW review process at the workflow's declared
182
+ review-entry state (reviewEntry: true), reviewing
183
+ <commitish>..HEAD — e.g. a colleague's PR branch. Requires
184
+ a clean tree resting at the workflow's initial state
185
+ fix Start a NEW process at the workflow's declared fix-entry
186
+ state (fixEntry: true) that goes straight into repairing the
187
+ current failing tests. Requires a clean tree resting at the
188
+ workflow's initial state
189
+ abandon End the process currently underway without completing it:
190
+ close any open review checkout window, then rewind HEAD to
191
+ the commit the process started from, keeping everything it
192
+ produced as uncommitted changes. A no-op when no process is
193
+ underway
194
+ restore Hard-reset HEAD back to the pre-squash tip retained by the
195
+ last squash/abandon (refs/worktree/gtd/history), undoing a
196
+ squash or bringing back an abandoned process's turns.
197
+ Refuses on a dirty working tree, when there is no retained
198
+ history, or when HEAD has advanced past the squash with
199
+ commits that would be lost
200
+ next Print the resolved rest's rendered script/prompt/message
201
+ (no mutation)
202
+ status Print the resolved rest's state/actor and which declared
203
+ pattern (if any) each pending change matches (no mutation)
204
+ validate Format and validate the steering file the resolved rest
205
+ declares, with its mode's commands (its file:/mode:);
206
+ exits non-zero with the findings when it is invalid
207
+ lsp Start the LSP server for .gtd/ steering files (stdio)
208
+ visualize Serve an interactive diagram of the active workflow on a
209
+ local web server (--port <n>, --no-open; --json prints the
210
+ model and exits)
211
+ version Print version and exit
212
+ help Print this help and exit
213
+
214
+ Options:
215
+ --json Output structured JSON instead of plain text
216
+ --port=<n> (gtd visualize only) port to serve on (default: a free port)
217
+ --no-open (gtd visualize only) do not open the browser
218
+ --cost=<n> (gtd step only) record the invocation's token cost
219
+ --model=<name> (gtd step only, with --cost) tag that cost's model
220
+ --once (bare gtd or gtd loop only) run exactly one loop beat (one
221
+ human-gate capture, one script check+step, or one agent
222
+ prompt+step), then exit
223
+ --version, -v Print version and exit
224
+ --help, -h Print this help and exit
225
+ ```
226
+
227
+ `--version` (`-v`) / `gtd version` and `--help` (`-h`) / `gtd help`
228
+ short-circuit before any git or repository-state work — they run outside a repo
229
+ and in any repo state. Bare `gtd` (no subcommand) and `gtd loop` both launch the
230
+ loop driver immediately — neither is a usage error. Any other, truly unknown
231
+ subcommand is a usage error: it prints the help text and exits 1 without
232
+ touching the repository. Every other (recognized) command must be run from the
233
+ **repository root** — gtd derives the workflow, pending changes, and process
234
+ history relative to cwd, so it refuses with a clear error if invoked from a
235
+ subdirectory.
236
+
237
+ `--json`, `--cost=<n>`, and `--model=<name>` (the latter two only for
238
+ `gtd step`) are the only long options the compiled bundle recognizes. Any other
239
+ `--` option (including a typo like `--jsn`) is rejected with a usage error
240
+ rather than silently ignored, so a mistyped flag can never degrade a JSON caller
241
+ to plain-text mode. A bare `--cost`/`--model` with no value, a non-numeric or
242
+ negative `--cost`, an empty `--model`, `--model` without `--cost`, or either
243
+ flag on any command other than `gtd step` are all usage errors.
244
+
245
+ `--once` is a separate, bash-level flag handled entirely by the `bin/gtd` driver
246
+ itself, stripped before anything reaches the bundle.
247
+
248
+ ### `gtd status`'s `Next:`/`next`
249
+
250
+ Both plain and `--json` output include a headline preview of what would happen
251
+ next: the first declared `on` edge whose pattern matches the pending changes AS
252
+ A WHOLE (the same first-match-wins semantics `gtd step` itself uses), using its
253
+ `action` when the edge declares one, else its raw pattern, alongside its target
254
+ state. Plain output prints a `Next: <action-or-pattern> → <target>` line (or
255
+ `Next: (no match — nothing would happen)`); `--json`'s `next` key mirrors it as
256
+ `{ action?, pattern, target }`, or `null` on no match.
257
+
258
+ This reports the **declared** route only: a capped `retry` may redirect
259
+ elsewhere at real step time, which `Next:`/`next` does not apply — it previews
260
+ what the declared `on` patterns would match, not a guarantee of where a real
261
+ `gtd step` lands.
262
+
263
+ ### Error envelope
264
+
265
+ Every command, in `--json` mode, reports a failure as a machine-readable
266
+ envelope on **stdout**, and still exits 1:
267
+
268
+ ```json
269
+ { "state": "error", "prompt": "<message>" }
270
+ ```
271
+
272
+ A human-readable `gtd: <message>` line is still written to **stderr** regardless
273
+ of `--json` — the envelope adds a structured stdout channel, it does not replace
274
+ the plain-text one.
275
+
276
+ ## Driving the loop
178
277
 
179
278
  Bare `gtd` (or `gtd loop`) is a ready-to-run driver for the whole protocol —
180
279
  point it at a repo and it runs the loop until it's your turn. It drives the
@@ -182,58 +281,339 @@ autonomous states (agent turns, check runs) and stops at the first
182
281
  non-autonomous one: reaching a human gate it prints the gate and exits. You act
183
282
  by editing files (answer a plan question, tick a review box, fix code) and
184
283
  re-launching it — its opening move captures whatever you left, so you never run
185
- `gtd step human` by hand. Anything richer at that boundary — opening your
186
- editor, desktop notifications, terminal-multiplexer status — is the job of an
187
- outer wrapper around `gtd`, not of the loop itself. Pass `--once` to restrict a
188
- run to exactly one beat — one human-gate capture, one check, or one agent turn —
189
- instead of driving all the way to idle. Bare `gtd` prints one line per event —
190
- colored and emoji on a real terminal, plain ASCII under `NO_COLOR` or when piped
191
- — and redirects the noisier agent/check/step subprocess output to a
192
- per-repo/per-worktree log file (its path is the run's first output line, ready
193
- to `tail -f`). See [Driving the loop](docs/loop.md).
194
-
195
- Before wiring gtd into a repo, note the
196
- [repository requirements](docs/cli.md#repository-requirements) — most
197
- importantly: gitignore everything your scripts write.
198
-
199
- Editor integration: `gtd lsp` starts an LSP server over stdio for `.gtd/`
200
- steering files — a symbol per `review`-mode chunk that still has an unchecked
201
- hunk (an outline of the packages left to review) plus check/uncheck actions over
202
- those chunks, go-to-definition from a `review`-mode hunk line into the file it
203
- points at (at its `#line`), symbols over a `qa`-mode file's open questions,
204
- diagnostics for both (live as you edit), and a `gtd.openSteeringFile` command
205
- that jumps to the current state's steering file. Config-driven via each state's
206
- `file:`/`mode:` (see [CLI reference](docs/cli.md#gtd-lsp)) — falls back to
207
- basename dispatch (`REVIEW.md` → `review`) with no config in sight. `qa` and
208
- `review` are gtd's built-in steering-file MODES with a VALIDATOR gtd itself
209
- implements; a mode's `format:` and `validate:` are shell commands a workflow (or
210
- a project's `.gtdrc`) declares for itself, so you bring your own formatter and
211
- your own checkers (see
212
- [Configuration](docs/configuration.md#modes--pluggable-steering-file-modes)). A
213
- third built-in, `prose` — format-only, no validator, used by the simple flow's
214
- plan file — has no live editor support: `gtd lsp` publishes no symbols or
215
- diagnostics for it, though `gtd validate` and the `gtd step` gate still format
216
- and validate it like any other mode. Both halves are enforced by `gtd validate`
217
- and the `gtd step` gate.
284
+ `gtd step human` by hand.
285
+
286
+ Anything richer at that boundary — opening your editor, desktop notifications,
287
+ terminal-multiplexer status — is the job of an outer wrapper around `gtd`, not
288
+ of the loop itself; see
289
+ [Terminal-multiplexer status: a herdr wrapper](#terminal-multiplexer-status-a-herdr-wrapper)
290
+ below for a worked example. Pass `--once` to restrict a run to exactly one beat
291
+ — one human-gate capture, one check, or one agent turn — instead of driving all
292
+ the way to idle.
293
+
294
+ Bare `gtd` prints one line per event — colored and emoji on a real terminal,
295
+ plain ASCII under `NO_COLOR` or when piped — and redirects the noisier
296
+ agent/check/step subprocess output to a per-repo/per-worktree log file (its path
297
+ is the run's first output line, ready to `tail -f`). Any execution that FAILS
298
+ also replays its last 20 lines of output inline on stderr, on top of the log
299
+ still holding the complete record: an agent turn that fails stops the loop (it
300
+ would fail identically next lap), while a check script that exits non-zero is
301
+ reported as a warning and the loop carries on, because the outcome lives in the
302
+ tree, not in the script's exit code.
218
303
 
219
304
  A workflow state can declare an optional `label:` — a human-readable display
220
- name surfaced in `gtd next --json`/`gtd status`. The reference `bin/gtd` driver
221
- uses it for its per-beat progress lines; an outer wrapper (a terminal
222
- multiplexer, a notifier) can use it the same way.
223
-
224
- ## Documentation
225
-
226
- - [STATES.md](STATES.md) — the full pattern-machine specification: the model,
227
- the pattern grammar, resolution, retry, the squash lifecycle, reusable
228
- sub-machines, and the bundled workflow template
229
- - [CLI reference](docs/cli.md) — every command, exit codes, JSON schemas,
230
- repository requirements
231
- - [Driving the loop](docs/loop.md) — the reference loop driver,
232
- `gtd`/`gtd loop`, custom agents
233
- - [Configuration](docs/configuration.md) — `gtd init`, the `.gtdrc` `workflow:`
234
- schema, lookup
235
- - [Upgrading](docs/upgrading.md) — breaking changes and migration
236
- - [Development](docs/development.md) — building, testing, releasing
305
+ name surfaced in `gtd next --json`/`gtd status`. The driver uses it for its
306
+ per-beat progress lines; an outer wrapper (a terminal multiplexer, a notifier)
307
+ can use it the same way.
308
+
309
+ ### Terminal-multiplexer status: a herdr wrapper
310
+
311
+ [herdr](https://herdr.dev) shows a per-pane status in its sidebar. This wrapper
312
+ reports `gtd`'s own lifecycle to that status without any herdr-specific
313
+ knowledge in `gtd` itself: `working` while the loop drives, `blocked` when it
314
+ comes to rest on a human (a gate, a stall, a non-zero exit), and `idle`
315
+ (rendered as "done" once the pane's tab is unfocused) when a run ends without
316
+ anything owed. It needs nothing from `gtd` beyond `gtd next --json`'s `.actor`
317
+ field, which the loop's driver already reads at every beat.
318
+
319
+ Save this as `~/.local/bin/gtdh`, `chmod +x` it, and run `gtdh` in place of
320
+ `gtd` — it's a plain bash file, so it works from fish or any other shell, and it
321
+ forwards every argument (`gtdh --once`, `gtdh status`, ...).
322
+
323
+ ```bash
324
+ #!/usr/bin/env bash
325
+ # Report gtd's own lifecycle to the herdr pane it runs in: working while the
326
+ # loop drives, blocked when it rests on you, idle (shown as "done") otherwise.
327
+ # Needs nothing from gtd but `gtd next --json`. Outside herdr it is a plain
328
+ # passthrough. Requires `jq` — so does gtd's own loop driver.
329
+ set -uo pipefail
330
+
331
+ pane="${HERDR_PANE_ID:-}"
332
+
333
+ # `--agent gtd` is deliberate: herdr only lets screen detection (or a visible
334
+ # approval prompt) override a reported state when the reported label names a
335
+ # KNOWN agent. "gtd" names none, so the loop's `claude -p` turns can never move
336
+ # this pane's status.
337
+ report() { # report <state>
338
+ [ -n "$pane" ] || return 0
339
+ herdr pane report-agent "$pane" \
340
+ --source custom:gtd --agent gtd --state "$1" >/dev/null 2>&1 || true
341
+ }
342
+
343
+ report working
344
+ trap 'report blocked; exit 130' INT TERM
345
+
346
+ # HERDR_PANE_ID is unset for the loop: herdr's Claude Code hook needs it, and
347
+ # without it no `claude -p` turn reports a claude SESSION identity for this
348
+ # pane. That is load-bearing, not cosmetic — a pane that owns one rejects every
349
+ # later state report, so the report below would be dropped.
350
+ env -u HERDR_PANE_ID gtd "$@"
351
+ rc=$?
352
+
353
+ # Whose turn is it now? `gtd next --json` is mutation-free. A human actor means
354
+ # gtd is waiting on you; anything else means the run ended with nothing owed.
355
+ actor="$(gtd next --json 2>/dev/null | jq -r '.actor // ""' 2>/dev/null || true)"
356
+
357
+ if [ "$rc" -ne 0 ] || [ -z "$actor" ] || [ "$actor" = human ]; then
358
+ report blocked
359
+ else
360
+ report idle
361
+ fi
362
+
363
+ exit $rc
364
+ ```
365
+
366
+ A few things to know before relying on it:
367
+
368
+ - **Give `gtd` its own pane.** Run an interactive `claude` (or any agent whose
369
+ herdr integration reports session identity) in the same pane and that pane
370
+ owns a `herdr:claude` session until the process exits; while it does, the
371
+ wrapper's reports are silently ignored and the sidebar stops tracking `gtd`.
372
+ - **`blocked` covers the resting `idle` state too** — gtd's own `idle` is a
373
+ human gate (it waits for you to write a steering file), so a finished cycle
374
+ reads as "your turn", which is what it is.
375
+ - The status **persists after the wrapper exits** — that's the point: the
376
+ sidebar keeps showing which worktree is waiting on you. Hand the pane back to
377
+ ordinary detection with
378
+ `herdr pane release-agent "$HERDR_PANE_ID" --source custom:gtd --agent gtd`.
379
+ - A non-zero exit (a stall, a refusal, an error) reports `blocked`: herdr has no
380
+ failure state, and those all need a human.
381
+ - Ctrl-C reports `blocked` rather than leaving a stale `working`.
382
+
383
+ Optionally,
384
+ `herdr pane report-metadata "$HERDR_PANE_ID" --source custom:gtd-display --token summary=<text>`
385
+ sets a value renderable as `$summary` in an Agent sidebar row, if you want more
386
+ than the state itself.
387
+
388
+ ## Configuration
389
+
390
+ gtd reads an optional `.gtdrc` config file via
391
+ [cosmiconfig](https://github.com/cosmiconfig/cosmiconfig). With no `workflow:`
392
+ configured anywhere in the cwd→home config chain, the bundled unified workflow
393
+ is used automatically, so a state command works out of the box with no config at
394
+ all. Supported filenames (searched in this order):
395
+
396
+ - `.gtdrc`
397
+ - `.gtdrc.json`
398
+ - `.gtdrc.yaml`
399
+ - `.gtdrc.yml`
400
+ - `gtd.config.json`
401
+ - `gtd.config.yaml`
402
+
403
+ ### Schema
404
+
405
+ `.gtdrc` has exactly three blessed top-level keys:
406
+
407
+ - **`workflow`** (object, optional) — the whole machine definition (its states,
408
+ plus its own `vars:` defaults and `modes:`). Absent = gtd's built-in default
409
+ is used. Declare it to fully REPLACE that default with your own machine (there
410
+ is no `extends`/merge).
411
+ - **`vars`** (object, optional) — a flat `name -> scalar` map, one layer of the
412
+ merged `it.vars` every template sees.
413
+ - **`modes`** (object, optional) — steering-file modes (`format:`/`validate:`
414
+ shell commands), layered over the active workflow's own `modes:` and gtd's
415
+ built-in validators, so a project can plug in its formatter or linter without
416
+ re-declaring that mode on the workflow itself.
417
+ - **`$schema`** (string, optional) — stripped before validation, so it never
418
+ counts as an unknown key. Point it at the published schema for editor-backed
419
+ autocompletion (this is what `gtd init` writes):
420
+
421
+ ```
422
+ https://cdn.jsdelivr.net/npm/@pmelab/gtd/schema.json
423
+ ```
424
+
425
+ That URL serves `schema.json` straight out of the published npm tarball, so it
426
+ always matches the latest release. Pin it to a major with
427
+ `@pmelab/gtd@8/schema.json`, or point at your own install
428
+ (`./node_modules/@pmelab/gtd/schema.json`) to work offline.
429
+
430
+ Any other top-level key is **rejected**. The engine blesses no VARIABLE NAMES
431
+ either — `testCommand` is workflow-authored data like any other `it.vars` entry,
432
+ not a special key gtd interprets.
433
+
434
+ ### The `workflow:` key
435
+
436
+ A declared `workflow:` key fully REPLACES gtd's built-in default. The built-in
437
+ default is itself a YAML asset (`src/workflows/unified.yaml`) compiled through
438
+ the exact same compiler your own `workflow:` value goes through — no privileged
439
+ code path. Its shape:
440
+
441
+ ```yaml
442
+ workflow:
443
+ vars: # optional — the workflow's own declared `it.vars` defaults
444
+ anyKey: anyScalarValue
445
+ modes: # optional — steering-file modes a state's `mode:` may name
446
+ <name>:
447
+ format: <shell command> # at least one of format/validate
448
+ validate: <shell command>
449
+ states:
450
+ <name>:
451
+ actor: <string> # forbidden on a commit state, required otherwise
452
+ script: <string> # exactly one of script/prompt/message/commit
453
+ prompt: <string>
454
+ message: <string>
455
+ commit: <string>
456
+ on: # a mapping, DECLARATION ORDER PRESERVED
457
+ "<pattern>": <targetState> # short form
458
+ "<pattern>": {
459
+ to: <targetState>,
460
+ describe: <sentence>,
461
+ action: <label>,
462
+ } # description/action
463
+ initial: true # exactly one state across the whole workflow
464
+ retry:
465
+ max: <number>
466
+ otherwise: <targetState>
467
+ model: <string> # optional, opaque harness hint — forbidden on a commit state
468
+ memory: <string> # optional, opaque memory-scope label — forbidden on a commit state
469
+ file: <string> # optional, an Eta template naming the state's steering file
470
+ mode: <modeName> # optional, requires "file" — a built-in (qa/review/prose) or a `modes:` entry
471
+ ```
472
+
473
+ Authoring or editing a workflow with a coding agent? `skills/authoring/SKILL.md`
474
+ is the agent-facing contract for producing a valid `workflow:` — the state
475
+ model, pattern grammar, load-time rules, and how to verify a change compiles.
476
+
477
+ ### Variables
478
+
479
+ Every template — `script`/`prompt`/`message`/`commit`, and
480
+ `model`/`memory`/`file` — sees `it.vars`: a flat `Record<string, string>`
481
+ assembled from three layers, **later wins**:
482
+
483
+ 1. **The workflow's own `vars:` key** (sibling to `states:`) — the workflow
484
+ author's declared defaults. The unified template declares
485
+ `vars: { testCommand: "npm test" }`, read by `checking`'s script as
486
+ `<%~ it.vars.testCommand %>`.
487
+ 2. **A top-level `.gtdrc` `vars:` key** (a sibling of `workflow:`, NOT nested
488
+ inside it) — per-repo tuning without redefining the whole workflow.
489
+ 3. **`GTD_<UPPERCASE-name>` environment variables** — highest precedence,
490
+ checked at every invocation, case-insensitively against each name already
491
+ declared by layer 1 or 2: `GTD_TESTCOMMAND` overrides `testCommand`. The
492
+ environment can only OVERRIDE a name a config layer already declared — a
493
+ `GTD_*` var matching no declared name is silently ignored.
494
+
495
+ Values in layers 1–2 must be YAML scalars (string/number/boolean), coerced to
496
+ strings at load time; an object or array value is a load error.
497
+
498
+ ```yaml
499
+ # .gtdrc — overriding the unified template's testCommand
500
+ vars:
501
+ testCommand: npm run test:ci
502
+ ```
503
+
504
+ ```bash
505
+ # highest precedence — beats both the workflow default and the .gtdrc value above
506
+ GTD_TESTCOMMAND="npm run test -- --bail" gtd next
507
+ ```
508
+
509
+ ### Lookup and precedence
510
+
511
+ gtd walks from the current working directory **up to your home directory** (or
512
+ to the filesystem root when cwd is outside home), collecting every `.gtdrc` it
513
+ finds along the way. All found levels are **deep-merged**, with the **innermost
514
+ (cwd) config winning** on conflicts — so a shared `.gtdrc` in a worktree-parent
515
+ directory cascades to every checkout beneath it, while any individual checkout
516
+ can still override with its own `.gtdrc`.
517
+
518
+ ### Validation and errors
519
+
520
+ Config-shape problems (unknown keys, wrong types, unreadable file references)
521
+ are collected together; if the shape is clean, the assembled definition is
522
+ additionally run through the engine's own validation. A bad config throws
523
+ **one** error listing every finding, at load time — before anything touches the
524
+ repository — never partially, and never deferred to step time:
525
+
526
+ ```
527
+ workflow config:
528
+ - state "idle": must declare exactly one of script/prompt/message/commit (found 2)
529
+ - state "idle": "on" target "nowhere" is not a defined state
530
+ ```
531
+
532
+ Those findings include the **semantic graph checks**: every `on` target and
533
+ `retry.otherwise` must name a defined state, and every state must be
534
+ **reachable** from the initial state. All load failures exit **1** and write to
535
+ **stderr**, never stdout.
536
+
537
+ Many of these problems never reach gtd at all if your editor validates against
538
+ the [published schema](#schema), which fully types the `workflow:` key. The
539
+ rules JSON Schema cannot express — exactly one content kind, exactly one
540
+ `initial: true`, targets naming defined states, reachability — remain the
541
+ compiler's job at load time.
542
+
543
+ ## Repository requirements
544
+
545
+ - **Single writer, linear branch.** A process's history is walked via
546
+ **first-parent** commits only.
547
+ - **Test/build artifacts must be gitignored.** This is **load-bearing**, not a
548
+ style preference: every step decision detects "clean" via
549
+ `git diff --name-status HEAD` (tracked changes) unioned with
550
+ `git ls-files --others --exclude-standard` (untracked files), which silently
551
+ omits anything matched by `.gitignore`. If a `script` state's command (or the
552
+ build it triggers) writes output — a `dist/`, a coverage report, a log file —
553
+ into the working tree, the tree never goes clean after a green run, and the
554
+ check's `"C"` pattern never fires. Gitignore every path your scripts write
555
+ before wiring gtd into a repo.
556
+ - **Repository root invocation.** Every state subcommand must run from the git
557
+ repository root. `--help`/`--version` (and the `help`/`version` subcommands),
558
+ `lsp`, and `visualize` skip this guard entirely (`visualize` still reads the
559
+ `.gtdrc` workflow, but needs no git state).
560
+ - **Linked worktrees are independent.** N `git worktree` worktrees of one
561
+ repository (sharing a single `.git`) each run their own gtd process: state is
562
+ derived from that worktree's own HEAD, and the review checkout window's refs
563
+ live in git's per-worktree `refs/worktree/gtd/*` namespace, so a review
564
+ resting in one worktree neither blocks nor rewrites any other.
565
+
566
+ ## Editor integration
567
+
568
+ `gtd lsp` starts an LSP server over stdio for `.gtd/` steering files — a symbol
569
+ per `review`-mode chunk that still has an unchecked hunk (an outline of the
570
+ packages left to review) plus check/uncheck actions over those chunks,
571
+ go-to-definition from a `review`-mode hunk line into the file it points at (at
572
+ its `#line`), symbols over a `qa`-mode file's open questions plus "pick this
573
+ option"/"uncheck this option" code actions on each option — offered anywhere on
574
+ the option's list item, including any wrapped continuation lines, not just its
575
+ own `- [ ]` line — diagnostics for both (live as you edit), and a
576
+ `gtd.openSteeringFile` command that jumps to the current state's steering file.
577
+
578
+ It is config-driven via each state's `file:`/`mode:`, and falls back to basename
579
+ dispatch (`REVIEW.md` → `review`) with no config in sight. `qa` and `review` are
580
+ gtd's built-in steering-file MODES with a VALIDATOR gtd itself implements; a
581
+ mode's `format:` and `validate:` are shell commands a workflow (or a project's
582
+ `.gtdrc`) declares for itself, so you bring your own formatter and your own
583
+ checkers. A third built-in, `prose` — format-only, no validator, used by the
584
+ simple flow's plan file — has no live editor support: `gtd lsp` publishes no
585
+ symbols or diagnostics for it, though `gtd validate` and the `gtd step` gate
586
+ still format and validate it like any other mode.
587
+
588
+ ## Development
589
+
590
+ ```bash
591
+ npm install
592
+ npm run dev # run from source, no build (node dev/run.mjs)
593
+ npm run build # tsdown → dist/gtd.bundle.mjs
594
+ npm test # format:check, typecheck, lint, unit + e2e tests, fallow
595
+ npm run test:unit # vitest unit tests (the pure resolver) — --project unit
596
+ npm run test:e2e # gherkin e2e via vitest + quickpickle — --project e2e
597
+ npm run test:mutation # StrykerJS mutation testing (manual only, ~10 min)
598
+ npm run typecheck
599
+ npm run lint
600
+ ```
601
+
602
+ A pre-commit hook is installed automatically via the `prepare` script when you
603
+ run `npm install` on a fresh clone — it runs
604
+ [lint-staged](https://github.com/lint-staged/lint-staged) with
605
+ [oxfmt](https://oxc.rs/docs/guide/usage/formatter.html), mirroring the
606
+ `format:check` step enforced in CI.
607
+
608
+ The decision core is pure and IO-free: the pattern machine's shape (states,
609
+ patterns, retry) is a plain-data `WorkflowDefinition` (`src/PatternMachine.ts`),
610
+ and the same module's `resolveState`/`step` are the pure resolver/interpreter
611
+ over it — so the whole engine is trivially unit-testable in isolation. All
612
+ git/filesystem/template IO is confined to the edge (`src/Edge.ts`).
613
+
614
+ Releases are automatic: push releasable Conventional Commits (`fix:`, `feat:`,
615
+ or breaking changes) to `main` and semantic-release computes the next version,
616
+ builds the bundle, tags it, and publishes.
237
617
 
238
618
  ## License
239
619