# Proposals (Öneri) The consent mechanism for agent work (ADR 0027 §2). An Ajan token's live mutations are never executed — each is dry-run previewed and **recorded** as an item of the token's one open proposal, answered with HTTP 202 `{proposed: true, auditId: null}`. "No audit row ⇔ nothing applied" stays literally true: a proposal stores envelopes and previews only — never data snapshots, no storefront bump, nothing visible until apply. ## Proposal statuses | status | meaning | |---|---| | `open` | the token's working set — every recorded mutation appends an item (one open proposal per token; idle proposals expire after 7 days) | | `submitted` | [`proposal.submit`](/reference/commands/proposal.submit) with a title + summary; the owner is mailed a review link; expires unreviewed after 14 days | | `applying` | the owner approved — [`proposal.approve`](/reference/commands/proposal.approve) is the audited consent event (owner sessions only; tokens structurally cannot approve) | | `paused` | a stale or failed item stopped the run; the owner skips, re-approves, or rejects | | `applied` | every item ran (or was skipped) | | `rejected` | owner refused ([`proposal.reject`](/reference/commands/proposal.reject)) | | `withdrawn` | the agent (or owner) dropped it ([`proposal.withdraw`](/reference/commands/proposal.withdraw)) | | `expired` | the open-idle or submitted window lapsed | Item statuses: `pending → applied`, or `stale` (preview drift), `failed` (execution error), `skipped` (owner's [`proposal.item.skip`](/reference/commands/proposal.item.skip)). A proposal holds at most 500 items (`proposalMaxItems`). ## Preview-hash drift At record time the item stores its dry-run preview and `sha256(canonicalJson(preview))`. At apply, each item is **re-previewed against the current world** and the hashes must match; design items additionally require the workspace hash unchanged and `liveSeq === baseSeq`. A mismatch means the world moved under the proposal (a price edited, an order shipped, the live design advanced): the item goes `stale` with the current preview attached, the proposal `paused`. The owner skips the item or re-approves to accept the new preview; `POST /v1/proposals/{prp}/devam` resumes. ## Apply semantics - Items run **in order, stop at the first failure, and are never rolled back** — every applied item is an ordinary audited command the owner undoes by the usual means. - Each item executes as the **approver's** merchant actor with `idempotencyKey = command_uid` (crash-retry replays produce one outcome), attributed `{viaProposalId, proposedByToken}` — **proposer and approver land on ONE audit row**. - Route items replay through the same handler functions the admin buttons call, so mail and provider side effects match exactly. - `proposal.approve` pins `expectedItemCount` — an item appended between render and click invalidates the approval.