@drisp/cli 0.7.11 → 0.7.13

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.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runMachineRunners
3
- } from "./chunk-YHXTTSJE.js";
3
+ } from "./chunk-5TM2ARN3.js";
4
4
 
5
5
  // src/app/entry/runnerDaemon.ts
6
6
  if (process.argv.includes("--help")) {
@@ -13,4 +13,4 @@ if (process.argv.includes("--help")) {
13
13
  process.exitCode = 1;
14
14
  });
15
15
  }
16
- //# sourceMappingURL=chunk-RSJVWETP.js.map
16
+ //# sourceMappingURL=chunk-CD6EY4ZE.js.map
@@ -20847,6 +20847,7 @@ var DEFAULT_COMPLETION_MARKER = "<!-- WORKFLOW_COMPLETE -->";
20847
20847
  var DEFAULT_NEEDS_HUMAN_MARKER = "<!-- NEEDS_HUMAN";
20848
20848
  var LEGACY_BLOCKED_MARKER = "<!-- WORKFLOW_BLOCKED";
20849
20849
  var WAITING_ON_CHILDREN_MARKER = "<!-- WAITING_ON_CHILDREN -->";
20850
+ var NEEDS_ORCHESTRATOR_MARKER = "<!-- NEEDS_ORCHESTRATOR";
20850
20851
  var AWAITING_APPROVAL_MARKER = "<!-- AWAITING_APPROVAL -->";
20851
20852
  var DEFAULT_JOURNAL_PATH = ".athena/{sessionId}/journal.md";
20852
20853
  var LEGACY_JOURNAL_FILENAME = "tracker.md";
@@ -20865,6 +20866,7 @@ function needsHumanMarkersFor(markers) {
20865
20866
  .../* @__PURE__ */ new Set([
20866
20867
  loopNeedsHumanMarker(markers),
20867
20868
  DEFAULT_NEEDS_HUMAN_MARKER,
20869
+ NEEDS_ORCHESTRATOR_MARKER,
20868
20870
  LEGACY_BLOCKED_MARKER
20869
20871
  ])
20870
20872
  ];
