@drisp/cli 0.7.6 → 0.7.8

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.
@@ -21227,6 +21227,11 @@ var BUILD_STRINGS = {
21227
21227
  templatePark: "Drisp was updated during this Build. Build again to re-plan.",
21228
21228
  notStarted: "Not started",
21229
21229
  waiting: "Waiting for child Runs.",
21230
+ /**
21231
+ * The Merge step's Sign-off when the member merges (`merge: you`, root ADR-0009): the `NEEDS_HUMAN` text a child
21232
+ * parks with, `{sha}` being the full head SHA it asks about. The app reads it as "Blocked · Your merge".
21233
+ */
21234
+ 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.",
21230
21235
  notAGitHubRepository: "The project folder is not a git repository with a GitHub origin.",
21231
21236
  cycle: (identifiers) => `These issues block each other in Linear: ${identifiers.join(", ")}.`,
21232
21237
  /** The comment the Orchestrator posts with its one Linear write, moving a merged issue to Done. */
@@ -21255,7 +21260,7 @@ var GATES = [
21255
21260
  },
21256
21261
  {
21257
21262
  title: "Checks",
21258
- passesWhen: "{install}{checks} pass",
21263
+ passesWhen: "{install}{checks} pass, each check run through `drisp build check <name> -- <command>` (for example `drisp build check typecheck -- make typecheck`), which records its outcome for the Build page",
21259
21264
  cannotPass: "This issue's checks still fail after repeated fixes. Open the Thread to see the failing output."
21260
21265
  },
21261
21266
  {
@@ -21281,7 +21286,7 @@ var GATES = [
21281
21286
  ];
21282
21287
 
21283
21288
  // src/core/workflows/builtins/orchestrator/workflow.md
21284
- 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 "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\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. For each listed issue: read it through `linear` (description, acceptance criteria, comments) and\nthe 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",\n "sizeClass": "S|M|L",\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- `qa`: `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- `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`),\nthen 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`, 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';
21289
+ 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';
21285
21290
 
21286
21291
  // src/core/workflows/builtins/orchestrator/index.ts
21287
21292
  var ORCHESTRATOR_WORKFLOW_NAME = "orchestrator";
@@ -21440,6 +21445,14 @@ Journal's last line.
21440
21445
 
21441
21446
  {instructions}
21442
21447
 
21448
+ ## Evidence receipts
21449
+
21450
+ Progress is not proof that a step passed. Follow each Finish instruction only after its acceptance condition holds.
21451
+ Record actual results, commands, output paths and acceptance criteria, never a generic claim of success.
21452
+ Commit repository changes before recording validation evidence. Changes to HEAD or source invalidate validation
21453
+ receipts: rerun affected tests, review, checks and QA, including after a rebase, before verifying the Merge step.
21454
+ Keep evidence files under .athena/evidence/ and do not commit them after validation, since a new commit invalidates receipts.
21455
+
21443
21456
  ## Gates
21444
21457
 
21445
21458
  Work through the gates in this order. Start each gate by running the command its "Start" line gives, which reports the
@@ -21464,6 +21477,9 @@ is in \`ignored\`: it does not block the merge and its name goes in the evidence
21464
21477
  empty, wait a minute and run it again. When \`failed\` is not empty, fix the change; if it still fails, the Merge gate
21465
21478
  cannot pass. Never judge a check by its timing.
21466
21479
 
21480
+ {signOff}Immediately before merging, verify the current revision again: {mergeVerify}. If evidence is stale, rerun the
21481
+ selected validation steps and record fresh receipts before continuing.
21482
+
21467
21483
  Merge with \`gh pr merge --squash --delete-branch --match-head-commit <sha>\`, where \`<sha>\` is HEAD after the last
21468
21484
  push. When it exits non-zero, \`gh pr view --json state\` decides: \`MERGED\` means the merge happened (a worktree
21469
21485
  cannot check out main afterwards); anything else means it did not.
@@ -21492,6 +21508,18 @@ Evidence. Never create issues and never edit an issue's title or description.
21492
21508
 
21493
21509
  When every gate has passed, end with \`<!-- WORKFLOW_COMPLETE -->\` as the Journal's last line.
21494
21510
  `;
21511
+ var MERGE_SIGN_OFF = {
21512
+ passesWhen: "the merge gate below holds, with the member\u2019s Sign-off on HEAD",
21513
+ section: `Sign-off: this Build's member merges, so the merge waits for their Sign-off on the exact head commit. When
21514
+ everything above holds and HEAD is pushed, end the Turn with \`<!-- NEEDS_HUMAN: {signOffText} -->\` as the Journal's
21515
+ last line, where \`<sha>\` is the full \`git rev-parse HEAD\`. The member answers in this Thread. When their reply signs
21516
+ off on that commit, record it with \`{signOffCommand}\`, which refuses any SHA but HEAD; it is your attestation of
21517
+ their reply, so never record one they did not give. Any other reply is guidance: act on it, then ask again. A
21518
+ Sign-off names one commit: after a rebase or any new commit, ask again before merging.
21519
+ When \`gh pr merge\` fails because the branch is protected, the Merge gate cannot pass.
21520
+
21521
+ `
21522
+ };
21495
21523
  var STAGE_TEMPLATE = `### {n}. {title}
21496
21524
 
21497
21525
  Start: run \`{stepCommand}\`.
@@ -21506,12 +21534,13 @@ No gate text: the loop's caps apply.`;
21506
21534
 
21507
21535
  // src/core/workflows/builtins/build-child/render.ts
21508
21536
  var RENDERER_RULES = {
21509
- alwaysSkills: ["tdd", "code-review"],
21537
+ alwaysSkills: [],
21538
+ profiles: ["implementation", "documentation", "investigation"],
21510
21539
  alwaysMcpServers: ["linear"],
21511
21540
  areaSkills: {
21512
21541
  macos: ["swiftui-expert-skill", "apple-docs", "apple-hig-components"]
21513
21542
  },
21514
- qaMcpServers: { web: ["drisp-browser"] },
21543
+ qaMcpServers: { web: ["drisp-browser"], both: ["drisp-browser"] },
21515
21544
  npmTestAreas: ["cli", "hub", "protocol"],
21516
21545
  /**
21517
21546
  * The Checks gate's commands, resolved from the fill's areas and the repo's layout at the plan's commit: a repo
@@ -21572,8 +21601,10 @@ var RENDERER_RULES = {
21572
21601
  permissions: {
21573
21602
  edit: ["Edit(./**)"],
21574
21603
  shell: [
21604
+ "drisp build check",
21575
21605
  "drisp build checks",
21576
21606
  "drisp build step",
21607
+ "drisp build merged",
21577
21608
  "gh pr checks",
21578
21609
  "gh pr create",
21579
21610
  "gh pr list",
@@ -21615,7 +21646,10 @@ var LAYOUT_MARKERS = [
21615
21646
  "apps/cli/package.json"
21616
21647
  ];
21617
21648
  function checkCommands(fill, layout) {
21649
+ if (fill.checks) return [...fill.checks];
21618
21650
  const rules = RENDERER_RULES.checks;
21651
+ if (fill.profile === "documentation" || fill.profile === "investigation")
21652
+ return [...rules.otherwise];
21619
21653
  if (layout.includes("Makefile"))
21620
21654
  return [
21621
21655
  ...rules.rootMakefile,
@@ -21634,7 +21668,11 @@ function checkCommands(fill, layout) {
21634
21668
  function installCommand(fill, layout) {
21635
21669
  const { install } = RENDERER_RULES.checks;
21636
21670
  if (!layout.includes(install.needs)) return void 0;
21637
- const needsNode = layout.includes("Makefile") || checkCommands(fill, layout).some((command) => /^np[mx] /.test(command));
21671
+ const needsNode = checkCommands(fill, layout).some(
21672
+ (command) => /^np[mx] /.test(command) || layout.includes("Makefile") && RENDERER_RULES.checks.rootMakefile.some(
21673
+ (rootCommand) => rootCommand === command
21674
+ )
21675
+ );
21638
21676
  return needsNode ? install.command : void 0;
21639
21677
  }
21640
21678
  var GENERATED_NAME = /^build--([a-z0-9]+)--([A-Z][A-Z0-9]*-[0-9]+)$/;
@@ -21652,6 +21690,8 @@ function renderedSkills(fill) {
21652
21690
  return [
21653
21691
  .../* @__PURE__ */ new Set([
21654
21692
  ...RENDERER_RULES.alwaysSkills,
21693
+ ...usesTestFirst(fill) ? ["tdd"] : [],
21694
+ ...usesReview(fill) ? ["code-review"] : [],
21655
21695
  ...fill.areas.flatMap((area) => RENDERER_RULES.areaSkills[area] ?? [])
21656
21696
  ])
21657
21697
  ].sort();
@@ -21664,8 +21704,24 @@ function renderedMcpServers(fill) {
21664
21704
  ])
21665
21705
  ].sort();
