@pmelab/gtd 9.6.0 → 9.8.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
@@ -90,28 +90,28 @@ The loop is one beat, repeated: run `gtd next --json` and dispatch on `kind` —
90
90
  opening beat, which the human triggered by re-running the driver: land that one,
91
91
  since changing nothing is itself a declared outcome at some gates); `"capture"`
92
92
  means a human gate the human already acted on (land it immediately); `"script"`
93
- means the driver runs `content` itself, then lands it; `"prompt"` means feed
94
- `content` to your agent — using the accompanying `session.id`/`session.resume`
95
- to continue or start that agent conversation (see "Driving the loop" below) —
96
- then run `gtd land` once it's done. gtd itself never executes anything — the
97
- driver owns running scripts. Every `gtd next` call is strictly mutation-free,
98
- safe to poll or peek at any time: `session.id`/`session.resume` are DERIVED,
99
- never stored, so looking is free — nothing distinguishes a peek from a dispatch,
100
- and there is no separate claiming form at all. A `prompt` beat whose turn
101
- changes nothing still lands: `gtd land` commits an EMPTY `gtd(<actor>): <state>`
102
- attempt instead of silently doing nothing, so the fruitless dispatch is visible
103
- in history rather than invisible. `"kind": "stalled"` is derived from that
104
- history — HEAD is an empty attempt at the resting state and the tree is clean —
105
- so every `gtd next --json` call reports it, and it stays reported on a repeat
106
- (there's no marker to consume) until something actually changes. The fix for a
107
- repeated stall is either a better prompt, a `retry:` cap on the state that
108
- redirects to an escalation state after N fruitless attempts, or — if the state
109
- can legitimately finish with nothing to change — declaring a `C` (clean-tree)
110
- pattern on it.
93
+ means the driver runs `gtd next`'s own plain-text output itself, then lands it;
94
+ `"prompt"` means feed `gtd next`'s output to your agent — using the accompanying
95
+ `session.id`/`session.resume` fields off `gtd next --json` to continue or start
96
+ that agent conversation (see "Driving the loop" below) — then run `gtd land`
97
+ once it's done. gtd itself never executes anything — the driver owns running
98
+ scripts. Every `gtd next` call is strictly mutation-free, safe to poll or peek
99
+ at any time: `session.id`/`session.resume` are DERIVED, never stored, so looking
100
+ is free — nothing distinguishes a peek from a dispatch, and there is no separate
101
+ claiming form at all. A `prompt` beat whose turn changes nothing still lands:
102
+ `gtd land` commits an EMPTY `gtd(<actor>): <state>` attempt instead of silently
103
+ doing nothing, so the fruitless dispatch is visible in history rather than
104
+ invisible. `"kind": "stalled"` is derived from that history — HEAD is an empty
105
+ attempt at the resting state and the tree is clean — so every `gtd next --json`
106
+ call reports it, and it stays reported on a repeat (there's no marker to
107
+ consume) until something actually changes. The fix for a repeated stall is
108
+ either a better prompt, a `retry:` cap on the state that redirects to an
109
+ escalation state after N fruitless attempts, or — if the state can legitimately
110
+ finish with nothing to change — declaring a `C` (clean-tree) pattern on it.
111
111
 
112
112
  An `on` edge may also carry a short imperative `action` (e.g. `Accept plan`)
113
113
  alongside its existing `describe` sentence — a human-facing name for the choice
114
- itself, not just the underlying glob pattern. `gtd status` (plain and `--json`)
114
+ itself, not just the underlying glob pattern. `gtd next` (plain and `--json`)
115
115
  and `gtd visualize`'s diagram and inspector all render the two composed
116
116
  together: the `action` leads when the edge declares one, with the raw pattern
117
117
  (and `describe`) still shown alongside it, so a human choosing between routes
@@ -121,14 +121,14 @@ reads intent first, glob second.
121
121
  to the tree — a hand-edit to real code, a scratch note in some file, anything at
122
122
  all — as a sketch to be finished, never as work to be preserved: there is no
123
123
  fork on which steering file you happen to create, and `idle` has exactly one
124
- outgoing edge, into `unwind`. `.gtd/TODO.md` (the `todoFile` var) is a good
125
- default place to start sketching when you'd rather write a note than touch code
126
- — despite living beside the workflow's own state files, that sketch is your
127
- input for `design.triage` to fold in, never gtd bookkeeping to preserve or
128
- ignore. `unwind` reverts that whole diff back out of your working tree
129
- (`git revert --no-commit` against the commit it just landed in) before planning
130
- ever starts — your tree is back to exactly what it was, and your intent doesn't
131
- disappear, it survives in history for `design.triage` to read.
124
+ outgoing edge, into `unwind`. `.gtd/TODO.md` is a good default place to start
125
+ sketching when you'd rather write a note than touch code — despite living beside
126
+ the workflow's own state files, that sketch is your input for `design.triage` to
127
+ fold in, never gtd bookkeeping to preserve or ignore. `unwind` reverts that
128
+ whole diff back out of your working tree (`git revert --no-commit` against the
129
+ commit it just landed in) before planning ever starts — your tree is back to
130
+ exactly what it was, and your intent doesn't disappear, it survives in history
131
+ for `design.triage` to read.
132
132
 
133
133
  From there, `start-gate.check` runs your test suite on the tree as it now stands
134
134
  — which, thanks to the unwind, is simply your repo's own baseline. The rule is
@@ -222,14 +222,8 @@ rather than sitting beside it) — an actionable round leaves that identity
222
222
  entirely, so it starts a fresh session like any other process. A comment is what
223
223
  asks for changes; landing with no comment signs off, whatever the boxes say,
224
224
  which collapses the whole process into one commit (a **squash finale** whose
225
- message an agent drafts). Deleting `.gtd/REVIEW.md` is refused, wherever the
226
- review doc lives: repointing `reviewFile` out of `.gtd/` (say to `REVIEW.md` at
227
- the repo root) changes nothing about that refusal, since it only asks whether
228
- the step's diff deletes the file. The exact-path exemption issue #128 fixed
229
- still matters elsewhere, though: the `deciding` state's own classification and
230
- the feedback-progress guard both exclude the state's own `file:` by exact path,
231
- not merely a `.gtd/` prefix, so your own edit to a relocated steering file is
232
- never mistaken for a code comment.
225
+ message an agent drafts). Deleting `.gtd/REVIEW.md` is refused — the guard only
226
+ asks whether the step's diff deletes the file.
233
227
 
234
228
  A second entry, the same review tail's own direct entry point —
235
229
  `gtd --entry review-gate.check --var reviewBase=<commitish>` starts a brand new
@@ -262,27 +256,9 @@ to target a state.
262
256
  Every agent state routes its model through two `vars` tiers — `plannerModel`
263
257
  (heavier planning and review) and `coderModel` (the coding turns) — so you can
264
258
  repoint the models globally in one place (a `vars:` edit or a `GTD_PLANNERMODEL`
265
- override) instead of per state. Steering-file path vars (`todoFile`,
266
- `feedbackFile`, `reviewFile`, …) work the same way, and propagate to the `on`
267
- patterns that route on them too — a repointed path var actually reroutes the
268
- machine, not just the templates that read/write the file. `todoFile` (default
269
- `.gtd/TODO.md`, `idle`'s own `file:` hint) is the one exception: it's the only
270
- per-file var gtd never writes and never reads, since it names your own sketch
271
- pad, not a file any state produces. A separate `stateDir` var (default `.gtd`)
272
- names only where the check scripts keep their own scratch/bookkeeping,
273
- independent of the per-file vars — relocate one steering file without touching
274
- it. The workflow declares its plumbing directory once, at the top level of the
275
- `workflow:` value (`stateDir: <%= it.vars.stateDir %>` in the bundled template),
276
- rendered from that same `stateDir` var — this is what the engine itself reads to
277
- tell gtd's own bookkeeping apart from your code (e.g. the review window's revert
278
- pathspec, the step guards' code-vs-plumbing check), not just a value the
279
- templates happen to share. The rendered value is constrained: it must name a
280
- directory inside the repository — not the repo root, not an absolute path, and
281
- not a path that escapes the repository via `..` — and it must be spelled
282
- canonically: no `.` and no empty path segments (`a/./state` or `a//state` are
283
- rejected, not rewritten), though a leading `./` and a trailing `/` are accepted
284
- — or the next `gtd next`/`gtd status`/`gtd land` fails loudly with that message,
285
- rather than silently excluding the whole tree from the review window's revert.
259
+ override) instead of per state. Every steering file the bundled workflow reads
260
+ or writes lives at a fixed path under `.gtd/`; `testCommand`, `plannerModel`,
261
+ `coderModel`, and `reviewBase` are the only vars left to tune.
286
262
 
