Platform Note: Project agents live in <state-dir>/agents/ where <state-dir> is .claude/ (Claude Code), .codex/ (Codex CLI), .cursor/ (Cursor IDE), or .pi/ (Pi). On Cursor IDE and Pi v1, parallel agent dispatch is not available — present wave tasks as a sequential execution list instead. See skills/_shared/platform-tools.md.
Session Plan Skill
Project-instruction file resolution: CLAUDE.md and AGENTS.md (Codex CLI) are transparent aliases — see skills/_shared/instruction-file-resolution.md. Wherever this skill mentions CLAUDE.md, the alias rule applies.
Phase 0.5: Parallel-Aware Preamble
Skip silently when persistence: false in Session Config.
Before any Phase 1 work, run the parallel-aware preamble per skills/_shared/parallel-aware-preamble.md. The preamble detects other active sessions in the worktree-family via findPeers(repoRoot, { mySessionId }), classifies the caller's mode via classifyMode(callerMode) against the exclusivity-matrix, and either:
- Returns
PASS_THROUGH (no other session / always-ok mode) → continue to Phase 1
- Returns
EXCLUSIVE_BLOCKED → fires Exclusive-Conflict AUQ from skills/_shared/parallel-aware-auq.md
- Returns
PROMOTION_OFFER → fires Worktree-Promotion AUQ (via enterWorktree() from scripts/lib/autopilot/worktree-pipeline.mjs — see parallel-aware-auq.md outcome-handling)
On any non-PASS_THROUGH outcome that does not result in immediate exit, append a Deviation to STATE.md via appendDeviationOnDisk(repoRoot, isoTimestamp, message) from scripts/lib/state-md.mjs.
Implementation reference: skills/_shared/parallel-aware-preamble.md § Implementation.
AUQ reference: skills/_shared/parallel-aware-auq.md.
Purpose
Transform the agreed session scope (from session-start Q&A) into an executable wave plan (using role-based assignment) with specific agent assignments, file scopes, and acceptance criteria per task.
Input: Session Scope
This skill receives the agreed session scope from session-start. The scope includes:
- Issue list: VCS issue numbers and titles selected by the user
- Session type: housekeeping, feature, or deep
- Recommended focus: the option the user selected in session-start Phase 7
- Session Config: parsed JSON from
parse-config.mjs
- Express-path signal (optional): session-start Phase 8.5 may set
EXPRESS_PATH=true in the handoff context when the activation conditions are met.
These are passed via the conversation context (not a file). Parse the preceding session-start output to extract the agreed scope.
Optional private capability context
Before either the express path or task decomposition, apply
Private capability context when the
owner explicitly supplies or authorizes a local catalog lookup for a known
private/internal planning audience. Reuse the bounded findings already supplied
by /plan new when applicable; do not repeat the same lookup. This step does not
require persistence. With no authorized context, or a public/unknown audience,
skip it without a prompt or lookup and continue the existing flow. Eligible
source references inform reuse alternatives and verification tasks; a catalog
match does not expand the agreed implementation scope or disable the express path.
Express Path Short-Circuit (#214)
Check this before Step 0. If the express path is active, this skill emits a minimal 1-wave plan and exits — no role decomposition, no wave splitting, no agent count computation.
Phase 8.5 of session-start hands off here NORMALLY when the express path activates — it does not skip session-plan (#1146). The banner below is printed by node scripts/express-path.mjs, and the 1-wave plan this section emits is the artifact /go detects.
Detect express-path activation: Search the conversation context for the banner line:
Express path activated — <N> tasks, coordinator-direct, no inter-wave checks.
If found AND express-path.enabled is true in Session Config (read via Step 0 below — skip only that field check if config read is needed):
Emit this 1-wave plan and exit the skill immediately (do not continue to Step 1 or beyond):
## Wave Plan (Session: housekeeping, 1 wave, isolation: none) [Express Path]
### Wave 1: Coordinator-Direct (<N> tasks)
- All agreed tasks executed sequentially by the coordinator — no subagents dispatched.
- Tasks: [list agreed issues/tasks]
- Isolation: none (coord-direct)
- Max-turns: N/A (coordinator executes directly)
### Execution Config
- Waves: 1 | Agents-per-wave: 0 (coordinator-direct) | Isolation: none
- Express path: active (housekeeping + scope ≤ 3 + no parallel agents needed)
- Total agents planned: 0
Express path — no inter-wave checks. Use /go to begin.
The express path's 1-wave plan is the same shape housekeeping resolves to — one wave with coordinatorDirect: true and no dispatched agents (scripts/session-shape.mjs --session-type housekeeping). The express path stays as written above; it does not need to call the shape resolver to know that.
When express-path banner is absent or express-path.enabled: false: Proceed to Step 0 and the full planning flow as normal.
Step 0: Read Session Config
Read and parse Session Config per skills/_shared/config-reading.md. Store result as $CONFIG.
Extract these fields for planning:
waves — number of execution waves; resolved by scripts/session-shape.mjs (totalWaves), do not compute by hand. The shape reports in wavesConfigHonored whether the configured value was used at all, and says why in notes.
agents-per-wave (may have session-type overrides per config-reading.md) — the operator's ceiling; the per-wave cap that actually binds is resolved by scripts/session-shape.mjs (waves[].agentCap), do not compute by hand.
isolation — Session Config input (worktree / none / auto) that feeds configIsolation into the graduated per-wave rule (resolveIsolation, issue #194, in scripts/lib/wave-sizing.mjs: an explicit config value always wins; otherwise ≤2 agents → none, ≥5 agents → worktree, 3-4 agents → none for housekeeping else worktree). The RESOLVED value for a given wave is waves[].isolation in the shape's JSON output (scripts/session-shape.mjs) — a wave with coordinatorDirect: true, or a read-only wave, resolves none without calling resolveIsolation at all. Do not compute by hand; the plan header's Isolation: line is copied straight from that wave entry.
enforcement (default: warn) — Session Config input (strict / warn / off) that feeds configEnforcement into resolveEnforcement (same module); the resolved per-wave value is waves[].enforcement. Isolation none auto-promotes warn to strict, since the scope-enforcement hook is then the only barrier left.
max-turns — agent turn budget; resolved by scripts/session-shape.mjs (waves[].maxTurns), do not compute by hand.
agent-mapping (optional) — explicit role-to-agent bindings
persistence (default: true) — whether to use STATE.md and learnings
Fallback: If session-start already output a ## Session Config (active) block in the conversation context, extract values from there to avoid a redundant parse. If not present in context, parse independently.
Step 1: Task Decomposition
- Check for resume context: > Skip if
persistence is false in Session Config.
If <state-dir>/STATE.md exists with status: active or status: paused, read it to understand:
- Which waves were completed in the prior session
- Which agents completed, which were partial/failed
- What deviations were logged
- Use this to avoid re-doing completed work and to prioritize carryover tasks
If no STATE.md or
status: completed, proceed with fresh planning.
0.5. Read project intelligence: > Skip if persistence is false in Session Config.
If .orchestrator/metrics/learnings.jsonl exists, read active learnings (confidence > 0.3, not expired). Sort by confidence DESC (tiebreaker: created_at DESC) and slice to the first learnings-surface-top-n entries (default 15) before applying the four categories below. If the top-N slice is empty, skip the categories.
- Fragile files: if any planned task touches a known fragile file, note it as a warning in the agent spec
- Effective sizing: use historical sizing data to inform Step 3 complexity scoring
- Recurring issues: pre-populate risk mitigation with known issue patterns
- Scope guidance: validate planned scope against historical session capacity
- Over-delivery sizing (#730/H4): read the over_delivery_ratio of recent same-session_type waves — from
effective-sizing learnings if present, else directly from the last ~5 sessions.jsonl records' waves[].over_delivery_ratio (skip records lacking the field — pre-#730; also skip Discovery/Finalization waves, whose planned set is empty by design). If the median ratio R > 1.3, the fleet historically under-briefs file scope: inflate the Step 3 "Files to change" estimate by R before scoring the complexity tier; note it under Project Intelligence Applied.
For each agreed task/issue:
- Read the VCS issue description and acceptance criteria
(if session-start Phase 7.1 emitted a
### Premise Verification Result entry for this issue, treat its verdict as binding — re-scope or drop tasks whose verdict is FALSCH-PRÄMISSE/SHIPPED before decomposing; do not re-run the greps, session-start already did)
- Identify affected files by searching the codebase (Grep/Glob — don't guess)
- Map dependencies: which tasks must complete before others can start
- Estimate complexity: small (1 agent), medium (2-3 agents), large (dedicated wave)
- Identify synergies: tasks that touch the same files → same wave, same agent
Step 1.5: Agent Discovery
Before assigning tasks to waves, discover available agents for this session:
Scan for project-level agents: Glob <state-dir>/agents/*.md (.claude/agents/*.md for Claude Code, .codex/agents/*.md for Codex CLI, .cursor/agents/*.md for Cursor IDE, .pi/agents/*.md for Pi)
- Read each file's YAML frontmatter: extract
name and description
- Filter out non-agent reference files (skip files with
description containing "Reference documentation" or "NOT an executable agent")
- Build a list of available project agents with their names and capabilities
Read agent-mapping from Session Config (optional):
- Field:
agent-mapping — a JSON object mapping role keys to agent names
- Role keys:
impl, test, db, ui, security, compliance, docs, perf
- Example:
agent-mapping: { impl: code-editor, test: test-specialist, db: database-architect }
- If present, these explicit mappings take priority over auto-matching
- A value MAY carry a channel prefix:
session-orchestrator:<plugin-agent> or cursor:<model> (foreign model, #1150). An unknown prefix is rejected fail-loud by scripts/lib/config.mjs at parse time — see docs/session-config-reference.md § agent-mapping values.
Validation: If agent-mapping specifies an agent name, verify the agent exists:
- For project agents: check
<state-dir>/agents/<name>.md exists
- For plugin agents: check the agent is registered (contains
: separator)
- For
cursor:<model> (foreign channel): the existence check is on the CHANNEL, not the model — cursor-agent on PATH and logged in (cursor-agent status). The model string is free-form and is validated only at dispatch time, because the model catalogue lives outside this repo.
- If the agent doesn't exist — or the cursor channel is unavailable (binary missing / not logged in) — warn the user and fall back to auto-discovery for that role (same fallback shape in both cases; never hard-fail the plan)
- Two constraints the plan must carry into the wave, both owned by
skills/wave-executor/wave-loop.md § Third branch: foreign-model dispatch (one place owns the contract — do not restate it here): a cursor:<model> mapping is INERT for any never_foreign role (impl-core, security-review, migration, release, secrets, incident, refactor-crosscut — the adapter refuses it), and every foreign run requires a MANDATORY Claude semantic diff-review before merge-back. Plan the review as work, not as a formality.
Build Agent Registry (resolution priority):
- Priority 1: Project agents (from
<state-dir>/agents/ — see Platform Note) — matched by name
- Priority 2: Plugin agents (
session-orchestrator:code-implementer, session-orchestrator:test-writer, session-orchestrator:ui-developer, session-orchestrator:db-specialist, session-orchestrator:security-reviewer)
- Priority 3:
general-purpose (fallback)
Match tasks to agents: For each task from Step 1:
If agent-mapping config specifies a mapping for the task's domain → use that agent. For Docs-role tasks specifically, check agent-mapping.docs first; if set, use that agent name instead of the default below.
Docs-role fast path (high-priority — runs before keyword matching): If the task's role is classified as Docs (per Step 1.8) AND docs-orchestrator.enabled: true in Session Config → resolve subagent_type: "docs-writer". The docs-writer project agent is discovered at <state-dir>/agents/docs-writer.md during the Priority 1 scan above. No colon prefix — it is a project agent, not a plugin agent. If agent-mapping.docs is set, use that name instead of "docs-writer".
Else, match task description against agent descriptions using the content-based routing table below. Match any keyword from the pattern column (case-insensitive) against the task title and description. Use the first matching row; rows are checked top to bottom.
| Keyword pattern |
Resolved agent |
migration, schema, RLS, index, query, ORM, supabase, postgres, database, db |
session-orchestrator:db-specialist |
component, tsx, css, tailwind, page, layout, a11y, wcag, responsive, UI, frontend, style |
session-orchestrator:ui-developer |
security, auth, csrf, csp, injection, XSS, sanitize, OWASP, vulnerability, pen test |
session-orchestrator:security-reviewer |
test, coverage, vitest, jest, playwright, spec, fixture, assertion |
session-orchestrator:test-writer |
| (none of the above match) |
session-orchestrator:code-implementer |
Else, use role-based default: Impl-Core/Impl-Polish → code-implementer, Quality → test-writer
Record the resolved subagent_type for each task
No agents found? If no project agents exist and plugin agents are available, use plugin agents. If neither, fall back to general-purpose for all tasks. The system works at every level.
Step 1.8: Task-to-Role Classification
For each task from Step 1, assign exactly one role. Use these signal-to-role mappings:
| Signal in task |
Role |
Examples |
| Needs codebase understanding before changes; audit, explore, verify assumptions, check existing coverage |
Discovery |
"Audit auth flow", "Check test coverage for module X", "Identify affected modules" |
| New feature code, new API endpoints, DB schema changes, primary UI components, new modules |
Impl-Core |
"Add /api/users endpoint", "Create migration for invoices table", "Implement auth middleware" |
| Bug fixes from prior waves, secondary features, integration work, edge cases, polish of existing code |
Impl-Polish |
"Fix pagination edge case", "Integrate payment with billing", "Handle error states in form" |
Documentation updates — new/changed README sections, CLAUDE.md (or AGENTS.md on Codex CLI) updates, vault context.md/decisions.md narratives, ADR edits. Audience-aware (User/Dev/Vault). Gated on docs-orchestrator.enabled |
Docs |
"Update README for new --no-vault flag", "Write CLAUDE.md section for new hook (or AGENTS.md on Codex CLI)", "Append vault decisions.md entry for architecture change" |
| Write/update tests, lint fixes, security review, code simplification, type errors |
Quality |
"Add tests for auth module", "Fix TypeScript errors", "Security audit of new API" |
| Documentation updates, issue cleanup, commit preparation, SSOT refresh, changelog |
Finalization |
"Update README", "Close resolved issues", "Write session handover notes" |
Disambiguation rules:
- If a task involves BOTH exploration AND implementation → split it: Discovery agent reads/validates, Impl-Core agent implements. Create two separate task entries.
- If a task is "fix something from a previous session" (not from this session's Impl-Core) → classify as Impl-Core (it is new work for this session).
- A "write tests for new feature code being built this session" task is created ONLY when Discovery or a qa-strategist run reported a named gap — a concrete bug or regression the current suite would let through, stated as such. When that gap exists, classify the task as Quality (not Impl-Core); tests run after implementation. "Feature X was built" is NOT by itself evidence of test demand: with no named gap, no Quality task is created — do not synthesize one to give the role something to do. A dispatched
test-writer may correspondingly report no-tests-needed as a SUCCESS status, not a failure.
- If unsure between Impl-Core and Impl-Polish → if the task is on the critical path (other tasks depend on it), it is Impl-Core. If independent polish, it is Impl-Polish.
- Docs role is only active when
docs-orchestrator.enabled: true in Session Config. When disabled (default), documentation-update tasks fall into Impl-Polish (inline doc changes alongside code) or Finalization (standalone doc/SSOT updates) as today.
Step 1.8 Docs-role: Consuming the Phase 2.5 Emission Block
When docs-orchestrator.enabled: true, session-start Phase 2.5 emits a delimited block in the conversation context. Read and parse it before synthesizing Docs-role tasks:
Locating the block: Search the conversation context for the header ### Docs Planning Result (Phase 2.5). If the header is absent, Phase 2.5 was skipped — emit 0 Docs tasks and do not fabricate any.
Parsing rules (apply in document order):
Audiences: — comma-separated list of active audience identifiers (e.g., user, dev). Trim whitespace around each value.
Mode: — single enum value: warn, strict, or off. Store as $docs_mode.
Docs-tasks-seed: — multi-entry bullet list. Each top-level - audience: bullet is one seed task. Parse in document order; do not merge entries. Each seed task has:
audience: — target audience (user, dev, or vault)
rationale: — free-text description of what needs documenting
Synthesizing Docs-role tasks: For each seed task entry (in document order):
- Set
role: Docs.
- Set
description derived from the rationale field (paraphrase as an actionable imperative, e.g., "Document the new --no-vault flag in user-facing README").
- Set
audience from the audience field.
- Set
target-pattern by looking up the audience in the Audiences & File Patterns table in skills/docs-orchestrator/audience-mapping.md. Use the glob pattern listed there for the matched audience row.
- Resolve
subagent_type per the Docs-role fast path in Step 1.5 point 4 above.
If the block is absent: Do not fabricate Docs tasks. The Docs role remains empty; apply the empty-role rule from Step 2.
- Housekeeping sessions: skip Steps 1.8, 2, and 3 — housekeeping is the maintenance loop, one coordinator-direct wave.
total-waves: 1 and the wave's coordinatorDirect: true come from the shape (scripts/session-shape.mjs --session-type housekeeping), not from this prose.
- No role classification — no wave-executor dispatch, no per-role agent sizing.
- Default scope, in this order:
- drift-check —
node skills/claude-md-drift-check/checker.mjs --mode warn
- expired-learnings sweep —
node scripts/sweep-expired-learnings.mjs --json, then --apply --json when the dry run reports archived > 0
/evolve analyze
/reconcile
/evolve dialectic — dry-run first, then --apply
/memory-cleanup
- Operator-selected housekeeping issues are appended AFTER the six maintenance items, in the order the operator picked them.
- Why coordinator-direct: four of the six are AUQ-gated, and
AskUserQuestion does not exist inside a dispatched agent (.claude/rules/ask-via-tool.md AUQ-004) — a wave-executor dispatch would strand the decision. "Coordinator-direct" means no wave-executor, NOT zero subagents: item 5 dispatches the read-only dialectic-deriver subagent directly.
- Wave plan output uses:
### Wave 1: Housekeeping (coordinator-direct, 0 agents)
Record the assigned role next to each task before proceeding to Step 2.
Docs-tasks persistence (for session-end Phase 3.2)
When docs-orchestrator.enabled: true AND the plan contains 1+ Docs tasks, session-plan MUST emit a machine-readable block at the end of its plan output (after the wave plan, before Ready to execute?). This block is the single source of truth (SSOT) consumed downstream:
- wave-executor Pre-Wave 1b (STATE.md init): reads this block and persists
docs-tasks: [...] into STATE.md frontmatter.
- session-end Phase 3.2 (docs verification): reads
docs-tasks back from STATE.md to verify each task produced a diff.
Emit format:
### Docs Tasks (machine-readable)
docs-tasks:
- id: docs-1
audience: <user|dev|vault>
target-pattern: <glob from skills/docs-orchestrator/audience-mapping.md>
rationale: <verbatim rationale from Phase 2.5 seed>
wave: <wave number where this docs-writer agent is dispatched>
status: planned
- id: docs-2
...
Field rules:
id: sequential index-based identifier (docs-1, docs-2, …). No UUID generation required.
audience: one of user, dev, vault.
target-pattern: the glob from skills/docs-orchestrator/audience-mapping.md for this audience row — do not invent patterns.
rationale: copy the rationale text from the Phase 2.5 seed entry verbatim (do not paraphrase here).
wave: the actual wave number assigned in Step 2 where the docs-writer agent for this task is dispatched.
status: always planned at plan time. Terminal values are set by session-end Phase 3.2 per-task verification loop: ok (diff substantive), partial (diff has <!-- REVIEW: source needed --> markers), or gap (no matching diff). wave-executor does NOT perform intermediate status updates — status: planned remains until session-end writes the terminal value.
Omission rule: When docs-orchestrator.enabled: false OR there are 0 Docs tasks, do NOT emit the ### Docs Tasks (machine-readable) block. Absence of the block signals to wave-executor and session-end that no docs verification is needed for this session.
Wave-Plan Mission Status (machine-readable)
When the wave plan contains 1 or more wave-plan items (i.e., for all non-empty plans), session-plan MUST emit a machine-readable mission-status block at the end of its plan output (after the Docs Tasks block if present, before Ready to execute?). This block is the SSOT consumed by wave-executor (for STATE.md persistence) and session-end Phase 1.9 (for enum-based classification).
- wave-executor Pre-Wave 1b (STATE.md init): reads this block and persists
mission-status: [...] into STATE.md frontmatter via writeMissionStatus from scripts/lib/state-md.mjs.
- session-end Phase 1.9: reads
mission-status back from STATE.md frontmatter via parseMissionStatus to classify items into the 1.1–1.4 buckets using enum values.
Emit format:
### Wave-Plan Mission Status (machine-readable)
mission-status:
- id: m-1
task: <task description from wave-plan item>
wave: <N>
status: brainstormed
- id: m-2
task: <task description from wave-plan item>
wave: <N>
status: brainstormed
Field rules:
id: sequential m-N identifier. No UUID generation required.
task: verbatim task description from the wave-plan item (do not paraphrase).
wave: the wave number where this task is dispatched.
status: always brainstormed at plan emission. Terminal values are updated at gate transitions by wave-executor: brainstormed → validated (user confirms via /go) → in-dev (agent dispatched) → testing (Quality wave) → completed (Quality gate green). session-end Phase 1.9 reads the current value to classify the item.
Transition gates (summary):
At plan time, all items start at brainstormed. When the user runs /go to approve the plan, wave-executor updates each item to validated. When an agent for a wave-plan item is dispatched, wave-executor updates that item to in-dev. When the Quality wave begins, items from prior waves move to testing. When the Quality gate passes, items finalize at completed. Rollback to brainstormed is permitted from any state. This ordering is coordinator convention, not a mechanical gate — nothing validates a transition before it is written (see "Default and transitions" below).
Omission rule: When the plan has 0 wave-plan items (e.g., pure express-path coord-direct with no sub-agent tasks), do NOT emit the ### Wave-Plan Mission Status (machine-readable) block.
Mission-Status Enum (#340)
Every wave-plan item carries a status field drawn from a 5-value enum. The field is always present on items emitted in the ### Wave-Plan Mission Status (machine-readable) block (see below). It is also the value persisted in STATE.md frontmatter and read back by session-end Phase 1.9 for enum-based classification.
Enum values
| Status |
Meaning |
Set when |
brainstormed |
Draft item from /plan, not yet user-confirmed |
Plan emitted by session-plan (all items start here) |
validated |
User confirmed via AUQ in session-plan (/go approval) |
wave-executor: user runs /go to approve the wave plan |
in-dev |
Agent picked up the task this wave |
wave-executor: agent dispatched for this item |
testing |
Implementation done, tests passing for this task |
wave-executor: Quality wave begins for this item's work |
completed |
Quality-Lite green for this task's wave |
wave-executor: Quality gate passes for this item |
Default and transitions
- Default at plan creation:
brainstormed — all items start here.
- Transitions are coordinator-level orchestration (not inside individual agent prompts). See
skills/wave-executor/SKILL.md "Mission-Status Updates (#340)" for when each transition fires.
- Rollback: any item may return to
brainstormed from any state (e.g. if work is discarded or re-planned).
- No mechanical validation — by design. The
status values come from the 5-value enum in the table above, but nothing checks a transition before it is written. setMissionStatus (scripts/lib/state-md/mission-status.mjs) mirrors whatever string it is handed onto BOTH the body section and the frontmatter array, deliberately without an enum gate: gating it would reintroduce the exact body-says-X/frontmatter-says-Y divergence that sync exists to remove. An out-of-enum value therefore lands visibly on both surfaces instead of being silently rejected on one. Keeping the enum honest is the coordinator's job.
Status field in wave-plan items
Every item in the wave plan output carries an implicit status: brainstormed at plan time. The ### Wave-Plan Mission Status (machine-readable) block below (emitted at the end of the plan output) is the machine-readable form that wave-executor and session-end Phase 1.9 consume. session-plan does NOT write STATUS transitions — it only emits the initial brainstormed values.
Step 2: Wave Assignment
Distribute tasks across the waves the session shape returned; each wave carries its own role. Which roles exist, and how many waves there are, is resolved by scripts/session-shape.mjs — see § Role-to-Wave Mapping below.
Wave Roles
| Role |
Purpose |
Agents modify code? |
| Discovery |
Understand the current state before changing anything |
No (read-only) |
| Impl-Core |
Primary implementation — core feature code, APIs, DB changes |
Yes |
| Impl-Polish |
Fix issues from Impl-Core, secondary tasks, integration, edge cases |
Yes |
| Quality |
Tests, typecheck, lint, security review |
Yes (tests only). Lint MUST use the canonical {lint-command} unscoped — never domain-split (e.g., pnpm lint src/ hides errors in tests/). See quality-gates § Scope Policy. |
| Finalization |
Documentation, issue cleanup, commit preparation |
Minimal |
Role-to-Wave Mapping
The wave list is not derived here. Resolve it ONCE at plan time from the session mode:
node scripts/session-shape.mjs --repo-root "$PWD" --session-type <housekeeping|feature|deep> \
[--profile ultradeep] [--known-scope true|false] --task-count <N>
Run it with event emission (no --no-event) — that record (orchestrator.session.shape_resolved in .orchestrator/metrics/events.jsonl) is the canonical record of this session's shape. Use --no-event only for a throwaway planning dry-run.
It prints one JSON line carrying:
totalWaves — the wave count
waves[] — one record per wave: n, role, agentCap, agentCapRaw, coordinatorDirect, writes, maxTurns, verification, qualityEarned, allowedPaths
discovery — whether a Discovery wave is part of the shape
wavesConfigHonored — whether the Session Config waves value was used
notes — human-readable reasons for any of the above
The plan's wave list IS that output. The coordinator fills tasks into the returned waves and NEVER adds, removes, or renumbers a wave — the sole exception is the empty-role rule below (and its coordinator-direct carve-out). --known-scope true is what drops the Discovery wave on a deep session; --profile ultradeep is what selects the ultradeep shape, and it applies ONLY when STATE.md frontmatter carries session-profile: ultradeep (written by the /session ultradeep argument alias — see commands/session.md). session-type stays deep; the profile changes the wave SHAPE, nothing else, and it ignores the Session Config waves value (the shape says so in wavesConfigHonored / notes). Spec: docs/prd/2026-09-06-ultradeep-session-profile.md § 5.
Ultradeep agent counts per wave: take each wave's cap from that wave's agentCap in the shape — there is no second table here to disagree with it. The caps are ceilings, not targets, and the Quality wave's cap is still EARNED per the Step 3 rule (the shape marks it qualityEarned: true); Research and Code-Discovery share wave 1's cap across their two separately-scoped groups; the Synthesis-Gate wave carries agentCap: 0 with coordinatorDirect: true and writes only the coordinator's own artifacts (audit report, STATE.md, plan).
Wave 1 splits into two disjointly-scoped groups: Research agents (web-enabled, see skills/wave-executor/SKILL.md § Ultradeep Profile) and Code-Discovery agents (repo-only). Both are read-only. Wave 2 dispatches NO agents — the coordinator consolidates wave 1, writes docs/audits/<YYYY-MM-DD>-<slug>.md, and asks ONE blocking AskUserQuestion before wave 3.
When roles are combined into a single wave, agents from both roles execute in that wave.
Docs role dispatch rule (conditional — docs-orchestrator.enabled: true only):
When docs-orchestrator.enabled: true, apply the following concrete dispatch rule based on the count of synthesized Docs tasks from Step 1.8:
len(docs-tasks) == 0 → skip Docs role entirely. Apply the empty-role rule: do not create a Docs wave slot, do not dispatch any docs-writer agent.
len(docs-tasks) == 1 → inline with Finalization wave. Dispatch one docs-writer agent alongside the Finalization agent in the Finalization wave. The docs-writer agent's file scope must not overlap the Finalization agent's files (deconflict per Step 3.5).
len(docs-tasks) >= 2 → dedicated Impl-Polish sub-slot or dedicated wave slot. Options in priority order:
- If Impl-Polish wave has remaining agent capacity (below
agents-per-wave): add docs-writer agents to the Impl-Polish wave as a sub-slot. The docs-writer agents MUST NOT share file scopes with any code-implementer agents in the same wave — verify via Step 3.5 deconfliction.
- If Impl-Polish is at capacity: add a dedicated Docs slot within the closest wave with capacity (prefer the wave immediately before Finalization).
- NEVER add a 6th wave for Docs. Docs always occupies an existing wave slot.
- When
docs-orchestrator.enabled is false (default), this rule has no effect — the Docs role does not exist.
Cross-role constraint in combined waves: Tasks from different roles within a combined wave (the feature shape's Impl-Polish+Quality is the one today) CANNOT be merged into a single agent — the roles carry different scope permissions. If the combined wave's tasks exceed its agentCap, defer the lower-priority role's tasks: in Impl-Polish+Quality, defer Quality tasks to a separate phase within the same wave.
A combined wave's verification field in the shape already carries the more restrictive of its two roles' levels — read it, do not re-derive it.
Empty roles: If a role has 0 tasks, skip its wave entirely. Do NOT dispatch an empty wave. Remaining waves retain their original role names but are renumbered sequentially, and total-waves in the plan output is updated to reflect the actual wave count. This rule never applies to Discovery. Discovery is dropped exactly once, at shape-resolution time, by passing --known-scope true to scripts/session-shape.mjs (§ Role-to-Wave Mapping above) — the shape itself renumbers the remaining waves and reports the new count as totalWaves in its JSON output, before the coordinator ever sees a wave list to assign tasks into. Applying this rule to Discovery by hand, after the fact, would be a second, competing renumbering of a decision the shape already made. The empty-role rule below is for the roles that stay ON the wave list after the shape is fixed (e.g., Docs, Quality) and whose task count can still fall to 0 during Step 1/1.8 classification.
Exception — a wave declared coordinator-direct: true is NEVER removed by the empty-role rule. The rule's premise is "0 tasks means nothing to dispatch, so the wave is dead weight". For a coordinator-direct wave that premise is inverted: dispatching zero agents is the wave's PURPOSE, not evidence of its emptiness. Its plan item therefore carries BOTH markers and is emitted verbatim:
- wave: 2
role: Synthesis-Gate
coordinator-direct: true
agents: 0
agents: 0 on such an item is a DECLARATION, never a defect — do not "fix" it upward, and do not let the Step 3.5 constraint check or the Step 3 tier table raise it.
- The wave still counts toward
total-waves and still occupies its wave number; the renumbering above skips over it, it does not absorb it.
- The ultradeep Synthesis-Gate (wave 2) is the only such wave today. Without this exception the empty-role rule deletes it — and it is the one wave whose entire job is to stop and ask before any code is written (
docs/prd/2026-09-06-ultradeep-session-profile.md AC-4).
- The exception is scoped to the MARKER, not to the profile: any future coordinator-direct wave inherits it without another edit here.
Role Details
Discovery
- Explore-type subagents (read-only, fast)
- Tasks: Audit affected code paths, verify assumptions, check test coverage, identify edge cases
- Output: Validated understanding, updated task scope if discoveries warrant it
- Tools: Read, Grep, Glob, Bash (read-only commands only) — do NOT use Edit or Write
- Scope enforcement: set
allowedPaths to [] (empty) for Discovery waves. Include in agent prompts: "You are READ-ONLY. Do NOT use Edit or Write tools."
- Distributional claims AND bare repo-state numbers MUST follow
.claude/rules/parallel-sessions.md § PSA-006 — quote the executed command + file scope + count + WHEN it was measured. Coordinators REJECT Discovery outputs that assert "N of M" / "100% of X" (deep-1647 W1-D3 incident class) or a bare count like "14 commits" / "92 learnings" (#908) without that evidence. Discovery facts age: re-verify a count before re-briefing it into a later wave.
Impl-Core
- Full implementation agents with Write/Edit/Bash access
- Tasks: Core feature code, database changes, API endpoints, primary UI components
- Output: Working implementation (may have rough edges)
Impl-Polish
- Targeted fix agents + new implementation agents
- Tasks: Bug fixes from Impl-Core, secondary features, integration, edge cases
- Output: Complete implementation with integrations working
Quality
- Simplification agents + test writers + quality reviewers
- Tasks: Simplify AI-generated code patterns (using slop-patterns.md from discovery skill), write/update tests (test files only —
**/*.test.*, **/*.spec.*, **/__tests__/**), run full quality checks per quality-gates skill, security review
- Scope restriction: Simplification agents may edit production files changed in this session. Test/review agents restricted to test file patterns and test configuration.
- Output: Simplified code, all tests passing, 0 TypeScript errors, no lint violations
Finalization
- 1-2 specialized agents
- Tasks: Update SSOT files, close issues, write session handover, prepare commits
- Output: Clean git state, updated documentation, issues resolved
Step 3: Complexity Assessment
Score the session scope to determine optimal agent counts per wave. Skip for housekeeping sessions (use fixed counts from Step 4).
Scoring Formula
| Factor |
0 points |
1 point |
2 points |
| Files to change |
1-5 |
6-15 |
16+ |
| Cross-module scope |
1 directory |
2-3 directories |
4+ directories |
| Issue count |
1 issue |
2-3 issues |
4+ issues |
Total score = sum of all factors (0-6 range).
Cross-module scope counts top-level source directories (e.g., src/auth/, src/api/, lib/utils/). Nested subdirectories under the same parent count as one directory. Non-source directories (docs, config, scripts) don't count unless they contain modified production code.
Over-delivery adjustment (#730/H4): when Step 0.5 surfaced a historical over-delivery ratio R > 1.3 for this session_type, score the "Files to change" row against ceil(briefed_files × R) rather than the raw briefed count — agents historically deliver R× the briefed scope, so the raw count under-sizes the wave.
Complexity Tiers
| Tier |
Score |
Description |
| Simple |
0-1 |
Small scope, few files, single module |
| Moderate |
2-3 |
Medium scope, multiple modules |
| Complex |
4-6 |
Large scope, many modules and issues |
Agent Count by Tier
The caps themselves are not derived from the tier: each wave's ceiling is waves[].agentCap from the shape (scripts/session-shape.mjs, § Role-to-Wave Mapping above), and agentCapRaw is that cap before the Session Config agents-per-wave ceiling was applied.
What the tier score decides is relaxation DOWNWARD only: a simple-tier session may plan fewer agents than the wave's agentCap where the briefed work does not fill it. It may never plan more — the cap is a hard ceiling, and a moderate or complex tier does not raise it.
**The Quality column i
…(truncated)
1---2name: session-plan3description: Creates a structured wave execution plan with role-based assignment after user alignment. Decomposes agreed tasks into waves resolved from the session mode by `scripts/session-shape.mjs`, with optimal agent assignment, dependency ordering, and inter-wave checkpoints. Activated by session-start after Q&A phase completes.4---56> **Platform Note:** Project agents live in `<state-dir>/agents/` where `<state-dir>` is `.claude/` (Claude Code), `.codex/` (Codex CLI), `.cursor/` (Cursor IDE), or `.pi/` (Pi). On Cursor IDE and Pi v1, parallel agent dispatch is not available — present wave tasks as a sequential execution list instead. See `skills/_shared/platform-tools.md`.78# Session Plan Skill910> Project-instruction file resolution: `CLAUDE.md` and `AGENTS.md` (Codex CLI) are transparent aliases — see [skills/_shared/instruction-file-resolution.md](../_shared/instruction-file-resolution.md). Wherever this skill mentions `CLAUDE.md`, the alias rule applies.1112## Phase 0.5: Parallel-Aware Preamble1314> Skip silently when `persistence: false` in Session Config.1516Before any Phase 1 work, run the parallel-aware preamble per `skills/_shared/parallel-aware-preamble.md`. The preamble detects other active sessions in the worktree-family via `findPeers(repoRoot, { mySessionId })`, classifies the caller's mode via `classifyMode(callerMode)` against the exclusivity-matrix, and either:1718- Returns `PASS_THROUGH` (no other session / `always-ok` mode) → continue to Phase 119- Returns `EXCLUSIVE_BLOCKED` → fires Exclusive-Conflict AUQ from `skills/_shared/parallel-aware-auq.md`20- Returns `PROMOTION_OFFER` → fires Worktree-Promotion AUQ (via `enterWorktree()` from `scripts/lib/autopilot/worktree-pipeline.mjs` — see `parallel-aware-auq.md` outcome-handling)2122On any non-PASS_THROUGH outcome that does not result in immediate exit, append a Deviation to STATE.md via `appendDeviationOnDisk(repoRoot, isoTimestamp, message)` from `scripts/lib/state-md.mjs`.2324**Implementation reference:** `skills/_shared/parallel-aware-preamble.md § Implementation`.25**AUQ reference:** `skills/_shared/parallel-aware-auq.md`.2627## Purpose2829Transform the agreed session scope (from session-start Q&A) into an executable wave plan (using role-based assignment) with specific agent assignments, file scopes, and acceptance criteria per task.3031## Input: Session Scope3233This skill receives the agreed session scope from session-start. The scope includes:34- **Issue list**: VCS issue numbers and titles selected by the user35- **Session type**: housekeeping, feature, or deep36- **Recommended focus**: the option the user selected in session-start Phase 737- **Session Config**: parsed JSON from `parse-config.mjs`38- **Express-path signal** (optional): session-start Phase 8.5 may set `EXPRESS_PATH=true` in the handoff context when the activation conditions are met.3940These are passed via the conversation context (not a file). Parse the preceding session-start output to extract the agreed scope.4142## Optional private capability context4344Before either the express path or task decomposition, apply45[Private capability context](../_shared/private-capability-context.md) when the46owner explicitly supplies or authorizes a local catalog lookup for a known47private/internal planning audience. Reuse the bounded findings already supplied48by `/plan new` when applicable; do not repeat the same lookup. This step does not49require persistence. With no authorized context, or a public/unknown audience,50skip it without a prompt or lookup and continue the existing flow. Eligible51source references inform reuse alternatives and verification tasks; a catalog52match does not expand the agreed implementation scope or disable the express path.5354## Express Path Short-Circuit (#214)5556> Check this **before Step 0**. If the express path is active, this skill emits a minimal 1-wave plan and exits — no role decomposition, no wave splitting, no agent count computation.5758> Phase 8.5 of session-start hands off here NORMALLY when the express path activates — it does not skip session-plan (#1146). The banner below is printed by `node scripts/express-path.mjs`, and the 1-wave plan this section emits is the artifact `/go` detects.5960**Detect express-path activation:** Search the conversation context for the banner line:6162```63Express path activated — <N> tasks, coordinator-direct, no inter-wave checks.64```6566If found AND `express-path.enabled` is `true` in Session Config (read via Step 0 below — skip only that field check if config read is needed):6768Emit this 1-wave plan and exit the skill immediately (do not continue to Step 1 or beyond):6970```71## Wave Plan (Session: housekeeping, 1 wave, isolation: none) [Express Path]7273### Wave 1: Coordinator-Direct (<N> tasks)74- All agreed tasks executed sequentially by the coordinator — no subagents dispatched.75- Tasks: [list agreed issues/tasks]76- Isolation: none (coord-direct)77- Max-turns: N/A (coordinator executes directly)7879### Execution Config80- Waves: 1 | Agents-per-wave: 0 (coordinator-direct) | Isolation: none81- Express path: active (housekeeping + scope ≤ 3 + no parallel agents needed)82- Total agents planned: 08384Express path — no inter-wave checks. Use /go to begin.85```8687> The express path's 1-wave plan is the same shape housekeeping resolves to — one wave with `coordinatorDirect: true` and no dispatched agents (`scripts/session-shape.mjs --session-type housekeeping`). The express path stays as written above; it does not need to call the shape resolver to know that.8889**When express-path banner is absent or `express-path.enabled: false`:** Proceed to Step 0 and the full planning flow as normal.9091## Step 0: Read Session Config9293Read and parse Session Config per `skills/_shared/config-reading.md`. Store result as `$CONFIG`.9495Extract these fields for planning:96- `waves` — number of execution waves; resolved by `scripts/session-shape.mjs` (`totalWaves`), do not compute by hand. The shape reports in `wavesConfigHonored` whether the configured value was used at all, and says why in `notes`.97- `agents-per-wave` (may have session-type overrides per `config-reading.md`) — the operator's ceiling; the per-wave cap that actually binds is resolved by `scripts/session-shape.mjs` (`waves[].agentCap`), do not compute by hand.98- `isolation` — Session Config input (`worktree` / `none` / `auto`) that feeds `configIsolation` into the graduated per-wave rule (`resolveIsolation`, issue #194, in `scripts/lib/wave-sizing.mjs`: an explicit config value always wins; otherwise ≤2 agents → `none`, ≥5 agents → `worktree`, 3-4 agents → `none` for housekeeping else `worktree`). The RESOLVED value for a given wave is `waves[].isolation` in the shape's JSON output (`scripts/session-shape.mjs`) — a wave with `coordinatorDirect: true`, or a read-only wave, resolves `none` without calling `resolveIsolation` at all. Do not compute by hand; the plan header's `Isolation:` line is copied straight from that wave entry.99- `enforcement` (default: warn) — Session Config input (`strict` / `warn` / `off`) that feeds `configEnforcement` into `resolveEnforcement` (same module); the resolved per-wave value is `waves[].enforcement`. Isolation `none` auto-promotes `warn` to `strict`, since the scope-enforcement hook is then the only barrier left.100- `max-turns` — agent turn budget; resolved by `scripts/session-shape.mjs` (`waves[].maxTurns`), do not compute by hand.101- `agent-mapping` (optional) — explicit role-to-agent bindings102- `persistence` (default: true) — whether to use STATE.md and learnings103104> **Fallback:** If session-start already output a `## Session Config (active)` block in the conversation context, extract values from there to avoid a redundant parse. If not present in context, parse independently.105106## Step 1: Task Decomposition1071080. **Check for resume context**: > Skip if `persistence` is `false` in Session Config.109 If `<state-dir>/STATE.md` exists with `status: active` or `status: paused`, read it to understand:110 - Which waves were completed in the prior session111 - Which agents completed, which were partial/failed112 - What deviations were logged113 - Use this to avoid re-doing completed work and to prioritize carryover tasks114 If no STATE.md or `status: completed`, proceed with fresh planning.1151160.5. **Read project intelligence**: > Skip if `persistence` is `false` in Session Config.117 If `.orchestrator/metrics/learnings.jsonl` exists, read active learnings (confidence > 0.3, not expired). Sort by `confidence` DESC (tiebreaker: `created_at` DESC) and slice to the first `learnings-surface-top-n` entries (default 15) before applying the four categories below. If the top-N slice is empty, skip the categories.118 - **Fragile files**: if any planned task touches a known fragile file, note it as a warning in the agent spec119 - **Effective sizing**: use historical sizing data to inform Step 3 complexity scoring120 - **Recurring issues**: pre-populate risk mitigation with known issue patterns121 - **Scope guidance**: validate planned scope against historical session capacity122 - **Over-delivery sizing (#730/H4)**: read the over_delivery_ratio of recent same-session_type waves — from `effective-sizing` learnings if present, else directly from the last ~5 sessions.jsonl records' `waves[].over_delivery_ratio` (skip records lacking the field — pre-#730; also skip Discovery/Finalization waves, whose planned set is empty by design). If the median ratio R > 1.3, the fleet historically under-briefs file scope: inflate the Step 3 "Files to change" estimate by R before scoring the complexity tier; note it under Project Intelligence Applied.123124For each agreed task/issue:1251. Read the VCS issue description and acceptance criteria126 (if session-start Phase 7.1 emitted a `### Premise Verification Result` entry for this issue, treat its verdict as binding — re-scope or drop tasks whose verdict is FALSCH-PRÄMISSE/SHIPPED before decomposing; do not re-run the greps, session-start already did)1272. Identify affected files by searching the codebase (Grep/Glob — don't guess)1283. Map dependencies: which tasks must complete before others can start1294. Estimate complexity: small (1 agent), medium (2-3 agents), large (dedicated wave)1305. Identify synergies: tasks that touch the same files → same wave, same agent131132## Step 1.5: Agent Discovery133134Before assigning tasks to waves, discover available agents for this session:1351361. **Scan for project-level agents**: Glob `<state-dir>/agents/*.md` (`.claude/agents/*.md` for Claude Code, `.codex/agents/*.md` for Codex CLI, `.cursor/agents/*.md` for Cursor IDE, `.pi/agents/*.md` for Pi)137 - Read each file's YAML frontmatter: extract `name` and `description`138 - Filter out non-agent reference files (skip files with `description` containing "Reference documentation" or "NOT an executable agent")139 - Build a list of available project agents with their names and capabilities1401412. **Read agent-mapping from Session Config** (optional):142 - Field: `agent-mapping` — a JSON object mapping role keys to agent names143 - Role keys: `impl`, `test`, `db`, `ui`, `security`, `compliance`, `docs`, `perf`144 - Example: `agent-mapping: { impl: code-editor, test: test-specialist, db: database-architect }`145 - If present, these explicit mappings take priority over auto-matching146 - A value MAY carry a channel prefix: `session-orchestrator:<plugin-agent>` or `cursor:<model>` (foreign model, #1150). An unknown prefix is rejected fail-loud by `scripts/lib/config.mjs` at parse time — see `docs/session-config-reference.md` § `agent-mapping` values.147148 **Validation:** If `agent-mapping` specifies an agent name, verify the agent exists:149 - For project agents: check `<state-dir>/agents/<name>.md` exists150 - For plugin agents: check the agent is registered (contains `:` separator)151 - For `cursor:<model>` (foreign channel): the existence check is on the CHANNEL, not the model — `cursor-agent` on `PATH` and logged in (`cursor-agent status`). The model string is free-form and is validated only at dispatch time, because the model catalogue lives outside this repo.152 - If the agent doesn't exist — or the cursor channel is unavailable (binary missing / not logged in) — warn the user and fall back to auto-discovery for that role (same fallback shape in both cases; never hard-fail the plan)153 - **Two constraints the plan must carry into the wave, both owned by `skills/wave-executor/wave-loop.md` § Third branch: foreign-model dispatch** (one place owns the contract — do not restate it here): a `cursor:<model>` mapping is INERT for any `never_foreign` role (impl-core, security-review, migration, release, secrets, incident, refactor-crosscut — the adapter refuses it), and every foreign run requires a MANDATORY Claude semantic diff-review before merge-back. Plan the review as work, not as a formality.1541553. **Build Agent Registry** (resolution priority):156 - **Priority 1**: Project agents (from `<state-dir>/agents/` — see Platform Note) — matched by name157 - **Priority 2**: Plugin agents (`session-orchestrator:code-implementer`, `session-orchestrator:test-writer`, `session-orchestrator:ui-developer`, `session-orchestrator:db-specialist`, `session-orchestrator:security-reviewer`)158 - **Priority 3**: `general-purpose` (fallback)1591604. **Match tasks to agents**: For each task from Step 1:161 - If `agent-mapping` config specifies a mapping for the task's domain → use that agent. For Docs-role tasks specifically, check `agent-mapping.docs` first; if set, use that agent name instead of the default below.162 - **Docs-role fast path (high-priority — runs before keyword matching):** If the task's role is classified as `Docs` (per Step 1.8) AND `docs-orchestrator.enabled: true` in Session Config → resolve `subagent_type: "docs-writer"`. The `docs-writer` project agent is discovered at `<state-dir>/agents/docs-writer.md` during the Priority 1 scan above. No colon prefix — it is a project agent, not a plugin agent. If `agent-mapping.docs` is set, use that name instead of `"docs-writer"`.163 - Else, match task description against agent descriptions using the content-based routing table below. Match any keyword from the pattern column (case-insensitive) against the task title and description. Use the first matching row; rows are checked top to bottom.164165 | Keyword pattern | Resolved agent |166 |---|---|167 | `migration`, `schema`, `RLS`, `index`, `query`, `ORM`, `supabase`, `postgres`, `database`, `db` | `session-orchestrator:db-specialist` |168 | `component`, `tsx`, `css`, `tailwind`, `page`, `layout`, `a11y`, `wcag`, `responsive`, `UI`, `frontend`, `style` | `session-orchestrator:ui-developer` |169 | `security`, `auth`, `csrf`, `csp`, `injection`, `XSS`, `sanitize`, `OWASP`, `vulnerability`, `pen test` | `session-orchestrator:security-reviewer` |170 | `test`, `coverage`, `vitest`, `jest`, `playwright`, `spec`, `fixture`, `assertion` | `session-orchestrator:test-writer` |171 | (none of the above match) | `session-orchestrator:code-implementer` |172 - Else, use role-based default: Impl-Core/Impl-Polish → `code-implementer`, Quality → `test-writer`173 - Record the resolved `subagent_type` for each task174175> **No agents found?** If no project agents exist and plugin agents are available, use plugin agents. If neither, fall back to `general-purpose` for all tasks. The system works at every level.176177## Step 1.8: Task-to-Role Classification178179For each task from Step 1, assign exactly one role. Use these signal-to-role mappings:180181| Signal in task | Role | Examples |182|---|---|---|183| Needs codebase understanding before changes; audit, explore, verify assumptions, check existing coverage | **Discovery** | "Audit auth flow", "Check test coverage for module X", "Identify affected modules" |184| New feature code, new API endpoints, DB schema changes, primary UI components, new modules | **Impl-Core** | "Add /api/users endpoint", "Create migration for invoices table", "Implement auth middleware" |185| Bug fixes from prior waves, secondary features, integration work, edge cases, polish of existing code | **Impl-Polish** | "Fix pagination edge case", "Integrate payment with billing", "Handle error states in form" |186| Documentation updates — new/changed README sections, CLAUDE.md (or AGENTS.md on Codex CLI) updates, vault context.md/decisions.md narratives, ADR edits. Audience-aware (User/Dev/Vault). Gated on `docs-orchestrator.enabled` | **Docs** | "Update README for new --no-vault flag", "Write CLAUDE.md section for new hook (or AGENTS.md on Codex CLI)", "Append vault decisions.md entry for architecture change" |187| Write/update tests, lint fixes, security review, code simplification, type errors | **Quality** | "Add tests for auth module", "Fix TypeScript errors", "Security audit of new API" |188| Documentation updates, issue cleanup, commit preparation, SSOT refresh, changelog | **Finalization** | "Update README", "Close resolved issues", "Write session handover notes" |189190**Disambiguation rules:**191- If a task involves BOTH exploration AND implementation → split it: Discovery agent reads/validates, Impl-Core agent implements. Create two separate task entries.192- If a task is "fix something from a previous session" (not from this session's Impl-Core) → classify as **Impl-Core** (it is new work for this session).193- A "write tests for new feature code being built this session" task is created ONLY when Discovery or a qa-strategist run reported a **named gap** — a concrete bug or regression the current suite would let through, stated as such. When that gap exists, classify the task as **Quality** (not Impl-Core); tests run after implementation. "Feature X was built" is NOT by itself evidence of test demand: with no named gap, no Quality task is created — do not synthesize one to give the role something to do. A dispatched `test-writer` may correspondingly report `no-tests-needed` as a SUCCESS status, not a failure.194- If unsure between Impl-Core and Impl-Polish → if the task is on the critical path (other tasks depend on it), it is **Impl-Core**. If independent polish, it is **Impl-Polish**.195- **Docs role** is only active when `docs-orchestrator.enabled: true` in Session Config. When disabled (default), documentation-update tasks fall into **Impl-Polish** (inline doc changes alongside code) or **Finalization** (standalone doc/SSOT updates) as today.196197#### Step 1.8 Docs-role: Consuming the Phase 2.5 Emission Block198199When `docs-orchestrator.enabled: true`, session-start Phase 2.5 emits a delimited block in the conversation context. Read and parse it before synthesizing Docs-role tasks:200201**Locating the block:** Search the conversation context for the header `### Docs Planning Result (Phase 2.5)`. If the header is absent, Phase 2.5 was skipped — emit **0 Docs tasks** and do not fabricate any.202203**Parsing rules (apply in document order):**204- `Audiences:` — comma-separated list of active audience identifiers (e.g., `user, dev`). Trim whitespace around each value.205- `Mode:` — single enum value: `warn`, `strict`, or `off`. Store as `$docs_mode`.206- `Docs-tasks-seed:` — multi-entry bullet list. Each top-level `- audience:` bullet is **one seed task**. Parse in document order; do not merge entries. Each seed task has:207 - `audience:` — target audience (`user`, `dev`, or `vault`)208 - `rationale:` — free-text description of what needs documenting209210**Synthesizing Docs-role tasks:** For each seed task entry (in document order):2111. Set `role: Docs`.2122. Set `description` derived from the `rationale` field (paraphrase as an actionable imperative, e.g., "Document the new `--no-vault` flag in user-facing README").2133. Set `audience` from the `audience` field.2144. Set `target-pattern` by looking up the audience in the `Audiences & File Patterns` table in `skills/docs-orchestrator/audience-mapping.md`. Use the glob pattern listed there for the matched audience row.2155. Resolve `subagent_type` per the Docs-role fast path in Step 1.5 point 4 above.216217**If the block is absent:** Do not fabricate Docs tasks. The Docs role remains empty; apply the empty-role rule from Step 2.218219- Housekeeping sessions: skip Steps 1.8, 2, and 3 — housekeeping is the **maintenance loop**, one coordinator-direct wave. `total-waves: 1` and the wave's `coordinatorDirect: true` come from the shape (`scripts/session-shape.mjs --session-type housekeeping`), not from this prose.220 - No role classification — no wave-executor dispatch, no per-role agent sizing.221 - **Default scope, in this order:**222 1. drift-check — `node skills/claude-md-drift-check/checker.mjs --mode warn`223 2. expired-learnings sweep — `node scripts/sweep-expired-learnings.mjs --json`, then `--apply --json` when the dry run reports `archived > 0`224 3. `/evolve analyze`225 4. `/reconcile`226 5. `/evolve dialectic` — dry-run first, then `--apply`227 6. `/memory-cleanup`228 - Operator-selected housekeeping issues are appended AFTER the six maintenance items, in the order the operator picked them.229 - **Why coordinator-direct:** four of the six are AUQ-gated, and `AskUserQuestion` does not exist inside a dispatched agent (`.claude/rules/ask-via-tool.md` AUQ-004) — a wave-executor dispatch would strand the decision. "Coordinator-direct" means no wave-executor, NOT zero subagents: item 5 dispatches the read-only `dialectic-deriver` subagent directly.230 - Wave plan output uses: `### Wave 1: Housekeeping (coordinator-direct, 0 agents)`231232Record the assigned role next to each task before proceeding to Step 2.233234### Docs-tasks persistence (for session-end Phase 3.2)235236When `docs-orchestrator.enabled: true` AND the plan contains 1+ Docs tasks, session-plan MUST emit a machine-readable block **at the end of its plan output** (after the wave plan, before `Ready to execute?`). This block is the single source of truth (SSOT) consumed downstream:237238- **wave-executor Pre-Wave 1b (STATE.md init):** reads this block and persists `docs-tasks: [...]` into STATE.md frontmatter.239- **session-end Phase 3.2 (docs verification):** reads `docs-tasks` back from STATE.md to verify each task produced a diff.240241**Emit format:**242243```yaml244### Docs Tasks (machine-readable)245docs-tasks:246 - id: docs-1247 audience: <user|dev|vault>248 target-pattern: <glob from skills/docs-orchestrator/audience-mapping.md>249 rationale: <verbatim rationale from Phase 2.5 seed>250 wave: <wave number where this docs-writer agent is dispatched>251 status: planned252 - id: docs-2253 ...254```255256**Field rules:**257- `id`: sequential index-based identifier (`docs-1`, `docs-2`, …). No UUID generation required.258- `audience`: one of `user`, `dev`, `vault`.259- `target-pattern`: the glob from `skills/docs-orchestrator/audience-mapping.md` for this audience row — do not invent patterns.260- `rationale`: copy the `rationale` text from the Phase 2.5 seed entry verbatim (do not paraphrase here).261- `wave`: the actual wave number assigned in Step 2 where the `docs-writer` agent for this task is dispatched.262- `status`: always `planned` at plan time. Terminal values are set by session-end Phase 3.2 per-task verification loop: `ok` (diff substantive), `partial` (diff has `<!-- REVIEW: source needed -->` markers), or `gap` (no matching diff). wave-executor does NOT perform intermediate status updates — `status: planned` remains until session-end writes the terminal value.263264**Omission rule:** When `docs-orchestrator.enabled: false` OR there are 0 Docs tasks, do NOT emit the `### Docs Tasks (machine-readable)` block. Absence of the block signals to wave-executor and session-end that no docs verification is needed for this session.265266### Wave-Plan Mission Status (machine-readable)267268When the wave plan contains 1 or more wave-plan items (i.e., for all non-empty plans), session-plan MUST emit a machine-readable mission-status block **at the end of its plan output** (after the Docs Tasks block if present, before `Ready to execute?`). This block is the SSOT consumed by wave-executor (for STATE.md persistence) and session-end Phase 1.9 (for enum-based classification).269270- **wave-executor Pre-Wave 1b (STATE.md init):** reads this block and persists `mission-status: [...]` into STATE.md frontmatter via `writeMissionStatus` from `scripts/lib/state-md.mjs`.271- **session-end Phase 1.9:** reads `mission-status` back from STATE.md frontmatter via `parseMissionStatus` to classify items into the 1.1–1.4 buckets using enum values.272273**Emit format:**274275```yaml276### Wave-Plan Mission Status (machine-readable)277mission-status:278 - id: m-1279 task: <task description from wave-plan item>280 wave: <N>281 status: brainstormed282 - id: m-2283 task: <task description from wave-plan item>284 wave: <N>285 status: brainstormed286```287288**Field rules:**289- `id`: sequential `m-N` identifier. No UUID generation required.290- `task`: verbatim task description from the wave-plan item (do not paraphrase).291- `wave`: the wave number where this task is dispatched.292- `status`: always `brainstormed` at plan emission. Terminal values are updated at gate transitions by wave-executor: `brainstormed` → `validated` (user confirms via `/go`) → `in-dev` (agent dispatched) → `testing` (Quality wave) → `completed` (Quality gate green). session-end Phase 1.9 reads the current value to classify the item.293294**Transition gates (summary):**295At plan time, all items start at `brainstormed`. When the user runs `/go` to approve the plan, wave-executor updates each item to `validated`. When an agent for a wave-plan item is dispatched, wave-executor updates that item to `in-dev`. When the Quality wave begins, items from prior waves move to `testing`. When the Quality gate passes, items finalize at `completed`. Rollback to `brainstormed` is permitted from any state. This ordering is **coordinator convention, not a mechanical gate** — nothing validates a transition before it is written (see "Default and transitions" below).296297**Omission rule:** When the plan has 0 wave-plan items (e.g., pure express-path coord-direct with no sub-agent tasks), do NOT emit the `### Wave-Plan Mission Status (machine-readable)` block.298299### Mission-Status Enum (#340)300301Every wave-plan item carries a `status` field drawn from a 5-value enum. The field is always present on items emitted in the `### Wave-Plan Mission Status (machine-readable)` block (see below). It is also the value persisted in STATE.md frontmatter and read back by session-end Phase 1.9 for enum-based classification.302303#### Enum values304305| Status | Meaning | Set when |306|---|---|---|307| `brainstormed` | Draft item from `/plan`, not yet user-confirmed | Plan emitted by session-plan (all items start here) |308| `validated` | User confirmed via AUQ in session-plan (`/go` approval) | wave-executor: user runs `/go` to approve the wave plan |309| `in-dev` | Agent picked up the task this wave | wave-executor: agent dispatched for this item |310| `testing` | Implementation done, tests passing for this task | wave-executor: Quality wave begins for this item's work |311| `completed` | Quality-Lite green for this task's wave | wave-executor: Quality gate passes for this item |312313#### Default and transitions314315- **Default at plan creation:** `brainstormed` — all items start here.316- **Transitions are coordinator-level orchestration** (not inside individual agent prompts). See `skills/wave-executor/SKILL.md` "Mission-Status Updates (#340)" for when each transition fires.317- **Rollback:** any item may return to `brainstormed` from any state (e.g. if work is discarded or re-planned).318- **No mechanical validation — by design.** The `status` values come from the 5-value enum in the table above, but nothing checks a transition before it is written. `setMissionStatus` (`scripts/lib/state-md/mission-status.mjs`) mirrors whatever string it is handed onto BOTH the body section and the frontmatter array, deliberately without an enum gate: gating it would reintroduce the exact body-says-X/frontmatter-says-Y divergence that sync exists to remove. An out-of-enum value therefore lands visibly on both surfaces instead of being silently rejected on one. Keeping the enum honest is the coordinator's job.319320#### Status field in wave-plan items321322Every item in the wave plan output carries an implicit `status: brainstormed` at plan time. The `### Wave-Plan Mission Status (machine-readable)` block below (emitted at the end of the plan output) is the machine-readable form that wave-executor and session-end Phase 1.9 consume. session-plan does NOT write STATUS transitions — it only emits the initial `brainstormed` values.323324## Step 2: Wave Assignment325326Distribute tasks across the waves the session shape returned; each wave carries its own `role`. Which roles exist, and how many waves there are, is resolved by `scripts/session-shape.mjs` — see § Role-to-Wave Mapping below.327328### Wave Roles329330| Role | Purpose | Agents modify code? |331|------|---------|---------------------|332| **Discovery** | Understand the current state before changing anything | No (read-only) |333| **Impl-Core** | Primary implementation — core feature code, APIs, DB changes | Yes |334| **Impl-Polish** | Fix issues from Impl-Core, secondary tasks, integration, edge cases | Yes |335| **Quality** | Tests, typecheck, lint, security review | Yes (tests only). Lint MUST use the canonical `{lint-command}` unscoped — never domain-split (e.g., `pnpm lint src/` hides errors in `tests/`). See quality-gates § Scope Policy. |336| **Finalization** | Documentation, issue cleanup, commit preparation | Minimal |337338### Role-to-Wave Mapping339340The wave list is not derived here. Resolve it ONCE at plan time from the session mode:341342```bash343node scripts/session-shape.mjs --repo-root "$PWD" --session-type <housekeeping|feature|deep> \344 [--profile ultradeep] [--known-scope true|false] --task-count <N>345```346347Run it **with** event emission (no `--no-event`) — that record (`orchestrator.session.shape_resolved` in `.orchestrator/metrics/events.jsonl`) is the canonical record of this session's shape. Use `--no-event` only for a throwaway planning dry-run.348349It prints one JSON line carrying:350351- `totalWaves` — the wave count352- `waves[]` — one record per wave: `n`, `role`, `agentCap`, `agentCapRaw`, `coordinatorDirect`, `writes`, `maxTurns`, `verification`, `qualityEarned`, `allowedPaths`353- `discovery` — whether a Discovery wave is part of the shape354- `wavesConfigHonored` — whether the Session Config `waves` value was used355- `notes` — human-readable reasons for any of the above356357**The plan's wave list IS that output.** The coordinator fills tasks into the returned waves and NEVER adds, removes, or renumbers a wave — the sole exception is the empty-role rule below (and its coordinator-direct carve-out). `--known-scope true` is what drops the Discovery wave on a deep session; `--profile ultradeep` is what selects the ultradeep shape, and it applies ONLY when STATE.md frontmatter carries `session-profile: ultradeep` (written by the `/session ultradeep` argument alias — see `commands/session.md`). `session-type` stays `deep`; the profile changes the wave SHAPE, nothing else, and it ignores the Session Config `waves` value (the shape says so in `wavesConfigHonored` / `notes`). Spec: `docs/prd/2026-09-06-ultradeep-session-profile.md` § 5.358359**Ultradeep agent counts per wave:** take each wave's cap from that wave's `agentCap` in the shape — there is no second table here to disagree with it. The caps are ceilings, not targets, and the Quality wave's cap is still EARNED per the Step 3 rule (the shape marks it `qualityEarned: true`); Research and Code-Discovery share wave 1's cap across their two separately-scoped groups; the Synthesis-Gate wave carries `agentCap: 0` with `coordinatorDirect: true` and writes only the coordinator's own artifacts (audit report, STATE.md, plan).360361Wave 1 splits into two disjointly-scoped groups: **Research** agents (web-enabled, see `skills/wave-executor/SKILL.md` § Ultradeep Profile) and **Code-Discovery** agents (repo-only). Both are read-only. Wave 2 dispatches NO agents — the coordinator consolidates wave 1, writes `docs/audits/<YYYY-MM-DD>-<slug>.md`, and asks ONE blocking `AskUserQuestion` before wave 3.362363When roles are combined into a single wave, agents from both roles execute in that wave.364365**Docs role dispatch rule (conditional — `docs-orchestrator.enabled: true` only):**366367When `docs-orchestrator.enabled: true`, apply the following concrete dispatch rule based on the count of synthesized Docs tasks from Step 1.8:368369- `len(docs-tasks) == 0` → **skip Docs role entirely**. Apply the empty-role rule: do not create a Docs wave slot, do not dispatch any `docs-writer` agent.370- `len(docs-tasks) == 1` → **inline with Finalization wave**. Dispatch one `docs-writer` agent alongside the Finalization agent in the Finalization wave. The `docs-writer` agent's file scope must not overlap the Finalization agent's files (deconflict per Step 3.5).371- `len(docs-tasks) >= 2` → **dedicated Impl-Polish sub-slot or dedicated wave slot**. Options in priority order:372 1. If Impl-Polish wave has remaining agent capacity (below `agents-per-wave`): add `docs-writer` agents to the Impl-Polish wave as a sub-slot. The `docs-writer` agents MUST NOT share file scopes with any `code-implementer` agents in the same wave — verify via Step 3.5 deconfliction.373 2. If Impl-Polish is at capacity: add a dedicated Docs slot within the closest wave with capacity (prefer the wave immediately before Finalization).374- **NEVER add a 6th wave** for Docs. Docs always occupies an existing wave slot.375- When `docs-orchestrator.enabled` is `false` (default), this rule has no effect — the Docs role does not exist.376377**Cross-role constraint in combined waves:** Tasks from different roles within a combined wave (the feature shape's `Impl-Polish+Quality` is the one today) CANNOT be merged into a single agent — the roles carry different scope permissions. If the combined wave's tasks exceed its `agentCap`, defer the lower-priority role's tasks: in `Impl-Polish+Quality`, defer Quality tasks to a separate phase within the same wave.378379> A combined wave's `verification` field in the shape already carries the more restrictive of its two roles' levels — read it, do not re-derive it.380381**Empty roles:** If a role has 0 tasks, skip its wave entirely. Do NOT dispatch an empty wave. Remaining waves retain their original role names but are renumbered sequentially, and `total-waves` in the plan output is updated to reflect the actual wave count. **This rule never applies to Discovery.** Discovery is dropped exactly once, at shape-resolution time, by passing `--known-scope true` to `scripts/session-shape.mjs` (§ Role-to-Wave Mapping above) — the shape itself renumbers the remaining waves and reports the new count as `totalWaves` in its JSON output, before the coordinator ever sees a wave list to assign tasks into. Applying this rule to Discovery by hand, after the fact, would be a second, competing renumbering of a decision the shape already made. The empty-role rule below is for the roles that stay ON the wave list after the shape is fixed (e.g., Docs, Quality) and whose task count can still fall to 0 during Step 1/1.8 classification.382383**Exception — a wave declared `coordinator-direct: true` is NEVER removed by the empty-role rule.** The rule's premise is "0 tasks means nothing to dispatch, so the wave is dead weight". For a coordinator-direct wave that premise is inverted: dispatching zero agents is the wave's PURPOSE, not evidence of its emptiness. Its plan item therefore carries BOTH markers and is emitted verbatim:384385```386- wave: 2387 role: Synthesis-Gate388 coordinator-direct: true389 agents: 0390```391392- `agents: 0` on such an item is a DECLARATION, never a defect — do not "fix" it upward, and do not let the Step 3.5 constraint check or the Step 3 tier table raise it.393- The wave still counts toward `total-waves` and still occupies its wave number; the renumbering above skips over it, it does not absorb it.394- The ultradeep Synthesis-Gate (wave 2) is the only such wave today. Without this exception the empty-role rule deletes it — and it is the one wave whose entire job is to stop and ask before any code is written (`docs/prd/2026-09-06-ultradeep-session-profile.md` AC-4).395- The exception is scoped to the MARKER, not to the profile: any future coordinator-direct wave inherits it without another edit here.396397### Role Details398399**Discovery**400- Explore-type subagents (read-only, fast)401- Tasks: Audit affected code paths, verify assumptions, check test coverage, identify edge cases402- Output: Validated understanding, updated task scope if discoveries warrant it403- Tools: Read, Grep, Glob, Bash (read-only commands only) — do NOT use Edit or Write404- Scope enforcement: set `allowedPaths` to `[]` (empty) for Discovery waves. Include in agent prompts: "You are READ-ONLY. Do NOT use Edit or Write tools."405- Distributional claims AND bare repo-state numbers MUST follow `.claude/rules/parallel-sessions.md` § PSA-006 — quote the executed command + file scope + count + WHEN it was measured. Coordinators REJECT Discovery outputs that assert "N of M" / "100% of X" (deep-1647 W1-D3 incident class) or a bare count like "14 commits" / "92 learnings" (#908) without that evidence. Discovery facts age: re-verify a count before re-briefing it into a later wave.406407**Impl-Core**408- Full implementation agents with Write/Edit/Bash access409- Tasks: Core feature code, database changes, API endpoints, primary UI components410- Output: Working implementation (may have rough edges)411412**Impl-Polish**413- Targeted fix agents + new implementation agents414- Tasks: Bug fixes from Impl-Core, secondary features, integration, edge cases415- Output: Complete implementation with integrations working416417**Quality**418- Simplification agents + test writers + quality reviewers419- Tasks: Simplify AI-generated code patterns (using slop-patterns.md from discovery skill), write/update tests (test files only — `**/*.test.*`, `**/*.spec.*`, `**/__tests__/**`), run full quality checks per quality-gates skill, security review420- Scope restriction: Simplification agents may edit production files changed in this session. Test/review agents restricted to test file patterns and test configuration.421- Output: Simplified code, all tests passing, 0 TypeScript errors, no lint violations422423**Finalization**424- 1-2 specialized agents425- Tasks: Update SSOT files, close issues, write session handover, prepare commits426- Output: Clean git state, updated documentation, issues resolved427428## Step 3: Complexity Assessment429430Score the session scope to determine optimal agent counts per wave. Skip for housekeeping sessions (use fixed counts from Step 4).431432### Scoring Formula433434| Factor | 0 points | 1 point | 2 points |435|--------|----------|---------|----------|436| Files to change | 1-5 | 6-15 | 16+ |437| Cross-module scope | 1 directory | 2-3 directories | 4+ directories |438| Issue count | 1 issue | 2-3 issues | 4+ issues |439440**Total score** = sum of all factors (0-6 range).441442> **Cross-module scope** counts top-level source directories (e.g., `src/auth/`, `src/api/`, `lib/utils/`). Nested subdirectories under the same parent count as one directory. Non-source directories (docs, config, scripts) don't count unless they contain modified production code.443444> **Over-delivery adjustment (#730/H4):** when Step 0.5 surfaced a historical over-delivery ratio R > 1.3 for this session_type, score the "Files to change" row against ceil(briefed_files × R) rather than the raw briefed count — agents historically deliver R× the briefed scope, so the raw count under-sizes the wave.445446### Complexity Tiers447448| Tier | Score | Description |449|------|-------|-------------|450| Simple | 0-1 | Small scope, few files, single module |451| Moderate | 2-3 | Medium scope, multiple modules |452| Complex | 4-6 | Large scope, many modules and issues |453454### Agent Count by Tier455456The caps themselves are **not** derived from the tier: each wave's ceiling is `waves[].agentCap` from the shape (`scripts/session-shape.mjs`, § Role-to-Wave Mapping above), and `agentCapRaw` is that cap before the Session Config `agents-per-wave` ceiling was applied.457458What the tier score decides is **relaxation DOWNWARD only**: a simple-tier session may plan fewer agents than the wave's `agentCap` where the briefed work does not fill it. It may never plan more — the cap is a hard ceiling, and a moderate or complex tier does not raise it.459460> **The Quality column i461462…(truncated)