session-plan
SkillProductivityCreates 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.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the session-plan skill
What this skill tells your AI
The instructions your AI receives, as published by kanevry/session-orchestrator in skills/session-plan/SKILL.md and read by ahel’s review.
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. Seeskills/_shared/platform-tools.md.
Session Plan Skill
Project-instruction file resolution:
CLAUDE.mdandAGENTS.md(Codex CLI) are transparent aliases — see skills/_shared/instruction-file-resolution.md. Wherever this skill mentionsCLAUDE.md, the alias rule applies.
Phase 0.5: Parallel-Aware Preamble
Skip silently when
persistence: falsein 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-okmode) → continue to Phase 1 - Returns
EXCLUSIVE_BLOCKED→ fires Exclusive-Conflict AUQ fromskills/_shared/parallel-aware-auq.md - Returns
PROMOTION_OFFER→ fires Worktree-Promotion AUQ (viaenterWorktree()fromscripts/lib/autopilot/worktree-pipeline.mjs— seeparallel-aware-auq.mdoutcome-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=truein 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/godetects.
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: trueand 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 byscripts/session-shape.mjs(totalWaves), do not compute by hand. The shape reports inwavesConfigHonoredwhether the configured value was used at all, and says why innotes.agents-per-wave(may have session-type overrides perconfig-reading.md) — the operator's ceiling; the per-wave cap that actually binds is resolved byscripts/session-shape.mjs(waves[].agentCap), do not compute by hand.isolation— Session Config input (worktree/none/auto) that feedsconfigIsolationinto the graduated per-wave rule (resolveIsolation, issue #194, inscripts/lib/wave-sizing.mjs: an explicit config value always wins; otherwise ≤2 agents →none, ≥5 agents →worktree, 3-4 agents →nonefor housekeeping elseworktree). The RESOLVED value for a given wave iswaves[].isolationin the shape's JSON output (scripts/session-shape.mjs) — a wave withcoordinatorDirect: true, or a read-only wave, resolvesnonewithout callingresolveIsolationat all. Do not compute by hand; the plan header'sIsolation:line is copied straight from that wave entry.enforcement(default: warn) — Session Config input (strict/warn/off) that feedsconfigEnforcementintoresolveEnforcement(same module); the resolved per-wave value iswaves[].enforcement. Isolationnoneauto-promoteswarntostrict, since the scope-enforcement hook is then the only barrier left.max-turns— agent turn budget; resolved byscripts/session-shape.mjs(waves[].maxTurns), do not compute by hand.agent-mapping(optional) — explicit role-to-agent bindingspersistence(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
persistenceisfalsein Session Config. If<state-dir>/STATE.mdexists withstatus: activeorstatus: 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-sizinglearnings 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 Resultentry 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/*.mdfor Claude Code,.codex/agents/*.mdfor Codex CLI,.cursor/agents/*.mdfor Cursor IDE,.pi/agents/*.mdfor Pi)- Read each file's YAML frontmatter: extract
nameanddescription - Filter out non-agent reference files (skip files with
descriptioncontaining "Reference documentation" or "NOT an executable agent") - Build a list of available project agents with their names and capabilities
- Read each file's YAML frontmatter: extract
-
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>orcursor:<model>(foreign model, #1150). An unknown prefix is rejected fail-loud byscripts/lib/config.mjsat parse time — seedocs/session-config-reference.md§agent-mappingvalues.
Validation: If
agent-mappingspecifies an agent name, verify the agent exists:- For project agents: check
<state-dir>/agents/<name>.mdexists - 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-agentonPATHand 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): acursor:<model>mapping is INERT for anynever_foreignrole (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.
- Field:
-
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)
- Priority 1: Project agents (from
-
Match tasks to agents: For each task from Step 1:
-
If
agent-mappingconfig specifies a mapping for the task's domain → use that agent. For Docs-role tasks specifically, checkagent-mapping.docsfirst; 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) ANDdocs-orchestrator.enabled: truein Session Config → resolvesubagent_type: "docs-writer". Thedocs-writerproject agent is discovered at<state-dir>/agents/docs-writer.mdduring the Priority 1 scan above. No colon prefix — it is a project agent, not a plugin agent. Ifagent-mapping.docsis 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,dbsession-orchestrator:db-specialistcomponent,tsx,css,tailwind,page,layout,a11y,wcag,responsive,UI,frontend,stylesession-orchestrator:ui-developersecurity,auth,csrf,csp,injection,XSS,sanitize,OWASP,vulnerability,pen testsession-orchestrator:security-reviewertest,coverage,vitest,jest,playwright,spec,fixture,assertionsession-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_typefor each task
-
No agents found? If no project agents exist and plugin agents are available, use plugin agents. If neither, fall back to
general-purposefor all tasks. The system works at every level.
Step 1.8: Task-to-Role Classification
Assigns exactly one role (Discovery/Impl-Core/Impl-Polish/Docs/Quality/Finalization) to each Step 1 task via the signal-to-role mapping table, the disambiguation rules, the Docs-role Phase 2.5 emission-block parsing, and the housekeeping short-circuit. Also emits the Docs Tasks and Wave-Plan Mission Status machine-readable blocks (SSOT for wave-executor + session-end) and the Mission-Status Enum (#340). See references/session-plan-task-classification.md. Read WHEN: after Step 1.5, before Step 2.
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 countwaves[]— one record per wave:n,role,agentCap,agentCapRaw,coordinatorDirect,writes,maxTurns,verification,qualityEarned,allowedPathsdiscovery— whether a Discovery wave is part of the shapewavesConfigHonored— whether the Session Configwavesvalue was usednotes— 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 anydocs-writeragent.len(docs-tasks) == 1→ inline with Finalization wave. Dispatch onedocs-writeragent alongside the Finalization agent in the Finalization wave. Thedocs-writeragent'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): adddocs-writeragents to the Impl-Polish wave as a sub-slot. Thedocs-writeragents MUST NOT share file scopes with anycode-implementeragents 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).
- If Impl-Polish wave has remaining agent capacity (below
- NEVER add a 6th wave for Docs. Docs always occupies an existing wave slot.
- When
docs-orchestrator.enabledisfalse(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
verificationfield 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: 0on 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-wavesand 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.mdAC-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
Shortened here. Read the whole file on GitHub.
Signals
- GitHub stars
- 51
- Forks
- 7
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
session-plan- Source
- github.com/kanevry/session-orchestrator