@pmelab/gtd 10.0.0 → 10.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -256,14 +256,19 @@ coding agent:
256
256
  ```
257
257
 
258
258
  It prints a self-contained briefing that teaches the agent to set the project
259
- up, build two commands for itself in whatever shell or runtime it runs, and ask
259
+ up, build four commands for itself in whatever shell or runtime it runs, and ask
260
260
  you what you want before it starts driving. You get one prompt to paste, not a
261
- state name to choose. The two commands: a **loop command** that drives beats
262
- until the process rests, and an **edit command** that opens the steering file
263
- the process is waiting on right now — falling back to `.gtd/TODO.md` when the
264
- resting state declares none. That fallback is also how you begin: on a clean
265
- repository the edit command opens the empty `.gtd/TODO.md`, and whatever you
266
- write there is the first sketch the whole process gets planned from.
261
+ state name to choose. The four commands: **`gtd-build`** drives beats until the
262
+ process rests; **`gtd-edit`** opens the steering file the process is waiting on
263
+ right now — falling back to `.gtd/TODO.md` when the resting state declares none;
264
+ **`gtd-review <commitish>`** starts a review round over that commitish and
265
+ drives it; **`gtd-fix`** enters the fix process and drives it. The
266
+ `.gtd/TODO.md` fallback is also how you begin: on a clean repository the edit
267
+ command opens the empty `.gtd/TODO.md`, and whatever you write there is the
268
+ first sketch the whole process gets planned from. As a final step, if it finds
269
+ an LSP-capable editor, the briefing also offers to wire up live diagnostics and
270
+ review actions in it — asking first and naming the exact file, and merging
271
+ rather than overwriting your editor's config.
267
272
 
268
273
  ### The workflow it ships with
269
274
 
@@ -190610,15 +190610,33 @@ const parseQuestionBlock = (block, status) => {
190610
190610
  };
190611
190611
  };
190612
190612
  /**
190613
+ * `## Open Questions` must precede every other level-2 section, and
190614
+ * `## Answered Questions` must follow every other level-2 section — so a
190615
+ * reader (and a driver walking the file) always finds open questions first
190616
+ * and resolved ones last. At most one finding per rule, regardless of how
190617
+ * many competing sections offend it. Level-1 headings and prose don't count.
190618
+ */
190619
+ const checkSectionOrder = (lines) => {
190620
+ const h2Lines = lines.map((line, index) => ({
190621
+ index,
190622
+ heading: parseHeading(line)
190623
+ })).filter((entry) => entry.heading?.level === 2).map((entry) => entry.index);
190624
+ const openIndex = lines.findIndex((line) => line.trim() === OPEN_QUESTIONS_HEADING);
190625
+ const answeredIndex = lines.findIndex((line) => line.trim() === ANSWERED_QUESTIONS_HEADING);
190626
+ const findings = [];
190627
+ if (openIndex !== -1 && h2Lines.some((i) => i !== openIndex && i < openIndex)) findings.push("A '##' section appears before '## Open Questions', which must come first");
190628
+ if (answeredIndex !== -1 && h2Lines.some((i) => i !== answeredIndex && i > answeredIndex)) findings.push("A '##' section appears after '## Answered Questions', which must come last");
190629
+ return findings;
190630
+ };
190631
+ /**
190613
190632
  * Parses the open-questions structure out of `content`. Total and
190614
190633
  * side-effect-free: always returns a result, never throws. Questions are
190615
- * returned in document order (by heading line) regardless of which section
190616
- * comes first.
190634
+ * returned in document order (by heading line).
190617
190635
  */
