# POPPY RUNNER — Autonomous Orchestration Spec
For: Lane B / Code. Status: blueprint (Mike approved 2026-06-27). Feeds Merger v2 / L2.

## The gap (why this exists)
Poppy can be the BRAIN — plan, create/pull/sequence tickets, recommend routing. It has no
HANDS: there is no autonomous executor to hand a ticket to. Today every deploy is a
human-driven session (chat-Claude via Composio->GitHub, or Claude Code). Those cannot be
dispatched by Poppy and trusted to finish. Build the substrate below and Poppy can drain a
locked scope through Code+Design, escalating to a human only on gated steps.

## Architecture
  Poppy (orchestrator/brain)
     -> writes jobs -> pure_mcp_jobs (the QUEUE, state machine)
        -> executor worker(s) CLAIM jobs (the HANDS)
           -> repo-write (GitHub) + deploy (Netlify) + run pre-push gate
              -> verification gate (the QA) -> pass=done / fail=loops back
                 -> human approval cards for deploy/money/PII (the BRAKES)

## Job lifecycle (pure_mcp_jobs — table already exists, formalize states)
queued -> claimed -> in_progress -> in_review -> verified -> done
                                              \-> failed -> (requeue w/ reason)
Columns to ensure: id, ticket_id, lane (A|B), kind, payload(jsonb), state, claimed_by,
attempts, last_error, verify_report(jsonb), created_at, updated_at, approval_required(bool),
approved_by, approved_at. Index on (state, lane).

## Components
### 1. Executor runtime (THE missing piece — build first)
A headless worker that: claims the oldest `queued` job for its lane, sets `claimed`,
executes (edit repo / write rows / deploy), runs the verification gate, sets `verified`+report
or `failed`+last_error. Two viable hosts:
  - v1 (pragmatic, ships now): Claude Code in an automated loop drains the queue. It already
    writes puremls + deploys via Composio (confirmed). Wrap it to poll pure_mcp_jobs.
  - v2 (fully headless): Supabase Edge Function `pure-runner` (or CI job) invoked on a cron /
    queue trigger; uses a service-role GitHub+Netlify token to commit+deploy. No human session.
Routing: lane=B -> backend executor (RPC/Edge/migrations); lane=A -> frontend executor
(HTML/CSS/JS edits). Poppy sets `lane` + `kind` when it enqueues.

### 2. Tool-use loop in the chat Edge Function (pure-mcp / chat.send)
Server-side Claude proxy gains tools: board.list/read (free), deal.read/lead.read (free),
board.add + jobs.create (gated -> emit approval card). reason->act->observe loop, capped
iterations + rate limit. This is what lets Poppy actually CREATE and SEQUENCE work, not just chat.

### 3. Verification gate (what makes it "seamless", not fire-and-forget)
Runs BEFORE a job flips to `done`: (a) pre-push gate ALL PASS; (b) link check on touched pages;
(c) responsive scan (the 112-page audit pattern: viewport meta present, no oversized fixed widths,
no bare repeat(N,1fr)); (d) for backend: RPC smoke test. Any fail -> state=failed, last_error set,
job requeued with the report. Report stored in verify_report jsonb + mirrored to audit_log.

### 4. Approval cards (the brakes)
Any job touching deploy / money / PII / RLS sets approval_required=true. Executor pauses at
in_review; Poppy surfaces a card to the human; on approve -> proceeds, on reject -> failed.
Reads always free. This is the human-gate rule from CLAUDE.md, enforced in the loop.

## Guardrails
Per-role scope (pure-gate), audit_log on every tool call (exists), per-lane rate caps
(pure_rate_limits exists; spend caps OFF per Mike for now). Anon stays read-only — all writes
go through service-role in the executor/Edge, never the browser.

## Build order (tickets queued on the board)
1. t-poppy-runner (L) — executor runtime + formalize pure_mcp_jobs lifecycle. <-- do first
2. chat.send tool-loop + board.add/list + jobs.create (part of L2).
3. verification gate module (reuse pre-push + audit scanner).
4. approval-card protocol end to end.
Once #1 exists, Poppy runs the locked Merger v2: generate tickets -> enqueue jobs ->
Code+Design executors claim+execute+self-verify -> Poppy advances the train.
