Give your AI agent a real memory in 50 lines of Python

Your AI agent is brilliant for exactly one conversation. Then it forgets everything.

It doesn’t remember that the user prefers dark mode. It can’t recall that it already solved this exact problem last Tuesday. It has no idea which approach worked and which one failed.

Most developers fix this by duct-taping a vector store, a Redis cache, and a conversation log together. That’s three services to maintain for something that should be built into the engine.

I wanted a single pip install that gives an AI agent the same three types of memory that cognitive science describes for humans: what it knows (semantic), what it experienced (episodic), and what it knows how to do (procedural). This distinction comes from Endel Tulving’s foundational work (1972) and Larry Squire’s taxonomy of memory systems (2004), and it has been successfully implemented in cognitive architectures like SOAR and ACT-R for decades.

So I built it into VelesDB.

The problem with “memory” in AI agents today

Most agent frameworks give you a flat list of messages or a vector store. That’s like giving a human a notebook and calling it memory.

Real memory has structure:

  • Semantic memory is your knowledge base. Facts, concepts, relationships. “Python is interpreted.” “The user’s timezone is UTC+1.”
  • Episodic memory is your autobiography. What happened, when, in what context. “Last Tuesday, the user asked about deployment and I suggested Docker.”
  • Procedural memory is your muscle memory. Skills, workflows, learned behaviors. “When the user asks about bugs, first check the logs, then reproduce the issue.”

LangChain gives you ConversationBufferMemory. That’s episodic at best, and it’s just a list. No semantic search, no temporal queries, no skill learning. If you want all three memory types, you’re stitching together Pinecone + Redis + Postgres – three services, three APIs, three failure modes.

Setup: one line

pip install velesdb

No Docker. No Redis. No Postgres. The entire engine – vector search, graph storage, and the agent memory SDK – ships as a ~3MB native binary inside the Python wheel.

A complete agent memory in 50 lines

Here’s a personal assistant that remembers facts about the user, recalls past conversations, and learns which approaches work:

import velesdb
import time
from sentence_transformers import SentenceTransformer

model = SentenceTransformer("all-MiniLM-L6-v2")
db = velesdb.Database("./agent_brain")
memory = db.agent_memory(384)

def embed(text):
    return model.encode(text).tolist()

# ---- Semantic: store facts the agent learns ----
memory.semantic.store(1, "User prefers dark mode in all applications", embed("User prefers dark mode in all applications"))
memory.semantic.store(2, "User's timezone is Europe/Paris (UTC+1)", embed("User's timezone is Europe/Paris (UTC+1)"))
memory.semantic.store(3, "User is building a RAG pipeline with LangChain", embed("User is building a RAG pipeline with LangChain"))

# ---- Episodic: record what happened during conversations ----
now = int(time.time())
memory.episodic.record(1, "User asked how to chunk PDFs for RAG", now - 86400, embed("User asked how to chunk PDFs for RAG"))
memory.episodic.record(2, "Suggested recursive text splitter, user said it worked", now - 3600, embed("Suggested recursive text splitter, user said it worked"))
memory.episodic.record(3, "User reported slow vector search on 50K vectors", now, embed("User reported slow vector search on 50K vectors"))

# ---- Procedural: learn skills from experience ----
memory.procedural.learn(1, "debug_slow_search", [
    "Check vector count and dimension",
    "Verify HNSW index is built",
    "Profile the query with timing",
    "Consider reducing top_k or adding filters"
], embed("debug slow vector search performance issues"), 0.85)

memory.procedural.learn(2, "help_with_rag", [
    "Ask about data source format (PDF, web, DB)",
    "Recommend chunking strategy based on source",
    "Suggest embedding model for their language",
    "Show how to store and search with VelesDB"
], embed("help user build a RAG retrieval pipeline"), 0.92)

That’s your agent’s brain. Persisted to disk, searchable, and it survives restarts. Now let’s use it.

Querying each memory type

Semantic: “What do I know about this user?”

# The agent needs context about the user before responding
facts = memory.semantic.query(embed("What is the user currently building?"), top_k=3)
for fact in facts:
    print(f"[{fact['score']:.2f}] {fact['content']}")
[0.32] User is building a RAG pipeline with LangChain
[0.11] User's timezone is Europe/Paris (UTC+1)
[0.10] User prefers dark mode in all applications

The agent now knows the user is working with LangChain and RAG before the user even mentions it in this session.

Episodic: “Have we dealt with this before?”

# User says: "My search is slow again"
similar_events = memory.episodic.recall_similar(embed("my vector search is slow"), top_k=2)
for event in similar_events:
    print(f"[{event['score']:.2f}] {event['description']}")
[0.76] User reported slow vector search on 50K vectors
[0.10] User asked how to chunk PDFs for RAG

The agent remembers this exact problem has happened before, with a strong similarity score. Instead of starting from scratch, it can reference past context.

You can also query by time:

# What happened in the last hour?
recent = memory.episodic.recent(limit=5)

# What happened before yesterday?
old_events = memory.episodic.older_than(before=int(time.time()) - 86400, limit=10)

Procedural: “What should I do here?”

# The user mentions slow search. The agent looks up its playbook.
procedures = memory.procedural.recall(embed("vector search is slow, need to debug performance"), top_k=1)
for proc in procedures:
    print(f"Procedure: {proc['name']} (confidence: {proc['confidence']:.0%})")
    for i, step in enumerate(proc['steps'], 1):
        print(f"  {i}. {step}")
Procedure: debug_slow_search (confidence: 85%)
  1. Check vector count and dimension
  2. Verify HNSW index is built
  3. Profile the query with timing
  4. Consider reducing top_k or adding filters

The agent has a learned playbook. And the best part: it gets better over time.

The feedback loop: agents that learn

After the agent follows a procedure and it works, reinforce it:

# The debug_slow_search procedure worked - boost its confidence
memory.procedural.reinforce(1, success=True)

If it failed:

# The procedure didn't help this time
memory.procedural.reinforce(1, success=False)

Confidence scores adjust automatically. Over time, the agent naturally favors procedures that have a track record of working. Confidence scores adjust automatically: +0.1 on success, -0.05 on failure, clamped to [0, 1]. It’s a deliberately simple mechanism. Over time, the agent naturally favors procedures that have a track record of working. The idea of reinforcing procedural knowledge through experience is inspired by cognitive architectures like SOAR and ACT-R, though VelesDB keeps the implementation pragmatic rather than trying to model the full complexity of human skill acquisition.

Real use case: a support agent that gets smarter

Here’s how these three memory types work together in a realistic scenario:

def handle_user_message(memory, message, model):
    """A support agent that uses all three memory types."""
    msg_vec = model.encode(message).tolist()

    # 1. What do I know about this user? (semantic)
    context = memory.semantic.query(msg_vec, top_k=3)

    # 2. Have we seen this problem before? (episodic)
    past = memory.episodic.recall_similar(msg_vec, top_k=3)

    # 3. What's my best playbook for this? (procedural)
    playbooks = memory.procedural.recall(msg_vec, top_k=1, min_confidence=0.5)

    # Build the prompt with real context
    prompt = f"User message: {message}nn"
    if context:
        prompt += "Known facts about this user:n"
        prompt += "n".join(f"- {c['content']}" for c in context) + "nn"
    if past:
        prompt += "Relevant past interactions:n"
        prompt += "n".join(f"- {p['description']}" for p in past) + "nn"
    if playbooks:
        p = playbooks[0]
        prompt += f"Recommended procedure ({p['name']}, {p['confidence']:.0%} confidence):n"
        prompt += "n".join(f"  {i+1}. {s}" for i, s in enumerate(p['steps']))

    return prompt

