@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,437 +0,0 @@
|
|
|
1
|
-
# Research: static validation of workflow definitions
|
|
2
|
-
|
|
3
|
-
> Status: PARTIALLY IMPLEMENTED. Scope decision: a workflow is bound to a
|
|
4
|
-
> project and edited as a project-wide change (never mid-process), so the
|
|
5
|
-
> mid-flight/backward-compat concerns in §6 don't apply, and no warning channel
|
|
6
|
-
> is needed — implemented checks are plain errors. Landed: the full `workflow:`
|
|
7
|
-
> typing in `schema.json` (§6.1, via `jsonSchema` annotations in
|
|
8
|
-
> `src/ConfigSchema.ts`) and the unreachable-state error (§3.1, as
|
|
9
|
-
> `validateReachability` in `src/PatternMachine.ts` — invalid `on`/
|
|
10
|
-
> `retry.otherwise` targets were already errors). Everything else in this
|
|
11
|
-
> document is deliberately on hold.
|
|
12
|
-
|
|
13
|
-
## 1. What is validated today (baseline)
|
|
14
|
-
|
|
15
|
-
Two layers run at config-load time, both aggregate-all-findings-then-throw
|
|
16
|
-
(`compileWorkflowConfig` merges its shape errors with `validateDefinition`'s
|
|
17
|
-
findings into one error — a bad config fails loudly at load, never at step
|
|
18
|
-
time):
|
|
19
|
-
|
|
20
|
-
- **Config shape** (`src/PatternConfig.ts`): unknown top-level/state/retry keys,
|
|
21
|
-
wrong types, unreadable `./` file references, non-scalar `vars:` values.
|
|
22
|
-
- **Definition rules** (`src/PatternMachine.ts` `validateDefinition`): at least
|
|
23
|
-
one state; exactly one non-commit `initial`; exactly one content kind per
|
|
24
|
-
state; commit states carry no `actor`/`on`/`model`/`file`/`mode`; non-commit
|
|
25
|
-
states carry an `actor`; every `on` pattern parses; every `on` target and
|
|
26
|
-
`retry.otherwise` names a defined state; `retry.max` is a non-negative
|
|
27
|
-
integer; `file`/`model` non-empty; `mode` ∈ {qa, review} and requires `file`.
|
|
28
|
-
|
|
29
|
-
Every one of these checks is **local** — a rule over one state (or one field) at
|
|
30
|
-
a time. Nothing today looks at the definition as a _graph_. The v2 design doc
|
|
31
|
-
(`configurable-state-machine.md` §3.11) planned exactly that class of checks
|
|
32
|
-
(totality, reachability, ambiguity, livelock bounds); the v3 rewrite shipped
|
|
33
|
-
without them.
|
|
34
|
-
|
|
35
|
-
## 2. The gap: definitions that validate clean but are broken
|
|
36
|
-
|
|
37
|
-
Each of these passes today's validation:
|
|
38
|
-
|
|
39
|
-
1. **Unreachable state** — defined, valid, but no `on` target or
|
|
40
|
-
`retry.otherwise` ever names it (typically a rename that missed the edges).
|
|
41
|
-
It silently never runs.
|
|
42
|
-
2. **Dead-end state** — a non-commit state with no `on` map (or an empty one).
|
|
43
|
-
Once entered, every step is a no-op (clean) or refusal (dirty) forever; the
|
|
44
|
-
process can never leave.
|
|
45
|
-
3. **Trap cycle** — a group of states that reach each other but from which no
|
|
46
|
-
terminal (a commit state, or re-entry into the initial state — the bundled
|
|
47
|
-
default's only "end") is reachable. The process runs but can never complete.
|
|
48
|
-
4. **Unbounded agent loop (livelock)** — a cycle consisting only of agent/check
|
|
49
|
-
states with no `retry` cap on any member. The default's `fixing ↔ checking`
|
|
50
|
-
loop is safe only because `fixing` carries
|
|
51
|
-
`retry: {max: 3, otherwise: escalate}`; delete that key and the machine
|
|
52
|
-
validates clean but can ping-pong forever with no human in the loop.
|
|
53
|
-
5. **Shadowed `on` row** — `on` is first-match-wins, so
|
|
54
|
-
`{"* **": a, "A .gtd/FEEDBACK.md": b}` never routes to `b`: the catch-all
|
|
55
|
-
subsumes the specific row. Dead rows are silent today.
|
|
56
|
-
6. **`retry.otherwise` cycle / self-reference** — `applyRetry` terminates at
|
|
57
|
-
runtime via its `visited` guard, but the guard's "accept the repeated target
|
|
58
|
-
as final" behavior is almost certainly not what the author meant; the config
|
|
59
|
-
is accepted silently.
|
|
60
|
-
7. **Pointless `retry`** — retry redirection applies when a state is entered _as
|
|
61
|
-
a transition target_; a `retry` on a state nothing targets never fires.
|
|
62
|
-
8. **Template errors discovered at step time** — Eta syntax errors in
|
|
63
|
-
`prompt`/`script`/`message`/`commit`/`file` templates surface only when that
|
|
64
|
-
state is first rendered, contradicting the "fails at load, never at step
|
|
65
|
-
time" contract everything else honors.
|
|
66
|
-
9. **The documented `vars` ↔ `on` desync** (`default.yaml`'s KNOWN LIMITATION) —
|
|
67
|
-
`on` patterns are literal strings while `file:` and content templates read
|
|
68
|
-
`it.vars.todoFile` etc.; repointing a var moves what templates read/write
|
|
69
|
-
without moving what patterns match.
|
|
70
|
-
|
|
71
|
-
## 3. Algorithm survey
|
|
72
|
-
|
|
73
|
-
The good news: a gtd workflow is a small, explicit, finite directed graph (the
|
|
74
|
-
bundled default: 12 nodes, ~25 edges; a custom one is the same order of
|
|
75
|
-
magnitude). Every algorithm below is textbook material and costs microseconds at
|
|
76
|
-
this size — there is no need for a model checker, SAT solver, or symbolic
|
|
77
|
-
anything. The edge set for all graph checks is `on` targets ∪ `retry.otherwise`
|
|
78
|
-
targets.
|
|
79
|
-
|
|
80
|
-
### 3.1 Reachability — accessible states (BFS/DFS from the initial state)
|
|
81
|
-
|
|
82
|
-
The classic "useless states" removal from automata theory, first half: a state
|
|
83
|
-
is _accessible_ iff some path from the start state reaches it. One BFS/DFS from
|
|
84
|
-
the `initial` state over the edge set, O(V+E). Anything not visited is finding
|
|
85
|
-
#1 (unreachable).
|
|
86
|
-
|
|
87
|
-
**gtd nuance:** history is an attributed state trace, and `resolveState` accepts
|
|
88
|
-
_any_ hand-authored `gtd(<actor>): <state>` subject naming a defined non-commit
|
|
89
|
-
state and a declared actor — so an edge-unreachable state is still _enterable_
|
|
90
|
-
by writing a commit by hand. That makes unreachability a **warning**, not a hard
|
|
91
|
-
error: it is almost always a typo'd rename, but a deliberate "manual entry
|
|
92
|
-
point" state is expressible and legal. (It also means an unreachable state's
|
|
93
|
-
`actor` still widens the closed-world actor vocabulary — one more reason the
|
|
94
|
-
finding deserves surfacing.)
|
|
95
|
-
|
|
96
|
-
### 3.2 Co-reachability — can-complete (reverse BFS from terminals)
|
|
97
|
-
|
|
98
|
-
The second half of the same textbook construction: a state is _co-accessible_
|
|
99
|
-
iff a terminal is reachable from it; an automaton restricted to accessible ∩
|
|
100
|
-
co-accessible states is called _trim_. Reverse the edges, BFS from the terminal
|
|
101
|
-
set, flag unvisited states.
|
|
102
|
-
|
|
103
|
-
**gtd's terminal set** is `commit` states ∪ the `initial` state — the bundled
|
|
104
|
-
default has no commit state at all; its cycle "ends" by re-entering `idle` (the
|
|
105
|
-
process-boundary rule in `computeProcessRun`). A reachable state that is not
|
|
106
|
-
co-accessible is a trap (finding #3); a state with no outgoing edges at all
|
|
107
|
-
(finding #2) is the degenerate case but deserves its own, more specific message.
|
|
108
|
-
|
|
109
|
-
This is also exactly what **workflow-net soundness** (van der Aalst; the Woflan
|
|
110
|
-
validator) checks in the Petri-net world: _option to complete_ (from every
|
|
111
|
-
reachable marking, the end is reachable) plus _no dead transitions_. gtd's
|
|
112
|
-
machine has no concurrency/tokens, so full soundness collapses to the two
|
|
113
|
-
reachability passes above — the whole "soundness" property is two BFS runs here.
|
|
114
|
-
|
|
115
|
-
### 3.3 Livelock heuristic — SCC analysis (Tarjan)
|
|
116
|
-
|
|
117
|
-
Full progress proofs are out of reach (whether a pattern fires depends on what
|
|
118
|
-
an agent writes), but the v2 plan's "livelock bounds" idea translates cleanly:
|
|
119
|
-
compute strongly connected components (Tarjan, O(V+E)), and for each cyclic SCC
|
|
120
|
-
check that it contains an _escape guarantee_:
|
|
121
|
-
|
|
122
|
-
- a state whose `actor` is a human-ish rest point (the machine stops and waits —
|
|
123
|
-
a human can always steer out), **or**
|
|
124
|
-
- a member with a `retry` cap whose `otherwise` leaves the SCC (bounded
|
|
125
|
-
redirection out).
|
|
126
|
-
|
|
127
|
-
An SCC with neither — e.g. `building → checking → fixing → checking` with no cap
|
|
128
|
-
— is a machine that can loop autonomously forever. Since "human-ish" is not
|
|
129
|
-
knowable (actors are open-vocabulary strings; the engine deliberately blesses no
|
|
130
|
-
names), the practical formulation is: **flag any cycle in which no member state
|
|
131
|
-
either awaits a different actor than the cycle's dominant one or carries a retry
|
|
132
|
-
cap escaping the cycle** — or, simpler and honest, make it a warning listing the
|
|
133
|
-
cycle and what would bound it. This mirrors how linters handle undecidable
|
|
134
|
-
properties: name the risk, don't pretend to prove it.
|
|
135
|
-
|
|
136
|
-
### 3.4 Shadowed/dead `on` rows — pattern subsumption (match-arm usefulness)
|
|
137
|
-
|
|
138
|
-
`on` is an ordered first-match-wins list — semantically identical to ML/Rust
|
|
139
|
-
`match` arms, where compilers warn on unreachable arms using the _usefulness_
|
|
140
|
-
algorithm (Maranget, "Warnings for pattern matching"). gtd's pattern grammar is
|
|
141
|
-
far simpler than ML patterns, so full usefulness reduces to pairwise
|
|
142
|
-
subsumption:
|
|
143
|
-
|
|
144
|
-
- Row _j_ is dead iff some earlier row _i_ fires on every diff row _j_ fires on.
|
|
145
|
-
Matching is existential over the change set, so language inclusion is
|
|
146
|
-
sufficient: if row _i_'s (status, glob) language ⊇ row _j_'s, any change
|
|
147
|
-
matching _j_ also matches _i_, hence _i_ fires first.
|
|
148
|
-
- `C` is disjoint from every diff row (clean vs. dirty), so it can only be
|
|
149
|
-
shadowed by an earlier duplicate `C`.
|
|
150
|
-
- Status subsumption is trivial (`*` ⊇ `A`/`M`/`D`; equal otherwise). Glob
|
|
151
|
-
inclusion over the grammar {literal chars, `*`, `**`} is decidable — both
|
|
152
|
-
compile to regular languages (`globToRegExp` already builds the regexes), and
|
|
153
|
-
regular-language inclusion is decidable via product construction. In practice
|
|
154
|
-
a direct segment-wise structural check (the approach gitignore/ file-watcher
|
|
155
|
-
tooling uses) covers it without regex machinery, and even the cheap sufficient
|
|
156
|
-
subset catches the real-world cases:
|
|
157
|
-
- an exact duplicate pattern string earlier in the list (see also §4.1 — YAML
|
|
158
|
-
may reject or silently drop these before the compiler sees them),
|
|
159
|
-
- `* **` (or `* <glob>` with the identical glob) declared before a
|
|
160
|
-
more-specific row,
|
|
161
|
-
- same glob, earlier row's status `*` vs. later row's specific letter.
|
|
162
|
-
|
|
163
|
-
This one is a hard **error** candidate: a dead row is never intentional, and the
|
|
164
|
-
fix (reorder) is mechanical.
|
|
165
|
-
|
|
166
|
-
Deliberately **not** worth adopting from the match-checking world:
|
|
167
|
-
_exhaustiveness_. A dirty tree matching no row is a refusal **by design**
|
|
168
|
-
(AGENTS.md: "something happened that nothing recognizes" is a signal distinct
|
|
169
|
-
from a no-op), and a clean step with no `C` row is the documented default no-op.
|
|
170
|
-
The event space is intentionally non-total; a totality lint would fight the
|
|
171
|
-
semantics.
|
|
172
|
-
|
|
173
|
-
### 3.5 Retry-chain analysis — cycle detection on a functional graph
|
|
174
|
-
|
|
175
|
-
`retry.otherwise` edges form a functional graph (≤1 outgoing edge per state), so
|
|
176
|
-
cycle detection is a trivial colored DFS (or Floyd's, but at this size DFS with
|
|
177
|
-
a visited set is clearer — `applyRetry` already contains the runtime version of
|
|
178
|
-
it). Findings: `otherwise` chains that cycle (error — the runtime guard's
|
|
179
|
-
"accept the repeated target" fallback is a documented we-must-terminate
|
|
180
|
-
behavior, not a meaning), `otherwise: <self>` (degenerate case of the same), and
|
|
181
|
-
a `retry` on a state that no `on` edge targets (warning — it can never fire, per
|
|
182
|
-
`applyRetry`'s entry-as-target semantics).
|
|
183
|
-
|
|
184
|
-
### 3.6 Template validation — compile at load time
|
|
185
|
-
|
|
186
|
-
Eta exposes compilation separately from rendering: `compile()` every
|
|
187
|
-
`prompt`/`script`/`message`/`commit`/`file` string at load and report syntax
|
|
188
|
-
errors as load errors, closing gap #8 without executing anything impure
|
|
189
|
-
(rendering stays an edge concern; compiling is pure).
|
|
190
|
-
|
|
191
|
-
One step further, the **undefined-variable lint**: scan template source for
|
|
192
|
-
`it.vars.<name>` references and compare against the merged
|
|
193
|
-
workflow-`vars:`/rc-`vars:` keys. Because a third layer (`GTD_*` env vars) can
|
|
194
|
-
legitimately supply a name at run time, an unknown name is a **warning**, not an
|
|
195
|
-
error. (A full "render with a tracking proxy and record misses" approach also
|
|
196
|
-
works but executes template code at load — scanning the source is safer and
|
|
197
|
-
catches the common typo just as well.)
|
|
198
|
-
|
|
199
|
-
### 3.7 Domain cross-checks (gtd-specific lints)
|
|
200
|
-
|
|
201
|
-
Not from the literature — from this codebase's own documented failure modes:
|
|
202
|
-
|
|
203
|
-
- **`file:` ↔ `on` consistency** (gap #9): render each state's `file:` template
|
|
204
|
-
with the workflow's _declared default_ vars (pure — the defaults are right
|
|
205
|
-
there in the definition) and warn when the rendered path is matched by none of
|
|
206
|
-
the workflow's `on` globs. This turns `default.yaml`'s KNOWN LIMITATION
|
|
207
|
-
comment into a machine-checked finding whenever a var repoint desyncs
|
|
208
|
-
templates from patterns. The same check re-run at `gtd status` time with the
|
|
209
|
-
_actual_ merged vars (env layer included) closes the runtime half of the gap.
|
|
210
|
-
- **Actor typo proximity**: the actor vocabulary is open by design, so `agnet`
|
|
211
|
-
is a legal actor — but a state awaiting it will never be stepped by the loop
|
|
212
|
-
driving `agent`. A Levenshtein "did you mean" warning when one declared actor
|
|
213
|
-
is within edit distance 1 of another (a pattern lifted from CLI arg parsers
|
|
214
|
-
and `git`'s command suggestions). Warning-only; false positives are possible
|
|
215
|
-
(`qa1`/`qa2`), so never an error.
|
|
216
|
-
|
|
217
|
-
### 3.8 Dynamic validation — model-based testing (complement, not substitute)
|
|
218
|
-
|
|
219
|
-
Since `step` is a pure function, the graph can be _executed_ exhaustively in
|
|
220
|
-
tests: for every state and every `on` row, synthesize a minimal diff that the
|
|
221
|
-
row's pattern matches (invert the glob: replace `*`/`**` with a literal segment)
|
|
222
|
-
and assert `step` routes there — i.e., every declared edge is exercisable. This
|
|
223
|
-
is the state-machine-testing pattern XState ships as `@xstate/graph`
|
|
224
|
-
(`getShortestPaths`/`getSimplePaths` for path coverage) and what the
|
|
225
|
-
model-based-testing literature calls transition coverage. It overlaps §3.4 (a
|
|
226
|
-
shadowed row shows up as an unexercisable edge) but from the behavioral side,
|
|
227
|
-
and it doubles as a regression harness for the pattern grammar itself. Fits
|
|
228
|
-
naturally as a property test over `defaultWorkflowDefinition` rather than a
|
|
229
|
-
load-time check.
|
|
230
|
-
|
|
231
|
-
## 4. Patterns from comparable systems
|
|
232
|
-
|
|
233
|
-
- **Automata theory** — _trim_ construction (accessible ∩ co-accessible),
|
|
234
|
-
§3.1/§3.2. The canonical "early problem detection" for FSMs.
|
|
235
|
-
- **Workflow nets** — soundness (option to complete, no dead transitions);
|
|
236
|
-
degenerates to the same two BFS passes for token-free graphs.
|
|
237
|
-
- **SCXML / W3C statecharts** — conformance validation is exactly this flavor:
|
|
238
|
-
every `target` IDREF resolves, `initial` names a real child, etc. gtd already
|
|
239
|
-
has the local half; the graph half is what's missing.
|
|
240
|
-
- **XState** — beyond TS-level typegen, its ecosystem does reachability via path
|
|
241
|
-
enumeration (`@xstate/graph`) and visual inspection (Stately inspector) rather
|
|
242
|
-
than a lint pass — an argument for also shipping a human-readable graph dump
|
|
243
|
-
(see §5, `gtd lint`).
|
|
244
|
-
- **ML/Rust match checking** — usefulness/unreachable-arm warnings, §3.4; also
|
|
245
|
-
the precedent for making dead-arm a _warning_ in some compilers and an _error_
|
|
246
|
-
in none... but gtd's rows are config, not code paths, and a dead row means a
|
|
247
|
-
whole state may be unreachable — error is defensible.
|
|
248
|
-
- **Model checking (CTL/LTL)** — names the property taxonomy worth keeping:
|
|
249
|
-
today's checks are all _safety_ ("nothing malformed"); §3.2/§3.3 add the cheap
|
|
250
|
-
_liveness_ half ("something good stays reachable"). At 12 nodes,
|
|
251
|
-
`AG EF terminal` is just reverse-BFS — no checker needed.
|
|
252
|
-
- **Linter architecture (ESLint et al.)** — severity tiers (error/warning),
|
|
253
|
-
collect-everything-then-report (gtd already does this for errors), stable
|
|
254
|
-
finding identifiers, and machine-readable output. The missing piece in gtd is
|
|
255
|
-
a **warning channel**: `compileWorkflowConfig` can only throw-or-pass, so
|
|
256
|
-
warning-tier findings (§3.1's manual-entry nuance, §3.3's heuristic, §3.6's
|
|
257
|
-
env-supplied vars, §3.7) currently have nowhere to go.
|
|
258
|
-
|
|
259
|
-
## 5. Recommendation
|
|
260
|
-
|
|
261
|
-
Tier the findings and keep every new check a pure function over
|
|
262
|
-
`WorkflowDefinition` (they need nothing but the definition — they belong in
|
|
263
|
-
`PatternMachine.ts` next to `validateDefinition`, except the Eta compile check,
|
|
264
|
-
which imports Eta and belongs in `PatternTemplates.ts`):
|
|
265
|
-
|
|
266
|
-
**Errors** (extend `validateDefinition`'s existing thrown aggregate; all
|
|
267
|
-
unambiguous, mechanical fixes):
|
|
268
|
-
|
|
269
|
-
1. Non-commit state with no `on` rows (dead end, §3.2's degenerate case).
|
|
270
|
-
2. Reachable state from which no terminal (commit state or initial) is reachable
|
|
271
|
-
(trap, §3.2).
|
|
272
|
-
3. `on` row strictly subsumed by an earlier row (§3.4).
|
|
273
|
-
4. `retry.otherwise` self-reference or cycle (§3.5).
|
|
274
|
-
5. Eta template that does not compile (§3.6).
|
|
275
|
-
|
|
276
|
-
**Warnings** (requires adding a `{ severity, message }` shape and a channel —
|
|
277
|
-
print to stderr at load and/or surface under `gtd status`; a `gtd lint`
|
|
278
|
-
subcommand that prints all findings plus the state graph would follow the
|
|
279
|
-
XState-inspector precedent and cost little):
|
|
280
|
-
|
|
281
|
-
6. State unreachable via edges from `initial` (§3.1 — legal via hand-authored
|
|
282
|
-
subjects, almost always a typo).
|
|
283
|
-
7. Autonomous cycle with no retry-capped escape (§3.3 heuristic).
|
|
284
|
-
8. `retry` on a state no edge targets (§3.5).
|
|
285
|
-
9. `it.vars.<name>` reference with no declared default (§3.6 — env layer may
|
|
286
|
-
supply it).
|
|
287
|
-
10. `file:` rendering (with declared-default vars) to a path no `on` glob
|
|
288
|
-
matches (§3.7).
|
|
289
|
-
11. Declared actor within edit distance 1 of another (§3.7).
|
|
290
|
-
|
|
291
|
-
**Test-side** (not load-time): transition-coverage property test over the
|
|
292
|
-
bundled default — synthesize a matching diff per `on` row, assert `step` takes
|
|
293
|
-
the edge (§3.8).
|
|
294
|
-
|
|
295
|
-
Sizing check: all graph passes are O(V+E) (two BFS + one Tarjan), subsumption is
|
|
296
|
-
O(rows² per state) over a trivial grammar, and template compilation is the only
|
|
297
|
-
cost that scales with content size. On workflows of tens of states this is
|
|
298
|
-
sub-millisecond — safe to run on every invocation, exactly like today's
|
|
299
|
-
`validateDefinition`.
|
|
300
|
-
|
|
301
|
-
### Interactions to respect
|
|
302
|
-
|
|
303
|
-
- **Severity of unreachability** hinges on `resolveState`'s hand-authored
|
|
304
|
-
subject entry (§3.1) — do not promote finding 6 to an error without also
|
|
305
|
-
deciding that manual-entry states are unsupported.
|
|
306
|
-
- **The bundled default must stay finding-free** — findings 7 and 10 are
|
|
307
|
-
calibrated against it (`fixing`'s cap bounds the check loop; the literal
|
|
308
|
-
`.gtd/...` patterns match the declared default vars). Any new check must be
|
|
309
|
-
validated against the bundled unified template (`src/workflows/unified.yaml`,
|
|
310
|
-
covering both its simple and advanced flows) before landing.
|
|
311
|
-
- **Warning channel is the only architectural change** — everything else slots
|
|
312
|
-
into the existing collect-and-merge flow in `compileWorkflowConfig`.
|
|
313
|
-
|
|
314
|
-
## 6. Reframe: safeguards for USER-authored machines
|
|
315
|
-
|
|
316
|
-
§1–§5 read as "validate the definition harder." The actual product goal is
|
|
317
|
-
narrower and different: the bundled default is already covered by e2e; what
|
|
318
|
-
needs protecting is a **user hand-writing a `workflow:` key in `.gtdrc`** — and
|
|
319
|
-
that shifts the design in three ways:
|
|
320
|
-
|
|
321
|
-
1. **Static analysis is structurally insufficient.** Whether an `on` pattern
|
|
322
|
-
ever fires depends on what an agent writes into the tree at run time. No
|
|
323
|
-
load-time check can prove a user's machine makes progress — it can only catch
|
|
324
|
-
the mechanically-dead shapes (§2's findings 1–8). The worst user-facing
|
|
325
|
-
failures — an agent↔check loop burning tokens all night, a process resting in
|
|
326
|
-
a state whose patterns never match what its own prompt asks the agent to
|
|
327
|
-
produce — need **runtime bounds**, not better proofs.
|
|
328
|
-
2. **Severity must be recalibrated for other people's configs.** A new hard
|
|
329
|
-
error is a breaking change: a config that ran yesterday must not brick the
|
|
330
|
-
repo after `npm update`, _especially_ mid-process. Checks that are
|
|
331
|
-
"error-worthy" for a definition we author (§5's shadowed-row, trap-state)
|
|
332
|
-
default to **warnings** for user configs; hard errors stay reserved for
|
|
333
|
-
definitions that cannot execute at all (today's structural rules, plus
|
|
334
|
-
templates that don't compile).
|
|
335
|
-
3. **A broken config must never take reporting down with it.** Today
|
|
336
|
-
`compileWorkflowConfig` throws, and everything — including `gtd status` —
|
|
337
|
-
dies with it. For a user mid-flight that's the difference between "gtd tells
|
|
338
|
-
me what's wrong" and "gtd won't even tell me where I am."
|
|
339
|
-
|
|
340
|
-
The safeguards form four layers, ordered by when they catch the problem:
|
|
341
|
-
|
|
342
|
-
### 6.1 Authoring time — before the machine ever runs
|
|
343
|
-
|
|
344
|
-
- **A real JSON Schema.** `schema.json` is generated from `ConfigSchema.ts`,
|
|
345
|
-
where `workflow`/`vars` are `Schema.Unknown` — so the published schema says
|
|
346
|
-
`"title": "unknown"` and editors validate NOTHING about the one key users
|
|
347
|
-
hand-write. Hand-authoring the workflow shape into the schema (states map,
|
|
348
|
-
actor, the four content kinds, `on` as string→string, `retry`
|
|
349
|
-
`{max, otherwise}`, `model`/`file`/`mode`) gives every yaml-language-server
|
|
350
|
-
editor squiggles and autocomplete for free, with zero gtd code. The compiler
|
|
351
|
-
stays the source of truth (the schema can't express "exactly one content kind"
|
|
352
|
-
or cross-state rules); the schema is the fast first net, not a replacement.
|
|
353
|
-
Cheapest, highest-leverage single item in this document.
|
|
354
|
-
- **`gtd lint` (a.k.a. "check my machine before I trust it").** Compile the
|
|
355
|
-
config, run every §5 check, print ALL findings (errors + warnings) plus a
|
|
356
|
-
human-readable dump of the graph — states, actors, edges, retry caps, and the
|
|
357
|
-
reachability/terminal classification per state — so an author can _see_ the
|
|
358
|
-
machine they wrote (mermaid `stateDiagram-v2` output fits: renderable in any
|
|
359
|
-
markdown viewer, no new dependency to emit text). Also dry-compiles every Eta
|
|
360
|
-
template. This is where warnings can be exhaustive and loud, because the user
|
|
361
|
-
explicitly asked.
|
|
362
|
-
|
|
363
|
-
### 6.2 Load time — every invocation, quiet by default
|
|
364
|
-
|
|
365
|
-
- **The §5 checks with user-config severities**: structural rules and
|
|
366
|
-
template-compile failures stay throwing errors; all graph findings
|
|
367
|
-
(unreachable, trap, shadowed row, retry cycle, autonomous cycle) surface as
|
|
368
|
-
stderr warnings — one line each, prefixed (`gtd: workflow warning: ...`),
|
|
369
|
-
never fatal. Errors keep the current all-findings-in-one-throw behavior.
|
|
370
|
-
- **Degrade reporting gracefully**: `gtd status` should still answer with the
|
|
371
|
-
findings when the config is broken (resolve HEAD against nothing, print the
|
|
372
|
-
compile errors as the status) rather than crashing with a stack trace.
|
|
373
|
-
`step`/`run`/`next` keep refusing — acting on a broken machine is worse than
|
|
374
|
-
stopping — but the _reporting_ path must survive any config.
|
|
375
|
-
|
|
376
|
-
### 6.3 Run time — the bounds static analysis cannot provide
|
|
377
|
-
|
|
378
|
-
These are the actual safeguards, in the circuit-breaker sense. All three are
|
|
379
|
-
pure functions of data the engine already has (`processTrace`, the pending diff,
|
|
380
|
-
the definition) — they fit `PatternMachine.step`'s purity discipline.
|
|
381
|
-
|
|
382
|
-
- **A per-process autonomous-turn budget.** The v2 plan's "chain-depth limit"
|
|
383
|
-
(§3.11: "bound it and hard-error past, say, 32 hops"), reborn for v3: count
|
|
384
|
-
process-trace entries into states whose actor differs from the workflow's
|
|
385
|
-
human-facing rest points — concretely, entries since the last turn authored by
|
|
386
|
-
a DIFFERENT actor than the loop's autonomous pair, or simplest and honest:
|
|
387
|
-
total trace length. Past a cap (workflow-overridable via a reserved-ish var or
|
|
388
|
-
top-level config key, default generous — e.g. 50), `step` refuses with a new
|
|
389
|
-
refusal reason (`"budget"`) naming the count and how to raise the cap. This is
|
|
390
|
-
the one safeguard that holds NO MATTER WHAT the user's graph looks like — the
|
|
391
|
-
livelock heuristic (§3.3) can then stay a soft warning without leaving a hole.
|
|
392
|
-
- **Stall detection in the engine, not the skill.** `skills/loop/SKILL.md`
|
|
393
|
-
currently tells the driver, in prose, to stop when the same state+content
|
|
394
|
-
repeats with no new commits. Promote the signal: `gtd next --json` (and
|
|
395
|
-
`status --json`) can report `"revisits": <n>` — how many times the current
|
|
396
|
-
state already appears in the process trace. Any driver (not just one that read
|
|
397
|
-
the skill carefully) can then apply its own stop rule; the skill's prose
|
|
398
|
-
heuristic becomes one consumer of a first-class field. No behavior change in
|
|
399
|
-
the engine's own decisions.
|
|
400
|
-
- **Self-explaining refusals.** A no-match refusal already lists the state's
|
|
401
|
-
declared patterns; for a user debugging their own machine, also list the
|
|
402
|
-
pending changes that failed to match (status + path, first N). "Your tree has
|
|
403
|
-
`M src/x.ts` but this state only matches `A .gtd/FEEDBACK.md`" turns the most
|
|
404
|
-
common custom-workflow dead end from a mystery into a diff-vs-pattern
|
|
405
|
-
diagnosis the user can act on immediately.
|
|
406
|
-
|
|
407
|
-
### 6.4 Recovery — no config may trap the repository
|
|
408
|
-
|
|
409
|
-
Already true, worth asserting and documenting as a guarantee: the machine's
|
|
410
|
-
whole state is HEAD's commit subject, so a hand-authored commit
|
|
411
|
-
(`git commit --allow-empty -m "gtd(human): <state>"` for any defined non-commit
|
|
412
|
-
state, or any non-gtd subject to fall back to the initial state) exits ANY state
|
|
413
|
-
a broken config strands a process in. A user's workflow can waste turns; it
|
|
414
|
-
cannot brick a repo. An e2e scenario pinning this (hand-commit out of an
|
|
415
|
-
intentionally-trapped custom workflow) makes the guarantee load-bearing instead
|
|
416
|
-
of incidental — and it is the reason every graph finding in §6.2 can afford to
|
|
417
|
-
be a warning.
|
|
418
|
-
|
|
419
|
-
### What deliberately stays out
|
|
420
|
-
|
|
421
|
-
- No sandboxing or vetting of `script:` content — the workflow author is
|
|
422
|
-
trusted; scripts already run verbatim by design (`gtd run`).
|
|
423
|
-
- No engine interpretation of actor names for the budget/livelock rules beyond
|
|
424
|
-
counting — the actor vocabulary stays unblessed.
|
|
425
|
-
- No auto-repair ("we removed your unreachable state") — findings report; the
|
|
426
|
-
user decides.
|
|
427
|
-
|
|
428
|
-
### Revised priority (user-safeguard lens)
|
|
429
|
-
|
|
430
|
-
1. JSON Schema for the `workflow:` shape (§6.1) — editor-time, zero engine code.
|
|
431
|
-
2. Warning channel + graceful `gtd status` degradation (§6.2) — the
|
|
432
|
-
architectural enabler.
|
|
433
|
-
3. Autonomous-turn budget refusal (§6.3) — the unconditional backstop.
|
|
434
|
-
4. Graph warnings from §5, demoted to warning severity (§6.2).
|
|
435
|
-
5. Refusal diagnostics + `revisits` in `--json` (§6.3).
|
|
436
|
-
6. `gtd lint` with graph dump (§6.1) — subsumes nothing above, packages all of
|
|
437
|
-
it for the authoring moment.
|
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
# Plan: steering-file validation loops in the default workflow
|
|
2
|
-
|
|
3
|
-
> SUPERSEDED (§2–§4): the in-machine validation-loop states this plan added —
|
|
4
|
-
> `todo-validating` and `review-validating` — and their `.gtd/FORMAT.md`
|
|
5
|
-
> steering file have all been DELETED. Deterministic format-checking now lives
|
|
6
|
-
> in the `gtd validate` command (which runs the same pure `parseOpenQuestions` /
|
|
7
|
-
> `parseReviewDoc` parsers the state's `mode:` selects) plus producing-agent
|
|
8
|
-
> self-validation, rather than in bash-port `check`/`script` states. The default
|
|
9
|
-
> workflow is now 10 states, not 12. See
|
|
10
|
-
> [docs/design/steering-file-validation-command.md](steering-file-validation-command.md)
|
|
11
|
-
> and [STATES.md §12](../STATES.md). §1 (the file formats) and §5 (the LSP
|
|
12
|
-
> resurrection) below remain valid and current.
|
|
13
|
-
|
|
14
|
-
> Status: §2–§4 (the two validation loops in the default workflow) AND §5 (the
|
|
15
|
-
> LSP resurrection) both LANDED 2026-07-22. Goal: the bundled default workflow
|
|
16
|
-
> completely maps the functionality the deleted v2 LSP server (`src/Lsp.ts`,
|
|
17
|
-
> removed in the v3 rewrite) provided over the two steering files: the
|
|
18
|
-
> **open-questions format** in `.gtd/TODO.md` and the **checkbox review format**
|
|
19
|
-
> in `.gtd/REVIEW.md` — including a deterministic validation loop for each.
|
|
20
|
-
> Everything is pure workflow configuration; the engine is untouched.
|
|
21
|
-
|
|
22
|
-
## 1. The formats (authoritative spec = the deleted parsers)
|
|
23
|
-
|
|
24
|
-
Extract the exact rules from git history —
|
|
25
|
-
`git show 60c7490^:src/OpenQuestions.ts` and
|
|
26
|
-
`git show 60c7490^:src/ReviewDoc.ts` — and embed them BOTH in the agent prompts
|
|
27
|
-
(so drafts are born valid) and in the validator scripts (so malformed drafts
|
|
28
|
-
bounce):
|
|
29
|
-
|
|
30
|
-
**`.gtd/TODO.md` open questions** (`OpenQuestions.ts`):
|
|
31
|
-
|
|
32
|
-
- Free-form prose, plus an OPTIONAL `## Open Questions` section (near the top)
|
|
33
|
-
and an OPTIONAL `## Answered Questions` section (at the bottom) — either
|
|
34
|
-
absent = zero questions of that status, valid.
|
|
35
|
-
- Every `###` sub-heading directly under one of those sections is one question;
|
|
36
|
-
its STATUS is POSITIONAL (under `## Open Questions` → open, under
|
|
37
|
-
`## Answered Questions` → answered). The body is free-form (the agent's
|
|
38
|
-
suggested answer for an open one, the settled resolution for an answered one)
|
|
39
|
-
— there is no `Suggested default:`/`Answer:` marker line. The only structural
|
|
40
|
-
error is a `###` heading with no question text.
|
|
41
|
-
- A question is answered/accepted by MOVING its `###` block down into
|
|
42
|
-
`## Answered Questions` (the agent does this on the next `grilling` lap; a
|
|
43
|
-
human leaving a suggestion untouched IS acceptance). Nothing enforces the move
|
|
44
|
-
or the section order — that is the producing agent's prompt contract.
|
|
45
|
-
- Each section ends at the next level-1/2 heading or EOF.
|
|
46
|
-
|
|
47
|
-
**`.gtd/REVIEW.md`** (`ReviewDoc.ts`):
|
|
48
|
-
|
|
49
|
-
- First non-blank line: `# Review: <short-hash>`.
|
|
50
|
-
- A `<!-- base: <hash> -->` comment somewhere in the document.
|
|
51
|
-
- At least one `## <Chunk Title>` (non-empty title), each with at least one file
|
|
52
|
-
pointer matching `- [ ] ./path/to/file.ts#42 — note` / `- [x] …` (checkbox,
|
|
53
|
-
`./`-relative path, optional `#line`, optional `—`/`-` note) — the deleted
|
|
54
|
-
`FILE_POINTER_RE` is the shape to port.
|
|
55
|
-
|
|
56
|
-
## 2. The new default workflow (12 states)
|
|
57
|
-
|
|
58
|
-
Current 7 states, plus the two loops. `smart` on `grilling` and `reviewing`.
|
|
59
|
-
|
|
60
|
-
| State | Actor | Content | `on` (declaration order matters) |
|
|
61
|
-
| ------------------- | ----- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
62
|
-
| `idle` (initial) | human | message | `* **` → `grilling` |
|
|
63
|
-
| `grilling` | agent | prompt | `* **` → `todo-validating` |
|
|
64
|
-
| `todo-validating` | check | script | `A/M .gtd/FORMAT.md` → `grilling`; `D .gtd/FORMAT.md` → `grilling-answer`; `C` → `grilling-answer` |
|
|
65
|
-
| `grilling-answer` | human | message | `C` → `building`; `* **` → `grilling` |
|
|
66
|
-
| `building` | agent | prompt | `* **` → `checking` |
|
|
67
|
-
| `checking` | check | script | `A/M .gtd/FEEDBACK.md` → `fixing`; `D .gtd/FEEDBACK.md` → `reviewing`; `C` → `reviewing` |
|
|
68
|
-
| `fixing` (retry 3) | agent | prompt | `* **` → `checking` |
|
|
69
|
-
| `escalate` | human | message | `* **` → `checking` |
|
|
70
|
-
| `reviewing` | agent | prompt | `* **` → `review-validating` |
|
|
71
|
-
| `review-validating` | check | script | `A/M .gtd/FORMAT.md` → `reviewing`; `D .gtd/FORMAT.md` → `await-review`; `C` → `await-review` |
|
|
72
|
-
| `await-review` | human | message | `D .gtd/REVIEW.md` → `idle`; `M .gtd/REVIEW.md` → `review-deciding`; `* **` → `grilling`; (no `C` row — a clean step is a no-op, still awaiting) |
|
|
73
|
-
| `review-deciding` | check | script | `A/M .gtd/TODO.md` → `grilling`; `D .gtd/REVIEW.md` → `idle`; `C` → `await-review` (defensive) |
|
|
74
|
-
|
|
75
|
-
**Loop 1 — TODO.md questions (maps the LSP's question symbols/format):**
|
|
76
|
-
|
|
77
|
-
- `grilling` develops `.gtd/TODO.md` with the exact Open Questions format; if
|
|
78
|
-
`.gtd/FORMAT.md` exists, its prompt says to fix those findings first.
|
|
79
|
-
- `todo-validating` (deterministic script): parse-check TODO.md against §1's
|
|
80
|
-
rules. Malformed → write findings to `.gtd/FORMAT.md` (one line each) → back
|
|
81
|
-
to `grilling`. Valid → `rm -f .gtd/FORMAT.md` → `grilling-answer`.
|
|
82
|
-
- `grilling-answer` (human): answer by editing a question's body in place, or
|
|
83
|
-
leave a suggestion untouched to accept it; accept all remaining suggestions
|
|
84
|
-
with a clean step (`C` → `building`); any edit loops back to `grilling` (the
|
|
85
|
-
agent folds the whole answered batch into the plan, moving each resolved block
|
|
86
|
-
down to `## Answered Questions`, possibly asks follow-ups, and re-validates).
|
|
87
|
-
|
|
88
|
-
**Loop 2 — REVIEW.md checkboxes (maps the LSP's review chunks/check actions):**
|
|
89
|
-
|
|
90
|
-
- Green `checking` now goes to `reviewing` (not straight to `await-review`).
|
|
91
|
-
- `reviewing` (agent, `model: smart`) writes `.gtd/REVIEW.md` in §1's exact
|
|
92
|
-
format — header hash from `<%= it.currentCommit %>` (short slice), base
|
|
93
|
-
comment from `<%= it.startCommit %>`, chunks grouped semantically over the
|
|
94
|
-
embedded `it.processDiff` (reuse the advanced example's reviewing prompt as
|
|
95
|
-
the base, tightened to the format spec).
|
|
96
|
-
- `review-validating` (deterministic script): structure check (header, base
|
|
97
|
-
comment, ≥1 chunk, every pointer matches the shape). Malformed →
|
|
98
|
-
`.gtd/FORMAT.md` → `reviewing`; valid → `await-review`.
|
|
99
|
-
- `await-review` (human): tick a pointer's checkbox to approve that item; when
|
|
100
|
-
EVERY box is ticked the cycle is approved. Add notes / untick / edit code for
|
|
101
|
-
feedback. Deleting `.gtd/REVIEW.md` outright is the power-user approve
|
|
102
|
-
shortcut. Any `M .gtd/REVIEW.md` step routes to the decider (declared before
|
|
103
|
-
`* **`, so a step that also touches code still goes to the decider); code-only
|
|
104
|
-
edits are feedback straight to `grilling`.
|
|
105
|
-
- `review-deciding` (deterministic script): if NO unticked `- [ ]` pointer
|
|
106
|
-
remains → approve: `rm .gtd/REVIEW.md` (→ `idle`). Otherwise → extract the
|
|
107
|
-
unticked pointers and their notes into a fresh `.gtd/TODO.md` (the next lap's
|
|
108
|
-
input) AND `rm .gtd/REVIEW.md` — the diff then contains both `A .gtd/TODO.md`
|
|
109
|
-
and `D .gtd/REVIEW.md`, and the `A/M .gtd/TODO.md` row is declared FIRST so
|
|
110
|
-
feedback wins (first-match declaration order is load-bearing here; pin it with
|
|
111
|
-
a test).
|
|
112
|
-
|
|
113
|
-
**Hygiene invariant (assert in e2e):** an approved cycle leaves `.gtd/` empty —
|
|
114
|
-
FEEDBACK.md is cleaned by green `checking`, FORMAT.md by a valid validation run,
|
|
115
|
-
REVIEW.md by the approval, TODO.md by `building`.
|
|
116
|
-
|
|
117
|
-
## 3. What the validators look like
|
|
118
|
-
|
|
119
|
-
Same discipline as `checking`/the old `picking`: mechanics-only bash, verdict =
|
|
120
|
-
file op, semantics = the `on` map. Grep/awk ports of the parser rules
|
|
121
|
-
(pragmatic, not a full markdown parser): e.g. a `###` question heading with no
|
|
122
|
-
question text is a finding; a pointer line failing the
|
|
123
|
-
`- [(x| )] ./path(#N)?( — note)?` shape is a finding; `- [ ]` presence is the
|
|
124
|
-
review-deciding branch condition. Findings go into `.gtd/FORMAT.md` verbatim
|
|
125
|
-
(file + line + rule), so the fixing agent gets actionable input.
|
|
126
|
-
|
|
127
|
-
## 4. Change chart
|
|
128
|
-
|
|
129
|
-
| Area | Change |
|
|
130
|
-
| ----------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
131
|
-
| `src/workflows/default.yaml` | +5 states (`todo-validating`, `grilling-answer`, `reviewing`, `review-validating`, `review-deciding`); `checking`'s green rows retarget `reviewing`; `await-review` rewritten (checkbox semantics, decider routing); prompts gain the format specs; header comment updated. |
|
|
132
|
-
| Engine / TypeScript | **No changes.** Everything is workflow data. |
|
|
133
|
-
| `STATES.md` §10 | New table + walkthrough (both loops, the declaration-order subtleties, the hygiene invariant). |
|
|
134
|
-
| `tests/integration/features/default-workflow.feature` | Rewritten: malformed-TODO lap (simulate `todo-validating` by writing `.gtd/FORMAT.md` + `gtd step check` — @inmem never executes scripts), valid draft → answer lap → accept; malformed-REVIEW lap; tick-all approve → `idle` with empty `.gtd/`; partial-tick feedback → TODO.md extraction → `grilling`; delete-shortcut approve; retry/escalation kept. |
|
|
135
|
-
| `smoke.feature` | Hop updates (`grilling` now routes to `todo-validating`). |
|
|
136
|
-
| Docs | `configuration.md` / `README.md` walkthrough mentions; the advanced flow is now part of the bundled unified template (see STATES.md §10) rather than a standalone example. |
|
|
137
|
-
| Live verification | Real-script drive (`gtd run` at each validator) covering: agent writes malformed TODO → FORMAT.md → fixed lap; answers folded; malformed REVIEW lap; tick-all approve; partial-tick feedback extracting TODO.md; final tree `.gtd`-free at idle. |
|
|
138
|
-
|
|
139
|
-
## 5. The LSP server comes back (v3-shaped)
|
|
140
|
-
|
|
141
|
-
The v2 LSP (`src/Lsp.ts`, deleted at the v3 rewrite) is resurrected, scoped to
|
|
142
|
-
what is workflow-agnostic — the FILE FORMATS, not any particular state machine:
|
|
143
|
-
|
|
144
|
-
- **Restore the pure parsers verbatim from history** —
|
|
145
|
-
`git show 60c7490^:src/OpenQuestions.ts` / `:src/ReviewDoc.ts` (+ their unit
|
|
146
|
-
tests) — they are pure (no git/fs/Effect) and restore cleanly. They are the
|
|
147
|
-
executable spec of §1; the bash validators in §3 implement the SAME rules
|
|
148
|
-
(note this dual-implementation contract in both places — the parsers' unit
|
|
149
|
-
tests are the format's spec tests).
|
|
150
|
-
- **New `src/Lsp.ts`** (recover from history, then STRIP the v2-model
|
|
151
|
-
dependencies — Events/Machine/ReviewWindow/STATE_FILE are gone): keyed on FILE
|
|
152
|
-
NAME, not state. For `.gtd/TODO.md`: document symbols for open questions
|
|
153
|
-
([open]/[answered]). For `.gtd/REVIEW.md`: chunk/hunk symbols and the
|
|
154
|
-
check/uncheck code actions (hunk and whole-chunk). BOTH files: publish the
|
|
155
|
-
parsers' `errors` as diagnostics — the same findings the workflow's
|
|
156
|
-
`.gtd/FORMAT.md` validators produce, live in the editor. The v2
|
|
157
|
-
`gtd.openSteeringFile` command and its hardcoded state→file map are NOT
|
|
158
|
-
restored (a v3 workflow is arbitrary data; a state names no file) — noted as a
|
|
159
|
-
possible future addition driven by workflow config.
|
|
160
|
-
- **CLI**: re-add the `gtd lsp` subcommand (stdio transport, as before) to
|
|
161
|
-
`program.ts` dispatch + help + `docs/cli.md`; re-add the
|
|
162
|
-
`vscode-languageserver` / `vscode-languageserver-textdocument` dependencies;
|
|
163
|
-
confirm the tsdown single-file bundle still builds and the subcommand starts
|
|
164
|
-
from `dist/gtd.bundle.mjs`.
|
|
165
|
-
- **Tests**: parser unit tests restored; the LSP's pure helpers
|
|
166
|
-
(symbol/edit/diagnostic builders) unit-tested as in v2 (recover `Lsp.test.ts`
|
|
167
|
-
from history and adapt); restore/adapt the old protocol-level e2e
|
|
168
|
-
(`lsp.steps.ts` + feature) if it adapts cleanly, otherwise a minimal @live
|
|
169
|
-
smoke that `gtd lsp` starts and answers `initialize`.
|
|
170
|
-
- **Housekeeping**: add the restored pure modules to `stryker.config.json`'s
|
|
171
|
-
mutate list; `docs/development.md` + `docs/upgrading.md` notes (the LSP is
|
|
172
|
-
back, file-format-keyed); README one-liner on editor integration.
|
|
173
|
-
|
|
174
|
-
## 6. Explicitly out of scope
|
|
175
|
-
|
|
176
|
-
- `ARCHITECTURE.md` (the old second question phase) — the default keeps a single
|
|
177
|
-
grilling step; the advanced example still shows the two-phase shape.
|
|
178
|
-
- Engine-level validation of steering files — validation stays a workflow
|
|
179
|
-
concern, per the v3 charter; the LSP is editor tooling over the same formats,
|
|
180
|
-
not an engine hook.
|
|
181
|
-
- The `gtd.openSteeringFile` command (needs a state→file mapping that v3
|
|
182
|
-
workflows don't declare — future work, possibly a per-state `file:` hint).
|