@pmelab/gtd 2.8.0 → 3.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.
package/docs/cli.md CHANGED
@@ -4,14 +4,19 @@
4
4
  Usage: gtd [command] [options]
5
5
 
6
6
  Commands:
7
- step Advance the workflow as the human actor (to fixpoint)
8
- step-agent Advance the workflow as the agent actor (to fixpoint)
9
- next Print the prompt for whichever actor is awaited (no mutation)
10
- status Predict the next commit and state from the working tree (no mutation)
11
- review <target> Anchor an ad-hoc human review against a git ref or branch
12
- questions List open questions from the active grilling/architecting doc
13
- changesets List changesets/files from the active review doc
7
+ step <actor> Authenticate as <actor>, match the resolved rest's
8
+ declared patterns against the pending changes, and commit
9
+ (or squash) the one resulting transition
10
+ next Print the resolved rest's rendered script/prompt/message
11
+ (no mutation)
12
+ run Execute the resolved rest's emitted script, then step its
13
+ actor (the built-in script driver)
14
+ status Print the resolved rest's state/actor and which declared
15
+ pattern (if any) each pending change matches (no mutation)
16
+ mermaid Print the active workflow's shape as Mermaid
17
+ stateDiagram-v2 source (no mutation)
14
18
  format <file> Format a markdown file in place
19
+ lsp Start the LSP server for .gtd/ steering files (stdio)
15
20
 
16
21
  Options:
17
22
  --json Output structured JSON instead of plain text
@@ -23,334 +28,270 @@ Options:
23
28
  repository-state work — they run outside a repo and in any repo state. Bare
24
29
  `gtd` (no subcommand) is a usage error: it prints the help text and exits 1
25
30
  without touching the repository. Every other command must be run from the
26
- **repository root** — gtd derives steering files, diffs, and pathspecs relative
27
- to cwd, so it refuses with a clear error if invoked from a subdirectory.
31
+ **repository root** — gtd derives the workflow, pending changes, and process
32
+ history relative to cwd, so it refuses with a clear error if invoked from a
33
+ subdirectory.
28
34
 
29
35
  `--json` is the only long option. Any other `--` option (including a typo like
30
36
  `--jsn`) is rejected with a usage error rather than silently ignored, so a
31
37
  mistyped flag can never degrade a JSON caller to plain-text mode.
32
38
 
