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.
One-shot inline review (signed-in UI)
Some extreme effects a signed-in human submits from the product UI take a one-shot inline review instead of an Approvals row. Today that path is live for skill.assign_to_seat when a human submits status: "approved" from the skill detail page (/skills/:slug → Assign to seat → Approve assignment) or from the seat Skills drawer.
The first submit authorizes the exact effect, builds a binding (tenant, human, browser session, command, effect variant, input digest, resource-version digest, policy version), persists a short-lived challenge with redacted replay args, and returns a colocated review card — creating zero pending_confirmations rows. The Confirm button submits only the opaque challenge token; it cannot replace the stored command, input, or target. One real transaction locks the challenge, reauthenticates, reauthorizes, rebuilds and compares the binding, executes, writes the decision / evidence / audit, and consumes the challenge. Stale, foreign, expired, or changed reviews terminalize with a typed no-effect result and a fresh-review action — they never revive old authority. Replay args never carry secrets or credential material.
The same command from CLI, MCP, or an agent still cannot mint direct-human evidence: those transports stage a pending confirmation for a human (or are refused). An ordinary UI submit that is not on the inline route — for example assigning with status: "proposed" — executes without creating a pending Approvals row or an inline challenge. Broader call-site migration of other forcePendingConfirmation UI paths remains a follow-on; this guide documents the path that is live.
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. That block always carries webUrl — the human path that always works — and carries cliCommand only when the server could prove, at the moment it built that card, that a non-interactive CLI approval of it would be accepted. For nearly every gated command it is absent.
When `cliCommand` is absent, approve through `approveVia.webUrl`. Absence means the proof was not available when the card was built, which is not always the same as "this could never be approved from a CLI" — a card rebuilt from storage omits the line because that builder has no tenant policy to check, not because the command is forbidden. Either way webUrl is the answer, and it always works. Do not substitute a re-run of the original command for the missing line unless you know that command's confirmation level is human_required: anything else does not stage for a human CLI caller, so it simply runs again and repeats its effect while this confirmation stays pending and approvable. The one family where re-running IS the remedy is spelled out a few paragraphs down, with the command level that makes it safe.
The human approves through the web link; the agent surfaces the link and stops. 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.
For onboarding.setup and onboarding.activate specifically — routed to the tenant's owners rather than a seat's steward when the seatless implementation-bootstrap principal stages them — the /onboarding page also shows a pending-gate banner naming which of the two is waiting and how long it has been waiting, with a link straight into /approvals. Scope differs between the two: the banner covers every pending gate of these two kinds regardless of who staged it, while the owner notification and its debounced reminder cover only implementation-run-staged ones. If the initial notification goes unanswered, the reminder follows once the confirmation has sat pending past a short threshold (15 minutes) — one reminder per confirmation, not a repeating nag. The earlier device-code exchange (before either of these is staged) has no equivalent banner or reminder today: a pending device-authorization row carries no tenant id until it is approved, so there is no tenant-scoped signal to derive one from without it being spoofable by an unrelated request against the same public endpoint.
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.
When a confirmation carries no `approveVia.cliCommand`, approve it through `approveVia.webUrl`. That link always works, for every confirmation.
Do not reach for "just run the original command again" as a general fallback. It is the right move for exactly one family — a command whose confirmation level is human_required, which is what makes it stage a confirmation for a human CLI caller in the first place. Re-running one of those at an interactive terminal is a clean single step: the CLI catches the confirmation the command returns, renders the gate, asks Approve <command-id> (confirmation <id>)? [y/N], and records the review.
# ONLY for a human_required command (for example mcp_token.revoke): re-running it at an
# interactive terminal stages the confirmation, prompts, and approves in one step.
rost command <the-human-required-command-id> --json '{ ...the same input... }'For any other command the re-run is not an approval at all — it is a second execution. A command that does not stage for a human CLI caller (anything whose confirmation level is not human_required) just runs again and repeats its effect, while the confirmation you were trying to clear stays pending and can still be approved later. That is two executions from one intent. A confirmation can exist for such a command even so: the company autonomy ceiling holds an over-ceiling autonomous action for a human, and the held card is approved from its web link.
Calling confirmation.approve directly is a different thing again, and it is not a way to get the prompt. Typing it at a terminal does NOT render the gate and does not record a review — the prompt only appears when the original gated command is the one that returned the confirmation. So a direct call is an unreviewed approval, which the server accepts only for a Trusted-eligible command in a workspace whose CLI confirmation mode permits one; otherwise it is refused. That is why approveVia.cliCommand is present only on the confirmations where it would actually run.
# Reject a pending confirmation (no review signal is required to decline).
rost command confirmation.reject --json '{"confirmation_id":"<confirmation-id>"}'The approvals queue
The /approvals page is a detailed, filterable view over the SAME canonical loader the Needs you tab described under "My Work" below reads — not a second, independently derived queue. That does not mean the two surfaces always show the same NUMBER: /approvals' Pending total also counts read-only Forge software-credential requests (surfaced there for visibility, never decided inline), which /tasks' Needs you tab deliberately moves to its Work tab instead, and /tasks additionally hides decisions nobody can act on any more while /approvals keeps showing them. So the two counts differ by design, not drift. /approvals is still the only surface with keyset pagination past the first page, the "Assigned to me" escalations-only tab, bulk-approve and bulk-accept controls, the expired-confirmations re-mint queue, and recent-delivery receipts, so a tenant with a large confirmation or escalation backlog, or a bookmarked ?confirmation=<id> deep link, has a stable place to work from. Its pagination covers pending confirmations; its escalation list is complete and uncapped. It is not a complete view of EVERY kind at any size — see the four capped kinds noted under "My Work" below. It is not the only place a decision clears, either: verification tasks decide on /tasks, blocked-awaiting-grant interventions decide on the seat page, and draft signal bindings decide on /signal. A conversational gate is ranked on Home for visibility but is still decided here, through a ?confirmation= deep link into this queue. 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 credential request is never decided inline, because the secret is only ever supplied through the Forge vault flow — it is surfaced here read-only and links to /forge to fulfill it. It is counted in the Pending total shown on this page, alongside confirmations, escalations, Forge gates, task proposals, and deliverable acceptances. 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.
My Work
/tasks — labelled My Work in navigation — reads three tabs off one ?tab= param: Needs you, Work, and Done. It is the operating queue for the decision kinds its loader reads. It is not a canonical single queue for every decision in the product, and it does not replace /approvals: the Approvals link in its header is the detailed view, and the escape hatch for everything the tabs do not show.
Needs you reads the SAME canonical loader /approvals does and renders through the same cards, but it is a scoped view, not an identical one, in three distinct ways. First, by kind. It loads five of the nine canonical decision kinds — confirmations, escalations, Forge gates, task proposals, and deliverable acceptances. The other four decide on their own surfaces and are not reachable from this tab at all: a conversational gate is ranked and decided on Home, a verification task is an ordinary Work commitment, a blocked-awaiting-grant intervention decides on the seat page, and a draft signal binding decides on /signal. So a tenant whose only open decision is a blocked-grant intervention sees Needs you report zero, and clears it from the seat page. Second, by size. It fetches a bounded first page of pending confirmations and open escalations — Forge gates, task proposals, deliverable acceptances, and credential requests already carry the same cap at the SQL layer — and it has no cursor-based "Show older" of its own. When EITHER of those two bounded populations has more rows past its bound, a banner names that population and points at /approvals, which paginates confirmations and keeps its escalation queue complete and uncapped. Third, by actionability. It shows only decisions a human can still act on, using the same actionability rule every decision count in the product uses: a proposal whose implementation run has died is filtered out here rather than rendered as a card nobody can decide, and stays visible on /approvals. Bulk approve and bulk accept stay on /approvals; every item on Needs you is decided individually. Widening the tab to the remaining four kinds is tracked as DER-4038.
Its count is an actionable-decision count over those five kinds, and it is exactly the number of cards on the tab. Forge credential requests are excluded, because they are Work, not a decision: a secret is only ever supplied through the Forge vault flow, never approved inline. That count differs from /approvals' own Pending total for three reasons, and the last is usually the largest: the credential-request exclusion, the actionability filter described above, and the fact that it is the length of a BOUNDED page. /approvals' Pending total counts pending confirmations and open escalations in full, so a tenant with 250 pending confirmations sees 250 there and 100+ here — the trailing + says the number is a floor, not a total, and is not drift. That + covers pending confirmations and open escalations, and nothing else. /approvals' total is not uncapped in every part either: Forge gates, credential requests, deliverable acceptances, and draft task proposals are each read at a cap of 100 and contribute their capped length, so a tenant with 101 pending Forge gates and nothing else sees 100 on both surfaces — with no pagination onto the 101st and no marker saying so. Needs you states that limit on the tab itself so its silence is never read as completeness, but neither surface over-fetches for those four kinds yet; closing that is tracked as DER-4034. Until it lands, do not read either number as proof that a very large backlog in those four kinds is fully drained. The bound also lands BEFORE the actionability filter, which the tab says outright: rows can be cut past the limit even when the count reads zero (DER-4054).
Work holds ordinary task commitments — your seats' queues and your stewarded agents' queues — plus a read-only "Action required" group for the pending Forge credential requests Needs you excluded. A seat you both occupy and steward appears once, in your own queues, and is counted once. That group is credential requests only; the rest of the recovery family — an agent with an unfinished setup step, a connection to repair, a seat with failed runs — stays on its own surface (the seat page, /settings) and is part of the same DER-4038 widening. Done shows recently completed tasks. Settled confirmations, escalations, task proposals and deliverable decisions are not listed there; decision history stays on /approvals, and folding it into Done is also DER-4038.
Deciding an item from Needs you runs exactly the same confirmation.approve / confirmation.reject and escalation commands described above, and every decision refreshes both surfaces, so the item settles wherever you were looking at it. Where you land afterwards differs by kind, and only one kind keeps you where you were: approving or rejecting a confirmation returns its outcome in place and leaves you on the tab, while deciding an escalation, a Forge gate, a task proposal or a deliverable acceptance still returns you to /approvals. Making that round trip land back on /tasks is tracked as DER-4001.
The Home "Needs you" band
/home opens on the same decisions, read from the same canonical loader /approvals and My Work read — but it is a bounded FIRST LOOK, not a third queue, and it says so in three ways. It shows a top-N. The band renders the newest few decisions across every kind the loader returns, ordered by recency across the whole set rather than by which read they arrived in, alongside the ranked notices Home owns itself (a conversational gate, a steward escalation, an open verification task, a blocked-awaiting-grant intervention, an unattended draft signal binding, and the company health and pace notices). Its header count is the number of rows on the screen — one projection produces both, so a decision reachable through two of those reads is shown once and counted once. It does not paginate in place. When more decisions exist than the band shows, it links to My Work, which is the surface with the queue; there is no "show older" on Home. It never asserts a total it cannot prove. The band reads a bounded number of pages, so the overflow line states an exact remaining count only when that read reached the end of every population it draws from; otherwise it says more are waiting and keeps the link, rather than printing a floor as if it were a total. Two separate things can make the remainder unprovable, and either one is enough. The first is pagination: the band stopped with pending confirmations still unread. The second is the same cap My Work states for itself — Forge gates, task proposals and deliverable acceptances are each read at a limit of 100 with no marker saying whether more exist (DER-4034), so when any of them comes back AT that limit the band drops to the same "more are waiting" wording instead of counting. A floor of zero is not rendered at all: unread rows are not evidence that any of them is a decision. Its escalation list is bounded the same way, and discloses the bound on the band when it applies. Deciding anything from /approvals or My Work refreshes Home too, so the band and its count settle wherever you were looking.
When an agent-led run ends before you decide
A confirmation staged by an agent-led implementation run answers to two clocks, not one. The card has its own expiry (4 hours at normal risk). The implementation run behind it has a much shorter one: the bootstrap bearer starts at about 55 minutes, and while a gate is waiting it only renews as the agent keeps working. If the agent's session ends — the terminal closes, the host sleeps, the run is completed or abandoned — nothing renews it, and the bearer lapses long before the card does. Removing or demoting the owner who approved the run has the same effect immediately.
From that moment the card cannot be approved, even though it is still listed and has not expired. ROST says so on every surface rather than letting you find out by clicking: the card on /approvals is labelled Run ended, states that the implementation run behind the proposal has finished, and its Approve (and, once the card expires, its Re-mint & approve) is disabled; the /onboarding banner no longer lists it among the decisions waiting on you and drops its review link; and the setup lifecycle card replaces "ready for your review" with the same explanation. The waiting-on-you reminder email is not sent for one either — there is no action left to remind you about.
Rejecting it still works, and is how you clear the card. Asking the previous agent to re-stage cannot work: its credential is gone, and a later implementation run is refused from acting on seats created by the dead run's setup receipt (seat_outside_implementation_receipt). The real fix is to re-run the whole implementation flow (rost implementation access start), which creates a new setup receipt that can stage a fresh proposal; nothing you approved earlier is lost, because an applied setup receipt is immutable and independent of the run that produced it. A card with no implementation run behind it — anything a human staged directly — is never affected by this.
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.