@pmelab/gtd 9.1.1 → 9.1.2

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 CHANGED
@@ -133,7 +133,13 @@ for). The two steering-file entries are chosen by which file you create:
133
133
  per-package test loop, and a per-package **agentic review** that verifies the
134
134
  package against its spec. All three phases share one machine identity, so the
135
135
  agent never re-explores the codebase from scratch between them, and the
136
- human's answers carry forward into decomposition.
136
+ human's answers carry forward into decomposition. A package whose work already
137
+ landed (an earlier package's fix turn pulled it in) is not a dead end: the
138
+ build turn records per-criterion evidence in `.gtd/SATISFIED.md` instead of
139
+ implementing anything, and the package still goes through the checks and the
140
+ spec review before closing out. If a queue item ever _does_ dead-end (a build
141
+ turn that authors nothing stalls), the supported recovery is to write that
142
+ same file yourself and `gtd land` — no hand-authored state commit.
137
143
 
138
144
  Both flows converge on the same tail: an agent hands you a `.gtd/REVIEW.md`
139
145
  checkbox review of the diff — the prompt never inlines the diff itself; it names
@@ -1136,7 +1142,9 @@ the compiler's job at load time.
1136
1142
  per `review`-mode chunk that still has an unchecked hunk (an outline of the
1137
1143
  packages left to review) plus check/uncheck actions over those chunks,
1138
1144
  go-to-definition from a `review`-mode hunk line into the file it points at (at
1139
- its `#line`), symbols over a `qa`-mode file's open questions plus "pick this
1145
+ its `#line` — a hunk's path is a single whitespace-delimited `./`-relative
1146
+ token, so a hyphen in a filename stays part of the path, never a note
1147
+ separator), symbols over a `qa`-mode file's open questions plus "pick this
1140
1148
  option"/"uncheck this option" code actions on each option — offered anywhere on
1141
1149
  the option's list item, including any wrapped continuation lines, not just its
1142
1150
  own `- [ ]` line — diagnostics for both (live as you edit), and a
@@ -1163,16 +1171,21 @@ gate still format and validate it like any other mode.
1163
1171
  npm install
1164
1172
  npm run dev # run from source, no build (node dev/run.mjs)
1165
1173
  npm run build # tsdown → dist/gtd.bundle.mjs
1166
- npm test # format:check, typecheck, lint, unit + e2e tests, fallow
1167
- npm run test:unit # vitest unit tests (the pure resolver) — --project unit
1168
- npm run test:e2e # gherkin e2e, both tiers — test:e2e:inmem + test:e2e:live
1169
- npm run test:e2e:inmem # @inmem scenarios, in-process, no build — --project e2e-inmem
1170
- npm run test:e2e:live # @live scenarios against the built bundle — --project e2e-live
1174
+ npm test # the whole gate, via turbo — cached and parallel
1175
+ npx turbo run test:unit # one task, cached (add --force to bypass)
1176
+ npx turbo run test:e2e:live # builds first (turbo dependsOn), then @live
1177
+ npm run test:changed # local pre-flight: only unit/@inmem tests git says changed
1171
1178
  npm run test:mutation # StrykerJS mutation testing (manual only, ~10 min)
1172
1179
  npm run typecheck
1173
1180
  npm run lint
1174
1181
  ```
1175
1182
 
1183
+ `npm test` is a turbo task graph (`turbo.json`): each check declares its own
1184
+ `inputs`, so an unchanged check is skipped, and a check that does run is run in
1185
+ parallel with the others. Caveat from the `test:e2e:live` task's `build`
1186
+ dependency: a bare `npm run test:e2e:live` skips the build, so use
1187
+ `npx turbo run test:e2e:live` to test against a fresh bundle.
1188
+
1176
1189
  A pre-commit hook is installed automatically via the `prepare` script when you
1177
1190
  run `npm install` on a fresh clone — it runs
1178
1191
  [lint-staged](https://github.com/lint-staged/lint-staged) with
@@ -191145,7 +191145,12 @@ const QA_FORMAT = {
191145
191145
  const HEADER_RE = /^#\s+Review:\s*(\S+)\s*$/;
191146
191146
  const BASE_COMMENT_RE = /^<!--\s*base:\s*(\S+)\s*-->$/;
191147
191147
  const CHUNK_HEADING_RE = /^##\s+(.+)$/;
191148
- const FILE_POINTER_RE = /^-\s*\[([ xX])\]\s*(\.\/\S+?)(?:#(\d+))?(?:\s*[—-]+\s*(.*))?$/;
191148
+ /** One `- [ ]`/`- [x]` pointer line: the box, the whitespace-delimited pointer token, and the rest of the line. */
191149
+ const FILE_POINTER_RE = /^-\s*\[([ xX])\]\s*(\S+)(?:\s+(.*))?$/;
191150
+ /** A pointer token's trailing `#<line>` (greedy, so a `#` inside the path stays in it). */
191151
+ const POINTER_LINE_RE = /^(.*)#(\d+)$/;
191152
+ /** The optional dash a trailing note may lead with — em dash, en dash, or hyphens. */
191153
+ const NOTE_SEPARATOR_RE = /^[—–-]+\s*/;
191149
191154
  /** The `# Review: <hash>` header, required as the document's first non-blank line. */