33
- One nuance to "(no mutation)": `next` and `status` never author commits or
34
- change workflow state, but while a human review is pending they do maintain the
35
- review checkout window (closing it to read state, re-arming it on the way out —
36
- see [Human review gate](workflow.md#human-review-gate)), which transiently moves
37
- HEAD and the index. The working tree is never touched.
38
-
39
- ## `gtd step` / `gtd step-agent`
40
-
41
- Both drive the **same fixpoint loop** — gather → resolve → perform the returned
42
- edge action → repeat — differing only in which actor's turn they are allowed to
43
- capture:
44
-
45
- - **`gtd step`** captures the **human** turn at whichever gate is awaiting one.
46
- - **`gtd step-agent`** captures the **agent** turn.
47
-
48
- **Fixpoint advance.** A single invocation may author several commits: it authors
49
- the awaited actor's turn commit, then keeps performing any further mid-chain
50
- routing (a test run, a routing commit, a package close, …) until it reaches a
51
- rest where a prompt would be shown, or a fixpoint where nothing changed.
52
- `gtd step`/`gtd step-agent` never print a prompt themselves — that's
53
- `gtd next`'s job.
54
-
55
- **Idempotence.** Re-running the same command again once the tree is settled at a
56
- rest authors **zero** new commits. It exits 0 while the rest still awaits that
57
- command's actor (an inert empty agent turn, the idle health check); once the
58
- rest awaits the _other_ actor, the re-run is an out-of-turn refusal — still zero
59
- commits, but non-zero exit.
60
-
61
- **Out-of-turn refusal.** Human and agent turns are strictly separated: the wrong
62
- mutator always errors, at every state, on clean and dirty trees alike.
63
- `gtd step-agent` while a human turn is awaited refuses with
64
- `"<state> awaits a human turn — run \`gtd step\`"`; `gtd
65
- step`while an agent turn is awaited refuses with`"<state> awaits an agent turn —
66
- run \`gtd
67
- step-agent\`"`— exit non-zero, zero commits either way. Human edits made while the agent is awaited (e.g. amendment notes in`.gtd/`package files after the`gtd:
68
- planning` commit lands) stay pending in the working tree and ride along as input
69
- to the agent's next captured turn; left unamended, the build proceeds.
70
-
71
- **Red-test fixpoints exit 0.** A red test run below the fix-attempt cap (or the
72
- health-fix cap) still writes its findings and commits — it is a normal,
73
- successful step of the loop, not a failure of the `step`/`step-agent`
74
- invocation. `step`/`step-agent` only exit non-zero for a genuine refusal or an
75
- operational error (bad config, missing test binary, corrupted state).
76
-
77
- **Output.** Plain mode prints one `committed: <subject>` line per commit this
78
- invocation authored (oldest→newest), then a final `state: <state>` line:
39
+ ## `gtd step <actor>`
40
+
41
+ Authenticates `<actor>` against the resolved rest and performs the ONE resulting
42
+ transition. Unlike v2, there is no fixpoint chain to drive: the pattern
43
+ machine's `on` edges are direct one-hop transitions, so a single invocation
44
+ authors at most one commit (a normal turn) or performs one squash
45
+ (§[STATES.md](../STATES.md#8-the-squash-lifecycle)) — never both, never a chain
46
+ of several. A caller that wants several transitions issues several invocations.
47
+
48
+ - **Out-of-turn refusal** — `<actor>` isn't the resolved state's declared actor:
49
+ exit non-zero, zero commits.
50
+ `gtd step <actor>: out of turn — "<state>" awaits <awaited-actor>`
51
+ - **No-match refusal** — the tree is dirty and no declared `on` pattern matches:
52
+ exit non-zero, zero commits, naming every declared pattern.
53
+ `gtd step <actor>: no declared pattern matches the pending changes at "<state>" — declared patterns: <p1>, <p2>, …`
54
+ - **No-op** — the tree is clean and the state declares no `C` pattern: exit
55
+ **0**, zero commits. This is the default, silent case a loop driver relies on
56
+ (see [Driving the loop](loop.md)).
57
+ - **Commit/squash** — a pattern matched: exit 0, one commit (or one squash)
58
+ authored.
59
+
60
+ Plain-mode output is one line:
79
61
 
80
62
  ```
81
63
  committed: gtd(human): grilling
82
- committed: gtd: architecting
83
- state: architecting
84
64
  ```
85
65
 
86
- `--json` emits `{state, actions, commits}` instead (see
87
- [JSON schemas](#json-schemas)).
88
-
89
- ## `gtd next`
90
-
91
- Pure prompt emitter — it **never mutates** the repository. It reports whichever
92
- actor is currently awaited and, if the tree is at a genuine rest, the full
93
- prompt for that actor.
94
-
95
- **Purity.** No commits, no file writes, no test runs — `gtd next` only gathers
96
- and resolves.
97
-
98
- **Dirty-tree refusal.** If the working tree has pending changes outside the
99
- steering-file set, `gtd next` refuses rather than guess at a prompt for a state
100
- that hasn't been captured yet:
66
+ or, at a no-op:
101
67
 
102
68
  ```
103
- gtd next: working tree is dirty — run `gtd status` to inspect it, then advance with `gtd step` or `gtd step-agent` (whichever actor is awaited)
69
+ nothing to do at "idle"
104
70
  ```
105
71
 
106
- **Pending.** If HEAD is mid-chain — bookkeeping the next `step`/`step-agent`
107
- invocation would perform before reaching a rest — `gtd next` reports
108
- `pending: true` with no prompt. Mid-chain bookkeeping is invoker-agnostic, so
109
- either mutator resumes it; the report names the actor whose chain it is. In
110
- plain mode an agent-driven checkpoint prints `"mid-chain checkpoint — run \`gtd
111
- step-agent\` to continue, then run \`gtd next\`
112
- again"`, a human-driven one prints `"mid-chain checkpoint — run \`gtd step\` to
113
- continue"`.
72
+ `--json` emits `{state, subject}` — `subject` is `null` at a no-op:
114
73
 
115
- **Agent tail lines.** In plain-mode output, a prompt for the **agent** actor
116
- ends with the pinned tail:
117
-
118
- ```
119
- Finish your turn by running `gtd step-agent`. Then run `gtd next` and follow
120
- its output — repeat this cycle as long as the output is addressed to you (the
121
- agent); when it awaits the human, stop and hand off.
74
+ ```json
75
+ { "state": "grilling", "subject": "gtd(human): grilling" }
122
76
  ```
123
77
 
124
- The first sentence closes the current turn; the second closes the outer loop —
125
- it is what lets a plain-text agent chain multiple iterations (e.g. successive
126
- test/fix cycles) without an external driver, until a human gate is reached.
127
- Human-actor prompts carry no tail. `--json` output never embeds the tail into
128
- `prompt` either — the structured `actor` field (see
129
- [JSON schemas](#json-schemas) below) carries the same information: `"agent"`
130
- means another agent round, `"human"` means stop and hand off.
131
-
132
- ## `gtd status`
78
+ ## `gtd next [--json]`
133
79
 
134
- Pure, read-only **dry-run prediction** — the same gather+resolve `gtd next`
135
- runs, but reporting a prediction of the next turn rather than the actual prompt.
136
- Performs no git mutation, no test run, no file write — guaranteed side-effect
137
- free, including on a dirty tree.
80
+ Pure emitter of the resolved rest's rendered content — it **never mutates** the
81
+ repository (no commits, no file writes, no script execution). Resolves HEAD
82
+ exactly like `gtd step`, renders that state's declared
83
+ `script`/`prompt`/`message` template, and prints it. `kind` is never `"commit"`
84
+ here: resolution never rests at a commit state (entering one always ends the
85
+ process in the same step that entered it — see
86
+ [STATES.md §5](../STATES.md#5-resolution)).
138
87
 
139
- Prints four fields:
88
+ Plain mode prints the rendered content verbatim (with exactly one trailing
89
+ newline) — never the `model`/`file`/`mode` hints, which are JSON-only. `--json`
90
+ emits `{state, actor, kind, content, model?, file?, mode?}`:
140
91
 
141
- ```
142
- State: grilling
143
- Awaits: human
144
- Predicted commit: gtd(human): grilling
145
- Predicted state: grilling
92
+ ```json
93
+ {
94
+ "state": "building",
95
+ "actor": "agent",
96
+ "kind": "prompt",
97
+ "content": "You are an autonomous coding agent. ..."
98
+ }
146
99
  ```
147
100
 
148
- - **State** — the currently resolved state.
149
- - **Awaits** — the actor (`human` or `agent`) whose turn it is.
150
- - **Predicted commit** — the subject `step`/`step-agent` would author next, or
151
- `(none)` at a fixpoint (e.g. idle with nothing to do).
152
- - **Predicted state** — the state that commit would land in.
153
-
154
- `gtd status` takes no arguments — extra positional args are rejected.
101
+ - `state` — the resolved state.
102
+ - `actor` — the state's declared actor.
103
+ - `kind` — `"script"` | `"prompt"` | `"message"` — the dispatch key a driver
104
+ switches on (see [Driving the loop](loop.md)).
105
+ - `content` — the fully rendered template.
106
+ - `model` — the state's opaque `model:` hint, RENDERED through the same template
107
+ context as `content` (see
108
+ [Configuration](configuration.md#model--the-opaque-harness-hint-template-rendered)),
109
+ present only when the state declares one; **omitted entirely** (never `null`)
110
+ when unset.
111
+ - `file` — the state's declared steering file, RENDERED the same way; `mode` —
112
+ its format, verbatim (`"qa"` | `"review"`) (see
113
+ [Configuration](configuration.md#filemode--the-steering-file-association)).
114
+ Both present only when the state declares them; **omitted entirely** (never
115
+ `null`) otherwise.
116
+
117
+ ## `gtd run`
118
+
119
+ The built-in driver for a `script`-content rest — the **only** place gtd itself
120
+ spawns a subprocess. Renders the resolved rest exactly like `gtd next`, executes
121
+ its content verbatim via `bash -c` (foreground, inherited stdio, exit code
122
+ deliberately ignored — a check script encodes its outcome in the tree, e.g.
123
+ writing a findings file, never in its exit status), then runs `gtd step <actor>`
124
+ for that state's own actor to capture the outcome in one command. Refuses (exit
125
+ non-zero, no execution, no step) when the resolved rest isn't a script:
155
126
 
156
- ## `gtd review <target>`
157
-
158
- A pure mutator that **anchors, then exits** — it never prints a prompt itself.
159
- Use it to start an ad-hoc human review against an explicit git ref or branch,
160
- independent of the automatic review base the workflow otherwise computes.
161
-
162
- 1. Refuses on a dirty tree.
163
- 2. Resolves `<target>` via merge-base semantics and computes the diff HEAD adds
164
- over `merge-base(<target>, HEAD)`.
165
- 3. Refuses if that diff is empty after filtering ("nothing to review").
166
- 4. Authors exactly one commit: `gtd: reviewing <full-hash-of-the-base>`.
167
- 5. Prints a short confirmation pointing at `gtd next` — it does **not** print
168
- the review prompt itself.
169
-
170
- ```bash
171
- gtd review main
172
- # anchored review at <hash> — run `gtd next` to get the review prompt
173
- gtd next --json
174
- # {"actor":"agent", ...} — the review-record prompt scoped to that anchor
175
127
  ```
176
-
177
- Errors (all exit 1, message on stderr):
178
-
179
- - Missing target: `gtd review: missing target argument`
180
- - Extra arguments:
181
- `gtd review: too many arguments — expected one target, got: …`
182
- - Unresolvable ref: `gtd review: cannot resolve ref '<target>': <error message>`
183
- - Empty diff:
184
- `gtd review: nothing to review (<target> diff is empty after filtering)`
185
-
186
- ## `gtd questions` / `gtd changesets`
187
-
188
- Pure, read-only reporters — no dirty-tree check, no mutation — that parse the
189
- structured content out of the active grilling/architecting and review documents,
190
- for a future UI:
191
-
192
- - **`gtd questions`** reads whichever of `.gtd/TODO.md` / `.gtd/ARCHITECTURE.md`
193
- is present and reports its `## Open Questions` list (see
194
- [Structured grilling/architecting and review files](workflow.md#structured-grillingarchitecting-and-review-files)).
195
- Reports an empty list when neither file exists.
196
- - **`gtd changesets`** reads `.gtd/REVIEW.md`, if present, and reports its
197
- chunks/file-pointer list. Reports an empty list when the file doesn't exist.
198
-
199
- Both take no arguments and always exit 0 — a malformed file is reported via the
200
- `errors` field/lines rather than failing the command (the same diagnosis
201
- `gtd step-agent` would refuse the agent's next turn capture with).
202
-
203
- ```bash
204
- gtd questions --json
205
- # {"file":".gtd/TODO.md","questions":[{"question":"Which operations?","status":"suggested","text":"add and subtract."}],"errors":[]}
206
- gtd changesets --json
207
- # {"file":".gtd/REVIEW.md","shortHash":"abc1234","fullHash":"abc1234...","changesets":[...],"errors":[]}
128
+ gtd run: "<state>" awaits a <kind> from "<actor>" — nothing scripted to run
208
129
  ```
209
130
 
210
- ## `gtd format <file>`
211
-
212
- Formats a markdown file in place with a bundled prettier (`parser: "markdown"`,
213
- `printWidth: 80`, `proseWrap: "always"`), ignoring the host repo's own
214
- `.prettierrc` so `.gtd/TODO.md`/`.gtd/REVIEW.md` stay consistently formatted
215
- regardless of the host project's toolchain. Rejects `--json` (exit 1,
216
- `gtd format does not accept --json`) — it is a plain file operation, not a v2
217
- state command.
218
-
219
- Errors (all exit 1, message on stderr):
220
-
221
- - Missing path: `gtd format: missing file path argument`
222
- - Extra arguments: `gtd format: too many arguments — expected one path, got: …`
223
- - Non-markdown file:
224
- `gtd format: <file> is not a markdown file (expected .md or .markdown)`
225
- - File not found: `gtd: skipped formatting <file>: not found`
226
-
227
- ## JSON schemas
131
+ Reports the same `{state, subject}` (or `committed: …` / `nothing to do at "…"`)
132
+ as `gtd step` for the capturing step it performs. Takes no arguments — extra
133
+ positional args are rejected.
228
134
 
229
- Pass `--json` to `step`, `step-agent`, `next`, `status`, `review`, `questions`,
230
- or `changesets` for machine-readable single-line JSON output instead of plain
231
- text.
135
+ ## `gtd status [--json]`
232
136
 
233
- **`step` / `step-agent`** — `{state, actions, commits}`:
137
+ Pure, read-only dry-run reporter — the same resolution `gtd next` performs, but
138
+ reporting the resolved state/actor and, for every pending change, which declared
139
+ `on` pattern (if any) matches it — no mutation, and no CONTENT rendering (the
140
+ `script`/`prompt`/`message`/`commit` template is never rendered here, unlike
141
+ `gtd next`). It DOES render the resolved state's `model:`/`file:` hints (if
142
+ declared) through the same `it.vars`-carrying template context `gtd next` uses —
143
+ see
144
+ [Configuration](configuration.md#model--the-opaque-harness-hint-template-rendered)
145
+ — so a templated `model:`/`file:` failing to render fails `gtd status` too,
146
+ exactly like it would fail `gtd next`.
234
147
 
235
- ```json
236
- {
237
- "state": "architecting",
238
- "actions": ["capture the human turn as \"gtd(human): grilling\""],
239
- "commits": ["gtd(human): grilling", "gtd: architecting"]
240
- }
148
+ ```
149
+ State: working
150
+ Awaits: agent
151
+ Pending:
152
+ A DONE.md -> A DONE.md
153
+ A scratch.txt -> (no match)
241
154
  ```
242
155
 
243
- - `state` — the final resolved state after the fixpoint loop settled.
244
- - `actions` — human-readable descriptions of every edge action this invocation
245
- performed, oldest→newest.
246
- - `commits` — every commit subject this invocation authored, oldest→newest.
156
+ or, on a clean tree: `Pending: (clean)`. A `Model: <value>` line appears right
157
+ after `Awaits:` when the resolved state declares a `model:` hint, and
158
+ `File: <value>`/`Mode: <value>` lines appear after that (in that order) when
159
+ declared — each independently, only when set.
247
160
 
248
- **`next`** — `{state, actor, pending, prompt}`:
161
+ `--json` emits
162
+ `{state, actor, changes: [{status, path, pattern}], model?, file?, mode?}` —
163
+ `pattern` is `null` when no declared row matches that change; `model`/`file`/
164
+ `mode` are present only when the resolved state declares them (omitted entirely,
165
+ never `null`, otherwise):
249
166
 
250
167
  ```json
251
168
  {
252
- "state": "building",
169
+ "state": "working",
253
170
  "actor": "agent",
254
- "pending": false,
255
- "prompt": "..."
171
+ "changes": [
172
+ { "status": "A", "path": "DONE.md", "pattern": "A DONE.md" },
173
+ { "status": "A", "path": "scratch.txt", "pattern": null }
174
+ ]
256
175
  }
257
176
  ```
258
177
 
259
- - `state` — the resolved state.
260
- - `actor` — `"human"` or `"agent"`: who owns the next move. This is the single
261
- loop-driver signal: `"agent"` means proceed with another round — act on
262
- `prompt` when present, then run `gtd step-agent`; at an agent-driven pending
263
- checkpoint (`prompt` is `null`, nothing to act on) just run `gtd step-agent`.
264
- `"human"` means halt and hand off (a human rest, whose prompt body already
265
- tells the human what to do, or a human-driven pending checkpoint resumed by
266
- `gtd step`).
267
- - `pending` — `true` at a mid-chain HEAD (no prompt yet — resume with a mutator
268
- first); `false` at a genuine rest.
269
- - `prompt` — the full prompt markdown when `pending` is `false`, else `null`.
270
-
271
- **`status`** — `{state, actor, predictedCommit, predictedState}`:
272
-
273
- ```json
274
- {
275
- "state": "grilling",
276
- "actor": "human",
277
- "predictedCommit": "gtd(human): grilling",
278
- "predictedState": "grilling"
279
- }
280
- ```
178
+ `gtd status` takes no arguments — extra positional args are rejected.
281
179
 
282
- `predictedCommit` is `null` when the next invocation would author nothing (e.g.
283
- idle with a green health check).
180
+ ## `gtd mermaid`
284
181
 
285
- **`questions`** — `{file, questions, errors}`:
182
+ Pure emitter of the active workflow's **shape** — not the resolved rest — as
183
+ Mermaid [`stateDiagram-v2`](https://mermaid.js.org/syntax/stateDiagram.html)
184
+ source (see `src/Mermaid.ts`): one node per declared state, the `[*] -->`
185
+ initial-state marker, one edge per declared `on` row labeled with its raw
186
+ pattern string (same declaration order the engine itself evaluates), a `--> [*]`
187
+ edge for every commit state (final, no outgoing edges — see
188
+ [STATES.md §8](../STATES.md#8-the-squash-lifecycle)), and one `note right of`
189
+ per rest naming its actor, content kind, and retry cap (e.g.
190
+ `agent · prompt · retry 3→escalate`). No git, no HEAD resolution, no template
191
+ rendering — purely a function of the compiled `WorkflowDefinition`, so its
192
+ output is identical regardless of the current process/branch state.
286
193
 
287
- ```json
288
- {
289
- "file": ".gtd/TODO.md",
290
- "questions": [
291
- {
292
- "question": "Which operations?",
293
- "status": "suggested",
294
- "text": "add and subtract."
295
- }
296
- ],
297
- "errors": []
298
- }
194
+ ```
195
+ $ gtd mermaid
196
+ stateDiagram-v2
197
+ state "idle" as idle
198
+ state "grilling" as grilling
199
+ ...
200
+ [*] --> idle
201
+ idle --> grilling : * **
202
+ ...
203
+ note right of idle : human · message
204
+ ...
299
205
  ```
300
206
 
301
- `file` is `null` when neither `.gtd/TODO.md` nor `.gtd/ARCHITECTURE.md` is
302
- present. `errors` lists any structural problems in the file — the same diagnosis
303
- that would make `gtd step-agent` refuse the agent's next turn capture.
207
+ Pipe it straight into a `.md`/`.mmd` file, a GitHub issue/PR description, or any
208
+ Mermaid-aware renderer (GitHub, GitLab, VS Code, Obsidian, the
209
+ [Mermaid Live Editor](https://mermaid.live)) to get a diagram of a custom
210
+ `.gtdrc` `workflow:` with no hand-maintained docs required.
304
211
 
305
- **`changesets`** — `{file, shortHash, fullHash, changesets, errors}`:
212
+ State names are aliased to Mermaid-safe identifiers (non-word characters fold to
213
+ `_`; a digit-led name gets an `s_` prefix) via a `state "<name>" as <alias>`
214
+ declaration up front, so a hyphenated name like `todo-validating` still displays
215
+ with its exact declared spelling. Rejects `--json` (exit 1,
216
+ `gtd mermaid does not accept --json`) — there is no structured shape to emit
217
+ beyond the Mermaid source itself — and takes no arguments (extra positional args
218
+ are rejected).
306
219
 
307
- ```json
308
- {
309
- "file": ".gtd/REVIEW.md",
310
- "shortHash": "abc1234",
311
- "fullHash": "abc1234def5678901234567890123456789abcd",
312
- "changesets": [
313
- {
314
- "title": "Add calculator",
315
- "description": "New add function for the calculator.",
316
- "files": [{ "path": "./src/calc.ts", "line": 1, "checked": false }]
317
- }
318
- ],
319
- "errors": []
320
- }
321
- ```
220
+ ## `gtd format <file>`
221
+
222
+ Formats a markdown file in place with a bundled prettier (`parser: "markdown"`,
223
+ `printWidth: 80`, `proseWrap: "always"`), ignoring the host repo's own
224
+ `.prettierrc` so `.gtd/`-tracked files stay consistently formatted regardless of
225
+ the host project's toolchain. Rejects `--json` (exit 1,
226
+ `gtd format does not accept --json`) — it's a plain file operation, not a state
227
+ command.
228
+
229
+ Errors (all exit 1, message on stderr):
322
230
 
323
- `file` is `null` when `.gtd/REVIEW.md` doesn't exist. `errors` mirrors
324
- `questions`' field above.
231
+ - Missing path: `gtd format: missing file path argument`
232
+ - Extra arguments: `gtd format: too many arguments — expected one path, got: …`
233
+ - Non-markdown file:
234
+ `gtd format: <file> is not a markdown file (expected .md or .markdown)`
235
+ - File not found: `gtd: skipped formatting <file>: not found`
325
236
 
326
- **Error envelope** — every command, in `--json` mode, reports failures inside
327
- the JSON object rather than as unstructured text, and still exits 1:
237
+ ## `gtd lsp`
238
+
239
+ Starts an LSP server over stdio for `.gtd/` steering files — document symbols
240
+ for a `qa`-mode file's open questions and a `review`-mode file's review
241
+ chunks/hunks, code actions to check/uncheck a hunk or a whole chunk, and
242
+ diagnostics publishing the same parser findings the bundled workflow's
243
+ `.gtd/FORMAT.md` validators produce (see `src/OpenQuestions.ts` /
244
+ `src/ReviewDoc.ts` and
245
+ [STATES.md §10](../STATES.md#10-the-bundled-default-workflow)).
246
+
247
+ **Config-driven** (see
248
+ [docs/design/state-file-association.md](design/state-file-association.md)): the
249
+ server locates the active gtd config the same way the CLI does, from the
250
+ `initialize` request's workspace root (falling back to the open document's own
251
+ directory), renders every state's `file:` into an absolute-path → `mode` map,
252
+ and dispatches on it — first declaring state wins a path conflict. A path the
253
+ map doesn't cover (or no config at all) falls back to the basename dispatch
254
+ (`TODO.md` → `qa`, `REVIEW.md` → `review`), so the server still works standalone
255
+ with no `.gtdrc` in sight. Also registers an `executeCommand`,
256
+ `gtd.openSteeringFile`: resolves the current state exactly like `gtd status`
257
+ (config + git HEAD) and asks the client to show its `file:`
258
+ (`window/showDocument`); a state with no `file:` gets an informational message
259
+ naming the state instead — bind it to an editor keybinding for a "jump to the
260
+ active steering file" command.
261
+
262
+ Dispatched before the repository-root guard and auto-init, like `gtd format`.
263
+ Rejects `--json` (exit 1, `gtd lsp does not accept --json`) and extra positional
264
+ arguments — it's a long-running server, not a state command. Runs until the
265
+ client disconnects (the LSP `exit` notification), then exits cleanly.
266
+
267
+ ## Error envelope
268
+
269
+ Every command, in `--json` mode, reports a failure as a machine-readable
270
+ envelope on **stdout**, and still exits 1:
328
271
 
329
272
  ```json
330
273
  { "state": "error", "prompt": "<message>" }
331
274
  ```
332
275
 
333
- There is no auto-advance flag anywhere in the wire format — `actor` replaces it.
334
- The caller decides whether to keep looping based on `actor` (halt on `"human"`)
335
- and `pending` (re-run `step`/`step-agent` first when `true`), not on a boolean
336
- auto-advance flag.
276
+ A human-readable `gtd: <message>` line is still written to **stderr** regardless
277
+ of `--json` — the envelope adds a structured stdout channel, it does not replace
278
+ the plain-text one.
337
279
 
338
280
  ## Repository requirements
339
281
 
340
- - **Single writer, linear branch.** State is folded from **first-parent**
341
- history only. A merge commit at HEAD is unsupported (documented, not handled)
342
- — it degrades gracefully on the default branch rather than crashing, but do
343
- not rely on merge commits mid-cycle.
282
+ - **Single writer, linear branch.** A process's history is walked via
283
+ **first-parent** commits only.
344
284
  - **Test/build artifacts must be gitignored.** This is **load-bearing**, not a
345
- style preference: every fixpoint hop in `gtd step`/`gtd step-agent` detects
346
- "clean" via `git status --porcelain`, which silently omits anything matched by
347
- `.gitignore`. If your `testCommand` (or the build it triggers) writes
348
- tracked-but-untracked output — a `dist/`, a coverage report, a log file — into
349
- the working tree, the tree never goes clean after a green test run, and the
350
- fixpoint loop cannot converge: it will either loop forever re-detecting a
351
- "dirty" boundary or misclassify build output as the human's next feature
352
- capture. Gitignore every path your test/build toolchain writes before wiring
353
- gtd into a repo.
354
- - **Repository root invocation.** Every subcommand except `--help`/`--version`
355
- must run from the git repository root — steering files and diffs are resolved
356
- against the process cwd.
285
+ style preference: every step decision detects "clean" via
286
+ `git diff --name-status HEAD` (tracked changes) unioned with
287
+ `git ls-files --others --exclude-standard` (untracked files), which silently
288
+ omits anything matched by `.gitignore`. If a `script` state's command (or the
289
+ build it triggers) writes tracked-but-untracked output — a `dist/`, a coverage
290
+ report, a log file — into the working tree, the tree never goes clean after a
291
+ green run, and the check's `"C"` pattern never fires. Gitignore every path
292
+ your scripts write before wiring gtd into a repo.
293
+ - **Repository root invocation.** Every state subcommand (`step`/`next`/`run`/
294
+ `status`/`mermaid`) must run from the git repository root — the workflow,
295
+ pending changes, and process history are resolved against the process cwd.
296
+ `--help`/`--version`, `format`, and `lsp` skip this guard entirely (and any
297
+ git/`.gtdrc` dependency along with it).