A spec-driven harness

Architecture in.
Software out.

An open-source framework that turns architectural specs into working code.

$ uvx ossature
v… · Open source · MIT License
Works with Anthropic · OpenAI · Mistral · Google · Ollama
API SpecDB SpecUI SpecValidateAuditaudit-report.mdplan.tomlapi tasksdb tasksui tasksui tasks depend on api, db tasksBuildapi.tsdb.tsui.ts
  1. api.spec · db.spec → ui.spec Spec DAG (UI depends on API & DB)
  2. Validate ✓ Check constraints
  3. Audit ✓ audit-report.md
  4. plan.toml T1: api · T2: db → T3: ui
  5. Build Tasks in dependency order
  6. api.ts · db.ts · ui.ts Output files

From Spec to Software

Here's a walk through Qoizig, a QOI image codec in Zig, generated from a single spec file into a working encoder and decoder over 9 tasks.

ossature.toml
$ ossature init creates the project config. Set the output language, pick your LLM model, then $ ossature new QOI_CODEC scaffolds a blank spec.
01

Write the spec

Describe what the module does in a .smd file — what it accepts, what it returns, and every error case. This spec defines the QOI header structure, the pixel hash function, and both the encode and decode commands. Run ossature validate to check structure.

specs/QOI_CODEC.smd — 200 lines
specs/QOI_CODEC.smd
# Qoizig

@id: QOI_CODEC
@status: draft
@priority: high
@depends: []

## Overview

A high-performance, zero-dependency command-line tool
and library implemented in Zig for the QOI (Quite OK
Image) format.

## Requirements

### QOI Format Fundamentals

**Header Structure:**
- `char[4]` magic: "qoif"
- `u32` width: image width in pixels (BE)
- `u32` height: image height in pixels (BE)
- `u8` channels: 3 = RGB, 4 = RGBA
- `u8` colorspace: 0 = sRGB, 1 = linear

…

### Encode Command (`qoizig encode`)

**Accepts:**
- `input` (positional, required): Path to source file
- `output` (positional, required): Path for QOI output

**Errors:**
- Input file not found -> print error and exit code 1
- Invalid PPM/PAM header -> print error and exit code 1

…
02

Audit catches the gaps

Before any code is written, ossature audit sends your specs to an LLM for review. Here it found that the encode command's examples show two positional arguments, but only one was documented. You fix the spec, re-run audit, and move on only when it's clean.

$ ossature audit
audit-report.md
### WARNING: Encode Command

The encode command CLI example shows two positional
arguments (input and output path), but the requirement
only specifies one positional argument.

**Suggestion:** Add `output` (positional) to the
Accepts list for the encode command, and clarify
the default behavior if omitted.
03

Review the plan

The audit produces a build plan — a topologically ordered list of tasks with dependency tracking. Each task generates 1–3 files and includes a verification command. You review and edit this before anything gets built.

plan.toml — 9 tasks total
plan.toml
# …tasks 001–002: scaffold, types…

[[task]]
id = "003"
spec = "QOI_CODEC"
title = "QOI Encoder Implementation"
outputs = ["src/encoder.zig"]
depends_on = ["001", "002"]
verify = "zig build --summary all"

[[task]]
id = "004"
spec = "QOI_CODEC"
title = "QOI Decoder Implementation"
outputs = ["src/decoder.zig"]
depends_on = ["001", "002"]
verify = "zig build --summary all"

# …tasks 005–009: tests, CLI, integration…
04

Build generates code

Each task gets a narrow context window — only the spec sections, types, and source files it needs. The encoder task sees the QOI format spec, the types module, and the build config. Nothing else. After generation, zig build verifies each step. Per-task token counts and cost get printed as the build runs, so you always know what you've spent.

$ ossature build
  001  ✓  scaffold      2.1k in,  0.4k out, $0.01
  002  ✓  types.zig     3.6k in,  0.9k out, $0.02
  003  ✓  encoder.zig   5.4k in,  1.8k out, $0.04
  004  ✓  decoder.zig   5.1k in,  1.6k out, $0.04
  …
  9 tasks  ·  38.4k in, 9.2k out  ·  $0.31
output/src/encoder.zig
/// Encode raw pixel data into a complete QOI byte stream.
pub fn encode(
    allocator: std.mem.Allocator,
    pixels: []const u8,
    width: u32,
    height: u32,
    channels: Channels,
    colorspace: Colorspace,
) error{ InvalidPixelDataLength, OutOfMemory }![]u8 {
    // …validation, buffer allocation…

    // Write header
    const header = QoiHeader{
        .width = width,
        .height = height,
        .channels = channels,
        .colorspace = colorspace,
    };
    const header_bytes = header.encode();
    @memcpy(output[pos .. pos + qoi.QOI_HEADER_SIZE], &header_bytes);
    pos += qoi.QOI_HEADER_SIZE;

    // Encoder state
    var index: [qoi.QOI_INDEX_SIZE]Pixel = // …
    var prev_px: Pixel = Pixel.default;
    var run: u8 = 0;

    // …chunk compression, end marker…
}

Explore complete projects

Each example includes the specs, the full build plan, every prompt sent to the LLM, and the generated code — so you can trace every decision from spec to output.

Browse examples on GitHub →

The spec is the source of truth.

You only edit the spec. The code is regenerated from it as the project grows.

The spec stays in sync.

Code changes go through the spec, never around it. The spec stays in sync as the project grows, on the tenth iteration as much as the first.

Always on disk.

Prompts, responses, and generated files all get checksummed and committed alongside the code. Nothing disappears when you close the window.

Resume any time.

Builds run incrementally and stop anywhere. Edit the plan, resume tomorrow. There's no conversation context to lose.

Nothing happens by accident.

01

Spec dependency graph

Specs form a DAG. Build ordering, interface contracts, and cascade invalidation all derive from it. Change one spec and only its dependents rebuild.

02

Narrow context per task

Each task gets ~2–5K tokens — only the spec sections, types, and source files it actually needs. Focused context produces better output than dumping everything.

03

Diff-aware incremental builds

Every input is SHA-256 checksummed. Edit a spec and the planner diffs it against the previous plan, keeping unaffected tasks even inside the spec you changed. Cascades stop at interface boundaries.

04

Full audit trail

Every audit and planner prompt is saved to disk next to the model's response. Files are checksummed. Token usage and cost get tracked per task. When something breaks at task 14, open the directory and see what happened.