---
requiresProjectContext: true
---

# Playbook: QA Session SETUP

You are {{agent_name}}, the {{agent_role}} on the {{project_name}} project.
TEAM ROOT: {{team_root}}

## Your Task

QA Session **SETUP** phase for session **{{session_id}}** (work item {{item_id}}).

A user asked Minions to QA the following target and flows:

- **Session id:** `{{session_id}}`
- **Target kind:** `{{target_kind}}`
- **Target PR id:** `{{target_pr_id}}`
- **Target branch:** `{{target_branch}}`
- **Target commit SHA:** `{{target_sha}}`
- **Target worktree (kind=current):** `{{target_worktree}}`
- **Raw target JSON:** `{{target_json}}`
- **Flows (natural language):** {{flows_raw}}
- **Runner hint (optional explicit runner):** `{{runner_hint}}`
- **Capture:** `{{capture}}`
- **Mode:** `{{session_mode}}`
- **Role (multi-project fan-out):** `{{role}}` *(empty = single-project session, behave as if `primary`)*
- **Primary project (multi-project fan-out):** `{{primary_project}}`
- **Co-services JSON (multi-project fan-out):** `{{co_services_json}}`

{{additional_context}}

## Multi-project fan-out (W-mpq6xqzj000606d0)

When the user picks **multiple projects** from the QA Session form, the engine
queues one SETUP work item **per project** in parallel. Each WI sees the same
target/flows/managed_spawn_name, but with a different `{{role}}` value:

- **`{{role}} == "primary"`** *(or empty for single-project sessions)*: you are
  the orchestrator. You own the canonical `{{managed_spawn_name}}` (no suffix)
  and your work item's wiPath is the one that gets the DRAFT phase queued
  on it once **all** SETUP WIs succeed. If `{{co_services_json}}` is non-empty
  (e.g. `["api","worker"]`), the DRAFT and EXECUTE agents will poll each of
  those co-services' managed-spawn health (name = `{{managed_spawn_name}}-<project>`)
  via `/api/managed-processes/by-name` before treating the system as ready.
- **`{{role}} == "co-service"`**: you are a supporting service (DB, API,
  worker, etc.). Your managed-spawn `name` MUST be `{{managed_spawn_name}}-<your-project>`
  (the engine appends `-<project>` automatically when computing the spec name).
  You do **not** write tests, do not queue DRAFT — just stand up your service,
  declare its healthcheck, and exit clean. The primary's DRAFT/EXECUTE will
  reach you over the network (use a deterministic port; document it in
  `managed-spawn.json` `ports[]`).

Any co-service SETUP that fails (sidecar invalid, healthcheck miss, build
error) **fails the entire session** with `failure_class: 'qa-session-setup-failed'`
and a per-project error JSON in `session.error`. The primary's WI does NOT
get the DRAFT phase queued in that case.

For single-project sessions (`{{co_services_json}}` empty or `[]`), ignore
this section — the original single-WI flow applies unchanged.

## What "qa-session-setup" means

A `qa-session-setup` task is the **first** of three chained work items the
engine dispatches for each QA Session (SETUP → DRAFT → EXECUTE). Your job is
to make the target runnable so the DRAFT and EXECUTE agents can drive a real
live instance:

1. **Resolve the target** (`{{target_kind}}`) into a checked-out worktree.
   - `pr`     → check out the PR's head branch (`{{target_pr_id}}`).
   - `branch` → check out branch `{{target_branch}}`.
   - `commit` → detach at `{{target_sha}}`.
   - `current` → reuse the existing worktree at `{{target_worktree}}` (or
     `MINIONS_AGENT_CWD` if `{{target_worktree}}` is empty).
2. **Inspect the codebase** to find a single "dev-up" command. Look in this
   order: `package.json` `scripts.dev|start|serve`, top-level `Procfile`,
   project README "Run locally / Getting Started" section, a `Makefile` `dev`
   target, or a docker-compose service that exposes an HTTP port. Pick the
   smallest command that brings the app up and binds to a TCP port.
3. **Write the managed-spawn sidecar** to
   `agents/{{agent_id}}/managed-spawn.json` (relative to the Minions root)
   with **exactly one** spec named **`{{managed_spawn_name}}`**. Use the JSON
   shape the `managed_spawn` section below documents. The engine ingests this
   sidecar on your exit and gates the next phase on the first healthcheck.

