diff --git a/.changeset/multiagent-workflow.md b/.changeset/multiagent-workflow.md new file mode 100644 index 000000000..0632e27b6 --- /dev/null +++ b/.changeset/multiagent-workflow.md @@ -0,0 +1,12 @@ +--- +"@fission-ai/openspec": minor +--- + +Add multi-agent orchestration workflow with dispec-driven schema + +- New `dispec-driven` schema for distributing work across multiple AI agents +- `/opsx:multiagent` workflow: plans and distributes a change into agent-assignable tasks +- `/opsx:multiagent-apply` workflow: orchestrates a Claude Code agent team to implement tasks in parallel +- New artifact types: `dependencies.md` (dependency analysis) and `distribution.md` (agent work packages) +- Schema templates for all new artifact types +- Support for GitHub-based npm dependency installation in `build.js` diff --git a/openspec/changes/add-multiagent-apply-workflow/.openspec.yaml b/openspec/changes/add-multiagent-apply-workflow/.openspec.yaml new file mode 100644 index 000000000..fbe5b53c8 --- /dev/null +++ b/openspec/changes/add-multiagent-apply-workflow/.openspec.yaml @@ -0,0 +1,2 @@ +schema: dispec-driven +created: 2026-03-01 diff --git a/openspec/changes/add-multiagent-apply-workflow/dependencies.md b/openspec/changes/add-multiagent-apply-workflow/dependencies.md new file mode 100644 index 000000000..95db14edf --- /dev/null +++ b/openspec/changes/add-multiagent-apply-workflow/dependencies.md @@ -0,0 +1,108 @@ +## Dependency Matrix + +| Task ID | Depends On | Type | Reason | +|---------|-----------|------|--------| +| 1.1 | — | — | No dependencies, new file | +| 1.2 | 1.1 | FS | Needs the file created in 1.1 | +| 1.3 | 1.1 | FS | Needs the file created in 1.1 | +| 2.1 | 1.1 | FS | Needs the export functions to exist | +| 2.2 | 1.1 | FS | Needs the template functions to register | +| 2.3 | 1.1 | FS | Needs the template functions to register | +| 2.4 | — | — | Independent constant update | +| 3.1 | — | — | Independent file edit | +| 3.2 | — | — | Independent file edit | +| 3.3 | — | — | Independent file edit | +| 4.1 | — | — | Independent template file edit | +| 5.1 | — | — | Independent schema file edit | +| 5.2 | — | — | Independent schema file edit, same file as 5.1 | +| 6.1 | — | — | Independent constant update | +| 6.2 | — | — | Independent constant update | +| 7.1 | 1.1, 1.2, 1.3 | FS | Tests the new skill template | +| 7.2 | 1.1, 1.2, 1.3 | FS | Tests the new command template | +| 7.3 | 2.1, 2.2, 2.3 | FS | Tests parity including new entry | +| 7.4 | 2.4 | FS | Tests ALL_WORKFLOWS includes new entry | +| 7.5 | 2.2, 2.3 | FS | Tests template count with new entry | +| 7.6 | — | — | Regression check, no dependencies | +| 7.7 | 3.1, 3.2 | FS | Verifies TodoWrite removal | + +## Critical Path + +``` +1.1 → 1.2 → 7.1 + → 7.2 +1.1 → 2.1 → 7.3 + 2.2 → + 2.3 → +``` + +The longest chain is: **1.1 → 1.2/1.3 → 7.1/7.2** (3 steps). + +Overall critical path duration is short since most tasks are independent edits. + +## Parallel Execution Waves + +### Wave 1 (no dependencies) +- 1.1 Create multiagent-apply.ts file +- 2.4 Add to ALL_WORKFLOWS +- 3.1 Fix TodoWrite in skill template +- 3.2 Fix TodoWrite in command template +- 3.3 Update output section reference +- 4.1 Update distribution.md template +- 5.1 Update distribution artifact instruction +- 5.2 Update apply.instruction +- 6.1 Add to tool-detection +- 6.2 Add to init +- 7.6 Regression check on dispec-driven tests + +### Wave 2 (depends on Wave 1) +- 1.2 Write skill instructions prompt (depends on 1.1) +- 1.3 Write command template content (depends on 1.1) +- 2.1 Export from skill-templates.ts (depends on 1.1) +- 2.2 Register in getSkillTemplates (depends on 1.1) +- 2.3 Register in getCommandTemplates (depends on 1.1) +- 7.4 Test ALL_WORKFLOWS (depends on 2.4) +- 7.7 Verify TodoWrite removed (depends on 3.1, 3.2) + +### Wave 3 (depends on Wave 2) +- 7.1 Test skill template (depends on 1.2, 1.3) +- 7.2 Test command template (depends on 1.2, 1.3) +- 7.3 Test parity (depends on 2.1, 2.2, 2.3) +- 7.5 Test template count (depends on 2.2, 2.3) + +## Float / Slack + +| Task ID | Float | Notes | +|---------|-------|-------| +| 2.4 | High | Independent of critical path, can be done anytime in Wave 1 | +| 3.1–3.3 | High | Independent edits, no downstream blockers except 7.7 | +| 4.1 | High | Independent template edit, no downstream blockers | +| 5.1–5.2 | High | Independent schema edits, no downstream blockers | +| 6.1–6.2 | High | Independent constant updates, no downstream blockers | +| 7.6 | High | Regression check, can run anytime | + +## Text DAG + +``` +Wave 1 Wave 2 Wave 3 +────── ────── ────── + +[1.1] ──────────────────→ [1.2] ──────────────────→ [7.1] + │ [1.3] ──────────────────→ [7.2] + │ + ├──────────────→ [2.1] ──┐ + ├──────────────→ [2.2] ──┼──────────────→ [7.3] + └──────────────→ [2.3] ──┘ [7.5] + +[2.4] ──────────────────→ [7.4] + +[3.1] ──┐ +[3.2] ──┼──────────────→ [7.7] +[3.3] │ + +[4.1] +[5.1] +[5.2] +[6.1] +[6.2] +[7.6] +``` diff --git a/openspec/changes/add-multiagent-apply-workflow/design.md b/openspec/changes/add-multiagent-apply-workflow/design.md new file mode 100644 index 000000000..c6cab08fb --- /dev/null +++ b/openspec/changes/add-multiagent-apply-workflow/design.md @@ -0,0 +1,91 @@ +## Context + +The `dispec-driven` schema produces six artifacts: proposal → specs → design → tasks → dependencies → distribution. The first four are shared with `spec-driven`; the last two (`dependencies.md`, `distribution.md`) are unique to multi-agent execution. The existing `/opsx:apply` skill reads `tasks.md` and works through tasks sequentially with a single agent. There is no apply variant that leverages the distribution plan to orchestrate a Claude Code agent team. + +Claude Code provides team coordination primitives: `TeamCreate` (creates a team + shared task list), `Agent` (spawns teammates with `team_name`), `TaskCreate`/`TaskUpdate`/`TaskList` (shared task list), and `SendMessage` (inter-agent communication). Teammates can be isolated via `isolation: "worktree"` on the Agent tool call. + +The `/opsx:multiagent` skill currently references `TodoWrite` (deprecated) and the distribution template's "Claude Code Team Setup" section uses pseudo-bash comments instead of describing real tool calls. + +## Goals / Non-Goals + +**Goals:** +- Create an `/opsx:multiagent-apply` skill that bridges the distribution plan to Claude Code team execution +- Update `/opsx:multiagent` to use correct Claude Code primitives (`TaskCreate`/`TaskUpdate` instead of `TodoWrite`) +- Update the `distribution.md` template to produce actionable team setup instructions +- Update the `dispec-driven` schema's `apply.instruction` to describe multi-agent execution + +**Non-Goals:** +- Changing the `spec-driven` schema or its apply workflow +- Adding a programmatic API for team orchestration (this is skill/prompt-level orchestration) +- Auto-merging worktree branches (agents produce branches; the user merges) +- Supporting non-Claude-Code agent runtimes + +## Decisions + +### 1. Skill-level orchestration, not programmatic + +**Decision:** The multiagent-apply workflow is implemented as a skill template (prompt instructions) that guides the AI agent through team setup, not as TypeScript code that calls Claude Code APIs directly. + +**Rationale:** OpenSpec skills are prompt-driven — they instruct the AI agent what tools to call. This matches the existing pattern (`/opsx:apply`, `/opsx:propose`, etc.) and doesn't require OpenSpec to have a programmatic dependency on Claude Code's internal APIs. The skill text tells the agent: "call TeamCreate with these params, then call Agent for each teammate." + +**Alternative considered:** A TypeScript orchestrator that programmatically calls Claude Code APIs. Rejected because OpenSpec is tool-agnostic and should not couple to Claude Code's runtime. + +### 2. One skill per concern: multiagent-apply is separate from apply + +**Decision:** Create `/opsx:multiagent-apply` as a new skill rather than adding multi-agent logic to the existing `/opsx:apply`. + +**Rationale:** The apply skill is already complex. Multi-agent adds team creation, task list population, teammate spawning, progress monitoring, and shutdown — fundamentally different from sequential task execution. Keeping them separate avoids conditional branching in the apply prompt and lets each skill be focused. + +**Alternative considered:** Adding a `--multiagent` flag to `/opsx:apply`. Rejected because it would make the apply prompt too long and hard to maintain. + +### 3. Worktree isolation for all teammates + +**Decision:** All spawned teammates use `isolation: "worktree"` to get an isolated copy of the repository. + +**Rationale:** Multiple agents writing to the same repo simultaneously causes conflicts. Worktrees give each agent a private branch. After completion, the user (or the team lead agent) can merge branches. This aligns with how Claude Code's Agent tool is designed to work in team scenarios. + +### 4. Task list as the coordination mechanism + +**Decision:** Use Claude Code's `TaskCreate`/`TaskUpdate`/`TaskList` tools as the shared coordination layer between agents, not file-based coordination. + +**Rationale:** Claude Code's task list is purpose-built for multi-agent coordination — it supports ownership, status tracking, and dependency relationships (`blockedBy`/`addBlocks`). Writing a custom file-based tracker would duplicate this functionality. + +### 5. Distribution template uses descriptive instructions, not tool-call JSON + +**Decision:** The "Claude Code Team Setup" section in `distribution.md` describes team setup in natural language with parameter listings, not raw JSON tool calls. + +**Rationale:** The distribution.md is a planning document read by both humans and AI. Raw JSON would be harder for humans to review. The `/opsx:multiagent-apply` skill is responsible for translating the plan into actual tool calls. + +## Risks / Trade-offs + +**[Worktree merge conflicts]** → Agents work on isolated branches, but merging may produce conflicts. Mitigation: the distribution plan enforces file ownership isolation (one agent per file). The skill warns if isolation is violated. + +**[Token cost scaling]** → N agents ≈ N× token cost. Mitigation: the skill displays an explicit cost warning and asks for confirmation before spawning. The distribution artifact already includes a cost warning section. + +**[Cross-agent sync latency]** → If agent A blocks agent B, B idles until A completes the blocking task. Mitigation: the dependency analysis minimizes cross-agent dependencies; the distribution plan groups dependent tasks into the same agent where possible. + +**[Skill prompt length]** → The multiagent-apply skill prompt is necessarily longer than simpler skills. Mitigation: structure the prompt with clear numbered steps and keep orchestration logic in the skill, not in the schema instruction. + +## Migration Plan + +No migration needed — this adds new capabilities without changing existing ones. The `/opsx:apply` skill continues to work unchanged for single-agent and `spec-driven` workflows. Users opt into multi-agent execution by using `/opsx:multiagent` (planning) followed by `/opsx:multiagent-apply` (execution). + +The `TodoWrite` → `TaskCreate`/`TaskUpdate` fix in `/opsx:multiagent` is backward-compatible since `TodoWrite` was already non-functional in current Claude Code. + +## Parallelism Considerations + +Three independent workstreams: + +1. **New skill template** (`multiagent-apply.ts`): The new `/opsx:multiagent-apply` skill and command templates. No dependencies on the other two workstreams — can be built first or in parallel. + +2. **Template fixes** (`multiagent.ts` + `distribution.md`): Update the existing `/opsx:multiagent` skill to fix `TodoWrite` references and update the distribution template's team setup section. Independent of workstream 1. + +3. **Schema instruction update** (`schema.yaml`): Update `dispec-driven`'s `apply.instruction` and `distribution` artifact instruction. Depends on workstream 1 being defined (needs to reference the new skill name) but can be written in parallel once the skill name is agreed (`/opsx:multiagent-apply`). + +**Serialization point:** Tests should be written after all three workstreams are complete, as they may need to verify the integration between the skill, template, and schema. + +## Open Questions + +1. **Should the team lead agent remain active for monitoring, or hand off to the user?** Current design: the skill acts as team lead, monitors progress via `TaskList`, and sends `shutdown_request` when done. Alternative: create the team and return immediately, letting the user monitor. + +2. **Should multiagent-apply automatically merge worktree branches?** Current design: no — agents produce branches, user merges. This is safer but adds a manual step. diff --git a/openspec/changes/add-multiagent-apply-workflow/distribution.md b/openspec/changes/add-multiagent-apply-workflow/distribution.md new file mode 100644 index 000000000..8eaf181ac --- /dev/null +++ b/openspec/changes/add-multiagent-apply-workflow/distribution.md @@ -0,0 +1,172 @@ +## Configuration + +- **Agent count**: 3 +- **Total tasks**: 22 +- **Tasks per agent**: ~7 + +## Token Cost Warning + +> **Multi-agent execution scales token costs.** Each agent maintains its own context window. +> With 3 agents, expect roughly 3× the token usage of a single-agent run. +> Estimated cost multiplier: **3×** + +## Feasibility Assessment + +3 agents is feasible. The dependency graph shows three natural clusters: + +1. **New skill creation** (tasks 1.x, 2.x) — all centered on the new `multiagent-apply.ts` file and its registration +2. **Existing template fixes** (tasks 3.x, 4.x, 5.x, 6.x) — edits to existing files, fully independent of cluster 1 +3. **Tests** (tasks 7.x) — depends on both clusters completing first + +With 3 agents, cross-agent dependencies are minimal: only the test agent needs to wait for the other two. + +## Agent Assignments + +### Agent 1: new-skill + +**Focus:** Create the new `/opsx:multiagent-apply` skill template and register it in the system. + +**Tasks:** +- 1.1 Create `multiagent-apply.ts` file +- 1.2 Write skill instructions prompt +- 1.3 Write command template content +- 2.1 Export from `skill-templates.ts` +- 2.2 Register in `getSkillTemplates()` +- 2.3 Register in `getCommandTemplates()` +- 2.4 Add `'multiagent-apply'` to `ALL_WORKFLOWS` + +**File ownership:** +- `src/core/templates/workflows/multiagent-apply.ts` (create) +- `src/core/templates/skill-templates.ts` (edit) +- `src/core/shared/skill-generation.ts` (edit) +- `src/core/profiles.ts` (edit) + +**Execution order:** +1.1 → 1.2 → 1.3 → 2.1 → 2.2 → 2.3 → 2.4 + +**Cross-agent dependencies:** +- None. This agent works independently. + +### Agent 2: template-fixes + +**Focus:** Fix the existing multiagent skill, distribution template, schema instructions, and tool detection. + +**Tasks:** +- 3.1 Fix TodoWrite in skill template +- 3.2 Fix TodoWrite in command template +- 3.3 Update output section to reference `/opsx:multiagent-apply` +- 4.1 Update distribution.md template team setup section +- 5.1 Update distribution artifact instruction in schema.yaml +- 5.2 Update apply.instruction in schema.yaml +- 6.1 Add to tool-detection.ts +- 6.2 Add to init.ts + +**File ownership:** +- `src/core/templates/workflows/multiagent.ts` (edit) +- `schemas/dispec-driven/templates/distribution.md` (edit) +- `schemas/dispec-driven/schema.yaml` (edit) +- `src/core/shared/tool-detection.ts` (edit) +- `src/core/init.ts` (edit) + +**Execution order:** +3.1 → 3.2 → 3.3 → 4.1 → 5.1 → 5.2 → 6.1 → 6.2 + +**Cross-agent dependencies:** +- None. This agent works independently. + +### Agent 3: tests + +**Focus:** Write and update all tests, verify integration. + +**Tasks:** +- 7.1 Test `getOpsxMultiagentApplySkillTemplate()` +- 7.2 Test `getOpsxMultiagentApplyCommandTemplate()` +- 7.3 Update parity test +- 7.4 Test ALL_WORKFLOWS includes `'multiagent-apply'` +- 7.5 Update skill-generation test counts +- 7.6 Regression check dispec-driven tests +- 7.7 Verify TodoWrite removal + +**File ownership:** +- `test/core/templates/multiagent-apply.test.ts` (create) +- `test/core/templates/skill-templates-parity.test.ts` (edit) +- `test/core/profiles.test.ts` (edit) +- `test/core/shared/skill-generation.test.ts` (edit) +- `test/core/artifact-graph/dispec-driven.test.ts` (read-only) + +**Execution order:** +7.6 (can start immediately) → wait for Agent 1 and Agent 2 → 7.1 → 7.2 → 7.3 → 7.4 → 7.5 → 7.7 + +**Cross-agent dependencies:** +- 7.1, 7.2 blocked by Agent 1 completing tasks 1.1–1.3 +- 7.3, 7.5 blocked by Agent 1 completing tasks 2.1–2.3 +- 7.4 blocked by Agent 1 completing task 2.4 +- 7.7 blocked by Agent 2 completing tasks 3.1–3.2 + +## File Ownership Isolation + +| File | Owner Agent | Notes | +|------|-------------|-------| +| `src/core/templates/workflows/multiagent-apply.ts` | new-skill | New file, exclusive | +| `src/core/templates/skill-templates.ts` | new-skill | Add export line | +| `src/core/shared/skill-generation.ts` | new-skill | Add entries to arrays | +| `src/core/profiles.ts` | new-skill | Add to ALL_WORKFLOWS | +| `src/core/templates/workflows/multiagent.ts` | template-fixes | Edit existing content | +| `schemas/dispec-driven/templates/distribution.md` | template-fixes | Edit template | +| `schemas/dispec-driven/schema.yaml` | template-fixes | Edit instructions | +| `src/core/shared/tool-detection.ts` | template-fixes | Add workflow entry | +| `src/core/init.ts` | template-fixes | Add workflow entry | +| `test/core/templates/multiagent-apply.test.ts` | tests | New file, exclusive | +| `test/core/templates/skill-templates-parity.test.ts` | tests | Edit assertions | +| `test/core/profiles.test.ts` | tests | Edit assertions | +| `test/core/shared/skill-generation.test.ts` | tests | Edit assertions | + +No file ownership conflicts detected. Each file is owned by exactly one agent. + +## Cross-Agent Dependencies + +| Waiting Agent | Blocked Task | Depends On | Owning Agent | +|---------------|-------------|------------|--------------| +| tests | 7.1 | 1.1, 1.2, 1.3 | new-skill | +| tests | 7.2 | 1.1, 1.2, 1.3 | new-skill | +| tests | 7.3 | 2.1, 2.2, 2.3 | new-skill | +| tests | 7.4 | 2.4 | new-skill | +| tests | 7.5 | 2.2, 2.3 | new-skill | +| tests | 7.7 | 3.1, 3.2 | template-fixes | + +The tests agent is the only one with cross-agent dependencies. Agents "new-skill" and "template-fixes" are fully independent of each other. + +## Claude Code Team Setup + +To execute this plan, use `/opsx:multiagent-apply` on the `add-multiagent-apply-workflow` change. + +Alternatively, set up the team manually: + +**1. Create the team:** + +Use `TeamCreate` with: +- `team_name`: `"add-multiagent-apply-workflow"` +- `description`: `"Add /opsx:multiagent-apply skill and fix existing multiagent workflow"` + +**2. Create tasks in the shared task list:** + +Use `TaskCreate` for each of the 22 tasks from `tasks.md`. Include the task description as `subject`, the `Files:` annotation as `description`, and a present-continuous `activeForm` (e.g., "Creating multiagent-apply.ts"). + +After all tasks are created, use `TaskUpdate` with `addBlockedBy` to set up dependency relationships, and `TaskUpdate` with `owner` to pre-assign tasks to agents per the assignment cards above. + +**3. Spawn teammates:** + +Use the `Agent` tool three times, once per agent: + +- Agent: `name: "new-skill"`, `team_name: "add-multiagent-apply-workflow"`, `subagent_type: "general-purpose"`, `isolation: "worktree"` + - Prompt: Assign tasks 1.1–1.3, 2.1–2.4. Include file ownership list and execution order. + +- Agent: `name: "template-fixes"`, `team_name: "add-multiagent-apply-workflow"`, `subagent_type: "general-purpose"`, `isolation: "worktree"` + - Prompt: Assign tasks 3.1–3.3, 4.1, 5.1–5.2, 6.1–6.2. Include file ownership list and execution order. + +- Agent: `name: "tests"`, `team_name: "add-multiagent-apply-workflow"`, `subagent_type: "general-purpose"`, `isolation: "worktree"` + - Prompt: Assign tasks 7.1–7.7. Note cross-agent dependencies: start with 7.6 (no deps), then wait for new-skill and template-fixes to complete before running remaining tests. + +**4. Monitor and shutdown:** + +Use `TaskList` to monitor progress. When all tasks are completed, send `shutdown_request` to each teammate via `SendMessage`. diff --git a/openspec/changes/add-multiagent-apply-workflow/proposal.md b/openspec/changes/add-multiagent-apply-workflow/proposal.md new file mode 100644 index 000000000..0ce3f0003 --- /dev/null +++ b/openspec/changes/add-multiagent-apply-workflow/proposal.md @@ -0,0 +1,29 @@ +## Why + +The `dispec-driven` schema produces a full distribution plan (dependencies.md, distribution.md) with agent assignments, file ownership, and cross-agent sync points. But the workflow stops at planning — there is no apply phase that actually sets up and runs a Claude Code agent team. Users must manually translate the distribution document into `TeamCreate`, `Agent`, and `TaskCreate` calls, which defeats the purpose of automated multi-agent execution. + +Additionally, the current `/opsx:multiagent` skill references `TodoWrite` (deprecated) and shows pseudo-bash comments instead of real Claude Code team primitives. + +## What Changes + +- **Add `/opsx:multiagent-apply` workflow**: A new skill/command that reads `distribution.md` and orchestrates a Claude Code agent team — creating the team, spawning teammates, populating the shared task list, and monitoring execution. +- **Update `/opsx:multiagent` skill**: Replace `TodoWrite` references with `TaskCreate`/`TaskUpdate`. Fix the "Claude Code Team Setup" section in `distribution.md` template to produce actionable instructions using real team primitives (`TeamCreate`, `Agent` with `team_name`, `SendMessage`). +- **Add team-aware apply instruction**: The `dispec-driven` schema's `apply.instruction` needs to describe multi-agent execution: how to create a team, spawn agents with isolated worktrees, assign tasks via `TaskUpdate`, and handle cross-agent dependencies. + +## Capabilities + +### New Capabilities + +- **multiagent-apply**: The `/opsx:multiagent-apply` skill that reads a completed distribution plan and orchestrates a Claude Code agent team for parallel implementation. + +### Modified Capabilities + +- **command-generation**: Update the distribution.md template to produce real Claude Code team setup instructions instead of pseudo-bash comments. + +## Impact + +- `src/core/templates/workflows/multiagent.ts` — update skill/command template text +- `schemas/dispec-driven/schema.yaml` — update apply instruction and distribution artifact instruction +- `schemas/dispec-driven/templates/distribution.md` — update team setup section +- New workflow module for the multiagent-apply skill +- Tests for the new workflow diff --git a/openspec/changes/add-multiagent-apply-workflow/specs/command-generation/spec.md b/openspec/changes/add-multiagent-apply-workflow/specs/command-generation/spec.md new file mode 100644 index 000000000..979691759 --- /dev/null +++ b/openspec/changes/add-multiagent-apply-workflow/specs/command-generation/spec.md @@ -0,0 +1,25 @@ +## MODIFIED Requirements + +### Requirement: Shared command body content + +The body content of commands SHALL be shared across all tools. + +#### Scenario: Same instructions across tools + +- **WHEN** generating the 'explore' command for Claude and Cursor +- **THEN** both SHALL use the same `body` content +- **AND** only the frontmatter and file path SHALL differ + +#### Scenario: Multiagent workflow references correct team primitives + +- **WHEN** generating the `/opsx:multiagent` command or skill body +- **THEN** the body SHALL reference `TaskCreate` and `TaskUpdate` for progress tracking +- **AND** the body SHALL NOT reference `TodoWrite` + +#### Scenario: Distribution template produces actionable team setup + +- **WHEN** generating the distribution.md template for the `dispec-driven` schema +- **THEN** the "Claude Code Team Setup" section SHALL describe team creation using `TeamCreate` with `team_name` and `description` parameters +- **AND** the section SHALL describe teammate spawning using the `Agent` tool with `name`, `team_name`, `subagent_type`, and `prompt` parameters +- **AND** the section SHALL describe task creation using `TaskCreate` with `subject`, `description`, and `activeForm` parameters +- **AND** the section SHALL NOT use pseudo-bash comments or placeholder syntax diff --git a/openspec/changes/add-multiagent-apply-workflow/specs/multiagent-apply/spec.md b/openspec/changes/add-multiagent-apply-workflow/specs/multiagent-apply/spec.md new file mode 100644 index 000000000..03ea224fb --- /dev/null +++ b/openspec/changes/add-multiagent-apply-workflow/specs/multiagent-apply/spec.md @@ -0,0 +1,103 @@ +## ADDED Requirements + +### Requirement: Multiagent apply skill reads distribution plan + +The system SHALL provide an `/opsx:multiagent-apply` skill that reads a completed `distribution.md` artifact from a `dispec-driven` change and uses it to orchestrate a Claude Code agent team. + +#### Scenario: Successful team orchestration from distribution plan + +- **WHEN** the user invokes `/opsx:multiagent-apply` on a change with all `dispec-driven` artifacts complete +- **THEN** the skill SHALL read `distribution.md` to extract agent count, agent assignments, file ownership, and cross-agent dependencies +- **AND** the skill SHALL create a Claude Code team using `TeamCreate` with `team_name` derived from the change name +- **AND** the skill SHALL create tasks in the shared task list using `TaskCreate` for each task from `tasks.md`, preserving dependency relationships via `addBlockedBy`/`addBlocks` +- **AND** the skill SHALL spawn one teammate per agent assignment using the `Agent` tool with `team_name` and `name` parameters + +#### Scenario: Change missing required artifacts + +- **WHEN** the user invokes `/opsx:multiagent-apply` on a change where `distribution.md` does not exist +- **THEN** the skill SHALL report which artifacts are missing +- **AND** the skill SHALL suggest running `/opsx:multiagent` to complete the planning phase + +#### Scenario: Change uses non-dispec schema + +- **WHEN** the user invokes `/opsx:multiagent-apply` on a change that uses `spec-driven` or another schema without a `distribution` artifact +- **THEN** the skill SHALL inform the user that multi-agent apply requires the `dispec-driven` schema +- **AND** the skill SHALL suggest using `/opsx:apply` for single-agent execution instead + +### Requirement: Team creation from distribution plan + +The system SHALL create a Claude Code team with teammates mapped from the distribution plan's agent assignment cards. + +#### Scenario: Team and teammates created correctly + +- **WHEN** the distribution plan specifies 3 agents named "schema-updates", "skill-templates", and "tests" +- **THEN** the skill SHALL call `TeamCreate` with `team_name` set to the change name (e.g., `"add-multiagent-apply-workflow"`) +- **AND** the skill SHALL spawn 3 teammates using the `Agent` tool, each with `name` matching the agent name from the distribution plan, `team_name` set to the team name, and `subagent_type` set to `"general-purpose"` + +#### Scenario: Teammate prompt includes agent context + +- **WHEN** spawning a teammate for an agent assignment +- **THEN** the teammate's `prompt` SHALL include: the agent's assigned task IDs, file ownership list, execution order, cross-agent dependency descriptions, and a reference to the change directory path +- **AND** the prompt SHALL instruct the teammate to use `TaskList` to find available work and `TaskUpdate` to claim and complete tasks + +### Requirement: Task list population from tasks.md + +The system SHALL parse `tasks.md` and create entries in the Claude Code shared task list that teammates can claim. + +#### Scenario: Tasks created with correct metadata + +- **WHEN** populating the task list from a `tasks.md` containing 20 checkbox tasks +- **THEN** the skill SHALL call `TaskCreate` once per task with `subject` set to the task description and `description` including the `Files:` annotation content +- **AND** each task SHALL have an `activeForm` derived from the task description (present continuous form) + +#### Scenario: Task dependencies preserved + +- **WHEN** `dependencies.md` specifies that task 2.1 has a Finish-to-Start dependency on task 1.3 +- **THEN** the skill SHALL use `TaskUpdate` with `addBlockedBy` to link task 2.1 to task 1.3 after both are created + +#### Scenario: Tasks pre-assigned to agents + +- **WHEN** `distribution.md` assigns tasks 1.1, 1.2, and 2.1 to agent "schema-updates" +- **THEN** the skill SHALL use `TaskUpdate` with `owner` set to `"schema-updates"` for those tasks after creation + +### Requirement: Teammate isolation with worktrees + +The system SHALL spawn teammates with `isolation: "worktree"` to prevent file conflicts between agents working in parallel. + +#### Scenario: Agents work in isolated worktrees + +- **WHEN** spawning teammates for parallel execution +- **THEN** each teammate Agent call SHALL include `isolation: "worktree"` so that each agent operates on an isolated copy of the repository + +#### Scenario: File ownership conflict detected + +- **WHEN** the distribution plan shows two agents writing to the same file +- **THEN** the skill SHALL warn the user about the conflict before spawning teammates +- **AND** the skill SHALL suggest reordering or reassigning tasks to eliminate the conflict + +### Requirement: Cross-agent dependency monitoring + +The system SHALL handle sync points where one agent must wait for another agent's output. + +#### Scenario: Agent blocked by cross-agent dependency + +- **WHEN** agent "tests" has tasks blocked by agent "schema-updates" completing task 1.3 +- **THEN** the task list SHALL reflect this via `blockedBy` relationships +- **AND** when agent "schema-updates" marks task 1.3 as completed via `TaskUpdate`, the blocked tasks for agent "tests" SHALL become unblocked automatically + +#### Scenario: Team lead monitors progress + +- **WHEN** the multi-agent execution is running +- **THEN** the skill (acting as team lead) SHALL periodically check `TaskList` to monitor overall progress +- **AND** the skill SHALL report completion status to the user when all tasks are done +- **AND** the skill SHALL send `shutdown_request` messages to all teammates when all tasks are completed + +### Requirement: Token cost warning before execution + +The system SHALL warn the user about token cost scaling before spawning agents. + +#### Scenario: Cost warning displayed + +- **WHEN** the user confirms multi-agent execution with N agents +- **THEN** the skill SHALL display a warning stating that N agents will consume approximately N× the token usage of a single-agent run +- **AND** the skill SHALL ask the user to confirm before proceeding with team creation diff --git a/openspec/changes/add-multiagent-apply-workflow/tasks.md b/openspec/changes/add-multiagent-apply-workflow/tasks.md new file mode 100644 index 000000000..a40724d8f --- /dev/null +++ b/openspec/changes/add-multiagent-apply-workflow/tasks.md @@ -0,0 +1,64 @@ +## 1. New multiagent-apply skill template + +- [ ] 1.1 Create `src/core/templates/workflows/multiagent-apply.ts` with `getOpsxMultiagentApplySkillTemplate()` and `getOpsxMultiagentApplyCommandTemplate()` functions + +- [ ] 1.2 Write the skill instructions prompt covering: change selection, schema validation (must be dispec-driven), reading distribution.md, TeamCreate, TaskCreate population from tasks.md, Agent spawning per assignment card, progress monitoring via TaskList, and shutdown + +- [ ] 1.3 Write the command template with the same content, category "Workflow", tags `['workflow', 'multiagent', 'apply']` + + +## 2. Register multiagent-apply in the template system + +- [ ] 2.1 Export the new template functions from `src/core/templates/skill-templates.ts` + +- [ ] 2.2 Add the new skill entry to `getSkillTemplates()` in `src/core/shared/skill-generation.ts` with `dirName: 'openspec-multiagent-apply'` and `workflowId: 'multiagent-apply'` + +- [ ] 2.3 Add the new command entry to `getCommandTemplates()` in `src/core/shared/skill-generation.ts` with `id: 'multiagent-apply'` + +- [ ] 2.4 Add `'multiagent-apply'` to `ALL_WORKFLOWS` in `src/core/profiles.ts` + + +## 3. Fix existing multiagent skill template + +- [ ] 3.1 Replace all `TodoWrite` references with `TaskCreate`/`TaskUpdate` in `getOpsxMultiagentSkillTemplate()` instructions + +- [ ] 3.2 Replace all `TodoWrite` references with `TaskCreate`/`TaskUpdate` in `getOpsxMultiagentCommandTemplate()` content + +- [ ] 3.3 Update the skill output section to reference `/opsx:multiagent-apply` as the next step instead of generic "set up a Claude Code team" + + +## 4. Update distribution.md template + +- [ ] 4.1 Replace the pseudo-bash "Claude Code Team Setup" section with actionable instructions describing `TeamCreate` (with `team_name` and `description` params), `Agent` spawning (with `name`, `team_name`, `subagent_type`, `isolation`, `prompt` params), and `TaskCreate` (with `subject`, `description`, `activeForm` params) + + +## 5. Update dispec-driven schema instructions + +- [ ] 5.1 Update the `distribution` artifact's `instruction` field to reference real Claude Code team primitives instead of pseudo-bash and to mention `/opsx:multiagent-apply` as the execution step + +- [ ] 5.2 Update the `apply.instruction` field to describe multi-agent execution: reading distribution.md, creating a team, spawning agents with worktree isolation, assigning tasks, and monitoring via TaskList + + +## 6. Update tool detection for multiagent-apply + +- [ ] 6.1 Add `'multiagent-apply'` to the tool detection workflow list in `src/core/shared/tool-detection.ts` if workflows are enumerated there + +- [ ] 6.2 Add `'multiagent-apply'` to the init workflow registration in `src/core/init.ts` if workflows are enumerated there + + +## 7. Tests + +- [ ] 7.1 Add unit test for `getOpsxMultiagentApplySkillTemplate()` verifying it returns valid `SkillTemplate` with correct name, description, and non-empty instructions + +- [ ] 7.2 Add unit test for `getOpsxMultiagentApplyCommandTemplate()` verifying it returns valid `CommandTemplate` with correct name, category, tags, and non-empty content + +- [ ] 7.3 Update `test/core/templates/skill-templates-parity.test.ts` to include multiagent-apply in parity checks + +- [ ] 7.4 Update `test/core/profiles.test.ts` to verify `ALL_WORKFLOWS` includes `'multiagent-apply'` + +- [ ] 7.5 Update `test/core/shared/skill-generation.test.ts` to include multiagent-apply in template count assertions + +- [ ] 7.6 Verify existing `test/core/artifact-graph/dispec-driven.test.ts` still passes (no changes expected, regression check) + +- [ ] 7.7 Verify the `/opsx:multiagent` skill template no longer references `TodoWrite` + diff --git a/openspec/changes/fix-opencode-commands-directory/.openspec.yaml b/openspec/changes/archive/2025-02-28-fix-opencode-commands-directory/.openspec.yaml similarity index 100% rename from openspec/changes/fix-opencode-commands-directory/.openspec.yaml rename to openspec/changes/archive/2025-02-28-fix-opencode-commands-directory/.openspec.yaml diff --git a/openspec/changes/fix-opencode-commands-directory/design.md b/openspec/changes/archive/2025-02-28-fix-opencode-commands-directory/design.md similarity index 100% rename from openspec/changes/fix-opencode-commands-directory/design.md rename to openspec/changes/archive/2025-02-28-fix-opencode-commands-directory/design.md diff --git a/openspec/changes/fix-opencode-commands-directory/proposal.md b/openspec/changes/archive/2025-02-28-fix-opencode-commands-directory/proposal.md similarity index 100% rename from openspec/changes/fix-opencode-commands-directory/proposal.md rename to openspec/changes/archive/2025-02-28-fix-opencode-commands-directory/proposal.md diff --git a/openspec/changes/fix-opencode-commands-directory/specs/command-generation/spec.md b/openspec/changes/archive/2025-02-28-fix-opencode-commands-directory/specs/command-generation/spec.md similarity index 100% rename from openspec/changes/fix-opencode-commands-directory/specs/command-generation/spec.md rename to openspec/changes/archive/2025-02-28-fix-opencode-commands-directory/specs/command-generation/spec.md diff --git a/openspec/changes/fix-opencode-commands-directory/tasks.md b/openspec/changes/archive/2025-02-28-fix-opencode-commands-directory/tasks.md similarity index 100% rename from openspec/changes/fix-opencode-commands-directory/tasks.md rename to openspec/changes/archive/2025-02-28-fix-opencode-commands-directory/tasks.md diff --git a/openspec/changes/graceful-status-no-changes/.openspec.yaml b/openspec/changes/archive/2025-02-28-graceful-status-no-changes/.openspec.yaml similarity index 100% rename from openspec/changes/graceful-status-no-changes/.openspec.yaml rename to openspec/changes/archive/2025-02-28-graceful-status-no-changes/.openspec.yaml diff --git a/openspec/changes/graceful-status-no-changes/design.md b/openspec/changes/archive/2025-02-28-graceful-status-no-changes/design.md similarity index 100% rename from openspec/changes/graceful-status-no-changes/design.md rename to openspec/changes/archive/2025-02-28-graceful-status-no-changes/design.md diff --git a/openspec/changes/graceful-status-no-changes/proposal.md b/openspec/changes/archive/2025-02-28-graceful-status-no-changes/proposal.md similarity index 100% rename from openspec/changes/graceful-status-no-changes/proposal.md rename to openspec/changes/archive/2025-02-28-graceful-status-no-changes/proposal.md diff --git a/openspec/changes/graceful-status-no-changes/specs/graceful-status-empty/spec.md b/openspec/changes/archive/2025-02-28-graceful-status-no-changes/specs/graceful-status-empty/spec.md similarity index 100% rename from openspec/changes/graceful-status-no-changes/specs/graceful-status-empty/spec.md rename to openspec/changes/archive/2025-02-28-graceful-status-no-changes/specs/graceful-status-empty/spec.md diff --git a/openspec/changes/graceful-status-no-changes/tasks.md b/openspec/changes/archive/2025-02-28-graceful-status-no-changes/tasks.md similarity index 100% rename from openspec/changes/graceful-status-no-changes/tasks.md rename to openspec/changes/archive/2025-02-28-graceful-status-no-changes/tasks.md diff --git a/package-lock.json b/package-lock.json index e1daf932f..5a485a6f4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1803,6 +1803,7 @@ "integrity": "sha512-oH72nZRfDv9lADUBSo104Aq7gPHpQZc4BTx38r9xf9pg5LfP6EzSyH2n7qFmmxRQXh7YlUXODcYsg6PuTDSxGg==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -1852,6 +1853,7 @@ "integrity": "sha512-IgSWvLobTDOjnaxAfDTIHaECbkNlAlKv2j5SjpB2v7QHKv1FIfjwMy8FsDbVfDX/KjmCmYICcw7uGaXLhtsLNg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.56.0", "@typescript-eslint/types": "8.56.0", @@ -2182,6 +2184,7 @@ "integrity": "sha512-hGISOaP18plkzbWEcP/QvtRW1xDXF2+96HbEX6byqQhAUbiS5oH6/9JwW+QsQCIYON2bI6QZBF+2PvOmrRZ9wA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@vitest/utils": "3.2.4", "fflate": "^0.8.2", @@ -2219,6 +2222,7 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2685,6 +2689,7 @@ "integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -4448,6 +4453,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -4546,6 +4552,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -4611,6 +4618,7 @@ "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -4727,6 +4735,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -4740,6 +4749,7 @@ "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", @@ -4919,6 +4929,7 @@ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", "license": "ISC", + "peer": true, "bin": { "yaml": "bin.mjs" }, diff --git a/schemas/dispec-driven/schema.yaml b/schemas/dispec-driven/schema.yaml new file mode 100644 index 000000000..da905297b --- /dev/null +++ b/schemas/dispec-driven/schema.yaml @@ -0,0 +1,282 @@ +name: dispec-driven +version: 1 +description: Distributed spec-driven workflow - proposal → (specs + design) → tasks → dependencies → distribution +artifacts: + - id: proposal + generates: proposal.md + description: Initial proposal document outlining the change + template: proposal.md + instruction: | + Create the proposal document that establishes WHY this change is needed. + + Sections: + - **Why**: 1-2 sentences on the problem or opportunity. What problem does this solve? Why now? + - **What Changes**: Bullet list of changes. Be specific about new capabilities, modifications, or removals. Mark breaking changes with **BREAKING**. + - **Capabilities**: Identify which specs will be created or modified: + - **New Capabilities**: List capabilities being introduced. Each becomes a new `specs//spec.md`. Use kebab-case names (e.g., `user-auth`, `data-export`). + - **Modified Capabilities**: List existing capabilities whose REQUIREMENTS are changing. Only include if spec-level behavior changes (not just implementation details). Each needs a delta spec file. Check `openspec/specs/` for existing spec names. Leave empty if no requirement changes. + - **Impact**: Affected code, APIs, dependencies, or systems. + + IMPORTANT: The Capabilities section is critical. It creates the contract between + proposal and specs phases. Research existing specs before filling this in. + Each capability listed here will need a corresponding spec file. + + Keep it concise (1-2 pages). Focus on the "why" not the "how" - + implementation details belong in design.md. + + This is the foundation - specs, design, and tasks all build on this. + + **Multi-agent consideration**: Scope capabilities so they can be worked on + independently by different agents. Identify modules or subsystems that are + naturally isolated, as this aids parallel execution downstream. + requires: [] + + - id: specs + generates: "specs/**/*.md" + description: Detailed specifications for the change + template: spec.md + instruction: | + Create specification files that define WHAT the system should do. + + Create one spec file per capability listed in the proposal's Capabilities section. + - New capabilities: use the exact kebab-case name from the proposal (specs//spec.md). + - Modified capabilities: use the existing spec folder name from openspec/specs// when creating the delta spec at specs//spec.md. + + Delta operations (use ## headers): + - **ADDED Requirements**: New capabilities + - **MODIFIED Requirements**: Changed behavior - MUST include full updated content + - **REMOVED Requirements**: Deprecated features - MUST include **Reason** and **Migration** + - **RENAMED Requirements**: Name changes only - use FROM:/TO: format + + Format requirements: + - Each requirement: `### Requirement: ` followed by description + - Use SHALL/MUST for normative requirements (avoid should/may) + - Each scenario: `#### Scenario: ` with WHEN/THEN format + - **CRITICAL**: Scenarios MUST use exactly 4 hashtags (`####`). Using 3 hashtags or bullets will fail silently. + - Every requirement MUST have at least one scenario. + + MODIFIED requirements workflow: + 1. Locate the existing requirement in openspec/specs//spec.md + 2. Copy the ENTIRE requirement block (from `### Requirement:` through all scenarios) + 3. Paste under `## MODIFIED Requirements` and edit to reflect new behavior + 4. Ensure header text matches exactly (whitespace-insensitive) + + Common pitfall: Using MODIFIED with partial content loses detail at archive time. + If adding new concerns without changing existing behavior, use ADDED instead. + + Example: + ``` + ## ADDED Requirements + + ### Requirement: User can export data + The system SHALL allow users to export their data in CSV format. + + #### Scenario: Successful export + - **WHEN** user clicks "Export" button + - **THEN** system downloads a CSV file with all user data + + ## REMOVED Requirements + + ### Requirement: Legacy export + **Reason**: Replaced by new export system + **Migration**: Use new export endpoint at /api/v2/export + ``` + + Specs should be testable - each scenario is a potential test case. + + **Multi-agent consideration**: For each capability, note the primary files and modules + it touches. This file/module ownership information aids task distribution across agents. + requires: + - proposal + + - id: design + generates: design.md + description: Technical design document with implementation details + template: design.md + instruction: | + Create the design document that explains HOW to implement the change. + + When to include design.md (create only if any apply): + - Cross-cutting change (multiple services/modules) or new architectural pattern + - New external dependency or significant data model changes + - Security, performance, or migration complexity + - Ambiguity that benefits from technical decisions before coding + + Sections: + - **Context**: Background, current state, constraints, stakeholders + - **Goals / Non-Goals**: What this design achieves and explicitly excludes + - **Decisions**: Key technical choices with rationale (why X over Y?). Include alternatives considered for each decision. + - **Risks / Trade-offs**: Known limitations, things that could go wrong. Format: [Risk] → Mitigation + - **Migration Plan**: Steps to deploy, rollback strategy (if applicable) + - **Parallelism Considerations**: Which components can be built independently? Identify shared interfaces or contracts that must be defined before parallel work begins. Call out any serialization points where one piece must finish before another can start. + - **Open Questions**: Outstanding decisions or unknowns to resolve + + Focus on architecture and approach, not line-by-line implementation. + Reference the proposal for motivation and specs for requirements. + + Good design docs explain the "why" behind technical decisions. + requires: + - proposal + + - id: tasks + generates: tasks.md + description: Implementation checklist with trackable tasks + template: tasks.md + instruction: | + Create the task list that breaks down the implementation work. + + **IMPORTANT: Follow the template below exactly.** The apply phase parses + checkbox format to track progress. Tasks not using `- [ ]` won't be tracked. + + Guidelines: + - Group related tasks under ## numbered headings + - Each task MUST be a checkbox: `- [ ] X.Y Task description` + - Tasks should be small enough to complete in one session + - Order tasks by dependency (what must be done first?) + - Aim for **15-30 tasks** sized for multi-agent distribution + - Each task MUST include a `Files:` annotation listing the primary files it touches + + Example: + ``` + ## 1. Setup + + - [ ] 1.1 Create new module structure + + - [ ] 1.2 Add dependencies to package.json + + + ## 2. Core Implementation + + - [ ] 2.1 Implement data export function + + - [ ] 2.2 Add CSV formatting utilities + + ``` + + Each task should be self-contained - an agent should be able to complete it + without needing to coordinate mid-task with other agents. + + Reference specs for what needs to be built, design for how to build it. + Each task should be verifiable - you know when it's done. + requires: + - specs + - design + + - id: dependencies + generates: dependencies.md + description: Dependency analysis of tasks for parallel execution + template: dependencies.md + instruction: | + Analyze the tasks from tasks.md and produce a dependency analysis document. + + This artifact identifies which tasks depend on others, enabling parallel execution + planning. Use project management dependency analysis concepts. + + **Dependency types** (use standard PM notation): + - **FS (Finish-to-Start)**: Task B cannot start until Task A finishes. Most common. + - **SS (Start-to-Start)**: Task B can start once Task A starts (but not before). + - **FF (Finish-to-Finish)**: Task B cannot finish until Task A finishes. + - **SF (Start-to-Finish)**: Task B cannot finish until Task A starts. Rare. + + **Sections to produce**: + + 1. **Dependency Matrix**: Table of all tasks with their dependency relationships. + Format: `| Task ID | Depends On | Type | Reason |` + + 2. **Critical Path**: The longest chain of dependent tasks that determines the + minimum total duration. List the chain of task IDs. + + 3. **Parallel Execution Waves**: Group tasks into waves that can execute + simultaneously. Wave 1 has no dependencies, Wave 2 depends only on Wave 1, etc. + + 4. **Float / Slack**: For each task NOT on the critical path, note how much + it can be delayed without affecting the overall timeline. + + 5. **Text DAG**: ASCII representation of the dependency graph showing + parallel lanes and dependencies between them. + + Read tasks.md carefully. Look at the `Files:` annotations to detect implicit + dependencies (tasks touching the same files likely have ordering constraints). + Be conservative - if two tasks might conflict, declare a dependency. + requires: + - tasks + + - id: distribution + generates: distribution.md + description: Agent distribution plan for parallel task execution + template: distribution.md + instruction: | + Create the agent distribution plan that assigns tasks to parallel agents. + + **Before starting**: Ask the user how many agents they want to use. + Recommend 3-5 agents based on Claude Code agent teams best practices. + If the user requests 1 agent, suggest using `/opsx:propose` instead for + a simpler single-agent workflow. + + **Validation**: + - Minimum 2 agents (otherwise use single-agent workflow) + - Maximum 5 agents (beyond 5, coordination overhead outweighs parallelism gains) + - Total tasks / agents should yield 5-6 tasks per agent ideally + + **Sections to produce**: + + 1. **Configuration**: Number of agents, total tasks, tasks per agent ratio. + + 2. **Token Cost Warning**: Multi-agent execution scales costs. Each agent + maintains its own context. With N agents, expect roughly N× the token usage + of a single-agent run. State the estimated multiplier clearly. + + 3. **Feasibility Assessment**: Given the dependency analysis, is the requested + agent count feasible? Can tasks be distributed without excessive cross-agent + dependencies? If not, recommend a lower agent count. + + 4. **Agent Assignment Cards**: For each agent, list: + - Agent name/number + - Assigned tasks (by ID) + - File ownership (which files this agent is responsible for) + - Execution order (respecting dependencies) + - Dependencies on other agents' tasks (cross-agent sync points) + + 5. **File Ownership Isolation**: Verify that no two agents write to the same + file. If unavoidable, document the conflict and specify ordering. + + 6. **Cross-Agent Dependencies**: List sync points where one agent must wait + for another. Minimize these - they are the main source of coordination overhead. + + 7. **Claude Code Team Setup**: Provide actionable team setup instructions using + Claude Code's team primitives: + - Team creation via `TeamCreate` with `team_name` (change name) and `description` + - Task list population via `TaskCreate` with `subject`, `description`, `activeForm` + for each task, then `TaskUpdate` with `addBlockedBy` for dependencies and `owner` + for agent pre-assignment + - Teammate spawning via the `Agent` tool with `name` (agent name), `team_name`, + `subagent_type: "general-purpose"`, `isolation: "worktree"`, and a `prompt` + containing assigned tasks, file ownership, and execution order + - Recommend running `/opsx:multiagent-apply` to automate the above steps + + **Key principles**: + - Minimize cross-agent dependencies (prefer independent task clusters) + - Respect file ownership isolation (one agent per file) + - Balance workload across agents (avoid one agent getting all critical-path tasks) + - Always warn about token cost scaling + requires: + - dependencies + +apply: + requires: [distribution] + tracks: tasks.md + instruction: | + This schema supports multi-agent parallel execution via `/opsx:multiagent-apply`. + + **Multi-agent execution (recommended):** + 1. Run `/opsx:multiagent-apply` to orchestrate a Claude Code agent team + 2. It reads distribution.md and automatically: + - Creates a team via `TeamCreate` with the change name + - Populates the shared task list via `TaskCreate` with dependencies + - Spawns teammates via `Agent` with `isolation: "worktree"` per assignment card + - Monitors progress via `TaskList` and shuts down agents when complete + + **Single-agent fallback:** + Read context files, work through pending tasks sequentially, mark complete as you go. + Follow the distribution plan's execution order for the critical path. + Pause if you hit blockers or need clarification. diff --git a/schemas/dispec-driven/templates/dependencies.md b/schemas/dispec-driven/templates/dependencies.md new file mode 100644 index 000000000..8eae63a63 --- /dev/null +++ b/schemas/dispec-driven/templates/dependencies.md @@ -0,0 +1,37 @@ +## Dependency Matrix + +| Task ID | Depends On | Type | Reason | +|---------|-----------|------|--------| +| | | | | + +## Critical Path + + + +## Parallel Execution Waves + +### Wave 1 (no dependencies) + + +### Wave 2 + + +### Wave 3 + + +## Float / Slack + +| Task ID | Float | Notes | +|---------|-------|-------| +| | | | + +## Text DAG + +``` + +``` diff --git a/schemas/dispec-driven/templates/design.md b/schemas/dispec-driven/templates/design.md new file mode 100644 index 000000000..b69796567 --- /dev/null +++ b/schemas/dispec-driven/templates/design.md @@ -0,0 +1,25 @@ +## Context + + + +## Goals / Non-Goals + +**Goals:** + + +**Non-Goals:** + + +## Decisions + + + +## Risks / Trade-offs + + + +## Parallelism Considerations + + diff --git a/schemas/dispec-driven/templates/distribution.md b/schemas/dispec-driven/templates/distribution.md new file mode 100644 index 000000000..b1c9721f4 --- /dev/null +++ b/schemas/dispec-driven/templates/distribution.md @@ -0,0 +1,73 @@ +## Configuration + +- **Agent count**: +- **Total tasks**: +- **Tasks per agent**: + +## Token Cost Warning + +> **Multi-agent execution scales token costs.** Each agent maintains its own context window. +> With agents, expect roughly × the token usage of a single-agent run. +> Estimated cost multiplier: **×** + +## Feasibility Assessment + + + +## Agent Assignments + + + +### Agent N: + +**Tasks:** + + +**File ownership:** + + +**Execution order:** + + +**Cross-agent dependencies:** + + +## File Ownership Isolation + +| File | Owner Agent | Notes | +|------|-------------|-------| +| | | | + +## Cross-Agent Dependencies + +| Waiting Agent | Blocked Task | Depends On | Owning Agent | +|---------------|-------------|------------|--------------| +| | | | | + +## Claude Code Team Setup + +To execute this plan, run `/opsx:multiagent-apply` on this change. It will automate the steps below. + +Alternatively, set up the team manually: + +**1. Create the team** using `TeamCreate`: +- `team_name`: the change name (kebab-case) +- `description`: brief description from the proposal + +**2. Populate the shared task list** using `TaskCreate` for each task: +- `subject`: task description (e.g., "1.1 Create module structure") +- `description`: include the `Files:` annotation and relevant context +- `activeForm`: present-continuous form (e.g., "Creating module structure") + +Then use `TaskUpdate` with `addBlockedBy` to set dependency relationships, and `TaskUpdate` with `owner` to pre-assign tasks per the agent assignments above. + +**3. Spawn teammates** using the `Agent` tool for each agent: +- `name`: agent name from assignments above +- `team_name`: the change name +- `subagent_type`: "general-purpose" +- `isolation`: "worktree" +- `prompt`: include assigned tasks, file ownership, execution order, and cross-agent dependencies + +**4. Monitor and shutdown:** Use `TaskList` to track progress. Send `shutdown_request` via `SendMessage` when all tasks are complete. diff --git a/schemas/dispec-driven/templates/proposal.md b/schemas/dispec-driven/templates/proposal.md new file mode 100644 index 000000000..c79b85d44 --- /dev/null +++ b/schemas/dispec-driven/templates/proposal.md @@ -0,0 +1,23 @@ +## Why + + + +## What Changes + + + +## Capabilities + +### New Capabilities + +- ``: + +### Modified Capabilities + +- ``: + +## Impact + + diff --git a/schemas/dispec-driven/templates/spec.md b/schemas/dispec-driven/templates/spec.md new file mode 100644 index 000000000..095d711c8 --- /dev/null +++ b/schemas/dispec-driven/templates/spec.md @@ -0,0 +1,8 @@ +## ADDED Requirements + +### Requirement: + + +#### Scenario: +- **WHEN** +- **THEN** diff --git a/schemas/dispec-driven/templates/tasks.md b/schemas/dispec-driven/templates/tasks.md new file mode 100644 index 000000000..c2dd8ab8a --- /dev/null +++ b/schemas/dispec-driven/templates/tasks.md @@ -0,0 +1,13 @@ +## 1. + +- [ ] 1.1 + +- [ ] 1.2 + + +## 2. + +- [ ] 2.1 + +- [ ] 2.2 + diff --git a/src/core/init.ts b/src/core/init.ts index 95728dc7e..581f28df1 100644 --- a/src/core/init.ts +++ b/src/core/init.ts @@ -72,6 +72,8 @@ const WORKFLOW_TO_SKILL_DIR: Record = { 'verify': 'openspec-verify-change', 'onboard': 'openspec-onboard', 'propose': 'openspec-propose', + 'multiagent': 'openspec-multiagent', + 'multiagent-apply': 'openspec-multiagent-apply', }; // ----------------------------------------------------------------------------- diff --git a/src/core/profile-sync-drift.ts b/src/core/profile-sync-drift.ts index 782bdcc9f..52df2a26f 100644 --- a/src/core/profile-sync-drift.ts +++ b/src/core/profile-sync-drift.ts @@ -23,6 +23,8 @@ export const WORKFLOW_TO_SKILL_DIR: Record = { 'verify': 'openspec-verify-change', 'onboard': 'openspec-onboard', 'propose': 'openspec-propose', + 'multiagent': 'openspec-multiagent', + 'multiagent-apply': 'openspec-multiagent-apply', }; function toKnownWorkflows(workflows: readonly string[]): WorkflowId[] { diff --git a/src/core/profiles.ts b/src/core/profiles.ts index f61215dfc..c6c2bd3c7 100644 --- a/src/core/profiles.ts +++ b/src/core/profiles.ts @@ -28,6 +28,8 @@ export const ALL_WORKFLOWS = [ 'bulk-archive', 'verify', 'onboard', + 'multiagent', + 'multiagent-apply', ] as const; export type WorkflowId = (typeof ALL_WORKFLOWS)[number]; diff --git a/src/core/shared/skill-generation.ts b/src/core/shared/skill-generation.ts index 898e7a25e..5c51e0cd7 100644 --- a/src/core/shared/skill-generation.ts +++ b/src/core/shared/skill-generation.ts @@ -16,6 +16,8 @@ import { getVerifyChangeSkillTemplate, getOnboardSkillTemplate, getOpsxProposeSkillTemplate, + getOpsxMultiagentSkillTemplate, + getOpsxMultiagentApplySkillTemplate, getOpsxExploreCommandTemplate, getOpsxNewCommandTemplate, getOpsxContinueCommandTemplate, @@ -27,6 +29,8 @@ import { getOpsxVerifyCommandTemplate, getOpsxOnboardCommandTemplate, getOpsxProposeCommandTemplate, + getOpsxMultiagentCommandTemplate, + getOpsxMultiagentApplyCommandTemplate, type SkillTemplate, } from '../templates/skill-templates.js'; import type { CommandContent } from '../command-generation/index.js'; @@ -66,6 +70,8 @@ export function getSkillTemplates(workflowFilter?: readonly string[]): SkillTemp { template: getVerifyChangeSkillTemplate(), dirName: 'openspec-verify-change', workflowId: 'verify' }, { template: getOnboardSkillTemplate(), dirName: 'openspec-onboard', workflowId: 'onboard' }, { template: getOpsxProposeSkillTemplate(), dirName: 'openspec-propose', workflowId: 'propose' }, + { template: getOpsxMultiagentSkillTemplate(), dirName: 'openspec-multiagent', workflowId: 'multiagent' }, + { template: getOpsxMultiagentApplySkillTemplate(), dirName: 'openspec-multiagent-apply', workflowId: 'multiagent-apply' }, ]; if (!workflowFilter) return all; @@ -92,6 +98,8 @@ export function getCommandTemplates(workflowFilter?: readonly string[]): Command { template: getOpsxVerifyCommandTemplate(), id: 'verify' }, { template: getOpsxOnboardCommandTemplate(), id: 'onboard' }, { template: getOpsxProposeCommandTemplate(), id: 'propose' }, + { template: getOpsxMultiagentCommandTemplate(), id: 'multiagent' }, + { template: getOpsxMultiagentApplyCommandTemplate(), id: 'multiagent-apply' }, ]; if (!workflowFilter) return all; diff --git a/src/core/shared/tool-detection.ts b/src/core/shared/tool-detection.ts index 72a0ebc8a..dec0df100 100644 --- a/src/core/shared/tool-detection.ts +++ b/src/core/shared/tool-detection.ts @@ -23,6 +23,8 @@ export const SKILL_NAMES = [ 'openspec-verify-change', 'openspec-onboard', 'openspec-propose', + 'openspec-multiagent', + 'openspec-multiagent-apply', ] as const; export type SkillName = (typeof SKILL_NAMES)[number]; @@ -42,6 +44,8 @@ export const COMMAND_IDS = [ 'verify', 'onboard', 'propose', + 'multiagent', + 'multiagent-apply', ] as const; export type CommandId = (typeof COMMAND_IDS)[number]; diff --git a/src/core/templates/skill-templates.ts b/src/core/templates/skill-templates.ts index ff687d900..375ec2cb8 100644 --- a/src/core/templates/skill-templates.ts +++ b/src/core/templates/skill-templates.ts @@ -17,4 +17,6 @@ export { getBulkArchiveChangeSkillTemplate, getOpsxBulkArchiveCommandTemplate } export { getVerifyChangeSkillTemplate, getOpsxVerifyCommandTemplate } from './workflows/verify-change.js'; export { getOnboardSkillTemplate, getOpsxOnboardCommandTemplate } from './workflows/onboard.js'; export { getOpsxProposeSkillTemplate, getOpsxProposeCommandTemplate } from './workflows/propose.js'; +export { getOpsxMultiagentSkillTemplate, getOpsxMultiagentCommandTemplate } from './workflows/multiagent.js'; +export { getOpsxMultiagentApplySkillTemplate, getOpsxMultiagentApplyCommandTemplate } from './workflows/multiagent-apply.js'; export { getFeedbackSkillTemplate } from './workflows/feedback.js'; diff --git a/src/core/templates/workflows/multiagent-apply.ts b/src/core/templates/workflows/multiagent-apply.ts new file mode 100644 index 000000000..526dcc333 --- /dev/null +++ b/src/core/templates/workflows/multiagent-apply.ts @@ -0,0 +1,162 @@ +/** + * Multiagent Apply Workflow + * + * Skill and command templates for orchestrating a Claude Code agent team + * from a completed dispec-driven distribution plan. + */ +import type { SkillTemplate, CommandTemplate } from '../types.js'; + +const MULTIAGENT_APPLY_INSTRUCTIONS = `Orchestrate a Claude Code agent team to implement a change in parallel using the distribution plan. + +**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes. + +**Steps** + +1. **Select the change** + + If a name is provided, use it. Otherwise: + - Infer from conversation context if the user mentioned a change + - Auto-select if only one active change exists + - If ambiguous, run \`openspec list --json\` to get available changes and use the **AskUserQuestion tool** to let the user select + + Always announce: "Using change: " + +2. **Validate the change uses dispec-driven schema** + \`\`\`bash + openspec status --change "" --json + \`\`\` + Parse the JSON to get \`schemaName\`. If it is NOT \`dispec-driven\`: + - Inform the user that multi-agent apply requires the \`dispec-driven\` schema + - Suggest using \`/opsx:apply\` for single-agent execution instead + - Stop + + If \`distribution\` artifact is not \`done\`: + - Report which artifacts are missing + - Suggest running \`/opsx:multiagent\` to complete the planning phase + - Stop + +3. **Read the distribution plan and context** + + Read the following files from the change directory: + - \`distribution.md\` — agent assignments, file ownership, cross-agent dependencies + - \`dependencies.md\` — task dependency matrix + - \`tasks.md\` — full task list with file annotations + - \`proposal.md\` — change context + - \`design.md\` — technical decisions (if exists) + + Extract from \`distribution.md\`: + - Agent count and names + - Each agent's assigned task IDs, file ownership, execution order + - Cross-agent dependency table + +4. **Display token cost warning and confirm** + + Show the user: + > **Multi-agent execution scales token costs.** With N agents, expect roughly N× the token usage of a single-agent run. + + Use the **AskUserQuestion tool** to confirm: + > "Ready to spawn N agents for change ''? This will use approximately N× token costs." + + Options: "Yes, proceed" / "No, cancel" + + If cancelled, stop. + +5. **Create the team** + + Use \`TeamCreate\` with: + - \`team_name\`: the change name (kebab-case) + - \`description\`: brief description from the proposal + +6. **Populate the shared task list** + + For each task in \`tasks.md\`: + - Use \`TaskCreate\` with: + - \`subject\`: the task description (e.g., "1.1 Create multiagent-apply.ts file") + - \`description\`: include the \`Files:\` annotation content and any relevant context + - \`activeForm\`: present-continuous form (e.g., "Creating multiagent-apply.ts file") + + After all tasks are created, set up dependencies and ownership: + - Use \`TaskUpdate\` with \`addBlockedBy\` to link tasks per the dependency matrix in \`dependencies.md\` + - Use \`TaskUpdate\` with \`owner\` to pre-assign tasks to agents per the assignment cards in \`distribution.md\` + +7. **Spawn teammates** + + For each agent in the distribution plan, use the \`Agent\` tool with: + - \`name\`: agent name from the distribution plan (e.g., "new-skill") + - \`team_name\`: the change name + - \`subagent_type\`: "general-purpose" + - \`isolation\`: "worktree" + - \`prompt\`: include: + - The agent's assigned task IDs and descriptions + - File ownership list + - Execution order + - Cross-agent dependencies (what to wait for) + - The change directory path + - Instructions: "Use \`TaskList\` to find available work. Use \`TaskUpdate\` to mark tasks as \`in_progress\` when starting and \`completed\` when done. Check \`TaskList\` after completing each task for newly unblocked work." + + Spawn agents that have no cross-agent dependencies first (they can start immediately). + Spawn agents with dependencies after — they will find their tasks blocked in the task list and wait. + +8. **Monitor progress** + + After spawning all teammates: + - Teammates will send messages automatically when they complete tasks or need help + - Respond to teammate messages as needed (clarify questions, resolve blockers) + - Periodically check \`TaskList\` to see overall progress + - When a teammate reports all their tasks done, acknowledge + +9. **Shutdown and report** + + When all tasks show as completed in \`TaskList\`: + - Send \`shutdown_request\` via \`SendMessage\` to each teammate + - Wait for shutdown confirmations + - Display final summary: + + \`\`\` + ## Multi-Agent Implementation Complete + + **Change:** + **Schema:** dispec-driven + **Agents:** N agents used + **Tasks:** M/M complete + + ### Agent Summary + - : N tasks completed (branch: ) + - : N tasks completed (branch: ) + ... + + ### Next Steps + - Review and merge agent branches + - Run tests to verify integration + - Archive the change with \`/opsx:archive\` + \`\`\` + +**Guardrails** +- MUST validate schema is \`dispec-driven\` before proceeding +- MUST validate all artifacts are complete (especially \`distribution\`) +- MUST show token cost warning and get user confirmation before spawning agents +- MUST use \`isolation: "worktree"\` for all teammates to prevent file conflicts +- If the distribution plan shows file ownership conflicts (two agents writing same file), warn the user and ask whether to proceed or reassign +- If a teammate reports a blocker, try to help resolve it before escalating to the user +- Do not create the team or spawn agents if the user cancels at the confirmation step`; + +export function getOpsxMultiagentApplySkillTemplate(): SkillTemplate { + return { + name: 'openspec-multiagent-apply', + description: 'Orchestrate a Claude Code agent team to implement a change in parallel using a dispec-driven distribution plan. Use when the user has completed /opsx:multiagent planning and wants to start multi-agent execution.', + instructions: MULTIAGENT_APPLY_INSTRUCTIONS, + license: 'MIT', + compatibility: 'Requires openspec CLI.', + metadata: { author: 'openspec', version: '1.0' }, + }; +} + +export function getOpsxMultiagentApplyCommandTemplate(): CommandTemplate { + return { + name: 'OPSX: Multiagent Apply', + description: 'Orchestrate a Claude Code agent team to implement a change in parallel', + category: 'Workflow', + tags: ['workflow', 'multiagent', 'apply'], + content: MULTIAGENT_APPLY_INSTRUCTIONS, + }; +} diff --git a/src/core/templates/workflows/multiagent.ts b/src/core/templates/workflows/multiagent.ts new file mode 100644 index 000000000..1dc380330 --- /dev/null +++ b/src/core/templates/workflows/multiagent.ts @@ -0,0 +1,145 @@ +/** + * Skill Template Workflow Modules + * + * This file is generated by splitting the legacy monolithic + * templates file into workflow-focused modules. + */ +import type { SkillTemplate, CommandTemplate } from '../types.js'; + +const MULTIAGENT_INSTRUCTIONS = `Plan and distribute a change across multiple AI agents for parallel execution. + +I'll create a change with artifacts: +- proposal.md (what & why) +- specs/ (detailed specifications) +- design.md (how, with parallelism considerations) +- tasks.md (implementation steps with file ownership) +- dependencies.md (task dependency analysis) +- distribution.md (agent assignment plan) + +When ready to implement, run /opsx:multiagent-apply + +--- + +**Input**: The argument after \`/opsx:multiagent\` is the change name (kebab-case), OR a description of what the user wants to build. + +**Steps** + +1. **If no input provided, ask what they want to build** + + Use the **AskUserQuestion tool** (open-ended, no preset options) to ask: + > "What change do you want to work on? Describe what you want to build or fix." + + From their description, derive a kebab-case name (e.g., "add user authentication" → \`add-user-auth\`). + + **IMPORTANT**: Do NOT proceed without understanding what the user wants to build. + +2. **Create the change directory** + \`\`\`bash + openspec new change "" --schema dispec-driven + \`\`\` + This creates a scaffolded change at \`openspec/changes//\` with \`.openspec.yaml\` using the dispec-driven schema. + +3. **Get the artifact build order** + \`\`\`bash + openspec status --change "" --json + \`\`\` + Parse the JSON to get: + - \`applyRequires\`: array of artifact IDs needed before implementation (e.g., \`["distribution"]\`) + - \`artifacts\`: list of all artifacts with their status and dependencies + +4. **Create artifacts in sequence until apply-ready** + + Use **TaskCreate** and **TaskUpdate** tools to track progress through the artifacts. + + Loop through artifacts in dependency order (artifacts with no pending dependencies first): + + a. **For each artifact that is \`ready\` (dependencies satisfied)**: + - Get instructions: + \`\`\`bash + openspec instructions --change "" --json + \`\`\` + - The instructions JSON includes: + - \`context\`: Project background (constraints for you - do NOT include in output) + - \`rules\`: Artifact-specific rules (constraints for you - do NOT include in output) + - \`template\`: The structure to use for your output file + - \`instruction\`: Schema-specific guidance for this artifact type + - \`outputPath\`: Where to write the artifact + - \`dependencies\`: Completed artifacts to read for context + - Read any completed dependency files for context + - Create the artifact file using \`template\` as the structure + - Apply \`context\` and \`rules\` as constraints - but do NOT copy them into the file + - Show brief progress: "Created " + + b. **For the distribution artifact**: Use the **AskUserQuestion tool** to ask: + > "How many agents do you want to distribute tasks across? (Recommended: 3-5)" + + With options: + - "2 agents" - Minimum viable parallelism + - "3 agents (Recommended)" - Good balance of parallelism and coordination + - "4 agents" - More parallelism, moderate coordination + - "5 agents" - Maximum parallelism, higher coordination overhead + + If the user selects fewer than 2, suggest using \`/opsx:propose\` instead for a simpler single-agent workflow. + If the user requests more than 5, warn about coordination overhead and recommend 3-5 agents. + + c. **Continue until all \`applyRequires\` artifacts are complete** + - After creating each artifact, re-run \`openspec status --change "" --json\` + - Check if every artifact ID in \`applyRequires\` has \`status: "done"\` in the artifacts array + - Stop when all \`applyRequires\` artifacts are done + + d. **If an artifact requires user input** (unclear context): + - Use **AskUserQuestion tool** to clarify + - Then continue with creation + +5. **Show final status** + \`\`\`bash + openspec status --change "" + \`\`\` + +**Output** + +After completing all artifacts, summarize: +- Change name and location +- List of artifacts created with brief descriptions +- **Agent assignment summary**: Which tasks go to which agent +- **Token cost warning**: Remind that N agents ≈ N× token usage +- What's ready: "All artifacts created! Ready for multi-agent implementation." +- Prompt: "Run \`/opsx:multiagent-apply\` to orchestrate a Claude Code agent team for parallel implementation." + +**Artifact Creation Guidelines** + +- Follow the \`instruction\` field from \`openspec instructions\` for each artifact type +- The schema defines what each artifact should contain - follow it +- Read dependency artifacts for context before creating new ones +- Use \`template\` as the structure for your output file - fill in its sections +- **IMPORTANT**: \`context\` and \`rules\` are constraints for YOU, not content for the file + - Do NOT copy \`\`, \`\`, \`\` blocks into the artifact + - These guide what you write, but should never appear in the output + +**Guardrails** +- Create ALL artifacts needed for implementation (as defined by schema's \`apply.requires\`) +- Always read dependency artifacts before creating a new one +- If context is critically unclear, ask the user - but prefer making reasonable decisions to keep momentum +- If a change with that name already exists, ask if user wants to continue it or create a new one +- Verify each artifact file exists after writing before proceeding to next`; + +export function getOpsxMultiagentSkillTemplate(): SkillTemplate { + return { + name: 'openspec-multiagent', + description: 'Plan and distribute a change across multiple AI agents for parallel execution. Use when the user wants to break down a complex change into tasks that can be worked on simultaneously by a team of agents.', + instructions: MULTIAGENT_INSTRUCTIONS, + license: 'MIT', + compatibility: 'Requires openspec CLI.', + metadata: { author: 'openspec', version: '1.0' }, + }; +} + +export function getOpsxMultiagentCommandTemplate(): CommandTemplate { + return { + name: 'OPSX: Multiagent', + description: 'Plan and distribute a change across multiple AI agents for parallel execution', + category: 'Workflow', + tags: ['workflow', 'artifacts', 'multiagent', 'experimental'], + content: MULTIAGENT_INSTRUCTIONS, + }; +} diff --git a/test/core/artifact-graph/dispec-driven.test.ts b/test/core/artifact-graph/dispec-driven.test.ts new file mode 100644 index 000000000..e2909b333 --- /dev/null +++ b/test/core/artifact-graph/dispec-driven.test.ts @@ -0,0 +1,158 @@ +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import * as os from 'node:os'; +import { resolveSchema } from '../../../src/core/artifact-graph/resolver.js'; +import { ArtifactGraph } from '../../../src/core/artifact-graph/graph.js'; +import { detectCompleted } from '../../../src/core/artifact-graph/state.js'; +import type { BlockedArtifacts } from '../../../src/core/artifact-graph/types.js'; + +/** + * Normalize BlockedArtifacts for comparison by sorting dependency arrays. + * The order of unmet dependencies is not guaranteed, so we sort for stable assertions. + */ +function normalizeBlocked(blocked: BlockedArtifacts): BlockedArtifacts { + const normalized: BlockedArtifacts = {}; + for (const [key, deps] of Object.entries(blocked)) { + normalized[key] = [...deps].sort(); + } + return normalized; +} + +describe('dispec-driven schema integration', () => { + let tempDir: string; + + beforeEach(() => { + tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'openspec-dispec-test-')); + }); + + afterEach(() => { + if (tempDir && fs.existsSync(tempDir)) { + fs.rmSync(tempDir, { recursive: true, force: true }); + } + }); + + describe('schema structure', () => { + it('should resolve and have 6 artifacts', () => { + const schema = resolveSchema('dispec-driven'); + const graph = ArtifactGraph.fromSchema(schema); + + expect(schema.name).toBe('dispec-driven'); + expect(schema.version).toBe(1); + expect(graph.getAllArtifacts()).toHaveLength(6); + }); + + it('should have valid dependency chain with no cycles', () => { + const schema = resolveSchema('dispec-driven'); + // fromSchema throws on cyclic dependencies + const graph = ArtifactGraph.fromSchema(schema); + expect(graph.getAllArtifacts()).toHaveLength(6); + }); + + it('should have correct build order', () => { + const schema = resolveSchema('dispec-driven'); + const graph = ArtifactGraph.fromSchema(schema); + const buildOrder = graph.getBuildOrder(); + + // proposal must come before specs, design, tasks, dependencies, distribution + const proposalIdx = buildOrder.indexOf('proposal'); + const specsIdx = buildOrder.indexOf('specs'); + const designIdx = buildOrder.indexOf('design'); + const tasksIdx = buildOrder.indexOf('tasks'); + const depsIdx = buildOrder.indexOf('dependencies'); + const distIdx = buildOrder.indexOf('distribution'); + + expect(proposalIdx).toBeLessThan(specsIdx); + expect(proposalIdx).toBeLessThan(designIdx); + expect(specsIdx).toBeLessThan(tasksIdx); + expect(designIdx).toBeLessThan(tasksIdx); + expect(tasksIdx).toBeLessThan(depsIdx); + expect(depsIdx).toBeLessThan(distIdx); + }); + + it('should require distribution for apply phase', () => { + const schema = resolveSchema('dispec-driven'); + expect(schema.apply).toBeDefined(); + expect(schema.apply!.requires).toEqual(['distribution']); + }); + + it('should track tasks.md in apply phase', () => { + const schema = resolveSchema('dispec-driven'); + expect(schema.apply!.tracks).toBe('tasks.md'); + }); + }); + + describe('workflow progression', () => { + it('should progress through complete workflow', () => { + const schema = resolveSchema('dispec-driven'); + const graph = ArtifactGraph.fromSchema(schema); + + // 1. Initial state - nothing complete, only proposal is ready + let completed = detectCompleted(graph, tempDir); + expect(completed.size).toBe(0); + expect(graph.getNextArtifacts(completed)).toEqual(['proposal']); + expect(graph.isComplete(completed)).toBe(false); + expect(normalizeBlocked(graph.getBlocked(completed))).toEqual({ + specs: ['proposal'], + design: ['proposal'], + tasks: ['design', 'specs'], + dependencies: ['tasks'], + distribution: ['dependencies'], + }); + + // 2. Create proposal.md - specs and design become ready + fs.writeFileSync(path.join(tempDir, 'proposal.md'), '# Proposal'); + completed = detectCompleted(graph, tempDir); + expect(completed).toEqual(new Set(['proposal'])); + expect(graph.getNextArtifacts(completed).sort()).toEqual(['design', 'specs']); + + // 3. Create design.md + fs.writeFileSync(path.join(tempDir, 'design.md'), '# Design'); + completed = detectCompleted(graph, tempDir); + expect(completed).toEqual(new Set(['proposal', 'design'])); + expect(graph.getNextArtifacts(completed)).toEqual(['specs']); + + // 4. Create specs + const specsDir = path.join(tempDir, 'specs'); + fs.mkdirSync(specsDir, { recursive: true }); + fs.writeFileSync(path.join(specsDir, 'feature.md'), '# Spec'); + completed = detectCompleted(graph, tempDir); + expect(completed).toEqual(new Set(['proposal', 'design', 'specs'])); + expect(graph.getNextArtifacts(completed)).toEqual(['tasks']); + + // 5. Create tasks.md - dependencies becomes ready + fs.writeFileSync(path.join(tempDir, 'tasks.md'), '# Tasks\n- [ ] 1.1 Do something'); + completed = detectCompleted(graph, tempDir); + expect(completed).toEqual(new Set(['proposal', 'design', 'specs', 'tasks'])); + expect(graph.getNextArtifacts(completed)).toEqual(['dependencies']); + + // 6. Create dependencies.md - distribution becomes ready + fs.writeFileSync(path.join(tempDir, 'dependencies.md'), '# Dependencies'); + completed = detectCompleted(graph, tempDir); + expect(completed).toEqual(new Set(['proposal', 'design', 'specs', 'tasks', 'dependencies'])); + expect(graph.getNextArtifacts(completed)).toEqual(['distribution']); + + // 7. Create distribution.md - workflow complete + fs.writeFileSync(path.join(tempDir, 'distribution.md'), '# Distribution'); + completed = detectCompleted(graph, tempDir); + expect(completed).toEqual(new Set(['proposal', 'design', 'specs', 'tasks', 'dependencies', 'distribution'])); + expect(graph.getNextArtifacts(completed)).toEqual([]); + expect(graph.isComplete(completed)).toBe(true); + expect(graph.getBlocked(completed)).toEqual({}); + }); + }); + + describe('build order consistency', () => { + it('should return consistent build order across multiple calls', () => { + const schema = resolveSchema('dispec-driven'); + const graph = ArtifactGraph.fromSchema(schema); + + const order1 = graph.getBuildOrder(); + const order2 = graph.getBuildOrder(); + const order3 = graph.getBuildOrder(); + + expect(order1).toEqual(order2); + expect(order2).toEqual(order3); + }); + }); +}); diff --git a/test/core/profiles.test.ts b/test/core/profiles.test.ts index 4df8e66c0..89c51b57c 100644 --- a/test/core/profiles.test.ts +++ b/test/core/profiles.test.ts @@ -20,14 +20,15 @@ describe('profiles', () => { }); describe('ALL_WORKFLOWS', () => { - it('should contain all 11 workflows', () => { - expect(ALL_WORKFLOWS).toHaveLength(11); + it('should contain all 13 workflows', () => { + expect(ALL_WORKFLOWS).toHaveLength(13); }); it('should contain expected workflow IDs', () => { const expected = [ 'propose', 'explore', 'new', 'continue', 'apply', 'ff', 'sync', 'archive', 'bulk-archive', 'verify', 'onboard', + 'multiagent', 'multiagent-apply', ]; expect([...ALL_WORKFLOWS]).toEqual(expected); }); diff --git a/test/core/shared/skill-generation.test.ts b/test/core/shared/skill-generation.test.ts index 6c755f51d..ffdf77269 100644 --- a/test/core/shared/skill-generation.test.ts +++ b/test/core/shared/skill-generation.test.ts @@ -8,9 +8,9 @@ import { describe('skill-generation', () => { describe('getSkillTemplates', () => { - it('should return all 11 skill templates', () => { + it('should return all 13 skill templates', () => { const templates = getSkillTemplates(); - expect(templates).toHaveLength(11); + expect(templates).toHaveLength(13); }); it('should have unique directory names', () => { @@ -35,6 +35,8 @@ describe('skill-generation', () => { expect(dirNames).toContain('openspec-verify-change'); expect(dirNames).toContain('openspec-onboard'); expect(dirNames).toContain('openspec-propose'); + expect(dirNames).toContain('openspec-multiagent'); + expect(dirNames).toContain('openspec-multiagent-apply'); }); it('should have valid template structure', () => { @@ -88,9 +90,9 @@ describe('skill-generation', () => { }); describe('getCommandTemplates', () => { - it('should return all 11 command templates', () => { + it('should return all 13 command templates', () => { const templates = getCommandTemplates(); - expect(templates).toHaveLength(11); + expect(templates).toHaveLength(13); }); it('should have unique IDs', () => { @@ -115,6 +117,8 @@ describe('skill-generation', () => { expect(ids).toContain('verify'); expect(ids).toContain('onboard'); expect(ids).toContain('propose'); + expect(ids).toContain('multiagent'); + expect(ids).toContain('multiagent-apply'); }); it('should filter by workflow IDs when provided', () => { @@ -142,9 +146,9 @@ describe('skill-generation', () => { }); describe('getCommandContents', () => { - it('should return all 11 command contents', () => { + it('should return all 13 command contents', () => { const contents = getCommandContents(); - expect(contents).toHaveLength(11); + expect(contents).toHaveLength(13); }); it('should have valid content structure', () => { diff --git a/test/core/shared/tool-detection.test.ts b/test/core/shared/tool-detection.test.ts index 5a66ff3cd..3e09b6b4b 100644 --- a/test/core/shared/tool-detection.test.ts +++ b/test/core/shared/tool-detection.test.ts @@ -28,7 +28,7 @@ describe('tool-detection', () => { describe('SKILL_NAMES', () => { it('should contain all skill names matching COMMAND_IDS', () => { - expect(SKILL_NAMES).toHaveLength(11); + expect(SKILL_NAMES).toHaveLength(13); expect(SKILL_NAMES).toContain('openspec-explore'); expect(SKILL_NAMES).toContain('openspec-new-change'); expect(SKILL_NAMES).toContain('openspec-continue-change'); @@ -40,6 +40,8 @@ describe('tool-detection', () => { expect(SKILL_NAMES).toContain('openspec-verify-change'); expect(SKILL_NAMES).toContain('openspec-onboard'); expect(SKILL_NAMES).toContain('openspec-propose'); + expect(SKILL_NAMES).toContain('openspec-multiagent'); + expect(SKILL_NAMES).toContain('openspec-multiagent-apply'); }); }); diff --git a/test/core/templates/multiagent-apply.test.ts b/test/core/templates/multiagent-apply.test.ts new file mode 100644 index 000000000..9456b6069 --- /dev/null +++ b/test/core/templates/multiagent-apply.test.ts @@ -0,0 +1,95 @@ +import { describe, it, expect } from 'vitest'; +import { + getOpsxMultiagentApplySkillTemplate, + getOpsxMultiagentApplyCommandTemplate, + getOpsxMultiagentSkillTemplate, + getOpsxMultiagentCommandTemplate, +} from '../../../src/core/templates/skill-templates.js'; + +describe('multiagent-apply templates', () => { + describe('getOpsxMultiagentApplySkillTemplate', () => { + it('should return a valid SkillTemplate', () => { + const template = getOpsxMultiagentApplySkillTemplate(); + + expect(template.name).toBe('openspec-multiagent-apply'); + expect(template.description).toBeTruthy(); + expect(template.instructions).toBeTruthy(); + expect(template.license).toBe('MIT'); + }); + + it('should reference TeamCreate in instructions', () => { + const template = getOpsxMultiagentApplySkillTemplate(); + expect(template.instructions).toContain('TeamCreate'); + }); + + it('should reference TaskCreate in instructions', () => { + const template = getOpsxMultiagentApplySkillTemplate(); + expect(template.instructions).toContain('TaskCreate'); + }); + + it('should reference TaskUpdate in instructions', () => { + const template = getOpsxMultiagentApplySkillTemplate(); + expect(template.instructions).toContain('TaskUpdate'); + }); + + it('should reference worktree isolation in instructions', () => { + const template = getOpsxMultiagentApplySkillTemplate(); + expect(template.instructions).toContain('worktree'); + }); + + it('should reference dispec-driven schema validation', () => { + const template = getOpsxMultiagentApplySkillTemplate(); + expect(template.instructions).toContain('dispec-driven'); + }); + + it('should reference shutdown_request', () => { + const template = getOpsxMultiagentApplySkillTemplate(); + expect(template.instructions).toContain('shutdown_request'); + }); + + it('should not reference TodoWrite', () => { + const template = getOpsxMultiagentApplySkillTemplate(); + expect(template.instructions).not.toContain('TodoWrite'); + }); + }); + + describe('getOpsxMultiagentApplyCommandTemplate', () => { + it('should return a valid CommandTemplate', () => { + const template = getOpsxMultiagentApplyCommandTemplate(); + + expect(template.name).toBe('OPSX: Multiagent Apply'); + expect(template.description).toBeTruthy(); + expect(template.category).toBe('Workflow'); + expect(template.tags).toContain('multiagent'); + expect(template.tags).toContain('apply'); + expect(template.content).toBeTruthy(); + }); + + it('should not reference TodoWrite', () => { + const template = getOpsxMultiagentApplyCommandTemplate(); + expect(template.content).not.toContain('TodoWrite'); + }); + }); +}); + +describe('existing multiagent templates do not reference TodoWrite', () => { + it('multiagent skill template should not reference TodoWrite', () => { + const template = getOpsxMultiagentSkillTemplate(); + expect(template.instructions).not.toContain('TodoWrite'); + }); + + it('multiagent command template should not reference TodoWrite', () => { + const template = getOpsxMultiagentCommandTemplate(); + expect(template.content).not.toContain('TodoWrite'); + }); + + it('multiagent skill template should reference TaskCreate', () => { + const template = getOpsxMultiagentSkillTemplate(); + expect(template.instructions).toContain('TaskCreate'); + }); + + it('multiagent skill template should reference /opsx:multiagent-apply', () => { + const template = getOpsxMultiagentSkillTemplate(); + expect(template.instructions).toContain('/opsx:multiagent-apply'); + }); +}); diff --git a/test/core/templates/skill-templates-parity.test.ts b/test/core/templates/skill-templates-parity.test.ts index f8fb1307b..27afbc068 100644 --- a/test/core/templates/skill-templates-parity.test.ts +++ b/test/core/templates/skill-templates-parity.test.ts @@ -21,6 +21,10 @@ import { getOpsxNewCommandTemplate, getOpsxOnboardCommandTemplate, getOpsxSyncCommandTemplate, + getOpsxMultiagentCommandTemplate, + getOpsxMultiagentSkillTemplate, + getOpsxMultiagentApplyCommandTemplate, + getOpsxMultiagentApplySkillTemplate, getOpsxProposeCommandTemplate, getOpsxProposeSkillTemplate, getOpsxVerifyCommandTemplate, @@ -52,6 +56,10 @@ const EXPECTED_FUNCTION_HASHES: Record = { getOpsxVerifyCommandTemplate: '9b4d3ca422553b7534764eb3a009da87a051612c5238e9baab294c7b1233e9a2', getOpsxProposeSkillTemplate: 'd67f937d44650e9c61d2158c865309fbab23cb3f50a3d4868a640a97776e3999', getOpsxProposeCommandTemplate: '41ad59b37eafd7a161bab5c6e41997a37368f9c90b194451295ede5cd42e4d46', + getOpsxMultiagentSkillTemplate: '9f0869a754c0f316c76d0970d886cbf96ebd073c5ba62278a6b6707d3535ccf4', + getOpsxMultiagentCommandTemplate: '353b3dc822145884acd27f33ed692b36f4b9d75349f350bb6ca9cbce01562892', + getOpsxMultiagentApplySkillTemplate: 'b90f8d481511a1c0d69651f2b7116166d044d12acfcc1fd9e336e4636cedfa05', + getOpsxMultiagentApplyCommandTemplate: '046abe3e18f86a807e79fd1f399a9105cae93106d1448b1a989d57623baed4c6', getFeedbackSkillTemplate: 'd7d83c5f7fc2b92fe8f4588a5bf2d9cb315e4c73ec19bcd5ef28270906319a0d', }; @@ -67,6 +75,8 @@ const EXPECTED_GENERATED_SKILL_CONTENT_HASHES: Record = { 'openspec-verify-change': '30d07c6f7051965f624f5964db51844ec17c7dfd05f0da95281fe0ca73616326', 'openspec-onboard': 'dbce376cf895f3fe4f63b4bce66d258c35b7b8884ac746670e5e35fabcefd255', 'openspec-propose': '20e36dabefb90e232bad0667292bd5007ec280f8fc4fc995dbc4282bf45a22e7', + 'openspec-multiagent': '4aeb273e34f9b4ac7385bc636ca4ddbd401b43afd2f9834eda76790617f5aa9c', + 'openspec-multiagent-apply': '55300df760099dca2f7e965d63d9bd75b7e2152b35638f9ac66a4f516d167204', }; function stableStringify(value: unknown): string { @@ -114,6 +124,10 @@ describe('skill templates split parity', () => { getOpsxVerifyCommandTemplate, getOpsxProposeSkillTemplate, getOpsxProposeCommandTemplate, + getOpsxMultiagentSkillTemplate, + getOpsxMultiagentCommandTemplate, + getOpsxMultiagentApplySkillTemplate, + getOpsxMultiagentApplyCommandTemplate, getFeedbackSkillTemplate, }; @@ -139,6 +153,8 @@ describe('skill templates split parity', () => { ['openspec-verify-change', getVerifyChangeSkillTemplate], ['openspec-onboard', getOnboardSkillTemplate], ['openspec-propose', getOpsxProposeSkillTemplate], + ['openspec-multiagent', getOpsxMultiagentSkillTemplate], + ['openspec-multiagent-apply', getOpsxMultiagentApplySkillTemplate], ]; const actualHashes = Object.fromEntries(