A spec-driven harness

Architecture in.
Software out.

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

$ uvx ossature
v0.0.1 · Open source · MIT License
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

Building Qoizig — a QOI image codec in Zig, from a single spec file to a working encoder and decoder, generated in 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.

$ ossature build
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 →

Every decision is yours.

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

Incremental builds

Every input is SHA-256 checksummed. Only rebuild what changed. Cascades stop at interface boundaries. Resume from any task.

04

Full audit trail

Every prompt sent, every response received, every file written with checksums. When something breaks at task 14, open the directory and see exactly what happened.