Workflow Guide¶
This page walks through a full project from init to generated code using markman, a Rust bookmark manager with a CLI and a read-only web UI. The Quick Start covers the same commands but without the context of why each step matters or what to do when things go wrong.
1. Initialize the Project¶
This creates an ossature.toml and a specs/ directory. Open the config and set it up:
[project]
name = "markman"
version = "0.0.1"
spec_dir = "specs"
[output]
dir = "output"
language = "rust"
[build]
setup = "cargo init --name markman"
verify = "cargo check"
[llm]
model = "anthropic:claude-haiku-4-5-20251001"
The [build] section is optional. setup runs once before the first task (here it initializes a Cargo project in the output directory). verify overrides the default verification command for all tasks. See Configuration for all available options.
2. Write Your Specs¶
Before writing any spec files, think about how your project breaks down into modules and what depends on what. For markman, we need three specs:
- STORAGE - SQLite persistence layer, no dependencies
- CLI - command-line interface, depends on STORAGE
- WEBUI - read-only web interface, depends on STORAGE
The @depends field creates an explicit ordering. STORAGE gets built first because CLI and WEBUI both need it.
Create the spec files:
Writing SMD files¶
Each .smd file starts with metadata, then describes what the module should do. Here's an abbreviated version of the storage spec:
# Storage
@id: STORAGE
@status: draft
@priority: critical
@depends: []
## Overview
SQLite-backed persistence layer for bookmarks. Each bookmark has a URL,
description, and comma-separated tags. This module owns all database
interaction; the CLI and web UI use it exclusively.
## Requirements
### Add Bookmark
Inserts a new bookmark record.
**Accepts:** conn (Connection), url (string, non-empty), desc (string,
may be empty), tags (string, comma-separated, may be empty)
**Returns:** `Result<i64, StorageError>` - the integer row id of the
newly inserted bookmark on success
**Errors:**
- Empty url -> returns `StorageError::InvalidInput("url must not be empty")`
- URL already exists -> returns `StorageError::Duplicate(url)`
- Any other database error -> returns `StorageError::Db(reason)`
Being specific matters. Each requirement says what it accepts, what it returns, and what happens on every error case. "Handle invalid input" leaves too much to interpretation. "Empty url returns StorageError::InvalidInput" does not.
The CLI spec declares its dependency on storage:
This tells Ossature that CLI tasks should come after STORAGE tasks in the build plan, and that the CLI's prompts should include STORAGE's public interface.
See the SMD Format reference for the full format, and the complete markman specs for the full example.
When to write an AMD¶
Architecture files (.amd) are optional. They let you define the internal structure of a module: components, file paths, data models, and public interfaces. If you skip them, the LLM infers the architecture during audit.
For markman, we skipped AMDs entirely. The specs are detailed enough that the LLM can figure out the structure on its own. If you know exactly what shape your system should take, writing an AMD gives the LLM less room to improvise. See the AMD Format reference.
3. Validate¶
Check that your specs are structurally correct:
This parses every .smd and .amd file and checks that all @depends targets exist (so [STORAGE] actually refers to a spec with @id: STORAGE), all @spec references in AMDs resolve to real SMDs, there are no duplicate component names within a spec, and there are no cycles in the dependency graph. No LLM is involved.
If there are errors, fix them and re-run until validation passes clean. Common issues at this stage are @depends targets that don't match any @id, requirement sections missing **Accepts:** or **Returns:**, and example sections missing **Input:** or **Output:** subsections.
4. Audit¶
Audit sends your specs to the LLM for semantic review and generates the build plan:
It runs through several stages: computing checksums and checking what changed since the last audit, reviewing each changed spec for ambiguity, contradictions, gaps, and feasibility issues, running a cross-spec audit if there are multiple specs, generating a project brief and per-spec briefs, extracting or inferring interface signatures, and finally generating the build plan.
Reviewing findings¶
The audit produces findings at three severity levels: errors (will likely cause build failures, fix these), warnings (potential problems worth considering), and info (observations you can usually ignore).
For example, auditing the markman storage spec produced a warning about an inconsistency between the example output and the requirement text:
Ambiguous timestamp format
The spec shows created_at in the example output as ISO format (2026-01-01T00:00:00), but the requirement states it is stored as YYYY-MM-DD HH:MM:SS (space-separated, as produced by SQLite datetime('now')). This could cause implementations to differ in timestamp format.
The example and the requirement disagreed on the format. The fix was to update the example output to use the space-separated format that SQLite actually produces:
**Output:**
```
[Bookmark { id: 1, url: "https://example.com", desc: "Example site",
tags: "example,test", created_at: "2026-01-01 00:00:00" }]
```
The audit also offers auto-fix, where the LLM edits your spec files directly to resolve findings. You're prompted before any edits are made. After audit completes, all findings are saved to .ossature/audit-report.md.
Incremental audits¶
On subsequent runs, audit only re-processes specs whose files have changed. If you edit storage.smd but leave cli.smd and webui.smd untouched, only STORAGE gets re-audited and re-planned. Tasks for CLI and WEBUI are preserved with their existing hashes. See Incremental Re-Planning for details.
5. Review the Plan¶
After audit, the build plan is written to .ossature/plan.toml. Read it before building.
The markman plan has 22 tasks across three specs. Here's what a couple of tasks look like:
[[task]]
id = "001"
spec = "STORAGE"
title = "Storage: Data Types & Errors"
description = "Define the core Bookmark struct and StorageError enum."
outputs = ["src/storage.rs"]
depends_on = []
spec_refs = ["STORAGE:Overview", "STORAGE:Requirements > Add Bookmark", ...]
status = "pending"
verify = "cargo check"
[[task]]
id = "002"
spec = "STORAGE"
title = "Storage: Database Initialization"
outputs = ["src/storage.rs"]
depends_on = ["001"]
inject_files = ["src/storage.rs"]
status = "pending"
verify = "cargo check"
Things worth checking: whether dependencies make sense and tasks are in a reasonable order, whether tasks are too broad (touching too many files) or too narrow, whether spec_refs is pulling the right spec sections into each task's prompt, and whether verify commands will actually catch problems.
The plan is human-editable. You can reorder tasks, change verify commands, add notes, or set a task's status to skipped. Your changes are respected when you run ossature build.
To discard the plan and regenerate from scratch, use ossature audit --replan.
6. Build¶
When the plan looks right:
For each task, Ossature assembles a prompt from the project brief, relevant spec sections, interface files, and output from earlier tasks. The LLM generates code and writes files to the output directory. After each task, the verify command runs. If verification fails, a separate fixer agent reads the errors and tries to repair the code, up to max_fix_attempts times (default 3).
By default the build continues silently on success and pauses on failure with a prompt: retry, skip, or quit. Other modes:
ossature build --step # pause after every task for approval
ossature build --auto # run to completion, stop on first failure
ossature build --auto --skip-failures # run everything, skip failures
For a first build, --step is useful so you can inspect the output before continuing.
If something fails¶
ossature retry re-runs failed tasks:
ossature retry # re-run all failed tasks
ossature retry --from 007 # redo everything from task 007 onwards
ossature retry --only 005 # re-run task 005 and all its dependents
Check progress at any point with ossature status.
See The Build System for the full details on the build loop, fix loop, invalidation, and retry.
7. Iterate¶
After a build, you'll usually want to change something. Edit the spec, then run the same sequence again:
The build is incremental. If you change storage.smd, only STORAGE tasks and any downstream tasks that reference STORAGE's interface get rebuilt. CLI and WEBUI tasks stay untouched if STORAGE's public interface didn't change.
Summary¶
ossature init create project
edit specs describe what to build
ossature validate fix structural issues (loop until clean)
ossature audit LLM review + plan generation (fix errors, loop until clean)
review plan.toml check task order, granularity, verify commands
ossature build generate code task by task
ossature retry re-run failures
edit specs iterate
The specs are your source of truth. The plan is your review checkpoint. When something breaks, you fix the spec or the plan and rebuild instead of starting over.
Next Steps¶
- SMD Format - Full spec format reference
- AMD Format - Architecture format reference
- Configuration - All config options
- Commands - CLI reference
- Build System - Build loop, invalidation, retry internals