---
id: w2/m10/t003
title: Rollback verb — a new deploy applying the recorded target
worker: worker2
status: todo
estimate: 30m
depends_on: [w2/m10/t001]
---

## Objective

`deploys.Service.Rollback(ctx, serviceID, deployID)`: authorize, load the named deploy's rollback target (t001), and create a **new** deploy that applies it — Render models rollback as a fresh deploy an agent polls to `live`, never as history rewrite. The apply rides the existing single-writer path (store row → projector) exactly like a normal deploy; the new record's metadata marks it a rollback of `dep-…` (provenance readable in the list).

## Context

- Rollback target semantics: restore *what ran* — the recorded image reference bypasses the build pipeline entirely (no rebuild; the digest is the point).
- Refusals: target-less rows (pre-migration), rollback to the currently-live deploy (no-op vs refuse — check Render's behavior, else pick and document), unknown deploy id → 404.
- Suspend interaction: rolling back a suspended service updates intent but stays at 0 pods (the m12 replica contract).

## Steps

1. Verify Render's rollback request/response shape from the OpenAPI spec.
2. Implement: load target → create deploy record (rollback provenance) → apply spec through the store write path → return the new deploy object.
3. Wire the refusal cases with typed errors.

## Files

- `lego/backend/internal/deploys/service.go`
- `docs/ADR006-bex-api.md` (deploys section)

## Acceptance criteria

- [ ] Rollback returns a new pollable deploy that converges to `live` serving the recorded image, with provenance linking the source deploy.
- [ ] Target-less and unknown-id cases refuse with distinct, named errors.

## Out of scope

- Surfaces (t004); rolling back env-vars/config beyond the recorded target set (documented in t001).
