@pmelab/gtd 6.11.0 → 6.13.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 +23 -11
- package/dist/gtd.bundle.mjs +179 -37
- package/docs/cli.md +57 -12
- package/docs/configuration.md +33 -1
- package/docs/design/state-file-association.md +7 -5
- package/package.json +1 -1
- package/schema.json +8 -0
package/README.md
CHANGED
|
@@ -88,6 +88,9 @@ Four commands drive it:
|
|
|
88
88
|
- **`gtd review <commitish>`** — start a brand new review process reviewing
|
|
89
89
|
`<commitish>..HEAD` (e.g. a colleague's PR branch), reusing the workflow's
|
|
90
90
|
existing review/feedback machinery over that diff.
|
|
91
|
+
- **`gtd fix`** — start a brand new process that goes straight into repairing
|
|
92
|
+
the current failing tests, then runs the shared review/squash tail (a no-op if
|
|
93
|
+
the suite is already green).
|
|
91
94
|
|
|
92
95
|
`gtd version` (or `gtd --version`/`-v`) prints the installed version and exits;
|
|
93
96
|
`gtd help` (or `gtd --help`/`-h`) prints the command list. Both short-circuit
|
|
@@ -101,8 +104,11 @@ never executes anything — the driver owns running scripts. See
|
|
|
101
104
|
[STATES.md](STATES.md) for the model and [Driving the loop](docs/loop.md) for
|
|
102
105
|
the full protocol.
|
|
103
106
|
|
|
104
|
-
The unified workflow has **
|
|
105
|
-
|
|
107
|
+
The unified workflow has **entry points behind a green-baseline gate, into one
|
|
108
|
+
shared tail**. Every entry first runs your test suite and only starts once it's
|
|
109
|
+
green — you never build (or review) on top of a red baseline; a red run halts
|
|
110
|
+
and tells you to repair it first (that's what `gtd fix` is for). The two
|
|
111
|
+
steering-file entries are chosen by which file you create:
|
|
106
112
|
|
|
107
113
|
- Create **`.gtd/TODO.md`** with a short sketch to start the **simple** flow: an
|
|
108
114
|
agent develops your sketch into a plan — asking any open question it can't
|
|
@@ -118,9 +124,12 @@ which steering file you create:
|
|
|
118
124
|
Both flows converge on the same tail: an agent hands you a `.gtd/REVIEW.md`
|
|
119
125
|
checkbox review of the diff — tick a box as you review each hunk (ticking just
|
|
120
126
|
records "I read this"), and leave a **comment** to request changes: a note on a
|
|
121
|
-
line,
|
|
122
|
-
|
|
123
|
-
|
|
127
|
+
line, an inline `// TODO`-style comment in the code, or a direct code edit. Any
|
|
128
|
+
comment sends a build + re-review round — an agent first turns your comments
|
|
129
|
+
into an explicit instruction list, then a build turn implements it (a re-review
|
|
130
|
+
then covers only the follow-through, and a hand-edit is treated as your own fix
|
|
131
|
+
the agent completes without reverting your lines; a comment can't be silently
|
|
132
|
+
dropped — a build turn that addresses nothing is refused). Ticking every box
|
|
124
133
|
with no comment is the sign-off, which collapses the whole cycle into one commit
|
|
125
134
|
(a **squash finale** whose message an agent drafts). Stepping with a box still
|
|
126
135
|
unticked and no comment is refused (finish reviewing first), as is deleting
|
|
@@ -129,7 +138,9 @@ unticked and no comment is refused (finish reviewing first), as is deleting
|
|
|
129
138
|
`<commitish>..HEAD` with no cycle of its own, e.g. a colleague's PR branch. Its
|
|
130
139
|
squash keeps and describes only the fixes made _during_ the review (not the
|
|
131
140
|
reviewed changeset); a clean sign-off with no fixes becomes an empty
|
|
132
|
-
`chore: human review` commit.
|
|
141
|
+
`chore: human review` commit. A fourth entry, `gtd fix`, starts from a clean
|
|
142
|
+
`idle` and goes straight into repairing a red baseline — repair, review, and
|
|
143
|
+
squash into one commit (a no-op if the suite is already green). See
|
|
133
144
|
[STATES.md](STATES.md#10-the-bundled-workflow-template) for the full shape. The
|
|
134
145
|
workflow is just `.gtdrc` config — edit it or write your own (see
|
|
135
146
|
[Configuration](docs/configuration.md)). Every agent state routes its model
|
|
@@ -150,11 +161,12 @@ Before wiring gtd into a repo, note the
|
|
|
150
161
|
importantly: gitignore everything your scripts write.
|
|
151
162
|
|
|
152
163
|
Editor integration: `gtd lsp` starts an LSP server over stdio for `.gtd/`
|
|
153
|
-
steering files —
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
164
|
+
steering files — a symbol per `review`-mode chunk that still has an unchecked
|
|
165
|
+
hunk (an outline of the packages left to review) plus check/uncheck actions over
|
|
166
|
+
those chunks, go-to-definition from a `review`-mode hunk line into the file it
|
|
167
|
+
points at (at its `#line`), symbols over a `qa`-mode file's open questions,
|
|
168
|
+
diagnostics for both (live as you edit), and a `gtd.openSteeringFile` command
|
|
169
|
+
that jumps to the current state's steering file. Config-driven via each state's
|
|
158
170
|
`file:`/`mode:` (see [CLI reference](docs/cli.md#gtd-lsp)) — falls back to
|
|
159
171
|
basename dispatch (`TODO.md`/`REVIEW.md`) with no config in sight. Those two
|
|
160
172
|
formats are gtd's built-in steering-file MODES — validators, not formatters: a
|
package/dist/gtd.bundle.mjs
CHANGED
|
@@ -189103,10 +189103,16 @@ const isCommitState = (state) => state.commit !== void 0;
|
|
|
189103
189103
|
const isReviewWindowState = (def, state) => def.states[state]?.reviewWindow === true;
|
|
189104
189104
|
/** True when `state` anchors the review window's diff base (see `StateDef.reviewBase`). Safe for an unknown state name (returns `false`). */
|
|
189105
189105
|
const isReviewBaseState = (def, state) => def.states[state]?.reviewBase === true;
|
|
189106
|
+
/** True when a step at `state` must be refused if its only change is deleting the state's `file:` (see `StateDef.requireProgress`). Safe for an unknown state name (returns `false`). */
|
|
189107
|
+
const isRequireProgressState = (def, state) => def.states[state]?.requireProgress === true;
|
|
189106
189108
|
/** The workflow's declared review-entry state name (see `StateDef.reviewEntry`) — `gtd review <commitish>` (`src/program.ts`) enters this state to start a new review process. `undefined` when no state declares one; `validateDefinition` guarantees at most one does, so the first (only) match wins. */
|
|
189107
189109
|
const reviewEntryStateOf = (def) => {
|
|
189108
189110
|
for (const [name, state] of Object.entries(def.states)) if (state.reviewEntry === true) return name;
|
|
189109
189111
|
};
|
|
189112
|
+
/** The workflow's declared fix-entry state name (see `StateDef.fixEntry`) — `gtd fix` (`src/program.ts`) enters this state to start a new fix process. `undefined` when no state declares one; `validateDefinition` guarantees at most one does, so the first (only) match wins. */
|
|
189113
|
+
const fixEntryStateOf = (def) => {
|
|
189114
|
+
for (const [name, state] of Object.entries(def.states)) if (state.fixEntry === true) return name;
|
|
189115
|
+
};
|
|
189110
189116
|
/** The ` → ` that separates a transition subject's source state from its target. */
|
|
189111
189117
|
const TRANSITION_SEP = " → ";
|
|
189112
189118
|
/**
|
|
@@ -189472,11 +189478,43 @@ const validateReviewEntry = (name, state) => {
|
|
|
189472
189478
|
if (state.initial === true) errors.push(`state "${name}": the initial state cannot declare "reviewEntry"`);
|
|
189473
189479
|
return errors;
|
|
189474
189480
|
};
|
|
189481
|
+
/**
|
|
189482
|
+
* `fixEntry`, when present, is forbidden on a commit state and on the initial
|
|
189483
|
+
* state — the same rule family as `reviewEntry` (see `validateReviewEntry`):
|
|
189484
|
+
* `gtd fix` REQUIRES resting at the initial state before it acts, so its entry
|
|
189485
|
+
* target must be a distinct state. The at-most-one-state rule is checked
|
|
189486
|
+
* separately over the whole definition (see `validateFixEntryUniqueness`).
|
|
189487
|
+
*/
|
|
189488
|
+
const validateFixEntry = (name, state) => {
|
|
189489
|
+
if (state.fixEntry === void 0) return [];
|
|
189490
|
+
const errors = [];
|
|
189491
|
+
if (isCommitState(state)) errors.push(`state "${name}": a commit state cannot declare "fixEntry"`);
|
|
189492
|
+
if (state.initial === true) errors.push(`state "${name}": the initial state cannot declare "fixEntry"`);
|
|
189493
|
+
return errors;
|
|
189494
|
+
};
|
|
189495
|
+
/**
|
|
189496
|
+
* `requireProgress`, when present, is forbidden on a commit state (never at
|
|
189497
|
+
* rest — same rule family as `reviewWindow`/`reviewBase`) and REQUIRES a
|
|
189498
|
+
* `file:`: the edge gate refuses a turn whose sole change is deleting that
|
|
189499
|
+
* file, so a state with no `file:` to name has nothing to guard.
|
|
189500
|
+
*/
|
|
189501
|
+
const validateRequireProgress = (name, state) => {
|
|
189502
|
+
if (state.requireProgress === void 0) return [];
|
|
189503
|
+
const errors = [];
|
|
189504
|
+
if (isCommitState(state)) errors.push(`state "${name}": a commit state cannot declare "requireProgress"`);
|
|
189505
|
+
if (state.file === void 0) errors.push(`state "${name}": "requireProgress" requires "file"`);
|
|
189506
|
+
return errors;
|
|
189507
|
+
};
|
|
189475
189508
|
/** At most one state may declare `reviewEntry: true` — `gtd review <commitish>` (`src/program.ts`) needs a single, unambiguous state name to enter (see `reviewEntryStateOf`). */
|
|
189476
189509
|
const validateReviewEntryUniqueness = (def, names) => {
|
|
189477
189510
|
const entryNames = names.filter((name) => def.states[name].reviewEntry === true);
|
|
189478
189511
|
return entryNames.length > 1 ? [`at most one state may declare "reviewEntry" (found ${entryNames.length}: ${entryNames.join(", ")})`] : [];
|
|
189479
189512
|
};
|
|
189513
|
+
/** At most one state may declare `fixEntry: true` — `gtd fix` (`src/program.ts`) needs a single, unambiguous state name to enter (see `fixEntryStateOf`). */
|
|
189514
|
+
const validateFixEntryUniqueness = (def, names) => {
|
|
189515
|
+
const entryNames = names.filter((name) => def.states[name].fixEntry === true);
|
|
189516
|
+
return entryNames.length > 1 ? [`at most one state may declare "fixEntry" (found ${entryNames.length}: ${entryNames.join(", ")})`] : [];
|
|
189517
|
+
};
|
|
189480
189518
|
/** Every `on` row parses, and its target names a defined state. */
|
|
189481
189519
|
const validateOnEdges = (name, state, names) => {
|
|
189482
189520
|
const errors = [];
|
|
@@ -189495,14 +189533,18 @@ const validateRetry = (name, state, names) => {
|
|
|
189495
189533
|
return errors;
|
|
189496
189534
|
};
|
|
189497
189535
|
/**
|
|
189498
|
-
* Every state is reachable from
|
|
189536
|
+
* Every state is reachable from an ENTRY ROOT by walking `on` targets and
|
|
189499
189537
|
* `retry.otherwise` redirects (a redirect ENTERS its `otherwise` state exactly
|
|
189500
189538
|
* like an `on` match enters its target — see `applyRetry` — so both are real
|
|
189501
|
-
* edges).
|
|
189502
|
-
*
|
|
189503
|
-
*
|
|
189504
|
-
*
|
|
189505
|
-
*
|
|
189539
|
+
* edges). The roots are the initial state PLUS the CLI-command entry states
|
|
189540
|
+
* (`reviewEntry`/`fixEntry`): a command enters those directly, so a state
|
|
189541
|
+
* reachable only from one of them is legitimately reachable, not dead config
|
|
189542
|
+
* (without seeding them, e.g. a `fixEntry` check whose only inbound path is
|
|
189543
|
+
* `gtd fix` would be wrongly flagged). Plain BFS; targets naming undefined
|
|
189544
|
+
* states are skipped here (they are `validateOnEdges`/`validateRetry` findings
|
|
189545
|
+
* of their own). Only called when `validateInitial` found no problem: with zero
|
|
189546
|
+
* or several `initial` states there is no well-defined start to walk from, and
|
|
189547
|
+
* reporting every state as unreachable would bury the real finding.
|
|
189506
189548
|
*
|
|
189507
189549
|
* An unreachable state is an ERROR, not a warning: a workflow is bound to a
|
|
189508
189550
|
* project and edited as a project-wide change, so "kept on purpose for manual
|
|
@@ -189511,9 +189553,13 @@ const validateRetry = (name, state, names) => {
|
|
|
189511
189553
|
* config is exactly what load-time validation exists to catch.
|
|
189512
189554
|
*/
|
|
189513
189555
|
const validateReachability = (def, names) => {
|
|
189514
|
-
const
|
|
189515
|
-
|
|
189516
|
-
|
|
189556
|
+
const roots = [
|
|
189557
|
+
names.find((name) => def.states[name].initial === true),
|
|
189558
|
+
reviewEntryStateOf(def),
|
|
189559
|
+
fixEntryStateOf(def)
|
|
189560
|
+
].filter((name) => name !== void 0);
|
|
189561
|
+
const visited = new Set(roots);
|
|
189562
|
+
const queue = [...roots];
|
|
189517
189563
|
while (queue.length > 0) {
|
|
189518
189564
|
const state = def.states[queue.shift()];
|
|
189519
189565
|
const targets = (state.on ?? []).map(([, target]) => target);
|
|
@@ -189523,7 +189569,7 @@ const validateReachability = (def, names) => {
|
|
|
189523
189569
|
queue.push(target);
|
|
189524
189570
|
}
|
|
189525
189571
|
}
|
|
189526
|
-
return names.filter((name) => !visited.has(name)).map((name) => `state "${name}" is unreachable from
|
|
189572
|
+
return names.filter((name) => !visited.has(name)).map((name) => `state "${name}" is unreachable from any entry state (${roots.join(", ")}) (no "on" target or "retry.otherwise" leads to it)`);
|
|
189527
189573
|
};
|
|
189528
189574
|
/** All per-state rule checkers, run over one state. */
|
|
189529
189575
|
const validateState = (def, name, names) => {
|
|
@@ -189538,7 +189584,9 @@ const validateState = (def, name, names) => {
|
|
|
189538
189584
|
...validateFile(name, state),
|
|
189539
189585
|
...validateMode(def, name, state),
|
|
189540
189586
|
...validateReviewWindow(name, state),
|
|
189541
|
-
...validateReviewEntry(name, state)
|
|
189587
|
+
...validateReviewEntry(name, state),
|
|
189588
|
+
...validateFixEntry(name, state),
|
|
189589
|
+
...validateRequireProgress(name, state)
|
|
189542
189590
|
];
|
|
189543
189591
|
};
|
|
189544
189592
|
/**
|
|
@@ -189560,11 +189608,12 @@ const validateState = (def, name, names) => {
|
|
|
189560
189608
|
* `knownModes`), requires a sibling `file`, and is never declared on a commit
|
|
189561
189609
|
* state; every `modes:` entry declares at least one non-blank
|
|
189562
189610
|
* `format`/`validate` command; `reviewWindow`/`reviewBase`, when
|
|
189563
|
-
* present, are never declared on a commit state; `reviewEntry`, when
|
|
189564
|
-
*
|
|
189565
|
-
* state across the whole workflow may declare
|
|
189566
|
-
* from the initial state
|
|
189567
|
-
*
|
|
189611
|
+
* present, are never declared on a commit state; `reviewEntry`/`fixEntry`, when
|
|
189612
|
+
* present, are never declared on a commit state or the initial state, and at
|
|
189613
|
+
* most one state across the whole workflow may declare each; every state is
|
|
189614
|
+
* reachable from an entry root (the initial state plus any `reviewEntry`/
|
|
189615
|
+
* `fixEntry` state) by walking `on` targets and `retry.otherwise` redirects
|
|
189616
|
+
* (checked only when the initial-state rule itself passed — see
|
|
189568
189617
|
* `validateReachability`).
|
|
189569
189618
|
*/
|
|
189570
189619
|
const validateDefinition = (def) => {
|
|
@@ -189576,6 +189625,7 @@ const validateDefinition = (def) => {
|
|
|
189576
189625
|
...validateModes(def),
|
|
189577
189626
|
...names.flatMap((name) => validateState(def, name, names)),
|
|
189578
189627
|
...validateReviewEntryUniqueness(def, names),
|
|
189628
|
+
...validateFixEntryUniqueness(def, names),
|
|
189579
189629
|
...initialErrors.length === 0 ? validateReachability(def, names) : []
|
|
189580
189630
|
];
|
|
189581
189631
|
};
|
|
@@ -189786,7 +189836,9 @@ const KNOWN_STATE_KEYS = /* @__PURE__ */ new Set([
|
|
|
189786
189836
|
"mode",
|
|
189787
189837
|
"reviewWindow",
|
|
189788
189838
|
"reviewBase",
|
|
189789
|
-
"reviewEntry"
|
|
189839
|
+
"reviewEntry",
|
|
189840
|
+
"fixEntry",
|
|
189841
|
+
"requireProgress"
|
|
189790
189842
|
]);
|
|
189791
189843
|
const KNOWN_TOP_KEYS = /* @__PURE__ */ new Set([
|
|
189792
189844
|
"vars",
|
|
@@ -190000,7 +190052,7 @@ const compileMode = (raw, name, errors) => {
|
|
|
190000
190052
|
/** The `initial` field: `true` only when the raw value is the literal boolean `true`. */
|
|
190001
190053
|
const compileInitial = (raw, name, errors) => compileBooleanFlag(raw, "initial", name, errors);
|
|
190002
190054
|
/**
|
|
190003
|
-
* A boolean state flag (`initial`/`reviewWindow`/`reviewBase`/`reviewEntry`): `true` only
|
|
190055
|
+
* A boolean state flag (`initial`/`reviewWindow`/`reviewBase`/`reviewEntry`/`fixEntry`): `true` only
|
|
190004
190056
|
* when the raw value is the literal `true`; a non-boolean is a config error;
|
|
190005
190057
|
* `false` (or absent) compiles away to `undefined` so it never lands in the
|
|
190006
190058
|
* `StateDef` — `false` and "unset" mean the same thing for every such flag.
|
|
@@ -190040,7 +190092,9 @@ const assembleStateDef = (parts) => ({
|
|
|
190040
190092
|
mode: parts.mode,
|
|
190041
190093
|
reviewWindow: parts.reviewWindow,
|
|
190042
190094
|
reviewBase: parts.reviewBase,
|
|
190043
|
-
reviewEntry: parts.reviewEntry
|
|
190095
|
+
reviewEntry: parts.reviewEntry,
|
|
190096
|
+
fixEntry: parts.fixEntry,
|
|
190097
|
+
requireProgress: parts.requireProgress
|
|
190044
190098
|
}),
|
|
190045
190099
|
...assembleContentFields(parts.content)
|
|
190046
190100
|
});
|
|
@@ -190064,7 +190118,9 @@ const compileState = (name, raw, configDir, errors, inlineFileRefs) => {
|
|
|
190064
190118
|
mode: compileMode(raw, name, errors),
|
|
190065
190119
|
reviewWindow: compileBooleanFlag(raw, "reviewWindow", name, errors),
|
|
190066
190120
|
reviewBase: compileBooleanFlag(raw, "reviewBase", name, errors),
|
|
190067
|
-
reviewEntry: compileBooleanFlag(raw, "reviewEntry", name, errors)
|
|
190121
|
+
reviewEntry: compileBooleanFlag(raw, "reviewEntry", name, errors),
|
|
190122
|
+
fixEntry: compileBooleanFlag(raw, "fixEntry", name, errors),
|
|
190123
|
+
requireProgress: compileBooleanFlag(raw, "requireProgress", name, errors)
|
|
190068
190124
|
});
|
|
190069
190125
|
};
|
|
190070
190126
|
/**
|
|
@@ -190290,6 +190346,14 @@ const workflowJsonSchema = {
|
|
|
190290
190346
|
reviewEntry: {
|
|
190291
190347
|
type: "boolean",
|
|
190292
190348
|
description: "Marks the (at most one) state `gtd review <commitish>` enters to start a brand new process reviewing <commitish>..HEAD. Forbidden on a commit state and on the initial state."
|
|
190349
|
+
},
|
|
190350
|
+
fixEntry: {
|
|
190351
|
+
type: "boolean",
|
|
190352
|
+
description: "Marks the (at most one) state `gtd fix` enters to start a brand new process that goes straight into repairing the current failing tests. Forbidden on a commit state and on the initial state."
|
|
190353
|
+
},
|
|
190354
|
+
requireProgress: {
|
|
190355
|
+
type: "boolean",
|
|
190356
|
+
description: "When true, a step at this state is refused if its only pending change is deleting the state's own `file:` — a work-free turn that discards its input without addressing it. A `NOTHING ACTIONABLE` sentinel file is exempt (a legitimately non-actionable round makes no code change). Requires a `file:`. Forbidden on a commit state."
|
|
190293
190357
|
}
|
|
190294
190358
|
}
|
|
190295
190359
|
}
|
|
@@ -191473,7 +191537,7 @@ var WorktreeReader = class WorktreeReader extends Tag("WorktreeReader")() {
|
|
|
191473
191537
|
};
|
|
191474
191538
|
//#endregion
|
|
191475
191539
|
//#region src/workflows/unified.yaml
|
|
191476
|
-
var unified_default = "# The unified gtd workflow template — written verbatim into `.gtdrc.json`\n# (nested under a `workflow:` key) by `gtd init` (no argument). gtd ships no\n# default workflow: a repo picks this bundled template with `gtd init`, and a\n# state command run with no workflow configured fails (see src/Config.ts).\n# See STATES.md §10 for the full walkthrough.\n#\n# Compiled through the exact same `compileWorkflowConfig` a user's `.gtdrc`\n# `workflow:` key goes through — no privileged code path\n# (src/workflows/templates.ts does the compiling). Every content string below\n# is INLINE (no `./`-relative file references): the bundled template ships\n# inside a single-file build (`dist/gtd.bundle.mjs`), so its content must\n# already be resolved at BUILD time. `gtd init` DERIVES the scaffolded\n# `.gtdrc.json` from this inline source (`renderInitScaffold`): each agent\n# state's `prompt:` is written out as an editable `gtd-prompts/<state>.md`\n# file; human `message:`/check `script:`/the `done` `commit:` body stay inline.\n#\n# ── Three entry points, one shared tail ────────────────────────────────────\n#\n# The initial `idle` state FORKS on which steering file the human creates:\n#\n# * .gtd/REQUIREMENTS.md -> the ADVANCED flow (product + technical Q&A,\n# package decomposition, per-package parallel build + agentic spec-review).\n# * anything else (e.g. .gtd/TODO.md) -> the SIMPLE flow (one grilling Q&A\n# loop, a monolithic build, no decomposition, no agentic review).\n#\n# A third entry, `gtd review <commitish>`, jumps straight to `reviewing`\n# (reviewEntry) to review a foreign branch through the same shared tail.\n#\n# All three converge at `reviewing` -> `await-review`. Ticking a REVIEW.md box\n# means only \"I reviewed this hunk\"; a COMMENT (not an unticked box) is what asks\n# for changes. Every human step at `await-review` goes to `review-deciding`,\n# which inspects the step and routes:\n# * no comment (every box ticked, no REVIEW.md note, no code edit) -> the diff\n# is a bare REVIEW.md deletion -> squashing -> done (the ONLY path to the\n# finale — the whole cycle collapses into one commit).\n# * any comment — a note in REVIEW.md OR a hand-edit to code — ->\n# review-deciding writes REVIEW_FEEDBACK.md -> feedback-building implements\n# it (completing any code the human hand-edited, never reverting their\n# lines) -> checking -> reviewing regenerates an INCREMENTAL REVIEW.md\n# (review-deciding carries `reviewBase: true`, so each feedback round's\n# review + checkout window covers only `last-review..HEAD`, i.e. the agent's\n# follow-through, not the human's own edit) -> await-review, looping until\n# sign-off.\n# Two dead ends never commit: the sign-off gate (src/program.ts) REFUSES a step\n# that leaves a box unticked with no comment (finish reviewing first) and a\n# deleted REVIEW.md (restore it and tick the boxes).\n#\n# The shared health segment (`checking`/`fixing`/`escalate`) serves the simple\n# build and the feedback loop — its green edge targets\n# `reviewing`. The advanced per-package loop keeps its OWN health triple\n# (`adv-checking`/`adv-fixing`/`adv-escalate`) because its green edge targets\n# the per-package `spec-review` gate instead.\n#\n# KNOWN LIMITATION: `on` pattern keys are NOT Eta templates — they keep the\n# LITERAL `.gtd/...` paths matching these vars' DEFAULT values. Repointing a\n# file var changes what templates read/write WITHOUT changing what the `on`\n# patterns match, desyncing the machine (see docs/configuration.md).\n\nvars:\n testCommand: npm test\n todoFile: .gtd/TODO.md\n requirementsFile: .gtd/REQUIREMENTS.md\n architectureFile: .gtd/ARCHITECTURE.md\n packagesDir: .gtd/packages\n nextFile: .gtd/NEXT.md\n reviewFile: .gtd/REVIEW.md\n reviewFeedbackFile: .gtd/REVIEW_FEEDBACK.md\n feedbackFile: .gtd/FEEDBACK.md\n specFeedbackFile: .gtd/SPEC_FEEDBACK.md\n commitMsgFile: .gtd/COMMIT_MSG.md\n # The two opaque model tiers every agent state draws from — repoint them here\n # (or via a top-level `.gtdrc` `vars:` key / a `GTD_VAR_plannerModel` env var)\n # to change every state on that tier at once. `plannerModel` (default \"smart\")\n # is the heavier tier for the one-shot planning/architecting/review turns;\n # `coderModel` (default \"base\") is the tier for the build/fix turns.\n plannerModel: smart\n coderModel: base\n\nstates:\n # ── Shared entry ────────────────────────────────────────────────────────\n idle:\n actor: human\n initial: true\n # Every human-gate `message:` below closes with the same rendered \"What\n # each change does next\" list, built from `it.edges` (the resting state's\n # own `on` edges — each edge's `describe:` is the single source of truth).\n message: |\n No active gtd cycle.\n\n To start one, create a steering file describing what you want built:\n\n - `.gtd/TODO.md` — a short sketch — starts the SIMPLE flow (plan, build,\n review).\n - `.gtd/REQUIREMENTS.md` — the product requirements — starts the ADVANCED\n flow (product + technical Q&A, package decomposition, parallel build,\n per-package agentic review).\n\n What each change does next (then run `gtd step human`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"* .gtd/REQUIREMENTS.md\":\n to: adv-grilling\n describe: >-\n Save the product requirements to `.gtd/REQUIREMENTS.md` to start the\n ADVANCED flow — the agent develops them through product then technical\n Q&A, decomposes the work into packages, and builds each in parallel\n (**adv-grilling**).\n \"* **\":\n to: grilling\n describe: >-\n Save a short sketch to `.gtd/TODO.md` (or any other change) to start\n the SIMPLE flow — the agent reads it, explores the codebase, and\n develops it into a concrete implementation plan (**grilling**).\n\n # ── Simple flow: one Q&A loop, monolithic build ───────────────────────────\n grilling:\n actor: agent\n file: <%= it.vars.todoFile %>\n mode: qa\n model: <%= it.vars.plannerModel %>\n memory: plan\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.todoFile %>`, which this prompt tells you to develop.\n\n `<%= it.vars.todoFile %>` holds a short sketch of what to build (or\n feedback from a prior review lap). Read it, explore the codebase, and\n develop it into a concrete implementation plan.\n\n For every question you cannot settle yourself, add it under a\n `## Open Questions` heading near the top, one `### <question>`\n sub-heading each, its body your suggested answer in plain prose (always\n suggest one — the human accepts it by leaving it untouched, or overrides\n it by editing it). Omit the section entirely once there are none.\n\n If `<%= it.vars.todoFile %>` already carries a `## Open Questions`\n section from a previous lap, the human has now responded to that whole\n batch: fold every one of those questions into the plan — the human's edit\n where they gave one, your own suggestion where they left it untouched (an\n untouched suggestion IS acceptance) — and MOVE each resolved\n `### <question>` block down into a `## Answered Questions` section at the\n bottom. Raise any genuinely new questions under `## Open Questions`.\n\n Leave `<%= it.vars.todoFile %>` uncommitted and finish your turn — do\n not commit, and do not run `gtd step agent` yourself; the harness does\n that.\n on:\n \"* **\": grilling-answer\n\n grilling-answer:\n actor: human\n file: <%= it.vars.todoFile %>\n mode: qa\n message: |\n `<%= it.vars.todoFile %>` holds the plan under development, with any\n open questions under `## Open Questions` — each carrying a suggested\n answer. Answer a question by editing its body in place; leave a\n suggestion untouched to accept it.\n\n What each change does next (then run `gtd step human`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"C\":\n to: building\n describe: >-\n Change nothing to accept every suggested answer as-is and move on to\n implementation (**building**).\n \"* **\":\n to: grilling\n describe: >-\n Edit the plan — answer a question, add detail, or raise a new one — to\n send it back for another round of grilling (**grilling**).\n\n building:\n actor: agent\n file: <%= it.vars.todoFile %>\n mode: qa\n model: <%= it.vars.coderModel %>\n memory: build\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create or edit anything under `.gtd/` except deleting\n `<%= it.vars.todoFile %>`, which this prompt tells you to delete once its\n work is complete.\n\n The plan to implement is: <%~ it.read(it.vars.todoFile) %>\n\n Read that plan and implement it in full. Use TDD discipline: one test,\n then the implementation that passes it, then the next — never write all\n the tests first.\n\n Delete `<%= it.vars.todoFile %>` once the work it describes is complete\n and verified. Leave everything else uncommitted and finish your turn.\n on:\n \"* **\": checking\n\n # ── Advanced flow: product + technical Q&A ────────────────────────────────\n adv-grilling:\n actor: agent\n file: <%= it.vars.requirementsFile %>\n mode: qa\n model: <%= it.vars.plannerModel %>\n memory: plan\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.requirementsFile %>`, which this prompt tells you to develop.\n\n `<%= it.vars.requirementsFile %>` holds the product requirements (or\n feedback from a prior review lap). Read it, explore the codebase, and\n develop it into a concrete, PRODUCT-level plan in this one turn.\n\n Scope: product and user-facing decisions only. Leave implementation\n details (file/module structure, data models, tech-stack choices) for the\n next phase (`<%= it.vars.architectureFile %>`).\n\n For every remaining open question, add it under a `## Open Questions`\n heading near the top, one `### <question>` sub-heading each, its body your\n suggested answer in plain prose (always suggest one). Omit the section\n once there are none. On a return lap, fold the whole answered batch into\n the plan and move each resolved `### <question>` block down into a\n `## Answered Questions` section at the bottom.\n\n Leave `<%= it.vars.requirementsFile %>` uncommitted and finish your turn.\n on:\n \"* **\": adv-grilling-answer\n\n adv-grilling-answer:\n actor: human\n file: <%= it.vars.requirementsFile %>\n mode: qa\n message: |\n `<%= it.vars.requirementsFile %>` holds the product plan under\n development, with any open questions under `## Open Questions` — each\n carrying a suggested answer. Answer a question by editing its body in\n place; leave a suggestion untouched to accept it.\n\n What each change does next (then run `gtd step human`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"C\":\n to: architecting\n describe: >-\n Change nothing to accept the product plan and move on to technical\n architecting (**architecting**).\n \"* **\":\n to: adv-grilling\n describe: >-\n Edit the product plan — answer a question, add detail, or raise a new\n one — to send it back for another round (**adv-grilling**).\n\n architecting:\n actor: agent\n file: <%= it.vars.architectureFile %>\n mode: qa\n model: <%= it.vars.plannerModel %>\n memory: plan\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.architectureFile %>` and `<%= it.vars.requirementsFile %>`,\n which this prompt tells you to write and delete respectively.\n\n Read `<%= it.vars.requirementsFile %>` (the converged product plan) and\n develop `<%= it.vars.architectureFile %>` from it in this one turn:\n file/module structure, data models, library/tech-stack choices,\n error-handling strategy — the *how*, building on the *what* already\n settled. Do not re-open product-level decisions; treat them as settled.\n\n For every remaining open question, add it under a `## Open Questions`\n heading exactly as the grilling phase does (one `### <question>` each, its\n body your suggested answer). Omit the section once there are none. On a\n return lap, fold the whole answered batch in and move each resolved block\n down into a `## Answered Questions` section at the bottom.\n\n Once `<%= it.vars.architectureFile %>` is written, delete\n `<%= it.vars.requirementsFile %>` — its content has been folded in and it\n must not linger. Leave everything uncommitted and finish your turn.\n on:\n \"* **\": architecting-answer\n\n architecting-answer:\n actor: human\n file: <%= it.vars.architectureFile %>\n mode: qa\n message: |\n `<%= it.vars.architectureFile %>` holds the technical plan under\n development, with any open questions under `## Open Questions`. Answer a\n question by editing its body in place; leave a suggestion untouched to\n accept it.\n\n What each change does next (then run `gtd step human`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"C\":\n to: decompose\n describe: >-\n Change nothing to accept the technical plan and move on to decomposing\n the work into packages (**decompose**).\n \"* **\":\n to: architecting\n describe: >-\n Edit the technical plan to send it back for another round\n (**architecting**).\n\n # ── Advanced flow: decomposition + per-package parallel build loop ─────────\n decompose:\n actor: agent\n model: <%= it.vars.coderModel %>\n memory: build\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/` except the\n package files this prompt tells you to write (under\n `<%= it.vars.packagesDir %>/`), and `<%= it.vars.architectureFile %>`,\n which it tells you to delete.\n\n Read `<%= it.vars.architectureFile %>` (the converged technical plan) and\n decompose it into an ordered set of WORK PACKAGES, one file each under\n `<%= it.vars.packagesDir %>/` (e.g. `<%= it.vars.packagesDir %>/01-name.md`,\n `<%= it.vars.packagesDir %>/02-name.md`, ...). Order the packages so the\n test suite can pass again after each one lands.\n\n Each package file describes a set of INDEPENDENT tasks that can be\n implemented concurrently without ordering constraints between them — a\n single build turn will fan them out to parallel subagents. Give each task\n a clear description, acceptance criteria as `- [ ]` checkboxes, and the\n relevant paths. Also state the package's overall requirement so it can be\n reviewed against its spec. Do NOT put ordering-dependent tasks in the same\n package — split them across ordered packages instead.\n\n No package file may reference any other `.gtd/` file. Once the package\n files are written, delete `<%= it.vars.architectureFile %>`. Leave\n everything uncommitted and finish your turn.\n on:\n \"* .gtd/packages/**\": picking\n\n picking:\n actor: check\n script: |\n #!/usr/bin/env bash\n # gtd check turn (the package queue arbiter) — the loop driver executes\n # this verbatim, then steps the check actor. Mechanics only: take the\n # first package file (by name) under the packages dir into NEXT.md, or\n # remove NEXT.md when the queue is empty. What NEXT.md's presence/absence\n # MEANS (another package to build vs. the queue closing out to review) is\n # decided by this state's own `on` rules at capture time — never here.\n set +e\n # Hoist the NEXT path once, at the TOP: Eta's autoTrim eats the newline\n # after an interpolation tag, so no tag may be the last token on a line —\n # it would glue the next line's `else`/`fi` onto it and break the script.\n # Below this point everything is plain bash.\n nextFile=\"<%~ it.vars.nextFile %>\"\n next=$(ls <%~ it.vars.packagesDir %>/*.md 2>/dev/null | head -n 1)\n if [ -n \"$next\" ]; then\n printf '%s' \"$next\" > \"$nextFile\"\n else\n rm -f \"$nextFile\"\n fi\n on:\n \"D .gtd/NEXT.md\": reviewing\n \"* .gtd/NEXT.md\": adv-building\n \"C\": reviewing\n\n adv-building:\n actor: agent\n model: <%= it.vars.coderModel %>\n memory: build\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/`; in\n particular do NOT delete the package file (the `spec-review` gate reads it\n after you) and never touch `<%= it.vars.nextFile %>` — the `picking` state\n owns it.\n\n The package to implement is: <%~ it.read(it.vars.nextFile) %>\n\n That file describes a set of INDEPENDENT tasks. Implement ALL of them in\n this one turn, fanning the independent tasks out to parallel subagents\n where your harness supports it — each task is self-contained and can be\n built concurrently. Implement EXACTLY the tasks this package describes — no\n more, no less; leave every other package file untouched. Use TDD\n discipline within each task.\n\n Leave the package file in place and everything else uncommitted, then\n finish your turn.\n on:\n \"* **\": adv-checking\n\n adv-checking:\n actor: check\n script: |\n #!/usr/bin/env bash\n # gtd check turn — run the suite; a red run records .gtd/FEEDBACK.md, a\n # green run cleans it up. The `on` rules decide what that means.\n set +e\n mkdir -p .gtd\n # Hoist the feedback path once, at the TOP: Eta's autoTrim eats the\n # newline after an interpolation tag, so no tag may be the last token on\n # a line — it would glue the next line's `else`/`fi` onto it and break\n # the script. Below this point everything is plain bash.\n feedback=\"<%~ it.vars.feedbackFile %>\"\n <%~ it.vars.testCommand %> > .gtd/.check-output 2>&1\n code=$?\n if [ \"$code\" -ne 0 ]; then\n if [ -s .gtd/.check-output ]; then\n mv .gtd/.check-output \"$feedback\"\n else\n rm -f .gtd/.check-output\n printf 'the test command failed with exit code %s and produced no output.' \"$code\" > \"$feedback\"\n fi\n else\n rm -f .gtd/.check-output\n rm -f \"$feedback\"\n fi\n on:\n \"A .gtd/FEEDBACK.md\": adv-fixing\n \"M .gtd/FEEDBACK.md\": adv-fixing\n \"D .gtd/FEEDBACK.md\": spec-review\n \"C\": spec-review\n\n adv-fixing:\n actor: agent\n file: <%= it.vars.feedbackFile %>\n model: <%= it.vars.coderModel %>\n memory: fix\n retry:\n max: 3\n otherwise: adv-escalate\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.feedbackFile %>`, which this prompt tells you to address.\n\n Read `<%= it.vars.feedbackFile %>` (the failing test output) and fix the\n code so the suite passes. Keep the change focused — do not refactor\n unrelated code. If the feedback is wrong, empty or delete\n `<%= it.vars.feedbackFile %>` instead of \"fixing\" a non-issue.\n\n Leave everything uncommitted and finish your turn — do not commit.\n on:\n \"* **\": adv-checking\n\n adv-escalate:\n actor: human\n file: <%= it.vars.feedbackFile %>\n message: |\n The agent could not get the check to pass after repeated attempts.\n `<%= it.vars.feedbackFile %>` holds the last failing output.\n\n What each change does next (then run `gtd step human`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"* **\":\n to: adv-checking\n describe: >-\n Fix the failure yourself — edit the code and/or\n `.gtd/FEEDBACK.md` — to re-run the check (**adv-checking**).\n\n spec-review:\n actor: agent\n model: <%= it.vars.plannerModel %>\n memory: review\n # Bounded per cycle: once entered `max` times, a transition into it is\n # redirected to `closing` (force-close the package and move on — the\n # unresolved concern is deferred to the shared human review at the tail,\n # which sees the whole diff anyway). NOTE the cap is PROCESS-scoped, so it\n # pools across packages — the same documented limitation the shared\n # `fixing` cap has (see STATES.md / docs/configuration.md).\n retry:\n max: 3\n otherwise: closing\n prompt: |\n You are an autonomous coding agent reviewing a freshly-built work package\n against its own spec. `.gtd/` holds this workflow's own state — never\n create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.specFeedbackFile %>`, which this prompt tells you to write\n when — and only when — you find problems.\n\n The package spec is: <%~ it.read(it.vars.nextFile) %>\n\n Read that spec, then verify the package's implementation (the diff below)\n against it: are all its tasks done, acceptance criteria met, and the code\n sound and consistent with the codebase?\n\n - If it fully satisfies the spec, write NOTHING and finish your turn — a\n clean turn is your approval.\n - If it does not, write `<%= it.vars.specFeedbackFile %>` listing the\n concrete problems for a fix turn to address. Be specific.\n\n Do not fix anything yourself and do not delete the package file — a later\n step owns that. Finish your turn.\n <% if (it.processDiff.trim()) { %>\n\n ## Diff under review\n\n ```diff\n <%~ it.processDiff %>\n ```\n <% } %>\n on:\n \"A .gtd/SPEC_FEEDBACK.md\": spec-fix\n \"M .gtd/SPEC_FEEDBACK.md\": spec-fix\n \"D .gtd/SPEC_FEEDBACK.md\": closing\n \"C\": closing\n\n spec-fix:\n actor: agent\n file: <%= it.vars.specFeedbackFile %>\n model: <%= it.vars.coderModel %>\n memory: fix\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.specFeedbackFile %>`, which this prompt tells you to address\n and then delete.\n\n Read `<%= it.vars.specFeedbackFile %>` (the reviewer's concerns about the\n current package) and the package spec (`<%= it.read(it.vars.nextFile) %>`),\n then fix the code to resolve every concern. Keep the change focused.\n\n Delete `<%= it.vars.specFeedbackFile %>` once every concern is addressed.\n Leave everything else uncommitted and finish your turn.\n on:\n \"* **\": adv-checking\n\n closing:\n actor: check\n script: |\n #!/usr/bin/env bash\n # gtd check turn (the package closer) — remove the just-reviewed package\n # file (its path is in NEXT.md) plus any leftover spec feedback, so\n # `picking` selects the NEXT package (or falls through to review when the\n # queue is empty). Reached on approval (spec-review clean) and on the\n # spec-review retry cap (force-close, deferring the concern to the tail).\n set +e\n pkg=$(cat <%~ it.vars.nextFile %> 2>/dev/null)\n [ -n \"$pkg\" ] && rm -f \"$pkg\"\n rm -f <%~ it.vars.specFeedbackFile %> <%~ it.vars.nextFile %>\n on:\n \"* **\": picking\n\n # ── Shared health segment (simple build, feedback loop, code-only edits) ──\n checking:\n actor: check\n script: |\n #!/usr/bin/env bash\n # gtd check turn — run the suite; a red run records .gtd/FEEDBACK.md, a\n # green run cleans it up. The `on` rules decide what that means.\n set +e\n mkdir -p .gtd\n # Hoist the feedback path once, at the TOP: Eta's autoTrim eats the\n # newline after an interpolation tag, so no tag may be the last token on\n # a line — it would glue the next line's `else`/`fi` onto it and break\n # the script. Below this point everything is plain bash.\n feedback=\"<%~ it.vars.feedbackFile %>\"\n <%~ it.vars.testCommand %> > .gtd/.check-output 2>&1\n code=$?\n if [ \"$code\" -ne 0 ]; then\n if [ -s .gtd/.check-output ]; then\n mv .gtd/.check-output \"$feedback\"\n else\n rm -f .gtd/.check-output\n printf 'the test command failed with exit code %s and produced no output.' \"$code\" > \"$feedback\"\n fi\n else\n rm -f .gtd/.check-output\n rm -f \"$feedback\"\n fi\n on:\n \"A .gtd/FEEDBACK.md\": fixing\n \"M .gtd/FEEDBACK.md\": fixing\n \"D .gtd/FEEDBACK.md\": reviewing\n \"C\": reviewing\n\n fixing:\n actor: agent\n file: <%= it.vars.feedbackFile %>\n model: <%= it.vars.coderModel %>\n memory: fix\n retry:\n max: 3\n otherwise: escalate\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.feedbackFile %>`, which this prompt tells you to address.\n\n Read `<%= it.vars.feedbackFile %>` (the failing test output) and fix the\n code so the suite passes. Keep the change focused — do not refactor\n unrelated code. If the feedback is wrong, empty or delete\n `<%= it.vars.feedbackFile %>` instead of \"fixing\" a non-issue.\n\n Leave everything uncommitted and finish your turn — do not commit.\n on:\n \"* **\": checking\n\n escalate:\n actor: human\n file: <%= it.vars.feedbackFile %>\n message: |\n The agent could not get the check to pass after repeated attempts.\n `<%= it.vars.feedbackFile %>` holds the last failing output.\n\n What each change does next (then run `gtd step human`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"* **\":\n to: checking\n describe: >-\n Fix the failure yourself — edit the code and/or `.gtd/FEEDBACK.md` —\n to re-run the check (**checking**).\n\n # ── Shared tail: agent review -> human review -> squash finale ────────────\n reviewing:\n actor: agent\n file: <%= it.vars.reviewFile %>\n mode: review\n model: <%= it.vars.plannerModel %>\n memory: review\n # `gtd review <commitish>` (a plain non-gtd branch, e.g. a colleague's PR)\n # enters here directly, recording the resolved base as a `Gtd-Review-Base:`\n # trailer on its empty entry commit — everything below then reuses the same\n # reviewing/await-review/feedback machinery over <commitish>..HEAD.\n reviewEntry: true\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.reviewFile %>`, which this prompt tells you to write.\n\n Write `<%= it.vars.reviewFile %>` to help a human review the diff below in\n this EXACT format:\n\n - First non-blank line: `# Review: <%= it.currentCommit.slice(0, 7) %>`\n - Somewhere in the document: `<!-- base: <%= it.startCommit %> -->`\n - At least one `## <Chunk Title>` heading, grouping hunks semantically\n (same feature/refactor/fix, even across files) — a short explanation of\n what changed and why, then one `- [ ] ./path/to/file.ts#42 — note` file\n pointer per hunk (checkboxes are for the human to tick, not you; every\n path is `./`-relative, with an optional `#line` and an optional\n ` — note`).\n\n The diff below is `it.reviewDiff` — on a feedback round it covers ONLY the\n changes made since the previous review, so you review just what changed,\n not items already signed off. Leave `<%= it.vars.reviewFile %>`\n uncommitted and finish your turn.\n <% if (it.reviewDiff.trim()) { %>\n\n ## Full diff under review\n\n ```diff\n <%~ it.reviewDiff %>\n ```\n <% } %>\n on:\n \"* **\": await-review\n\n await-review:\n actor: human\n file: <%= it.vars.reviewFile %>\n mode: review\n # While the cycle rests here, gtd opens a \"review checkout window\": raw HEAD\n # is rewound to the review base (the most-recent `review-deciding` commit —\n # so a feedback round shows only `last-review..HEAD` — or the process start\n # on the first review) with the working tree untouched, surfacing the diff\n # as ordinary uncommitted changes. It closes on the next invocation.\n #\n # Every human step here routes to `review-deciding`, which decides sign-off\n # vs. feedback from the step's CONTENT (a comment, not a tick). Ticking a box\n # only records \"I reviewed this hunk\". Two dead ends can't be told apart by a\n # file-pattern edge, so the sign-off gate (`enforceReviewSignoffGate` in\n # src/program.ts) catches them BEFORE the step commits and refuses: an\n # unfinished review (a box still `- [ ]` with no comment) and a deleted\n # REVIEW.md.\n reviewWindow: true\n message: |\n `<%= it.vars.reviewFile %>` holds the review record for the cycle — one\n `- [ ]` checkbox per reviewable item, grouped into chunks. Tick a box\n (`- [x]`) as you review each hunk; ticking only records that you've read\n it, it is not sign-off.\n\n When you've been through the whole diff, run `gtd step human`:\n\n - Tick EVERY box and leave no comment — no note in\n `<%= it.vars.reviewFile %>`, no code edit — to sign off and collapse the\n cycle into one commit (**squashing**).\n - Leave a comment to request changes — a note on a\n `<%= it.vars.reviewFile %>` line, or a direct code edit — to send it to a\n build + re-review round (**review-deciding** → **feedback-building**). A\n code edit counts as your own fix: the agent completes the follow-through\n it implies and never reverts your lines.\n\n Stepping with a box still unticked and no comment is refused (finish\n reviewing first); so is deleting `<%= it.vars.reviewFile %>`.\n on:\n \"* **\": review-deciding\n\n review-deciding:\n actor: check\n file: <%= it.vars.reviewFile %>\n mode: review\n # Anchors the INCREMENTAL review base: its most-recent in-process commit is\n # the point the previous review round ended, so the next `reviewing` turn\n # (and the review checkout window) covers only the changes since then. On\n # the first review there is no such commit, so the base is the cycle start.\n reviewBase: true\n script: |\n #!/usr/bin/env bash\n # gtd check turn — decide the human's review step from its CONTENT. HEAD is\n # the human's `await-review -> review-deciding` commit; HEAD^ is the\n # `reviewing` commit that wrote the agent's original REVIEW.md (the sign-off\n # gate refuses an incomplete step, so review-deciding is entered exactly\n # once per round — nothing commits in between). It is FEEDBACK when the\n # human left a comment: a note in REVIEW.md (any edit beyond a \"[ ]\"->\"[x]\"\n # tick) OR a hand-edit to any non-.gtd/ file this round. Otherwise it is a\n # clean sign-off. Either way, remove REVIEW.md; what the resulting diff\n # MEANS is decided by this state's own `on` rules — the A/M\n # REVIEW_FEEDBACK.md rows are declared BEFORE the D REVIEW.md row so a\n # feedback round (which also deletes REVIEW.md) is captured as feedback,\n # not mistaken for a sign-off.\n set +e\n mkdir -p .gtd\n # Hoist the paths once, at the TOP: Eta's autoTrim eats the newline after\n # an interpolation tag, so no tag may be the last token on a line — it\n # would glue the next line onto it and break the script. Below this point\n # everything is plain bash.\n reviewFile=\"<%~ it.vars.reviewFile %>\"\n reviewFeedbackFile=\"<%~ it.vars.reviewFeedbackFile %>\"\n # Which non-.gtd/ files did the human hand-edit this round?\n codeFiles=$(git diff-tree --no-commit-id --name-only -r HEAD | grep -v '^\\.gtd/')\n # A note is any REVIEW.md change beyond a checkbox flip: normalize every\n # \"[ ]\"/\"[x]\" to a placeholder, then compare the agent's original (HEAD^)\n # against the human's version (HEAD).\n notes=\"\"\n before=$(git show \"HEAD^:$reviewFile\" 2>/dev/null | sed -E 's/\\[[ xX]\\]/[_]/g')\n after=$(git show \"HEAD:$reviewFile\" 2>/dev/null | sed -E 's/\\[[ xX]\\]/[_]/g')\n if [ \"$before\" != \"$after\" ]; then notes=1; fi\n if [ -n \"$codeFiles\" ] || [ -n \"$notes\" ]; then\n {\n echo \"Review feedback to address, then delete this file. The human reviewed the diff and asked for the changes below.\"\n echo \"Build on their own edits: complete only the follow-through each change implies (callers, tests, types, docs). Treat any lines the human wrote as final — never revert or rewrite them.\"\n if [ -n \"$notes\" ]; then\n echo\n echo \"## Notes left in the review (ticked items are approved — act on the notes)\"\n echo\n echo '```diff'\n git show HEAD --format= -- \"$reviewFile\"\n echo '```'\n fi\n if [ -n \"$codeFiles\" ]; then\n echo\n echo \"## Code the human hand-edited this round — already committed, build on top\"\n echo\n echo '```diff'\n git show HEAD --format= -- $codeFiles\n echo '```'\n fi\n } > \"$reviewFeedbackFile\"\n fi\n rm -f \"$reviewFile\"\n on:\n \"A .gtd/REVIEW_FEEDBACK.md\": feedback-building\n \"M .gtd/REVIEW_FEEDBACK.md\": feedback-building\n \"D .gtd/REVIEW.md\": squashing\n\n feedback-building:\n actor: agent\n file: <%= it.vars.reviewFeedbackFile %>\n model: <%= it.vars.coderModel %>\n memory: build\n prompt: |\n You are an autonomous coding agent addressing review feedback. `.gtd/`\n holds this workflow's own state — never create, edit, or delete anything\n under `.gtd/` except `<%= it.vars.reviewFeedbackFile %>`, which this\n prompt tells you to address and then delete.\n\n The review feedback to address is: <%~ it.read(it.vars.reviewFeedbackFile) %>\n\n Implement exactly those changes — no Q&A, no re-planning. Use TDD\n discipline. When the feedback includes code the human hand-edited this\n round, that edit is ALREADY committed: treat their lines as final, build ON\n TOP of them to complete only the follow-through they imply (callers, tests,\n types, docs), and never revert or rewrite what they changed. Delete\n `<%= it.vars.reviewFeedbackFile %>` once every item is addressed. Leave\n everything else uncommitted and finish your turn.\n on:\n \"* **\": checking\n\n squashing:\n actor: agent\n file: <%= it.vars.commitMsgFile %>\n model: <%= it.vars.coderModel %>\n memory: build\n prompt: |\n You are an autonomous coding agent. The cycle is approved and done.\n `.gtd/` holds this workflow's own state — never create, edit, or delete\n anything under `.gtd/` except `<%= it.vars.commitMsgFile %>`, which this\n prompt tells you to write.\n\n Leave `<%= it.vars.commitMsgFile %>` uncommitted and finish your turn —\n entering this file squashes everything this process retains into one\n commit using its content as the message. The diff below is exactly what\n that commit will contain: `it.retainedDiff`, the changes THIS process\n made (its trace/retry boundary onward). For a normal build cycle that is\n the whole feature; for a `gtd review` process it is only the fixes made\n during the review — NOT the changeset that was under review.\n <% if (it.retainedDiff.trim()) { %>\n\n Write `<%= it.vars.commitMsgFile %>` with ONE conventional-commits message\n for the changes below (subject line `type(scope): subject`, imperative\n mood, <= 72 characters; a body explaining the why, trade-offs, and key\n decisions). Plain text, no markdown wrapper.\n\n ## Retained diff\n\n ```diff\n <%~ it.retainedDiff %>\n ```\n <% } else { %>\n\n This process retained NO changes (a clean review sign-off with no fixes).\n Write `<%= it.vars.commitMsgFile %>` with exactly this one line and\n nothing else:\n\n chore: human review\n <% } %>\n on:\n \"A .gtd/COMMIT_MSG.md\": done\n \"M .gtd/COMMIT_MSG.md\": done\n\n done:\n commit: |\n <%~ it.read(it.vars.commitMsgFile) %>\n\n Token cost: <%= it.processCost %>\n <% it.processCostByModel.forEach(function(m){ %>\n - <%= m.model %>: <%= m.cost %>\n <% }) %>\n";
|
|
191540
|
+
var unified_default = "# The unified gtd workflow template — written verbatim into `.gtdrc.json`\n# (nested under a `workflow:` key) by `gtd init` (no argument). gtd ships no\n# default workflow: a repo picks this bundled template with `gtd init`, and a\n# state command run with no workflow configured fails (see src/Config.ts).\n# See STATES.md §10 for the full walkthrough.\n#\n# Compiled through the exact same `compileWorkflowConfig` a user's `.gtdrc`\n# `workflow:` key goes through — no privileged code path\n# (src/workflows/templates.ts does the compiling). Every content string below\n# is INLINE (no `./`-relative file references): the bundled template ships\n# inside a single-file build (`dist/gtd.bundle.mjs`), so its content must\n# already be resolved at BUILD time. `gtd init` DERIVES the scaffolded\n# `.gtdrc.json` from this inline source (`renderInitScaffold`): each agent\n# state's `prompt:` is written out as an editable `gtd-prompts/<state>.md`\n# file; human `message:`/check `script:`/the `done` `commit:` body stay inline.\n#\n# ── Four entry points behind a green-baseline gate, one shared tail ─────────\n#\n# EVERY entry first runs the test suite and only proceeds when it is GREEN — you\n# never start new work (or a review) on top of a red baseline. A red run halts\n# at a human `*-start-blocked` gate (fix the failures and re-run, exactly like\n# the `escalate` states), or is what `gtd fix` exists to repair.\n#\n# The initial `idle` state FORKS on which steering file the human creates,\n# routing into that flow's OWN start gate:\n#\n# * .gtd/REQUIREMENTS.md -> `adv-start-check` -> the ADVANCED flow (product +\n# technical Q&A, package decomposition, per-package parallel build +\n# agentic spec-review).\n# * anything else (e.g. .gtd/TODO.md) -> `start-check` -> the SIMPLE flow (one\n# grilling Q&A loop, a monolithic build, no decomposition, no agentic\n# review).\n#\n# A third entry, `gtd review <commitish>`, jumps to `review-start-check`\n# (reviewEntry) and, once green, to `reviewing` to review a foreign branch\n# through the same shared tail. A fourth, `gtd fix`, jumps to `fix-check`\n# (fixEntry): a red suite drops straight into the shared `fixing` loop and out\n# through the review + squash tail; a green suite is a no-op back to `idle`.\n#\n# All three converge at `reviewing` -> `await-review`. Ticking a REVIEW.md box\n# means only \"I reviewed this hunk\"; a COMMENT (not an unticked box) is what asks\n# for changes. Every human step at `await-review` goes to `review-deciding`,\n# which inspects the step and routes:\n# * no comment (every box ticked, no REVIEW.md note, no code edit) -> the diff\n# is a bare REVIEW.md deletion -> squashing -> done (the ONLY path to the\n# finale — the whole cycle collapses into one commit).\n# * any comment — a note in REVIEW.md OR a hand-edit to code — ->\n# review-deciding writes REVIEW_FEEDBACK.md -> feedback-building implements\n# it (completing any code the human hand-edited, never reverting their\n# lines) -> checking -> reviewing regenerates an INCREMENTAL REVIEW.md\n# (review-deciding carries `reviewBase: true`, so each feedback round's\n# review + checkout window covers only `last-review..HEAD`, i.e. the agent's\n# follow-through, not the human's own edit) -> await-review, looping until\n# sign-off.\n# Two dead ends never commit: the sign-off gate (src/program.ts) REFUSES a step\n# that leaves a box unticked with no comment (finish reviewing first) and a\n# deleted REVIEW.md (restore it and tick the boxes).\n#\n# The shared health segment (`checking`/`fixing`/`escalate`) serves the simple\n# build and the feedback loop — its green edge targets\n# `reviewing`. The advanced per-package loop keeps its OWN health triple\n# (`adv-checking`/`adv-fixing`/`adv-escalate`) because its green edge targets\n# the per-package `spec-review` gate instead.\n#\n# KNOWN LIMITATION: `on` pattern keys are NOT Eta templates — they keep the\n# LITERAL `.gtd/...` paths matching these vars' DEFAULT values. Repointing a\n# file var changes what templates read/write WITHOUT changing what the `on`\n# patterns match, desyncing the machine (see docs/configuration.md).\n\nvars:\n testCommand: npm test\n todoFile: .gtd/TODO.md\n requirementsFile: .gtd/REQUIREMENTS.md\n architectureFile: .gtd/ARCHITECTURE.md\n packagesDir: .gtd/packages\n nextFile: .gtd/NEXT.md\n reviewFile: .gtd/REVIEW.md\n reviewRawFile: .gtd/REVIEW_RAW.md\n reviewFeedbackFile: .gtd/REVIEW_FEEDBACK.md\n feedbackFile: .gtd/FEEDBACK.md\n specFeedbackFile: .gtd/SPEC_FEEDBACK.md\n commitMsgFile: .gtd/COMMIT_MSG.md\n # The two opaque model tiers every agent state draws from — repoint them here\n # (or via a top-level `.gtdrc` `vars:` key / a `GTD_VAR_plannerModel` env var)\n # to change every state on that tier at once. `plannerModel` (default \"smart\")\n # is the heavier tier for the one-shot planning/architecting/review turns;\n # `coderModel` (default \"base\") is the tier for the build/fix turns.\n plannerModel: smart\n coderModel: base\n\nstates:\n # ── Shared entry ────────────────────────────────────────────────────────\n idle:\n actor: human\n initial: true\n # Every human-gate `message:` below closes with the same rendered \"What\n # each change does next\" list, built from `it.edges` (the resting state's\n # own `on` edges — each edge's `describe:` is the single source of truth).\n message: |\n No active gtd cycle.\n\n To start one, create a steering file describing what you want built:\n\n - `.gtd/TODO.md` — a short sketch — starts the SIMPLE flow (plan, build,\n review).\n - `.gtd/REQUIREMENTS.md` — the product requirements — starts the ADVANCED\n flow (product + technical Q&A, package decomposition, parallel build,\n per-package agentic review).\n\n What each change does next (then run `gtd step human`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"* .gtd/REQUIREMENTS.md\":\n to: adv-start-check\n describe: >-\n Save the product requirements to `.gtd/REQUIREMENTS.md` to start the\n ADVANCED flow — gtd first checks the test baseline is green, then the\n agent develops them through product then technical Q&A, decomposes the\n work into packages, and builds each in parallel (**adv-start-check**).\n \"* **\":\n to: start-check\n describe: >-\n Save a short sketch to `.gtd/TODO.md` (or any other change) to start\n the SIMPLE flow — gtd first checks the test baseline is green, then the\n agent reads it, explores the codebase, and develops it into a concrete\n implementation plan (**start-check**).\n\n # ── Green-baseline gate: simple flow ──────────────────────────────────────\n # Runs the suite before any planning starts. Its script is `checking`'s test\n # wrapper verbatim (red -> .gtd/FEEDBACK.md, green -> remove it); the `on`\n # rules turn that into \"halt at start-blocked\" vs \"proceed to grilling\".\n start-check:\n actor: check\n script: |\n #!/usr/bin/env bash\n # gtd check turn — run the suite before starting new work; a red run\n # records .gtd/FEEDBACK.md (halt), a green run cleans it up (proceed).\n set +e\n mkdir -p .gtd\n # Hoist the feedback path once, at the TOP: Eta's autoTrim eats the\n # newline after an interpolation tag, so no tag may be the last token on\n # a line — it would glue the next line's `else`/`fi` onto it and break\n # the script. Below this point everything is plain bash.\n feedback=\"<%~ it.vars.feedbackFile %>\"\n <%~ it.vars.testCommand %> > .gtd/.check-output 2>&1\n code=$?\n if [ \"$code\" -ne 0 ]; then\n if [ -s .gtd/.check-output ]; then\n mv .gtd/.check-output \"$feedback\"\n else\n rm -f .gtd/.check-output\n printf 'the test command failed with exit code %s and produced no output.' \"$code\" > \"$feedback\"\n fi\n else\n rm -f .gtd/.check-output\n rm -f \"$feedback\"\n fi\n on:\n \"A .gtd/FEEDBACK.md\": start-blocked\n \"M .gtd/FEEDBACK.md\": start-blocked\n \"D .gtd/FEEDBACK.md\": grilling\n \"C\": grilling\n\n start-blocked:\n actor: human\n file: <%= it.vars.feedbackFile %>\n message: |\n The test baseline is red — gtd will not start new work on a broken suite.\n `<%= it.vars.feedbackFile %>` holds the failing output.\n\n What each change does next (then run `gtd step human`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"* **\":\n to: start-check\n describe: >-\n Fix the failing tests — edit the code and/or `.gtd/FEEDBACK.md` — to\n re-run the baseline check (**start-check**). To repair the baseline as\n its own separate reviewed commit instead, abandon this start and run\n `gtd fix` from a clean `idle`.\n\n # ── Simple flow: one Q&A loop, monolithic build ───────────────────────────\n grilling:\n actor: agent\n file: <%= it.vars.todoFile %>\n mode: qa\n model: <%= it.vars.plannerModel %>\n memory: plan\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.todoFile %>`, which this prompt tells you to develop.\n\n `<%= it.vars.todoFile %>` holds a short sketch of what to build (or\n feedback from a prior review lap). Read it, explore the codebase, and\n develop it into a concrete implementation plan.\n\n For every question you cannot settle yourself, add it under a\n `## Open Questions` heading near the top, one `### <question>`\n sub-heading each, its body your suggested answer in plain prose (always\n suggest one — the human accepts it by leaving it untouched, or overrides\n it by editing it). Omit the section entirely once there are none.\n\n If `<%= it.vars.todoFile %>` already carries a `## Open Questions`\n section from a previous lap, the human has now responded to that whole\n batch: fold every one of those questions into the plan — the human's edit\n where they gave one, your own suggestion where they left it untouched (an\n untouched suggestion IS acceptance) — and MOVE each resolved\n `### <question>` block down into a `## Answered Questions` section at the\n bottom. Raise any genuinely new questions under `## Open Questions`.\n\n Leave `<%= it.vars.todoFile %>` uncommitted and finish your turn — do\n not commit, and do not run `gtd step agent` yourself; the harness does\n that.\n on:\n \"* **\": grilling-answer\n\n grilling-answer:\n actor: human\n file: <%= it.vars.todoFile %>\n mode: qa\n message: |\n `<%= it.vars.todoFile %>` holds the plan under development, with any\n open questions under `## Open Questions` — each carrying a suggested\n answer. Answer a question by editing its body in place; leave a\n suggestion untouched to accept it.\n\n What each change does next (then run `gtd step human`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"C\":\n to: building\n describe: >-\n Change nothing to accept every suggested answer as-is and move on to\n implementation (**building**).\n \"* **\":\n to: grilling\n describe: >-\n Edit the plan — answer a question, add detail, or raise a new one — to\n send it back for another round of grilling (**grilling**).\n\n building:\n actor: agent\n file: <%= it.vars.todoFile %>\n mode: qa\n model: <%= it.vars.coderModel %>\n memory: build\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create or edit anything under `.gtd/` except deleting\n `<%= it.vars.todoFile %>`, which this prompt tells you to delete once its\n work is complete.\n\n The plan to implement is: <%~ it.read(it.vars.todoFile) %>\n\n Read that plan and implement it in full. Use TDD discipline: one test,\n then the implementation that passes it, then the next — never write all\n the tests first.\n\n Delete `<%= it.vars.todoFile %>` once the work it describes is complete\n and verified. Leave everything else uncommitted and finish your turn.\n on:\n \"* **\": checking\n\n # ── Green-baseline gate: advanced flow ────────────────────────────────────\n adv-start-check:\n actor: check\n script: |\n #!/usr/bin/env bash\n # gtd check turn — run the suite before starting the advanced flow; a red\n # run records .gtd/FEEDBACK.md (halt), a green run cleans it up (proceed).\n set +e\n mkdir -p .gtd\n # Hoist the feedback path once, at the TOP: Eta's autoTrim eats the\n # newline after an interpolation tag, so no tag may be the last token on\n # a line — it would glue the next line's `else`/`fi` onto it and break\n # the script. Below this point everything is plain bash.\n feedback=\"<%~ it.vars.feedbackFile %>\"\n <%~ it.vars.testCommand %> > .gtd/.check-output 2>&1\n code=$?\n if [ \"$code\" -ne 0 ]; then\n if [ -s .gtd/.check-output ]; then\n mv .gtd/.check-output \"$feedback\"\n else\n rm -f .gtd/.check-output\n printf 'the test command failed with exit code %s and produced no output.' \"$code\" > \"$feedback\"\n fi\n else\n rm -f .gtd/.check-output\n rm -f \"$feedback\"\n fi\n on:\n \"A .gtd/FEEDBACK.md\": adv-start-blocked\n \"M .gtd/FEEDBACK.md\": adv-start-blocked\n \"D .gtd/FEEDBACK.md\": adv-grilling\n \"C\": adv-grilling\n\n adv-start-blocked:\n actor: human\n file: <%= it.vars.feedbackFile %>\n message: |\n The test baseline is red — gtd will not start new work on a broken suite.\n `<%= it.vars.feedbackFile %>` holds the failing output.\n\n What each change does next (then run `gtd step human`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"* **\":\n to: adv-start-check\n describe: >-\n Fix the failing tests — edit the code and/or `.gtd/FEEDBACK.md` — to\n re-run the baseline check (**adv-start-check**). To repair the baseline\n as its own separate reviewed commit instead, abandon this start and run\n `gtd fix` from a clean `idle`.\n\n # ── Advanced flow: product + technical Q&A ────────────────────────────────\n adv-grilling:\n actor: agent\n file: <%= it.vars.requirementsFile %>\n mode: qa\n model: <%= it.vars.plannerModel %>\n memory: plan\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.requirementsFile %>`, which this prompt tells you to develop.\n\n `<%= it.vars.requirementsFile %>` holds the product requirements (or\n feedback from a prior review lap). Read it, explore the codebase, and\n develop it into a concrete, PRODUCT-level plan in this one turn.\n\n Scope: product and user-facing decisions only. Leave implementation\n details (file/module structure, data models, tech-stack choices) for the\n next phase (`<%= it.vars.architectureFile %>`).\n\n For every remaining open question, add it under a `## Open Questions`\n heading near the top, one `### <question>` sub-heading each, its body your\n suggested answer in plain prose (always suggest one). Omit the section\n once there are none. On a return lap, fold the whole answered batch into\n the plan and move each resolved `### <question>` block down into a\n `## Answered Questions` section at the bottom.\n\n Leave `<%= it.vars.requirementsFile %>` uncommitted and finish your turn.\n on:\n \"* **\": adv-grilling-answer\n\n adv-grilling-answer:\n actor: human\n file: <%= it.vars.requirementsFile %>\n mode: qa\n message: |\n `<%= it.vars.requirementsFile %>` holds the product plan under\n development, with any open questions under `## Open Questions` — each\n carrying a suggested answer. Answer a question by editing its body in\n place; leave a suggestion untouched to accept it.\n\n What each change does next (then run `gtd step human`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"C\":\n to: architecting\n describe: >-\n Change nothing to accept the product plan and move on to technical\n architecting (**architecting**).\n \"* **\":\n to: adv-grilling\n describe: >-\n Edit the product plan — answer a question, add detail, or raise a new\n one — to send it back for another round (**adv-grilling**).\n\n architecting:\n actor: agent\n file: <%= it.vars.architectureFile %>\n mode: qa\n model: <%= it.vars.plannerModel %>\n memory: plan\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.architectureFile %>` and `<%= it.vars.requirementsFile %>`,\n which this prompt tells you to write and delete respectively.\n\n Read `<%= it.vars.requirementsFile %>` (the converged product plan) and\n develop `<%= it.vars.architectureFile %>` from it in this one turn:\n file/module structure, data models, library/tech-stack choices,\n error-handling strategy — the *how*, building on the *what* already\n settled. Do not re-open product-level decisions; treat them as settled.\n\n For every remaining open question, add it under a `## Open Questions`\n heading exactly as the grilling phase does (one `### <question>` each, its\n body your suggested answer). Omit the section once there are none. On a\n return lap, fold the whole answered batch in and move each resolved block\n down into a `## Answered Questions` section at the bottom.\n\n Once `<%= it.vars.architectureFile %>` is written, delete\n `<%= it.vars.requirementsFile %>` — its content has been folded in and it\n must not linger. Leave everything uncommitted and finish your turn.\n on:\n \"* **\": architecting-answer\n\n architecting-answer:\n actor: human\n file: <%= it.vars.architectureFile %>\n mode: qa\n message: |\n `<%= it.vars.architectureFile %>` holds the technical plan under\n development, with any open questions under `## Open Questions`. Answer a\n question by editing its body in place; leave a suggestion untouched to\n accept it.\n\n What each change does next (then run `gtd step human`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"C\":\n to: decompose\n describe: >-\n Change nothing to accept the technical plan and move on to decomposing\n the work into packages (**decompose**).\n \"* **\":\n to: architecting\n describe: >-\n Edit the technical plan to send it back for another round\n (**architecting**).\n\n # ── Advanced flow: decomposition + per-package parallel build loop ─────────\n decompose:\n actor: agent\n model: <%= it.vars.coderModel %>\n memory: build\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/` except the\n package files this prompt tells you to write (under\n `<%= it.vars.packagesDir %>/`), and `<%= it.vars.architectureFile %>`,\n which it tells you to delete.\n\n Read `<%= it.vars.architectureFile %>` (the converged technical plan) and\n decompose it into an ordered set of WORK PACKAGES, one file each under\n `<%= it.vars.packagesDir %>/` (e.g. `<%= it.vars.packagesDir %>/01-name.md`,\n `<%= it.vars.packagesDir %>/02-name.md`, ...). Order the packages so the\n test suite can pass again after each one lands.\n\n Each package file describes a set of INDEPENDENT tasks that can be\n implemented concurrently without ordering constraints between them — a\n single build turn will fan them out to parallel subagents. Give each task\n a clear description, acceptance criteria as `- [ ]` checkboxes, and the\n relevant paths. Also state the package's overall requirement so it can be\n reviewed against its spec. Do NOT put ordering-dependent tasks in the same\n package — split them across ordered packages instead.\n\n No package file may reference any other `.gtd/` file. Once the package\n files are written, delete `<%= it.vars.architectureFile %>`. Leave\n everything uncommitted and finish your turn.\n on:\n \"* .gtd/packages/**\": picking\n\n picking:\n actor: check\n script: |\n #!/usr/bin/env bash\n # gtd check turn (the package queue arbiter) — the loop driver executes\n # this verbatim, then steps the check actor. Mechanics only: take the\n # first package file (by name) under the packages dir into NEXT.md, or\n # remove NEXT.md when the queue is empty. What NEXT.md's presence/absence\n # MEANS (another package to build vs. the queue closing out to review) is\n # decided by this state's own `on` rules at capture time — never here.\n set +e\n # Hoist the NEXT path once, at the TOP: Eta's autoTrim eats the newline\n # after an interpolation tag, so no tag may be the last token on a line —\n # it would glue the next line's `else`/`fi` onto it and break the script.\n # Below this point everything is plain bash.\n nextFile=\"<%~ it.vars.nextFile %>\"\n next=$(ls <%~ it.vars.packagesDir %>/*.md 2>/dev/null | head -n 1)\n if [ -n \"$next\" ]; then\n printf '%s' \"$next\" > \"$nextFile\"\n else\n rm -f \"$nextFile\"\n fi\n on:\n \"D .gtd/NEXT.md\": reviewing\n \"* .gtd/NEXT.md\": adv-building\n \"C\": reviewing\n\n adv-building:\n actor: agent\n model: <%= it.vars.coderModel %>\n memory: build\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/`; in\n particular do NOT delete the package file (the `spec-review` gate reads it\n after you) and never touch `<%= it.vars.nextFile %>` — the `picking` state\n owns it.\n\n The package to implement is: <%~ it.read(it.vars.nextFile) %>\n\n That file describes a set of INDEPENDENT tasks. Implement ALL of them in\n this one turn, fanning the independent tasks out to parallel subagents\n where your harness supports it — each task is self-contained and can be\n built concurrently. Implement EXACTLY the tasks this package describes — no\n more, no less; leave every other package file untouched. Use TDD\n discipline within each task.\n\n Leave the package file in place and everything else uncommitted, then\n finish your turn.\n on:\n \"* **\": adv-checking\n\n adv-checking:\n actor: check\n script: |\n #!/usr/bin/env bash\n # gtd check turn — run the suite; a red run records .gtd/FEEDBACK.md, a\n # green run cleans it up. The `on` rules decide what that means.\n set +e\n mkdir -p .gtd\n # Hoist the feedback path once, at the TOP: Eta's autoTrim eats the\n # newline after an interpolation tag, so no tag may be the last token on\n # a line — it would glue the next line's `else`/`fi` onto it and break\n # the script. Below this point everything is plain bash.\n feedback=\"<%~ it.vars.feedbackFile %>\"\n <%~ it.vars.testCommand %> > .gtd/.check-output 2>&1\n code=$?\n if [ \"$code\" -ne 0 ]; then\n if [ -s .gtd/.check-output ]; then\n mv .gtd/.check-output \"$feedback\"\n else\n rm -f .gtd/.check-output\n printf 'the test command failed with exit code %s and produced no output.' \"$code\" > \"$feedback\"\n fi\n else\n rm -f .gtd/.check-output\n rm -f \"$feedback\"\n fi\n on:\n \"A .gtd/FEEDBACK.md\": adv-fixing\n \"M .gtd/FEEDBACK.md\": adv-fixing\n \"D .gtd/FEEDBACK.md\": spec-review\n \"C\": spec-review\n\n adv-fixing:\n actor: agent\n file: <%= it.vars.feedbackFile %>\n model: <%= it.vars.coderModel %>\n memory: fix\n retry:\n max: 3\n otherwise: adv-escalate\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.feedbackFile %>`, which this prompt tells you to address.\n\n Read `<%= it.vars.feedbackFile %>` (the failing test output) and fix the\n code so the suite passes. Keep the change focused — do not refactor\n unrelated code. If the feedback is wrong, empty or delete\n `<%= it.vars.feedbackFile %>` instead of \"fixing\" a non-issue.\n\n Leave everything uncommitted and finish your turn — do not commit.\n on:\n \"* **\": adv-checking\n\n adv-escalate:\n actor: human\n file: <%= it.vars.feedbackFile %>\n message: |\n The agent could not get the check to pass after repeated attempts.\n `<%= it.vars.feedbackFile %>` holds the last failing output.\n\n What each change does next (then run `gtd step human`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"* **\":\n to: adv-checking\n describe: >-\n Fix the failure yourself — edit the code and/or\n `.gtd/FEEDBACK.md` — to re-run the check (**adv-checking**).\n\n spec-review:\n actor: agent\n model: <%= it.vars.plannerModel %>\n memory: review\n # Bounded per cycle: once entered `max` times, a transition into it is\n # redirected to `closing` (force-close the package and move on — the\n # unresolved concern is deferred to the shared human review at the tail,\n # which sees the whole diff anyway). NOTE the cap is PROCESS-scoped, so it\n # pools across packages — the same documented limitation the shared\n # `fixing` cap has (see STATES.md / docs/configuration.md).\n retry:\n max: 3\n otherwise: closing\n prompt: |\n You are an autonomous coding agent reviewing a freshly-built work package\n against its own spec. `.gtd/` holds this workflow's own state — never\n create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.specFeedbackFile %>`, which this prompt tells you to write\n when — and only when — you find problems.\n\n The package spec is: <%~ it.read(it.vars.nextFile) %>\n\n Read that spec, then verify the package's implementation (the diff below)\n against it: are all its tasks done, acceptance criteria met, and the code\n sound and consistent with the codebase?\n\n - If it fully satisfies the spec, write NOTHING and finish your turn — a\n clean turn is your approval.\n - If it does not, write `<%= it.vars.specFeedbackFile %>` listing the\n concrete problems for a fix turn to address. Be specific.\n\n Do not fix anything yourself and do not delete the package file — a later\n step owns that. Finish your turn.\n <% if (it.processDiff.trim()) { %>\n\n ## Diff under review\n\n ```diff\n <%~ it.processDiff %>\n ```\n <% } %>\n on:\n \"A .gtd/SPEC_FEEDBACK.md\": spec-fix\n \"M .gtd/SPEC_FEEDBACK.md\": spec-fix\n \"D .gtd/SPEC_FEEDBACK.md\": closing\n \"C\": closing\n\n spec-fix:\n actor: agent\n file: <%= it.vars.specFeedbackFile %>\n model: <%= it.vars.coderModel %>\n memory: fix\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.specFeedbackFile %>`, which this prompt tells you to address\n and then delete.\n\n Read `<%= it.vars.specFeedbackFile %>` (the reviewer's concerns about the\n current package) and the package spec (`<%= it.read(it.vars.nextFile) %>`),\n then fix the code to resolve every concern. Keep the change focused.\n\n Delete `<%= it.vars.specFeedbackFile %>` once every concern is addressed.\n Leave everything else uncommitted and finish your turn.\n on:\n \"* **\": adv-checking\n\n closing:\n actor: check\n script: |\n #!/usr/bin/env bash\n # gtd check turn (the package closer) — remove the just-reviewed package\n # file (its path is in NEXT.md) plus any leftover spec feedback, so\n # `picking` selects the NEXT package (or falls through to review when the\n # queue is empty). Reached on approval (spec-review clean) and on the\n # spec-review retry cap (force-close, deferring the concern to the tail).\n set +e\n pkg=$(cat <%~ it.vars.nextFile %> 2>/dev/null)\n [ -n \"$pkg\" ] && rm -f \"$pkg\"\n rm -f <%~ it.vars.specFeedbackFile %> <%~ it.vars.nextFile %>\n on:\n \"* **\": picking\n\n # ── Shared health segment (simple build, feedback loop, code-only edits) ──\n checking:\n actor: check\n script: |\n #!/usr/bin/env bash\n # gtd check turn — run the suite; a red run records .gtd/FEEDBACK.md, a\n # green run cleans it up. The `on` rules decide what that means.\n set +e\n mkdir -p .gtd\n # Hoist the feedback path once, at the TOP: Eta's autoTrim eats the\n # newline after an interpolation tag, so no tag may be the last token on\n # a line — it would glue the next line's `else`/`fi` onto it and break\n # the script. Below this point everything is plain bash.\n feedback=\"<%~ it.vars.feedbackFile %>\"\n <%~ it.vars.testCommand %> > .gtd/.check-output 2>&1\n code=$?\n if [ \"$code\" -ne 0 ]; then\n if [ -s .gtd/.check-output ]; then\n mv .gtd/.check-output \"$feedback\"\n else\n rm -f .gtd/.check-output\n printf 'the test command failed with exit code %s and produced no output.' \"$code\" > \"$feedback\"\n fi\n else\n rm -f .gtd/.check-output\n rm -f \"$feedback\"\n fi\n on:\n \"A .gtd/FEEDBACK.md\": fixing\n \"M .gtd/FEEDBACK.md\": fixing\n \"D .gtd/FEEDBACK.md\": reviewing\n \"C\": reviewing\n\n fixing:\n actor: agent\n file: <%= it.vars.feedbackFile %>\n model: <%= it.vars.coderModel %>\n memory: fix\n retry:\n max: 3\n otherwise: escalate\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.feedbackFile %>`, which this prompt tells you to address.\n\n Read `<%= it.vars.feedbackFile %>` (the failing test output) and fix the\n code so the suite passes. Keep the change focused — do not refactor\n unrelated code. If the feedback is wrong, empty or delete\n `<%= it.vars.feedbackFile %>` instead of \"fixing\" a non-issue.\n\n Leave everything uncommitted and finish your turn — do not commit.\n on:\n \"* **\": checking\n\n escalate:\n actor: human\n file: <%= it.vars.feedbackFile %>\n message: |\n The agent could not get the check to pass after repeated attempts.\n `<%= it.vars.feedbackFile %>` holds the last failing output.\n\n What each change does next (then run `gtd step human`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"* **\":\n to: checking\n describe: >-\n Fix the failure yourself — edit the code and/or `.gtd/FEEDBACK.md` —\n to re-run the check (**checking**).\n\n # ── Green-baseline gate: `gtd review` entry ───────────────────────────────\n # `gtd review <commitish>` enters HERE (reviewEntry), not `reviewing`. It runs\n # the suite on HEAD first; green proceeds to `reviewing`, red halts. The\n # `Gtd-Review-Base:` trailer rides on this state's entry commit, so the review\n # window / incremental-diff machinery downstream is unchanged.\n review-start-check:\n actor: check\n reviewEntry: true\n script: |\n #!/usr/bin/env bash\n # gtd check turn — run the suite before reviewing; a red run records\n # .gtd/FEEDBACK.md (halt), a green run cleans it up (proceed to reviewing).\n set +e\n mkdir -p .gtd\n # Hoist the feedback path once, at the TOP: Eta's autoTrim eats the\n # newline after an interpolation tag, so no tag may be the last token on\n # a line — it would glue the next line's `else`/`fi` onto it and break\n # the script. Below this point everything is plain bash.\n feedback=\"<%~ it.vars.feedbackFile %>\"\n <%~ it.vars.testCommand %> > .gtd/.check-output 2>&1\n code=$?\n if [ \"$code\" -ne 0 ]; then\n if [ -s .gtd/.check-output ]; then\n mv .gtd/.check-output \"$feedback\"\n else\n rm -f .gtd/.check-output\n printf 'the test command failed with exit code %s and produced no output.' \"$code\" > \"$feedback\"\n fi\n else\n rm -f .gtd/.check-output\n rm -f \"$feedback\"\n fi\n on:\n \"A .gtd/FEEDBACK.md\": review-start-blocked\n \"M .gtd/FEEDBACK.md\": review-start-blocked\n \"D .gtd/FEEDBACK.md\": reviewing\n \"C\": reviewing\n\n review-start-blocked:\n actor: human\n file: <%= it.vars.feedbackFile %>\n message: |\n The test baseline is red — gtd will not start a review on a broken suite.\n `<%= it.vars.feedbackFile %>` holds the failing output.\n\n What each change does next (then run `gtd step human`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"* **\":\n to: review-start-check\n describe: >-\n Fix the failing tests — edit the code and/or `.gtd/FEEDBACK.md` — to\n re-run the baseline check (**review-start-check**).\n\n # ── Green-baseline gate: `gtd fix` entry ──────────────────────────────────\n # `gtd fix` enters HERE (fixEntry): run the suite, then a red run drops\n # straight into the shared `fixing` loop (-> checking -> review + squash\n # tail); a green run is a no-op back to `idle` (nothing to fix).\n fix-check:\n actor: check\n fixEntry: true\n script: |\n #!/usr/bin/env bash\n # gtd check turn — run the suite to find what needs fixing; a red run\n # records .gtd/FEEDBACK.md (-> fixing), a green run cleans it up (-> idle).\n set +e\n mkdir -p .gtd\n # Hoist the feedback path once, at the TOP: Eta's autoTrim eats the\n # newline after an interpolation tag, so no tag may be the last token on\n # a line — it would glue the next line's `else`/`fi` onto it and break\n # the script. Below this point everything is plain bash.\n feedback=\"<%~ it.vars.feedbackFile %>\"\n <%~ it.vars.testCommand %> > .gtd/.check-output 2>&1\n code=$?\n if [ \"$code\" -ne 0 ]; then\n if [ -s .gtd/.check-output ]; then\n mv .gtd/.check-output \"$feedback\"\n else\n rm -f .gtd/.check-output\n printf 'the test command failed with exit code %s and produced no output.' \"$code\" > \"$feedback\"\n fi\n else\n rm -f .gtd/.check-output\n rm -f \"$feedback\"\n fi\n on:\n \"A .gtd/FEEDBACK.md\": fixing\n \"M .gtd/FEEDBACK.md\": fixing\n \"D .gtd/FEEDBACK.md\": idle\n \"C\": idle\n\n # ── Shared tail: agent review -> human review -> squash finale ────────────\n reviewing:\n actor: agent\n file: <%= it.vars.reviewFile %>\n mode: review\n model: <%= it.vars.plannerModel %>\n memory: review\n # `gtd review <commitish>` (a plain non-gtd branch, e.g. a colleague's PR)\n # does NOT enter here directly — it enters `review-start-check` (reviewEntry)\n # first, which runs the green-baseline gate and, once green, transitions\n # here. The `Gtd-Review-Base:` trailer rides on that entry commit (the\n # process's oldest), so everything below still reuses the same\n # reviewing/await-review/feedback machinery over <commitish>..HEAD.\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.reviewFile %>`, which this prompt tells you to write.\n\n Write `<%= it.vars.reviewFile %>` to help a human review the diff below in\n this EXACT format:\n\n - First non-blank line: `# Review: <%= it.currentCommit.slice(0, 7) %>`\n - Somewhere in the document: `<!-- base: <%= it.startCommit %> -->`\n - At least one `## <Chunk Title>` heading, grouping hunks semantically\n (same feature/refactor/fix, even across files) — a short explanation of\n what changed and why, then one `- [ ] ./path/to/file.ts#42 — note` file\n pointer per hunk (checkboxes are for the human to tick, not you; every\n path is `./`-relative, with an optional `#line` and an optional\n ` — note`).\n\n The diff below is `it.reviewDiff` — on a feedback round it covers ONLY the\n changes made since the previous review, so you review just what changed,\n not items already signed off. Leave `<%= it.vars.reviewFile %>`\n uncommitted and finish your turn.\n <% if (it.reviewDiff.trim()) { %>\n\n ## Full diff under review\n\n ```diff\n <%~ it.reviewDiff %>\n ```\n <% } %>\n on:\n \"* **\": await-review\n\n await-review:\n actor: human\n file: <%= it.vars.reviewFile %>\n mode: review\n # While the cycle rests here, gtd opens a \"review checkout window\": raw HEAD\n # is rewound to the review base (the most-recent `review-deciding` commit —\n # so a feedback round shows only `last-review..HEAD` — or the process start\n # on the first review) with the working tree untouched, surfacing the diff\n # as ordinary uncommitted changes. It closes on the next invocation.\n #\n # Every human step here routes to `review-deciding`, which decides sign-off\n # vs. feedback from the step's CONTENT (a comment, not a tick). Ticking a box\n # only records \"I reviewed this hunk\". Two dead ends can't be told apart by a\n # file-pattern edge, so the sign-off gate (`enforceReviewSignoffGate` in\n # src/program.ts) catches them BEFORE the step commits and refuses: an\n # unfinished review (a box still `- [ ]` with no comment) and a deleted\n # REVIEW.md.\n reviewWindow: true\n message: |\n `<%= it.vars.reviewFile %>` holds the review record for the cycle — one\n `- [ ]` checkbox per reviewable item, grouped into chunks. Tick a box\n (`- [x]`) as you review each hunk; ticking only records that you've read\n it, it is not sign-off.\n\n When you've been through the whole diff, run `gtd step human`:\n\n - Tick EVERY box and leave no comment — no note in\n `<%= it.vars.reviewFile %>`, no code edit — to sign off and collapse the\n cycle into one commit (**squashing**).\n - Leave a comment to request changes — a note on a\n `<%= it.vars.reviewFile %>` line, or a direct code edit — to send it to a\n build + re-review round (**review-deciding** → **feedback-building**). A\n code edit counts as your own fix: the agent completes the follow-through\n it implies and never reverts your lines.\n\n Stepping with a box still unticked and no comment is refused (finish\n reviewing first); so is deleting `<%= it.vars.reviewFile %>`.\n on:\n \"* **\": review-deciding\n\n review-deciding:\n actor: check\n file: <%= it.vars.reviewFile %>\n mode: review\n # Anchors the INCREMENTAL review base: its most-recent in-process commit is\n # the point the previous review round ended, so the next `reviewing` turn\n # (and the review checkout window) covers only the changes since then. On\n # the first review there is no such commit, so the base is the cycle start.\n reviewBase: true\n script: |\n #!/usr/bin/env bash\n # gtd check turn — decide the human's review step from its CONTENT. HEAD is\n # the human's `await-review -> review-deciding` commit; HEAD^ is the\n # `reviewing` commit that wrote the agent's original REVIEW.md (the sign-off\n # gate refuses an incomplete step, so review-deciding is entered exactly\n # once per round — nothing commits in between). It is FEEDBACK when the\n # human left a comment: a note in REVIEW.md (any edit beyond a \"[ ]\"->\"[x]\"\n # tick) OR a hand-edit to any non-.gtd/ file this round. Otherwise it is a\n # clean sign-off. This turn only CAPTURES the raw material into REVIEW_RAW.md\n # (the `feedback-collecting` agent then turns it into instructions); it does\n # NOT interpret it. Either way, remove REVIEW.md; what the resulting diff\n # MEANS is decided by this state's own `on` rules — the A/M REVIEW_RAW.md\n # rows are declared BEFORE the D REVIEW.md row so a feedback round (which\n # also deletes REVIEW.md) is captured as feedback, not mistaken for a\n # sign-off.\n set +e\n mkdir -p .gtd\n # Hoist the paths once, at the TOP: Eta's autoTrim eats the newline after\n # an interpolation tag, so no tag may be the last token on a line — it\n # would glue the next line onto it and break the script. Below this point\n # everything is plain bash.\n reviewFile=\"<%~ it.vars.reviewFile %>\"\n reviewRawFile=\"<%~ it.vars.reviewRawFile %>\"\n # Which non-.gtd/ files did the human hand-edit this round?\n codeFiles=$(git diff-tree --no-commit-id --name-only -r HEAD | grep -v '^\\.gtd/')\n # A note is any REVIEW.md change beyond a checkbox flip: normalize every\n # \"[ ]\"/\"[x]\" to a placeholder, then compare the agent's original (HEAD^)\n # against the human's version (HEAD).\n notes=\"\"\n before=$(git show \"HEAD^:$reviewFile\" 2>/dev/null | sed -E 's/\\[[ xX]\\]/[_]/g')\n after=$(git show \"HEAD:$reviewFile\" 2>/dev/null | sed -E 's/\\[[ xX]\\]/[_]/g')\n if [ \"$before\" != \"$after\" ]; then notes=1; fi\n if [ -n \"$codeFiles\" ] || [ -n \"$notes\" ]; then\n {\n echo \"Raw review material captured for classification. The human reviewed the diff and left the feedback below; a downstream agent turns it into an instruction list. This file is machine-captured input, not instructions.\"\n if [ -n \"$notes\" ]; then\n echo\n echo \"## Notes the human added to REVIEW.md this round\"\n echo\n echo '```diff'\n git show HEAD --format= -- \"$reviewFile\"\n echo '```'\n fi\n if [ -n \"$codeFiles\" ]; then\n echo\n echo \"## Files the human hand-edited this round (already committed)\"\n echo\n echo '```diff'\n git show HEAD --format= -- $codeFiles\n echo '```'\n fi\n } > \"$reviewRawFile\"\n fi\n rm -f \"$reviewFile\"\n on:\n \"A .gtd/REVIEW_RAW.md\": feedback-collecting\n \"M .gtd/REVIEW_RAW.md\": feedback-collecting\n \"D .gtd/REVIEW.md\": squashing\n\n feedback-collecting:\n actor: agent\n file: <%= it.vars.reviewFeedbackFile %>\n model: <%= it.vars.plannerModel %>\n memory: review\n prompt: |\n You are an autonomous coding agent turning raw review feedback into an\n explicit instruction list for a builder. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.reviewRawFile %>` (delete it when done) and\n `<%= it.vars.reviewFeedbackFile %>` (write it). Do NOT change any other\n file — you classify, you do not build.\n\n The raw review material is: <%~ it.read(it.vars.reviewRawFile) %>\n\n Classify every piece of it into `<%= it.vars.reviewFeedbackFile %>` as a\n flat, numbered instruction list. Each item is `N. ./path/to/file.ts#line —\n <imperative>`. Read the two RAW sections and apply these rules exactly:\n\n 1. NOTES the human added to REVIEW.md — each note is a MANDATORY change,\n regardless of whether its checkbox is ticked (ticking means only \"I read\n this hunk\", never sign-off). Anchor each to the `./path#line` its review\n item names (or the chunk it sits under). Emit it as a concrete\n imperative.\n 2. Every COMMENT the human ADDED this round (a `+` line that is a code\n comment, in the hand-edited files) is a MANDATORY instruction the human\n is giving the builder — even a plain-prose one, even if its box is\n ticked. Emit an imperative to satisfy it and note that the comment line\n itself must be REMOVED once addressed. If a comment turns out to describe\n behaviour the code already has, the instruction is still \"confirm the\n code matches and remove the comment line\".\n 3. Every NON-comment code line the human hand-edited is an already-committed\n INTENT, NOT a do-then-delete item. Emit an instruction that names those\n files and says: their lines are final — complete ONLY the follow-through\n they imply (callers, tests, types, docs) and NEVER revert or rewrite\n them. Do not turn their own edit into a task.\n\n Split each hand-edited hunk per line: comment `+` lines follow rule 2, the\n surrounding real code follows rule 3.\n\n If — and only if — nothing in the raw material is actionable (e.g. the human\n left only an approving remark), write `<%= it.vars.reviewFeedbackFile %>`\n containing exactly one line: `NOTHING ACTIONABLE — <one-sentence reason>`.\n Never invent work to satisfy a non-actionable remark.\n\n Delete `<%= it.vars.reviewRawFile %>`, leave everything else uncommitted,\n and finish your turn.\n on:\n \"A .gtd/REVIEW_FEEDBACK.md\": feedback-building\n \"M .gtd/REVIEW_FEEDBACK.md\": feedback-building\n\n feedback-building:\n actor: agent\n file: <%= it.vars.reviewFeedbackFile %>\n model: <%= it.vars.coderModel %>\n memory: build\n # Refuse a work-free turn: a step whose only pending change is deleting the\n # instructions file (the original \"captured then discarded\" bug). The edge\n # gate (`enforceFeedbackProgressGate` in src/program.ts) reads this flag and\n # exempts a `NOTHING ACTIONABLE` sentinel (a non-actionable review round,\n # which legitimately makes no code change). The pure engine carries it inert.\n requireProgress: true\n prompt: |\n You are an autonomous coding agent addressing review feedback. `.gtd/`\n holds this workflow's own state — never create, edit, or delete anything\n under `.gtd/` except `<%= it.vars.reviewFeedbackFile %>`, which this\n prompt tells you to address and then delete.\n\n The instructions to implement are: <%~ it.read(it.vars.reviewFeedbackFile) %>\n\n This is a flat instruction list, not a diff — execute every item exactly as\n written, no Q&A, no re-planning, using TDD discipline:\n\n - An item that says the human's own lines are final: build ON TOP of them to\n complete only the follow-through it names (callers, tests, types, docs).\n NEVER revert or rewrite the lines they already committed.\n - An item derived from a human comment: satisfy it AND delete that comment\n line from the code (the human's review comments are transient — none of\n them survive this round).\n\n If the file's sole content is a `NOTHING ACTIONABLE` line, make no code\n change; just delete the instructions file.\n\n Delete `<%= it.vars.reviewFeedbackFile %>` once every item is addressed.\n Leave everything else uncommitted and finish your turn. Deleting the\n instructions file WITHOUT making the changes it asks for is refused — the\n feedback must actually be addressed.\n on:\n \"* **\": checking\n\n squashing:\n actor: agent\n file: <%= it.vars.commitMsgFile %>\n model: <%= it.vars.coderModel %>\n memory: build\n prompt: |\n You are an autonomous coding agent. The cycle is approved and done.\n `.gtd/` holds this workflow's own state — never create, edit, or delete\n anything under `.gtd/` except `<%= it.vars.commitMsgFile %>`, which this\n prompt tells you to write.\n\n Leave `<%= it.vars.commitMsgFile %>` uncommitted and finish your turn —\n entering this file squashes everything this process retains into one\n commit using its content as the message. The diff below is exactly what\n that commit will contain: `it.retainedDiff`, the changes THIS process\n made (its trace/retry boundary onward). For a normal build cycle that is\n the whole feature; for a `gtd review` process it is only the fixes made\n during the review — NOT the changeset that was under review.\n <% if (it.retainedDiff.trim()) { %>\n\n Write `<%= it.vars.commitMsgFile %>` with ONE conventional-commits message\n for the changes below (subject line `type(scope): subject`, imperative\n mood, <= 72 characters; a body explaining the why, trade-offs, and key\n decisions). Plain text, no markdown wrapper.\n\n ## Retained diff\n\n ```diff\n <%~ it.retainedDiff %>\n ```\n <% } else { %>\n\n This process retained NO changes (a clean review sign-off with no fixes).\n Write `<%= it.vars.commitMsgFile %>` with exactly this one line and\n nothing else:\n\n chore: human review\n <% } %>\n on:\n \"A .gtd/COMMIT_MSG.md\": done\n \"M .gtd/COMMIT_MSG.md\": done\n\n done:\n commit: |\n <%~ it.read(it.vars.commitMsgFile) %>\n\n Token cost: <%= it.processCost %>\n <% it.processCostByModel.forEach(function(m){ %>\n - <%= m.model %>: <%= m.cost %>\n <% }) %>\n";
|
|
191477
191541
|
//#endregion
|
|
191478
191542
|
//#region src/workflows/templates.ts
|
|
191479
191543
|
/**
|
|
@@ -203830,12 +203894,7 @@ const questionDiagnostics = (content) => {
|
|
|
203830
203894
|
source: "gtd"
|
|
203831
203895
|
}));
|
|
203832
203896
|
};
|
|
203833
|
-
|
|
203834
|
-
const box = file.checked ? "[x]" : "[ ]";
|
|
203835
|
-
const location = file.line !== void 0 ? `${file.path}#${file.line}` : file.path;
|
|
203836
|
-
return file.note ? `${box} ${location} — ${file.note}` : `${box} ${location}`;
|
|
203837
|
-
};
|
|
203838
|
-
/** Document symbols for `.gtd/REVIEW.md`'s chunks (the user-facing "work packages") and their hunks. */
|
|
203897
|
+
/** Document symbols for `.gtd/REVIEW.md`: only the headlines of chunks (the user-facing "work packages") that still carry at least one unchecked hunk — the outline is the list of packages left to review, nothing else. */
|
|
203839
203898
|
const reviewSymbols = (content) => {
|
|
203840
203899
|
const { changesets } = parseReviewDoc(content);
|
|
203841
203900
|
const lines = content.split(/\r?\n/);
|
|
@@ -203843,20 +203902,14 @@ const reviewSymbols = (content) => {
|
|
|
203843
203902
|
const start = chunk.headingLine;
|
|
203844
203903
|
const end = Math.max(start, (changesets[i + 1]?.headingLine ?? lines.length) - 1);
|
|
203845
203904
|
const checkedCount = chunk.files.filter((file) => file.checked).length;
|
|
203846
|
-
const children = chunk.files.map((file) => ({
|
|
203847
|
-
name: hunkLabel(file),
|
|
203848
|
-
kind: import_main.SymbolKind.Boolean,
|
|
203849
|
-
range: lineRange(lines, file.sourceLine),
|
|
203850
|
-
selectionRange: lineRange(lines, file.sourceLine)
|
|
203851
|
-
}));
|
|
203852
203905
|
return {
|
|
203853
203906
|
name: `${chunk.title} (${checkedCount}/${chunk.files.length})`,
|
|
203854
203907
|
kind: import_main.SymbolKind.Package,
|
|
203855
203908
|
range: spanRange(lines, start, end),
|
|
203856
203909
|
selectionRange: lineRange(lines, start),
|
|
203857
|
-
|
|
203910
|
+
unchecked: checkedCount < chunk.files.length
|
|
203858
203911
|
};
|
|
203859
|
-
});
|
|
203912
|
+
}).filter((symbol) => symbol.unchecked).map(({ unchecked: _unchecked, ...symbol }) => symbol);
|
|
203860
203913
|
};
|
|
203861
203914
|
/** Diagnostics for `.gtd/REVIEW.md` — the same findings `gtd validate` reports for a `review`-mode file, published live over LSP instead. Whole-document range: `ReviewDoc.errors` carries no per-line position. */
|
|
203862
203915
|
const reviewDiagnostics = (content) => {
|
|
@@ -204431,6 +204484,10 @@ Commands:
|
|
|
204431
204484
|
review-entry state (reviewEntry: true), reviewing
|
|
204432
204485
|
<commitish>..HEAD — e.g. a colleague's PR branch. Requires
|
|
204433
204486
|
a clean tree resting at the workflow's initial state
|
|
204487
|
+
fix Start a NEW process at the workflow's declared fix-entry
|
|
204488
|
+
state (fixEntry: true) that goes straight into repairing the
|
|
204489
|
+
current failing tests. Requires a clean tree resting at the
|
|
204490
|
+
workflow's initial state
|
|
204434
204491
|
next Print the resolved rest's rendered script/prompt/message
|
|
204435
204492
|
(no mutation)
|
|
204436
204493
|
status Print the resolved rest's state/actor and which declared
|
|
@@ -204643,6 +204700,7 @@ const stepAsActor = (invoker, cost, model) => gen(function* () {
|
|
|
204643
204700
|
const context = yield* buildTemplateContext(git, worktree.read, renderedState, invoker, run, vars, rest.def.states[renderedState]?.on, cost ?? 0, model);
|
|
204644
204701
|
yield* enforceSteeringGate(worktree.read, invoker, rest, context, decision.kind);
|
|
204645
204702
|
yield* enforceReviewSignoffGate(worktree.read, invoker, rest, context, changes, decision.kind);
|
|
204703
|
+
yield* enforceFeedbackProgressGate(invoker, rest, context, changes, decision.kind);
|
|
204646
204704
|
const outcome = yield* executeDecision(git, run, executable, context, cost, model);
|
|
204647
204705
|
return {
|
|
204648
204706
|
state: rest.state,
|
|
@@ -204716,6 +204774,40 @@ const runReviewCommand = (argv, json, write) => gen(function* () {
|
|
|
204716
204774
|
else write(`committed: ${subject}\n`);
|
|
204717
204775
|
});
|
|
204718
204776
|
/**
|
|
204777
|
+
* `gtd fix`: start a brand NEW process at the active workflow's declared
|
|
204778
|
+
* `fixEntry: true` state (see `PatternMachine.fixEntryStateOf`), which goes
|
|
204779
|
+
* straight into repairing the current failing tests. Mirrors `runReviewCommand`
|
|
204780
|
+
* but simpler — it takes no argument and writes NO `Gtd-Review-Base:` trailer:
|
|
204781
|
+
* the fix process reviews its own fixes from the ordinary process start, so a
|
|
204782
|
+
* plain empty turn commit (`gtd(human): <fix-entry-state>`) is all it needs.
|
|
204783
|
+
* `computeProcessRun` then treats that commit as the process's first turn like
|
|
204784
|
+
* any other, and the shared health/review/squash tail runs unmodified.
|
|
204785
|
+
*
|
|
204786
|
+
* Any failure below is a plain refusal: nothing is written.
|
|
204787
|
+
*
|
|
204788
|
+
* a. The machine must currently rest at the workflow's INITIAL state — a
|
|
204789
|
+
* dedicated `gtd fix` entry is distinct from the "no active cycle" rest, and
|
|
204790
|
+
* a process already underway refuses (finish or abandon it first).
|
|
204791
|
+
* b. The working tree must be clean — the fixes belong to their own process.
|
|
204792
|
+
* c. The active workflow must declare a `fixEntry: true` state.
|
|
204793
|
+
*/
|
|
204794
|
+
const runFixCommand = (argv, json, write) => gen(function* () {
|
|
204795
|
+
yield* rejectExtraArgs("fix", argv);
|
|
204796
|
+
const git = yield* GitService;
|
|
204797
|
+
const rest = yield* resolveRest();
|
|
204798
|
+
if (rest.state !== initialStateOf(rest.def)) return yield* fail$2(/* @__PURE__ */ new Error(`gtd fix: a process is already underway (resting at "${rest.state}") — finish or abandon it before starting a fix`));
|
|
204799
|
+
if ((yield* pendingChanges(git)).length > 0) return yield* fail$2(/* @__PURE__ */ new Error("gtd fix: the working tree must be clean before starting a fix"));
|
|
204800
|
+
const entryState = fixEntryStateOf(rest.def);
|
|
204801
|
+
if (entryState === void 0) return yield* fail$2(/* @__PURE__ */ new Error("gtd fix: the active workflow declares no fix entry state"));
|
|
204802
|
+
const subject = stateSubject("human", entryState);
|
|
204803
|
+
yield* git.commitAsIs(subject);
|
|
204804
|
+
if (json) write(JSON.stringify({
|
|
204805
|
+
state: entryState,
|
|
204806
|
+
subject
|
|
204807
|
+
}) + "\n");
|
|
204808
|
+
else write(`committed: ${subject}\n`);
|
|
204809
|
+
});
|
|
204810
|
+
/**
|
|
204719
204811
|
* The self-validation instruction gtd APPENDS to a `prompt` rest that declares
|
|
204720
204812
|
* both `file:` and `mode:` — i.e. a state whose actor hands over a steering
|
|
204721
204813
|
* file `gtd validate` formats and checks. Appended ONLY to plain `gtd next`
|
|
@@ -204859,6 +204951,53 @@ const enforceReviewSignoffGate = (worktreeRead, invoker, rest, context, changes,
|
|
|
204859
204951
|
});
|
|
204860
204952
|
if (verdict.kind === "refuse") return yield* fail$2(new Error(verdict.reason));
|
|
204861
204953
|
});
|
|
204954
|
+
/** The one-line marker `feedback-collecting` writes when a review round left nothing actionable — the ONLY content that lets a `requireProgress` state's instructions file be deleted without a code change (see `classifyFeedbackProgress`). */
|
|
204955
|
+
const NOTHING_ACTIONABLE_SENTINEL = "NOTHING ACTIONABLE";
|
|
204956
|
+
/**
|
|
204957
|
+
* The PURE core of the feedback-progress gate (see `enforceFeedbackProgressGate`).
|
|
204958
|
+
* At a `requireProgress` state the `file` is an instruction list the agent must
|
|
204959
|
+
* ADDRESS, then delete. Refuse a turn that deletes the file while touching no
|
|
204960
|
+
* code (`hasCodeChange` false) — the "review feedback captured then silently
|
|
204961
|
+
* discarded" bug. Two escapes ALLOW the step: the file isn't being deleted (the
|
|
204962
|
+
* agent left work, or the list, in place), or there IS a code change alongside
|
|
204963
|
+
* (real work was done). The one delete-with-no-code exception is a
|
|
204964
|
+
* `NOTHING ACTIONABLE` sentinel (`deletedContent`): a legitimately
|
|
204965
|
+
* non-actionable round makes no code change by design. Kept separate from the
|
|
204966
|
+
* Effect so it is directly unit-tested (see program.test.ts).
|
|
204967
|
+
*/
|
|
204968
|
+
const classifyFeedbackProgress = (input) => {
|
|
204969
|
+
if (!input.changes.some((c) => c.path === input.file && c.status === "D")) return { kind: "allow" };
|
|
204970
|
+
if (input.changes.some((c) => !c.path.startsWith(".gtd/"))) return { kind: "allow" };
|
|
204971
|
+
if (input.deletedContent.trim().startsWith(NOTHING_ACTIONABLE_SENTINEL)) return { kind: "allow" };
|
|
204972
|
+
return {
|
|
204973
|
+
kind: "refuse",
|
|
204974
|
+
reason: `gtd step ${input.invoker}: ${input.file} was deleted at "${input.stateName}" without addressing its instructions — implement the changes it lists (then delete it), don't just remove the file.`
|
|
204975
|
+
};
|
|
204976
|
+
};
|
|
204977
|
+
/**
|
|
204978
|
+
* The feedback-progress gate (edge, like the sign-off gate above). At a
|
|
204979
|
+
* `requireProgress: true` state (the unified template's `feedback-building`), a
|
|
204980
|
+
* turn that just deletes the instructions file without doing the work it lists
|
|
204981
|
+
* is refused BEFORE it can commit — the pure `classifyFeedbackProgress` decides.
|
|
204982
|
+
* A no-op when the state is not a `requireProgress` state or the decision is a
|
|
204983
|
+
* squash/no-op. The committed (pre-deletion) file content — read once here —
|
|
204984
|
+
* feeds the sentinel exemption.
|
|
204985
|
+
*/
|
|
204986
|
+
const enforceFeedbackProgressGate = (invoker, rest, context, changes, kind) => gen(function* () {
|
|
204987
|
+
if (kind !== "commit") return;
|
|
204988
|
+
if (!isRequireProgressState(rest.def, rest.state)) return;
|
|
204989
|
+
const file = yield* renderFile(rest.stateDef, context);
|
|
204990
|
+
if (file === void 0) return;
|
|
204991
|
+
const deletedContent = yield* (yield* GitService).readFileAtRef("HEAD", file).pipe(catchAll(() => succeed$3("")));
|
|
204992
|
+
const verdict = classifyFeedbackProgress({
|
|
204993
|
+
file,
|
|
204994
|
+
stateName: rest.state,
|
|
204995
|
+
invoker,
|
|
204996
|
+
changes,
|
|
204997
|
+
deletedContent
|
|
204998
|
+
});
|
|
204999
|
+
if (verdict.kind === "refuse") return yield* fail$2(new Error(verdict.reason));
|
|
205000
|
+
});
|
|
204862
205001
|
/**
|
|
204863
205002
|
* `gtd validate [--json]`: format (in place) then validate the steering file
|
|
204864
205003
|
* the resolved rest declares (`file:` rendered, `mode:` selecting how), over
|
|
@@ -204986,6 +205125,7 @@ const runMermaidCommand = (argv, json, write) => gen(function* () {
|
|
|
204986
205125
|
const KNOWN_SUBCOMMANDS = [
|
|
204987
205126
|
"step",
|
|
204988
205127
|
"review",
|
|
205128
|
+
"fix",
|
|
204989
205129
|
"next",
|
|
204990
205130
|
"status",
|
|
204991
205131
|
"validate",
|
|
@@ -205057,6 +205197,7 @@ const dispatchKnownSubcommand = (sub, argv, json, write, cost, model) => {
|
|
|
205057
205197
|
switch (sub) {
|
|
205058
205198
|
case "step": return runStepCommand(argv, json, write, cost, model);
|
|
205059
205199
|
case "review": return runReviewCommand(argv, json, write);
|
|
205200
|
+
case "fix": return runFixCommand(argv, json, write);
|
|
205060
205201
|
case "next": return runNextCommand(json, write);
|
|
205061
205202
|
case "status": return runStatusCommand(argv, json, write);
|
|
205062
205203
|
case "validate": return runValidateCommand(argv, json, write);
|
|
@@ -205071,8 +205212,9 @@ const dispatchKnownSubcommand = (sub, argv, json, write, cost, model) => {
|
|
|
205071
205212
|
* this with no arguments; the test world supplies an in-memory layer set and
|
|
205072
205213
|
* captures stdout via the `write` callback.
|
|
205073
205214
|
*
|
|
205074
|
-
* v3 command surface: `step <actor>` / `
|
|
205075
|
-
* `mermaid` (see `src/Edge.ts` and
|
|
205215
|
+
* v3 command surface: `step <actor>` / `review <commitish>` / `fix` / `next` /
|
|
205216
|
+
* `status` / `validate` / `mermaid` (see `src/Edge.ts` and
|
|
205217
|
+
* `docs/design/pattern-machine-plan.md` §3),
|
|
205076
205218
|
* plus `lsp` and `init` — both dispatched before the config-reading
|
|
205077
205219
|
* path since they must work with no workflow configured yet. Bare `gtd` or an
|
|
205078
205220
|
* unknown subcommand is a usage error. Shared setup (argv parsing, the
|
package/docs/cli.md
CHANGED
|
@@ -21,6 +21,10 @@ Commands:
|
|
|
21
21
|
review-entry state (reviewEntry: true), reviewing
|
|
22
22
|
<commitish>..HEAD — e.g. a colleague's PR branch. Requires
|
|
23
23
|
a clean tree resting at the workflow's initial state
|
|
24
|
+
fix Start a NEW process at the workflow's declared fix-entry
|
|
25
|
+
state (fixEntry: true) that goes straight into repairing the
|
|
26
|
+
current failing tests. Requires a clean tree resting at the
|
|
27
|
+
workflow's initial state
|
|
24
28
|
next Print the resolved rest's rendered script/prompt/message
|
|
25
29
|
(no mutation)
|
|
26
30
|
status Print the resolved rest's state/actor and which declared
|
|
@@ -69,12 +73,15 @@ default workflow, so this is the one-time setup step for a repo; a state command
|
|
|
69
73
|
run before it fails with `gtd: no workflow configured — run \`gtd init\` to
|
|
70
74
|
create .gtdrc.json`.
|
|
71
75
|
|
|
72
|
-
The unified template has
|
|
73
|
-
|
|
76
|
+
The unified template has four entry points — each behind a green-baseline gate
|
|
77
|
+
that runs the suite before starting — into one shared review/squash tail (see
|
|
78
|
+
[STATES.md §10](../STATES.md#10-the-bundled-workflow-templates)): creating
|
|
74
79
|
`.gtd/TODO.md` starts the simple flow (grilling → building → checking), creating
|
|
75
80
|
`.gtd/REQUIREMENTS.md` starts the advanced flow (two-phase Q&A planning,
|
|
76
|
-
per-package parallel build, agentic spec-review),
|
|
77
|
-
enters review directly
|
|
81
|
+
per-package parallel build, agentic spec-review), `gtd review <commitish>`
|
|
82
|
+
enters review directly, and `gtd fix` goes straight into repairing failing
|
|
83
|
+
tests. A red baseline halts at a `*-start-blocked` gate rather than starting new
|
|
84
|
+
work.
|
|
78
85
|
|
|
79
86
|
The file is written **uncommitted**; review and commit it before your first
|
|
80
87
|
`gtd step` (an uncommitted `.gtdrc.json` is a pending change the initial state
|
|
@@ -175,7 +182,7 @@ at `<commitish>..HEAD` (see
|
|
|
175
182
|
Plain-mode output is one line, same shape as `gtd step`:
|
|
176
183
|
|
|
177
184
|
```
|
|
178
|
-
committed: gtd(human):
|
|
185
|
+
committed: gtd(human): review-start-check
|
|
179
186
|
```
|
|
180
187
|
|
|
181
188
|
`--json` emits `{state, subject}` — `state` is the entered review-entry state,
|
|
@@ -184,6 +191,43 @@ committed: gtd(human): reviewing
|
|
|
184
191
|
Takes exactly one positional argument (`<commitish>`); missing or extra
|
|
185
192
|
arguments are usage errors.
|
|
186
193
|
|
|
194
|
+
In the bundled unified template the review-entry state is `review-start-check` —
|
|
195
|
+
a green-baseline gate that runs the suite and, once green, transitions to
|
|
196
|
+
`reviewing`; a red run halts at `review-start-blocked` (see
|
|
197
|
+
[STATES.md §10](../STATES.md#10-the-bundled-workflow-template)).
|
|
198
|
+
|
|
199
|
+
## `gtd fix [--json]`
|
|
200
|
+
|
|
201
|
+
Starts a BRAND NEW process at the active workflow's declared `fixEntry: true`
|
|
202
|
+
state (see [STATES.md §10](../STATES.md#10-the-bundled-workflow-template)) that
|
|
203
|
+
goes straight into repairing the current failing tests. It is the standalone
|
|
204
|
+
counterpart to the entry gates: the gates refuse to start new work on a red
|
|
205
|
+
baseline, and `gtd fix` is the dedicated way to get back to green — repair,
|
|
206
|
+
review, and squash a broken baseline into one commit.
|
|
207
|
+
|
|
208
|
+
Requires, in order (any failure is a plain refusal — exit non-zero, nothing
|
|
209
|
+
written):
|
|
210
|
+
|
|
211
|
+
- the machine currently resting at the workflow's **initial state** (a process
|
|
212
|
+
already underway refuses);
|
|
213
|
+
- a **clean working tree**;
|
|
214
|
+
- the active workflow declaring a **`fixEntry: true`** state (otherwise:
|
|
215
|
+
`gtd fix: the active workflow declares no fix entry state`).
|
|
216
|
+
|
|
217
|
+
Takes no positional argument (extra arguments are a usage error). Unlike
|
|
218
|
+
`gtd review`, it writes NO `Gtd-Review-Base:` trailer — a fix reviews its own
|
|
219
|
+
fixes from the ordinary process start — so on success it writes just one empty
|
|
220
|
+
commit, `gtd(human): <fix-entry-state>`:
|
|
221
|
+
|
|
222
|
+
```
|
|
223
|
+
committed: gtd(human): fix-check
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
`--json` emits `{state, subject}`, same shape as `gtd review`. In the bundled
|
|
227
|
+
unified template the fix-entry state is `fix-check`: a red suite drops into the
|
|
228
|
+
shared `fixing` loop and out through the review + squash tail; a green suite is
|
|
229
|
+
a no-op back to `idle`.
|
|
230
|
+
|
|
187
231
|
## `gtd next [--json]`
|
|
188
232
|
|
|
189
233
|
Pure emitter of the resolved rest's rendered content — it **never mutates** the
|
|
@@ -390,13 +434,14 @@ are rejected).
|
|
|
390
434
|
## `gtd lsp`
|
|
391
435
|
|
|
392
436
|
Starts an LSP server over stdio for `.gtd/` steering files — document symbols
|
|
393
|
-
for a `qa`-mode file's open questions and a `review`-mode file
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
437
|
+
for a `qa`-mode file's open questions and, for a `review`-mode file, one symbol
|
|
438
|
+
per chunk that still has an unchecked hunk (the outline lists only the packages
|
|
439
|
+
left to review, with no hunk children), code actions to check/uncheck a hunk or
|
|
440
|
+
a whole chunk, go-to-definition (`textDocument/definition`) from a `review`-mode
|
|
441
|
+
hunk pointer line into the file it points at (at its 1-based `#line`, or the top
|
|
442
|
+
of the file for a bare `./path`; the `./`-relative path resolves against the
|
|
443
|
+
repo's git toplevel), and diagnostics publishing the same parser findings
|
|
444
|
+
`gtd validate` reports (see `src/OpenQuestions.ts` / `src/ReviewDoc.ts` and
|
|
400
445
|
[STATES.md §12](../STATES.md#12-steering-file-validation-gtd-validate)).
|
|
401
446
|
|
|
402
447
|
**Config-driven** (see
|
package/docs/configuration.md
CHANGED
|
@@ -482,7 +482,39 @@ parent is a non-workflow commit, so the ordinary boundary rule already stops
|
|
|
482
482
|
there. See [STATES.md §11](../STATES.md#11-the-review-checkout-window) for the
|
|
483
483
|
full mechanism.
|
|
484
484
|
|
|
485
|
-
The bundled default enables `reviewEntry: true` on `
|
|
485
|
+
The bundled default enables `reviewEntry: true` on `review-start-check` — the
|
|
486
|
+
green-baseline gate in front of `reviewing`.
|
|
487
|
+
|
|
488
|
+
### `fixEntry:` — the fix entry point (`gtd fix`)
|
|
489
|
+
|
|
490
|
+
A state may declare `fixEntry: true` (at most one across the whole workflow;
|
|
491
|
+
forbidden on a commit state and on the initial state — the same rule family as
|
|
492
|
+
`reviewEntry`). It marks the state [`gtd fix`](cli.md#gtd-fix---json) enters to
|
|
493
|
+
start a BRAND NEW process that goes straight into repairing the current failing
|
|
494
|
+
tests.
|
|
495
|
+
|
|
496
|
+
```yaml
|
|
497
|
+
workflow:
|
|
498
|
+
states:
|
|
499
|
+
# …
|
|
500
|
+
fix-check:
|
|
501
|
+
actor: check
|
|
502
|
+
fixEntry: true # `gtd fix` starts a new process here
|
|
503
|
+
script: <%~ it.vars.testCommand %> || echo fail > .gtd/FEEDBACK.md
|
|
504
|
+
on:
|
|
505
|
+
"A .gtd/FEEDBACK.md": fixing # red -> repair loop
|
|
506
|
+
"C": idle # green -> nothing to fix
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
`gtd fix` requires resting at the workflow's initial state with a clean tree,
|
|
510
|
+
and requires the active workflow to declare a `fixEntry` state. Unlike
|
|
511
|
+
`gtd review`, it writes NO `Gtd-Review-Base:` trailer — a fix reviews its own
|
|
512
|
+
fixes from the ordinary process start — so it simply writes one empty
|
|
513
|
+
`gtd(human): <fix-entry-state>` turn commit and lets the workflow's own `on`
|
|
514
|
+
routing take over. The bundled default enables `fixEntry: true` on `fix-check`:
|
|
515
|
+
a red suite drops into the shared `fixing` loop and out through the review +
|
|
516
|
+
squash tail; a green suite is a no-op back to `idle`. See
|
|
517
|
+
[STATES.md §10](../STATES.md#10-the-bundled-workflow-template).
|
|
486
518
|
|
|
487
519
|
### Template variables
|
|
488
520
|
|
|
@@ -84,11 +84,13 @@ same `file:`/`mode:` annotations (it is the LSP-heavy flow), including
|
|
|
84
84
|
(vars-layer context; the LSP process's env supplies `GTD_VAR_` overrides like
|
|
85
85
|
any invocation), and build a map of absolute rendered paths → mode. Document
|
|
86
86
|
features dispatch on that map: `qa` files get question symbols + parser-error
|
|
87
|
-
diagnostics; `review` files get chunk
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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.
|
|
92
94
|
- **The jump command returns:** `gtd.openSteeringFile` — resolve the CURRENT
|
|
93
95
|
state exactly like the CLI (config + git HEAD via the existing Edge helpers;
|
|
94
96
|
this re-adds the git/config wiring the v2 server had), render its `file:`, and
|
package/package.json
CHANGED
package/schema.json
CHANGED
|
@@ -140,6 +140,14 @@
|
|
|
140
140
|
"reviewEntry": {
|
|
141
141
|
"type": "boolean",
|
|
142
142
|
"description": "Marks the (at most one) state `gtd review <commitish>` enters to start a brand new process reviewing <commitish>..HEAD. Forbidden on a commit state and on the initial state."
|
|
143
|
+
},
|
|
144
|
+
"fixEntry": {
|
|
145
|
+
"type": "boolean",
|
|
146
|
+
"description": "Marks the (at most one) state `gtd fix` enters to start a brand new process that goes straight into repairing the current failing tests. Forbidden on a commit state and on the initial state."
|
|
147
|
+
},
|
|
148
|
+
"requireProgress": {
|
|
149
|
+
"type": "boolean",
|
|
150
|
+
"description": "When true, a step at this state is refused if its only pending change is deleting the state's own `file:` — a work-free turn that discards its input without addressing it. A `NOTHING ACTIONABLE` sentinel file is exempt (a legitimately non-actionable round makes no code change). Requires a `file:`. Forbidden on a commit state."
|
|
143
151
|
}
|
|
144
152
|
}
|
|
145
153
|
}
|