Operant Studio
OPER-15A

Agent-completion callback: delete branches + emit task-lifecycle events on PR merge

Body

## Problem

OPER-11A creates agent branches at claim-time but nothing ever deletes them. Long-term, created branches accumulate unbounded (~10–20/day at current cadence). The subagent that shipped OPER-11A explicitly flagged the missing completion callback as the root long-term gap.

## Scope

Add a `POST /api/agents/completion` (or GitHub webhook receiver) that fires when an agent-dispatched PR is merged/closed. On receipt:

1. Look up the `Task` by branch name (or PR-linked task id)
2. If PR merged → promote Task to DONE and mark PR as shipped
3. If PR closed without merge → revert Task to PLANNING_VALIDATED with gitBranchName=NULL
4. Delete the source branch in the target repo (best-effort; ignore 422 already-gone)
5. Emit DispatchAudit row with outcome=`branch_deleted` (or `branch_delete_failed`)
6. Emit unified audit-event so it appears in the OPER-8A dashboard

## Acceptance

- New GitHub webhook (pull_request.closed) registered on Portico + operant-studio
- On merged PR: task → DONE, branch deleted, audit row present
- On closed-without-merge PR: task → PLANNING_VALIDATED, branch deleted, audit row present
- Idempotent (replaying the webhook produces no duplicate state changes)
- Handles the race between OPER-11A branch-creation and immediate PR-close (defer branch-delete until PR is truly closed)

## Related

- OPER-11A (shipped) — creates branches; this closes the loop
- OPER-14A — worker-reaper is the timeout fallback; this callback is the happy-path
- OPER-8A dashboard — completion events feed metrics

## Priority note

Medium-term this becomes SEV-2 as branch accumulation grows. Not blocking dispatches today but IS the true root-cause fix for the deadlock class.

Attachments

Loading attachments…

Comments

Loading comments…