← Back to Timeline

🏗️ The Runtime That Keeps Receipts: Why I Deleted My Own AI Platform and Rebuilt It Around an Approval Gate

Stratum began as a confession: hundreds of files of simulated intelligence, beautiful dashboards, and not one real file changed. This is the story of tearing that down and building an agentic execution runtime governed by one rule — nothing is observable until something actually happened.

Every few weeks last year, I gave a demo that worked perfectly.

A session would open. A proposal would appear. A decision-intelligence panel would score it, a governance timeline would light up, projections would refresh across six tabs. Observers nodded. I nodded. Everything on those screens was real data, persisted in real databases, served by a real FastAPI backend with more than three hundred modules.

There was just one problem. None of it had ever touched a file. No repository had been transformed. No command had run. The proposals were born from mocks, the approvals approved nothing pending, the decision intelligence reasoned over events that no execution had produced. I had built a gorgeous mirror of a product — a theater of agency — and the pytest suite passed beautifully, which made it worse.

"A dashboard can show anything. That is exactly what makes dashboards dangerous. The question that broke my illusion was embarrassingly simple: show me the diff."

If you build or buy agentic AI software today, you have probably met its ghost. Chat windows that claim work happened. Agent loops whose only evidence is their own narration. Demos where the mock is the feature. Governance pages counting approvals of proposals that were never executable. The industry has become extraordinarily good at representing AI work and remarkably forgiving about whether any occurred.

This is the story of how I stopped doing that, deleted my own architecture, and rebuilt it around a single non-negotiable idea: an execution runtime must keep receipts.

The Reckoning: An Audit of My Own Code

The rebuild started not with new code but with verdicts against the old code. Every module in the legacy platform had to answer one question: are you causally connected to something that actually happened?

The hardest part was not writing the verdicts. It was accepting that tests passing was never evidence the product worked. My old suite verified that simulated objects behaved as their simulations defined them. Circularity is not validation; it is choreography.

What Today’s Agent Runtime World Gets Wrong

Stratum exists because five failure modes keep repeating across the agentic tooling landscape:

Failure Mode What We See Today What a Runtime Owes You
Narration as evidence Agents report success in prose; the transcript is the only artifact. Durable events per transition: typed, sequenced, causally linked facts — not stories.
Approval as UI decoration Confirm buttons wired to nothing pending; approvals recorded for plans that cannot execute. A structural gate: execution is unreachable in code until a human decision exists for that exact plan.
Mock-driven acceptance Demos pass because canned responses define both the stimulus and the assertion. Live-provider acceptance: the primary proof path hits a real endpoint and shows its receipts.
Fragile sessions State lives in memory or an opaque model session; a crash erases the work and the context. Recoverable executions: pending work survives restarts and resumes without re-invoking the model.
Provider lock-in Business logic fused to one vendor SDK’s object model. An adapter seam: swap OpenAI, Azure, Groq, Ollama without touching the engine.

None of these are exotic research problems. They are plumbing failures — and plumbing is precisely what a runtime is for.

Why Now?

Three curves crossed to make this the right moment:

First, agentic coding went mainstream. CLI copilots and autonomous engineers are committing to real repositories daily. When the tool changes your files, “trust the demo” stops being an engineering stance and becomes an audit finding waiting to happen.

Second, durable event infrastructure became commodity. Kafka-compatible brokers like Redpanda run comfortably on a laptop. The excuse “event sourcing is for enterprises” expired the moment a single-container broker could hold your entire execution history.

Third, my own research told me where the gap was. In a prior prototype, AI Event Fabric, I explored whether independent AI and human participants could coordinate through durable events alone. The retained evidence supported the event-boundary hypothesis — but the lifecycle experiments exposed the deeper truth: a broker gives you transport and history, not a worker runtime. Readiness, recovery, approval authority, and execution control live above the event layer. Stratum is the answer to that discovery: the runtime layer the fabric experiments proved was missing.

The Vision: One Spine, No Shortcuts

Stratum’s entire contract fits in one picture. Seven stages. One of them belongs to a human, unconditionally.

📋
Task
repo + intent
+ bounded context
🤖
Plan
strict JSON
validated schema
🔒
APPROVAL GATE
human decision,
recorded as fact
⚙️
Execute
read · write
· verify commands
👀
Observe
exit codes
diffs · artifacts
📡
Events
Redpanda
+ SQLite index
Replay
no AI calls
no side effects

The wording matters. The gate is not a policy setting or a checkbox in a settings panel. It is a guard clause in the engine: decide_and_execute refuses any transition that does not originate from APPROVAL_REQUIRED with a matching pending plan id. During UAT, I watched a duplicate approve request get rejected with “cannot resolve approval from status COMPLETED.” That error message is my favorite feature.

The Architecture at a Glance

Zoom out from the spine and the system reads like a distributed-systems diagram, not an AI demo. One Kafka-grade event backbone sits at the center. Everything else — humans, CLIs, consoles, planners, verifiers — is just another participant, attached as a producer, a consumer, or both. Around the backbone sit three dashed seams: swap anything behind a dashed border without touching the engine, the contracts, or each other.