@@ -20926,6 +20928,7 @@ function parseJournalState(content, markers = {}) {
20926
20928
  waitingOnChildren: terminalLine === WAITING_ON_CHILDREN_MARKER,
20927
20929
  awaitingApproval: terminalLine === AWAITING_APPROVAL_MARKER,
20928
20930
  needsHumanReason,
20931
+ ...matchedMarker === NEEDS_ORCHESTRATOR_MARKER ? { needsOrchestrator: true } : {},
20929
20932
  misplacedTerminalMarker: getMisplacedTerminalMarker(
20930
20933
  lines,
20931
20934
  completionMarker,
@@ -21237,8 +21240,22 @@ var BUILD_STRINGS = {
21237
21240
  mergeSignOff: "Your merge: this pull request is ready to merge at {sha}. Reply in this Thread to sign off on that commit, or say what to change.",
21238
21241
  notAGitHubRepository: "The project folder is not a git repository with a GitHub origin.",
21239
21242
  cycle: (identifiers) => `These issues block each other in Linear: ${identifiers.join(", ")}.`,
21240
- /** The comment the Orchestrator posts with its one Linear write, moving a merged issue to Done. */
21241
- repairComment: (prUrl, orchestratorRunId) => `Merged in ${prUrl}. Marked Done by Drisp Build ${orchestratorRunId}.`,
21243
+ /** The comment reconcile asks for once when an issue's pull request opens. */
21244
+ prComment: (prUrl) => `PR: ${prUrl}`,
21245
+ /**
21246
+ * The ask comment reconcile asks for once when a Run parks with `NEEDS_HUMAN` (`linearRules.md`, "Ask comment
21247
+ * format"): the park's first line is the ask, at most three more say what the agent tried. The Run line names the
21248
+ * step when the tick knows it.
21249
+ */
21250
+ askComment: (ask) => {
21251
+ const [first = "", ...tried] = ask.message.trim().split("\n");
21252
+ const step2 = ask.step ? ` \xB7 Step ${ask.step.index} of ${ask.step.total} \xB7 ${ask.step.title}` : "";
21253
+ return [
21254
+ `**Needs input:** ${first}`,
21255
+ `Run: ${ask.runId}${step2}`,
21256
+ ...tried.slice(0, 3)
21257
+ ].join("\n");
21258
+ },
21242
21259
  rebaseFailed: (branch) => `Rebasing ${branch} onto main needs a person.`,
21243
21260
  keyBusy: (identifier) => `Another Run is already working on ${identifier}.`,
21244
21261
  workflowUnavailable: (identifier) => `The Runner has not listed the Workflow for ${identifier} yet.`,
@@ -21264,7 +21281,7 @@ function tokens(count) {
21264
21281
  var GATES = [
21265
21282
  {
21266
21283
  title: "Explore",
21267
- passesWhen: "the issue, its acceptance criteria and the ADRs/CONTEXT it touches are read; `git status`, `git log origin/main..HEAD` and `gh pr list --head <branch> --state all` are read, and any existing commits, changes and open PR are treated as this Run's own work; the Linear issue is moved to In Progress",
21284
+ passesWhen: "the issue, its acceptance criteria and the ADRs/CONTEXT it touches are read; `git status`, `git log origin/main..HEAD` and `gh pr list --head <branch> --state all` are read, and any existing commits, changes and open PR are treated as this Run's own work",
21268
21285
  cannotPass: "Drisp could not read this issue in Linear. Check the Runner's Linear key, then Build again."
21269
21286
  },
21270
21287
  {
@@ -21283,7 +21300,7 @@ var GATES = [
21283
21300
  },
21284
21301
  {
21285
21302
  title: "Pull request",
21286
- passesWhen: "the branch is pushed with `git push -u origin <branch>`; the open PR for the branch is continued, else a new PR is opened with `gh pr create --base main` (a closed unmerged PR stays closed); a Linear comment `PR: <url>` is posted",
21303
+ passesWhen: "the branch is pushed with `git push -u origin <branch>`; the open PR for the branch is continued, else a new PR is opened with `gh pr create --base main` (a closed unmerged PR stays closed)",
21287
21304
  cannotPass: "Drisp could not push this branch or open its pull request. Check the Runner's GitHub login, then Build again."
21288
21305
  },
21289
21306
  {
@@ -21298,18 +21315,23 @@ var GATES = [
21298
21315
  },
21299
21316
  {
21300
21317
  title: "Evidence",
21301
- passesWhen: "the evidence comment is posted and the Linear issue is moved to Done",
21302
- cannotPass: "The pull request merged, but Drisp could not update Linear. Mark the issue Done in Linear."
21318
+ passesWhen: "the evidence comment is posted on the Linear issue",
21319
+ cannotPass: "The pull request merged, but Drisp could not post the evidence comment on the Linear issue. Post it there."
21303
21320
  }
21304
21321
  ];
21305
21322
 
21323
+ // src/core/build/linearRules.md
21324
+ var linearRules_default = '**Linear holds the gates, not the activity.** "Working on it" is not a Linear status; the app\'s Build word (Running \xB7\nStep n of m) carries that. **In Progress is derived, never hand-set:** its fact is a live Run on the issue, and\nreconcile is the only writer, so it cannot go stale. There is no Merged status: Merged is a Build word, never on\nLinear\'s state.\n\n**Statuses:** Backlog \u2192 Todo \u2192 In Progress \u2192 Review \u2192 QA \u2192 Ready \u2192 Released. Sideways: **Needs Input**. Terminal:\nCanceled, Duplicate. **Ready** is a status (type started, between QA and Released); `ready-for-agent` and\n`ready-for-human` are triage labels on a different axis. The team has no native Triage status; triage state is carried\nby labels.\n\n| Gate | Set when | Fact it is derived from | Applies to |\n| ----------- | ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- |\n| Todo | triaged `ready-for-agent`; no Run on it | no live Run, no PR | all |\n| In Progress | a Run on `issue:<id>` is live (or an interactive session has picked it up) | Hub: Run not closed, no PR | all |\n| Review | PR opened; `PR: <url>` comment once; stays until merged | `pull_request` artifact exists, not merged | all |\n| QA | merged to main and on staging; owned by the **QA Run** (`qa:<issueId>` child of the Orchestrator) | `pull_request.mergedAt`, plus the Hub `deployed` fact once `/version` exists; until then mergedAt alone, with one re-check after a short wait before any fail | web/Hub issues only (profile `qa: web` or `both`) |\n| Ready | QA passed (web) or merged (macOS and CLI, whose QA is the pre-merge step); awaiting the release decision | `qa` artifact passed, or mergedAt for non-web | all |\n| Released | in a Hub Release record (cross-Plan, manual) | Release membership | all |\n| Needs Input | the agent has tried all means and is blocked on a person (a `NEEDS_HUMAN` park), from any gate; one ask comment, format below | Run parked `awaiting_attention` | all |\n\n**QA fails (v1, limit-driven):** the QA Run posts the report comment; the issue returns to Todo; the Orchestrator\nstarts one more attempt on the same `issue:` key and branch (a second PR; the PR field shows the newest); re-QA after\nmerge; after 2 QA fails the issue goes to Needs Input. This amends the rule that failures found later become linked\nIssues, because the Linear issue limit is hit; revert to linked fix Issues when the plan is upgraded.\n\n**Resume from Needs Input:** back to the gate the facts imply. Nothing stores a "previous status".\n\n**Run closed without a PR** (stopped, failed, abandoned): back to Todo. Stopping a Build whose issues are in QA cancels\nthe QA Runs; merged issues stay in QA.\n\n**Born-human issues** are created in Needs Input with `ready-for-human`; only a person moves them.\n\n**Project status rule:**\n\n- Planned \u2192 In Progress: the first Run starts on any of its issues.\n- In Progress \u2192 Completed: every issue is Released or Canceled, and no open issue carries `ready-for-human` or is in\n Needs Input.\n- Completed \u2192 In Progress: any issue is reopened.\n- Backlog, Canceled and Paused are manual only.\n\n**Ask comment format** (for Needs Input):\n\n```\n**Needs input:** <one-line ask>\nRun: <run id> \xB7 Step <n> of <m> \xB7 <step name>\n<what the agent tried, at most 3 lines>\n```\n\n**Eligibility:** an issue is agent-ready only in Todo with `ready-for-agent`, never with `ready-for-human`,\n`needs-info`, `needs-triage` or `wontfix`, and never in Needs Input. An issue in In Progress, Review, QA or Ready is\nowned by a Run or awaiting a decision and is not re-scheduled. Sweep rule: no issue In Progress without a live Run.\n\n**The pre-merge step keeps its name QA** (a Build child\'s gate): step QA (pre-merge, in the worktree) and status QA\n(post-merge, on staging) are the same activity, walking acceptance criteria against running software, at two points.\n';
21325
+
21326
+ // src/core/build/linearRules.ts
21327
+ var LINEAR_RULES = linearRules_default.trim();
21328
+
21306
21329
  // src/core/workflows/builtins/orchestrator/workflow.md
21307
- var workflow_default = '# Orchestrator: plan and run a Build\n\nYou plan and run a Drisp Build of one Plan: the open issues of one Linear project. The Run\'s prompt names the\nLinear project, the Plan key and the project folder, which is your working directory. You never write code here:\neach issue is built by its own child Run, which `drisp build tick` starts.\n\nThe Runner sets these for you:\n\n- `DRISP_RUN_ARTIFACTS`: this Run\'s artifact directory. Files you write there are shown to the member.\n- `DRISP_RUN_ID`, `DRISP_RUNNER_ID`, `DRISP_RUN_TOKEN`, `DRISP_HUB_URL`: this Run, its Runner and its right to start\n child Runs; `drisp build` reads them itself.\n\n## 1. Read Linear (first Turn)\n\nWith the `linear` MCP server, read the project and every issue in it, then write\n`$DRISP_RUN_ARTIFACTS/input/plan-input.json` (create the `input` directory) in exactly this shape. Every issue carries\nevery field, empty when it has none. `readAt` is the output of\n`node -e "console.log(new Date().toISOString())"` run just before you read Linear; never write a time yourself:\n\n```json\n{\n "version": 1,\n "readAt": "<the command\'s output, e.g. 2026-10-11T09:30:00.000Z>",\n "project": {\n "id": "<the project\'s `uuid`, not its `id`>",\n "name": "<the project\'s `name`>",\n "slugId": "<the last `-`-separated part of the project\'s `url`>"\n },\n "issues": [\n {\n "id": "<the issue\'s `uuid`, not its `id`>",\n "identifier": "<the issue\'s `id`, e.g. DRI-12>",\n "title": "<the issue\'s `title`>",\n "description": "<full current description>",\n "acceptanceCriteria": "<acceptance criteria, empty when included in description>",\n "external": false,\n "url": "<the issue\'s `url`>",\n "branchName": "<the issue\'s `gitBranchName`, or null>",\n "state": {\n "type": "<the issue\'s `statusType`: backlog|unstarted|started|completed|canceled|triage>",\n "name": "<the issue\'s `status`>"\n },\n "labels": ["<each of the issue\'s `labels`>"],\n "parentId": "<the parent issue\'s `uuid`, or null>",\n "blockedBy": ["<the `uuid` of each issue in `relations.blockedBy`>"],\n "prioritySortOrder": 0\n }\n ]\n}\n```\n\nLinear\'s MCP names fields differently from this file, and the difference matters: the app finds each issue\'s row by\nits UUID, so a plan that carries identifiers shows no Build state on any row.\n\n- The MCP\'s `id` is the readable identifier (`DRI-12`, or `P-DRI-7` for a project); the UUID is in `uuid`. Every\n `id`, `parentId` and `blockedBy` value in `plan-input.json` is a UUID (`7f7b03f7-accf-46b2-98fb-abadfed76594`).\n- An issue\'s `parentId` and `relations.blockedBy` from the MCP name issues by identifier. Look each one up: use the\n `uuid` of the project\'s issue with that identifier, or `get_issue` it when it is outside the project.\n- Read relations with `get_issue` and `includeRelations: true`; read the project\'s issues with `list_issues` and\n `fields` including `uuid`.\n- The project\'s `url` ends in its `slugId` (`\u2026/project/build-sandbox-b7640301cd64` \u2192 `b7640301cd64`).\n\n`drisp build plan` rejects a plan with an identifier where a UUID belongs and names the field to fix.\n\nAlso include every referenced blocker outside the project, with `external: true`, its current status and UUID. Follow blocker relations recursively; deduplicate by UUID. Never mark a project issue external. External issues are read only and never scheduled. Preserve actual prioritySortOrder when available.\n\nOnly ready-for-agent issues without conflicting ready-for-human, needs-info, needs-triage or wontfix labels are eligible. Do not invent or change labels to make an issue eligible.\n\nInclude every issue of the project, done and canceled ones too: the plan decides what to skip. Then run:\n\n```sh\ndrisp build plan --input "$DRISP_RUN_ARTIFACTS/input/plan-input.json"\n```\n\nIt prints one JSON object. Act on its `status`.\n\n## 2. `needs_fills`\n\n`fills` lists issues that need a `fill.json` (`identifier`, `title`, `url`, `path`) and `templateVersion` is the value\neach fill must carry. A `reason`, when present, says what to correct in the listed fills (fills that disagree on\n`merge`, say); fix that and keep the rest of each fill. For each listed issue: read it through `linear` (description,\nacceptance criteria, comments) and the repo\'s ADRs and CONTEXT files it touches, then write its `fill.json` at `path`:\n\n```json\n{\n "version": 1,\n "templateVersion": "<templateVersion, copied exactly>",\n "areas": ["macos|web|cli|hub|protocol|docs", "\u2026"],\n "qa": "none|web|macos|both",\n "profile": "implementation|documentation|investigation",\n "rationale": "<why these steps are appropriate>",\n "acceptanceEvidence": [\n "<each acceptance criterion and the evidence that demonstrates it>"\n ],\n "sizeClass": "S|M|L",\n "merge": "agent|you",\n "instructions": "<what the child needs beyond the issue>",\n "ownWork": true\n}\n```\n\n- `areas`: the parts of the repo the change touches, each once.\n- `profile`: implementation for behavior changes, documentation for prose-only repository changes, investigation for findings with no repository changes. Implementation defaults to TDD and two-reviewer code review; documentation and investigation omit these by default. Choose the profile from the actual deliverable, not the issue size.\n- Optional `testFirst` and `review`: `{ "enabled": true|false, "reason": "<issue-specific justification>" }`. Retain tests and review for behavioral changes; omit only when not applicable and explain why. These selections also control required skills.\n- Optional `checks`: a nonempty list of actual validation commands from the repo, using the supported npm, vitest, make or git diff command families. Read repository tooling and add the relevant test/build checks: typechecking alone does not prove behavior. If a necessary command is unsupported, state that limitation in instructions and require human attention, never claim validation passed.\n- `acceptanceEvidence`: cover every criterion, including the method and expected outcome.\n- `qa`: `both` when both interfaces change, `web` when the change shows in a web page, `macos` when it shows in the macOS app, else `none`.\n- `sizeClass`: `S` for a change of a file or two, `M` for a feature across a few modules, `L` for a large or risky\n change.\n- `merge`: who merges each pull request. `agent` (the default) unless the member\'s own words in this chat, in the\n Run\'s prompt or a guidance reply, ask to merge themselves ("ask me before merging", "I\'ll merge"): then `you`, and\n the child\'s Merge step waits for the member\'s Sign-off in its Thread. It is one choice for the whole Build: write the\n same value in every fill. It is fixed once the member presses Start: if they ask to change it after Start, tell\n them to Cancel the Build and Build again.\n- `instructions` (at most 6000 characters): start with one short paragraph saying what to build, then restate the\n acceptance criteria, name the ADRs it touches and the parts of the codebase involved. Plain prose and `-` lists\n only: no headings, code fences or HTML comments (`drisp build plan` rejects them).\n- `ownWork`: `false` only for an issue with sub-issues whose own description asks for nothing beyond them, such as\n a PRD above its slices; the plan then skips it and waits for its sub-issues instead. Otherwise `true`.\n\nThen run `drisp build plan` again with the same `--input` path.\n\n## 3. `planned` or `failed`\n\nOn `planned` the Build plan is written and shown to the member, who decides whether to start it: end the Turn with\n`<!-- AWAITING_APPROVAL -->` as the Journal\'s last line. On `failed` the reason is shown instead: end with\n`<!-- WORKFLOW_COMPLETE -->`.\n\n## 4. Every later wake: tick\n\nYou are woken when the member answers the approval question (with Start, or with words of their own before it) and whenever one of your child Runs closes or parks.\nEach time: read Linear again into `$DRISP_RUN_ARTIFACTS/input/plan-input.json` as in step 1 (with a new `readAt`),\nincluding full descriptions, acceptance criteria, labels and external blocker status. A scope change holds the affected issue for a fresh Build and approval. Then run:\n\n```sh\ndrisp build tick --input "$DRISP_RUN_ARTIFACTS/input/plan-input.json"\n```\n\nIt prints one JSON object. Act on its `action`, and repeat until an action ends the Turn:\n\n- `refresh_input`: read Linear into the input again (a newer `readAt`) and tick again.\n- `replan`: the member wrote words before starting, and `guidance` lists every round of them, oldest first. Plan again\n with them as the member\'s wishes: read the issues they concern (and the repo) as in step 2, rewrite the `fill.json`\n of each issue the guidance changes (`instructions`, `areas`, `qa`, `sizeClass`, `merge` in every fill when the\n member says who merges, or `ownWork` to leave an issue out),\n and run `drisp build plan` again with the same `--input` path. Then end as in step 3: with\n `<!-- AWAITING_APPROVAL -->` on `planned`, `<!-- WORKFLOW_COMPLETE -->` on `failed`. Write nothing but those\n `fill.json` files.\n- `repair`: for each of `issues`, read it in Linear first and skip an issue Linear now shows Done or Canceled (its\n child got there first); otherwise move it to Done and post its `comment` verbatim through `linear`. Then read\n Linear into the input again and tick again.\n- `rebase`: for each of `items`, in its `worktree`: `git rebase origin/main`, resolve the conflicts, then\n `git push --force-with-lease origin <branch>` (the item\'s `branch`) if the branch is on the remote. Never push any\n other branch. If a rebase cannot be resolved, `git rebase --abort`.\n Then read Linear into the input again and tick again, adding `--rebase-failed <identifier>` for each that failed.\n- `wait`: end the Turn with `<!-- WAITING_ON_CHILDREN -->` as the Journal\'s last line.\n- `park`: end the Turn with `<!-- NEEDS_HUMAN: <message> -->` as the Journal\'s last line, copying `message`\n verbatim.\n- `complete`: end the Turn with `<!-- WORKFLOW_COMPLETE -->` as the Journal\'s last line.\n\n## Rules\n\n- Never start, cancel or edit Runs, Workflows, worktrees or branches except as an action says; `drisp build` does\n what is needed.\n- Never write to Linear except a `repair`; never open a pull request.\n- Write only `plan-input.json` and the `fill.json` files `drisp build plan` asks for. Never write `answer.json`.\n';
21330
+ var workflow_default = '# Orchestrator: plan and run a Build\n\nYou plan and run a Drisp Build of one Plan: the open issues of one Linear project. The Run\'s prompt names the\nLinear project, the Plan key and the project folder, which is your working directory. You never write code here:\neach issue is built by its own child Run, which `drisp build tick` starts. You own delivery of the approved plan, including diagnosing and unblocking those children. A waiting child is work to assess, not an automatic handoff to the member.\n\nThe Runner sets these for you:\n\n- `DRISP_RUN_ARTIFACTS`: this Run\'s artifact directory. Files you write there are shown to the member.\n- `DRISP_RUN_ID`, `DRISP_RUNNER_ID`, `DRISP_RUN_TOKEN`, `DRISP_HUB_URL`: this Run, its Runner and its right to start\n child Runs; `drisp build` reads them itself.\n\n## Delivery ownership and authority\n\nUse the member\'s existing instructions and the approved acceptance criteria to make routine technical decisions.\nDo not ask again for authorization already given. Keep implementation with the child that owns its worktree.\nNever weaken acceptance criteria, bypass checks, spend beyond the Build budget, impersonate a human approval,\nor perform production changes to make progress appear complete.\n\nYour child tools are shell commands, authenticated with this Run\'s delegation token:\n\n- `drisp build children`: list the actual child Runs, scheduling state, interruptions and usage.\n- `drisp build inspect <child-run-id> [history-cursor]`: read a child\'s state and one page of history, including its\n reported artifacts. Continue with the returned `history.through` until `history.availableThrough` when needed.\n- `drisp build guide <child-run-id> "<technical guidance>"`: send guidance to a running child for its next Turn,\n or answer a parked technical question explicitly addressed to the Orchestrator. Acceptance is not proof of\n delivery or progress: inspect the next child update. A stale-revision refusal means inspect again before acting.\n\nUse only these delegated tools, never a member credential or direct edits to a child\'s Journal or approval files.\nPermission prompts, human sign-off, production decisions and recovery interruptions remain member-owned. The Hub\nwill refuse answers to them. Older children without an explicit Orchestrator question also require a member reply.\n\nBefore escalating or waiting on a blocked child:\n\n1. Inspect its question, recent history, evidence, approved instructions, and relevant repository facts. Find answers\n already supplied by the member or another child. Distinguish missing information from missing authorization.\n2. Resolve technical uncertainty within approved scope: supply evidence links, explain an existing decision, identify\n the failed command and repair, or tell the owning child how to rebase or retarget its own PR. Do not edit live\n child worktrees yourself. Investigate read-only even when `tick` has no mutation action for the diagnosis.\n3. Send concrete guidance with the evidence and expected outcome. Record the decision, child id, question, attempt\n and result in this Journal. Do not resend the same guidance while it is queued. Use at most three recovery rounds\n for the same blocker; diagnose each failed round before trying a different repair.\n4. After three unsuccessful recovery rounds on the same blocker, or if only a person can unblock it, stop retrying\n that blocker and escalate. Report the exact decision, evidence, attempted repairs and your recommended option.\n Guide a child parked on a technical question to preserve its work and re-park with `NEEDS_HUMAN`, without claiming\n approval. Verify that transition on the next wake. Continue independent eligible work; do not hide the unresolved\n question behind a generic wait or reset the retry count by renaming the blocker.\n\nOn each wake inspect children before ticking. Before ending on `wait`, account for every unresolved issue: an active\nworker, a queued Run, an unresolved dependency, or a specific member decision. Persist this accounting and the next\ncheck in the Journal. Waiting releases the Runner slot; it is not continuous monitoring. Child closes and parks wake\nyou; external changes require a member wake until the runtime provides an external event subscription.\n\nPlan dependencies must be executable: never put repairing a prerequisite into a child blocked by that prerequisite.\nIdentify readiness or scope changes before approval. After approval, explain the precise amendment needed rather\nthan changing labels or inventing completion. A merged PR alone does not establish production acceptance.\n\n## 1. Read Linear (first Turn)\n\nWith the `linear` MCP server, read the project and every issue in it, then write\n`$DRISP_RUN_ARTIFACTS/input/plan-input.json` (create the `input` directory) in exactly this shape. Every issue carries\nevery field, empty when it has none. `readAt` is the output of\n`node -e "console.log(new Date().toISOString())"` run just before you read Linear; never write a time yourself:\n\n```json\n{\n "version": 1,\n "readAt": "<the command\'s output, e.g. 2026-10-11T09:30:00.000Z>",\n "project": {\n "id": "<the project\'s `uuid`, not its `id`>",\n "name": "<the project\'s `name`>",\n "slugId": "<the last `-`-separated part of the project\'s `url`>",\n "status": "<the project\'s `status.name`, e.g. Planned>"\n },\n "issues": [\n {\n "id": "<the issue\'s `uuid`, not its `id`>",\n "identifier": "<the issue\'s `id`, e.g. DRI-12>",\n "title": "<the issue\'s `title`>",\n "description": "<full current description>",\n "acceptanceCriteria": "<acceptance criteria, empty when included in description>",\n "external": false,\n "url": "<the issue\'s `url`>",\n "branchName": "<the issue\'s `gitBranchName`, or null>",\n "state": {\n "type": "<the issue\'s `statusType`: backlog|unstarted|started|completed|canceled|triage>",\n "name": "<the issue\'s `status`>"\n },\n "labels": ["<each of the issue\'s `labels`>"],\n "teamStates": [\n {\n "name": "<actual team status name>",\n "type": "<backlog|unstarted|started|completed|canceled|triage>"\n }\n ],\n "parentId": "<the parent issue\'s `uuid`, or null>",\n "blockedBy": ["<the `uuid` of each issue in `relations.blockedBy`>"],\n "prioritySortOrder": 0\n }\n ]\n}\n```\n\nLinear\'s MCP names fields differently from this file, and the difference matters: the app finds each issue\'s row by\nits UUID, so a plan that carries identifiers shows no Build state on any row.\n\n- The MCP\'s `id` is the readable identifier (`DRI-12`, or `P-DRI-7` for a project); the UUID is in `uuid`. Every\n `id`, `parentId` and `blockedBy` value in `plan-input.json` is a UUID (`7f7b03f7-accf-46b2-98fb-abadfed76594`).\n- An issue\'s `parentId` and `relations.blockedBy` from the MCP name issues by identifier. Look each one up: use the\n `uuid` of the project\'s issue with that identifier, or `get_issue` it when it is outside the project.\n- Read relations with `get_issue` and `includeRelations: true`; read the project\'s issues with `list_issues` and\n `fields` including `uuid`.\n- The project\'s `url` ends in its `slugId` (`\u2026/project/build-sandbox-b7640301cd64` \u2192 `b7640301cd64`).\n\n`drisp build plan` rejects a plan with an identifier where a UUID belongs and names the field to fix.\n\nAlso include every referenced blocker outside the project, with `external: true`, its current status and UUID. Follow blocker relations recursively; deduplicate by UUID. Never mark a project issue external. External issues are read only and never scheduled. Preserve actual prioritySortOrder when available.\n\nOnly ready-for-agent issues without conflicting ready-for-human, needs-info, needs-triage or wontfix labels are eligible. An issue in the Needs Input status is skipped like one labelled ready-for-human, with the reason needs-input, and its dependents wait for it. Do not invent or change labels to make an issue eligible.\n\nRead the actual issue statuses for each team and include that team\'s complete `teamStates` catalog on its issues.\nKeep `state.name` and `state.type` exactly as Linear reports them: never write logical overrides. Reconciliation maps\nits desired state to this catalog; a team without Needs Input keeps its actual status and gets the ask comment.\n\nInclude every issue of the project, done and canceled ones too: the plan decides what to skip. Then run:\n\n```sh\ndrisp build plan --input "$DRISP_RUN_ARTIFACTS/input/plan-input.json"\n```\n\nIt prints one JSON object. Act on its `status`.\n\n## 2. `needs_fills`\n\n`fills` lists issues that need a `fill.json` (`identifier`, `title`, `url`, `path`) and `templateVersion` is the value\neach fill must carry. A `reason`, when present, says what to correct in the listed fills (fills that disagree on\n`merge`, say); fix that and keep the rest of each fill. For each listed issue: read it through `linear` (description,\nacceptance criteria, comments) and the repo\'s ADRs and CONTEXT files it touches, then write its `fill.json` at `path`:\n\n```json\n{\n "version": 1,\n "templateVersion": "<templateVersion, copied exactly>",\n "areas": ["macos|web|cli|hub|protocol|docs", "\u2026"],\n "qa": "none|web|macos|both",\n "profile": "implementation|documentation|investigation",\n "rationale": "<why these steps are appropriate>",\n "acceptanceEvidence": [\n "<each acceptance criterion and the evidence that demonstrates it>"\n ],\n "sizeClass": "S|M|L",\n "merge": "agent|you",\n "instructions": "<what the child needs beyond the issue>",\n "ownWork": true\n}\n```\n\n- `areas`: the parts of the repo the change touches, each once.\n- `profile`: implementation for behavior changes, documentation for prose-only repository changes, investigation for findings with no repository changes. Implementation defaults to TDD and two-reviewer code review; documentation and investigation omit these by default. Choose the profile from the actual deliverable, not the issue size.\n- Optional `testFirst` and `review`: `{ "enabled": true|false, "reason": "<issue-specific justification>" }`. Retain tests and review for behavioral changes; omit only when not applicable and explain why. These selections also control required skills.\n- Optional `checks`: a nonempty list of actual validation commands from the repo, using the supported npm, vitest, make or git diff command families. Read repository tooling and add the relevant test/build checks: typechecking alone does not prove behavior. If a necessary command is unsupported, state that limitation in instructions and require human attention, never claim validation passed.\n- `acceptanceEvidence`: cover every criterion, including the method and expected outcome.\n- `qa`: `both` when both interfaces change, `web` when the change shows in a web page, `macos` when it shows in the macOS app, else `none`.\n- `sizeClass`: `S` for a change of a file or two, `M` for a feature across a few modules, `L` for a large or risky\n change.\n- `merge`: who merges each pull request. Treat an explicit "full send" as `agent`, and "full stop" as `you` (prepare and prove the PR, then wait for the member; never approve that sign-off yourself). `agent` (the default) unless the member\'s own words in this chat, in the\n Run\'s prompt or a guidance reply, ask to merge themselves ("ask me before merging", "I\'ll merge"): then `you`, and\n the child\'s Merge step waits for the member\'s Sign-off in its Thread. It is one choice for the whole Build: write the\n same value in every fill. It is fixed once the member presses Start: if they ask to change it after Start, tell\n them to Cancel the Build and Build again.\n- `instructions` (at most 6000 characters): start with one short paragraph saying what to build, then restate the\n acceptance criteria, name the ADRs it touches and the parts of the codebase involved. Plain prose and `-` lists\n only: no headings, code fences or HTML comments (`drisp build plan` rejects them).\n- `ownWork`: `false` only for an issue with sub-issues whose own description asks for nothing beyond them, such as\n a PRD above its slices; the plan then skips it and waits for its sub-issues instead. Otherwise `true`.\n\nThen run `drisp build plan` again with the same `--input` path.\n\n## 3. `planned` or `failed`\n\nOn `planned` the Build plan is written and shown to the member, who decides whether to start it: end the Turn with\n`<!-- AWAITING_APPROVAL -->` as the Journal\'s last line. On `failed` the reason is shown instead: end with\n`<!-- WORKFLOW_COMPLETE -->`.\n\n## 4. Every later wake: tick\n\nYou are woken when the member answers the approval question (with Start, or with words of their own before it) and whenever one of your child Runs closes or parks.\nEach time: read Linear again into `$DRISP_RUN_ARTIFACTS/input/plan-input.json` as in step 1 (with a new `readAt`),\nincluding full descriptions, acceptance criteria, labels and external blocker status. A scope change holds the affected issue for a fresh Build and approval. Then run:\n\n```sh\ndrisp build tick --input "$DRISP_RUN_ARTIFACTS/input/plan-input.json"\n```\n\nIt prints one JSON object. Act on its `action`, and repeat until an action ends the Turn:\n\n- `refresh_input`: read Linear into the input again (a newer `readAt`) and tick again.\n- `replan`: the member wrote words before starting, and `guidance` lists every round of them, oldest first. Plan again\n with them as the member\'s wishes: read the issues they concern (and the repo) as in step 2, rewrite the `fill.json`\n of each issue the guidance changes (`instructions`, `areas`, `qa`, `sizeClass`, `merge` in every fill when the\n member says who merges, or `ownWork` to leave an issue out),\n and run `drisp build plan` again with the same `--input` path. Then end as in step 3: with\n `<!-- AWAITING_APPROVAL -->` on `planned`, `<!-- WORKFLOW_COMPLETE -->` on `failed`. Write nothing but those\n `fill.json` files.\n- `reconcile`: make every one of `actions` in Linear verbatim, in order, through `linear`, then read Linear into the\n input again and tick again:\n - `issue.state`: move the issue whose UUID is `issue` to the status named `state`.\n - `issue.comment`: post `body` on the issue whose UUID is `issue`, exactly as given. `once` is the tick\'s own\n record that it asked; never post or check it.\n - `project.state`: set the status of the project whose UUID is `project` to the status named `state`.\n- `rebase`: for each of `items`, in its `worktree`: `git rebase origin/main`, resolve the conflicts, then\n `git push --force-with-lease origin <branch>` (the item\'s `branch`) if the branch is on the remote. Never push any\n other branch. If a rebase cannot be resolved, `git rebase --abort`.\n Then read Linear into the input again and tick again, adding `--rebase-failed <identifier>` for each that failed.\n- `supervise`: inspect each listed child, diagnose its technical question using Delivery ownership above, then use\n `drisp build guide` with evidence-backed guidance. Refresh Linear and tick again. If the question actually requires\n a human decision, guide the child to preserve its work and re-park with `NEEDS_HUMAN` naming that decision; explicitly\n say no approval has been granted. Verify that reclassification on the next wake, give the member the specific\n escalation, and continue independent work. Do not leave the same question cycling through `supervise` unanswered.\n- `wait`: after the child accounting and blocker assessment above, end the Turn with `<!-- WAITING_ON_CHILDREN -->` as the Journal\'s last line.\n- `park`: end the Turn with `<!-- NEEDS_HUMAN: <message> -->` as the Journal\'s last line, copying `message`\n verbatim.\n- `complete`: end the Turn with `<!-- WORKFLOW_COMPLETE -->` as the Journal\'s last line.\n\n## Linear rules\n\nThese rules decide every Linear status, comment and project status in a Build. `drisp build tick` applies them and\nasks for each write as a `reconcile` action; you never decide a write yourself. The rules tell you what the statuses\nmean when you read them.\n\n{{LINEAR_RULES}}\n\n## Rules\n\n- Never start, cancel or edit Runs, Workflows, worktrees or branches except through the child supervision tools above or as a tick action says; `drisp build` does\n what is needed.\n- Never write to Linear except what a `reconcile` action asks for; never open a pull request.\n- Keep diagnostic findings and recovery decisions in the Journal. Write only `plan-input.json` and the `fill.json` files `drisp build plan` asks for. Never write `answer.json`.\n';
21308
21331
 
21309
21332
  // src/core/workflows/builtins/orchestrator/index.ts
21310
21333
  var ORCHESTRATOR_WORKFLOW_NAME = "orchestrator";
21311
21334
  var ORCHESTRATOR_LIMITS = {
21312
- maxChildren: 2,
21313
21335
  tickAllowance: 300
21314
21336
  };
21315
21337
  var ORCHESTRATOR_LOOP = {
@@ -21318,7 +21340,10 @@ var ORCHESTRATOR_LOOP = {
21318
21340
  maxIterations: 400,
21319
21341
  maxRunTokens: 12e6
21320
21342
  };
21321
- var ORCHESTRATOR_INSTRUCTIONS = workflow_default;
21343
+ var ORCHESTRATOR_INSTRUCTIONS = workflow_default.replace(
21344
+ "{{LINEAR_RULES}}",
21345
+ () => LINEAR_RULES
21346
+ );
21322
21347
  function orchestratorWorkflow(workflowFile) {
21323
21348
  return {
21324
21349
  name: ORCHESTRATOR_WORKFLOW_NAME,
@@ -21463,6 +21488,27 @@ Journal's last line.
21463
21488
 
21464
21489
  {instructions}
21465
21490
 
21491
+ ## Ownership and getting unstuck
21492
+
21493
+ Carry the approved issue through proof, review, checks and delivery without asking about routine implementation
21494
+ choices. Preserve existing work. Fix meaningful CI and review findings; do not stop merely because the PR is open.
21495
+ When review is selected, maintain a finding ledger (id, severity, evidence, fixed or declined with reason). Later
21496
+ rounds review changed behavior and unresolved findings. Keep fixes within scope; agreement without evidence and an
21497
+ exhausted review budget are not approval. Report the actual reviewers and rounds, never invented provider coverage.
21498
+
21499
+ The Build Orchestrator can inspect your history and send technical guidance. When blocked on a technical question,
21500
+ missing evidence, a dependency fact or a repair you cannot resolve yourself, first record what failed, what you tried,
21501
+ relevant paths/PRs and your recommended next action in the Journal. End with
21502
+ \`<!-- NEEDS_ORCHESTRATOR: <specific question and recommendation> -->\` as the last line. Your parent can answer
21503
+ this question and resume you. Guidance from the Orchestrator is not a human approval and cannot waive acceptance,
21504
+ permissions or a reserved sign-off. Never mark a human decision as a technical question to obtain approval indirectly.
21505
+
21506
+ Reserve NEEDS_HUMAN for permission grants, credentials, product decisions that change scope, production changes and
21507
+ explicit member sign-off. A technical gate failure goes to the Orchestrator first: use its gate failure text as context
21508
+ and include actual failing evidence. Do not repeatedly ask the same question without evaluating the supplied guidance.
21509
+ For browser screenshots use \`drisp build evidence-copy <source-image> <unique-name.png>\` in this worktree instead
21510
+ of shell cp. It copies a PNG, JPEG or WebP into .athena/evidence without replacing existing evidence.
21511
+
21466
21512
  ## Evidence receipts
21467
21513
 
21468
21514
  Progress is not proof that a step passed. Follow each Finish instruction only after its acceptance condition holds.
@@ -21475,8 +21521,8 @@ Keep evidence files under .athena/evidence/ and do not commit them after validat
21475
21521
 
21476
21522
  Work through the gates in this order. Start each gate by running the command its "Start" line gives, which reports the
21477
21523
  gate to the Build page, then record the gate in the Journal before moving on. Never skip a gate and never reorder
21478
- them. When a gate cannot pass, end the Turn with its "cannot pass" line exactly as written here, inside
21479
- \`<!-- NEEDS_HUMAN: \u2026 -->\`, as the Journal's last line; never write your own reason.
21524
+ them. When a gate cannot pass, diagnose it and route the question using Ownership and getting unstuck above.
21525
+ The gate's "cannot pass" sentence is context, not a requirement to escalate every technical problem to a person.
21480
21526
 
21481
21527
  {gates}
21482
21528
 
@@ -21521,8 +21567,11 @@ Ignored checks (no steps ran): <names> | none
21521
21567
 
21522
21568
  ## Linear
21523
21569
 
21524
- Your Linear writes are exactly: In Progress in Explore, \`PR: <url>\` in Pull request, the evidence comment and Done in
21525
- Evidence. Never create issues and never edit an issue's title or description.
21570
+ Your Linear writes are exactly: the evidence comment. The Build's Orchestrator makes every other write the rules below
21571
+ describe, statuses and the \`PR: <url>\` comment included, from the facts this Run leaves; so never write a status.
21572
+ Never create issues and never edit an issue's title or description. The rules describe what each status means.
21573
+
21574
+ ${LINEAR_RULES}
21526
21575
 
21527
21576
  When every gate has passed, end with \`<!-- WORKFLOW_COMPLETE -->\` as the Journal's last line.
21528
21577
  `;
@@ -21545,7 +21594,7 @@ Start: run \`{stepCommand}\`.
21545
21594
  Passes when: {passesWhen}.`;
21546
21595
  var CANNOT_PASS_TEMPLATE = `
21547
21596
 
21548
- Cannot pass: \`<!-- NEEDS_HUMAN: {cannotPass} -->\``;
21597
+ Cannot pass: \`<!-- {respondentMarker}: {cannotPass} -->\``;
21549
21598
  var NO_GATE_TEXT_TEMPLATE = `
21550
21599
 
21551
21600
  No gate text: the loop's caps apply.`;
@@ -21623,6 +21672,7 @@ var RENDERER_RULES = {
21623
21672
  "drisp build checks",
21624
21673
  "drisp build step",
21625
21674
  "drisp build merged",
21675
+ "drisp build evidence-copy",
21626
21676
  "gh pr checks",
21627
21677
  "gh pr create",
21628
21678
  "gh pr list",
@@ -21760,7 +21810,7 @@ function selectedGates(fill) {
21760
21810
  passesWhen: "{install}{checks} pass, each check run through `drisp build check <name> -- <command>`; each finding is supported by a source or reproducible observation and each acceptance criterion has evidence; unresolved questions are stated explicitly"
21761
21811
  } : fill.profile === "investigation" && gate.title === "Evidence" ? {
21762
21812
  ...gate,
21763
- passesWhen: "findings and evidence for each acceptance criterion are posted on the Linear issue, with unresolved questions explicitly stated; the issue is moved to Done only when all acceptance criteria are satisfied"
21813
+ passesWhen: "findings and evidence for each acceptance criterion are posted on the Linear issue, with unresolved questions explicitly stated"
21764
21814
  } : fill.merge === "you" && gate.title === "Merge" ? { ...gate, passesWhen: MERGE_SIGN_OFF.passesWhen } : gate
21765
21815
  );
21766
21816
  if (!usesTestFirst(fill))
@@ -21810,7 +21860,10 @@ function renderStages(fill, checks, install) {
21810
21860
 
21811
21861
  Before doing this step: run \`${command} --verify\`. Missing or stale evidence must be refreshed first.` : "") + `
21812
21862
 
21813
- Finish: write the actual result and supporting paths to \`.athena/evidence/step-${index + 1}.md\`, then run \`${finish} --complete --evidence .athena/evidence/step-${index + 1}.md\`. This records an agent attestation, not independent verification.` + (gate.cannotPass ? fillSlots(CANNOT_PASS_TEMPLATE, { cannotPass: gate.cannotPass }) : NO_GATE_TEXT_TEMPLATE);
21863
+ Finish: write the actual result and supporting paths to \`.athena/evidence/step-${index + 1}.md\`, then run \`${finish} --complete --evidence .athena/evidence/step-${index + 1}.md\`. This records an agent attestation, not independent verification.` + (gate.cannotPass ? fillSlots(CANNOT_PASS_TEMPLATE, {
21864
+ cannotPass: gate.cannotPass,
21865
+ respondentMarker: ["Explore", "Pull request"].includes(gate.title) ? "NEEDS_HUMAN" : "NEEDS_ORCHESTRATOR"
21866
+ }) : NO_GATE_TEXT_TEMPLATE);
21814
21867
  }).join("\n\n");
21815
21868
  }
21816
21869
  function render(fill, workflowName, layout) {
@@ -21852,8 +21905,8 @@ function render(fill, workflowName, layout) {
21852
21905
  "## Findings\n\nPost findings and acceptance evidence on the Linear issue. No pull request or merge is required.\n\n## Linear"
21853
21906
  );
21854
21907
  template = template.replace(
21855
- "Your Linear writes are exactly: In Progress in Explore, `PR: <url>` in Pull request, the evidence comment and Done in\nEvidence.",
21856
- "Your Linear writes are In Progress in Explore, findings and evidence, and Done in Evidence."
21908
+ "Your Linear writes are exactly: the evidence comment.",
21909
+ "Your Linear writes are exactly: the findings and evidence comment."
21857
21910
  );
21858
21911
  }
21859
21912
  const mergeCommand = `drisp build step ${gateTitles(fill).indexOf("Merge") + 1} --qa ${fill.qa}${selectionFlags(fill)}`;
@@ -22246,7 +22299,7 @@ import crypto from "crypto";
22246
22299
  import path9 from "path";
22247
22300
 
22248
22301
  // src/core/workflows/stateMachine.md
22249
- var stateMachine_default = "# Turn Protocol\n\nYou run inside a managed workflow loop. **Run until the work is done.** The Dossier \u2014 `.athena/<session_id>/` \u2014 is your durable memory; `journal.md` is your index into it: read it, work, write it as you go. Your conversation context usually survives between stops \u2014 the runner resumes your session rather than restarting it \u2014 but never rely on that: the runner may kill a long Turn, your session may be replaced at a context bound, the process may die mid-task. Anything not in the Dossier can be lost.\n\nTwo kinds of Turn exist, and you should know which you are in:\n\n- **A fresh Turn** \u2014 the first Turn of the run, or the Turn right after a Handover (the runner's context reset). You start with no memory of prior work; the Journal checkpoint is your saved state.\n- **A resumed Turn** \u2014 the runner continued your existing session with a new instruction (a corrective nudge, a retry after a transient failure, a human's reply, or a human steer). Your context is intact; act on the new instruction and keep going.\n\nEither kind of Turn may open with a **human steer**: a block delimited by `=== HUMAN STEER \u2026 ===` and `=== END HUMAN STEER ===`, carrying instructions a human sent into the run while it was in progress (several are numbered in arrival order). Read it before you plan. Where it conflicts with the journal's planned next action, the steer wins; note what it changed in the journal and continue. The runner has already recorded the steer itself in the journal, with its origin and the Turn it reached.\n\n## First action, in a fresh Turn\n\n1. If the seed prompt contains a validated **Restart contract**, execute its next action using the objective, constraints, changes, open questions and references it carries. Read additional Journal and Unit Record sections selectively when needed. Do not read the whole Journal as an opening ritual.\n2. Otherwise read the journal at the configured path (default: `.athena/<session_id>/journal.md`). The runner provides the session ID \u2014 do not invent one.\n3. If it contains `<!-- JOURNAL_SKELETON -->`, run [**Orient**](#orient-turn-1). Otherwise run [**Execute**](#execute-continuation) from its saved next action.\n\nUpdate durable state only when it changes; never append checkpoint-processed notes. The Runner may suspend if the restart contract cannot fit its context allowance.\n\nIn a resumed Turn, your context is already loaded \u2014 skim the journal only if you have any doubt it still matches reality, then continue.\n\n## Journal contract\n\nThe journal must always answer five questions:\n\n1. What are we trying to accomplish?\n2. What has been done?\n3. What's left?\n4. What should the next Turn do first?\n5. What else, beyond this file, must the next Turn read to have full context?\n\nA fresh Turn has no other context. If something isn't here or in a file question 5 names, it doesn't exist. Section headings may vary by workflow, but these five answers must be explicit and easy to find. Question 5 is the journal's pointer into the rest of the Dossier (see [The Dossier](#the-dossier)): on a single-file Run its honest answer is \"nothing else\"; once anything has been shed, it names exactly which files, so a fresh Turn's opening read stays small even when the Dossier has grown.\n\n### Terminal markers\n\nDefault markers (workflows may override \u2014 use the markers configured for the active workflow):\n\n- `<!-- WORKFLOW_COMPLETE -->` \u2014 all work done and verified\n- `<!-- NEEDS_HUMAN -->` or `<!-- NEEDS_HUMAN: reason -->` \u2014 you need a human: a question only they can answer, or an external blocker only they can clear\n\nRules:\n\n- Only the last non-empty line of the journal is authoritative. Marker-like text in notes, examples, or quoted instructions earlier in the file is ignored.\n- When you write a terminal marker, it must be the final non-empty line of the journal. Put every summary, status note, and next-step sentence before the marker. Never append prose after it.\n- The runner trusts markers unconditionally. A premature `WORKFLOW_COMPLETE` ends the run with no automatic recovery \u2014 write it only when its criteria are fully met.\n- Include a concrete reason after `NEEDS_HUMAN:` whenever possible \u2014 it is what the human sees when deciding how to answer you. Put the full question there.\n\n### Step block\n\nWhen the workflow has named steps \u2014 phases, gates, stages, whatever the workflow document calls them \u2014 keep one Turn Protocol block in the journal naming the step you are on, revised in place as you move:\n\n```\n<!-- TURN_PROTOCOL\nstep: Build\nstep_index: 3\nstep_total: 5\n-->\n```\n\n`step` is the step's name as the workflow writes it. `step_index` and `step_total` are optional, 1-based, and `step_index` never exceeds `step_total`. The runner reads the block after every Turn and shows the step in the feed as the run's progress; it never changes what you do. Leave the block out when the workflow has no named steps. A block the runner cannot read is ignored with a warning \u2014 it never fails a Turn.\n\n## The Dossier\n\n`.athena/<session_id>/` is the Dossier: the journal plus everywhere it can shed cold detail once shedding earns its keep.\n\n```\njournal.md state: index, open loops, next action, terminal marker\nunits/<slug>.md record: contract, problem, design + rationale, build state, gate evidence\norientation.md cross-unit knowledge, revised in place\n```\n\n**On most Runs the Dossier never grows past `journal.md` itself** \u2014 one file, no ceremony, no extra directories, nothing below this line to act on. Nothing here changes how a short, single-unit Run works.\n\nA **unit** is a bounded piece of the plan that reaches its own closed state while the Run keeps going \u2014 one issue in a multi-issue epic, one phase of a larger goal. A Run with only one unit never closes a unit while another stays open, so it can never trip the first shed trigger below.\n\n### When to shed\n\nTwo triggers, both structural. A single-unit Run can only ever reach the second, and only once that unit has outgrown the bound:\n\n- **A unit closes while another is still open.** Cut that unit's whole detail out of the journal now, before starting the next one.\n- **The journal crosses ~8,000 tokens** (roughly 32,000 characters \u2014 a token is about 4 characters at this codebase's measured rate). This is a backstop for one long single unit, not a target to design toward \u2014 shed the completed phases of the still-open unit.\n\n### How to shed\n\nShedding is three positive acts on one whole named `##` section \u2014 never a summary, never a partial move:\n\n1. **Cut** the section, verbatim, out of `journal.md`.\n2. **Paste** it, verbatim, into `units/<slug>.md` (create the file on that unit's first shed).\n3. **Pointer** \u2014 leave one index row in the journal's unit table with a relative path to where the detail went.\n\nContent is demoted, never deleted. If you find yourself rewording or condensing while moving text, stop \u2014 that is not shedding, that is exactly where fidelity leaks.\n\n### The unit table\n\nThe journal's unit table is the index the runner reads to keep the CLI's own task list in sync with your plan \u2014 it is parsed by tooling, so its shape is fixed, not free-form prose. Keep it under a `## Units` heading, as a GFM table with exactly two columns:\n\n```\n## Units\n\n| Unit | Record |\n| ---------------------- | --------------------------- |\n| Add the size nudge | units/size-nudge.md |\n| Wire up task projection | units/task-projection.md |\n```\n\n- **Unit** \u2014 a short human-readable label for the unit (what step 3 of [How to shed](#how-to-shed) points at).\n- **Record** \u2014 the path to that unit's `units/<slug>.md`, relative to the journal's own directory.\n\nOne row per unit that has been shed; units still fully inline in the journal (never shed) don't need a row. A row whose Record file is missing, unreadable, or malformed is simply skipped by the runner \u2014 never guessed at, never a failure (see the next section). Malformed rows in an otherwise-fine table don't invalidate the rest of the table.\n\nEach `units/<slug>.md` record opens with a small YAML frontmatter block the runner reads to know the unit's state:\n\n```\n---\nstatus: open\ngates: []\n---\n\n<the shed section content, verbatim>\n```\n\n- `status` \u2014 `open` or `closed`, exactly (case-insensitive). Any other value, or a missing `status` key, means the runner treats the whole record as unparseable for projection purposes \u2014 it still exists as your durable journal, it just doesn't surface in the task list.\n- `gates` \u2014 reserved for future gate-evidence tracking; not read by anything yet. Leave it present but empty, or omit it \u2014 either is fine today.\n\nThis frontmatter is the only structured part of a unit record; everything after the closing `---` is free-form prose exactly like the rest of the Dossier.\n\n### Parsing is best-effort, never a gate\n\nThe unit table and unit-record frontmatter exist so the runner can mirror your plan into the harness's own task list (see [Task UI projection](#task-ui-projection)) and, separately, so it can nudge you when the journal grows past its size backstop. Both are conveniences layered on top of the Dossier, not requirements on it:\n\n- A missing table, an extra column, a typo'd status, a unit record that fails to parse \u2014 none of it fails your Turn, and none of it is worth stopping to fix for the runner's sake. The runner silently skips whatever it can't parse and, at most, folds a nudge into your next prompt: a size nudge when the journal is over its backstop, or a shed-integrity nudge when a shed was left half done \u2014 a `units/*.md` record with no row in the `## Units` table, or a `##` heading present in both the journal and a record. Finish the shed (cut, paste, pointer) and carry on.\n- Never restructure the journal or a unit record just to make automated parsing happy at the expense of the content itself. If the table and the prose disagree, the prose (what you actually did) is the truth.\n\n### orientation.md\n\nKnowledge that spans more than one unit lives here, revised in place instead of appended to \u2014 the one Dossier surface you edit rather than move things into. Two sections with different rules:\n\n- **Established** \u2014 decisions. Never decay; only explicitly superseded (\"we chose X over Y because Z \u2014 superseded 2026-09-03: \u2026\").\n- **Observed** \u2014 facts carrying a `file.ts:123`-style anchor. Revalidate the anchor before relying on it; code moves.\n\nA correction is an edit to the existing entry, not a new capitalized warning stacked on top of stale text.\n\n## Run until done; declare when you need a human\n\nThe loop's contract with you:\n\n- **Do not stop early.** There is no checkpoint budget and no reason to end a Turn \"to be safe\" \u2014 context refresh is the runner's job, not yours. When your context approaches its bound the runner performs a **Handover**: a fresh session resumes from the bounded `## Restart` section in your Journal; missing or stale state pauses the Run. You will not see this happen; just keep the journal current so nothing is lost.\n- **Stopping without a marker is a mistake**, not a signal. The runner reads it as a premature stop and resumes you with a corrective prompt; repeated markerless stops without journal progress escalate to a human. Never stop as a way of asking \"should I continue?\" \u2014 the answer is always to continue or to declare.\n- **Need a human? Declare it.** Write `NEEDS_HUMAN: <your question or blocker>` as the journal's final non-empty line and end. The run suspends until a human replies; their reply resumes your session with the answer. This is the only correct way to wait for a person \u2014 an interactive question asked into an unattended run cannot be answered.\n- Transient infrastructure failures are not yours to manage: the runner retries them by resuming your session. Just make sure the journal reflects reality before risky operations.\n\n## Phases\n\n### Orient (Turn 1)\n\n1. **Replace the skeleton immediately**, before any domain work. Even a three-line journal (goal + \"orienting\") protects you if the Turn dies during setup.\n2. Identify and load the applicable workflow skills before doing domain work. If a workflow, plugin, or local skill table names a relevant skill, read it fully and follow it. Do not assume you already know the workflow's conventions, tool sequence, quality gates, or implementation details.\n3. Use a dedicated git worktree for repository-changing work. If you are not already inside a task-specific worktree, create or enter one before editing files, record its branch/path in the journal, and continue there. Skip this only when the workflow explicitly forbids it or the task is read-only.\n4. Run the workflow's orientation steps exactly as written. These vary by domain \u2014 a test-writing workflow explores the product in a browser; a migration workflow audits the schema. The workflow defines what orientation means. Do not skip, reorder, reinterpret, or replace workflow steps with a generic approach unless the workflow explicitly allows it or the journal records a concrete blocker that makes the written step impossible.\n5. Refine the journal into a granular plan. Each task a concrete, verifiable unit of work, including verification steps (running checks, reviewing output) \u2014 not just implementation. Vague tasks (\"write tests\") cannot be meaningfully resumed by a future Turn that has no idea what they mean here.\n6. Record concrete observations \u2014 what you actually saw, not what you assumed. Wrong assumptions burn entire future Turns on rework.\n7. **Single-Turn requests still go through this phase.** If the entire request is satisfied quickly, write a minimal journal (what was asked, what was done, the outcome) and append `<!-- WORKFLOW_COMPLETE -->`. Leaving the skeleton in place gets you nudged \u2014 the runner cannot trust work it can't read from the journal \u2014 and repeated stops with an untouched skeleton escalate to a human.\n\n### Execute (continuation)\n\n- Work from where the journal says, in the workflow's prescribed sequence, and keep going until the work is done or a declared blocker stops you.\n- Be strict with workflow steps. Before starting each unit, identify the next required workflow step from the workflow document and journal, follow it as written, and record completion or blockers against that step. Do not substitute your own process, collapse separate gates into one, or advance past an unchecked step.\n- Be strict with skills. Before each new activity, check the workflow, plugin metadata, local skill table, and journal for relevant skills. Load the appropriate skill first, read it completely, and follow its instructions. If no skill applies, record that explicitly in the journal before proceeding. Skills carry the implementation detail (scaffolding steps, locator rules, anti-patterns, code templates) that this protocol intentionally doesn't repeat.\n- Keep repository work inside the recorded git worktree. If a continuation starts outside the recorded worktree, enter it before editing. If no worktree is recorded and edits are still required, create or enter one before proceeding.\n- Delegate heavy exploration or generation to subagents via the Task tool. Pass file paths, conventions, and concrete output expectations; tell them which skill to load. Respect the workflow's **delegation constraints** \u2014 some operations must run in the main agent because their output is proof, or because the main agent needs to interpret results in context.\n- Run quality gates in order. Do not skip \u2014 they exist because skipping cascades into rework. On a failing verdict, address the issues and re-run before proceeding. Respect the workflow's **retry limits**: repeated failure usually signals a deeper issue another retry won't fix.\n\n### End\n\nYou end the run only by declaring:\n\n1. Journal reflects all progress, discoveries, and blockers.\n2. Journal says clearly what a fresh Turn would need to do first (a Handover can happen at any time).\n3. If all work is verified: append the completion marker as the final non-empty line.\n4. If a human is needed to proceed: append the needs-human marker as the final non-empty line, with the question or blocker spelled out as the reason.\n\n## When to write the journal\n\nWrite on **concrete triggers**, not on a vague sense of \"meaningful progress.\" The right cadence sits between every-tool-call (noisy log, wastes tokens) and end-of-run (everything lost if you die mid-task). This matters more, not less, now that Turns run long: the Journal checkpoint is what carries a killed or reset session.\n\n- **Discrete unit done** \u2014 file written, fix applied, test run, gate passed. Reflect the new reality before starting the next unit. If another unit is still open, this is also a shed trigger (see [The Dossier](#the-dossier)): cut this unit's detail into `units/<slug>.md` before you start the next one.\n- **Insight learned** \u2014 API quirk, config field that turned out to matter, dead end ruled out, decision between two approaches. Insights are journal-worthy even when no code changed; rediscovering them costs a future Turn a full re-exploration. The journal is a knowledge ledger, not just a task log. Insight that spans more than one unit belongs in `orientation.md`, not the journal.\n- **About to do something risky or long-running** \u2014 subagent dispatch, long build, flaky external call, large refactor. Write _first_, then act. If the operation kills your Turn, only what's on disk survives.\n- **Plan changed** \u2014 task resequenced, new task surfaced, planned task no longer needed. Stale plans poison continuation Turns.\n- **The journal crosses the ~8,000-token backstop** \u2014 shed the completed phases of the still-open unit into its `units/<slug>.md` record now, mid-unit; don't wait for it to close.\n- **You haven't written in a while** \u2014 if you can't remember the last update, you've gone too long. A short defensive update (\"doing X, last completed Y, next is Z\") beats nothing.\n\nEach update covers: what changed (work or knowledge), what's now next, and any caveat a future Turn needs. Don't transcribe tool calls \u2014 the journal is a contract with your future self, not a replay log.\n\nThe cost of one extra journal update is a few tokens. The cost of dying without one is rework. Bias toward writing.\n\n## Task UI projection\n\nThe journal is the durable source of truth. Your harness's task tools are a session-scoped UI projection of the same plan, shown to the user in their CLI widget. They do not survive process exit.\n\n{{TASK_TOOL_INSTRUCTIONS}}\n\n- **Turn 1, after orientation:** project the journal's task plan into the task tools.\n- **In a fresh continuation (e.g. after a Handover):** recreate the projection from the journal; do not assume task IDs from prior sessions still exist.\n- **During work:** update both \u2014 the task tools for immediate UI feedback, the journal for persistence \u2014 in the same working phase.\n\nSeparately, the runner independently re-derives a task list from the journal's [unit table](#the-unit-table) and each unit record's frontmatter after every Turn, and mirrors it into the CLI's own task display. This is a backstop, not a substitute for the above \u2014 it only ever reaches `open`/`closed`, so keep calling the task tools yourself for anything finer-grained. It never blocks or fails a Turn: an unparseable table or record just means that Turn's mirror is skipped, exactly as described in [Parsing is best-effort, never a gate](#parsing-is-best-effort-never-a-gate).\n\n## Quick reference\n\nOn a Handover, use the validated **Restart contract** in the seed prompt: objective, next action, essential constraints, changes, open questions and evidence references. Read additional Journal and Unit Record sections selectively when the next action needs them. Update durable state only when it changes; never append checkpoint-processed notes. The Runner may suspend if the restart contract cannot fit its context allowance.\n\n- [ ] Replace the skeleton immediately, even for single-Turn requests\n- [ ] Run until the work is done \u2014 do not stop at checkpoints, and never stop as a way of asking permission to continue\n- [ ] Need a human? Declare it: `NEEDS_HUMAN: <question>` as the final non-empty line, then end\n- [ ] Update the journal on concrete triggers \u2014 unit done, insight learned, risky op pending, plan changed\n- [ ] Shed a unit's detail into `units/<slug>.md` the moment it closes while another stays open, or the journal crosses ~8,000 tokens \u2014 cut, paste, pointer, never summarize\n- [ ] Keep the unit table and each shed record's `status: open|closed` frontmatter current \u2014 the runner mirrors them into the task list and skips silently on any parse miss\n On a Handover, use the validated **Restart contract** in the seed prompt: objective, next action, essential constraints, changes, open questions and evidence references. Read additional Journal and Unit Record sections selectively when the next action needs them. Update durable state only when it changes; never append checkpoint-processed notes. The Runner may suspend if the restart contract cannot fit its context allowance.\n- [ ] Project the journal plan into task tools at session start; keep both in sync as work lands\n- [ ] Follow the workflow steps as written; do not skip, reorder, or substitute your own process\n- [ ] Workflow has named steps? Keep the `TURN_PROTOCOL` step block in the journal naming the one you are on\n- [ ] Load the appropriate skill before each activity; do not rely on assumed knowledge\n- [ ] Use and record a dedicated git worktree for repository-changing work\n- [ ] Run quality gates in order; respect delegation constraints and retry limits\n- [ ] Write the completion marker only when all work is verified, and make it the final non-empty line\n\n## Restart checkpoint\n\nMaintain one `## Restart` section in the Journal during normal work, using the\nRun id and token bound supplied by the Runner. Include `Run: <runId>` and these\nnonempty fields: Objective, Next action, Constraints, Changes, Open questions,\nReferences. Use `none` when appropriate. Preserve essential constraints; point to\nevidence instead of copying cold history. Update after orientation and concrete\nmilestones, before large reads or risky operations. Write atomically. At the\ncontext boundary the Runner restarts directly from this section, or pauses if it\nis missing, invalid or already consumed. Continue normal work; do not request a\nseparate summarization Turn.\n";
22302
+ var stateMachine_default = "# Turn Protocol\n\nYou run inside a managed workflow loop. **Run until the work is done.** The Dossier \u2014 `.athena/<session_id>/` \u2014 is your durable memory; `journal.md` is your index into it: read it, work, write it as you go. Your conversation context usually survives between stops \u2014 the runner resumes your session rather than restarting it \u2014 but never rely on that: the runner may kill a long Turn, your session may be replaced at a context bound, the process may die mid-task. Anything not in the Dossier can be lost.\n\nTwo kinds of Turn exist, and you should know which you are in:\n\n- **A fresh Turn** \u2014 the first Turn of the run, or the Turn right after a Handover (the runner's context reset). You start with no memory of prior work; the Journal checkpoint is your saved state.\n- **A resumed Turn** \u2014 the runner continued your existing session with a new instruction (a corrective nudge, a retry after a transient failure, a human's reply, or a human steer). Your context is intact; act on the new instruction and keep going.\n\nEither kind of Turn may open with a **human steer**: a block delimited by `=== HUMAN STEER \u2026 ===` and `=== END HUMAN STEER ===`, carrying instructions a human sent into the run while it was in progress (several are numbered in arrival order). Read it before you plan. Where it conflicts with the journal's planned next action, the steer wins; note what it changed in the journal and continue. The runner has already recorded the steer itself in the journal, with its origin and the Turn it reached.\n\n## First action, in a fresh Turn\n\n1. If the seed prompt contains a validated **Restart contract**, execute its next action using the objective, constraints, changes, open questions and references it carries. Read additional Journal and Unit Record sections selectively when needed. Do not read the whole Journal as an opening ritual.\n2. Otherwise read the journal at the configured path (default: `.athena/<session_id>/journal.md`). The runner provides the session ID \u2014 do not invent one.\n3. If it contains `<!-- JOURNAL_SKELETON -->`, run [**Orient**](#orient-turn-1). Otherwise run [**Execute**](#execute-continuation) from its saved next action.\n\nUpdate durable state only when it changes; never append checkpoint-processed notes. The Runner may suspend if the restart contract cannot fit its context allowance.\n\nIn a resumed Turn, your context is already loaded \u2014 skim the journal only if you have any doubt it still matches reality, then continue.\n\n## Journal contract\n\nThe journal must always answer five questions:\n\n1. What are we trying to accomplish?\n2. What has been done?\n3. What's left?\n4. What should the next Turn do first?\n5. What else, beyond this file, must the next Turn read to have full context?\n\nA fresh Turn has no other context. If something isn't here or in a file question 5 names, it doesn't exist. Section headings may vary by workflow, but these five answers must be explicit and easy to find. Question 5 is the journal's pointer into the rest of the Dossier (see [The Dossier](#the-dossier)): on a single-file Run its honest answer is \"nothing else\"; once anything has been shed, it names exactly which files, so a fresh Turn's opening read stays small even when the Dossier has grown.\n\n### Terminal markers\n\nDefault markers (workflows may override \u2014 use the markers configured for the active workflow):\n\n- `<!-- WORKFLOW_COMPLETE -->` \u2014 all work done and verified\n- `<!-- NEEDS_HUMAN -->` or `<!-- NEEDS_HUMAN: reason -->` \u2014 you need a human: a question only they can answer, or an external blocker only they can clear\n- `<!-- NEEDS_ORCHESTRATOR: question -->` \u2014 a Build child needs technical guidance from its parent. Include evidence, attempted fixes and a recommendation. This never grants permissions or substitutes for human sign-off; outside a child Run it remains a member question.\n\nRules:\n\n- Only the last non-empty line of the journal is authoritative. Marker-like text in notes, examples, or quoted instructions earlier in the file is ignored.\n- When you write a terminal marker, it must be the final non-empty line of the journal. Put every summary, status note, and next-step sentence before the marker. Never append prose after it.\n- The runner trusts markers unconditionally. A premature `WORKFLOW_COMPLETE` ends the run with no automatic recovery \u2014 write it only when its criteria are fully met.\n- Include a concrete reason after `NEEDS_HUMAN:` whenever possible \u2014 it is what the human sees when deciding how to answer you. Put the full question there.\n\n### Step block\n\nWhen the workflow has named steps \u2014 phases, gates, stages, whatever the workflow document calls them \u2014 keep one Turn Protocol block in the journal naming the step you are on, revised in place as you move:\n\n```\n<!-- TURN_PROTOCOL\nstep: Build\nstep_index: 3\nstep_total: 5\n-->\n```\n\n`step` is the step's name as the workflow writes it. `step_index` and `step_total` are optional, 1-based, and `step_index` never exceeds `step_total`. The runner reads the block after every Turn and shows the step in the feed as the run's progress; it never changes what you do. Leave the block out when the workflow has no named steps. A block the runner cannot read is ignored with a warning \u2014 it never fails a Turn.\n\n## The Dossier\n\n`.athena/<session_id>/` is the Dossier: the journal plus everywhere it can shed cold detail once shedding earns its keep.\n\n```\njournal.md state: index, open loops, next action, terminal marker\nunits/<slug>.md record: contract, problem, design + rationale, build state, gate evidence\norientation.md cross-unit knowledge, revised in place\n```\n\n**On most Runs the Dossier never grows past `journal.md` itself** \u2014 one file, no ceremony, no extra directories, nothing below this line to act on. Nothing here changes how a short, single-unit Run works.\n\nA **unit** is a bounded piece of the plan that reaches its own closed state while the Run keeps going \u2014 one issue in a multi-issue epic, one phase of a larger goal. A Run with only one unit never closes a unit while another stays open, so it can never trip the first shed trigger below.\n\n### When to shed\n\nTwo triggers, both structural. A single-unit Run can only ever reach the second, and only once that unit has outgrown the bound:\n\n- **A unit closes while another is still open.** Cut that unit's whole detail out of the journal now, before starting the next one.\n- **The journal crosses ~8,000 tokens** (roughly 32,000 characters \u2014 a token is about 4 characters at this codebase's measured rate). This is a backstop for one long single unit, not a target to design toward \u2014 shed the completed phases of the still-open unit.\n\n### How to shed\n\nShedding is three positive acts on one whole named `##` section \u2014 never a summary, never a partial move:\n\n1. **Cut** the section, verbatim, out of `journal.md`.\n2. **Paste** it, verbatim, into `units/<slug>.md` (create the file on that unit's first shed).\n3. **Pointer** \u2014 leave one index row in the journal's unit table with a relative path to where the detail went.\n\nContent is demoted, never deleted. If you find yourself rewording or condensing while moving text, stop \u2014 that is not shedding, that is exactly where fidelity leaks.\n\n### The unit table\n\nThe journal's unit table is the index the runner reads to keep the CLI's own task list in sync with your plan \u2014 it is parsed by tooling, so its shape is fixed, not free-form prose. Keep it under a `## Units` heading, as a GFM table with exactly two columns:\n\n```\n## Units\n\n| Unit | Record |\n| ---------------------- | --------------------------- |\n| Add the size nudge | units/size-nudge.md |\n| Wire up task projection | units/task-projection.md |\n```\n\n- **Unit** \u2014 a short human-readable label for the unit (what step 3 of [How to shed](#how-to-shed) points at).\n- **Record** \u2014 the path to that unit's `units/<slug>.md`, relative to the journal's own directory.\n\nOne row per unit that has been shed; units still fully inline in the journal (never shed) don't need a row. A row whose Record file is missing, unreadable, or malformed is simply skipped by the runner \u2014 never guessed at, never a failure (see the next section). Malformed rows in an otherwise-fine table don't invalidate the rest of the table.\n\nEach `units/<slug>.md` record opens with a small YAML frontmatter block the runner reads to know the unit's state:\n\n```\n---\nstatus: open\ngates: []\n---\n\n<the shed section content, verbatim>\n```\n\n- `status` \u2014 `open` or `closed`, exactly (case-insensitive). Any other value, or a missing `status` key, means the runner treats the whole record as unparseable for projection purposes \u2014 it still exists as your durable journal, it just doesn't surface in the task list.\n- `gates` \u2014 reserved for future gate-evidence tracking; not read by anything yet. Leave it present but empty, or omit it \u2014 either is fine today.\n\nThis frontmatter is the only structured part of a unit record; everything after the closing `---` is free-form prose exactly like the rest of the Dossier.\n\n### Parsing is best-effort, never a gate\n\nThe unit table and unit-record frontmatter exist so the runner can mirror your plan into the harness's own task list (see [Task UI projection](#task-ui-projection)) and, separately, so it can nudge you when the journal grows past its size backstop. Both are conveniences layered on top of the Dossier, not requirements on it:\n\n- A missing table, an extra column, a typo'd status, a unit record that fails to parse \u2014 none of it fails your Turn, and none of it is worth stopping to fix for the runner's sake. The runner silently skips whatever it can't parse and, at most, folds a nudge into your next prompt: a size nudge when the journal is over its backstop, or a shed-integrity nudge when a shed was left half done \u2014 a `units/*.md` record with no row in the `## Units` table, or a `##` heading present in both the journal and a record. Finish the shed (cut, paste, pointer) and carry on.\n- Never restructure the journal or a unit record just to make automated parsing happy at the expense of the content itself. If the table and the prose disagree, the prose (what you actually did) is the truth.\n\n### orientation.md\n\nKnowledge that spans more than one unit lives here, revised in place instead of appended to \u2014 the one Dossier surface you edit rather than move things into. Two sections with different rules:\n\n- **Established** \u2014 decisions. Never decay; only explicitly superseded (\"we chose X over Y because Z \u2014 superseded 2026-09-03: \u2026\").\n- **Observed** \u2014 facts carrying a `file.ts:123`-style anchor. Revalidate the anchor before relying on it; code moves.\n\nA correction is an edit to the existing entry, not a new capitalized warning stacked on top of stale text.\n\n## Run until done; declare when you need a human\n\nThe loop's contract with you:\n\n- **Do not stop early.** There is no checkpoint budget and no reason to end a Turn \"to be safe\" \u2014 context refresh is the runner's job, not yours. When your context approaches its bound the runner performs a **Handover**: a fresh session resumes from the bounded `## Restart` section in your Journal; missing or stale state pauses the Run. You will not see this happen; just keep the journal current so nothing is lost.\n- **Stopping without a marker is a mistake**, not a signal. The runner reads it as a premature stop and resumes you with a corrective prompt; repeated markerless stops without journal progress escalate to a human. Never stop as a way of asking \"should I continue?\" \u2014 the answer is always to continue or to declare.\n- **Need a human? Declare it.** Write `NEEDS_HUMAN: <your question or blocker>` as the journal's final non-empty line and end. The run suspends until a human replies; their reply resumes your session with the answer. This is the only correct way to wait for a person \u2014 an interactive question asked into an unattended run cannot be answered.\n- Transient infrastructure failures are not yours to manage: the runner retries them by resuming your session. Just make sure the journal reflects reality before risky operations.\n\n## Phases\n\n### Orient (Turn 1)\n\n1. **Replace the skeleton immediately**, before any domain work. Even a three-line journal (goal + \"orienting\") protects you if the Turn dies during setup.\n2. Identify and load the applicable workflow skills before doing domain work. If a workflow, plugin, or local skill table names a relevant skill, read it fully and follow it. Do not assume you already know the workflow's conventions, tool sequence, quality gates, or implementation details.\n3. Use a dedicated git worktree for repository-changing work. If you are not already inside a task-specific worktree, create or enter one before editing files, record its branch/path in the journal, and continue there. Skip this only when the workflow explicitly forbids it or the task is read-only.\n4. Run the workflow's orientation steps exactly as written. These vary by domain \u2014 a test-writing workflow explores the product in a browser; a migration workflow audits the schema. The workflow defines what orientation means. Do not skip, reorder, reinterpret, or replace workflow steps with a generic approach unless the workflow explicitly allows it or the journal records a concrete blocker that makes the written step impossible.\n5. Refine the journal into a granular plan. Each task a concrete, verifiable unit of work, including verification steps (running checks, reviewing output) \u2014 not just implementation. Vague tasks (\"write tests\") cannot be meaningfully resumed by a future Turn that has no idea what they mean here.\n6. Record concrete observations \u2014 what you actually saw, not what you assumed. Wrong assumptions burn entire future Turns on rework.\n7. **Single-Turn requests still go through this phase.** If the entire request is satisfied quickly, write a minimal journal (what was asked, what was done, the outcome) and append `<!-- WORKFLOW_COMPLETE -->`. Leaving the skeleton in place gets you nudged \u2014 the runner cannot trust work it can't read from the journal \u2014 and repeated stops with an untouched skeleton escalate to a human.\n\n### Execute (continuation)\n\n- Work from where the journal says, in the workflow's prescribed sequence, and keep going until the work is done or a declared blocker stops you.\n- Be strict with workflow steps. Before starting each unit, identify the next required workflow step from the workflow document and journal, follow it as written, and record completion or blockers against that step. Do not substitute your own process, collapse separate gates into one, or advance past an unchecked step.\n- Be strict with skills. Before each new activity, check the workflow, plugin metadata, local skill table, and journal for relevant skills. Load the appropriate skill first, read it completely, and follow its instructions. If no skill applies, record that explicitly in the journal before proceeding. Skills carry the implementation detail (scaffolding steps, locator rules, anti-patterns, code templates) that this protocol intentionally doesn't repeat.\n- Keep repository work inside the recorded git worktree. If a continuation starts outside the recorded worktree, enter it before editing. If no worktree is recorded and edits are still required, create or enter one before proceeding.\n- Delegate heavy exploration or generation to subagents via the Task tool. Pass file paths, conventions, and concrete output expectations; tell them which skill to load. Respect the workflow's **delegation constraints** \u2014 some operations must run in the main agent because their output is proof, or because the main agent needs to interpret results in context.\n- Run quality gates in order. Do not skip \u2014 they exist because skipping cascades into rework. On a failing verdict, address the issues and re-run before proceeding. Respect the workflow's **retry limits**: repeated failure usually signals a deeper issue another retry won't fix.\n\n### End\n\nYou end the run only by declaring:\n\n1. Journal reflects all progress, discoveries, and blockers.\n2. Journal says clearly what a fresh Turn would need to do first (a Handover can happen at any time).\n3. If all work is verified: append the completion marker as the final non-empty line.\n4. If a human is needed to proceed: append the needs-human marker as the final non-empty line, with the question or blocker spelled out as the reason.\n\n## When to write the journal\n\nWrite on **concrete triggers**, not on a vague sense of \"meaningful progress.\" The right cadence sits between every-tool-call (noisy log, wastes tokens) and end-of-run (everything lost if you die mid-task). This matters more, not less, now that Turns run long: the Journal checkpoint is what carries a killed or reset session.\n\n- **Discrete unit done** \u2014 file written, fix applied, test run, gate passed. Reflect the new reality before starting the next unit. If another unit is still open, this is also a shed trigger (see [The Dossier](#the-dossier)): cut this unit's detail into `units/<slug>.md` before you start the next one.\n- **Insight learned** \u2014 API quirk, config field that turned out to matter, dead end ruled out, decision between two approaches. Insights are journal-worthy even when no code changed; rediscovering them costs a future Turn a full re-exploration. The journal is a knowledge ledger, not just a task log. Insight that spans more than one unit belongs in `orientation.md`, not the journal.\n- **About to do something risky or long-running** \u2014 subagent dispatch, long build, flaky external call, large refactor. Write _first_, then act. If the operation kills your Turn, only what's on disk survives.\n- **Plan changed** \u2014 task resequenced, new task surfaced, planned task no longer needed. Stale plans poison continuation Turns.\n- **The journal crosses the ~8,000-token backstop** \u2014 shed the completed phases of the still-open unit into its `units/<slug>.md` record now, mid-unit; don't wait for it to close.\n- **You haven't written in a while** \u2014 if you can't remember the last update, you've gone too long. A short defensive update (\"doing X, last completed Y, next is Z\") beats nothing.\n\nEach update covers: what changed (work or knowledge), what's now next, and any caveat a future Turn needs. Don't transcribe tool calls \u2014 the journal is a contract with your future self, not a replay log.\n\nThe cost of one extra journal update is a few tokens. The cost of dying without one is rework. Bias toward writing.\n\n## Task UI projection\n\nThe journal is the durable source of truth. Your harness's task tools are a session-scoped UI projection of the same plan, shown to the user in their CLI widget. They do not survive process exit.\n\n{{TASK_TOOL_INSTRUCTIONS}}\n\n- **Turn 1, after orientation:** project the journal's task plan into the task tools.\n- **In a fresh continuation (e.g. after a Handover):** recreate the projection from the journal; do not assume task IDs from prior sessions still exist.\n- **During work:** update both \u2014 the task tools for immediate UI feedback, the journal for persistence \u2014 in the same working phase.\n\nSeparately, the runner independently re-derives a task list from the journal's [unit table](#the-unit-table) and each unit record's frontmatter after every Turn, and mirrors it into the CLI's own task display. This is a backstop, not a substitute for the above \u2014 it only ever reaches `open`/`closed`, so keep calling the task tools yourself for anything finer-grained. It never blocks or fails a Turn: an unparseable table or record just means that Turn's mirror is skipped, exactly as described in [Parsing is best-effort, never a gate](#parsing-is-best-effort-never-a-gate).\n\n## Quick reference\n\nOn a Handover, use the validated **Restart contract** in the seed prompt: objective, next action, essential constraints, changes, open questions and evidence references. Read additional Journal and Unit Record sections selectively when the next action needs them. Update durable state only when it changes; never append checkpoint-processed notes. The Runner may suspend if the restart contract cannot fit its context allowance.\n\n- [ ] Replace the skeleton immediately, even for single-Turn requests\n- [ ] Run until the work is done \u2014 do not stop at checkpoints, and never stop as a way of asking permission to continue\n- [ ] Need a human? Declare it: `NEEDS_HUMAN: <question>` as the final non-empty line, then end\n- [ ] Update the journal on concrete triggers \u2014 unit done, insight learned, risky op pending, plan changed\n- [ ] Shed a unit's detail into `units/<slug>.md` the moment it closes while another stays open, or the journal crosses ~8,000 tokens \u2014 cut, paste, pointer, never summarize\n- [ ] Keep the unit table and each shed record's `status: open|closed` frontmatter current \u2014 the runner mirrors them into the task list and skips silently on any parse miss\n On a Handover, use the validated **Restart contract** in the seed prompt: objective, next action, essential constraints, changes, open questions and evidence references. Read additional Journal and Unit Record sections selectively when the next action needs them. Update durable state only when it changes; never append checkpoint-processed notes. The Runner may suspend if the restart contract cannot fit its context allowance.\n- [ ] Project the journal plan into task tools at session start; keep both in sync as work lands\n- [ ] Follow the workflow steps as written; do not skip, reorder, or substitute your own process\n- [ ] Workflow has named steps? Keep the `TURN_PROTOCOL` step block in the journal naming the one you are on\n- [ ] Load the appropriate skill before each activity; do not rely on assumed knowledge\n- [ ] Use and record a dedicated git worktree for repository-changing work\n- [ ] Run quality gates in order; respect delegation constraints and retry limits\n- [ ] Write the completion marker only when all work is verified, and make it the final non-empty line\n\n## Restart checkpoint\n\nMaintain one `## Restart` section in the Journal during normal work, using the\nRun id and token bound supplied by the Runner. Include `Run: <runId>` and these\nnonempty fields: Objective, Next action, Constraints, Changes, Open questions,\nReferences. Use `none` when appropriate. Preserve essential constraints; point to\nevidence instead of copying cold history. Update after orientation and concrete\nmilestones, before large reads or risky operations. Write atomically. At the\ncontext boundary the Runner restarts directly from this section, or pauses if it\nis missing, invalid or already consumed. Continue normal work; do not request a\nseparate summarization Turn.\n";
22250
22303
 
22251
22304
  // src/core/workflows/stateMachine.ts
22252
22305
  function buildTaskToolInstructions(harness) {
@@ -22443,10 +22496,11 @@ function resolveTurnOutcome(input2) {
22443
22496
  };
22444
22497
  }
22445
22498
  if (journal.needsHuman) {
22499
+ const declaration = journal.needsOrchestrator ? "NEEDS_ORCHESTRATOR" : "NEEDS_HUMAN";
22446
22500
  return {
22447
22501
  kind: "suspend",
22448
22502
  status: "awaiting_attention",
22449
- stopReason: journal.needsHumanReason ? `agent declared NEEDS_HUMAN: ${journal.needsHumanReason}` : "agent declared NEEDS_HUMAN",
22503
+ stopReason: journal.needsHumanReason ? `agent declared ${declaration}: ${journal.needsHumanReason}` : `agent declared ${declaration}`,
22450
22504
  ...journal.deprecatedMarker ? { deprecation: buildMarkerDeprecation(journal.deprecatedMarker) } : {}
22451
22505
  };
22452
22506
  }
@@ -22676,7 +22730,7 @@ var STEER_BLOCK_OPEN = "=== HUMAN STEER";
22676
22730
  var STEER_BLOCK_END = "=== END HUMAN STEER ===";
22677
22731
  function steerHeading(steer, index, total) {
22678
22732
  const position = total > 1 ? `${index + 1} of ${total}, ` : "";
22679
- return `${STEER_BLOCK_OPEN} (${position}via ${steer.origin}, received ${new Date(
22733
+ return `${steer.origin === "orchestrator" ? "=== ORCHESTRATOR GUIDANCE (not human approval)" : STEER_BLOCK_OPEN} (${position}via ${steer.origin}, received ${new Date(
22680
22734
  steer.receivedAt
22681
22735
  ).toISOString()}) ===`;
22682
22736
  }
@@ -22686,6 +22740,11 @@ function buildSteerBlock(steers) {
22686
22740
  ${steer.text.trim()}`
22687
22741
  );
22688
22742
  const noun = steers.length > 1 ? "steers" : "steer";
22743
+ if (steers.some((steer) => steer.origin === "orchestrator"))
22744
+ return `${entries.join("\n")}
22745
+ === END STEERING ===
22746
+
22747
+ Read each message with its labelled source before planning. Orchestrator guidance is a technical decision within the approved scope, never human approval. Preserve human constraints and approval gates. Apply eligible guidance, note it in the journal, and continue the workflow.`;
22689
22748
  return `${entries.join("\n")}
22690
22749
  ${STEER_BLOCK_END}
22691
22750
 
@@ -22705,7 +22764,7 @@ function formatSteerJournalEntry(steer, iteration) {
22705
22764
 
22706
22765
  ---
22707
22766
 
22708
- ## Human steer (via ${steer.origin})
22767
+ ## ${steer.origin === "orchestrator" ? "Orchestrator guidance (not human approval)" : "Human steer"} (via ${steer.origin})
22709
22768
 
22710
22769
  _Received ${new Date(steer.receivedAt).toISOString()}; delivered into Turn ${iteration}._
22711
22770
 
@@ -23565,7 +23624,7 @@ function isBoundedTurn(value) {
23565
23624
  function isQueuedSteer(value) {
23566
23625
  if (!value || typeof value !== "object") return false;
23567
23626
  const steer = value;
23568
- return typeof steer.text === "string" && (steer.origin === "hub" || steer.origin === "local") && typeof steer.receivedAt === "number";
23627
+ return typeof steer.text === "string" && (steer.origin === "hub" || steer.origin === "local" || steer.origin === "orchestrator") && typeof steer.receivedAt === "number";
23569
23628
  }
23570
23629
 
23571
23630
  // src/core/workflows/workflowRunner.ts
@@ -24402,4 +24461,4 @@ export {
24402
24461
  compileWorkflowPlan,
24403
24462
  collectMcpServersWithOptions
24404
24463
  };
24405
- //# sourceMappingURL=chunk-XITT6UQX.js.map
24464
+ //# sourceMappingURL=chunk-SACVOS6T.js.map