# Example
prompt = handle_user_message(memory, "My vector search is really slow", model)
print(prompt)

The agent doesn’t just answer the question. It brings the full context: who the user is, what happened before, and what to do about it. That’s the difference between a chatbot and an assistant.

What this replaces

What you need Without VelesDB With VelesDB
Semantic memory Pinecone/Chroma + custom metadata memory.semantic.store()
Episodic memory Redis/Postgres + timestamp queries memory.episodic.record()
Procedural memory Custom code + JSON files memory.procedural.learn()
Infrastructure 3 services, 3 APIs, Docker pip install velesdb
Disk footprint 500MB+ Docker images ~3MB binary, data files only

Limitations (being honest)

VelesDB is a single-node embedded database. It’s not a replacement for a distributed system.

  • Scaling: if your agent needs to handle millions of memories across multiple machines, use a client-server vector database
  • Multi-process: VelesDB locks the database directory, so one process at a time (fine for most agent use cases)
  • Embedding model: you bring your own, VelesDB stores and searches vectors but doesn’t generate them

For the 90% of agent projects that run on a single machine with thousands to hundreds of thousands of memories, this is the entire backend.

Getting started

pip install velesdb
import velesdb

db = velesdb.Database("./agent_brain")
memory = db.agent_memory(384)  # dimension of your embedding model

# Store a fact
memory.semantic.store(1, "User likes Python", [0.1, 0.2, ...])  # 384-dim vector

# Record an event
memory.episodic.record(1, "User asked about Python", int(time.time()), [0.1, ...])

# Learn a procedure
memory.procedural.learn(1, "help_with_python", ["Check version", "Suggest docs"], [0.1, ...], 0.8)

Full docs: velesdb.com/en
GitHub: github.com/cyberlife-coder/VelesDB
Python SDK (MIT): pypi.org/project/velesdb

References

The Agent Memory SDK is grounded in decades of cognitive science research:

  • Tulving, E. (1972). Episodic and Semantic Memory. In E. Tulving & W. Donaldson (Eds.), Organization of Memory (pp. 381-403). Academic Press. The foundational paper that introduced the distinction between semantic memory (general knowledge) and episodic memory (personal experiences).
  • Squire, L.R. (2004). Memory systems of the brain: A brief history and current perspective. Neurobiology of Learning and Memory, 82(3), 171-177. The modern taxonomy that maps declarative (semantic + episodic) and nondeclarative (procedural) memory to distinct brain systems.
  • Anderson, J.R. (1996). ACT-R: A Theory of Higher Level Cognition. Human-Computer Interaction, 12(4), 439-462. The cognitive architecture that models how declarative knowledge compiles into procedural skills through practice. VelesDB’s procedural.reinforce() borrows the core idea (procedures gain or lose confidence with experience) while keeping the implementation simple and predictable.
  • Laird, J.E., Newell, A., & Rosenbloom, P.S. (1987). SOAR: An Architecture for General Intelligence. Artificial Intelligence, 33(1), 1-64. The first cognitive architecture to unify problem-solving, learning, and multiple memory systems in a single agent framework.

I’m building VelesDB as a source-available local-first database for AI applications. If you’re working on AI agents and struggling with memory management, I’d like to hear about your setup. What are you currently using to persist agent state between sessions?

April 2 – AI, ML and Computer Vision Meetup

Join us on April 2 at 9 AM Pacific for the monthly AI, ML and Computer Vision Meetup!

Register for the Zoom

Talks will include:

  • Visual AI at the Edge: Beyond the Model – David Moser at Orella Vision
  • Sanitizing Evaluation Datasets: From Detection to Correction – Nick Lotz at Voxel51
  • Async Agents in Production: Failure Modes and Fixes – Meryem Arik at Doubleword
  • Building enterprise agentic systems that scale – Aman Sardana at Cisco

I Built an All-in-One Local AI App — Chat, Image Gen, and Video Gen in One UI

Last year my desktop looked like a disaster zone. I had an Ollama terminal running in one corner, ComfyUI open in Firefox with like 14 tabs of different workflows, a separate janky video generation tool I found on GitHub that only worked half the time, and a text file where I kept track of which models I had downloaded. Alt-tabbing between all of this was my entire workflow.

One night I was trying to generate an image, refine a prompt with a chat model, then feed that into a video gen pipeline, and I just… stopped. Closed everything. Opened VS Code instead.

I thought: why isn’t there just ONE app for all of this?

So I built one. It’s called Locally Uncensored, and it’s completely open source.

The Problem Nobody Talks About

The local AI space is incredible right now. Ollama made running LLMs stupid easy. ComfyUI is a powerhouse for image generation. There are video gen tools popping up every week.

But they’re all separate things. Separate UIs. Separate configs. Separate browser tabs. Separate terminal windows.

If you’re just casually chatting with an LLM, sure, the Ollama CLI is fine. But the second you want to do anything more — switch personas, generate an image, manage your models — you’re juggling apps like a circus act.

I wanted one window. One UI. Everything local. Everything offline. No API keys, no subscriptions, no data leaving my machine.

What I Actually Built

Locally Uncensored is a desktop-first web app that wraps Ollama and ComfyUI into a single, clean interface. You get:

  • Chat with any Ollama model, with 25+ built-in personas (coding assistant, creative writer, debate partner, you name it — and yeah, you can make your own)
  • Image generation through ComfyUI, without needing to touch ComfyUI’s node editor
  • Video generation in the same UI
  • A model manager so you can pull, delete, and switch models without touching the terminal
  • Dark and light mode because I’m not an animal

Everything runs 100% on your machine. No cloud. No telemetry. No “we updated our privacy policy” emails.

The Tech Stack (for the curious)

I went with React 19 and TypeScript because, honestly, that’s what I know best and I wanted to move fast. Tailwind CSS 4 for styling — I know some people have opinions about Tailwind but it let me iterate on the UI incredibly quickly without context-switching to separate CSS files. Vite 8 for the build tooling because life’s too short for slow dev servers.

The backend talks to Ollama’s API directly and integrates with ComfyUI’s API for image and video generation. No middleware server, no extra layers. It’s as thin as I could make it.

One thing I’m genuinely proud of is how snappy it feels. I’ve used a lot of local AI UIs that feel sluggish or over-engineered. I wanted this to feel like a native app, not a web page pretending to be one.

The Persona System (My Favorite Feature)

Okay, I have to talk about this because it’s the feature that made me actually enjoy using local models day-to-day.

Most chat UIs give you a system prompt box and call it a day. I wanted something more structured. Locally Uncensored ships with 25+ personas out of the box — things like a Python tutor, a sarcastic code reviewer, a Socratic philosophy partner, a creative writing coach.

But the real magic is that switching between them is instant. Mid-conversation, I can go from talking to a coding assistant to switching over to a creative writing persona without opening a new tab or starting a new session. It sounds small but it completely changed how I interact with local models.

And of course you can create your own. I have a custom one that’s basically “grumpy senior engineer who reviews my pull requests” and it’s genuinely caught bugs in my code. Would recommend.

Why “Uncensored”?

I get asked about the name. It’s not about being edgy. It’s about the principle that when you run AI locally, on your own hardware, you should have full control. No corporate content filters deciding what you can and can’t ask your own computer. That’s the whole point of local AI.

You pick the models. You set the boundaries. Your machine, your rules.

The Model Manager Saved My Sanity

Before this, I had a sticky note (a real physical one, stuck to my monitor) with a list of models I’d downloaded through Ollama. Which ones were good for code. Which ones were better for creative writing. Which ones were too big for my GPU.

