Snapshots & rollback
Every apply takes a snapshot first. Any apply can be undone.
Snapshots
Before any agent-initiated write, KrowForge calls services/apply_engine.apply() which:
- Reads the current state of every file the diff will touch.
- Stores a snapshot in
services/snapshot_store.py— a per-workspace ring buffer (capacity 100 snapshots). - Then applies the diff.
The snapshot has a stable ID (UUID) and is referenced by the receipt.
Rolling back
Three ways to trigger a rollback:
From the run history drawer
Each completed run shows a Rollback button. Clicking it restores every file the run touched to its pre-run snapshot.
From the receipt
POST /api/agent/rollback with a snapshot ID. Returns the diff that was reversed.
From the Changes panel
A Rollback last apply button at the top of the Changes panel (when there's a recent applied run).
Rollback receipts
Every rollback creates its own receipt. The audit chain is bidirectional:
… → receipt#41 (apply) → receipt#42 (rollback of #41) → …
You can roll forward again — but it's a new apply, not a re-application of the original. The agent re-generates the diff with current context.
Capacity & eviction
The buffer holds 100 snapshots per workspace. When full, the oldest snapshot is evicted. Once a snapshot is evicted, that specific apply can't be rolled back through the UI — but the receipt persists forever, so the audit trail stays intact.
If you anticipate needing very-long-tail rollback (security audits, compliance), pin the workspace into a self-hosted instance with a larger snapshot capacity (SNAPSHOT_BUFFER_SIZE env var).
What snapshots cover
- Files written by the apply engine.
- File creates (rollback deletes the file).
- File deletes (rollback recreates the file with snapshot content).
What snapshots don't cover:
- Shell command side effects (DB rows, network calls, sent emails).
- External API calls (cloud deploys, third-party services).
- Anything outside the workspace root.
psql -c "DROP TABLE users", that's not in any snapshot. Snapshots cover file changes, not arbitrary shell side effects. Use autonomy modes and per-tool guards to constrain side effects upstream.