--- name: batch-pipeline description: | Parallel SDLC batch runner. Analyzes a list of independent task descriptions, groups them by heuristic file-conflict prediction, dispatches one worktree-isolated `codex exec` process per task (each running the full pipeline-orchestrator: BA → Dev → QA → Sec → Docs), and aggregates results into a single batch summary. Use when: - `$sdlc-batch` invokes this skill after parsing its arguments (Step 2 of the `sdlc-batch` skill) - You have 2+ independent feature/fix descriptions to run through the SDLC pipeline in parallel Do NOT use for: - A single task (use pipeline-orchestrator directly via $sdlc-start) - Tasks that must share a single working tree (this skill isolates each task in its own git worktree) --- # Batch Pipeline You are the SDLC Batch Runner. You take multiple independent task descriptions, isolate each one in its own git worktree, and dispatch one `codex exec` process per task that runs the full `pipeline-orchestrator` skill end-to-end (BA → Dev → QA → Security → Docs) for that single task. You never write or edit project code directly — that happens inside each task's worktree, by the dispatched agent. --- ## Inputs (from the `sdlc-batch` skill, Step 2) - `tasks[]` — parsed task descriptions (each may carry a `[simple]` / `[complex]` complexity hint inline; strip it into `CONTEXT.tasks[i].complexity_hint` if present). - `forced_stack` — `--stack=NAME` override, or `"auto-detect"`. - `dry_run` — boolean. --- ## Conflict detection is heuristic — say so This skill predicts file overlap from **task description text only** (keyword → likely-path heuristics below). It does **not** read a real diff — there is no diff yet, since no worktree has touched any files at the point grouping happens. Treat grouping as a conservative scheduling aid, not a guarantee of conflict-free execution. Post-hoc conflicts (if any) surface as a normal PR merge conflict on the target branch, which is outside this skill's scope. --- ## Branch naming This runner creates each worktree and branch itself (`{SDLC_HOME}/scripts/batch-run.sh`), so batch branches follow the project's convention from `references/GIT-FLOW.md` Step E. The PR base is still derived from the task type and the detected branching model, not from the branch name. Each worktree branches from the current base at dispatch time — a task that depends on another task's merged output must run in a later batch, not the same one. ## Algorithm ### Step 1 — Per-task scope pass For each task in `tasks[]`: 1. Generate `task_slug` the same way `pipeline-orchestrator` Step 2 does: lowercase, alphanumerics + dashes, max 40 chars, derived from the task text. 2. Extract `complexity_hint` from a trailing `[simple]` / `[complex]` tag if present; otherwise `"unspecified"`. 3. Predict a `likely_paths[]` set from keywords in the task text — e.g. an endpoint/route noun → `routes/`, `controllers/`; "test"/"spec" → test dirs; a named file mentioned literally → that path. This is a coarse heuristic, not codebase-verified (no worktree exists yet to `Glob` against). 🚨 **MUST PRINT VERBATIM:** ``` 📋 Batch scope (N tasks): 1. {task_slug} [{complexity_hint}] — likely: {csv of likely_paths, or "unknown"} 2. ... ``` ### Step 2 — Conflict grouping Build groups via the heuristic paths from Step 1: - Two tasks whose `likely_paths[]` intersect → same **sequential chain** (run one after another; the second does not start until the first's PR is **opened**). Note this is scheduling only, not a conflict guarantee: each worktree branches from the current `main`/base branch, so a chained task does not see the previous task's *unmerged* changes. Real overlap is only fully resolved when a human merges each PR before the dependent one is reviewed. State this caveat in the Step 2 print so the user doesn't assume more safety than the heuristic provides. - Tasks with no path overlap with any other task → each its own **parallel group member**. 🚨 **MUST PRINT VERBATIM:** ``` 🧩 Execution plan: Group 1 (parallel): - {task_slug_a} - {task_slug_b} Group 2 (sequential chain, overlap: {csv of shared paths}): - {task_slug_c} → {task_slug_d} ``` If `forced_stack` is set, note it applies to every task uniformly: `stack: {forced_stack} (forced for all tasks)`. ### Step 3 — Budget estimate and confirmation (mandatory) Estimate each task as a full `pipeline-orchestrator` run, in **output tokens, not money**: OpenAI publishes no per-token price for the Codex model family, so this pipeline never reports currency (ADR-11). Baseline for a medium feature: **~45k output tokens** across the five phases, including the `gpt-5.6-sol` planning pass in the development phase. Scale it with `complexity_hint`: `simple ≈ 0.5×`, `unspecified ≈ 1×`, `complex ≈ 2×`. The baseline is an estimate, not a measurement, and it counts subagent phases only — each batch task additionally runs its own orchestrator. Treat the total as a lower bound when deciding whether to proceed. 🚨 **MUST PRINT VERBATIM:** ``` 🧮 Estimated budget: ~{total} output tokens across {N} tasks ({M} groups, {P} run in parallel) Proceed? (yes / adjust / abort) ``` Wait for the user's answer: - `yes` → continue to Step 4 (or stop here if `dry_run == true`, see below). - `adjust` → ask which tasks to drop/change, re-run Step 1–3 on the revised list. - `abort` → print `⛔ Batch aborted by user.` and stop. No worktrees created. **If `dry_run == true`:** after printing the budget estimate, print `🔍 Dry run — stopping before worktree creation.` and stop. Do not ask for confirmation, do not create worktrees, do not dispatch agents. ### Step 4 — Isolation and the non-interactive approval gate **Isolation:** run `{SDLC_HOME}/scripts/batch-run.sh `. Do NOT try to isolate tasks by telling a spawned agent "your cwd is X" — a spawned agent does not inherit a working directory from prose in its prompt, and `pipeline-orchestrator` uses relative paths (`docs/plans/{task_slug}/`) and `git diff {base_branch}...HEAD` internally. Without a real separate checkout every parallel task writes into the same `docs/plans/` and operates on the same git index — collisions guaranteed. `batch-run.sh` creates one `git worktree` per task and runs `codex exec` inside it, so each task gets a real, separate checkout and its own process cwd. It names the branch itself, which is why batch branches follow the project's convention (see *Branch naming* above). Worktrees are left in place after the run for inspection; the script prints the removal command. **Non-interactive approval gate:** `pipeline-orchestrator` Step 3b-special has a mandatory human approval gate on the development-phase plan (approve / request changes / abort). A `codex exec` process has **no user channel** — only this batch-runner's own conversation talks to the human, and it cannot forward a mid-run prompt from inside a dispatched agent. Running batch tasks unattended therefore requires waiving that gate for batch-dispatched runs: instruct each dispatched agent explicitly (in its prompt, Step 5 below) to self-review its own development plan against the BA spec and proceed automatically — do not wait for external approval. This is a deliberate trade-off of unattended batch execution, not an oversight. Disclose it to the user in the Step 3 cost-estimate confirmation: ``` Note: batch-dispatched pipelines auto-approve their own development plan (no human-in-the-loop gate — tasks run unattended). Review each PR's diff before merging. ``` **Hard rule:** if a task's worktree cannot be created (`batch-run.sh` prints `SKIP`), mark it `failed_setup` and exclude it from dispatch — do not block other tasks' groups. If setup fails for **every** task in a group, do not proceed with that group — print the failures and ask the user how to proceed (retry / skip group / abort batch). This mirrors the hard rule in the `sdlc-batch` skill, Step 3. ### Step 5 — Dispatch Write the task list to a file (one task per line), then invoke the runner: ```bash cat > /tmp/sdlc-batch-tasks.txt <<'TASKS' {task 1 description} {task 2 description} TASKS MAX_PARALLEL={group size} bash {SDLC_HOME}/scripts/batch-run.sh {base_branch} /tmp/sdlc-batch-tasks.txt ``` For a **sequential chain**, write one file per link and invoke the runner once per link, waiting for each to return before starting the next — a later task in a chain must branch from the earlier one's merged result, which the runner cannot know about on its own. The runner passes each task to `codex exec` as: ``` Use the sdlc-start skill for this task: {task} ``` so the dispatched process enters the normal pipeline. Additional prompt directives below are appended by the runner via the same string. Each agent's prompt: ``` Task: {original task text, complexity hint stripped} Read and execute the `pipeline-orchestrator` SKILL.md for this single task, exactly as $sdlc-start would. Stack override: {forced_stack or "auto-detect"}. BATCH MODE OVERRIDE: you are running unattended as part of a batch dispatch. There is no user available to answer either of the pipeline's interactive gates, so treat both as auto-resolved: 1. Development-phase approval gate (pipeline-orchestrator Step 3b-special). Skip waiting for approve/request-changes/abort: after Pass 1 (planning), review the plan yourself against the BA spec for obvious gaps or risk, then proceed directly to Pass 2 (implementation) as if it had been approved. Record in your compact summary that auto-approval was used, so the human reviewing the resulting PR knows no one but the agent itself reviewed the plan. 2. Branch gate (Step 0b-git / GIT-FLOW.md Step F). Do NOT ask and do NOT create a branch: you already run in a dedicated worktree on a branch batch-run.sh created, so a second branch would be wrong. Still run detection — the model, task type and especially `pr_base_branch` decide where your PR is targeted — then continue on the current branch with branch_action = "continued". Run the full pipeline to completion (BA → Dev → QA → Security → Docs) and end with an opened PR. Return a COMPACT summary (≤1K tokens): - task_slug - stack detected - PR URL (or failure reason) - total_output_tokens from the pipeline's own telemetry - any phase that failed or was skipped - confirmation that the dev-plan gate was self-approved (batch mode) - branch and PR base branch used ``` 🚨 **MUST PRINT VERBATIM** before dispatching each group: ``` ▶ Dispatching group {G}/{total_groups} — {count} task(s) {in parallel|sequentially} ``` **Hard rule:** if ALL tasks in a group fail, do not auto-continue to the next group — print the failures and ask the user: retry group / skip to next group / abort remaining batch. ### Step 6 — Aggregation and summary Collect each `codex exec` process's final JSONL result plus the `worktree` path and branch printed by `batch-run.sh` into `docs/plans/_batch/{batch_run_id}/summary.json`: ```json { "batch_run_id": "...", "started_at": "", "completed_at": "", "tasks": [ { "task_slug": "...", "group": 1, "worktree": "{path printed by batch-run.sh}", "branch": "{branch printed by batch-run.sh}", "status": "completed" | "failed_setup" | "failed_pipeline", "pr_url": "...", "output_tokens": 0, "dev_plan_self_approved": true } ], "total_output_tokens": 0 } ``` Print the final summary: ``` ✅ Batch completed: {N_completed}/{N_total} tasks ✅ {task_slug} → {pr_url} ({output_tokens} out) — dev plan self-approved, review before merge ✅ {task_slug} → {pr_url} ({output_tokens} out) — dev plan self-approved, review before merge ❌ {task_slug} → failed at {phase} (see docs/plans/{task_slug}/_telemetry.json) Total output tokens: {total_output_tokens} Worktrees (one per task, printed by batch-run.sh): {task_slug}: {worktree_path} (branch {branch}) Remove a worktree with: git worktree remove {worktree_path} (after merging or discarding the PR) ``` Keep every created worktree for inspection. The user reviews or merges the PR, then removes the worktree manually. --- ## Hard rules for the batch runner You **never**: - Edit project source files directly. Dispatched agents handle that inside their worktrees. - Manually run `git worktree add` for isolation — always use `{SDLC_HOME}/scripts/batch-run.sh`; prose instructions about cwd do not actually change a dispatched agent's working directory. - Skip the token-estimate confirmation (Step 3), except when `dry_run == true`. - Skip disclosing the dev-plan auto-approval trade-off in the Step 3 confirmation — it must be visible before the user types `yes`. - Continue to the next group if ALL tasks in the previous group failed — ask the user first. - Proceed with a group if isolation setup failed for every task in it. - Treat the Step 2 grouping as a guarantee — it is a heuristic prediction, not a real diff-based conflict check. You **always**: - Isolate every task via its own `git worktree`, regardless of grouping — that is what `batch-run.sh` does. - Instruct each dispatched agent to self-approve its own development plan (batch mode has no human channel) and to report that it did so. - Pass dispatched agents a COMPACT prompt (task text + stack override + batch-mode override), not the full batch context. - Record telemetry per task, even for failed/aborted tasks. - Print the final summary, even on partial completion.