# VlinderCLI **AI agents that can time travel.**
*a.k.a. "Highly observable, replayable AI agents via type systems, pure functions, event sourcing, DDD, and Merkle DAGs in Rust."*
...but nobody would click on that.
Ashwin Bilgi --- ## Demo ...else you won't believe me. --- ## The Problem LLMs are fundamentally **non-deterministic**. Even with pinned weights, GPU vector math randomness and temperature values mean identical inputs produce different outputs. Public models are worse. You can't even pin them to a version. "Opus 4.6" is a pricing indicator, not a deterministic behaviour guarantee. --- ## Non-Determinism in Production Non-deterministic system in production? You need to **watch it closely** and **react fast**. Wrong flight booked. Wrong price quoted. Real-world consequences. So what stops you from reacting fast? --- ## Coupling
If your agent ships agentic processing **and** business logic, you are slower to react.
More code to test. More teams involved. More consensus to ship a fix.
A focused component is easier to change than a monolith.
*Principle: Keep agents small. Single Responsibility Principle.* --- ## Isolate the Fault Domain
Small isn't enough. You need to **isolate failure boundaries**.
The **Bulkhead Pattern**: watertight compartments in a ship. A breach in one doesn't sink the whole vessel.
Your LLM agent is one compartment. The rest of your system is another.
*Principle: Isolate failure boundaries. Bulkhead Pattern.* --- ## Contracts
Isolation doesn't mean silence. Agents still need to communicate their effects.
A **contract**: a published format for state changes, without knowing who consumes it or how.
*Principle: Publish state changes through contracts.* --- ## Push, Not Pull
Two mechanisms: **pull** (polling) or **push** (emitting events).
LLMs are slow. Polling a slow system means repetitive traffic and blocked consumers spinning for nothing.
Agents should **push**.
*Principle: Push state changes. Don't wait for pull.* --- ## Queues Over Webhooks
| | Webhooks | Queue | |---|----------|-------| | Subscriber tracking | Agent's problem | Not agent's problem | | Retry on failure | Agent gets fat | Queue handles it | | Coupling | Tight | None | A message queue keeps the agent lean. Fire and forget.
*Principle: Use message queues. Fire and forget.* --- ## Capture Everything
The queue message can contain just the state change. Useful.
But what's more useful? The **complete state** at that moment:
All variables in memory. Container stdout/stderr. Software versions. Full debugging context.
More information beats less information. Every time.
*Principle: Capture complete state, not just changes.* --- ## Content-Addressable Storage
Storing complete state on every event leads to disk explosion. But most state doesn't change between events.
The fix: the address of data is its **content hash**. 100 files with the same content = **one file on disk**, with fixed-size pointers.
CAS makes capturing everything affordable.
*Principle: Deduplicate with content-addressable storage.* --- ## Events Form a DAG
State transitions are directional. A leads to B, or A leads to C. Never circular.
This is a **Directed Acyclic Graph**.
Every event in your system is a node. Every causal relationship is an edge.
*Principle: Model events as a DAG.* --- ## DAG + CAS = Merkle DAG
We already have content-addressable storage. Now we have a DAG.
Combine them: a **Merkle DAG**. Every node is content-addressed. Every edge is verifiable.
Same data structure as **git**.
*Principle: Use Merkle DAGs to store state and relationships.* --- ## The Transactional Outbox Every write goes to **both** the database and the queue. Atomically. Same data in N+2 stores: 1. Database. 2. Queue. 3. Every queue consumer. Iron-clad guarantees.
*Principle: Both succeed, or neither does.* --- ## Time Travel = Changing a Pointer
Complete system state stored as content-addressed nodes forming a Merkle DAG in the database.
To time travel:
View
the graph offline.
Pick
where you want to go offline.
Change
HEAD by making an API call to production.
*Principle: Time travel works exactly like git.* --- ## VlinderCLI **The agent framework for engineers who:**
- **...think AI, the actual technology, is really cool,** - **...absolutely hate how everyone uses it,** - **...are terrified of running it unsupervised in production,** - **and will absolutely be the ones on the incident call at 3 AM when it hallucinates a refund policy that doesn't exist.**
*(This tagline needs work. So does the project. Come help with either.)*
---
vlindercli.dev
github.com/vlindercli discord.vlindercli.dev