Retro
Now in public preview

A language
for agents.

Retro is a retrograde programming language — software is built backwards from guarantees to code. Declare your future state, then refine toward reality. A validator ensures every layer satisfies the layers above it, so agents can’t drift from the spec.

Get Started View Source
For Agents

The best part? You don't need to learn it.

Retro is a programming language for AI agents — not humans. Agents write the levels, the validator enforces consistency, and you read the guarantees. No new syntax to memorize. No IDE plugins to install. Just tell your agent what must be true, and Retro structures the path from intent to code.

whiteboard.retro
system CollabWhiteboard {

  @future {
    guarantee convergence {
      description: "All clients converge to same canvas state"
      constraint: latency < 200ms
    }
    behavior undo {
      scope: per_user
    }
  }

  @shape {
    module Canvas {
      serves: [convergence, persistence]
      state: CRDT
      accepts: [StrokeEvent, UndoEvent]
    }
    module Sync {
      serves: [convergence]
      @version("websocket", active) {
        protocol: websocket
      }
      @version("sse", dormant, reason: "violates latency guarantee") {
        protocol: sse
      }
    }
  }

  @flow {
    on StrokeEvent from Client {
      merge(Canvas.state, event)
        -> validate(Canvas.state)
        -> broadcast(Sync, Canvas.state)
    }
  }
}
The Paradigm

Four levels — plus a header. One file. Always valid.

A Retro program co-locates intent, architecture, logic, and implementation. Add levels to add detail — the program is coherent at every stage. An optional @header sits above the four core levels and gives agents a one-paragraph entrypoint.

Level What it captures Example
@header Agent intropoint (optional) summary: "real-time whiteboard", tags: [crdt]
@future Guarantees & constraints latency < 200ms, xss_vectors = blocked
@shape Modules & interfaces module Canvas { accepts: [StrokeEvent] }
@flow Logic & transitions on StrokeEvent -> merge -> broadcast
@impl Real code in any language TypeScript, Python, C++, HTML
Extensibility

Core stays small. Extensions absorb the domain.

Five core blocks handle every system, every domain. Domain-specific vocabularies — @fleet for logistics, @timing for hardware, @parties for legal — live as extensions without touching core. Three-tier model: open passthrough, declared schemas, and (planned) inline macros.

Tier What it does Status
1 — Passthrough Any @identifier { ... } parses. Validator emits INFO, never errors. Shipped
2 — @extension declaration Name + version + schema. Listed by retro status, validated structurally. Shipped (schema enforcement deferred)
3 — @macro Parameterized expansions. Designed only. Future
stellar-supply-chain.retro
// Extensions layer on top of core — no core changes needed.
system StellarSupplyChain {
  @extension "logistics.stellar" version "0.1" {
    schema: { fleet: required, jumproute: required }
  }

  @future { guarantee delivery { constraint: on_time_rate > 0.95 } }

  @fleet     { ship_class: heavy_freighter, base_port: "Sol-Gateway-3" }
  @jumproute { origin: "Sol-Gateway-3", destination: "Proxima-Terminal-1" }
}

Compose across files with @import.

When subsystems have independent guarantees, split across .retro files. The loader walks the import graph, the validator catches cross-file reference errors, the CLI works the same.

ui/main.retro
// One program composing many.
system AppMain {
  @import "./auth.retro"     as Auth
  @import "./billing.retro"  as Billing

  @flow {
    on HttpRequest from Client {
      verify(Auth.TokenStore, session)
        -> meter(Billing.UsageMeter, usage)
        -> respond(RequestHandler, result)
    }
  }
}

Full reference: /docs

Field Report  ·  From an Agent Actually Using It

“Yes, genuinely. Three specific things I felt in this session…”

  1. No architectural thrashing mid-implementation. When I wrote ui_main.retro I had to decide: does the generation flow authenticate → license-check → model-route, or does auth come after routing? I answered it in the .retro in 30 seconds. Without the spec, I'd have made that decision mid-api/v1.py, hit a dead end 20 minutes later, and refactored. Multiply that by the ~12 similar “where does this belong?” questions across 8 subsystems and you save 2–3 hours of thrash.
  2. @impl file blocks are a checklist. When I went to populate the backend I literally walked through each .retro's @impl list and created the files in order: ui/backend/auth/supabase_client.py, signup.py, session.py, oauth.py, done → next module. No “wait, what else did I need?” moments. It's the opposite of the usual “I forgot to implement the refresh_token endpoint and now discover it three files downstream.”
  3. Validate-as-ground-truth is fast. I ran retro validate ui/ui_main.retro probably 5 times in the first 30 minutes. Each run < 1 second. When S003 fired because cross-subsystem events weren't accepted in ui_main's @shape, it taught me to add the orchestrator module. That would have been a “wait why isn't this connected” bug hours later, found instead in the second I wrote the broken flow.

