Phase 3 Slice 11a admin endpoints under /admin/api/rules, all
gated through auth.RequireAdminFunc:
GET /admin/api/rules — paginated list with filters
GET /admin/api/rules/{id} — full row
POST /admin/api/rules — create draft
PATCH /admin/api/rules/{id} — update draft only
POST /admin/api/rules/{id}/clone-as-draft
POST /admin/api/rules/{id}/publish
POST /admin/api/rules/{id}/archive
POST /admin/api/rules/{id}/restore
GET /admin/api/rules/{id}/audit — paginated audit log
GET /admin/api/rules/{id}/preview — preview-on-trigger-date
GET /admin/api/rules/export-migrations — SQL blob for the
migration-export flow
Every write endpoint takes a `reason` body field; missing reason →
HTTP 400 (ErrAuditReasonRequired surfaced by the service). The
service writes the reason into paliad.audit_reason in the same tx
as the UPDATE so mig 079's trigger captures it.
writeRuleEditorError maps service-level typed errors to HTTP
statuses (404 for ErrRuleNotFound, 409 for ErrInvalidLifecycleState
+ ErrCyclicSpawn, 400 for ErrAuditReasonRequired + ErrInvalidInput).
dbServices gains a ruleEditor field; Services.RuleEditor in the
public bundle gets wired from main.go via NewRuleEditorService.
Route ordering: export-migrations is registered BEFORE the
{id}-shaped routes so the static path doesn't get captured by the
{id} placeholder. (Go 1.22+'s ServeMux requires the explicit
registration order for shadowing-resolution.)
Frontend (Slice 11b) will hire a new coder to surface the API in
an admin UI. Slice 11a ships the backend in isolation so the editor
can drive the lifecycle via curl / mai instructions today.