@pmelab/gtd 3.4.0 → 3.6.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.
@@ -0,0 +1,386 @@
1
+ # Replacing the steering-file validation _states_ with a `gtd validate` command
2
+
3
+ > Status: IMPLEMENTED. The authoritative reference is now
4
+ > [STATES.md §12](../../STATES.md) (the model) and §10 (the 10-state bundled
5
+ > default); this doc is the design record. Supersedes the two validation-loop
6
+ > states in [steering-file-loops.md](steering-file-loops.md) §2–§4
7
+ > (`todo-validating`, `review-validating`); everything else that doc describes
8
+ > (the file formats §1, the LSP resurrection §5) stays.
9
+ >
10
+ > **As built — two decisions differ from the first draft of this plan:**
11
+ >
12
+ > 1. **No new state property.** A state is treated as "must hand over a valid
13
+ > file" purely by declaring both `file:` and `mode:` — there is no
14
+ > `validate:` flag and no `PatternMachine`/`PatternConfig`/`schema.json`
15
+ > change. This is the "Alternative considered" from §2.3, chosen for zero
16
+ > engine surface.
17
+ > 2. **`gtd next --json` is unchanged.** It emits no new field (it already
18
+ > carries `file`/`mode`); the driver simply runs `gtd validate` after every
19
+ > agent turn (a no-op, exit 0, when the state has nothing to validate). Only
20
+ > plain `gtd next` changed — it appends the self-validation instruction to a
21
+ > `prompt` rest that declares `file:`+`mode:`.
22
+ >
23
+ > **Follow-up refinement (also built):**
24
+ >
25
+ > 3. **`gtd validate` also formats**, and the check runs after a human edits
26
+ > too. `gtd validate` now formats the steering file in place (the
27
+ > `gtd format` markdown formatter) before parsing it. And `gtd step` runs the
28
+ > SAME format-and-validate as a capture gate (`enforceSteeringGate` in
29
+ > `src/program.ts`): committing a turn out of a state that declares
30
+ > `file:`+`mode:` formats+validates that file and REFUSES the step when it is
31
+ > invalid. That makes the check apply to a human's edit at a gate
32
+ > (`grilling-answer`, `await-review`) exactly as to an agent's draft, and a
33
+ > malformed steering file is never committed. An absent file (a deletion —
34
+ > `building`'s TODO.md, an `await-review` delete-to-approve) is a no-op, so
35
+ > those flows still pass. `gtd validate`'s earlier "missing file read as
36
+ > empty" behavior is dropped in favor of "absent → nothing to validate".
37
+ >
38
+ > The sections below are the original plan; read §2.1–§3.2 through these
39
+ > decisions (esp.: validate formats and is enforced at `gtd step`, and an absent
40
+ > file is a no-op rather than parsed-as-empty).
41
+
42
+ ## 1. Motivation
43
+
44
+ The bundled default workflow (`src/workflows/default.yaml`) currently carries
45
+ two dedicated `check` states whose only job is to parse a steering file and
46
+ bounce a malformed draft back to its authoring agent via a `.gtd/FORMAT.md`
47
+ findings file:
48
+
49
+ - `todo-validating` — validates `.gtd/TODO.md`'s open-questions format after
50
+ `grilling`, before the human answers at `grilling-answer`.
51
+ - `review-validating` — validates `.gtd/REVIEW.md`'s checkbox format after
52
+ `reviewing`, before the human reviews at `await-review`.
53
+
54
+ This is noise in the state machine:
55
+
56
+ 1. **2 of 12 states** exist only to re-derive a format check.
57
+ 2. **A hand-synced dual implementation.** Each validator is a `grep/awk` port of
58
+ `src/OpenQuestions.ts` / `src/ReviewDoc.ts` — an explicit "executable spec ↔
59
+ bash validator" contract kept in sync _by hand_, with no shared code path. A
60
+ whole class of drift bugs.
61
+ 3. **A third steering file** (`.gtd/FORMAT.md`) with its own lifecycle and
62
+ hygiene rules, plus "never touch FORMAT.md" / "fix FORMAT.md first" clauses
63
+ bolted onto the `grilling` and `reviewing` prompts.
64
+ 4. **Two extra commits on every happy-path cycle** (`gtd(check): …` turns that
65
+ only say "the format was fine").
66
+
67
+ The core realization: the validation is redundant with tooling we already have.
68
+ `src/OpenQuestions.ts` / `src/ReviewDoc.ts` are pure parsers, and `src/Lsp.ts`
69
+ already publishes their `errors` as live editor diagnostics. What was missing
70
+ was a way for the **producing agent** to run the same canonical check itself,
71
+ mid-turn, so the human is only ever handed a _validated_ file.
72
+
73
+ ## 2. The design
74
+
75
+ Validation stops being a **state** and becomes a **tool the producing agent uses
76
+ before it finishes its turn** (or that the driver runs on the agent's behalf).
77
+ No bash port, no `.gtd/FORMAT.md`, no extra states. The pure engine
78
+ (`src/PatternMachine.ts`) never validates a steering file — as today, that stays
79
+ out of scope for the engine (steering-file-loops.md §6). The only engine-shaped
80
+ addition is one declarative state property that marks _which_ states must hand
81
+ over a valid file (see §2.3).
82
+
83
+ Three parts:
84
+
85
+ ### 2.1 A `gtd validate` command (current-state-scoped, canonical parsers)
86
+
87
+ A new subcommand that resolves the current state exactly like `gtd status`,
88
+ renders that state's `file:`, reads its working-tree contents, and runs the
89
+ parser its `mode:` selects:
90
+
91
+ - `mode: qa` → `parseOpenQuestions(content).errors`
92
+ - `mode: review` → `parseReviewDoc(content).errors`
93
+
94
+ Behavior:
95
+
96
+ - **Nothing to validate** — the resolved state declares no `file:` **or** no
97
+ `mode:` → exit 0, report "nothing to validate at \"\<state\>\"".
98
+ - **Valid** — the parser returns no `errors` → exit 0, report "\<file\>: valid".
99
+ - **Invalid** — the parser returns `errors` → **fail the Effect** with the
100
+ findings (one per line, each prefixed with the rendered file path), which
101
+ makes `main.ts` print `gtd: …` to stderr and exit non-zero. This non-zero exit
102
+ is the whole point: the producing agent (or the driver) loops
103
+ `write → gtd validate → fix` until it exits 0.
104
+ - A **missing** `file:` on disk is read as empty content (catch the read error,
105
+ treat as `""`), NOT as a hard error. For `qa` an empty/absent file is
106
+ trivially valid (no `## Open Questions` section); for `review` an empty file
107
+ fails the parser (no header, no chunks) — both are the correct outcomes.
108
+ Keeping validate purely a function of the parser over the file's content is
109
+ the crisp contract: **`gtd validate` fails ⟺ the file exists and violates its
110
+ format.**
111
+
112
+ `--json` shape: on success
113
+ `{ "state", "file"?, "mode"?, "valid": true, "errors": [] }`; on violations, the
114
+ existing error envelope (`{ "state": "error", "prompt": "<findings>" }`) via
115
+ `makeProgram`'s `--json` `catchAll` — consistent with how step refusals surface
116
+ under `--json`.
117
+
118
+ `gtd validate` takes no positional args (`rejectExtraArgs("validate", argv)`),
119
+ accepts `--json`, and — like every state command — goes through the repo-root
120
+ guard, auto-init, and the review-window close-first/re-arm-last bracketing.
121
+
122
+ ### 2.2 Conditional self-validation instruction on `gtd next` (THE key rule)
123
+
124
+ The instruction that tells the agent to validate its own output is **not** a
125
+ static line in the YAML prompt. gtd appends it **only in plain (non-JSON)
126
+ `gtd next` output**; under `--json` it is withheld and the _driver_ owns the
127
+ validate-and-retry loop — mirroring how the driver already owns turn
128
+ orchestration.
129
+
130
+ - **`gtd next` (plain text):** when the resolved rest is a state that must hand
131
+ over a valid file (see §2.3), append a standard block to the rendered prompt,
132
+ referencing the rendered `file:`, e.g.:
133
+
134
+ ```
135
+ <rendered prompt…>
136
+
137
+ Before finishing your turn, run `gtd validate` and fix every violation it
138
+ reports in <file> until it exits cleanly. Do not finish while it still
139
+ reports violations.
140
+ ```
141
+
142
+ This is for a human (or a simple driver) who reads `gtd next` and hands the
143
+ text to an agent: the agent self-validates.
144
+
145
+ - **`gtd next --json`:** do **NOT** append the block. The `content` field stays
146
+ the clean rendered prompt. Instead emit the marker field `"validate": true`
147
+ (omitted when false — same discipline as `model`/`memory`/`file`/`mode`). The
148
+ driver reads this and, after the agent's turn but **before** `gtd step`, runs
149
+ `gtd validate`; on a non-zero exit it re-invokes the agent with the findings
150
+ and loops until `gtd validate` passes (subject to a cap — §2.5), then steps.
151
+ This is the JSON-mode equivalent of the appended self-instruction.
152
+
153
+ The appended text is gtd-owned (a constant in `program.ts`); it references the
154
+ already-rendered `rest.file`. It is emitted only by `runNextCommand`'s
155
+ plain-text branch — `gtd run` drives _script_ states (the `check` actor), never
156
+ prompt states, so it is unaffected.
157
+
158
+ ### 2.3 A `validate: true` state property (the marker)
159
+
160
+ Add one optional boolean state property, `validate: true`, meaning: _the actor
161
+ at this state must hand over a `file:` that passes `gtd validate` (per its
162
+ `mode:`) before the turn is considered done._ Set it on `grilling` and
163
+ `reviewing` in the bundled default.
164
+
165
+ Why a dedicated property rather than keying off `mode:` presence: `building`
166
+ also declares `mode: qa` but its job is to **delete** `.gtd/TODO.md`, not
167
+ produce it — keying self-validation on `mode:` would wrongly target `building`.
168
+ `validate: true` names exactly the producing states.
169
+
170
+ Semantics / validation (in `validateDefinition`):
171
+
172
+ - `validate: true` **requires** both `file:` and `mode:` on the same state
173
+ (there is nothing to validate without a file and a format).
174
+ - **Forbidden on a commit state** (never at rest — same rule as
175
+ `reviewWindow`/`reviewBase`).
176
+ - Engine never branches on it at step time; it is edge/emission-only data, read
177
+ by `renderRest`/`gtd next` and surfaced in `--json`.
178
+
179
+ > **Alternative considered (no new property, lower engine risk):** skip
180
+ > `validate: true`; in plain `gtd next` append the block to any `prompt` state
181
+ > declaring `file:`+`mode:` (accepting a harmless no-op instruction on
182
+ > `building`, whose deleted TODO validates as empty-valid), and have the driver
183
+ > run `gtd validate` after _every_ agent turn (a no-op exit 0 whenever there is
184
+ > nothing to validate). This removes all `PatternMachine`/`PatternConfig`/schema
185
+ > changes. It is viable but less self-documenting (no `validate:true` in
186
+ > `--json` or `gtd status`, and the `building` prompt carries an odd line).
187
+ > **Recommended: the explicit property.** If the implementer prefers to avoid
188
+ > engine changes, the alternative is acceptable — pick one and keep it
189
+ > consistent across code, docs, and tests.
190
+
191
+ ## 3. Concrete change list
192
+
193
+ ### 3.1 New command — `src/program.ts`
194
+
195
+ - Import `parseOpenQuestions` (`./OpenQuestions.js`) and `parseReviewDoc`
196
+ (`./ReviewDoc.js`); `WorktreeReader` is already imported.
197
+ - Add `runValidateCommand(argv, json, write)`:
198
+ - `rejectExtraArgs("validate", argv)`.
199
+ - `git = yield* GitService`; `worktree = yield* WorktreeReader`.
200
+ - `{ rest, context } = yield* resolveRestContext(git)` (reuse the existing
201
+ helper — it builds the same context `gtd status`/`gtd next` use).
202
+ - `file = yield* renderFile(rest.stateDef, context)`;
203
+ `mode = rest.stateDef.mode`.
204
+ - No `file`/no `mode` → success "nothing to validate".
205
+ - Read `file` via `worktree.read`, catching a missing-file error into `""`.
206
+ - `errors = mode === "qa" ? parseOpenQuestions(content).errors : parseReviewDoc(content).errors`.
207
+ - Empty `errors` → success ("\<file\>: valid" / JSON `valid:true`).
208
+ - Non-empty →
209
+ `Effect.fail(new Error(<file> + " is not valid:\n" + errors.map(e => " - " + e).join("\n")))`.
210
+ - Add `"validate"` to `KNOWN_SUBCOMMANDS` and a `case "validate":` in
211
+ `dispatchKnownSubcommand`.
212
+ - Add the appended-instruction constant and wire it into `runNextCommand`'s
213
+ **plain** branch only (append when `rendered` is a prompt state with the
214
+ `validate` marker and a defined `rendered.file`); in the `--json` branch add
215
+ `...(rendered.validate ? { validate: true } : {})` to the emitted object and
216
+ do NOT touch `content`.
217
+ - Add the `validate` line to `HELP_TEXT`.
218
+
219
+ Exit-code note: failing the Effect is the mechanism for non-zero exit (see
220
+ `main.ts` `catchAll`). Findings land on stderr in plain mode, and under `prompt`
221
+ in the `--json` error envelope.
222
+
223
+ ### 3.2 New state property — engine + compiler + emission
224
+
225
+ - `src/PatternMachine.ts`: add `validate?: boolean` to `StateDef`; in
226
+ `validateDefinition` add the two rules from §2.3 (requires `file`+`mode`;
227
+ forbidden on commit states).
228
+ - `src/PatternConfig.ts`: compile the `validate:` key (boolean; reject
229
+ non-boolean) in the per-state field compilers.
230
+ - `src/Edge.ts`: add `validate?: boolean` to `RenderedRest`; set it in
231
+ `renderRest` (`...(rest.stateDef.validate ? { validate: true } : {})`).
232
+ - `src/program.ts`: emit `validate` in `gtd next --json` (see §3.1) and,
233
+ optionally, in `gtd status --json` + a `Validate: yes` plain line for symmetry
234
+ with `model`/`memory`/`file`/`mode`.
235
+ - `schema.json`: add the `validate` boolean to the per-state schema.
236
+
237
+ ### 3.3 `src/workflows/default.yaml`
238
+
239
+ - **Delete** the `todo-validating` and `review-validating` states.
240
+ - **Rewire**: `grilling.on: "* **": grilling-answer`;
241
+ `reviewing.on: "* **": await-review`.
242
+ - **Mark**: add `validate: true` to `grilling` and `reviewing` (they already
243
+ declare `file:`+`mode:`).
244
+ - **Prompts**: from `grilling` and `reviewing`, remove the `.gtd/FORMAT.md`
245
+ clauses ("If `.gtd/FORMAT.md` exists…", "Never touch `.gtd/FORMAT.md`…"). Do
246
+ NOT add a `gtd validate` line — gtd appends it in plain mode (§2.2). **Keep**
247
+ the embedded format spec (so drafts are born valid).
248
+ - **Header comment**: 12 → 10 states; drop the `todo-validating`/
249
+ `review-validating` cycle-order lines and rewrite Loop 1 / Loop 2 to describe
250
+ self-validation via `gtd validate`; remove `.gtd/FORMAT.md` from the hygiene
251
+ invariant (it no longer exists); update the "dual-implementation contract"
252
+ paragraph (there is no bash port anymore — `OpenQuestions.ts`/`ReviewDoc.ts`
253
+ are the single source of truth, consumed by the LSP and `gtd validate`).
254
+
255
+ Reachability after rewiring (assert mentally / via `validateDefinition`): all 10
256
+ states reachable —
257
+ `idle→grilling→grilling-answer→building→checking→(fixing| reviewing)`,
258
+ `fixing↔checking`, `fixing→escalate→checking`,
259
+ `reviewing→ await-review→(idle|review-deciding|grilling)`,
260
+ `review-deciding→(grilling|idle| await-review)`.
261
+
262
+ ### 3.4 Driver contract — `skills/loop/SKILL.md` + `bin/gtd-loop`
263
+
264
+ Teach the reference driver the `--json` half of §2.2:
265
+
266
+ - After emitting an agent `prompt` turn whose `gtd next --json` carried
267
+ `"validate": true`, and after the agent has acted but **before**
268
+ `gtd step <actor>`, run `gtd validate`.
269
+ - On a non-zero exit, re-invoke the agent with a fix instruction that includes
270
+ the `gtd validate` findings, then run `gtd validate` again. Loop until it
271
+ passes.
272
+ - Cap the fix attempts (§2.5) and reuse the loop's existing no-progress / stall
273
+ handling to avoid spinning; surface a clear message on the cap.
274
+ - Then `gtd step <actor>` as today.
275
+
276
+ Document the same contract in `skills/loop/SKILL.md` (it is a genuine addition
277
+ to the driver protocol — `dispatch on kind` + the new validate gate).
278
+
279
+ ### 3.5 Doc / comment sweep
280
+
281
+ - **`STATES.md`**: §1 property table — add the `validate` row. §10 — table 12 →
282
+ 10 states (drop the two validator rows; update `grilling`/`reviewing` `on`;
283
+ note `validate: true` on `grilling`/`reviewing`); rewrite the Loop 1 / Loop 2
284
+ walkthrough; drop `.gtd/FORMAT.md` from the hygiene invariant.
285
+ - **`docs/cli.md`**: document `gtd validate` (and the `--json` shape).
286
+ - **`docs/configuration.md`**: document the `validate:` property; remove the
287
+ FORMAT-loop description; keep the `file:`/`mode:` known-limitation.
288
+ - **`docs/design/steering-file-loops.md`**: add a status banner at the top
289
+ pointing here and noting §2–§4's validator states were replaced by
290
+ `gtd validate` + the `validate:` marker (leave §1 formats and §5 LSP intact).
291
+ - **`docs/upgrading.md`**, **`docs/development.md`**,
292
+ **`docs/design/state-file-association.md`**,
293
+ **`docs/design/state-machine-validation.md`**: replace `todo-validating`/
294
+ `review-validating`/`.gtd/FORMAT.md` references with the `gtd validate` model.
295
+ - **`README.md`**: adjust the steering-file / editor-integration mention.
296
+ - **`AGENTS.md`**: update the "Changing the Workflow" e2e feature list and the
297
+ "Scripted Check Actor" / dual-implementation notes to reflect that the bundled
298
+ default no longer ships the two validator states (the `check` actor still
299
+ exists for `checking`/`review-deciding`).
300
+ - **`src/OpenQuestions.ts` / `src/ReviewDoc.ts`** docstrings: remove the
301
+ "executable spec ↔ bash validator contract" paragraph and the stale
302
+ `gtd questions` / `gtd changesets` command references; state they are now the
303
+ single source of truth consumed by the LSP (`src/Lsp.ts`) and `gtd validate`.
304
+ - **`src/Lsp.ts`** docstrings: the two "same findings the workflow's
305
+ `todo-validating`/`review-validating` script would write to `.gtd/FORMAT.md`"
306
+ comments → reference `gtd validate` instead.
307
+
308
+ ### 3.6 Tests
309
+
310
+ - **`tests/integration/features/smoke.feature`**: happy path becomes
311
+ `idle → grilling → grilling-answer → building → checking` (drop the
312
+ `todo-validating` hop and its `gtd step check`).
313
+ - **`tests/integration/features/default-workflow.feature`**: major rewrite.
314
+ Remove the `.gtd/FORMAT.md` malformed-lap simulations from the main journey
315
+ and the two standalone "malformed … bounces back" scenarios (that machinery is
316
+ gone). New hops: `grilling → grilling-answer` and `reviewing → await-review`
317
+ directly. The `.gtd/FORMAT.md does not exist` hygiene assertion can stay
318
+ (trivially true); update the comment that attributed its cleanup to the
319
+ validators.
320
+ - **`tests/integration/features/mermaid.feature`**: drop the
321
+ `state "todo-validating" as todo_validating` assertion (no longer a default
322
+ state); keep the rest.
323
+ - **`src/Mermaid.test.ts`**: the hyphenated-name unit test uses
324
+ `"todo-validating"` as a _synthetic_ fixture name — fine to leave, it is not
325
+ the bundled default. The "renders the bundled default … covering every state"
326
+ test iterates actual states and needs no change.
327
+ - **New `tests/integration/features/validate.feature` (@inmem)** — cucumber
328
+ scenarios for the new feature (per `AGENTS.md`: one scenario per feature,
329
+ composable `Given` steps, real file content in the scenario text). `@inmem` is
330
+ fine: `gtd validate` reads the working tree and runs a pure parser — no script
331
+ execution. Cover, with HEAD placed at the relevant state via
332
+ `a commit "gtd(<actor>): <state>" that adds …`:
333
+ - `grilling` + a valid `.gtd/TODO.md` → `gtd validate` succeeds, stdout
334
+ "valid".
335
+ - `grilling` + a malformed `.gtd/TODO.md` (an `### ` question with no
336
+ `Suggested default:`/`Answer:` line) → `gtd validate` fails, stderr carries
337
+ the finding.
338
+ - `reviewing` + a malformed `.gtd/REVIEW.md` (missing `# Review:` header) →
339
+ fails with the finding; a valid one → succeeds.
340
+ - a state with a `file:` but no `mode:` (e.g. `fixing`) → succeeds with
341
+ "nothing to validate".
342
+ - `gtd next` at `grilling` (plain) **contains** the appended validate
343
+ instruction; `gtd next --json` at `grilling` **does not** contain it in
344
+ `content` and **does** emit `"validate":true`.
345
+ - **Optional (nice-to-have) `@live`**: a `gtd-loop`-style scenario proving the
346
+ driver runs `gtd validate` after a producing agent turn and re-runs the agent
347
+ on findings before stepping. If added, follow `gtd-loop.feature`'s stub-agent
348
+ pattern. May be deferred if the driver-contract change is landed with unit
349
+ coverage instead.
350
+
351
+ ### 3.7 No change needed
352
+
353
+ - `stryker.config.json` — the parsers stay in the mutate list; nothing removed.
354
+ - `src/PatternTemplates.ts` — the append happens in `program.ts`, not in
355
+ template rendering.
356
+
357
+ ## 4. Acceptance checklist
358
+
359
+ - [ ] `gtd validate` exists, is current-state-scoped, exits non-zero with
360
+ findings on a malformed file and 0 otherwise; `--json` shape as in §2.1.
361
+ - [ ] `gtd next` (plain) appends the self-validation instruction at
362
+ `grilling`/`reviewing`; `gtd next --json` withholds it and emits
363
+ `"validate": true`.
364
+ - [ ] `validate:` property compiles, validates (requires `file`+`mode`,
365
+ forbidden on commit states), and is emitted in `--json`.
366
+ - [ ] The bundled default is 10 states; `todo-validating`/`review-validating`/
367
+ `.gtd/FORMAT.md` are gone; an approved cycle still leaves `.gtd/` empty.
368
+ - [ ] The reference driver runs `gtd validate` after a `validate:true` agent
369
+ turn and loops the agent on findings before stepping (capped).
370
+ - [ ] `npm test` green; `npm run build` (single-file bundle) green; docs and
371
+ `AGENTS.md` updated. (Do NOT run `npm run test:mutation`.)
372
+
373
+ ## 5. Notes for the implementer
374
+
375
+ - Keep CLI flags orthogonal (`AGENTS.md`): `gtd validate` accepts only `--json`;
376
+ reject unknown `--` options (the existing `unknownOption` guard already covers
377
+ this once `validate` is a known subcommand).
378
+ - `gtd validate` resolves the CURRENT state — during a producing agent's turn,
379
+ HEAD still points at the commit that entered `grilling`/`reviewing` and the
380
+ agent's file is uncommitted in the working tree, so resolution +
381
+ `worktree.read` see exactly the right thing. No `gtd step` has happened yet.
382
+ - The `file:`/`mode:` known-limitation (STATES.md §10, configuration.md) still
383
+ applies: `on` pattern keys are literal `.gtd/...` paths. `gtd validate` reads
384
+ the _rendered_ `file:`, so repointing `todoFile`/`reviewFile` via `vars:`
385
+ keeps validate correct even though the (now-removed) `on` FORMAT rows used to
386
+ desync.
@@ -62,12 +62,14 @@ git/`.gtdrc` dependency at all and serves whatever content the editor hands it
62
62
  over the LSP protocol, exactly like any other document.
63
63
 
64
64
  `src/OpenQuestions.ts` and `src/ReviewDoc.ts` are the pure parsers behind both
65
- the LSP and the bundled default workflow's own bash validators
66
- (`todo-validating`/`review-validating` in `src/workflows/default.yaml`) — see
67
- [docs/design/steering-file-loops.md](design/steering-file-loops.md) for the
68
- "executable spec ↔ bash validator" contract linking the two independent
69
- implementations, and each module's own doc comment for the format it defines.
70
- Their unit tests are that format's spec tests.
65
+ the LSP and the `gtd validate` command, which resolves the current state, reads
66
+ its `file:`, and runs the parser its `mode:` selects (`qa` →
67
+ `parseOpenQuestions`, `review` → `parseReviewDoc`) — ONE source of truth per
68
+ format, no bash port and no dual-implementation contract to keep in sync. See
69
+ [docs/design/steering-file-validation-command.md](design/steering-file-validation-command.md)
70
+ and [STATES.md §12](../STATES.md) for the command, and each module's own doc
71
+ comment for the format it defines. Their unit tests are that format's spec
72
+ tests.
71
73
 
72
74
  ## Mutation testing
73
75
 
@@ -405,9 +405,25 @@ workflow:
405
405
  "M .gtd/COMMIT_MSG.md": done
406
406
 
407
407
  done:
408
- commit: '<%~ it.read(".gtd/COMMIT_MSG.md") %>'
408
+ commit: |
409
+ <%~ it.read(".gtd/COMMIT_MSG.md") %>
410
+
411
+ Token cost: <%= it.processCost %>
412
+ <% it.processCostByModel.forEach(function(m){ %>
413
+ - <%= m.model %>: <%= m.cost %>
414
+ <% }) %>
409
415
  ````
410
416
 
417
+ The `done` commit appends a token-cost summary to the squashed message: if the
418
+ loop driving this workflow passes `gtd step <actor> --cost=<n> --model=<name>`
419
+ after each agent turn, `it.processCost` is the summed cost of the whole cycle
420
+ (including the `squashing` step itself) and `it.processCostByModel` itemizes it
421
+ per model — so the single squash commit records the feature's complete cost,
422
+ broken down by which model spent what, even though the per-turn trailers are
423
+ discarded with the turns they collapsed. Drop these lines if your driver doesn't
424
+ track cost — `it.processCost` is `0` and `it.processCostByModel` empty then. See
425
+ [Configuration: Token cost](../configuration.md#token-cost).
426
+
411
427
  A clean `gtd step human` at `grilling-answer`/`architecting-answer` (the `C`
412
428
  event) accepts the current phase's draft and moves on; any other pending change
413
429
  loops back for another round. `picking` is a `script` state whose only job is to
package/docs/loop.md CHANGED
@@ -5,7 +5,10 @@ gtd (v3, the pattern machine) exposes three commands a loop driver combines:
5
5
  - **`gtd step <actor>`** — authenticate as `<actor>`, match the resolved rest's
6
6
  declared patterns against the pending changes, and commit (or squash) the one
7
7
  resulting transition. Performs AT MOST one transition — there is no fixpoint
8
- chain to drive.
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)).
9
12
  - **`gtd next`** — print the resolved rest's rendered script/prompt/message,
10
13
  without mutating anything.
11
14
  - **`gtd run`** — execute the resolved rest's emitted script (only for a
package/docs/upgrading.md CHANGED
@@ -61,15 +61,18 @@ upgrading.
61
61
  checkbox-only REVIEW.md diffs, and doc-structure validation are gone as ENGINE
62
62
  mechanisms — verdicts are now expressed purely by which file a turn writes or
63
63
  deletes, matched by an ordinary `on` pattern. The bundled default workflow's
64
- own two validation loops (`todo-validating`/`review-validating`/
65
- `review-deciding` — see
66
- [docs/design/steering-file-loops.md](design/steering-file-loops.md)) rebuild
67
- checkbox-review and doc-structure verdicts as ordinary WORKFLOW data
68
- (`script`-content states + `on` patterns), not an engine hook: `D REVIEW.md` =
69
- approve, `M REVIEW.md` = route to the deterministic decider, in the bundled
70
- default.
71
- - **`gtd questions` / `gtd changesets` / `gtd review <target>`.** Gone; the v3
72
- command surface is `step` / `next` / `run` / `status` / `format` / `lsp` (see
64
+ own checkbox-review verdict (`review-deciding`) is ordinary WORKFLOW data (an
65
+ `on` pattern), not an engine hook: `D REVIEW.md` = approve, `M REVIEW.md` =
66
+ route to the deterministic decider, in the bundled default. 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; the v3 command surface is `step` / `next` /
75
+ `run` / `status` / `format` / `validate` / `lsp` (see
73
76
  [CLI reference](cli.md)).
74
77
  - **Model tiers, the decision log.** No `models` config key, no `Gtd-Decisions`
75
78
  trailer scan, no grilling/architecting "prior decisions" context assembled
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pmelab/gtd",
3
- "version": "3.4.0",
3
+ "version": "3.6.0",
4
4
  "private": false,
5
5
  "description": "Git-aware CLI that emits the next prompt for an autonomous coding agent based on the current repository state",
6
6
  "bin": {