@pmelab/gtd 9.5.0 → 9.7.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.
Files changed (3) hide show
  1. package/README.md +470 -273
  2. package/dist/gtd.bundle.mjs +2264 -1700
  3. package/package.json +3 -2
package/README.md CHANGED
@@ -85,16 +85,17 @@ gtd is a small **pattern machine**: named states, each awaiting one actor and
85
85
  carrying one piece of content (a script, a prompt, a message, or a squash commit
86
86
  template), with an ordered set of change-patterns routing to the next state.
87
87
 
88
- The loop is one beat, repeated: run `gtd next --json` and dispatch on `kind` —
88
+ The loop is one beat, repeated: run `gtd status --json` and dispatch on `kind` —
89
89
  `"message"` means it's a human's move (stop and hand off, unless it's the run's
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,
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 status --json` to continue or
96
+ start that agent conversation (see "Driving the loop" below) — then run
97
+ `gtd land` once it's done. gtd itself never executes anything — the driver owns
98
+ running scripts. Every `gtd next`/ `gtd status` call is strictly mutation-free,
98
99
  safe to poll or peek at any time: `session.id`/`session.resume` are DERIVED,
99
100
  never stored, so looking is free — nothing distinguishes a peek from a dispatch,
100
101
  and there is no separate claiming form at all. A `prompt` beat whose turn
@@ -102,7 +103,7 @@ changes nothing still lands: `gtd land` commits an EMPTY `gtd(<actor>): <state>`
102
103
  attempt instead of silently doing nothing, so the fruitless dispatch is visible
103
104
  in history rather than invisible. `"kind": "stalled"` is derived from that
104
105
  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
+ so every `gtd status --json` call reports it, and it stays reported on a repeat
106
107
  (there's no marker to consume) until something actually changes. The fix for a
107
108
  repeated stall is either a better prompt, a `retry:` cap on the state that
108
109
  redirects to an escalation state after N fruitless attempts, or — if the state
@@ -179,41 +180,57 @@ file yourself and `gtd land` — no hand-authored state commit.
179
180
  The process converges on that same shared tail: an agent hands you a
180
181
  `.gtd/REVIEW.md` checkbox review of the diff — the prompt never inlines the diff
181
182
  itself; it names the commit the changes are based at and the agent runs
182
- `git diff` to read the range before writing the review. While the process rests
183
- at that gate, the landing script opens a **review checkout window**: HEAD is
184
- rewound to the review base with the working tree untouched, so the whole
185
- reviewable change shows up as ordinary uncommitted changes in your editor's
186
- normal git integration (and files added during the process show up as ordinary
187
- untracked files, so discarding one deletes it — an untracked file you leave
188
- alone is not a pending change, and only actually removing it from disk counts as
189
- a deletion). The next landing's own script closes the window before it commits.
190
- Tick a box as you review each hunk (ticking just records "I read this"), and
191
- leave a **comment** to request changes: a note on a line, an inline
192
- `// TODO`-style comment in the code, or a direct code edit. A comment sends a
193
- FULL development lap, not a quick fix-and-re-review: an agent first judges
194
- whether your comment is actually actionable (a genuinely approving remark with
195
- no code edit short-circuits straight to sign-off instead), and an actionable
196
- round is re-planned from scratch through triage, architecture, and the package
197
- queue again — a hand-edit you made during review is treated as a **sketch**, the
198
- same as any other change that starts a process, not a fix the agent builds on:
199
- it is reverted out of the tree first, and your intent survives only in your own
200
- review-round commit for triage to read. There is no baseline check on the way
201
- back into planning. If a styled `.gtd/REVIEW.md` ever comes out malformed (a
202
- paragraph where a `- [ ] ./path#line` row belongs), `gtd check review` refuses
203
- the step before it commits: the file stays exactly as written in the working
204
- tree, unlanded, and re-running the same prompt is the recovery — nothing is
205
- lost. Only the turn that drafts the final squash message resumes the session
206
- that built the feature in the first place (the review tail is nested inside that
207
- build identity rather than sitting beside it) — an actionable round leaves that
208
- identity entirely, so it starts a fresh session like any other process. Ticking
209
- every box with no comment is the sign-off, which collapses the whole process
210
- into one commit (a **squash finale** whose message an agent drafts). Landing
211
- with a box still unticked and no comment is refused (finish reviewing first), as
212
- is deleting `.gtd/REVIEW.md`. Both refusals hold wherever the review doc lives:
213
- repointing `reviewFile` out of `.gtd/` (say to `REVIEW.md` at the repo root)
214
- changes nothing about them — your own edit to the review doc is never mistaken
215
- for a code comment, and the doc's pre-turn copy is read at the review window's
216
- saved head rather than at the rewound `HEAD`.
183
+ `git diff` to read the range before writing the review. Each hunk is a
184
+ `- [ ] ./path/to/file.ts#42` pointer with its explanation on the line(s) BELOW
185
+ it, not trailing the pointer on the same line:
186
+
187
+ ```
188
+ - [ ] ./path/to/file.ts#42
189
+ what this hunk does, with room to run to several lines
190
+ ```
191
+
192
+ Text after the pointer token on the pointer's own line is a validation error —
193
+ `gtd check`/`gtd validate`/the LSP all reject it — and, because the review
194
+ gate's own emitted script runs that same check ahead of every commit, upgrading
195
+ gtd mid-review leaves an already-open `.gtd/REVIEW.md` written in the old
196
+ same-line form UNLANDABLE until every pointer in it is hand-edited into the new
197
+ shape; there is no migration tool. If a styled `.gtd/REVIEW.md` ever comes out
198
+ malformed some other way (a paragraph where a `- [ ] ./path#line` row belongs),
199
+ `gtd check review` refuses the step the same way: the file stays exactly as
200
+ written in the working tree, unlanded, and re-running the same prompt is the
201
+ recovery — nothing is lost. While the process rests at that gate, the landing
202
+ script opens a **review checkout window**: HEAD is rewound to the review base
203
+ with the working tree untouched, so the whole reviewable change shows up as
204
+ ordinary uncommitted changes in your editor's normal git integration (and files
205
+ added during the process show up as ordinary untracked files, so discarding one
206
+ deletes it — an untracked file you leave alone is not a pending change, and only
207
+ actually removing it from disk counts as a deletion). The next landing's own
208
+ script closes the window before it commits. Tick a box as you review each hunk
209
+ (ticking just records "I read this"), and leave a **comment** to request
210
+ changes: a note on a line, an inline `// TODO`-style comment in the code, or a
211
+ direct code edit. A comment sends a FULL development lap, not a quick
212
+ fix-and-re-review: an agent first judges whether your comment is actually
213
+ actionable (a genuinely approving remark with no code edit short-circuits
214
+ straight to sign-off instead), and an actionable round is re-planned from
215
+ scratch through triage, architecture, and the package queue again — a hand-edit
216
+ you made during review is treated as a **sketch**, the same as any other change
217
+ that starts a process, not a fix the agent builds on: it is reverted out of the
218
+ tree first, and your intent survives only in your own review-round commit for
219
+ triage to read. There is no baseline check on the way back into planning. Only
220
+ the turn that drafts the final squash message resumes the session that built the
221
+ feature in the first place (the review tail is nested inside that build identity
222
+ rather than sitting beside it) — an actionable round leaves that identity
223
+ entirely, so it starts a fresh session like any other process. A comment is what
224
+ asks for changes; landing with no comment signs off, whatever the boxes say,
225
+ which collapses the whole process into one commit (a **squash finale** whose
226
+ message an agent drafts). Deleting `.gtd/REVIEW.md` is refused, wherever the
227
+ review doc lives: repointing `reviewFile` out of `.gtd/` (say to `REVIEW.md` at
228
+ the repo root) changes nothing about that refusal, since it only asks whether
229
+ the step's diff deletes the file. The exact-path exemption issue #128 fixed
230
+ still matters elsewhere, though: the `deciding` state's own classification and
231
+ the feedback-progress guard both exclude the state's own `file:` by exact path,
232
+ not merely a `.gtd/` prefix, so your own edit to a relocated steering file is
233
+ never mistaken for a code comment.
217
234
 
