@pmelab/gtd 8.5.0 → 9.0.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 +468 -150
- package/dist/gtd.bundle.mjs +1989 -515
- package/package.json +2 -3
- package/bin/gtd +0 -715
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ No chat scrollback. No lost sessions. No infinite fix loops. Just git.
|
|
|
17
17
|
|
|
18
18
|
- **Durable & replayable.** The workflow state _is_ your git history — a pure
|
|
19
19
|
fold over commit subjects and the working tree. Kill the session, reboot, come
|
|
20
|
-
back next week: run
|
|
20
|
+
back next week: run the driver and it resumes exactly where it stopped.
|
|
21
21
|
- **Shareable.** Push the branch, and the workflow travels with it — the state
|
|
22
22
|
lives in the commits, so another machine (or another person) picks up exactly
|
|
23
23
|
where you left off.
|
|
@@ -43,10 +43,10 @@ No chat scrollback. No lost sessions. No infinite fix loops. Just git.
|
|
|
43
43
|
npm install -g @pmelab/gtd
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
Or run without installing:
|
|
46
|
+
Or run without installing (prefix every `gtd` below with `npx`):
|
|
47
47
|
|
|
48
48
|
```bash
|
|
49
|
-
npx @pmelab/gtd
|
|
49
|
+
npx @pmelab/gtd next
|
|
50
50
|
```
|
|
51
51
|
|
|
52
52
|
That's it — gtd ships the **unified** workflow as its **built-in default**, so a
|
|
@@ -66,7 +66,7 @@ format only, so gtd still validates the `qa`/`review` ones; `prose`, used by the
|
|
|
66
66
|
simple flow's plan file, has no validator to begin with); edit or drop either
|
|
67
67
|
freely (point `testCommand` at your suite, swap Prettier for dprint or a script,
|
|
68
68
|
delete a key). It writes **no** `workflow:` key — the machine is built in — so
|
|
69
|
-
review and commit the file before your first `gtd
|
|
69
|
+
review and commit the file before your first `gtd land`. `gtd init` takes no
|
|
70
70
|
argument and refuses to clobber an existing config; it may also run in a plain
|
|
71
71
|
parent directory (not a git repo) to seed a shared config a nested repo picks
|
|
72
72
|
up. To customize the machine itself, add a `workflow:` key (there is no default
|
|
@@ -79,10 +79,26 @@ carrying one piece of content (a script, a prompt, a message, or a squash commit
|
|
|
79
79
|
template), with an ordered set of change-patterns routing to the next state.
|
|
80
80
|
|
|
81
81
|
The loop is one beat, repeated: run `gtd next --json` and dispatch on `kind` —
|
|
82
|
-
`"message"` means it's a human's move (stop and hand off); `"
|
|
83
|
-
|
|
84
|
-
`content`
|
|
85
|
-
|
|
82
|
+
`"message"` means it's a human's move (stop and hand off); `"capture"` means a
|
|
83
|
+
human gate the human already acted on (land it immediately); `"script"` means
|
|
84
|
+
the driver runs `content` itself, then lands it; `"prompt"` means feed `content`
|
|
85
|
+
to your agent — using the accompanying `session.id`/`session.resume` to continue
|
|
86
|
+
or start that agent conversation (see "Driving the loop" below) — then run
|
|
87
|
+
`gtd land` once it's done. gtd itself never executes anything — the driver owns
|
|
88
|
+
running scripts. Every `gtd next` call is strictly mutation-free, safe to poll
|
|
89
|
+
or peek at any time: `session.id`/`session.resume` are DERIVED, never stored, so
|
|
90
|
+
looking is free — nothing distinguishes a peek from a dispatch, and there is no
|
|
91
|
+
separate claiming form at all. A `prompt` beat whose turn changes nothing still
|
|
92
|
+
lands: `gtd land` commits an EMPTY `gtd(<actor>): <state>` attempt instead of
|
|
93
|
+
silently doing nothing, so the fruitless dispatch is visible in history rather
|
|
94
|
+
than invisible. `"kind": "stalled"` is derived from that history — HEAD is an
|
|
95
|
+
empty attempt at the resting state and the tree is clean — so every
|
|
96
|
+
`gtd next --json` call reports it, and it stays reported on a repeat (there's no
|
|
97
|
+
marker to consume) until something actually changes. The fix for a repeated
|
|
98
|
+
stall is either a better prompt, a `retry:` cap on the state that redirects to
|
|
99
|
+
an escalation state after N fruitless attempts, or — if the state can
|
|
100
|
+
legitimately finish with nothing to change — declaring a `C` (clean-tree)
|
|
101
|
+
pattern on it.
|
|
86
102
|
|
|
87
103
|
An `on` edge may also carry a short imperative `action` (e.g. `Accept plan`)
|
|
88
104
|
alongside its existing `describe` sentence — a human-facing name for the choice
|
|
@@ -120,27 +136,27 @@ for). The two steering-file entries are chosen by which file you create:
|
|
|
120
136
|
Both flows converge on the same tail: an agent hands you a `.gtd/REVIEW.md`
|
|
121
137
|
checkbox review of the diff — the prompt never inlines the diff itself; it names
|
|
122
138
|
the commit the changes are based at and the agent runs `git diff` to read the
|
|
123
|
-
range before writing the review. While the process rests at that gate,
|
|
124
|
-
a **review checkout window**: HEAD is rewound to the review
|
|
125
|
-
working tree untouched, so the whole reviewable change shows up as
|
|
126
|
-
uncommitted changes in your editor's normal git integration (and files
|
|
127
|
-
during the process show up as ordinary untracked files, so discarding one
|
|
128
|
-
deletes it). The
|
|
129
|
-
review each hunk (ticking just records "I read this"), and
|
|
130
|
-
to request changes: a note on a line, an inline
|
|
131
|
-
code, or a direct code edit. Any comment sends a
|
|
132
|
-
agent first turns your comments into an explicit
|
|
133
|
-
turn implements it (a re-review then covers only
|
|
134
|
-
hand-edit is treated as your own fix the agent
|
|
135
|
-
lines; a comment can't be silently dropped — a
|
|
136
|
-
is refused). For the simple flow, the build
|
|
137
|
-
feedback and the turn that drafts the final squash
|
|
138
|
-
session that built the feature in the first place,
|
|
139
|
-
nested inside that build identity rather than sitting
|
|
140
|
-
box with no comment is the sign-off, which collapses
|
|
141
|
-
commit (a **squash finale** whose message an agent
|
|
142
|
-
still unticked and no comment is refused (finish
|
|
143
|
-
deleting `.gtd/REVIEW.md`.
|
|
139
|
+
range before writing the review. While the process rests at that gate, the
|
|
140
|
+
landing script opens a **review checkout window**: HEAD is rewound to the review
|
|
141
|
+
base with the working tree untouched, so the whole reviewable change shows up as
|
|
142
|
+
ordinary uncommitted changes in your editor's normal git integration (and files
|
|
143
|
+
added during the process show up as ordinary untracked files, so discarding one
|
|
144
|
+
deletes it). The next landing's own script closes the window before it commits.
|
|
145
|
+
Tick a box as you review each hunk (ticking just records "I read this"), and
|
|
146
|
+
leave a **comment** to request changes: a note on a line, an inline
|
|
147
|
+
`// TODO`-style comment in the code, or a direct code edit. Any comment sends a
|
|
148
|
+
build + re-review round — an agent first turns your comments into an explicit
|
|
149
|
+
instruction list, then a build turn implements it (a re-review then covers only
|
|
150
|
+
the follow-through, and a hand-edit is treated as your own fix the agent
|
|
151
|
+
completes without reverting your lines; a comment can't be silently dropped — a
|
|
152
|
+
build turn that addresses nothing is refused). For the simple flow, the build
|
|
153
|
+
turn that follows through on feedback and the turn that drafts the final squash
|
|
154
|
+
message both resume the same session that built the feature in the first place,
|
|
155
|
+
since the review tail is nested inside that build identity rather than sitting
|
|
156
|
+
beside it. Ticking every box with no comment is the sign-off, which collapses
|
|
157
|
+
the whole process into one commit (a **squash finale** whose message an agent
|
|
158
|
+
drafts). Landing with a box still unticked and no comment is refused (finish
|
|
159
|
+
reviewing first), as is deleting `.gtd/REVIEW.md`.
|
|
144
160
|
|
|
145
161
|
The same review tail also has a direct entry point —
|
|
146
162
|
`gtd --entry review-gate.check --var reviewBase=<commitish>` starts a brand new
|
|
@@ -157,9 +173,9 @@ no commit is left behind.
|
|
|
157
173
|
|
|
158
174
|
`--entry` itself isn't limited to states flagged `entry: true` — it accepts
|
|
159
175
|
**any** declared, non-commit state of the active workflow (see
|
|
160
|
-
[`gtd
|
|
161
|
-
|
|
162
|
-
|
|
176
|
+
[`gtd --entry`](#commands) below). `entry: true` only marks a state as an
|
|
177
|
+
_extra_ reachability root (and drives a badge in `gtd visualize`) for a state
|
|
178
|
+
that would otherwise be unreachable from the ordinary `idle` rest —
|
|
163
179
|
`review-gate.check` and `fix-precheck` need it for exactly that reason, while
|
|
164
180
|
`plan-gate.check`/`spec-gate.check` carry it too (the bundled template dedups
|
|
165
181
|
the three `entryGate` instances into one shared machine, so flagging the shared
|
|
@@ -186,12 +202,6 @@ the workflow is just `.gtdrc` config.
|
|
|
186
202
|
Usage: gtd [command] [options]
|
|
187
203
|
|
|
188
204
|
Commands:
|
|
189
|
-
(no command), loop
|
|
190
|
-
Launch the loop driver (bin/gtd), which repeatedly drives
|
|
191
|
-
an agent through gtd next/gtd step calls until the
|
|
192
|
-
workflow rests at a human gate (a non-autonomous state)
|
|
193
|
-
or settles. A bare gtd invocation and gtd loop both
|
|
194
|
-
launch it identically
|
|
195
205
|
init Scaffold a minimal .gtdrc.json for this repo, seeding the
|
|
196
206
|
default variables you are most likely to change (the test
|
|
197
207
|
command) and a Prettier formatting suggestion. gtd runs its
|
|
@@ -200,19 +210,21 @@ Commands:
|
|
|
200
210
|
Takes no argument. Run once per repo; refuses if a gtd
|
|
201
211
|
config already exists. Leaves the file uncommitted for you
|
|
202
212
|
to review and commit
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
(
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
+
land Land whatever the tree now shows at the currently
|
|
214
|
+
resolved rest — a human capture, an agent/check turn, an
|
|
215
|
+
empty attempt (a fruitless prompt turn), or a squash — and
|
|
216
|
+
print the script that records it; a driver runs the
|
|
217
|
+
script, e.g. `gtd land | bash`. Pass --cost=<n>
|
|
218
|
+
(optionally --model=<name>) to record the just-finished
|
|
219
|
+
invocation's token cost and model on the turn commit
|
|
220
|
+
(summed into it.processCost/processCostByModel). Exits 0
|
|
221
|
+
when a script is emitted (or a benign no-op at a clean
|
|
222
|
+
message rest), 3 when SETTLED — nothing owed: a no-op at a
|
|
223
|
+
script rest, or the initial-state collapse (stdout still
|
|
224
|
+
carries the script) — 1 on any refusal
|
|
213
225
|
(no command) --entry <state>
|
|
214
|
-
|
|
215
|
-
|
|
226
|
+
Starts a new process authenticated as human, e.g.
|
|
227
|
+
'gtd --entry <state>'
|
|
216
228
|
abandon End the process currently underway without completing it:
|
|
217
229
|
close any open review checkout window, then rewind HEAD to
|
|
218
230
|
the commit the process started from, keeping everything it
|
|
@@ -224,16 +236,43 @@ Commands:
|
|
|
224
236
|
Refuses on a dirty working tree, when there is no retained
|
|
225
237
|
history, or when HEAD has advanced past the squash with
|
|
226
238
|
commits that would be lost
|
|
227
|
-
next Print the resolved rest's rendered script/prompt/message
|
|
239
|
+
next Print the resolved rest's rendered script/prompt/message
|
|
240
|
+
(no mutation, safe to poll). --json emits the whole beat
|
|
241
|
+
document instead: kind (capture|message|script|prompt|
|
|
242
|
+
stalled) selects what a driver does, content is what it
|
|
243
|
+
runs or shows, plus the prompt session, model, validate
|
|
244
|
+
script, log path and the resting state's own fields
|
|
228
245
|
status Print the resolved rest's state/actor and which declared
|
|
229
246
|
pattern (if any) each pending change matches (no mutation)
|
|
230
|
-
validate
|
|
231
|
-
|
|
232
|
-
|
|
247
|
+
validate Print the script that formats (when declared) then
|
|
248
|
+
validates the resolved rest's steering file, using its
|
|
249
|
+
mode's commands (its file:/mode:), instead of running it —
|
|
250
|
+
a driver runs the script and reads the findings from its
|
|
251
|
+
own exit code/output. Always exits 0; --json emits
|
|
252
|
+
{state, file?, mode?, script} (script is "" when there is
|
|
253
|
+
nothing to validate; plain text prints "nothing to
|
|
254
|
+
validate" in that case). On a non-zero validate exit
|
|
255
|
+
the emitted script prints a ready-to-send fix prompt
|
|
256
|
+
(instruction + findings) and exits with the
|
|
257
|
+
validator's own code
|
|
233
258
|
lsp Start the LSP server for .gtd/ steering files (stdio)
|
|
234
259
|
visualize Serve an interactive diagram of the active workflow on a
|
|
235
260
|
local web server (--port <n>, --no-open; --json prints the
|
|
236
261
|
model and exits)
|
|
262
|
+
check <mode> <file>
|
|
263
|
+
Read <file> and run the built-in steering format named
|
|
264
|
+
<mode> (see `gtd validate`'s modes: qa, review) over its
|
|
265
|
+
contents, printing each finding one per line and exiting
|
|
266
|
+
non-zero when there are any. Resolves no workflow state and
|
|
267
|
+
reads no config — standalone, runnable from any directory
|
|
268
|
+
with <mode>/<file> given explicitly. This is what a
|
|
269
|
+
workflow's emitted validation script invokes as a leaf step
|
|
270
|
+
install Print a complete, self-contained briefing that teaches an
|
|
271
|
+
agent (or a human) to build a gtd driver in any shell or
|
|
272
|
+
runtime — the self-serve version of README's 'Writing
|
|
273
|
+
your own driver'. Writes nothing: this installs knowledge
|
|
274
|
+
into the calling agent's context, not files on disk. Runs
|
|
275
|
+
from any directory, in or out of a repository
|
|
237
276
|
version Print version and exit
|
|
238
277
|
help Print this help and exit
|
|
239
278
|
|
|
@@ -241,13 +280,11 @@ Options:
|
|
|
241
280
|
--json Output structured JSON instead of plain text
|
|
242
281
|
--port=<n> (gtd visualize only) port to serve on (default: a free port)
|
|
243
282
|
--no-open (gtd visualize only) do not open the browser
|
|
244
|
-
--cost=<n> (gtd
|
|
245
|
-
--model=<name> (gtd
|
|
246
|
-
--entry <state> (
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
combinable with --cost/--model (an entry is not a metered
|
|
250
|
-
agent turn)
|
|
283
|
+
--cost=<n> (gtd land only) record the invocation's token cost
|
|
284
|
+
--model=<name> (gtd land only, with --cost) tag that cost's model
|
|
285
|
+
--entry <state> (with no command at all) start a brand new process at
|
|
286
|
+
<state> — any declared, non-commit state — authenticated
|
|
287
|
+
as human
|
|
251
288
|
--var <name>=<value>
|
|
252
289
|
(with --entry; repeatable) supply a fixed it.vars
|
|
253
290
|
override for the new process; the name must already be
|
|
@@ -258,44 +295,46 @@ Options:
|
|
|
258
295
|
|
|
259
296
|
`--version` (`-v`) / `gtd version` and `--help` (`-h`) / `gtd help`
|
|
260
297
|
short-circuit before any git or repository-state work — they run outside a repo
|
|
261
|
-
and in any repo state. Bare `gtd` (no subcommand) and `gtd loop` both
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
298
|
+
and in any repo state. Bare `gtd` (no subcommand) and `gtd loop` are both usage
|
|
299
|
+
errors that exit 1 and print help — gtd decides and prints, full stop; driving a
|
|
300
|
+
loop is a driver's job, not a bundled command (see
|
|
301
|
+
[Driving the loop](#driving-the-loop) below). Any other, truly unknown
|
|
302
|
+
subcommand is likewise a usage error: it prints the help text and exits 1
|
|
303
|
+
without touching the repository. The state commands (`land`, `--entry`,
|
|
304
|
+
`abandon`, `restore`, `next`, `status`, `validate`) must run from the
|
|
265
305
|
**repository root** — gtd derives the workflow, pending changes, and process
|
|
266
|
-
history relative to cwd, so
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
`--
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
`--
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
itself, stripped before anything reaches the bundle.
|
|
306
|
+
history relative to cwd, so they refuse with a clear error from a subdirectory;
|
|
307
|
+
`lsp`, `init`, `visualize`, `check`, and `install` are standalone and run from
|
|
308
|
+
anywhere (see each command's own help entry).
|
|
309
|
+
|
|
310
|
+
`--json`, `--cost=<n>`, `--model=<name>` (the latter two only for `gtd land`),
|
|
311
|
+
`--entry <state>` (no other command at all), and `--var <name>=<value>` (with
|
|
312
|
+
`--entry`, repeatable) are the only long options the compiled bundle recognizes.
|
|
313
|
+
`--entry`/`--var` accept both the `--flag=value` and the space-separated
|
|
314
|
+
`--flag value` form. Any other `--` option (including a typo like `--jsn`) is
|
|
315
|
+
rejected with a usage error rather than silently ignored, so a mistyped flag can
|
|
316
|
+
never degrade a JSON caller to plain-text mode. `--var` with no `--entry`, a
|
|
317
|
+
duplicate `--var` name, or `--cost`/`--model`/`--entry` combined with another
|
|
318
|
+
command are all usage errors too — landing and entering are different verbs, so
|
|
319
|
+
`gtd land --entry <state>` is a usage error, not a synonym. A bare
|
|
320
|
+
`--cost`/`--model` with no value, a non-numeric or negative `--cost`, an empty
|
|
321
|
+
`--model`, `--model` without `--cost`, or `--cost`/`--model` on any command
|
|
322
|
+
other than `gtd land` are all usage errors.
|
|
284
323
|
|
|
285
324
|
### `gtd status`'s `Next:`/`next`
|
|
286
325
|
|
|
287
326
|
Both plain and `--json` output include a headline preview of what would happen
|
|
288
327
|
next: the first declared `on` edge whose pattern matches the pending changes AS
|
|
289
|
-
A WHOLE (the same first-match-wins semantics `gtd
|
|
328
|
+
A WHOLE (the same first-match-wins semantics `gtd land` itself uses), using its
|
|
290
329
|
`action` when the edge declares one, else its raw pattern, alongside its target
|
|
291
330
|
state. Plain output prints a `Next: <action-or-pattern> → <target>` line (or
|
|
292
331
|
`Next: (no match — nothing would happen)`); `--json`'s `next` key mirrors it as
|
|
293
332
|
`{ action?, pattern, target }`, or `null` on no match.
|
|
294
333
|
|
|
295
334
|
This reports the **declared** route only: a capped `retry` may redirect
|
|
296
|
-
elsewhere
|
|
297
|
-
what the declared `on` patterns would match, not a guarantee of where a
|
|
298
|
-
`gtd
|
|
335
|
+
elsewhere when the land is decided, which `Next:`/`next` does not apply — it
|
|
336
|
+
previews what the declared `on` patterns would match, not a guarantee of where a
|
|
337
|
+
real `gtd land` lands.
|
|
299
338
|
|
|
300
339
|
### Error envelope
|
|
301
340
|
|
|
@@ -319,31 +358,23 @@ already authored with its own `gtd:`/`gtd <cmd>:` prefix is never doubled.
|
|
|
319
358
|
|
|
320
359
|
## Driving the loop
|
|
321
360
|
|
|
322
|
-
|
|
323
|
-
|
|
361
|
+
gtd itself is not a loop — it decides and prints, full stop; driving a loop is a
|
|
362
|
+
driver's job. The README's minimal driver below (see
|
|
363
|
+
[A complete minimal driver](#a-complete-minimal-driver)) is that driver: save it
|
|
364
|
+
as `~/.local/bin/gtd-loop`, `chmod +x` it, and run it from a repository root —
|
|
365
|
+
it takes no arguments and runs the loop until it's your turn. It drives the
|
|
324
366
|
autonomous states (agent turns, check runs) and stops at the first
|
|
325
|
-
non-autonomous one: reaching a human gate it prints the gate and
|
|
326
|
-
by editing files (answer a plan question, tick a review box, fix
|
|
327
|
-
re-
|
|
328
|
-
`
|
|
367
|
+
non-autonomous one: reaching a human gate it prints the gate's message and
|
|
368
|
+
exits. You act by editing files (answer a plan question, tick a review box, fix
|
|
369
|
+
code) and re-running it — your pending edit arrives as the loop's first beat
|
|
370
|
+
(`kind: "capture"`, landed immediately), so you never run `gtd land` by hand; a
|
|
371
|
+
mid-process restart simply resumes driving from whatever beat is actually next.
|
|
329
372
|
|
|
330
373
|
Anything richer at that boundary — opening your editor, desktop notifications,
|
|
331
|
-
terminal-multiplexer status — is the job of an outer wrapper around
|
|
332
|
-
of the loop itself; see
|
|
374
|
+
terminal-multiplexer status — is the job of an outer wrapper around the driver,
|
|
375
|
+
not of the loop itself; see
|
|
333
376
|
[Terminal-multiplexer status: a herdr wrapper](#terminal-multiplexer-status-a-herdr-wrapper)
|
|
334
|
-
below for a worked example.
|
|
335
|
-
— one human-gate capture, one check, or one agent turn — instead of driving all
|
|
336
|
-
the way to idle.
|
|
337
|
-
|
|
338
|
-
Bare `gtd` prints one line per event — colored and emoji on a real terminal,
|
|
339
|
-
plain ASCII under `NO_COLOR` or when piped — and redirects the noisier
|
|
340
|
-
agent/check/step subprocess output to a per-repo/per-worktree log file (its path
|
|
341
|
-
is the run's first output line, ready to `tail -f`). Any execution that FAILS
|
|
342
|
-
also replays its last 20 lines of output inline on stderr, on top of the log
|
|
343
|
-
still holding the complete record: an agent turn that fails stops the loop (it
|
|
344
|
-
would fail identically next lap), while a check script that exits non-zero is
|
|
345
|
-
reported as a warning and the loop carries on, because the outcome lives in the
|
|
346
|
-
tree, not in the script's exit code.
|
|
377
|
+
below for a worked example.
|
|
347
378
|
|
|
348
379
|
A workflow state can declare an optional `label:` — a human-readable display
|
|
349
380
|
name surfaced in `gtd next --json`/`gtd status`. The driver uses it for its
|
|
@@ -375,20 +406,30 @@ resumes an implementer's session, and vice versa** — a reviewer machine and th
|
|
|
375
406
|
implementer machine it reviews are always different instances with different
|
|
376
407
|
scopes.
|
|
377
408
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
session
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
the
|
|
390
|
-
|
|
391
|
-
|
|
409
|
+
`gtd` itself stores NOTHING to make this work: `session.id` is
|
|
410
|
+
`UUIDv5(<fixed gtd namespace>, <memory key>)` — a deterministic hash of the
|
|
411
|
+
computed `<scope>#<hash7>` key above — so the same scope-run always re-derives
|
|
412
|
+
the exact same id, and there is no table, no file, no write to keep in sync.
|
|
413
|
+
`session.resume` is `true` iff a prior `prompt` turn already landed within that
|
|
414
|
+
same scope-run. Because nothing is written, calling `gtd next --json` twice in a
|
|
415
|
+
row — a driver's own opening peek, a status poll, a curious human — derives
|
|
416
|
+
IDENTICAL `session.id`/`session.resume` values both times, since there is
|
|
417
|
+
nothing to poison: looking is free, and there is no separate claiming form to
|
|
418
|
+
protect. The per-scope survival story (a child machine's own excursion doesn't
|
|
419
|
+
disturb the parent's session) falls out of the key itself, not out of a
|
|
420
|
+
per-scope row: the parent's anchor commit is unaffected by whatever the child
|
|
421
|
+
scope does in between. A driver maps `session.id`/`session.resume` straight onto
|
|
422
|
+
the agent CLI's own session flags — the README's minimal driver passes them as
|
|
423
|
+
`--session-id <id>`/`--resume <id>` to `claude -p`, but treats `resume` as a
|
|
424
|
+
HINT rather than a contract: it tries the flag `resume` points at first and
|
|
425
|
+
falls back to the other on failure. That fallback is what recovers BOTH
|
|
426
|
+
mismatches that used to need manual intervention — a crashed prior turn that
|
|
427
|
+
minted an id but landed no commit (so the next lap re-derives the SAME id with
|
|
428
|
+
`resume: false`, and `--session-id` on an already-used id fails, so the driver
|
|
429
|
+
falls back to `--resume`), and the inverse (`resume: true`, but the remembered
|
|
430
|
+
session is gone — retention expired, `~/.claude/projects` wiped — so `--resume`
|
|
431
|
+
fails and the driver falls back to `--session-id`). There is no file to delete
|
|
432
|
+
and nothing to restart.
|
|
392
433
|
|
|
393
434
|
### Terminal-multiplexer status: a herdr wrapper
|
|
394
435
|
|
|
@@ -397,19 +438,18 @@ reports `gtd`'s own lifecycle to that status without any herdr-specific
|
|
|
397
438
|
knowledge in `gtd` itself: `working` while the loop drives, `blocked` when it
|
|
398
439
|
comes to rest on a human (a gate, a stall, a non-zero exit), and `idle`
|
|
399
440
|
(rendered as "done" once the pane's tab is unfocused) when a run ends without
|
|
400
|
-
anything owed. It needs nothing from `gtd` beyond `gtd
|
|
401
|
-
field
|
|
441
|
+
anything owed. It needs nothing from `gtd` beyond `gtd status --json`'s `.actor`
|
|
442
|
+
field — a strictly read-only orientation peek.
|
|
402
443
|
|
|
403
444
|
Save this as `~/.local/bin/gtdh`, `chmod +x` it, and run `gtdh` in place of
|
|
404
|
-
`gtd` — it's a plain bash file, so it works from fish or any other shell
|
|
405
|
-
forwards every argument (`gtdh --once`, `gtdh status`, ...).
|
|
445
|
+
`gtd-loop` — it's a plain bash file, so it works from fish or any other shell.
|
|
406
446
|
|
|
407
447
|
```bash
|
|
408
448
|
#!/usr/bin/env bash
|
|
409
449
|
# Report gtd's own lifecycle to the herdr pane it runs in: working while the
|
|
410
450
|
# loop drives, blocked when it rests on you, idle (shown as "done") otherwise.
|
|
411
|
-
# Needs nothing from gtd but `gtd
|
|
412
|
-
# passthrough. Requires `jq` — so does
|
|
451
|
+
# Needs nothing from gtd but `gtd status --json`. Outside herdr it is a plain
|
|
452
|
+
# passthrough. Requires `jq` — so does the README's minimal driver.
|
|
413
453
|
set -uo pipefail
|
|
414
454
|
|
|
415
455
|
pane="${HERDR_PANE_ID:-}"
|
|
@@ -431,12 +471,15 @@ trap 'report blocked; exit 130' INT TERM
|
|
|
431
471
|
# without it no `claude -p` turn reports a claude SESSION identity for this
|
|
432
472
|
# pane. That is load-bearing, not cosmetic — a pane that owns one rejects every
|
|
433
473
|
# later state report, so the report below would be dropped.
|
|
434
|
-
env -u HERDR_PANE_ID gtd
|
|
474
|
+
env -u HERDR_PANE_ID gtd-loop
|
|
435
475
|
rc=$?
|
|
436
476
|
|
|
437
|
-
# Whose turn is it now? `gtd
|
|
438
|
-
# gtd is
|
|
439
|
-
|
|
477
|
+
# Whose turn is it now? `gtd status --json` is a strictly read-only peek —
|
|
478
|
+
# every gtd command is, including `gtd next --json` (its prompt session id is
|
|
479
|
+
# derived, never minted/stored) — only the emitted scripts a driver runs
|
|
480
|
+
# actually touch git. A human actor means gtd is waiting on you; anything else
|
|
481
|
+
# means the run ended with nothing owed.
|
|
482
|
+
actor="$(gtd status --json 2>/dev/null | jq -r '.actor // ""' 2>/dev/null || true)"
|
|
440
483
|
|
|
441
484
|
if [ "$rc" -ne 0 ] || [ -z "$actor" ] || [ "$actor" = human ]; then
|
|
442
485
|
report blocked
|
|
@@ -469,6 +512,282 @@ Optionally,
|
|
|
469
512
|
sets a value renderable as `$summary` in an Agent sidebar row, if you want more
|
|
470
513
|
than the state itself.
|
|
471
514
|
|
|
515
|
+
## Writing your own driver
|
|
516
|
+
|
|
517
|
+
Run `gtd install` to print this whole chapter's protocol as a complete,
|
|
518
|
+
self-contained briefing — paste it into an agent's context (or read it yourself)
|
|
519
|
+
to build a driver without leaving your terminal. The briefing ends by directing
|
|
520
|
+
the agent to INTERVIEW you first — which agent CLI runs the turns, under what
|
|
521
|
+
permission model, how you want to invoke the loop, what should happen at a human
|
|
522
|
+
gate — and to build the driver your answers describe rather than copying the
|
|
523
|
+
reference bash. It writes nothing: "install" means installing knowledge into the
|
|
524
|
+
calling agent's context, and the briefing it prints is always exactly as current
|
|
525
|
+
as the `gtd` that printed it.
|
|
526
|
+
|
|
527
|
+
The README's minimal driver below is gtd's own reference driver, not a
|
|
528
|
+
privileged one — the engine itself is a supported public surface, and anything
|
|
529
|
+
below holds for any driver you write against it. gtd decides and prints; it
|
|
530
|
+
never touches git itself. The four commands that change anything — `gtd land`,
|
|
531
|
+
`gtd --entry <state>`, `gtd abandon`, and `gtd restore` — perform no git write
|
|
532
|
+
when run. Instead, each one's output carries `required`/ `optional`: bash
|
|
533
|
+
scripts for YOU to execute. Plain-text output (no `--json`) prints them combined
|
|
534
|
+
into ONE pasteable script — `required` verbatim, then `optional` wrapped so its
|
|
535
|
+
own failure can't turn a landed turn into a non-zero exit — so `gtd land | bash`
|
|
536
|
+
is a complete, single-command way to land a turn. Under `--json`, each one's
|
|
537
|
+
output object instead carries `required`/`optional` as two separate string
|
|
538
|
+
fields (plus, for `gtd land`, the same two combined into `script`), for a driver
|
|
539
|
+
that wants to run them apart (e.g. to skip `optional` outright). Either way,
|
|
540
|
+
printing gtd's output and never running it is not driving anything; a driver
|
|
541
|
+
must pipe or `jq`-and-execute what gtd prints.
|
|
542
|
+
|
|
543
|
+
- **`required`** is everything that decides what lands in git — closing an open
|
|
544
|
+
review checkout window, the resting state's own steering-mode
|
|
545
|
+
`format:`/`validate:` commands, the commit or squash itself (`gtd land` and
|
|
546
|
+
`gtd --entry <state>`), or the ref update and reset that undo a process
|
|
547
|
+
(`gtd abandon`, `gtd restore`) — and, last, a printed line naming what just
|
|
548
|
+
landed (`src/OutcomeScript.ts`'s `gtd_report_*` calls): a transition or
|
|
549
|
+
capture's changed-file rows, or the abandon/restore prose, resolved from the
|
|
550
|
+
repository AFTER the write above it. Run this one. Skipping it means the turn
|
|
551
|
+
never lands, and you never see what it did.
|
|
552
|
+
- **`optional`** is presentation only: re-opening the review checkout window
|
|
553
|
+
(the `<<<<<<< HEAD` diff view) after `gtd land` lands at a
|
|
554
|
+
`reviewWindow: true` state, so an editor's diff view has something to show.
|
|
555
|
+
Skip it and you lose nothing but that view — the workflow is still driven
|
|
556
|
+
correctly. `gtd abandon`/`gtd restore` always emit `optional: ""` (there is no
|
|
557
|
+
window to reopen after either).
|
|
558
|
+
|
|
559
|
+
`gtd next --json` carries one more field worth a custom driver's attention:
|
|
560
|
+
|
|
561
|
+
- **`log`** — the per-worktree loop log path, always present. It's derived from
|
|
562
|
+
the worktree's own git dir, so two worktrees looping concurrently never share
|
|
563
|
+
one file; set `$GTD_LOOP_LOG` to override it verbatim. gtd itself neither
|
|
564
|
+
creates nor truncates this file — a driver appends subprocess output to it and
|
|
565
|
+
truncates once at the start of a run, exactly like the driver above does.
|
|
566
|
+
|
|
567
|
+
`optional` may be the empty string `""`, meaning "nothing to do here".
|
|
568
|
+
`required` is never empty — even a genuine no-op `gtd land` (a clean tree
|
|
569
|
+
matching no `on` pattern) emits a PRINT-ONLY script: no git write, just the same
|
|
570
|
+
`nothing to do at "<state>"` line the script prints when a driver runs it (or,
|
|
571
|
+
under plain text with no `--json`, when gtd prints the very same script back to
|
|
572
|
+
you). Both scripts are self-contained (each carries its own precondition assert
|
|
573
|
+
and retry helper) and safe to run standalone, in sequence, or not at all — paste
|
|
574
|
+
either into a terminal and it does exactly what it says, printed output
|
|
575
|
+
included: the scripts detect their own tty/`NO_COLOR` at RUN time (not at the
|
|
576
|
+
moment gtd generated them), so the fancy/plain rendering always matches wherever
|
|
577
|
+
you actually run them.
|
|
578
|
+
|
|
579
|
+
A driver has no opening move. It does not need to know whose turn it is before
|
|
580
|
+
it starts — `gtd next --json` tells it, and a human's pending edit arrives as an
|
|
581
|
+
ordinary `kind: "capture"` beat that the driver lands like any other. The rule
|
|
582
|
+
this replaces: never `gtd land` outside a beat you dispatched. A stray land at a
|
|
583
|
+
clean `prompt` rest authors an empty attempt on purpose (that IS the stall
|
|
584
|
+
bookkeeping), so an unconditional opening land would manufacture a stall out of
|
|
585
|
+
a fresh start.
|
|
586
|
+
|
|
587
|
+
`gtd land`'s exit code IS the settled signal: 0 for an ordinary landing
|
|
588
|
+
(capture, turn, attempt, squash, or a benign no-op at a clean `message` rest), 3
|
|
589
|
+
for SETTLED, 1 for a refusal or usage error. SETTLED means nothing owed, for
|
|
590
|
+
either of two terminal shapes — a no-op at a `script` rest (the check ran, left
|
|
591
|
+
nothing any pattern claims, and re-running the same beat cannot change that), or
|
|
592
|
+
a step whose decision rewinds the process back to the workflow's initial state
|
|
593
|
+
retaining nothing (`gtd --entry fix-precheck` against a green suite is the
|
|
594
|
+
shipped example — the probe collapses away rather than landing a round trip
|
|
595
|
+
through the log). Either way a loop should exit rather than spin — with
|
|
596
|
+
`set -o pipefail`, `gtd land | bash` propagates gtd's own exit code through the
|
|
597
|
+
pipe, and `--json` carries the same fact as `settled: true`. A no-op at a
|
|
598
|
+
`prompt` rest is NOT settled — an agent that was asked to act and produced
|
|
599
|
+
nothing is a stall, a driver's own concern, not this signal's. Declaring a `C`
|
|
600
|
+
edge on a `script` state is the workflow-side way to make the state advance
|
|
601
|
+
instead of settling.
|
|
602
|
+
|
|
603
|
+
### Drivers other than bash
|
|
604
|
+
|
|
605
|
+
The protocol above is JSON in, subprocesses out — bash is one convenient shape,
|
|
606
|
+
not the only one. A **program** parses `gtd next --json`, switches on `kind`,
|
|
607
|
+
and runs `content`/`required`/`optional` as subprocesses — no bash anywhere in
|
|
608
|
+
it. A **human** runs plain `gtd next` (no `--json`), does what it says, and
|
|
609
|
+
pastes `gtd land | bash`: plain-text output exists for exactly this, printing
|
|
610
|
+
`required` and `optional` already combined into one pasteable script. An
|
|
611
|
+
**agent** gets `gtd install` as its instructions — the same protocol, in a form
|
|
612
|
+
built to be pasted into a context window rather than read. A **CI job** is the
|
|
613
|
+
program case with the `prompt` arm pointed at a headless agent CLI, and
|
|
614
|
+
`kind: "message"`/`kind: "stalled"` mapped onto "stop and report".
|
|
615
|
+
|
|
616
|
+
### A complete minimal driver
|
|
617
|
+
|
|
618
|
+
This is the whole protocol described above, compressed into a loop small enough
|
|
619
|
+
to paste from this README and own outright — save it as `~/.local/bin/gtd-loop`
|
|
620
|
+
(`chmod +x`) and it's the ready-to-run driver
|
|
621
|
+
[Driving the loop](#driving-the-loop) above points at; swap the `claude` line
|
|
622
|
+
for any agent CLI and it keeps working.
|
|
623
|
+
|
|
624
|
+
This exact fenced block is extracted and executed by
|
|
625
|
+
`tests/integration/features/readme-driver.feature` — renaming the heading above
|
|
626
|
+
or splitting the block into more than one fence breaks the build.
|
|
627
|
+
|
|
628
|
+
```bash
|
|
629
|
+
#!/usr/bin/env bash
|
|
630
|
+
set -euo pipefail
|
|
631
|
+
|
|
632
|
+
# The bundle plans; this executes. Emitted scripts print their own outcomes.
|
|
633
|
+
# Exit 3 means SETTLED — nothing owed — so this exits the whole driver at 0.
|
|
634
|
+
gtd_land() {
|
|
635
|
+
local json code=0
|
|
636
|
+
json="$(gtd land --json)" || code=$?
|
|
637
|
+
[ "$code" = 0 ] || [ "$code" = 3 ] || return "$code"
|
|
638
|
+
bash -c "$(jq -r '.required // empty' <<<"$json")" || return $?
|
|
639
|
+
bash -c "$(jq -r '.optional // empty' <<<"$json")" ||
|
|
640
|
+
echo "warn: presentation follow-up failed — continuing" >&2
|
|
641
|
+
[ "$code" = 3 ] && exit 0
|
|
642
|
+
return 0
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
while :; do
|
|
646
|
+
next="$(gtd next --json)" || exit 1
|
|
647
|
+
kind="$(jq -r .kind <<<"$next")"
|
|
648
|
+
log="$(jq -r .log <<<"$next")"
|
|
649
|
+
case "$kind" in
|
|
650
|
+
stalled) jq -r .content <<<"$next" >&2; exit 1 ;;
|
|
651
|
+
message) jq -r .content <<<"$next"; exit 0 ;;
|
|
652
|
+
capture) ;; # the human already acted — just land it
|
|
653
|
+
script) bash -c "$(jq -r .content <<<"$next")" >>"$log" 2>&1 || true ;;
|
|
654
|
+
prompt)
|
|
655
|
+
sid="$(jq -r '.session.id // empty' <<<"$next")"
|
|
656
|
+
c="$(jq -r .content <<<"$next")" model="$(jq -r '.model // empty' <<<"$next")"
|
|
657
|
+
agent_turn() { claude -p "$c" "$1" "$sid" ${model:+--model "$model"} \
|
|
658
|
+
--dangerously-skip-permissions >>"$log" 2>&1; }
|
|
659
|
+
if [ "$(jq -r '.session.resume // false' <<<"$next")" = true ]
|
|
660
|
+
then agent_turn --resume || agent_turn --session-id
|
|
661
|
+
else agent_turn --session-id || agent_turn --resume
|
|
662
|
+
fi
|
|
663
|
+
v="$(jq -r '.validate // empty' <<<"$next")" n=0
|
|
664
|
+
while [ -n "$v" ] && ! out="$(bash -c "$v" 2>&1)"; do
|
|
665
|
+
n=$((n + 1)) && [ "$n" -gt 3 ] && { printf '%s\n' "$out" >&2; exit 1; }
|
|
666
|
+
claude -p "$out" --resume "$sid" --dangerously-skip-permissions \
|
|
667
|
+
>>"$log" 2>&1 # $out IS the fix prompt, verbatim
|
|
668
|
+
done ;;
|
|
669
|
+
esac
|
|
670
|
+
gtd_land || exit 1
|
|
671
|
+
done
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
Line by line it is the protocol described above: no opening move at all — a
|
|
675
|
+
human's pending edit arrives as the `capture` beat, and a stray `gtd land`
|
|
676
|
+
outside a beat you acted on would author an empty attempt at a clean prompt rest
|
|
677
|
+
on purpose; one `gtd next --json` beat document read per loop (`kind: "stalled"`
|
|
678
|
+
guarding against a spinning agent); `message` halts, `capture` lands a human's
|
|
679
|
+
already-made edit outright, `script` runs in the driver, `prompt` goes to the
|
|
680
|
+
agent with the document's own `session.id`/`session.resume` mapped onto the
|
|
681
|
+
agent's session flags — trying `resume`'s hinted flag first and falling back to
|
|
682
|
+
the other on failure, since `session.id` is derived, not remembered (see
|
|
683
|
+
[Driving the loop](#driving-the-loop) above) — and its embedded `.validate`
|
|
684
|
+
script's output re-prompted verbatim on failure (the driver owns only the retry
|
|
685
|
+
cap); and every landed turn executed — and reported — by the emitted scripts
|
|
686
|
+
themselves, with `gtd land`'s exit 3 (SETTLED) ending a run that has nothing
|
|
687
|
+
left to do.
|
|
688
|
+
|
|
689
|
+
### The self-validation gate
|
|
690
|
+
|
|
691
|
+
After an agent turn at a state that declares `file:`+`mode:`, run the script:
|
|
692
|
+
either `gtd next --json`'s own embedded `.validate` field (present at every
|
|
693
|
+
`prompt` beat that hands over a validatable file), or, standalone,
|
|
694
|
+
`gtd validate --json`'s `.script` — both resolve the exact same script from one
|
|
695
|
+
shared resolver. Exit 0 means the file is well-formed — proceed to `gtd land`. A
|
|
696
|
+
non-zero exit means the script's own captured output IS a complete,
|
|
697
|
+
ready-to-send fix prompt (an instruction plus the findings, see `src/Emit.ts`'s
|
|
698
|
+
`failurePromptWrapper`): send it back to the same agent session verbatim, and
|
|
699
|
+
cap how many fix attempts you allow yourself — the driver owns that retry count,
|
|
700
|
+
not gtd. Landing never TRUSTS that you validated, either: the emitted `land`
|
|
701
|
+
script carries the same format/validate commands ahead of its own commit and
|
|
702
|
+
fails without committing when they fail, so a malformed file is never captured
|
|
703
|
+
whether or not you ran the validate script first.
|
|
704
|
+
|
|
705
|
+
### Failure taxonomy and recovery
|
|
706
|
+
|
|
707
|
+
Two different things can go wrong, and they mean different things — and one
|
|
708
|
+
non-zero-looking exit is not a failure at all:
|
|
709
|
+
|
|
710
|
+
- **`gtd` itself exits 1.** Nothing was attempted — this is a refusal (a guard
|
|
711
|
+
rejected the turn) or a usage error. No script was ever produced.
|
|
712
|
+
- **`gtd land` exits 3 (SETTLED).** This is NOT a failure: nothing is owed, and
|
|
713
|
+
stdout still carries a script (a print-only note, or a genuine retain+rewind)
|
|
714
|
+
that a driver must still run.
|
|
715
|
+
- **An emitted script exits non-zero when YOU run it.** Something may have
|
|
716
|
+
partially happened — e.g. a `gtd_retry`-wrapped git write landed but a later
|
|
717
|
+
step in the same script failed.
|
|
718
|
+
|
|
719
|
+
Recovery is the same in every case: **ask gtd again** (`gtd next`, then land).
|
|
720
|
+
It re-reads the real repository state fresh every time — never a cached plan —
|
|
721
|
+
and emits whatever still needs to happen from there. This works because every
|
|
722
|
+
emitted script opens by asserting its own precondition
|
|
723
|
+
(`[ "$(git rev-parse --verify --quiet HEAD 2>/dev/null)" = <expected> ] || { ...; exit 1; }`,
|
|
724
|
+
and the same shape for a review window's saved ref — see `src/Emit.ts`'s
|
|
725
|
+
`headAssertion`/`reviewWindowAssertion`), so a script generated against a
|
|
726
|
+
repository state that has since moved refuses loudly instead of corrupting
|
|
727
|
+
anything. In a repository with no commits yet, `<expected>` is the empty string
|
|
728
|
+
— the deciding read's own convention for "no commits yet" — so the first
|
|
729
|
+
workflow commit is allowed to land instead of being blocked by an unborn `HEAD`.
|
|
730
|
+
**Emitted scripts are re-runnable**: this is the single most important property
|
|
731
|
+
for a driver's recovery logic. Re-running a script that already fully applied is
|
|
732
|
+
a no-op (its git writes are `--allow-empty` commits, idempotent ref updates, and
|
|
733
|
+
tolerant staged-restore calls), and re-running one that only partially applied
|
|
734
|
+
resumes correctly, because the precondition either still holds (nothing landed
|
|
735
|
+
yet — safe to retry verbatim) or gtd's next invocation reads the new real state
|
|
736
|
+
and emits a fresh script for what remains. A driver never needs its own
|
|
737
|
+
retry/resume logic beyond "if the script failed, ask gtd again."
|
|
738
|
+
|
|
739
|
+
### Prerequisites
|
|
740
|
+
|
|
741
|
+
- **`jq`** — to pull the `required`/`optional` strings back out of gtd's
|
|
742
|
+
`--json` output.
|
|
743
|
+
- **`gtd` on `PATH`** — a mode's seeded `validate:` command (the one the
|
|
744
|
+
compiler fills in for the built-in `qa`/`review` formats) is literally the
|
|
745
|
+
string `gtd check <mode> '<file>'`, invoked by NAME from inside an emitted
|
|
746
|
+
script, not by absolute path (see `src/SteeringFormats.ts`'s
|
|
747
|
+
`seededValidateCommand`). This is a deliberate trade: a readable, overridable,
|
|
748
|
+
copy-pasteable command in exchange for depending on shell name resolution at
|
|
749
|
+
the moment the script runs. The sharp edge: if the `gtd` binary you invoked to
|
|
750
|
+
GENERATE the script differs from the `gtd` that resolves on `PATH` when the
|
|
751
|
+
script later RUNS (a locally-built dev binary vs. a globally-installed
|
|
752
|
+
release, say), you can get version skew between the two — the command that
|
|
753
|
+
validates may not be the command that decided. Keep the two in sync (one `gtd`
|
|
754
|
+
on `PATH`, consistently) if you care about that gap.
|
|
755
|
+
|
|
756
|
+
### The normalization-only contract on `format:`
|
|
757
|
+
|
|
758
|
+
A mode's `format:` command may reformat a steering file — whitespace, wrapping,
|
|
759
|
+
reordering — but must NEVER change what a land-capture guard would decide. gtd's
|
|
760
|
+
guards (the review sign-off check, the feedback-progress check, the
|
|
761
|
+
answer-completeness check — `src/StepGuards.ts`) decide ONCE, against whichever
|
|
762
|
+
bytes are on disk at the moment `gtd land` runs, which may be before OR after an
|
|
763
|
+
emitted script's own `format:` line has run (the script runs `format:` then
|
|
764
|
+
`validate:` then the commit — see `src/SteeringMode.ts`'s
|
|
765
|
+
`renderSteeringCommands` — but gtd's decision and the driver's script execution
|
|
766
|
+
are different processes at different times, so there's no guaranteed ordering
|
|
767
|
+
between "gtd decided" and "the script formatted"). That's only safe because
|
|
768
|
+
every built-in guard judges content, not incidental formatting (e.g. it
|
|
769
|
+
normalizes `[ ]`/`[x]` checkboxes before comparing). If you plug in your own
|
|
770
|
+
`format:` command, the same rule binds it: a formatter that also changes meaning
|
|
771
|
+
— ticking a box, stripping a paragraph — makes the guard's decision and the
|
|
772
|
+
file's actual content disagree, and gtd will not catch that for you.
|
|
773
|
+
|
|
774
|
+
### Built-in steering formats are ordinary modes
|
|
775
|
+
|
|
776
|
+
`qa` and `review` are gtd's two built-in steering-file formats (parsed and
|
|
777
|
+
validated in-process because `gtd lsp` needs the same parsers for live
|
|
778
|
+
diagnostics), but their `validate:` is not hardcoded or hidden: the compiler
|
|
779
|
+
SEEDS every workflow's `modes:` map with `qa`/`review` entries whose `validate:`
|
|
780
|
+
is the same `gtd check <mode> '<file>'` string described above
|
|
781
|
+
(`src/PatternConfig.ts`, `src/SteeringFormats.ts`'s `seededValidateCommand`).
|
|
782
|
+
That seeded command is visible in `gtd visualize`'s compiled model and in the
|
|
783
|
+
editor JSON schema like any other mode, and it's overridable the same way any
|
|
784
|
+
mode is — declare `modes: { qa: { validate: "your-own-command" } }` (in the
|
|
785
|
+
workflow or in `.gtdrc`) and your command displaces the seed; declaring only a
|
|
786
|
+
`format:` for `qa`/`review` composes with the seeded `validate:` rather than
|
|
787
|
+
replacing it. There is no special-cased built-in behavior a driver needs to know
|
|
788
|
+
about beyond the ordinary mode-resolution rules already documented under
|
|
789
|
+
[The `workflow:` key](#the-workflow-key).
|
|
790
|
+
|
|
472
791
|
## Configuration
|
|
473
792
|
|
|
474
793
|
gtd reads an optional `.gtdrc` config file via
|
|
@@ -568,8 +887,8 @@ workflow:
|
|
|
568
887
|
|
|
569
888
|
There is no `memory:` key anywhere in this shape — a state's memory scope is
|
|
570
889
|
never authored, only computed from its position in the machine tree (see
|
|
571
|
-
[Driving the loop](#driving-the-loop) above for the key format and
|
|
572
|
-
|
|
890
|
+
[Driving the loop](#driving-the-loop) above for the key format and how it
|
|
891
|
+
derives a driver's session id).
|
|
573
892
|
|
|
574
893
|
The top-level `entry:` key (naming the root machine, `entry.default`) and a
|
|
575
894
|
state's own `entry: true` flag are the same word at two different levels, by
|
|
@@ -623,11 +942,10 @@ model, pattern grammar, load-time rules, and how to verify a change compiles.
|
|
|
623
942
|
> Those two keys, and the `gtd review <commitish>`/`gtd fix` commands that used
|
|
624
943
|
> them, are gone. Replace `entry.review: <target>`/`entry.fix: <target>` with a
|
|
625
944
|
> plain `entry: true` flag on that same state, and enter it with
|
|
626
|
-
> `gtd
|
|
627
|
-
> `gtd --entry <state>`) instead of the removed commands.
|
|
945
|
+
> `gtd --entry <state>` instead of the removed commands.
|
|
628
946
|
> `gtd review <commitish>` required a clean tree and a `<commitish>` argument;
|
|
629
947
|
> the replacement instead captures whatever is pending in the working tree (just
|
|
630
|
-
> like an ordinary `gtd
|
|
948
|
+
> like an ordinary `gtd land`) and takes the commitish as a
|
|
631
949
|
> `--var reviewBase=<commitish>` override consumed by that state's own
|
|
632
950
|
> template-form `reviewBase:` (see the `workflow:` shape above and
|
|
633
951
|
> [`gtd --entry`](#commands)). `gtd fix` likewise becomes
|
|
@@ -691,12 +1009,11 @@ assembled from four layers, **later wins**:
|
|
|
691
1009
|
2. **A top-level `.gtdrc` `vars:` key** (a sibling of `workflow:`, NOT nested
|
|
692
1010
|
inside it) — per-repo tuning without redefining the whole workflow.
|
|
693
1011
|
3. **The current process's entry `--var` overrides**, if it was started via
|
|
694
|
-
`gtd
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
a silent no-op.
|
|
1012
|
+
`gtd --entry <state>` — repeatable `--var <name>=<value>` flags fixed at the
|
|
1013
|
+
moment of entry and recorded as `Gtd-Var: <name>=<value>` trailers on the
|
|
1014
|
+
process's oldest commit, re-parsed on every turn for as long as that process
|
|
1015
|
+
is underway. Each `--var` name must already be declared by layer 1 or 2; an
|
|
1016
|
+
undeclared name is a usage error, not a silent no-op.
|
|
700
1017
|
4. **`GTD_<UPPERCASE-name>` environment variables** — highest precedence,
|
|
701
1018
|
checked at every invocation, case-insensitively against each name already
|
|
702
1019
|
declared by layers 1–3: `GTD_TESTCOMMAND` overrides `testCommand`. The
|
|
@@ -733,7 +1050,7 @@ Config-shape problems (unknown keys, wrong types, unreadable file references)
|
|
|
733
1050
|
are collected together; if the shape is clean, the assembled definition is
|
|
734
1051
|
additionally run through the engine's own validation. A bad config throws
|
|
735
1052
|
**one** error listing every finding, at load time — before anything touches the
|
|
736
|
-
repository — never partially, and never deferred to
|
|
1053
|
+
repository — never partially, and never deferred to land time:
|
|
737
1054
|
|
|
738
1055
|
```
|
|
739
1056
|
workflow config:
|
|
@@ -757,7 +1074,7 @@ the compiler's job at load time.
|
|
|
757
1074
|
- **Single writer, linear branch.** A process's history is walked via
|
|
758
1075
|
**first-parent** commits only.
|
|
759
1076
|
- **Test/build artifacts must be gitignored.** This is **load-bearing**, not a
|
|
760
|
-
style preference: every
|
|
1077
|
+
style preference: every land decision detects "clean" via
|
|
761
1078
|
`git diff --name-status HEAD` (tracked changes) unioned with
|
|
762
1079
|
`git ls-files --others --exclude-standard` (untracked files), which silently
|
|
763
1080
|
omits anything matched by `.gitignore`. If a `script` state's command (or the
|
|
@@ -767,8 +1084,9 @@ the compiler's job at load time.
|
|
|
767
1084
|
before wiring gtd into a repo.
|
|
768
1085
|
- **Repository root invocation.** Every state subcommand must run from the git
|
|
769
1086
|
repository root. `--help`/`--version` (and the `help`/`version` subcommands),
|
|
770
|
-
`lsp`,
|
|
771
|
-
`.gtdrc` workflow, but needs no git state
|
|
1087
|
+
`lsp`, `init`, `visualize`, `check`, and `install` skip this guard entirely
|
|
1088
|
+
(`visualize` still reads the `.gtdrc` workflow, but needs no git state; `init`
|
|
1089
|
+
may even run outside a repository to seed a shared parent-dir config).
|
|
772
1090
|
- **Linked worktrees are independent.** N `git worktree` worktrees of one
|
|
773
1091
|
repository (sharing a single `.git`) each run their own gtd process: state is
|
|
774
1092
|
derived from that worktree's own HEAD, and the review checkout window's refs
|
|
@@ -799,8 +1117,8 @@ but `gtd lsp` never runs a shell command per keystroke, so live diagnostics
|
|
|
799
1117
|
become one `Information` notice pointing at `gtd validate` instead of the
|
|
800
1118
|
built-in findings. Any OTHER mode name — `prose` (used by the simple flow's plan
|
|
801
1119
|
file), or a project's own declared name — has no built-in format, so it gets no
|
|
802
|
-
live editor support at all: `gtd validate` and the `gtd
|
|
803
|
-
and validate it like any other mode.
|
|
1120
|
+
live editor support at all: `gtd validate`'s emitted script and the `gtd land`
|
|
1121
|
+
gate still format and validate it like any other mode.
|
|
804
1122
|
|
|
805
1123
|
## Development
|
|
806
1124
|
|
|
@@ -834,7 +1152,7 @@ now into a single snapshot, and whose `planStep`/`planEntry` pair separates
|
|
|
834
1152
|
deciding a transition from performing it — so a command's own capture guards run
|
|
835
1153
|
between the two. The edge reaches the outside world through two content ports
|
|
836
1154
|
(`src/RepoFiles.ts` for working-tree and committed reads, `src/CommandRunner.ts`
|
|
837
|
-
for the one place gtd spawns a subprocess itself), with the `gtd
|
|
1155
|
+
for the one place gtd spawns a subprocess itself), with the `gtd land` capture
|
|
838
1156
|
guards (`src/StepGuards.ts`) built on top of them.
|
|
839
1157
|
|
|
840
1158
|
`src/testing/` holds the in-memory git/config/filesystem test double every
|