# 0.5.2 — Review findings spawn surgical follow-up tickets

Previously, an M8 `request-changes` verdict on a 9-ticket feature
marked all 9 tickets ABORTED — the operator was forced to choose
which to retry, and the team would re-run whole tickets even when
Caterpillar's findings only named coherence gaps between them.
That's exactly the wasteful loop the squathero3 validation run
surfaced: 9 iterations to apply 2-3 specific findings.

## What changed

**Request-changes routing pivots from "abort everything" to
"materialize the findings as follow-up tickets."**

When M8 emits a review with `verdict=request-changes` (or
`block`):

1. The iteration's IN_PROGRESS tickets transition to **DONE** —
   they shipped their original scope; findings are gaps in the
   space *between* the tickets, not failures of individual ones.
2. Each ticket-worthy finding becomes a new TicketPayload:
   - `title` ← `finding.title`
   - `description` ← `finding.concern` + `finding.request` +
     location
   - `sources` ← `[parent_feature_slug, review_slug]` so the
     ticket's `**Sources:**` line carries provenance back to the
     review that birthed it
   - `stack_span` ← heuristic from `finding.location` (file path:
     `frontend/...` → frontend, `src/backend/...` → backend,
     mixed/unrecognised → full-stack)
   - `owner` ← derived from stack_span
   - `tier` ← V1 (merge-blockers; high priority)
   - `estimate` ← `tbd — operator should refine` placeholder
   - `acceptance` ← `[finding.request]`
3. New tickets land in **QUEUED** state — the next imp run
   iterates only them. Operator can un-queue from the dashboard
   if Caterpillar over-emitted.

**Severity filter:** only `block` and `change-required` findings
spawn tickets. `suggestion` and `note` findings stay
informational in the review artifact — Caterpillar's lower-
severity observations don't burn a fresh iteration cycle.

**Feature state derives correctly through the loop:**

- Originals all DONE + new tickets QUEUED → feature derives to
  `queued` (any ticket queued wins the rollup).
- Next imp run iterates the follow-ups; when they finish, all
  tickets are DONE → feature derives to `ready_for_review`.
- Operator verifies the feature once the rollup says ready.

## Test plan

Replay the squathero3 case (9 tickets, request-changes verdict
on full-stack workout logging):

1. Open the project dashboard. The 9 original tickets should
   now show `✓ done` badges (not `⚠ aborted`).
2. The follow-up tickets synthesized from Caterpillar's findings
   should appear in the tree with `▶ queued` badges, sourced
   from the review.
3. Each follow-up's `**Stack span:**` should reflect where the
   finding pointed (frontend file → frontend; backend file →
   backend). Tickets that span both stay full-stack.
4. Each follow-up's markdown body should carry a `From review
   <slug> (<severity>):` block citing the concern + request +
   location.
5. Launch implementation — only the follow-ups iterate; the
   originals don't get re-touched. Hatter and the relevant
   Tweedle work just the follow-up scope.
6. When the follow-ups land DONE + Caterpillar accepts the
   second review, feature derives to `ready_for_review`.

## Known follow-ups

- **Severity-filter config knob.** Default is "block +
  change-required spawn tickets." Some operators might want to
  also include suggestions (or only blocks). Filed for follow-up
  — a workflow-YAML option on M8 would expose it.
- **Stack-span heuristic edge cases.** Path matchers
  (`frontend/...` vs `src/backend/...`) work for the common
  fullstack-fastapi-react skeleton but might miss for skeletons
  with different directory layouts. The fallback to full-stack
  is safe (no narrowing harm) but not optimal.
- **Cross-ticket findings** (one finding citing two files on
  opposite sides of the seam) currently land full-stack since
  the heuristic detects both markers. Reasonable default;
  operator can re-label on the dashboard.