287
263
  To inspect or change the machine itself, see [Configuration](#configuration) —
288
264
  the workflow is just `.gtdrc` config.
@@ -301,18 +277,19 @@ Commands:
301
277
  Takes no argument. Run once per repo; refuses if a gtd
302
278
  config already exists. Leaves the file uncommitted for you
303
279
  to review and commit
304
- land Land whatever the tree now shows at the currently
305
- resolved rest — a human capture, an agent/check turn, an
306
- empty attempt (a fruitless prompt turn), or a squash — and
307
- print the script that records it; a driver runs the
308
- script, e.g. `gtd land | bash`. Pass --cost=<n>
309
- (optionally --model=<name>) to record the just-finished
310
- invocation's token cost and model on the turn commit
311
- (summed into it.processCost/processCostByModel). Exits 0
312
- when a script is emitted (or a benign no-op at a clean
313
- message rest), 3 when SETTLED — nothing owed: a no-op at a
314
- script rest, or the initial-state collapse (stdout still
315
- carries the script) — 1 on any refusal
280
+ land Land whatever the tree now shows at the currently resolved
281
+ rest — a human capture, an agent/check turn, an empty
282
+ attempt (a fruitless prompt turn), or a squash. Pass
283
+ --cost=<n> (optionally --model=<name>) to record the
284
+ just-finished invocation's token cost and model on the
285
+ turn commit (summed into it.processCost/
286
+ processCostByModel). Plain (the default) prints ONLY the
287
+ script that records the landing; a driver runs it, e.g.
288
+ `gtd land | sh`. --json/--sh instead emit script (that
289
+ same script, byte-identical) alongside settled, idle,
290
+ state (the post-land target), subject, cost and model —
291
+ --json/--sh are mutually exclusive. Exits 0 on success, 1
292
+ on any refusal — see the Exit codes section below
316
293
  (no command) --entry <state>
317
294
  Starts a new process authenticated as human, e.g.
318
295
  'gtd --entry <state>'
@@ -327,29 +304,35 @@ Commands:
327
304
  Refuses on a dirty working tree, when there is no retained
328
305
  history, or when HEAD has advanced past the squash with
329
306
  commits that would be lost
330
- next Print the resolved rest's rendered script/prompt/message
331
- (no mutation, safe to poll). --json emits the whole beat
332
- document instead: kind (capture|message|script|prompt|
333
- stalled) selects what a driver does, content is what it
334
- runs or shows, plus the prompt session, model, validate
335
- script, log path and the resting state's own fields
336
- status Print the resolved rest's state/actor and which declared
337
- pattern (if any) each pending change matches (no mutation)
307
+ next Print the resolved rest's beat (no mutation, safe to
308
+ poll), in one of three encodings. Plain (the default): a
309
+ status summary, a blank line, then the step verbatim —
310
+ except at a prompt rest, which is the bare step (plus the
311
+ self-validation instruction when applicable) with no
312
+ header, since those bytes are the agent's own input. --json
313
+ emits the one structured surface gtd has: kind
314
+ (capture|message|script|prompt|stalled) selects what a
315
+ driver does, content is what it runs or shows, idle marks
316
+ the workflow's initial state with a clean tree, plus the
317
+ prompt session, model, validate script, log path, changes,
318
+ next and the resting state's own fields. --sh emits the
319
+ same fields as gtd_-prefixed POSIX shell assignments.
320
+ --json/--sh are mutually exclusive. Exits 0 — see the
321
+ Exit codes section below
338
322
  validate Print the script that formats (when declared) then
339
323
  validates the resolved rest's steering file, using its
340
324
  mode's commands (its file:/mode:), instead of running it —
341
325
  a driver runs the script and reads the findings from its