Participants
producer + consumer
CLI Operator
stratum run / replay / consume
Browser Console
approve · observe · replay
HTTP Client
POST /tasks · /approve
Human Approver
the gate — first-class participant
Planner Agent
proposes structured plans
Replay Engine
folds history into narrative
emit / subscribe emit / subscribe emit / subscribe
Event backbone
Kafka-grade
📡 Durable Event Stream — Redpanda (Kafka API) stratum.runtime.events.v1
DURABLE LOG ORDERED PER EXECUTION CAUSAL CHAIN REPLAYABLE AUDIT-GRADE HISTORY
Every transition is a fact on the log — task created, plan proposed, human decision, tool result, verification exit code. Nothing observable happens off-stream. Any consumer can reconstruct any execution, at any time, forever.
pluggable seams pluggable seams pluggable seams
Swappable
seams
🤖 LLM Gateway Seam
swap freely
Any OpenAI-compatible endpoint: OpenAI · Azure · Groq · OpenRouter · Ollama · vLLM. Vendor- and provider-agnostic by contract (AIAdapter) — the engine never learns a vendor’s name.
💾 Persistence Seam
index only
SQLite local index + execution projection (2 tables, WAL). A cache over the log — never a competing source of truth. Broker down? The stream still has every answer.
⚙️ Tool & Harness Seam
extend safely
Schema-driven tools (read_file · write_file · run_command) inside workspace boundaries. Harness-agnostic: CLI, console, HTTP — identical guarantees, one gate.
Solid amber: the backbone — the only state you must trust Boxes: participants — all peers, producer/consumer symmetric Dashed: plug-and-play seams — replaceable per deployment

Under the Hood: Six Decisions That Define Stratum

🖌️ A Vocabulary of Three Actions
Plans may contain exactly read_file, write_file, run_command. Small vocabularies are auditable vocabularies. The model proposes within the schema; anything else is rejected before a human ever sees it — invalid output fails the task before approval exists.
📡 Events Are the Only Authority
Seventeen typed events with per-execution sequence numbers, correlation ids, and causal parent links. Every lifecycle transition emits one. Projections are rebuildable; the stream is not negotiable. Topic: stratum.runtime.events.v1, keyed by execution id for total ordering.
🔒 The Gate Is Code, Not Policy
Rejection provably touches nothing — tested. Mutating steps force requires_approval. And because the boundary lives in the engine, every transport (CLI prompt, browser button, HTTP call) inherits identical guarantees for free.
💾 SQLite, Deliberately
Postgres lost. A local-first runtime should not demand a database server. Two tables — an event index and an execution projection — deliver queryable history, fast listings, and crash recovery with zero operational burden. Redpanda stays authoritative when present; SQLite never competes.
🌐 One Adapter Seam, Many Providers
A single protocol (AIAdapter) separates “how do I talk to this model?” from “what does the runtime do with the result?” OpenAI, Azure OpenAI, Groq, OpenRouter, Ollama, vLLM all speak the same dialect. New providers are new adapters; the engine never learns their names.
⏪ Replay Must Be Pure
Replaying an execution folds recorded events into state and narrative. It never invokes the provider and never repeats a filesystem effect. If replay needs the AI to tell you what happened, you did not build an event system — you built a log with amnesia.

The Crash Experiment

Every claim above survives contact with a terminal. My favorite afternoon of the whole rebuild was spent trying to break the approval boundary with a kill signal.

The setup: submit a task through the browser console. Watch the live provider return a structured plan. Status: APPROVAL_REQUIRED. Then, before approving — kill the server process outright.

Restart. The engine reads its two-table SQLite projection and prints:

$ stratum serve --port 8899 --brokers 127.0.0.1:9092
Resumed 1 pending execution(s) awaiting approval:
  exe_mt4t9iev-3h7trr15  Change the greeting returned by hello.py...

The plan was approvable again — same steps, same identity, sequence numbering continuing exactly where the dead process left off, correlation preserved. Approving it through the restarted console executed the transformation normally. The resumed path never called the provider once (a test asserts this by replacing the adapter with one that fails loudly if touched). And when I tried approving a second time out of curiosity, the engine answered like a strict accountant:

InvalidTransitionError: cannot resolve approval from status COMPLETED

Meanwhile the event stream told the whole truth to two independent observers: Redpanda’s watermark advanced by exactly the number of emitted events, and the SQLite index held the same twenty-six rows. Two stores, one reality, zero reconciliation drift.

Show Me the Diff: A Real Run

This is the loop Stratum optimizes for — a genuine end-to-end transformation, operator in the middle:

$ stratum run --repo ./example-repo \
    --task 'Change the greeting returned by hello.py from
            "Hello" to "Hello Stratum" and update the test accordingly.' \
    --file hello.py --file test_hello.py \
    --brokers 127.0.0.1:9092

Repository validated.

Planning...

PLAN
  1. [read_file] Read current hello.py (hello.py)
* 2. [write_file] Write updated hello.py ...       (* mutates repo)
  3. [run_command] Run tests ...

Approve plan for exe_mt4oq87s-kkrlpg49? [y/N] y