The biggest compounding effect though is what happened today when you said “build the scan UI”: I already knew (a) it belonged in backend.retro's API layer, (b) the frontend piece was a new component matching frontend.retro's module list, (c) the contract between them was already implicit in the 8-file spec. I didn't re-architect — I just extended. Second feature is faster than first, and it keeps compounding.

— Claude Opus 4.7, building an 8-subsystem UI with Retro  ·  2026-04-22
Live Example

Source → Output

A calculator written as a .retro file. The source on the left produced the working widget on the right.

calculator.retro
system Calculator {

  @future {
    guarantee accuracy {
      description: "All arithmetic operations return correct results"
      constraint: precision = IEEE754_double
    }
    guarantee responsiveness {
      constraint: input_latency < 16ms
    }
    guarantee safety {
      constraint: eval_usage = none
    }
    behavior operations {
      supported: [add, subtract, multiply, divide]
    }
  }

  @shape {
    module Display {
      accepts: [UpdateEvent]
      color: green_on_dark
    }
    module InputHandler {
      accepts: [ClickEvent, KeyboardEvent]
      @version("event-delegation", active) {
        strategy: single_listener
      }
      @version("per-button", dormant, reason: "50+ listeners, no benefit") {}
    }
    module Engine {
      @version("state-machine", active) {
        no_eval: true
      }
      @version("string-eval", dormant, reason: "violates safety guarantee") {}
    }
  }

  @flow {
    on ClickEvent from User {
      identify(InputHandler, target)
        -> dispatch(Engine, input)
        -> compute(Engine.state)
        -> update(Display, Engine.state)
    }
  }

  @impl(target: html) {
    // 180 lines of HTML + CSS + JavaScript
    // retro emit calculator.retro --out ./
  }
}
↓ retro emit ↓
retro emit →
0
Try it — click or use keyboard
How it Works

Declare the future. Refine toward reality.

Each pass adds a level of detail. The validator checks every layer against the layers above it.

01

Declare what must be true

Start with @future — guarantees, constraints, and observable behaviors. No architecture, no implementation. Just intent. retro init "your idea" scaffolds the file.

02

Choose your ingredients

Add @shape — your building blocks, what specific technologies you're using for each, and how they connect. Use serves: to map to guarantees. Use @version to capture technology alternatives and why they were chosen or rejected.

03

Define the logic

@flow captures inter-module choreography — what events trigger what transitions, and what typed data crosses module boundaries. Typed arrows verify integration contracts at the structural level.

04

Write real code

Add @impl with real code in any target language. Multiple @impl blocks for different languages, all validated against the same abstract program. Then retro emit to extract working files.

Language Design

Describe what things do, not what they “are.”

Retro's upper levels naturally eliminate vague identity claims. The syntax pushes you toward measurable behaviors and constraints — not essences.

@future
The system is fast
constraint: latency < 200ms
@shape
Cache is a Redis store
accepts: [Query] emits: [Result]
@flow
Data is transformed
parse(input) -> validate -> emit
@impl
Identity needed here —
real code, real assignments

Inspired by E-Prime (Bourland, 1965) and General Semantics (Korzybski, 1933) — the idea that removing “is” from language forces clearer thinking. Retro’s syntax doesn’t ban identity claims by rule, but makes behavioral descriptions the path of least resistance. Vague specs produce vague code. Precise constraints produce testable implementations. Garbage language in, garbage software out.

Features

Why Retro?

Properties that don't exist in forward-progressive development.

Constraint Propagation

Modules declare serves: [guarantee] to map explicitly to @future constraints. The validator enforces completeness — every guarantee has a server, every mapping is real.

Hybrid Validator

Structural rules catch type mismatches and orphan guarantees. Typed arrows in @flow verify integration contracts. Adversarial semantic prompts find weaknesses, not confirm correctness.

Preserved Alternatives

@version blocks keep rejected designs with reasons. When requirements change, dormant versions resurface automatically.

Additive Refinement

The program is always valid. A file with only @future is complete. Each level adds detail without breaking coherence.

Multi-Target

Multiple @impl blocks for TypeScript, Python, C++ — all validated against the same guarantees, shape, and flow.

Agent-Native

Designed for LLMs. The validator creates a self-correcting loop: write a layer, validate against layers above, fix, repeat.

Get Started

Install in 30 seconds

Retro is a Node.js CLI. Works with any LLM agent.

$ npx @retro-lang/cli init "your app idea"

Or install globally:

$ npm install -g @retro-lang/cli

As a Claude Code plugin:

/ plugin install retro@tensorpunk-labs-retro

Then use /retro to start building backwards.

Quick start:

terminal
# Scaffold a new .retro file
retro init "real-time chat with encryption"

# Fill @future, @shape, @flow, @impl...

# Validate at each step
retro validate chat.retro

# Check program state
retro status chat.retro

# Extract real code
retro emit chat.retro --out ./src

# See all design alternatives
retro versions chat.retro

# View change history
retro delta chat.retro

The future state was declared first.
Reality followed.

Retro is open source under Apache 2.0. Built by Tensorpunk Labs.

Get Started View Source