# Follow-up Dispatch (PR-comment → new WI)

When a human leaves a PR comment that asks for work that is **legitimate but
outside the scope of the current PR** (a different bug, a related-but-separate
feature, a follow-up cleanup that doesn't belong in the current diff), do **not**
broaden the PR and do **not** silently rebut the comment. Spin off a new Minions
work item via the dashboard API and reply on the originating comment thread with
a link to it.

This carve-out only enables creating a **separate** work item. It does not
loosen the rule that the *current* PR should stay focused on the original
review feedback.

## Decision tree

For each human comment that asks for additional work, classify it before
editing:

1. **Fix in this PR** — the request is in scope (regression of the current
   diff, a flaw in the new code, missing requested behavior the PR was already
   supposed to cover). Fix it on this branch, push, reply on the thread.
2. **Answer with rationale** — the request is invalid, stale, already
   addressed, or would harm the code. Post an evidence-backed rebuttal on the
   thread; do **not** create a follow-up WI for it.
3. **Follow-up WI** — the request is legitimate but clearly out of scope for
   the current PR (different file/system, different bug, distinct feature, a
   refactor the reviewer explicitly asked you to "track separately" or "do in a
   follow-up"). Create a new work item via the API (see below) and reply on the
   originating thread with the new WI id.

If you are unsure between #1 and #3, prefer **#3 (follow-up)** so the current
PR stays small and reviewable. If you are unsure between #2 and #3, prefer #2
and explain your reasoning — don't manufacture work.

## The API call

The Minions dashboard runs on `http://localhost:7331`. Create a follow-up work
item with `POST /api/work-items`:

```bash
curl -sS -X POST http://localhost:7331/api/work-items \
  -H 'Content-Type: application/json' \
  -H 'X-Minions-Agent: <your-agent-id>' \
  -H 'X-Minions-Origin-Wi: <current-wi-id>' \
  -d '{
    "title": "Short imperative summary of the follow-up",
    "type": "implement",
    "priority": "medium",
    "project": "<project-name>",
    "description": "Why this is a follow-up, what should change, acceptance.\n\nOriginated from <reviewer> on PR #<N> (<url>): \"<verbatim quote of the ask>\"",
    "references": [
      {"url": "<originating-pr-url>", "label": "Originated from PR #<N> comment"}
    ],
    "meta": {
      "pr_followup": {
        "parent_pr_url": "<originating-pr-url>",
        "parent_pr_id":  "<canonical PR id, e.g. github:owner/repo#123 or PR-123>",
        "parent_comment_id": "<exact comment/thread id from the host API>",
        "parent_comment_author": "<reviewer login>"
      }
    }
  }'
```

Required fields and their conventions:

- **`title`** — short imperative ("Add foo to bar", "Fix off-by-one in baz").
- **`type`** — pick from the routing types (`implement`, `fix`, `test`,
  `docs`, `ask`, `explore`, …). For new feature/fix work, `implement` is
  almost always correct; use `fix` only when the follow-up is about an existing
  PR's bug. Do **not** set `agent` — let `routing.md` pick.
- **`project`** — name of the same project the originating PR belongs to.
  Required when more than one project is configured.
- **`description`** — explain *why* this is a follow-up and quote the exact
  ask. Future readers should understand the scope without re-reading the PR
  thread.
- **`references`** — at least one entry pointing back at the originating PR.
- **`meta.pr_followup`** — the four-field shape above. Used by the dashboard
  for traceability chips and by the server for dedup (a second request with the
  same `parent_comment_id` returns the existing WI id rather than creating a
  duplicate).

Headers:

- **`X-Minions-Agent`** — your agent id (e.g. `lambert`, `ripley`,
  `temp-mp3dop1v…`). Persisted as `_originAgent` on the new WI.
- **`X-Minions-Origin-Wi`** — the id of the work item you were dispatched
  against (the one whose PR you are responding to). Persisted as `_originWi`.

Both headers are optional but strongly recommended — they let the dashboard
correlate the follow-up to the dispatch that created it.

## Server responses

| Status | Body | Meaning |
|---|---|---|
| 200 | `{ ok: true, id: "W-…" }` | New follow-up WI created. |
| 200 | `{ ok: true, id: "W-…", duplicate: true, duplicateOf: "W-…" }` | The standard title/type/project dedup window matched an in-flight WI. Same WI id is returned; reply on the comment thread with that id. |
| 409 | `{ error: "followup_already_dispatched", existingWiId: "W-…" }` | Another agent (or your previous attempt) already dispatched a follow-up for this exact `parent_comment_id`. Reply on the comment thread linking the existing WI; do not retry. |
| 400 | `{ error: "<message>" }` | Validation failed. Inspect the message: the most common cause is a malformed `meta.pr_followup` shape. Fix and retry. |

Treat 409 as a **success** for your purposes — the follow-up exists, you just
weren't the one who created it. Still post the reply on the comment thread with
the returned `existingWiId`.

## Mandatory post-dispatch step

After the API call returns a WI id (whether `id` from 200 or `existingWiId`
from 409), reply on the originating comment thread with a link back:

> Tracked as follow-up: **W-…** — see the Minions dashboard. Continuing with
> the in-place fix on this PR.

Then **resolve that sub-thread** (GitHub: "Resolve conversation"; ADO: set
status to `closed`) even if other review threads on the PR are still open. The
follow-up WI is the durable artifact; the comment thread should not be left
hanging.

## Record the follow-up in your completion report

Add a `followups` array to your JSON completion report so the engine has an
auditable record of what you fanned out to:

```json
{
  "status": "success",
  "summary": "…",
  "followups": [
    {
      "wi_id": "W-mp7abc…",
      "title": "Add foo to bar",
      "reason": "Out-of-scope ask from reviewer on PR #123 comment #4567890",
      "parent_comment_id": "4567890"
    }
  ]
}
```

The engine logs each entry at `info` and warns if the array references a WI id
that doesn't exist in any project's `work-items.json` (mismatch between what
you claim and what the API actually created — usually a sign that the dispatch
was reverted or the response was misread).

## Do NOT

- Do not pick `agent` for the follow-up; the routing layer assigns one based
  on `type` + `project`.
- Do not set `X-CC-Turn-Id` (that header is for Command Center turns; agents
  don't have a turn id). Use `X-Minions-Agent`/`X-Minions-Origin-Wi` instead.
- Do not skip the comment-thread reply — without it, the human has no signal
  that their ask was tracked and the engine will keep re-routing the comment
  as `pendingFix`.
- Do not spin off follow-ups for nits, style notes, or things you are about
  to fix in this PR anyway. Reserve this for asks that genuinely belong in a
  separate WI.
- Do not omit `meta.pr_followup.parent_comment_id`; without it the server
  cannot dedup and you will create duplicates on every retry.
