Add implement-workflow.mjs — a Workflow script that runs one worktree-isolated Sonnet worker per lane with after: edges expressed as promise-ordered pipeline stages — wire its selection into build behind workflow: always, --workflow, or six or more lanes, and pin it with a static test suite, all authored as three worker lanes plus lead.
The lane dispatcher runs every laned plan on the Agent tool today; this adds the Workflow-engine escalation for six-plus lanes or workflow: always, and it is the first plan ever authored with ### Lane: headings — building it with the new dispatcher is the pilot run the lane feature has been waiting on. Done when the three lanes merge back in order, the script parses as a workflow body, and the tests exit 0 on the merged tree.
Read and implement all steps in the plan at docs/plans/add-workflow-engine-escalation.md — Ship the Workflow-engine escalation as the lane pilot. Verify against the plan's Tests, Verification, and Acceptance Criteria before reporting done. If everything passed, mark completion in docs/plans/add-workflow-engine-escalation.md — tick each step's [x] marker and each criterion's - [x], set status: completed — and re-render the HTML from the spec. If any check failed, leave status: in-progress and say which.
More ways to run this plan — goal & workflow prompts, file path
Achieve this goal: Ship the Workflow-engine escalation as the lane pilot. The plan at docs/plans/add-workflow-engine-escalation.md describes one approach — use it as reference, but optimize for the outcome. Fan out across parallel subagents where that serves the outcome. Verify against the plan's Tests, Verification, and Acceptance Criteria before reporting done. If everything passed, mark completion in docs/plans/add-workflow-engine-escalation.md — tick each step's [x] marker and each criterion's - [x], set status: completed — and re-render the HTML from the spec. If any check failed, leave status: in-progress and say which.
Run a workflow to implement the plan at docs/plans/add-workflow-engine-escalation.md — Ship the Workflow-engine escalation as the lane pilot. Brief subagents with the plan file at docs/plans/add-workflow-engine-escalation.md. Reserve a final verification phase for the lead agent, not a subagent. Verify against the plan's Tests, Verification, and Acceptance Criteria before reporting done. If everything passed, mark completion in docs/plans/add-workflow-engine-escalation.md — tick each step's [x] marker and each criterion's - [x], set status: completed — and re-render the HTML from the spec. If any check failed, leave status: in-progress and say which.
You are implementing lane "script" of the plan at docs/plans/add-workflow-engine-escalation.md — Ship the Workflow-engine escalation as the lane pilot.
You own ONLY these paths: kit/plugins/plan-agent/skills/build/references/implement-workflow.mjs. Do not create, edit, or delete any file outside them; if a step needs one, stop and report it.
1. git checkout -b <plan-branch>--script <plan-branch>
2. Implement these steps in order, exactly as written:
1. Write kit/plugins/plan-agent/skills/build/references/implement-workflow.mjs, a Workflow script mirroring kit/plugins/plan-agent/skills/review-plan/references/review-workflow.mjs in shape — read that file first and copy its header-comment style, its `export const meta` pure literal, and its use of `agent()` — with this contract: the header comment states that the script is passed inline as the Workflow tool's `script` input and never launched by path, that it is selected only on a plan with 2 or more lanes and only by `workflow: always`, the `--workflow` flag, or 6 or more lanes, that fewer than 2 lanes runs sequentially and only emits the /workflows prompt, and that the Workflow tool is probed for availability and never version-asserted; `export const meta` has name `plan-implement-lanes`, a one-line description, and phases `Implement` and `Report`; the body reads `args.planPath`, `args.planBranch`, `args.workerModel` (default `'sonnet'`), and `args.lanes` — an array of `{ name, owns, after, brief }` where `brief` is the fully substituted worker brief — and throws an Error naming the missing key when `planPath`, `planBranch`, or `lanes` is absent or `lanes` is empty; it skips any lane named `lead` (the lead runs it, never a worker); it defines `const LANE_REPORT` as a JSON schema with `type: 'object'`, `required: ['lane', 'branch', 'steps_done', 'verify', 'files_changed', 'blocked']`, where `steps_done` and `files_changed` are arrays of strings and the rest are strings; wave ordering is a `Map` from lane name to a promise — each lane's promise first awaits the promises of every lane its `after:` names, then calls `agent(lane.brief, { label: 'lane:' + lane.name, phase: 'Implement', isolation: 'worktree', agentType: 'general-purpose', model: workerModel, schema: LANE_REPORT })` and resolves to that result (null when the worker died — never reject); after every promise settles it calls `log()` with one line counting reported and dead lanes and returns `{ reports, mergeOrder }` where `reports` is `[{ name, after, report }]` and `mergeOrder` is the worker lane names topologically sorted by `after:`; it uses no `Date.now()`, `Math.random()`, `new Date()`, or Node API, and never runs git. Why: the Workflow engine mirrors the proven review-workflow shape, and a promise per lane is what turns `after:` edges into pipeline stages without a barrier. Verify: `node --input-type=module -e "import('node:fs').then(fs=>{const s=fs.readFileSync('kit/plugins/plan-agent/skills/build/references/implement-workflow.mjs','utf8');new (Object.getPrototypeOf(async function(){}).constructor)('args','agent','pipeline','parallel','log','phase','budget','workflow',s.replace(/^export /gm,''));console.log('parses')})"` prints `parses`, and `grep -c "LANE_REPORT\|mergeOrder\|isolation: 'worktree'\|6 or more lanes" kit/plugins/plan-agent/skills/build/references/implement-workflow.mjs` is at least 4.
3. After each step run its Verify command and record pass or fail.
4. Commit on your branch after the last step. Do not push. Do not edit docs/plans/add-workflow-engine-escalation.md.
5. End with exactly this block:
LANE REPORT
lane: script
branch: <plan-branch>--script
steps_done: <comma-separated step numbers>
verify: <N: pass|fail, one per step>
files_changed: <paths>
blocked: <none | what and why>
You are implementing lane "wiring" of the plan at docs/plans/add-workflow-engine-escalation.md — Ship the Workflow-engine escalation as the lane pilot.
You own ONLY these paths: kit/plugins/plan-agent/skills/build/SKILL.md, kit/plugins/plan-agent/skills/build/references/dispatch-lanes.md, kit/plugins/plan-agent/skills/build/references/invocation.md. Do not create, edit, or delete any file outside them; if a step needs one, stop and report it.
1. git checkout -b <plan-branch>--wiring <plan-branch>
2. Implement these steps in order, exactly as written:
2. Append a `## Workflow engine (escalation)` section to the end of kit/plugins/plan-agent/skills/build/references/dispatch-lanes.md stating: it is selected only on a spec with 2 or more lanes, and only by `workflow: always`, the `--workflow` flag, or 6 or more lanes — fewer than 2 lanes runs the sequential Step 2 and only emits the /workflows prompt, whatever the frontmatter says; before using it, probe that the `Workflow` tool is callable in this session the way review-plan Step 3 does, never asserting a version, and when it is absent print exactly `This laned build asked for the Workflow engine, which is not available in this session; re-run without --workflow (or with workflow: auto) to use the Agent dispatcher.` and stop; otherwise Read `references/implement-workflow.mjs` and pass its contents as the Workflow tool's inline `script` input, never by path, with `args` as a real object — `planPath`, `planBranch`, `workerModel`, and `lanes` as `[{ name, owns, after, brief }]` where each brief is the renderer's worker brief with `<plan-branch>` substituted and `lead` omitted; on return merge each lane in the returned `mergeOrder` using section 6 exactly as the Agent path does, then run `lead` and the gates; and that calling Workflow from this skill is authorized by the tool's own opt-in rule for a skill whose instructions say to call it. Why: one merge procedure serves both engines, and the escalation needs a canonical home the core can point at without growing. Verify: `grep -c "Workflow engine (escalation)\|--workflow\|6 or more lanes\|implement-workflow.mjs\|not available in this session" kit/plugins/plan-agent/skills/build/references/dispatch-lanes.md` prints at least 5.
3. In kit/plugins/plan-agent/skills/build/SKILL.md, add `[--workflow]` to the `argument-hint` and extend the "2 or more lanes" bullet of Step 2 with one clause — `workflow: always`, `--workflow`, or 6+ lanes takes that reference's Workflow engine section — without pushing the file to 600 words or more (count with `python3 -c "print(len(open('kit/plugins/plan-agent/skills/build/SKILL.md',encoding='utf-8').read().split()))"`; trim only prose you added if needed, never the sequential wording, the permission warning, or the plan-mode guard line); then in kit/plugins/plan-agent/skills/build/references/invocation.md add `--workflow` to the Rule 0 strip list and a valueless-flag bullet beside `--sequential` saying it selects the Workflow engine on a laned spec, is ignored below 2 lanes, and that `--sequential` and `--workflow` together is an error naming both. Why: the core stays a pointer under its word ceiling while the flag parses like every other flag. Verify: `bash tests/plugins/test-progressive-disclosure.sh` and `bash tests/plugins/test-build-skill.sh` both exit 0, and `grep -c -- "--workflow" kit/plugins/plan-agent/skills/build/SKILL.md kit/plugins/plan-agent/skills/build/references/invocation.md` shows at least 1 for each file.
3. After each step run its Verify command and record pass or fail.
4. Commit on your branch after the last step. Do not push. Do not edit docs/plans/add-workflow-engine-escalation.md.
5. End with exactly this block:
LANE REPORT
lane: wiring
branch: <plan-branch>--wiring
steps_done: <comma-separated step numbers>
verify: <N: pass|fail, one per step>
files_changed: <paths>
blocked: <none | what and why>
You are implementing lane "tests" of the plan at docs/plans/add-workflow-engine-escalation.md — Ship the Workflow-engine escalation as the lane pilot.
You own ONLY these paths: tests/implement-workflow.test.mjs. Do not create, edit, or delete any file outside them; if a step needs one, stop and report it.
1. git checkout -b <plan-branch>--tests <plan-branch>
2. Implement these steps in order, exactly as written:
4. Write tests/implement-workflow.test.mjs mirroring tests/review-plan-workflow.test.mjs — read it first and reuse its `check()` helper and its AsyncFunction `parsesAsWorkflowBody()` trick verbatim — asserting on kit/plugins/plan-agent/skills/build/references/implement-workflow.mjs that: the file exists and parses as a workflow body; `export const meta` names `plan-implement-lanes` and a phase titled `Implement`; the `LANE_REPORT` schema requires `lane`, `branch`, `steps_done`, `verify`, `files_changed`, and `blocked`; wave ordering is derived from `after:` — the source awaits the after-named lanes' promises before its `agent(` call, and that call passes `isolation: 'worktree'` and `schema: LANE_REPORT`; a lane named `lead` is skipped; the script returns `mergeOrder`; the selection gate is documented in the header — `workflow: always`, `--workflow`, and `6 or more lanes` all appear, as does the fewer-than-2-lanes sequential rule; the script says the Workflow tool is probed rather than version-asserted; and the source uses none of `Date.now`, `Math.random`, `new Date(`, or `require(`. End with the PASS/FAIL tally and `process.exit(fail > 0 ? 1 : 0)`. Why: a workflow script has no runner of its own, so the suite is the contract that keeps the script and the selection rule from drifting apart. Verify: `node tests/implement-workflow.test.mjs` exits 0 with every line PASS.
3. After each step run its Verify command and record pass or fail.
4. Commit on your branch after the last step. Do not push. Do not edit docs/plans/add-workflow-engine-escalation.md.
5. End with exactly this block:
LANE REPORT
lane: tests
branch: <plan-branch>--tests
steps_done: <comma-separated step numbers>
verify: <N: pass|fail, one per step>
files_changed: <paths>
blocked: <none | what and why>
add-workflow-engine-escalation.html
docs/plans/add-workflow-engine-escalation.html
docs/plans/add-workflow-engine-escalation.md
Context
The story behind this plan — what prompted the work and why it matters now.
Phase 3 of docs/plans/add-lane-orchestration.md made build a lane dispatcher on the Agent tool. The proposal's decision 6 names a Workflow script as the escalation for six or more lanes or workflow: always, mirroring the shape review-plan already proves in kit/plugins/plan-agent/skills/review-plan/references/review-workflow.mjs. This plan ships that script and is deliberately the first laned plan in the repository: the script, wiring, and tests lanes own disjoint files, wiring and tests both wait on script, and lead holds the version bump, changelog, and docs. Building it with /plan-agent:build is the pilot — the dispatch run's wall-clock and tokens against a --sequential run of the same plan are the numbers the 9.17.0 changelog publishes.
Constraints every lane inherits: kit/plugins/plan-agent/skills/build/SKILL.md must stay under 600 words (tests/plugins/test-progressive-disclosure.sh), no Markdown under kit/plugins/ may carry a shell-expansion sequence (tests/plugins/test-no-shell-expansion.sh), and a Workflow script is a hybrid — module-level export const meta plus a top-level return — that node --check rejects, so it is parsed the way tests/review-plan-workflow.test.mjs parses review-workflow.mjs.
Decisions
Choices already settled — read these before re-opening any of them.
- Wave ordering is a promise per lane, not a barrier: each lane awaits the promises its
after:names and then callsagent(), so a lane starts the moment its dependencies finish and a dead worker resolves to null instead of rejecting the run. - The script never runs git. It returns the reports and a topological
mergeOrder; the lead merges lane branches with the same section-6 audit the Agent path uses, so there is one merge procedure, not two. - The selection gate is probed, never version-asserted, exactly as
review-planStep 3 probes for the Workflow tool. testsdepends only onscript, not onwiring: the selection rule is stated in the script's own header comment so the suite can assert it without waiting for the skill text, which keepswiringandtestsin the same wave.
Files that change
Every file this plan touches, and what happens to each one.
kit/plugins/plan-agent/skills/build/references/implement-workflow.mjsnew the Workflow-engine scripttests/implement-workflow.test.mjsnew parse, schema, wave ordering, selection gate, hard-stopkit/plugins/plan-agent/skills/build/references/dispatch-lanes.mdmodified the Workflow engine sectionkit/plugins/plan-agent/skills/build/SKILL.mdmodified the selection clause and--workflowin the argument hintkit/plugins/plan-agent/skills/build/references/invocation.mdmodified the--workflowflag.claude-plugin/marketplace.jsonmodified 9.17.0kit/plugins/plan-agent/CHANGELOG.mdmodified the 9.17.0 entry with the pilot numbersREADME.mdmodified regenerated Plugin Reference Tabledocs/guides/how-to/plan-agent.mdmodified the build entry's command and flagsCHANGELOG.mdmodified the root Unreleased bullets
Lanes
Independent groups of steps, each with the paths it owns — one worker per lane, merged back in dependency order.
-
lane script step 1
Owns
kit/plugins/plan-agent/skills/build/references/implement-workflow.mjs
-
lane wiring steps 2–3
Owns
kit/plugins/plan-agent/skills/build/SKILL.mdkit/plugins/plan-agent/skills/build/references/dispatch-lanes.mdkit/plugins/plan-agent/skills/build/references/invocation.md
Afterscript
-
lane tests step 4
Owns
tests/implement-workflow.test.mjs
Afterscript
-
lane lead step 5
Runs in the main session — owns the shared filesAfter
scriptwiringtests
Steps
The step-by-step work, in order — each step says what to do, why it matters, and how to check it worked.
export const meta pure literal, and its use of agent() — with this contract: the header comment states that the script is passed inline as the Workflow tool's script input and never launched by path, that it is selected only on a plan with 2 or more lanes and only by workflow: always, the --workflow flag, or 6 or more lanes, that fewer than 2 lanes runs sequentially and only emits the /workflows prompt, and that the Workflow tool is probed for availability and never version-asserted; export const meta has name plan-implement-lanes, a one-line description, and phases Implement and Report; the body reads args.planPath, args.planBranch, args.workerModel (default 'sonnet'), and args.lanes — an array of { name, owns, after, brief } where brief is the fully substituted worker brief — and throws an Error naming the missing key when planPath, planBranch, or lanes is absent or lanes is empty; it skips any lane named lead (the lead runs it, never a worker); it defines const LANE_REPORT as a JSON schema with type: 'object', required: ['lane', 'branch', 'steps_done', 'verify', 'files_changed', 'blocked'], where steps_done and files_changed are arrays of strings and the rest are strings; wave ordering is a Map from lane name to a promise — each lane's promise first awaits the promises of every lane its after: names, then calls agent(lane.brief, { label: 'lane:' + lane.name, phase: 'Implement', isolation: 'worktree', agentType: 'general-purpose', model: workerModel, schema: LANE_REPORT }) and resolves to that result (null when the worker died — never reject); after every promise settles it calls log() with one line counting reported and dead lanes and returns { reports, mergeOrder } where reports is [{ name, after, report }] and mergeOrder is the worker lane names topologically sorted by after:; it uses no Date.now(), Math.random(), new Date(), or Node API, and never runs git.
after: edges into pipeline stages without a barrier.node --input-type=module -e "import('node:fs').then(fs=>{const s=fs.readFileSync('kit/plugins/plan-agent/skills/build/references/implement-workflow.mjs','utf8');new (Object.getPrototypeOf(async function(){}).constructor)('args','agent','pipeline','parallel','log','phase','budget','workflow',s.replace(/^export /gm,''));console.log('parses')})" prints parses, and grep -c "LANE_REPORT\|mergeOrder\|isolation: 'worktree'\|6 or more lanes" kit/plugins/plan-agent/skills/build/references/implement-workflow.mjs is at least 4.## Workflow engine (escalation) section to the end of kit/plugins/plan-agent/skills/build/references/dispatch-lanes.md stating: it is selected only on a spec with 2 or more lanes, and only by workflow: always, the --workflow flag, or 6 or more lanes — fewer than 2 lanes runs the sequential Step 2 and only emits the /workflows prompt, whatever the frontmatter says; before using it, probe that the Workflow tool is callable in this session the way review-plan Step 3 does, never asserting a version, and when it is absent print exactly This laned build asked for the Workflow engine, which is not available in this session; re-run without --workflow (or with workflow: auto) to use the Agent dispatcher. and stop; otherwise Read references/implement-workflow.mjs and pass its contents as the Workflow tool's inline script input, never by path, with args as a real object — planPath, planBranch, workerModel, and lanes as [{ name, owns, after, brief }] where each brief is the renderer's worker brief with <plan-branch> substituted and lead omitted; on return merge each lane in the returned mergeOrder using section 6 exactly as the Agent path does, then run lead and the gates; and that calling Workflow from this skill is authorized by the tool's own opt-in rule for a skill whose instructions say to call it.
grep -c "Workflow engine (escalation)\|--workflow\|6 or more lanes\|implement-workflow.mjs\|not available in this session" kit/plugins/plan-agent/skills/build/references/dispatch-lanes.md prints at least 5.[--workflow] to the argument-hint and extend the "2 or more lanes" bullet of Step 2 with one clause — workflow: always, --workflow, or 6+ lanes takes that reference's Workflow engine section — without pushing the file to 600 words or more (count with python3 -c "print(len(open('kit/plugins/plan-agent/skills/build/SKILL.md',encoding='utf-8').read().split()))"; trim only prose you added if needed, never the sequential wording, the permission warning, or the plan-mode guard line); then in kit/plugins/plan-agent/skills/build/references/invocation.md add --workflow to the Rule 0 strip list and a valueless-flag bullet beside --sequential saying it selects the Workflow engine on a laned spec, is ignored below 2 lanes, and that --sequential and --workflow together is an error naming both.
bash tests/plugins/test-progressive-disclosure.sh and bash tests/plugins/test-build-skill.sh both exit 0, and grep -c -- "--workflow" kit/plugins/plan-agent/skills/build/SKILL.md kit/plugins/plan-agent/skills/build/references/invocation.md shows at least 1 for each file.check() helper and its AsyncFunction parsesAsWorkflowBody() trick verbatim — asserting on kit/plugins/plan-agent/skills/build/references/implement-workflow.mjs that: the file exists and parses as a workflow body; export const meta names plan-implement-lanes and a phase titled Implement; the LANE_REPORT schema requires lane, branch, steps_done, verify, files_changed, and blocked; wave ordering is derived from after: — the source awaits the after-named lanes' promises before its agent( call, and that call passes isolation: 'worktree' and schema: LANE_REPORT; a lane named lead is skipped; the script returns mergeOrder; the selection gate is documented in the header — workflow: always, --workflow, and 6 or more lanes all appear, as does the fewer-than-2-lanes sequential rule; the script says the Workflow tool is probed rather than version-asserted; and the source uses none of Date.now, Math.random, new Date(, or require(. End with the PASS/FAIL tally and process.exit(fail > 0 ? 1 : 0).
node tests/implement-workflow.test.mjs exits 0 with every line PASS.git fetch origin && BASE_REF=main node scripts/check-plugin-versions.mjs and bash scripts/verify.sh both exit 0.Tests
The tests that prove the change does what it promises.
Definition of done
The plan counts as done when every statement below is true — check each one off as you verify it.
Final check
One last pass to confirm the whole change works end to end.
Build this plan with /plan-agent:build docs/plans/add-workflow-engine-escalation.md and watch it commit the spec, dispatch the script worker, then wiring and tests together once script merges, audit each lane branch's diff against its owns:, and merge --no-ff in that order; then run lead in the session. On the merged tree node tests/implement-workflow.test.mjs exits 0, bash tests/run-all.sh exits 0 with the new suite included, and bash tests/plugins/test-progressive-disclosure.sh confirms the build core stayed under its ceiling. Rebuild the same plan with --sequential in a fresh session on a scratch branch, and record both sessions' wall-clock and /usage tokens into the 9.17.0 changelog entry.
Wrapping up
Three gates that must all pass before this plan is marked completed.
Completion Report
No items to report — all requirements met.