190618
190636
  const parseOpenQuestions = (content) => {
190619
190637
  const lines = content.split(/\r?\n/);
190620
190638
  const questions = [];
190621
- const errors = [];
190639
+ const errors = [...checkSectionOrder(lines)];
190622
190640
  const sections = [[OPEN_QUESTIONS_HEADING, "open"], [ANSWERED_QUESTIONS_HEADING, "answered"]];
190623
190641
  for (const [heading, status] of sections) {
190624
190642
  const headingIndex = lines.findIndex((line) => line.trim() === heading);
@@ -191749,7 +191767,7 @@ const compileWorkflowConfig = (raw, configDir, rcModes, inlineFileRefs = true, f
191749
191767
  };
191750
191768
  //#endregion
191751
191769
  //#region src/workflows/unified.yaml
191752
- var unified_default = "# gtd's BUILT-IN DEFAULT workflow — used when no `workflow:` key is\n# configured anywhere in the cwd→home config chain; `gtd init` seeds only\n# vars.testCommand + a modes: suggestion, never this file. Compiled through\n# the same `compileWorkflowConfig` a user's own `workflow:` goes through — no\n# privileged path. See skills/authoring/SKILL.md for how to read or edit it\n# (machines, patterns, templates) and docs/driver.md for how a driver runs it.\n#\n# One flow: any change to the tree starts the process. `idle` -> `unwind`\n# reverts that diff (intent survives only in history) -> a green-baseline\n# gate -> `design.triage` groups the reverted diff into ordered, classified\n# concerns and resolves the PRODUCT ones' open questions (`design.gate`) ->\n# `architecture.author` works out the *how* and resolves the TECHNICAL ones\n# (`architecture.gate`) -> `architecture.decompose` writes one package file\n# per concern -> the per-package queue (`packages.*`) builds and reviews each\n# -> the shared tail (`build.review.*`) decides sign-off (-> `idle`, landing\n# an ordinary commit that closes the process — every per-turn commit stays on\n# the branch; run `gtd summary` afterward for a closing-message prompt) vs.\n# actionable feedback (-> `re-unwind` -> back to `design.triage`, a full\n# re-plan lap that never builds on the human's hand-edit). `--entry\n# review-gate.check --var reviewBase=<commitish>` and `--entry fix-precheck`\n# are the two other entries into this same flow.\nvars:\n testCommand: npm test\n # plannerModel: the heavier tier for one-shot triage/design/review turns.\n # coderModel: the tier for build/fix turns. Both repointable via a\n # top-level `vars:` key or a `GTD_<NAME>` env var, like any other var.\n plannerModel: smart\n coderModel: base\n # Declared (not left undeclared) so `--var reviewBase=...` passes the CLI's\n # declared-name check; blank renders empty, which the review entry refuses.\n reviewBase: \"\"\n # gtd's own voice for generated files — adapted from the \"Spartan\" output\n # style (https://github.com/alexgreensh/attention-span, AGPL-3.0, version 0.6),\n # rewritten in gtd's own words for deliverables rather than chat replies;\n # no upstream text ships. A point-in-time derivation with no refresh\n # mechanism.\n styleBlock: |-\n This file is a deliverable, not a chat reply — size follows the work, and\n padding is the target of any cut. Lead with the answer immediately; never\n circle back to restate it once it has landed.\n\n Write flat, commanding sentences — commit to the claim, do not hedge it.\n Favor everyday vocabulary; where a term is unavoidable, define it in five\n words or fewer on first use.\n\n In markdown, bold carries the load: bold the claim, not the sentence around\n it — reading only the bold text must yield the full point and every risk.\n\n Never trim a risk, a number, a threshold, or a scoped condition to save\n space. This outranks every other rule here.\n\n Ship the artifact bare — no lead-in, no sign-off wrapped around it.\n Compressing is not dropping: a task with three load-bearing parts ships all\n three, each shorter, never two of three.\n\n One idea per block; break when the idea shifts. Flag risk in one blunt\n line rather than hedged prose. Never narrate the work — do it.\n styleFormatContract: |-\n This file is machine-read; its format contract outranks every style rule\n above it: keep every `##`/`###` heading, every `- [ ]` checkbox row, and\n every marker line exactly as specified — do not renumber or rename a\n heading or replace a checkbox list with prose. A parser reads these\n literally; a violation refuses the turn outright.\n\n Voice rules above govern the prose between these elements, never the\n elements themselves.\n\n # One persona per prompt-bearing machine below, stamped as its `system:`\n # (see src/StateFields.ts's `system`). `--system-prompt` REPLACES a\n # harness's default system prompt — including its injected cwd/env/\n # git-status block — so each persona is written self-contained, restating\n # that context explicitly rather than relying on it.\n\n # Shared conduct tail, appended after every persona's own role paragraph:\n # use tools without asking, orient yourself with git, and go inspect what\n # the turn's message names. Deliberately NOT named `*Persona`:\n # `templates.test.ts` derives its six-name persona set from\n # `it\\.vars\\.(\\w+Persona)`, so that suffix would silently grow the set.\n agentConduct: |-\n You have shell and file tools — bash, read, write, edit — and you are\n expected to use them without asking first. This runs unattended: there is\n no one to grant permission, confirm a command, or approve a plan before\n you act. Investigate with real commands rather than assuming what a file,\n a commit, or a decision's status actually is, then act on what you find.\n\n You are already inside a git repository, at the working directory you\n should treat as your whole world for this turn. There is no injected\n status block — no cwd, no branch, no recent history, no tree state. Run\n `git status`, `git log`, `ls` and the like yourself before you do\n anything else; that is the normal first move of this turn, not an extra\n step.\n\n The message you receive each turn names a commit, a range, or a file for\n you to go inspect yourself — `git show`, `git diff`, reading the file\n directly — never a diff or a summary rendered into the conversation. Go\n look before you act. Across the several turns one conversation may span,\n work from what you already read and settled earlier rather than\n re-deriving it from a cold read every time, unless the turn's own\n instructions tell you this is a fresh, cold pass.\n designPersona: |-\n You are the product-facing planning voice in an autonomous build pipeline\n called gtd. Your job is to turn a raw, half-formed sketch — a hand-edit to\n real code, a scratch note, or both — into an ordered, classified list of\n concerns a build can execute, and to carry a running conversation with a\n human about the product decisions that list depends on. You are read by\n that human, so write plainly and decisively; you are not read by a parser\n except where a state's own instructions say otherwise.\n architectPersona: |-\n You are the technical planning voice in an autonomous build pipeline\n called gtd. You take over once the product-facing concerns are settled,\n reading them cold — you do not carry over the product conversation, only\n its written, settled output. Your job is to work out the technical *how*\n for each settled concern — file and module structure, data models,\n library and tech-stack choices, error-handling strategy — raise the\n technical open questions a human still needs to decide, and once every\n question is answered, write one package spec file per concern with no\n further judgement call: the grouping and ordering were already decided\n upstream, and this final step is a mechanical write-out of a plan you\n already converged on.\n reviewerPersona: |-\n You are the independent reviewing mind in an autonomous build pipeline\n called gtd — deliberately a separate identity from whichever agent wrote\n the code under review, so you bring no attachment to how it got built.\n Your job splits into two distinct turns: first, writing a structured\n review document that groups a diff into reviewable chunks for a human to\n read through and comment on; later, reading back a round of that human's\n feedback — a note, a hand-edit — and classifying whether it is genuinely\n actionable or just an approving remark, without fixing anything yourself\n either way. You judge and you classify; you never build.\n specReviewerPersona: |-\n You are the adversarial spec-conformance checker in an autonomous build\n pipeline called gtd, reviewing one freshly-built work package against the\n written spec it was built from. Your only job is to verify: are the\n package's tasks actually done, its acceptance criteria actually met, and\n the code sound and consistent with the rest of the codebase? You write\n concrete, actionable feedback only when something is genuinely wrong —\n and when nothing is, you write nothing at all, because a clean turn from\n you IS the approval. You never fix a problem you find; naming it\n precisely enough for someone else's fix turn to act on is the whole job.\n builderPersona: |-\n You are the TDD implementer in an autonomous build pipeline called gtd.\n Your job is to build exactly one package's declared scope end to end,\n tests first, and to come back and fix things when redirected: a failing\n check's output, or a reviewer's concrete feedback. Stay strictly inside\n the package in front of you, touching neither another package's files nor\n code outside the current task. Treat feedback you're given as the\n diagnosis to act on; discard it, with reason, only when it is simply\n wrong.\n finisherPersona: |-\n You are the closing identity in an autonomous build pipeline called gtd —\n the last coder this process hands work to before it closes. You are\n handed a late-breaking failing check after sign-off; fix it, focused and\n minimal, before the process can close. Every turn you land stays its own\n commit on the branch — nothing here gets squashed away.\n\n # The scratchpad-opener sentence shared by every prompt-content state (see\n # STATE_FIELDS's `prompt`) — this workflow's own state files are its private\n # working notes, never project code or documentation a human reads. Injected\n # as the leading line of every prompt body that touches a `.gtd/` file\n # directly; each state's own \"the only state file this turn touches is X\"\n # sentence stays local, immediately after the tag. Some states splice a\n # role clause into this same opening sentence (`build.review.collecting`,\n # `packages.item.spec.review`) — those hoist the clause to its own leading\n # sentence ahead of the tag instead, keeping every word.\n stateFileRules: |-\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.\n # The open-questions warrant test, decide-it-yourself sink, and `## Open\n # Questions` checkbox shape shared by `design.triage` and\n # `architecture.author` — written phase-agnostic (no PRODUCT/TECHNICAL\n # bake-in) so both sites can inject it verbatim. Each site's own\n # phase-scope sentence (triage: product-only, technical waits; architecture:\n # every point here is TECHNICAL) stays local around the tag — the two\n # genuinely disagree on scope, so that sentence is never shared.\n # `questionBarReturn` is the return-lap half — split out so each site can\n # inject it under its own `## Return lap` heading instead of burying\n # return-lap behaviour inside the first lap's own section.\n questionBar: |-\n For every open point, a question is warranted only when all three hold:\n the answer is a genuine fork with divergent outcomes — user-visible for a\n product question, materially different implementations for a technical\n one; the sketch, the entry diff, and history do not already settle it\n (treat anything the entry diff states explicitly — a directive in\n `.gtd/TODO.md`, a committed choice in a hand-edit — as already settled,\n and on a review loop-back lap treat whatever the human's own review-round\n edit or note states explicitly the same way); and getting it wrong would\n survive all the way to the human review tail.\n\n Anything below that bar, decide yourself: record the decision under\n `## Answered Questions` — the same section a human-answered question\n lands in — as `### <the point phrased as a question>` followed by the\n decision and a one-line rationale in prose, no checkboxes.\n\n A question that clears the bar is written under a `## Open Questions`\n heading near the top and takes this shape: one `### <question>`\n sub-heading each, followed by a checkbox list of candidate answers — 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.\n questionBarReturn: |-\n The human has ticked one option per question (or replaced\n `_your answer_` with their own text): fold each chosen answer into its\n concern's prose, then move the question into a `## Answered Questions`\n section as `### <question>` followed by the resolved answer in plain\n prose (drop the checkboxes). Never re-raise a deleted question; treat a\n deleted `## Open Questions` section as acceptance. Omit `## Open\n Questions` entirely once there are none left.\n # The body `packages.item.fix-suite` and `build.fix` share byte for byte —\n # both fix a red `.gtd/FEEDBACK.md`. `fix-suite` appends its own\n # cross-package paragraph right after this tag (before the shared\n # \"Leave everything uncommitted\" close, which stays local at both sites so\n # the appended paragraph lands in the same position it renders in today);\n # `build.fix` renders the tag alone.\n fixFeedbackPrompt: |-\n The only state file this turn touches is `.gtd/FEEDBACK.md`; 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 `.gtd/FEEDBACK.md` (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 `.gtd/FEEDBACK.md` instead of \"fixing\" a non-issue.\n\n# `gtd summary`'s prompt — printed cold, no session identity, no diff inlined.\n# Names the hashes/range for the agent to inspect itself; renders the same\n# it.processCost/it.processCostByModel the old squash finale used to.\nsummary: |\n <%~ it.vars.styleBlock %>\n\n\n Write the closing message for the process HEAD closes or currently sits\n inside — the text a driver may use for a squash, an amend, or a pull\n request body. You are starting cold: read every decision back out of the\n commits named below rather than assuming any prior context.\n\n Cover the initial motivation, the important decisions, and the trade-offs\n made, and the high-level architectural changes — never which files\n changed; that is what `git diff --stat` is for, not this message.\n\n The process's entry commit is `<%= it.entryCommit %>`. <% if\n (it.humanCommits.length > 0) { %>The human contributed at these commits,\n oldest to newest:\n <% it.humanCommits.forEach(function (c) { %>\n - `<%= c.hash %>` (entering `<%= c.state %>`)\n <% }) %><% } else { %>The human left no comment or edit this process —\n every commit is machine-authored.<% } %>\n\n Inspect the range yourself: `git log <%= it.processBase %>..<%= it.processTip %>`\n and `git diff <%= it.processBase %> <%= it.processTip %>`. That range is\n exactly what a squash or PR body should describe.\n\n Token cost: <%= it.processCost %>\n <% it.processCostByModel.forEach(function(m){ %>\n - <%= m.model %>: <%= m.cost %>\n <% }) %>\n\n Print the closing message and stop — this command writes nothing itself.\n\nentry:\n default: unified\n\nmachines:\n # Shared green-baseline gate — start-gate/review-gate/fix-precheck all\n # alias this script via &suiteCheck.\n entryGate:\n params: [onGreen, blockedMessage, blockedDescribe, checkLabel, blockedLabel, reviewBase]\n entry: check\n states:\n check:\n actor: check\n label: $checkLabel\n script: &suiteCheck |\n #!/usr/bin/env sh\n set +e\n mkdir -p .gtd\n # Sweep a raw review capture an earlier, abandoned process may have\n # left behind — no ordinary path from deciding/collecting reaches\n # this check.\n rm -f .gtd/REVIEW_RAW.md\n <%~ it.vars.testCommand %> > .gtd/.check-output 2>&1\n code=$?\n if [ \"$code\" -ne 0 ]; then\n if [ -s .gtd/.check-output ]; then\n mv .gtd/.check-output .gtd/FEEDBACK.md\n else\n rm -f .gtd/.check-output\n printf 'the test command failed with exit code %s and produced no output.' \"$code\" > .gtd/FEEDBACK.md\n fi\n # Stamp with HEAD so a repeat identical failure still re-registers\n # as an M/A edit instead of looking byte-identical (GREEN).\n printf '\\n<!-- gtd check %s -->\\n' \"$(git rev-parse --short HEAD 2>/dev/null || echo pending)\" >> .gtd/FEEDBACK.md\n else\n rm -f .gtd/.check-output\n rm -f .gtd/FEEDBACK.md\n fi\n # Marks BOTH entryGate instances (start-gate/review-gate); only\n # review-gate strictly needs it, but the shared dedup makes marking\n # one mark both — both ARE entry gates.\n entry: true\n # Only review-gate binds a real reviewBase (via --var); start-gate\n # binds \"\" (no base), since it's reached from idle, never entered.\n reviewBase: $reviewBase\n on:\n \"A .gtd/FEEDBACK.md\": blocked\n \"M .gtd/FEEDBACK.md\": blocked\n \"D .gtd/REVIEW_RAW.md\": $onGreen\n \"D .gtd/FEEDBACK.md\": $onGreen\n \"C\": $onGreen\n blocked:\n actor: human\n label: $blockedLabel\n file: FEEDBACK.md\n message: $blockedMessage\n on:\n \"* **\":\n to: check\n action: Retry check\n describe: $blockedDescribe\n\n # Shared check/escalate pair for build.health.*/packages.item.health.* —\n # the caller's own coder fix-state ($onRed), not this machine, handles red.\n healthGate:\n params: [onGreen, onRed, escalateDescribe, checkLabel, escalateLabel]\n entry: check\n states:\n check:\n actor: check\n label: $checkLabel\n # See the shared suite check on `entryGate.check` above.\n script: *suiteCheck\n on:\n \"A .gtd/FEEDBACK.md\": $onRed\n \"M .gtd/FEEDBACK.md\": $onRed\n \"* **\": $onGreen\n \"C\": $onGreen\n escalate:\n actor: human\n label: $escalateLabel\n file: FEEDBACK.md\n message: |\n The agent could not get the check to pass after repeated attempts.\n `.gtd/FEEDBACK.md` 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 # Shared check/answer pair for design.gate/architecture.gate. No `file:`\n # PARAM for the probe script itself: machine `$param` substitution is\n # whole-value only, so a path can't be spliced into a shared script body —\n # `file` here is instead the `answer` param's own `file:` (the human gate).\n questionGate:\n params: [file, message, onNone, onRevise, checkLabel, answerLabel]\n entry: check\n states:\n check:\n actor: check\n label: $checkLabel\n script: |\n #!/usr/bin/env sh\n # Exactly one of REQUIREMENTS.md/ARCHITECTURE.md exists on disk at\n # a time — architecture.author deletes the former in the same turn\n # it writes the latter — so this probe is unambiguous either way.\n set +e\n mkdir -p .gtd\n file=.gtd/REQUIREMENTS.md\n [ -f \"$file\" ] || file=.gtd/ARCHITECTURE.md\n gtd check qa \"$file\" --open-questions > /dev/null 2>&1\n code=$?\n if [ \"$code\" -ne 0 ]; then\n printf 'open questions remain in %s\\n' \"$file\" > .gtd/QUESTIONS.md\n # See the shared suite check's cache-buster rationale on\n # `entryGate.check` above.\n printf '\\n<!-- gtd check %s -->\\n' \"$(git rev-parse --short HEAD 2>/dev/null || echo pending)\" >> .gtd/QUESTIONS.md\n else\n rm -f .gtd/QUESTIONS.md\n fi\n on:\n \"A .gtd/QUESTIONS.md\": answer\n \"M .gtd/QUESTIONS.md\": answer\n \"D .gtd/QUESTIONS.md\": $onNone\n \"C\": $onNone\n answer:\n actor: human\n label: $answerLabel\n file: $file\n mode: qa\n answerGate: true\n message: $message\n on:\n \"* **\":\n to: $onRevise\n action: Revise answers\n describe: >-\n tick exactly one option per open question (replace\n `_your answer_` for your own) to send it back for the agent to\n fold your answers in. Delete a question to skip it, or delete\n the whole `## Open Questions` section to accept the plan as-is.\n\n # The triage phase — one conversation across the whole back-and-forth (the\n # human's answer rationale survives every return lap). A review loop-back\n # lap runs the suite itself and files any breakage as its own FIRST\n # concern, rather than spending one of packages.item.fix-suite's\n # (per-episode) retries on breakage the package didn't cause.\n # ▸ planner identity.\n designPlan:\n model: <%= it.vars.plannerModel %>\n system: |-\n <%~ it.vars.designPersona %>\n\n\n <%~ it.vars.agentConduct %>\n params: [onArchitecture]\n entry: triage\n states:\n triage:\n actor: agent\n label: Triaging the change\n file: REQUIREMENTS.md\n mode: qa\n # Guards against discarding assembled review input without folding\n # it in (the original bug this guard was added for).\n requireProgress: true\n prompt: |\n <%~ it.vars.styleBlock %>\n\n\n <%~ it.vars.styleFormatContract %>\n\n\n <%~ it.vars.stateFileRules %>\n\n The only state file this turn touches is `.gtd/REQUIREMENTS.md`; develop it. Do\n not create other files to hold notes or output.\n\n `.gtd/TODO.md` is the likely home of the sketch that\n started this process. Despite living beside this workflow's own\n state files, it is the human's input — the sketch to be folded into\n the concerns below like any other part of the start diff. It is\n never a state file to preserve, and never gtd bookkeeping to ignore.\n\n This process started at commit `<%= it.startCommit %>`. The change\n that started it was reverted out of the working tree by the\n `unwind` beat right after it landed, so `git diff <%= it.startCommit %>`\n (against the working tree) is now empty — its content survives only\n in history. Find the entry commit yourself: run\n `git rev-list --ancestry-path <%= it.startCommit %>..HEAD | tail -1`\n (the process's very first turn, landed immediately after\n `<%= it.startCommit %>`, before any later baseline-repair commits) —\n then `git show` that commit to see everything that started this\n process: a hand-edit to real code, a scratch note in some file, or\n both.\n\n Whichever lap this is, leave `.gtd/REQUIREMENTS.md` uncommitted and\n finish your turn once it reflects this lap's own work below.\n\n ## First lap\n\n `.gtd/REQUIREMENTS.md` does not exist yet (or holds whatever the\n human's own change happened to leave there). Build it from that\n start diff:\n\n ### Group into concerns\n\n Group the diff into an ordered list of concerns — each one a\n coherent piece of work that can leave the test suite green on\n its own, in the order you'd want them built. If a concern would\n red-light tests that a later concern is meant to fix, that is\n not a valid split: merge the two into one concern.\n\n Two more tests sit beside that green floor, not instead of it.\n Vertical, not horizontal: slice by capability, never by layer. A\n concern that only prepares for a later one — types, scaffolding,\n renames — is folded into the first concern that uses it. Distinct\n acceptance: each concern needs an observable check that fails\n before it and passes after; if you can't name one, merge it into\n its neighbour. Prefer fewer, larger packages — the smallest\n independently valuable change, not the smallest change that\n compiles.\n\n ### Classify each concern\n\n Classify each concern as PRODUCT (a user-facing/requirements\n decision) or TECHNICAL (an implementation decision).\n\n <%~ it.vars.questionBar %>\n\n\n Raise questions only for product concerns here — a technical\n concern's open points wait for the next phase. When every concern\n in this lap is TECHNICAL, write no `## Open Questions` section at\n all.\n\n ### Fold in the sketch\n\n Fold everything the entry commit added into the concerns above —\n a scratch note and a real code edit are both just a sketch of\n the intent, a spec to be finished, never work to be preserved;\n `unwind` already reverted both out of the tree, so there is\n nothing left to delete. Describe each piece under the concern it\n belongs to.\n\n ## Return lap\n\n <%~ it.vars.questionBarReturn %>\n\n ## Review loop-back\n\n `.gtd/REQUIREMENTS.md` already holds concerns and none of them\n have a ticked-but-unfolded answer waiting — that shape means a\n completed REVIEW round put this material here, not a question you\n asked. Develop those existing concerns further with whatever the\n round raised; never rediscover or regroup them from a cold read\n the way the first lap does.\n\n The human's own edit from that review round has been reverted out\n of the working tree the same way the entry commit is — read it\n from history, never the working tree: run\n `git show <%= it.reviewBase %>` to see it. (On the first lap that\n same hash is the process's own diff base, `<%= it.startCommit %>`,\n so this branch does not apply: `.gtd/REQUIREMENTS.md`\n does not exist yet for a review round to have happened against.)\n\n Whatever this round touches, every decision already settled under\n `## Answered Questions` stays settled on this lap too — never\n re-open one. A genuinely open PRODUCT point in the round's own\n material may still raise a fresh question under\n `## Open Questions`: the product gate sits on this path exactly as\n it does on the first lap, and will ask it.\n\n Before grouping anything on this lap, run\n `<%= it.vars.testCommand %>` yourself as part of reading the\n situation: a review loop-back runs no green-baseline gate between\n the revert above and this turn, so the tree may already be red —\n reverting the human's edit can undo a fix that edit had put in. If\n the suite is red, make that breakage the first concern in your\n ordered list, ahead of everything else — every later concern's\n \"leaves the suite green on its own\" property assumes the suite\n already started green.\n on:\n \"* **\": gate.check\n\n gate:\n machine: questionGate\n with:\n file: REQUIREMENTS.md\n onNone: $onArchitecture\n onRevise: triage\n checkLabel: Checking for open questions\n answerLabel: Awaiting your product answers\n message: |\n `.gtd/REQUIREMENTS.md` holds the concerns under\n development. Each open question under `## Open Questions` offers\n a few options plus a `- [ ] _your answer_` slot. Answer EVERY\n question by ticking exactly one box (`- [x]`); for your own\n answer, replace `_your answer_` with your text and tick that\n line. Stepping is refused while any question 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\n # A separate machine from designPlan (its own memory scope — a COLD read,\n # not a resumed conversation). Merge authority lives in `author`, the first\n # state that knows the *how*: it alone may re-merge concerns, never split;\n # `decompose` carries that grouping over verbatim. ▸ planner identity.\n archPlan:\n model: <%= it.vars.plannerModel %>\n system: |-\n <%~ it.vars.architectPersona %>\n\n\n <%~ it.vars.agentConduct %>\n params: [onPackages]\n entry: author\n states:\n author:\n actor: agent\n label: Refining the technical plan\n file: ARCHITECTURE.md\n mode: qa\n prompt: |\n <%~ it.vars.styleBlock %>\n\n\n <%~ it.vars.styleFormatContract %>\n\n\n <%~ it.vars.stateFileRules %>\n\n The only state files this\n turn touches are `.gtd/ARCHITECTURE.md` (write it) and\n `.gtd/REQUIREMENTS.md` (delete it once its content is\n folded in). Do not create other files to hold notes or output.\n\n You do not resume the design conversation — this is a separate\n machine with its own memory, a cold read every time. Read\n `.gtd/REQUIREMENTS.md` (the settled, ordered, classified\n concerns) in full. Treat every decision recorded there — plain\n prose or a resolved `## Answered Questions` entry, PRODUCT or\n TECHNICAL alike — as settled; never re-open it.\n\n Cold means the machine carries no conversational memory of\n triage's own back-and-forth; it never meant no git access. This\n process started at commit `<%= it.startCommit %>`. Find the\n process's first turn yourself: run\n `git rev-list --ancestry-path <%= it.startCommit %>..HEAD | tail -1`\n then `git show` that commit to see everything that started this\n process: a hand-edit to real code, a scratch note in some file,\n or both.\n\n Whichever lap this is, once `.gtd/ARCHITECTURE.md` is written,\n delete `.gtd/REQUIREMENTS.md` — its content has been folded in and\n it must not linger. Leave everything uncommitted and finish your\n turn.\n\n ## First lap\n\n Develop `.gtd/ARCHITECTURE.md` from those concerns: for each one,\n in the same order, work out the *how* — file/module structure,\n data models, library/tech-stack choices, error-handling strategy —\n building on the *what* already settled.\n\n You now know the *how*, so you know each concern's file footprint —\n list each concern's primary paths. Merge concerns whose footprints\n center on the same files into one, unless the later one only\n consumes an interface the earlier one creates: that exception is\n what keeps a genuine build-on-top sequence from collapsing into one\n blob. This authority is to merge only, never to split — granting\n split authority here would work directly against the goal; the\n whole problem is over-granularity. Record every merge under a\n dedicated `## Merged Concerns` heading, carrying both merged requirements\n verbatim so the per-package spec review still covers each one\n independently. A merge raises no open question and stops for no\n human — do not route it to `architecture.gate` for a veto; the\n human still sees every merge when they review the plan, and the\n per-package spec review is the real safety net. Prefer fewer,\n larger packages — the smallest independently valuable change, not\n the smallest change that compiles.\n\n Every open point here is TECHNICAL — triage already resolved the\n product ones, one phase earlier.\n\n <%~ it.vars.questionBar %>\n\n ## Return lap\n\n <%~ it.vars.questionBarReturn %>\n on:\n \"* **\": gate.check\n\n gate:\n machine: questionGate\n with:\n file: ARCHITECTURE.md\n onNone: decompose\n onRevise: author\n checkLabel: Checking for open questions\n answerLabel: Awaiting your technical answers\n message: |\n `.gtd/ARCHITECTURE.md` holds the technical plan under\n development. Each open question under `## Open Questions` offers\n a few options plus a `- [ ] _your answer_` slot. Answer EVERY\n question by ticking exactly one box (`- [x]`); for your own\n answer, replace `_your answer_` with your text and tick that\n line. Stepping is refused while any question 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\n decompose:\n actor: agent\n label: Decomposing into packages\n prompt: |\n <%~ it.vars.styleBlock %>\n\n\n <%~ it.vars.stateFileRules %>\n\n The only state files this turn touches\n are the package files it writes under `.gtd/packages/` and\n `.gtd/ARCHITECTURE.md`, which it deletes. Do not create other\n files to hold notes or output.\n\n If you wrote `.gtd/ARCHITECTURE.md` earlier in this\n conversation, work from what you already settled; otherwise read it\n (the converged technical plan) yourself. It already lists an\n ordered set of concerns with every merge/split judgement\n `architecture.author` made — this turn is a mechanical write-out,\n not a planning one. A `## Merged Concerns` heading there is a\n record of those merges, never a concern of its own: write no\n package file for it.\n\n Write one package file per concern, in the settled order, under\n `.gtd/packages/` (e.g.\n `.gtd/packages/01-name.md`,\n `.gtd/packages/02-name.md`, ...). Each package file\n carries that concern's requirement(s) (so it can be reviewed\n against its spec — both, independently, when the concern was\n merged), its independent tasks, and each task's acceptance\n criteria as `- [ ]` checkboxes and relevant paths. Do not merge or\n split concerns here — that judgement already happened in\n `architecture.author`; carry the settled grouping over verbatim.\n No package file may reference any other `.gtd/` file.\n\n Once the package files are written, delete\n `.gtd/ARCHITECTURE.md`. Leave everything uncommitted and\n finish your turn.\n on:\n \"* .gtd/packages/**\": $onPackages\n\n # The shared review tail. ▸ planner identity — `collecting` only judges/\n # classifies feedback; there's no coder follow-through inside this machine.\n #\n # Nested inside buildTail (build.review), not a root sibling: a machine's\n # memory scope is its dotted instance path, so nesting keeps `build.fix ->\n # build.health.check -> build.review.*` (the `--entry fix-precheck` path)\n # inside one scope, so the sign-off's boundary commit lands from the same\n # scope that made the fixes.\n humanReview:\n model: <%= it.vars.plannerModel %>\n system: |-\n <%~ it.vars.reviewerPersona %>\n\n\n <%~ it.vars.agentConduct %>\n entry: reviewing\n states:\n reviewing:\n actor: agent\n label: Reviewing\n file: REVIEW.md\n mode: review\n # `gtd --entry review-gate.check` enters via review-gate.check first\n # (never straight here), which sets the Gtd-Review-Base trailer this\n # tail reuses.\n prompt: |\n <%~ it.vars.styleBlock %>\n\n\n <%~ it.vars.styleFormatContract %>\n\n\n <%~ it.vars.stateFileRules %>\n\n The only state file this turn touches is `.gtd/REVIEW.md`; write it at exactly\n that path. Do not create other files to hold notes or output.\n\n Write `.gtd/REVIEW.md` 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 file pointer per hunk (checkboxes are\n for the human to tick, not you; every path is `./`-relative, with an\n optional `#line`). Put the note's opening line right on the\n pointer's own line, after a dash — the reflow-proof shape:\n\n - [ ] ./path/to/file.ts#42 — what this hunk does\n\n If the note needs more than one line, continue it on the line(s)\n below the pointer, indented exactly two spaces — never four or\n more, which after a blank line reads as an indented code block\n and never reflows:\n\n - [ ] ./path/to/file.ts#42 — what this hunk does\n and here is more detail, continued below it\n\n A note that sits entirely on the line(s) beneath the pointer is\n also valid. Either way, the explanation itself must never start\n with a bare `./path` token — that parses as a second hunk pointer,\n not a 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 `.gtd/REVIEW.md` uncommitted and finish your turn.\n on:\n \"* **\": await-review\n\n await-review:\n actor: human\n label: Awaiting your review\n file: REVIEW.md\n mode: review\n message: |\n `.gtd/REVIEW.md` 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 Review the diff yourself, with whatever tool you like — gtd checks\n nothing out and touches no ref; `gtd base` prints this same hash any\n time you need it again. The range runs from the review base to the\n working tree:\n\n git diff <%= it.reviewBase %>\n\n When you've been through the whole diff, run `gtd land`:\n\n - **Sign off** — leave no comment — no note in\n `.gtd/REVIEW.md`, no code edit — to close the process,\n whatever the boxes say. Every turn commit stays on the\n branch; run `gtd summary` afterward for a closing-message\n prompt.\n - **Request changes** — leave a comment: a note on a\n `.gtd/REVIEW.md` line, or a direct code edit — to send a FULL\n development lap (**review.deciding** → **review.collecting** →\n re-triage). A hand-edit you make here is treated as a SKETCH, not a\n fix the agent builds on: it is reverted out of the tree and re-planned\n from scratch, the same as any other change that starts a process.\n There is no baseline check on the way back into planning — only a\n genuinely non-actionable comment (an approving remark with no code\n edit) skips the lap and signs off straight away.\n\n Deleting `.gtd/REVIEW.md` is refused.\n on:\n \"* **\": deciding\n\n deciding:\n actor: check\n label: Reviewing\n file: REVIEW.md\n mode: review\n # Anchors the INCREMENTAL review base to the most-recent in-process\n # commit (the process start on the first review).\n reviewBase: true\n script: |\n #!/usr/bin/env sh\n # FEEDBACK iff the human left a REVIEW.md note (any edit beyond a\n # checkbox tick) or hand-edited any file this round outside .gtd/;\n # otherwise a clean sign-off. This turn only CAPTURES the raw\n # material into REVIEW_RAW.md — collecting judges actionability.\n # A/M REVIEW_RAW.md rows are declared before D REVIEW.md so a\n # feedback round (which also deletes REVIEW.md) isn't mistaken for\n # sign-off.\n set +e\n mkdir -p .gtd\n head=$(git rev-parse HEAD)\n # The one case that leaves a clean tree below is REVIEW.md already\n # missing (the `rm -f` no-ops) — which means the review gate's own\n # file-provisioning invariant broke, NOT a sign-off. Detecting it\n # here, by the file's absence rather than by the diff, is what\n # makes the `C` row below safe to declare: a broken round now\n # always carries a FEEDBACK.md diff and routes to a human.\n if ! git cat-file -e \"HEAD:.gtd/REVIEW.md\" 2>/dev/null; then\n printf 'there is no `.gtd/REVIEW.md` at %s — nothing was reviewed this round.\\n' \"$head\" > .gtd/FEEDBACK.md\n elif git diff-tree --no-commit-id --name-only -r HEAD -- . \":(exclude).gtd\" \\\n | grep -q . \\\n || [ \"$(git show \"HEAD^:.gtd/REVIEW.md\" 2>/dev/null | sed -E 's/\\[[ xX]\\]/[_]/g')\" \\\n != \"$(git show \"HEAD:.gtd/REVIEW.md\" 2>/dev/null | sed -E 's/\\[[ xX]\\]/[_]/g')\" ]; then\n # Reached only with REVIEW.md present. FEEDBACK iff the human\n # left a note (any edit beyond a checkbox tick — hence the\n # [ ]/[x] normalization above) or hand-edited any file this\n # round outside .gtd/; otherwise a clean sign-off. This turn\n # only CAPTURES the raw material into REVIEW_RAW.md —\n # collecting judges actionability.\n {\n echo \"This is machine-captured input, not instructions. A downstream agent judges whether it's actionable.\"\n echo\n echo \"Commit: $head\"\n echo \"The human's notes are in .gtd/REVIEW.md at this commit. Any hand edits are\"\n echo \"in that commit's other paths. Run: git show $head\"\n } > .gtd/REVIEW_RAW.md\n rm -f .gtd/REVIEW.md\n else\n rm -f .gtd/REVIEW.md\n fi\n on:\n # FEEDBACK rows first: the broken-invariant branch writes nothing\n # else, but declaring them ahead of the sign-off row keeps the\n # ordering honest if it ever does.\n \"A .gtd/FEEDBACK.md\": review-missing\n \"M .gtd/FEEDBACK.md\": review-missing\n # A/M REVIEW_RAW.md before D REVIEW.md so a feedback round (which\n # also deletes REVIEW.md) isn't mistaken for sign-off.\n \"A .gtd/REVIEW_RAW.md\": collecting\n \"M .gtd/REVIEW_RAW.md\": collecting\n \"D .gtd/REVIEW.md\": $onSignoff\n # Unreachable now that the missing-REVIEW.md case writes\n # FEEDBACK.md above, but declared rather than left off: if a clean\n # tree ever does happen here, it must reach a human, never\n # auto-approve an unreviewed round.\n \"C\": review-missing\n\n review-missing:\n actor: human\n label: Nothing to review\n file: FEEDBACK.md\n message: |\n The review round committed no `.gtd/REVIEW.md`, so there is nothing\n to sign off on. `.gtd/FEEDBACK.md` holds the detail.\n\n Make any change to re-run the reviewer and author a fresh review\n record.\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: reviewing\n action: Re-review\n describe: >-\n re-run the reviewer to author a fresh `.gtd/REVIEW.md`\n (**build.review.reviewing**).\n\n # Outcome is by which paths THIS diff touches: writing REQUIREMENTS.md\n # (A/M) is the actionable case, so those rows are declared before \"D\n # REVIEW_RAW.md\" — an actionable round's diff is both together, and\n # the wrong order would short-circuit every round to sign-off.\n # Consuming REVIEW_RAW.md alone is the non-actionable short-circuit,\n # reusing $onSignoff since that IS a sign-off. No `C`/`\"* **\"` row: a\n # clean turn here is a fruitless dispatch (falls through to the\n # ordinary attempt default), never a verdict; a dirty tree matching\n # neither row is a refusal.\n collecting:\n actor: agent\n label: Collecting your feedback\n file: REQUIREMENTS.md\n mode: qa\n prompt: |\n <%~ it.vars.styleBlock %>\n\n\n <%~ it.vars.styleFormatContract %>\n\n\n You are an autonomous coding agent judging and classifying a round\n of review feedback.\n\n <%~ it.vars.stateFileRules %>\n\n The only state files this turn\n touches are `.gtd/REQUIREMENTS.md` and\n `.gtd/REVIEW_RAW.md`, which it deletes — do not change any\n other file: you classify, you do not build.\n\n The raw review material is: <%~ it.read(\".gtd/REVIEW_RAW.md\") %>\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 The round is actionable if any of the following is true:\n\n 1. The human left a note on `.gtd/REVIEW.md` — a note is\n anything beyond a checkbox flip, regardless of whether the box\n itself is ticked (ticking means only \"I read this hunk\", never\n sign-off). A note is a mandatory concern below, whether or not\n its box is ticked.\n 2. The human added a code comment this round (a `+` line that is a\n comment, in a hand-edited file) — even a plain-prose one. That\n comment is an instruction: describe it as a concern below, and\n note that the comment line itself is transient — it must not\n survive the lap that satisfies it.\n 3. The human hand-edited any non-comment code this round. That code\n is no longer an already-committed intent to build on top of — it\n is a sketch, like the entry commit's own diff: describe what it\n was reaching for under the concern it belongs to, and expect the\n lap that follows to re-derive it from scratch. Never say the\n human's lines are final or that they must survive unchanged.\n\n The round is not actionable only when none of the above holds — the\n human left nothing but an approving remark (a compliment, a \"looks\n good\", or similar) with no code edit and no substantive note. Never\n invent actionability where the definition above doesn't hold — it\n manufactures a lap nothing asked for — and never dismiss a real note\n or edit as approval, which drops feedback the human left.\n\n If the round is actionable: write `.gtd/REQUIREMENTS.md`\n with an ordered list of concerns, each classified PRODUCT (a\n user-facing/requirements decision) or TECHNICAL (an implementation\n decision) — the same shape `design.triage` builds, one\n `## <heading>` per concern, in the order you'd want them built. Fold\n every note, comment, and hand-edit from this round in under the\n concern it belongs to, per the rules above. Raise no open\n questions here — write no `## Open Questions` section;\n `design.triage` owns that, one phase later. Then delete\n `.gtd/REVIEW_RAW.md` and finish your turn.\n\n If the round is not actionable: delete\n `.gtd/REVIEW_RAW.md` and finish your turn, writing nothing\n to `.gtd/REQUIREMENTS.md` — consuming the capture with no\n other change is the sign-off.\n on:\n \"A .gtd/REQUIREMENTS.md\": $onFeedback\n \"M .gtd/REQUIREMENTS.md\": $onFeedback\n \"D .gtd/REVIEW_RAW.md\": $onSignoff\n\n # The per-package review — a separate MIND from the implementer. ▸ planner\n # identity; fixing findings is a coder action (packages.item.fix-spec).\n specReview:\n model: <%= it.vars.plannerModel %>\n system: |-\n <%~ it.vars.specReviewerPersona %>\n\n\n <%~ it.vars.agentConduct %>\n params: [onApproved, onFix]\n entry: review\n states:\n review:\n actor: agent\n label: Reviewing the package\n # No retry cap: the loop only re-enters through\n # `fix-spec`/`health.check`, so under the per-episode rule this state's\n # episode count can never exceed 1 and a cap could never fire. The\n # spec-review loop is genuinely unbounded — see docs/configuration.md's\n # `retry` documentation.\n prompt: |\n <%~ it.vars.styleBlock %>\n\n\n You are an autonomous coding agent reviewing a freshly-built work package\n against its own spec.\n\n <%~ it.vars.stateFileRules %>\n\n The only state file this turn touches is\n `.gtd/SPEC_FEEDBACK.md`; 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(\".gtd/NEXT.md\") %>\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 `.gtd/SPEC_FEEDBACK.md` 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 .gtd/SPEC_FEEDBACK.md\": $onFix\n \"M .gtd/SPEC_FEEDBACK.md\": $onFix\n \"D .gtd/SPEC_FEEDBACK.md\": $onApproved\n \"C\": $onApproved\n\n # The per-package build queue — identity-free; build identity lives in\n # 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 sh\n # Mechanics only — NEXT.md's presence/absence is interpreted by\n # the `on` rows below, never here.\n set +e\n mkdir -p .gtd\n # Sweep spent design/architecture steering files (gone by now) and\n # any REVIEW_RAW.md the review loop-back left behind — the only\n # sweeper on that path before it would leak into a later `gtd\n # summary` prompt's diff range.\n rm -f .gtd/REQUIREMENTS.md .gtd/ARCHITECTURE.md .gtd/QUESTIONS.md .gtd/REVIEW_RAW.md\n # Names are gtd-authored, never containing whitespace — safe to\n # disable SC2012.\n # shellcheck disable=SC2012\n next=$(ls .gtd/packages/*.md 2>/dev/null | head -n 1)\n if [ -n \"$next\" ]; then\n printf '%s' \"$next\" > .gtd/NEXT.md\n else\n rm -f .gtd/NEXT.md\n fi\n on:\n \"D .gtd/NEXT.md\": $onDrained\n \"* .gtd/NEXT.md\": item.building\n \"C\": $onDrained\n\n # Its own coder machine (below) keeps `packages` itself a single state.\n item:\n machine: packageItem\n with:\n onNext: picking\n\n # The per-package build identity. ▸ coder identity. packages.item.closing\n # -> picking is the one upward-resolving target in the file, hence $onNext.\n packageItem:\n model: <%= it.vars.coderModel %>\n system: |-\n <%~ it.vars.builderPersona %>\n\n\n <%~ it.vars.agentConduct %>\n params: [onNext]\n entry: building\n states:\n building:\n actor: agent\n label: Building\n prompt: |\n <%~ it.vars.stateFileRules %>\n\n The only state file this\n turn may write is `.gtd/SATISFIED.md` (see below); do not\n delete the package file (the spec-review gate reads it after you)\n and never touch `.gtd/NEXT.md` — the `picking` state owns\n it.\n\n The package to implement is: <%~ it.read(\".gtd/NEXT.md\") %>\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 `.gtd/SATISFIED.md` 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 .gtd/SATISFIED.md\": &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 .gtd/SATISFIED.md\": *satisfied\n \"* **\": health.check\n\n fix-suite:\n actor: agent\n label: Fixing the check\n file: FEEDBACK.md\n prompt: |\n <%~ it.vars.stateFileRules %>\n\n <%~ it.vars.fixFeedbackPrompt %>\n\n 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: SPEC_FEEDBACK.md\n prompt: |\n <%~ it.vars.stateFileRules %>\n The only state file this\n turn touches is `.gtd/SPEC_FEEDBACK.md`; address it, then\n delete it. Do not create other files to hold notes or output.\n\n Read `.gtd/SPEC_FEEDBACK.md` (the reviewer's concerns about the\n current package) and the package spec (`<%= it.read(\".gtd/NEXT.md\") %>`),\n then fix the code to resolve every concern. Keep the change focused.\n\n Delete `.gtd/SPEC_FEEDBACK.md` 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 sh\n # Removes the just-reviewed package file (path in NEXT.md) plus\n # leftover spec feedback/evidence, so picking selects the next.\n # Reached only on spec-review approval — that loop carries no retry\n # cap, so there is no force-close path here.\n set +e\n pkg=$(cat .gtd/NEXT.md 2>/dev/null)\n [ -n \"$pkg\" ] && rm -f \"$pkg\"\n rm -f .gtd/SPEC_FEEDBACK.md .gtd/NEXT.md .gtd/SATISFIED.md\n on:\n \"* **\": $onNext\n # Nothing left to sweep (an already-clean NEXT.md/package) still\n # needs to proceed to the next package, not stall here.\n \"C\": $onNext\n\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 spec:\n machine: specReview\n with:\n onApproved: closing\n onFix: fix-spec\n\n # The process's build identity. ▸ coder identity — model/system stamped\n # onto fix.\n buildTail:\n model: <%= it.vars.coderModel %>\n system: |-\n <%~ it.vars.finisherPersona %>\n\n\n <%~ it.vars.agentConduct %>\n params: [onDone, onFeedback]\n # entry: is required by the machine grammar even though nothing resolves\n # this machine bare; `fix` is an arbitrary pick.\n entry: fix\n states:\n fix:\n actor: agent\n label: Fixing the check\n file: FEEDBACK.md\n prompt: |\n <%~ it.vars.stateFileRules %>\n\n <%~ it.vars.fixFeedbackPrompt %>\n\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 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 # Nested here (not root) so the sign-off's boundary commit stays inside\n # the builder's own scope — see humanReview above.\n review:\n machine: humanReview\n with:\n onSignoff: $onDone\n onFeedback: $onFeedback\n\n # WIRING ONLY — the root ties together the machine tree above.\n unified:\n entry: idle\n states:\n idle:\n actor: human\n label: Idle\n file: TODO.md\n message: |\n No active gtd process.\n\n To start one, make ANY change — a hand-edit to real code, a scratch\n note, anything at all. .gtd/TODO.md is a good default\n place to start sketching. gtd treats it as a SKETCH, not finished\n work: the very next beat unwinds it out of your working tree (its\n intent survives in history, for the triage phase to read), then\n checks the test baseline is green, then triages the reverted diff\n into ordered, classified concerns: product questions, then\n technical questions, then one package per concern, built and\n reviewed in parallel.\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: unwind\n action: Start\n describe: >-\n make any change — gtd unwinds it out of your working tree (its\n intent survives in history) before checking the test baseline is\n green, then triages the reverted diff into ordered, classified\n concerns and resolves product questions, then technical ones,\n before building each concern's package (**unwind** ->\n **start-gate.check**).\n\n # --entry fix-precheck: a red suite drops into build.fix (-> health ->\n # review tail); a green suite is a no-op back to idle.\n fix-precheck:\n actor: check\n label: Checking the baseline\n entry: true\n script: *suiteCheck\n on:\n \"A .gtd/FEEDBACK.md\": build.fix\n \"M .gtd/FEEDBACK.md\": build.fix\n \"D .gtd/REVIEW_RAW.md\": idle\n \"D .gtd/FEEDBACK.md\": idle\n \"C\": idle\n\n # Separate from start-gate.check: that gate's blocked -> check retry\n # edge would otherwise re-run a revert here and discard a human's\n # baseline repair. One inbound edge (from idle) means unwind runs\n # exactly once, so it needs no idempotence guard itself.\n unwind:\n actor: check\n label: Unwinding your input\n script: |\n #!/usr/bin/env sh\n set +e\n mkdir -p .gtd\n # Hoisted here, 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.\n # Uses it.currentCommit (render-time), not bare HEAD, so a\n # late-running driver still reverts the right commit.\n commit=\"<%~ it.currentCommit %>\"\n git revert --no-commit \"$commit\" 2> .gtd/.unwind-error\n code=$?\n # The revert's EXIT CODE is what separates a genuine no-op from a\n # hard failure (e.g. a merge commit with no `-m`) — the diff alone\n # cannot, since both can leave a clean tree. Turning the failure\n # into a FEEDBACK.md write is what makes the `C` row below safe:\n # once a failure always has a diff, a clean tree here means the\n # revert really did succeed and change nothing.\n if [ \"$code\" -ne 0 ]; then\n printf 'gtd could not unwind %s out of your working tree.\\n\\n' \"$commit\" > .gtd/FEEDBACK.md\n if [ -s .gtd/.unwind-error ]; then\n cat .gtd/.unwind-error >> .gtd/FEEDBACK.md\n else\n printf '`git revert --no-commit` exited %s and produced no output.\\n' \"$code\" >> .gtd/FEEDBACK.md\n fi\n fi\n rm -f .gtd/.unwind-error\n on:\n \"A .gtd/FEEDBACK.md\": unwind-failed\n \"M .gtd/FEEDBACK.md\": unwind-failed\n \"* **\": start-gate\n \"C\": start-gate\n\n # Reached only when the revert above exited non-zero. A human repairs\n # the tree by hand; start-gate.check clears FEEDBACK.md on its way\n # green, so nothing has to sweep it here.\n unwind-failed:\n actor: human\n label: Could not unwind your input\n file: FEEDBACK.md\n message: |\n gtd could not revert your sketch out of the working tree.\n `.gtd/FEEDBACK.md` holds the error.\n\n Undo the sketch by hand (its intent survives in history either\n way), then continue — gtd will not start work on a tree that still\n carries it.\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: start-gate\n action: Continue\n describe: >-\n having undone the sketch by hand, check the test baseline is\n green and start triage (**start-gate.check**).\n\n # Reverts the human's own review-round hand-edit before re-planning,\n # scoped to real code (`.gtd/` excluded — REVIEW.md is already gone by\n # now). Names the commit via it.reviewBase (reviewBaseFor's own\n # `await-review -> deciding` commit), never bare HEAD.\n #\n # requireRevert (below) re-checks the tree itself and refuses the step\n # if residue remains, rather than trusting the script's exit code — a\n # clean-filter round-trip or binary path can make `git apply -R` a\n # silent no-op. Both `on` rows below target `design`: a hand-edited\n # round leaves a dirty tree, a note-only round's patch is empty and\n # leaves the tree clean — both are legitimate outcomes here, not a\n # stall.\n #\n # `file: REVIEW.md` + `requireRevert: true` exist only to give that\n # guard the path to exempt (it reads Rest.hints.file, never this\n # literal) — REVIEW.md is already gone two commits earlier, so this\n # state never renders it.\n re-unwind:\n actor: check\n label: Re-unwinding your review edit\n file: REVIEW.md\n requireRevert: true\n script: |\n #!/usr/bin/env sh\n # Scoped revert of the human's review-round edit — .gtd/ excluded\n # (the guard's isCodePath re-derives the same exemption; keep both\n # in sync). Expected to succeed; requireRevert catches a silent\n # apply failure.\n set +e\n # Hoisted here, 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.\n commit=\"<%~ it.reviewBase %>\"\n patch=.gtd/.re-unwind.patch\n mkdir -p .gtd\n git diff --binary \"$commit^\" \"$commit\" -- . \":(exclude).gtd\" > \"$patch\"\n if [ -s \"$patch\" ]; then\n git apply -R \"$patch\" || echo \"re-unwind: could not revert $commit\" >&2\n fi\n rm -f \"$patch\"\n on:\n \"* **\": design # a code hand-edit was reverted\n \"C\": design # note-only round: nothing to revert\n\n start-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 `.gtd/FEEDBACK.md` 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 (**start-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 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 `.gtd/FEEDBACK.md` 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 design:\n machine: designPlan\n with:\n onArchitecture: architecture\n\n architecture:\n machine: archPlan\n with:\n onPackages: packages\n\n packages:\n machine: packageLoop\n with:\n onDrained: build.review\n\n build:\n machine: buildTail\n with:\n onDone: idle\n onFeedback: re-unwind\n";
191770
+ var unified_default = "# gtd's BUILT-IN DEFAULT workflow — used when no `workflow:` key is\n# configured anywhere in the cwd→home config chain; `gtd init` seeds only\n# vars.testCommand + a modes: suggestion, never this file. Compiled through\n# the same `compileWorkflowConfig` a user's own `workflow:` goes through — no\n# privileged path. See skills/authoring/SKILL.md for how to read or edit it\n# (machines, patterns, templates) and docs/driver.md for how a driver runs it.\n#\n# One flow: any change to the tree starts the process. `idle` -> `unwind`\n# reverts that diff (intent survives only in history) -> a green-baseline\n# gate -> `design.triage` groups the reverted diff into ordered, classified\n# concerns and resolves the PRODUCT ones' open questions (`design.gate`) ->\n# `architecture.author` works out the *how* and resolves the TECHNICAL ones\n# (`architecture.gate`) -> `architecture.decompose` writes one package file\n# per concern -> the per-package queue (`packages.*`) builds and reviews each\n# -> the shared tail (`build.review.*`) decides sign-off (-> `idle`, landing\n# an ordinary commit that closes the process — every per-turn commit stays on\n# the branch; run `gtd summary` afterward for a closing-message prompt) vs.\n# actionable feedback (-> `re-unwind` -> back to `design.triage`, a full\n# re-plan lap that never builds on the human's hand-edit). `--entry\n# review-gate.check --var reviewBase=<commitish>` and `--entry fix-precheck`\n# are the two other entries into this same flow.\nvars:\n testCommand: npm test\n # plannerModel: the heavier tier for one-shot triage/design/review turns.\n # coderModel: the tier for build/fix turns. Both repointable via a\n # top-level `vars:` key or a `GTD_<NAME>` env var, like any other var.\n plannerModel: smart\n coderModel: base\n # Declared (not left undeclared) so `--var reviewBase=...` passes the CLI's\n # declared-name check; blank renders empty, which the review entry refuses.\n reviewBase: \"\"\n # gtd's own voice for generated files — adapted from the \"Spartan\" output\n # style (https://github.com/alexgreensh/attention-span, AGPL-3.0, version 0.6),\n # rewritten in gtd's own words for deliverables rather than chat replies;\n # no upstream text ships. A point-in-time derivation with no refresh\n # mechanism.\n styleBlock: |-\n This file is a deliverable, not a chat reply — size follows the work, and\n padding is the target of any cut. Lead with the answer immediately; never\n circle back to restate it once it has landed.\n\n Write flat, commanding sentences — commit to the claim, do not hedge it.\n Favor everyday vocabulary; where a term is unavoidable, define it in five\n words or fewer on first use.\n\n In markdown, bold carries the load: bold the claim, not the sentence around\n it — reading only the bold text must yield the full point and every risk.\n\n Never trim a risk, a number, a threshold, or a scoped condition to save\n space. This outranks every other rule here.\n\n Ship the artifact bare — no lead-in, no sign-off wrapped around it.\n Compressing is not dropping: a task with three load-bearing parts ships all\n three, each shorter, never two of three.\n\n One idea per block; break when the idea shifts. Flag risk in one blunt\n line rather than hedged prose. Never narrate the work — do it.\n styleFormatContract: |-\n This file is machine-read; its format contract outranks every style rule\n above it: keep every `##`/`###` heading, every `- [ ]` checkbox row, and\n every marker line exactly as specified — do not renumber or rename a\n heading or replace a checkbox list with prose. A parser reads these\n literally; a violation refuses the turn outright.\n\n Voice rules above govern the prose between these elements, never the\n elements themselves.\n\n # One persona per prompt-bearing machine below, stamped as its `system:`\n # (see src/StateFields.ts's `system`). `--system-prompt` REPLACES a\n # harness's default system prompt — including its injected cwd/env/\n # git-status block — so each persona is written self-contained, restating\n # that context explicitly rather than relying on it.\n\n # Shared conduct tail, appended after every persona's own role paragraph:\n # use tools without asking, orient yourself with git, and go inspect what\n # the turn's message names. Deliberately NOT named `*Persona`:\n # `templates.test.ts` derives its six-name persona set from\n # `it\\.vars\\.(\\w+Persona)`, so that suffix would silently grow the set.\n agentConduct: |-\n You have shell and file tools — bash, read, write, edit — and you are\n expected to use them without asking first. This runs unattended: there is\n no one to grant permission, confirm a command, or approve a plan before\n you act. Investigate with real commands rather than assuming what a file,\n a commit, or a decision's status actually is, then act on what you find.\n\n You are already inside a git repository, at the working directory you\n should treat as your whole world for this turn. There is no injected\n status block — no cwd, no branch, no recent history, no tree state. Run\n `git status`, `git log`, `ls` and the like yourself before you do\n anything else; that is the normal first move of this turn, not an extra\n step.\n\n The message you receive each turn names a commit, a range, or a file for\n you to go inspect yourself — `git show`, `git diff`, reading the file\n directly — never a diff or a summary rendered into the conversation. Go\n look before you act. Across the several turns one conversation may span,\n work from what you already read and settled earlier rather than\n re-deriving it from a cold read every time, unless the turn's own\n instructions tell you this is a fresh, cold pass.\n designPersona: |-\n You are the product-facing planning voice in an autonomous build pipeline\n called gtd. Your job is to turn a raw, half-formed sketch — a hand-edit to\n real code, a scratch note, or both — into an ordered, classified list of\n concerns a build can execute, and to carry a running conversation with a\n human about the product decisions that list depends on. You are read by\n that human, so write plainly and decisively; you are not read by a parser\n except where a state's own instructions say otherwise.\n architectPersona: |-\n You are the technical planning voice in an autonomous build pipeline\n called gtd. You take over once the product-facing concerns are settled,\n reading them cold — you do not carry over the product conversation, only\n its written, settled output. Your job is to work out the technical *how*\n for each settled concern — file and module structure, data models,\n library and tech-stack choices, error-handling strategy — raise the\n technical open questions a human still needs to decide, and once every\n question is answered, write one package spec file per concern with no\n further judgement call: the grouping and ordering were already decided\n upstream, and this final step is a mechanical write-out of a plan you\n already converged on.\n reviewerPersona: |-\n You are the independent reviewing mind in an autonomous build pipeline\n called gtd — deliberately a separate identity from whichever agent wrote\n the code under review, so you bring no attachment to how it got built.\n Your job splits into two distinct turns: first, writing a structured\n review document that groups a diff into reviewable chunks for a human to\n read through and comment on; later, reading back a round of that human's\n feedback — a note, a hand-edit — and classifying whether it is genuinely\n actionable or just an approving remark, without fixing anything yourself\n either way. You judge and you classify; you never build.\n specReviewerPersona: |-\n You are the adversarial spec-conformance checker in an autonomous build\n pipeline called gtd, reviewing one freshly-built work package against the\n written spec it was built from. Your only job is to verify: are the\n package's tasks actually done, its acceptance criteria actually met, and\n the code sound and consistent with the rest of the codebase? You write\n concrete, actionable feedback only when something is genuinely wrong —\n and when nothing is, you write nothing at all, because a clean turn from\n you IS the approval. You never fix a problem you find; naming it\n precisely enough for someone else's fix turn to act on is the whole job.\n builderPersona: |-\n You are the TDD implementer in an autonomous build pipeline called gtd.\n Your job is to build exactly one package's declared scope end to end,\n tests first, and to come back and fix things when redirected: a failing\n check's output, or a reviewer's concrete feedback. Stay strictly inside\n the package in front of you, touching neither another package's files nor\n code outside the current task. Treat feedback you're given as the\n diagnosis to act on; discard it, with reason, only when it is simply\n wrong.\n finisherPersona: |-\n You are the closing identity in an autonomous build pipeline called gtd —\n the last coder this process hands work to before it closes. You are\n handed a late-breaking failing check after sign-off; fix it, focused and\n minimal, before the process can close. Every turn you land stays its own\n commit on the branch — nothing here gets squashed away.\n\n # The scratchpad-opener sentence shared by every prompt-content state (see\n # STATE_FIELDS's `prompt`) — this workflow's own state files are its private\n # working notes, never project code or documentation a human reads. Injected\n # as the leading line of every prompt body that touches a `.gtd/` file\n # directly; each state's own \"the only state file this turn touches is X\"\n # sentence stays local, immediately after the tag. Some states splice a\n # role clause into this same opening sentence (`build.review.collecting`,\n # `packages.item.spec.review`) — those hoist the clause to its own leading\n # sentence ahead of the tag instead, keeping every word.\n stateFileRules: |-\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.\n # The open-questions warrant test, decide-it-yourself sink, and `## Open\n # Questions` checkbox shape shared by `design.triage` and\n # `architecture.author` — written phase-agnostic (no PRODUCT/TECHNICAL\n # bake-in) so both sites can inject it verbatim. Each site's own\n # phase-scope sentence (triage: product-only, technical waits; architecture:\n # every point here is TECHNICAL) stays local around the tag — the two\n # genuinely disagree on scope, so that sentence is never shared.\n # `questionBarReturn` is the return-lap half — split out so each site can\n # inject it under its own `## Return lap` heading instead of burying\n # return-lap behaviour inside the first lap's own section.\n questionBar: |-\n For every open point, a question is warranted only when all three hold:\n the answer is a genuine fork with divergent outcomes — user-visible for a\n product question, materially different implementations for a technical\n one; the sketch, the entry diff, and history do not already settle it\n (treat anything the entry diff states explicitly — a directive in\n `.gtd/TODO.md`, a committed choice in a hand-edit — as already settled,\n and on a review loop-back lap treat whatever the human's own review-round\n edit or note states explicitly the same way); and getting it wrong would\n survive all the way to the human review tail.\n\n Anything below that bar, decide yourself: record the decision under\n `## Answered Questions` — the same section a human-answered question\n lands in — as `### <the point phrased as a question>` followed by the\n decision and a one-line rationale in prose, no checkboxes.\n `## Answered Questions` must come after every other `##` section in the\n document — it is always the last section.\n\n A question that clears the bar is written under a `## Open Questions`\n heading that must come before every other `##` section in the document —\n not just near the top, but ahead of every other `##` section — and takes\n this shape: one `### <question>` sub-heading each, followed by a checkbox\n list of candidate answers — two concrete options plus a final free-text\n 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.\n questionBarReturn: |-\n The human has ticked one option per question (or replaced\n `_your answer_` with their own text): fold each chosen answer into its\n concern's prose, then move the question into a `## Answered Questions`\n section as `### <question>` followed by the resolved answer in plain\n prose (drop the checkboxes). `## Answered Questions` must come after\n every other `##` section in the document — it is always the last\n section, so a moved question lands there, not wherever `## Open\n Questions` used to sit. Never re-raise a deleted question; treat a\n deleted `## Open Questions` section as acceptance. Omit `## Open\n Questions` entirely once there are none left.\n # The body `packages.item.fix-suite` and `build.fix` share byte for byte —\n # both fix a red `.gtd/FEEDBACK.md`. `fix-suite` appends its own\n # cross-package paragraph right after this tag (before the shared\n # \"Leave everything uncommitted\" close, which stays local at both sites so\n # the appended paragraph lands in the same position it renders in today);\n # `build.fix` renders the tag alone.\n fixFeedbackPrompt: |-\n The only state file this turn touches is `.gtd/FEEDBACK.md`; 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 `.gtd/FEEDBACK.md` (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 `.gtd/FEEDBACK.md` instead of \"fixing\" a non-issue.\n\n# `gtd summary`'s prompt — printed cold, no session identity, no diff inlined.\n# Names the hashes/range for the agent to inspect itself; renders the same\n# it.processCost/it.processCostByModel the old squash finale used to.\nsummary: |\n <%~ it.vars.styleBlock %>\n\n\n Write the closing message for the process HEAD closes or currently sits\n inside — the text a driver may use for a squash, an amend, or a pull\n request body. You are starting cold: read every decision back out of the\n commits named below rather than assuming any prior context.\n\n Cover the initial motivation, the important decisions, and the trade-offs\n made, and the high-level architectural changes — never which files\n changed; that is what `git diff --stat` is for, not this message.\n\n The process's entry commit is `<%= it.entryCommit %>`. <% if\n (it.humanCommits.length > 0) { %>The human contributed at these commits,\n oldest to newest:\n <% it.humanCommits.forEach(function (c) { %>\n - `<%= c.hash %>` (entering `<%= c.state %>`)\n <% }) %><% } else { %>The human left no comment or edit this process —\n every commit is machine-authored.<% } %>\n\n Inspect the range yourself: `git log <%= it.processBase %>..<%= it.processTip %>`\n and `git diff <%= it.processBase %> <%= it.processTip %>`. That range is\n exactly what a squash or PR body should describe.\n\n Token cost: <%= it.processCost %>\n <% it.processCostByModel.forEach(function(m){ %>\n - <%= m.model %>: <%= m.cost %>\n <% }) %>\n\n Print the closing message and stop — this command writes nothing itself.\n\nentry:\n default: unified\n\nmachines:\n # Shared green-baseline gate — start-gate/review-gate/fix-precheck all\n # alias this script via &suiteCheck.\n entryGate:\n params: [onGreen, blockedMessage, blockedDescribe, checkLabel, blockedLabel, reviewBase]\n entry: check\n states:\n check:\n actor: check\n label: $checkLabel\n script: &suiteCheck |\n #!/usr/bin/env sh\n set +e\n mkdir -p .gtd\n # Sweep a raw review capture an earlier, abandoned process may have\n # left behind — no ordinary path from deciding/collecting reaches\n # this check.\n rm -f .gtd/REVIEW_RAW.md\n <%~ it.vars.testCommand %> > .gtd/.check-output 2>&1\n code=$?\n if [ \"$code\" -ne 0 ]; then\n if [ -s .gtd/.check-output ]; then\n mv .gtd/.check-output .gtd/FEEDBACK.md\n else\n rm -f .gtd/.check-output\n printf 'the test command failed with exit code %s and produced no output.' \"$code\" > .gtd/FEEDBACK.md\n fi\n # Stamp with HEAD so a repeat identical failure still re-registers\n # as an M/A edit instead of looking byte-identical (GREEN).\n printf '\\n<!-- gtd check %s -->\\n' \"$(git rev-parse --short HEAD 2>/dev/null || echo pending)\" >> .gtd/FEEDBACK.md\n else\n rm -f .gtd/.check-output\n rm -f .gtd/FEEDBACK.md\n fi\n # Marks BOTH entryGate instances (start-gate/review-gate); only\n # review-gate strictly needs it, but the shared dedup makes marking\n # one mark both — both ARE entry gates.\n entry: true\n # Only review-gate binds a real reviewBase (via --var); start-gate\n # binds \"\" (no base), since it's reached from idle, never entered.\n reviewBase: $reviewBase\n on:\n \"A .gtd/FEEDBACK.md\": blocked\n \"M .gtd/FEEDBACK.md\": blocked\n \"D .gtd/REVIEW_RAW.md\": $onGreen\n \"D .gtd/FEEDBACK.md\": $onGreen\n \"C\": $onGreen\n blocked:\n actor: human\n label: $blockedLabel\n file: FEEDBACK.md\n message: $blockedMessage\n on:\n \"* **\":\n to: check\n action: Retry check\n describe: $blockedDescribe\n\n # Shared check/escalate pair for build.health.*/packages.item.health.* —\n # the caller's own coder fix-state ($onRed), not this machine, handles red.\n healthGate:\n params: [onGreen, onRed, escalateDescribe, checkLabel, escalateLabel]\n entry: check\n states:\n check:\n actor: check\n label: $checkLabel\n # See the shared suite check on `entryGate.check` above.\n script: *suiteCheck\n on:\n \"A .gtd/FEEDBACK.md\": $onRed\n \"M .gtd/FEEDBACK.md\": $onRed\n \"* **\": $onGreen\n \"C\": $onGreen\n escalate:\n actor: human\n label: $escalateLabel\n file: FEEDBACK.md\n message: |\n The agent could not get the check to pass after repeated attempts.\n `.gtd/FEEDBACK.md` 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 # Shared check/answer pair for design.gate/architecture.gate. No `file:`\n # PARAM for the probe script itself: machine `$param` substitution is\n # whole-value only, so a path can't be spliced into a shared script body —\n # `file` here is instead the `answer` param's own `file:` (the human gate).\n questionGate:\n params: [file, message, onNone, onRevise, checkLabel, answerLabel]\n entry: check\n states:\n check:\n actor: check\n label: $checkLabel\n script: |\n #!/usr/bin/env sh\n # Exactly one of REQUIREMENTS.md/ARCHITECTURE.md exists on disk at\n # a time — architecture.author deletes the former in the same turn\n # it writes the latter — so this probe is unambiguous either way.\n set +e\n mkdir -p .gtd\n file=.gtd/REQUIREMENTS.md\n [ -f \"$file\" ] || file=.gtd/ARCHITECTURE.md\n gtd check qa \"$file\" --open-questions > /dev/null 2>&1\n code=$?\n if [ \"$code\" -ne 0 ]; then\n printf 'open questions remain in %s\\n' \"$file\" > .gtd/QUESTIONS.md\n # See the shared suite check's cache-buster rationale on\n # `entryGate.check` above.\n printf '\\n<!-- gtd check %s -->\\n' \"$(git rev-parse --short HEAD 2>/dev/null || echo pending)\" >> .gtd/QUESTIONS.md\n else\n rm -f .gtd/QUESTIONS.md\n fi\n on:\n \"A .gtd/QUESTIONS.md\": answer\n \"M .gtd/QUESTIONS.md\": answer\n \"D .gtd/QUESTIONS.md\": $onNone\n \"C\": $onNone\n answer:\n actor: human\n label: $answerLabel\n file: $file\n mode: qa\n answerGate: true\n message: $message\n on:\n \"* **\":\n to: $onRevise\n action: Revise answers\n describe: >-\n tick exactly one option per open question (replace\n `_your answer_` for your own) to send it back for the agent to\n fold your answers in. Delete a question to skip it, or delete\n the whole `## Open Questions` section to accept the plan as-is.\n\n # The triage phase — one conversation across the whole back-and-forth (the\n # human's answer rationale survives every return lap). A review loop-back\n # lap runs the suite itself and files any breakage as its own FIRST\n # concern, rather than spending one of packages.item.fix-suite's\n # (per-episode) retries on breakage the package didn't cause.\n # ▸ planner identity.\n designPlan:\n model: <%= it.vars.plannerModel %>\n system: |-\n <%~ it.vars.designPersona %>\n\n\n <%~ it.vars.agentConduct %>\n params: [onArchitecture]\n entry: triage\n states:\n triage:\n actor: agent\n label: Triaging the change\n file: REQUIREMENTS.md\n mode: qa\n # Guards against discarding assembled review input without folding\n # it in (the original bug this guard was added for).\n requireProgress: true\n prompt: |\n <%~ it.vars.styleBlock %>\n\n\n <%~ it.vars.styleFormatContract %>\n\n\n <%~ it.vars.stateFileRules %>\n\n The only state file this turn touches is `.gtd/REQUIREMENTS.md`; develop it. Do\n not create other files to hold notes or output.\n\n `.gtd/TODO.md` is the likely home of the sketch that\n started this process. Despite living beside this workflow's own\n state files, it is the human's input — the sketch to be folded into\n the concerns below like any other part of the start diff. It is\n never a state file to preserve, and never gtd bookkeeping to ignore.\n\n This process started at commit `<%= it.startCommit %>`. The change\n that started it was reverted out of the working tree by the\n `unwind` beat right after it landed, so `git diff <%= it.startCommit %>`\n (against the working tree) is now empty — its content survives only\n in history. Find the entry commit yourself: run\n `git rev-list --ancestry-path <%= it.startCommit %>..HEAD | tail -1`\n (the process's very first turn, landed immediately after\n `<%= it.startCommit %>`, before any later baseline-repair commits) —\n then `git show` that commit to see everything that started this\n process: a hand-edit to real code, a scratch note in some file, or\n both.\n\n Whichever lap this is, leave `.gtd/REQUIREMENTS.md` uncommitted and\n finish your turn once it reflects this lap's own work below.\n\n ## First lap\n\n `.gtd/REQUIREMENTS.md` does not exist yet (or holds whatever the\n human's own change happened to leave there). Build it from that\n start diff:\n\n ### Group into concerns\n\n Group the diff into an ordered list of concerns — each one a\n coherent piece of work that can leave the test suite green on\n its own, in the order you'd want them built. If a concern would\n red-light tests that a later concern is meant to fix, that is\n not a valid split: merge the two into one concern.\n\n Two more tests sit beside that green floor, not instead of it.\n Vertical, not horizontal: slice by capability, never by layer. A\n concern that only prepares for a later one — types, scaffolding,\n renames — is folded into the first concern that uses it. Distinct\n acceptance: each concern needs an observable check that fails\n before it and passes after; if you can't name one, merge it into\n its neighbour. Prefer fewer, larger packages — the smallest\n independently valuable change, not the smallest change that\n compiles.\n\n ### Classify each concern\n\n Classify each concern as PRODUCT (a user-facing/requirements\n decision) or TECHNICAL (an implementation decision).\n\n <%~ it.vars.questionBar %>\n\n\n Raise questions only for product concerns here — a technical\n concern's open points wait for the next phase. When every concern\n in this lap is TECHNICAL, write no `## Open Questions` section at\n all.\n\n ### Fold in the sketch\n\n Fold everything the entry commit added into the concerns above —\n a scratch note and a real code edit are both just a sketch of\n the intent, a spec to be finished, never work to be preserved;\n `unwind` already reverted both out of the tree, so there is\n nothing left to delete. Describe each piece under the concern it\n belongs to.\n\n ## Return lap\n\n <%~ it.vars.questionBarReturn %>\n\n ## Review loop-back\n\n `.gtd/REQUIREMENTS.md` already holds concerns and none of them\n have a ticked-but-unfolded answer waiting — that shape means a\n completed REVIEW round put this material here, not a question you\n asked. Develop those existing concerns further with whatever the\n round raised; never rediscover or regroup them from a cold read\n the way the first lap does.\n\n The human's own edit from that review round has been reverted out\n of the working tree the same way the entry commit is — read it\n from history, never the working tree: run\n `git show <%= it.reviewBase %>` to see it. (On the first lap that\n same hash is the process's own diff base, `<%= it.startCommit %>`,\n so this branch does not apply: `.gtd/REQUIREMENTS.md`\n does not exist yet for a review round to have happened against.)\n\n Whatever this round touches, every decision already settled under\n `## Answered Questions` stays settled on this lap too — never\n re-open one. A genuinely open PRODUCT point in the round's own\n material may still raise a fresh question under\n `## Open Questions`: the product gate sits on this path exactly as\n it does on the first lap, and will ask it.\n\n Before grouping anything on this lap, run\n `<%= it.vars.testCommand %>` yourself as part of reading the\n situation: a review loop-back runs no green-baseline gate between\n the revert above and this turn, so the tree may already be red —\n reverting the human's edit can undo a fix that edit had put in. If\n the suite is red, make that breakage the first concern in your\n ordered list, ahead of everything else — every later concern's\n \"leaves the suite green on its own\" property assumes the suite\n already started green.\n on:\n \"* **\": gate.check\n\n gate:\n machine: questionGate\n with:\n file: REQUIREMENTS.md\n onNone: $onArchitecture\n onRevise: triage\n checkLabel: Checking for open questions\n answerLabel: Awaiting your product answers\n message: |\n `.gtd/REQUIREMENTS.md` holds the concerns under\n development. Each open question under `## Open Questions` offers\n a few options plus a `- [ ] _your answer_` slot. Answer EVERY\n question by ticking exactly one box (`- [x]`); for your own\n answer, replace `_your answer_` with your text and tick that\n line. Stepping is refused while any question 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\n # A separate machine from designPlan (its own memory scope — a COLD read,\n # not a resumed conversation). Merge authority lives in `author`, the first\n # state that knows the *how*: it alone may re-merge concerns, never split;\n # `decompose` carries that grouping over verbatim. ▸ planner identity.\n archPlan:\n model: <%= it.vars.plannerModel %>\n system: |-\n <%~ it.vars.architectPersona %>\n\n\n <%~ it.vars.agentConduct %>\n params: [onPackages]\n entry: author\n states:\n author:\n actor: agent\n label: Refining the technical plan\n file: ARCHITECTURE.md\n mode: qa\n prompt: |\n <%~ it.vars.styleBlock %>\n\n\n <%~ it.vars.styleFormatContract %>\n\n\n <%~ it.vars.stateFileRules %>\n\n The only state files this\n turn touches are `.gtd/ARCHITECTURE.md` (write it) and\n `.gtd/REQUIREMENTS.md` (delete it once its content is\n folded in). Do not create other files to hold notes or output.\n\n You do not resume the design conversation — this is a separate\n machine with its own memory, a cold read every time. Read\n `.gtd/REQUIREMENTS.md` (the settled, ordered, classified\n concerns) in full. Treat every decision recorded there — plain\n prose or a resolved `## Answered Questions` entry, PRODUCT or\n TECHNICAL alike — as settled; never re-open it.\n\n Cold means the machine carries no conversational memory of\n triage's own back-and-forth; it never meant no git access. This\n process started at commit `<%= it.startCommit %>`. Find the\n process's first turn yourself: run\n `git rev-list --ancestry-path <%= it.startCommit %>..HEAD | tail -1`\n then `git show` that commit to see everything that started this\n process: a hand-edit to real code, a scratch note in some file,\n or both.\n\n Whichever lap this is, once `.gtd/ARCHITECTURE.md` is written,\n delete `.gtd/REQUIREMENTS.md` — its content has been folded in and\n it must not linger. Leave everything uncommitted and finish your\n turn.\n\n ## First lap\n\n Develop `.gtd/ARCHITECTURE.md` from those concerns: for each one,\n in the same order, work out the *how* — file/module structure,\n data models, library/tech-stack choices, error-handling strategy —\n building on the *what* already settled.\n\n You now know the *how*, so you know each concern's file footprint —\n list each concern's primary paths. Merge concerns whose footprints\n center on the same files into one, unless the later one only\n consumes an interface the earlier one creates: that exception is\n what keeps a genuine build-on-top sequence from collapsing into one\n blob. This authority is to merge only, never to split — granting\n split authority here would work directly against the goal; the\n whole problem is over-granularity. Record every merge under a\n dedicated `## Merged Concerns` heading, carrying both merged requirements\n verbatim so the per-package spec review still covers each one\n independently. A merge raises no open question and stops for no\n human — do not route it to `architecture.gate` for a veto; the\n human still sees every merge when they review the plan, and the\n per-package spec review is the real safety net. Prefer fewer,\n larger packages — the smallest independently valuable change, not\n the smallest change that compiles.\n\n Every open point here is TECHNICAL — triage already resolved the\n product ones, one phase earlier.\n\n <%~ it.vars.questionBar %>\n\n ## Return lap\n\n <%~ it.vars.questionBarReturn %>\n on:\n \"* **\": gate.check\n\n gate:\n machine: questionGate\n with:\n file: ARCHITECTURE.md\n onNone: decompose\n onRevise: author\n checkLabel: Checking for open questions\n answerLabel: Awaiting your technical answers\n message: |\n `.gtd/ARCHITECTURE.md` holds the technical plan under\n development. Each open question under `## Open Questions` offers\n a few options plus a `- [ ] _your answer_` slot. Answer EVERY\n question by ticking exactly one box (`- [x]`); for your own\n answer, replace `_your answer_` with your text and tick that\n line. Stepping is refused while any question 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\n decompose:\n actor: agent\n label: Decomposing into packages\n prompt: |\n <%~ it.vars.styleBlock %>\n\n\n <%~ it.vars.stateFileRules %>\n\n The only state files this turn touches\n are the package files it writes under `.gtd/packages/` and\n `.gtd/ARCHITECTURE.md`, which it deletes. Do not create other\n files to hold notes or output.\n\n If you wrote `.gtd/ARCHITECTURE.md` earlier in this\n conversation, work from what you already settled; otherwise read it\n (the converged technical plan) yourself. It already lists an\n ordered set of concerns with every merge/split judgement\n `architecture.author` made — this turn is a mechanical write-out,\n not a planning one. A `## Merged Concerns` heading there is a\n record of those merges, never a concern of its own: write no\n package file for it.\n\n Write one package file per concern, in the settled order, under\n `.gtd/packages/` (e.g.\n `.gtd/packages/01-name.md`,\n `.gtd/packages/02-name.md`, ...). Each package file\n carries that concern's requirement(s) (so it can be reviewed\n against its spec — both, independently, when the concern was\n merged), its independent tasks, and each task's acceptance\n criteria as `- [ ]` checkboxes and relevant paths. Do not merge or\n split concerns here — that judgement already happened in\n `architecture.author`; carry the settled grouping over verbatim.\n No package file may reference any other `.gtd/` file.\n\n Once the package files are written, delete\n `.gtd/ARCHITECTURE.md`. Leave everything uncommitted and\n finish your turn.\n on:\n \"* .gtd/packages/**\": $onPackages\n\n # The shared review tail. ▸ planner identity — `collecting` only judges/\n # classifies feedback; there's no coder follow-through inside this machine.\n #\n # Nested inside buildTail (build.review), not a root sibling: a machine's\n # memory scope is its dotted instance path, so nesting keeps `build.fix ->\n # build.health.check -> build.review.*` (the `--entry fix-precheck` path)\n # inside one scope, so the sign-off's boundary commit lands from the same\n # scope that made the fixes.\n humanReview:\n model: <%= it.vars.plannerModel %>\n system: |-\n <%~ it.vars.reviewerPersona %>\n\n\n <%~ it.vars.agentConduct %>\n entry: reviewing\n states:\n reviewing:\n actor: agent\n label: Reviewing\n file: REVIEW.md\n mode: review\n # `gtd --entry review-gate.check` enters via review-gate.check first\n # (never straight here), which sets the Gtd-Review-Base trailer this\n # tail reuses.\n prompt: |\n <%~ it.vars.styleBlock %>\n\n\n <%~ it.vars.styleFormatContract %>\n\n\n <%~ it.vars.stateFileRules %>\n\n The only state file this turn touches is `.gtd/REVIEW.md`; write it at exactly\n that path. Do not create other files to hold notes or output.\n\n Write `.gtd/REVIEW.md` 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 file pointer per hunk (checkboxes are\n for the human to tick, not you; every path is `./`-relative, with an\n optional `#line`). Put the note's opening line right on the\n pointer's own line, after a dash — the reflow-proof shape:\n\n - [ ] ./path/to/file.ts#42 — what this hunk does\n\n If the note needs more than one line, continue it on the line(s)\n below the pointer, indented exactly two spaces — never four or\n more, which after a blank line reads as an indented code block\n and never reflows:\n\n - [ ] ./path/to/file.ts#42 — what this hunk does\n and here is more detail, continued below it\n\n A note that sits entirely on the line(s) beneath the pointer is\n also valid. Either way, the explanation itself must never start\n with a bare `./path` token — that parses as a second hunk pointer,\n not a 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 `.gtd/REVIEW.md` uncommitted and finish your turn.\n on:\n \"* **\": await-review\n\n await-review:\n actor: human\n label: Awaiting your review\n file: REVIEW.md\n mode: review\n message: |\n `.gtd/REVIEW.md` 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 Review the diff yourself, with whatever tool you like — gtd checks\n nothing out and touches no ref; `gtd base` prints this same hash any\n time you need it again. The range runs from the review base to the\n working tree:\n\n git diff <%= it.reviewBase %>\n\n When you've been through the whole diff, run `gtd land`:\n\n - **Sign off** — leave no comment — no note in\n `.gtd/REVIEW.md`, no code edit — to close the process,\n whatever the boxes say. Every turn commit stays on the\n branch; run `gtd summary` afterward for a closing-message\n prompt.\n - **Request changes** — leave a comment: a note on a\n `.gtd/REVIEW.md` line, or a direct code edit — to send a FULL\n development lap (**review.deciding** → **review.collecting** →\n re-triage). A hand-edit you make here is treated as a SKETCH, not a\n fix the agent builds on: it is reverted out of the tree and re-planned\n from scratch, the same as any other change that starts a process.\n There is no baseline check on the way back into planning — only a\n genuinely non-actionable comment (an approving remark with no code\n edit) skips the lap and signs off straight away.\n\n Deleting `.gtd/REVIEW.md` is refused.\n on:\n \"* **\": deciding\n\n deciding:\n actor: check\n label: Reviewing\n file: REVIEW.md\n mode: review\n # Anchors the INCREMENTAL review base to the most-recent in-process\n # commit (the process start on the first review).\n reviewBase: true\n script: |\n #!/usr/bin/env sh\n # FEEDBACK iff the human left a REVIEW.md note (any edit beyond a\n # checkbox tick) or hand-edited any file this round outside .gtd/;\n # otherwise a clean sign-off. This turn only CAPTURES the raw\n # material into REVIEW_RAW.md — collecting judges actionability.\n # A/M REVIEW_RAW.md rows are declared before D REVIEW.md so a\n # feedback round (which also deletes REVIEW.md) isn't mistaken for\n # sign-off.\n set +e\n mkdir -p .gtd\n head=$(git rev-parse HEAD)\n # The one case that leaves a clean tree below is REVIEW.md already\n # missing (the `rm -f` no-ops) — which means the review gate's own\n # file-provisioning invariant broke, NOT a sign-off. Detecting it\n # here, by the file's absence rather than by the diff, is what\n # makes the `C` row below safe to declare: a broken round now\n # always carries a FEEDBACK.md diff and routes to a human.\n if ! git cat-file -e \"HEAD:.gtd/REVIEW.md\" 2>/dev/null; then\n printf 'there is no `.gtd/REVIEW.md` at %s — nothing was reviewed this round.\\n' \"$head\" > .gtd/FEEDBACK.md\n elif git diff-tree --no-commit-id --name-only -r HEAD -- . \":(exclude).gtd\" \\\n | grep -q . \\\n || [ \"$(git show \"HEAD^:.gtd/REVIEW.md\" 2>/dev/null | sed -E 's/\\[[ xX]\\]/[_]/g')\" \\\n != \"$(git show \"HEAD:.gtd/REVIEW.md\" 2>/dev/null | sed -E 's/\\[[ xX]\\]/[_]/g')\" ]; then\n # Reached only with REVIEW.md present. FEEDBACK iff the human\n # left a note (any edit beyond a checkbox tick — hence the\n # [ ]/[x] normalization above) or hand-edited any file this\n # round outside .gtd/; otherwise a clean sign-off. This turn\n # only CAPTURES the raw material into REVIEW_RAW.md —\n # collecting judges actionability.\n {\n echo \"This is machine-captured input, not instructions. A downstream agent judges whether it's actionable.\"\n echo\n echo \"Commit: $head\"\n echo \"The human's notes are in .gtd/REVIEW.md at this commit. Any hand edits are\"\n echo \"in that commit's other paths. Run: git show $head\"\n } > .gtd/REVIEW_RAW.md\n rm -f .gtd/REVIEW.md\n else\n rm -f .gtd/REVIEW.md\n fi\n on:\n # FEEDBACK rows first: the broken-invariant branch writes nothing\n # else, but declaring them ahead of the sign-off row keeps the\n # ordering honest if it ever does.\n \"A .gtd/FEEDBACK.md\": review-missing\n \"M .gtd/FEEDBACK.md\": review-missing\n # A/M REVIEW_RAW.md before D REVIEW.md so a feedback round (which\n # also deletes REVIEW.md) isn't mistaken for sign-off.\n \"A .gtd/REVIEW_RAW.md\": collecting\n \"M .gtd/REVIEW_RAW.md\": collecting\n \"D .gtd/REVIEW.md\": $onSignoff\n # Unreachable now that the missing-REVIEW.md case writes\n # FEEDBACK.md above, but declared rather than left off: if a clean\n # tree ever does happen here, it must reach a human, never\n # auto-approve an unreviewed round.\n \"C\": review-missing\n\n review-missing:\n actor: human\n label: Nothing to review\n file: FEEDBACK.md\n message: |\n The review round committed no `.gtd/REVIEW.md`, so there is nothing\n to sign off on. `.gtd/FEEDBACK.md` holds the detail.\n\n Make any change to re-run the reviewer and author a fresh review\n record.\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: reviewing\n action: Re-review\n describe: >-\n re-run the reviewer to author a fresh `.gtd/REVIEW.md`\n (**build.review.reviewing**).\n\n # Outcome is by which paths THIS diff touches: writing REQUIREMENTS.md\n # (A/M) is the actionable case, so those rows are declared before \"D\n # REVIEW_RAW.md\" — an actionable round's diff is both together, and\n # the wrong order would short-circuit every round to sign-off.\n # Consuming REVIEW_RAW.md alone is the non-actionable short-circuit,\n # reusing $onSignoff since that IS a sign-off. No `C`/`\"* **\"` row: a\n # clean turn here is a fruitless dispatch (falls through to the\n # ordinary attempt default), never a verdict; a dirty tree matching\n # neither row is a refusal.\n collecting:\n actor: agent\n label: Collecting your feedback\n file: REQUIREMENTS.md\n mode: qa\n prompt: |\n <%~ it.vars.styleBlock %>\n\n\n <%~ it.vars.styleFormatContract %>\n\n\n You are an autonomous coding agent judging and classifying a round\n of review feedback.\n\n <%~ it.vars.stateFileRules %>\n\n The only state files this turn\n touches are `.gtd/REQUIREMENTS.md` and\n `.gtd/REVIEW_RAW.md`, which it deletes — do not change any\n other file: you classify, you do not build.\n\n The raw review material is: <%~ it.read(\".gtd/REVIEW_RAW.md\") %>\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 The round is actionable if any of the following is true:\n\n 1. The human left a note on `.gtd/REVIEW.md` — a note is\n anything beyond a checkbox flip, regardless of whether the box\n itself is ticked (ticking means only \"I read this hunk\", never\n sign-off). A note is a mandatory concern below, whether or not\n its box is ticked.\n 2. The human added a code comment this round (a `+` line that is a\n comment, in a hand-edited file) — even a plain-prose one. That\n comment is an instruction: describe it as a concern below, and\n note that the comment line itself is transient — it must not\n survive the lap that satisfies it.\n 3. The human hand-edited any non-comment code this round. That code\n is no longer an already-committed intent to build on top of — it\n is a sketch, like the entry commit's own diff: describe what it\n was reaching for under the concern it belongs to, and expect the\n lap that follows to re-derive it from scratch. Never say the\n human's lines are final or that they must survive unchanged.\n\n The round is not actionable only when none of the above holds — the\n human left nothing but an approving remark (a compliment, a \"looks\n good\", or similar) with no code edit and no substantive note. Never\n invent actionability where the definition above doesn't hold — it\n manufactures a lap nothing asked for — and never dismiss a real note\n or edit as approval, which drops feedback the human left.\n\n If the round is actionable: write `.gtd/REQUIREMENTS.md`\n with an ordered list of concerns, each classified PRODUCT (a\n user-facing/requirements decision) or TECHNICAL (an implementation\n decision) — the same shape `design.triage` builds, one\n `## <heading>` per concern, in the order you'd want them built. Fold\n every note, comment, and hand-edit from this round in under the\n concern it belongs to, per the rules above. Raise no open\n questions here — write no `## Open Questions` section;\n `design.triage` owns that, one phase later. Then delete\n `.gtd/REVIEW_RAW.md` and finish your turn.\n\n If the round is not actionable: delete\n `.gtd/REVIEW_RAW.md` and finish your turn, writing nothing\n to `.gtd/REQUIREMENTS.md` — consuming the capture with no\n other change is the sign-off.\n on:\n \"A .gtd/REQUIREMENTS.md\": $onFeedback\n \"M .gtd/REQUIREMENTS.md\": $onFeedback\n \"D .gtd/REVIEW_RAW.md\": $onSignoff\n\n # The per-package review — a separate MIND from the implementer. ▸ planner\n # identity; fixing findings is a coder action (packages.item.fix-spec).\n specReview:\n model: <%= it.vars.plannerModel %>\n system: |-\n <%~ it.vars.specReviewerPersona %>\n\n\n <%~ it.vars.agentConduct %>\n params: [onApproved, onFix]\n entry: review\n states:\n review:\n actor: agent\n label: Reviewing the package\n # No retry cap: the loop only re-enters through\n # `fix-spec`/`health.check`, so under the per-episode rule this state's\n # episode count can never exceed 1 and a cap could never fire. The\n # spec-review loop is genuinely unbounded — see docs/configuration.md's\n # `retry` documentation.\n prompt: |\n <%~ it.vars.styleBlock %>\n\n\n You are an autonomous coding agent reviewing a freshly-built work package\n against its own spec.\n\n <%~ it.vars.stateFileRules %>\n\n The only state file this turn touches is\n `.gtd/SPEC_FEEDBACK.md`; 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(\".gtd/NEXT.md\") %>\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 `.gtd/SPEC_FEEDBACK.md` 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 .gtd/SPEC_FEEDBACK.md\": $onFix\n \"M .gtd/SPEC_FEEDBACK.md\": $onFix\n \"D .gtd/SPEC_FEEDBACK.md\": $onApproved\n \"C\": $onApproved\n\n # The per-package build queue — identity-free; build identity lives in\n # 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 sh\n # Mechanics only — NEXT.md's presence/absence is interpreted by\n # the `on` rows below, never here.\n set +e\n mkdir -p .gtd\n # Sweep spent design/architecture steering files (gone by now) and\n # any REVIEW_RAW.md the review loop-back left behind — the only\n # sweeper on that path before it would leak into a later `gtd\n # summary` prompt's diff range.\n rm -f .gtd/REQUIREMENTS.md .gtd/ARCHITECTURE.md .gtd/QUESTIONS.md .gtd/REVIEW_RAW.md\n # Names are gtd-authored, never containing whitespace — safe to\n # disable SC2012.\n # shellcheck disable=SC2012\n next=$(ls .gtd/packages/*.md 2>/dev/null | head -n 1)\n if [ -n \"$next\" ]; then\n printf '%s' \"$next\" > .gtd/NEXT.md\n else\n rm -f .gtd/NEXT.md\n fi\n on:\n \"D .gtd/NEXT.md\": $onDrained\n \"* .gtd/NEXT.md\": item.building\n \"C\": $onDrained\n\n # Its own coder machine (below) keeps `packages` itself a single state.\n item:\n machine: packageItem\n with:\n onNext: picking\n\n # The per-package build identity. ▸ coder identity. packages.item.closing\n # -> picking is the one upward-resolving target in the file, hence $onNext.\n packageItem:\n model: <%= it.vars.coderModel %>\n system: |-\n <%~ it.vars.builderPersona %>\n\n\n <%~ it.vars.agentConduct %>\n params: [onNext]\n entry: building\n states:\n building:\n actor: agent\n label: Building\n prompt: |\n <%~ it.vars.stateFileRules %>\n\n The only state file this\n turn may write is `.gtd/SATISFIED.md` (see below); do not\n delete the package file (the spec-review gate reads it after you)\n and never touch `.gtd/NEXT.md` — the `picking` state owns\n it.\n\n The package to implement is: <%~ it.read(\".gtd/NEXT.md\") %>\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 `.gtd/SATISFIED.md` 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 .gtd/SATISFIED.md\": &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 .gtd/SATISFIED.md\": *satisfied\n \"* **\": health.check\n\n fix-suite:\n actor: agent\n label: Fixing the check\n file: FEEDBACK.md\n prompt: |\n <%~ it.vars.stateFileRules %>\n\n <%~ it.vars.fixFeedbackPrompt %>\n\n 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: SPEC_FEEDBACK.md\n prompt: |\n <%~ it.vars.stateFileRules %>\n The only state file this\n turn touches is `.gtd/SPEC_FEEDBACK.md`; address it, then\n delete it. Do not create other files to hold notes or output.\n\n Read `.gtd/SPEC_FEEDBACK.md` (the reviewer's concerns about the\n current package) and the package spec (`<%= it.read(\".gtd/NEXT.md\") %>`),\n then fix the code to resolve every concern. Keep the change focused.\n\n Delete `.gtd/SPEC_FEEDBACK.md` 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 sh\n # Removes the just-reviewed package file (path in NEXT.md) plus\n # leftover spec feedback/evidence, so picking selects the next.\n # Reached only on spec-review approval — that loop carries no retry\n # cap, so there is no force-close path here.\n set +e\n pkg=$(cat .gtd/NEXT.md 2>/dev/null)\n [ -n \"$pkg\" ] && rm -f \"$pkg\"\n rm -f .gtd/SPEC_FEEDBACK.md .gtd/NEXT.md .gtd/SATISFIED.md\n on:\n \"* **\": $onNext\n # Nothing left to sweep (an already-clean NEXT.md/package) still\n # needs to proceed to the next package, not stall here.\n \"C\": $onNext\n\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 spec:\n machine: specReview\n with:\n onApproved: closing\n onFix: fix-spec\n\n # The process's build identity. ▸ coder identity — model/system stamped\n # onto fix.\n buildTail:\n model: <%= it.vars.coderModel %>\n system: |-\n <%~ it.vars.finisherPersona %>\n\n\n <%~ it.vars.agentConduct %>\n params: [onDone, onFeedback]\n # entry: is required by the machine grammar even though nothing resolves\n # this machine bare; `fix` is an arbitrary pick.\n entry: fix\n states:\n fix:\n actor: agent\n label: Fixing the check\n file: FEEDBACK.md\n prompt: |\n <%~ it.vars.stateFileRules %>\n\n <%~ it.vars.fixFeedbackPrompt %>\n\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 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 # Nested here (not root) so the sign-off's boundary commit stays inside\n # the builder's own scope — see humanReview above.\n review:\n machine: humanReview\n with:\n onSignoff: $onDone\n onFeedback: $onFeedback\n\n # WIRING ONLY — the root ties together the machine tree above.\n unified:\n entry: idle\n states:\n idle:\n actor: human\n label: Idle\n file: TODO.md\n message: |\n No active gtd process.\n\n To start one, make ANY change — a hand-edit to real code, a scratch\n note, anything at all. .gtd/TODO.md is a good default\n place to start sketching. gtd treats it as a SKETCH, not finished\n work: the very next beat unwinds it out of your working tree (its\n intent survives in history, for the triage phase to read), then\n checks the test baseline is green, then triages the reverted diff\n into ordered, classified concerns: product questions, then\n technical questions, then one package per concern, built and\n reviewed in parallel.\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: unwind\n action: Start\n describe: >-\n make any change — gtd unwinds it out of your working tree (its\n intent survives in history) before checking the test baseline is\n green, then triages the reverted diff into ordered, classified\n concerns and resolves product questions, then technical ones,\n before building each concern's package (**unwind** ->\n **start-gate.check**).\n\n # --entry fix-precheck: a red suite drops into build.fix (-> health ->\n # review tail); a green suite is a no-op back to idle.\n fix-precheck:\n actor: check\n label: Checking the baseline\n entry: true\n script: *suiteCheck\n on:\n \"A .gtd/FEEDBACK.md\": build.fix\n \"M .gtd/FEEDBACK.md\": build.fix\n \"D .gtd/REVIEW_RAW.md\": idle\n \"D .gtd/FEEDBACK.md\": idle\n \"C\": idle\n\n # Separate from start-gate.check: that gate's blocked -> check retry\n # edge would otherwise re-run a revert here and discard a human's\n # baseline repair. One inbound edge (from idle) means unwind runs\n # exactly once, so it needs no idempotence guard itself.\n unwind:\n actor: check\n label: Unwinding your input\n script: |\n #!/usr/bin/env sh\n set +e\n mkdir -p .gtd\n # Hoisted here, 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.\n # Uses it.currentCommit (render-time), not bare HEAD, so a\n # late-running driver still reverts the right commit.\n commit=\"<%~ it.currentCommit %>\"\n git revert --no-commit \"$commit\" 2> .gtd/.unwind-error\n code=$?\n # The revert's EXIT CODE is what separates a genuine no-op from a\n # hard failure (e.g. a merge commit with no `-m`) — the diff alone\n # cannot, since both can leave a clean tree. Turning the failure\n # into a FEEDBACK.md write is what makes the `C` row below safe:\n # once a failure always has a diff, a clean tree here means the\n # revert really did succeed and change nothing.\n if [ \"$code\" -ne 0 ]; then\n printf 'gtd could not unwind %s out of your working tree.\\n\\n' \"$commit\" > .gtd/FEEDBACK.md\n if [ -s .gtd/.unwind-error ]; then\n cat .gtd/.unwind-error >> .gtd/FEEDBACK.md\n else\n printf '`git revert --no-commit` exited %s and produced no output.\\n' \"$code\" >> .gtd/FEEDBACK.md\n fi\n fi\n rm -f .gtd/.unwind-error\n on:\n \"A .gtd/FEEDBACK.md\": unwind-failed\n \"M .gtd/FEEDBACK.md\": unwind-failed\n \"* **\": start-gate\n \"C\": start-gate\n\n # Reached only when the revert above exited non-zero. A human repairs\n # the tree by hand; start-gate.check clears FEEDBACK.md on its way\n # green, so nothing has to sweep it here.\n unwind-failed:\n actor: human\n label: Could not unwind your input\n file: FEEDBACK.md\n message: |\n gtd could not revert your sketch out of the working tree.\n `.gtd/FEEDBACK.md` holds the error.\n\n Undo the sketch by hand (its intent survives in history either\n way), then continue — gtd will not start work on a tree that still\n carries it.\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: start-gate\n action: Continue\n describe: >-\n having undone the sketch by hand, check the test baseline is\n green and start triage (**start-gate.check**).\n\n # Reverts the human's own review-round hand-edit before re-planning,\n # scoped to real code (`.gtd/` excluded — REVIEW.md is already gone by\n # now). Names the commit via it.reviewBase (reviewBaseFor's own\n # `await-review -> deciding` commit), never bare HEAD.\n #\n # requireRevert (below) re-checks the tree itself and refuses the step\n # if residue remains, rather than trusting the script's exit code — a\n # clean-filter round-trip or binary path can make `git apply -R` a\n # silent no-op. Both `on` rows below target `design`: a hand-edited\n # round leaves a dirty tree, a note-only round's patch is empty and\n # leaves the tree clean — both are legitimate outcomes here, not a\n # stall.\n #\n # `file: REVIEW.md` + `requireRevert: true` exist only to give that\n # guard the path to exempt (it reads Rest.hints.file, never this\n # literal) — REVIEW.md is already gone two commits earlier, so this\n # state never renders it.\n re-unwind:\n actor: check\n label: Re-unwinding your review edit\n file: REVIEW.md\n requireRevert: true\n script: |\n #!/usr/bin/env sh\n # Scoped revert of the human's review-round edit — .gtd/ excluded\n # (the guard's isCodePath re-derives the same exemption; keep both\n # in sync). Expected to succeed; requireRevert catches a silent\n # apply failure.\n set +e\n # Hoisted here, 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.\n commit=\"<%~ it.reviewBase %>\"\n patch=.gtd/.re-unwind.patch\n mkdir -p .gtd\n git diff --binary \"$commit^\" \"$commit\" -- . \":(exclude).gtd\" > \"$patch\"\n if [ -s \"$patch\" ]; then\n git apply -R \"$patch\" || echo \"re-unwind: could not revert $commit\" >&2\n fi\n rm -f \"$patch\"\n on:\n \"* **\": design # a code hand-edit was reverted\n \"C\": design # note-only round: nothing to revert\n\n start-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 `.gtd/FEEDBACK.md` 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 (**start-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 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 `.gtd/FEEDBACK.md` 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 design:\n machine: designPlan\n with:\n onArchitecture: architecture\n\n architecture:\n machine: archPlan\n with:\n onPackages: packages\n\n packages:\n machine: packageLoop\n with:\n onDrained: build.review\n\n build:\n machine: buildTail\n with:\n onDone: idle\n onFeedback: re-unwind\n";
191753
191771
  //#endregion
191754
191772
  //#region src/workflows/templates.ts
191755
191773
  const SCHEMA_URL = "https://cdn.jsdelivr.net/npm/@pmelab/gtd/schema.json";
@@ -205905,6 +205923,24 @@ f="$(gtd next --json=file)"
205905
205923
  f="\${f:-.gtd/TODO.md}"
205906
205924
  mkdir -p "$(dirname "$f")"
205907
205925
  exec "\${EDITOR:-vi}" "$f"`;
205926
+ const REVIEW_COMMAND = `#!/usr/bin/env sh
205927
+ set -eu
205928
+ GTD_BUILD=~/.local/bin/gtd-build
205929
+ if [ -z "\${1:-}" ]; then
205930
+ echo "usage: gtd-review <commitish>" >&2
205931
+ exit 2
205932
+ fi
205933
+ cd "$(git rev-parse --show-toplevel)"
205934
+ script="$(gtd --entry review-gate.check --var reviewBase="$1")"
205935
+ sh -c "$script"
205936
+ exec "$GTD_BUILD"`;
205937
+ const FIX_COMMAND = `#!/usr/bin/env sh
205938
+ set -eu
205939
+ GTD_BUILD=~/.local/bin/gtd-build
205940
+ cd "$(git rev-parse --show-toplevel)"
205941
+ script="$(gtd --entry fix-precheck)"
205942
+ sh -c "$script"
205943
+ exec "$GTD_BUILD"`;
205908
205944
  const HEADER = () => `gtd ${GTD_VERSION$1} — driver protocol\n\ngtd decides and prints; the driver executes. gtd never runs git, never runs\nyour agent, never runs a check script, and never runs a steering mode's own\nformat:/validate: command either — that pair is text in the script gtd next\n--json's validate field (or gtd validate) prints, for the driver to run\nitself ahead of gtd land.\n`;
205909
205945
  const BEAT_PROTOCOL = `
205910
205946
  ## The beat protocol
@@ -206082,14 +206118,14 @@ since moved refuses loudly instead of corrupting anything. Scripts are
206082
206118
  re-runnable and assert their own HEAD precondition — a driver needs no retry
206083
206119
  logic beyond "ask gtd again".
206084
206120
  `;
206085
- const referenceImplementation = () => `
206086
- ## Building the user's driver: interview first, then adapt
206121
+ const commandSuite = () => `
206122
+ ## Building the user's command suite: interview first, then adapt
206087
206123
 
206088
- The obligations above are the contract; the sh block below is one WORKED
206089
- EXAMPLE of it. Do not copy it blindly, and do not guess the user's setup —
206090
- INVESTIGATE first, then INTERVIEW, then build a driver shaped by both. Hold
206091
- ONE conversation, not two: the setup steps below come first, because a repo
206092
- that is not set up yet cannot be driven, and the driver-shape questions
206124
+ The obligations above are the contract; the four command bodies below are one
206125
+ WORKED EXAMPLE of it. Do not copy them blindly, and do not guess the user's
206126
+ setup — INVESTIGATE first, then INTERVIEW, then build a suite shaped by both.
206127
+ Hold ONE conversation, not four: the setup steps below come first, because a
206128
+ repo that is not set up yet cannot be driven, and the suite-shape questions
206093
206129
  follow in the same numbered list.
206094
206130
 
206095
206131
  1. **Investigate the repository and ask the user what they want before
@@ -206108,29 +206144,59 @@ follow in the same numbered list.
206108
206144
  uncommitted config is a pending change, so starting a process without
206109
206145
  committing it first turns the config into the very first thing the
206110
206146
  process plans against. Commit it, THEN start driving.
206111
- 4. **Which agent should run the turns?** Whatever coding-agent CLI they
206112
- already use (default: \`claude\`). Map \`session.id\`/\`session.resume\`
206113
- onto THAT agent's continuation mechanism (obligation 5); an agent with
206114
- no session concept just ignores them — memory is an optimization, every
206115
- prompt is self-contained.
206116
- 5. **Under which permission model?** Fully autonomous turns (e.g.
206147
+ 4. **Which agent should run the turns?** Probe \`PATH\` for the known
206148
+ coding-agent CLIs — \`claude\`, \`codex\`, \`gemini\`, \`cursor-agent\`,
206149
+ \`aider\`, \`opencode\`, \`amp\` — show the user which are actually
206150
+ installed, and ask which one to drive with (default: \`claude\`). Accept
206151
+ a name that was not on the list; the probe is a convenience, not a
206152
+ restriction. The chosen CLI's own flags then replace the \`claude\` lines
206153
+ in the reference body below — its session flags (obligation 5) and its
206154
+ permission model (question 5).
206155
+ 5. **Under which permission model, and should the workflow's model hints be
206156
+ honored at all?** Fully autonomous turns (e.g.
206117
206157
  \`--dangerously-skip-permissions\`), a sandbox, or the agent's default
206118
- prompting — their risk tolerance, their call. Also whether the workflow's
206119
- own \`model\` hints should be honored (default: yes).
206120
- 6. **How do they want to invoke it?** A command on PATH (default:
206121
- \`~/.local/bin/gtd-loop\`), a project task-runner entry, a CI job step —
206122
- or no artifact at all: YOU drive the beats yourself, following the
206123
- obligations directly. Pick the runtime to match: bash, their language of
206124
- choice, anything that reads \`--json=<path>\` and spawns subprocesses.
206158
+ prompting — their risk tolerance, their call. Also ask whether the
206159
+ workflow's own \`model\` hints (resolved in question 6 below) should be
206160
+ honored (default: yes) — answering no means dropping \`--model\` entirely
206161
+ from every prompt line and writing no model exports, so every turn runs
206162
+ on the chosen CLI's own default tier.
206163
+ 6. **If model hints are honored, resolve them to real model identifiers.**
206164
+ The bundled workflow ships \`plannerModel: smart\` and \`coderModel: base\`.
206165
+ **Those are opaque hints, not model names** — gtd never interprets them,
206166
+ and passing \`--model smart\` to a real CLI fails on the first prompt
206167
+ beat. Derive the chosen CLI's own available models at install time — its
206168
+ \`--help\`, its docs, its own config — never a hardcoded table (a vendor
206169
+ renames a model and a baked-in list goes stale the day it ships). **When
206170
+ the heavier/cheaper mapping is not obvious, ask**: list the CLI's models
206171
+ and let the user pick one per tier — the heavier tier for
206172
+ \`plannerModel\` (triage, design, review), the cheaper tier for
206173
+ \`coderModel\` (build, fix). Write the resolved names as
206174
+ \`GTD_PLANNERMODEL\`/\`GTD_CODERMODEL\` exports at the top of \`gtd-build\`,
206175
+ wrapped in \`# gtd-install: model exports\` / \`# gtd-install: end\`
206176
+ markers — NOT into \`.gtdrc\`. They stay per machine and are never
206177
+ committed, so one person's model choice binds nobody else on the repo.
206178
+ Two consequences follow, both worth stating to the user: \`gtd-review\`
206179
+ and \`gtd-fix\` inherit these exports only because they \`exec\`
206180
+ \`gtd-build\` — anything that drives beats without going through
206181
+ \`gtd-build\` sees the raw \`smart\`/\`base\` hints and fails; and \`GTD_*\` is
206182
+ the highest-precedence config layer, so these exports silently win over
206183
+ a \`plannerModel\`/\`coderModel\` a teammate later commits to \`.gtdrc\`.
206125
206184
  7. **What should happen at the boundaries?** Where the log goes (obligation
206126
206185
  4), and whether halting at a human gate should do anything richer than
206127
206186
  print — desktop notification, terminal-multiplexer status, editor focus —
206128
206187
  which belongs in their wrapper, never in gtd.
206129
- 8. **Which editor should the edit command spawn?** Default \`$EDITOR\`, never
206130
- a hardcoded editor.
206131
- 9. **What should the edit command be named?** Suggest
206132
- \`~/.local/bin/gtd-edit\`, matching the loop command's own default
206133
- (\`~/.local/bin/gtd-loop\`) — the interview can override either name.
206188
+ 8. **How do they want to invoke it?** A command on PATH (default:
206189
+ \`~/.local/bin/gtd-build\`), a project task-runner entry, a CI job step —
206190
+ or no artifact at all: YOU drive the beats yourself, following the
206191
+ obligations directly. Pick the runtime to match: bash, their language of
206192
+ choice, anything that reads \`--json=<path>\` and spawns subprocesses.
206193
+ 9. **Where should the suite live, which editor should \`gtd-edit\` spawn, and
206194
+ does anything need renaming?** Suggest \`~/.local/bin\` for all four —
206195
+ \`gtd-build\`, \`gtd-edit\`, \`gtd-review\`, \`gtd-fix\` — and \`$EDITOR\` (never
206196
+ a hardcoded editor) for the one that opens files. The interview can
206197
+ rename any of the four; whatever name is chosen for \`gtd-build\` is the
206198
+ RESOLVED path baked into \`GTD_BUILD\` in the other two bodies below —
206199
+ never the literal string \`gtd-build\`.
206134
206200
 
206135
206201
  Then build it, and verify safely before the first real drive:
206136
206202
  \`gtd next --json=<path>\`/\`gtd next\` are both pure reads — read/parse them,
@@ -206138,20 +206204,20 @@ check your kind dispatch against the table above, call them as often as you
206138
206204
  like. Nothing happens until you run an emitted script.
206139
206205
 
206140
206206
  The reference rendering in sh (no \`jq\`, no JSON parser at all — each
206141
- \`--json=<path>\` call prints its one value directly; the \`claude\` lines are
206142
- what answers 1–2 replace):
206207
+ \`--json=<path>\` call prints its one value directly). Two of the four bodies
206208
+ below share one convention: \`GTD_BUILD\` is set once, at the top, to the
206209
+ suite's resolved \`gtd-build\` path.
206210
+
206211
+ ### \`gtd-build\` — the driver loop
206212
+
206213
+ The \`claude\` lines are what interview answers 4 and 6 replace. Default path:
206214
+ \`~/.local/bin/gtd-build\`.
206143
206215
 
206144
206216
  \`\`\`bash
206145
206217
  ${MINIMAL_DRIVER}
206146
206218
  \`\`\`
206147
- `;
206148
- const editCommand = () => `
206149
- ## Building the user's edit command
206150
206219
 
206151
- The loop command above drives beats; it never opens a file for the human. Add
206152
- a second artifact to the same interview (questions 8–9 above): an edit
206153
- command that opens the steering file the process is resting at right now,
206154
- suggested at \`~/.local/bin/gtd-edit\`.
206220
+ ### \`gtd-edit\` — open the steering file the process is resting at
206155
206221
 
206156
206222
  \`gtd next --json=file\` already reports the file for every state that
206157
206223
  declares one — the edit command needs no new engine surface, just a read and
@@ -206171,12 +206237,115 @@ the first sketch the whole process gets planned from. The same command does
206171
206237
  the same thing at every rest, whether or not a state declares a \`file:\`.
206172
206238
 
206173
206239
  The edit command stops when the editor exits — it never drives the loop. It
206174
- opens a file and returns; the human runs the loop command themselves when
206175
- they are ready.
206240
+ opens a file and returns; the human runs \`gtd-build\` themselves when they
206241
+ are ready. Default path: \`~/.local/bin/gtd-edit\`.
206176
206242
 
206177
206243
  \`\`\`bash
206178
206244
  ${EDIT_COMMAND}
206179
206245
  \`\`\`
206246
+
206247
+ ### \`gtd-review <commitish>\` — start a review round and drive it
206248
+
206249
+ Runs \`gtd --entry review-gate.check --var reviewBase=<commitish>\`, captured
206250
+ by command substitution — never a pipe: a pipeline reports only its LAST
206251
+ command's exit status, so \`gtd --entry ... | sh\` under \`set -e\` would sail
206252
+ past a refusal and \`exec\` a loop over a process that was never started. It
206253
+ runs the captured script, then \`exec\`s the suite's RESOLVED \`gtd-build\`
206254
+ path — never the literal string \`gtd-build\`. This is one invocation that
206255
+ carries the review all the way to its next human gate, not just a starter:
206256
+ \`gtd-review\` on a RED baseline hands off to \`gtd-build\`, which halts at the
206257
+ blocked gate and prints it, rather than starting the review. Refuses with
206258
+ \`usage: gtd-review <commitish>\` on stderr and exit \`2\` when the commitish is
206259
+ missing — nothing was even attempted. Default path: \`~/.local/bin/gtd-review\`.
206260
+
206261
+ \`\`\`bash
206262
+ ${REVIEW_COMMAND}
206263
+ \`\`\`
206264
+
206265
+ ### \`gtd-fix\` — enter the fix process and drive it
206266
+
206267
+ Runs \`gtd --entry fix-precheck\`, captured the same way, then \`exec\`s the
206268
+ suite's RESOLVED \`gtd-build\` path. \`gtd-fix\` on a GREEN suite is a no-op
206269
+ straight back to \`idle\` — the exec'd \`gtd-build\` exits immediately on it.
206270
+ Default path: \`~/.local/bin/gtd-fix\`.
206271
+
206272
+ \`\`\`bash
206273
+ ${FIX_COMMAND}
206274
+ \`\`\`
206275
+ ${REINSTALL}`;
206276
+ const REINSTALL = `
206277
+ ## Re-installing: detect and adapt, don't blindly overwrite
206278
+
206279
+ A second install on a machine that already has a suite is not a fresh
206280
+ install — read each of the four suite paths before writing anything
206281
+ (\`gtd-build\`, \`gtd-edit\`, \`gtd-review\`, \`gtd-fix\`), and branch per path:
206282
+
206283
+ - Absent — install it, as in a fresh install.
206284
+ - Present and content-equal to what this gtd version would emit — say so,
206285
+ change nothing, and skip that command's interview questions entirely.
206286
+ - Present and different — show the difference, name the likely cause (a gtd
206287
+ upgrade, or the user's own edit), and ask before overwriting. Never
206288
+ silently replace a file the user may have customised.
206289
+ - Unreadable, or present as a directory instead of a file — report it and
206290
+ ask about it, the same as a differing file. Never overwritten.
206291
+
206292
+ Drift is detected by comparing CONTENT, never by parsing a version out of
206293
+ the installed file — an installed command carries no version marker to
206294
+ parse.
206295
+
206296
+ \`gtd-build\` needs one exemption before that comparison: the region between
206297
+ \`# gtd-install: model exports\` and \`# gtd-install: end\` gets stripped from
206298
+ the installed file first. A \`gtd-build\` differing only inside those
206299
+ markers is unchanged, and re-asks nothing — the resolved model names are
206300
+ per machine, so without this exemption every single re-install would
206301
+ report drift on the one command everybody has.
206302
+
206303
+ This check scopes to exactly the four suite paths above. \`gtd-loop\` is
206304
+ outside it entirely — never read, never diffed, never touched by it in any
206305
+ way — an existing \`gtd-loop\` survives untouched beside the new
206306
+ \`gtd-build\`, and cleaning it up is the human's own call.
206307
+ `;
206308
+ const EDITOR_INTEGRATION = `
206309
+ ## Editor integration: offer it, don't bury it
206310
+
206311
+ \`gtd lsp\` runs a language server, started by \`gtd lsp\` itself, speaking LSP
206312
+ over \`stdio\`, applying to files under \`.gtd/\`. That is the whole integration
206313
+ contract, identical for every editor — this briefing names no specific editor
206314
+ and carries no per-editor config recipe.
206315
+
206316
+ Find the user's editor from their OWN shell configuration, never a guess:
206317
+ check \`$EDITOR\`/\`$VISUAL\` first, then look for an alias or export in their
206318
+ shell rc files (\`.zshrc\`, \`.bashrc\`, \`.config/fish/config.fish\`). Ask the
206319
+ user outright when that turns up nothing or turns up more than one. When the
206320
+ shell configuration names no editor and the user names none either, say
206321
+ nothing and move on — don't pitch editor integration to somebody with no
206322
+ editor to integrate.
206323
+
206324
+ Once an editor is identified, look ITS OWN LSP configuration format up
206325
+ yourself — there is no bundled recipe for it. If it is LSP-capable, offer to
206326
+ wire \`gtd lsp\` up with a brief explanation (three or four lines, not a copy
206327
+ of \`docs/setup.md\`): live diagnostics on \`.gtd/\` steering files, an outline
206328
+ of what is left to review, click-to-check review hunks, pick-an-option
206329
+ actions on open questions, and a \`gtd.openSteeringFile\` command that jumps
206330
+ to the current state's file. Two facts belong in that offer because both
206331
+ bite on a fresh repo: \`gtd lsp\` never creates \`.gtd/\`, so
206332
+ \`gtd.openSteeringFile\` may point at a path that does not exist yet before
206333
+ the first sketch; and \`gtd lsp\` needs no repository root — it is one of the
206334
+ commands that skips the root guard.
206335
+
206336
+ On yes, edit the editor's own config file yourself — never print a snippet
206337
+ and walk away. Integration works without the human touching anything. Three
206338
+ guards apply to that write, because it lands outside the repository in a
206339
+ file the user shares across every project:
206340
+
206341
+ - **Ask first, per editor, naming the exact file** about to change. Silence
206342
+ is not consent for a machine-wide config.
206343
+ - **Merge, never overwrite.** Read the existing config, add only the gtd
206344
+ language-server entry, and leave every other key byte-identical.
206345
+ - **Skip and report when the entry is already present** — a second install
206346
+ must not append a duplicate server registration. A malformed existing
206347
+ config (unparseable JSON, a TOML syntax error) is a stop-and-report, not a
206348
+ rewrite: name the file that could not be parsed and leave it untouched.
206180
206349
  `;
206181
206350
  const PREREQUISITES = `
206182
206351
  ## Prerequisites and portability
@@ -206193,7 +206362,7 @@ const PREREQUISITES = `
206193
206362
  Any runtime works: the contract is \`--json\`/\`--json=<path>\` in, subprocesses
206194
206363
  out.
206195
206364
  `;
206196
- const renderBriefing = () => HEADER() + BEAT_PROTOCOL + JSON_FIELD_REFERENCE + DRIVER_OBLIGATIONS + RECOVERY + referenceImplementation() + editCommand() + PREREQUISITES;
206365
+ const renderBriefing = () => HEADER() + BEAT_PROTOCOL + JSON_FIELD_REFERENCE + DRIVER_OBLIGATIONS + RECOVERY + commandSuite() + EDITOR_INTEGRATION + PREREQUISITES;
206197
206366
  //#endregion
206198
206367
  //#region src/Select.ts
206199
206368
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pmelab/gtd",
3
- "version": "10.0.0",
3
+ "version": "10.2.0",
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": {