The model manager in Locally Uncensored shows you everything you have installed, lets you pull new models, delete old ones, and see their sizes at a glance. It’s not revolutionary technology — it’s basically a nice UI over ollama list and ollama pull — but it’s one of those things where having it RIGHT THERE in the same app as your chat makes everything feel cohesive.

No more terminal tab just for model management. No more forgetting what you have installed.

What I Learned Building This

A few things that surprised me:

Ollama’s API is really well designed. Streaming responses, model management, it’s all clean and straightforward. Made my life way easier than I expected.

ComfyUI’s API is… not. I love ComfyUI and what it can do, but integrating with it programmatically was a journey. The workflow-based API is powerful but requires you to basically construct node graphs in JSON. I spent way too many late nights debugging why my image gen requests were silently failing. If you’re thinking about building on top of ComfyUI, budget extra time for this.

People really want local AI to be easier. I posted an early version in a few Discord servers expecting maybe 10 people to try it. The response was way beyond what I anticipated. Turns out a LOT of people are in the same boat — they want to run AI locally but the tooling fragmentation is a real barrier.

It’s Open Source, Go Break It

The whole thing is on GitHub: https://github.com/PurpleDoubleD/locally-uncensored

MIT licensed. Clone it, fork it, rip it apart, rebuild it, I don’t care. If you find a bug, open an issue. If you want to add a feature, PRs are welcome.

You’ll need Ollama installed for chat and a ComfyUI instance running for image/video gen. The README walks through setup — it’s pretty straightforward if you’ve already got those tools running.

What’s Next

I’m actively working on this. Some things on my radar:

  • Better ComfyUI workflow templates so you don’t need to configure anything for basic image gen
  • RAG support for chatting with your own documents
  • More personas (always more personas)
  • Possibly an Electron wrapper for a true desktop app experience

But honestly, the roadmap is shaped by whoever shows up and uses it. If you try it and think “man, I wish it did X” — tell me. Open an issue. That’s how the best features have been added so far.

TL;DR

I got tired of running five different tools to use local AI. So I built one app that does chat, image gen, and video gen in a single UI. It’s called Locally Uncensored, it runs 100% offline, and it’s open source.

Give it a spin if you’re drowning in AI tool tabs like I was. And if you hate it, at least you’ll have a clean starting point to build something better.

🔗 GitHub — Locally Uncensored

Stop Writing Tests First — Write Formal Specs. Let AI Agent Teams Build Your System.

The Problem Nobody’s Talking About

Everyone’s excited about AI-powered coding. GitHub Copilot autocompletes your functions. Claude and GPT-4 generate entire modules from prompts. But here’s the uncomfortable truth: we’re still thinking about AI as a single tool, when the real paradigm shift is multiple AI agents working as a team.

And that raises a question nobody’s answering well:

When three AI agents are building three modules in parallel, what prevents them from producing code that doesn’t fit together?

Natural language specs? Too ambiguous. One agent interprets “the order must be valid” differently from another. Test-driven development? Tests are inductive — they verify specific cases, not the contract between modules. You can have 100% coverage on each module and still watch the system crash at integration.

What If Agents Had Blueprints?

Think about how a construction project works. You don’t hand three subcontractors a paragraph of prose and hope for the best. You give them engineering drawings — precise, unambiguous documents that define every interface: where the plumbing connects to the electrical, what load the foundation bears, which walls are structural.

Formal specifications are the engineering drawings of software. And a method called VDM-SL (Vienna Development Method – Specification Language), originally from IBM in the 1970s, turns out to be nearly ideal for AI agents to read, write, and reason about.

Here’s why it matters for multi-agent development:

Module A (Order)                Module B (Inventory)
┌─────────────────┐            ┌─────────────────┐
│                  │            │                  │
│  PlaceOrder()    │            │  ReserveStock()  │
│  post:           │──contract──│  pre:            │
│   order.status   │            │   productId ∈    │
│    = <CONFIRMED> │   A.post   │    dom inventory │
│   ∧ stock        │    ⇒       │   ∧ quantity > 0 │
│    reserved      │   B.pre    │   ∧ available ≥  │
│                  │            │     quantity     │
└─────────────────┘            └─────────────────┘

A’s postcondition implies B’s precondition. This is mechanically verifiable. No ambiguity. No “alignment meetings” between agents. No integration surprises.

A Concrete Example

Here’s what a VDM-SL specification for an order module actually looks like:

module Order

types
  OrderId = nat1;
  ProductId = nat1;

  LineItem :: productId : ProductId
             quantity : nat1
             unitPrice : nat;

  OrderStatus = <PENDING> | <CONFIRMED> | <COMPLETED> | <CANCELLED>;

  Order :: orderId    : OrderId
           customerId : nat1
           lineItems  : seq of LineItem
           status     : OrderStatus
           totalAmount : nat;

state OrderStore of
  orders    : map OrderId to Order
  nextOrderId : nat1
inv mk_OrderStore(orders, nextOrderId) ==
  nextOrderId > 0
  and forall id in set dom orders & orders(id).orderId = id

operations

CreateOrder: nat1 * seq of LineItem ==> OrderId
CreateOrder(customerId, items) ==
  let id = nextOrderId in (
    orders := orders ++ {id |-> mk_Order(id, customerId, items,
                          <PENDING>, SumItems(items))};
    nextOrderId := nextOrderId + 1;
    return id
  )
pre items <> []
post RESULT = nextOrderId~ - 1
     and RESULT in set dom orders
     and orders(RESULT).status = <PENDING>;

Notice what this gives you:

  • Types with invariants — nat1 means positive integers only. No null, no zero.
  • State invariant — the inv clause is always true. Every order ID in the map matches the order’s own ID.
  • Pre-conditions — items <> [] means you can’t create an empty order. This is a structural guarantee, not a test case.
  • Post-conditions — after CreateOrder, the order exists in the store with status PENDING. Any agent reading this knows exactly what to expect.

An AI agent building the inventory module doesn’t need to see the Order module’s implementation. It only needs the interface contract — the types and operation signatures with pre/post conditions. That’s a fraction of the context window.

The Multi-Agent Architecture

Here’s the workflow we propose:

Phase 1: Human + Architect AI (1-3 days)

The human (domain expert) and an architect AI collaborate to define:

  • System-wide VDM-SL specification
  • Module decomposition
  • Interface contracts between modules (the A.post ⇒ B.pre relationships)

The human doesn’t need to read VDM-SL. The AI explains the spec in natural language: “This says a customer can’t place an order unless they have at least one item, and after the order is placed, inventory is reserved. Does that match your business rules?”

Phase 2-4: Parallel Module Agents

Each module gets its own AI agent. Agent A builds Order. Agent B builds Inventory. Agent C builds Payment. In parallel.

Each agent’s context contains only:

  • Its own module spec (~200-500 lines of VDM-SL)
  • Interface contracts of dependent modules (~50-100 lines each)
  • Cross-cutting decisions from Phase 1

No agent needs another agent’s implementation code. This fits comfortably in current context windows.

Integration Verification

A dedicated integration agent checks A.post ⇒ B.pre across all module boundaries. It never reads implementation code — only interface specs. If Order’s ConfirmOrder postcondition guarantees stock_reserved = true, and Inventory’s ShipOrder precondition requires stock_reserved = true, the composition is verified.

