How it works

Recovery — undo

When a run goes wrong, gmonk reverses it — per action, drift-checked against the live system, and recorded. It never overwrites a change it didn't make.

Preview, then apply

bash
gmonk runs                          # find the run
gmonk reverse-plan <run-id>         # read-only preview + live drift check
gmonk reverse-run  <run-id> --dry-run   # show exactly what an undo would do
gmonk reverse-run  <run-id>         # apply — only 'ready' steps run
gmonk reverse-run  <run-id> --force # also overwrite drifted rows

At apply time each action is classified live:

StatusMeaning
readyBefore-state exists and the system is exactly as the agent left it → safe to undo.
driftSomething changed since the agent → undoing would clobber it → skipped unless --force.
noopAlready at the before-state — nothing to do.
blockedNo safe automatic undo (external/opaque, or nothing captured) → needs a human.

Concurrency-preserving undo

The differentiator. Rather than writing an old snapshot back (last-writer-wins), gmonk recovers the operation and applies its inverse atomically, so concurrent writes survive. The op algebra covers:

  • additivecol = col ± n
  • multiplicativecol = col × k (exact rational inverse)
  • jsonb-merge — per-key register; inverse restores or removes each key
  • array-append — multiset; inverse removes the appended elements

Commutativity is verified, not assumed: if a known later write on the same cell doesn't commute, gmonk refuses to silently compose it and surfaces drift instead.

Causal excise

Undo one action and exactly its transitive causal dependents — from the dependency graph — all-or-nothing, leaving independent work untouched. This is the flagship surgical undo.

bash
gmonk excise <run-id> <seq…>

Rewind-repair-replay & point-in-time

  • Rewind-repair-replay (gmonk rrr) — rewind to a bad step, repair it (exclude/override), and replay later steps forward in dependency order, preserving good downstream work.
  • Point-in-time (gmonk state-at) — reconstruct the value of any touched row as-of just before a given action.
  • Blast radius (gmonk deps) — show the dependency graph and everything an action would affect.

External compensation

Effects that can't be restored by writing state back — a payment charge, a created SaaS resource — are reversed by a compensating call you define using the open-source @gmonk/sdk. gmonk builds the descriptor (including an idempotency key) and hands it to your executor; it never calls a third party on its own.

js
import { registerCompensation, registerCompensationExecutor } from "@gmonk/sdk";

registerCompensation({
  tool: "stripe.createCharge",
  capturesFromResult: ["charge_id"],
  compensate: ({ charge_id }) => ({
    tool: "stripe.refund",
    args: { charge: charge_id },
    idempotencyKey: `gmonk-refund-${charge_id}`,
  }),
});

registerCompensationExecutor("stripe.refund", async (desc) => {
  const refund = await stripe.refunds.create(desc.args, { idempotencyKey: desc.idempotencyKey });
  return { ok: true, detail: `refunded ${desc.args.charge}`, result: { refund_id: refund.id } };
});

Point gmonk at it with GMONK_CONFIG=/path/to/gmonk.config.mjs.

Every reversal is itself captured before it runs — so an undo is also reversible — and recorded in the audit log under a per-run advisory lock.