# Epic Runner — Composition Primitives Example # # Demonstrates: iterate (sequential), gate (ci_pass), forge template variables # # Execution flow: # # fetch-children ← persona step: fetch parent issue, emit child URLs as JSON # │ # implement-children ← iterate (sequential): runs impl-issue for each child URL # │ # ci-gate ← gate (ci_pass): block until CI is green across all PRs # │ # summarise ← persona step: post a completion comment on the epic kind: WavePipeline metadata: name: ops-implement-epic description: "Implement all child issues from a parent epic, gate on CI, then summarise" category: composition release: true requires: tools: - gh input: source: cli example: "https://github.com/re-cinq/wave/issues/184" schema: type: string description: "Parent epic URL (e.g. https://github.com/owner/repo/issues/N)" steps: # ── Step 1: fetch parent issue, emit child issue URLs ───────────────────── - id: fetch-children persona: "gitea-analyst" workspace: mount: - source: ./ target: /project mode: readonly exec: type: prompt source: | Input: {{ input }} You are given a parent epic URL. Your job is to extract all child issue URLs listed in the epic body. 1. Parse the URL to get repo (owner/repo) and issue number. 2. Fetch the issue: {{ forge.cli_tool }} issue view --repo \ --json number,title,body,url 3. Scan the body for linked child issues. Look for patterns like: - Checkbox lists: `- [ ] #123` or `- [ ] https://github.com/…/issues/123` - "Closes #N" / "Part of #N" references - Bulleted task lists pointing to issue URLs 4. For each child issue URL found, verify it is open: {{ forge.cli_tool }} issue view --repo --json state,url Include only open issues. 5. Output a JSON object with: { "parent_url": "", "repo": "", "child_urls": ["https://…/issues/N", …] } Write this JSON to .wave/output/epic-children.json. output_artifacts: - name: children path: .wave/output/epic-children.json type: json required: true retry: policy: aggressive max_attempts: 2 handover: contract: type: json_schema source: .wave/output/epic-children.json schema_path: .wave/contracts/epic-children.schema.json must_pass: true on_failure: retry # ── Step 2: iterate over child issues, run impl-issue for each ─────────── - id: implement-children dependencies: [fetch-children] pipeline: impl-issue input: "{{ item }}" iterate: over: "{{ fetch-children.output.child_urls }}" mode: sequential max_concurrent: 3 # ── Step 3: gate — wait for CI to pass across all opened PRs ───────────── - id: ci-gate dependencies: [implement-children] gate: type: ci_pass timeout: 2h message: "Waiting for CI to pass on all PRs opened by child-issue implementations" # ── Step 4: post a summary comment on the parent epic ──────────────────── - id: summarise persona: "gitea-commenter" dependencies: [ci-gate] memory: inject_artifacts: - step: fetch-children artifact: children as: epic_children workspace: mount: - source: ./ target: /project mode: readonly exec: type: prompt source: | Input: {{ input }} All child issues have been implemented and CI has passed. Read .wave/artifacts/epic_children to get the parent epic URL and repo. Post a completion comment on the parent epic: {{ forge.cli_tool }} issue comment --repo --body-file /tmp/summary.md The comment should include: - A completion header: "All child issues implemented — CI green" - A checkbox list of every child URL that was processed (mark each done) - A note to reviewers about next steps (merge the PRs in dependency order) Write the comment body to /tmp/summary.md before posting. output_artifacts: - name: epic-summary path: .wave/output/epic-summary.json type: json outcomes: - type: url extract_from: .wave/output/epic-summary.json json_path: .comment_url label: "Epic Summary Comment" retry: policy: aggressive max_attempts: 2