┌──────────────────────────────────────────────────┐
│            Phase 1: Architecture                  │
│     Human + Architect AI                          │
│     → System spec + module decomposition          │
│     → Interface contracts (A.post ⇒ B.pre)        │
└────────────┬─────────────┬───────────────┬───────┘
             │             │               │
             ▼             ▼               ▼
     ┌──────────┐  ┌──────────┐   ┌──────────┐
     │ Agent A  │  │ Agent B  │   │ Agent C  │
     │ Order    │  │Inventory │   │ Payment  │
     │ Module   │  │ Module   │   │ Module   │
     └─────┬────┘  └────┬─────┘   └────┬─────┘
           │             │               │
           ▼             ▼               ▼
     ┌──────────────────────────────────────────┐
     │      Integration Verification Agent       │
     │   Checks A.post ⇒ B.pre across all       │
     │   module boundaries (spec-level only)     │
     └──────────────────────────────────────────┘

Why Not Just Use Natural Language Specs?

Three reasons:

1. Ambiguity scales exponentially. With 2 modules and natural language specs, you have a manageable amount of misinterpretation risk. With 10 modules and 45 pairwise interactions, the ambiguity explodes. Formal specs eliminate this at the root — nat1 means one thing, everywhere, always.

2. Verification becomes mechanical. You can’t machine-check whether “the order should be valid” in one module agrees with “valid orders have at least one item” in another. But you can machine-check whether Order.ConfirmOrder.post ⇒ Inventory.ReserveStock.pre.

3. Context windows stay small. Each agent needs only its module spec + dependency interfaces — not the full natural-language requirements document that keeps growing. A 10-module system might have 200 pages of natural language specs but only 50 lines of interface contract per module boundary.

Why TDD Doesn’t Solve This

This is the part that might get me some angry comments, but hear me out.

Dijkstra said it in 1969:

“Program testing can be used to show the presence of bugs, but never to show their absence.”

TDD is inductive reasoning: you verify specific inputs and hope they generalize. Formal specification is deductive reasoning: you state what must always be true, then derive implementations that satisfy those conditions.

For a single developer, TDD is a useful discipline. For multi-agent AI development, it fails structurally:

Aspect TDD Formal Specs
Reasoning Inductive (specific → general) Deductive (general → specific)
Inter-agent contract Implicit in shared tests Explicit in interface specs
Verification Run tests, hope for the best Mechanical proof of composition
Context per agent Needs test suite + code + shared state Needs only interface contracts
Scales with agents Coordination overhead grows Contracts remain verifiable

Note: we’re not saying “never write tests.” Tests still have a role — integration tests for external systems, performance tests, E2E UI tests. What we’re saying is: tests are no longer the center. Formal specifications are.

The Human Role: Not Obsolete, Elevated

This paradigm doesn’t remove humans. It redefines what humans do:

Before: Write code, write tests, review code, debug integration failures.

After:

  • Domain expert — “This is how our business works. These are the rules.”
  • Architecture decision maker — “Split the system into these modules. This module depends on that one.”
  • Quality judge — “The AI says the spec means X. Does X match what we actually need?”

You don’t need to read VDM-SL. You don’t need to write code. You need deep domain knowledge and the ability to ask sharp questions when the AI explains its specifications back to you.

The Open-Source Framework

We’ve packaged this entire methodology as an open-source project:

formal-spec-driven-dev — Apache 2.0 licensed

What’s included:

📄 Foundational paper (Japanese + English) — the full theoretical argument, ~30 min read

📋 VDM-SL templates — module template, interface contract template. Copy, customize, go.

🤖 AI prompt templates for all 4 phases:

  • Phase 1: Specification dialogue (elicit requirements → VDM-SL)
  • Phase 2: Technical design (VDM-SL → architecture decisions)
  • Phase 3: Implementation (VDM-SL → production code)
  • Phase 4: Verification (code ↔ spec cross-check)

🔧 Multi-agent orchestration:

  • agent-config.yaml — role definitions, context requirements, outputs
  • workflow.md — step-by-step guide to running multi-agent development today

📦 Working example: E-commerce order system with 3 modules (Order, Inventory, Payment), complete with VDM-SL specs, integration verification, and cross-module contracts.

Try It in 30 Minutes

  1. Clone the repo
  2. Open templates/prompts/phase1-specification.md
  3. Copy the system prompt into Claude or GPT-4
  4. Start describing a module from your own project
  5. Watch the AI produce a VDM-SL spec, then explain it back to you in plain language

That’s it. No VDM-SL expertise required. The AI reads and writes the formal notation. You verify the meaning.

What We Need from the Community

This is early-stage. The methodology is sound, but the tooling ecosystem needs work:

  • Domain templates — Healthcare, fintech, logistics, IoT. Each domain has its own patterns. We need VDM-SL templates for common domain models.
  • Orchestration tooling — Better ways to coordinate multiple AI agents with shared specifications. Integration with existing agent frameworks.
  • Case studies — Real teams trying this on real projects. What worked? What broke? Where are the rough edges?
  • Verification automation — Tools that automatically check A.post ⇒ B.pre across module boundaries.

If any of this resonates, check out the repo and open an issue. PRs welcome. Case study reports are especially valuable — even “I tried this and it didn’t work because…” helps enormously.

The Bottom Line

The era of single-agent AI coding is already giving way to multi-agent development. The unsolved problem is coordination. Natural language specs create ambiguity that scales exponentially with the number of agents. Tests verify specific cases but can’t guarantee compositional correctness.

Formal specifications — specifically, VDM-SL with pre/post conditions and invariants — provide what multi-agent development needs: unambiguous, mechanically verifiable contracts between agents.

The human role doesn’t disappear. It elevates. You become the domain expert and architect who ensures the AI team builds the right thing. The specs ensure they build it correctly.

Hikaru Ando — IID Systems
GitHub: formal-spec-driven-dev | Apache 2.0

This article is also available in Japanese (日本語).

Before the Internet

Before the Internet

Remembering a World Without WiFi

It’s hard to imagine life before the internet, especially for developers. Our days are shaped by push notifications, cloud deployments, and Stack Overflow tabs. But not so long ago, coding—and life in general—looked very different. No online documentation, no GitHub repositories, and definitely no memes about CSS selectors.

So, what did the world look like for developers before the internet connected us all? Let’s dive into some key aspects of this pre-web era: where code lived, how knowledge spread, what collaboration looked like, and how creativity thrived despite (or because of) the limitations.

Code Was Physical

When you’re used to deploying to Heroku with a single command, it’s wild to think code was once something you could hold in your hands.

Here’s how coding worked:

  • Paper First: Code was written out by hand. Developers (or, let’s be honest, sometimes their assistants) would draft programs on paper before typing them into a terminal or feeding them into a punch card machine.
  • Punch Cards: You’d take your handwritten code, convert it to punch cards—a stack of cardboard rectangles, each representing a line of code. Drop them off at the computer lab and wait for results.
  • Physical Floppies: In later years, code was traded on floppy disks. Want to share your program? Hand someone a disk. No downloads. No git clone. Just a literal exchange.

Example:

Imagine debugging a program, but instead of console logs, your output is printed on paper after a batch run. If you made a typo, you’d have to fix the card, re-run the batch, and wait (sometimes overnight) for the next output. Efficiency wasn’t exactly the name of the game.

Learning Was Local and Slow

Today, you can find answers to just about any programming question online in seconds. Back then, learning was a marathon, not a sprint.

  • Books and Manuals: The bible for any programmer was the official manual. These were thick tomes you kept nearby. If you didn’t own it, you’d check the library—or borrow from a friend.
  • User Groups: Local meetups mattered. Programmers would gather after work to swap tips, share sample code, and commiserate over bugs. Some of these groups became the backbone of early tech communities.
  • Magazines: Monthly magazines like BYTE or Dr. Dobb’s Journal were packed with articles, code listings, and news. If you wanted to try a program, you might type it out from a magazine—character by character.

Example:

