From agentic-development-workflow
Bridges product context and feature lifecycle by syncing workspace state, scoring stories, and dispatching to design or launch. Use when ready to start building or asking 'what next?'.
How this skill is triggered — by the user, by Claude, or both
Slash command
/agentic-development-workflow:dispatchThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Bridge between the product context (control plane) and the feature lifecycle (execution plane). Syncs workspace state, scores stories, picks what to build next, assembles context, and routes into `/aep-design` or `/aep-launch`.
Bridge between the product context (control plane) and the feature lifecycle (execution plane). Syncs workspace state, scores stories, picks what to build next, assembles context, and routes into /aep-design or /aep-launch.
Where this fits:
/aep-envision → /aep-map → /aep-model (UI-facing) → /aep-scaffold
→ [ /aep-dispatch → /aep-design → /aep-launch → /aep-build → /aep-wrap ]
▲ you are here
→ /aep-reflect → loop
Session: Main, interactive
Input: Product definition from product/index.yaml (split mode) or product-context.yaml (v1 mode); operational state from product-context.yaml
Output: OpenSpec change with pre-assembled context, story status updated, handoff to /aep-design or /aep-launch
For autonomous orchestration: Use
/aep-autopilotinstead. Autopilot runs the full dispatch-launch-monitor-review-wrap-dispatch cycle as a tick-based state machine via/loop. Dispatch remains a single-pass interactive tool.For hands-free batch under Claude Code: dispatch a wave "with workflow" to build the whole wave as a single dynamic workflow (executor workflow mode) instead of N monitorable workers. See Step 5 → Dynamic Workflow mode.
File Resolution:
ls product/index.yaml 2>/dev/null && echo "SPLIT MODE" || echo "V1 MODE"
cat product-context.yaml
product/index.yaml exists): Read product definition from product/index.yaml for context assembly. Read stories, topology, architecture, cost from product-context.yaml.product-context.yaml.If product-context.yaml doesn't exist, run /aep-envision then /aep-map first.
If the stories section is empty, run /aep-map to decompose the product.
Every dispatch run follows the same 7-step protocol. Each step is idempotent — running /aep-dispatch twice with no state changes produces the same result.
① SYNC signals → bring YAML up to date with workspace reality
② CASCADE states → compute all pending→ready, pending→blocked transitions
③ SCORE stories → rank the ready queue by dispatch_score
④ PRESENT queue → show user the scored dispatch queue
⑤ DISPATCH → lock stories (status→in_progress), create OpenSpec changes
⑥ MONITOR → agents work, main session watches signals
⑦ COMPLETE → /aep-wrap updates YAML, atomic cascade, re-invoke dispatch
Before calculating anything, sync workspace signals into the YAML to reflect reality:
For each story with status: in_progress or in_review:
workspace = story.assigned_to
signal_path = .feature-workspaces/<workspace>/.dev-workflow/signals/status.json
Read signal file (if exists):
If signal.story_status == "completed":
Update YAML: story.status → completed
Update: story.completed_at = signal.completed_at
Update: story.pr_url = signal.pr_url
Update: story.cost_usd = signal.cost_usd
If signal.story_status == "in_review":
Update YAML: story.status → in_review
Update: story.pr_url = signal.pr_url
If signal.story_status == "failed":
Update YAML: story.status → failed
Append signal.failure_log to story.failure_logs
Why: Without signal sync, the YAML shows in_progress for stories already done. Downstream stories stay pending even though they're actually ready.
After syncing all signals, compute all state transitions in one pass:
For each story with status: pending:
If any story in dependencies[] has status: failed:
Transition → blocked
Elif any story in dependencies[] has status: deferred:
Transition → blocked (dependency deferred)
Elif all stories in dependencies[] have status: completed:
Transition → ready
For each story with status: blocked:
If the blocking dependency is now completed:
Transition → pending (will be re-evaluated in this same pass)
Recovery transitions (user-initiated, handle if requested):
failed → pending — user resets after fixing the specdeferred → pending — user un-defers a storyValidate the YAML after all updates (see references/yaml-guardrails.md):
npx js-yaml product-context.yaml > /dev/null && echo "YAML OK"
If this fails, fix the YAML before proceeding. Common fixes: quote list items containing colons, flatten nested sub-lists, escape embedded double quotes.
Commit the synced + cascaded state to YAML before computing scores. Increment dispatch_epoch.
For each layer (0, 1, 2, ...):
If any story in this layer has status not in [completed, deferred]:
This is the active layer. Stop.
Layer gate check: If active layer > 0, verify layer_gates[active_layer - 1].status == passed. If not, block and suggest running the layer gate test.
From ready stories in the active layer, remove stories with file-level conflicts:
For each ready story:
For each in_progress story:
If files_affected intersection is non-empty:
Mark as conflicted — cannot dispatch until the in_progress story completes
Before scoring, compute each story's readiness (spec completeness):
readiness_score = (
min(3, acceptance_criteria_count) # 0-3
+ (interface_obligations_defined ? 2 : 0) # 0 or 2
+ (files_affected_identified ? 1 : 0) # 0 or 1
+ (verification_defined ? 2 : 0) # 0 or 2
+ (no_relevant_open_questions ? 2 : 0) # 0 or 2
) / 10
Write readiness_score to the story in YAML. This is used for routing in Step 7.
Each remaining ready story gets a score:
dispatch_score = (business_value + unblock_potential + critical_path_urgency + reuse_leverage) / (complexity_cost + ambiguity_penalty + interface_risk)
Use story.business_value if explicitly set. Otherwise derive from priority:
critical = 10
high = 7
medium = 4
low = 1
unblock_potential = min(10, count of stories that directly depend on this one * 2)
A story that unblocks 5 others scores 10. A leaf story scores 0.
Compute the critical path through the dependency DAG (longest chain from any root to any leaf within the active layer). Stories on the critical path get maximum urgency:
If story is on critical path:
critical_path_urgency = 10
Else:
slack = latest_possible_start - earliest_possible_start
critical_path_urgency = max(0, 10 - slack)
Stories that produce shared enablers (auth middleware, base components, shared utilities) score higher:
reuse_leverage = min(10, count_of_modules_depending_on_output * 3)
Only applies to stories with compile_mode: shared_enabler or whose module appears in 2+ other modules' depends_on.
S = 1 (fast feedback)
M = 2
L = 4 (slow, expensive)
ambiguity_penalty = 0
If acceptance_criteria count < 3: +2
If interface_obligations empty: +1
If relevant open_questions exist: +1
If files_affected empty: +1
Stories with high ambiguity get lower scores, biasing dispatch toward well-specified work.
interface_risk = min(3, count of interface contracts this story creates or modifies)
Cross-module interface changes carry integration risk in parallel execution.
| Story | Value | Unblock | CP | Reuse | Cost | Ambig | IFace | Score |
|---|---|---|---|---|---|---|---|---|
| Auth middleware (critical path, high, unblocks 3, enabler) | 7 | 6 | 10 | 6 | S=1 | 0 | 1 | 14.5 |
| User model (not critical, medium, unblocks 2) | 4 | 4 | 4 | 0 | S=1 | 0 | 0 | 12.0 |
| Dashboard layout (not critical, low, leaf, ambiguous) | 1 | 0 | 2 | 0 | L=4 | 3 | 0 | 0.43 |
For stories with compile_mode: grouped_change sharing the same change_group:
business_value and unblock_potential across the group; use max critical_path_urgency and max reuse_leverage; divide by sum of complexity_cost + max ambiguity_penalty + max interface_riskShow the sorted queue with context:
Dispatch Queue (Layer 0 — 4 ready, 2 in_progress, WIP 3/5)
1. ★ PROJ-003 "Setup auth middleware" score: 14.5
[high] S | Module: auth | Wave 1 | Critical path | Shared enabler
Unblocks: PROJ-005, PROJ-007, PROJ-008
→ Readiness: 0.9 — skip to /aep-launch
2. PROJ-004 "Create user model" score: 12.0
[medium] S | Module: db | Wave 1 | 4h slack
Unblocks: PROJ-006, PROJ-009
→ Readiness: 0.8 — skip to /aep-launch
3. PROJ-010 "Add settings page" score: 0.43
[low] L | Module: web | Wave 3 | Leaf
→ Readiness: 0.3 — go through /aep-design (ambiguous)
Conflicted (waiting):
• PROJ-006 — files overlap with in_progress PROJ-002
Blocked (dependencies not met):
• PROJ-008 — waiting on PROJ-003 (auth middleware)
In progress:
• PROJ-001 (tab: feat-api-scaffold) — Phase 4, 60% complete
• PROJ-002 (tab: feat-db-schema) — Phase 5, code review
Recommendation: Always highlight the top story and explain why (highest score = critical path + high value + unblocks the most).
User picks stories one at a time. Best for early layers or learning the system.
--batch wave)Dispatch all ready stories in the current wave (execution slice) at once:
Dispatches all ready stories in Wave N (up to WIP limit)
Creates N workspaces via /aep-launch
--batch wave + "…with workflow")When the user explicitly asks to dispatch a wave "with workflow" AND the host
is Claude Code with the dynamic-workflow (Workflow) tool, route the batch through
the workflow mode instead of creating N workers. The dispatch front-end is
identical — sync, cascade, score, lock, assemble context — only the execution
plane changes: instead of N /aep-launch workers, author one dynamic workflow that
fans out pipeline(stories, build, verify) with one agent per story (recipe:
aep-executor/references/backends.md → "Mode: workflow").
Locks + creates OpenSpec changes for the ready stories in Wave N — up to the WIP limit (as usual)
Creates the .feature-workspaces/<name> worktrees (launch guardrails apply)
Then: one dynamic workflow, one agent per locked story (build → verify), each bound to its worktree
After the run: collect `gated` results → ask the human → resume gated stories with the answers
Respect the WIP limit. Workflow mode does not exempt the wave from the WIP cap below:
each workflow agent still opens a PR, so the integration/merge bottleneck is the
same as Wave Batch. Lock at most available_slots stories into the workflow
(available_slots = concurrency_limit − current in_progress); the workflow's own
per-agent concurrency cap is a separate, lower-level limit and does not replace
this one.
Announce the mode (this path bypasses /aep-launch). Because dispatch authors
the workflow directly instead of handing to /aep-launch, dispatch owns the
announcement that /aep-launch normally makes: state "workflow mode (dynamic
workflow) — autonomous, billed, background; no mid-stage steering; human
gates park and return here for confirmation, then gated stories resume"
before authoring the workflow.
This is the hands-free batch path: autonomous, billed, background. Steering is
at stage boundaries only — but human decisions are NOT lost: a worker that hits
one returns a gated result (gate-and-park), this session asks you, and the
story resumes in its worktree with your answer. Use it when you want a wave
built autonomously without watching individual workers. Requires Claude Code +
Workflow tool (see .claude/skills/aep-executor/references/backends.md,
"Mode: workflow"). If the host can't support it, fall back to Wave Batch and
say so.
max_wip = topology.routing.concurrency_limit (default: 5)
current_wip = count of stories with status: in_progress
available_slots = max_wip - current_wip
Never dispatch more than available_slots stories.
Why (Little's Law): Lead Time = WIP / Throughput. If you merge 3 stories/day, WIP 3 = 1 day lead time. WIP 15 = 5 days. The bottleneck is usually human PR review, not agent speed.
For each selected story, dispatch atomically:
1. Re-read story.status from YAML (not from cache)
2. If status != ready → SKIP (already dispatched by another run)
3. Write to YAML:
status: in_progress
assigned_to: <workspace-name>
openspec_change: <story-id>
started_at: <ISO 8601 now>
dispatched_at_epoch: <current dispatch_epoch>
4. Commit YAML immediately (this IS the lock)
5. THEN create OpenSpec change and workspace
The commit happens BEFORE the workspace is created. Two consecutive /aep-dispatch runs: Run 1 writes in_progress and commits. Run 2 reads in_progress, skips. No double dispatch.
For each dispatched story, create the OpenSpec change with pre-assembled context:
openspec/changes/<story-id>/
├── proposal.md ← story description + why + business value
├── design.md ← module definition + interface contracts + dependency APIs
├── specs/<module>.md ← acceptance criteria + interface obligations + verification
├── tasks.md ← story decomposed into implementation tasks
└── .context/ ← pre-assembled context package
├── stable-prefix.md ← shared product/architecture context (cacheable)
├── dependencies.md ← public APIs from completed dependency stories
└── retrieval.md ← what to explore at runtime
Extracted from product definition (product/index.yaml in split mode, product-context.yaml in v1 mode):
product.problem — what we're solvingproduct.constraints — tech stack, infrastructureproduct.layers[active_layer] — what the user can do at this layerarchitecture.overview — high-level structurearchitecture.technical_spec — if set, include the technical specification document (or relevant sections for the story's module). This provides Symphony-style precision for protocol-heavy systems.stories sectionarchitecture.modules matching story.modulearchitecture.interfaces where from or to = story module## Files to read first
- <files_affected from story spec>
## Patterns to explore
- Check existing patterns in <module> directory
- Read interface contract tests for consumed interfaces
## Do not read
- Other module internals — use dependency_outputs above
.5 alignment layers and calibrated stories)For stories with calibration_type set, or stories in .5 alignment layers:
Heavy calibrations (visual-design, ux-flow, copy-tone):
Include the calibration artifact — calibration/<type>.yaml (e.g., calibration/visual-design.yaml)
For visual-design: Also include reference design files from docs/design-references/ matching the story's page (by story activity or title)
Include calibration constraint directive:
This story has calibrated <dimension> decisions.
Follow the calibration artifact in calibration/<type>.yaml strictly.
Do not introduce new [visual tokens / flow patterns / voice patterns]
not defined in the calibration artifact.
If the required calibration/<type>.yaml does not exist, do not dispatch — instruct the user to run /aep-calibrate <type> first.
Light calibrations (api-surface, data-model, scope-direction, performance-quality):
No additional context needed — decisions are already in the architecture section of product-context.yaml and the product section of product/index.yaml (split mode), which flow through the stable prefix (Part 1) and story-specific payload (Part 2).
Backward compatibility: For .5 layer stories without calibration_type set, default to visual-design. Check both calibration/visual-design.yaml and design-context.yaml (legacy path).
A story is UI-facing when it has object_model_refs set, calibration_type in
{visual-design, ux-flow}, or a non-null activity whose module has kind: ui
(architecture.modules[].kind). For these, inject the Object Map slice, not the
whole model:
Resolve the capability: use story.capability if set; otherwise (v1 /
single-journey) the default capability is the project slug. The Object Map is
product/maps/<capability>/object-map.yaml.
Gate (must pass to dispatch): the resolved object-map must exist, have
status: approved, and its coverage[] must list this story id. If it is missing,
status is draft or stale, or it does not cover the story → do not
dispatch; instruct the user to run /aep-model first (same posture as the
calibration gate). If story.capability is unset, fall back to scanning
product/maps/*/object-map.yaml for a coverage[].story match — an approved
match resolves the capability.
From the map's coverage index, select only the objects this story realizes and
include just those entries: their attributes (core/secondary/metadata), the
relationships among them, the CTAs (object × role) on them, and the screen(s) the
story builds. Skip unrelated objects — keep the slice minimal.
Include the object-first directive:
This story realizes objects from an approved Object Map.
Build object-first (noun→verb): the listed screens, object cards/detail,
attributes, and CTA placements come from product/maps/<capability>/object-map.yaml.
Do not introduce objects or screen structures not in this slice, and do not
collapse the flow into a step-by-step wizard unless the map marks it task_oriented.
Visual look, copy voice, and journey/transition still come from
calibration/{visual-design,copy-tone,ux-flow}.yaml — the Object Map governs object
structure and CTA grammar, not taste.
CRITICAL: Commit and push ALL dispatch artifacts (YAML updates, OpenSpec changes, changelog) to remote BEFORE handing off to
/aep-launch. If the dispatch commit stays local, it will be lost when workspace PRs merge to the integration branch and you rebase. The push ensures OpenSpec changes survive on the remote.
Append to the changelog section:
- date: <today>
type: dispatch
author: human
summary: "Dispatched PROJ-003 (auth middleware), PROJ-004 (user model) — Layer 0, Wave 1"
sections_changed: [stories]
Commit and push:
# Resolve $BASE (integration branch) — see git-ref "Integration Branch" (override → develop → main)
BASE=$(git config --get aep.integration-branch 2>/dev/null || true)
[ -z "$BASE" ] && { git show-ref --verify --quiet refs/heads/develop \
|| git show-ref --verify --quiet refs/remotes/origin/develop; } && BASE=develop
BASE=${BASE:-main}
git pull --ff-only origin "$BASE"
git add product-context.yaml openspec/changes/
git commit -m "feat: dispatch PROJ-003, PROJ-004 — Layer 0 Wave 1"
git push origin "$BASE"
Verify the push succeeded before proceeding to handoff. If push fails (e.g., remote conflict), resolve before launching workspaces.
Launch mode is normally resolved at
/aep-launch, not here. For the default path dispatch stays executor-agnostic — it hands a well-specified change to/aep-launch, which detects the host and selects a mode (native-bg-subagent / claude-bg / codex-subagent / codex-exec / legacy) viaaep-executor. Native modes outrank tmux on every host; dispatch does not need to know. The one exception is the Dynamic Workflow opt-in (Step 5): that path runs the workflow mode from dispatch, bypassing/aep-launch, so dispatch itself owns mode selection and the announcement for that case.
Determine the handoff based on story completeness:
Use the readiness_score computed in Step 3:
/aep-launch (spec is dispatch-ready)/aep-launch or /aep-design)/aep-design (spec needs refinement)Routing of an under-ready story depends on two topology.routing flags — the
full_auto master switch (default false) and the finer-grained auto_design
(default false). full_auto sits above auto_design: full_auto: true
implies auto_design: true.
full_auto: true OR auto_design: true → a medium/low-readiness story
(readiness < 0.7) is resolved by a non-interactive gen/eval design resolver —
a design agent that refines the spec without a human, then routes to
/aep-launch — instead of escalating to interactive /aep-design (the G3 human
gate). No strategic pause.full_auto: false AND auto_design: false (default) → keep escalating: a
medium/low-readiness story routes to interactive /aep-design for human design
refinement before launch. The strategic "what to build" gate stays with the human.Story PROJ-003 dispatched (score: 14.5, critical path, shared enabler).
OpenSpec change: openspec/changes/PROJ-003/
Context package: openspec/changes/PROJ-003/.context/
Recommendation: Readiness 0.9 — well-specified
→ Skip to /aep-launch
/aep-launch ← start building immediately
/aep-design ← refine the spec first
For batch dispatch, create all workspaces via /aep-launch:
Batch dispatched: PROJ-003 (score 23.0), PROJ-004 (score 12.0)
/aep-launch PROJ-003 → tab: auth-middleware
/aep-launch PROJ-004 → tab: user-model
in_progress stories are stuck (high attempt_count, old started_at). Suggest checking workspace progress or running /aep-reflect./aep-reflect for final review..context/ directory is what the agent reads.npx claudepluginhub memorysaver/agentic-engineering-patterns --plugin agentic-development-workflowProvides behavioral guidelines to reduce common LLM coding mistakes, focusing on simplicity, surgical changes, assumption surfacing, and verifiable success criteria.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.