## Hard requirements on the sidecar

The engine validates the sidecar through `evaluateManagedSpawnAcceptance`.
Anything that fails validation flips your dispatch to FAILED with
`failure_class: 'invalid-managed-spawn'` and the QA Session transitions to
`failed` automatically. Specifically:

- `specs[0].name` MUST equal `{{managed_spawn_name}}` (exact match). The
  engine joins the spawn back to its owning session by this convention.
- `specs[0].healthcheck` MUST be present and verifiable. Prefer HTTP
  (`type: 'http'`) with a real URL and `expect_status` set; fall back to
  `type: 'command'` only when the app has no HTTP surface (e.g. a CLI worker).
- `specs[0].cmd` MUST be on the engine's allowlist (`node`, `bun`, `npm`,
  `npx`, `pnpm`, `yarn`, `python`, `docker`, `adb`, `gradle`, `gradlew`,
  `mvn`, `pwsh`, `powershell`, `bash`, `sh`, `curl`, `git`, …). If the
  project requires a non-allowlisted binary, **stop and report a setup
  failure** (see below) — do NOT try to work around it.
- `specs[0].cwd` MUST be an absolute path inside the resolved worktree.
- Pick a free port and put it in both `ports[]` and the healthcheck URL.

The `managed_spawn` block injected later in this prompt has the full schema
and the executable allowlist enumerated. Read it before writing the sidecar.

## No PR, no commit, no test code yet

SETUP only resolves the target and writes the managed-spawn spec. **Do not**:

- write any test code — that belongs to the DRAFT phase
- commit, push, or open a PR — sessions are tracked via the session record,
  not a merged PR
- modify project source — the dev-up command should run the project as-is
- start the app yourself (`bun run dev` in a detached process) — the engine
  spawns the spec for you after you exit

If the project genuinely will not run without a code change (missing
dependency wiring, hard-coded prod URL, etc.), stop and report a setup
failure so the human can decide whether to patch it themselves.

## Failure path (REQUIRED)

If you cannot resolve the target, cannot find a dev-up command, hit a
non-allowlisted binary, or otherwise produce a sidecar that
`evaluateManagedSpawnAcceptance` would reject, **do not write a malformed
sidecar**. Instead, write your completion report with:

```json
{
  "status": "failed",
  "summary": "<one-line human-readable explanation of what blocked SETUP>",
  "failure_class": "qa-session-setup-failed",
  "retryable": false,
  "needs_rerun": false,
  "nonce": "<value of MINIONS_COMPLETION_NONCE env var>",
  "artifacts": []
}
```

The `engine/qa-sessions.js#handleSetupComplete` hook reads `failure_class`
and the summary, transitions the session to `failed`, and surfaces the
explanation in the dashboard session card so the human knows exactly why
SETUP gave up.

Examples of legitimate failure summaries:

- `"Project has no detectable dev-up command — no package.json scripts.dev, Procfile, or Makefile dev target."`
- `"Required binary 'cargo' is not on the engine's managed-spawn allowlist."`
- `"Target PR #1234 head branch could not be checked out: fatal: reference is not a tree."`
- `"Detected dev-up command but it requires a database connection string we have no way to provide in CI."`

## Working directory

```bash
# PowerShell
echo $env:MINIONS_AGENT_CWD
pwd

# bash/zsh
echo "$MINIONS_AGENT_CWD"
pwd
```

`MINIONS_AGENT_CWD` is the engine-resolved worktree root. Prefer it over
`pwd` for any cwd-sensitive command.

## Findings

Write findings to `{{team_root}}/notes/inbox/{{agent_id}}-{{item_id}}-{{date}}.md`
only after successful completion. Include:

- Session id + target summary
- Dev-up command chosen and where it was discovered (file:line)
- Managed-spawn name, healthcheck shape, port
- Notes for future setup runs on the same target (flaky startup, env-vars
  needed, port collisions)

## Constraints

- Do not modify production code unless explicitly asked.
- Do not remove worktrees; the engine handles cleanup automatically.
- The sidecar is the deliverable — without it, the session is stuck in
  `spawning` until the SETUP WI times out.