Picture trying to learn a new programming language, but the only resources are a handful of books and the occasional magazine article. If you hit a wall, your options are: ask your local group, write a letter to the magazine (and wait months for a reply), or brute-force your way through.

Collaboration Was a Challenge

Working on a team project? Today, you spin up a repo, push commits, and track issues online. Before the internet, collaboration was a logistical puzzle.

  • Sharing Code: Teams exchanged physical disks or tapes. There was no version control—just hope everyone kept track of changes.
  • Email (Eventually): For a while, email was limited to academic or corporate networks. Even then, sending files was clunky and unreliable.
  • Face-to-Face Meetings: The best way to sync was in person. Developers crowded around a terminal, debated algorithms, and made edits together. Remote work wasn’t just rare—it was nearly impossible.

Example:

Imagine two developers working in different cities. They might mail each other disks containing the latest version. If something went wrong, they’d discuss over the phone, maybe even travel to meet up and debug together. Every collaboration was slow, but deeply personal.

Creativity Despite Constraints

It’s easy to think limitations held people back, but the opposite was often true. Constraints sparked creativity.

  • Optimized Code: Memory and storage were expensive. Developers wrote lean, clever code because they had to. Tricks like loop unrolling or bit manipulation were everyday practices.
  • Homebrew Solutions: Need a tool? Build it yourself. Programmers routinely crafted utilities, debuggers, and even games, sharing them with local groups.
  • Community Sharing: Early software was freely traded. The spirit of open source started here, even if licenses weren’t formalized.

Example:

Let’s say you want to create a game. With limited documentation and hardware, you invent your own graphics routines, squeeze every byte, and share the result at a local user group. That feedback loop was slower but uniquely rewarding.

What We’ve Gained—and Lost

The internet brought speed, convenience, and democratized access. Today’s developer can learn, build, and deploy faster than ever. But something got left behind: the tactile satisfaction of physical code, the face-to-face camaraderie, and the creative necessity born from scarcity.

Modern perks:

  • Instant answers from forums and documentation
  • Easy code sharing and collaboration
  • Version control and backups

Old-school strengths:

  • Deep focus without distractions
  • Strong local communities
  • Resourcefulness and ingenuity

Conclusion: A Different Kind of Connection

Before the internet, developers were explorers, not just consumers. Every bug fixed, every program traded, every meet-up attended was an adventure. We gained speed and scale with the web, but lost some of the intimacy and resourcefulness that defined early programming.

Maybe next time you’re frustrated with a slow build or a tricky bug, pause to appreciate how far we’ve come—and how much creativity was sparked by the challenges of yesterday. The internet changed everything, but the spirit of discovery is timeless.

Opus 5 Is Coming – Is Your Code Governance Ready?

Anthropic accidentally leaked details of their most powerful model ever built. The implications for teams using AI coding agents are significant — and most aren’t prepared.

What We Know About Claude Mythos / Opus 5

This week, Fortune reported that Anthropic acknowledged testing a new AI model that represents a “step change” in capabilities. Internal documents describe it as scoring “dramatically higher” than Opus 4.6 in coding, reasoning, and cybersecurity.

The details that matter for engineering teams:

  • It’s reportedly a 10 trillion parameter model
  • Anthropic says it’s “very expensive for us to serve, and will be very expensive for our customers to use”
  • Early access is restricted to cybersecurity firms to “help cyber defenders prepare”
  • They’re taking a “slower, more gradual approach to releasing Mythos than we have with our other models”

When the model maker restricts access because of security concerns, that tells you something.

Why This Matters for Your Codebase

Here’s the progression over the past 48 hours:

March 26: Claude Code ships auto-fix and auto-merge. Your AI agent can now fix CI failures and merge PRs autonomously.

March 27: Vercel open-sources OpenReview, a Claude-powered code review bot. AI reviewing AI-generated code becomes commoditized.

March 27: Anthropic confirms Opus 5 exists and is too powerful to release without restrictions.

Connect the dots: models are getting dramatically more powerful, agents are getting more autonomous, and review tools are proliferating. The one thing that isn’t keeping pace is governance — the layer that decides what actually ships to production.

The Governance Gap Is Accelerating

A year ago, the AI coding workflow looked like this:

Developer prompts AI → AI suggests code → Developer reviews and edits → Developer commits

Today it looks like this:

AI agent writes code → AI agent opens PR → AI reviewer checks it → Auto-merge if CI passes

The human went from being the author and reviewer to being… optional. That works fine when the AI is writing a simple utility function. It becomes a problem when it’s rewriting your authentication middleware or refactoring your payment pipeline.

And with Opus 5, the code will look even more correct. It will pass more tests. It will follow more patterns. It will be harder to distinguish from expert human code. Which means the failure modes become more subtle and more dangerous.

What Risk-Proportional Governance Looks Like

The solution isn’t to slow down — it’s to be smarter about what gets human attention.

Every PR that enters your codebase should be evaluated on multiple dimensions before a merge decision is made:

Risk scoring across dimensions. Not just “did tests pass” but how complex is this change, what’s the security surface area, how many files does it touch, are there breaking changes, and where are the test coverage gaps.

Agent-aware analysis. Knowing which AI tool authored the code matters. Each agent has a different risk profile based on its track record in your codebase. A Dependabot version bump from an agent with 100 safe merges is very different from a new agent’s first PR touching your database schema.

Trust that’s earned, not assumed. AI agents should start with limited autonomy and earn more as they prove reliable. The same way you wouldn’t give a new hire production merge access on day one.

Proportional response. Low-risk PRs from trusted agents auto-merge. Medium-risk gets lightweight review. High-risk gets full human analysis with escalation to designated reviewers.

Preparing for the Next Generation

When Opus 5 becomes generally available and developers start using it to write production code, the teams that will be fine are the ones that already have governance infrastructure in place:

  • Automated risk scoring on every PR, regardless of source
  • Agent detection that tracks which model and tool generated each change
  • Trust scores that reflect actual performance in your specific codebase
  • Approval workflows that trigger based on risk, not just author type
  • Audit trails that show exactly what was merged, by which agent, with what risk score

The teams that will struggle are the ones still relying on “the tests passed, ship it.”

The Bottom Line

Opus 5 isn’t a threat to developers — it’s a tool that will make them dramatically more productive. But productivity without governance is just velocity without direction.

The review process that worked when humans wrote all the code doesn’t work when AI writes 41% of it. And it definitely won’t work when the next generation of models makes that number 60%, 70%, or higher.

The time to build your governance pipeline is before you need it, not after a production incident forces your hand.

I’m building MergeShield to solve exactly this — risk scoring, agent trust, and auto-merge governance for GitHub teams. You can explore the interactive demo without signing up, or install the GitHub Action to try it on your repos.

Porting Test Drive II from SNES to PC, Part 39: Closing the No-Opponent Handoff Into 02:9016

SNES Select Opponent clock slot

This checkpoint closes the last front-end ambiguity in the SNES Select Opponent flow.

The fourth slot was already structurally solved as the stopwatch / clock path. What remained open was whether that path actually promoted into the same downstream gameplay corridor as the regular rival-car choices, or whether it diverged somewhere after 01:BE43.

SNES Select Opponent clock slot

What is now recovered

A callback-relative probe keyed to the first live 01:C1D2 hit, plus a later be43+17-22:start confirm, now recovers the no-opponent path organically through:

  • L00C20B -> 01:C1D2 -> L00BE76 -> 01:BE43 -> L008B87 -> 01:902D -> 01:9111 -> active_main = 02:9016

The important part is that this is not a forced callback lane. It is an organic input-driven path recovered from boot with timing tied to the live callback surface.

The state difference survives the shared handoff