191150
191155
  const parseHeader = (lines) => {
191151
191156
  const firstNonBlank = lines.find((line) => line.trim().length > 0);
@@ -191162,11 +191167,17 @@ const parseBaseComment = (lines) => {
191162
191167
  const parseFilePointer = (line, sourceLine) => {
191163
191168
  const match = FILE_POINTER_RE.exec(line);
191164
191169
  if (!match) return void 0;
191170
+ const token = match[2];
191171
+ if (!token.startsWith("./")) return void 0;
191172
+ const lineMatch = POINTER_LINE_RE.exec(token);
191173
+ const path = lineMatch ? lineMatch[1] : token;
191174
+ if (path.length <= 2) return void 0;
191175
+ const note = (match[3] ?? "").replace(NOTE_SEPARATOR_RE, "").trim();
191165
191176
  return {
191166
191177
  checked: match[1] !== " ",
191167
- path: match[2],
191168
- ...match[3] !== void 0 ? { line: Number(match[3]) } : {},
191169
- ...match[4] && match[4].trim().length > 0 ? { note: match[4].trim() } : {},
191178
+ path,
191179
+ ...lineMatch ? { line: Number(lineMatch[2]) } : {},
191180
+ ...note.length > 0 ? { note } : {},
191170
191181
  sourceLine
191171
191182
  };
191172
191183
  };
@@ -191273,9 +191284,9 @@ const toggleFilePointer = (content, line) => {
191273
191284
  if (raw === void 0) return void 0;
191274
191285
  const leading = raw.length - raw.trimStart().length;
191275
191286
  const trimmed = raw.slice(leading);
191276
- const match = FILE_POINTER_RE.exec(trimmed);
191277
- if (!match) return void 0;
191278
- const character = leading + (match[0].indexOf("[") + 1);
191287
+ const pointer = parseFilePointer(trimmed, line);
191288
+ if (pointer === void 0) return void 0;
191289
+ const character = leading + trimmed.indexOf("[") + 1;
191279
191290
  return {
191280
191291
  range: {
191281
191292
  start: {
@@ -191287,7 +191298,7 @@ const toggleFilePointer = (content, line) => {
191287
191298
  character: character + 1
191288
191299
  }
191289
191300
  },
191290
- newText: match[1] === " " ? "x" : " "
191301
+ newText: pointer.checked ? " " : "x"
191291
191302
  };
191292
191303
  };
191293
191304
  const lineRange = (lines, line) => ({
@@ -192187,7 +192198,7 @@ const compileWorkflowConfig = (raw, configDir, rcModes, inlineFileRefs = true, f
192187
192198
  };
192188
192199
  //#endregion
192189
192200
  //#region src/workflows/unified.yaml
192190
- var unified_default = "# The unified gtd workflow template — gtd's BUILT-IN DEFAULT workflow. When no\n# `workflow:` key is configured anywhere in the cwd→home config chain,\n# src/Config.ts runs this template directly, so a state command works with no\n# configuration at all. `gtd init` seeds only the settings a project tunes\n# (vars.testCommand + a modes: formatting suggestion), never the workflow\n# itself. See the README's \"How it works\" and \"Driving the loop\" sections for\n# 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. `renderInitConfig` (src/workflows/\n# templates.ts) can MATERIALIZE this raw source into a `.gtdrc.json`\n# `workflow:` value — the way to eject/customize the machine, and the hermetic\n# test fixture — but `gtd init` itself no longer writes the workflow.\n#\n# ── Authored with MACHINES (entry:/machines:) ───────────────────────────────\n#\n# This template is written as a tree of reusable, parameterized MACHINES (see\n# src/Machines.ts) — a gate/loop written once and instantiated several times\n# with different `with:` bindings (dedup), or a complex cluster grouped under\n# one name for source comprehension (encapsulation). Every reference below is\n# an ordinary `{ machine: <name>, with: { ... } }` local, expanded at\n# compile-time into concrete, qualified states before the pure engine ever\n# sees the definition — no runtime footprint.\n#\n# MACHINE BOUNDARIES LINE UP WITH CONVERSATIONAL IDENTITY: a machine that holds\n# an identity (a planner or a coder persona) declares its own `model:` ONCE, at\n# the machine level (stamped onto every one of its own `prompt` states — see\n# src/Machines.ts's `resolveInstanceModel`), instead of repeating `model:` on\n# every state; no state anywhere declares `model:` or `memory:` directly any\n# more (the memory key is computed from the machine tree — see\n# src/PatternMachine.ts's `memoryScopeAt` / src/Edge.ts's `memoryKeyFor`). Two\n# gate/queue machines below are deliberately IDENTITY-FREE (no `model:`):\n# `entryGate`/`healthGate` are infrastructure, not a persona, and `packageLoop`\n# is a bookkeeping queue, not a builder.\n#\n# * entryGate (dedup ×3) — the green-baseline gate (check + human blocked)\n# guarding the simple (`plan-gate`), advanced\n# (`spec-gate`) and review (`review-gate`)\n# entries. Identity-free.\n# * healthGate (dedup ×2) — the check/escalate health pair shared by the\n# simple build's own tail (`build.health.*`) and\n# the per-package build's own tail\n# (`packages.item.health.*`) — each caller's OWN\n# `fix` state (a coder-identity action) lives\n# beside this machine, not inside it, since\n# fixing IS a coder action and this machine has\n# no identity of its own. Identity-free.\n# * advancedPlan (encap) — the advanced flow's one design conversation:\n# product Q&A, technical Q&A, and decomposition\n# (`design.*`: product-author/-answer,\n# technical-author/-answer, decompose) all share\n# ONE machine identity, so the agent explores the\n# codebase once, not three times, and the human's\n# answer rationale survives into decomposition.\n# ▸ planner.\n# * planLoop (encap) — the simple flow's plan-iteration loop (`plan.*`).\n# ▸ planner.\n# * humanReview (encap) — the shared review tail, nested INSIDE\n# `simpleBuild` (`build.review.*`: reviewing →\n# await-review → deciding → collecting) — see\n# \"Why humanReview is nested, not a root sibling\"\n# below. ▸ planner — the implementer's OWN\n# follow-through on feedback is a coder action,\n# not a reviewer one, so it lives in\n# `build.addressing` instead.\n# * specReview (encap) — the advanced flow's per-package agentic review,\n# now a SINGLE state (`packages.item.spec.review`)\n# — a machine whose entire job is to be a\n# separate mind from the implementer. ▸ planner\n# — fixing the reviewer's own findings is a coder\n# action, so it lives in `packages.item.fix-spec`\n# instead.\n# * packageLoop (encap) — the advanced flow's per-package build queue\n# (`packages.picking`). Identity-free — the\n# actual per-package build identity lives in its\n# own `item` child (packageItem, below).\n# * packageItem (encap) — the advanced flow's per-package build identity\n# (`packages.item.*`: building, fix-suite,\n# fix-spec, closing), plus its own `health` and\n# `spec` children. ▸ coder.\n# * simpleBuild (encap) — the simple flow's build identity (`build.*`:\n# building, squashing, addressing, fix), plus its\n# own `health` child AND its own `review` child\n# (humanReview, above). ▸ coder.\n#\n# ── Why humanReview is nested, not a root sibling ───────────────────────────\n#\n# `humanReview` sits UNDER `simpleBuild` (`build.review`) rather than beside it\n# at the root, because a machine's memory scope is its dotted instance path\n# (see src/PatternMachine.ts's `memoryScopeAt`): entering a DESCENDANT scope\n# never breaks the parent's unbroken run, but entering a sibling does. The\n# review tail sits between the two coder turns that most need continuity —\n# `build.addressing` (follows through on review feedback) and `build.squashing`\n# (drafts the finale from the range this process built) — so hoisting it back\n# to a root sibling would force both of those turns to re-derive the feature\n# from scratch on every pass through the tail, instead of resuming the session\n# that built it. The reviewer itself is unaffected either way: `build.review.*`\n# is its own scope regardless of where it's nested, so it never resumes the\n# builder's session.\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 blocked gate (fix the failures and re-run, exactly like the\n# escalate states), or is what `gtd --entry fix-precheck` 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# * the configured requirementsFile (default .gtd/REQUIREMENTS.md) ->\n# `spec-gate.check` -> the ADVANCED flow (one design conversation across\n# product + technical Q&A and decomposition, per-package parallel build +\n# agentic spec-review).\n# * anything else (e.g. the configured todoFile, default .gtd/TODO.md) ->\n# `plan-gate.check` -> the SIMPLE flow (one grilling Q&A loop, a monolithic\n# build, no decomposition, no agentic review).\n#\n# A third entry, `gtd --entry review-gate.check`, jumps there directly and,\n# once green, to `build.review.reviewing` to review a foreign branch through\n# the same shared tail. A fourth, `gtd --entry fix-precheck`, jumps there: a\n# red suite drops straight into the shared `build.fix` loop and out through the\n# review + squash tail; a green suite is a no-op back to `idle`. Both states\n# declare `entry: true` — otherwise unreachable from `entries.default`.\n#\n# All three converge at `build.review.reviewing` -> `build.review.await-review`.\n# Ticking a REVIEW.md box means only \"I reviewed this hunk\"; a COMMENT (not an\n# unticked box) is what asks for changes. Every human step at\n# `build.review.await-review` goes to `build.review.deciding`, which inspects\n# 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 -> build.squashing -> done (the ONLY path\n# to the finale — the whole process collapses into one commit).\n# * any comment — a note in REVIEW.md OR a hand-edit to code — ->\n# build.review.deciding writes REVIEW_FEEDBACK.md -> build.review.collecting\n# implements it (completing any code the human hand-edited, never\n# reverting their lines) -> build.addressing (a coder action, not a\n# reviewer one — see \"Why humanReview is nested\" above) -> back through\n# `build.health.check` (the shared health pair, bound via the\n# `$onFeedback`/local `review` reference) -> build.review.reviewing\n# regenerates an INCREMENTAL REVIEW.md (build.review.deciding carries\n# `reviewBase: true`, so each feedback round's review + checkout window\n# covers only `last-review..HEAD`, i.e. the agent's follow-through, not the\n# human's own edit) -> await-review, looping until sign-off.\n# Two dead ends never commit: the review-signoff guard (src/StepGuards.ts)\n# REFUSES a step that leaves a box unticked with no comment (finish reviewing\n# first) and a deleted REVIEW.md (restore it and tick the boxes).\n#\n# The shared health pair (`build.health.check`/`.escalate`, plus the caller's\n# own `build.fix`) serves the simple build and the feedback loop — its green\n# edge targets `build.review.reviewing`. The advanced per-package loop keeps\n# its OWN health pair (`packages.item.health.check`/`.escalate`, plus\n# `packages.item.fix-suite`) because its green edge targets the per-package\n# `packages.item.spec.review` gate instead.\n#\n# Every `on` pattern's path below is written as the matching var's Eta\n# template (e.g. `\"A <%= it.vars.feedbackFile %>\"`), not a literal `.gtd/...`\n# path — repointing a var reroutes the machine along with every template that\n# reads/writes it (see the README's \"Variables\" section).\n\nvars:\n testCommand: npm test\n # Where gtd keeps its own scratch/bookkeeping (the check scripts' temp output\n # file, review-deciding's state-file exclusion). Independent of the per-file\n # vars below — relocating a file wholesale means setting both.\n stateDir: .gtd\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_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/technical-qa/review turns;\n # `coderModel` (default \"base\") is the tier for the build/fix turns.\n plannerModel: smart\n coderModel: base\n # The fixed diff base for `gtd --entry review-gate.check --var\n # reviewBase=<commitish>` — a plain non-gtd branch reviewed against that\n # commitish rather than the process start. Declared here (rather than left\n # undeclared) so `--var reviewBase=...` passes the CLI's declared-name\n # check; the default empty string renders blank, which the entry command\n # refuses (a review entry always requires an explicit base).\n reviewBase: \"\"\n\n# `prose` is a format-only steering-file mode this workflow blesses for itself:\n# a curated free-form document with no gtd-side schema (the simple flow's plan\n# file, .gtd/TODO.md — see the `plan.planning` state below). An empty entry is\n# the format-only tier any workflow can declare for a name with no built-in\n# parser; `qa`/`review` need no entry here — src/PatternConfig.ts's compiler\n# seeds those two from the built-in registry (src/SteeringFormats.ts)\n# automatically.\nmodes:\n prose: {}\n\nentry:\n default: unified\n\nmachines:\n # ── DEDUP: the green-baseline entry gate (identity-free) ──────────────────\n # Runs the suite before any work starts (its script is the shared suite-check\n # wrapper — red -> the configured feedbackFile, green -> remove it); the `on`\n # rules turn that into \"halt at blocked\" vs \"proceed to $onGreen\". The script\n # and blocked message/describe differ per entry, so they ride in as `with:`\n # params. Kept SEPARATE from `healthGate` (below) even though both are a\n # check+something-else shape: this gate runs BEFORE planning, so it must NOT\n # sweep the plan/review steering files healthGate does — a human's fresh\n # sketch legitimately still exists here.\n entryGate:\n params: [onGreen, blockedMessage, blockedDescribe, checkLabel, blockedLabel, reviewBase]\n entry: check\n states:\n check:\n actor: check\n label: $checkLabel\n # Anchored here and aliased by `fix-precheck` (`gtd --entry\n # fix-precheck` entry): one suite-check script shared by all four\n # call sites (this machine's ×3 instances plus `fix-precheck`)\n # instead of four source copies. These gates run BEFORE planning, so\n # — unlike `healthGate.check` — they must NOT sweep\n # `todoFile`/`requirementsFile`: the human's fresh sketch still\n # legitimately exists here.\n # `entry: true` on the shared local marks all three `entryGate`\n # instances (plan-gate/spec-gate/review-gate) as manual entries —\n # only `review-gate.check` actually needs the reachability root (it\n # is otherwise unreachable from `entries.default`), but the shared\n # dedup makes marking one mark all three. Accepted as correct: all\n # three ARE the workflow's entry gates.\n entry: true\n # `reviewBase: $reviewBase` — only `review-gate` binds a real\n # template (`<%= it.vars.reviewBase %>`), fixing the whole process's\n # diff base to whatever `gtd --entry review-gate.check --var\n # reviewBase=<commitish>` supplies. `plan-gate`/`spec-gate` bind the\n # literal empty string, which compiles away to \"no base\" here — they\n # are reached from `idle`, never manually entered with a base.\n reviewBase: $reviewBase\n script: &suiteCheck |\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 \"<%~ it.vars.stateDir %>\"\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 mkdir -p \"$(dirname \"$feedback\")\"\n <%~ it.vars.testCommand %> > <%~ it.vars.stateDir %>/.check-output 2>&1\n code=$?\n if [ \"$code\" -ne 0 ]; then\n if [ -s <%~ it.vars.stateDir %>/.check-output ]; then\n mv <%~ it.vars.stateDir %>/.check-output \"$feedback\"\n else\n rm -f <%~ it.vars.stateDir %>/.check-output\n printf 'the test command failed with exit code %s and produced no output.' \"$code\" > \"$feedback\"\n fi\n # A repeat identical failure would otherwise leave FEEDBACK.md\n # byte-identical to its committed copy — no diff, so the check would\n # look GREEN. Stamp each red run with the current HEAD (which always\n # advances between checks) so a still-red re-run always re-registers\n # as an M/A edit and takes the red edge.\n printf '\\n<!-- gtd check %s -->\\n' \"$(git rev-parse --short HEAD 2>/dev/null || echo pending)\" >> \"$feedback\"\n else\n rm -f <%~ it.vars.stateDir %>/.check-output\n rm -f \"$feedback\"\n fi\n on:\n \"A <%= it.vars.feedbackFile %>\": blocked\n \"M <%= it.vars.feedbackFile %>\": blocked\n \"D <%= it.vars.feedbackFile %>\": $onGreen\n \"C\": $onGreen\n blocked:\n actor: human\n label: $blockedLabel\n file: <%= it.vars.feedbackFile %>\n message: $blockedMessage\n on:\n \"* **\":\n to: check\n action: Retry check\n describe: $blockedDescribe\n\n # ── DEDUP: the check/escalate health pair (identity-free) ─────────────────\n # Runs the suite; a red run records the configured feedbackFile and routes to\n # the caller's OWN fixer (`$onRed` — a coder-identity action that lives\n # beside this machine, not inside it, since fixing IS a coder action and this\n # machine has no identity); a green run cleans it up and routes to\n # `$onGreen`. Escalation redirects here after the caller's fixer has retried\n # `max` times without success. Instantiated for the simple build's own tail\n # (`build.health.*`, caller's fixer is `build.fix`) and the per-package\n # build's own tail (`packages.item.health.*`, caller's fixer is\n # `packages.item.fix-suite`).\n healthGate:\n params: [onGreen, onRed, escalateDescribe, checkLabel, escalateLabel]\n entry: check\n states:\n check:\n actor: check\n label: $checkLabel\n # This script ALSO sweeps spent steering files (`todoFile`,\n # `reviewRawFile`, `reviewFeedbackFile`) — a mechanic `entryGate.check`\n # must NOT run, since it executes before planning, while a fresh\n # sketch legitimately still exists.\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 \"<%~ it.vars.stateDir %>\"\n # Hoist every 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\n # 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 mkdir -p \"$(dirname \"$feedback\")\"\n # Sweep steering files spent by the states that led here: the plan\n # (building), the raw review capture (review.collecting), and the\n # feedback instructions (build.addressing) — each `rm -f` is a\n # no-op when the file is already gone or never existed on this path.\n todo=\"<%~ it.vars.todoFile %>\"\n reviewRaw=\"<%~ it.vars.reviewRawFile %>\"\n reviewFeedback=\"<%~ it.vars.reviewFeedbackFile %>\"\n rm -f \"$todo\" \"$reviewRaw\" \"$reviewFeedback\"\n <%~ it.vars.testCommand %> > <%~ it.vars.stateDir %>/.check-output 2>&1\n code=$?\n if [ \"$code\" -ne 0 ]; then\n if [ -s <%~ it.vars.stateDir %>/.check-output ]; then\n mv <%~ it.vars.stateDir %>/.check-output \"$feedback\"\n else\n rm -f <%~ it.vars.stateDir %>/.check-output\n printf 'the test command failed with exit code %s and produced no output.' \"$code\" > \"$feedback\"\n fi\n # A repeat identical failure would otherwise leave FEEDBACK.md\n # byte-identical to its committed copy — no diff, so the check would\n # look GREEN. Stamp each red run with the current HEAD (which always\n # advances between checks) so a still-red re-run always re-registers\n # as an M/A edit and takes the red edge.\n printf '\\n<!-- gtd check %s -->\\n' \"$(git rev-parse --short HEAD 2>/dev/null || echo pending)\" >> \"$feedback\"\n else\n rm -f <%~ it.vars.stateDir %>/.check-output\n rm -f \"$feedback\"\n fi\n on:\n \"A <%= it.vars.feedbackFile %>\": $onRed\n \"M <%= it.vars.feedbackFile %>\": $onRed\n \"* **\": $onGreen\n \"C\": $onGreen\n escalate:\n actor: human\n label: $escalateLabel\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 land`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + (e.action ? \"**\" + e.action + \"** — \" : \"\") + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"* **\":\n to: check\n action: Retry check\n describe: $escalateDescribe\n\n # ── ENCAPSULATION: the advanced flow's design conversation ─────────────────\n # One planner identity across product Q&A -> technical Q&A -> decomposition:\n # a single codebase exploration instead of three, and the human's answer\n # rationale survives from product into technical and into decomposition (all\n # five states share one memory scope, `design`). Replaces the former\n # `qaLoop ×2` (which split one design conversation into two minds that each\n # re-explored the codebase) plus `simpleBuild`'s own `decompose` (which\n # landed on the coder tier only because it happened to live in the build\n # machine — decomposition is judgement-heavy planning work, not building).\n # ▸ planner identity — `model:` declared once here, stamped onto\n # `product-author`, `technical-author`, and `decompose` (its own prompt\n # states; the `*-answer` states are human turns and carry no prompt).\n advancedPlan:\n model: <%= it.vars.plannerModel %>\n params: [onPackages]\n entry: product-author\n states:\n product-author:\n actor: agent\n label: Refining the product plan\n file: <%= it.vars.requirementsFile %>\n mode: qa\n prompt: |\n You are an autonomous coding agent. This workflow steers itself\n through its own state files — treat them as its private scratchpad,\n never as project code or documentation. The only state file this turn\n touches is `<%= it.vars.requirementsFile %>`; develop it. Do not\n create other files to hold notes or output.\n\n `<%= it.vars.requirementsFile %>` holds the product requirements (or the\n plan you already wrote plus the human's answers on a return lap). Read it,\n explore the codebase, and develop it into a concrete, PRODUCT-level plan\n 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 open point you cannot settle yourself, add a question under a\n `## Open Questions` heading near the top: one `### <question>` sub-heading\n each, followed by a checkbox list of candidate answers — aim for TWO\n concrete options plus a final free-text slot, ALL left UNTICKED:\n\n ### <the question>\n\n - [ ] <first option — a concrete answer, a few words of rationale>\n - [ ] <second option>\n - [ ] _your answer_\n\n Never tick a box yourself — the human ticks exactly one per question. The\n last `- [ ] _your answer_` line is the human's own-answer slot; write it\n with the literal `_your answer_` placeholder and leave it for them.\n\n On a RETURN lap the human has ticked one option per question (or replaced\n `_your answer_` with their own text). For each answered question: fold the\n chosen answer into the plan prose, then MOVE the question down into a\n `## Answered Questions` section as `### <question>` followed by the\n resolved answer in plain PROSE (drop the checkboxes). Never re-raise a\n question the human deleted; if the human deleted the whole\n `## Open Questions` section, treat the plan as accepted and surface no new\n questions unless something is genuinely still blocking. Omit\n `## Open Questions` entirely once there are none left.\n\n Leave `<%= it.vars.requirementsFile %>` uncommitted and finish your turn.\n on:\n \"* **\": product-answer\n\n product-answer:\n actor: human\n label: Awaiting your answers\n file: <%= it.vars.requirementsFile %>\n mode: qa\n answerGate: true\n message: |\n `<%= it.vars.requirementsFile %>` holds the product plan under\n development. Each question under `## Open Questions` offers a few options\n plus a `- [ ] _your answer_` slot. Answer EVERY question by ticking\n exactly one box (`- [x]`); for your own answer, replace `_your answer_`\n with your text and tick that line. Stepping is refused while any question\n is unanswered.\n\n What each change does next (then run `gtd land`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + (e.action ? \"**\" + e.action + \"** — \" : \"\") + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"C\":\n to: technical-author\n action: Accept answers\n describe: >-\n with no open questions left (the agent surfaced none, or you deleted\n the section and it finalized the plan), change nothing to move on to\n technical Q&A (**design.technical-author**).\n \"* **\":\n to: product-author\n action: Revise answers\n describe: >-\n tick exactly one option per open question (replace `_your answer_` for\n your own) to send the plan back for the agent to fold your answers in\n (**design.product-author**). Delete a question to skip it, or delete the whole\n `## Open Questions` section to accept the plan as-is.\n\n technical-author:\n actor: agent\n label: Refining the technical plan\n file: <%= it.vars.architectureFile %>\n mode: qa\n prompt: |\n You are an autonomous coding agent. This workflow steers itself\n through its own state files — treat them as its private scratchpad,\n never as project code or documentation. The only state files this\n turn touches are `<%= it.vars.architectureFile %>` (write it) and\n `<%= it.vars.requirementsFile %>` (delete it once its content is\n folded in). Do not create other files to hold notes or output.\n\n If you developed those requirements earlier in this conversation, work\n from what you already settled; otherwise read\n `<%= it.vars.requirementsFile %>` (the converged product plan) yourself.\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 open point you cannot settle yourself, add a question under a\n `## Open Questions` heading exactly as the grilling phase does: one\n `### <question>` each, followed by a checkbox list — TWO concrete options\n plus a final free-text slot, ALL left UNTICKED:\n\n ### <the question>\n\n - [ ] <first option>\n - [ ] <second option>\n - [ ] _your answer_\n\n Never tick a box yourself. On a RETURN lap the human has ticked one option\n per question (or replaced `_your answer_` with their own text): fold each\n chosen answer into the plan prose, then MOVE the question into a\n `## Answered Questions` section as `### <question>` + the resolved answer\n in plain PROSE (drop the checkboxes). Never re-raise a deleted question;\n treat a deleted `## Open Questions` section as acceptance. Omit\n `## Open Questions` once there are none left.\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 \"* **\": technical-answer\n\n technical-answer:\n actor: human\n label: Awaiting your answers\n file: <%= it.vars.architectureFile %>\n mode: qa\n answerGate: true\n message: |\n `<%= it.vars.architectureFile %>` holds the technical plan under\n development. Each question under `## Open Questions` offers a few options\n plus a `- [ ] _your answer_` slot. Answer EVERY question by ticking\n exactly one box (`- [x]`); for your own answer, replace `_your answer_`\n with your text and tick that line. Stepping is refused while any question\n is unanswered.\n\n What each change does next (then run `gtd land`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + (e.action ? \"**\" + e.action + \"** — \" : \"\") + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"C\":\n to: decompose\n action: Accept answers\n describe: >-\n with no open questions left (the agent surfaced none, or you deleted\n the section and it finalized the plan), change nothing to move on to\n decomposing the work into packages (**design.decompose**).\n \"* **\":\n to: technical-author\n action: Revise answers\n describe: >-\n tick exactly one option per open question (replace `_your answer_` for\n your own) to send the plan back for the agent to fold your answers in\n (**design.technical-author**). Delete a question to skip it, or delete the whole\n `## Open Questions` section to accept the plan as-is.\n\n decompose:\n actor: agent\n label: Decomposing into packages\n prompt: |\n You are an autonomous coding agent. This workflow steers itself through\n its own state files — treat them as its private scratchpad, never as\n project code or documentation. The only state files this turn touches\n are the package files it writes under `<%= it.vars.packagesDir %>/` and\n `<%= it.vars.architectureFile %>`, which it deletes. Do not create other\n files to hold notes or output.\n\n If you wrote `<%= it.vars.architectureFile %>` earlier in this\n conversation, work from what you already settled; otherwise read it\n (the converged technical plan) yourself. Decompose it into an ordered\n 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 \"* <%= it.vars.packagesDir %>/**\": $onPackages\n\n # ── ENCAPSULATION: the simple flow's plan-iteration loop ───────────────────\n # ▸ planner identity — `model:` declared once here, stamped onto `planning`\n # (its only prompt state).\n planLoop:\n model: <%= it.vars.plannerModel %>\n params: [onAccept]\n entry: planning\n states:\n planning:\n actor: agent\n label: Refining the plan\n file: <%= it.vars.todoFile %>\n mode: prose\n prompt: |\n You are an autonomous coding agent. This workflow steers itself\n through its own state files — treat them as its private scratchpad,\n never as project code or documentation. The only state file this\n turn touches is `<%= it.vars.todoFile %>`; develop it into the plan.\n Do not create other files to hold notes or output.\n\n `<%= it.vars.todoFile %>` holds a short sketch of what to build — or, on a\n return lap, the plan you already wrote plus the human's edits and comments\n on it. Read it, explore the codebase, and develop it into a concrete\n implementation plan: the files to change, the approach, and the steps to\n take.\n\n Do NOT ask questions and do NOT add an \"Open Questions\" section — decide\n every open point yourself and write the plan as a set of concrete\n decisions. Where a decision is genuinely a judgement call, state the\n choice you made and, in one line, why. The human iterates on the plan by\n editing it directly, not by answering questions.\n\n On a return lap, `<%= it.vars.todoFile %>` carries the human's edits and\n inline comments on your previous plan. If you wrote that plan earlier in\n this conversation, work from what you already know plus these edits;\n otherwise read the file fresh. Fold every edit and comment into a\n revised, self-contained plan and remove the addressed comments — the plan\n is always the current, clean proposal, never a diff.\n\n Leave `<%= it.vars.todoFile %>` uncommitted and finish your turn — do\n not commit, and do not run `gtd land` yourself; the harness does\n that.\n on:\n \"* **\": await-plan\n\n await-plan:\n actor: human\n label: Awaiting your review\n file: <%= it.vars.todoFile %>\n mode: prose\n message: |\n `<%= it.vars.todoFile %>` holds the proposed implementation plan. Read it,\n then either accept it as-is or edit it — rewrite a step, add a\n constraint, or drop an inline comment — to send it back for another round.\n\n What each change does next (then run `gtd land`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + (e.action ? \"**\" + e.action + \"** — \" : \"\") + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"C\":\n to: $onAccept\n action: Accept plan\n describe: >-\n change nothing to move on to implementation (**build.building**).\n \"* **\":\n to: planning\n action: Revise plan\n describe: >-\n rewrite a step, add a constraint, or leave an inline comment to\n send it back for another round (**plan.planning**).\n\n # ── ENCAPSULATION: the shared review tail ──────────────────────────────────\n # ▸ planner identity — `model:` declared once here, stamped onto `reviewing`\n # and `collecting` (its only prompt states). The implementer's OWN\n # follow-through on feedback is a coder action, not a reviewer one — it\n # lives in `build.addressing` (`$onFeedback`), not inside this machine.\n humanReview:\n model: <%= it.vars.plannerModel %>\n entry: reviewing\n states:\n reviewing:\n actor: agent\n label: Reviewing\n file: <%= it.vars.reviewFile %>\n mode: review\n # `gtd --entry review-gate.check` (a plain non-gtd branch, e.g. a\n # colleague's PR) does NOT enter here directly — it enters\n # `review-gate.check` first, which runs the green-baseline gate and,\n # once green, transitions here. The `Gtd-Review-Base:` trailer rides on that\n # entry commit (the process's oldest), so everything below still reuses\n # the same reviewing/await-review/feedback machinery over <commitish>..HEAD.\n prompt: |\n You are an autonomous coding agent. This workflow steers itself\n through its own state files — treat them as its private scratchpad,\n never as project code or documentation. The only state file this\n turn touches is `<%= it.vars.reviewFile %>`; write it at exactly\n that path. Do not create other files to hold notes or output.\n\n Write `<%= it.vars.reviewFile %>` to help a human review the changes in\n this EXACT format:\n\n - First non-blank line: `# Review: <%= it.currentCommit.slice(0, 7) %>`\n - Somewhere in the document: `<!-- base: <%= it.reviewBase %> -->`\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 This prompt carries no diff — read the changes yourself. The range runs\n from `<%= it.reviewBase %>` to the working tree (committed turns plus\n anything still pending); on a feedback round that base is the previous\n review's boundary, so it covers only what changed since then, not items\n already signed off.\n\n Leave `<%= it.vars.reviewFile %>` uncommitted and finish your turn.\n on:\n \"* **\": await-review\n\n await-review:\n actor: human\n label: Awaiting your review\n file: <%= it.vars.reviewFile %>\n mode: review\n # While the process rests here, gtd opens a \"review checkout window\": raw HEAD\n # is rewound to the review base (the most-recent `deciding` commit — so a\n # feedback round shows only `last-review..HEAD` — or the process start on\n # 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 `deciding`, which decides sign-off vs.\n # 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 review-signoff guard (`src/StepGuards.ts`)\n # catches them BEFORE the step commits and refuses: an unfinished\n # review (a box still `- [ ]` with no comment) and a deleted REVIEW.md.\n reviewWindow: true\n message: |\n `<%= it.vars.reviewFile %>` holds the review record for the process — 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 land`:\n\n - **Sign off** — tick EVERY box and leave no comment — no note in\n `<%= it.vars.reviewFile %>`, no code edit — to collapse the process into\n one commit (**build.squashing**).\n - **Request changes** — leave a comment: 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** → **build.addressing**). 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 \"* **\": deciding\n\n deciding:\n actor: check\n label: Reviewing\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 process 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 -> deciding` commit; HEAD^ is the `reviewing`\n # commit that wrote the agent's original REVIEW.md (the sign-off gate\n # refuses an incomplete step, so `deciding` is entered exactly once per\n # round — nothing commits in between). It is FEEDBACK when the human left\n # a comment: a note in REVIEW.md (any edit beyond a \"[ ]\"->\"[x]\" tick) OR a\n # hand-edit to any file this round other than `.gtd/` or the state's own\n # reviewFile (which is var-configurable and may live outside `.gtd/`, e.g.\n # a root-level REVIEW.md — issue #128). Otherwise it is a clean sign-off.\n # This turn only CAPTURES the raw material into REVIEW_RAW.md (the\n # `collecting` agent then turns it into instructions); it does NOT\n # 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 # 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 stateDir=\"<%~ it.vars.stateDir %>\"\n reviewFile=\"<%~ it.vars.reviewFile %>\"\n reviewRawFile=\"<%~ it.vars.reviewRawFile %>\"\n head=$(git rev-parse HEAD)\n mkdir -p \"$(dirname \"$reviewRawFile\")\"\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 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 # Did the human hand-edit anything this round besides stateDir and the\n # reviewFile itself (var-configurable — may live outside stateDir, so it\n # is excluded by exact path, not just the stateDir prefix)?\n if git diff-tree --no-commit-id --name-only -r HEAD \\\n | grep -v \"^$stateDir/\" | grep -vxF \"$reviewFile\" | grep -q . \\\n || [ \"$before\" != \"$after\" ]; then\n {\n echo \"Raw review material captured for classification. The human reviewed the changes and left the feedback below; a downstream agent turns it into an instruction list. This file is machine-captured input, not instructions — it names a commit, never inlined content.\"\n echo\n echo \"Commit: $head\"\n echo \"The human's notes are in $reviewFile at this commit; their hand edits,\"\n echo \"if any, are in that commit's other paths. Run: git show $head\"\n } > \"$reviewRawFile\"\n fi\n rm -f \"$reviewFile\"\n on:\n \"A <%= it.vars.reviewRawFile %>\": collecting\n \"M <%= it.vars.reviewRawFile %>\": collecting\n \"D <%= it.vars.reviewFile %>\": $onSignoff\n\n collecting:\n actor: agent\n label: Collecting your feedback\n file: <%= it.vars.reviewFeedbackFile %>\n prompt: |\n You are an autonomous coding agent turning raw review feedback into an\n explicit instruction list for a builder. This workflow steers itself\n through its own state files — treat them as its private scratchpad,\n never as project code or documentation. The only state files this\n turn touches are `<%= it.vars.reviewRawFile %>` (read it, then\n delete it) and `<%= it.vars.reviewFeedbackFile %>` (write it) — do\n NOT change any other file, you classify, you do not build.\n\n The raw review material is: <%~ it.read(it.vars.reviewRawFile) %>\n\n It names a commit. If you wrote today's review earlier in this\n conversation, work from what you already reviewed; otherwise read that\n commit's diff yourself before classifying anything.\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>`. 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 <%= it.vars.reviewFeedbackFile %>\": $onFeedback\n \"M <%= it.vars.reviewFeedbackFile %>\": $onFeedback\n\n # ── ENCAPSULATION: the advanced flow's per-package agentic review ──────────\n # A single state: a machine whose entire job is to be a separate MIND from\n # the implementer, distinct from the coder identity that builds/fixes the\n # package (`packages.item.*`). ▸ planner identity — `model:` declared once\n # here, stamped onto `review` (its only prompt state). Fixing the reviewer's\n # own findings is a coder action, not a reviewer one — it lives in\n # `packages.item.fix-spec` (`$onFix`), not inside this machine.\n specReview:\n model: <%= it.vars.plannerModel %>\n params: [onApproved, onFix]\n entry: review\n states:\n review:\n actor: agent\n label: Reviewing the package\n # Bounded per process: once entered `max` times, a transition into it is\n # redirected to `$onApproved` (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 # `build.fix` cap has (see the README's \"Why\" section, \"Bounded, not\n # runaway\").\n retry:\n max: 3\n otherwise: $onApproved\n prompt: |\n You are an autonomous coding agent reviewing a freshly-built work package\n against its own spec. This workflow steers itself through its own\n state files — treat them as its private scratchpad, never as project\n code or documentation. The only state file this turn touches is\n `<%= it.vars.specFeedbackFile %>`; write it only when — and only\n when — you find problems. Do not create other files to hold notes or\n output.\n\n The package spec is: <%~ it.read(it.vars.nextFile) %>\n\n Read that spec, then verify the package's implementation against it: are\n all its tasks done, acceptance criteria met, and the code sound and\n consistent with the codebase? This prompt carries no diff — read it\n yourself: the range runs from `<%= it.startCommit %>` to the working\n tree.\n\n Note the range is process-wide (from the start of the whole build, not\n just this package), so it can span earlier packages too.\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 on:\n \"A <%= it.vars.specFeedbackFile %>\": $onFix\n \"M <%= it.vars.specFeedbackFile %>\": $onFix\n \"D <%= it.vars.specFeedbackFile %>\": $onApproved\n \"C\": $onApproved\n\n # ── ENCAPSULATION: the advanced flow's per-package build queue ─────────────\n # Identity-free (a bookkeeping queue, not a builder) — the actual per-package\n # build identity lives in its own `item` child (packageItem, below).\n packageLoop:\n params: [onDrained]\n entry: picking\n states:\n picking:\n actor: check\n label: Picking the next package\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 # Sweep the advanced flow's spent Q&A steering files: requirementsFile\n # is consumed by technical-qa, architectureFile by decompose — both are\n # gone by the time the package queue starts. No-op if already removed.\n requirements=\"<%~ it.vars.requirementsFile %>\"\n architecture=\"<%~ it.vars.architectureFile %>\"\n rm -f \"$requirements\" \"$architecture\"\n next=$(ls <%~ it.vars.packagesDir %>/*.md 2>/dev/null | head -n 1)\n if [ -n \"$next\" ]; then\n mkdir -p \"$(dirname \"$nextFile\")\"\n printf '%s' \"$next\" > \"$nextFile\"\n else\n rm -f \"$nextFile\"\n fi\n on:\n \"D <%= it.vars.nextFile %>\": $onDrained\n \"* <%= it.vars.nextFile %>\": item.building\n \"C\": $onDrained\n\n # The per-package build identity — its own coder machine (below), so\n # deleting a package file's own lifecycle stays inside `item` while the\n # queue itself (`packages`) stays a single state.\n item:\n machine: packageItem\n with:\n onNext: picking\n\n # ── ENCAPSULATION: the advanced flow's per-package build identity ──────────\n # ▸ coder identity — `model:` declared once here, stamped onto `building`,\n # `fix-suite`, and `fix-spec` (its own prompt states). `closing` ends that\n # package file's own lifecycle; keeping it here (not in the queue) keeps\n # `packages` itself to a single state. `packages.item.closing` -> `picking`\n # is the ONE upward-resolving target in the whole file, so it MUST be a\n # param (`$onNext`, bound by `packages` — see the machine's own reference).\n packageItem:\n model: <%= it.vars.coderModel %>\n params: [onNext]\n entry: building\n states:\n building:\n actor: agent\n label: Building\n prompt: |\n You are an autonomous coding agent. This workflow steers itself\n through its own state files — treat them as its private scratchpad,\n never as project code or documentation. This turn writes no state\n file: do NOT delete the package file (the spec-review gate reads\n it after you) and never touch `<%= it.vars.nextFile %>` — the\n `picking` state 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 \"* **\": health.check\n\n fix-suite:\n actor: agent\n label: Fixing the check\n file: <%= it.vars.feedbackFile %>\n prompt: &suiteFixPrompt |\n You are an autonomous coding agent. This workflow steers itself\n through its own state files — treat them as its private scratchpad,\n never as project code or documentation. The only state file this\n turn touches is `<%= it.vars.feedbackFile %>`; fix it per its\n contents, or delete it if the feedback turns out to be wrong. Do not\n create other files to hold notes or output.\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 retry:\n max: 3\n otherwise: health.escalate\n on:\n \"* **\": health.check\n\n fix-spec:\n actor: agent\n label: Fixing review feedback\n file: <%= it.vars.specFeedbackFile %>\n prompt: |\n You are an autonomous coding agent. This workflow steers itself\n through its own state files — treat them as its private scratchpad,\n never as project code or documentation. The only state file this\n turn touches is `<%= it.vars.specFeedbackFile %>`; address it, then\n delete it. Do not create other files to hold notes or output.\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 \"* **\": health.check\n\n closing:\n actor: check\n label: Closing out the package\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 \"* **\": $onNext\n\n # Per-package health pair: packages.item.health.check/.escalate ->\n # packages.item.spec.review (green) or packages.item.fix-suite (red).\n health:\n machine: healthGate\n with:\n onGreen: spec\n onRed: fix-suite\n escalateDescribe: >-\n edit the code and/or `.gtd/FEEDBACK.md` yourself\n (**packages.item.health.check**).\n checkLabel: Running checks\n escalateLabel: Escalating to a human\n\n # Per-package agentic review: packages.item.spec.review -> closing\n # (approved), or packages.item.fix-spec (findings — re-verify via the\n # full health check before re-reviewing).\n spec:\n machine: specReview\n with:\n onApproved: closing\n onFix: fix-spec\n\n # ── ENCAPSULATION: the simple flow's build identity ────────────────────────\n # ▸ coder identity — `model:` declared once here, stamped onto `building`,\n # `squashing`, `addressing`, and `fix` (its own prompt states). Decomposition\n # (`design.decompose`) belongs to the planner tier now, not here — see\n # `advancedPlan` below. `addressing` is the implementer's OWN follow-through\n # on review feedback (a coder action) — reached via `$onFeedback`, bound at\n # its own `review:` reference below, not nested inside the (planner-only)\n # `humanReview` machine. `humanReview` is nested HERE (not at the root) so\n # the review tail's run stays inside the builder's own scope — see the\n # header comment.\n simpleBuild:\n model: <%= it.vars.coderModel %>\n params: [onDone]\n entry: building\n states:\n building:\n actor: agent\n label: Building\n file: <%= it.vars.todoFile %>\n prompt: |\n You are an autonomous coding agent. This workflow steers itself through\n its own state files — treat them as its private scratchpad, never as\n project code or documentation. The only state file this turn touches is\n `<%= it.vars.todoFile %>`; delete it once its work is complete. Do not\n create other files to hold notes or output.\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 \"* **\": health.check\n\n squashing:\n actor: agent\n label: Squashing\n file: <%= it.vars.commitMsgFile %>\n prompt: |\n You are an autonomous coding agent. The process is approved and done. This\n workflow steers itself through its own state files — treat them as its\n private scratchpad, never as project code or documentation. The only\n state file this turn touches is `<%= it.vars.commitMsgFile %>`; write\n it. Do not create other files to hold notes or output.\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. This prompt carries no diff —\n if you built that range yourself earlier in this conversation, work from\n what you already know; otherwise read the range yourself. It runs from\n `<%= it.retainedBase %>` to the working tree, and is exactly what that\n commit will contain, the changes THIS process made (its trace/retry\n boundary onward). For a normal build\n process that is the whole feature; for a `gtd --entry review-gate.check`\n process it is only the fixes made during the review — NOT the changeset\n that was under review.\n\n If that range is non-empty, write `<%= it.vars.commitMsgFile %>` with ONE\n conventional-commits message for the changes (subject line\n `type(scope): subject`, imperative mood, <= 72 characters; a body\n explaining the why, trade-offs, and key decisions). Plain text, no\n markdown wrapper.\n\n If that range is empty (a clean review sign-off with no fixes), write\n `<%= it.vars.commitMsgFile %>` with exactly this one line and nothing\n else:\n\n chore: human review\n on:\n \"A <%= it.vars.commitMsgFile %>\": $onDone\n \"M <%= it.vars.commitMsgFile %>\": $onDone\n\n addressing:\n actor: agent\n label: Addressing your feedback\n file: <%= it.vars.reviewFeedbackFile %>\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\n # feedback-progress guard (`src/StepGuards.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. This\n workflow steers itself through its own state files — treat them as\n its private scratchpad, never as project code or documentation. The\n only state file this turn touches is\n `<%= it.vars.reviewFeedbackFile %>`; address it, then delete it. Do\n not create other files to hold notes or output.\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 \"* **\": health.check\n\n fix:\n actor: agent\n label: Fixing the check\n file: <%= it.vars.feedbackFile %>\n prompt: *suiteFixPrompt\n retry:\n max: 3\n otherwise: health.escalate\n on:\n \"* **\": health.check\n\n # Shared health pair: build.health.check/.escalate -> build.review.reviewing\n # (green) or build.fix (red).\n health:\n machine: healthGate\n with:\n onGreen: review\n onRed: fix\n escalateDescribe: >-\n edit the code and/or `.gtd/FEEDBACK.md` yourself\n (**build.health.check**).\n checkLabel: Running checks\n escalateLabel: Escalating to a human\n\n # The shared review tail, nested HERE (not at the root) so its run stays\n # inside the builder's own scope across the feedback round-trip — see\n # the header comment. Sign-off -> squashing; feedback -> addressing (the\n # implementer's own coder-identity follow-through, not a reviewer action).\n review:\n machine: humanReview\n with:\n onSignoff: squashing\n onFeedback: addressing\n\n # The root machine — the whole \"advanced/simple\" flow tree described in\n # this file's header comment. WIRING ONLY: no identity of its own, just the\n # shared entry/exit states and the top-level machine tree.\n unified:\n entry: idle\n states:\n # ── Shared entry ────────────────────────────────────────────────────────\n idle:\n actor: human\n label: Idle\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 process.\n\n To start one, create a steering file describing what you want built:\n\n - `<%= it.vars.todoFile %>` — a short sketch — starts the SIMPLE flow\n (plan, build, review).\n - `<%= it.vars.requirementsFile %>` — the product requirements — starts\n the ADVANCED flow (product + technical Q&A, package decomposition,\n parallel build, per-package agentic review).\n\n What each change does next (then run `gtd land`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + (e.action ? \"**\" + e.action + \"** — \" : \"\") + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"* <%= it.vars.requirementsFile %>\":\n to: spec-gate\n action: Start advanced flow\n describe: >-\n save the product requirements to `.gtd/REQUIREMENTS.md` — gtd first\n checks the test baseline is green, then the agent develops them\n through product then technical Q&A, decomposes the work into\n packages, and builds each in parallel (**spec-gate.check**).\n \"* **\":\n to: plan-gate\n action: Start simple flow\n describe: >-\n save a short sketch to `.gtd/TODO.md` (or any other change) — gtd\n first checks the test baseline is green, then the agent reads it,\n explores the codebase, and develops it into a concrete\n implementation plan (**plan-gate.check**).\n\n # ── Green-baseline gate: `gtd --entry fix-precheck` entry ─────────────────\n # `gtd --entry fix-precheck` enters HERE: run the suite, then a red run\n # drops straight into the shared `build.fix` loop (-> build.health.check ->\n # review + squash tail); a green run is a no-op back to `idle` (nothing\n # to fix).\n fix-precheck:\n actor: check\n label: Checking the baseline\n entry: true\n script: *suiteCheck\n on:\n \"A <%= it.vars.feedbackFile %>\": build.fix\n \"M <%= it.vars.feedbackFile %>\": build.fix\n \"D <%= it.vars.feedbackFile %>\": idle\n \"C\": idle\n\n # ── Shared tail: the finale ─────────────────────────────────────────────\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\n # ── Green-baseline gate: simple flow entry. ────────────────────────────────\n plan-gate:\n machine: entryGate\n with:\n onGreen: plan\n reviewBase: \"\"\n blockedMessage: |\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 land`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + (e.action ? \"**\" + e.action + \"** — \" : \"\") + e.describe + \"\\n\" %>\n <% } }) %>\n blockedDescribe: >-\n edit the code and/or `.gtd/FEEDBACK.md` to fix the failing tests\n (**plan-gate.check**). To repair the baseline as its own separate\n reviewed commit instead, abandon this start and run\n `gtd --entry fix-precheck` from a clean `idle`.\n checkLabel: Checking the baseline\n blockedLabel: Baseline is red\n\n # ── Green-baseline gate: advanced flow entry. ──────────────────────────────\n spec-gate:\n machine: entryGate\n with:\n onGreen: design\n reviewBase: \"\"\n blockedMessage: |\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 land`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + (e.action ? \"**\" + e.action + \"** — \" : \"\") + e.describe + \"\\n\" %>\n <% } }) %>\n blockedDescribe: >-\n edit the code and/or `.gtd/FEEDBACK.md` to fix the failing tests\n (**spec-gate.check**). To repair the baseline as its own separate\n reviewed commit instead, abandon this start and run\n `gtd --entry fix-precheck` from a clean `idle`.\n checkLabel: Checking the baseline\n blockedLabel: Baseline is red\n\n # ── Green-baseline gate: `gtd --entry review-gate.check` entry -> build.review.reviewing. ─\n review-gate:\n machine: entryGate\n with:\n onGreen: build.review\n reviewBase: <%= it.vars.reviewBase %>\n blockedMessage: |\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 land`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + (e.action ? \"**\" + e.action + \"** — \" : \"\") + e.describe + \"\\n\" %>\n <% } }) %>\n blockedDescribe: >-\n edit the code and/or `.gtd/FEEDBACK.md` to fix the failing tests\n (**review-gate.check**).\n checkLabel: Checking the baseline\n blockedLabel: Baseline is red\n\n # ── Advanced flow: the design conversation (design.product-author/.product-answer ─\n # -> design.technical-author/.technical-answer -> design.decompose -> packages). ──\n design:\n machine: advancedPlan\n with:\n onPackages: packages\n\n # ── Simple flow plan-iteration loop (plan.planning/.await-plan -> build.building). ──\n plan:\n machine: planLoop\n with:\n onAccept: build.building\n\n # ── Advanced per-package build queue (packages.* -> build.review). ────────\n packages:\n machine: packageLoop\n with:\n onDrained: build.review\n\n # ── Simple flow's build identity (build.* -> build.review -> done). ───────\n build:\n machine: simpleBuild\n with:\n onPackages: packages\n onDone: done\n";
192201
+ var unified_default = "# The unified gtd workflow template — gtd's BUILT-IN DEFAULT workflow. When no\n# `workflow:` key is configured anywhere in the cwd→home config chain,\n# src/Config.ts runs this template directly, so a state command works with no\n# configuration at all. `gtd init` seeds only the settings a project tunes\n# (vars.testCommand + a modes: formatting suggestion), never the workflow\n# itself. See the README's \"How it works\" and \"Driving the loop\" sections for\n# 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. `renderInitConfig` (src/workflows/\n# templates.ts) can MATERIALIZE this raw source into a `.gtdrc.json`\n# `workflow:` value — the way to eject/customize the machine, and the hermetic\n# test fixture — but `gtd init` itself no longer writes the workflow.\n#\n# ── Authored with MACHINES (entry:/machines:) ───────────────────────────────\n#\n# This template is written as a tree of reusable, parameterized MACHINES (see\n# src/Machines.ts) — a gate/loop written once and instantiated several times\n# with different `with:` bindings (dedup), or a complex cluster grouped under\n# one name for source comprehension (encapsulation). Every reference below is\n# an ordinary `{ machine: <name>, with: { ... } }` local, expanded at\n# compile-time into concrete, qualified states before the pure engine ever\n# sees the definition — no runtime footprint.\n#\n# MACHINE BOUNDARIES LINE UP WITH CONVERSATIONAL IDENTITY: a machine that holds\n# an identity (a planner or a coder persona) declares its own `model:` ONCE, at\n# the machine level (stamped onto every one of its own `prompt` states — see\n# src/Machines.ts's `resolveInstanceModel`), instead of repeating `model:` on\n# every state; no state anywhere declares `model:` or `memory:` directly any\n# more (the memory key is computed from the machine tree — see\n# src/PatternMachine.ts's `memoryScopeAt` / src/Edge.ts's `memoryKeyFor`). Two\n# gate/queue machines below are deliberately IDENTITY-FREE (no `model:`):\n# `entryGate`/`healthGate` are infrastructure, not a persona, and `packageLoop`\n# is a bookkeeping queue, not a builder.\n#\n# * entryGate (dedup ×3) — the green-baseline gate (check + human blocked)\n# guarding the simple (`plan-gate`), advanced\n# (`spec-gate`) and review (`review-gate`)\n# entries. Identity-free.\n# * healthGate (dedup ×2) — the check/escalate health pair shared by the\n# simple build's own tail (`build.health.*`) and\n# the per-package build's own tail\n# (`packages.item.health.*`) — each caller's OWN\n# `fix` state (a coder-identity action) lives\n# beside this machine, not inside it, since\n# fixing IS a coder action and this machine has\n# no identity of its own. Identity-free.\n# * advancedPlan (encap) — the advanced flow's one design conversation:\n# product Q&A, technical Q&A, and decomposition\n# (`design.*`: product-author/-answer,\n# technical-author/-answer, decompose) all share\n# ONE machine identity, so the agent explores the\n# codebase once, not three times, and the human's\n# answer rationale survives into decomposition.\n# ▸ planner.\n# * planLoop (encap) — the simple flow's plan-iteration loop (`plan.*`).\n# ▸ planner.\n# * humanReview (encap) — the shared review tail, nested INSIDE\n# `simpleBuild` (`build.review.*`: reviewing →\n# await-review → deciding → collecting) — see\n# \"Why humanReview is nested, not a root sibling\"\n# below. ▸ planner — the implementer's OWN\n# follow-through on feedback is a coder action,\n# not a reviewer one, so it lives in\n# `build.addressing` instead.\n# * specReview (encap) — the advanced flow's per-package agentic review,\n# now a SINGLE state (`packages.item.spec.review`)\n# — a machine whose entire job is to be a\n# separate mind from the implementer. ▸ planner\n# — fixing the reviewer's own findings is a coder\n# action, so it lives in `packages.item.fix-spec`\n# instead.\n# * packageLoop (encap) — the advanced flow's per-package build queue\n# (`packages.picking`). Identity-free — the\n# actual per-package build identity lives in its\n# own `item` child (packageItem, below).\n# * packageItem (encap) — the advanced flow's per-package build identity\n# (`packages.item.*`: building, fix-suite,\n# fix-spec, closing), plus its own `health` and\n# `spec` children. ▸ coder.\n# * simpleBuild (encap) — the simple flow's build identity (`build.*`:\n# building, squashing, addressing, fix), plus its\n# own `health` child AND its own `review` child\n# (humanReview, above). ▸ coder.\n#\n# ── Why humanReview is nested, not a root sibling ───────────────────────────\n#\n# `humanReview` sits UNDER `simpleBuild` (`build.review`) rather than beside it\n# at the root, because a machine's memory scope is its dotted instance path\n# (see src/PatternMachine.ts's `memoryScopeAt`): entering a DESCENDANT scope\n# never breaks the parent's unbroken run, but entering a sibling does. The\n# review tail sits between the two coder turns that most need continuity —\n# `build.addressing` (follows through on review feedback) and `build.squashing`\n# (drafts the finale from the range this process built) — so hoisting it back\n# to a root sibling would force both of those turns to re-derive the feature\n# from scratch on every pass through the tail, instead of resuming the session\n# that built it. The reviewer itself is unaffected either way: `build.review.*`\n# is its own scope regardless of where it's nested, so it never resumes the\n# builder's session.\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 blocked gate (fix the failures and re-run, exactly like the\n# escalate states), or is what `gtd --entry fix-precheck` 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# * the configured requirementsFile (default .gtd/REQUIREMENTS.md) ->\n# `spec-gate.check` -> the ADVANCED flow (one design conversation across\n# product + technical Q&A and decomposition, per-package parallel build +\n# agentic spec-review).\n# * anything else (e.g. the configured todoFile, default .gtd/TODO.md) ->\n# `plan-gate.check` -> the SIMPLE flow (one grilling Q&A loop, a monolithic\n# build, no decomposition, no agentic review).\n#\n# A third entry, `gtd --entry review-gate.check`, jumps there directly and,\n# once green, to `build.review.reviewing` to review a foreign branch through\n# the same shared tail. A fourth, `gtd --entry fix-precheck`, jumps there: a\n# red suite drops straight into the shared `build.fix` loop and out through the\n# review + squash tail; a green suite is a no-op back to `idle`. Both states\n# declare `entry: true` — otherwise unreachable from `entries.default`.\n#\n# All three converge at `build.review.reviewing` -> `build.review.await-review`.\n# Ticking a REVIEW.md box means only \"I reviewed this hunk\"; a COMMENT (not an\n# unticked box) is what asks for changes. Every human step at\n# `build.review.await-review` goes to `build.review.deciding`, which inspects\n# 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 -> build.squashing -> done (the ONLY path\n# to the finale — the whole process collapses into one commit).\n# * any comment — a note in REVIEW.md OR a hand-edit to code — ->\n# build.review.deciding writes REVIEW_FEEDBACK.md -> build.review.collecting\n# implements it (completing any code the human hand-edited, never\n# reverting their lines) -> build.addressing (a coder action, not a\n# reviewer one — see \"Why humanReview is nested\" above) -> back through\n# `build.health.check` (the shared health pair, bound via the\n# `$onFeedback`/local `review` reference) -> build.review.reviewing\n# regenerates an INCREMENTAL REVIEW.md (build.review.deciding carries\n# `reviewBase: true`, so each feedback round's review + checkout window\n# covers only `last-review..HEAD`, i.e. the agent's follow-through, not the\n# human's own edit) -> await-review, looping until sign-off.\n# Two dead ends never commit: the review-signoff guard (src/StepGuards.ts)\n# REFUSES a step that leaves a box unticked with no comment (finish reviewing\n# first) and a deleted REVIEW.md (restore it and tick the boxes).\n#\n# The shared health pair (`build.health.check`/`.escalate`, plus the caller's\n# own `build.fix`) serves the simple build and the feedback loop — its green\n# edge targets `build.review.reviewing`. The advanced per-package loop keeps\n# its OWN health pair (`packages.item.health.check`/`.escalate`, plus\n# `packages.item.fix-suite`) because its green edge targets the per-package\n# `packages.item.spec.review` gate instead.\n#\n# Every `on` pattern's path below is written as the matching var's Eta\n# template (e.g. `\"A <%= it.vars.feedbackFile %>\"`), not a literal `.gtd/...`\n# path — repointing a var reroutes the machine along with every template that\n# reads/writes it (see the README's \"Variables\" section).\n\nvars:\n testCommand: npm test\n # Where gtd keeps its own scratch/bookkeeping (the check scripts' temp output\n # file, review-deciding's state-file exclusion). Independent of the per-file\n # vars below — relocating a file wholesale means setting both.\n stateDir: .gtd\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 # Written by packages.item.building when a package's acceptance criteria are\n # already met (an earlier package's fix turn pulled the work in); read by\n # the spec reviewer as the evidence a clean turn there is a real approval,\n # not a lazy/crashed one; removed by packages.item.closing.\n satisfiedFile: .gtd/SATISFIED.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_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/technical-qa/review turns;\n # `coderModel` (default \"base\") is the tier for the build/fix turns.\n plannerModel: smart\n coderModel: base\n # The fixed diff base for `gtd --entry review-gate.check --var\n # reviewBase=<commitish>` — a plain non-gtd branch reviewed against that\n # commitish rather than the process start. Declared here (rather than left\n # undeclared) so `--var reviewBase=...` passes the CLI's declared-name\n # check; the default empty string renders blank, which the entry command\n # refuses (a review entry always requires an explicit base).\n reviewBase: \"\"\n\n# `prose` is a format-only steering-file mode this workflow blesses for itself:\n# a curated free-form document with no gtd-side schema (the simple flow's plan\n# file, .gtd/TODO.md — see the `plan.planning` state below). An empty entry is\n# the format-only tier any workflow can declare for a name with no built-in\n# parser; `qa`/`review` need no entry here — src/PatternConfig.ts's compiler\n# seeds those two from the built-in registry (src/SteeringFormats.ts)\n# automatically.\nmodes:\n prose: {}\n\nentry:\n default: unified\n\nmachines:\n # ── DEDUP: the green-baseline entry gate (identity-free) ──────────────────\n # Runs the suite before any work starts (its script is the shared suite-check\n # wrapper — red -> the configured feedbackFile, green -> remove it); the `on`\n # rules turn that into \"halt at blocked\" vs \"proceed to $onGreen\". The script\n # and blocked message/describe differ per entry, so they ride in as `with:`\n # params. Kept SEPARATE from `healthGate` (below) even though both are a\n # check+something-else shape: this gate runs BEFORE planning, so it must NOT\n # sweep the plan/review steering files healthGate does — a human's fresh\n # sketch legitimately still exists here.\n entryGate:\n params: [onGreen, blockedMessage, blockedDescribe, checkLabel, blockedLabel, reviewBase]\n entry: check\n states:\n check:\n actor: check\n label: $checkLabel\n # Anchored here and aliased by `fix-precheck` (`gtd --entry\n # fix-precheck` entry): one suite-check script shared by all four\n # call sites (this machine's ×3 instances plus `fix-precheck`)\n # instead of four source copies. These gates run BEFORE planning, so\n # — unlike `healthGate.check` — they must NOT sweep\n # `todoFile`/`requirementsFile`: the human's fresh sketch still\n # legitimately exists here.\n # `entry: true` on the shared local marks all three `entryGate`\n # instances (plan-gate/spec-gate/review-gate) as manual entries —\n # only `review-gate.check` actually needs the reachability root (it\n # is otherwise unreachable from `entries.default`), but the shared\n # dedup makes marking one mark all three. Accepted as correct: all\n # three ARE the workflow's entry gates.\n entry: true\n # `reviewBase: $reviewBase` — only `review-gate` binds a real\n # template (`<%= it.vars.reviewBase %>`), fixing the whole process's\n # diff base to whatever `gtd --entry review-gate.check --var\n # reviewBase=<commitish>` supplies. `plan-gate`/`spec-gate` bind the\n # literal empty string, which compiles away to \"no base\" here — they\n # are reached from `idle`, never manually entered with a base.\n reviewBase: $reviewBase\n script: &suiteCheck |\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 \"<%~ it.vars.stateDir %>\"\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 mkdir -p \"$(dirname \"$feedback\")\"\n <%~ it.vars.testCommand %> > <%~ it.vars.stateDir %>/.check-output 2>&1\n code=$?\n if [ \"$code\" -ne 0 ]; then\n if [ -s <%~ it.vars.stateDir %>/.check-output ]; then\n mv <%~ it.vars.stateDir %>/.check-output \"$feedback\"\n else\n rm -f <%~ it.vars.stateDir %>/.check-output\n printf 'the test command failed with exit code %s and produced no output.' \"$code\" > \"$feedback\"\n fi\n # A repeat identical failure would otherwise leave FEEDBACK.md\n # byte-identical to its committed copy — no diff, so the check would\n # look GREEN. Stamp each red run with the current HEAD (which always\n # advances between checks) so a still-red re-run always re-registers\n # as an M/A edit and takes the red edge.\n printf '\\n<!-- gtd check %s -->\\n' \"$(git rev-parse --short HEAD 2>/dev/null || echo pending)\" >> \"$feedback\"\n else\n rm -f <%~ it.vars.stateDir %>/.check-output\n rm -f \"$feedback\"\n fi\n on:\n \"A <%= it.vars.feedbackFile %>\": blocked\n \"M <%= it.vars.feedbackFile %>\": blocked\n \"D <%= it.vars.feedbackFile %>\": $onGreen\n \"C\": $onGreen\n blocked:\n actor: human\n label: $blockedLabel\n file: <%= it.vars.feedbackFile %>\n message: $blockedMessage\n on:\n \"* **\":\n to: check\n action: Retry check\n describe: $blockedDescribe\n\n # ── DEDUP: the check/escalate health pair (identity-free) ─────────────────\n # Runs the suite; a red run records the configured feedbackFile and routes to\n # the caller's OWN fixer (`$onRed` — a coder-identity action that lives\n # beside this machine, not inside it, since fixing IS a coder action and this\n # machine has no identity); a green run cleans it up and routes to\n # `$onGreen`. Escalation redirects here after the caller's fixer has retried\n # `max` times without success. Instantiated for the simple build's own tail\n # (`build.health.*`, caller's fixer is `build.fix`) and the per-package\n # build's own tail (`packages.item.health.*`, caller's fixer is\n # `packages.item.fix-suite`).\n healthGate:\n params: [onGreen, onRed, escalateDescribe, checkLabel, escalateLabel]\n entry: check\n states:\n check:\n actor: check\n label: $checkLabel\n # This script ALSO sweeps spent steering files (`todoFile`,\n # `reviewRawFile`, `reviewFeedbackFile`) — a mechanic `entryGate.check`\n # must NOT run, since it executes before planning, while a fresh\n # sketch legitimately still exists.\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 \"<%~ it.vars.stateDir %>\"\n # Hoist every 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\n # 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 mkdir -p \"$(dirname \"$feedback\")\"\n # Sweep steering files spent by the states that led here: the plan\n # (building), the raw review capture (review.collecting), and the\n # feedback instructions (build.addressing) — each `rm -f` is a\n # no-op when the file is already gone or never existed on this path.\n todo=\"<%~ it.vars.todoFile %>\"\n reviewRaw=\"<%~ it.vars.reviewRawFile %>\"\n reviewFeedback=\"<%~ it.vars.reviewFeedbackFile %>\"\n rm -f \"$todo\" \"$reviewRaw\" \"$reviewFeedback\"\n <%~ it.vars.testCommand %> > <%~ it.vars.stateDir %>/.check-output 2>&1\n code=$?\n if [ \"$code\" -ne 0 ]; then\n if [ -s <%~ it.vars.stateDir %>/.check-output ]; then\n mv <%~ it.vars.stateDir %>/.check-output \"$feedback\"\n else\n rm -f <%~ it.vars.stateDir %>/.check-output\n printf 'the test command failed with exit code %s and produced no output.' \"$code\" > \"$feedback\"\n fi\n # A repeat identical failure would otherwise leave FEEDBACK.md\n # byte-identical to its committed copy — no diff, so the check would\n # look GREEN. Stamp each red run with the current HEAD (which always\n # advances between checks) so a still-red re-run always re-registers\n # as an M/A edit and takes the red edge.\n printf '\\n<!-- gtd check %s -->\\n' \"$(git rev-parse --short HEAD 2>/dev/null || echo pending)\" >> \"$feedback\"\n else\n rm -f <%~ it.vars.stateDir %>/.check-output\n rm -f \"$feedback\"\n fi\n on:\n \"A <%= it.vars.feedbackFile %>\": $onRed\n \"M <%= it.vars.feedbackFile %>\": $onRed\n \"* **\": $onGreen\n \"C\": $onGreen\n escalate:\n actor: human\n label: $escalateLabel\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 land`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + (e.action ? \"**\" + e.action + \"** — \" : \"\") + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"* **\":\n to: check\n action: Retry check\n describe: $escalateDescribe\n\n # ── ENCAPSULATION: the advanced flow's design conversation ─────────────────\n # One planner identity across product Q&A -> technical Q&A -> decomposition:\n # a single codebase exploration instead of three, and the human's answer\n # rationale survives from product into technical and into decomposition (all\n # five states share one memory scope, `design`). Replaces the former\n # `qaLoop ×2` (which split one design conversation into two minds that each\n # re-explored the codebase) plus `simpleBuild`'s own `decompose` (which\n # landed on the coder tier only because it happened to live in the build\n # machine — decomposition is judgement-heavy planning work, not building).\n # ▸ planner identity — `model:` declared once here, stamped onto\n # `product-author`, `technical-author`, and `decompose` (its own prompt\n # states; the `*-answer` states are human turns and carry no prompt).\n advancedPlan:\n model: <%= it.vars.plannerModel %>\n params: [onPackages]\n entry: product-author\n states:\n product-author:\n actor: agent\n label: Refining the product plan\n file: <%= it.vars.requirementsFile %>\n mode: qa\n prompt: |\n You are an autonomous coding agent. This workflow steers itself\n through its own state files — treat them as its private scratchpad,\n never as project code or documentation. The only state file this turn\n touches is `<%= it.vars.requirementsFile %>`; develop it. Do not\n create other files to hold notes or output.\n\n `<%= it.vars.requirementsFile %>` holds the product requirements (or the\n plan you already wrote plus the human's answers on a return lap). Read it,\n explore the codebase, and develop it into a concrete, PRODUCT-level plan\n 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 open point you cannot settle yourself, add a question under a\n `## Open Questions` heading near the top: one `### <question>` sub-heading\n each, followed by a checkbox list of candidate answers — aim for TWO\n concrete options plus a final free-text slot, ALL left UNTICKED:\n\n ### <the question>\n\n - [ ] <first option — a concrete answer, a few words of rationale>\n - [ ] <second option>\n - [ ] _your answer_\n\n Never tick a box yourself — the human ticks exactly one per question. The\n last `- [ ] _your answer_` line is the human's own-answer slot; write it\n with the literal `_your answer_` placeholder and leave it for them.\n\n On a RETURN lap the human has ticked one option per question (or replaced\n `_your answer_` with their own text). For each answered question: fold the\n chosen answer into the plan prose, then MOVE the question down into a\n `## Answered Questions` section as `### <question>` followed by the\n resolved answer in plain PROSE (drop the checkboxes). Never re-raise a\n question the human deleted; if the human deleted the whole\n `## Open Questions` section, treat the plan as accepted and surface no new\n questions unless something is genuinely still blocking. Omit\n `## Open Questions` entirely once there are none left.\n\n Leave `<%= it.vars.requirementsFile %>` uncommitted and finish your turn.\n on:\n \"* **\": product-answer\n\n product-answer:\n actor: human\n label: Awaiting your answers\n file: <%= it.vars.requirementsFile %>\n mode: qa\n answerGate: true\n message: |\n `<%= it.vars.requirementsFile %>` holds the product plan under\n development. Each question under `## Open Questions` offers a few options\n plus a `- [ ] _your answer_` slot. Answer EVERY question by ticking\n exactly one box (`- [x]`); for your own answer, replace `_your answer_`\n with your text and tick that line. Stepping is refused while any question\n is unanswered.\n\n What each change does next (then run `gtd land`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + (e.action ? \"**\" + e.action + \"** — \" : \"\") + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"C\":\n to: technical-author\n action: Accept answers\n describe: >-\n with no open questions left (the agent surfaced none, or you deleted\n the section and it finalized the plan), change nothing to move on to\n technical Q&A (**design.technical-author**).\n \"* **\":\n to: product-author\n action: Revise answers\n describe: >-\n tick exactly one option per open question (replace `_your answer_` for\n your own) to send the plan back for the agent to fold your answers in\n (**design.product-author**). Delete a question to skip it, or delete the whole\n `## Open Questions` section to accept the plan as-is.\n\n technical-author:\n actor: agent\n label: Refining the technical plan\n file: <%= it.vars.architectureFile %>\n mode: qa\n prompt: |\n You are an autonomous coding agent. This workflow steers itself\n through its own state files — treat them as its private scratchpad,\n never as project code or documentation. The only state files this\n turn touches are `<%= it.vars.architectureFile %>` (write it) and\n `<%= it.vars.requirementsFile %>` (delete it once its content is\n folded in). Do not create other files to hold notes or output.\n\n If you developed those requirements earlier in this conversation, work\n from what you already settled; otherwise read\n `<%= it.vars.requirementsFile %>` (the converged product plan) yourself.\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 open point you cannot settle yourself, add a question under a\n `## Open Questions` heading exactly as the grilling phase does: one\n `### <question>` each, followed by a checkbox list — TWO concrete options\n plus a final free-text slot, ALL left UNTICKED:\n\n ### <the question>\n\n - [ ] <first option>\n - [ ] <second option>\n - [ ] _your answer_\n\n Never tick a box yourself. On a RETURN lap the human has ticked one option\n per question (or replaced `_your answer_` with their own text): fold each\n chosen answer into the plan prose, then MOVE the question into a\n `## Answered Questions` section as `### <question>` + the resolved answer\n in plain PROSE (drop the checkboxes). Never re-raise a deleted question;\n treat a deleted `## Open Questions` section as acceptance. Omit\n `## Open Questions` once there are none left.\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 \"* **\": technical-answer\n\n technical-answer:\n actor: human\n label: Awaiting your answers\n file: <%= it.vars.architectureFile %>\n mode: qa\n answerGate: true\n message: |\n `<%= it.vars.architectureFile %>` holds the technical plan under\n development. Each question under `## Open Questions` offers a few options\n plus a `- [ ] _your answer_` slot. Answer EVERY question by ticking\n exactly one box (`- [x]`); for your own answer, replace `_your answer_`\n with your text and tick that line. Stepping is refused while any question\n is unanswered.\n\n What each change does next (then run `gtd land`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + (e.action ? \"**\" + e.action + \"** — \" : \"\") + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"C\":\n to: decompose\n action: Accept answers\n describe: >-\n with no open questions left (the agent surfaced none, or you deleted\n the section and it finalized the plan), change nothing to move on to\n decomposing the work into packages (**design.decompose**).\n \"* **\":\n to: technical-author\n action: Revise answers\n describe: >-\n tick exactly one option per open question (replace `_your answer_` for\n your own) to send the plan back for the agent to fold your answers in\n (**design.technical-author**). Delete a question to skip it, or delete the whole\n `## Open Questions` section to accept the plan as-is.\n\n decompose:\n actor: agent\n label: Decomposing into packages\n prompt: |\n You are an autonomous coding agent. This workflow steers itself through\n its own state files — treat them as its private scratchpad, never as\n project code or documentation. The only state files this turn touches\n are the package files it writes under `<%= it.vars.packagesDir %>/` and\n `<%= it.vars.architectureFile %>`, which it deletes. Do not create other\n files to hold notes or output.\n\n If you wrote `<%= it.vars.architectureFile %>` earlier in this\n conversation, work from what you already settled; otherwise read it\n (the converged technical plan) yourself. Decompose it into an ordered\n 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`, ...).\n\n Every package must leave the test suite GREEN on its own, in the order\n you write them. If a package's change would red-light tests that a\n later package is meant to fix, that is not a valid split: MERGE the\n two into one package. Never annotate a package with \"expected\n sequencing\" or \"this will fail until package N\" — an expected-red\n window is a decomposition error, not a note.\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 \"* <%= it.vars.packagesDir %>/**\": $onPackages\n\n # ── ENCAPSULATION: the simple flow's plan-iteration loop ───────────────────\n # ▸ planner identity — `model:` declared once here, stamped onto `planning`\n # (its only prompt state).\n planLoop:\n model: <%= it.vars.plannerModel %>\n params: [onAccept]\n entry: planning\n states:\n planning:\n actor: agent\n label: Refining the plan\n file: <%= it.vars.todoFile %>\n mode: prose\n prompt: |\n You are an autonomous coding agent. This workflow steers itself\n through its own state files — treat them as its private scratchpad,\n never as project code or documentation. The only state file this\n turn touches is `<%= it.vars.todoFile %>`; develop it into the plan.\n Do not create other files to hold notes or output.\n\n `<%= it.vars.todoFile %>` holds a short sketch of what to build — or, on a\n return lap, the plan you already wrote plus the human's edits and comments\n on it. Read it, explore the codebase, and develop it into a concrete\n implementation plan: the files to change, the approach, and the steps to\n take.\n\n Do NOT ask questions and do NOT add an \"Open Questions\" section — decide\n every open point yourself and write the plan as a set of concrete\n decisions. Where a decision is genuinely a judgement call, state the\n choice you made and, in one line, why. The human iterates on the plan by\n editing it directly, not by answering questions.\n\n On a return lap, `<%= it.vars.todoFile %>` carries the human's edits and\n inline comments on your previous plan. If you wrote that plan earlier in\n this conversation, work from what you already know plus these edits;\n otherwise read the file fresh. Fold every edit and comment into a\n revised, self-contained plan and remove the addressed comments — the plan\n is always the current, clean proposal, never a diff.\n\n Leave `<%= it.vars.todoFile %>` uncommitted and finish your turn — do\n not commit, and do not run `gtd land` yourself; the harness does\n that.\n on:\n \"* **\": await-plan\n\n await-plan:\n actor: human\n label: Awaiting your review\n file: <%= it.vars.todoFile %>\n mode: prose\n message: |\n `<%= it.vars.todoFile %>` holds the proposed implementation plan. Read it,\n then either accept it as-is or edit it — rewrite a step, add a\n constraint, or drop an inline comment — to send it back for another round.\n\n What each change does next (then run `gtd land`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + (e.action ? \"**\" + e.action + \"** — \" : \"\") + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"C\":\n to: $onAccept\n action: Accept plan\n describe: >-\n change nothing to move on to implementation (**build.building**).\n \"* **\":\n to: planning\n action: Revise plan\n describe: >-\n rewrite a step, add a constraint, or leave an inline comment to\n send it back for another round (**plan.planning**).\n\n # ── ENCAPSULATION: the shared review tail ──────────────────────────────────\n # ▸ planner identity — `model:` declared once here, stamped onto `reviewing`\n # and `collecting` (its only prompt states). The implementer's OWN\n # follow-through on feedback is a coder action, not a reviewer one — it\n # lives in `build.addressing` (`$onFeedback`), not inside this machine.\n humanReview:\n model: <%= it.vars.plannerModel %>\n entry: reviewing\n states:\n reviewing:\n actor: agent\n label: Reviewing\n file: <%= it.vars.reviewFile %>\n mode: review\n # `gtd --entry review-gate.check` (a plain non-gtd branch, e.g. a\n # colleague's PR) does NOT enter here directly — it enters\n # `review-gate.check` first, which runs the green-baseline gate and,\n # once green, transitions here. The `Gtd-Review-Base:` trailer rides on that\n # entry commit (the process's oldest), so everything below still reuses\n # the same reviewing/await-review/feedback machinery over <commitish>..HEAD.\n prompt: |\n You are an autonomous coding agent. This workflow steers itself\n through its own state files — treat them as its private scratchpad,\n never as project code or documentation. The only state file this\n turn touches is `<%= it.vars.reviewFile %>`; write it at exactly\n that path. Do not create other files to hold notes or output.\n\n Write `<%= it.vars.reviewFile %>` to help a human review the changes in\n this EXACT format:\n\n - First non-blank line: `# Review: <%= it.currentCommit.slice(0, 7) %>`\n - Somewhere in the document: `<!-- base: <%= it.reviewBase %> -->`\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 This prompt carries no diff — read the changes yourself. The range runs\n from `<%= it.reviewBase %>` to the working tree (committed turns plus\n anything still pending); on a feedback round that base is the previous\n review's boundary, so it covers only what changed since then, not items\n already signed off.\n\n Leave `<%= it.vars.reviewFile %>` uncommitted and finish your turn.\n on:\n \"* **\": await-review\n\n await-review:\n actor: human\n label: Awaiting your review\n file: <%= it.vars.reviewFile %>\n mode: review\n # While the process rests here, gtd opens a \"review checkout window\": raw HEAD\n # is rewound to the review base (the most-recent `deciding` commit — so a\n # feedback round shows only `last-review..HEAD` — or the process start on\n # 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 `deciding`, which decides sign-off vs.\n # 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 review-signoff guard (`src/StepGuards.ts`)\n # catches them BEFORE the step commits and refuses: an unfinished\n # review (a box still `- [ ]` with no comment) and a deleted REVIEW.md.\n reviewWindow: true\n message: |\n `<%= it.vars.reviewFile %>` holds the review record for the process — 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 land`:\n\n - **Sign off** — tick EVERY box and leave no comment — no note in\n `<%= it.vars.reviewFile %>`, no code edit — to collapse the process into\n one commit (**build.squashing**).\n - **Request changes** — leave a comment: 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** → **build.addressing**). 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 \"* **\": deciding\n\n deciding:\n actor: check\n label: Reviewing\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 process 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 -> deciding` commit; HEAD^ is the `reviewing`\n # commit that wrote the agent's original REVIEW.md (the sign-off gate\n # refuses an incomplete step, so `deciding` is entered exactly once per\n # round — nothing commits in between). It is FEEDBACK when the human left\n # a comment: a note in REVIEW.md (any edit beyond a \"[ ]\"->\"[x]\" tick) OR a\n # hand-edit to any file this round other than `.gtd/` or the state's own\n # reviewFile (which is var-configurable and may live outside `.gtd/`, e.g.\n # a root-level REVIEW.md — issue #128). Otherwise it is a clean sign-off.\n # This turn only CAPTURES the raw material into REVIEW_RAW.md (the\n # `collecting` agent then turns it into instructions); it does NOT\n # 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 # 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 stateDir=\"<%~ it.vars.stateDir %>\"\n reviewFile=\"<%~ it.vars.reviewFile %>\"\n reviewRawFile=\"<%~ it.vars.reviewRawFile %>\"\n head=$(git rev-parse HEAD)\n mkdir -p \"$(dirname \"$reviewRawFile\")\"\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 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 # Did the human hand-edit anything this round besides stateDir and the\n # reviewFile itself (var-configurable — may live outside stateDir, so it\n # is excluded by exact path, not just the stateDir prefix)?\n if git diff-tree --no-commit-id --name-only -r HEAD \\\n | grep -v \"^$stateDir/\" | grep -vxF \"$reviewFile\" | grep -q . \\\n || [ \"$before\" != \"$after\" ]; then\n {\n echo \"Raw review material captured for classification. The human reviewed the changes and left the feedback below; a downstream agent turns it into an instruction list. This file is machine-captured input, not instructions — it names a commit, never inlined content.\"\n echo\n echo \"Commit: $head\"\n echo \"The human's notes are in $reviewFile at this commit; their hand edits,\"\n echo \"if any, are in that commit's other paths. Run: git show $head\"\n } > \"$reviewRawFile\"\n fi\n rm -f \"$reviewFile\"\n on:\n \"A <%= it.vars.reviewRawFile %>\": collecting\n \"M <%= it.vars.reviewRawFile %>\": collecting\n \"D <%= it.vars.reviewFile %>\": $onSignoff\n\n collecting:\n actor: agent\n label: Collecting your feedback\n file: <%= it.vars.reviewFeedbackFile %>\n prompt: |\n You are an autonomous coding agent turning raw review feedback into an\n explicit instruction list for a builder. This workflow steers itself\n through its own state files — treat them as its private scratchpad,\n never as project code or documentation. The only state files this\n turn touches are `<%= it.vars.reviewRawFile %>` (read it, then\n delete it) and `<%= it.vars.reviewFeedbackFile %>` (write it) — do\n NOT change any other file, you classify, you do not build.\n\n The raw review material is: <%~ it.read(it.vars.reviewRawFile) %>\n\n It names a commit. If you wrote today's review earlier in this\n conversation, work from what you already reviewed; otherwise read that\n commit's diff yourself before classifying anything.\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>`. 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 <%= it.vars.reviewFeedbackFile %>\": $onFeedback\n \"M <%= it.vars.reviewFeedbackFile %>\": $onFeedback\n\n # ── ENCAPSULATION: the advanced flow's per-package agentic review ──────────\n # A single state: a machine whose entire job is to be a separate MIND from\n # the implementer, distinct from the coder identity that builds/fixes the\n # package (`packages.item.*`). ▸ planner identity — `model:` declared once\n # here, stamped onto `review` (its only prompt state). Fixing the reviewer's\n # own findings is a coder action, not a reviewer one — it lives in\n # `packages.item.fix-spec` (`$onFix`), not inside this machine.\n specReview:\n model: <%= it.vars.plannerModel %>\n params: [onApproved, onFix]\n entry: review\n states:\n review:\n actor: agent\n label: Reviewing the package\n # Bounded per process: once entered `max` times, a transition into it is\n # redirected to `$onApproved` (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 # `build.fix` cap has (see the README's \"Why\" section, \"Bounded, not\n # runaway\").\n retry:\n max: 3\n otherwise: $onApproved\n prompt: |\n You are an autonomous coding agent reviewing a freshly-built work package\n against its own spec. This workflow steers itself through its own\n state files — treat them as its private scratchpad, never as project\n code or documentation. The only state file this turn touches is\n `<%= it.vars.specFeedbackFile %>`; write it only when — and only\n when — you find problems. Do not create other files to hold notes or\n output.\n\n The package spec is: <%~ it.read(it.vars.nextFile) %>\n\n Read that spec, then verify the package's implementation against it: are\n all its tasks done, acceptance criteria met, and the code sound and\n consistent with the codebase? This prompt carries no diff — read it\n yourself: the range runs from `<%= it.startCommit %>` to the working\n tree.\n\n Note the range is process-wide (from the start of the whole build, not\n just this package), so it can span earlier packages too.\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 on:\n \"A <%= it.vars.specFeedbackFile %>\": $onFix\n \"M <%= it.vars.specFeedbackFile %>\": $onFix\n \"D <%= it.vars.specFeedbackFile %>\": $onApproved\n \"C\": $onApproved\n\n # ── ENCAPSULATION: the advanced flow's per-package build queue ─────────────\n # Identity-free (a bookkeeping queue, not a builder) — the actual per-package\n # build identity lives in its own `item` child (packageItem, below).\n packageLoop:\n params: [onDrained]\n entry: picking\n states:\n picking:\n actor: check\n label: Picking the next package\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 # Sweep the advanced flow's spent Q&A steering files: requirementsFile\n # is consumed by technical-qa, architectureFile by decompose — both are\n # gone by the time the package queue starts. No-op if already removed.\n requirements=\"<%~ it.vars.requirementsFile %>\"\n architecture=\"<%~ it.vars.architectureFile %>\"\n rm -f \"$requirements\" \"$architecture\"\n next=$(ls <%~ it.vars.packagesDir %>/*.md 2>/dev/null | head -n 1)\n if [ -n \"$next\" ]; then\n mkdir -p \"$(dirname \"$nextFile\")\"\n printf '%s' \"$next\" > \"$nextFile\"\n else\n rm -f \"$nextFile\"\n fi\n on:\n \"D <%= it.vars.nextFile %>\": $onDrained\n \"* <%= it.vars.nextFile %>\": item.building\n \"C\": $onDrained\n\n # The per-package build identity — its own coder machine (below), so\n # deleting a package file's own lifecycle stays inside `item` while the\n # queue itself (`packages`) stays a single state.\n item:\n machine: packageItem\n with:\n onNext: picking\n\n # ── ENCAPSULATION: the advanced flow's per-package build identity ──────────\n # ▸ coder identity — `model:` declared once here, stamped onto `building`,\n # `fix-suite`, and `fix-spec` (its own prompt states). `closing` ends that\n # package file's own lifecycle; keeping it here (not in the queue) keeps\n # `packages` itself to a single state. `packages.item.closing` -> `picking`\n # is the ONE upward-resolving target in the whole file, so it MUST be a\n # param (`$onNext`, bound by `packages` — see the machine's own reference).\n packageItem:\n model: <%= it.vars.coderModel %>\n params: [onNext]\n entry: building\n states:\n building:\n actor: agent\n label: Building\n prompt: |\n You are an autonomous coding agent. This workflow steers itself\n through its own state files — treat them as its private scratchpad,\n never as project code or documentation. The only state file this\n turn may write is `<%= it.vars.satisfiedFile %>` (see below); do NOT\n delete the package file (the spec-review gate reads it after you)\n and never touch `<%= it.vars.nextFile %>` — the `picking` state owns\n it.\n\n The package to implement is: <%~ it.read(it.vars.nextFile) %>\n\n Before implementing anything, check the package's acceptance criteria\n against the current tree — an earlier package's fix turn may already\n have satisfied them. If **every** criterion is already met, implement\n nothing: write `<%= it.vars.satisfiedFile %>` recording each criterion\n and the concrete evidence it is already satisfied (the commit, file,\n or symbol that proves it), change nothing else, and finish your turn.\n If any criterion is outstanding, implement the package normally and\n do not write that file.\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 \"A <%= it.vars.satisfiedFile %>\": &satisfied\n to: health.check\n action: Close as already satisfied\n describe: >-\n record per-criterion evidence in .gtd/SATISFIED.md when this\n package's work already landed (an earlier package's fix turn\n pulled it in) — the package still runs the checks and the spec\n review, then closes out.\n \"M <%= it.vars.satisfiedFile %>\": *satisfied\n \"* **\": health.check\n\n fix-suite:\n actor: agent\n label: Fixing the check\n file: <%= it.vars.feedbackFile %>\n prompt: |\n You are an autonomous coding agent. This workflow steers itself\n through its own state files — treat them as its private scratchpad,\n never as project code or documentation. The only state file this\n turn touches is `<%= it.vars.feedbackFile %>`; fix it per its\n contents, or delete it if the feedback turns out to be wrong. Do not\n create other files to hold notes or output.\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. If the\n only way to green the suite is to implement work that another\n package file describes, make the smallest change that gets there and\n do it — that later package will then legitimately report itself\n already satisfied when its turn comes.\n\n Leave everything uncommitted and finish your turn — do not commit.\n retry:\n max: 3\n otherwise: health.escalate\n on:\n \"* **\": health.check\n\n fix-spec:\n actor: agent\n label: Fixing review feedback\n file: <%= it.vars.specFeedbackFile %>\n prompt: |\n You are an autonomous coding agent. This workflow steers itself\n through its own state files — treat them as its private scratchpad,\n never as project code or documentation. The only state file this\n turn touches is `<%= it.vars.specFeedbackFile %>`; address it, then\n delete it. Do not create other files to hold notes or output.\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 \"* **\": health.check\n\n closing:\n actor: check\n label: Closing out the package\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 and\n # already-satisfied evidence, so `picking` selects the NEXT package (or\n # falls through to review when the queue is empty). Reached on approval\n # (spec review clean) and on the spec review retry cap (force-close,\n # 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 %> <%~ it.vars.satisfiedFile %>\n on:\n \"* **\": $onNext\n\n # Per-package health pair: packages.item.health.check/.escalate ->\n # packages.item.spec.review (green) or packages.item.fix-suite (red).\n health:\n machine: healthGate\n with:\n onGreen: spec\n onRed: fix-suite\n escalateDescribe: >-\n edit the code and/or `.gtd/FEEDBACK.md` yourself\n (**packages.item.health.check**).\n checkLabel: Running checks\n escalateLabel: Escalating to a human\n\n # Per-package agentic review: packages.item.spec.review -> closing\n # (approved), or packages.item.fix-spec (findings — re-verify via the\n # full health check before re-reviewing).\n spec:\n machine: specReview\n with:\n onApproved: closing\n onFix: fix-spec\n\n # ── ENCAPSULATION: the simple flow's build identity ────────────────────────\n # ▸ coder identity — `model:` declared once here, stamped onto `building`,\n # `squashing`, `addressing`, and `fix` (its own prompt states). Decomposition\n # (`design.decompose`) belongs to the planner tier now, not here — see\n # `advancedPlan` below. `addressing` is the implementer's OWN follow-through\n # on review feedback (a coder action) — reached via `$onFeedback`, bound at\n # its own `review:` reference below, not nested inside the (planner-only)\n # `humanReview` machine. `humanReview` is nested HERE (not at the root) so\n # the review tail's run stays inside the builder's own scope — see the\n # header comment.\n simpleBuild:\n model: <%= it.vars.coderModel %>\n params: [onDone]\n entry: building\n states:\n building:\n actor: agent\n label: Building\n file: <%= it.vars.todoFile %>\n prompt: |\n You are an autonomous coding agent. This workflow steers itself through\n its own state files — treat them as its private scratchpad, never as\n project code or documentation. The only state file this turn touches is\n `<%= it.vars.todoFile %>`; delete it once its work is complete. Do not\n create other files to hold notes or output.\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 \"* **\": health.check\n\n squashing:\n actor: agent\n label: Squashing\n file: <%= it.vars.commitMsgFile %>\n prompt: |\n You are an autonomous coding agent. The process is approved and done. This\n workflow steers itself through its own state files — treat them as its\n private scratchpad, never as project code or documentation. The only\n state file this turn touches is `<%= it.vars.commitMsgFile %>`; write\n it. Do not create other files to hold notes or output.\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. This prompt carries no diff —\n if you built that range yourself earlier in this conversation, work from\n what you already know; otherwise read the range yourself. It runs from\n `<%= it.retainedBase %>` to the working tree, and is exactly what that\n commit will contain, the changes THIS process made (its trace/retry\n boundary onward). For a normal build\n process that is the whole feature; for a `gtd --entry review-gate.check`\n process it is only the fixes made during the review — NOT the changeset\n that was under review.\n\n If that range is non-empty, write `<%= it.vars.commitMsgFile %>` with ONE\n conventional-commits message for the changes (subject line\n `type(scope): subject`, imperative mood, <= 72 characters; a body\n explaining the why, trade-offs, and key decisions). Plain text, no\n markdown wrapper.\n\n If that range is empty (a clean review sign-off with no fixes), write\n `<%= it.vars.commitMsgFile %>` with exactly this one line and nothing\n else:\n\n chore: human review\n on:\n \"A <%= it.vars.commitMsgFile %>\": $onDone\n \"M <%= it.vars.commitMsgFile %>\": $onDone\n\n addressing:\n actor: agent\n label: Addressing your feedback\n file: <%= it.vars.reviewFeedbackFile %>\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\n # feedback-progress guard (`src/StepGuards.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. This\n workflow steers itself through its own state files — treat them as\n its private scratchpad, never as project code or documentation. The\n only state file this turn touches is\n `<%= it.vars.reviewFeedbackFile %>`; address it, then delete it. Do\n not create other files to hold notes or output.\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 \"* **\": health.check\n\n fix:\n actor: agent\n label: Fixing the check\n file: <%= it.vars.feedbackFile %>\n prompt: |\n You are an autonomous coding agent. This workflow steers itself\n through its own state files — treat them as its private scratchpad,\n never as project code or documentation. The only state file this\n turn touches is `<%= it.vars.feedbackFile %>`; fix it per its\n contents, or delete it if the feedback turns out to be wrong. Do not\n create other files to hold notes or output.\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 retry:\n max: 3\n otherwise: health.escalate\n on:\n \"* **\": health.check\n\n # Shared health pair: build.health.check/.escalate -> build.review.reviewing\n # (green) or build.fix (red).\n health:\n machine: healthGate\n with:\n onGreen: review\n onRed: fix\n escalateDescribe: >-\n edit the code and/or `.gtd/FEEDBACK.md` yourself\n (**build.health.check**).\n checkLabel: Running checks\n escalateLabel: Escalating to a human\n\n # The shared review tail, nested HERE (not at the root) so its run stays\n # inside the builder's own scope across the feedback round-trip — see\n # the header comment. Sign-off -> squashing; feedback -> addressing (the\n # implementer's own coder-identity follow-through, not a reviewer action).\n review:\n machine: humanReview\n with:\n onSignoff: squashing\n onFeedback: addressing\n\n # The root machine — the whole \"advanced/simple\" flow tree described in\n # this file's header comment. WIRING ONLY: no identity of its own, just the\n # shared entry/exit states and the top-level machine tree.\n unified:\n entry: idle\n states:\n # ── Shared entry ────────────────────────────────────────────────────────\n idle:\n actor: human\n label: Idle\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 process.\n\n To start one, create a steering file describing what you want built:\n\n - `<%= it.vars.todoFile %>` — a short sketch — starts the SIMPLE flow\n (plan, build, review).\n - `<%= it.vars.requirementsFile %>` — the product requirements — starts\n the ADVANCED flow (product + technical Q&A, package decomposition,\n parallel build, per-package agentic review).\n\n What each change does next (then run `gtd land`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + (e.action ? \"**\" + e.action + \"** — \" : \"\") + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"* <%= it.vars.requirementsFile %>\":\n to: spec-gate\n action: Start advanced flow\n describe: >-\n save the product requirements to `.gtd/REQUIREMENTS.md` — gtd first\n checks the test baseline is green, then the agent develops them\n through product then technical Q&A, decomposes the work into\n packages, and builds each in parallel (**spec-gate.check**).\n \"* **\":\n to: plan-gate\n action: Start simple flow\n describe: >-\n save a short sketch to `.gtd/TODO.md` (or any other change) — gtd\n first checks the test baseline is green, then the agent reads it,\n explores the codebase, and develops it into a concrete\n implementation plan (**plan-gate.check**).\n\n # ── Green-baseline gate: `gtd --entry fix-precheck` entry ─────────────────\n # `gtd --entry fix-precheck` enters HERE: run the suite, then a red run\n # drops straight into the shared `build.fix` loop (-> build.health.check ->\n # review + squash tail); a green run is a no-op back to `idle` (nothing\n # to fix).\n fix-precheck:\n actor: check\n label: Checking the baseline\n entry: true\n script: *suiteCheck\n on:\n \"A <%= it.vars.feedbackFile %>\": build.fix\n \"M <%= it.vars.feedbackFile %>\": build.fix\n \"D <%= it.vars.feedbackFile %>\": idle\n \"C\": idle\n\n # ── Shared tail: the finale ─────────────────────────────────────────────\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\n # ── Green-baseline gate: simple flow entry. ────────────────────────────────\n plan-gate:\n machine: entryGate\n with:\n onGreen: plan\n reviewBase: \"\"\n blockedMessage: |\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 land`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + (e.action ? \"**\" + e.action + \"** — \" : \"\") + e.describe + \"\\n\" %>\n <% } }) %>\n blockedDescribe: >-\n edit the code and/or `.gtd/FEEDBACK.md` to fix the failing tests\n (**plan-gate.check**). To repair the baseline as its own separate\n reviewed commit instead, abandon this start and run\n `gtd --entry fix-precheck` from a clean `idle`.\n checkLabel: Checking the baseline\n blockedLabel: Baseline is red\n\n # ── Green-baseline gate: advanced flow entry. ──────────────────────────────\n spec-gate:\n machine: entryGate\n with:\n onGreen: design\n reviewBase: \"\"\n blockedMessage: |\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 land`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + (e.action ? \"**\" + e.action + \"** — \" : \"\") + e.describe + \"\\n\" %>\n <% } }) %>\n blockedDescribe: >-\n edit the code and/or `.gtd/FEEDBACK.md` to fix the failing tests\n (**spec-gate.check**). To repair the baseline as its own separate\n reviewed commit instead, abandon this start and run\n `gtd --entry fix-precheck` from a clean `idle`.\n checkLabel: Checking the baseline\n blockedLabel: Baseline is red\n\n # ── Green-baseline gate: `gtd --entry review-gate.check` entry -> build.review.reviewing. ─\n review-gate:\n machine: entryGate\n with:\n onGreen: build.review\n reviewBase: <%= it.vars.reviewBase %>\n blockedMessage: |\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 land`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + (e.action ? \"**\" + e.action + \"** — \" : \"\") + e.describe + \"\\n\" %>\n <% } }) %>\n blockedDescribe: >-\n edit the code and/or `.gtd/FEEDBACK.md` to fix the failing tests\n (**review-gate.check**).\n checkLabel: Checking the baseline\n blockedLabel: Baseline is red\n\n # ── Advanced flow: the design conversation (design.product-author/.product-answer ─\n # -> design.technical-author/.technical-answer -> design.decompose -> packages). ──\n design:\n machine: advancedPlan\n with:\n onPackages: packages\n\n # ── Simple flow plan-iteration loop (plan.planning/.await-plan -> build.building). ──\n plan:\n machine: planLoop\n with:\n onAccept: build.building\n\n # ── Advanced per-package build queue (packages.* -> build.review). ────────\n packages:\n machine: packageLoop\n with:\n onDrained: build.review\n\n # ── Simple flow's build identity (build.* -> build.review -> done). ───────\n build:\n machine: simpleBuild\n with:\n onPackages: packages\n onDone: done\n";
192191
192202
  //#endregion
192192
192203
  //#region src/workflows/templates.ts
192193
192204
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pmelab/gtd",
3
- "version": "9.1.1",
3
+ "version": "9.1.2",
4
4
  "private": false,
5
5
  "description": "Git-aware CLI that emits the next prompt for an autonomous coding agent based on the current repository state",
6
6
  "bin": {
@@ -25,15 +25,17 @@
25
25
  "node": ">=22"
26
26
  },
27
27
  "type": "module",
28
+ "packageManager": "npm@11.17.0",
28
29
  "scripts": {
29
30
  "prepare": "husky",
30
31
  "dev": "node dev/run.mjs",
31
32
  "build": "tsdown",
32
33
  "postbuild": "jiti scripts/generate-schema.ts && node scripts/assert-no-test-doubles.mjs",
33
34
  "watch": "tsdown --watch",
34
- "test": "npm run format:check && npm run typecheck && npm run lint && npm run test:unit && npm run test:e2e && fallow --summary --quiet",
35
+ "test": "turbo run format:check typecheck lint test:unit test:e2e:inmem test:e2e:live deadcode",
35
36
  "test:unit": "vitest run --project unit",
36
37
  "test:watch": "vitest",
38
+ "test:changed": "vitest run --project unit --project e2e-inmem --changed HEAD",
37
39
  "test:e2e": "npm run test:e2e:inmem && npm run test:e2e:live",
38
40
  "typecheck": "tsc --noEmit",
39
41
  "lint": "oxlint .",
@@ -41,8 +43,8 @@
41
43
  "format:check": "oxfmt --check .",
42
44
  "test:mutation": "stryker run",
43
45
  "test:e2e:inmem": "vitest run --project e2e-inmem",
44
- "pretest:e2e:live": "tsdown --logLevel silent",
45
- "test:e2e:live": "vitest run --project e2e-live"
46
+ "test:e2e:live": "vitest run --project e2e-live",
47
+ "deadcode": "fallow --summary --quiet"
46
48
  },
47
49
  "lint-staged": {
48
50
  "*": "oxfmt --no-error-on-unmatched-pattern --write"
@@ -68,6 +70,7 @@
68
70
  "quickpickle": "1.11.2",
69
71
  "semantic-release": "^25.0.5",
70
72
  "tsdown": "^0.22.8",
73
+ "turbo": "^2.10.9",
71
74
  "vitest": "^3.2.0"
72
75
  },
73
76
  "peerDependencies": {