Confirmations and human gates guide
How ROST routes authority-changing work through human confirmation, and why agents never approve their own requests.
ROST separates work an agent may do from decisions only a human may make. Durable, authority-changing, or sensitive commands carry a confirmation level so the system can stop and ask a person.
Confirmation levels
none: no confirmation gate — the command never returns a pending confirmation. Most are reads or reversible drafts an agent can run directly, including over MCP (graph reads,charter.draft,status.record,sync.brief.compile,sync.run.start,task.complete). A fewnonecommands still require a human actor or an interactive channel —confirmation.approveisnone(approving a gate cannot itself be gated) yet runs only as a human in the UI or CLI — sononemeans "no confirmation gate", not always "agent-callable".human_required: a human must approve. Structural, staffing, resolution, tenant profile, and go-live commands (seat.reparent,charter.approve,goal.reparent,goal.reassign_owner,friction.resolve,agent.go_live,mcp_token.create,tenant.rename).credential_flow: routes through the vault-backed credential path so a secret is captured as a vault reference, never stored or logged in the clear (credential.ingress,tenant.anthropic_key.save, andintegration.connect_rest;agent.configure_toolsiscredential_flowtoo — it stages credential-ingress requests through the same path without ever taking raw secret material).dangerous: the highest-risk human gate. Only three commands carry it —settings.update,agent.decommission, andtenant.model_gateway_policy.update(unlocking managed models without a zero-retention tier — a reviewed act even undertrusted_operator).
The dangerous confirmation level is rare and is not the same as the risk badge a pending confirmation can display. The badge shows "dangerous" when the command's confirmation level is dangerous, when it redacts secrets (its audit redaction is secret_strict), or when that command's input-aware risk resolver classifies this particular proposal as dangerous. For example, onboarding.setup remains human_required, but a setup plan that supplies managed_inference_hard_cap_usd receives the dangerous risk badge and always stays in interactive human review; omitting that field keeps the ordinary risk. Likewise, a credential_flow command such as credential.ingress shows the dangerous badge while still gating through the credential path — neither case changes the command's confirmation level. Read the badge as "handle with care" and the confirmation level as "who must approve". confirmation.approve and confirmation.reject are themselves none — approving a gate cannot itself require approval.
Agent-led onboarding: which pauses are reachable
The bounded implementation-bootstrap principal behind an agent-led onboarding run (see the CLI/MCP guide's "Bounded bootstrap surface") pauses by a rule: it pauses once for every command the bootstrap policy classifies `stage`, because every command in that class is human_required and returns a pending confirmation instead of mutating. Its read-only commands are none and never pause; commands outside the classified surface do not create a bootstrap pause, and any corresponding human decision remains on the owner-controlled surface.
The stage class tells you which kinds of pause a run can raise; the ones below are the kinds whose count depends on how your agent works. It does not bound how many:
compass.set/compass.approve_version— activating the Compass.compass.settakes anapproveflag defaulting tofalse, so an agent that drafts first and activates after raises two confirmations here, while one that passesapprove: trueraises one.agent.create_custom/agent.create_from_template— creating an agent. Both arehuman_required, so this repeats once per agent proposed, and a run that proposes none never raises it.onboarding.finish— closing out the run.agent.run_now— dispatching a manual production run on an agent the same implementation setup created, to prove it works end to end. It is the one pause in this list that acts on an agent that is already live rather than creating something, and your agent only ever asks: it cannot approve its own request, and the server refuses a request for any agent outside that setup's own receipt — at staging and again when the approved run dispatches. This repeats once per run requested, and a run that asks for none never raises it.
Two properties of this surface are commonly assumed and are not true. Cardinality is an input property, not a registry one — the counts above depend on the arguments the agent passes, and any stage command can simply be invoked more than once, so no fixed total is correct. Order is not enforced — neither agent-creation command requires an active Compass, and onboarding.finish does not re-check its requirement set when it executes, so no server-side sequence backs a numbered timeline.
One more asymmetry worth knowing: the lifecycle commands implementation_access.complete and implementation_access.abandon are confirmation: "none". They close the run and revoke its bearer without an owner approval. They are durable, but they act on the bootstrap run itself rather than the owner's workspace — so the guarantee to state is that durable workspace changes are staged, never that everything durable is.
Charter drafting, approval, and other mutation commands (charter.draft, charter.draft_all, charter.set, charter.approve, charter.sign_manifest, charter.update_draft) are denied to this principal, so Charter drafting and approval are unreachable through this flow regardless of what the agent asks for — but Charter reads (charter.list, charter.get) are allowed as read-only projections, so the agent can inspect existing Charters without ever being able to change one. The web onboarding shell's entry screen and run view state this same rule: they name the pause kinds and each kind's own repetition, they never promise a Charter-approval pause the bootstrap allowlist denies, and they assert no pause total and no sequence.
What a gated command returns over MCP
A gated command does not mutate when an agent calls it. It returns a pending confirmation with the command id, a risk level, and an approveVia block containing a web URL and the exact rost command confirmation.approve --json ... line. The human approves through the web link or the CLI; the agent surfaces the link and stops. A logged-in human at an interactive CLI can run their own gated rost command <id> without copy-pasting the approval command: the CLI first renders the complete gate, then asks Approve <command-id> (confirmation <id>)? [y/N], and calls confirmation.approve with reviewed: true only after an explicit y or yes. TTY presence alone is never approval; decline, silence, or a missing prompt callback leaves the confirmation pending.
When an agent triggers a gate, ROST also notifies the seat's steward (Slack with an email fallback) so the held action does not sit silent — a human is told a decision is waiting and given the approve link. A human pausing their own gated command does not self-notify. See the notifications guide.
Approval replays are validated again before execution. If the stored confirmation input no longer matches the original command schema, the approval fails without consuming the pending confirmation or running the durable command; reject it and re-issue the gated action from the current product surface.
For complete-render parity, the server-side confirmation.approve check currently covers only the dedicated complete-review contracts for seat.create_complete and onboarding.activate; each projection must match what the approval card renders before replay can consume it. onboarding.setup remains unsupported/HOLD for this parity check: its generic card is a partial projection, so the server does not silently treat a guessed partial view as a complete approval.
A confirmation whose original command has since been retired is handled differently, because no retry could ever succeed — the command no longer exists to replay. Approving it does not run anything and does not error: it resolves the confirmation to a terminal state, consumes the card so nobody can act on it again, and writes an audit event recording that the command was retired. The approve output carries expired: true, executed: false, and a reason naming the retired command id, so the caller can tell "the confirmation was closed out" apart from "your command ran". Re-minting such a card is refused for the same reason (there is nothing to re-stage). How you close out the refused card depends on whether it is still live: a live one can be rejected, but a card that reached re-mint is by definition already expired, and an expired card can only be dismissed — rejecting one is refused as expired. The re-mint refusal now says so and names confirmation.dismiss directly. Then re-issue the action through the current command.
A command this build simply does not recognize is not treated as retired. That also happens temporarily while a deployment is rolling out, when one instance has not yet loaded a command another instance already staged a confirmation for. Approving or re-minting in that window reports a transient conflict and tells you to retry shortly; the card is left pending and nothing is consumed, so the same approval succeeds once the deploy settles. Only a deliberately retired command reaches the permanent, terminal outcome above — the distinction matters because the terminal outcome cannot be undone.
Resolving a confirmation requires authority over the scope it was raised for, checked against your current access at the moment you act rather than whoever originally requested it. Which rule applies depends on what you're doing with the card, and it is not one rule for everything.
Deciding a card — approving it, rejecting it, or re-minting an expired one — takes: a company owner or an admin, for any card raised for a seat; only the person currently holding that seat, for anyone else (the stored-target command family described below is the one exception to "that seat": approve, reject, and re-mint all decide it against the CURRENT target's steward chain instead, not the card's original seat); and for the small set of company-wide administrative cards, the owner alone.
Clearing a card — dismissing an expired one — and seeing cards in /approvals or confirmation.list follow a narrower rule: the owner, or the person currently holding the seat the card was raised for. An admin who holds no seat neither sees nor dismisses that seat's cards — admin authority reaches deciding, not clearing or listing.
One deliberate bridge: a live conversational gate is surfaced to any owner or admin who may decide it, so an admin is never shown a gate they can see but cannot act on.
Put plainly, an admin can approve or reject a seat's pending card even when that same card never shows up in their own /approvals list — that asymmetry is intentional, not a bug, so "a card you cannot see is a card you cannot close out" does not hold for every action. An attempt without the right authority for what you're trying to do is refused and changes nothing: the card stays pending and no decision or audit row is written.
A small family of commands — agent.activation_sign, agent.trust.create, agent.trust.expire, agent.trust.supersede, agent.trust.revoke, and agent_grant_intervention.decide — carry a stored-target exception to the rules above, because the seat, agent, or grant the card is actually about can move after the card is minted. For these commands, authority is resolved against the CURRENT steward chain of the command's stored target (the target agent's occupying seat and its stewards, or the intervention's seat) or an owner/admin, not against the confirmation's own originating scope. That exception reaches all four surfaces, but not identically: live visibility in /approvals and confirmation.list shows one of these cards only to the current target steward chain or an owner/admin — occupying the card's original seat is not enough; expired visibility in the re-mint queue follows the same current-target-only rule; re-minting an expired card is decided the same way, so confirmation.remint re-authorizes against the live stored target rather than the card's original scope; dismissal is the one surface where the exception is additive rather than exclusive — a human may dismiss an expired card of this kind through EITHER the current stored-target authority OR the confirmation's original scope, so a target moving away never revokes an already-valid original-scope dismissal. gate.decide is not part of this family and keeps the separate owner-or-admin rule described above.
A held tool call actuates the same way regardless of which command backs it — a connector write (like gmail.send) and a durable command (like task.create) both replay through the same governed command executor on approval: re-checked against the live permission manifest, reserved so a second approval or replay can never double-actuate, and audited with a fresh tool_calls row. Resolving the escalation that raised the hold — over the CLI, MCP, or /approvals — actuates identically; none of those paths silently drops the held action once a human approves it.
# Human approves a pending confirmation
rost command confirmation.approve --json '{"confirmation_id":"<confirmation-id>"}'
# Or rejects it
rost command confirmation.reject --json '{"confirmation_id":"<confirmation-id>"}'The approvals queue
The /approvals page is one place a human clears every pending decision across the company. It unions still-pending confirmations, open steward escalations, and agent-proposed draft tasks into a single queue, each item shown with its risk or proposal type, the source or proposing seat, and redacted arguments — secret material is never displayed. You see only items for seats you have authority over: an owner sees all of them; anyone else — including an admin — sees confirmations for the seats they occupy. The one exception is the stored-target command family described above: those cards never surface through seat occupancy at all — they appear only when the viewer currently stewards or administers the card's live target, even for a seat they don't occupy. The queue also shows escalations routed to the viewer's steward chain, and task proposals where they occupy or steward the target seat. An admin can still decide (approve or reject) a seat's confirmation by id even when it does not appear in their own queue here; see "Resolving a confirmation" above. Confirming a task proposal runs task.confirm_proposal and moves the draft to Offered; declining runs task.decline_proposal and records a human reason. Neither action accepts the task, schedules a run, or executes agent work. Approve and reject route through the same confirmation.approve / confirmation.reject and escalation decision commands the CLI and per-seat surfaces use, so the audit trail and the agents-recommend-humans-decide rule are identical wherever you act. The queue also unions pending Forge software-credential requests (owner-scoped, read-only, deep-linking to /forge) — it surfaces the ask and links to the vault-backed fulfillment flow, never collecting the secret itself. A confirmation that fails on approval shows the actual execution error inline as a failed card instead of a dead generic banner, and its Retry re-runs confirmation.approve against current state; re-minting the same request supersedes the older card instead of leaving a duplicate behind.
Clearing expired confirmations
A confirmation that expires before a human acts on it is dead — it can no longer be approved. Re-mint revives one; dismiss is the opposite gesture for a card you do *not* want to revive. confirmation.dismiss clears one obsolete expired card from the queue, and confirmation.dismiss_stale clears every currently-expired card in your authority scope at once (bulk "dismiss all stale"). Dismiss is human-only queue hygiene: it never re-executes the original command and never records a decision — it just marks the dead card dismissed, notes who dismissed it, and writes an audit event for each row (dismiss_stale writes one per card). Only an already-expired card is dismissable; a still-live pending confirmation is refused, because its decision is approve or reject, not dismiss. You can only dismiss cards you already have authority over — an owner over any, anyone else — including an admin — over the seats they occupy, plus, for the stored-target command family described above, any card whose current live target they steward or administer even without occupying its original seat. An admin who does not hold that seat and does not steward/administer a stored-target card's live target cannot dismiss it, even though they can still decide (approve or reject) a still-live one.
The rule for agents
An agent never approves or rejects its own request. decisions.decided_by is always a human. A rejection is a decision too: rejecting any human-gated confirmation writes its own decisions row (decided_by = the rejecting human, referencing the confirmation, the command id, and its redacted argument summary), so a human's "no" is recorded in the durable audit trail — the same standing an approval leaves, not just a status flip on the pending card. Dismissing an expired card is the sole exception (queue hygiene, not a decision). The steward decision commands (escalation.resolve, escalation.reject) and proposal decision commands (task.confirm_proposal, task.decline_proposal) are not exposed over MCP. When an agent hits a gate, it prepares the evidence and the recommended action, returns the approve link, and waits for a human. Draft task proposals follow the same rule: the agent can propose the commitment, but a target-side human confirms or declines it unless a bounded Trusted grant applies.
When to stop
Stop before: approving a Charter, signing a manifest, connecting a tool or credential, minting a token, going live, resolving an escalation or Friction, reparenting or dropping a goal, changing a member role, or updating budget caps. These are human gates by design.