The regular rival baseline and the no-opponent clock path now reach the same later corridor at the same timestamps, but they do not collapse to the same state.

  • Rival baseline keeps $1C70 = 0, $1C76 = 1
  • Clock / no-opponent path keeps $1C70 = 3, $1C76 = 0

That difference is still visible when both runs have already reached the shared downstream path and later active_main = 02:9016.

SNES Select Opponent selection box

Why this matters

This narrows the remaining question sharply.

The open problem is no longer:

  • how to select the fourth slot
  • how to confirm it
  • whether it reaches the gameplay handoff corridor at all

The open problem is now much better:

  • what $1C76 = 0 changes after both paths have already converged to the shared 02:9016 corridor

That is a much more tractable archaeology target, because it pushes the search away from menu timing drift and into direct post-handoff behavior.

Artifacts

Main promoted artifacts for this checkpoint:

  • tools/out/snes_select_opponent_no_opponent_organic_path.json
  • tools/out/snes_select_opponent_no_opponent_organic_path.md
  • snes_play_session_gate.json
  • snes_selection_state_contract.json
  • docs/snes_dos_correlation.md
  • docs/snes_unknowns.md

Next gate

The next step is to capture and compare the first post-02:9016 gameplay-facing window for:

  • default rival path
  • no-opponent clock path

At this point, the front-end path itself is no longer the blocker. The blocker is identifying the first runtime-visible effect of the preserved no-opponent state.

Stop Sending Every PDF Page to a VLM: A Parser-First Document AI Pattern with LiteParse

Most Document AI teams are overusing VLMs.

The default pattern still looks like this:

  1. take a PDF
  2. send the whole thing to a big multimodal model
  3. hope the output is good enough
  4. patch the failures later

That works for demos. It is usually the wrong pattern for production.

I have been testing a different approach: parser first, validation second, VLM escalation only when needed.

One of the cleanest tools I have used for that pattern recently is LiteParse.

In this tutorial, I will show:

  • why parser-first pipelines matter
  • what LiteParse is actually useful for
  • the result I got from a real PDF
  • how to use it in a practical Document AI pipeline
  • when to escalate to a stronger VLM instead of parsing everything blindly

Why parser-first pipelines matter

A lot of teams treat document understanding like a single-model problem.

In practice, it is usually a systems design problem.

The important question is not only:

Which model reads documents best?

The more useful question is:

Which pages actually need an expensive model, and which ones can be handled by a faster structural parser with better auditability?

That distinction matters because production document workflows care about more than extraction quality alone:

  • cost
  • latency
  • routing
  • failure reviewability
  • deterministic validation
  • operational visibility

If a parser can already recover structure and geometry from most pages, then the VLM should become an exception handler, not the default engine.

That is the lens I used when testing LiteParse.

What LiteParse is good at

LiteParse is useful when you need more than plain extracted text.

Instead of treating a PDF as a blob of text, it gives you a more useful intermediate representation:

  • page-level structure
  • spatial regions
  • bounding-box style geometry
  • text blocks that can be routed, inspected, and validated

That matters because geometry is often the missing layer in Document AI systems.

Once you have it, you can do things like:

  • validate whether expected fields are even present in the right area
  • compare layouts across templates
  • flag unusual pages before extraction
  • build escalation logic for hard pages
  • preserve evidence for human review

In other words, the parser output becomes part of your control plane.

My test result on a real PDF

I used LiteParse on a real enterprise-style PDF workflow and got a surprisingly strong baseline.

Result

  • 8-page PDF
  • parsed in about 1 second locally
  • 1,330 spatial text boxes recovered
  • 210 text regions on page 1 alone

That is the kind of result that changes how you think about pipeline design.

The interesting part was not only speed.

The more important insight was this:

Once you can recover geometry and text regions this cheaply, the value shifts from “bigger model first” to “better routing and validation first.”

That is a much more production-friendly design principle.

Install LiteParse

A simple starting point is:

npm install @llamaindex/liteparse

From there, the main workflow is straightforward:

  1. load a PDF
  2. parse it into structured output
  3. inspect page regions and text blocks
  4. decide whether the page is “easy” or “hard”
  5. only escalate hard pages to a heavier OCR/VLM path

A practical parser-first workflow

Here is the architecture pattern I would recommend.

Step 1: Parse the PDF first

Run LiteParse against the full document and capture:

  • page objects
  • spatial blocks
  • text output
  • per-page structure

At this stage, you are not trying to solve everything.

You are building a cheap structural understanding layer.

Step 2: Validate structure before extraction

Before asking a larger model to reason over the document, ask simpler questions:

  • Is the layout close to what I expect?
  • Are key sections present?
  • Are there obvious anomalies in page density or missing blocks?
  • Are there template shifts that will likely break rule-based extraction?

This is where parser-first systems become much stronger than “model-first everything.”

You are no longer blind.

Step 3: Escalate only hard pages

This is the key move.

Do not treat every page equally.

Escalate only when:

  • the layout is unusual
  • the parser output is sparse or fragmented
  • important fields are missing
  • page geometry suggests ambiguity
  • downstream validation fails

That gives you a better architecture:

  • cheap parser for easy pages
  • stronger model only for exception handling

This reduces cost and increases operational clarity.

Step 4: Preserve page-level evidence

One of the biggest production mistakes in Document AI systems is losing the intermediate evidence.

Do not throw it away.

Keep:

  • parsed regions
  • page-level overlays
  • validation summaries
  • escalation reasons

That evidence helps you:

  • debug extraction failures
  • explain model decisions
  • review pipeline drift
  • improve routing policies over time

Why this matters more than another benchmark

There is a broader takeaway here.

A lot of discussion in OCR and VLM tooling is still framed like a model race:

  • which model is newest
  • which benchmark is highest
  • which release is most impressive

That framing misses the real engineering problem.

In production, the real leverage often comes from:

  • better orchestration
  • better intermediate representations
  • better failure visibility
  • better escalation rules

That is why LiteParse stood out to me.

It is not just “another parser.”

It helps expose a more useful design pattern:

parse first, validate structure, escalate selectively, keep evidence

That pattern is much closer to how robust enterprise document systems should be built.

Where I would use this pattern

I would use this parser-first architecture for:

  • loan or payslip workflows
  • invoice and financial document routing
  • document intake pipelines
  • layout anomaly detection
  • OCR failure triage
  • pre-VLM gating for enterprise document systems

It is especially useful when:

  • cost matters
  • latency matters
  • auditability matters
  • document templates vary but not completely randomly

A simple mental model

If I had to summarize the LiteParse lesson in one line:

The next Document AI moat is often not a bigger model. It is knowing when you do not need one.

That is the shift.

Parser-first pipelines give you:

  • faster first-pass understanding
  • better structure visibility
  • cheaper routing
  • more explainable failures

And that is usually more valuable than sending every page to the biggest model in the stack.

Final thoughts

My LiteParse test did not make me think:

Great, now I can avoid VLMs entirely.

It made me think:

Good — now I have a cleaner control layer before I use them.

That is the right way to think about modern Document AI systems.

VLMs are powerful.

But they are much more valuable when they are used as targeted reasoning engines inside a well-designed pipeline, not as the default answer to every document problem.

If you are building OCR or Document AI systems, that architectural distinction will matter a lot more than people think.

If you are designing parser-first + VLM escalation workflows for real document operations, I am opening a small number of Document AI Routing Audit slots.

I help teams review:

  • where parser-first is enough
  • where to escalate to stronger models
  • how to preserve evidence for debugging and governance
  • how to reduce cost without making the system brittle

I Stopped Vibe Coding and Started Using Prompt Contracts, Here’s What the Data

