Deployment Actions
The Deployments surface is AuditTrail's audited control plane for
operational changes proposed against your live agent fleet. Every
action is recorded as an immutable DeploymentAction row with a
deterministic state machine, approval TTL, and — for the highest-tier
irreversible actions — typed confirmation + second-approver
requirements.
Visit /deployments after demo-login to see the queue in action.
The 3 tiers
| Tier | Action types | Execution rule |
|---|---|---|
| Tier 1 | alert · tag_trace · create_incident | Autonomous-safe. In mode="autonomous", auto-executes on creation. |
| Tier 2 | throttle · switch_model · disable_flag | Supervised only. Requires explicit approval before execution, even in autonomous mode. |
| Tier 3 | kill_run · rollback_deploy · scale_down | Explicit-enable only. Requires typed confirmation (first 8 chars of target ref) + operator reason. |
Modes
dry_run— proposal surfaces in the queue, never executessupervised— execute after explicit approval within the TTLautonomous— Tier 1 executes immediately on creation; Tier 2/3 still require supervised approval regardless of mode
State machine
propose
│
▼
proposed ──── reject ──► rejected
│
├─ approve ───► approved ──── reject ──► rejected
│ │
│ └─ mark-executed ──► executed
│
├─ TTL exceeded ──► expired
│
└─ propose w/ mode=autonomous + Tier 1 ──► executed
proposed → approved → executedis the standard supervised flow- Tier 1 in
autonomousmode: single-stepproposed → executed - TTL enforcement: approval attempts after the TTL window flip the row
to
expiredand 400 the request
The /deployments page
When you open /deployments, the page renders:
- A KPI strip summarizing Proposed · Pending approval · Executed today · Rejected today counts
- A filter tab bar (All · Proposed · Approved · Executed · Rejected · Expired)
- A table listing every deployment action visible to your user, sorted by proposed-at descending, with columns: Tier · Type · Target · Reason · Mode · Status · Proposed at · Actions
- Clicking a row opens a side drawer with the full payload, outcome JSON, approver note, rejection note, audit timestamps, and — when the proposal targeted a trace/span — a "Why this action" evidence panel (see below)
- Approve / Reject / Mark-executed buttons on each row (visible only for valid state transitions)
Attribution evidence ("Why this action")
Since v3.30.0, a proposal that targets a trace or span carries attribution evidence computed at propose time — the same no-LLM surrogate SHAP attribution that powers the Sankey view, attached directly to the action row so the approval drawer shows why, not just a reason string.
How it works:
- When an action is proposed (via the API, the assistant, or a WS
command) with a
target_refthat resolves to one of your traces or spans, AuditTrail picks the decision-relevant tool span — the explicitly-targeted span if it is a tool span, otherwise the most recent tool span carrying an amber/red constitutional evaluation (the offending span), otherwise the trace's last tool span. - It resolves the user prompt for that span (walking up to the root
ancestor when the tool span only carries tool arguments), extracts
the surrogate feature vector, and runs
surrogate.explain— real SHAP values when the startup-trained model exists. - The result is stored on the action's
evidencefield: a deterministic English explanation, the top feature weights, the surrogate F1, the violated rule (if one triggered the concern), and the span/trace ids for a one-click jump into the trace's XAI tabs.
Honesty notes:
- Provenance is explicit.
source: "surrogate"appears only when the model is really trained (≥ 5 tool spans at startup); before that the evidence is labelledheuristicand the explanation says so. - No target, no evidence. Actions against deploy/flag refs (or with
no target at all) have
evidence: null, and the drawer states that no span-level attribution exists — nothing is fabricated. - Evidence never blocks a propose. It is computed best-effort; a failure logs a warning and the proposal proceeds without it.
Typed confirmation (Tier 3)
Tier 3 actions (kill_run, rollback_deploy, scale_down) require
the operator to type the first 8 characters of the target reference
before the Approve button activates. This is the same pattern used by
the right-click NodeActionMenu in the DAG viewer — implemented as a
single reusable <ConfirmTypedDialog> component.
Endpoints
All endpoints are user-scoped (JWT or sk-at-* API key).
| Method | Path | Purpose |
|---|---|---|
GET | /api/v1/deployments/actions?status=&limit= | List actions, ordered by proposed_at desc |
POST | /api/v1/deployments/actions | Propose a new action |
POST | /api/v1/deployments/actions/{id}/approve | Approve a proposed action |
POST | /api/v1/deployments/actions/{id}/reject | Reject a proposed or approved action |
POST | /api/v1/deployments/actions/{id}/mark-executed | External runner callback |
See api-reference for full schemas.
Demo data
On demo-login, the seed function _seed_demo_deployment_actions
populates 10 rows spanning every (tier × status) combo so the queue
renders populated immediately. Each row has a realistic reason
string describing the kind of operational concern that would trigger
the action (cost spikes, latency degradation, canary drift, etc.).
Proposed rows that can target a real seeded trace also carry
attribution evidence, computed through the same builder real proposals
use; the Tier-3 rollback proposal targets a deploy ref and honestly
shows the no-evidence state.
Rate limits
State-transition routes (propose / approve / reject / mark-executed) are bounded at 30/minute per client. List reads are unrestricted.
Shipped in: prod-v2.6.4 — backend was in place since V1.3
(2026-Q1); the frontend queue page landed in this sprint.