Skip to main content
Four tools answer “what would happen” without touching a real system. They run calls through a throwaway engine: an in-memory ledger and run state, your real manifests and policy, and nothing executed anywhere. The real ledger and runs are not changed.

Capture

The ledger never stores arguments, only hashes. Replay and simulation need the arguments, so keeping them is a separate, explicit choice. Capture is off by default. Turn it on in reversi.config.yaml:
or with REVERSI_CAPTURE=1 (and REVERSI_CAPTURE_TTL_DAYS=14) in the environment of the gateway or server. reversi doctor reports whether it is on. The gateway and the server both record when it is on. What is stored, per call: run id and step (no step for a call that was blocked before it became one), tool, actor, tenant, environment, the arguments, the result once the call finishes, the decision (allow, would_block, block) with its reasons, the outcome (succeeded, failed, uncertain), the manifest version, the risk score, and when it was captured and when it expires. Redaction happens before anything is written:
  • credentials in any string (tokens, keys) become [REDACTED:github_token] and the like;
  • personal data (emails, phone numbers, card numbers, SSNs, IBANs, with checksum validation) becomes [PII:email] and the like, unless redact_pii: false;
  • every path in the manifest’s audit.redaction ($args.card_token, $result.secret) becomes [REDACTED];
  • strings over 20,000 characters are cut, lists are cut to 200 items and nesting to 12 levels.
Expiry. A record past its expires time is no longer listed or used, and reversi capture purge deletes expired records (or everything for one run with --run). A retention run purges captures along with the run state.
Because values are redacted, a rule that reads inside a redacted value can decide differently in a simulation than it did live. The report says so with a note.

Policy simulation

Replay captured calls through a candidate policy and see what changes.
Each call is compared with what was decided at the time: allow, needs_approval or block. Calls that now differ are listed under newly_blocked, newly_needs_approval and newly_allowed. The command exits 2 when something would be newly blocked, so it fits in CI before a policy change. A run that was needs_approval and is now block counts as newly blocked.
The sandbox sees the manifests and the candidate policy, not the live state around the original call. A call the history blocked for another reason (a halt, a missing prerequisite, a content check) can show up as newly allowed. Read those rows against the reason that was recorded.
Where it is available:

Replay a run with a change

Replay one captured run twice in the sandbox, as recorded and with your changes, and compare each decision and the rollback plan.
--set is repeatable and takes path=value, with the value as JSON (a plain word is a string): --policy <file> also tries a different policy in the same replay. The JSON report has baseline, variant, differences, baseline_plan and variant_plan.

Impact preview

What would approving this call do, and what could be undone afterwards? It reads manifests and policy only, so it works with capture off.
The preview gives the decision now (allow, needs_approval, block) with the reasons, the risk score and its factors, owner and risk labels, the tools that must run first, who must approve and how many, and the rollback outlook: the undo tool, its residue, whether it is verified, whether it needs approval. Warnings cover irreversible effects, draft or deprecated manifests and an unverified undo. Prerequisites in requires are assumed satisfied, so the answer is about this call.

Speculative execution

Compare alternative plans before running any of them. Each plan is a list of calls; each is run in the sandbox and scored.
plans.yaml
For each plan you get: the first call that would be refused (blocked_at), how many approvals it needs, the highest and total risk score, the irreversible tools, the residue a rollback would leave, whether it is fully reversible, and the rollback plan. Plans are ranked, lower is safer: 10,000 if the plan is blocked, plus 10 per approval, plus the highest risk score, plus 50 per irreversible call, plus 25 if it is not fully reversible. recommended is the best plan that can run to the end. A result you give is used to build compensation arguments; without one the result is synthesized from the manifest and the plan notes it. Risk scores appear when the policy has a risk section (here it has none, so they are 0).

Availability at a glance

Limits

  • Simulations use the manifests and policy as loaded now, with the real clock for time windows and freezes.
  • A sandbox run has every permission in the manifests, so scope problems do not show.
  • Limit and budget counters start empty in the sandbox and count only the replayed calls, so a limit that fired live because of other traffic may not fire in a replay.
  • Only calls captured while capture was on, and not yet expired, can be simulated or replayed.