@pmelab/gtd 8.0.0 → 8.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +490 -110
- package/bin/gtd +104 -20
- package/dist/gtd.bundle.mjs +214 -102
- package/package.json +1 -2
- package/schema.json +33 -9
- package/docs/cli.md +0 -675
- package/docs/configuration.md +0 -994
- package/docs/design/configurable-state-machine.md +0 -1439
- package/docs/design/delta-purity-plan.md +0 -283
- package/docs/design/pattern-machine-plan.md +0 -164
- package/docs/design/pluggable-steering-modes.md +0 -154
- package/docs/design/scripted-actor.md +0 -289
- package/docs/design/state-file-association.md +0 -120
- package/docs/design/state-machine-validation.md +0 -437
- package/docs/design/steering-file-loops.md +0 -182
- package/docs/design/steering-file-validation-command.md +0 -385
- package/docs/design/work-packages.md +0 -193
- package/docs/development.md +0 -109
- package/docs/loop.md +0 -219
- package/docs/upgrading.md +0 -293
package/docs/development.md
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
# Development
|
|
2
|
-
|
|
3
|
-
```bash
|
|
4
|
-
npm install
|
|
5
|
-
npm run dev # run from source, no build (node dev/run.mjs)
|
|
6
|
-
npm run build # tsdown → dist/gtd.bundle.mjs
|
|
7
|
-
npm test # format:check, typecheck, lint, unit + e2e tests, fallow
|
|
8
|
-
npm run test:unit # vitest unit tests (the pure resolver) — --project unit
|
|
9
|
-
npm run test:e2e # gherkin e2e via vitest + quickpickle — --project e2e
|
|
10
|
-
npm run test:mutation # StrykerJS mutation testing (manual only, ~2 min)
|
|
11
|
-
npm run typecheck
|
|
12
|
-
npm run lint
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
## Pre-commit hook
|
|
16
|
-
|
|
17
|
-
A pre-commit hook is installed automatically via the `prepare` script when you
|
|
18
|
-
run `npm install` on a fresh clone — no manual setup needed. The hook runs
|
|
19
|
-
[lint-staged](https://github.com/lint-staged/lint-staged) with
|
|
20
|
-
[oxfmt](https://oxc.rs/docs/guide/usage/formatter.html), formatting every staged
|
|
21
|
-
file before each commit (`oxfmt --no-error-on-unmatched-pattern --write`),
|
|
22
|
-
mirroring the `format:check` step enforced in CI (`oxfmt --check .`).
|
|
23
|
-
|
|
24
|
-
## Templates
|
|
25
|
-
|
|
26
|
-
Every state's content (`script`/`prompt`/`message`/`commit`) is an Eta template
|
|
27
|
-
string, rendered by `src/PatternTemplates.ts`'s `renderStateTemplate` against
|
|
28
|
-
the variable set documented in
|
|
29
|
-
[Configuration](configuration.md#template-variables). There is no template-file
|
|
30
|
-
registry to keep in sync: a workflow's content strings are either inline in its
|
|
31
|
-
YAML/`.gtdrc` or a `./`-relative file reference auto-inlined at config load
|
|
32
|
-
(`src/PatternConfig.ts`) — see
|
|
33
|
-
[Configuration](configuration.md#content-values-inline-or-a-file-reference). The
|
|
34
|
-
bundled workflow template (`src/workflows/unified.yaml`) gtd runs as its
|
|
35
|
-
built-in default (and materializes into config form via `renderInitConfig`) is
|
|
36
|
-
imported as raw text via tsdown's `.yaml`-as-text loader (`tsdown.config.ts`)
|
|
37
|
-
and compiled through the same path a `.gtdrc` `workflow:` key goes through
|
|
38
|
-
(`src/workflows/templates.ts`) — no privileged code path.
|
|
39
|
-
|
|
40
|
-
`npm run dev` runs `src/main.ts` directly via Node's native TypeScript
|
|
41
|
-
type-stripping (requires Node 22.6+). It registers `dev/hooks.mjs`, which
|
|
42
|
-
resolves `./Foo.js` specifiers to the on-disk `./Foo.ts` and imports `*.yaml`
|
|
43
|
-
files as raw text, mirroring tsdown's `.yaml`-as-text loader — this is what lets
|
|
44
|
-
`src/workflows/unified.yaml` resolve when running from source.
|
|
45
|
-
|
|
46
|
-
The decision core is pure and IO-free: the pattern machine's shape (states,
|
|
47
|
-
patterns, retry) is a plain-data `WorkflowDefinition` (`src/PatternMachine.ts`),
|
|
48
|
-
and the same module's `resolveState`/`step` are the pure resolver/interpreter
|
|
49
|
-
over it — so the whole engine is trivially unit-testable in isolation; all
|
|
50
|
-
git/filesystem/template IO is confined to the edge (`src/Edge.ts`).
|
|
51
|
-
|
|
52
|
-
`npm run build` produces `dist/gtd.bundle.mjs`, which npm exposes as the `gtd`
|
|
53
|
-
binary via the `bin` field in `package.json`.
|
|
54
|
-
|
|
55
|
-
## The LSP server
|
|
56
|
-
|
|
57
|
-
`gtd lsp` (`src/Lsp.ts`) serves editor tooling over `.gtd/`'s two steering-file
|
|
58
|
-
formats — document symbols and diagnostics for `.gtd/TODO.md`'s open questions
|
|
59
|
-
and `.gtd/REVIEW.md`'s review chunks/hunks, plus check/uncheck code actions for
|
|
60
|
-
the latter. It's keyed on file NAME, not workflow state: a v3 workflow declares
|
|
61
|
-
no state→file mapping (a state names no file), so the server needs no
|
|
62
|
-
git/`.gtdrc` dependency at all and serves whatever content the editor hands it
|
|
63
|
-
over the LSP protocol, exactly like any other document.
|
|
64
|
-
|
|
65
|
-
`src/OpenQuestions.ts` and `src/ReviewDoc.ts` are the pure parsers behind both
|
|
66
|
-
the LSP and the `gtd validate` command, which resolves the current state, reads
|
|
67
|
-
its `file:`, and evaluates it per its `mode:` — ONE source of truth per format,
|
|
68
|
-
no bash port and no dual-implementation contract to keep in sync. Mode dispatch
|
|
69
|
-
itself lives in `src/SteeringMode.ts`, half by half: the two BUILT-IN mode names
|
|
70
|
-
validate with these parsers (`qa` → `parseOpenQuestions`, `review` →
|
|
71
|
-
`parseReviewDoc`), while a `modes:` entry's `format:`/`validate:` shell commands
|
|
72
|
-
take precedence per half — gtd itself ships no formatter. See
|
|
73
|
-
[docs/design/steering-file-validation-command.md](design/steering-file-validation-command.md)
|
|
74
|
-
and
|
|
75
|
-
[docs/design/pluggable-steering-modes.md](design/pluggable-steering-modes.md)
|
|
76
|
-
and [STATES.md §12](../STATES.md) for the command, and each module's own doc
|
|
77
|
-
comment for the format it defines. Their unit tests are that format's spec
|
|
78
|
-
tests.
|
|
79
|
-
|
|
80
|
-
## Mutation testing
|
|
81
|
-
|
|
82
|
-
Run mutation testing on-demand with `npm run test:mutation` (StrykerJS) — never
|
|
83
|
-
run it as part of routine development; it is a deliberate, manually-triggered
|
|
84
|
-
check. `stryker.config.json`'s `mutate` list names the v3 pattern-machine module
|
|
85
|
-
set (`src/PatternMachine.ts`, `src/PatternConfig.ts`, `src/PatternTemplates.ts`,
|
|
86
|
-
`src/Edge.ts`, `src/Config.ts`, `src/Format.ts` — see
|
|
87
|
-
[Architecture](../AGENTS.md#the-pattern-machine-module-map) for the v3 module
|
|
88
|
-
map), plus the steering-file format parsers and the LSP built on them
|
|
89
|
-
(`src/OpenQuestions.ts`, `src/ReviewDoc.ts`, `src/Lsp.ts` — see
|
|
90
|
-
["The LSP server"](#the-lsp-server) below). `src/Lsp.ts`'s protocol-adapter tail
|
|
91
|
-
(`startLspServer`) is exercised only by the `@live` e2e (excluded from the
|
|
92
|
-
mutation run's Cucumber harness), so mutants confined to that tail may survive
|
|
93
|
-
without it being a coverage gap in the ordinary test suite — the pure
|
|
94
|
-
symbol/edit/diagnostic builders above it are unit-tested and mutation-checked
|
|
95
|
-
normally.
|
|
96
|
-
|
|
97
|
-
`src/Git.ts` is excluded: the Cucumber harness stubs git at the Effect boundary,
|
|
98
|
-
so `Git.ts` mutants have zero in-memory coverage.
|
|
99
|
-
|
|
100
|
-
The HTML report lands in `reports/mutation/mutation.html` (git-ignored).
|
|
101
|
-
|
|
102
|
-
## Releasing
|
|
103
|
-
|
|
104
|
-
Releases are automatic. Push releasable Conventional Commits (`fix:`, `feat:`,
|
|
105
|
-
or breaking changes) to `main` and the Release workflow runs the tests, then
|
|
106
|
-
`npx semantic-release`. Semantic-release computes the next version, writes it
|
|
107
|
-
into `package.json`, builds the bundle, commits the bump back as
|
|
108
|
-
`chore(release): X.Y.Z [skip ci]`, tags `vX.Y.Z`, and creates the GitHub release
|
|
109
|
-
with `gtd.bundle.mjs` attached.
|
package/docs/loop.md
DELETED
|
@@ -1,219 +0,0 @@
|
|
|
1
|
-
# Driving the loop
|
|
2
|
-
|
|
3
|
-
gtd (v3, the pattern machine) exposes three commands a loop driver combines:
|
|
4
|
-
|
|
5
|
-
- **`gtd step <actor>`** — authenticate as `<actor>`, match the resolved rest's
|
|
6
|
-
declared patterns against the pending changes, and commit (or squash) the one
|
|
7
|
-
resulting transition. Performs AT MOST one transition — there is no fixpoint
|
|
8
|
-
chain to drive. Pass `--cost=<n>` (optionally `--model=<name>`) to record the
|
|
9
|
-
just-finished invocation's token cost and model on the turn commit; gtd sums
|
|
10
|
-
them across the process into `it.processCost`/`it.processCostByModel` (see
|
|
11
|
-
[Configuration: Token cost](configuration.md#token-cost)).
|
|
12
|
-
- **`gtd next`** — print the resolved rest's rendered script/prompt/message,
|
|
13
|
-
without mutating anything.
|
|
14
|
-
|
|
15
|
-
gtd never executes a workflow script itself: at a `script`-content rest the
|
|
16
|
-
DRIVER runs the emitted `content` and then steps that state's own actor.
|
|
17
|
-
|
|
18
|
-
A loop is a simple cycle:
|
|
19
|
-
|
|
20
|
-
1. Run `gtd next --json` — see [`cli.md`](cli.md#gtd-next---json) for the exact
|
|
21
|
-
`{state, actor, kind, content, model?, memory?, label?}` field shape. `kind`
|
|
22
|
-
is the dispatch key: `"message"` → halt (a human rest); `"script"` → execute
|
|
23
|
-
`content` verbatim (ignoring its exit code), then `gtd step <actor>` to
|
|
24
|
-
capture the outcome; `"prompt"` → feed `content` to the agent, then
|
|
25
|
-
`gtd step <actor>` yourself once it's done acting. The optional `model` is an
|
|
26
|
-
opaque string the workflow author chose, which you map onto your own
|
|
27
|
-
harness's model selection if you use one. The optional `label` is a
|
|
28
|
-
human-readable display name for the resolved state, present only when that
|
|
29
|
-
state declares `label:` in the workflow; when it's absent, a driver that
|
|
30
|
-
wants to show something falls back to `state` itself (that fallback lives in
|
|
31
|
-
the driver, not gtd core — see `bin/gtd-loop`'s
|
|
32
|
-
`label="$(jq -r '.label // .state' <<<"$next_json")"`).
|
|
33
|
-
2. Repeat until a `"message"` rest halts the loop, or a zero-commit script step
|
|
34
|
-
at idle settles it (the green terminal signal).
|
|
35
|
-
|
|
36
|
-
A human acts by editing files (e.g. writing/editing `.gtd/TODO.md`, fixing
|
|
37
|
-
code); `gtd step human` captures the edit as their turn. A driver can run that
|
|
38
|
-
capture for the human as its opening move (see below), so re-launching the loop
|
|
39
|
-
is the only thing the human ever does — at a gate they edit and re-run, and the
|
|
40
|
-
loop picks their change up.
|
|
41
|
-
|
|
42
|
-
**A `"message"` rest is a real handoff, not a slow beat.** No driver signs off
|
|
43
|
-
for the human: in the bundled workflow the loop runs the whole cycle unattended
|
|
44
|
-
up to `await-review` and then rests there with the review checkout window open
|
|
45
|
-
(the reviewable diff surfaced as uncommitted changes — see
|
|
46
|
-
[STATES.md §11](../STATES.md#11-the-review-checkout-window)). Advancing takes a
|
|
47
|
-
human edit to `.gtd/REVIEW.md`: ticking every box with **no** comment signs off
|
|
48
|
-
into the squash finale, while any comment (a note, or a code edit of your own)
|
|
49
|
-
is feedback that routes into another build + re-review round. That gate is the
|
|
50
|
-
contract, not a stall — nothing advances until the edit is captured, whether the
|
|
51
|
-
capture happens within the same run or a later one. A review nobody is going to
|
|
52
|
-
finish is ended with [`gtd abandon`](cli.md#gtd-abandon---json), which drops the
|
|
53
|
-
process's commits and keeps their content as uncommitted changes.
|
|
54
|
-
|
|
55
|
-
**The loop drives only the autonomous states.** Reaching a `"message"` rest, it
|
|
56
|
-
prints the gate via `gtd next` and exits 0 — the human edits and re-launches the
|
|
57
|
-
loop, whose opening move captures what they left. Anything richer at that
|
|
58
|
-
boundary (opening an editor on the gate's file, desktop notifications,
|
|
59
|
-
terminal-multiplexer status) is deliberately an OUTER wrapper's responsibility,
|
|
60
|
-
not `bin/gtd`'s.
|
|
61
|
-
|
|
62
|
-
Pass `--once` — see [`cli.md`](cli.md#--once) for its exact syntax and
|
|
63
|
-
positioning — to restrict the loop to exactly one beat — one human-gate capture,
|
|
64
|
-
one script check+step, or one agent prompt+step — then exit, instead of driving
|
|
65
|
-
all the way to idle/settled.
|
|
66
|
-
|
|
67
|
-
```bash
|
|
68
|
-
gtd next --json # ask who's up and what they should do
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
See [`skills/loop/SKILL.md`](../skills/loop/SKILL.md) for the agent-facing
|
|
72
|
-
instructions that follow the same pinned contract. `gtd` itself (bare, or with
|
|
73
|
-
`loop` as its first argument — see below) is the packaged, ready-to-run
|
|
74
|
-
implementation of that same script for anyone who doesn't want to drive the loop
|
|
75
|
-
by hand.
|
|
76
|
-
|
|
77
|
-
## The reference loop driver
|
|
78
|
-
|
|
79
|
-
A minimal bash implementation of the pinned protocol, driving an agent CLI (e.g.
|
|
80
|
-
`claude -p`) against `gtd next --json` output. This is the authoritative
|
|
81
|
-
reference for what a loop driver must do; keep any other implementation
|
|
82
|
-
(including `skills/loop/SKILL.md`) consistent with it rather than editing both
|
|
83
|
-
independently. Both open with the same move — capture the human's pending edit
|
|
84
|
-
when the machine rests at a `"message"` gate — so `gtd` is the only command a
|
|
85
|
-
human runs. `bin/gtd` is the packaged entry point: invoked bare, or with `loop`
|
|
86
|
-
as its first argument, it runs this exact script; any other first argument (e.g.
|
|
87
|
-
`next`, `step`, `status`) hands off to `node dist/gtd.bundle.mjs` instead. The
|
|
88
|
-
loop body it runs adds five things on top of the reference script above: it can
|
|
89
|
-
restrict itself to exactly one beat via `--once` instead of driving to
|
|
90
|
-
idle/settled; it stops with a diagnostic if the same `"prompt"` state/content
|
|
91
|
-
repeat with no progress (see `skills/loop/SKILL.md`'s "Stall detection"); it
|
|
92
|
-
lets `GTD_LOOP_AGENT_CMD` swap in any coding agent CLI in place of the default
|
|
93
|
-
`claude -p`, receiving the prompt via `$GTD_LOOP_PROMPT`; it exports the
|
|
94
|
-
resolved state's optional `model` hint as `$GTD_LOOP_MODEL`, appending
|
|
95
|
-
`--model "$GTD_LOOP_MODEL"` to the default `claude -p` invocation whenever it's
|
|
96
|
-
non-empty; and it acts on the optional `memory` scope hint (see "Agent memory
|
|
97
|
-
scope" below) — continuing one agent session across consecutive same-scope turns
|
|
98
|
-
and starting fresh when the scope changes.
|
|
99
|
-
|
|
100
|
-
`bin/gtd` resolves both its bundle hand-off and the loop's own internal `gtd`
|
|
101
|
-
calls against `dist/gtd.bundle.mjs` next to its own location. Set `GTD_BIN` — a
|
|
102
|
-
full command, not just a path — to point both at source instead, e.g. for
|
|
103
|
-
development or testing:
|
|
104
|
-
|
|
105
|
-
```bash
|
|
106
|
-
GTD_BIN="node $PWD/dev/run.mjs" bin/gtd
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
```bash
|
|
110
|
-
#!/usr/bin/env bash
|
|
111
|
-
set -euo pipefail
|
|
112
|
-
|
|
113
|
-
# Opening move: if the machine rests at a human gate, capture the human's
|
|
114
|
-
# pending edit first, so re-launching the loop is the ONLY thing a human does
|
|
115
|
-
# (they never run `gtd step human` by hand). Peek with the pure `gtd next
|
|
116
|
-
# --json` and step only at a `"message"` rest — at an agent/check rest
|
|
117
|
-
# `gtd step human` would refuse out-of-turn, and a mid-cycle restart must just
|
|
118
|
-
# resume driving. A clean gate is a no-op; a genuine refusal (malformed steering
|
|
119
|
-
# file, unrecognized edit) stops the loop instead of being driven past.
|
|
120
|
-
if [[ "$(gtd next --json | jq -r .kind)" == "message" ]]; then
|
|
121
|
-
gtd step human >/dev/null
|
|
122
|
-
fi
|
|
123
|
-
|
|
124
|
-
while true; do
|
|
125
|
-
next_json="$(gtd next --json)"
|
|
126
|
-
state="$(jq -r .state <<<"$next_json")"
|
|
127
|
-
actor="$(jq -r .actor <<<"$next_json")"
|
|
128
|
-
kind="$(jq -r .kind <<<"$next_json")"
|
|
129
|
-
content="$(jq -r .content <<<"$next_json")"
|
|
130
|
-
|
|
131
|
-
if [[ "$kind" == "message" ]]; then
|
|
132
|
-
echo "your turn ($state)" # bin/gtd renders this via emit_gate instead
|
|
133
|
-
gtd next
|
|
134
|
-
exit 0
|
|
135
|
-
fi
|
|
136
|
-
|
|
137
|
-
if [[ "$kind" == "script" ]]; then
|
|
138
|
-
head_before="$(git rev-parse HEAD 2>/dev/null || echo none)"
|
|
139
|
-
bash -c "$content" || true # run the emitted script; exit code ignored
|
|
140
|
-
gtd step "$actor" >/dev/null # capture whatever it left in the tree
|
|
141
|
-
head_after="$(git rev-parse HEAD 2>/dev/null || echo none)"
|
|
142
|
-
if [[ "$head_before" == "$head_after" ]]; then
|
|
143
|
-
echo "settled ($state: check passed, nothing to do)" # bin/gtd renders this via emit_settled instead
|
|
144
|
-
exit 0
|
|
145
|
-
fi
|
|
146
|
-
continue
|
|
147
|
-
fi
|
|
148
|
-
|
|
149
|
-
# kind == "prompt": feed the prompt to the agent, then close out its turn.
|
|
150
|
-
claude -p "$content" --dangerously-skip-permissions
|
|
151
|
-
gtd step "$actor" >/dev/null
|
|
152
|
-
done
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
The driver — not the prompt text — owns ending the agent's turn
|
|
156
|
-
(`gtd step "$actor"` right after the agent acts): every default-workflow agent
|
|
157
|
-
prompt says explicitly not to run `gtd step agent` itself.
|
|
158
|
-
|
|
159
|
-
The loop's logfile lives at `"$(worktree_git_dir)/gtd-loop.log"` by default, or
|
|
160
|
-
`$GTD_LOOP_LOG` verbatim when set. `worktree_git_dir` derives the git dir from
|
|
161
|
-
the cwd with `GIT_DIR`/`GIT_WORK_TREE` scrubbed, so an inherited `GIT_DIR`
|
|
162
|
-
(leaked from a parent git process, a hook, or another worktree's shell) can
|
|
163
|
-
never key the log — or the loop's memory marker — to a different worktree; two
|
|
164
|
-
worktrees looping at once each keep their own log.
|
|
165
|
-
|
|
166
|
-
## Rendered output and logging
|
|
167
|
-
|
|
168
|
-
`bin/gtd` detects its output capability once at startup (`FANCY`, true when
|
|
169
|
-
stdout is a real terminal AND `NO_COLOR` is unset): on a real terminal it prints
|
|
170
|
-
one colored/emoji line per event, and under `NO_COLOR` or a piped/redirected
|
|
171
|
-
stdout it prints the same events as plain ASCII with no escape codes, per the
|
|
172
|
-
[NO_COLOR convention](https://no-color.org). Normal events (a state transition,
|
|
173
|
-
a captured commit, an agent/check turn starting, settling at idle) render to
|
|
174
|
-
stdout; refusals, stalls, and validate-cap halts render to stderr, each followed
|
|
175
|
-
by a `📄 see <log>` pointer (plain: `see <log>`).
|
|
176
|
-
|
|
177
|
-
The three previously terminal-visible subprocess streams — the agent turn's own
|
|
178
|
-
output, the check script's own output, and `gtd step`'s own output — are no
|
|
179
|
-
longer printed directly: they're appended to the per-repo/per-worktree log file
|
|
180
|
-
(truncated once at the start of the run) instead — the loop prints the log's
|
|
181
|
-
path as its first line, ready to `tail -f`.
|
|
182
|
-
|
|
183
|
-
## Agent memory scope
|
|
184
|
-
|
|
185
|
-
A `"prompt"` beat may carry an optional `memory` key — an opaque scope label the
|
|
186
|
-
workflow author chose (e.g. `plan`, `build`, `fix`). gtd never interprets it; it
|
|
187
|
-
signals to the driver whether this agent turn should continue from the previous
|
|
188
|
-
turn's memory or start fresh. The rule is a comparison, not a command: track the
|
|
189
|
-
`memory` of the last `"prompt"` beat (script/message beats have none and never
|
|
190
|
-
change it), and when a new `"prompt"` beat's `memory` **equals** the tracked
|
|
191
|
-
value, continue the same agent session; when it **differs** — or it is the first
|
|
192
|
-
turn, or either side has no `memory` — start fresh.
|
|
193
|
-
|
|
194
|
-
This is what makes a loop retain memory while a phase boundary clears it: a loop
|
|
195
|
-
that keeps re-entering one state emits the same label every lap, so the agent
|
|
196
|
-
accumulates context across the loop; the move to the next phase's
|
|
197
|
-
differently-labelled state resets it. `bin/gtd` implements this by mapping the
|
|
198
|
-
signal onto claude's session flags — `--session-id` to pin a fresh session when
|
|
199
|
-
the scope changes, `--resume` to continue it while the scope holds — and
|
|
200
|
-
persists the current scope + session id in the git dir (never the working tree,
|
|
201
|
-
so `gtd status` and the pending diff never see them) so retention survives even
|
|
202
|
-
across the restarts a human gate forces mid-loop. See `skills/loop/SKILL.md`'s
|
|
203
|
-
"Agent memory scope" for the same contract stated for a hand-driven loop.
|
|
204
|
-
|
|
205
|
-
## Using a different agent
|
|
206
|
-
|
|
207
|
-
`gtd` (bare, or `gtd loop`) defaults to
|
|
208
|
-
`claude -p "$GTD_LOOP_PROMPT" --dangerously-skip-permissions`, but the agent
|
|
209
|
-
invocation is swappable: set `GTD_LOOP_AGENT_CMD` to any shell command, and it
|
|
210
|
-
runs with the prompt available as `$GTD_LOOP_PROMPT` in its environment, along
|
|
211
|
-
with the resolved state's opaque `model` hint (if any) as `$GTD_LOOP_MODEL` and
|
|
212
|
-
its `memory` scope as `$GTD_LOOP_MEMORY` / `$GTD_LOOP_SESSION_ID` /
|
|
213
|
-
`$GTD_LOOP_MEMORY_RESUME` (`1` when this turn should continue the prior
|
|
214
|
-
session). An adapter that ignores any of these keeps working unchanged. For
|
|
215
|
-
example, to drive a different agent CLI:
|
|
216
|
-
|
|
217
|
-
```bash
|
|
218
|
-
GTD_LOOP_AGENT_CMD='my-agent-cli --prompt "$GTD_LOOP_PROMPT"' gtd
|
|
219
|
-
```
|
package/docs/upgrading.md
DELETED
|
@@ -1,293 +0,0 @@
|
|
|
1
|
-
# Upgrading to v3 (BREAKING CHANGE)
|
|
2
|
-
|
|
3
|
-
## v3 is a clean break
|
|
4
|
-
|
|
5
|
-
v3 ("the pattern machine") deletes the entire v2 definition model — gates, guard
|
|
6
|
-
functions, actor kinds, interrupt/fallback ladders, capture rules, turn and
|
|
7
|
-
routing rules, `Gtd-Counters` trailers, conflicts, the review checkout window —
|
|
8
|
-
and replaces it with the much smaller pattern machine described in
|
|
9
|
-
[STATES.md](../STATES.md): named states, each with one content kind and an
|
|
10
|
-
ordered map of change-patterns to next states.
|
|
11
|
-
|
|
12
|
-
**All pre-v3 history is unrecognized and resolves to the initial state, by
|
|
13
|
-
design — v1 and v2 subjects alike.** v3's `resolveState`
|
|
14
|
-
(`src/PatternMachine.ts`) only recognizes its own `gtd(<actor>): <from> → <to>`
|
|
15
|
-
subject (or the bare `gtd(<actor>): <to>`) naming a state and an actor the
|
|
16
|
-
active workflow currently declares; everything else — a v1 `gtd: grilling`, a v2
|
|
17
|
-
`gtd(agent): building`, a plain `chore: …` commit, anything from a foreign repo
|
|
18
|
-
— parses as unrecognized and lands at the workflow's initial state (`idle` in
|
|
19
|
-
the unified template). There is no special-casing for "this looks like an old
|
|
20
|
-
gtd commit": the mechanism is exactly the same one that already made v1 history
|
|
21
|
-
inert to v2, and v2 history inert to a differently-configured workflow — v3 just
|
|
22
|
-
applies it uniformly to all prior history instead of drawing a v1/v2 line.
|
|
23
|
-
|
|
24
|
-
**Finish or squash any in-flight cycle first.** A repo mid-cycle on v1 or v2 has
|
|
25
|
-
no v3-shaped commit backing its steering files; upgrading mid-cycle lands cold
|
|
26
|
-
at the initial state with those files still pending, which the new workflow's
|
|
27
|
-
`on` patterns will classify as ordinary pending changes, not as the phase they
|
|
28
|
-
used to represent. Finish the cycle (or squash it) on the old binary first, or
|
|
29
|
-
manually clean up the steering files and commit a plain boundary, before
|
|
30
|
-
upgrading.
|
|
31
|
-
|
|
32
|
-
## What died
|
|
33
|
-
|
|
34
|
-
- **Counters.** `Gtd-Counters` trailers, `fixAttemptCap`/`reviewThreshold`
|
|
35
|
-
config keys, and the counter-stamp machinery are gone. The only budget
|
|
36
|
-
affordance left is a state's own `retry: { max, otherwise }`
|
|
37
|
-
(§[STATES.md](../STATES.md#7-retry)) — a plain per-process entry cap
|
|
38
|
-
redirected at write time, with no trailer to read back.
|
|
39
|
-
- **`.gtdrc` keys.** The old `testCommand`, `fixAttemptCap`, `reviewThreshold`,
|
|
40
|
-
`agenticReview`, `squash`, `learning`, `decisionLog`, and `models` keys are
|
|
41
|
-
all gone — `workflow:` and `vars:` are the only two blessed keys (see
|
|
42
|
-
[Configuration](configuration.md)). A check's command now lives inline in its
|
|
43
|
-
own `script:` content, reading a workflow-declared `it.vars` entry (the
|
|
44
|
-
unified template's `testCommand`, overridable via the top-level `vars:` key or
|
|
45
|
-
a `GTD_TESTCOMMAND` environment variable — see
|
|
46
|
-
[Configuration's "Variables"](configuration.md#variables)) rather than a
|
|
47
|
-
blessed `testCommand` config key; squashing is a `commit:` state instead of a
|
|
48
|
-
boolean flag; there is no learning phase, no decision log, and no model
|
|
49
|
-
tiering — a workflow author is free to build any of that shape back with
|
|
50
|
-
states of their own, but gtd no longer bakes it in.
|
|
51
|
-
- **The review checkout window.** v2 rewound HEAD/index to the review base while
|
|
52
|
-
a human review rested, so editors would show the diff directly. Dropped in the
|
|
53
|
-
v3.0 rewrite, then RE-INTRODUCED as the explicit state property it was always
|
|
54
|
-
meant to become: a state declaring `reviewWindow: true` opens the window while
|
|
55
|
-
the machine rests there (base = the process start, or a state marked
|
|
56
|
-
`reviewBase: true`). The unified template enables it on `await-review`. Unlike
|
|
57
|
-
v2's hardwired gate, it is pure workflow DATA and the engine stays oblivious —
|
|
58
|
-
see [STATES.md §11](../STATES.md) and the `file:`/`mode:` neighbours in
|
|
59
|
-
[configuration.md](configuration.md).
|
|
60
|
-
- **`forceApprove`, content-inspection verdicts.** FEEDBACK.md emptiness,
|
|
61
|
-
checkbox-only REVIEW.md diffs, and doc-structure validation are gone as ENGINE
|
|
62
|
-
mechanisms — verdicts are now expressed purely by which file a turn writes or
|
|
63
|
-
deletes, matched by an ordinary `on` pattern. The unified template's own
|
|
64
|
-
checkbox-review verdict (`review-deciding`) is ordinary WORKFLOW data (an `on`
|
|
65
|
-
pattern), not an engine hook: `D REVIEW.md` = approve, `M REVIEW.md` = route
|
|
66
|
-
to the deterministic decider, in the unified template. Deterministic
|
|
67
|
-
format-checking of the steering files is likewise no longer a pair of
|
|
68
|
-
in-machine `check`/`script` states — the old `todo-validating`/
|
|
69
|
-
`review-validating` states and their `.gtd/FORMAT.md` steering file are gone,
|
|
70
|
-
replaced by the `gtd validate` command plus producing-agent self-validation
|
|
71
|
-
(see
|
|
72
|
-
[docs/design/steering-file-validation-command.md](design/steering-file-validation-command.md)
|
|
73
|
-
and [STATES.md §12](../STATES.md)).
|
|
74
|
-
- **`gtd review <target>`.** Gone in the initial v3 rewrite, then RETURNED in
|
|
75
|
-
this version with different, narrower semantics: it no longer inspects an
|
|
76
|
-
arbitrary target the way v2 did — it starts a brand NEW review process at a
|
|
77
|
-
workflow-declared `reviewEntry: true` state, reviewing `<target>..HEAD` (e.g.
|
|
78
|
-
a colleague's PR branch with no gtd process of its own) by writing one empty
|
|
79
|
-
entry commit with a `Gtd-Review-Base:` trailer and reusing the workflow's
|
|
80
|
-
existing review/feedback machinery unmodified — see
|
|
81
|
-
[STATES.md §11](../STATES.md#11-the-review-checkout-window) and
|
|
82
|
-
[CLI reference](cli.md#gtd-review-commitish---json). The current command
|
|
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.
|
|
91
|
-
|
|
92
|
-
- **`gtd format <file>`, and the bundled prettier with it.** gtd no longer
|
|
93
|
-
formats anything on its own: a steering-file mode declares its own `format:`
|
|
94
|
-
SHELL COMMAND, so a project brings whatever formatter it already uses. Adding
|
|
95
|
-
four lines to `.gtdrc` restores (and improves on) the old auto-formatting for
|
|
96
|
-
the unified template:
|
|
97
|
-
|
|
98
|
-
```yaml
|
|
99
|
-
modes:
|
|
100
|
-
qa:
|
|
101
|
-
format: "npx prettier --write <%= it.file %>"
|
|
102
|
-
review:
|
|
103
|
-
format: "npx prettier --write <%= it.file %>"
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
Validation is unaffected — `qa`/`review` remain gtd's own built-in validators.
|
|
107
|
-
See
|
|
108
|
-
[docs/design/pluggable-steering-modes.md](design/pluggable-steering-modes.md)
|
|
109
|
-
and [Configuration](configuration.md#modes--pluggable-steering-file-modes).
|
|
110
|
-
|
|
111
|
-
- **Model tiers, the decision log.** No `models` config key, no `Gtd-Decisions`
|
|
112
|
-
trailer scan, no grilling/architecting "prior decisions" context assembled
|
|
113
|
-
from history.
|
|
114
|
-
- **LSP.** Deleted at the v3 rewrite, then resurrected file-format-keyed rather
|
|
115
|
-
than state-keyed (`gtd lsp` — see
|
|
116
|
-
[Development](development.md#the-lsp-server)). The `gtd.openSteeringFile`
|
|
117
|
-
command is BACK (see
|
|
118
|
-
[docs/design/state-file-association.md](design/state-file-association.md)) —
|
|
119
|
-
config-driven rather than the v2 hardcoded state→file map: any state may
|
|
120
|
-
declare a `file:` (an Eta template) and a `mode:` (the file's format — the
|
|
121
|
-
built-in `qa`/`review`, or a workflow-declared `modes:` entry, though the LSP
|
|
122
|
-
itself only understands the two built-ins), the LSP reads that same config the
|
|
123
|
-
CLI does to build its path→mode dispatch, and `gtd.openSteeringFile` resolves
|
|
124
|
-
the current state and shows its `file:`. No mapping declared at all (the
|
|
125
|
-
bundled template's predecessor shape, or any workflow with no `file:`/`mode:`)
|
|
126
|
-
falls back to basename dispatch (`TODO.md`/`REVIEW.md`), same as before this
|
|
127
|
-
addition.
|
|
128
|
-
|
|
129
|
-
## How to adopt
|
|
130
|
-
|
|
131
|
-
`workflow:` is **optional** — gtd ships the bundled **unified** workflow as its
|
|
132
|
-
built-in default, so after upgrading a repo works with **no** config and no init
|
|
133
|
-
at all. The default forks on which steering file you create: `.gtd/TODO.md`
|
|
134
|
-
starts the **simple flow** (a `planning` ⇄ `plan-review` plan-iteration loop,
|
|
135
|
-
direct `building`, a `checking`/`fixing` loop), while `.gtd/REQUIREMENTS.md`
|
|
136
|
-
starts the **advanced flow** (two-phase grilling ⇄ grilling-answer /
|
|
137
|
-
architecting ⇄ architecting-answer / decompose / picking / per-package build
|
|
138
|
-
with an agentic spec-review gate). Both converge on the same `reviewing` →
|
|
139
|
-
`await-review` tail, and a full sign-off squashes the whole cycle into one
|
|
140
|
-
commit (see [STATES.md §10](../STATES.md#10-the-bundled-workflow-templates)).
|
|
141
|
-
|
|
142
|
-
`gtd init` is now optional and no longer writes a workflow — it seeds only a
|
|
143
|
-
minimal `.gtdrc.json` (a `testCommand` var and a Prettier formatting
|
|
144
|
-
suggestion). Run it if you want to tune those; otherwise nothing is required:
|
|
145
|
-
|
|
146
|
-
```bash
|
|
147
|
-
gtd init # optional — seeds testCommand + a formatting suggestion
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
A repo that customized v2's `workflow:` key (actors, gates, guard vocabulary,
|
|
151
|
-
ladders) needs to rewrite it from scratch in the v3 schema — see
|
|
152
|
-
[Configuration](configuration.md) for the schema and a complete worked example.
|
|
153
|
-
There is no automatic migration: the two vocabularies don't map field-for-field
|
|
154
|
-
(guards become patterns, capture rules become `on` targets, counters become
|
|
155
|
-
`retry`), so a v2 `workflow:` config is simply invalid v3 config and fails
|
|
156
|
-
loudly at load time rather than silently misinterpreting.
|
|
157
|
-
|
|
158
|
-
**Re-copy the loop skill.** If you vendor `skills/loop/` into a consuming repo
|
|
159
|
-
or agent harness, upgrading the `gtd` binary also means re-copying that skill
|
|
160
|
-
from this release.
|
|
161
|
-
|
|
162
|
-
## Unreleased: bundled prompts no longer name `.gtd/`; a new `stateDir` var
|
|
163
|
-
|
|
164
|
-
The bundled template's agent prompts used to open with a sentence naming `.gtd/`
|
|
165
|
-
as the directory the agent must never touch except its own owned file. That
|
|
166
|
-
framing broke down once a project repointed a steering-file var (e.g.
|
|
167
|
-
`reviewFile`) outside `.gtd/`: the prompt still pointed the agent at a directory
|
|
168
|
-
that no longer held the file it was writing. Every prompt's opener now states a
|
|
169
|
-
path-agnostic principle instead — "this workflow steers itself through its own
|
|
170
|
-
state files — treat them as its private scratchpad, never as project code or
|
|
171
|
-
documentation" — and names only the specific file(s) its `vars:` entry owns.
|
|
172
|
-
|
|
173
|
-
The check scripts also gained a new `vars.stateDir` (default `.gtd`) —
|
|
174
|
-
independent of the per-file vars — naming only where they keep their own
|
|
175
|
-
scratch/bookkeeping (the `.check-output` temp file, `review-deciding`'s
|
|
176
|
-
exclusion of gtd's other state files). Each script now also creates its target
|
|
177
|
-
file's own parent directory (`mkdir -p "$(dirname "$feedback")"`, etc.), so a
|
|
178
|
-
per-file var repointed outside `stateDir` (or to a nested path with no existing
|
|
179
|
-
parent) no longer fails to write.
|
|
180
|
-
|
|
181
|
-
**Fully backward-compatible: no config change is needed.** Both `stateDir` and
|
|
182
|
-
every per-file var keep their existing `.gtd/…` defaults, so an unconfigured
|
|
183
|
-
repo renders byte-identical scripts and behavior to before. A repo that already
|
|
184
|
-
relocated a steering-file var outside `.gtd/` (working around issue #128 for
|
|
185
|
-
`reviewFile`) can keep its override as-is; nothing further to change.
|
|
186
|
-
|
|
187
|
-
## The var-override env prefix is now `GTD_<UPPERCASE-name>`, not `GTD_VAR_<name>`
|
|
188
|
-
|
|
189
|
-
The environment-variable layer of `it.vars` (see
|
|
190
|
-
[Configuration's "Variables"](configuration.md#variables)) changed its naming
|
|
191
|
-
scheme: the exact-case `GTD_VAR_<name>` prefix is gone, replaced by
|
|
192
|
-
`GTD_<UPPERCASE-name>` matched case-insensitively against each name already
|
|
193
|
-
declared by the workflow's own `vars:` or the top-level `.gtdrc` `vars:` key.
|
|
194
|
-
`GTD_VAR_testCommand` becomes `GTD_TESTCOMMAND`; `GTD_VAR_plannerModel` becomes
|
|
195
|
-
`GTD_PLANNERMODEL`.
|
|
196
|
-
|
|
197
|
-
This is also a narrowing, not just a rename: because an uppercased env key can't
|
|
198
|
-
round-trip back to an arbitrary camelCase name, the environment can no longer
|
|
199
|
-
introduce a var no config layer declared — a `GTD_*` env var matching no
|
|
200
|
-
declared name is now silently ignored, where it previously appeared verbatim
|
|
201
|
-
under its post-prefix name.
|
|
202
|
-
|
|
203
|
-
## 7.4: `gtd-loop` is gone — bare `gtd` and `gtd loop` run it directly
|
|
204
|
-
|
|
205
|
-
`gtd-loop`, the standalone loop-driver binary installed alongside `gtd`, no
|
|
206
|
-
longer exists. Its body now lives in `bin/gtd` itself, which is a bash entry
|
|
207
|
-
script that dispatches on its first argument:
|
|
208
|
-
|
|
209
|
-
| behavior | before (≤ 7.3) | from 7.4 |
|
|
210
|
-
| ------------------------------------------------------ | ---------------------------------------------- | ----------------------------------------------- |
|
|
211
|
-
| `gtd-loop` | separate installed binary | removed |
|
|
212
|
-
| bare `gtd` (no subcommand) | usage error, prints help, exits 1 | runs the loop driver immediately |
|
|
213
|
-
| `gtd loop` | not a recognized subcommand | runs the loop driver immediately |
|
|
214
|
-
| any other subcommand (`step`, `status`, `validate`, …) | handled by `node dist/gtd.bundle.mjs` directly | same, now reached via `bin/gtd`'s hand-off/exec |
|
|
215
|
-
|
|
216
|
-
**Bare `gtd` is a behavior flip, not merely an addition** — the same kind of
|
|
217
|
-
change as the v1 → v2 label grammar below: previously it printed the help text
|
|
218
|
-
and exited 1 without touching the repository; from 7.4 it launches the loop
|
|
219
|
-
driver immediately, identically to `gtd loop`. A script that relied on bare
|
|
220
|
-
`gtd` failing fast (e.g. to detect a missing subcommand) now gets a running loop
|
|
221
|
-
instead of an error.
|
|
222
|
-
|
|
223
|
-
Because `bin/gtd` is a bash script for every invocation now, **running `gtd` at
|
|
224
|
-
all — including any subcommand — requires bash**. Before 7.4, only the loop
|
|
225
|
-
driver (`gtd-loop`) needed bash; a subcommand could be invoked directly via the
|
|
226
|
-
bundle (e.g. `node node_modules/.bin/gtd`) with no bash in between. This is
|
|
227
|
-
intentional and accepted, not an oversight: gtd's loop, its `script`-content
|
|
228
|
-
checks, and a steering-file mode's own `format:`/`validate:` commands already
|
|
229
|
-
run via `bash -c` (see
|
|
230
|
-
[Configuration](configuration.md#modes--pluggable-steering-file-modes)) — gtd
|
|
231
|
-
already assumes a unix/bash agent environment everywhere else, so the entry
|
|
232
|
-
point catching up removes an inconsistency rather than introducing a new
|
|
233
|
-
dependency.
|
|
234
|
-
|
|
235
|
-
**Migration:**
|
|
236
|
-
|
|
237
|
-
- Anyone invoking `gtd-loop` directly — in scripts, CI, or docs — should invoke
|
|
238
|
-
`gtd` or `gtd loop` instead.
|
|
239
|
-
- Anyone relying on `node node_modules/.bin/gtd` resolving straight to the
|
|
240
|
-
bundle should note `gtd` is now a bash script that execs the bundle for
|
|
241
|
-
subcommands, so behavior for actual subcommands (`gtd step`, `gtd status`,
|
|
242
|
-
etc.) is unchanged — only the underlying file changed.
|
|
243
|
-
|
|
244
|
-
## 7.2: the review checkout window is now per worktree
|
|
245
|
-
|
|
246
|
-
The review checkout window's two refs moved from the SHARED `refs/gtd/*`
|
|
247
|
-
namespace to git's PER-WORKTREE `refs/worktree/*` one:
|
|
248
|
-
|
|
249
|
-
| before (≤ 7.1) | from 7.2 |
|
|
250
|
-
| ---------------------- | ------------------------------- |
|
|
251
|
-
| `refs/gtd/review-head` | `refs/worktree/gtd/review-head` |
|
|
252
|
-
| `refs/gtd/review-base` | `refs/worktree/gtd/review-base` |
|
|
253
|
-
|
|
254
|
-
Nothing in a `.gtdrc` or a workflow definition references those names, so **no
|
|
255
|
-
config change is needed**. What changes is behaviour in a repository checked out
|
|
256
|
-
as several linked worktrees (`git worktree add`, one `.git` shared between
|
|
257
|
-
them): each worktree now has its own window, so a review resting in one no
|
|
258
|
-
longer "closes" from another — which, on the shared refs, mixed-reset that other
|
|
259
|
-
worktree's branch onto the reviewing worktree's saved head and left every
|
|
260
|
-
sibling refusing with "a process is already underway".
|
|
261
|
-
|
|
262
|
-
A window an OLDER gtd left open across the upgrade still closes from the legacy
|
|
263
|
-
refs, so an in-flight review in a single-worktree repo needs no attention. In a
|
|
264
|
-
multi-worktree repo gtd cannot tell which worktree a shared ref belongs to, so
|
|
265
|
-
it closes only when HEAD is contained in the saved head and otherwise refuses
|
|
266
|
-
with the exact recovery commands — run them in the worktree that owns the
|
|
267
|
-
window:
|
|
268
|
-
|
|
269
|
-
```bash
|
|
270
|
-
git reset --mixed refs/gtd/review-head
|
|
271
|
-
git update-ref -d refs/gtd/review-head
|
|
272
|
-
git update-ref -d refs/gtd/review-base
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
7.2 also adds [`gtd abandon`](cli.md#gtd-abandon---json), the supported way out
|
|
276
|
-
of a process that will never be finished (it closes the window, then rewinds
|
|
277
|
-
HEAD to the process's start parent, keeping everything the process produced as
|
|
278
|
-
uncommitted changes) — no hand-editing of refs required.
|
|
279
|
-
|
|
280
|
-
## Prior breaking change: the v1 → v2 label grammar (historical)
|
|
281
|
-
|
|
282
|
-
v2 replaced v1's undifferentiated turn labels with six labels carrying the
|
|
283
|
-
branch outcome at capture time, moved steering files under `.gtd/`, and
|
|
284
|
-
introduced `Gtd-Counters` commit trailers. All of that is itself now superseded
|
|
285
|
-
by v3's clean break above — a v1 or v2 repo upgrades to v3 the same way
|
|
286
|
-
(finish/squash any in-flight cycle, then upgrade from a settled boundary);
|
|
287
|
-
there's no reason to upgrade v1 → v2 → v3 in two hops.
|
|
288
|
-
|
|
289
|
-
For maintainers: this repo releases via `semantic-release` reading Conventional
|
|
290
|
-
Commits, and needs **no config change** for a major bump — but the release
|
|
291
|
-
commit/PR **must carry a `BREAKING CHANGE:` footer** (or a `!` after the type)
|
|
292
|
-
for `@semantic-release/commit-analyzer` to compute the next major version rather
|
|
293
|
-
than a minor/patch bump.
|