Skip to content

feat: skill dependency management, git worktree workspaces, and preflight validation #76

@nextlevelshit

Description

@nextlevelshit

Problem

Pipeline steps depend on external skills (speckit, BMAD, OpenSpec) and tools (gh, git, node) but Wave has no mechanism to:

  1. Install skills — The .claude/commands/ and .specify/ files in the repo were manually created. There's no declarative, reproducible installation path.
  2. Provision workspaces — Workspaces are empty directories (workspace root: ./ with no mount: creates just mkdir). Skills, commands, scripts, and templates aren't available inside them. Claude Code discovers slash commands only by accident (walking up the directory tree to the project root).
  3. Validate dependencies — If a pipeline requires speckit or gh, Wave doesn't check until the step fails mid-execution — after burning tokens and time.
  4. Isolate git operations — PR fix(pipeline): prevent create-new-feature.sh from switching main repo branch #64 fixed a symptom where concurrent pipeline runs collided because create-new-feature.sh ran git checkout -b on the main repo. The root cause is that workspaces aren't real git checkouts, so all git operations reach back to the shared project root.

The skill_mounts config exists in the manifest schema but is dead code — parsed, validated, never wired into the executor or adapter.

External Skill Sources

These are external open-source projects, not Wave-owned code:

Skill Source Install Init
Spec-Kit github/spec-kit uv tool install specify-cli --from git+https://github.com/github/spec-kit.git specify init <project>
BMAD bmad-code-org/BMAD-METHOD npx bmad-method install (interactive) or npx bmad-method install --tools claude-code --yes included in install
OpenSpec Fission-AI/OpenSpec npm install -g @fission-ai/openspec@latest openspec init

Each tool has its own CLI installer that generates .claude/commands/, .specify/, and related files. Wave should not embed or vendor these — it should delegate installation to their native installers.

Current State (Broken)

wave run speckit-flow "add auth"
  │
  ├─ Workspace: .wave/workspaces/speckit-flow/specify/
  │   ├─ .claude/settings.json  ✅ (created by prepareWorkspace)
  │   ├─ CLAUDE.md              ✅ (created by prepareWorkspace)
  │   ├─ .claude/commands/      ❌ (blocked by skipDirs)
  │   ├─ .specify/              ❌ (no mount, empty workspace)
  │   └─ .git                   ❌ (blocked by skipDirs)
  │
  ├─ Claude Code discovers commands by walking up to project root (accidental)
  ├─ Prompts contain 80 lines of git/workspace plumbing to compensate
  └─ Concurrent runs collide on shared project root (PR #64)

Proposed Design

1. Skill declarations in wave.yaml (optional)

skills:
  speckit:
    install: "uv tool install specify-cli --from git+https://github.com/github/spec-kit.git"
    init: "specify init"
    check: "specify --version"
  bmad:
    install: "npx bmad-method install --tools claude-code --yes"
    check: "ls .claude/commands/bmad.*.md"
  openspec:
    install: "npm install -g @fission-ai/openspec@latest"
    init: "openspec init"
    check: "openspec --version"

Skills are optional — only declare what your project uses. Each skill declares how to install, initialize, and verify itself.

2. Pipeline requires: block

kind: WavePipeline
metadata:
  name: speckit-flow
requires:
  skills: [speckit]
  tools: [git]

steps:
  - id: specify
    persona: implementer
    workspace:
      type: worktree
      branch: "{{ branch }}"
    exec:
      type: slash_command
      command: speckit.specify
      args: "{{ input }}"

Pipelines declare their dependencies. skills: checks for installed command files. tools: checks $PATH.

3. Preflight validation with auto-install

Before any step runs:

$ wave run speckit-flow "add auth"

Preflight:
  ✓ tool 'git' found
  ✗ skill 'speckit' not installed
  → installing speckit...
    running: uv tool install specify-cli --from git+https://github.com/github/spec-kit.git
    running: specify init
  ✓ skill 'speckit' installed

Running pipeline speckit-flow...
  • Tools: check $PATH, fail with actionable error if missing (Wave can't install system tools)
  • Skills: check if commands exist → if missing and install:/init: declared → auto-install → recheck → fail if still missing
  • No install declared: fail with suggestion to add it to wave.yaml or install manually

4. Git worktree workspaces

Instead of empty directories, Wave creates workspaces as git worktrees:

workspace:
  type: worktree       # Wave creates: git worktree add <workspace-path> <branch>
  branch: "{{ branch }}"

This means:

  • Workspace IS a full git checkout on its own branch
  • All project files available (no mount/copy needed for project sources)
  • Git operations work natively (commit, push, branch — local to worktree)
  • Concurrent pipeline runs cannot collide (each worktree is independent)
  • Wave manages worktree lifecycle (create before step, remove after)
  • Eliminates the need for PR fix(pipeline): prevent create-new-feature.sh from switching main repo branch #64's prompt-level workarounds entirely

5. Skill provisioning into workspaces

During workspace setup, prepareWorkspace() copies skill commands into the workspace:

workspace/
  .claude/
    settings.json     ← existing (persona permissions)
    commands/
      speckit.*.md    ← NEW (from installed skills)
  CLAUDE.md           ← existing (persona prompt)
  .specify/           ← available via worktree (project files)
  artifacts/          ← existing (injected from prior steps)

Claude Code discovers commands locally. No walking up the tree. No reaching back to project root.

6. Prompt simplification

Pipeline prompts shrink from 80 lines of plumbing to pure intent:

Run `/speckit.specify` with: {{ input }}

No REPO_ROOT, no cd, no git worktree add, no worktree cleanup. Wave handles all infrastructure.

Target State

wave run speckit-flow "add auth"
  │
  ├─ Preflight
  │   ├─ tool 'git' on PATH? ✓
  │   ├─ skill 'speckit' installed? → auto-install ✓
  │
  ├─ Step: specify
  │   ├─ git worktree add .wave/workspaces/speckit-flow/specify <branch>
  │   ├─ copy .claude/commands/speckit.*.md → workspace/.claude/commands/
  │   ├─ write .claude/settings.json + CLAUDE.md
  │   ├─ launch Claude Code (cwd = worktree)
  │   ├─ prompt: "Run /speckit.specify with: add auth"
  │   ├─ skill runs natively — commands, scripts, templates all local
  │   ├─ git operations safe — isolated worktree, no main repo mutation
  │   └─ cleanup: git worktree remove
  │
  └─ Next step (artifact injection from specify's output)

Implementation Phases

Phase 1: Preflight validation

  • Add requires: field to pipeline schema (skills + tools)
  • Implement preflight check in executor (before workspace creation)
  • Fail-fast with actionable error messages

Phase 2: Skill provisioning

  • Wire up skill_mounts in prepareWorkspace()
  • Copy .claude/commands/ into workspace alongside settings.json
  • Remove .claude from skipDirs for commands subdirectory only

Phase 3: Git worktree workspaces

  • Add workspace.type: worktree option
  • Wave creates/removes worktrees as part of step lifecycle
  • Branch management moves from prompts to Wave infrastructure

Phase 4: Skill installation

  • Add skills: config to wave.yaml manifest schema
  • Implement auto-install during preflight using each skill's native installer
  • install:, init:, and check: commands per skill declaration

Phase 5: Prompt cleanup

  • Replace 80-line inline prompts with slash command references
  • New exec.type: slash_command that just invokes a command with args
  • Remove all REPO_ROOT, cd, worktree boilerplate from prompts

Related Issues

References

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions