@pmelab/gtd 7.0.0 → 7.1.1
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 +9 -4
- package/dist/gtd.bundle.mjs +872 -220
- package/docs/cli.md +114 -49
- package/docs/configuration.md +69 -3
- package/docs/loop.md +13 -0
- package/docs/upgrading.md +44 -2
- package/package.json +1 -1
- package/schema.json +180 -0
package/docs/cli.md
CHANGED
|
@@ -25,6 +25,11 @@ Commands:
|
|
|
25
25
|
state (fixEntry: true) that goes straight into repairing the
|
|
26
26
|
current failing tests. Requires a clean tree resting at the
|
|
27
27
|
workflow's initial state
|
|
28
|
+
abandon End the process currently underway without completing it:
|
|
29
|
+
close any open review checkout window, then rewind HEAD to
|
|
30
|
+
the commit the process started from, keeping everything it
|
|
31
|
+
produced as uncommitted changes. A no-op when no process is
|
|
32
|
+
underway
|
|
28
33
|
next Print the resolved rest's rendered script/prompt/message
|
|
29
34
|
(no mutation)
|
|
30
35
|
status Print the resolved rest's state/actor and which declared
|
|
@@ -32,14 +37,15 @@ Commands:
|
|
|
32
37
|
validate Format and validate the steering file the resolved rest
|
|
33
38
|
declares, with its mode's commands (its file:/mode:);
|
|
34
39
|
exits non-zero with the findings when it is invalid
|
|
35
|
-
mermaid Print the active workflow's shape as Mermaid
|
|
36
|
-
stateDiagram-v2 source (no mutation)
|
|
37
40
|
lsp Start the LSP server for .gtd/ steering files (stdio)
|
|
41
|
+
visualize Serve an interactive diagram of the active workflow locally
|
|
38
42
|
version Print version and exit
|
|
39
43
|
help Print this help and exit
|
|
40
44
|
|
|
41
45
|
Options:
|
|
42
46
|
--json Output structured JSON instead of plain text
|
|
47
|
+
--port=<n> (gtd visualize only) port to serve on (default: a free port)
|
|
48
|
+
--no-open (gtd visualize only) do not open the browser
|
|
43
49
|
--cost=<n> (gtd step only) record the invocation's token cost
|
|
44
50
|
--model=<name> (gtd step only, with --cost) tag that cost's model
|
|
45
51
|
--version, -v Print version and exit
|
|
@@ -166,7 +172,8 @@ written):
|
|
|
166
172
|
|
|
167
173
|
- the machine currently resting at the workflow's **initial state** (a plain
|
|
168
174
|
non-gtd branch resolves there via the inert-subject rule — the normal case; a
|
|
169
|
-
process already underway refuses
|
|
175
|
+
process already underway refuses — finish it, or
|
|
176
|
+
[`gtd abandon`](#gtd-abandon---json) it);
|
|
170
177
|
- a **clean working tree**;
|
|
171
178
|
- the active workflow declaring a **`reviewEntry: true`** state (otherwise:
|
|
172
179
|
`gtd review: the active workflow declares no review entry state`);
|
|
@@ -212,7 +219,8 @@ Requires, in order (any failure is a plain refusal — exit non-zero, nothing
|
|
|
212
219
|
written):
|
|
213
220
|
|
|
214
221
|
- the machine currently resting at the workflow's **initial state** (a process
|
|
215
|
-
already underway refuses)
|
|
222
|
+
already underway refuses — finish it, or [`gtd abandon`](#gtd-abandon---json)
|
|
223
|
+
it);
|
|
216
224
|
- a **clean working tree**;
|
|
217
225
|
- the active workflow declaring a **`fixEntry: true`** state (otherwise:
|
|
218
226
|
`gtd fix: the active workflow declares no fix entry state`).
|
|
@@ -231,6 +239,62 @@ unified template the fix-entry state is `fix-check`: a red suite drops into the
|
|
|
231
239
|
shared `fixing` loop and out through the review + squash tail; a green suite is
|
|
232
240
|
a no-op back to `idle`.
|
|
233
241
|
|
|
242
|
+
## `gtd abandon [--json]`
|
|
243
|
+
|
|
244
|
+
Ends the process currently underway **without completing it** — the way out of a
|
|
245
|
+
process nobody is going to finish, and the command `gtd review`/`gtd fix` name
|
|
246
|
+
when they refuse ("finish it, or run `gtd abandon`, before starting a review").
|
|
247
|
+
A workflow's own exit is its squash finale
|
|
248
|
+
([STATES.md §8](../STATES.md#8-the-squash-lifecycle)); this is the human's, and
|
|
249
|
+
it targets the same boundary:
|
|
250
|
+
|
|
251
|
+
1. any open **review checkout window** is closed first (the bracket every state
|
|
252
|
+
subcommand runs — see
|
|
253
|
+
[STATES.md §11](../STATES.md#11-the-review-checkout-window)), so the rewind
|
|
254
|
+
starts from the real head, not the rewound base;
|
|
255
|
+
2. HEAD is `git reset --mixed`ed to the commit the process started from (its
|
|
256
|
+
start parent — the same target a squash resets to).
|
|
257
|
+
|
|
258
|
+
**Nothing is discarded.** Every turn commit the process wrote is dropped, and
|
|
259
|
+
everything they carried — the code, the `.gtd/` steering files — stays in the
|
|
260
|
+
working tree as uncommitted changes for you to keep, re-commit, or throw away
|
|
261
|
+
with ordinary git:
|
|
262
|
+
|
|
263
|
+
```
|
|
264
|
+
abandoned the process resting at "await-review" — HEAD is back at 1a2b3c4
|
|
265
|
+
("feat: add calculator"), resting at "idle".
|
|
266
|
+
Everything the process produced is kept as uncommitted changes (`git status`);
|
|
267
|
+
discard them with `git checkout -- . && git clean -fd .gtd` for a clean tree.
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Takes no positional argument (extra arguments are a usage error). Resting at the
|
|
271
|
+
workflow's initial state is a **no-op success**, not a refusal — a recovery
|
|
272
|
+
command that fails when there is nothing to recover is a worse tool:
|
|
273
|
+
|
|
274
|
+
```
|
|
275
|
+
no gtd process is underway (resting at "idle") — nothing to abandon
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
The one refusal is a process whose first commit is the repository's own root
|
|
279
|
+
commit: there is no earlier commit to rewind to, so its commits have to be
|
|
280
|
+
removed by hand.
|
|
281
|
+
|
|
282
|
+
`--json` emits `{state, abandoned, from, head}` — `state` is the initial state
|
|
283
|
+
the machine returns to, `abandoned` whether anything was rewound, and (only when
|
|
284
|
+
it was) `from` the state the abandoned process rested at plus `head` the full
|
|
285
|
+
hash HEAD now points at:
|
|
286
|
+
|
|
287
|
+
```json
|
|
288
|
+
{
|
|
289
|
+
"state": "idle",
|
|
290
|
+
"abandoned": true,
|
|
291
|
+
"from": "await-review",
|
|
292
|
+
"head": "1a2b3c4…"
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
The no-op reports `{"state": "idle", "abandoned": false}` and still exits 0.
|
|
297
|
+
|
|
234
298
|
## `gtd next [--json]`
|
|
235
299
|
|
|
236
300
|
Pure emitter of the resolved rest's rendered content — it **never mutates** the
|
|
@@ -394,46 +458,6 @@ agent or human:
|
|
|
394
458
|
|
|
395
459
|
`gtd validate` takes no arguments.
|
|
396
460
|
|
|
397
|
-
## `gtd mermaid`
|
|
398
|
-
|
|
399
|
-
Pure emitter of the active workflow's **shape** — not the resolved rest — as
|
|
400
|
-
Mermaid [`stateDiagram-v2`](https://mermaid.js.org/syntax/stateDiagram.html)
|
|
401
|
-
source (see `src/Mermaid.ts`): one node per declared state, the `[*] -->`
|
|
402
|
-
initial-state marker, one edge per declared `on` row labeled with its raw
|
|
403
|
-
pattern string (same declaration order the engine itself evaluates), a `--> [*]`
|
|
404
|
-
edge for every commit state (final, no outgoing edges — see
|
|
405
|
-
[STATES.md §8](../STATES.md#8-the-squash-lifecycle)), and one `note right of`
|
|
406
|
-
per rest naming its actor, content kind, and retry cap (e.g.
|
|
407
|
-
`agent · prompt · retry 3→escalate`). No git, no HEAD resolution, no template
|
|
408
|
-
rendering — purely a function of the compiled `WorkflowDefinition`, so its
|
|
409
|
-
output is identical regardless of the current process/branch state.
|
|
410
|
-
|
|
411
|
-
```
|
|
412
|
-
$ gtd mermaid
|
|
413
|
-
stateDiagram-v2
|
|
414
|
-
state "idle" as idle
|
|
415
|
-
state "planning" as planning
|
|
416
|
-
...
|
|
417
|
-
[*] --> idle
|
|
418
|
-
idle --> planning : * **
|
|
419
|
-
...
|
|
420
|
-
note right of idle : human · message
|
|
421
|
-
...
|
|
422
|
-
```
|
|
423
|
-
|
|
424
|
-
Pipe it straight into a `.md`/`.mmd` file, a GitHub issue/PR description, or any
|
|
425
|
-
Mermaid-aware renderer (GitHub, GitLab, VS Code, Obsidian, the
|
|
426
|
-
[Mermaid Live Editor](https://mermaid.live)) to get a diagram of a custom
|
|
427
|
-
`.gtdrc` `workflow:` with no hand-maintained docs required.
|
|
428
|
-
|
|
429
|
-
State names are aliased to Mermaid-safe identifiers (non-word characters fold to
|
|
430
|
-
`_`; a digit-led name gets an `s_` prefix) via a `state "<name>" as <alias>`
|
|
431
|
-
declaration up front, so a hyphenated name like `plan-review` still displays
|
|
432
|
-
with its exact declared spelling. Rejects `--json` (exit 1,
|
|
433
|
-
`gtd mermaid does not accept --json`) — there is no structured shape to emit
|
|
434
|
-
beyond the Mermaid source itself — and takes no arguments (extra positional args
|
|
435
|
-
are rejected).
|
|
436
|
-
|
|
437
461
|
## `gtd lsp`
|
|
438
462
|
|
|
439
463
|
Starts an LSP server over stdio for `.gtd/` steering files — document symbols
|
|
@@ -469,6 +493,39 @@ extra positional arguments — it's a long-running server, not a state command.
|
|
|
469
493
|
Runs until the client disconnects (the LSP `exit` notification), then exits
|
|
470
494
|
cleanly.
|
|
471
495
|
|
|
496
|
+
## `gtd visualize [--port=<n>] [--no-open] [--json]`
|
|
497
|
+
|
|
498
|
+
Serve an interactive diagram of the ACTIVE workflow on a local web server: the
|
|
499
|
+
main flow as a graph (one box per sub-machine), and a click-through inspector
|
|
500
|
+
with each state's actor, content kind, model/memory, steering file+mode, retry,
|
|
501
|
+
flags, and outgoing/incoming edges. This is the replacement for the removed
|
|
502
|
+
`gtd mermaid` — a live viewer instead of a static diagram dump.
|
|
503
|
+
|
|
504
|
+
```
|
|
505
|
+
$ gtd visualize
|
|
506
|
+
gtd visualize running at http://127.0.0.1:53017 — Ctrl-C to stop
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
The server serves two routes: `/` (the self-contained HTML page) and
|
|
510
|
+
`/workflow.json` (the model the page renders). It runs until interrupted
|
|
511
|
+
(Ctrl-C), then closes cleanly. Options (orthogonal, `gtd visualize` only):
|
|
512
|
+
|
|
513
|
+
- `--port=<n>` (or `--port <n>`) — serve on a specific port (0–65535); the
|
|
514
|
+
default is a free ephemeral port, printed on start.
|
|
515
|
+
- `--no-open` — do not open the default browser (the URL is always printed).
|
|
516
|
+
- `--json` — print the workflow model to stdout and exit WITHOUT starting a
|
|
517
|
+
server. The model is `{ states, initial, groups, vars }`; each state carries
|
|
518
|
+
its `actor`/`kind`/`model`/`memory`/`file`/`mode`/`retry`/`flags`, its `on`
|
|
519
|
+
edges, its computed `incoming` edges, and its sub-machine `group`. `groups`
|
|
520
|
+
lists each sub-machine invocation and the concrete states it produced.
|
|
521
|
+
|
|
522
|
+
Dispatched before the repository-root guard and the config-reading path's review
|
|
523
|
+
window — it reads the active workflow (the built-in default when none is
|
|
524
|
+
configured) but touches no git/HEAD/review-window state. The diagram is rendered
|
|
525
|
+
with Mermaid loaded from a CDN, so the graph needs network access the first time
|
|
526
|
+
a browser loads the page; the inspector works offline regardless. Rejects
|
|
527
|
+
unknown options and unexpected positional arguments.
|
|
528
|
+
|
|
472
529
|
## Error envelope
|
|
473
530
|
|
|
474
531
|
Every command, in `--json` mode, reports a failure as a machine-readable
|
|
@@ -496,8 +553,16 @@ the plain-text one.
|
|
|
496
553
|
green run, and the check's `"C"` pattern never fires. Gitignore every path
|
|
497
554
|
your scripts write before wiring gtd into a repo.
|
|
498
555
|
- **Repository root invocation.** Every state subcommand (`step`/`review`/
|
|
499
|
-
`next`/`status
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
`
|
|
503
|
-
|
|
556
|
+
`next`/`status`) must run from the git repository root — the workflow, pending
|
|
557
|
+
changes, and process history are resolved against the process cwd.
|
|
558
|
+
`--help`/`--version` (and the `help`/`version` subcommands), `lsp`, and
|
|
559
|
+
`visualize` skip this guard entirely (`visualize` still reads the `.gtdrc`
|
|
560
|
+
workflow, but needs no git state).
|
|
561
|
+
- **Linked worktrees are independent.** N `git worktree` worktrees of one
|
|
562
|
+
repository (sharing a single `.git`) each run their own gtd process: state is
|
|
563
|
+
derived from that worktree's own HEAD, and the review checkout window's refs
|
|
564
|
+
live in git's per-worktree `refs/worktree/gtd/*` namespace, so a review
|
|
565
|
+
resting in one worktree neither blocks nor rewrites any other
|
|
566
|
+
([STATES.md §11](../STATES.md#11-the-review-checkout-window)). This holds from
|
|
567
|
+
7.2 on; gtd ≤ 7.1 kept the window under the shared `refs/gtd/*` refs, where
|
|
568
|
+
the first worktree to open one clobbered every sibling's branch.
|
package/docs/configuration.md
CHANGED
|
@@ -449,9 +449,10 @@ The unified template enables `reviewWindow: true` on `await-review` and marks
|
|
|
449
449
|
`review-deciding` `reviewBase: true`, so the first review covers the whole cycle
|
|
450
450
|
but each subsequent feedback round's window narrows to `last-review..HEAD`. The
|
|
451
451
|
pure engine never observes an open window — it is opened/closed entirely at the
|
|
452
|
-
edge; the real head is preserved under `refs/gtd/review-head` (the base
|
|
453
|
-
`refs/gtd/review-base`) for the window's lifetime
|
|
454
|
-
|
|
452
|
+
edge; the real head is preserved under `refs/worktree/gtd/review-head` (the base
|
|
453
|
+
under `refs/worktree/gtd/review-base`) for the window's lifetime — git's
|
|
454
|
+
per-worktree ref namespace, so linked worktrees of one repository each get their
|
|
455
|
+
own window. See [STATES.md §11](../STATES.md) for the full lifecycle.
|
|
455
456
|
|
|
456
457
|
### `reviewEntry:` — the review entry point (`gtd review <commitish>`)
|
|
457
458
|
|
|
@@ -588,6 +589,71 @@ done:
|
|
|
588
589
|
command), and gtd never interprets the number's unit (tokens, cents, whatever
|
|
589
590
|
the driver records) or the model name — it only records, sums, and groups them.
|
|
590
591
|
|
|
592
|
+
## Sub-machines (`submachines:` / `use:`)
|
|
593
|
+
|
|
594
|
+
Two OPTIONAL top-level keys let you factor repeated or complex clusters of
|
|
595
|
+
states into reusable **sub-machines** that are expanded into ordinary concrete
|
|
596
|
+
states at load time (see [STATES.md §13](../STATES.md) and
|
|
597
|
+
`src/Submachines.ts`). They add NO runtime behavior — the engine only ever sees
|
|
598
|
+
the expanded states — so a workflow authored with them is exactly a workflow
|
|
599
|
+
written flat.
|
|
600
|
+
|
|
601
|
+
```yaml
|
|
602
|
+
workflow:
|
|
603
|
+
submachines:
|
|
604
|
+
gate: # authored once
|
|
605
|
+
params: [onGreen] # names referenced as `$onGreen` below
|
|
606
|
+
states:
|
|
607
|
+
check:
|
|
608
|
+
actor: check
|
|
609
|
+
script: <%~ it.vars.testCommand %>
|
|
610
|
+
on:
|
|
611
|
+
"A .gtd/FEEDBACK.md": blocked # a local state -> renamed by `as:`
|
|
612
|
+
"C": "$onGreen" # a param -> bound by `with:`
|
|
613
|
+
blocked:
|
|
614
|
+
actor: human
|
|
615
|
+
file: <%= it.vars.feedbackFile %>
|
|
616
|
+
message: "fix the baseline"
|
|
617
|
+
on: { "* **": check }
|
|
618
|
+
use:
|
|
619
|
+
- submachine: gate
|
|
620
|
+
as: { check: start-check, blocked: start-blocked } # local -> concrete (omit for identity)
|
|
621
|
+
with: { onGreen: planning } # bind each `$param`
|
|
622
|
+
set: { check: { reviewEntry: true } } # optional extra fields on a renamed state
|
|
623
|
+
states:
|
|
624
|
+
idle:
|
|
625
|
+
{ actor: human, initial: true, message: "…", on: { "* **": start-check } }
|
|
626
|
+
planning: { actor: agent, prompt: "…", on: { "* **": start-check } }
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
Rules:
|
|
630
|
+
|
|
631
|
+
- **`submachines: { <name>: { params, states } }`** — `states` are ordinary
|
|
632
|
+
state definitions, except an `on`/`retry.otherwise` target or any whole field
|
|
633
|
+
value may be a `$param` placeholder.
|
|
634
|
+
- **`use:`** is an ordered list. Each entry names a `submachine:` and may set:
|
|
635
|
+
- **`as:`** — a `local -> concreteName` rename map. Omit it (or a given local)
|
|
636
|
+
for identity. Alias to the names a flat workflow would use to keep history
|
|
637
|
+
and `git log` unchanged.
|
|
638
|
+
- **`with:`** — bindings for the sub-machine's `$param`s.
|
|
639
|
+
- **`set:`** — extra fields merged onto a renamed state (e.g. `reviewEntry`).
|
|
640
|
+
- **Substitution is WHOLE-VALUE only.** `$name` resolves only when it is the
|
|
641
|
+
entire value of a field or target — never a substring — so bash `$x`/`${x}`
|
|
642
|
+
and Eta `<%= it.vars.x %>` inside content are untouched. Pass per-instance
|
|
643
|
+
text (a whole message/prompt/`describe`) as a whole `$name` binding.
|
|
644
|
+
- A target naming one of the sub-machine's own locals is rewritten via `as:`;
|
|
645
|
+
any other target passes through verbatim (a concrete top-level state). `on`
|
|
646
|
+
PATTERN keys are never substituted.
|
|
647
|
+
- Expansion strips `submachines:`/`use:`; the merged `states` are then compiled
|
|
648
|
+
and `validateDefinition`'d normally. Unknown sub-machine, unbound `$param`,
|
|
649
|
+
and generated-name collisions are load-time errors, aggregated with the rest.
|
|
650
|
+
|
|
651
|
+
`gtd init` scaffolds the EXPANDED flat config, so a fresh project never sees
|
|
652
|
+
`submachines:`/`use:` unless it adds them itself. The bundled template is
|
|
653
|
+
authored with them (dedup: `assertGreen`/`makeGreen`/`qaLoop`; comprehension:
|
|
654
|
+
`planLoop`/`humanReview`/`specGate`/`packageLoop`) and its golden test pins that
|
|
655
|
+
expansion is byte-identical to the flat form.
|
|
656
|
+
|
|
591
657
|
## Variables
|
|
592
658
|
|
|
593
659
|
Every template — `script`/`prompt`/`message`/`commit`, and `model`/`memory`/
|
package/docs/loop.md
CHANGED
|
@@ -34,6 +34,19 @@ capture for the human as its opening move (see below), so re-launching the loop
|
|
|
34
34
|
is the only thing the human ever does — at a gate they edit and re-run, and the
|
|
35
35
|
loop picks their change up.
|
|
36
36
|
|
|
37
|
+
**A `"message"` rest is a real handoff, not a slow beat.** No driver signs off
|
|
38
|
+
for the human: in the bundled workflow the loop runs the whole cycle unattended
|
|
39
|
+
up to `await-review` and then STOPS there with the review checkout window open
|
|
40
|
+
(the reviewable diff surfaced as uncommitted changes — see
|
|
41
|
+
[STATES.md §11](../STATES.md#11-the-review-checkout-window)). Advancing takes a
|
|
42
|
+
human edit to `.gtd/REVIEW.md`: ticking every box with **no** comment signs off
|
|
43
|
+
into the squash finale, while any comment (a note, or a code edit of your own)
|
|
44
|
+
is feedback that routes into another build + re-review round. Re-launching the
|
|
45
|
+
loop after that edit picks the decision up. Until then the loop halts on every
|
|
46
|
+
run — that is the contract, not a stall. A review nobody is going to finish is
|
|
47
|
+
ended with [`gtd abandon`](cli.md#gtd-abandon---json), which drops the process's
|
|
48
|
+
commits and keeps their content as uncommitted changes.
|
|
49
|
+
|
|
37
50
|
```bash
|
|
38
51
|
gtd next --json # ask who's up and what they should do
|
|
39
52
|
```
|
package/docs/upgrading.md
CHANGED
|
@@ -80,8 +80,14 @@ upgrading.
|
|
|
80
80
|
existing review/feedback machinery unmodified — see
|
|
81
81
|
[STATES.md §11](../STATES.md#11-the-review-checkout-window) and
|
|
82
82
|
[CLI reference](cli.md#gtd-review-commitish---json). The current command
|
|
83
|
-
surface is `init` / `step` / `next` / `status` / `validate` / `
|
|
84
|
-
`
|
|
83
|
+
surface is `init` / `step` / `next` / `status` / `validate` / `review` /
|
|
84
|
+
`lsp`.
|
|
85
|
+
|
|
86
|
+
- **`gtd mermaid` → `gtd visualize`.** The static Mermaid emitter has been
|
|
87
|
+
replaced by `gtd visualize`, which serves an interactive diagram of the active
|
|
88
|
+
workflow (main flow, sub-machines, per-state details) on a local web server —
|
|
89
|
+
see [CLI reference](cli.md#gtd-visualize---portn---no-open---json). Both are
|
|
90
|
+
read-only views of the active workflow; nothing in the machine changes.
|
|
85
91
|
|
|
86
92
|
- **`gtd format <file>`, and the bundled prettier with it.** gtd no longer
|
|
87
93
|
formats anything on its own: a steering-file mode declares its own `format:`
|
|
@@ -153,6 +159,42 @@ loudly at load time rather than silently misinterpreting.
|
|
|
153
159
|
or agent harness, upgrading the `gtd` binary also means re-copying that skill
|
|
154
160
|
from this release.
|
|
155
161
|
|
|
162
|
+
## 7.2: the review checkout window is now per worktree
|
|
163
|
+
|
|
164
|
+
The review checkout window's two refs moved from the SHARED `refs/gtd/*`
|
|
165
|
+
namespace to git's PER-WORKTREE `refs/worktree/*` one:
|
|
166
|
+
|
|
167
|
+
| before (≤ 7.1) | from 7.2 |
|
|
168
|
+
| ---------------------- | ------------------------------- |
|
|
169
|
+
| `refs/gtd/review-head` | `refs/worktree/gtd/review-head` |
|
|
170
|
+
| `refs/gtd/review-base` | `refs/worktree/gtd/review-base` |
|
|
171
|
+
|
|
172
|
+
Nothing in a `.gtdrc` or a workflow definition references those names, so **no
|
|
173
|
+
config change is needed**. What changes is behaviour in a repository checked out
|
|
174
|
+
as several linked worktrees (`git worktree add`, one `.git` shared between
|
|
175
|
+
them): each worktree now has its own window, so a review resting in one no
|
|
176
|
+
longer "closes" from another — which, on the shared refs, mixed-reset that other
|
|
177
|
+
worktree's branch onto the reviewing worktree's saved head and left every
|
|
178
|
+
sibling refusing with "a process is already underway".
|
|
179
|
+
|
|
180
|
+
A window an OLDER gtd left open across the upgrade still closes from the legacy
|
|
181
|
+
refs, so an in-flight review in a single-worktree repo needs no attention. In a
|
|
182
|
+
multi-worktree repo gtd cannot tell which worktree a shared ref belongs to, so
|
|
183
|
+
it closes only when HEAD is contained in the saved head and otherwise refuses
|
|
184
|
+
with the exact recovery commands — run them in the worktree that owns the
|
|
185
|
+
window:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
git reset --mixed refs/gtd/review-head
|
|
189
|
+
git update-ref -d refs/gtd/review-head
|
|
190
|
+
git update-ref -d refs/gtd/review-base
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
7.2 also adds [`gtd abandon`](cli.md#gtd-abandon---json), the supported way out
|
|
194
|
+
of a process that will never be finished (it closes the window, then rewinds
|
|
195
|
+
HEAD to the process's start parent, keeping everything the process produced as
|
|
196
|
+
uncommitted changes) — no hand-editing of refs required.
|
|
197
|
+
|
|
156
198
|
## Prior breaking change: the v1 → v2 label grammar (historical)
|
|
157
199
|
|
|
158
200
|
v2 replaced v1's undifferentiated turn labels with six labels carrying the
|
package/package.json
CHANGED
package/schema.json
CHANGED
|
@@ -36,6 +36,182 @@
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
},
|
|
39
|
+
"submachines": {
|
|
40
|
+
"type": "object",
|
|
41
|
+
"description": "Optional reusable sub-machines, invoked by `use:` and expanded into concrete states at load time (see src/Submachines.ts).",
|
|
42
|
+
"additionalProperties": {
|
|
43
|
+
"type": "object",
|
|
44
|
+
"description": "A reusable, parameterized cluster of states, expanded at load time by each `use:` invocation into concrete states (see src/Submachines.ts). Purely a source-authoring convenience — the engine only ever sees the expanded states.",
|
|
45
|
+
"additionalProperties": false,
|
|
46
|
+
"required": ["states"],
|
|
47
|
+
"properties": {
|
|
48
|
+
"params": {
|
|
49
|
+
"type": "array",
|
|
50
|
+
"items": {
|
|
51
|
+
"type": "string"
|
|
52
|
+
},
|
|
53
|
+
"description": "Parameter names. A `$name` token used as a whole field value or `on`/`retry.otherwise` target is replaced by the invocation's `with:` binding."
|
|
54
|
+
},
|
|
55
|
+
"states": {
|
|
56
|
+
"type": "object",
|
|
57
|
+
"description": "The sub-machine's local states. `on`/`retry.otherwise` targets naming another local are rewritten via `as:`; a `$name` value is bound from `with:`.",
|
|
58
|
+
"minProperties": 1,
|
|
59
|
+
"additionalProperties": {
|
|
60
|
+
"type": "object",
|
|
61
|
+
"description": "One workflow state. Declare exactly one content kind (script/prompt/message/commit). A commit state is final: no actor, no on.",
|
|
62
|
+
"additionalProperties": false,
|
|
63
|
+
"properties": {
|
|
64
|
+
"actor": {
|
|
65
|
+
"type": "string",
|
|
66
|
+
"description": "Who acts at this state. Required on every non-commit state; forbidden on a commit state."
|
|
67
|
+
},
|
|
68
|
+
"script": {
|
|
69
|
+
"type": "string",
|
|
70
|
+
"description": "Content kind: a shell script (Eta template). The loop driver executes it verbatim via bash, then steps this state's actor."
|
|
71
|
+
},
|
|
72
|
+
"prompt": {
|
|
73
|
+
"type": "string",
|
|
74
|
+
"description": "Content kind: an agent prompt (Eta template), emitted by `gtd next`."
|
|
75
|
+
},
|
|
76
|
+
"message": {
|
|
77
|
+
"type": "string",
|
|
78
|
+
"description": "Content kind: a human-facing message (Eta template), emitted by `gtd next`."
|
|
79
|
+
},
|
|
80
|
+
"commit": {
|
|
81
|
+
"type": "string",
|
|
82
|
+
"description": "Content kind: entering this state ends the process by squashing it into one commit with this message (Eta template). Final — no actor, no on."
|
|
83
|
+
},
|
|
84
|
+
"on": {
|
|
85
|
+
"type": "object",
|
|
86
|
+
"description": "Ordered map of change pattern -> target state. Patterns: \"C\" (clean tree) or \"<A|M|D|*> <glob>\" over the pending diff; first declared match wins. Every target must name a defined state, and every non-initial state must be reachable through these edges (or a retry.otherwise). A value is either the target state name (string) or a { to, describe } object whose describe is a human-readable sentence templates can surface as it.edges (e.g. in a human gate's message).",
|
|
87
|
+
"additionalProperties": {
|
|
88
|
+
"oneOf": [
|
|
89
|
+
{
|
|
90
|
+
"type": "string",
|
|
91
|
+
"description": "The target state name."
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"type": "object",
|
|
95
|
+
"additionalProperties": false,
|
|
96
|
+
"required": ["to"],
|
|
97
|
+
"properties": {
|
|
98
|
+
"to": {
|
|
99
|
+
"type": "string",
|
|
100
|
+
"description": "The target state name."
|
|
101
|
+
},
|
|
102
|
+
"describe": {
|
|
103
|
+
"type": "string",
|
|
104
|
+
"description": "Human-readable sentence describing where this change routes; surfaced verbatim (never Eta-rendered) to templates as it.edges[].describe."
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
]
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
"initial": {
|
|
112
|
+
"type": "boolean",
|
|
113
|
+
"description": "Exactly one state in the workflow must declare initial: true (and it must not be a commit state)."
|
|
114
|
+
},
|
|
115
|
+
"retry": {
|
|
116
|
+
"type": "object",
|
|
117
|
+
"description": "Redirect transitions INTO this state once it has been entered `max` times in the current process.",
|
|
118
|
+
"additionalProperties": false,
|
|
119
|
+
"required": ["max", "otherwise"],
|
|
120
|
+
"properties": {
|
|
121
|
+
"max": {
|
|
122
|
+
"type": "integer",
|
|
123
|
+
"minimum": 0,
|
|
124
|
+
"description": "Entries allowed this process before redirecting."
|
|
125
|
+
},
|
|
126
|
+
"otherwise": {
|
|
127
|
+
"type": "string",
|
|
128
|
+
"description": "Defined state to redirect to once over the cap."
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
"model": {
|
|
133
|
+
"type": "string",
|
|
134
|
+
"description": "Opaque harness hint passed through `gtd next --json`/`gtd status --json` (e.g. \"smart\"). Never interpreted by gtd. Forbidden on a commit state."
|
|
135
|
+
},
|
|
136
|
+
"memory": {
|
|
137
|
+
"type": "string",
|
|
138
|
+
"description": "Opaque memory-scope label passed through `gtd next --json`/`gtd status --json` (e.g. \"plan\"). A memory-aware driver retains an agent's memory across consecutive agent turns sharing this label and starts fresh when it changes. Never interpreted by gtd. Forbidden on a commit state."
|
|
139
|
+
},
|
|
140
|
+
"file": {
|
|
141
|
+
"type": "string",
|
|
142
|
+
"description": "The state's steering file: an Eta template naming the file a human/editor should look at while the machine rests here. Forbidden on a commit state."
|
|
143
|
+
},
|
|
144
|
+
"mode": {
|
|
145
|
+
"type": "string",
|
|
146
|
+
"description": "The steering file's format: the name of a built-in mode (qa/review, validated in-process by gtd) or of a `modes:` entry. gtd formats and validates the file with that mode before capturing a turn out of this state, and the LSP dispatches live diagnostics on the built-in names. Requires a sibling `file:`. Forbidden on a commit state."
|
|
147
|
+
},
|
|
148
|
+
"reviewWindow": {
|
|
149
|
+
"type": "boolean",
|
|
150
|
+
"description": "When true, gtd opens a review checkout window while the machine rests here — HEAD/index are rewound to the review base so the whole base..HEAD diff surfaces as uncommitted changes in the editor. Forbidden on a commit state."
|
|
151
|
+
},
|
|
152
|
+
"reviewBase": {
|
|
153
|
+
"type": "boolean",
|
|
154
|
+
"description": "Marks the state whose most-recent in-process commit anchors the review window's diff base; absent any, the base is the process start. Forbidden on a commit state."
|
|
155
|
+
},
|
|
156
|
+
"reviewEntry": {
|
|
157
|
+
"type": "boolean",
|
|
158
|
+
"description": "Marks the (at most one) state `gtd review <commitish>` enters to start a brand new process reviewing <commitish>..HEAD. Forbidden on a commit state and on the initial state."
|
|
159
|
+
},
|
|
160
|
+
"fixEntry": {
|
|
161
|
+
"type": "boolean",
|
|
162
|
+
"description": "Marks the (at most one) state `gtd fix` enters to start a brand new process that goes straight into repairing the current failing tests. Forbidden on a commit state and on the initial state."
|
|
163
|
+
},
|
|
164
|
+
"requireProgress": {
|
|
165
|
+
"type": "boolean",
|
|
166
|
+
"description": "When true, a step at this state is refused if its only pending change is deleting the state's own `file:` — a work-free turn that discards its input without addressing it. A `NOTHING ACTIONABLE` sentinel file is exempt (a legitimately non-actionable round makes no code change). Requires a `file:`. Forbidden on a commit state."
|
|
167
|
+
},
|
|
168
|
+
"answerGate": {
|
|
169
|
+
"type": "boolean",
|
|
170
|
+
"description": "When true, a step at this state is refused unless every open question in its qa-mode `file:` is answered — exactly one checkbox ticked per question. Requires a `file:` and `mode: qa`. Forbidden on a commit state."
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
"use": {
|
|
179
|
+
"type": "array",
|
|
180
|
+
"description": "Sub-machine invocations. Each clones a `submachines:` entry into concrete states: `as:` renames its locals (identity when omitted), `with:` binds its `$param`s, `set:` adds extra per-instance state fields.",
|
|
181
|
+
"items": {
|
|
182
|
+
"type": "object",
|
|
183
|
+
"additionalProperties": false,
|
|
184
|
+
"required": ["submachine"],
|
|
185
|
+
"properties": {
|
|
186
|
+
"submachine": {
|
|
187
|
+
"type": "string",
|
|
188
|
+
"description": "Name of a declared sub-machine."
|
|
189
|
+
},
|
|
190
|
+
"name": {
|
|
191
|
+
"type": "string",
|
|
192
|
+
"description": "Optional label for this instance (inert to expansion; used by tooling like `gtd visualize` to name the group). Defaults to the sub-machine name."
|
|
193
|
+
},
|
|
194
|
+
"as": {
|
|
195
|
+
"type": "object",
|
|
196
|
+
"description": "Rename map: local state name -> concrete state name (identity when omitted).",
|
|
197
|
+
"additionalProperties": {
|
|
198
|
+
"type": "string"
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
"with": {
|
|
202
|
+
"type": "object",
|
|
203
|
+
"description": "Bindings for the sub-machine's `$param`s."
|
|
204
|
+
},
|
|
205
|
+
"set": {
|
|
206
|
+
"type": "object",
|
|
207
|
+
"description": "Extra fields merged onto a renamed local state, keyed by local name.",
|
|
208
|
+
"additionalProperties": {
|
|
209
|
+
"type": "object"
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
},
|
|
39
215
|
"states": {
|
|
40
216
|
"type": "object",
|
|
41
217
|
"description": "The workflow's named states. At least one; exactly one with initial: true.",
|
|
@@ -148,6 +324,10 @@
|
|
|
148
324
|
"requireProgress": {
|
|
149
325
|
"type": "boolean",
|
|
150
326
|
"description": "When true, a step at this state is refused if its only pending change is deleting the state's own `file:` — a work-free turn that discards its input without addressing it. A `NOTHING ACTIONABLE` sentinel file is exempt (a legitimately non-actionable round makes no code change). Requires a `file:`. Forbidden on a commit state."
|
|
327
|
+
},
|
|
328
|
+
"answerGate": {
|
|
329
|
+
"type": "boolean",
|
|
330
|
+
"description": "When true, a step at this state is refused unless every open question in its qa-mode `file:` is answered — exactly one checkbox ticked per question. Requires a `file:` and `mode: qa`. Forbidden on a commit state."
|
|
151
331
|
}
|
|
152
332
|
}
|
|
153
333
|
}
|