218
235
  A second entry, the same review tail's own direct entry point —
219
236
  `gtd --entry review-gate.check --var reviewBase=<commitish>` starts a brand new
@@ -285,18 +302,19 @@ Commands:
285
302
  Takes no argument. Run once per repo; refuses if a gtd
286
303
  config already exists. Leaves the file uncommitted for you
287
304
  to review and commit
288
- land Land whatever the tree now shows at the currently
289
- resolved rest — a human capture, an agent/check turn, an
290
- empty attempt (a fruitless prompt turn), or a squash — and
291
- print the script that records it; a driver runs the
292
- script, e.g. `gtd land | bash`. Pass --cost=<n>
293
- (optionally --model=<name>) to record the just-finished
294
- invocation's token cost and model on the turn commit
295
- (summed into it.processCost/processCostByModel). Exits 0
296
- when a script is emitted (or a benign no-op at a clean
297
- message rest), 3 when SETTLED — nothing owed: a no-op at a
298
- script rest, or the initial-state collapse (stdout still
299
- carries the script) — 1 on any refusal
305
+ land Land whatever the tree now shows at the currently resolved
306
+ rest — a human capture, an agent/check turn, an empty
307
+ attempt (a fruitless prompt turn), or a squash. Pass
308
+ --cost=<n> (optionally --model=<name>) to record the
309
+ just-finished invocation's token cost and model on the
310
+ turn commit (summed into it.processCost/
311
+ processCostByModel). Plain (the default) prints ONLY the
312
+ script that records the landing; a driver runs it, e.g.
313
+ `gtd land | sh`. --json/--sh instead emit script (that
314
+ same script, byte-identical) alongside settled, idle,
315
+ state (the post-land target), subject, cost and model —
316
+ --json/--sh are mutually exclusive. Exits 0 on success, 1
317
+ on any refusal — see the Exit codes section below
300
318
  (no command) --entry <state>
301
319
  Starts a new process authenticated as human, e.g.
302
320
  'gtd --entry <state>'
@@ -311,29 +329,35 @@ Commands:
311
329
  Refuses on a dirty working tree, when there is no retained
312
330
  history, or when HEAD has advanced past the squash with
313
331
  commits that would be lost
314
- next Print the resolved rest's rendered script/prompt/message
315
- (no mutation, safe to poll). --json emits the whole beat
316
- document instead: kind (capture|message|script|prompt|
317
- stalled) selects what a driver does, content is what it
318
- runs or shows, plus the prompt session, model, validate
319
- script, log path and the resting state's own fields
320
- status Print the resolved rest's state/actor and which declared
321
- pattern (if any) each pending change matches (no mutation)
332
+ next Print the resolved rest's beat (no mutation, safe to
333
+ poll), in one of three encodings. Plain (the default): a
334
+ status summary, a blank line, then the step verbatim —
335
+ except at a prompt rest, which is the bare step (plus the
336
+ self-validation instruction when applicable) with no
337
+ header, since those bytes are the agent's own input. --json
338
+ emits the one structured surface gtd has: kind
339
+ (capture|message|script|prompt|stalled) selects what a
340
+ driver does, content is what it runs or shows, idle marks
341
+ the workflow's initial state with a clean tree, plus the
342
+ prompt session, model, validate script, log path, changes,
343
+ next and the resting state's own fields. --sh emits the
344
+ same fields as gtd_-prefixed POSIX shell assignments.
345
+ --json/--sh are mutually exclusive. Exits 0 — see the
346
+ Exit codes section below
322
347
  validate Print the script that formats (when declared) then
323
348
  validates the resolved rest's steering file, using its
324
349
  mode's commands (its file:/mode:), instead of running it —
325
350
  a driver runs the script and reads the findings from its
