Approvals & the write guard
Reading knowledge is one thing; changing something is another. Actions that write to the outside world pass through a write guard and, where policy requires it, an approval gate.
Propose is not authorize
An agent can propose an action. It never approves one.
This holds regardless of how the proposal arrived:
- a model deciding a proposal should be submitted,
- a connector document containing instructions,
- a remote A2A agent responding with a suggested task,
- a task owner scheduling work that implies a write.
None of those is consent. Each emits an audit event and stops at the same gate.
What counts as a write
| Category | Examples | Guard |
|---|---|---|
| Member-owned store | artifacts, widgets, tasks | Write guard, owner-scoped, no external effect |
| Isolated execution | run_code | Sandbox lease bound to actor + conversation |
| External systems | Kamiwaza demo resource ensures and approved package tools | Write guard and approval gate |
| Federated clusters | federated_write job and retrieval submissions to a paired Kamiwaza cluster | Declares confirmation; the receiving cluster's own gates still decide |
The distinction is whether the effect can be undone inside Kaizen. An artifact version is immutable but contained. A platform change may not be.
Interactive approvals
When an action requires confirmation, a generic conversation item shows its tool, capability, redacted arguments, and any tool-provided member-safe fields. It sits inline beside the waiting turn, leaving the conversation and any already-committed artifact visible. The model first forms one concrete tool request; approval gates execution of that exact request, not model token generation. Generated artifact arguments are therefore held in the trusted backend as non-persistent fragments: they do not cross to the browser before approval. A create mounts no artifact pane and creates no durable artifact before approval. An update leaves the committed version unchanged until approval, then the approved call's fragments cross the stream and replay in place while retaining the committed artifact's language mode. Approval opens one owner-only checkpoint version before that replay becomes visible; the durable tool result completes that same version instead of appending another. An interruption retains the checkpoint privately, while shared and published reads stay on the last completed version. Denial drops the server-held fragments without changing the Workspace or creating a version. The member confirms or denies the tool and its redacted arguments in context, and the decision is recorded on the same durable item. An unattended notification targets the exact conversation item.
Pending items remain expanded so the decision and safe review fields are immediately available. Approved and denied items persist as collapsed summaries beside their originating turn; expanding a summary shows the retained details without allowing historical decisions to cover later conversation activity.
The lifecycle is append-only in the conversation journal. Kaizen
appends a member-safe request fact after releasing the sandbox, then appends an
approved or denied fact attributed to the authenticated member. The decision
causally references the request. Raw arguments never enter that journal; only
their canonical digest does. Interactive request facts also retain the opaque
Pi tool-call identity, which lets clients associate the decision with the exact
buffer without retaining its content. A separate mutable request row remains the
one-shot execution projection (pending, approved or denied, then
consumed), but changing that projection never rewrites the retained facts.
Approve once is scoped to that action. Always allow tool in this chat
adds an append-only grant bound to the authenticated owner, conversation,
agent, and approved tool name. It can satisfy only the member prompt that the
current mode would otherwise require; it cannot satisfy a different tool,
cross into another chat or agent, override Read only, widen a tool grant or
capability ceiling, replace MFA, or extend to a Task. The Chat mode itself is
unchanged. The durable decision endpoint accepts this option only for an
authenticated interactive-chat approval, and the approved journal fact records
the tool_chat scope without storing raw arguments.
For Demo Builder, manage_kamiwaza_demo preview is read-only. The approval
digest binds the exact preview and environment. After approval, the resumed
tool call rechecks the session actor, one-shot approval receipt, staged digest,
current environment binding, and write guard before the SDK may change
platform state. Denial has no platform effect.
Generated Core operations use their compiled effect class. Reads do not request
write approval; writes and sensitive operations enter the normal approval and
capability policy. Core can declare x-kamiwaza-effect explicitly. Without it,
GET, HEAD, and OPTIONS are reads, DELETE is sensitive, and POST, PUT, and PATCH
are writes. Approval never substitutes for Kamiwaza's authorization check.
Member permission modes
The built-in workspace default is Never ask, so a permitted write does not pause for a member prompt. An administrator can choose a different workspace default, and members can ask only when a tool declares confirmation, ask before every write, make all agents read-only, or retain never ask. The preference resolves the administrator's workspace default, the initiating member's default, that member's per-agent default, the current-chat override, and a task override, in that order. The accepted turn snapshots the result so changing a preference cannot rewrite work already queued. Delegation can tighten this result but cannot weaken an ancestor's mode.
The current-chat override is participant-specific. The browser stores it under a server-derived scope for the authenticated platform identity plus the conversation, and the backend resolves the accepted turn from that initiating member's principal. One participant choosing Never ask therefore cannot change another participant's Ask before writes behavior in a shared room, including after account switching in the same browser profile.
Never ask bypasses only this member prompt. It cannot grant a tool, raise an agent's capability ceiling, satisfy MFA, override administrator policy, widen a resource ACL, escape the sandbox, or suppress audit. The warning must be accepted when the mode is selected.
The runtime carries that decision into a tool call as an execution-local confirmation fact. A consumed one-shot approval remains a separate fact, so a tool cannot report that approval was consumed when Never ask authorized the call. Neither fact is accepted from model arguments, browser payloads, or the sidecar, and neither survives into a later tool call.
Federated calls follow the same modes. federated_read is a read and never
prompts, including in read-only mode. federated_write declares confirmation,
so it asks under the default and Ask before writes modes, runs under
Never ask, and is refused in read-only mode. Its approval card names the
federated cluster, the request and the job command, because the call runs on
another organization's cluster under the member's linked identity. On a
deployment that keeps the built-in Never ask workspace default, members see
no card unless they choose otherwise; an administrator who wants federated
writes reviewed should set the workspace default to ask when a tool declares
confirmation.
Durable approval waits
Scheduled work has no browser attached to its worker, so Kaizen persists the decision rather than retaining execution capacity while it waits.
A request is owner-scoped and bound to the conversation input, task run when present, agent, tool, capability, and canonical argument digest. An interactive request additionally binds the generated Pi tool-call identity; a Task omits that transport identity because approval resumes in a fresh worker. The member UI receives a bounded redacted argument preview. Approval is one-shot: the matching execution atomically consumes it before the tool runs.
The ordering is a safety and availability invariant:
- persist the pending request;
- release any conversation-affinity sandbox worker;
- atomically mark the request ready, append its conversation-journal fact, and notify the member if unattended;
- transition a task to
awaiting_approval, clearing its producer lease; - on approval, queue a fresh attempt that reacquires both resources.
If sandbox release fails, the request cannot be approved and the task blocks. If the task lease transition loses its fence, the waiting event is rolled back. A denial closes a task as policy-blocked. A task waiting indefinitely therefore holds neither a task execution slot nor a sandbox lease.
An unattended task cannot borrow:
- an interactive approval receipt,
- a browser confirmation,
- MFA state,
- ordinary membership,
- task-owner status,
- a role label supplied by a model or a request body.
Sensitive actions still require the global MFA step-up and therefore deny in a headless task. A saved never-ask choice is server-owned policy, not a model- or request-supplied bypass marker.
Task ownership is not approval. Owning the task means you decided it should run; it does not mean you consented to whatever it turns out to want to do.
Exact unattended approval
Core owns unattended approval as part of one reserved effect. The approval is bound to the member and registered workload, exact task and run revision, claim fence, canonical request digest, protected resource and action, policy version, quota reservation, credential binding, and decision expiry. The approver must hold the current platform role when deciding; task ownership is not sufficient.
Approval parks the effect. It does not issue reusable permission and does not run provider code. A denied, expired, revoked, policy-drifted, revision-drifted, or cancellation-affected decision is terminal for that reservation. Resume requires a current run claim and a fresh sender-constrained effect capability; the resource guard consumes that capability exactly once before the protected operation.
A durable member approval wait is a different thing and does not claim to be this. It records the task owner's one-shot decision, and it cannot satisfy a policy that requires a current platform approver, decision expiry, or revocation semantics.
Re-authorization at the moment of effect
Before dispatch, and again before every model, credential, external write, or sensitive tool decision, the shared runtime invokes a memory-only authorizer. It verifies Core readiness, the active automation grant, current member entitlement, workload revision, run claim and fence, immutable authority deadline, task revision, protected resource contract, exact request digest, quota, and approval state. The execution callback repeats the same current-state checks before it accepts the capability.
The execution callback also refuses an exact pending approval before invoking the tool, even if a sidecar calls it out of order. If approval persistence is unavailable for a call whose policy requires it, execution fails closed.
Safe-test grants remain read-only, and sensitive effects deny headlessly.
Failure returns one content-minimized authority denial. Credentials and raw provider errors are never persisted.
Missing transient authority blocks. It never falls back to a stored owner snapshot, local policy, workload identity alone, or a browser credential.
Cancellation and retry
Cancellation is durable first. Queued and retry-wait work becomes cancelled
immediately. Running work receives the cancellation signal, loses side-effect
authority at the lease fence, and can no longer publish a stale success.
Automatic retry is permitted only when runtime evidence explicitly says the failure is retry-safe and no external effect occurred.
An ambiguous write outcome is terminal and is never replayed. The effect stays
ambiguous, its quota and audit correlation remain intact, and a new approval
cannot convert it into a retry. If Kaizen cannot prove a ticket was not filed,
it will not file it again.
Across the A2A boundary
An Agent Card or remote skill is a capability description, not permission. A remote response may propose work, and an inbound A2A task may pause in an authorization-required state — but neither can approve an action. Execution still stops at Kaizen's existing member approval gate.
Audit
Every proposal, approval, denial, and execution emits a security audit event with a bounded reason code. The conversation journal separately retains the member-safe request and decision facts needed to rebuild the room. Approval decisions are recorded in both records for their different readers; the raw content that prompted them is in neither.