Executing...

  [+] read hello.py (86 bytes)
  [+] updated hello.py (94 chars)
  [+] `python -m pytest -q` exited 0

Task COMPLETED.
Execution ID: exe_mt4oq87s-kkrlpg49

And when you ask for the history back, replay folds the recorded events into a narrative — without calling the model, without touching the filesystem:

$ stratum replay exe_mt4oq87s-kkrlpg49 --brokers 127.0.0.1:9092
Execution exe_mt4oq87s-kkrlpg49
  Task:     Change the greeting ...
  Approval: granted by cli-operator
  [+] 18:01:38 read_file - read hello.py (86 bytes)
  [+] 18:01:38 write_file - updated hello.py (94 chars)
  [+] 18:01:38 run_command - `pytest -q` exited 0
  artifact: hello.py (94 bytes)
  Status:   COMPLETED

(26 events replayed; no AI calls, no side effects)

Trust, but Verify: The Negative Control

The question I respect most from skeptics is blunt: “how do I know a real model did the work and not a mock?” So the runtime ships with its own interrogation kit.

Every AI interaction emits ai.requested and ai.responded events carrying the endpoint host, the model id, the provider’s own request identifier, token usage, and latency. Query the store after any run and you will find records like chatcmpl-32ead6b1-… issued by Groq’s servers — cross-checkable against provider billing consoles.

And the decisive experiment takes ten seconds. Run any task with a bad key:

$ STRATUM_PROVIDER_API_KEY=bogus-key stratum run --repo ./demo-repo --task t

Task failed before approval: provider failed: provider returned 401:
{"error":{"message":"Invalid API Key",
"type":"invalid_request_error","code":"invalid_api_key"}}

A mock cannot make a remote service reject you. The deterministic test adapter exists strictly under tests/; production paths import only the httpx adapter. The acceptance suite additionally runs a vertical test against a live provider and a round-trip test against a live broker — the primary evidence path is real infrastructure, by design and by conviction.

The Stack, Briefly

Who Gets What

🛠️ Engineers Who Are Tired of Vibes
An agent whose claims are checkable: git diffs you can inspect, exit codes that were really earned, artifacts with hashes, and a replay that reconstructs any run without re-running it.
🏅 Teams Adopting Agentic Tooling
A governance story that survives an audit: who proposed what, who approved it, when, under which plan id — answerable from durable events rather than application-specific screens.
🔍 Skeptics and Auditors
Built-in interrogation: provider-issued request ids in the event store, a documented negative control, and an explicit separation between deterministic tests and live acceptance evidence.
🔧 Builders of the Next Runtime
Stable seams with one honest implementation each. Swap the provider, the broker, the store, or the transport without rewriting the spine — the interfaces are the scalable part.
💡 Lessons the Rebuild Taught Me
  • Representations seduce; consequences teach. Every hour spent polishing a projection of work was an hour not spent making work possible.
  • Approval must cost the system something. A gate the engine can route around is decoration. Put the guard in the code path and let the error messages do the preaching.
  • Mocks belong in unit tests, never in acceptance. The most valuable tests in the suite are the ones that would fail if a vendor changed their API tomorrow — because they actually call the vendor.
  • Failures are findings. A verification command that exits non-zero and gets recorded honestly is worth more than a green checkmark that was never at risk.

The Call to Action

The next wave of agentic software will be judged not by how fluently it narrates its intentions but by how verifiably it acts. The tools we adopt will need to answer, durably and without hand-waving: what did you do, who allowed it, and can you prove both?

Stratum is my answer — small enough to read in an afternoon, opinionated enough to refuse lying to you, extensible enough to grow real capabilities on top. It runs locally, speaks to the provider you already have, keeps its promises in a broker you control, and treats your approval as the most important event in the stream.

Clone it. Point it at a disposable repository. Give it a tiny task. Approve the plan. Then try to find something in the event store that did not happen — that search will come up empty, and that emptiness is the product.

⚡ Try Stratum

The full benchmark — planning against a live provider, your approval, real file edits, verification, Redpanda streaming, and replay — is one clone away:

git clone https://github.com/mailtotanvir/stratum.git
cd stratum

# configure any OpenAI-compatible provider
export GROQ_API_KEY="your-key"

# install + start broker + run the live acceptance suite
cd stratum && uv venv .venv --python 3.11
uv pip install -e '.[dev,api]' && source .venv/bin/activate
docker compose -f ../docker-compose.redpanda.yml up -d
STRATUM_KAFKA_BROKERS=127.0.0.1:9092 pytest

# transform a real repository, with you holding the gate
printf 'y\n' | stratum run --repo ./example-repo \
  --task 'Change the greeting returned by hello.py from "Hello" to "Hello Stratum".' \
  --file hello.py --file test_hello.py --brokers 127.0.0.1:9092

Star the repo, break the approval gate (please — I want to know how), and watch the receipts accumulate in Redpanda. The event stream never blinks.

Agentic Runtime Human-in-the-Loop Event Sourcing Redpanda / Kafka SQLite Local-First OpenAI-Compatible Replayable Execution Python asyncio Acceptance Testing