@pmelab/gtd 8.0.0 → 8.1.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.
@@ -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.