@pmelab/gtd 7.11.2 → 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.
- package/README.md +409 -130
- package/bin/gtd +59 -287
- package/dist/gtd.bundle.mjs +187 -98
- package/package.json +1 -2
- package/schema.json +33 -9
- package/docs/cli.md +0 -747
- package/docs/configuration.md +0 -994
- package/docs/design/configurable-state-machine.md +0 -1439
- package/docs/design/delta-purity-plan.md +0 -283
- package/docs/design/pattern-machine-plan.md +0 -164
- package/docs/design/pluggable-steering-modes.md +0 -154
- package/docs/design/scripted-actor.md +0 -289
- package/docs/design/state-file-association.md +0 -120
- package/docs/design/state-machine-validation.md +0 -437
- package/docs/design/steering-file-loops.md +0 -182
- package/docs/design/steering-file-validation-command.md +0 -385
- package/docs/design/work-packages.md +0 -193
- package/docs/development.md +0 -109
- package/docs/loop.md +0 -289
- package/docs/upgrading.md +0 -293
|
@@ -1,283 +0,0 @@
|
|
|
1
|
-
# Implementation plan: full δ(label, diff)
|
|
2
|
-
|
|
3
|
-
> **Historical design record.** The counters/labels machinery this plan built on
|
|
4
|
-
> (turn labels, capture rules, `Gtd-Counters` trailers) was deleted by the v3
|
|
5
|
-
> rewrite; superseded by `docs/design/pattern-machine-plan.md` and
|
|
6
|
-
> [STATES.md](../../STATES.md).
|
|
7
|
-
|
|
8
|
-
> **Status (updated during implementation):**
|
|
9
|
-
>
|
|
10
|
-
> - **Phase 0+A — LANDED.** Phase 0's table review surfaced a major
|
|
11
|
-
> simplification the plan's draft table over-engineered: the actor is already
|
|
12
|
-
> part of every turn label (`gtd(human): X` ≠ `gtd(agent): X`), so only labels
|
|
13
|
-
> whose OWN diff disambiguated their meaning needed splitting — six new labels
|
|
14
|
-
> (`grilling-accepted`, `architecting-accepted`, `review-approved`,
|
|
15
|
-
> `review-feedback`, `agentic-approved`, `agentic-findings`) instead of the
|
|
16
|
-
> drafted ~30-state redraw. Turn capture is rule-driven (`captureRules` per
|
|
17
|
-
> state), inert-by-default with opt-in `empty` rules, the fixpoint is a label
|
|
18
|
-
> fact, `headTurnIsEmpty` / `headTurnReviewSubstantive` are deleted, and the
|
|
19
|
-
> FEEDBACK.md interrupt dissolved into the verdict labels (a fallback rung
|
|
20
|
-
> keeps rider/crash recovery).
|
|
21
|
-
> - **Phase B — LANDED.** Template probes are pending-diff matchers
|
|
22
|
-
> (`squashMsgDirty` / `learningMsgDirty`); content inspection is down to
|
|
23
|
-
> FEEDBACK.md emptiness and REVIEW.md checkbox-only, both consumed at capture.
|
|
24
|
-
> The effects-as-diffs invariant holds structurally (every `perform()` arm
|
|
25
|
-
> ends in a commit or a stop).
|
|
26
|
-
> - **Phase C1 — LANDED.** `gtd: escalated`: a red check at the cap writes
|
|
27
|
-
> ERRORS.md and labels the escalation at write time (runTest AND
|
|
28
|
-
> runHealthCheck). `gtd: test-failed`/`gtd: health-check` classify
|
|
29
|
-
> unconditionally, the ERRORS interrupt dissolved (fallback recovery rung
|
|
30
|
-
> kept), `ClassifyFlags.errorsPresent` and the at-cap health carve-out are
|
|
31
|
-
> deleted.
|
|
32
|
-
> - **Phase C2 — LANDED.** `runTest.onGreen` decided at dispatch: packages +
|
|
33
|
-
> threshold → INLINE close (one `gtd: close-package` commit, no marker);
|
|
34
|
-
> packages → new machine label `gtd: agentic-review` (rest for the verdict);
|
|
35
|
-
> no packages → `gtd: tests-green`, now health-path-only with a settle-only
|
|
36
|
-
> routing rule. The step-agent checkpoint guard keys on the agentic-review
|
|
37
|
-
> label; the recovery-ladder `runTest` gets the same fill.
|
|
38
|
-
> - **Phase C3 — LANDED.** Counter trailers: every machine-written commit (turns
|
|
39
|
-
> AND labels) carries its vector as a `Gtd-Counters: t=n r=n h=n` body
|
|
40
|
-
> trailer, computed by the writer — label stamps live in `labelCounterStamps`
|
|
41
|
-
> (`test-failed` t+1, `health-check` h+1, `building`/`close-package` t=r=0,
|
|
42
|
-
> `tests-green` h=0), turn stamps in `CaptureRule.stamp` (verdict turns r+1,
|
|
43
|
-
> the escalate turn's ERRORS-deletion t=h=0). The machine attaches vectors at
|
|
44
|
-
> dispatch (`attachCounters` + `fillInActionBudgets`); write-time-decided
|
|
45
|
-
> labels (runTest/runHealthCheck outcomes, the inline close) get the previous
|
|
46
|
-
> vector and the edge applies the chosen label's stamp. `gatherEvents` reads
|
|
47
|
-
> ONE trailer (nearest workflow commit) into `payload.counters`;
|
|
48
|
-
> `foldCounters` and all `CommitEvent` counter flags are deleted; squash
|
|
49
|
-
> commits carry no trailer. Zero vector is the no-trailer default (the upgrade
|
|
50
|
-
> rule). E2e grew `Given a commit "…" with counters "t=… r=… h=…"` and the
|
|
51
|
-
> cap/threshold scenarios spell their vectors explicitly; two scenarios pin
|
|
52
|
-
> the trailer wire format on machine-written commits.
|
|
53
|
-
> - **Phase D — LANDED.** (1) The δ conformance property test
|
|
54
|
-
> (`src/Machine.property.test.ts`): with the history fold gone, `resolve` /
|
|
55
|
-
> `predictTurn` output is a function of the nearest workflow commit plus the
|
|
56
|
-
> RESOLVE payload — the test replaces older history, injects boundary commits
|
|
57
|
-
> before and after the nearest workflow commit, and asserts identical output
|
|
58
|
-
> (including thrown corruption errors). (2) Hardening as
|
|
59
|
-
> definition-consistency unit tests (`src/Workflow.test.ts`): declared-actor
|
|
60
|
-
> references in states, prompts, capture rules, turn/routing/ladder outcomes;
|
|
61
|
-
> stamp non-negativity. (No new compiler machinery — the contract types
|
|
62
|
-
> already make gates/phases/outcome sets closed at compile time.) (3) Docs:
|
|
63
|
-
> STATES.md §1 states the δ contract, §2 rewritten as the current transition
|
|
64
|
-
> tables (branch labels, write-time check outcomes, `gtd: escalated` /
|
|
65
|
-
> `gtd: agentic-review` rows, the `Gtd-Counters` trailer, the v1
|
|
66
|
-
> `grilling`/`building` collision note); AGENTS.md and the upgrading doc
|
|
67
|
-
> describe the trailer scheme and its zero-vector upgrade rule.
|
|
68
|
-
|
|
69
|
-
> Companion to `configurable-state-machine.md` Appendix C. Decisions taken:
|
|
70
|
-
> **full purity** (all five moves, including counters and config at write time),
|
|
71
|
-
> **keep the corruption refusal** (steering files + no label → hard error),
|
|
72
|
-
> **split states are the public vocabulary** (labels, `gtd status`, `--json`,
|
|
73
|
-
> docs), **phased rollout** with every phase landing green.
|
|
74
|
-
>
|
|
75
|
-
> Target invariant, stated once: at every resolution,
|
|
76
|
-
> `next = δ(nearest label (incl. its trailers), pending diff)` — the invoker
|
|
77
|
-
> authenticates but adds no information; counters, config outcomes, and branch
|
|
78
|
-
> decisions are baked into labels at write time; the external world enters only
|
|
79
|
-
> through diffs; exactly two sanctioned impurities remain (the boundary-skip
|
|
80
|
-
> walk and the corruption refusal).
|
|
81
|
-
|
|
82
|
-
## 1. The target machine (draft label/state table)
|
|
83
|
-
|
|
84
|
-
States have exactly ONE awaited actor (rest) or ONE machine act —
|
|
85
|
-
`awaits: "dynamic"` is abolished. Labels are the transitions; `stateOf(label)`
|
|
86
|
-
is a total map. Names are draft — Phase 0 finalizes them.
|
|
87
|
-
|
|
88
|
-
### Entry (from `start` = no labeled commit reachable)
|
|
89
|
-
|
|
90
|
-
| From state | Diff matcher (invoker: human) | Label written | Enters state |
|
|
91
|
-
| ---------- | ----------------------------- | -------------------------------- | -------------------- |
|
|
92
|
-
| start | adds `.gtd/HEALTH.md` | `gtd(human): health-entry` | health-briefed |
|
|
93
|
-
| start | adds `.gtd/PLAN.md` | `gtd(human): plan-entry` | seed-from-plan (act) |
|
|
94
|
-
| start | adds `.gtd/ARCHITECTURE.md` | `gtd(human): architecting-entry` | architecting-draft |
|
|
95
|
-
| start | anything else (dirty) | `gtd(human): grilling-entry` | grilling-draft |
|
|
96
|
-
|
|
97
|
-
`start` with steering files present but NO reachable label → **corruption
|
|
98
|
-
refusal** (the one retained guess-refusal; supersedes today's illegal-
|
|
99
|
-
combination table for the no-label case).
|
|
100
|
-
|
|
101
|
-
### Grilling / architecting (the dynamic-gate split)
|
|
102
|
-
|
|
103
|
-
| From state (awaits) | Diff matcher | Label | Enters |
|
|
104
|
-
| ----------------------- | --------------------------- | ------------------------------- | -------------------- |
|
|
105
|
-
| grilling-draft (agent) | touches TODO.md (validated) | `gtd(agent): grilling-draft` | grilling-answer |
|
|
106
|
-
| grilling-answer (human) | non-empty | `gtd(human): grilling-answered` | grilling-draft |
|
|
107
|
-
| grilling-answer (human) | **empty** | `gtd(human): grilling-accepted` | seed-from-todo (act) |
|
|
108
|
-
| seed-from-todo (act) | writes ARCH, deletes TODO | `gtd: architecting` | architecting-draft |
|
|
109
|
-
|
|
110
|
-
Architecting mirrors it (`architecting-draft/-answered/-accepted`, accepted →
|
|
111
|
-
`gtd: grilled` → decompose). The empty-turn _meaning_ is now a declared
|
|
112
|
-
diff-matcher rule (`empty`), not a classification of HEAD's past diff —
|
|
113
|
-
`headTurnIsEmpty` and `headTurnReviewSubstantive` are deleted from
|
|
114
|
-
`ResolvePayload`.
|
|
115
|
-
|
|
116
|
-
### Build / test / fix loop (counters ride labels — see §2)
|
|
117
|
-
|
|
118
|
-
| From state | Trigger | Label | Enters |
|
|
119
|
-
| ------------------ | ------------------------------ | ------------------------------- | ------------------ |
|
|
120
|
-
| decompose (agent) | adds package dirs | `gtd(agent): decomposed` | consume-arch (act) |
|
|
121
|
-
| consume-arch (act) | removes ARCH | `gtd: building` | building |
|
|
122
|
-
| building (agent) | code diff | `gtd(agent): building` | testing (act) |
|
|
123
|
-
| testing (act) | red below cap, writes FEEDBACK | `gtd: test-failed` + trailers | fixing |
|
|
124
|
-
| testing (act) | red at cap, writes ERRORS | `gtd: escalated` | escalate |
|
|
125
|
-
| testing (act) | green, pkgs, threshold reached | `gtd: close-package` | close-pkg (act) |
|
|
126
|
-
| testing (act) | green, pkgs | `gtd: agentic-review` | agentic-review |
|
|
127
|
-
| testing (act) | green, no pkgs (health rerun) | settle label (§ settle) | — |
|
|
128
|
-
| fixing (agent) | any diff | `gtd(agent): fixing` + trailers | testing (act) |
|
|
129
|
-
| escalate (human) | deletes ERRORS.md | `gtd(human): escalate-resumed` | testing (act) |
|
|
130
|
-
|
|
131
|
-
`gtd: tests-green` and `gtd: test-failed`'s read-time branches are gone: **the
|
|
132
|
-
check decides and labels** (write-time config + counters). The force-approve
|
|
133
|
-
guard moves from `routingRules["tests-green"]` into the `runTest` act.
|
|
134
|
-
|
|
135
|
-
### Agentic review (emptiness read from the DIFF)
|
|
136
|
-
|
|
137
|
-
| From state | Diff matcher | Label | Enters |
|
|
138
|
-
| ---------------------- | ------------------------------ | ------------------------------ | --------------- |
|
|
139
|
-
| agentic-review (agent) | adds FEEDBACK, zero content | `gtd(agent): agentic-approved` | close-pkg (act) |
|
|
140
|
-
| agentic-review (agent) | adds FEEDBACK, non-empty | `gtd(agent): agentic-findings` | fixing |
|
|
141
|
-
| close-pkg (act) | rm pkg+FEEDBACK; pkgs remain | `gtd: building` | building |
|
|
142
|
-
| close-pkg (act) | none remain, reviewable | `gtd: review-record` | review-record |
|
|
143
|
-
| close-pkg (act) | none remain, nothing to review | settle label | — |
|
|
144
|
-
|
|
145
|
-
The delete-dispute (`pendingFeedbackDeletion`) becomes a matcher row on the
|
|
146
|
-
fixing state (deletes/empties FEEDBACK → still a fixing turn), not a payload
|
|
147
|
-
special case.
|
|
148
|
-
|
|
149
|
-
### Human review, learning, squash, health, idle
|
|
150
|
-
|
|
151
|
-
| From state | Trigger | Label | Enters |
|
|
152
|
-
| ---------------------- | ------------------------------------ | ------------------------------------------------------------------------- | --------------------- |
|
|
153
|
-
| review-record (agent) | adds REVIEW.md (validated) | `gtd(agent): review-record` | await-review (window) |
|
|
154
|
-
| await-review (human) | empty / checkbox-only / deletes R | `gtd(human): review-approved` | cycle-close (act) |
|
|
155
|
-
| await-review (human) | anything else | `gtd(human): review-feedback` | regrill (act) |
|
|
156
|
-
| cycle-close (act) | rm REVIEW | `gtd: done` | settle |
|
|
157
|
-
| regrill (act) | rm REVIEW | `gtd: grilling` | grilling-draft |
|
|
158
|
-
| settle (act) | learning on + base | `gtd: learning` (writes template) | learning-draft |
|
|
159
|
-
| settle (act) | squash on + base | `gtd: squashing` (writes template) | squash-draft |
|
|
160
|
-
| settle (act) | neither | `gtd: idle` | idle |
|
|
161
|
-
| learning-draft (agent) | **touches LEARNINGS.md** | `gtd(agent): learning-draft` | learning-review |
|
|
162
|
-
| squash-draft (agent) | **touches SQUASH_MSG.md** | `gtd(agent): squash-draft` | do-squash (act) |
|
|
163
|
-
| idle (human) | step (clean) → act runs health check | red: `gtd: health-fixing` / cap: `gtd: escalated` / green: stop or settle | … |
|
|
164
|
-
| health-briefed (agent) | any NON-empty diff | `gtd(agent): health-fixed` | testing (act) |
|
|
165
|
-
| health-fixing (agent) | any diff **or empty** | `gtd(agent): health-fixed` | testing (act) |
|
|
166
|
-
|
|
167
|
-
Two dissolutions worth naming: the template-unmodified probe becomes "diff
|
|
168
|
-
touches the template file" (a matcher), and health-fixing's
|
|
169
|
-
`inertWhen: humanEntryHead` guard becomes two states — `health-briefed` (entered
|
|
170
|
-
via the human's entry; no empty-rule, so an empty agent step is inert by
|
|
171
|
-
_absence of a rule_) vs `health-fixing` (entered via a red check; empty-rule
|
|
172
|
-
present = environmental-fix signal).
|
|
173
|
-
|
|
174
|
-
## 2. Counters: commit-message trailers, not subject suffixes
|
|
175
|
-
|
|
176
|
-
Counters must be derivable from the nearest label alone, but a loop's counter
|
|
177
|
-
lives across several states (test-failed → fixing → testing), and
|
|
178
|
-
`reviewFixCount` spans the whole package loop. Encoding decision:
|
|
179
|
-
|
|
180
|
-
- Subjects stay clean (`gtd(agent): fixing`).
|
|
181
|
-
- Every label written _inside a counted loop_ carries a structured trailer:
|
|
182
|
-
|
|
183
|
-
```
|
|
184
|
-
gtd(agent): fixing
|
|
185
|
-
|
|
186
|
-
Gtd-Counters: t=2/3 r=1/3
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
- The writer computes each value from the previous label's trailer (+increment /
|
|
190
|
-
reset per the definition's counter rules); a label with no trailer means
|
|
191
|
-
all-zero (loop entry). `δ` reads label = (subject, trailers).
|
|
192
|
-
- `foldCounters` is deleted; caps are checked at act time from the trailer.
|
|
193
|
-
|
|
194
|
-
Why trailers: subjects remain human-scannable and e2e-assertable; trailers are
|
|
195
|
-
already precedent (`Gtd-Decisions`); `git log --format=%B` is already fetched
|
|
196
|
-
(decisionLog reuses it — no new subprocess); and squash collapses them away at
|
|
197
|
-
cycle end. Wire-format cost acknowledged: changing a budget's _semantics_
|
|
198
|
-
becomes a history-compat concern (mitigated: values are `n/cap` so the cap
|
|
199
|
-
travels with the count; a config cap change affects only future writes).
|
|
200
|
-
|
|
201
|
-
## 3. What gets deleted (the payoff)
|
|
202
|
-
|
|
203
|
-
| Deleted | Replaced by |
|
|
204
|
-
| ------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------- |
|
|
205
|
-
| interrupt ladder + fallback ladder | `stateOf(label)` + per-state matcher rules; ONE retained refusal |
|
|
206
|
-
| `classifyHead` turn/routing branch rules | total `stateOf` map (labels are unambiguous) |
|
|
207
|
-
| `foldCounters` + `CommitEvent` flags (`isErrors`, `isFeedback`, …) | counter trailers |
|
|
208
|
-
| `headTurnIsEmpty`, `headTurnReviewSubstantive`, `pendingFeedbackDeletion`, `squashMsgIsTemplate`, `learningMsgIsTemplate`, `feedbackEmpty` (as guards) | diff matchers at capture time |
|
|
209
|
-
| `emptyAgentTurn` policies (`inert`/`inertWhen`) | inert-by-default: no matching rule = no-op (empty-rules are opt-in) |
|
|
210
|
-
| illegal-combination table (mostly) | can't arise: every write commits with its label; kept as capture-time lint |
|
|
211
|
-
| `agenticReviewForceApproved` read-time guard, `settle` read-time branches | write-time decisions in `runTest`/`closePackage`/`settle` acts |
|
|
212
|
-
| `awaits: "dynamic"`, `GATE_OVERRIDES`-style sharing | one actor per state |
|
|
213
|
-
|
|
214
|
-
`ResolvePayload` shrinks to: invoker, pending-diff facts (per-artifact
|
|
215
|
-
touched/added/deleted/empty-add/checkbox-only), steering-file presence (for the
|
|
216
|
-
corruption refusal + prompts), config (consumed by ACTS only), and prompt
|
|
217
|
-
passthrough (diffs/content for templates — prompts remain impure by design, per
|
|
218
|
-
Appendix C §C.4.2).
|
|
219
|
-
|
|
220
|
-
## 4. Phases
|
|
221
|
-
|
|
222
|
-
Each phase: full suite green (unit + all 263 e2e scenarios' behavior preserved,
|
|
223
|
-
labels/state names mapped), `feat!:` commit, docs synced.
|
|
224
|
-
|
|
225
|
-
**Phase 0 — Spec (no code).** Finalize the label/state table above as the new
|
|
226
|
-
STATES.md §1 (names, matchers, trailer grammar, `stateOf`). Acceptance: a table
|
|
227
|
-
review — every current e2e scenario hand-traced onto new labels with no
|
|
228
|
-
ambiguity. This is where naming bikesheds happen, deliberately.
|
|
229
|
-
|
|
230
|
-
**Phase A — State splitting + capture-time turn labels.** (`~21 → ~30` states;
|
|
231
|
-
the `dynamic` gates, review approval/feedback, health-briefed split, entry
|
|
232
|
-
labels.) Turn capture evaluates diff matchers and writes the decided label;
|
|
233
|
-
`stateOf` replaces turn-rule branching; `headTurn*` payload fields deleted.
|
|
234
|
-
Machine act labels unchanged in this phase. Biggest e2e churn (every
|
|
235
|
-
`gtd(actor): <gate>` assertion), but mechanical per scenario since the exercised
|
|
236
|
-
branch is evident from surrounding steps.
|
|
237
|
-
|
|
238
|
-
**Phase B — Probes → diff matchers + effects-as-diffs invariant.** Template
|
|
239
|
-
probes become touch-matchers; feedback emptiness read from the capture diff;
|
|
240
|
-
delete-dispute becomes a matcher row. Add the engine invariant (dev/test
|
|
241
|
-
assert): every `perform()` must commit a label and/or change files, or return
|
|
242
|
-
stop. Mostly internal; small e2e deltas around crash-recovery scenarios (retrace
|
|
243
|
-
them explicitly — this is where content-vs-diff divergence lives).
|
|
244
|
-
|
|
245
|
-
**Phase C — Write-time outcomes + counter trailers.** `tests-green`/
|
|
246
|
-
`test-failed` read-time branches move into the acts (labels
|
|
247
|
-
`agentic-review`/`close-package`/`escalated`/`test-failed`+trailer);
|
|
248
|
-
close-package decides building/review/settle at write time; settle decided at
|
|
249
|
-
`done`/green with config in hand; `foldCounters`, commit-event flags, and both
|
|
250
|
-
ladders deleted; corruption refusal re-anchored on "steering files present, no
|
|
251
|
-
label reachable". Second wire-format break; e2e label updates are
|
|
252
|
-
context-dependent this time (a green assertion becomes whichever outcome label
|
|
253
|
-
the scenario's context produced) — budget one careful pass.
|
|
254
|
-
|
|
255
|
-
**Phase D — Conformance + hardening.** (1) The δ property test: generate
|
|
256
|
-
histories, then mutate anything _other than_ the nearest label+trailers and the
|
|
257
|
-
pending diff (permute older commits, inject boundary commits, vary config where
|
|
258
|
-
an act already decided) and assert `resolve` output is byte-identical. This test
|
|
259
|
-
IS the purity claim. (2) Compiler checks: `stateOf` total, one actor per state,
|
|
260
|
-
every act's outcome set covered, trailer continuity (a counted loop's labels all
|
|
261
|
-
carry the vector). (3) STATES.md rewritten as the transition table; upgrading
|
|
262
|
-
doc gains the third grammar migration note (old labels → boundary → `start`,
|
|
263
|
-
refusal if steering files linger).
|
|
264
|
-
|
|
265
|
-
## 5. Risks and open items
|
|
266
|
-
|
|
267
|
-
- **Kill-switch semantics change (accepted):** config flips no longer affect a
|
|
268
|
-
rest already labeled; document loudly in configuration.md.
|
|
269
|
-
- **Idle green stop:** a green idle health check commits nothing (today's
|
|
270
|
-
behavior) — fine for δ (no transition happened), but it means `start`/ `idle`
|
|
271
|
-
must be re-derivable cheaply every run; unchanged from today.
|
|
272
|
-
- **Review checkout window:** unchanged (program-edge; keyed on the await-review
|
|
273
|
-
state's label).
|
|
274
|
-
- **Trailer parsing:** extend `parseSubject` → `parseLabel(subject, body)`; Git
|
|
275
|
-
already returns full messages on the history read — verify no extra subprocess
|
|
276
|
-
creeps in (AGENTS.md decisionLog rule).
|
|
277
|
-
- **Squash-base / review-base scans:** remain history reads inside acts and
|
|
278
|
-
prompts (sanctioned impurity); unchanged.
|
|
279
|
-
- **Naming risk:** the table above is draft; Phase 0 exists so name changes cost
|
|
280
|
-
a review, not a re-implementation.
|
|
281
|
-
- **Rough sizing:** Phase 0 small; A the largest (engine + ~35 feature files); B
|
|
282
|
-
small-medium; C medium with careful e2e mapping; D medium (property test is
|
|
283
|
-
the substance).
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
# Plan: the pattern machine (gtd v3)
|
|
2
|
-
|
|
3
|
-
> **Status: implemented** (commits `b89e1ae..HEAD`). Two amendments made during
|
|
4
|
-
> implementation, not reflected in the plan text below: (1) the commit subject's
|
|
5
|
-
> `<actor>` half was fixed to record WHO STEPPED (the invoker), not the resolved
|
|
6
|
-
> state's own declared actor — see `PatternMachine.ts`'s `resolveState` doc
|
|
7
|
-
> comment and commit `09cd9df`; (2) the bundled default workflow's content is
|
|
8
|
-
> fully INLINE, with no `./`-relative file references — the single-file
|
|
9
|
-
> `dist/gtd.bundle.mjs` build can't ship a sibling `prompts/` directory to
|
|
10
|
-
> resolve them against at runtime, unlike a `.gtdrc`-configured workflow, which
|
|
11
|
-
> resolves file refs relative to its own config file's location on disk.
|
|
12
|
-
|
|
13
|
-
> Interview outcome (all decided with the user, 2026-07-22):
|
|
14
|
-
>
|
|
15
|
-
> 1. **Ground-up v3.** The current definition model (gates, guard functions,
|
|
16
|
-
> actor kinds, interrupt/fallback ladders, counters-as-trailers, conflicts)
|
|
17
|
-
> is DELETED. The engine shrinks to: states, change-pattern transitions, and
|
|
18
|
-
> one content property per state. The shipped default workflow is re-authored
|
|
19
|
-
> in the new vocabulary as a bundled config; the e2e suite is rewritten
|
|
20
|
-
> around it; behavior changes where the old shape doesn't map.
|
|
21
|
-
> 2. **Commit format:** `gtd(<actor>): <state>` — every workflow commit records
|
|
22
|
-
> the state being ENTERED and who authored the step. History is an attributed
|
|
23
|
-
> state trace; resolution = read HEAD's state name.
|
|
24
|
-
> 3. **Scripts: the driver executes.** gtd only ever emits the rendered script
|
|
25
|
-
> (`gtd next`); the outer loop or the built-in `gtd run` convenience executes
|
|
26
|
-
> it and steps. The machine stays execution-free.
|
|
27
|
-
> 4. **Budgets:** one engine affordance only — a per-state retry limit that
|
|
28
|
-
> redirects the transition to a named state when the limit is hit. Everything
|
|
29
|
-
> else (verdict encoding, force-approve, content signals) moves out of the
|
|
30
|
-
> engine into which files scripts/agents write or delete. `Gtd-Counters`
|
|
31
|
-
> trailers die.
|
|
32
|
-
> 5. **Patterns:** contains-match over the pending diff, declaration order,
|
|
33
|
-
> first match wins. Statuses `A` (added), `M` (modified), `D` (deleted), `*`
|
|
34
|
-
> (any change kind), plus the special `C` (clean tree). Glob paths (`*`
|
|
35
|
-
> within a segment, `**` across segments).
|
|
36
|
-
> 6. **No-match dirty step:** refusal — exit non-zero, name the state's declared
|
|
37
|
-
> patterns, commit nothing.
|
|
38
|
-
> 7. **Squash = the `commit:` content kind** (revised 2026-07-22, replacing the
|
|
39
|
-
> earlier `final: true` flag). A state whose content property is `commit:` is
|
|
40
|
-
> the final state — no separate flag, no actor. Entering it performs, in one
|
|
41
|
-
> execution: (a) render the `commit:` Eta template against the PENDING
|
|
42
|
-
> working tree (`it.read(path)` reads files as they sit — the message file is
|
|
43
|
-
> never committed first; a failed render refuses the step and touches
|
|
44
|
-
> nothing), (b) soft-reset to the process's start parent and write ONE commit
|
|
45
|
-
> with the rendered message, (c) discard all remaining uncommitted changes,
|
|
46
|
-
> message file included. Squashing is optional (no `commit:` state = no
|
|
47
|
-
> squash) and nothing about it is hardcoded: the message filename appears
|
|
48
|
-
> only in user-authored patterns/templates, and the standard shape is an
|
|
49
|
-
> ordinary prompt state (`squashing`) whose agent authors the message file,
|
|
50
|
-
> with `"A COMMIT_MSG.md": done` routing to the commit state.
|
|
51
|
-
> 8. **Template variables** for script/prompt/message/commit (all Eta; inline or
|
|
52
|
-
> a path relative to the config file, auto-inlined): `startCommit`,
|
|
53
|
-
> `currentCommit`, `previousCommit`, `state`, `actor`, `processDiff`
|
|
54
|
-
> (startCommit..HEAD + pending), `lastDiff` (the last transition's diff),
|
|
55
|
-
> `read(path)` (working-tree file read, available in every content kind), and
|
|
56
|
-
> `config` passthrough. No blessed config keys: there is no `testCommand` — a
|
|
57
|
-
> check's command lives inline in its `script:` (or the referenced file).
|
|
58
|
-
|
|
59
|
-
## 1. The model
|
|
60
|
-
|
|
61
|
-
A workflow is a set of named **states**. Each state declares:
|
|
62
|
-
|
|
63
|
-
| Property | Meaning |
|
|
64
|
-
| --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
65
|
-
| `actor` | A plain string: who is expected to act here. No kinds — every actor just creates modifications in the tree. `gtd step <actor>` authenticates against it. Commit states have NO actor — gtd itself performs them. |
|
|
66
|
-
| `script` \| `prompt` \| `message` \| `commit` | Exactly one — the content kind. `script` — an executable the DRIVER runs (`gtd next` emits it rendered; `gtd run` executes + steps). `prompt` — instructions for an agent. `message` — text for a human (drivers halt here). `commit` — the squash-commit message template; having it makes the state FINAL (see decision 7). Inline string or a file path relative to the config file (auto-inlined). All are Eta templates over the variable set above. |
|
|
67
|
-
| `on` | An ordered map of change patterns → next state. Evaluated at step time against the pending diff; first match wins; the step commits everything pending as `gtd(<actor>): <next-state>` — unless the target is a commit state, in which case the step performs the squash instead (decision 7). A commit state has no `on`: the process ends there. |
|
|
68
|
-
| `initial: true` | Exactly one state: where an unrecognized HEAD (any non-`gtd(actor): state` subject, or a state name outside this workflow) resolves. THE entry point; replaces entry rules, the fallback ladder, and boundary handling. |
|
|
69
|
-
| `retry` | Optional: `{ max: <n>, otherwise: <state> }` — when a transition would enter this state for the (max+1)-th time within the current process, it enters `otherwise` instead, decided at write time and encoded in the committed label. Visit counting walks only the current process's commits (start → HEAD), so it stays bounded and resets naturally at entry/squash. |
|
|
70
|
-
|
|
71
|
-
Pattern grammar: `<status> <glob>` where status ∈ `A|M|D|*`, or the bare token
|
|
72
|
-
`C` (clean tree — the empty step as an explicit, opt-in event). Contains-match:
|
|
73
|
-
a pattern fires if any pending change matches it. `"* *"` is the catch-all for
|
|
74
|
-
any dirty tree.
|
|
75
|
-
|
|
76
|
-
Resolution stays δ-pure and gets simpler:
|
|
77
|
-
`next = f(HEAD's state name, pending diff pattern)` with exactly one impurity
|
|
78
|
-
left (the retry count's process-scoped walk). Turn-taking keeps only:
|
|
79
|
-
out-of-turn refusal (invoker ≠ state's actor), the no-match refusal, and the
|
|
80
|
-
clean-step no-op when no `C` event is declared.
|
|
81
|
-
|
|
82
|
-
## 2. What dies
|
|
83
|
-
|
|
84
|
-
- Actor kinds (`interactive`/`autonomous`/`scripted`) and every carve-out keyed
|
|
85
|
-
on them (dirty-boundary entry turn, inert-clean-step defaults, draft
|
|
86
|
-
validation).
|
|
87
|
-
- Gates, `TurnGate`, capture rules, turn rules, routing rules — replaced by `on`
|
|
88
|
-
maps. The two-namespace grammar collapses to one subject form.
|
|
89
|
-
- Guard functions and the `WorkflowConfig` guard vocabulary (fact/all/any/
|
|
90
|
-
counterAtLeast/…) — replaced by change patterns.
|
|
91
|
-
- Interrupt/fallback ladders, conflicts (illegal combinations), entry rules —
|
|
92
|
-
replaced by the initial state + explicit `on` edges.
|
|
93
|
-
- `Gtd-Counters` trailers, stamps, forceApprove, fixAttemptCap/ reviewThreshold
|
|
94
|
-
config keys — replaced by `retry` + script-owned logic.
|
|
95
|
-
- The content-inspection exceptions (FEEDBACK.md emptiness, checkbox-only
|
|
96
|
-
REVIEW.md diffs, doc-structure validation): verdicts are now expressed by
|
|
97
|
-
which file is written or deleted (`D FEEDBACK.md` = approve, `M FEEDBACK.md` =
|
|
98
|
-
findings).
|
|
99
|
-
- The review checkout window (v3.0 drops it; may return later as a state flag —
|
|
100
|
-
noted as a follow-up, not in scope).
|
|
101
|
-
|
|
102
|
-
## 3. What the CLI becomes
|
|
103
|
-
|
|
104
|
-
- `gtd step <actor>` — authenticate, match patterns, commit
|
|
105
|
-
`gtd(<actor>): <next-state>` (with retry redirection at write time). A
|
|
106
|
-
transition targeting a commit state performs the squash instead of a turn
|
|
107
|
-
commit. Refusals: out-of-turn, no-match, failed `commit:` render.
|
|
108
|
-
- `gtd next [--json]` — emit the resolved state's rendered
|
|
109
|
-
script/prompt/message; JSON carries
|
|
110
|
-
`{state, actor, kind: script|prompt|message, content}` as the driver dispatch
|
|
111
|
-
key. Resolution never rests AT a commit state (entering one ends the process),
|
|
112
|
-
so `kind: commit` never appears here.
|
|
113
|
-
- `gtd run` — the built-in script driver: execute the emitted script verbatim,
|
|
114
|
-
then `gtd step <actor>`.
|
|
115
|
-
- `gtd status` — state, actor, and which pattern each pending change matches.
|
|
116
|
-
- `bin/gtd-loop` / the loop skill dispatch on `kind`.
|
|
117
|
-
|
|
118
|
-
## 4. The bundled default workflow (re-authored)
|
|
119
|
-
|
|
120
|
-
Sketch (final shape decided during Phase 3): `idle` (initial; message;
|
|
121
|
-
`"* *" → grilling`… actually entry captures the dirty tree), `grilling` ⇄
|
|
122
|
-
`grilling-answer`, `architecting` ⇄ `architecting-answer`, `decompose`,
|
|
123
|
-
`building`, `checking` (script — the test command lives INLINE in the script, no
|
|
124
|
-
`testCommand` config key; `A FEEDBACK.md → fixing`, retry max 3 → `escalate`,
|
|
125
|
-
`C → reviewing`), `fixing`, `reviewing`, `await-review`
|
|
126
|
-
(`D REVIEW.md → squashing`, `M REVIEW.md → grilling`), `escalate`, `squashing`
|
|
127
|
-
(prompt: author the commit-message file; `A COMMIT_MSG.md → done`), `done`
|
|
128
|
-
(`commit: <%~ it.read("COMMIT_MSG.md") %>`). Health path becomes `idle`'s own
|
|
129
|
-
script + `A HEALTH.md` edge. Verdict changes vs today: agentic approval = DELETE
|
|
130
|
-
FEEDBACK.md; review approval = DELETE REVIEW.md; the check script owns its
|
|
131
|
-
attempt count only if we want output-dependent budgets (the engine `retry`
|
|
132
|
-
covers the standard case).
|
|
133
|
-
|
|
134
|
-
## 5. Phases (each lands green, committed, pushed)
|
|
135
|
-
|
|
136
|
-
1. **Engine core.** New `src/PatternMachine.ts` (or repurposed Machine.ts):
|
|
137
|
-
state defs, pattern parser/matcher, resolve (HEAD state name → state;
|
|
138
|
-
unrecognized → initial), step semantics (refusals, C-event, no-match), retry
|
|
139
|
-
redirection, the commit-state squash action (render-then-squash-then-drop,
|
|
140
|
-
refusal on render failure). New minimal `ResolvePayload` (pending changes as
|
|
141
|
-
`{status, path}[]`, hashes, clean flag). Unit + property tests (δ: next =
|
|
142
|
-
f(state, diff); totality; refusal invariants).
|
|
143
|
-
2. **Config + templates.** The `workflow:` key becomes the ONLY definition
|
|
144
|
-
source (bundled default = a YAML asset compiled the same way): states, actor
|
|
145
|
-
strings, script/prompt/message/commit (inline or file-relative, auto-inlined
|
|
146
|
-
at load), `on` maps, validation (exactly one initial, exactly one content
|
|
147
|
-
property, every target defined, retry.otherwise defined, commit states carry
|
|
148
|
-
no actor and no `on`). Eta rendering with the agreed variable set, including
|
|
149
|
-
the `read(path)` helper.
|
|
150
|
-
3. **Default workflow re-authoring + CLI.** Write the bundled default config;
|
|
151
|
-
rewire program.ts (step/next/run/status), gtd-loop, loop skill; delete the
|
|
152
|
-
old model (Workflow.ts rules, WorkflowConfig guard language, Subjects
|
|
153
|
-
two-namespace grammar → one subject parser over the active state set;
|
|
154
|
-
Events.ts flag/trailer plumbing).
|
|
155
|
-
4. **E2e rewrite.** New feature files pinning the engine semantics (patterns,
|
|
156
|
-
refusals, retry, final squash, initial-state entry, driver protocol) +
|
|
157
|
-
default-workflow journeys. Old features deleted or rewritten.
|
|
158
|
-
5. **Docs.** STATES.md replaced by the v3 model reference; configuration.md
|
|
159
|
-
workflow section rewritten; upgrading doc gains the v3 note (old histories:
|
|
160
|
-
all previous subjects are unrecognized → initial state, by design); AGENTS.md
|
|
161
|
-
updated.
|
|
162
|
-
|
|
163
|
-
Sizing: phases 1–2 are the substance (new core + compiler); phase 3 is the big
|
|
164
|
-
deletion; phase 4 is broad but mechanical; phase 5 small.
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
# Pluggable steering-file modes
|
|
2
|
-
|
|
3
|
-
> Status: IMPLEMENTED. The authoritative reference is
|
|
4
|
-
> [STATES.md §12](../../STATES.md) (the model) and
|
|
5
|
-
> [docs/configuration.md](../configuration.md#modes--pluggable-steering-file-modes)
|
|
6
|
-
> (the config surface); this doc is the design record. Builds on
|
|
7
|
-
> [steering-file-validation-command.md](steering-file-validation-command.md),
|
|
8
|
-
> which introduced `gtd validate` and the `gtd step` capture gate.
|
|
9
|
-
|
|
10
|
-
## 1. Motivation
|
|
11
|
-
|
|
12
|
-
`mode:` was a CLOSED vocabulary of two names — `qa` and `review` — each
|
|
13
|
-
hardwired to a parser gtd ships (`src/OpenQuestions.ts` / `src/ReviewDoc.ts`).
|
|
14
|
-
That is fine for the bundled default workflow, whose steering files ARE those
|
|
15
|
-
two formats, but it makes the rest of the machine's extensibility story stop
|
|
16
|
-
short: a workflow is data (states, actors, patterns, content, `vars:`) right up
|
|
17
|
-
to the moment it wants a state whose output is checkable in some other way — an
|
|
18
|
-
ADR, a JSON spec, an OpenAPI document, a translation catalogue. Such a state can
|
|
19
|
-
declare a `file:`, but not what "valid" means for it, so the format gate the
|
|
20
|
-
whole design leans on (a human gate is only ever handed a well-formed file)
|
|
21
|
-
simply doesn't exist for it.
|
|
22
|
-
|
|
23
|
-
Meanwhile gtd already knows how to let a workflow bring its own executable
|
|
24
|
-
behavior: the scripted check actor (`script:` content run verbatim through
|
|
25
|
-
`bash`, its outcome read back off the tree). Modes are the same shape of problem
|
|
26
|
-
and deserve the same answer — a shell command, not a code change in gtd.
|
|
27
|
-
|
|
28
|
-
## 2. The design
|
|
29
|
-
|
|
30
|
-
A **mode is a pair of shell commands over one file**: `format` (rewrite it in
|
|
31
|
-
place) and `validate` (exit 0, or exit non-zero with the findings on
|
|
32
|
-
stdout/stderr). Both optional; at least one required.
|
|
33
|
-
|
|
34
|
-
```yaml
|
|
35
|
-
workflow:
|
|
36
|
-
modes:
|
|
37
|
-
adr:
|
|
38
|
-
format: "npx prettier --write <%= it.file %>"
|
|
39
|
-
validate: "./scripts/check-adr.sh <%= it.file %>"
|
|
40
|
-
states:
|
|
41
|
-
drafting:
|
|
42
|
-
actor: agent
|
|
43
|
-
prompt: "Write the ADR."
|
|
44
|
-
file: docs/adr/<%= it.vars.slug %>.md
|
|
45
|
-
mode: adr
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
Five decisions:
|
|
49
|
-
|
|
50
|
-
### 2.1 Modes are workflow data, with a project layer over it
|
|
51
|
-
|
|
52
|
-
A `modes:` map sits beside `states:` and `vars:` INSIDE `workflow:`, compiling
|
|
53
|
-
onto `WorkflowDefinition.modes`: a mode exists to serve a state's `mode:`, and
|
|
54
|
-
states are workflow data, so a workflow that needs a custom mode carries its
|
|
55
|
-
own.
|
|
56
|
-
|
|
57
|
-
A top-level `.gtdrc` `modes:` key layers over that, per half (`mergeModes`),
|
|
58
|
-
exactly the way the top-level `vars:` key layers over the workflow's own
|
|
59
|
-
`vars:`. Without it, a project on the BUNDLED default workflow could not plug in
|
|
60
|
-
a formatter at all without copying every state into its `.gtdrc` — and since gtd
|
|
61
|
-
ships no formatter (§2.2), that is precisely the common case. The rc layer is
|
|
62
|
-
merged into the definition BEFORE `validateDefinition` runs
|
|
63
|
-
(`compileWorkflowConfig`'s `rcModes` argument), so a state may name a mode
|
|
64
|
-
either layer declares and the load-time name check (§2.3) stays a pure function
|
|
65
|
-
of one definition.
|
|
66
|
-
|
|
67
|
-
### 2.2 `qa` and `review` stay built in — as VALIDATORS, and only that
|
|
68
|
-
|
|
69
|
-
The two existing modes' parsers are also what `gtd lsp` publishes as live
|
|
70
|
-
diagnostics and what its document symbols and code actions are built on —
|
|
71
|
-
shelling out per keystroke over an unsaved buffer is a different (and much
|
|
72
|
-
worse) design. So gtd keeps them as its `BUILT_IN_MODES`, available in every
|
|
73
|
-
workflow without being declared.
|
|
74
|
-
|
|
75
|
-
Their FORMATTING half went the other way. gtd used to bundle prettier and expose
|
|
76
|
-
it as a `gtd format <file>` subcommand, which the built-in modes then called;
|
|
77
|
-
both are gone (the subcommand, and prettier as a runtime dependency — the
|
|
78
|
-
single-file bundle drops from ~15 MB to ~10 MB). A project brings its own
|
|
79
|
-
formatter and plugs it into whichever mode should use it. The two built-ins
|
|
80
|
-
therefore VALIDATE and nothing else, until a `modes:` entry gives them a
|
|
81
|
-
`format:`.
|
|
82
|
-
|
|
83
|
-
Which forces the resolution rule: the halves resolve INDEPENDENTLY, first layer
|
|
84
|
-
wins per half.
|
|
85
|
-
`modes: { qa: { format: "npx prettier --write <%= it.file %>" } }` adds
|
|
86
|
-
formatting to `qa` and keeps gtd's open-questions validation; declaring
|
|
87
|
-
`validate:` instead displaces the parser. (An earlier draft of this design had a
|
|
88
|
-
declared entry REPLACE a built-in wholesale — that cannot express "gtd's
|
|
89
|
-
validation plus my formatter", which is the common case.)
|
|
90
|
-
|
|
91
|
-
### 2.3 `mode:` stays load-time-validated, against a definition-derived set
|
|
92
|
-
|
|
93
|
-
`validateDefinition` no longer checks a hardcoded vocabulary; it checks
|
|
94
|
-
`knownModes(def)` — the built-ins plus the declared names — exactly the way the
|
|
95
|
-
commit grammar's closed actor set derives from `declaredActors`. A typo'd mode
|
|
96
|
-
must not silently disable both the capture gate and the editor's diagnostics, so
|
|
97
|
-
this stays an error at config-load time, and its message lists both sets.
|
|
98
|
-
|
|
99
|
-
### 2.4 Commands are Eta templates with `it.file`, executed at the edge
|
|
100
|
-
|
|
101
|
-
A command sees the resting state's full template context plus `it.file`, the
|
|
102
|
-
already-rendered steering-file path (`PatternTemplates.ModeCommandContext`) — so
|
|
103
|
-
a command can read `it.vars`, and a workflow whose `file:` is vars-parameterized
|
|
104
|
-
never has to repeat the path. Execution is `bash -c` from the repo root, output
|
|
105
|
-
captured (unlike `gtd run`'s scripted actor, whose output is for the human).
|
|
106
|
-
|
|
107
|
-
`./scripts/check-adr.sh` in a command is a COMMAND, never a `./`-relative file
|
|
108
|
-
reference the way a content string is — inlining it as template text would break
|
|
109
|
-
the obvious reading.
|
|
110
|
-
|
|
111
|
-
### 2.5 The pure engine still never validates anything
|
|
112
|
-
|
|
113
|
-
Everything above is an edge concern, in one new module, `src/SteeringMode.ts`
|
|
114
|
-
(resolution + execution) — the same shape as `src/ReviewWindow.ts`. The engine
|
|
115
|
-
carries `modes:` as inert data and validates its shape; `PatternMachine.step`
|
|
116
|
-
never sees a mode.
|
|
117
|
-
|
|
118
|
-
## 3. What changed
|
|
119
|
-
|
|
120
|
-
| Area | Change |
|
|
121
|
-
| --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
122
|
-
| `src/PatternMachine.ts` | `ModeDef`; `WorkflowDefinition.modes`; `StateMode` widened from `"qa" \| "review"` to `string`; `BuiltInMode`/`isBuiltInMode`/`knownModes`; `validateModes`; `validateMode` now definition-derived |
|
|
123
|
-
| `src/PatternConfig.ts` | `compileModesMap` + `mergeModes` (mirroring `compileVarsMap`), `modes` added to `KNOWN_TOP_KEYS`, `compileWorkflowConfig`'s `rcModes` argument |
|
|
124
|
-
| `src/Config.ts` / `src/ConfigSchema.ts` | the third blessed top-level key, `modes:` (`compileRcModes`), layered over the compiled workflow (or the bundled default) per half |
|
|
125
|
-
| `src/Format.ts`, `gtd format <file>` | DELETED, and `prettier` dropped from the runtime dependencies — formatting is a mode's own command now |
|
|
126
|
-
| `src/PatternTemplates.ts` | `ModeCommandContext` (`TemplateContext` + `file`) and `renderModeCommand` |
|
|
127
|
-
| `src/SteeringMode.ts` (new) | `resolveSteeringMode` (per half), `formatSteeringFile`, `validateSteeringFile`, `formatAndValidateSteeringFile`, `unknownModeMessage` |
|
|
128
|
-
| `src/program.ts` | `formatAndCheckSteeringFile` routes through the resolved mode (was: a hardcoded `qa`/`review` switch plus `formatFile`); `gtd validate` and the capture gate are otherwise unchanged |
|
|
129
|
-
| `src/ConfigSchema.ts` / `schema.json` | the `modes:` object; `mode:`'s `enum` dropped |
|
|
130
|
-
| `src/Lsp.ts` | docs only — a custom mode gets no live diagnostics/symbols/code actions (documented limitation) |
|
|
131
|
-
| `src/workflows/default.yaml` | unchanged — the bundled default keeps `qa`/`review`, which resolve to the built-in validators |
|
|
132
|
-
|
|
133
|
-
One behavior change for existing users: steering files are no longer
|
|
134
|
-
auto-formatted, because gtd no longer has a formatter to do it with. Validation
|
|
135
|
-
is untouched. A project that wants the old behavior adds four lines to its
|
|
136
|
-
`.gtdrc` (the `modes: { qa: { format: … } }` snippet in
|
|
137
|
-
[configuration.md](../configuration.md#modes--pluggable-steering-file-modes)).
|
|
138
|
-
|
|
139
|
-
## 4. Known limitations
|
|
140
|
-
|
|
141
|
-
- **No live editor support for a custom mode.** `gtd lsp` dispatches on the
|
|
142
|
-
built-in names only; a workflow-declared mode's file gets no diagnostics, no
|
|
143
|
-
document symbols, no code actions. It is still formatted and validated by
|
|
144
|
-
`gtd validate` and the `gtd step` gate.
|
|
145
|
-
- **Nothing is formatted out of the box.** The bundled default declares no
|
|
146
|
-
`format:` command, so a fresh install validates but never rewrites. This was
|
|
147
|
-
the deliberate alternative to shipping an `npx prettier` default, which would
|
|
148
|
-
make every gate depend on prettier resolving in the user's project.
|
|
149
|
-
- **A command runs on the developer's machine, unsandboxed** — same trust model
|
|
150
|
-
as a `script:` state: a `.gtdrc` is project code.
|
|
151
|
-
- **`gtd next`'s self-validation instruction** is appended for any `prompt` rest
|
|
152
|
-
declaring `file:`+`mode:`, including a mode that declares only `format:`
|
|
153
|
-
(where `gtd validate` can only ever pass). Harmless, and keeps the rule "a
|
|
154
|
-
state that hands over a steering file tells its agent to check it".
|