# Structured operator proposals An Office owner approves one frozen operation. The explanation, the proposed effect, supporting records and execution result are separate parts of its review. ## Preparing a proposal `propose_action` is a local Office/operator tool. It is offered only when the deployment has enabled its preparation provider. It is not a public MCP tool, SDK method, or a tool that can be executed by replaying a human approval. ```json { "operation": { "tool": "manage_substrate", "arguments": { "action": "cancel_outbox", "outbox_id": "obx_example" } }, "reason": "The original draft was replaced and should not be sent.", "evidence_refs": [{ "kind": "artifact", "id": "art_example" }] } ``` The reason is trimmed and must contain 1–2000 characters. Supporting records are optional: zero to ten references. References may identify an entity, artifact, learning, decision, commitment, app, storage object, or authorized conversation message. File references contain `app_id` and stable `object_id`; message references contain `conversation_id` and `message_id`. Reference labels and previews come from the server. A reference's existence and accessibility do not prove the operator's explanation. Do not supply a title, preview, organization, policy verdict or evidence label. Preparation validates actual executable contracts without running handlers, checks the organization's target and references, and freezes material facts. Unknown or unsupported operations return `proposal_unreviewable`; they never become generic approvable requests. An intercepted consequential call without an explicit reason returns `proposal_incomplete` to the operator. Call the wrapper with the same operation and a reason. Do not turn unrelated assistant text or a scheduled sweep into the owner's intent. Existing automatic operations retain their direct path; the wrapper returns `proposal_not_required` and executes nothing for them. When substrate parks a normally automatic operation, its server-issued result is recorded before the operator supplies review details. Completion uses that existing action. Do not propose it again, supply an arbitrary `approve` call, or resolve the owner's own queue from agent code. Substrate replay keys bind the normalized capability, payload and trusted application/rule scope within the organization. Matched replays return the existing action's actual `status`. Reusing a key for different work, or replaying an ambiguous historical key, returns `409 idempotency_intent_conflict` rather than authorizing a different action. New work needs its own key and review. ## Reviewing and resolving The authenticated approval detail adds `approval.review`: - `reviewStatus`: `ready`, `stale`, `unavailable`, or `legacy`; - a safe prepared preview, attributed operator reason and accessible references; - independent `executionOutcome` and `permittedActions`. The public demo omits this private review. Its browser-only answers do not resolve approvals or execute operations. `POST /v1/dashboard-agent/operator/approvals/:id/resolve` accepts only the resolution fields. Request arguments, preview and operation cannot be replaced in that body. Current authorization, schemas, immutable artifacts and material facts are rechecked before dispatch. Changed facts expire the old gate and complete its original model call; another owner review is required. Temporarily unavailable reads fail closed and remain retryable. For a legacy or insufficient gate, an owner can call `POST /v1/dashboard-agent/operator/approvals/:id/request-review` with `{}`. This expires that gate and records a request to prepare the same frozen operation. It does not approve, execute or invent a reason. Paused work retains the request until a permitted runner can resume it. Denial is separate and is not consent to propose the operation again. The optional authenticated list query `?history=1` returns pending `approvals` and up to 50 recent resolved reviews in `history`, scoped to the organization. It also returns a separate `preparations` collection. Preparation is not execution: requests move through `waiting`, `running`, `replaced`, `blocked` or `failed` in `dashboard_agent_review_preparations` (migration 135). A failed or blocked request stays visible and can be retried explicitly by the owner. Already-expired `proposal_review_required` records are recoverable; other expired, successful or uncertain executions are not retry shortcuts. Preparation rows may include an optional read-only `explanation` containing `title`, `summary`, `effects`, `fields`, a nullable attributed operator `reason`, accessible `evidence`, and an optional current `blocker`. `facts.source` identifies newly checked facts (`current`) or the previously prepared snapshot (`prepared`); `facts.at` records the corresponding check or preparation time. Readers must not treat historical snapshot facts as a current eligibility check. The optional provider `explain` hook reads current target facts without preparing an executable review or scheduling work. Recovery enrichment rechecks organization scope and current-viewer evidence access, and bounds lookup concurrency. Missing or inaccessible facts remain unavailable; raw operations, chat prose and provider errors are not used as explanations. A readable saved proposal can supply its recorded reason/evidence when no current description is available. Providers that do not implement the hook remain supported. The compatibility projection for an expired review with no preparation record has `state: failed`, `safeErrorCode: proposal_not_prepared`, and `attemptCount: 0`. This means **not prepared**, not an attempted failure or work currently running. Requesting preparation is still an explicit owner action and is not approval. Preparation of a delivery cancellation is refused while that delivery is in flight. It leaves the delivery unchanged and creates no execution or replacement approval. Reconcile the delivery state before requesting preparation again. The source approval ID makes repeated requests idempotent. Claims carry a twenty-minute lease and token. Only a matching frozen intent, organization, conversation and current lease can link a persisted replacement atomically. Provider errors, preparation refusal, or a turn ending without a replacement record an unsuccessful preparation; assistant prose does not settle a request. Explicit preparation input is injected by the operator runner, not appended by the request endpoint after a potentially unanswered tool call. Focused preparation turns report `preparationAttempted` so a scheduler cannot count them as a completed full reconciliation sweep. Executable arguments stay server-side; credential-named display fields are redacted. Arbitrary source code is displayed inertly and is not promised to be secret-free or to have fully predicted downstream effects. ## Execution reporting and recovery Decision status is independent of execution: `not_started`, `running`, `succeeded`, `failed`, or `unconfirmed`. Missing legacy metadata does not prove that an action never ran. A successful HTTP resolution response alone is not the execution-success signal. Confirmed cached execution can finish interrupted transcript completion without repeating the effect. A known failure remains pending and retryable. A running or ambiguous attempt with no confirmed result requires reconciliation; it must not automatically repeat a possibly completed effect. A confirmed effect cannot later be relabeled as a denial because transcript completion was interrupted. Uncertain responses also leave a negative execution receipt. This applies to older approvals and to the native approval leg after a proposal was parked. A lost response or an exception after dispatch cannot silently cause another request. Review refresh checks this receipt even if its outcome projection is missing. Only an explicit trusted no-effect refusal remains retryable. Concurrent resolutions serialize around the existing execution cache. This prevents duplicate callers from dispatching together; it does not make remote side effects universally crash-proof or exactly once across every failure. Deployment is additive: compatibility readers and resolvers must be installed before enabling new writers. Disabling writers never disables checks on an existing prepared proposal. Keep compatibility code and columns while such proposals remain pending; follow the deployment-specific rollback runbook. ## Whole-application deletion reviews A deployment-specific adapter may prepare `appdelete` only after reading the currently owned application's material state and bounded resource inventory. Its review must show the pause or preservation note, deployed function count and stored-file inventory, and explicitly state that all tables and rows will be removed while their contents were not inspected. This is not a data-retention audit. Unknown, foreign or unavailable targets fail closed. The current human owner must approve the frozen operation under existing policy; review preparation authorizes no deletion. An accepted asynchronous deletion request is not evidence that teardown has completed. Adapter availability depends on the deployment; this generic contract adds no MCP/SDK/CLI action.