Working with proposals
How the proposal mechanism looks from the agent side of the wire. The rule underneath everything: an Ajan token's mutation is recorded, never executed — nothing you do is visible in the store until the owner applies it.
202 means recorded
A mutation through any surface — POST /v1/commands, a propose-policy route, an MCP tool — answers HTTP 202 with the normal outcome shape plus proposed: true and auditId: null:
{ "ok": true, "proposed": true, "proposalId": "prp_…", "itemId": "pri_…", "position": 3, "preview": { "wouldChange": [...], "warnings": [] } }
auditId: null is the tell — "no audit row ⇔ nothing applied" is a platform invariant, so you can distinguish proposed from applied without guessing. Over MCP, results of recorded calls start with PROPOSED (not applied). The recorded preview is the same dry-run the owner will see; read its wouldChange and warnings to know what you actually proposed.
You have one open proposal per token; the first recorded mutation creates it, later ones append items in order (max proposalMaxItems items). Items keep their order at apply — sequence your calls the way they must run (create the product before pricing it).
Notes
Every recorded call accepts a note (≤500 chars) — the rationale the owner sees next to that item in Öneriler. Use it: "Fiyat listesindeki yeni KDV oranına göre güncelleme" reviews faster than a bare diff.
Submitting
When the change set is complete, proposal.submit with a title and a summary. The proposal moves to submitted, the owner is mailed a review link, and it expires unreviewed after proposalSubmittedDays days. An empty proposal cannot be submitted. From the CLI: propose.
While a proposal is open you can keep appending; after submit, a further mutation opens a fresh proposal.
Drift: stale items
At apply time each item is re-previewed against the current world and its recorded preview hash must match. If the store moved underneath you — the owner edited the same price, an order shipped, the live design advanced — that item goes stale, the proposal pauses, and the owner decides: skip the item, or accept the new preview and continue. You do not resolve drift; the owner does. What you can do is keep proposals small and submit promptly — drift is a function of time.
Failures behave the same way: apply stops at the first failing item and never rolls back applied ones (each applied item is an ordinary audited command).
Watching status
proposals.list/proposals.get— your proposals (agent tokens see only their own), with per-item status:pending,applied,stale,failed,skipped.- The MCP resource
mzk://proposal/currentserves the same view. - CLI:
mozaik proposal list|status(proposal).
Withdrawing and expiry
proposal.withdraw drops your own proposal in open, submitted or paused — use it when you discover a mistake rather than submitting a correction on top. Open proposals idle out after proposalOpenIdleDays days; submitted ones expire after proposalSubmittedDays days; a revoked token's open proposal is withdrawn automatically. Expired or withdrawn proposals apply nothing.