21666
21706
  }
21707
+ function shellCommands(fill) {
21708
+ const commands = [...RENDERER_RULES.permissions.shell];
21709
+ return fill.profile !== "investigation" ? commands : commands.filter(
21710
+ (command) => ![
21711
+ "drisp build merged",
21712
+ "gh pr create",
21713
+ "gh pr merge",
21714
+ "git add",
21715
+ "git commit",
21716
+ "git push",
21717
+ "git rebase",
21718
+ "git restore"
21719
+ ].includes(command)
21720
+ );
21721
+ }
21667
21722
  function renderedPermissions(fill) {
21668
- const { edit, shell, deny } = RENDERER_RULES.permissions;
21723
+ const { edit, deny } = RENDERER_RULES.permissions;
21724
+ const shell = shellCommands(fill);
21669
21725
  return {
21670
21726
  allow: [
21671
21727
  ...edit,
@@ -21675,10 +21731,29 @@ function renderedPermissions(fill) {
21675
21731
  deny: [...deny]
21676
21732
  };
21677
21733
  }
21678
- function gateTitles(fill) {
21679
- return GATES.filter((gate) => gate.title !== "QA" || fill.qa !== "none").map(
21680
- (gate) => gate.title
21734
+ var usesTestFirst = (fill) => fill.testFirst?.enabled ?? (!fill.profile || fill.profile === "implementation");
21735
+ var usesReview = (fill) => fill.review?.enabled ?? (!fill.profile || fill.profile === "implementation");
21736
+ function selectedGates(fill) {
21737
+ const gates = GATES.filter(
21738
+ (gate) => (gate.title !== "QA" || fill.qa !== "none") && (gate.title !== "Test first" || usesTestFirst(fill)) && (gate.title !== "Code review" || usesReview(fill)) && (fill.profile !== "investigation" || !["Pull request", "Merge"].includes(gate.title))
21739
+ ).map(
21740
+ (gate) => fill.profile === "investigation" && gate.title === "Checks" ? {
21741
+ ...gate,
21742
+ 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"
21743
+ } : fill.profile === "investigation" && gate.title === "Evidence" ? {
21744
+ ...gate,
21745
+ 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"
21746
+ } : fill.merge === "you" && gate.title === "Merge" ? { ...gate, passesWhen: MERGE_SIGN_OFF.passesWhen } : gate
21681
21747
  );
21748
+ if (!usesTestFirst(fill))
21749
+ gates.splice(1, 0, {
21750
+ title: fill.profile === "investigation" ? "Investigate" : "Implement",
21751
+ passesWhen: fill.profile === "investigation" ? "the question is investigated and findings address every acceptance criterion; no repository changes are made" : "the requested change is implemented and each acceptance criterion is checked"
21752
+ });
21753
+ return gates;
21754
+ }
21755
+ function gateTitles(fill) {
21756
+ return selectedGates(fill).map((gate) => gate.title);
21682
21757
  }
21683
21758
  function summarizeInstructions(instructions) {
21684
21759
  const paragraph = (instructions.trim().split(/\n\s*\n/)[0] ?? "").replace(/\s+/g, " ").trim();
@@ -21693,21 +21768,31 @@ function checksText(commands) {
21693
21768
  return quoted.length === 1 ? quoted[0] : `${quoted.slice(0, -1).join(", ")} and ${quoted.at(-1)}`;
21694
21769
  }
21695
21770
  var fillSlots = (template, slots) => template.replace(/\{(\w+)\}/g, (whole, key) => slots[key] ?? whole);
21771
+ function selectionFlags(fill) {
21772
+ return `${fill.profile ? ` --profile ${fill.profile}` : ""}${fill.testFirst ? ` --test-first ${fill.testFirst.enabled}` : ""}${fill.review ? ` --review ${fill.review.enabled}` : ""}`;
21773
+ }
21774
+ var mergeFlag = (fill) => fill.merge === "you" ? " --merge you" : "";
21696
21775
  function renderStages(fill, checks, install) {
21697
- const gates = GATES.filter((gate) => gate.title !== "QA" || fill.qa !== "none");
21776
+ const gates = selectedGates(fill);
21698
21777
  return gates.map((gate, index) => {
21699
21778
  const passesWhen = fillSlots(gate.passesWhen, {
21700
21779
  install: install ? `after \`${install}\`, ` : "",
21701
21780
  checks: checksText(checks),
21702
- qa: fill.qa === "none" ? "" : QA_SECTIONS[fill.qa]
21781
+ qa: fill.qa === "none" ? "" : fill.qa === "both" ? `${QA_SECTIONS.web}; also ${QA_SECTIONS.macos}` : QA_SECTIONS[fill.qa]
21703
21782
  });
21783
+ const command = `drisp build step ${index + 1} --qa ${fill.qa}${selectionFlags(fill)}`;
21704
21784
  const stage = fillSlots(STAGE_TEMPLATE, {
21705
21785
  n: String(index + 1),
21706
21786
  title: gate.title,
21707
- stepCommand: `drisp build step ${index + 1} --qa ${fill.qa}`,
21787
+ stepCommand: command,
21708
21788
  passesWhen
21709
21789
  });
21710
- return stage + (gate.cannotPass ? fillSlots(CANNOT_PASS_TEMPLATE, { cannotPass: gate.cannotPass }) : NO_GATE_TEXT_TEMPLATE);
21790
+ const finish = `${command}${gate.title === "Merge" ? mergeFlag(fill) : ""}`;
21791
+ return stage + (["Merge", "Evidence"].includes(gate.title) ? `
21792
+
21793
+ Before doing this step: run \`${command} --verify\`. Missing or stale evidence must be refreshed first.` : "") + `
21794
+
21795
+ 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);
21711
21796
  }).join("\n\n");
21712
21797
  }
21713
21798
  function render(fill, workflowName, layout) {
@@ -21717,7 +21802,7 @@ function render(fill, workflowName, layout) {
21717
21802
  const limits = sizeClassLimits(fill.sizeClass);
21718
21803
  const workflow = {
21719
21804
  name: workflowName,
21720
- description: fillSlots(DESCRIPTION_TEMPLATE, {
21805
+ description: fill.profile === "investigation" ? `Investigates ${parsed.identifier} and delivers findings with acceptance evidence.` : fillSlots(DESCRIPTION_TEMPLATE, {
21721
21806
  identifier: parsed.identifier
21722
21807
  }),
21723
21808
  promptTemplate: "{input}",
@@ -21734,12 +21819,45 @@ function render(fill, workflowName, layout) {
21734
21819
  permissions: renderedPermissions(fill)
21735
21820
  };
21736
21821
  const checks = checkCommands(fill, layout);
21737
- const workflowMd = fillSlots(WORKFLOW_MD_TEMPLATE, {
21822
+ let template = WORKFLOW_MD_TEMPLATE;
21823
+ if (!usesReview(fill))
21824
+ template = template.replaceAll(
21825
+ "both reviewers pass;",
21826
+ "review is omitted by the approved plan;"
21827
+ ).replaceAll(
21828
+ "Review: Standards \u2713 \xB7 Spec \u2713",
21829
+ "Review: omitted by the approved plan"
21830
+ );
21831
+ if (fill.profile === "investigation") {
21832
+ template = template.replace(
21833
+ /## Merge gate[\s\S]*?## Linear/,
21834
+ "## Findings\n\nPost findings and acceptance evidence on the Linear issue. No pull request or merge is required.\n\n## Linear"
21835
+ );
21836
+ template = template.replace(
21837
+ "Your Linear writes are exactly: In Progress in Explore, `PR: <url>` in Pull request, the evidence comment and Done in\nEvidence.",
21838
+ "Your Linear writes are In Progress in Explore, findings and evidence, and Done in Evidence."
21839
+ );
21840
+ }
21841
+ const mergeCommand = `drisp build step ${gateTitles(fill).indexOf("Merge") + 1} --qa ${fill.qa}${selectionFlags(fill)}`;
21842
+ const workflowMd = fillSlots(template, {
21738
21843
  identifier: parsed.identifier,
21739
- instructions: fill.instructions.trim(),
21844
+ instructions: [
21845
+ fill.instructions.trim(),
21846
+ fill.rationale ? `Plan rationale: ${fill.rationale}` : "",
21847
+ fill.testFirst ? `Test first: ${fill.testFirst.enabled ? "included" : "omitted"} \u2014 ${fill.testFirst.reason}` : "",
21848
+ fill.review ? `Code review: ${fill.review.enabled ? "included" : "omitted"} \u2014 ${fill.review.reason}` : "",
21849
+ ...(fill.acceptanceEvidence ?? []).map(
21850
+ (item) => `Acceptance evidence: ${item}`
21851
+ )
21852
+ ].filter(Boolean).join("\n\n"),
21740
21853
  gates: renderStages(fill, checks, installCommand(fill, layout)),
21854
+ signOff: fill.merge === "you" ? fillSlots(MERGE_SIGN_OFF.section, {
21855
+ signOffText: BUILD_STRINGS.mergeSignOff.replace("{sha}", "<sha>"),
21856
+ signOffCommand: `${mergeCommand} --merge you --sign-off <sha>`
21857
+ }) : "",
21858
+ mergeVerify: `\`${mergeCommand}${mergeFlag(fill)} --verify\``,
21741
21859
  checksEvidence: checks.map((command) => `${command} \u2713`).join(" \xB7 "),
21742
- shellCommands: checksText([...RENDERER_RULES.permissions.shell])
21860
+ shellCommands: checksText(shellCommands(fill))
21743
21861
  });
21744
21862
  return { workflowJson: `${JSON.stringify(workflow, null, 2)}
21745
21863
  `, workflowMd };
@@ -24189,6 +24307,7 @@ function collectMcpServersWithOptions(pluginDirs) {
24189
24307
  export {
24190
24308
  WAITING_ON_CHILDREN_STOP_REASON,
24191
24309
  AWAITING_APPROVAL_STOP_REASON,
24310
+ budgetTokens,
24192
24311
  createSteerQueue,
24193
24312
  DEFAULT_PERMISSION_GRACE_MS,
24194
24313
  serializeRunMemory,
@@ -24228,6 +24347,7 @@ export {
24228
24347
  DESCRIPTION_TEMPLATE,
24229
24348
  QA_SECTIONS,
24230
24349
  WORKFLOW_MD_TEMPLATE,
24350
+ MERGE_SIGN_OFF,
24231
24351
  STAGE_TEMPLATE,
24232
24352
  CANNOT_PASS_TEMPLATE,
24233
24353
  NO_GATE_TEXT_TEMPLATE,
@@ -24254,4 +24374,4 @@ export {
24254
24374
  compileWorkflowPlan,
24255
24375
  collectMcpServersWithOptions
24256
24376
  };
24257
- //# sourceMappingURL=chunk-W57EQYCM.js.map
24377
+ //# sourceMappingURL=chunk-OYUBT6RS.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runMachineRunners
3
- } from "./chunk-2N7IBKJF.js";
3
+ } from "./chunk-HGMJ3YL2.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-Y6XJPUR2.js.map
16
+ //# sourceMappingURL=chunk-TBUUNMC6.js.map