326
- own exit code/output. Always exits 0; --json emits
327
- {state, file?, mode?, script} (script is "" when there is
328
- nothing to validate; plain text prints "nothing to
329
- validate" in that case). On a non-zero validate exit
330
- the emitted script prints a ready-to-send fix prompt
331
- (instruction + findings) and exits with the
332
- validator's own code
351
+ own exit code/output. Always exits 0; prints "nothing to
352
+ validate" when the resolved rest declares nothing to run.
353
+ On a non-zero validate exit the emitted script prints a
354
+ ready-to-send fix prompt (instruction + findings) and
355
+ exits with the validator's own code
333
356
  lsp Start the LSP server for .gtd/ steering files (stdio)
334
357
  visualize Serve an interactive diagram of the active workflow on a
335
- local web server (--port <n>, --no-open; --json prints the
336
- model and exits)
358
+ local web server (--port <n>, --no-open). Prints the
359
+ chosen port on its own line — with --port 0, this is the
360
+ only way to learn which port was picked
337
361
  check <mode> <file>
338
362
  Read <file> and run the built-in steering format named
339
363
  <mode> (see `gtd validate`'s modes: qa, review) over its
@@ -354,7 +378,11 @@ Commands:
354
378
  help Print this help and exit
355
379
 
356
380
  Options:
357
- --json Output structured JSON instead of plain text
381
+ --json (gtd next/gtd land only) output structured JSON
382
+ instead of plain text. Mutually exclusive with --sh
383
+ --sh (gtd next/gtd land only) output gtd_-prefixed POSIX
384
+ shell assignments instead of plain text. Mutually
385
+ exclusive with --json
358
386
  --port=<n> (gtd visualize only) port to serve on (default: a free port)
359
387
  --no-open (gtd visualize only) do not open the browser
360
388
  --cost=<n> (gtd land only) record the invocation's token cost
@@ -370,23 +398,73 @@ Options:
370
398
  instead run the qa open-questions predicate over <file>,
371
399
  printing each unanswered question one per line and exiting
372
400
  non-zero when any remain
373
- --version, -v Print version and exit
401
+ --verbose enable stderr narration for this invocation: which rest
402
+ resolved, which declared pattern each pending change
403
+ matched, which review-window action was emitted, and how
404
+ config resolved across layers. Aliased to -v
405
+ --version, -V Print version and exit
374
406
  --help, -h Print this help and exit
375
407
  ```
376
408
 
377
- `--version` (`-v`) / `gtd version` and `--help` (`-h`) / `gtd help`
409
+ ### Plain output is not a parsing surface
410
+
411
+ `gtd next`'s plain encoding (and `gtd status`'s own) is for a human, or a driver
412
+ that merely displays it — never for scraping. Parsing lives in `--json`/`--sh`,
413
+ both of which read from the exact same field set (`gtd install`'s briefing has
414
+ the full reference); anything that greps, cuts, or `awk`s plain text is
415
+ unsupported, and its shape may change across releases with no warning.
416
+
417
+ ### Exit codes
418
+
419
+ Closed at five numbers, five meanings — a new command never grows this table;
420
+ whose turn is next lives in `gtd next --json`'s own `kind` field instead.
421
+
422
+ | Code | Meaning |
423
+ | --------- | ---------------- |
424
+ | 0 | success |
425
+ | 1 | runtime error |
426
+ | 2 | usage error |
427
+ | 130 / 143 | SIGINT / SIGTERM |
428
+
429
+ Every command follows this table uniformly, `next`/`land` included: `0` on
430
+ success, `1` on refusal, `2` on usage error, `130`/`143` when gtd itself dies by
431
+ that signal — a parent's `wait` sees a real signal death (`WIFSIGNALED`), not a
432
+ chosen exit code that merely reuses the same number. No command's exit code
433
+ carries a second meaning.
434
+
435
+ **Migration — read this even if you already migrated for a prior release.** This
436
+ is the second inversion in as many releases, folded into one note rather than
437
+ two to compose in your head: `10`/`20` (whose turn was next) are gone — every
438
+ command exits `0`/`1`/`2` uniformly now; `gtd status` is gone (folded into
439
+ `gtd next`); and plain `gtd next` now prints a status header at every kind
440
+ EXCEPT `prompt` — agent input is untouched, since plain `gtd next` at a `prompt`
441
+ rest is byte-identical to before. A driver must read whose turn is next off
442
+ `gtd next --json`'s own `kind` field
443
+ (`capture`/`message`/`script`/`prompt`/`stalled`) — never off gtd's exit code,
444
+ which no longer carries that signal at all.
445
+
446
+ Every usage mistake — an unknown option or command, missing/extra arguments, a
447
+ scope violation (e.g. `--cost` on a command other than `gtd land`), a bad flag
448
+ value — is a USAGE error (`2`), never a runtime error (`1`): nothing was even
449
+ attempted. `--help`/`--version` still exit `0`.
450
+
451
+ `--version` (`-V`) / `gtd version` and `--help` (`-h`) / `gtd help`
378
452
  short-circuit before any git or repository-state work — they run outside a repo
379
- and in any repo state. Bare `gtd` (no subcommand) and `gtd loop` are both usage
380
- errors that exit 1 and print help — gtd decides and prints, full stop; driving a
381
- loop is a driver's job, not a bundled command (see
382
- [Driving the loop](#driving-the-loop) below). Any other, truly unknown
383
- subcommand is likewise a usage error: it prints the help text and exits 1
384
- without touching the repository. The state commands (`land`, `--entry`,
385
- `abandon`, `restore`, `next`, `status`, `validate`) must run from the
386
- **repository root** — gtd derives the workflow, pending changes, and process
387
- history relative to cwd, so they refuse with a clear error from a subdirectory;
388
- `lsp`, `init`, `visualize`, `check`, and `install` are standalone and run from
389
- anywhere (see each command's own help entry).
453
+ and in any repo state, and print to **stdout** at exit 0. `--verbose` (`-v`) is
454
+ not a short-circuit — it gates narration for whatever command follows (see
455
+ [Narration and remediation](#narration-and-remediation) below). Bare `gtd` (no
456
+ subcommand) is a usage error that exits 2 — gtd decides and prints, full stop;
457
+ driving a loop is a driver's job, not a bundled command (see
458
+ [Driving the loop](#driving-the-loop) below) — printing its help text to
459
+ **stderr**, not stdout: stdout stays byte-empty on every failure, a usage error
460
+ included (see [Error envelope](#error-envelope) below). Any other, truly unknown
461
+ subcommand is likewise a usage error exiting 2 without touching the repository.
462
+ The state commands (`land`, `--entry`, `abandon`, `restore`, `next`, `status`,
463
+ `validate`) must run from the **repository root** — gtd derives the workflow,
464
+ pending changes, and process history relative to cwd, so they refuse with a
465
+ clear error from a subdirectory; `lsp`, `init`, `visualize`, `check`, and
466
+ `install` are standalone and run from anywhere (see each command's own help
467
+ entry).
390
468
 
391
469
  `--json`, `--cost=<n>`, `--model=<name>` (the latter two only for `gtd land`),
392
470
  `--entry <state>` (no other command at all), and `--var <name>=<value>` (with
@@ -419,23 +497,68 @@ real `gtd land` lands.
419
497
 
420
498
  ### Error envelope
421
499
 
422
- Every command, in `--json` mode, reports a failure as a machine-readable
423
- envelope on **stdout**, and still exits 1:
500
+ **stdout is either the complete artifact or byte-empty — never a partial write
501
+ followed by an error.** Every command buffers everything it would print and
502
+ flushes that buffer to stdout exactly once, only after it succeeds; on any
503
+ failure the buffer is simply discarded, so stdout never carries a half-written
504
+ prompt/script alongside a message about why it stopped. `gtd visualize` is the
505
+ one exception worth knowing: it flushes its served-URL line immediately, before
506
+ blocking on `Ctrl-C`, since a flush-on-success would never otherwise fire.
507
+
508
+ Any invocation that carries `--json` (valid only for `gtd status` — every other
509
+ command usage-errors on it) reports a failure as a machine-readable envelope on
510
+ **stderr** — including the scope violation itself, so a driver that always adds
511
+ `--json` still gets a parseable envelope on every failure, not only
512
+ `gtd status`'s own:
424
513
 
425
514
  ```json
426
515
  { "state": "error", "prompt": "<message>" }
427
516
  ```
428
517
 
429
- This covers every failure mode, not just a command's own refusal: a **usage
430
- error** (an unknown flag, a missing argument, `gtd --entry version`'s "not an
431
- enterable state") and a **defect** (a layer throwing outside the ordinary error
432
- channel) both get the same envelope — there is no failure path that reaches
433
- `--json` without one.
434
-
435
- A human-readable `gtd: <message>` line is still written to **stderr** regardless
436
- of `--json` — the envelope adds a structured stdout channel, it does not replace
437
- the plain-text one. Stderr always carries exactly one `gtd: ` prefix: a message
438
- already authored with its own `gtd:`/`gtd <cmd>:` prefix is never doubled.
518
+ This covers every failure mode, not just a command's own refusal (exit 1): a
519
+ **usage error** (an unknown flag, a missing argument, `gtd --entry version`'s
520
+ "not an enterable state" — exit 2) and a **defect** (a layer throwing outside
521
+ the ordinary error channel — exit 1) both get the same envelope shape — there is
522
+ no failure path that reaches `--json` without one — but a usage error's exit
523
+ code is 2, never 1, so a driver can tell "you invoked gtd wrong" apart from "gtd
524
+ refused/broke" (see [Exit codes](#exit-codes)).
525
+
526
+ A human-readable `gtd: <message>` line is also always written to **stderr**,
527
+ right after the envelope — stdout carries neither one on a failing run. Stderr
528
+ always carries exactly one `gtd: ` prefix: a message already authored with its
529
+ own `gtd:`/`gtd <cmd>:` prefix is never doubled.
530
+
531
+ **Accepted cost:** a driver that pipes stdout into `jq` on a failed run now
532
+ reads nothing instead of a parseable error object — it must read stderr or the
533
+ exit code to learn why a run failed.
534
+
535
+ ### Narration and remediation
536
+
537
+ Stderr carries two things beyond the `gtd: ` message line above: NARRATION,
538
+ gated by `--verbose`/`-v`, and REMEDIATION, unconditional.
539
+
540
+ `--verbose` (alias `-v`) turns on one line of commentary per in-process fact a
541
+ command's dispatch already computes — which rest resolved, which declared
542
+ pattern each pending change matched, which review-window action was emitted, and
543
+ how config resolved across `.gtdrc` layers. Without it, none of this is printed;
544
+ stdout is never touched either way — narration is a stderr-only concern, exactly
545
+ like the error envelope above.
546
+
547
+ A failure's remediation detail is unconditional — it prints at every verbosity,
548
+ on the line(s) right after the `gtd: `-prefixed message, each indented two
549
+ spaces: a bad config key names the offending key and which `.gtdrc` layer it
550
+ came from, a corrupted ref names the ref, and a missing binary in a steering
551
+ mode's `format:`/`validate:` command names the resolved `$PATH` it was looked up
552
+ in.
553
+
554
+ ### Non-interactive today
555
+
556
+ gtd is non-interactive: no readline, no `/dev/tty`, no prompt call anywhere in
557
+ `src/`. If interaction is ever added, it goes to `/dev/tty`, never stderr, and
558
+ never blocks — a question fails with a code when the tty cannot be opened rather
559
+ than hanging. The reason is the section above: stderr already has two occupants,
560
+ narration and remediation, and a question mixed into that stream would deadlock
561
+ a driver that never reads stderr.
439
562
 
440
563
  ## Driving the loop
441
564
 
@@ -469,24 +592,24 @@ not of the loop itself; see
469
592
  below for a worked example.
470
593
 
471
594
  A workflow state can declare an optional `label:` — a human-readable display
472
- name surfaced in `gtd next --json`/`gtd status`. The driver uses it for its
473
- per-beat progress lines; an outer wrapper (a terminal multiplexer, a notifier)
474
- can use it the same way.
595
+ name surfaced in `gtd status --json`/plain `gtd status`. The driver uses it for
596
+ its per-beat progress lines; an outer wrapper (a terminal multiplexer, a
597
+ notifier) can use it the same way.
475
598
 
476
599
  Memory is **entry-scoped to a machine**, not a state-authored label: each
477
600
  machine instance (a node in the `machines:` tree, e.g. `build`, `build.health`,
478
601
  `packages.item`, `packages.item.health`) owns its own conversational scope, and
479
- a `prompt`-content state's `memory` key — surfaced in `gtd next --json`/
480
- `gtd status --json`'s `memory` field, and as a `Memory: <key>` line in plain
481
- `gtd status` — is computed, never authored, as `<scope>#<hash7>`: `<scope>` is
482
- that machine instance's dotted path (the root instance is shown as `root`), and
483
- `<hash7>` anchors to the commit the CURRENT unbroken entry into that scope
484
- started FROM. Entering a **descendant** scope (e.g. dipping from `build` into
485
- `build.health`) does not break the parent's unbroken run — a full agent turn in
486
- a nested child machine, then back to the parent, still resumes the SAME parent
487
- conversation; entering a **sibling or unrelated** scope does start a fresh one.
488
- The bundled template's `build.review` (the human review tail) is a worked
489
- example: it is nested INSIDE `build` (the builder's own machine) so that a
602
+ a `prompt`-content state's `memory` key — surfaced in `gtd status --json`'s
603
+ `memory` field, and as a `Memory: <key>` line in plain `gtd status` — is
604
+ computed, never authored, as `<scope>#<hash7>`: `<scope>` is that machine
605
+ instance's dotted path (the root instance is shown as `root`), and `<hash7>`
606
+ anchors to the commit the CURRENT unbroken entry into that scope started FROM.
607
+ Entering a **descendant** scope (e.g. dipping from `build` into `build.health`)
608
+ does not break the parent's unbroken run — a full agent turn in a nested child
609
+ machine, then back to the parent, still resumes the SAME parent conversation;
610
+ entering a **sibling or unrelated** scope does start a fresh one. The bundled
611
+ template's `build.review` (the human review tail) is a worked example: it is
612
+ nested INSIDE `build` (the builder's own machine) so that a
490
613
  `gtd --entry fix-precheck` run — `build.fix` -> `build.health.check` ->
491
614
  `build.review.*` -> `build.squashing` — stays inside one subtree, letting
492
615
  `build.squashing` resume the SAME session that made the fixes instead of a
@@ -507,8 +630,8 @@ scopes.
507
630
  computed `<scope>#<hash7>` key above — so the same scope-run always re-derives
508
631
  the exact same id, and there is no table, no file, no write to keep in sync.
509
632
  `session.resume` is `true` iff a prior `prompt` turn already landed within that
510
- same scope-run. Because nothing is written, calling `gtd next --json` twice in a
511
- row — a driver's own opening peek, a status poll, a curious human — derives
633
+ same scope-run. Because nothing is written, calling `gtd status --json` twice in
634
+ a row — a driver's own opening peek, a status poll, a curious human — derives
512
635
  IDENTICAL `session.id`/`session.resume` values both times, since there is
513
636
  nothing to poison: looking is free, and there is no separate claiming form to
514
637
  protect. The per-scope survival story (a child machine's own excursion doesn't
@@ -534,7 +657,7 @@ reports `gtd`'s own lifecycle to that status without any herdr-specific
534
657
  knowledge in `gtd` itself: `working` while the loop drives, `blocked` when it
535
658
  comes to rest on a human (a gate, a stall, a non-zero exit), and `idle`
536
659
  (rendered as "done" once the pane's tab is unfocused) when a run ends without
537
- anything owed. It needs nothing from `gtd` beyond `gtd status --json`'s `.actor`
660
+ anything owed. It needs nothing from `gtd` beyond `gtd next --sh`'s `gtd_actor`
538
661
  field — a strictly read-only orientation peek.
539
662
 
540
663
  Save this as `~/.local/bin/gtdh`, `chmod +x` it, and run `gtdh` in place of
@@ -544,8 +667,8 @@ Save this as `~/.local/bin/gtdh`, `chmod +x` it, and run `gtdh` in place of
544
667
  #!/usr/bin/env bash
545
668
  # Report gtd's own lifecycle to the herdr pane it runs in: working while the
546
669
  # loop drives, blocked when it rests on you, idle (shown as "done") otherwise.
547
- # Needs nothing from gtd but `gtd status --json`. Outside herdr it is a plain
548
- # passthrough. Requires `jq` — so does the README's minimal driver.
670
+ # Needs nothing from gtd but `gtd next --sh`. Outside herdr it is a plain
671
+ # passthrough.
549
672
  set -uo pipefail
550
673
 
551
674
  pane="${HERDR_PANE_ID:-}"
@@ -570,14 +693,16 @@ trap 'report blocked; exit 130' INT TERM
570
693
  env -u HERDR_PANE_ID gtd-loop
571
694
  rc=$?
572
695
 
573
- # Whose turn is it now? `gtd status --json` is a strictly read-only peek —
574
- # every gtd command is, including `gtd next --json` (its prompt session id is
575
- # derived, never minted/stored) — only the emitted scripts a driver runs
576
- # actually touch git. A human actor means gtd is waiting on you; anything else
577
- # means the run ended with nothing owed.
578
- actor="$(gtd status --json 2>/dev/null | jq -r '.actor // ""' 2>/dev/null || true)"
696
+ # Whose turn is it now? `gtd next --sh` is a strictly read-only peek — every
697
+ # gtd command is, including `gtd next` (its prompt session id is derived,
698
+ # never minted/stored) — only the emitted scripts a driver runs actually
699
+ # touch git. A human actor means gtd is waiting on you; anything else means
700
+ # the run ended with nothing owed. Assign first, THEN eval — same reason as
701
+ # the driver above.
702
+ out="$(gtd next --sh 2>/dev/null)" || out=""
703
+ eval "${out:-}" 2>/dev/null || true
579
704
 
580
- if [ "$rc" -ne 0 ] || [ -z "$actor" ] || [ "$actor" = human ]; then
705
+ if [ "$rc" -ne 0 ] || [ -z "${gtd_actor:-}" ] || [ "$gtd_actor" = human ]; then
581
706
  report blocked
582
707
  else
583
708
  report idle
@@ -616,7 +741,7 @@ to build a driver without leaving your terminal. The briefing ends by directing
616
741
  the agent to INTERVIEW you first — which agent CLI runs the turns, under what
617
742
  permission model, how you want to invoke the loop, what should happen at a human
618
743
  gate — and to build the driver your answers describe rather than copying the
619
- reference bash. It writes nothing: "install" means installing knowledge into the
744
+ reference sh. It writes nothing: "install" means installing knowledge into the
620
745
  calling agent's context, and the briefing it prints is always exactly as current
621
746
  as the `gtd` that printed it.
622
747
 
@@ -625,88 +750,109 @@ privileged one — the engine itself is a supported public surface, and anything
625
750
  below holds for any driver you write against it. gtd decides and prints; it
626
751
  never touches git itself. The four commands that change anything — `gtd land`,
627
752
  `gtd --entry <state>`, `gtd abandon`, and `gtd restore` — perform no git write
628
- when run. Instead, each one's output carries `required`/ `optional`: bash
629
- scripts for YOU to execute. Plain-text output (no `--json`) prints them combined
630
- into ONE pasteable script — `required` verbatim, then `optional` wrapped so its
631
- own failure can't turn a landed turn into a non-zero exit — so `gtd land | bash`
632
- is a complete, single-command way to land a turn. Under `--json`, each one's
633
- output object instead carries `required`/`optional` as two separate string
634
- fields (plus, for `gtd land`, the same two combined into `script`), for a driver
635
- that wants to run them apart (e.g. to skip `optional` outright). Either way,
636
- printing gtd's output and never running it is not driving anything; a driver
637
- must pipe or `jq`-and-execute what gtd prints.
638
-
639
- - **`required`** is everything that decides what lands in git — closing an open
640
- review checkout window, the resting state's own steering-mode
753
+ when run: each one prints ONE POSIX sh script for YOU to execute
754
+ (`src/Emit.ts`'s `combinedScript`) — a leading comment ("gtd emitted this and
755
+ did NOT run it — pipe it into `sh` to land the turn"), then the REQUIRED half
756
+ verbatim, then, only when there's a presentation-only follow-up, a second
757
+ comment ("presentation only — safe to skip") and the OPTIONAL half wrapped in a
758
+ subshell whose own non-zero exit is swallowed (reported to stderr as a warning,
759
+ never turning a landed turn into a failing one). Printing gtd's output and never
760
+ running it is not driving anything; a driver must pipe or execute what gtd
761
+ prints — e.g. the capture-then-pipe form the reference driver below uses — never
762
+ a bare `gtd land | sh`, which would hand an empty script to a shell on a refusal
763
+ instead of stopping first (see the reference driver's own use of `gtd land --sh`
764
+ below).
765
+
766
+ Every script gtd emits — `gtd land`, `gtd --entry <state>`, `gtd abandon`,
767
+ `gtd restore`, and the format/validate script `gtd validate` prints — is POSIX
768
+ `sh`, portable to `dash`: a driver may run any of them with any POSIX-compliant
769
+ shell, not specifically bash. The same convention extends to the workflow's own
770
+ `vars.testCommand` (what a `script`-content check state actually executes): it
771
+ is expected to be POSIX sh-compatible too, but this is a DOCUMENTED CONVENTION
772
+ only — gtd never inspects or validates `testCommand`'s shell dialect itself, it
773
+ only renders the value into a script and hands it to whatever shell the driver
774
+ invokes that script with.
775
+
776
+ - **The required half** is everything that decides what lands in git — closing
777
+ an open review checkout window, the resting state's own steering-mode
641
778
  `format:`/`validate:` commands, the commit or squash itself (`gtd land` and
642
779
  `gtd --entry <state>`), or the ref update and reset that undo a process
643
780
  (`gtd abandon`, `gtd restore`) — and, last, a printed line naming what just
644
781
  landed (`src/OutcomeScript.ts`'s `gtd_report_*` calls): a transition or
645
782
  capture's changed-file rows, or the abandon/restore prose, resolved from the
646
- repository AFTER the write above it. Run this one. Skipping it means the turn
647
- never lands, and you never see what it did.
648
- - **`optional`** is presentation only: re-opening the review checkout window
649
- (the `<<<<<<< HEAD` diff view) after `gtd land` lands at a
783
+ repository AFTER the write above it. Its own exit code IS the printed script's
784
+ exit code — skipping it means the turn never lands, and you never see what it
785
+ did.
786
+ - **The optional half** is presentation only: re-opening the review checkout
787
+ window (the `<<<<<<< HEAD` diff view) after `gtd land` lands at a
650
788
  `reviewWindow: true` state, so an editor's diff view has something to show.
651
- Skip it and you lose nothing but that view — the workflow is still driven
652
- correctly. `gtd abandon`/`gtd restore` always emit `optional: ""` (there is no
653
- window to reopen after either).
789
+ Its own failure is swallowed by the wrapping subshell (a warning on stderr,
790
+ nothing more) — skip it (or let it fail) and you lose nothing but that view,
791
+ the workflow is still driven correctly. `gtd abandon`/`gtd restore` never
792
+ carry one — there is no window to reopen after either, so their printed
793
+ artifact is the required half alone, with no trailing comment/subshell at all.
654
794
 
655
- `gtd next --json` carries one more field worth a custom driver's attention:
795
+ `gtd status --json` carries one more field worth a custom driver's attention:
656
796
 
657
797
  - **`log`** — the per-worktree loop log path, always present. It's derived from
658
798
  the worktree's own git dir, so two worktrees looping concurrently never share
659
- one file; set `$GTD_LOOP_LOG` to override it verbatim. gtd itself neither
660
- creates nor truncates this file — a driver appends subprocess output to it and
661
- truncates once at the start of a run, exactly like the driver above does.
662
-
663
- `optional` may be the empty string `""`, meaning "nothing to do here".
664
- `required` is never empty — even a genuine no-op `gtd land` (a clean tree
665
- matching no `on` pattern) emits a PRINT-ONLY script: no git write, just the same
666
- `nothing to do at "<state>"` line the script prints when a driver runs it (or,
667
- under plain text with no `--json`, when gtd prints the very same script back to
668
- you). Both scripts are self-contained (each carries its own precondition assert
669
- and retry helper) and safe to run standalone, in sequence, or not at all — paste
670
- either into a terminal and it does exactly what it says, printed output
671
- included: the scripts detect their own tty/`NO_COLOR` at RUN time (not at the
672
- moment gtd generated them), so the fancy/plain rendering always matches wherever
673
- you actually run them.
799
+ one file; set `$GTD_LOOP_LOG` to override it verbatim, or `$GIT_DIR` to name
800
+ the git dir directly (both honored the same way every git subprocess already
801
+ honors them — an inherited `$GIT_DIR` moves the log path along with it). gtd
802
+ itself neither creates nor truncates this file — a driver appends subprocess
803
+ output to it and truncates once at the start of a run, exactly like the driver
804
+ above does.
805
+
806
+ Even a genuine no-op `gtd land` (a clean tree matching no `on` pattern) prints a
807
+ PRINT-ONLY script: no git write, just the same `nothing to do at "<state>"` line
808
+ the script prints when a driver runs it. Every encoding (plain, `--json`,
809
+ `--sh`) carries that same script verbatim in its `script` field — running it is
810
+ never optional, whichever encoding a driver reads it from. The script is
811
+ self-contained (it carries its own precondition assert and retry helper) and
812
+ safe to run standalone, in sequence, or not at all — paste it into a terminal
813
+ and it does exactly what it says, printed output included: it detects its own
814
+ tty/`NO_COLOR` at RUN time (not at the moment gtd generated it), so the
815
+ fancy/plain rendering always matches wherever you actually run it.
674
816
 
675
817
  A driver has no opening move. It does not need to know whose turn it is before
676
- it starts — `gtd next --json` tells it, and a human's pending edit arrives as an
677
- ordinary `kind: "capture"` beat that the driver lands like any other. The rule
678
- this replaces: never `gtd land` outside a beat you dispatched. A stray land at a
679
- clean `prompt` rest authors an empty attempt on purpose (that IS the stall
680
- bookkeeping), so an unconditional opening land would manufacture a stall out of
681
- a fresh start.
682
-
683
- `gtd land`'s exit code IS the settled signal: 0 for an ordinary landing
684
- (capture, turn, attempt, squash, or a benign no-op at a clean `message` rest), 3
685
- for SETTLED, 1 for a refusal or usage error. SETTLED means nothing owed, for
686
- either of two terminal shapes — a no-op at a `script` rest (the check ran, left
687
- nothing any pattern claims, and re-running the same beat cannot change that), or
688
- a step whose decision rewinds the process back to the workflow's initial state
689
- retaining nothing (`gtd --entry fix-precheck` against a green suite is the
690
- shipped example — the probe collapses away rather than landing a round trip
691
- through the log). Either way a loop should exit rather than spin — with
692
- `set -o pipefail`, `gtd land | bash` propagates gtd's own exit code through the
693
- pipe, and `--json` carries the same fact as `settled: true`. A no-op at a
694
- `prompt` rest is NOT settled — an agent that was asked to act and produced
695
- nothing is a stall, a driver's own concern, not this signal's. Declaring a `C`
696
- edge on a `script` state is the workflow-side way to make the state advance
697
- instead of settling.
698
-
699
- ### Drivers other than bash
700
-
701
- The protocol above is JSON in, subprocesses out — bash is one convenient shape,
702
- not the only one. A **program** parses `gtd next --json`, switches on `kind`,
703
- and runs `content`/`required`/`optional` as subprocesses — no bash anywhere in
704
- it. A **human** runs plain `gtd next` (no `--json`), does what it says, and
705
- pastes `gtd land | bash`: plain-text output exists for exactly this, printing
706
- `required` and `optional` already combined into one pasteable script. An
707
- **agent** gets `gtd install` as its instructions — the same protocol, in a form
708
- built to be pasted into a context window rather than read. A **CI job** is the
709
- program case with the `prompt` arm pointed at a headless agent CLI, and
818
+ it starts — `gtd next --json`/`--sh` tells it, and a human's pending edit
819
+ arrives as an ordinary `kind: "capture"` beat that the driver lands like any
820
+ other. The rule this replaces: never `gtd land` outside a beat you dispatched. A
821
+ stray land at a clean `prompt` rest authors an empty attempt on purpose (that IS
822
+ the stall bookkeeping), so an unconditional opening land would manufacture a
823
+ stall out of a fresh start. The one EXCEPTION is the run's very first beat: land
824
+ it before trusting `idle` there too, so a workflow whose initial state declares
825
+ its own clean-tree `"C"` pattern still gets a chance to advance on a human's
826
+ bare re-invocation, rather than the driver concluding "nothing owed" without
827
+ ever giving that edge a turn (see the reference driver's own comment on this).
828
+
829
+ Exit code carries none of this any more — every command, `next`/`land` included,
830
+ exits `0` on success (see [Exit codes](#exit-codes) above). Whose turn is next,
831
+ and whether a landing settles, are both PLAIN FIELDS on `gtd next --json`/`--sh`
832
+ and `gtd land --json`/`--sh`: `next`'s own `kind`
833
+ (`capture`/`message`/`script`/`prompt`/`stalled`) and `idle`; `land`'s own
834
+ `settled` and `idle`. `gtd land --sh` carries `settled`/`idle` alongside the
835
+ script itself, so ONE invocation both runs the landing and tells the driver
836
+ whether to stop — no second read needed to decide. A no-op at a `script` rest
837
+ settles right where it rests (stop immediately, nothing more to read); an
838
+ ordinary or squash landing that finishes the whole process instead resolves
839
+ `idle` on the FOLLOWING `gtd next` — the reference driver below reads once more
840
+ only to DISPLAY that gate's message, never to decide whether to stop. Declaring
841
+ a `C` edge on a `script` state is the workflow-side way to make the state
842
+ advance instead of settling.
843
+
844
+ ### Drivers other than sh
845
+
846
+ The protocol above is JSON in (from `gtd status --json` alone), subprocesses out
847
+ — sh is one convenient shape, not the only one. A **program** parses
848
+ `gtd status --json`, switches on `kind`, and runs `gtd next`'s (plain-text)
849
+ output and `gtd land`'s (POSIX sh) output as subprocesses — no sh anywhere in
850
+ it. A **human** runs plain `gtd next` (there is no JSON form of it at all), does
851
+ what it says, and pastes `gtd land | sh`: plain-text output exists for exactly
852
+ this, printing the combined script ready to paste. An **agent** gets
853
+ `gtd install` as its instructions — the same protocol, in a form built to be
854
+ pasted into a context window rather than read. A **CI job** is the program case
855
+ with the `prompt` arm pointed at a headless agent CLI, and
710
856
  `kind: "message"`/`kind: "stalled"` mapped onto "stop and report".
711
857
 
712
858
  ### A complete minimal driver
@@ -721,102 +867,149 @@ This exact fenced block is extracted and executed by
721
867
  `tests/integration/features/readme-driver.feature` — renaming the heading above
722
868
  or splitting the block into more than one fence breaks the build.
723
869
 
870
+ **Pipe the prompt to the agent CLI on stdin — never pass it as a command-line
871
+ argument.** A `prompt` beat's content embeds a full diff, and argv is capped:
872
+ roughly 1 MB on macOS, and POSIX guarantees only 4 KB (`ARG_MAX`). Both are
873
+ reachable by an ordinary diff, so a driver that passes the prompt as an argument
874
+ works in testing and then fails on the first large change, in a way that looks
875
+ like an agent error rather than a driver bug. The driver below pipes `$c`/`$out`
876
+ into `claude -p` over stdin for exactly this reason.
877
+
724
878
  ```bash
725
- #!/usr/bin/env bash
726
- set -euo pipefail
727
-
728
- # The bundle plans; this executes. Emitted scripts print their own outcomes.
729
- # Exit 3 means SETTLED — nothing owed — so this exits the whole driver at 0.
730
- gtd_land() {
731
- local json code=0
732
- json="$(gtd land --json)" || code=$?
733
- [ "$code" = 0 ] || [ "$code" = 3 ] || return "$code"
734
- bash -c "$(jq -r '.required // empty' <<<"$json")" || return $?
735
- bash -c "$(jq -r '.optional // empty' <<<"$json")" ||
736
- echo "warn: presentation follow-up failed — continuing" >&2
737
- [ "$code" = 3 ] && exit 0
738
- return 0
739
- }
879
+ #!/usr/bin/env sh
880
+ set -eu
740
881
 
741
882
  beat=1
742
883
  while :; do
743
- next="$(gtd next --json)" || exit 1
744
- kind="$(jq -r .kind <<<"$next")"
745
- log="$(jq -r .log <<<"$next")"
746
- case "$kind" in
747
- stalled) jq -r .content <<<"$next" >&2; exit 1 ;;
884
+ # One invocation per beat: assign first, THEN eval — command substitution
885
+ # inside eval's own argument would swallow a failed `gtd next` under
886
+ # `set -e` (eval would see only the empty string and abort on some later
887
+ # unset variable with a confusing message). Assigning to `out` first makes
888
+ # this a simple command whose own exit status IS the substitution's, so
889
+ # `set -e` aborts correctly on a genuine failure.
890
+ out="$(gtd next --sh)"
891
+ eval "$out"
892
+
893
+ # `gtd_idle` (true iff the initial state, clean tree) is the one shape
894
+ # that means the process is genuinely done — EXCEPT on the run's opening
895
+ # beat: land it anyway, so a workflow whose initial state declares its own
896
+ # clean-tree "C" pattern still gets a chance to fire.
897
+ if [ "$beat" -gt 1 ] && [ "${gtd_idle:-}" = true ]; then
898
+ gtd next
899
+ exit 0
900
+ fi
901
+
902
+ case "$gtd_kind" in
903
+ stalled) printf '%s\n' "$gtd_content" >&2; exit 1 ;;
748
904
  # you re-ran us resting here: you either edited something or accepted by
749
905
  # editing nothing, so land the opening beat either way. Later beats are
750
906
  # gates we just produced and you have not read yet — hand off.
751
- message) [ "$beat" = 1 ] || { jq -r .content <<<"$next"; exit 0; } ;;
907
+ message) [ "$beat" = 1 ] || { gtd next; exit 0; } ;;
752
908
  capture) ;; # the human already acted — just land it
753
- script) bash -c "$(jq -r .content <<<"$next")" >>"$log" 2>&1 || true ;;
909
+ script)
910
+ sh -c "$gtd_content" >>"$gtd_log" 2>&1 || true ;;
754
911
  prompt)
755
- sid="$(jq -r '.session.id // empty' <<<"$next")"
756
- c="$(jq -r .content <<<"$next")" model="$(jq -r '.model // empty' <<<"$next")"
757
- agent_turn() { claude -p "$c" "$1" "$sid" ${model:+--model "$model"} \
758
- --dangerously-skip-permissions >>"$log" 2>&1; }
759
- if [ "$(jq -r '.session.resume // false' <<<"$next")" = true ]
912
+ # $gtd_content embeds a full diff, so it goes to the agent over
913
+ # stdin, never as an argv positional — argv is capped (~1 MB on
914
+ # macOS, and POSIX guarantees only 4 KB, ARG_MAX), and a diff crosses
915
+ # that far sooner than you'd expect.
916
+ agent_turn() { printf '%s' "$gtd_content" | claude -p "$1" "$gtd_session_id" \
917
+ ${gtd_model:+--model "$gtd_model"} --dangerously-skip-permissions \
918
+ >>"$gtd_log" 2>&1; }
919
+ if [ "${gtd_session_resume:-}" = true ]
760
920
  then agent_turn --resume || agent_turn --session-id
761
921
  else agent_turn --session-id || agent_turn --resume
762
922
  fi
763
- v="$(jq -r '.validate // empty' <<<"$next")" n=0
764
- while [ -n "$v" ] && ! out="$(bash -c "$v" 2>&1)"; do
765
- n=$((n + 1)) && [ "$n" -gt 3 ] && { printf '%s\n' "$out" >&2; exit 1; }
766
- claude -p "$out" --resume "$sid" --dangerously-skip-permissions \
767
- >>"$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
928
+ printf '%s' "$fix" | claude -p --resume "$gtd_session_id" \
929
+ --dangerously-skip-permissions >>"$gtd_log" 2>&1
768
930
  done ;;
769
931
  esac
770
- gtd_land || exit 1
932
+
933
+ # `gtd land --sh` carries `settled`/`idle` alongside the script itself —
934
+ # one invocation tells us both what to run and whether to stop, with no
935
+ # second read needed to decide either.
936
+ out="$(gtd land --sh)"
937
+ eval "$out"
938
+ printf '%s\n' "$gtd_script" | sh
939
+ if [ "${gtd_settled:-}" = true ]; then
940
+ [ "${gtd_idle:-}" = true ] && gtd next
941
+ exit 0
942
+ fi
771
943
  beat=$((beat + 1))
772
944
  done
773
945
  ```
774
946
 
775
- Line by line it is the protocol described above: no opening move at all — a
776
- human's pending edit arrives as the `capture` beat, and a stray `gtd land`
777
- outside a beat you acted on would author an empty attempt at a clean prompt rest
778
- on purpose; one `gtd next --json` beat document read per loop (`kind: "stalled"`
779
- guarding against a spinning agent); `message` halts unless it is the opening
780
- beat, which the human's own re-invocation authored and which therefore lands
781
- like any other decision (see [Driving the loop](#driving-the-loop) above — this
782
- is the one place the driver, not gtd, decides, because "has the human read this
783
- gate" is run-scoped knowledge gtd deliberately does not keep); `capture` lands a
784
- human's already-made edit outright, `script` runs in the driver, `prompt` goes
785
- to the agent with the document's own `session.id`/`session.resume` mapped onto
786
- the agent's session flags — trying `resume`'s hinted flag first and falling back
787
- to the other on failure, since `session.id` is derived, not remembered (see
788
- [Driving the loop](#driving-the-loop) above) — and its embedded `.validate`
789
- script's output re-prompted verbatim on failure (the driver owns only the retry
790
- cap); and every landed turn executed — and reported — by the emitted scripts
791
- themselves, with `gtd land`'s exit 3 (SETTLED) ending a run that has nothing
792
- left to do.
947
+ Line by line it is the protocol described above: `out="$(gtd next --sh)"` then
948
+ `eval "$out"` is one invocation per beat — assigning first is what lets `set -e`
949
+ abort correctly on a genuine failure, where `eval "$(gtd next --sh)"` would
950
+ instead eval the empty string and die later on some unrelated unset variable;
951
+ the driver carries no `unset` of its own, since the `--sh` document's own
952
+ preamble makes `eval` self-contained. The very first beat lands even when
953
+ `gtd_idle` is already true, so a workflow whose initial state declares its own
954
+ clean-tree `"C"` pattern still gets a chance to fire before the driver calls it
955
+ done. `kind: "stalled"` prints `$gtd_content` (the diagnosis itself) to stderr
956
+ and stops — no re-invocation needed, since the beat already fetched it;
957
+ `message` halts unless it is the opening beat, which the human's own
958
+ re-invocation authored and which therefore lands like any other decision (see
959
+ [Driving the loop](#driving-the-loop) above — this is the one place the driver,
960
+ not gtd, decides, because "has the human read this gate" is run-scoped knowledge
961
+ gtd deliberately does not keep); `capture` lands a human's already-made edit
962
+ outright, no display needed; `script` runs `$gtd_content` in the driver;
963
+ `prompt` sends it to the agent over stdin with `$gtd_session_id`/`$gtd_model`
964
+ mapped onto the agent's session flags — trying `resume`'s hinted flag first and
965
+ falling back to the other on failure, since the session id is derived, not
966
+ remembered (see [Driving the loop](#driving-the-loop) above) — and its own
967
+ `$gtd_validate` script's output re-prompted verbatim on failure (the driver owns
968
+ only the retry cap). Every optional variable (`$gtd_model`, `$gtd_validate`,
969
+ `$gtd_session_resume`, `$gtd_idle`, `$gtd_settled`) is read as `${var:-}` or
970
+ `${var:+...}` — under `set -u`, the `--sh` document's own `unset` preamble makes
971
+ an absent field genuinely unset, not empty, so a bare `$gtd_model` would abort
972
+ the driver. Every landed turn is executed — and reported — by the emitted
973
+ scripts themselves, via `gtd land --sh`'s own `$gtd_script` field, captured then
974
+ piped to `sh`. `$gtd_settled` ends a run that has nothing left to do: a no-op at
975
+ a `script` rest settles right where it rests (stop immediately, nothing more to
976
+ read), while an ordinary or squash landing that finishes the whole process
977
+ instead resolves `$gtd_idle` on the FOLLOWING beat's own `gtd next --sh` read —
978
+ the reference driver reads once more (a plain `gtd next`) only to DISPLAY that
979
+ gate's own message, the decision to stop already made from `$gtd_settled`/
980
+ `$gtd_idle` alone.
793
981
 
794
982
  ### The self-validation gate
795
983
 
796
984
  After an agent turn at a state that declares `file:`+`mode:`, run the script:
797
- either `gtd next --json`'s own embedded `.validate` field (present at every
985
+ either `gtd status --json`'s own embedded `.validate` field (present at every
798
986
  `prompt` beat that hands over a validatable file), or, standalone,
799
- `gtd validate --json`'s `.script` — both resolve the exact same script from one
800
- shared resolver. Exit 0 means the file is well-formed — proceed to `gtd land`. A
801
- non-zero exit means the script's own captured output IS a complete,
802
- ready-to-send fix prompt (an instruction plus the findings, see `src/Emit.ts`'s
803
- `failurePromptWrapper`): send it back to the same agent session verbatim, and
804
- cap how many fix attempts you allow yourself — the driver owns that retry count,
805
- not gtd. Landing never TRUSTS that you validated, either: the emitted `land`
806
- script carries the same format/validate commands ahead of its own commit and
807
- fails without committing when they fail, so a malformed file is never captured
808
- whether or not you ran the validate script first.
987
+ `gtd validate`'s own plain-text output — both resolve the exact same script from
988
+ one shared resolver. Exit 0 means the file is well-formed — proceed to
989
+ `gtd land`. A non-zero exit means the script's own captured output IS a
990
+ complete, ready-to-send fix prompt (an instruction plus the findings, see
991
+ `src/Emit.ts`'s `failurePromptWrapper`): send it back to the same agent session
992
+ verbatim, and cap how many fix attempts you allow yourself — the driver owns
993
+ that retry count, not gtd. Landing never TRUSTS that you validated, either: the
994
+ emitted `land` script carries the same format/validate commands ahead of its own
995
+ commit and fails without committing when they fail, so a malformed file is never
996
+ captured whether or not you ran the validate script first.
809
997
 
810
998
  ### Failure taxonomy and recovery
811
999
 
812
- Two different things can go wrong, and they mean different things — and one
813
- non-zero-looking exit is not a failure at all:
1000
+ Several different things can go wrong, and they mean different things — and most
1001
+ non-zero-looking exits are not a failure at all:
814
1002
 
815
1003
  - **`gtd` itself exits 1.** Nothing was attempted — this is a refusal (a guard
816
- rejected the turn) or a usage error. No script was ever produced.
817
- - **`gtd land` exits 3 (SETTLED).** This is NOT a failure: nothing is owed, and
818
- stdout still carries a script (a print-only note, or a genuine retain+rewind)
819
- that a driver must still run.
1004
+ rejected the turn). No script was ever produced.
1005
+ - **`gtd` itself exits 2.** A usage error — nothing was even attempted, the
1006
+ invocation itself was wrong (unknown option/command, bad arity, a scope
1007
+ violation).
1008
+ - **`gtd land` succeeds (exit 0) but doesn't settle.** This is NOT a failure:
1009
+ whose turn is next lives in the FOLLOWING `gtd next`'s own `kind` field, not
1010
+ in `gtd land`'s exit code (see [Exit codes](#exit-codes)) — and `gtd land`'s
1011
+ own stdout still carries a script (a print-only note, or a genuine
1012
+ retain+rewind, or an ordinary commit) that a driver must still run.
820
1013
  - **An emitted script exits non-zero when YOU run it.** Something may have
821
1014
  partially happened — e.g. a `gtd_retry`-wrapped git write landed but a later
822
1015
  step in the same script failed.
@@ -840,8 +1033,10 @@ its own retry/resume logic beyond "if the script failed, ask gtd again."
840
1033
 
841
1034
  ### Prerequisites
842
1035
 
843
- - **`jq`** — to pull the `required`/`optional` strings back out of gtd's
844
- `--json` output.
1036
+ - **A POSIX `sh`** (dash, ash, bash's own POSIX mode, etc.) — gtd's own emitted
1037
+ scripts (`gtd land`, `gtd --entry <state>`, `gtd abandon`, `gtd restore`) are
1038
+ POSIX sh; captured, then piped into it (see
1039
+ [Writing your own driver](#writing-your-own-driver) above).
845
1040
  - **`gtd` on `PATH`** — a mode's seeded `validate:` command (the one the
846
1041
  compiler fills in for the built-in `qa`/`review` formats) is literally the
847
1042
  string `gtd check <mode> '<file>'`, invoked by NAME from inside an emitted
@@ -859,7 +1054,7 @@ its own retry/resume logic beyond "if the script failed, ask gtd again."
859
1054
 
860
1055
  A mode's `format:` command may reformat a steering file — whitespace, wrapping,
861
1056
  reordering — but must NEVER change what a land-capture guard would decide. gtd's
862
- guards (the review sign-off check, the feedback-progress check, the
1057
+ guards (the review-doc check, the feedback-progress check, the
863
1058
  answer-completeness check, the require-revert check — `src/StepGuards.ts`)
864
1059
  decide ONCE, against whichever bytes are on disk at the moment `gtd land` runs,
865
1060
  which may be before OR after an emitted script's own `format:` line has run (the
@@ -867,18 +1062,20 @@ script runs `format:` then `validate:` then the commit — see
867
1062
  `src/SteeringMode.ts`'s `renderSteeringCommands` — but gtd's decision and the
868
1063
  driver's script execution are different processes at different times, so there's
869
1064
  no guaranteed ordering between "gtd decided" and "the script formatted"). That's
870
- only safe because every built-in guard judges content, not incidental formatting
871
- (e.g. it normalizes `[ ]`/`[x]` checkboxes before comparing). If you plug in
872
- your own `format:` command, the same rule binds it: a formatter that also
873
- changes meaning — ticking a box, stripping a paragraph — makes the guard's
874
- decision and the file's actual content disagree, and gtd will not catch that for
875
- you.
1065
+ only safe because every built-in guard judges only the content it explicitly
1066
+ cares about, not incidental formatting around it — the feedback-progress guard,
1067
+ for instance, only checks whether a deleted file's trimmed first line is the
1068
+ `NOTHING ACTIONABLE` sentinel, so reindenting the rest of it changes nothing the
1069
+ guard reads. If you plug in your own `format:` command, the same rule binds it:
1070
+ a formatter that also changes meaning — stripping a paragraph a guard reads —
1071
+ makes the guard's decision and the file's actual content disagree, and gtd will
1072
+ not catch that for you.
876
1073
 
877
1074
  One case never runs your `format:` (or `validate:`) at all: a step whose diff
878
1075
  DELETES the state's own `file:`. Deleting it is a legitimate outcome — a review
879
1076
  sign-off's whole diff is the review doc's deletion — and there is nothing left
880
1077
  to format. Emitting the command anyway would make such a step unlandable, since
881
- `format:` is the first line of a `set -euo pipefail` script and a formatter like
1078
+ `format:` is the first line of a `set -eu` script and a formatter like
882
1079
  `prettier --write` exits non-zero on a path that is not there.
883
1080
 
884
1081
  ### Built-in steering formats are ordinary modes
@@ -984,7 +1181,7 @@ workflow:
984
1181
  retry:
985
1182
  max: <number>
986
1183
  otherwise: <targetState>
987
- label: <string> # optional, opaque display name passed through `gtd next --json`/`gtd status --json`
1184
+ label: <string> # optional, opaque display name passed through `gtd status --json`
988
1185
  file: <string> # optional, an Eta template naming the state's steering file
989
1186
  mode: <modeName> # optional, requires "file" — must be declared in `modes:` (qa/review are seeded for you; everything else, including prose, you declare)
990
1187
  reviewWindow: true # optional — open the review checkout window at rest here