You asked Claude Code to build a Supabase auth flow with row-level security.
You got a flawless, production-grade Firebase auth system.
Technically impressive. Fundamentally wrong. 2,400 lines of perfectly clean code, deleted at 2 AM.
That’s the story that kicked off Philippe Eveilleau’s viral Medium post — and it hit because every developer using Claude Code has a version of that story. Maybe yours was a payment integration that worked in staging and silently failed in production. Maybe it was an auth system that inverted a truthy check and gave deactivated accounts admin access for two weeks. Maybe it was just a function that passed your test case and rejected every international email address your users actually had.
The pattern has a name now. The industry has data on it. And there’s a method that actually fixes it — one that Eveilleau formalized as Prompt Contracts, which has now been turned into a book, a framework, and a workflow that developers are genuinely shipping with.
Let’s go deep.
The Vibe Coding Crisis Is Real and Documented
First, let’s establish that this isn’t just one developer’s bad night.
“Vibe coding” — the term coined by Andrej Karpathy in February 2025 — was named Collins Dictionary’s Word of the Year for 2025. It entered the cultural lexicon as software development where you describe what you want, accept what the AI produces, and iterate via prompts rather than comprehension.
One year in, the vibes are measurably off.
The Productivity Paradox
In July 2025, METR ran a rigorous randomized controlled trial with experienced open-source developers. Real engineers, real codebases, real tasks. The result: developers using AI coding tools were 19% slower than those working without them. The same developers predicted they’d be 24% faster going in — and even after the experiment, still believed they’d been 20% faster.
They were measurably slower, and they didn’t know it.
A broader analysis of vibe coding statistics found this pattern consistent: 95% of developers report feeling more productive while simultaneously producing lower-quality output. The explanation is straightforward — AI eliminates the easy, fast parts (scaffolding, boilerplate, repetitive patterns), which feels like a massive speed gain. But it quietly increases time spent on the hard parts: debugging unfamiliar code, hunting subtle logic errors in code you didn’t write, and understanding hidden assumptions you never explicitly made.
The Code Quality Numbers Are Worse
A December 2025 analysis by CodeRabbit examined 470 open-source GitHub pull requests and found that AI co-authored code contained approximately 1.7x more “major” issues than human-written code. Specifically:
Logic errors: incorrect dependencies, flawed control flow — measurably elevated
Misconfigurations: 75% more common in AI-generated code
Security vulnerabilities: 2.74x higher in AI-generated versus human-written code
A separate Veracode study in October 2025 confirmed that while LLMs had become dramatically better at generating functional code over three years, the security profile of generated code had not improved at pace. Larger models weren’t meaningfully better than smaller ones at generating secure output.
The Stack Overflow Developer Survey found 66% of developers experience the “productivity tax” — that specific frustration of AI-generated code that is almost, but not quite, right. Close enough to accept; wrong enough to break things later.
The CTO Survey That Should End the Debate
Final Round AI surveyed 18 CTOs in August 2025. 16 out of 18 (89%) reported experiencing production disasters directly caused by AI-generated code. One CTO’s take: “AI promised to make us all 10x developers, but instead it’s making juniors into prompt engineers and seniors into code janitors.”
IBM reported a 60% reduction in development time for internal tools with AI assistance — but that’s the sweet spot. Internal tools with low security bars, low maintenance expectations, and high bug tolerance. The moment you’re building something that needs to be correct, secure, and maintained: the math changes.
What Vibe Coding Actually Gets Wrong
Before we get to the fix, it’s worth being precise about the failure mode.
The problem isn’t that Claude Code is bad at writing code. It’s excellent at writing code. The problem is that it writes code to match what you described, not what you actually need — and the gap between those two things is where every 2 AM debugging session lives.
Dua Asif, writing in Activated Thinker (Feb 2026), described this precisely with the email validation story. She asked Claude to write an email validation function. Claude delivered — clean, with regex patterns, handling basic cases. Tests passed. Production deploy. Life was good.
Until customer support started getting complaints. The function rejected perfectly valid international email addresses, plus-sign filter addresses, addresses that followed RFC 5322 but didn’t match the regex. The function worked exactly as asked. She just asked for the wrong thing.
“The quality of your output is directly proportional to the specificity of your input. Garbage in, garbage out applies to AI just as much as it does to traditional programming.”
The hidden cost of vibe coding is you get what you request, not what you need. And that gap is invisible until production reveals it.
The deeper issue: vibe coding externalizes cognition into prompts and post-hoc evaluation. Traditional development front-loads thinking — you understand the problem deeply before writing code. Vibe coding moves that understanding after the code exists, which means you’re evaluating assumptions you never consciously made. Review time goes up even as coding time goes down. Comprehension debt accumulates.
Prompt Contracts: The Method
Eveilleau’s Prompt Contracts framework is, at its core, a simple reframe: stop treating Claude Code like a search engine and start treating it like an API you’re writing a contract for.
When you call an external API, you don’t just describe the vibes of what you want. You specify the endpoint, the expected inputs, the expected outputs, the error states, the edge cases, and the acceptance criteria. You define what success looks like before you make the call.
A Prompt Contract applies that same discipline to Claude Code interactions.
The Structure of a Prompt Contract
A well-formed Prompt Contract has five components:

  1. Stack Declaration
    Be explicit and complete about your technical environment. Don’t say “set up authentication.” Say “implement authentication using Supabase with row-level security, in a Next.js 14 app with TypeScript strict mode, using the existing user table schema in schema.sql.”
    This is the single change that prevents the Supabase-becomes-Firebase moment. Claude Code is not psychic about your stack. It will make reasonable assumptions. Those assumptions will sometimes be wrong in spectacular ways.
  2. Success Criteria
    Define what “done” means in measurable terms. Not “handle edge cases” — that’s a vibe. Instead:
    Must handle email addresses with non-ASCII characters
    Must handle plus-sign addressing ([email protected])
    Must comply with RFC 5322
    Must return a specific error type for invalid inputs (not throw)
    Must handle malformed API responses with trailing commas
    As Dua Asif notes: “What does ‘works correctly’ mean? Does it mean the function never raises exceptions? Does it mean it handles 99% of cases? Does it mean it fails gracefully with helpful error messages?” If you don’t answer this in the prompt, Claude will answer it for you — and it may not answer it the way you need.
  3. Explicit Constraints
    What can’t it do? What must it not touch? Which patterns must it follow?
    Do not introduce new dependencies
    Do not modify the existing auth middleware
    Must use the existing error handling pattern from src/lib/errors.ts
    Performance: must complete in under 200ms for p99
    The Anthropic API documentation itself instructs Claude Code: avoid over-engineering, don’t add features beyond what was asked, don’t add error handling for scenarios that can’t happen. But “can’t happen” is only knowable if your constraints are explicit.
  4. Output Format Specification
    Specify the exact form of the output. A single function? A module? Including tests? With what kind of comments? In what file? Replacing existing code or alongside it?
    Vague output specs produce vague outputs.
  5. Verification Criteria (The Self-Check Loop)
    This is the Prompt Contracts move that changes everything: ask Claude to verify its own output before delivering it.
    Eveilleau’s framework includes what he calls the Verification Loop — prompting Claude to test its own code in the browser before you take delivery. The Amazon book description frames it as “the AI tests its own code before you take delivery.”
    A simple version: append to every production-grade prompt — “Before responding, check your output against these criteria: [list your success criteria]. If any criterion is not met, revise before responding.”
    Claude’s ability to catch its own mistakes when explicitly asked is meaningfully better than its tendency to catch them unprompted. Build that into the contract.
    The Bigger Framework: Where Prompt Contracts Fit
    Prompt Contracts didn’t emerge in a vacuum. The industry has been converging on the same insight from multiple directions.
    Spec-Driven Development
    GitHub announced a spec-driven development toolkit in September 2025. Martin Fowler published an analysis of it. It appeared in Thoughtworks Technology Radar Volume 33. AWS shipped Kiro — a spec-driven IDE — in public preview. The core idea: write a detailed specification before any code is generated. The spec includes requirements, architecture, API contracts, error handling, and edge cases.
    Heeki Park’s write-up on using spec-driven development with Claude Code (March 2026) captures the experience of the convert: in earlier projects, he’d write a few quick sentences and start generating immediately, then course-correct repeatedly. After adopting specs: “most of my follow-up interactions were small tweaks rather than wholesale changes to the entire project.”
    The pattern: upfront planning time pays compounding dividends in implementation quality.
    The CLAUDE.md System
    Claude Code natively supports a CLAUDE.md file — a persistent project constitution that Claude reads at session start. It functions as a standing prompt contract for the entire project: stack, conventions, folder structure, coding standards, commands, and non-negotiables.
    Nick Babich’s guide on Claude Code project structure describes a well-formed CLAUDE.md as including project overview, architecture, tech stack, coding conventions (TypeScript strict mode, functional components, no default exports), folder structure, commands, and important rules for performance, accessibility, and testing.
    When your project has a CLAUDE.md, every prompt inherits that context automatically. You stop re-explaining your stack. You stop re-specifying your conventions. The contract becomes ambient.
    The GSD (Get Shit Done) Framework
    For Claude Code users who want to go further, the GSD build framework formalizes the spec-first approach with .spec.md files per feature. Instead of “Build me a login page”, you create login.spec.md:
    # Specification: User Login Component
    ## Requirements
  6. Input: Email and Password
  7. Validation: Email must be valid format; Password > 8 chars
  8. Action: Call /api/v1/login on submit
  9. State: Show loading spinner during request
    ## Tech Stack
  10. React + Tailwind CSS
  11. Lucide Icons for eye/hide password toggle
    Then: claude “Implement the feature described in docs/specs/login.spec.md”.
    The system ensures output isn’t a snippet — it’s a production-ready file built against a specification you reviewed before any code was generated.
    Prompt Contracts in Practice: Real Examples
    Here’s the contrast between a vibe prompt and a contract prompt, using a real scenario.
    Vibe Prompt
    Add rate limiting to our API endpoints
    Contract Prompt
    STACK: Express.js API, Node 20, Redis (upstash), TypeScript strict.
    Existing rate limiting: none.
    Middleware pattern: see src/middleware/auth.ts for the pattern to follow.