342
- own exit code/output. Always exits 0; --json emits
343
- {state, file?, mode?, script} (script is "" when there is
344
- nothing to validate; plain text prints "nothing to
345
- validate" in that case). On a non-zero validate exit
346
- the emitted script prints a ready-to-send fix prompt
347
- (instruction + findings) and exits with the
348
- validator's own code
326
+ own exit code/output. Always exits 0; prints "nothing to
327
+ validate" when the resolved rest declares nothing to run.
328
+ On a non-zero validate exit the emitted script prints a
329
+ ready-to-send fix prompt (instruction + findings) and
330
+ exits with the validator's own code
349
331
  lsp Start the LSP server for .gtd/ steering files (stdio)
350
332
  visualize Serve an interactive diagram of the active workflow on a
351
- local web server (--port <n>, --no-open; --json prints the
352
- model and exits)
333
+ local web server (--port <n>, --no-open). Prints the
334
+ chosen port on its own line — with --port 0, this is the
335
+ only way to learn which port was picked
353
336
  check <mode> <file>
354
337
  Read <file> and run the built-in steering format named
355
338
  <mode> (see `gtd validate`'s modes: qa, review) over its
@@ -370,7 +353,11 @@ Commands:
370
353
  help Print this help and exit
371
354
 
372
355
  Options:
373
- --json Output structured JSON instead of plain text
356
+ --json (gtd next/gtd land only) output structured JSON
357
+ instead of plain text. Mutually exclusive with --sh
358
+ --sh (gtd next/gtd land only) output gtd_-prefixed POSIX
359
+ shell assignments instead of plain text. Mutually
360
+ exclusive with --json
374
361
  --port=<n> (gtd visualize only) port to serve on (default: a free port)
375
362
  --no-open (gtd visualize only) do not open the browser
376
363
  --cost=<n> (gtd land only) record the invocation's token cost
@@ -386,23 +373,73 @@ Options:
386
373
  instead run the qa open-questions predicate over <file>,
387
374
  printing each unanswered question one per line and exiting
388
375
  non-zero when any remain
389
- --version, -v Print version and exit
376
+ --verbose enable stderr narration for this invocation: which rest
377
+ resolved, which declared pattern each pending change
378
+ matched, which review-window action was emitted, and how
379
+ config resolved across layers. Aliased to -v
380
+ --version, -V Print version and exit
390
381
  --help, -h Print this help and exit
391
382
  ```
392
383
 
393
- `--version` (`-v`) / `gtd version` and `--help` (`-h`) / `gtd help`
384
+ ### Plain output is not a parsing surface
385
+
386
+ `gtd next`'s plain encoding is for a human, or a driver that merely displays it
387
+ — never for scraping. Parsing lives in `--json`/`--sh`, both of which read from
388
+ the exact same field set (`gtd install`'s briefing has the full reference);
389
+ anything that greps, cuts, or `awk`s plain text is unsupported, and its shape
390
+ may change across releases with no warning.
391
+
392
+ ### Exit codes
393
+
394
+ Closed at five numbers, five meanings — a new command never grows this table;
395
+ whose turn is next lives in `gtd next --json`'s own `kind` field instead.
396
+
397
+ | Code | Meaning |
398
+ | --------- | ---------------- |
399
+ | 0 | success |
400
+ | 1 | runtime error |
401
+ | 2 | usage error |
402
+ | 130 / 143 | SIGINT / SIGTERM |
403
+
404
+ Every command follows this table uniformly, `next`/`land` included: `0` on
405
+ success, `1` on refusal, `2` on usage error, `130`/`143` when gtd itself dies by
406
+ that signal — a parent's `wait` sees a real signal death (`WIFSIGNALED`), not a
407
+ chosen exit code that merely reuses the same number. No command's exit code
408
+ carries a second meaning.
409
+
410
+ **Migration — read this even if you already migrated for a prior release.** This
411
+ is the second inversion in as many releases, folded into one note rather than
412
+ two to compose in your head: `10`/`20` (whose turn was next) are gone — every
413
+ command exits `0`/`1`/`2` uniformly now; `gtd status` is gone (folded into
414
+ `gtd next`); and plain `gtd next` now prints a status header at every kind
415
+ EXCEPT `prompt` — agent input is untouched, since plain `gtd next` at a `prompt`
416
+ rest is byte-identical to before. A driver must read whose turn is next off
417
+ `gtd next --json`'s own `kind` field
418
+ (`capture`/`message`/`script`/`prompt`/`stalled`) — never off gtd's exit code,
419
+ which no longer carries that signal at all.
420
+
421
+ Every usage mistake — an unknown option or command, missing/extra arguments, a
422
+ scope violation (e.g. `--cost` on a command other than `gtd land`), a bad flag
423
+ value — is a USAGE error (`2`), never a runtime error (`1`): nothing was even
424
+ attempted. `--help`/`--version` still exit `0`.
425
+
426
+ `--version` (`-V`) / `gtd version` and `--help` (`-h`) / `gtd help`
394
427
  short-circuit before any git or repository-state work — they run outside a repo
395
- and in any repo state. Bare `gtd` (no subcommand) and `gtd loop` are both usage
396
- errors that exit 1 and print help — gtd decides and prints, full stop; driving a
397
- loop is a driver's job, not a bundled command (see
398
- [Driving the loop](#driving-the-loop) below). Any other, truly unknown
399
- subcommand is likewise a usage error: it prints the help text and exits 1
400
- without touching the repository. The state commands (`land`, `--entry`,
401
- `abandon`, `restore`, `next`, `status`, `validate`) must run from the
402
- **repository root** — gtd derives the workflow, pending changes, and process
403
- history relative to cwd, so they refuse with a clear error from a subdirectory;
404
- `lsp`, `init`, `visualize`, `check`, and `install` are standalone and run from
405
- anywhere (see each command's own help entry).
428
+ and in any repo state, and print to **stdout** at exit 0. `--verbose` (`-v`) is
429
+ not a short-circuit — it gates narration for whatever command follows (see
430
+ [Narration and remediation](#narration-and-remediation) below). Bare `gtd` (no
431
+ subcommand) is a usage error that exits 2 — gtd decides and prints, full stop;
432
+ driving a loop is a driver's job, not a bundled command (see
433
+ [Driving the loop](#driving-the-loop) below) — printing its help text to
434
+ **stderr**, not stdout: stdout stays byte-empty on every failure, a usage error
435
+ included (see [Error envelope](#error-envelope) below). Any other, truly unknown
436
+ subcommand is likewise a usage error exiting 2 without touching the repository.
437
+ The state commands (`land`, `--entry`, `abandon`, `restore`, `next`, `status`,
438
+ `validate`) must run from the **repository root** — gtd derives the workflow,
439
+ pending changes, and process history relative to cwd, so they refuse with a
440
+ clear error from a subdirectory; `lsp`, `init`, `visualize`, `check`, and
441
+ `install` are standalone and run from anywhere (see each command's own help
442
+ entry).
406
443
 
407
444
  `--json`, `--cost=<n>`, `--model=<name>` (the latter two only for `gtd land`),
408
445
  `--entry <state>` (no other command at all), and `--var <name>=<value>` (with
@@ -418,7 +455,7 @@ command are all usage errors too — landing and entering are different verbs, s
418
455
  `--model`, `--model` without `--cost`, or `--cost`/`--model` on any command
419
456
  other than `gtd land` are all usage errors.
420
457
 
421
- ### `gtd status`'s `Next:`/`next`
458
+ ### `gtd next`'s `Next:`/`next`
422
459
 
423
460
  Both plain and `--json` output include a headline preview of what would happen
424
461
  next: the first declared `on` edge whose pattern matches the pending changes AS
@@ -435,23 +472,68 @@ real `gtd land` lands.
435
472
 
436
473
  ### Error envelope
437
474
 
438
- Every command, in `--json` mode, reports a failure as a machine-readable
439
- envelope on **stdout**, and still exits 1:
475
+ **stdout is either the complete artifact or byte-empty — never a partial write
476
+ followed by an error.** Every command buffers everything it would print and
477
+ flushes that buffer to stdout exactly once, only after it succeeds; on any
478
+ failure the buffer is simply discarded, so stdout never carries a half-written
479
+ prompt/script alongside a message about why it stopped. `gtd visualize` is the
480
+ one exception worth knowing: it flushes its served-URL line immediately, before
481
+ blocking on `Ctrl-C`, since a flush-on-success would never otherwise fire.
482
+
483
+ Any invocation that carries `--json` (valid only for `gtd next`/`gtd land` —
484
+ every other command usage-errors on it) reports a failure as a machine-readable
485
+ envelope on **stderr** — including the scope violation itself, so a driver that
486
+ always adds `--json` still gets a parseable envelope on every failure, not only
487
+ `gtd next`'s/`gtd land`'s own:
440
488
 
441
489
  ```json
442
490
  { "state": "error", "prompt": "<message>" }
443
491
  ```
444
492
 
445
- This covers every failure mode, not just a command's own refusal: a **usage
446
- error** (an unknown flag, a missing argument, `gtd --entry version`'s "not an
447
- enterable state") and a **defect** (a layer throwing outside the ordinary error
448
- channel) both get the same envelope — there is no failure path that reaches
449
- `--json` without one.
450
-
451
- A human-readable `gtd: <message>` line is still written to **stderr** regardless
452
- of `--json` — the envelope adds a structured stdout channel, it does not replace
453
- the plain-text one. Stderr always carries exactly one `gtd: ` prefix: a message
454
- already authored with its own `gtd:`/`gtd <cmd>:` prefix is never doubled.
493
+ This covers every failure mode, not just a command's own refusal (exit 1): a
494
+ **usage error** (an unknown flag, a missing argument, `gtd --entry version`'s
495
+ "not an enterable state" — exit 2) and a **defect** (a layer throwing outside
496
+ the ordinary error channel — exit 1) both get the same envelope shape — there is
497
+ no failure path that reaches `--json` without one — but a usage error's exit
498
+ code is 2, never 1, so a driver can tell "you invoked gtd wrong" apart from "gtd
499
+ refused/broke" (see [Exit codes](#exit-codes)).
500
+
501
+ A human-readable `gtd: <message>` line is also always written to **stderr**,
502
+ right after the envelope — stdout carries neither one on a failing run. Stderr
503
+ always carries exactly one `gtd: ` prefix: a message already authored with its
504
+ own `gtd:`/`gtd <cmd>:` prefix is never doubled.
505
+
506
+ **Accepted cost:** a driver that pipes stdout into `jq` on a failed run now
507
+ reads nothing instead of a parseable error object — it must read stderr or the
508
+ exit code to learn why a run failed.
509
+
510
+ ### Narration and remediation
511
+
512
+ Stderr carries two things beyond the `gtd: ` message line above: NARRATION,
513
+ gated by `--verbose`/`-v`, and REMEDIATION, unconditional.
514
+
515
+ `--verbose` (alias `-v`) turns on one line of commentary per in-process fact a
516
+ command's dispatch already computes — which rest resolved, which declared
517
+ pattern each pending change matched, which review-window action was emitted, and
518
+ how config resolved across `.gtdrc` layers. Without it, none of this is printed;
519
+ stdout is never touched either way — narration is a stderr-only concern, exactly
520
+ like the error envelope above.
521
+
522
+ A failure's remediation detail is unconditional — it prints at every verbosity,
523
+ on the line(s) right after the `gtd: `-prefixed message, each indented two
524
+ spaces: a bad config key names the offending key and which `.gtdrc` layer it
525
+ came from, a corrupted ref names the ref, and a missing binary in a steering
526
+ mode's `format:`/`validate:` command names the resolved `$PATH` it was looked up
527
+ in.
528
+
529
+ ### Non-interactive today
530
+
531
+ gtd is non-interactive: no readline, no `/dev/tty`, no prompt call anywhere in
532
+ `src/`. If interaction is ever added, it goes to `/dev/tty`, never stderr, and
533
+ never blocks — a question fails with a code when the tty cannot be opened rather
534
+ than hanging. The reason is the section above: stderr already has two occupants,
535
+ narration and remediation, and a question mixed into that stream would deadlock
536
+ a driver that never reads stderr.
455
537
 
456
538
  ## Driving the loop
457
539
 
@@ -485,38 +567,62 @@ not of the loop itself; see
485
567
  below for a worked example.
486
568
 
487
569
  A workflow state can declare an optional `label:` — a human-readable display
488
- name surfaced in `gtd next --json`/`gtd status`. The driver uses it for its
570
+ name surfaced in `gtd next --json`/plain `gtd next`. The driver uses it for its
489
571
  per-beat progress lines; an outer wrapper (a terminal multiplexer, a notifier)
490
572
  can use it the same way.
491
573
 
574
+ A machine may also declare `system:` — like `model:`, stamped once at the
575
+ machine level onto every one of that machine's own `prompt` states, never
576
+ authored on an individual state. It is passed to the agent CLI as (in the
577
+ reference driver's case) `--system-prompt`, which **replaces** the harness's own
578
+ default system prompt outright rather than appending to it (contrast with an
579
+ `--append-system-prompt`-shaped flag) — so a machine declaring `system:` loses
580
+ not only the harness's own tool-use instructions but also its dynamic per-turn
581
+ sections: current working directory, environment info, memory-path information,
582
+ git status. A workflow author reaching for `system:` for the first time is
583
+ therefore writing a complete replacement system prompt, not a tweak on top of
584
+ the harness's own. The scoping is machine-level for the same reason memory is
585
+ machine-scoped below: a machine is the unit of conversational identity, so its
586
+ system prompt — like its model tier — is one property of that identity, constant
587
+ across every turn of the one resumed session, never a per-state override. Like
588
+ `model:`/`file:`, a `system:` value prefixed `./` or `../` is inlined from the
589
+ declaring config file's own directory at compile time — for a user's own
590
+ `.gtdrc` `workflow:` config only; the bundled template ships no filesystem
591
+ references at all, since it has to work inside a single-file build. The bundled
592
+ default template ships six such personas already, one per prompt-bearing machine
593
+ — `designPersona`, `architectPersona`, `reviewerPersona`, `specReviewerPersona`,
594
+ `builderPersona`, `finisherPersona` — and, like any other bundled var, each is
595
+ overridable through a top-level `.gtdrc` `vars:` key or a `GTD_<NAME>`-style
596
+ environment variable (e.g. `GTD_DESIGNPERSONA`) — gtd's existing, generic
597
+ vars-override mechanism, nothing new.
598
+
492
599
  Memory is **entry-scoped to a machine**, not a state-authored label: each
493
600
  machine instance (a node in the `machines:` tree, e.g. `build`, `build.health`,
494
601
  `packages.item`, `packages.item.health`) owns its own conversational scope, and
495
- a `prompt`-content state's `memory` key — surfaced in `gtd next --json`/
496
- `gtd status --json`'s `memory` field, and as a `Memory: <key>` line in plain
497
- `gtd status` — is computed, never authored, as `<scope>#<hash7>`: `<scope>` is
498
- that machine instance's dotted path (the root instance is shown as `root`), and
499
- `<hash7>` anchors to the commit the CURRENT unbroken entry into that scope
500
- started FROM. Entering a **descendant** scope (e.g. dipping from `build` into
501
- `build.health`) does not break the parent's unbroken run — a full agent turn in
502
- a nested child machine, then back to the parent, still resumes the SAME parent
503
- conversation; entering a **sibling or unrelated** scope does start a fresh one.
504
- The bundled template's `build.review` (the human review tail) is a worked
505
- example: it is nested INSIDE `build` (the builder's own machine) so that a
506
- `gtd --entry fix-precheck` run — `build.fix` -> `build.health.check` ->
507
- `build.review.*` -> `build.squashing` — stays inside one subtree, letting
508
- `build.squashing` resume the SAME session that made the fixes instead of a
509
- root-level sibling forcing it to re-derive the range from scratch on every pass
510
- through the tail. (An actionable review round breaks the run on purpose instead
511
- — it leaves `build` entirely through a root-level `re-unwind` state and a full
512
- re-plan, since a hand-edit made during review is a sketch to reconsider, not a
513
- fix to build on.) Two instances of the same reusable machine (e.g.
514
- `build.health` and `packages.item.health`, both instantiating `healthGate`) get
515
- different scopes and so never share a key, even though they're the "same shaped"
516
- machine. One consequence is a structural guarantee: **a reviewer's turn never
517
- resumes an implementer's session, and vice versa** — a reviewer machine and the
518
- implementer machine it reviews are always different instances with different
519
- scopes.
602
+ a `prompt`-content state's `memory` key — surfaced in `gtd next --json`'s
603
+ `memory` field, and as a `Memory: <key>` line in plain `gtd next` — is computed,
604
+ never authored, as `<scope>#<hash7>`: `<scope>` is that machine instance's
605
+ dotted path (the root instance is shown as `root`), and `<hash7>` anchors to the
606
+ commit the CURRENT unbroken entry into that scope started FROM. Entering a
607
+ **descendant** scope (e.g. dipping from `build` into `build.health`) does not
608
+ break the parent's unbroken run — a full agent turn in a nested child machine,
609
+ then back to the parent, still resumes the SAME parent conversation; entering a
610
+ **sibling or unrelated** scope does start a fresh one. The bundled template's
611
+ `build.review` (the human review tail) is a worked example: it is nested INSIDE
612
+ `build` (the builder's own machine) so that a `gtd --entry fix-precheck` run —
613
+ `build.fix` -> `build.health.check` -> `build.review.*` -> `build.squashing` —
614
+ stays inside one subtree, letting `build.squashing` resume the SAME session that
615
+ made the fixes instead of a root-level sibling forcing it to re-derive the range
616
+ from scratch on every pass through the tail. (An actionable review round breaks
617
+ the run on purpose instead — it leaves `build` entirely through a root-level
618
+ `re-unwind` state and a full re-plan, since a hand-edit made during review is a
619
+ sketch to reconsider, not a fix to build on.) Two instances of the same reusable
620
+ machine (e.g. `build.health` and `packages.item.health`, both instantiating
621
+ `healthGate`) get different scopes and so never share a key, even though they're
622
+ the "same shaped" machine. One consequence is a structural guarantee: **a
623
+ reviewer's turn never resumes an implementer's session, and vice versa** — a
624
+ reviewer machine and the implementer machine it reviews are always different
625
+ instances with different scopes.
520
626
 
521
627
  `gtd` itself stores NOTHING to make this work: `session.id` is
522
628
  `UUIDv5(<fixed gtd namespace>, <memory key>)` — a deterministic hash of the
@@ -550,7 +656,7 @@ reports `gtd`'s own lifecycle to that status without any herdr-specific
550
656
  knowledge in `gtd` itself: `working` while the loop drives, `blocked` when it
551
657
  comes to rest on a human (a gate, a stall, a non-zero exit), and `idle`
552
658
  (rendered as "done" once the pane's tab is unfocused) when a run ends without
553
- anything owed. It needs nothing from `gtd` beyond `gtd status --json`'s `.actor`
659
+ anything owed. It needs nothing from `gtd` beyond `gtd next --sh`'s `gtd_actor`
554
660
  field — a strictly read-only orientation peek.
555
661
 
556
662
  Save this as `~/.local/bin/gtdh`, `chmod +x` it, and run `gtdh` in place of
@@ -560,8 +666,8 @@ Save this as `~/.local/bin/gtdh`, `chmod +x` it, and run `gtdh` in place of
560
666
  #!/usr/bin/env bash
561
667
  # Report gtd's own lifecycle to the herdr pane it runs in: working while the
562
668
  # loop drives, blocked when it rests on you, idle (shown as "done") otherwise.
563
- # Needs nothing from gtd but `gtd status --json`. Outside herdr it is a plain
564
- # passthrough. Requires `jq` — so does the README's minimal driver.
669
+ # Needs nothing from gtd but `gtd next --sh`. Outside herdr it is a plain
670
+ # passthrough.
565
671
  set -uo pipefail
566
672
 
567
673
  pane="${HERDR_PANE_ID:-}"
@@ -586,14 +692,16 @@ trap 'report blocked; exit 130' INT TERM
586
692
  env -u HERDR_PANE_ID gtd-loop
587
693
  rc=$?
588
694
 
589
- # Whose turn is it now? `gtd status --json` is a strictly read-only peek —
590
- # every gtd command is, including `gtd next --json` (its prompt session id is
591
- # derived, never minted/stored) — only the emitted scripts a driver runs
592
- # actually touch git. A human actor means gtd is waiting on you; anything else
593
- # means the run ended with nothing owed.
594
- actor="$(gtd status --json 2>/dev/null | jq -r '.actor // ""' 2>/dev/null || true)"
695
+ # Whose turn is it now? `gtd next --sh` is a strictly read-only peek — every
696
+ # gtd command is, including `gtd next` (its prompt session id is derived,
697
+ # never minted/stored) — only the emitted scripts a driver runs actually
698
+ # touch git. A human actor means gtd is waiting on you; anything else means
699
+ # the run ended with nothing owed. Assign first, THEN eval — same reason as
700
+ # the driver above.
701
+ out="$(gtd next --sh 2>/dev/null)" || out=""
702
+ eval "${out:-}" 2>/dev/null || true
595
703
 
596
- if [ "$rc" -ne 0 ] || [ -z "$actor" ] || [ "$actor" = human ]; then
704
+ if [ "$rc" -ne 0 ] || [ -z "${gtd_actor:-}" ] || [ "$gtd_actor" = human ]; then
597
705
  report blocked
598
706
  else
599
707
  report idle
@@ -632,7 +740,7 @@ to build a driver without leaving your terminal. The briefing ends by directing
632
740
  the agent to INTERVIEW you first — which agent CLI runs the turns, under what
633
741
  permission model, how you want to invoke the loop, what should happen at a human
634
742
  gate — and to build the driver your answers describe rather than copying the
635
- reference bash. It writes nothing: "install" means installing knowledge into the
743
+ reference sh. It writes nothing: "install" means installing knowledge into the
636
744
  calling agent's context, and the briefing it prints is always exactly as current
637
745
  as the `gtd` that printed it.
638
746
 
@@ -641,88 +749,109 @@ privileged one — the engine itself is a supported public surface, and anything
641
749
  below holds for any driver you write against it. gtd decides and prints; it
642
750
  never touches git itself. The four commands that change anything — `gtd land`,
643
751
  `gtd --entry <state>`, `gtd abandon`, and `gtd restore` — perform no git write
644
- when run. Instead, each one's output carries `required`/ `optional`: bash
645
- scripts for YOU to execute. Plain-text output (no `--json`) prints them combined
646
- into ONE pasteable script — `required` verbatim, then `optional` wrapped so its
647
- own failure can't turn a landed turn into a non-zero exit — so `gtd land | bash`
648
- is a complete, single-command way to land a turn. Under `--json`, each one's
649
- output object instead carries `required`/`optional` as two separate string
650
- fields (plus, for `gtd land`, the same two combined into `script`), for a driver
651
- that wants to run them apart (e.g. to skip `optional` outright). Either way,
652
- printing gtd's output and never running it is not driving anything; a driver
653
- must pipe or `jq`-and-execute what gtd prints.
654
-
655
- - **`required`** is everything that decides what lands in git — closing an open
656
- review checkout window, the resting state's own steering-mode
752
+ when run: each one prints ONE POSIX sh script for YOU to execute
753
+ (`src/Emit.ts`'s `combinedScript`) — a leading comment ("gtd emitted this and
754
+ did NOT run it — pipe it into `sh` to land the turn"), then the REQUIRED half
755
+ verbatim, then, only when there's a presentation-only follow-up, a second
756
+ comment ("presentation only — safe to skip") and the OPTIONAL half wrapped in a
757
+ subshell whose own non-zero exit is swallowed (reported to stderr as a warning,
758
+ never turning a landed turn into a failing one). Printing gtd's output and never
759
+ running it is not driving anything; a driver must pipe or execute what gtd
760
+ prints — e.g. the capture-then-pipe form the reference driver below uses — never
761
+ a bare `gtd land | sh`, which would hand an empty script to a shell on a refusal
762
+ instead of stopping first (see the reference driver's own use of `gtd land --sh`
763
+ below).
764
+
765
+ Every script gtd emits — `gtd land`, `gtd --entry <state>`, `gtd abandon`,
766
+ `gtd restore`, and the format/validate script `gtd validate` prints — is POSIX
767
+ `sh`, portable to `dash`: a driver may run any of them with any POSIX-compliant
768
+ shell, not specifically bash. The same convention extends to the workflow's own
769
+ `vars.testCommand` (what a `script`-content check state actually executes): it
770
+ is expected to be POSIX sh-compatible too, but this is a DOCUMENTED CONVENTION
771
+ only — gtd never inspects or validates `testCommand`'s shell dialect itself, it
772
+ only renders the value into a script and hands it to whatever shell the driver
773
+ invokes that script with.
774
+
775
+ - **The required half** is everything that decides what lands in git — closing
776
+ an open review checkout window, the resting state's own steering-mode
657
777
  `format:`/`validate:` commands, the commit or squash itself (`gtd land` and
658
778
  `gtd --entry <state>`), or the ref update and reset that undo a process
659
779
  (`gtd abandon`, `gtd restore`) — and, last, a printed line naming what just
660
780
  landed (`src/OutcomeScript.ts`'s `gtd_report_*` calls): a transition or
661
781
  capture's changed-file rows, or the abandon/restore prose, resolved from the
662
- repository AFTER the write above it. Run this one. Skipping it means the turn
663
- never lands, and you never see what it did.
664
- - **`optional`** is presentation only: re-opening the review checkout window
665
- (the `<<<<<<< HEAD` diff view) after `gtd land` lands at a
782
+ repository AFTER the write above it. Its own exit code IS the printed script's
783
+ exit code — skipping it means the turn never lands, and you never see what it
784
+ did.
785
+ - **The optional half** is presentation only: re-opening the review checkout
786
+ window (the `<<<<<<< HEAD` diff view) after `gtd land` lands at a
666
787
  `reviewWindow: true` state, so an editor's diff view has something to show.
667
- Skip it and you lose nothing but that view — the workflow is still driven
668
- correctly. `gtd abandon`/`gtd restore` always emit `optional: ""` (there is no
669
- window to reopen after either).
788
+ Its own failure is swallowed by the wrapping subshell (a warning on stderr,
789
+ nothing more) — skip it (or let it fail) and you lose nothing but that view,
790
+ the workflow is still driven correctly. `gtd abandon`/`gtd restore` never
791
+ carry one — there is no window to reopen after either, so their printed
792
+ artifact is the required half alone, with no trailing comment/subshell at all.
670
793
 
671
794
  `gtd next --json` carries one more field worth a custom driver's attention:
672
795
 
673
796
  - **`log`** — the per-worktree loop log path, always present. It's derived from
674
797
  the worktree's own git dir, so two worktrees looping concurrently never share
675
- one file; set `$GTD_LOOP_LOG` to override it verbatim. gtd itself neither
676
- creates nor truncates this file — a driver appends subprocess output to it and
677
- truncates once at the start of a run, exactly like the driver above does.
678
-
679
- `optional` may be the empty string `""`, meaning "nothing to do here".
680
- `required` is never empty — even a genuine no-op `gtd land` (a clean tree
681
- matching no `on` pattern) emits a PRINT-ONLY script: no git write, just the same
682
- `nothing to do at "<state>"` line the script prints when a driver runs it (or,
683
- under plain text with no `--json`, when gtd prints the very same script back to
684
- you). Both scripts are self-contained (each carries its own precondition assert
685
- and retry helper) and safe to run standalone, in sequence, or not at all — paste
686
- either into a terminal and it does exactly what it says, printed output
687
- included: the scripts detect their own tty/`NO_COLOR` at RUN time (not at the
688
- moment gtd generated them), so the fancy/plain rendering always matches wherever
689
- you actually run them.
798
+ one file; set `$GTD_LOOP_LOG` to override it verbatim, or `$GIT_DIR` to name
799
+ the git dir directly (both honored the same way every git subprocess already
800
+ honors them — an inherited `$GIT_DIR` moves the log path along with it). gtd
801
+ itself neither creates nor truncates this file — a driver appends subprocess
802
+ output to it and truncates once at the start of a run, exactly like the driver
803
+ above does.
804
+
805
+ Even a genuine no-op `gtd land` (a clean tree matching no `on` pattern) prints a
806
+ PRINT-ONLY script: no git write, just the same `nothing to do at "<state>"` line
807
+ the script prints when a driver runs it. Every encoding (plain, `--json`,
808
+ `--sh`) carries that same script verbatim in its `script` field — running it is
809
+ never optional, whichever encoding a driver reads it from. The script is
810
+ self-contained (it carries its own precondition assert and retry helper) and
811
+ safe to run standalone, in sequence, or not at all — paste it into a terminal
812
+ and it does exactly what it says, printed output included: it detects its own
813
+ tty/`NO_COLOR` at RUN time (not at the moment gtd generated it), so the
814
+ fancy/plain rendering always matches wherever you actually run it.
690
815
 
691
816
  A driver has no opening move. It does not need to know whose turn it is before
692
- it starts — `gtd next --json` tells it, and a human's pending edit arrives as an
693
- ordinary `kind: "capture"` beat that the driver lands like any other. The rule
694
- this replaces: never `gtd land` outside a beat you dispatched. A stray land at a
695
- clean `prompt` rest authors an empty attempt on purpose (that IS the stall
696
- bookkeeping), so an unconditional opening land would manufacture a stall out of
697
- a fresh start.
698
-
699
- `gtd land`'s exit code IS the settled signal: 0 for an ordinary landing
700
- (capture, turn, attempt, squash, or a benign no-op at a clean `message` rest), 3
701
- for SETTLED, 1 for a refusal or usage error. SETTLED means nothing owed, for
702
- either of two terminal shapes — a no-op at a `script` rest (the check ran, left
703
- nothing any pattern claims, and re-running the same beat cannot change that), or
704
- a step whose decision rewinds the process back to the workflow's initial state
705
- retaining nothing (`gtd --entry fix-precheck` against a green suite is the
706
- shipped example — the probe collapses away rather than landing a round trip
707
- through the log). Either way a loop should exit rather than spin — with
708
- `set -o pipefail`, `gtd land | bash` propagates gtd's own exit code through the
709
- pipe, and `--json` carries the same fact as `settled: true`. A no-op at a
710
- `prompt` rest is NOT settled — an agent that was asked to act and produced
711
- nothing is a stall, a driver's own concern, not this signal's. Declaring a `C`
712
- edge on a `script` state is the workflow-side way to make the state advance
713
- instead of settling.
714
-
715
- ### Drivers other than bash
716
-
717
- The protocol above is JSON in, subprocesses out — bash is one convenient shape,
718
- not the only one. A **program** parses `gtd next --json`, switches on `kind`,
719
- and runs `content`/`required`/`optional` as subprocesses — no bash anywhere in
720
- it. A **human** runs plain `gtd next` (no `--json`), does what it says, and
721
- pastes `gtd land | bash`: plain-text output exists for exactly this, printing
722
- `required` and `optional` already combined into one pasteable script. An
723
- **agent** gets `gtd install` as its instructions — the same protocol, in a form
724
- built to be pasted into a context window rather than read. A **CI job** is the
725
- program case with the `prompt` arm pointed at a headless agent CLI, and
817
+ it starts — `gtd next --json`/`--sh` tells it, and a human's pending edit
818
+ arrives as an ordinary `kind: "capture"` beat that the driver lands like any
819
+ other. The rule this replaces: never `gtd land` outside a beat you dispatched. A
820
+ stray land at a clean `prompt` rest authors an empty attempt on purpose (that IS
821
+ the stall bookkeeping), so an unconditional opening land would manufacture a
822
+ stall out of a fresh start. The one EXCEPTION is the run's very first beat: land
823
+ it before trusting `idle` there too, so a workflow whose initial state declares
824
+ its own clean-tree `"C"` pattern still gets a chance to advance on a human's
825
+ bare re-invocation, rather than the driver concluding "nothing owed" without
826
+ ever giving that edge a turn (see the reference driver's own comment on this).
827
+
828
+ Exit code carries none of this any more — every command, `next`/`land` included,
829
+ exits `0` on success (see [Exit codes](#exit-codes) above). Whose turn is next,
830
+ and whether a landing settles, are both PLAIN FIELDS on `gtd next --json`/`--sh`
831
+ and `gtd land --json`/`--sh`: `next`'s own `kind`
832
+ (`capture`/`message`/`script`/`prompt`/`stalled`) and `idle`; `land`'s own
833
+ `settled` and `idle`. `gtd land --sh` carries `settled`/`idle` alongside the
834
+ script itself, so ONE invocation both runs the landing and tells the driver
835
+ whether to stop — no second read needed to decide. A no-op at a `script` rest
836
+ settles right where it rests (stop immediately, nothing more to read); an
837
+ ordinary or squash landing that finishes the whole process instead resolves
838
+ `idle` on the FOLLOWING `gtd next` — the reference driver below reads once more
839
+ only to DISPLAY that gate's message, never to decide whether to stop. Declaring
840
+ a `C` edge on a `script` state is the workflow-side way to make the state
841
+ advance instead of settling.
842
+
843
+ ### Drivers other than sh
844
+
845
+ The protocol above is JSON in (from `gtd next --json` alone), subprocesses out —
846
+ sh is one convenient shape, not the only one. A **program** parses
847
+ `gtd next --json`, switches on `kind`, and runs `gtd next`'s (plain-text) output
848
+ and `gtd land`'s (POSIX sh) output as subprocesses — no sh anywhere in it. A
849
+ **human** runs plain `gtd next` (there is no JSON form of it at all), does what
850
+ it says, and pastes `gtd land | sh`: plain-text output exists for exactly this,
851
+ printing the combined script ready to paste. An **agent** gets `gtd install` as
852
+ its instructions — the same protocol, in a form built to be pasted into a
853
+ context window rather than read. A **CI job** is the program case with the
854
+ `prompt` arm pointed at a headless agent CLI, and
726
855
  `kind: "message"`/`kind: "stalled"` mapped onto "stop and report".
727
856
 
728
857
  ### A complete minimal driver
@@ -737,102 +866,173 @@ This exact fenced block is extracted and executed by
737
866
  `tests/integration/features/readme-driver.feature` — renaming the heading above
738
867
  or splitting the block into more than one fence breaks the build.
739
868
 
869
+ **Pipe the prompt to the agent CLI on stdin — never pass it as a command-line
870
+ argument.** A `prompt` beat's content embeds a full diff, and argv is capped:
871
+ roughly 1 MB on macOS, and POSIX guarantees only 4 KB (`ARG_MAX`). Both are
872
+ reachable by an ordinary diff, so a driver that passes the prompt as an argument
873
+ works in testing and then fails on the first large change, in a way that looks
874
+ like an agent error rather than a driver bug. The driver below pipes `$c`/`$out`
875
+ into `claude -p` over stdin for exactly this reason.
876
+
740
877
  ```bash
741
- #!/usr/bin/env bash
742
- set -euo pipefail
743
-
744
- # The bundle plans; this executes. Emitted scripts print their own outcomes.
745
- # Exit 3 means SETTLED — nothing owed — so this exits the whole driver at 0.
746
- gtd_land() {
747
- local json code=0
748
- json="$(gtd land --json)" || code=$?
749
- [ "$code" = 0 ] || [ "$code" = 3 ] || return "$code"
750
- bash -c "$(jq -r '.required // empty' <<<"$json")" || return $?
751
- bash -c "$(jq -r '.optional // empty' <<<"$json")" ||
752
- echo "warn: presentation follow-up failed — continuing" >&2
753
- [ "$code" = 3 ] && exit 0
754
- return 0
755
- }
878
+ #!/usr/bin/env sh
879
+ set -eu
756
880
 
757
881
  beat=1
758
882
  while :; do
759
- next="$(gtd next --json)" || exit 1
760
- kind="$(jq -r .kind <<<"$next")"
761
- log="$(jq -r .log <<<"$next")"
762
- case "$kind" in
763
- stalled) jq -r .content <<<"$next" >&2; exit 1 ;;
883
+ # One invocation per beat: assign first, THEN eval — command substitution
884
+ # inside eval's own argument would swallow a failed `gtd next` under
885
+ # `set -e` (eval would see only the empty string and abort on some later
886
+ # unset variable with a confusing message). Assigning to `out` first makes
887
+ # this a simple command whose own exit status IS the substitution's, so
888
+ # `set -e` aborts correctly on a genuine failure.
889
+ out="$(gtd next --sh)"
890
+ eval "$out"
891
+
892
+ # `gtd_idle` (true iff the initial state, clean tree) is the one shape
893
+ # that means the process is genuinely done — EXCEPT on the run's opening
894
+ # beat: land it anyway, so a workflow whose initial state declares its own
895
+ # clean-tree "C" pattern still gets a chance to fire.
896
+ if [ "$beat" -gt 1 ] && [ "${gtd_idle:-}" = true ]; then
897
+ gtd next
898
+ exit 0
899
+ fi
900
+
901
+ case "$gtd_kind" in
902
+ stalled) printf '%s\n' "$gtd_content" >&2; exit 1 ;;
764
903
  # you re-ran us resting here: you either edited something or accepted by
765
904
  # editing nothing, so land the opening beat either way. Later beats are
766
905
  # gates we just produced and you have not read yet — hand off.
767
- message) [ "$beat" = 1 ] || { jq -r .content <<<"$next"; exit 0; } ;;
906
+ message) [ "$beat" = 1 ] || { gtd next; exit 0; } ;;
768
907
  capture) ;; # the human already acted — just land it
769
- script) bash -c "$(jq -r .content <<<"$next")" >>"$log" 2>&1 || true ;;
908
+ script)
909
+ sh -c "$gtd_content" >>"$gtd_log" 2>&1 || true ;;
770
910
  prompt)
771
- sid="$(jq -r '.session.id // empty' <<<"$next")"
772
- c="$(jq -r .content <<<"$next")" model="$(jq -r '.model // empty' <<<"$next")"
773
- agent_turn() { claude -p "$c" "$1" "$sid" ${model:+--model "$model"} \
774
- --dangerously-skip-permissions >>"$log" 2>&1; }
775
- if [ "$(jq -r '.session.resume // false' <<<"$next")" = true ]
911
+ # $gtd_content embeds a full diff, so it goes to the agent over
912
+ # stdin, never as an argv positional — argv is capped (~1 MB on
913
+ # macOS, and POSIX guarantees only 4 KB, ARG_MAX), and a diff crosses
914
+ # that far sooner than you'd expect.
915
+ agent_turn() { printf '%s' "$gtd_content" | claude -p "$1" "$gtd_session_id" \
916
+ ${gtd_model:+--model "$gtd_model"} \
917
+ ${gtd_system:+--system-prompt "$gtd_system"} \
918
+ --dangerously-skip-permissions >>"$gtd_log" 2>&1; }
919
+ if [ "${gtd_session_resume:-}" = true ]
776
920
  then agent_turn --resume || agent_turn --session-id
777
921
  else agent_turn --session-id || agent_turn --resume
778
922
  fi
779
- v="$(jq -r '.validate // empty' <<<"$next")" n=0
780
- while [ -n "$v" ] && ! out="$(bash -c "$v" 2>&1)"; do
781
- n=$((n + 1)) && [ "$n" -gt 3 ] && { printf '%s\n' "$out" >&2; exit 1; }
782
- claude -p "$out" --resume "$sid" --dangerously-skip-permissions \
783
- >>"$log" 2>&1 # $out IS the fix prompt, verbatim
923
+ n=0
924
+ while [ -n "${gtd_validate:-}" ] && ! fix="$(sh -c "$gtd_validate" 2>&1)"; do
925
+ n=$((n + 1)) && [ "$n" -gt 3 ] && { printf '%s\n' "$fix" >&2; exit 1; }
926
+ # $fix IS the fix prompt, verbatim — piped for the same reason as
927
+ # $gtd_content above. Whether `claude --resume` re-applies the
928
+ # original session's model/system prompt is a harness detail gtd
929
+ # cannot verify from outside, so this passes the identical
930
+ # $gtd_model/$gtd_system on both calls rather than assume it does —
931
+ # otherwise this fix turn might silently fall back to Claude Code's
932
+ # own defaults while the turn that produced the file ran under the
933
+ # workflow's own model and persona.
934
+ printf '%s' "$fix" | claude -p --resume "$gtd_session_id" \
935
+ ${gtd_model:+--model "$gtd_model"} \
936
+ ${gtd_system:+--system-prompt "$gtd_system"} \
937
+ --dangerously-skip-permissions >>"$gtd_log" 2>&1
784
938
  done ;;
785
939
  esac
786
- gtd_land || exit 1
940
+
941
+ # `gtd land --sh` carries `settled`/`idle` alongside the script itself —
942
+ # one invocation tells us both what to run and whether to stop, with no
943
+ # second read needed to decide either.
944
+ out="$(gtd land --sh)"
945
+ eval "$out"
946
+ printf '%s\n' "$gtd_script" | sh
947
+ if [ "${gtd_settled:-}" = true ]; then
948
+ [ "${gtd_idle:-}" = true ] && gtd next
949
+ exit 0
950
+ fi
787
951
  beat=$((beat + 1))
788
952
  done
789
953
  ```
790
954
 
791
- Line by line it is the protocol described above: no opening move at all — a
792
- human's pending edit arrives as the `capture` beat, and a stray `gtd land`
793
- outside a beat you acted on would author an empty attempt at a clean prompt rest
794
- on purpose; one `gtd next --json` beat document read per loop (`kind: "stalled"`
795
- guarding against a spinning agent); `message` halts unless it is the opening
796
- beat, which the human's own re-invocation authored and which therefore lands
797
- like any other decision (see [Driving the loop](#driving-the-loop) above — this
798
- is the one place the driver, not gtd, decides, because "has the human read this
799
- gate" is run-scoped knowledge gtd deliberately does not keep); `capture` lands a
800
- human's already-made edit outright, `script` runs in the driver, `prompt` goes
801
- to the agent with the document's own `session.id`/`session.resume` mapped onto
802
- the agent's session flags — trying `resume`'s hinted flag first and falling back
803
- to the other on failure, since `session.id` is derived, not remembered (see
804
- [Driving the loop](#driving-the-loop) above) — and its embedded `.validate`
955
+ Line by line it is the protocol described above: `out="$(gtd next --sh)"` then
956
+ `eval "$out"` is one invocation per beat — assigning first is what lets `set -e`
957
+ abort correctly on a genuine failure, where `eval "$(gtd next --sh)"` would
958
+ instead eval the empty string and die later on some unrelated unset variable;
959
+ the driver carries no `unset` of its own, since the `--sh` document's own
960
+ preamble makes `eval` self-contained. The very first beat lands even when
961
+ `gtd_idle` is already true, so a workflow whose initial state declares its own
962
+ clean-tree `"C"` pattern still gets a chance to fire before the driver calls it
963
+ done. `kind: "stalled"` prints `$gtd_content` (the diagnosis itself) to stderr
964
+ and stops — no re-invocation needed, since the beat already fetched it;
965
+ `message` halts unless it is the opening beat, which the human's own
966
+ re-invocation authored and which therefore lands like any other decision (see
967
+ [Driving the loop](#driving-the-loop) above — this is the one place the driver,
968
+ not gtd, decides, because "has the human read this gate" is run-scoped knowledge
969
+ gtd deliberately does not keep); `capture` lands a human's already-made edit
970
+ outright, no display needed; `script` runs `$gtd_content` in the driver;
971
+ `prompt` sends it to the agent over stdin with
972
+ `$gtd_session_id`/`$gtd_model`/`$gtd_system` mapped onto the agent's session
973
+ flags — trying `resume`'s hinted flag first and falling back to the other on
974
+ failure, since the session id is derived, not remembered (see
975
+ [Driving the loop](#driving-the-loop) above) — and its own `$gtd_validate`
805
976
  script's output re-prompted verbatim on failure (the driver owns only the retry
806
- cap); and every landed turn executed — and reported — by the emitted scripts
807
- themselves, with `gtd land`'s exit 3 (SETTLED) ending a run that has nothing
808
- left to do.
977
+ cap). Every optional variable (`$gtd_model`, `$gtd_system`, `$gtd_validate`,
978
+ `$gtd_session_resume`, `$gtd_idle`, `$gtd_settled`) is read as `${var:-}` or
979
+ `${var:+...}` — under `set -u`, the `--sh` document's own `unset` preamble makes
980
+ an absent field genuinely unset, not empty, so a bare `$gtd_model` would abort
981
+ the driver. Every landed turn is executed — and reported — by the emitted
982
+ scripts themselves, via `gtd land --sh`'s own `$gtd_script` field, captured then
983
+ piped to `sh`. `$gtd_settled` ends a run that has nothing left to do: a no-op at
984
+ a `script` rest settles right where it rests (stop immediately, nothing more to
985
+ read), while an ordinary or squash landing that finishes the whole process
986
+ instead resolves `$gtd_idle` on the FOLLOWING beat's own `gtd next --sh` read —
987
+ the reference driver reads once more (a plain `gtd next`) only to DISPLAY that
988
+ gate's own message, the decision to stop already made from `$gtd_settled`/
989
+ `$gtd_idle` alone.
990
+
991
+ This paste passes `$gtd_system` on argv, and POSIX guarantees only 4 KB of argv
992
+ (`ARG_MAX`) — real systems are far higher (roughly 1 MB on both macOS and
993
+ Linux), and the bundled personas are only a few KB each, so this works
994
+ everywhere in practice. A driver shipping much larger system prompts, or
995
+ targeting a platform sitting at the POSIX floor, should write the string to a
996
+ file and pass that instead: gtd's own contract ends at emitting `gtd_system` as
997
+ a string, and how a driver hands that string to its agent CLI — argv, a temp
998
+ file, or something else — is the driver's own call, since it alone knows its
999
+ platform's argv limit and whether it has a writable temp directory; gtd adds no
1000
+ flag, no file, and no mechanism of its own for this. (The doc-test below only
1001
+ proves this paste parses and runs against a `claude` shim — not that the flag is
1002
+ spelled or placed correctly; that's a `claude --help` check plus one live run,
1003
+ not something the green suite can catch.)
809
1004
 
810
1005
  ### The self-validation gate
811
1006
 
812
1007
  After an agent turn at a state that declares `file:`+`mode:`, run the script:
813
1008
  either `gtd next --json`'s own embedded `.validate` field (present at every
814
1009
  `prompt` beat that hands over a validatable file), or, standalone,
815
- `gtd validate --json`'s `.script` — both resolve the exact same script from one
816
- shared resolver. Exit 0 means the file is well-formed — proceed to `gtd land`. A
817
- non-zero exit means the script's own captured output IS a complete,
818
- ready-to-send fix prompt (an instruction plus the findings, see `src/Emit.ts`'s
819
- `failurePromptWrapper`): send it back to the same agent session verbatim, and
820
- cap how many fix attempts you allow yourself — the driver owns that retry count,
821
- not gtd. Landing never TRUSTS that you validated, either: the emitted `land`
822
- script carries the same format/validate commands ahead of its own commit and
823
- fails without committing when they fail, so a malformed file is never captured
824
- whether or not you ran the validate script first.
1010
+ `gtd validate`'s own plain-text output — both resolve the exact same script from
1011
+ one shared resolver. Exit 0 means the file is well-formed — proceed to
1012
+ `gtd land`. A non-zero exit means the script's own captured output IS a
1013
+ complete, ready-to-send fix prompt (an instruction plus the findings, see
1014
+ `src/Emit.ts`'s `failurePromptWrapper`): send it back to the same agent session
1015
+ verbatim, and cap how many fix attempts you allow yourself — the driver owns
1016
+ that retry count, not gtd. Landing never TRUSTS that you validated, either: the
1017
+ emitted `land` script carries the same format/validate commands ahead of its own
1018
+ commit and fails without committing when they fail, so a malformed file is never
1019
+ captured whether or not you ran the validate script first.
825
1020
 
826
1021
  ### Failure taxonomy and recovery
827
1022
 
828
- Two different things can go wrong, and they mean different things — and one
829
- non-zero-looking exit is not a failure at all:
1023
+ Several different things can go wrong, and they mean different things — and most
1024
+ non-zero-looking exits are not a failure at all:
830
1025
 
831
1026
  - **`gtd` itself exits 1.** Nothing was attempted — this is a refusal (a guard
832
- rejected the turn) or a usage error. No script was ever produced.
833
- - **`gtd land` exits 3 (SETTLED).** This is NOT a failure: nothing is owed, and
834
- stdout still carries a script (a print-only note, or a genuine retain+rewind)
835
- that a driver must still run.
1027
+ rejected the turn). No script was ever produced.
1028
+ - **`gtd` itself exits 2.** A usage error — nothing was even attempted, the
1029
+ invocation itself was wrong (unknown option/command, bad arity, a scope
1030
+ violation).
1031
+ - **`gtd land` succeeds (exit 0) but doesn't settle.** This is NOT a failure:
1032
+ whose turn is next lives in the FOLLOWING `gtd next`'s own `kind` field, not
1033
+ in `gtd land`'s exit code (see [Exit codes](#exit-codes)) — and `gtd land`'s
1034
+ own stdout still carries a script (a print-only note, or a genuine
1035
+ retain+rewind, or an ordinary commit) that a driver must still run.
836
1036
  - **An emitted script exits non-zero when YOU run it.** Something may have
837
1037
  partially happened — e.g. a `gtd_retry`-wrapped git write landed but a later
838
1038
  step in the same script failed.
@@ -856,8 +1056,10 @@ its own retry/resume logic beyond "if the script failed, ask gtd again."
856
1056
 
857
1057
  ### Prerequisites
858
1058
 
859
- - **`jq`** — to pull the `required`/`optional` strings back out of gtd's
860
- `--json` output.
1059
+ - **A POSIX `sh`** (dash, ash, bash's own POSIX mode, etc.) — gtd's own emitted
1060
+ scripts (`gtd land`, `gtd --entry <state>`, `gtd abandon`, `gtd restore`) are
1061
+ POSIX sh; captured, then piped into it (see
1062
+ [Writing your own driver](#writing-your-own-driver) above).
861
1063
  - **`gtd` on `PATH`** — a mode's seeded `validate:` command (the one the
862
1064
  compiler fills in for the built-in `qa`/`review` formats) is literally the
863
1065
  string `gtd check <mode> '<file>'`, invoked by NAME from inside an emitted
@@ -896,7 +1098,7 @@ One case never runs your `format:` (or `validate:`) at all: a step whose diff
896
1098
  DELETES the state's own `file:`. Deleting it is a legitimate outcome — a review
897
1099
  sign-off's whole diff is the review doc's deletion — and there is nothing left
898
1100
  to format. Emitting the command anyway would make such a step unlandable, since
899
- `format:` is the first line of a `set -euo pipefail` script and a formatter like
1101
+ `format:` is the first line of a `set -eu` script and a formatter like
900
1102
  `prettier --write` exits non-zero on a path that is not there.
901
1103
 
902
1104
  ### Built-in steering formats are ordinary modes
@@ -977,7 +1179,6 @@ workflow:
977
1179
  <name>:
978
1180
  format: <shell command> # both optional — {} is the format-only tier
979
1181
  validate: <shell command>
980
- stateDir: <string> # optional, an Eta template naming where this workflow keeps its own plumbing (default ".gtd") — the definition-level declaration the engine reads, distinct from the `vars.stateDir` var the bundled template renders it from
981
1182
  entry:
982
1183
  default: <machine name> # which machine is the ROOT instance
983
1184
  machines:
@@ -1002,8 +1203,8 @@ workflow:
1002
1203
  retry:
1003
1204
  max: <number>
1004
1205
  otherwise: <targetState>
1005
- label: <string> # optional, opaque display name passed through `gtd next --json`/`gtd status --json`
1006
- file: <string> # optional, an Eta template naming the state's steering file
1206
+ label: <string> # optional, opaque display name passed through `gtd next --json`
1207
+ file: <string> # optional, an Eta template naming the state's steering file RELATIVE to ".gtd/" — the compiler prepends that directory automatically
1007
1208
  mode: <modeName> # optional, requires "file" — must be declared in `modes:` (qa/review are seeded for you; everything else, including prose, you declare)
1008
1209
  reviewWindow: true # optional — open the review checkout window at rest here
1009
1210
  reviewBase: true # optional — anchor the review window's diff base to this state's most-recent commit
@@ -1023,11 +1224,7 @@ derives a driver's session id).
1023
1224
  The top-level `entry:` key (naming the root machine, `entry.default`) and a
1024
1225
  state's own `entry: true` flag are the same word at two different levels, by
1025
1226
  design: one selects the workflow's root machine, the other opts one state in as
1026
- an extra manual entry point. The top-level `stateDir:` key and the
1027
- `vars.stateDir` var it renders from are the same call: the definition-level
1028
- declaration is what the engine reads (e.g. to tell gtd's own plumbing apart from
1029
- your code), while the var is the knob you actually override — repointing
1030
- `vars.stateDir` moves both.
1227
+ an extra manual entry point.
1031
1228
 
1032
1229
  A workflow is authored as a TREE of reusable, parameterized machines — a
1033
1230
  gate/loop written once and instantiated several times with different `with:`
@@ -1060,8 +1257,8 @@ Besides `it.vars` (below), a `script`/`prompt`/`message`/`commit` template sees:
1060
1257
 
1061
1258
  A template never sees rendered diff CONTENT — no field carries a diff. It names
1062
1259
  a base and leaves the agent to run `git diff <base>` itself; this keeps every
1063
- render cheap (no diff computed on `gtd next`/`gtd status`/`gtd lsp`) and the
1064
- prompt small and cacheable.
1260
+ render cheap (no diff computed on `gtd next`/`gtd lsp`) and the prompt small and
1261
+ cacheable.
1065
1262
 
1066
1263
  Authoring or editing a workflow with a coding agent? `skills/authoring/SKILL.md`
1067
1264
  is the agent-facing contract for producing a valid `workflow:` — the state
@@ -1222,10 +1419,10 @@ top-level `.gtdrc` `vars:` key, or the matching `GTD_STYLEBLOCK` /
1222
1419
  `GTD_STYLEFORMATCONTRACT` environment variable):
1223
1420
 
1224
1421
  - **`styleBlock`** — the voice itself, injected at all seven prompt states that
1225
- generate content, not just three: the free-prose ones — the `packagesDir`
1226
- package files (`architecture.decompose`), `specFeedbackFile`
1227
- (`packages.item.spec.review`), and `commitMsgFile` (`build.squashing`) — plus
1228
- the four machine-parsed states named in the next bullet. Blanking
1422
+ generate content, not just three: the free-prose ones — the `.gtd/packages/`
1423
+ package files (`architecture.decompose`), `.gtd/SPEC_FEEDBACK.md`
1424
+ (`packages.item.spec.review`), and `.gtd/COMMIT_MSG.md` (`build.squashing`) —
1425
+ plus the four machine-parsed states named in the next bullet. Blanking
1229
1426
  `GTD_STYLEBLOCK` strips the voice from all seven, including the machine-parsed
1230
1427
  ones, not only the free-prose three. In short: why density matters before any
1231
1428
  rule, answer-first with no preamble or restatement, blunt and imperative,
@@ -1238,17 +1435,17 @@ top-level `.gtdrc` `vars:` key, or the matching `GTD_STYLEBLOCK` /
1238
1435
  ahead of the role sentence and well before any state-specific format-contract
1239
1436
  text — `styleBlock` first, then `styleFormatContract`, at the four prompt
1240
1437
  states whose output a parser reads: the two `qa`-mode steering files
1241
- (`requirementsFile` at `design.triage`, `architectureFile` at
1242
- `architecture.author`) and the `review`-mode one (`reviewFile` at
1243
- `build.review.reviewing`), plus `requirementsFile` again at
1438
+ (`.gtd/REQUIREMENTS.md` at `design.triage`, `.gtd/ARCHITECTURE.md` at
1439
+ `architecture.author`) and the `review`-mode one (`.gtd/REVIEW.md` at
1440
+ `build.review.reviewing`), plus `.gtd/REQUIREMENTS.md` again at
1244
1441
  `build.review.collecting`, which classifies a review round straight into it.
1245
1442
 
1246
1443
  Three more generated files carry no injected voice, because a script — not an
1247
- agent — writes them: `feedbackFile` (verbatim test-suite output plus a HEAD
1248
- stamp — the tool's content, not gtd's prose), `nextFile` (a bare path, no prose
1249
- to style), and `reviewRawFile` (gtd's own prose, hand-tightened in the voice
1250
- directly in `build.review.deciding`'s script rather than templated in through
1251
- either variable).
1444
+ agent — writes them: `.gtd/FEEDBACK.md` (verbatim test-suite output plus a HEAD
1445
+ stamp — the tool's content, not gtd's prose), `.gtd/NEXT.md` (a bare path, no
1446
+ prose to style), and `.gtd/REVIEW_RAW.md` (gtd's own prose, hand-tightened in
1447
+ the voice directly in `build.review.deciding`'s script rather than templated in
1448
+ through either variable).
1252
1449
 
1253
1450
  ### Lookup and precedence
1254
1451
 
@@ -1323,8 +1520,8 @@ option"/"uncheck this option" code actions on each option — offered anywhere o
1323
1520
  the option's list item, including any wrapped continuation lines, not just its
1324
1521
  own `- [ ]` line — diagnostics for both (live as you edit), and a
1325
1522
  `gtd.openSteeringFile` command that jumps to the current state's steering file —
1326
- including `idle`, whose `todoFile` hint gives the one keybinding an answer even
1327
- before a process has started. The command only names that path — it never
1523
+ including `idle`, whose `.gtd/TODO.md` hint gives the one keybinding an answer
1524
+ even before a process has started. The command only names that path — it never
1328
1525
  stat-checks or creates it — so on a repo that has never run gtd, `.gtd/` may not
1329
1526
  exist yet and editors differ on opening a file whose parent directory is missing
1330
1527
  (an empty buffer that creates it on save vs. refusing the path outright); this