@pmelab/gtd 8.0.0 → 8.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +402 -110
- package/bin/gtd +3 -4
- package/dist/gtd.bundle.mjs +184 -92
- package/package.json +1 -2
- package/schema.json +33 -9
- package/docs/cli.md +0 -675
- package/docs/configuration.md +0 -994
- package/docs/design/configurable-state-machine.md +0 -1439
- package/docs/design/delta-purity-plan.md +0 -283
- package/docs/design/pattern-machine-plan.md +0 -164
- package/docs/design/pluggable-steering-modes.md +0 -154
- package/docs/design/scripted-actor.md +0 -289
- package/docs/design/state-file-association.md +0 -120
- package/docs/design/state-machine-validation.md +0 -437
- package/docs/design/steering-file-loops.md +0 -182
- package/docs/design/steering-file-validation-command.md +0 -385
- package/docs/design/work-packages.md +0 -193
- package/docs/development.md +0 -109
- package/docs/loop.md +0 -219
- package/docs/upgrading.md +0 -293
|
@@ -1,289 +0,0 @@
|
|
|
1
|
-
# Exploration: every rest is a prompt — checks as a scripted actor
|
|
2
|
-
|
|
3
|
-
> **Historical design record.** The actor-kind model this explores (v2's
|
|
4
|
-
> gates/counters, actor kinds) was deleted by the v3 rewrite; superseded by
|
|
5
|
-
> `docs/design/pattern-machine-plan.md` and [STATES.md](../../STATES.md).
|
|
6
|
-
|
|
7
|
-
> Companion to `configurable-state-machine.md` (its Appendix C's δ-purity
|
|
8
|
-
> program is now fully landed — see `delta-purity-plan.md`). This document
|
|
9
|
-
> explores the next generalization: **executed commands become emitted
|
|
10
|
-
> prompts**, run by the outer loop as shell scripts under a third actor kind,
|
|
11
|
-
> with their results entering the machine the same way every other actor's work
|
|
12
|
-
> does — as working-tree modifications captured on the next invocation.
|
|
13
|
-
|
|
14
|
-
## 1. The idea
|
|
15
|
-
|
|
16
|
-
Today the machine has exactly one place where it executes external code itself:
|
|
17
|
-
the check (`runTest` / `runHealthCheck` in `src/Events.ts`, backed by the
|
|
18
|
-
`TestRunner` service). Everything else an invocation does is git/fs bookkeeping.
|
|
19
|
-
The check is also the only transition that branches on something other than a
|
|
20
|
-
diff — an **exit code** — and the only reason `perform()` needs a subprocess
|
|
21
|
-
executor, timeout semantics, and output capture inside the `gtd` process.
|
|
22
|
-
|
|
23
|
-
The generalization: make the check an **actor**. A state that today fires
|
|
24
|
-
`runTest` instead _rests_, awaiting a `check` actor. `gtd next` emits that
|
|
25
|
-
state's prompt — which for a scripted actor is a **shell script** — and the
|
|
26
|
-
outer loop executes it, exactly as it feeds an agent prompt to an LLM or shows a
|
|
27
|
-
human prompt in a terminal. The script's effects land in the working tree
|
|
28
|
-
(`.gtd/FEEDBACK.md` on a red run, nothing on green), and the next
|
|
29
|
-
`gtd step check` captures them as the check's turn, with capture rules deciding
|
|
30
|
-
the label from the pending diff — the same δ(label, diff) discipline every other
|
|
31
|
-
actor already follows.
|
|
32
|
-
|
|
33
|
-
The end state: **every rest is a prompt state** — for a human, an agent, or a
|
|
34
|
-
shell. The machine performs only bookkeeping; every external effect (human
|
|
35
|
-
thought, LLM inference, command execution) happens outside the loop and enters
|
|
36
|
-
exclusively through the tree.
|
|
37
|
-
|
|
38
|
-
```
|
|
39
|
-
rest ──gtd next──▶ prompt for the awaited actor
|
|
40
|
-
human → text in a terminal
|
|
41
|
-
agent → LLM prompt
|
|
42
|
-
check → shell script ◀── the new kind
|
|
43
|
-
actor acts (edits the tree, or doesn't)
|
|
44
|
-
──gtd step <actor>──▶ capture rules label the turn from the diff
|
|
45
|
-
──routing chains──▶ next rest
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
## 2. What executes where today (the residue)
|
|
49
|
-
|
|
50
|
-
| Effect | Where it runs today | Enters the machine as |
|
|
51
|
-
| ------------------------- | ------------------------------------- | ----------------------------------- |
|
|
52
|
-
| Human thought | outside (terminal) | pending diff → captured turn |
|
|
53
|
-
| Agent inference | outside (harness/LLM) | pending diff → captured turn |
|
|
54
|
-
| **Test / health command** | **inside `perform()` (`TestRunner`)** | **exit code → branch in `perform`** |
|
|
55
|
-
| Git/fs bookkeeping | inside `perform()` | commits (labels + trailers) |
|
|
56
|
-
|
|
57
|
-
The third row is the asymmetry this exploration removes. Note that Phase C
|
|
58
|
-
already moved the _decision_ to write time (the red/green/cap outcome is encoded
|
|
59
|
-
in the label the check writes) — but the _execution_ still lives inside the
|
|
60
|
-
invocation, which is why `gtd step agent` can block for the length of a test
|
|
61
|
-
suite, why `TestRunner` needs spawn-failure/timeout/empty-output handling
|
|
62
|
-
in-process, and why the `@inmem` e2e tier still spawns real subprocesses for
|
|
63
|
-
`testCommand`.
|
|
64
|
-
|
|
65
|
-
## 3. The model
|
|
66
|
-
|
|
67
|
-
### 3.1 A third actor kind: `scripted`
|
|
68
|
-
|
|
69
|
-
`ActorDef.kind` gains a third value alongside `interactive` and `autonomous`:
|
|
70
|
-
|
|
71
|
-
```ts
|
|
72
|
-
actors: [
|
|
73
|
-
{ name: "human", kind: "interactive" },
|
|
74
|
-
{ name: "agent", kind: "autonomous" },
|
|
75
|
-
{ name: "check", kind: "scripted" },
|
|
76
|
-
]
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
The turn-taking engine keys on the kind, as it already does:
|
|
80
|
-
|
|
81
|
-
- **interactive** — dirty boundary tree is the entry turn; empty turns are
|
|
82
|
-
meaningful signals.
|
|
83
|
-
- **autonomous** — clean-tree steps at inert gates are no-ops; drafts are
|
|
84
|
-
structurally validated.
|
|
85
|
-
- **scripted** — its prompt is executable; its turn is _mechanical_ (the driver
|
|
86
|
-
runs the script verbatim and steps — there is no judgement in between). Its
|
|
87
|
-
empty turn is typically the green signal (an opt-in `empty` rule, as
|
|
88
|
-
everywhere else).
|
|
89
|
-
|
|
90
|
-
Nothing else in the engine changes: refusals, the fixpoint, and capture rules
|
|
91
|
-
are actor-generic already (the actor generalization landed earlier).
|
|
92
|
-
|
|
93
|
-
### 3.2 The prompt is the script — and gtd generates it
|
|
94
|
-
|
|
95
|
-
The state's prompt template for a scripted actor renders a **wrapper script**,
|
|
96
|
-
not the raw `testCommand`:
|
|
97
|
-
|
|
98
|
-
```bash
|
|
99
|
-
#!/usr/bin/env bash
|
|
100
|
-
# gtd check turn — run verbatim, then: gtd step check
|
|
101
|
-
set +e
|
|
102
|
-
npm run test > .gtd/.check-output 2>&1
|
|
103
|
-
code=$?
|
|
104
|
-
if [ "$code" -ne 0 ]; then
|
|
105
|
-
mkdir -p .gtd
|
|
106
|
-
if [ -s .gtd/.check-output ]; then mv .gtd/.check-output .gtd/FEEDBACK.md
|
|
107
|
-
else echo "Test command \`npm run test\` failed with exit code $code and produced no output." > .gtd/FEEDBACK.md
|
|
108
|
-
fi
|
|
109
|
-
else
|
|
110
|
-
rm -f .gtd/.check-output
|
|
111
|
-
fi
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
gtd owns the wrapper's shape (output capture, the empty-failure sentinel, the
|
|
115
|
-
target file), templated from config at emit time — the driver stays dumb. This
|
|
116
|
-
keeps the machine the single author of check mechanics while moving the
|
|
117
|
-
_execution_ out.
|
|
118
|
-
|
|
119
|
-
### 3.3 Mechanics in the script, semantics at capture
|
|
120
|
-
|
|
121
|
-
A crucial split, learned from the Phase C work: the script must encode only
|
|
122
|
-
**mechanics** (did it exit non-zero; what was the output), never **semantics**
|
|
123
|
-
(is the budget spent; is this an escalation). Semantics belong in the capture
|
|
124
|
-
rules, decided from the pending diff plus the trailer vector at step time:
|
|
125
|
-
|
|
126
|
-
```ts
|
|
127
|
-
testing: {
|
|
128
|
-
kind: "prompt",
|
|
129
|
-
awaits: "check",
|
|
130
|
-
prompts: { check: "@run-test" },
|
|
131
|
-
captureRules: [
|
|
132
|
-
// red at the cap — same file, different label; the escalated label's
|
|
133
|
-
// routing chain renames FEEDBACK.md → ERRORS.md as bookkeeping
|
|
134
|
-
{ when: (p) => p.feedbackPresent && p.counters.testFixCount >= p.fixAttemptCap,
|
|
135
|
-
label: "escalated", stamp: carry },
|
|
136
|
-
// red below the cap
|
|
137
|
-
{ when: (p) => p.feedbackPresent, label: "test-failed", stamp: tPlus1 },
|
|
138
|
-
// green — the outcome labels already decided at write time today,
|
|
139
|
-
// now decided at capture of the check's empty turn
|
|
140
|
-
{ empty: true, when: (p) => p.packagesPresent && !forceApprove(p), label: "agentic-review" },
|
|
141
|
-
{ empty: true, when: (p) => p.packagesPresent, label: "close-package-approved" },
|
|
142
|
-
{ empty: true, label: "tests-green", stamp: h0 },
|
|
143
|
-
],
|
|
144
|
-
}
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
Why this split matters: if the script encoded `capReached` (as `perform()` does
|
|
148
|
-
today), a script emitted before the budget changed and executed after would
|
|
149
|
-
write the wrong file — a stale-script race with two sources of truth. With
|
|
150
|
-
semantics at capture, a stale script is harmless: the mechanics it performs
|
|
151
|
-
(write FEEDBACK.md with the output) are timeless, and the label — the part that
|
|
152
|
-
steers — is decided against the _current_ trailer when the turn is captured.
|
|
153
|
-
This is the δ discipline applied once more: the diff carries facts, the capture
|
|
154
|
-
encodes meaning.
|
|
155
|
-
|
|
156
|
-
### 3.4 Wire format
|
|
157
|
-
|
|
158
|
-
Check turns land as ordinary turn commits in the existing grammar:
|
|
159
|
-
|
|
160
|
-
```
|
|
161
|
-
gtd(agent): building ← the build turn
|
|
162
|
-
gtd: testing ← routing: rest, awaiting check (label state today)
|
|
163
|
-
gtd(check): test-failed ← the check's captured turn (FEEDBACK.md in its diff)
|
|
164
|
-
|
|
165
|
-
Gtd-Counters: t=1 r=0 h=0
|
|
166
|
-
gtd(agent): fixing ← the fixer's turn
|
|
167
|
-
gtd: testing
|
|
168
|
-
gtd(check): agentic-review ← green, packages, threshold not reached
|
|
169
|
-
|
|
170
|
-
Gtd-Counters: t=1 r=0 h=0
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
`git log --oneline` becomes a _complete_ trace: today the check outcome appears
|
|
174
|
-
as a bare machine label; under this model it is attributed — you can see that
|
|
175
|
-
the check actor ran, when, and what it concluded, exactly as for humans and
|
|
176
|
-
agents. Old histories remain inert boundaries (closed-set rule), and the trailer
|
|
177
|
-
scheme carries over unchanged.
|
|
178
|
-
|
|
179
|
-
## 4. What each current piece becomes
|
|
180
|
-
|
|
181
|
-
| Today | Under the scripted actor |
|
|
182
|
-
| ------------------------------------------------- | ------------------------------------------------------------------------------ |
|
|
183
|
-
| `runTest` / `runHealthCheck` edge actions | deleted — `testing`/`health-check` become prompt states awaiting `check` |
|
|
184
|
-
| `TestRunner` service (spawn, timeout, output) | deleted from gtd; wrapper script + driver responsibility |
|
|
185
|
-
| exit-code branch in `perform()` | capture rules on the check state (diff + trailer) |
|
|
186
|
-
| `onGreen` decided at dispatch | green outcome decided at capture of the check's empty turn — same write moment |
|
|
187
|
-
| `capReached` baked into the action | capture-rule guard reading `p.counters` — no stale-script race |
|
|
188
|
-
| empty-failure sentinel in TS | templated into the wrapper script |
|
|
189
|
-
| `gtd step agent` blocks for the test suite | `gtd` returns instantly; the loop runs the script between invocations |
|
|
190
|
-
| `@inmem` e2e runs real `testCommand` subprocesses | scenarios write FEEDBACK.md by hand and `gtd step check` — fully in-memory |
|
|
191
|
-
| loop protocol: agent-only dispatch | uniform: read `gtd next --json`, dispatch on the awaited actor's kind |
|
|
192
|
-
| `testCommand` consumed by `TestRunner` | consumed by the prompt template (config → script text) |
|
|
193
|
-
|
|
194
|
-
And the generalization dividend: **any state can declare a scripted gate.** A
|
|
195
|
-
lint pass before review, a deploy verification after close-package, a schema
|
|
196
|
-
check between grilling and architecting — all become one `StateDef` with
|
|
197
|
-
`awaits: "check"` (or a second scripted actor with its own command), zero new
|
|
198
|
-
engine code. This is the missing piece of the original "completely configurable
|
|
199
|
-
state machine" goal: mechanical gates were the one transition type a user
|
|
200
|
-
couldn't add through configuration.
|
|
201
|
-
|
|
202
|
-
## 5. What this buys
|
|
203
|
-
|
|
204
|
-
1. **The machine's last impurity is gone.** `perform()` becomes pure git/fs
|
|
205
|
-
bookkeeping; the Effect graph loses `TestRunner` and `CommandExecutor`; the
|
|
206
|
-
always-clean invariant becomes structural (nothing inside gtd can block or
|
|
207
|
-
die mid-run with a half-written tree).
|
|
208
|
-
2. **`gtd` becomes instant and deterministic.** Every invocation is a pure
|
|
209
|
-
resolve + bookkeeping hop. All nondeterminism (LLM, human, subprocess) is
|
|
210
|
-
outside — which is also exactly what makes the property-test story and the
|
|
211
|
-
`@inmem` tier stronger.
|
|
212
|
-
3. **One protocol.** The outer loop stops special-casing: every iteration is
|
|
213
|
-
"ask `gtd next` who's up and what the prompt is; produce that actor's turn;
|
|
214
|
-
`gtd step <actor>`". Timeouts, retries, and sandboxing of the test command
|
|
215
|
-
move to the driver, which already owns them for the agent.
|
|
216
|
-
4. **Attributed history.** Check outcomes are turns with an author, a diff, and
|
|
217
|
-
a trailer — `git log` reads as a complete conversation between three actors.
|
|
218
|
-
5. **Configurable mechanical gates** (see §4) — without touching gtd.
|
|
219
|
-
|
|
220
|
-
## 6. Where it falls down
|
|
221
|
-
|
|
222
|
-
1. **It breaks the CLI contract — this is NOT behavior-preserving.** Every
|
|
223
|
-
driver and all ~40 feature files change shape: `gtd step agent` no longer
|
|
224
|
-
runs the tests; a check that used to be one invocation is now three
|
|
225
|
-
(`step agent` → run script → `step check`). Unlike the δ refactor, there is
|
|
226
|
-
no way to hide this behind the same e2e suite. It is a major version with a
|
|
227
|
-
migration story, not an internal refactor.
|
|
228
|
-
2. **Human ergonomics regress without a driver.** Today a human at `escalate`
|
|
229
|
-
runs `gtd step human` and the re-test happens. Under this model they must
|
|
230
|
-
copy-paste a script and run a second command. Mitigation: a thin optional
|
|
231
|
-
convenience — `gtd run` (or the loop skill) that executes the _emitted_ check
|
|
232
|
-
script verbatim and immediately steps the check actor. The machine still
|
|
233
|
-
never executes; the convenience wrapper does, behind an explicit opt-in
|
|
234
|
-
command whose only input is gtd's own emitted script. This preserves the
|
|
235
|
-
purity boundary while keeping the two-keystroke UX.
|
|
236
|
-
3. **The idle carve-out gets awkward.** Idle today awaits the human, and a human
|
|
237
|
-
step re-runs the health check inline. If idle awaits `check` instead, a bare
|
|
238
|
-
`gtd step human` at idle would be refused — hostile. Options: keep the idle
|
|
239
|
-
carve-out (a human step at idle _is_ the check turn's trigger via the
|
|
240
|
-
convenience wrapper), or split idle (rest for human) from a health-check
|
|
241
|
-
state (rest for check) that the human's empty idle turn chains into. Needs a
|
|
242
|
-
decision; neither is free.
|
|
243
|
-
4. **Generated bash is a liability.** Quoting the configured command, platform
|
|
244
|
-
variance (POSIX sh vs bash vs Windows), output-size limits — all move from
|
|
245
|
-
unit-tested TypeScript into templated script text that can only be tested by
|
|
246
|
-
executing it. The wrapper must stay tiny and boring; anything clever belongs
|
|
247
|
-
in capture rules.
|
|
248
|
-
5. **The prompt channel now carries executable code.** `testCommand` is already
|
|
249
|
-
arbitrary code execution by config, so the _trust_ is unchanged — but the
|
|
250
|
-
_transport_ changes: drivers must execute only the script emitted by
|
|
251
|
-
`gtd next` for a scripted actor, never script-looking content from
|
|
252
|
-
agent-authored files. The `--json` output should carry the actor kind and the
|
|
253
|
-
script in a dedicated field so drivers never have to parse prose.
|
|
254
|
-
6. **Two extra hops per check.** Negligible for an agent loop (the test run
|
|
255
|
-
dwarfs them), noticeable for scripted gates used as fast validations. The
|
|
256
|
-
fixpoint chaining could allow `step check` to be invoked by the driver
|
|
257
|
-
immediately after the script with no intervening `next`, keeping it to one
|
|
258
|
-
extra invocation.
|
|
259
|
-
7. **A non-cooperating driver can stall the machine.** Today a check cannot be
|
|
260
|
-
skipped: `step agent` runs it, period. Under this model a driver could keep
|
|
261
|
-
stepping other actors... except it can't — the rest awaits `check`, and every
|
|
262
|
-
other actor is refused out-of-turn. The stall is real (nothing runs the
|
|
263
|
-
script), but the safety property (nothing _bypasses_ the gate) is exactly as
|
|
264
|
-
strong as today's, enforced by the same refusal mechanism.
|
|
265
|
-
|
|
266
|
-
## 7. Verdict
|
|
267
|
-
|
|
268
|
-
This is the natural completion of the δ program: after Phase C, the check's
|
|
269
|
-
_decision_ already lives at write time and its _budget_ already rides on
|
|
270
|
-
trailers — the only thing still inside the machine is the subprocess itself.
|
|
271
|
-
Moving it out makes every rest a prompt and every actor a peer, at the cost of a
|
|
272
|
-
breaking CLI/protocol change and a real (but boundable) bash-templating
|
|
273
|
-
liability.
|
|
274
|
-
|
|
275
|
-
If pursued, the phasing writes itself:
|
|
276
|
-
|
|
277
|
-
- **Phase 1 — dual-mode:** add the `scripted` kind, the check states' capture
|
|
278
|
-
rules, and `gtd step check`; keep `runTest` as a deprecated inline path so the
|
|
279
|
-
existing e2e suite stays green while a parallel suite drives the new protocol.
|
|
280
|
-
- **Phase 2 — flip the default:** loop skill dispatches on actor kind; `gtd run`
|
|
281
|
-
convenience lands; feature files migrate scenario by scenario (each check hop
|
|
282
|
-
becomes script-run + step).
|
|
283
|
-
- **Phase 3 — delete:** `TestRunner`, `runTest`/`runHealthCheck`, and the
|
|
284
|
-
idle/testing carve-outs (per the idle decision taken in Phase 1).
|
|
285
|
-
|
|
286
|
-
The one decision worth taking _before_ any of this: §3.3's mechanics/semantics
|
|
287
|
-
split. Even today's inline check could be restructured so the label is decided
|
|
288
|
-
purely from (written file, trailer) — that is the piece that makes the model
|
|
289
|
-
safe, and it is adoptable independently of everything else here.
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
# Design: state ↔ steering-file association (`file:` + `mode:`)
|
|
2
|
-
|
|
3
|
-
> Status: LANDED (2026-07-22). SUPERSEDED IN ONE RESPECT: `mode:` is no longer a
|
|
4
|
-
> closed `qa`/`review` vocabulary — those two are now gtd's BUILT-IN modes and a
|
|
5
|
-
> workflow may declare its own (a `format:`/`validate:` shell-command pair) in a
|
|
6
|
-
> `modes:` map, see [pluggable-steering-modes.md](pluggable-steering-modes.md).
|
|
7
|
-
> Everything below about the state↔file association and the LSP's config-driven
|
|
8
|
-
> dispatch still holds (the LSP understands the two built-ins only). Goal:
|
|
9
|
-
> retain the v2 relationship between states and steering files — but as pure
|
|
10
|
-
> workflow CONFIGURATION that works for any state machine. Each state may
|
|
11
|
-
> declare one associated `file:` (an Eta template, so filenames live in `vars:`
|
|
12
|
-
> and are never repeated) and a `mode:` (`qa` | `review`) naming the file's
|
|
13
|
-
> FORMAT. The LSP stops hardcoding basenames: it reads the gtd config from the
|
|
14
|
-
> workspace (cwd/parent lookup, same cosmiconfig search the CLI uses), maps
|
|
15
|
-
> rendered file paths to modes, and regains the jump-to-relevant- file command —
|
|
16
|
-
> full v2 functionality, zero hardcoded workflow knowledge.
|
|
17
|
-
|
|
18
|
-
## 1. The two new state properties
|
|
19
|
-
|
|
20
|
-
| Property | Meaning |
|
|
21
|
-
| -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
22
|
-
| `file:` | Optional. THE steering file this state is about — the file a human/editor should look at while the machine rests here. An **Eta template** rendered with the standard `TemplateContext` (in practice `<%= it.vars.… %>`; the full context is available). Forbidden on commit states (never at rest), must render non-empty. |
|
|
23
|
-
| `mode:` | Optional, requires `file:`. The file's FORMAT, from a closed, documented vocabulary: `qa` (the open-questions format — `## Open Questions` / `## Answered Questions` sections, `###` question with a free-form body, status positional) or `review` (the checkbox review format — `# Review:` header, `##` chunks, `- [ ]` pointers). Unknown values are a load error (typos must not silently disable editor support). The ENGINE never branches on it — like `model`, it is emitted data; only the LSP interprets it. |
|
|
24
|
-
|
|
25
|
-
Multiple states may (and in the default, do) share one file+mode — the mode
|
|
26
|
-
describes the file, the association is per state.
|
|
27
|
-
|
|
28
|
-
**Emission:** `gtd next --json` and `gtd status --json` gain optional `file`
|
|
29
|
-
(rendered) and `mode` fields, omitted when unset (exactly like `model`); plain
|
|
30
|
-
`gtd status` prints `File:`/`Mode:` lines when set. This gives drivers and
|
|
31
|
-
editors the jump target without speaking LSP, and gives e2e an assertion
|
|
32
|
-
surface.
|
|
33
|
-
|
|
34
|
-
**Engine/compiler touch points:** `StateDef` gains `file?`/`mode?`;
|
|
35
|
-
`validateDefinition` gains the rules above (non-empty strings, mode ∈ {qa,
|
|
36
|
-
review}, mode requires file, both forbidden on commit states, errors
|
|
37
|
-
aggregated); `PatternConfig` compiles the two keys (KNOWN_STATE_KEYS, string
|
|
38
|
-
checks); `Edge`/`program` render `file:` at rest exactly where `model` is
|
|
39
|
-
rendered (a render failure errors like a model render failure).
|
|
40
|
-
|
|
41
|
-
## 2. The bundled default gets the association
|
|
42
|
-
|
|
43
|
-
New `vars:` entries (single source for the filenames):
|
|
44
|
-
|
|
45
|
-
```yaml
|
|
46
|
-
vars:
|
|
47
|
-
testCommand: npm test
|
|
48
|
-
todoFile: .gtd/TODO.md
|
|
49
|
-
reviewFile: .gtd/REVIEW.md
|
|
50
|
-
feedbackFile: .gtd/FEEDBACK.md
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
Per-state mapping (10 states):
|
|
54
|
-
|
|
55
|
-
| States | `file:` | `mode:` |
|
|
56
|
-
| ---------------------------------------------- | ----------------------------- | ------------------------- |
|
|
57
|
-
| `grilling`, `grilling-answer`, `building` | `<%= it.vars.todoFile %>` | `qa` |
|
|
58
|
-
| `reviewing`, `await-review`, `review-deciding` | `<%= it.vars.reviewFile %>` | `review` |
|
|
59
|
-
| `fixing`, `escalate` | `<%= it.vars.feedbackFile %>` | — (plain text, no format) |
|
|
60
|
-
| `idle`, `checking` | — | — |
|
|
61
|
-
|
|
62
|
-
Prompts and scripts that already name these files switch to the vars
|
|
63
|
-
(`<%~ it.vars.todoFile %>` etc.) so each filename has ONE source of truth in
|
|
64
|
-
templates. **Known limitation, documented:** `on` pattern keys are NOT Eta
|
|
65
|
-
templates — the default's patterns keep literal `.gtd/…` paths, so repointing a
|
|
66
|
-
filename var (`.gtdrc`/`GTD_`) without also overriding the workflow's patterns
|
|
67
|
-
desyncs the machine. The vars are a DRY mechanism inside templates and the
|
|
68
|
-
state↔file association, not a rename switch. (Making pattern keys var-aware at
|
|
69
|
-
compile time is noted as possible future work.)
|
|
70
|
-
|
|
71
|
-
The advanced flow of the bundled unified template (see STATES.md §10) gains the
|
|
72
|
-
same `file:`/`mode:` annotations (it is the LSP-heavy flow), including
|
|
73
|
-
`architectureFile` for its architecting phases (also `qa`-format).
|
|
74
|
-
|
|
75
|
-
## 3. The LSP becomes config-driven
|
|
76
|
-
|
|
77
|
-
- **Config discovery:** the server locates the gtd config by the SAME
|
|
78
|
-
cosmiconfig search the CLI uses, from the workspace root (initialize
|
|
79
|
-
`workspaceFolders`/`rootUri`), falling back to the open document's directory
|
|
80
|
-
walking upward. Reuse `ConfigService`'s loading/compiling — no second config
|
|
81
|
-
code path. Config is (re)loaded lazily per request (it is small); no watcher
|
|
82
|
-
needed for v1.
|
|
83
|
-
- **Path→mode dispatch:** compile the definition, render every state's `file:`
|
|
84
|
-
(vars-layer context; the LSP process's env supplies `GTD_` overrides like any
|
|
85
|
-
invocation), and build a map of absolute rendered paths → mode. Document
|
|
86
|
-
features dispatch on that map: `qa` files get question symbols + parser-error
|
|
87
|
-
diagnostics; `review` files get one headline symbol per chunk that still has
|
|
88
|
-
an unchecked hunk (an outline of the packages left to review — no hunk
|
|
89
|
-
children) + check/uncheck code actions + diagnostics. Conflicting modes for
|
|
90
|
-
one path: first declaring state wins, log a warning. **Fallback:** when NO
|
|
91
|
-
config resolves (or a `.gtd` file isn't mapped), keep today's basename
|
|
92
|
-
dispatch (`TODO.md` → qa, `REVIEW.md` → review) so the server still works
|
|
93
|
-
standalone.
|
|
94
|
-
- **The jump command returns:** `gtd.openSteeringFile` — resolve the CURRENT
|
|
95
|
-
state exactly like the CLI (config + git HEAD via the existing Edge helpers;
|
|
96
|
-
this re-adds the git/config wiring the v2 server had), render its `file:`, and
|
|
97
|
-
`ShowDocumentRequest` it; a state with no `file:` yields an informational
|
|
98
|
-
message naming the state (v2 behavior). Registered as an executeCommand;
|
|
99
|
-
documented for editor keybinding.
|
|
100
|
-
|
|
101
|
-
## 4. Change chart
|
|
102
|
-
|
|
103
|
-
| Area | Change |
|
|
104
|
-
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
105
|
-
| `src/PatternMachine.ts` | `StateDef.file?/mode?` + validation rules (closed mode set). |
|
|
106
|
-
| `src/PatternConfig.ts` | Compile `file`/`mode` keys. |
|
|
107
|
-
| `src/Edge.ts` / `src/program.ts` | Render `file:` at rest; `file`/`mode` in both `--json` payloads + plain `status` lines. |
|
|
108
|
-
| `src/Lsp.ts` | Config discovery, path→mode dispatch (with basename fallback), `gtd.openSteeringFile`. |
|
|
109
|
-
| `src/workflows/default.yaml` | §2's vars + per-state `file:`/`mode:`; templates switch to filename vars. |
|
|
110
|
-
| Docs | STATES.md property table + §10; configuration.md (`file:`/`mode:` section + mode vocabulary + the pattern-literal limitation); cli.md JSON contracts; the bundled unified template's advanced-flow annotations (STATES.md §10); upgrading note (jump command is back, config-driven). |
|
|
111
|
-
| Tests | Unit: validation (mode vocabulary, mode-requires-file, commit-state prohibition, aggregation), compile, `file:` render + failure path; LSP path→mode dispatch, fallback, openSteeringFile (pure helpers). E2e: `driver-json-status.feature` file/mode emission (present + omitted); `lsp.feature` @live — documentSymbol served for a CUSTOM-named qa file mapped via a real `.gtdrc` (proving config-driven dispatch), and the jump command returning the rendered path at a known state. |
|
|
112
|
-
| Live verification | Scratch repo with a custom workflow whose `file:` uses a var: `gtd status` shows the rendered `File:`/`Mode:`; LSP smoke against the custom filename; default workflow spot-check (`grilling` → `File: .gtd/TODO.md`, `Mode: qa`). |
|
|
113
|
-
|
|
114
|
-
## 5. Out of scope
|
|
115
|
-
|
|
116
|
-
- Eta-rendering of `on` pattern keys (the rename-switch story) — future work,
|
|
117
|
-
noted in §2.
|
|
118
|
-
- New LSP modes beyond `qa`/`review` — the vocabulary can grow with new formats;
|
|
119
|
-
adding one means: parser (spec), a `gtd validate` dispatch arm for the mode,
|
|
120
|
-
LSP dispatch arm, docs.
|