TASK: Implement rate limiting middleware for all /api/* routes.

SUCCESS CRITERIA:

  • 100 requests/minute per IP for unauthenticated routes
  • 1000 requests/minute per authenticated user (use existing auth context)
  • Returns 429 with Retry-After header when limit exceeded
  • Rate limit headers (X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset) on all responses
  • Graceful degradation: if Redis is unavailable, allow requests through (log warning)

CONSTRAINTS:

  • Do not modify existing auth middleware
  • Do not introduce dependencies beyond upstash/ratelimit (already in package.json)
  • Must work in edge runtime (Vercel)

OUTPUT: Single middleware file at src/middleware/rateLimit.ts + update to src/app.ts showing usage. Include 3 test cases.

VERIFY: Before responding, check that the Redis unavailability case is handled and that the Retry-After header is correctly calculated from the window reset time.
The second prompt takes 90 seconds to write. It produces something that actually goes to production. It also becomes documentation — when you come back to this code in three months, the spec is right there.
As Dua Asif noted: “The prompt contract became documentation.”
When Not to Write a Contract
Prompt Contracts have a cost: they take time to write. For casual work, they’re overkill.
The rule of thumb that’s emerged from practitioners: write a contract when the code matters. When it’s going to production, when other people will maintain it, when bugs would be costly, when it touches security or payments or auth.
Don’t write a contract for:
Explaining a concept
Refactoring a small, isolated function you’re going to review line-by-line
Throwaway scripts you’ll delete in an hour
Exploring or prototyping to understand a new technology
Do write a contract for:
Any feature that touches user data
Any API integration
Any auth or permissions logic
Any code that will outlive the current sprint
Any code that other team members will inherit
The threshold for “code that matters” is lower than most developers initially think. That “quick script” has a way of becoming a critical pipeline six months later.
The Senior Developer Advantage
One of the most consistent findings in vibe coding research: senior developers (10+ years) report 81% productivity gains from AI coding tools, while junior developers show mixed or negative results.
The explanation: AI tools amplify existing judgment. Senior engineers can evaluate what AI produces — they recognize when an architecture decision is wrong, when a security pattern is suspect, when a function is doing too much. They use AI for the mechanical parts while retaining oversight on the judgment calls.
Junior developers lack the reference points to evaluate AI output. They accept code they can’t fully evaluate, which means they can’t catch what the AI got wrong.
Prompt Contracts partially bridge this gap. By forcing explicit success criteria and constraints before generation, you build in the checkpoints that a senior engineer would apply mentally. The contract externalizes the judgment that experience normally provides implicitly.
The Productivity Tax and How Contracts Reduce It
Stack Overflow’s data: 66% of developers experience the productivity tax — “code that is almost, but not quite, right.” That “almost” is the most expensive word in software development. It’s the bug you can’t find because the code looks right. It’s the edge case that passes every test you wrote because you didn’t know to write that test.
A research analysis of vibe coding statistics put it precisely: vibe coding doesn’t eliminate engineering effort — it redistributes it. Speed gains in early development are real. They’re counterbalanced by increases in review burden, defect risk, and organizational knowledge decay.
Prompt Contracts specifically attack the knowledge decay problem. When every production prompt includes a stack declaration, success criteria, and constraints, you accumulate a library of specifications that document what the code was supposed to do — not just what it does. That’s documentation that writes itself.
The Bottom Line for Developers in 2026
The data has converged on something the Prompt Contracts framework formalized in practice:
AI code generation is not a replacement for engineering judgment. It’s an amplifier of it.
The developers who ship reliably with Claude Code in 2026 are not the ones typing natural language and hoping for the best. They’re the ones who treat Claude Code as a powerful junior engineer who executes brilliantly but needs precise briefs. They specify the stack, define success, constrain the scope, and build verification in.
Vibe coding will get you a beautiful Firebase implementation when you asked for Supabase.
A Prompt Contract will get you exactly what you needed — even the edge cases you forgot you needed.
The risotto might be delicious. But at 2 AM, you wanted pizza.
Resources & Further Reading
Philippe Eveilleau — Prompt Contracts on Medium (original article)
Philippe Eveilleau — Prompt Contracts: The Book on Amazon
Dua Asif — I Stopped Vibe Coding and Started Prompt Contracts (Activated Thinker, Feb 2026)
Wikipedia — Vibe Coding — METR study, CodeRabbit data, industry reaction
Hashnode — The State of Vibe Coding in 2026
Pixelmojo — The AI Coding Technical Debt Crisis
Bloomberg Businessweek — Claude Code and the Great Productivity Panic of 2026 (Feb 2026)
Heeki Park — Using Spec-Driven Development with Claude Code (March 2026)
Thoughtworks — Spec-Driven Development: 2025 Emerging Practice
Anthropic — Claude Code Best Practices
Anthropic — Claude Code Prompting Best Practices
What does your current AI prompting workflow look like? Are you writing specs before you generate, or still iterating from vibes? Drop it in the comments — genuinely curious where the community has landed.