writ-vcs
The first AI native version control system for agentic development. (writ for short)
Git was built for humans passing patches between each other. It works. But the world it was designed for (one developer, one branch, manual merges) isn’t the world agents work in. When you scale from one agent to five, and from five to fifty, you don’t need a better interface to git. You need a version control system that understands what agents actually do.
Writ is that system. Git is the storage layer. Writ is the intelligence layer.
What Agents Actually Need
Agents don’t read git log the way humans do. They parse it, tokenize it, reconstruct state from unstructured text across multiple tool calls. Every call costs tokens. Every response needs interpretation. The more agents you have, the more that cost compounds.
Writ replaces that workflow with structured, single call access to everything an agent needs to start working:
-
Context in one call.
writ contextreturns specs, seals, working state, file contention, integration risk, and divergence status as structured JSON. Not text to parse. Not five tool calls stitched together. One call, one response, the format agents work best with. -
Seals instead of commits. A seal carries agent identity, spec linkage, test results, status lifecycle, and cryptographic integrity. Every seal is chained to its predecessor via BLAKE3 hashes. The metadata that git forces you to encode in commit messages and branch names is a first class part of the data model.
-
Specs instead of branches. A spec is a task unit with a hash ID, lifecycle states, agent claiming, and a genesis snapshot, closer to an issue inside the VCS than a branch. When an agent seals work against a spec, that link is permanent. Context groups work by spec. Convergence merges spec by spec. Scope enforcement keeps agents in their lane.
-
Convergence instead of merge. Writ’s convergence engine merges agent work using sealed histories and genesis trees as the common ancestor. Non-conflicting changes compose automatically. Real conflicts escalate with structured context and confidence scores. Giving an agent better git access doesn’t make
git mergesmarter. Convergence is the problem, and it requires a purpose built solution.
Where Writ Fits
Writ sits alongside git, not instead of it. A human checks out a branch, runs writ init, and agents work in writ: sealing checkpoints, checking context, converging changes. When they’re done, writ finish commits everything back to git with full provenance. Git handles what it’s good at: storage, distribution, collaboration history. Writ handles what it was never designed for: agent native workflows at scale.
Human world Agent world Human world
┌──────────┐ writ init ┌─────────────────┐ writ finish ┌──────────────┐
│ git repo │ ──────────────→│ agents work in │ ─────────────→ │ git commit │
│ (branch) │ │ writ: specs, │ │ with full │
│ │ │ seals, context │ │ provenance │
└──────────┘ └─────────────────┘ └──────────────┘
When You Need It
If you have one agent doing straightforward work on isolated files, git is probably fine. Writ’s value is proportional to complexity.
The inflection point comes when:
- Multiple agents work concurrently on overlapping codebases
- You need structured awareness of who is doing what and which files are contested
- Merging agent work requires understanding code structure, not just line positions
- Context cost matters: agents spend real tokens reconstructing state that writ delivers in one call
- You need cryptographic proof of what happened, who did it, and that nothing was tampered with
The single agent with git world is today. The fleet of fifty agents world is next quarter. Writ is built for that transition.
Quick Example
# Set up writ in any project
writ init
# Agents seal checkpoints as they work
writ seal -s "added auth module" --spec auth
writ seal -s "tests passing" --spec auth --tests-passed 42
# One call gives agents everything they need
writ context --format json
# When done, commit back to git
writ finish
Note: Writ is in early alpha. The core is stable and thoroughly tested (2,096+ Rust tests, 834+ Python tests), but the API may change before 1.0. We welcome feedback and contributions.
Next Steps
- Installation to get writ on your machine
- Quickstart for a five minute walkthrough
- Seals vs Commits to understand the data model
- Convergence for the deep dive on how writ merges agent work
Installation
Writ can be installed through several package managers. Choose whichever fits your toolchain.
Python (PyPI)
The fastest way to get started. Installs both the CLI and the Python SDK.
pip install writ-vcs
After installation, the writ command is available in your terminal and import writ works in Python.
Rust (Cargo)
Build the CLI from source. Requires the Rust toolchain.
cargo install --path crates/writ-cli
Or, once published to crates.io:
cargo install writ
macOS (Homebrew)
Coming soon. Once the Homebrew tap is published:
brew install writ
From Source
Clone the repository and build:
git clone https://github.com/andrew-garfield101/writ.git
cd writ
cargo build --release
The binary will be at target/release/writ. Add it to your PATH or copy it somewhere convenient.
For the Python bindings:
cd crates/writ-py
python3 -m venv .venv && source .venv/bin/activate
pip install maturin
maturin develop
Verify Installation
writ --version
# writ 0.1.0
writ --help
# Shows all available commands
Removing Writ
Writ has two layers: per project setup and the system install. Remove either or both depending on what you need.
Remove writ from a project
writ uninit --force
This removes .writ/, framework hooks (CLAUDE.md markers, slash commands, settings.json permissions), and the .gitignore entry. Your source code, git history, and other config are never touched.
Use --keep-writignore if you want to preserve your .writignore file.
Remove writ from your system
Depends on how you installed it:
# PyPI
pip uninstall writ-vcs -y
# Cargo
cargo uninstall writ
# Homebrew (when available)
brew uninstall writ
# From source — remove the binary you added to PATH
rm /path/to/writ
After uninstalling, optionally remove global config:
rm -rf ~/.writ/
This is a small config directory. Removing it is safe and has no effect on your projects or git history.
Next Steps
Head to the Quickstart to set up writ in a project and create your first seal.
Quickstart
Get from zero to a working writ workflow in five minutes.
Prerequisites
- A project directory (with or without git)
- Writ installed (see Installation)
1. Initialize Writ in Your Project
Navigate to your project and run:
cd my-project
writ init
On first run, writ prompts for your name, preferred output format, and default agent frameworks. These go into a global config (~/.writ/config) that applies to all projects. Then it sets up the current project:
initialized writ repository in .writ/
created .writignore
git: main @ a3f8b2c1
imported git baseline: 47 file(s), seal d81a5736e16d
tracked: 47 file(s)
output format: toon (token-optimized for LLM agents)
workflow mode: user (you run `writ finish` to commit)
Writ detects your environment automatically. If you’re in a git repo, it reads the branch and HEAD. If Claude Code is present, it installs slash commands and writ workflow instructions in CLAUDE.md. If Codex is detected, it adds workflow instructions to AGENTS.md. For any other framework, the CLI and Python SDK work out of the box. Override any setting per project with .writ/config.toml or per command with flags.
2. Create a Seal
A seal is writ’s structured checkpoint. After making some changes to your project:
writ seal -s "added authentication endpoint" --spec auth
Output:
sealed 3 file(s)
seal: a7c2e8f4b31a
agent: dev-1
spec: auth
status: in-progress
Every seal records who created it, which spec it serves, and what status the work is in. This is metadata that git forces into commit messages and branch naming conventions. In writ, it’s structured data.
3. Check Context
This is the single most important command in writ. One call returns everything an agent needs to understand the current project state:
writ context --format human
This shows:
- Active specs and their status
- Recent seals with who did what
- Working directory changes
- File contention (files touched by multiple agents)
- Integration risk level
- Diverged branches with convergence recommendations
For agents consuming this programmatically, use TOON for maximum token efficiency:
writ context --format toon
TOON delivers the same structured data as JSON in up to 33% fewer bytes. Field names declared once, rows streamed as values, no braces or repeated keys. Compare this to the alternative: git log, git diff, git status, read a few files, parse all the text, synthesize a mental model. With writ, that entire workflow collapses into one call. See Output Formats for benchmark numbers and format details.
4. Add a Spec
Specs are structured requirements that agents work against. Think of them as feature tickets that live inside the VCS:
writ spec add "JWT-based auth with token refresh"
Now when agents seal work with --spec auth, that work is permanently linked to this requirement. Context output scopes to the spec’s files and shows progress. Convergence merges spec by spec. Scope enforcement can restrict agents to their spec’s declared files.
5. Mark the Spec Complete
When an agent finishes its work on a spec:
writ spec done auth -s "JWT auth with token refresh, all tests passing"
This creates a final seal, transitions the spec to completed, and prints a hint:
spec auth marked as completed
final seal: b4e3c2d8a91f
agent: dev-1 | seals: 3 | files: 4 changed
run `writ status` to see all completed specs.
run `writ finish` to commit completed work to git.
The agent is done. It can terminate or pick up another spec.
6. Check Status and Finish
The user checks in on progress from anywhere:
writ status
Active 2 agents 1 spec in progress
Done 1 agent 1 spec completed (not committed)
S-auth Authentication System dev-1 3 seals Complete
1 spec complete · 4 files changed · run `writ finish` when ready
When ready, promote completed work to git:
writ finish
writ finish shows all completed specs, lets you select which to include, and offers commit strategy options (single commit, per spec, or grouped). It generates a commit message from the full session history: which agents worked on which specs, what was completed, what was tested. Full provenance, automatically.
# Or manually with a generated commit message
git commit -m "$(writ summary --format commit)"
# Or create a PR with a detailed description
gh pr create --body "$(writ summary --format pr)"
What Just Happened
In five minutes you:
- Initialized writ with automatic environment detection and format configuration
- Sealed a checkpoint with structured metadata (agent, spec, status)
- Checked context in token optimized TOON format, one call for full project state
- Defined a spec that agents can work against
- Completed the spec with
writ spec done, final seal and clean lifecycle transition - Checked status to see fleet progress at a glance
- Committed back to git with
writ finish, auto generated provenance and strategy selection
Git stayed in place the whole time. Writ added the intelligence layer on top.
Python SDK
Everything above also works through the Python SDK:
import writ
repo = writ.Repository.open(".")
ctx = repo.context(spec="auth")
repo.seal(
summary="added auth endpoint",
agent_id="dev-1",
spec_id="auth",
tests_passed=12,
)
Multiple Agents
Everything above works with multiple agents in the same project directory. Each agent seals with --spec, and writ keeps their changes separate. For multi-agent work:
# Define tasks in batch
writ plan "Implement auth" "Add payments" "Build dashboard"
# Launch agents — they discover specs via writ context, claim one, and work
# Convergence runs automatically at spec done and at writ finish
No workspaces, no branches, no path management. Agents work in the same directory. Convergence merges overlapping changes automatically when specs complete. See the Multi Agent Workflow guide for the full walkthrough.
Next Steps
- Multi Agent Workflow for running multiple agents in the same directory
- Your First Convergence to see what happens when agents’ work overlaps
- Output Formats for TOON benchmarks and format configuration
- Seals vs Commits to understand the data model
- Configuration for workflow modes, format config, and deployment profiles
- Convergence for the deep dive on semantic merging
Your First Convergence
This walkthrough demonstrates the core problem writ solves: merging work from multiple agents who touch the same files. Not with line based diffing, but with sealed histories and genesis trees.
The Scenario
Two agents work in parallel on different specs. Both touch some of the same files. When they’re done, their work needs to be merged. In git, this produces conflict markers. In writ, it composes naturally.
Setup
Start with a project that has writ initialized:
mkdir demo && cd demo
git init && echo "# Demo" > README.md && git add . && git commit -m "init"
writ init
Create two specs:
writ spec add "Backend API"
writ spec add "Frontend Components"
Agent A: Backend Work
Agent A adds a utility module and updates the main application:
# utils.py (new file)
def validate_token(token: str) -> bool:
return len(token) > 0
def hash_password(password: str) -> str:
import hashlib
return hashlib.sha256(password.encode()).hexdigest()
writ seal -s "added auth utilities" --spec backend
Agent B: Frontend Work
Meanwhile, Agent B adds its own utility functions and also updates the main application:
# utils.py (new file, different functions)
def format_date(timestamp: float) -> str:
from datetime import datetime
return datetime.fromtimestamp(timestamp).isoformat()
def sanitize_input(text: str) -> str:
return text.strip().replace("<", "<").replace(">", ">")
writ seal -s "added display utilities" --spec frontend
The Divergence
Both agents created a utils.py with different functions. In git, this is a conflict. Two sides changed the same file. Give up, produce markers, make a human figure it out.
writ context --format human
Context shows:
- Two diverged branches (backend, frontend)
convergence_recommended: true- Integration risk assessment
Converge
writ converge-all --apply --strategy escalate
Writ’s convergence engine analyzes the conflict:
- Pool filter selects both specs for convergence (both have sealed changes to the same file)
- Genesis tree merge uses each spec’s genesis snapshot as the common ancestor. Since both added new content to the same file, the merge identifies non overlapping additions
- Confidence scoring rates the merge at high confidence. Both sides added independent functions with no overlap, so they compose cleanly
The merged utils.py contains all four functions from both agents:
def validate_token(token: str) -> bool:
return len(token) > 0
def hash_password(password: str) -> str:
import hashlib
return hashlib.sha256(password.encode()).hexdigest()
def format_date(timestamp: float) -> str:
from datetime import datetime
return datetime.fromtimestamp(timestamp).isoformat()
def sanitize_input(text: str) -> str:
return text.strip().replace("<", "<").replace(">", ">")
No manual intervention. No conflict markers. Both agents’ contributions preserved. The engine composed additive changes instead of forcing a choice.
When Conflicts Are Real
If both agents had modified the same function body differently, that’s a genuine conflict. Writ escalates it with full context instead of producing <<<<<<< markers:
{
"escalations": [{
"file": "utils.py",
"conflict_type": "BothModified",
"region": "validate_token function body",
"left_version": "...",
"right_version": "...",
"confidence": 0.35,
"recommendation": "Manual review required"
}]
}
Structured data, not text to parse. An orchestrator agent can resolve this programmatically, no regex parsing of conflict markers required.
Why This Matters
This is a two agent demo. The principle scales. When you have ten agents working across five specs, all touching overlapping files, the merge problem doesn’t scale linearly. It explodes combinatorially. Writ’s convergence engine handles this by:
- Optimizing merge order. Disjoint specs merge first, reducing conflict surface for later merges
- Composing additive changes. The common case in multi agent work where agents build complementary features
- Escalating real conflicts. With structured context that agents or humans can act on
Giving each agent a git worktree solves isolation. This is what solves convergence.
Next Steps
- Convergence for the deep dive on how writ merges agent work
- Multi Agent Workflow for production patterns
- Convergence Resolution for handling escalations
Seals vs Commits
Git commits were designed for humans. An email address, a timestamp, a diff, and a message. Everything else (which task it serves, whether tests passed, which requirement it belongs to) is convention layered on top through commit messages and branch names.
Writ’s fundamental unit of history is the seal. A seal carries the same structured metadata that agents need to work effectively, built into the data model rather than bolted on through naming conventions.
What a Git Commit Looks Like
commit a3f8b2c1
Author: dev@example.com
Date: Mon Feb 24 14:30:00 2026
fix: update auth token handling
A commit knows: who (an email), when (a timestamp), and what (a diff). That’s it. An agent parsing this has to tokenize unstructured text, hope the commit message follows conventions, and make multiple tool calls to reconstruct the state that writ delivers in one structured response.
What a Writ Seal Looks Like
{
"id": "a7c2e8f4b31a",
"timestamp": "2026-02-24T14:30:00Z",
"agent_id": "auth-dev",
"agent_type": "agent",
"spec_id": "auth-migration",
"summary": "token refresh endpoint with 15-min expiry",
"status": "in-progress",
"tests_passed": 12,
"tests_failed": 0,
"linted": true,
"files_changed": 3,
"content_hash": "blake3:...",
"parent_seal_hash": "blake3:...",
"chain_hash": "blake3:..."
}
A seal knows everything a commit knows, plus:
- Who made it (agent identity with trust level and role, not just an email)
- What task it serves (linked spec with status and file scope)
- What status the task is in (in progress, complete, blocked)
- Whether it was tested (pass/fail counts, lint status)
- Cryptographic integrity (BLAKE3 hash chain linking to the previous seal)
This is the intelligence layer. Git stores diffs. Writ stores structured knowledge about what happened, who did it, and why.
Key Differences
| Git Commit | Writ Seal | |
|---|---|---|
| Identity | Email address | Agent ID with trust level and role |
| Task linkage | Branch name (convention) | First class spec reference |
| Status | None | in-progress, complete, blocked |
| Test results | None (CI runs after the fact) | tests_passed, tests_failed, linted |
| Integrity | SHA-1 hash | BLAKE3 content hash + chain hash + Ed25519 signature |
| Scope awareness | None | Can enforce file scope constraints per agent |
| Machine readable | Needs parsing | Structured JSON from the start |
Immutability
Like git commits, seals are immutable. Once created, a seal’s content and metadata never change. The hash chain guarantees this: tamper with any seal and every subsequent chain hash breaks.
Unlike git, writ never rewrites history. There’s no rebase, no amend, no force push. If something goes wrong, you writ restore to a previous seal and seal the restored state. The old seals stay in the log. Every seal is an immutable snapshot. This is what makes safe rollback possible in fully autonomous environments where agents operate without human oversight.
Creating a Seal
# Minimal
writ seal -s "added login endpoint"
# With full metadata
writ seal -s "auth system complete" \
--agent auth-dev \
--spec auth-migration \
--status complete \
--tests-passed 42 \
--tests-failed 0 \
--linted
# Python SDK
repo.seal(
summary="auth system complete",
agent_id="auth-dev",
spec_id="auth-migration",
status="complete",
tests_passed=42,
)
Viewing Seal History
# Recent seals
writ log
# All seals, including from diverged branches
writ log --all
# Seals for a specific spec
writ log --spec auth-migration
# Inspect a specific seal
writ show a7c2e8f4b31a --diff
Hash Chains
Every seal contains three hashes:
- Content hash: BLAKE3 hash of the seal’s own data (files, metadata). Proves the seal hasn’t been modified.
- Parent seal hash: BLAKE3 hash of the previous seal. Links seals into a chain.
- Chain hash: BLAKE3 hash combining the content hash and parent seal hash. Provides tamper evidence across the entire history.
Verify the chain at any time:
writ verify
If any seal has been tampered with, verification fails and reports exactly where the chain broke.
Next Steps
- Specs and Agents to understand how work is organized
- Security Model for the full cryptographic integrity story
Specs and Agents
Orchestration frameworks coordinate what agents do. Writ controls what agents produce. Two concepts make this possible: specs (structured requirements) and agents (registered identities with trust levels).
Specs
A spec is a structured requirement that agents work against. In git, the relationship between code and requirements is implicit. A branch named feature/auth is convention. Nothing enforces it. Nothing links a commit to a ticket. Nothing tells an agent “these are the files you should be touching.”
In writ, that relationship is explicit and machine readable.
Creating a Spec
writ spec add "JWT-based auth with token refresh and role-based access"
Or during install:
writ init --spec auth --title "Authentication System"
What a Spec Contains
| Field | Purpose |
|---|---|
id | Unique identifier, used in --spec flags |
title | Human readable name |
description | What needs to be built |
status | Current state (in-progress, complete, blocked) |
depends_on | Other spec IDs this depends on |
file_scope | Which files this spec is expected to touch |
Lifecycle
Specs move through a managed lifecycle:
active -> stale -> completed -> archived
-> cancelled -> archived
- Active: Work is happening. Seals reference this spec.
- Stale: No activity for a configurable period.
writ contextwarns about stale specs so nothing falls through the cracks. - Completed: All work is done. Marked with
writ spec complete <id>. - Cancelled: Abandoned. Marked with
writ spec cancel <id>. - Archived: Eligible for garbage collection after retention period.
Why Specs Matter
When a sub agent in an automated pipeline modifies a file that another sub agent depends on, git has no structured record of what changed, who changed it, or which requirement it serves. Writ does:
writ seal -s "added token refresh" --agent dev-1 --spec auth
This seal is now permanently linked to the auth spec. Context output groups work by spec. Convergence merges spec by spec. Scope enforcement restricts agents to their spec’s declared files. This is the structured provenance that makes multi agent workflows manageable at scale.
Agents
An agent is a registered identity in writ. It can represent an AI model, a human developer, or an automated system.
Registering an Agent
writ agent register --id backend-dev --role implementer --trust-level standard
Trust Levels
| Level | Description | Convergence Impact |
|---|---|---|
full | Unrestricted. Typically the project owner or lead agent. | Full confidence scoring |
standard | Normal working agent. The default for most agents. | Standard confidence scoring |
restricted | Limited scope. For agents that should only touch specific files. | Reduced confidence caps |
untrusted | New or unverified agents. | Lowest confidence caps, more likely to escalate |
Trust levels directly affect convergence. When two agents’ changes conflict, the higher trust agent’s changes receive a confidence boost. An untrusted agent’s changes are more likely to be escalated for human review rather than auto resolved. This matters most in zero trust environments where every agent action needs verification, and in fully autonomous pipelines where human review isn’t available for every merge.
Agent Identity in Practice
Every seal records which agent created it:
writ seal -s "implemented login" --agent backend-dev --spec auth
writ context shows per agent activity: which files each agent has modified, their latest work, and how many seals they’ve created. For a fleet of agents working concurrently, this is the difference between “which of my 12 agents touched this file” being a five minute archaeology project versus a single structured lookup.
Scope Enforcement
Agents can be constrained to specific files or directories:
# Register an agent with scope constraints
writ agent register --id frontend-dev --role implementer \
--trust-level standard --scope "src/components/*,src/styles/*"
When scope enforcement is enabled, sealing changes to files outside the agent’s scope triggers a warning (or a rejection, if configured to be strict):
writ seal -s "updated database config" --agent frontend-dev --enforce-scope
# Warning: frontend-dev modified src/db/config.py, which is outside scope
Scope violations appear in writ context and in the security event log.
Suspension and Revocation
Agents can be suspended (temporarily blocked from sealing) or revoked (permanently deactivated) without deleting their history. Every seal they created remains in the log. In autonomous environments, this is how you respond to a compromised or misbehaving agent without losing the audit trail.
How Specs and Agents Work Together
The combination of specs and agents gives writ something git fundamentally lacks: structured provenance.
writ context --format human
Shows:
SPECS:
auth (in-progress): Authentication System
agents: backend-dev (12 seals), test-bot (3 seals)
files: src/auth.py, src/middleware.py, tests/test_auth.py
payments (in-progress): Payment Integration
agents: payments-dev (8 seals)
files: src/payments.py, src/stripe.py
INTEGRATION RISK: LOW (score: 15)
No file contention between specs
Every piece of work is linked to a requirement and attributed to an identity. Convergence uses this metadata to make better merge decisions. Security auditing uses it to track who changed what and whether they were authorized. And context delivers all of it in one call: structured data an agent can act on immediately.
Next Steps
- Convergence to see how specs and agents inform merge decisions
- Security Model for trust levels, scope enforcement, and audit trails
Convergence
Giving an agent better git access doesn’t make git merge smarter. When five agents work concurrently and three of them touch the same file, the merge problem doesn’t go away because you gave each agent its own worktree. The work still has to come back together.
Git’s merge is line based. It compares text and gives up when two sides change the same region. It doesn’t know the difference between an import and a function body. It can’t tell that two agents adding different functions to the same file is perfectly safe, not a conflict. The tool was built for humans resolving one merge at a time, not for fleets of agents producing changes that need to compose automatically.
Writ’s convergence engine merges agent work using sealed histories and genesis trees. Instead of comparing raw text on disk, it reads each agent’s sealed snapshots and uses the genesis state (a snapshot of the codebase at spec creation) as the common ancestor. Non-conflicting changes merge automatically. Real conflicts escalate with structured context and confidence scores. The core principle: compose, don’t choose. Multi agent work is fundamentally additive. Agents build complementary features. The engine preserves all contributions wherever possible and escalates clearly when it can’t.
How It Works
Pool Filter
Before any merging begins, a three layer filter determines which specs participate:
- Epoch boundary: only specs from the current session (since the last
writ finish) - Commit state: only uncommitted specs
- Genesis tree: structural filtering against the common ancestor
This prevents completed, already committed, or irrelevant specs from interfering with the merge.
Genesis Tree Merge
Every spec has a genesis tree: a snapshot of the file index at the moment the spec was created. Writ’s convergence engine uses this as the common ancestor:
- Base: the genesis tree (what files looked like when the spec started)
- Left: spec A’s sealed version of the file
- Right: spec B’s sealed version of the file
Non-conflicting changes (different lines, additive edits, independent sections) merge automatically. When both specs modify the same region in incompatible ways, the Escalate strategy auto-resolves by selecting the more complete version. If that’s ambiguous, the conflict escalates with structured context for human or orchestrator review.
Shadow Materialization
Merge results are stored in the object store as shadow state, not written to disk. This means convergence can run while agents are still working without disturbing anyone’s files. Disk materialization only happens at writ finish.
Verification
The HardenedVerifier runs integrity checks on every merged output:
- Duplicate definitions: No function or class name appears twice
- Balanced delimiters: Brackets, braces, and parentheses are balanced
- Content loss detection: Warns if significant content from either side was lost
- Conflict marker scan: Ensures no
<<<<<<<markers leaked into output
If verification fails, the merge is rejected. Writ never silently applies a broken merge.
When Convergence Runs
- At
writ spec done: checks for overlapping work with other completed specs - At
writ finish: final backstop that catches anything remaining before git commit - Manually:
writ converge-all --applyfor explicit control
Confidence Thresholds
| Range | Action |
|---|---|
| >= 0.85 | Auto resolve. The merge is highly confident. Apply without human review. |
| 0.60 to 0.84 | Suggest. Present as a recommendation for review. |
| < 0.60 | Escalate. The conflict is too ambiguous for automation. Escalate to a human or orchestrator agent. |
Running Convergence
# Merge all diverged specs, escalate what can't be auto resolved
writ converge-all --apply --strategy escalate
# Preview what would happen without applying
writ converge-all --dry-run
# Two spec convergence
writ converge backend frontend --apply
# Python SDK
report = repo.converge_all(strategy="escalate", apply=True)
print(f"Merged {len(report['merge_order'])} specs")
print(f"Auto-merged: {report['total_auto_merged']}")
Strategies
| Strategy | Behavior |
|---|---|
escalate | Auto resolve high confidence merges, escalate the rest. Recommended for most workflows. |
three-way-merge | Standard three way merge. Leaves conflict markers where resolution fails. |
most-recent | Prefers the most recently sealed version on conflict. |
orchestrator | Reports all conflicts as structured JSON for an orchestrator agent to resolve programmatically. |
The orchestrator strategy is designed for automated pipelines. Instead of <<<<<<< markers that need text parsing, conflicts come back as structured JSON that orchestrator agents can resolve programmatically. This is the convergence equivalent of what writ context does for project state: structured data, not text to parse.
Merge Ordering
When multiple specs have diverged, writ optimizes the merge order:
- Specs that touch disjoint files merge first (zero conflict risk)
- Specs with minimal overlap merge next
- Specs with high overlap merge last, benefiting from the cleaner base established by earlier merges
This greedy overlap minimizing approach reduces total conflict complexity. For a fleet of agents working across many specs, ordering matters. It’s the difference between cascading conflicts and clean sequential merges.
Integration Risk
Before starting work or after convergence, check the risk level:
writ context --format human
INTEGRATION RISK: HIGH (score: 65)
7 diverged specs (>3)
file touched by 11 agents (>=5)
6 scope violations (>5)
Integration risk is scored 0 to 100 based on:
- Number of diverged specs
- File contention (files touched by multiple agents)
- Scope violations
- Number of active agents
When risk is high, converge before starting new work. Context surfaces this automatically so agents don’t need to discover it themselves.
Example: Additive Changes
Two agents both modify app.py. Agent A adds:
from auth import validate_token
Agent B adds:
from payments import process_charge
Git sees conflicting changes to the import block and produces conflict markers. Writ’s convergence engine recognizes both as non-conflicting additive changes and composes them:
from auth import validate_token
from payments import process_charge
Auto resolved. No human intervention needed.
Example: Real Conflict
Agent A changes calculate_tax() to use a flat rate. Agent B changes it to use a progressive rate. These are incompatible implementations of the same function.
Writ classifies this as a real conflict with no automatic resolution. It escalates with full context:
{
"file": "billing.py",
"conflict_type": "BothModified",
"region": "calculate_tax function body",
"left_agent": "billing-dev",
"right_agent": "tax-specialist",
"confidence": 0.30,
"recommendation": "Manual review required"
}
Structured data, not text to parse. An orchestrator agent or human can review and decide.
In Development
Two additional convergence phases are implemented and feature flagged for future releases:
Spec aware resolution (Phase 4). Uses writ’s first class spec metadata (file scope, acceptance criteria, design notes) to resolve ambiguous conflicts. Does this file belong to spec A or spec B? Which spec has this file in its declared scope? Which agent has the higher trust level? Spec context gives convergence something no other VCS can offer.
LLM assisted resolution (Phase 5). Sends unresolved conflicts to an LLM for resolution with full context. The LLM can select, reorder, and combine from the inputs. Composition only, never novel code generation. Includes content traceability: every line in merged output must trace back to an input, preventing hallucinated or injected content from reaching the working tree.
Both phases use the same structured metadata that writ context surfaces. When enabled, they slot into the pipeline between the genesis tree merge and verification.
Next Steps
- Security Model for how trust levels affect convergence confidence
- Convergence Resolution Guide for handling escalations
- Multi Agent Workflow for production patterns
Output Formats
For most version control systems, output format is an afterthought. Humans read the terminal, so the output is text. But writ’s primary consumer isn’t a human reading a terminal. It’s an LLM agent parsing structured data into its context window. Every unnecessary token in that output is a token the agent can’t spend on reasoning.
This is the token tax. JSON’s repeated key names, braces, quotes, and commas are structural overhead that carries no information for an agent that already knows the schema. For a single context call, the cost is small. For a fleet of agents making dozens of context calls per session, it compounds into real compute savings.
Writ’s format system addresses this directly. Every command that produces structured output supports multiple formats, and the choice is configurable at the global, project, or per command level.
Available Formats
| Format | Flag | Description |
|---|---|---|
| JSON | json | Standard JSON with indentation. Maximum compatibility. Human readable. |
| JSON Compact | json-compact | Minified JSON with no whitespace. Smaller than pretty JSON, still parseable by any JSON library. |
| TOON | toon | Token Oriented Object Notation. Field names declared once, rows streamed as values. Designed for LLM consumption. |
The Token Tax
Consider a typical context response for a project with 20 tracked files. In JSON, every row repeats every key name:
{
"files": [
{"path": "src/main.rs", "hash": "a3f2b1c...", "modified": "2026-03-04T10:00:00Z", "agent": "cc", "spec": "S-041"},
{"path": "src/lib.rs", "hash": "b4e3c2d...", "modified": "2026-03-04T09:45:00Z", "agent": "amis", "spec": "S-039"},
{"path": "src/convergence.rs", "hash": "c5f4d3e...", "modified": "2026-03-04T09:30:00Z", "agent": "cc", "spec": "S-041"}
]
}
"path", "hash", "modified", "agent", "spec". Repeated for every single row. For 20 files with 5 keys, that’s 100 redundant key tokens before you even count the quotes, braces, colons, and commas.
The same data in TOON:
files[20]{path,hash,modified,agent,spec}:
src/main.rs,a3f2b1c...,2026-03-04T10:00:00Z,agent-1,S-041
src/lib.rs,b4e3c2d...,2026-03-04T09:45:00Z,agent-2,S-039
src/convergence.rs,c5f4d3e...,2026-03-04T09:30:00Z,agent-1,S-041
Field names declared once in the header. Row count declared explicitly. No braces, no repeated keys, no quotes unless a value contains a delimiter. The LLM receives identical information in significantly fewer tokens.
Benchmarks
All numbers come from writ’s benchmark suite, which runs in CI using tiktoken cl100k_base (the BPE tokenizer used by GPT-4 and close enough to Claude’s tokenizer for benchmarking). The benchmark generates a realistic ContextOutput struct with 5 specs, 10 recent seals, and 40 tracked files, the same struct that repo.context() returns in production, and formats it through each formatter. Results are verified against Claude’s actual tokenizer via the Anthropic API.
Git vs Writ: cost per capability
The core question: what does it cost an agent to understand project state?
Without writ, an agent runs multiple git commands (git status, git log, git diff --stat, git branch -a), each returning unstructured text that needs parsing and synthesis. With writ, one call returns structured data ready for immediate consumption.
| Source | Tokens | Capabilities | Tokens per Capability |
|---|---|---|---|
| git (5 commands) | 895 | 4 | 224 |
| writ context (TOON) | 1,685 | 10 | 168 |
Git capabilities (4): working state, commit history, diff stat, branches.
Writ capabilities (10): working state, seal history, diff, specs, agent activity, integration risk, convergence status, file contention, chain integrity, session state.
Writ’s total token count is higher because it delivers 2.5x more information. But per capability, writ is 25% more efficient. It does it in a single call with structured output, versus five separate commands returning text that the agent has to parse, correlate, and synthesize.
TOON vs JSON: format efficiency
Within writ, TOON reduces the structural overhead of the output itself. Real byte savings on representative project data:
| Data Type | Savings (TOON vs JSON) |
|---|---|
| Seal log (20 seals) | ~33% |
| Full context | ~20% |
| Spec list | ~10% |
Actual token savings from BPE tokenization are more modest than byte savings. BPE tokenizers are smart about merging structural characters ({" becomes one token, ": " becomes one token). The measured token reductions are approximately 29% on seal logs, 15% on full context, and 10% on spec lists. Still meaningful, still compounding at scale, but we report the real numbers.
Fleet scaling
At scale, the efficiency compounds. A 10 agent fleet reading context 5 times each makes 50 writ calls vs 250 git commands. The token cost is 84,250 (writ) vs 44,750 (git). But writ delivers structured, pre-parsed output with full multi-agent awareness. Git output requires each agent to parse, correlate, and synthesize five separate command outputs, adding interpretation overhead that doesn’t show up in raw token counts.
The tool call reduction alone is significant. 50 calls vs 250 calls means fewer round trips, less orchestration complexity, and less context window consumed by intermediate parsing.
Adaptive output
Writ context is adaptive. Empty sections are omitted entirely. No diverged branches means the field doesn’t appear, no scope violations means no violations section, low integration risk from a solo agent means no risk block. This isn’t a format trick. It’s a design principle. Solo agents get a lean context. Fleet deployments get the full picture. The output scales with complexity, not with a fixed schema.
Verifying benchmarks
Writ’s token efficiency claims are tested in CI. You can reproduce them yourself.
Rust benchmarks (tiktoken cl100k_base)
Requires the writ source and Rust toolchain:
git clone https://github.com/andrew-garfield101/writ.git
cd writ
cargo test -p writ-core benchmark -- --nocapture
This runs 10 benchmark tests covering context, seal logs, spec lists, fleet scaling, and per-section token breakdowns.
Anthropic API benchmarks (ground truth Claude counts)
Requires Python and an Anthropic API key:
pip install anthropic
export ANTHROPIC_API_KEY=sk-ant-...
python scripts/anthropic-token-bench.py
Add --live to benchmark against your own writ repo:
python scripts/anthropic-token-bench.py --live
These use Claude’s actual tokenizer via the API, giving exact token counts rather than estimates.
Choosing a Format
Use TOON when the consumer is an LLM agent. This is the primary use case writ was designed for. TOON delivers the same structured data in fewer tokens, leaving more room for reasoning. This is the recommended format for agentic workflows.
Use JSON when you need maximum compatibility. JSON is the universal interchange format: every language, every tool, every pipeline can parse it. Use JSON for debugging, for piping writ output to other tools, or when you’re unsure what will consume the output.
Use JSON Compact when you want JSON compatibility with reduced whitespace. This is a middle ground: parseable by any JSON library, smaller than pretty JSON, but not as token efficient as TOON.
Configuration
Format preference follows a resolution chain. Higher priority overrides lower:
CLI flag --format toon (highest priority)
Environment var WRIT_FORMAT=toon
Project config .writ/config.toml [output] format = "toon"
Global config ~/.writ/config [output] format = "toon"
Default json (lowest priority)
Setting Format Globally
During writ init, you choose a default format that applies to all projects:
writ config --global output.format toon
Or set it during first run setup, where TOON is presented as the recommended option for agent workflows.
Setting Format Per Project
Override the global default for a specific project:
writ config output.format json
This writes to .writ/config.toml and applies only to the current project.
Setting Format Per Command
Override everything with a flag:
writ context --format toon
writ context --format json
writ context --format json-compact
writ context -f toon # short flag
Environment Variable
For CI and scripting where you want all writ output in a specific format:
export WRIT_FORMAT=toon
writ context # uses TOON without --format flag
Commands That Support Formats
| Command | Notes |
|---|---|
writ context | Primary use case for TOON. Full project state. |
writ log | Seal history. Highly tabular, ideal for TOON. |
writ spec status | Active specs and their state. |
writ status | Fleet overview: agents, specs, progress. |
writ show | Single seal detail. |
Python SDK
The Python SDK supports format selection through the format parameter:
import writ
repo = writ.Repository.open(".")
# Default: returns Python dict (parsed internally, no format overhead)
ctx = repo.context()
# TOON string — for embedding directly in LLM prompts
ctx_toon = repo.context(format="toon")
# JSON string — for debugging or piping to tools
ctx_json = repo.context(format="json")
The format="dict" default is what most Python code wants: a native Python dict with no serialization cost. Use format="toon" when building prompts to send to an LLM, so the context string goes directly into the prompt without re-serialization:
import writ
context_str = writ.context(format="toon")
prompt = f"""Here is the current project state:
{context_str}
Your task: implement the storage compression feature per spec S-042.
"""
# context_str is already TOON — minimal tokens, maximum information
TOON Format Reference
TOON (Token Oriented Object Notation) uses a tabular header format. Field names are declared once, then each row is a comma separated list of values in the same order.
Syntax
section_name[row_count]{field1,field2,field3}:
value1,value2,value3
value4,value5,value6
Rules
- Unquoted strings: Values without commas or newlines are unquoted (
src/main.rs) - Quoted strings: Values containing commas or newlines are quoted (
"commit message, with comma") - Empty values: Represented as empty fields between commas (trailing comma for last field)
- Row count: Always declared in the header (
[20]). Helps agents validate completeness. - Unicode: Passed through as is. TOON is UTF-8.
- Nested data: Falls back to indented object notation for non tabular structures (rare in writ’s output).
Full Context Example
# writ context | project: my-app | format: toon | timestamp: 2026-03-04T12:00:00Z
files[3]{path,hash,modified,agent,spec}:
src/main.rs,a3f2b1c,2026-03-04T10:00:00Z,agent-1,S-041
src/lib.rs,b4e3c2d,2026-03-04T09:45:00Z,agent-2,S-039
src/convergence.rs,c5f4d3e,2026-03-04T09:30:00Z,agent-1,S-041
seals[2]{id,summary,agent,timestamp,spec}:
seal-0041,Implement phase 3 pattern matching,agent-1,2026-03-04T10:00:00Z,S-041
seal-0039,Auth middleware and token validation,agent-2,2026-03-04T09:45:00Z,S-039
specs[2]{id,description,status,agent}:
S-041,Convergence phase 3,active,agent-1
S-039,Auth middleware,complete,agent-2
The single line comment header at the top costs a few tokens but gives the LLM metadata about what it’s reading: project name, format, timestamp. Negligible cost, meaningful orientation.
Next Steps
- Configuration for the full config file reference
- CLI Reference for all commands that support
--format - Python SDK for programmatic format selection
Security Model
Writ is built for environments where multiple autonomous agents have write access to the same codebase. That demands security guarantees that traditional VCS was never designed for.
Whether you’re running a zero trust setup where every agent action is verified, a fully autonomous system where agents operate without human oversight, or a mixed environment with humans in the loop. The security model is the same. Trust nothing by default. Verify everything cryptographically. Log every security relevant action.
Cryptographic Seal Chains
Every seal is linked to its predecessor through a chain of BLAKE3 hashes. Tamper with any seal and the entire chain breaks.
How It Works
Each seal contains three cryptographic fields:
| Field | What It Hashes | What It Proves |
|---|---|---|
content_hash | The seal’s own data (files, summary, metadata) | This seal hasn’t been modified |
parent_seal_hash | The previous seal’s content | The previous seal is the real predecessor |
chain_hash | Combination of content hash and parent seal hash | The entire history up to this point is intact |
Verification
# Verify the full chain from genesis to HEAD
writ verify
# Verify a specific seal
writ verify --seal a7c2e8f4b31a
Both commands support --format json for programmatic consumption and --format human for readable output.
If any seal has been tampered with, verification reports exactly where the chain broke and what was expected versus what was found.
Ed25519 Signatures
Seals can be signed with Ed25519 digital signatures, authenticating who created them. Writ generates a dedicated convergence keypair on writ init, stored in .writ/keys/ with AES-GCM encryption and restricted file permissions (0600).
This means convergence seals (created by the engine during merge) are cryptographically distinguishable from agent seals (created by agents during work). You can always verify whether a merge result came from the convergence engine or was manually crafted.
Agent Identity
Every agent in writ is a registered entity, not just a string ID. In environments where dozens of agents have write access, knowing who did what, and being able to prove it cryptographically, is not optional.
Registration
writ agent register --id backend-dev --role implementer --trust-level standard
Trust Levels
| Level | Description | Impact |
|---|---|---|
| Full | Project owner or lead. Unrestricted access. | Full confidence scoring in convergence. |
| Standard | Normal working agent. The default. | Standard confidence scoring. |
| Restricted | Limited scope. Constrained to specific files. | Reduced confidence caps. Changes more likely to be reviewed. |
| Untrusted | New or unverified. | Lowest confidence caps. Changes almost always escalated. |
Trust levels directly affect convergence. When two agents’ changes conflict, the engine factors in their trust levels when scoring confidence. An untrusted agent’s changes receive lower confidence, making auto resolution less likely and human review more likely. This is how writ scales security across fleets of agents with varying levels of trust: from a known, verified lead agent to a newly introduced model that hasn’t earned trust yet.
Suspension and Revocation
Agents can be:
- Suspended: Temporarily blocked from creating seals. All existing seals remain.
- Revoked: Permanently deactivated. History preserved, but the agent cannot create new seals.
Both actions are recorded as security events in the audit log. In a fleet of autonomous agents, this is how you respond to a compromised agent immediately. Suspend it, review the damage, and all seals created after the compromise timestamp are automatically flagged.
Scope Enforcement
Specs declare which files they own. Agents can be constrained to specific files or directories.
How It Works
- A spec declares its file scope:
src/auth/*,tests/test_auth.py - An agent is registered with scope constraints
- When the agent seals changes to files outside its scope, writ responds based on configuration:
- Warning mode (default): The seal succeeds but a scope violation is logged
- Enforce mode: The seal is rejected
Configuration
Scope enforcement is configurable:
# Warning mode (default): seal succeeds, violation logged
writ seal -s "updated config" --agent frontend-dev
# Enforce mode: seal rejected if out of scope
writ seal -s "updated config" --agent frontend-dev --enforce-scope
Visibility
Scope violations appear in three places:
writ contextoutput (under scope violations)- Security event log (
writ security events) - Integration risk scoring (violations increase the risk score)
No more agents silently modifying files they shouldn’t touch.
Content Traceability (In Development)
Content traceability is part of the LLM assisted convergence pipeline (Phase 5), currently feature flagged for a future release. When enabled, it enforces a strict rule: every line in merged output must trace back to an input (base, left, or right).
This will prevent:
- Hallucinated content from leaking into merge results when an LLM is involved in resolution
- Convergence bugs that might inject novel content
- Silent data corruption during complex multi way merges
Security Event Monitoring
Writ maintains an append only security event log that records:
| Event Type | When It Fires |
|---|---|
scope_violation | Agent seals changes outside its declared scope |
chain_hash_failure | Seal chain verification detects tampering |
authentication_failure | Signature verification fails |
agent_revoked | An agent is revoked or suspended |
convergence_low_confidence | Convergence produces results below the confidence threshold |
unrecognized_agent | A seal references an unknown agent ID |
Viewing Events
# All events
writ security events
# Filter by severity
writ security events --severity warning
# Filter by event type
writ security events --event-type scope_violation
Events are severity classified (info, warning, critical) with configurable retention. GC can clean old events past their retention period, but the event log is append only during normal operation.
Summary
| Layer | What It Protects |
|---|---|
| Hash chains | History integrity. No seal can be modified after creation. |
| Signatures | Authorship. Every seal can be verified back to its creator. |
| Trust levels | Convergence quality. Lower trust agents get more scrutiny. |
| Scope enforcement | File boundaries. Agents stay in their lane. |
| Content traceability | Merge integrity. No hallucinated or injected content survives. (In development, part of LLM convergence pipeline) |
| Event monitoring | Auditability. Every security relevant action is logged. |
Next Steps
- Seals vs Commits for the data model behind seal chains
- Convergence for how trust levels affect merge decisions
- Troubleshooting for common verification failures and how to resolve them
Multi Agent Workflow
Multiple agents work in the same project directory. Each agent seals its own changes with --spec, and writ keeps them separate. No workspaces, no branches, no path copying. Agents work. Writ handles the rest.
The Flow
# One time setup
writ init
# Define tasks (optional — agents can also create their own specs)
writ plan "Implement OAuth2 auth" "Add Stripe payments" "Build admin dashboard"
# Launch agents however you normally do
# Agents discover specs via writ context, claim one, work, seal, done.
# Convergence runs automatically at spec done and at finish.
# When ready
writ finish
git push
Three writ commands for the human: init, plan, finish. Plan is optional. Everything between init and finish is the agent’s world.
Same Steps as Git
The biggest misconception writ needs to fight: “it’s more stuff to learn.” It’s not. It’s the same number of steps as git, mapped to agent native concepts.
Git (human workflow): Writ (agent workflow):
--------------------- ----------------------
git init writ init
git checkout -b feature (agents just start working)
write code agents work
git add . && git commit writ seal (agents do this)
git checkout main && git merge writ finish (converges + commits)
git push git push
Same rhythm. Same mental model. The concepts map directly:
| Git Concept | Writ Concept | What Improves |
|---|---|---|
| (nothing) | Spec | Task tracking with lifecycle, agent claiming, genesis snapshots. No git equivalent |
| Commit | Seal | Agent identity, spec linkage, immutable chain |
| Merge | Converge | Writ’s convergence engine merges seal trees. Auto resolves independent changes |
| Worktree | Workspace | Shared object store, scoped context, native convergence (Level 2 only) |
git status | writ context | One call, structured output, token optimized |
git log | writ log | Filterable by spec, agent, workspace |
| (nothing) | writ plan | Batch task definition |
The Human’s Commands
writ init Once per project
writ plan -f tasks.txt Once if pre-defining tasks (optional)
writ status Whenever you want to check in
writ watch Live seal monitoring (optional)
writ finish Once when work is done
git push Standard git
Most sessions: init, finish, push. Three commands total. Two of those are git.
The Agent’s Commands
writ context Understand the project and find available specs
writ spec claim <id> Claim a spec (or auto-claim on first seal)
writ seal -s "..." --spec Checkpoint work (captures only this spec's changes)
writ spec done <id> Mark task complete
The agent never creates workspaces, never runs convergence, never touches git. It discovers its task via context, claims it, works, seals, and reports done. Everything else is writ’s job.
Spec-Scoped Sealing
This is the foundation that makes same-directory multi-agent work possible.
When an agent runs writ seal -s "auth endpoint" --spec auth-feature, writ captures only the files that changed since this agent’s last seal for this spec. Other agents’ changes are invisible to the seal, not because they’re in a different directory, but because writ knows they belong to a different spec.
# 4 agents, same directory, zero ceremony
cd my-project
writ init
# Terminal 1: Agent works on auth (touches src/auth/)
# Terminal 2: Agent works on payments (touches src/payments/)
# Terminal 3: Agent works on UI (touches src/components/)
# Terminal 4: Agent works on docs (touches docs/)
# Each agent seals only its own changes
# Agent 1: writ seal -s "auth endpoint" --spec auth → only src/auth/ files
# Agent 2: writ seal -s "stripe setup" --spec payments → only src/payments/ files
# No cross-contamination. No "no changes to seal" errors.
Sealing without --spec still captures the entire working directory for backward compatibility. Single-agent and human workflows are unchanged.
Three Isolation Levels
Not every multi-agent scenario needs the same kind of isolation. Writ provides three levels, each building on the last.
Level 0: No Isolation Needed (Most Common)
Agents touch different files. Agent A works on src/auth/, Agent B works on src/payments/. They never modify the same file. Spec-scoped sealing keeps each agent’s changes in its own seals. No convergence needed because there’s nothing to converge.
Agent A seals: src/auth/login.py, src/auth/tokens.py
Agent B seals: src/payments/stripe.py, src/payments/webhooks.py
→ No overlap. Each seal contains only its own files.
→ writ finish combines everything into one git commit.
This is the majority of multi-agent work. Good task decomposition naturally separates file concerns. Writ handles it with zero overhead.
Level 1: Additive Overlap (Common)
Agents add to the same file in different sections: different functions, different config blocks, different test cases. The changes are independent and can be merged. Writ’s convergence engine handles this automatically at writ spec done and as a backstop at writ finish.
Agent A seals: added loginWithGoogle() to auth.ts
Agent B seals: added loginWithGitHub() to auth.ts
→ Agent A calls spec done → convergence checks for overlaps
→ convergence engine: both are independent function additions
→ auth.ts now has both functions
→ merged automatically, no human intervention
Two agents both adding to a shared config file, both adding functions to a utility module, both adding routes to an API file. The convergence engine handles it because non-conflicting additions compose automatically.
Level 2: Competing Rewrites (Rare)
Agents rewrite the same code in fundamentally different ways. Agent A rewrites the entire auth module using PKCE flow. Agent B rewrites it using implicit flow. They need separate copies of the file to work from because each agent’s changes would break the other’s in-progress work.
THIS is when you need workspaces:
writ task "rewrite auth: PKCE approach" # creates isolated workspace
writ task "rewrite auth: implicit approach" # creates isolated workspace
# launch agents in workspace directories
# each has their own copy of auth.ts
# convergence (or human decision) reconciles at the end
Level 2 is rare. Most multi-agent work is Levels 0 and 1. But when it happens, workspaces are there. The writ task flow and all workspace plumbing remain available as the advanced tool for genuine physical isolation. See the Workspaces guide for the full setup.
The Hierarchy
Level 0: Different files → spec-scoped sealing (automatic)
Level 1: Same file, additions → convergence engine (auto at spec done / finish)
Level 2: Same file, rewrites → workspaces (explicit via writ task)
Users start at Level 0. Most stay there. If they hit Level 1, convergence handles it automatically when specs complete. If they genuinely need Level 2, they opt in with writ task. Each level is discovered when needed, not configured upfront.
Writ Watch
writ watch is a live monitoring tool that shows seal events as they happen. It gives you a real time view of agent activity without needing to poll writ status.
Starting the Watcher
writ watch
Runs in the foreground, showing real time output:
$ writ watch
writ watch active — monitoring for new seals...
[10:15:03] seal a3f8b2c9 (agent-1, auth-feature): 3 files
[10:15:47] seal b7e2a4f1 (agent-2, payments): 2 files
[10:16:12] seal d4c1e8a6 (agent-3, auth-feature): 1 file — overlaps with agent-1
[10:22:05] seal e9f3c7d2 (agent-4, dashboard): 4 files
Press q to quit.
Run this in a dedicated terminal tab for passive visibility into agent activity. Overlapping files are flagged so you can track where convergence will be needed.
How Convergence Works
Convergence runs automatically at two points:
writ spec done: when an agent marks its task complete, convergence checks for overlapping work with other completed specswrit finish: final backstop that catches anything remaining before git commit
You do not need to run convergence manually in the normal workflow. Agents work, call spec done, and convergence handles the rest. writ converge-all --apply is available for explicit manual control when needed.
Configuration
# .writ/config.toml
[watch]
interval = 5 # polling interval in seconds (default: 5)
CLI overrides:
writ watch --interval 10 # custom polling interval
writ watch --daemon # run as background process
writ watch --stop # stop running daemon
writ watch --status # show daemon status
Writ Plan
writ plan is batch spec creation for medium-to-large scale task setup. It reads a list of tasks and creates specs for all of them in one shot.
# From inline arguments
writ plan "Implement OAuth2 auth" "Add Stripe payments" "Build admin dashboard"
# From a file (one task per line)
writ plan -f tasks.txt
# From stdin (pipe from any tool)
cat tasks.txt | writ plan
Output:
$ writ plan "Implement OAuth2 auth" "Add Stripe payments" "Build admin dashboard"
3 specs created:
implement-oauth2-auth "Implement OAuth2 auth"
add-stripe-payments "Add Stripe payments"
build-admin-dashboard "Build admin dashboard"
Next: launch your agents. They discover specs via `writ context`.
Titles are slugified into spec IDs automatically. Agents discover unclaimed specs via writ context and claim them.
Three Paths to Scale
Path A: Agent Self-Organization (1-5 agents)
The agent creates its own spec from its prompt. Zero ceremony. Human just launches agents.
writ init
# launch agents with prompts
# agents: context → spec add → work → seal → spec done
writ finish
Path B: Batch Planning (5-50 agents)
User defines a task list. Agents discover and claim.
writ init
writ plan -f tasks.txt
# launch agents
# agents: context → claim spec → work → seal → spec done
writ finish
Path C: Programmatic SDK (50+ agents)
Orchestrator creates specs and launches agents via code.
import writ
repo = writ.Repository.open(".")
for task in tasks:
repo.add_spec(id=task.id, title=task.title)
orchestrator.launch_agent(task)
All three paths converge: specs exist → agents work (same directory, spec scoped sealing) → convergence runs at spec done and finish → git push. One set of internals. Three entry points for different scales.
Spec Claiming
When an agent runs writ context and sees unclaimed specs, it picks one up:
$ writ context
unclaimed_specs:
implement-oauth2-auth "Implement OAuth2 auth"
add-stripe-payments "Add Stripe payments"
build-admin-dashboard "Build admin dashboard"
The agent claims a spec explicitly:
writ spec claim implement-oauth2-auth
# "Claimed spec implement-oauth2-auth for agent claude-1"
Or implicitly via first seal:
writ seal -s "started auth work" --spec implement-oauth2-auth
# Spec auto-claimed by this agent on first seal
Once claimed, the spec does not appear as unclaimed for other agents. This prevents duplicate work.
Checking Progress
$ writ status
Active 3 agents 2 specs in progress
Done 1 agent 1 spec completed (not committed)
a3f8b2c9 implement-oauth2-auth agent-1 3 seals working
b7e2a4f1 add-stripe-payments agent-2 complete (5 seals)
d4c1e8a6 build-admin-dashboard agent-3 1 seal working
1 spec complete · run `writ finish` when ready
Full transparency into what every agent is doing without branch archaeology or parsing commit messages.
Finishing
writ finish promotes completed work to git. Convergence runs as a final backstop, catching any remaining overlaps before committing.
$ writ finish
3 specs ready to commit.
Commit strategy: single (all specs in one commit)
[main abc1234] feat: OAuth2 auth, Stripe payments, admin dashboard
$ git push
Convergence also runs at writ spec done, so most overlaps are already resolved by finish time.
Troubleshooting
“No changes to seal”
This means nothing changed since your last seal for this spec. Check:
- Did you modify any files since your last seal?
- Are you using
--speccorrectly? Without--spec, writ captures the full directory.
Convergence conflict flagged
Run writ status to see which files have conflicts. The conflict means two agents rewrote the same code in incompatible ways. The human resolves it by choosing one version or manually merging.
Agent not seeing other agents’ work
Run writ context to refresh. Convergence runs at writ spec done and writ finish. To merge outstanding overlaps manually, run writ converge-all --apply.
Next Steps
- Workspaces for Level 2 physical isolation when competing rewrites require separate directories
- Convergence for the deep dive on how writ merges agent work
- Workflow Modes for commit automation options
- CLI Reference for the full command reference
Workflow Modes
Writ’s workflow scales from a solo developer pressing enter through prompts to an enterprise fleet of 500 agents committing autonomously. The scaling happens in the automation layer between agents and git, not in the user’s interface. One agent or 500, the user checks in with writ status, promotes work with writ finish, and pushes with git push.
Two modes control how completed work becomes git commits.
User Mode (Default)
The user runs writ finish manually. Maximum control.
agents complete work → user runs writ status → user runs writ finish → git commit
# .writ/config.toml
[workflow]
commit_mode = "user"
This is the default because it’s the safest starting point. The user reviews everything. Good for solo developers, small teams, and learning writ.
The Three Command Interface
From the user’s perspective, the entire workflow is three commands:
writ status # What's happening?
writ finish # Promote completed work to git
git push # Standard git from here
writ status shows a fleet overview: how many agents are active, which specs are complete, what’s ready to commit. It adapts automatically to scale, expanding details for small projects and collapsing to summaries for large ones.
writ finish is interactive. It shows all completed specs, lets you select which to include, and offers commit strategies:
- Single commit (default): All specs as one git commit. Clean and simple.
- Per spec commits: Each spec becomes its own git commit. Good when git bisect and per feature rollback matter.
- Grouped commits: Writ auto detects logical groupings by file overlap. Good for large scale where coherence matters more than granularity.
Live Monitoring
writ status --watch
Refreshes every 5 seconds. Shows agents completing specs in real time. Keyboard shortcuts let you jump to finish or diff without leaving the view.
Auto Mode
Fully autonomous. Orchestrator commits directly. No human in the loop.
agents complete work → orchestrator runs writ finish --auto → git commit
# .writ/config.toml
[workflow]
commit_mode = "auto"
[workflow.auto]
verify_command = "cargo test --quiet" # must exit 0 to commit
max_specs_per_commit = 10 # prevent mega-commits
branch = "writ/auto" # commit to a branch, not main
notify = "log" # log | stdout | none
Safety Rails
Auto mode is powerful and dangerous. The configuration includes guardrails:
Test verification: The verify_command must exit 0 before a commit proceeds. If tests fail, the commit is blocked and the spec goes into a blocked state.
Branch targeting: Auto commits go to a designated branch (writ/auto), not directly to main. The human merges when ready. This is the strongest safety rail. Agents commit freely, but the human controls what reaches main.
Max specs per commit: Prevents runaway mega-commits. If more than N specs are completed, they’re committed in batches.
When to Use Auto
- CI pipelines where completed work should commit immediately
- Overnight batch runs with trusted agents
- Environments with strong test suites that catch regressions
The Agent’s Perspective
Agents follow the same workflow regardless of mode:
writ context # understand project state, find unclaimed specs
writ spec claim auth # claim a spec (or auto-claim on first seal)
# ... do work ...
writ seal -s "added auth endpoint" --spec auth # checkpoint (captures only this spec's changes)
# ... more work ...
writ spec done auth -s "JWT auth complete" # mark task complete
Agents seal checkpoints and mark tasks complete. They do not run writ finish or git commit. The workflow mode determines what happens after spec done, and that’s not the agent’s concern. Multiple agents can work in the same directory simultaneously. Spec-scoped sealing keeps each agent’s changes separate.
Choosing a Mode
| Situation | Mode | Why |
|---|---|---|
| Learning writ | user | Full control, see everything |
| Solo developer | user | Simple, direct |
| Small team (2-5 agents) | user | Easy to track manually |
| Medium team (10-50 agents) | user | Convergence runs at spec done and finish, user controls commits |
| CI pipeline | auto | Needs to commit without waiting |
| Overnight batch | auto | No human present |
Configuration
Set the mode during writ init or directly in config. See Configuration for the full reference.
# During init:
# Default workflow mode:
# (1) user You run `writ finish` manually (recommended)
# (2) auto Fully autonomous (CI/pipelines)
Global config applies to all projects. Override per project in .writ/config.toml.
Git Integration
Writ works alongside git, not instead of it. Git handles remote collaboration, code review, CI, and everything teams already depend on. Writ adds the intelligence layer for agents: structured checkpoints, fleet coordination, and semantic convergence. The two systems connect through a clean round trip.
The Round Trip
git repo → writ init → agents work in writ → writ finish → git commit → git push
Starting: writ init
When you run writ init in a git repository, writ:
- Detects the git branch and HEAD commit
- Imports the current working tree as a baseline seal (
bridge import) - Configures agent frameworks (CLAUDE.md, AGENTS.md, etc.)
- Sets up format preferences and workflow mode
The baseline seal becomes the “base” for all convergence operations. Every agent’s work is measured against this common starting point.
During Work: Agents Seal, Not Commit
Agents use writ seal to checkpoint their work and writ spec done to mark tasks complete. They do not run git add, git commit, or git push. The seal chain captures richer metadata than git commits (agent identity, spec linkage, test results, verification status) and keeps the git history clean.
Finishing: writ finish
writ finish bridges writ’s seal chain back to git:
- Shows all completed specs
- Lets you select which to include
- Offers commit strategies (single, per spec, grouped)
- Auto generates commit messages from seal history
- Runs
git addandgit commit
writ finish # interactive: select specs, choose strategy
writ finish --yes # accept defaults, one commit, no prompts
writ finish --strategy per-spec # one git commit per completed spec
writ finish --dry-run # preview without committing
After writ finish, you’re back in standard git. Push, create PRs, run CI. All normal.
Manual Alternative
If you prefer driving git yourself:
git commit -m "$(writ summary --format commit)"
gh pr create --body "$(writ summary --format pr)"
writ summary generates commit messages and PR descriptions from the full seal history. writ finish uses this internally, but the standalone commands work for custom workflows.
Bridge Commands
Import: Git to Writ
writ bridge import # import current working tree
writ bridge import --git-ref v1.0 # import a specific git ref
Creates a baseline seal from the git state. This is done automatically by writ init, but you can re-import if you need to update the baseline (e.g., after pulling new changes from remote).
Export: Writ to Git
writ bridge export # export seals as git commits
writ bridge export --branch feature # target a specific branch
writ bridge export --pr-body # include PR-style metadata in commit messages
For workflows where you want each seal represented as a git commit (rather than using writ finish to create a summary commit).
Workflow Modes and Git
The workflow mode determines how writ finish creates git commits:
- User mode: You run
writ finishmanually. Full control over what gets committed and when. - Propose mode: An orchestrator proposes commits. You review and accept. Git commits are created on acceptance.
- Auto mode: Commits happen automatically, targeted to a specific branch (
writ/auto). You merge to main when ready.
Auto mode’s branch targeting is the key safety rail. Agents commit freely to writ/auto, but the human controls what reaches main through standard git merge or PR workflows.
Best Practices
- Run
writ initonce per project, at the start of each session - Let agents work entirely in writ (
seal,spec done,context) - Use
writ statusto monitor progress - Run
writ finishwhen you’re ready to commit to git - Push with standard git after finishing
- If you pull new changes from remote, consider re-importing:
writ bridge import
Suggested Uses
This guide is coming soon. It will cover recommended writ configurations for common use cases: solo agent workflows, small team setups, CI/CD integration, and large scale multi-agent deployments.
CI/CD Integration
This guide is coming soon. It will cover integrating writ into CI/CD pipelines: automated sealing, convergence in CI, deployment profiles, and using writ verify as a pipeline gate.
Convergence Resolution
This guide is coming soon. It will cover what to do when convergence escalates a conflict: understanding escalation reports, manual resolution strategies, the writ resolve command, and configuring auto-resolution thresholds.
In the meantime, see Convergence for how the pipeline works and what confidence thresholds mean.
Workspaces
Workspaces provide physical file isolation for agents. Most multi-agent work does not need workspaces. Agents work in the same directory and spec-scoped sealing keeps their changes separate. Workspaces exist for Level 2 scenarios where agents make competing rewrites to the same file and need separate copies to work from.
When you run writ task, writ creates a workspace automatically, a full copy of your project where an agent works without stepping on anyone else’s files. When the work is done, writ finish converges everything back together and commits to git. You never interact with workspaces directly. You think in tasks. Writ handles the rest.
When to use workspaces: Two agents need to rewrite the same function in fundamentally different ways. One agent takes the PKCE approach, another takes the implicit flow approach. They need separate copies of the file. For everything else (different files, additive changes to the same file), use the same-directory workflow instead.
The Flow
# One time setup
writ init
# Create tasks (one command each)
writ task "backend API work"
writ task "payment integration"
writ task "dashboard UI"
# Each command prints a launch path:
# task created: backend-api-work
# workspace: workspaces/backend-api-work/
#
# Launch an agent in this workspace:
# cd workspaces/backend-api-work/
# Launch agents in the printed workspace paths
# (however you normally launch agents — Claude Code, Codex, scripts, etc.)
# Agents work. They use 3 commands: context, seal, spec done.
# They don't know they're in a workspace. They just work.
# When ready, converge and commit
writ finish
git push
Three phases. Create tasks. Agents work. Finish.
Same Steps as Git
The biggest misconception writ needs to fight: “it’s more stuff to learn.” It’s not. It’s the same number of steps as git, mapped to agent native concepts.
Git (human workflow): Writ (agent workflow):
--------------------- ----------------------
git init writ init
git checkout -b feature writ task "description"
write code agents work
git add . && git commit writ seal (agents do this)
git merge feature writ converge-all
writ finish writ finish
git push git push
Same rhythm. Same mental model. The concepts map directly:
| Git Concept | Writ Concept | What Improves |
|---|---|---|
| Branch | Task | Structured metadata: title, status, agent assignment, file scope |
| Commit | Seal | Agent identity, spec linkage, workspace tagging, immutable chain |
| Merge | Converge | Genesis tree based, confidence scoring, auto resolves independent changes |
git status | writ context | One call, structured output, token optimized, agent ready |
git log | writ log | Filterable by spec, agent, workspace |
Writ doesn’t add complexity. It replaces git’s human oriented steps with agent native equivalents that carry more information and require less manual intervention. The step count is identical. The capability per step is higher.
The Human’s 3 Commands
writ init Once per project
writ task "..." Once per task (creates everything agents need)
writ finish Once when work is done (converges + commits to git)
Everything between writ task and writ finish is the agent’s world. Check in with writ status whenever you want, but you don’t have to.
The Agent’s 3 Commands
writ context Understand the project and assigned task
writ seal -s "..." Checkpoint work
writ spec done <id> Mark task complete
The agent never creates specs, never touches workspaces, never runs git. It discovers its task via writ context, works, checkpoints, and reports done.
When an agent runs writ context from a workspace directory, the first thing it sees is its assigned task:
task: backend-api-work
title: "Backend API work"
status: active
recent_seals: (none yet)
No discovery gap. No ID mismatch. The task is right there.
Task Discovery
In earlier testing, agents sometimes created new specs instead of using pre-assigned ones. writ task eliminates this entirely: the spec already exists when the agent starts. The agent doesn’t need to create anything.
If an agent tries writ spec add for a spec that already exists in its workspace:
error: spec 'backend-api-work' already exists
hint: this spec is assigned to your workspace — use it with:
writ seal -s "your summary" --spec backend-api-work
Checking Progress
$ writ status
-- Tasks -----------------------------------------------
backend-api-work agent-1 3 seals working
payment-integration agent-2 complete (5 seals)
dashboard-ui agent-3 1 seal working
1 complete / 2 in progress
Run `writ finish` when ready.
Tasks are labeled as “Tasks” in status output because you created them with writ task. Full transparency into what every agent is doing without branch archaeology or parsing commit messages.
Finishing
writ finish handles convergence automatically. When workspaces exist with completed work:
- Writ detects all workspaces with changes
- Runs convergence: files changed in only one workspace merge cleanly, overlapping changes go through the convergence engine
- If convergence is clean, proceeds to git commit
- If there are escalations that need human resolution, writ stops and shows you what needs attention
- After a successful commit, prompts to clean up workspace directories
$ writ finish
Converging 3 workspaces...
3 tasks committed.
Clean up workspaces? [Y/n]: y
removed workspaces/backend-api-work/
removed workspaces/payment-integration/
removed workspaces/dashboard-ui/
Use --cleanup to auto-clean without the prompt, or --no-cleanup to keep workspace directories around for inspection.
Two Paths
A user trying writ for the first time has two natural paths:
Path 1: Task driven (multi agent)
writ init
writ task "build payment system"
writ task "redesign dashboard"
# launch agents in printed workspace paths
# agents work...
writ finish
git push
The user defines tasks, writ sets up everything, agents work in isolation, convergence brings it together.
Path 2: Direct (single agent or manual work)
writ init
# launch agent (or work manually) in the project directory
# agent uses: context, seal, spec add, spec done
writ finish
git push
No tasks, no workspaces. The agent works directly in the project. This is the simplest path, identical to pre-workspace writ. Works perfectly for single agent use or manual human work.
Both paths end the same way: writ finish then git push. The difference is whether you need isolation (Path 1) or not (Path 2). You don’t have to decide upfront. Start with Path 2 and switch to Path 1 by running writ task whenever you need a second agent.
Under the Hood
writ task is syntactic sugar. One command that assembles three things:
- Spec: a structured task definition (ID derived from title, or
--idto override) - Workspace: a full project copy at
workspaces/<id>/with its own index and HEAD - Assignment: the spec is scoped to the workspace so
writ contextsurfaces it as the agent’s task
All workspaces share the same .writ/ directory for objects, seals, and specs. Creating a workspace copies files but doesn’t duplicate the content addressable storage.
my-project/
.writ/ <- writ internals (hidden, gitignored)
workspaces/ <- agent working directories (gitignored)
backend-api-work/ <- full project copy, agent works here
payment-integration/ <- full project copy, agent works here
dashboard-ui/ <- full project copy, agent works here
src/
...
The first writ task invocation adds workspaces/ to .gitignore automatically. Running writ task from inside a workspace shows a warning. Tasks should be created from the main project directory.
Advanced Usage
The power user commands from workspaces v1 still exist for advanced use cases and orchestrator scripts:
# Create a workspace manually (without a task)
writ workspace create custom-workspace --path ../my-workspace
# Assign additional specs to a workspace
writ spec assign another-spec --workspace custom-workspace
# Remove a workspace assignment
writ spec unassign another-spec
# List workspaces
writ workspace list
# Delete a workspace (preserves seals, specs, and objects)
writ workspace delete custom-workspace
Most users will never need these. writ task and writ finish handle the full lifecycle.
When to Use Workspaces
| Scenario | Recommendation |
|---|---|
| Single agent | No workspace needed. Work directly in the project. |
| Multiple agents, different files | No workspace needed. Same directory, spec-scoped sealing. See Multi Agent Workflow. |
| Multiple agents, additive changes to same file | No workspace needed. Convergence runs at spec done and writ finish. |
| Multiple agents, competing rewrites of same code | writ task per approach. Physical isolation prevents in-progress breakage. |
| CI pipeline with parallel jobs | writ task per job. Each gets isolation with shared history. |
Most multi-agent work is Level 0 (different files) or Level 1 (additive changes). Workspaces are the Level 2 tool for the rare case where agents rewrite the same code in incompatible ways.
MCP Server
Writ ships a native MCP (Model Context Protocol) server built in Rust. It’s part of the writ binary. No separate install, no Python runtime, no plugins. When an agent connects via MCP, every writ command is available as a native tool in the agent’s palette.
How It Works
The MCP server is a thin bridge between the MCP protocol and the writ CLI. Each tool function calls the writ CLI via subprocess: same behavior, same output, same enforcement as running commands directly. The server communicates over stdio using the standard MCP protocol.
┌─────────────────┐ stdio ┌──────────────────┐
│ │ ◄─────────────────────► │ │
│ Claude Code │ MCP Protocol │ writ mcp-serve │
│ (or Claude │ (JSON messages) │ │
│ Desktop) │ │ Rust binary │
│ │ Tool calls: │ (part of writ) │
│ Agent sees │ writ_context() │ │
│ writ tools │ writ_seal() │ Calls writ CLI │
│ natively │ writ_spec_add() │ via Command │
│ │ │ │
└─────────────────┘ └──────────────────┘
Setup
Automatic (Claude Code)
writ init detects Claude Code and generates .mcp.json in your project root:
writ init
# ✓ Generated .mcp.json (MCP server for Claude Code)
The generated .mcp.json:
{
"mcpServers": {
"writ": {
"command": "writ",
"args": ["mcp-serve"]
}
}
}
Commit this file to git. Every developer who clones the project gets MCP tools automatically. Zero setup for the team.
Manual (Claude Code)
If you skipped init or want to add MCP to an existing project:
writ mcp-install
This writes .mcp.json to the project root. Equivalent to what writ init generates.
Claude Desktop
writ mcp-install --desktop
This writes the MCP server config to Claude Desktop’s configuration file (~/Library/Application Support/Claude/claude_desktop_config.json on macOS).
Starting the Server
You don’t normally start the server manually. The MCP client (Claude Code or Claude Desktop) starts it automatically using the config. But for debugging or testing:
writ mcp-serve
The server starts on stdio and waits for MCP protocol messages.
Tools
21 tools are available, organized by category. Each tool maps directly to a CLI command.
Core Workflow
| Tool | Parameters | What It Does |
|---|---|---|
writ_context | spec (optional), format (optional, default: toon) | Full project state. Run this FIRST at the start of every task. |
writ_seal | summary (required), spec (optional), agent (optional, default: claude-code), paths (optional) | Checkpoint current work. Auto-scoped when agent has one claimed spec. |
writ_spec_add | description (required), id (optional), title (optional) | Create a new task/spec. Hash ID auto-generated from description. |
writ_spec_done | id (optional), summary (optional) | Mark a spec complete. Creates a final seal. |
Status and Review
| Tool | Parameters | What It Does |
|---|---|---|
writ_status | completed (optional), active (optional), agent (optional), spec (optional) | Fleet overview: agents, specs, progress. |
writ_diff | spec (optional), agent (optional), stat (optional) | View file changes, optionally scoped to a spec or agent. |
writ_log | spec (optional), agent (optional), all (optional), limit (optional) | Seal history. all includes diverged branches. |
writ_show | seal_id (required), diff (optional) | Inspect a specific seal in detail. |
Spec Management
| Tool | Parameters | What It Does |
|---|---|---|
writ_spec_status | state (optional), format (optional) | List specs, optionally filtered by lifecycle state. |
writ_spec_show | id (required) | Detailed view of a single spec. |
writ_spec_reopen | id (required) | Reopen a completed spec for continued work. |
Round Trip
| Tool | Parameters | What It Does |
|---|---|---|
writ_finish | specs (optional), message (optional), strategy (optional), dry_run (optional) | Promote completed specs to git commits. |
writ_summary | format (optional, default: commit) | Generate commit messages or PR descriptions from seal history. |
Recovery and Convergence
| Tool | Parameters | What It Does |
|---|---|---|
writ_restore | seal_id (required) | Restore working directory to any seal’s state. |
writ_converge | strategy (optional), dry_run (optional) | Merge diverged branches. Strategies: escalate, three-way-merge, most-recent, orchestrator. |
Diagnostics
| Tool | Parameters | What It Does |
|---|---|---|
writ_verify | chain (optional), all_chains (optional), seal (optional) | Verify cryptographic integrity of seals and chains. |
writ_doctor | fix (optional) | Run 8 diagnostic checks on repo health. |
Smart Defaults
The MCP server applies sensible defaults that reduce the number of parameters agents need to pass:
- Agent identity: Defaults to
claude-code. No need to pass--agenton every seal. - Output format: Defaults to
toonforwrit_context. Agents get token optimized output without asking. - Auto-scoping: When an agent has exactly one claimed spec,
specis optional onwrit_sealandwrit_spec_done. The MCP server resolves the claimed spec automatically. - Context token:
writ_contextvia MCP writes.writ/.context_token(via the CLI passthrough), satisfying the freshness check automatically.
The Five Layer Adoption Stack
The MCP server is one layer in writ’s full agent adoption architecture:
| Layer | What | How |
|---|---|---|
| 1. Instructions | Writ workflow in CLAUDE.md / AGENTS.md | Generated by writ init |
| 2. Slash Commands | 20 commands in .claude/commands/ | Generated by writ init |
| 3. MCP Tools | 21 native tools via MCP protocol | .mcp.json generated by writ init |
| 4. Hooks | SessionStart hook runs writ context automatically | Configured by writ init |
| 5. Enforcement | Seal requires spec (C.13), context freshness check (C.14) | Built into the CLI |
Each layer reinforces the others. Instructions tell agents what to do. Slash commands and MCP tools make it easy. Hooks provide automatic context. Enforcement catches anything that slips through. An agent that ignores the instructions still sees writ tools in its palette, still gets context injected at session start, and still gets blocked if it tries to seal without a spec.
Distribution
The MCP server ships with every writ installation method:
pip install writ-vcs # includes writ mcp-serve
cargo install writ-vcs # includes writ mcp-serve
brew install writ # includes writ mcp-serve
Single binary. No runtime dependencies. writ mcp-serve just works.
Slash Commands
Writ generates slash commands for Claude Code during writ init. These are markdown files in .claude/commands/ that expose writ’s full CLI as discoverable commands in the agent’s palette. Agents see them alongside other project commands and can invoke them without remembering exact CLI syntax.
How They Work
Each slash command is a .md file that tells the agent what command to run and what flags are available. They’re generated once during writ init and regenerated on writ init --reconfigure. Each file includes a <!-- Generated by writ init. Do not edit. --> header so writ can identify and manage its own files.
Slash commands are thin wrappers. They don’t contain business logic. They document the CLI command and let the agent invoke it. The CLI is the source of truth.
Full Command List
Core Workflow
| Command | What It Does |
|---|---|
/writ-context | Get structured project state. Run this FIRST at the start of every task. |
/writ-seal | Checkpoint current work with a summary and spec linkage. |
/writ-spec-add | Create a new task/spec with an ID and title. |
/writ-spec-done | Mark a spec complete. Creates a final seal. |
Status and Review
| Command | What It Does |
|---|---|
/writ-status | Fleet overview: agents, specs, progress, commit readiness. |
/writ-diff | View file changes. Supports --spec, --agent, --stat, --name-only. |
/writ-log | Seal history. --all includes diverged branches. |
/writ-show | Inspect a specific seal. --diff shows file changes. |
Spec Management
| Command | What It Does |
|---|---|
/writ-spec-status | List specs, optionally filtered by lifecycle state. |
/writ-spec-show | Detailed view of a single spec. |
/writ-spec-reopen | Reopen a completed spec for continued work. |
Round Trip
| Command | What It Does |
|---|---|
/writ-finish | Promote completed specs to git commits. Supports --strategy, --dry-run. |
/writ-summary | Generate commit messages (--format commit) or PR descriptions (--format pr). |
Recovery and Convergence
| Command | What It Does |
|---|---|
/writ-restore | Restore working directory to any seal’s state. |
/writ-converge | Merge diverged branches. Strategies: escalate, three-way-merge, most-recent, orchestrator. |
Diagnostics
| Command | What It Does |
|---|---|
/writ-verify | Verify cryptographic integrity of the seal chain. |
/writ-doctor | Run diagnostic checks on repo health. |
Generation and Cleanup
During Init
writ init
# ✓ Generated 20 slash commands in .claude/commands/
Slash commands are only generated when Claude Code is detected (presence of CLAUDE.md or .claude/ directory). Other frameworks get the CLI and instruction templates appropriate to their environment.
Regeneration
writ init --reconfigure
This overwrites all existing writ-*.md files in .claude/commands/ with fresh copies. Non-writ files are preserved.
Cleanup
writ uninit
Removes only writ-*.md files from .claude/commands/. Any slash commands you’ve created yourself are left untouched.
Slash Commands vs MCP Tools
Both provide the same capabilities. The difference is how the agent discovers and invokes them:
| Slash Commands | MCP Tools | |
|---|---|---|
| How agent finds them | Command palette, / prefix | Tool palette, native MCP discovery |
| How agent invokes them | Runs the CLI command in a shell | Calls the MCP tool function |
| Output | CLI stdout (text or TOON) | MCP tool result (text) |
| Works without MCP | Yes, just files on disk | No, requires MCP connection |
| Works without Claude Code | No, Claude Code specific | Yes, any MCP client |
In a Claude Code environment with both enabled (the default after writ init), the agent has two paths to every writ command. The MCP path is typically preferred by agents because it’s a native tool call rather than a shell command, but both work identically.
Relationship to Instruction Templates
Slash commands are Layer 2 in writ’s adoption stack. They complement, not replace, the instruction templates (Layer 1) in CLAUDE.md:
- Instructions tell agents the workflow: run context first, seal after work, mark specs complete.
- Slash commands make the workflow easy to execute: the exact commands are discoverable in the palette.
- MCP tools (Layer 3) make it native: no shell involved, direct tool calls.
All three are generated by writ init. All three are cleaned up by writ uninit.
CLI Reference
Complete reference for all writ commands.
Core Commands
writ init
Guided setup: initializes writ, scans environment, detects git, imports baseline, configures agent frameworks, sets output format preferences.
writ init [OPTIONS]
Options:
--yes / -y Accept all defaults, no prompts (CI-safe)
--bare Create only .writ/ directory, no framework integration
--no-git Skip git integration even if git repo detected
--no-claude Skip Claude Code integration
--no-codex Skip Codex / OpenAI integration
--no-generic Skip generic agent instructions
--frameworks <LIST> Comma-separated: claude,codex,generic
--format <FORMAT> Set output format: toon, json, json-compact
--name <PROJECT> Set project name (default: directory name)
--profile <PROFILE> Deployment profile (storage budgets, retention)
--spec <SPEC> Create a spec during init
--title <TITLE> Title for the spec (used with --spec)
--description <DESCRIPTION> Description for the spec (used with --spec)
Note:
writ installandwrit uninstallare deprecated aliases forwrit initandwrit uninitrespectively. They print a deprecation notice and call the new command.
writ uninit
Remove writ from the project. Deletes .writ/ directory and framework hooks.
writ uninit [OPTIONS]
Options:
--force Skip confirmation prompt
--keep-writignore Keep the .writignore file
writ task
Create a task for an agent. One command that creates a spec, a workspace, and prints a launch command.
writ task <TITLE> [OPTIONS]
Options:
--id <ID> Override the auto-derived spec/workspace ID
--format <FORMAT> Output: human (default), json
The title is slugified into a spec ID automatically (e.g. “backend API work” becomes backend-api-work). Use --id to override.
Output:
task created: backend-api-work
workspace: workspaces/backend-api-work/
Launch an agent in this workspace:
cd workspaces/backend-api-work/
Suggested prompt: "Backend API work."
Or provide your own prompt for the agent.
The first writ task invocation adds workspaces/ to .gitignore automatically. Running writ task from inside a workspace shows a warning: tasks should be created from the main project directory.
writ task list
Show all active tasks and their status.
writ task list [OPTIONS]
Options:
--format <FORMAT> Output: human (default), json
writ plan
Batch spec creation from a list of tasks.
writ plan [TASKS]... [OPTIONS]
Options:
-f, --file <PATH> Read tasks from a file (one per line)
Reads from inline arguments, a file, or stdin. Titles are slugified into spec IDs automatically (e.g. “Implement OAuth2 auth” becomes implement-oauth2-auth).
Output:
3 specs created:
implement-oauth2-auth "Implement OAuth2 auth"
add-stripe-payments "Add Stripe payments"
build-admin-dashboard "Build admin dashboard"
Next: launch your agents. They discover specs via `writ context`.
writ seal
Create a structured checkpoint from current changes. When --spec is provided, the seal captures only the files that changed since this agent’s last seal for this spec (spec-scoped sealing). Without --spec, the seal captures all changes in the working directory.
writ seal -s <SUMMARY> [OPTIONS]
Options:
-s, --summary <SUMMARY> Summary of what changed and why (required)
--agent <AGENT> Agent identifier [default: human]
--spec <SPEC> Linked spec ID (enables spec-scoped change detection)
--status <STATUS> Task status: in-progress, complete, blocked [default: in-progress]
--paths <PATHS> Seal only these paths (comma-separated)
--tests-passed <N> Number of tests that passed
--tests-failed <N> Number of tests that failed
--linted Whether the code was linted
--allow-empty Allow sealing with no file changes
--expected-head <SEAL_ID> Optimistic conflict detection
--enforce-scope Reject out of scope file changes
When --spec is used with an unclaimed spec, the spec is auto-claimed by this agent on first seal.
writ context
Structured project state for LLM consumption.
writ context [OPTIONS]
Options:
--spec <SPEC> Scope to a specific spec
--for-agent <AGENT> Scope entire context to an agent's world
--seal-limit <N> Maximum recent seals to include [default: 10]
--status <STATUS> Filter seals by status
--agent <AGENT> Filter seals by agent ID
--format <FORMAT> Output: json, toon, human, brief
When called from inside a workspace created by writ task, the output includes a task field at the top with the assigned task ID, title, and status.
When unclaimed specs exist (created via writ plan or writ spec add but not yet claimed by an agent), the output includes an unclaimed_specs section listing available tasks.
writ watch
Live seal event monitoring. Shows agent activity in real time without needing to poll writ status.
writ watch [OPTIONS]
Options:
--interval <SECONDS> Polling interval [default: 5]
--daemon Run as background process
--stop Stop running daemon
--status Show daemon status and recent activity
By default, runs in the foreground showing real time output. Press q to quit. Seal events display as they happen, with overlapping files flagged for visibility.
Daemon mode (--daemon) runs the watcher as a background process with output logged to .writ/watch.log and PID stored in .writ/watch.pid.
Note: convergence runs automatically at writ spec done and as a backstop at writ finish. Watch is a monitoring tool, not a convergence trigger.
Configuration via .writ/config.toml:
[watch]
interval = 5 # polling interval in seconds
log_file = ".writ/watch.log"
writ status
High level project overview. Agent activity, spec progress, commit readiness. Complements writ state (low level working directory state) with a fleet aware, progress oriented view.
writ status [OPTIONS]
Options:
--completed Show all completed specs in detail
--active Show all in-progress specs in detail
--agent <AGENT> Filter to a single agent's work
--spec <SPEC> Detail view of one spec
--watch Live-updating view (refreshes every 5s)
--interval <SECONDS> Refresh interval for --watch [default: 5]
--format <FORMAT> Output: json, toon, json-compact
The default view adapts automatically to project scale, expanding details for small projects and collapsing to summaries for large fleets. Use filter flags to drill down.
When tasks exist (specs created via writ task), they appear under a “Tasks” header showing task name, agent, seal count, and status.
writ log
Show seal history.
writ log [OPTIONS]
Options:
--all Include seals from diverged branches
--spec <SPEC> Filter by spec ID
--limit <N> Maximum entries
--format <FORMAT> Output: json, human
writ show
Inspect a specific seal.
writ show <SEAL_ID> [OPTIONS]
Options:
--diff Show file changes
--format <FORMAT> Output: json, human
writ diff
Show content level diff of working directory changes. Enhanced with spec and agent aware filtering.
writ diff [OPTIONS]
Options:
--completed Diff across completed specs (default)
--spec <SPEC> Diff for a single spec
--agent <AGENT> Diff for a single agent's work
--all Include in-progress specs
--file <PATH> Diff for a single file
--stat Summary only (files and line counts)
--full Full unified diff output
--format <FORMAT> Output format for machine consumption
writ state
Show working directory state (new, modified, deleted files).
writ state [OPTIONS]
Options:
--format <FORMAT> Output: json, human
writ restore
Restore working directory to a seal’s state.
writ restore <SEAL_ID>
Round Trip Commands
writ summary
Generate session summary for git commits and PRs.
writ summary --format <FORMAT>
Formats:
commit One-line commit message with provenance
pr Full PR description with spec/agent breakdown
human Detailed session overview
json Machine-readable output
writ finish
Promote completed specs to git commits. Auto-converges outstanding changes before committing (both same-directory overlaps and workspace changes). Interactive spec selection, commit strategy options, and auto generated messages from seal history.
writ finish [OPTIONS]
Options:
--yes, -y Accept defaults, no prompts (matches existing behavior)
--full Per-spec commits with interactive review
--strategy <STRATEGY> Commit strategy: single (default), per-spec, grouped
--message, -m <MSG> Commit message (skips message prompt)
--specs <LIST> Comma-separated spec IDs to include
--all Include all completed specs (no selection prompt)
--dry-run Preview what would be committed without doing it
--cleanup Auto-clean workspace directories after commit (no prompt)
--no-cleanup Keep workspace directories after commit (no prompt)
# Auto mode (workflow.commit_mode = "auto")
--auto Commit immediately without approval
--verify Run verification command before committing
--no-verify Skip verification command
When workspaces exist with changes, writ finish automatically:
- Detects all non-main workspaces
- Runs convergence (prints merge report)
- If convergence has unresolved escalations, errors out before committing
- If convergence is clean, proceeds to git commit
- After commit, prompts for workspace directory cleanup (default: yes)
Convergence Commands
writ converge
Two-spec convergence.
writ converge <LEFT_SPEC> <RIGHT_SPEC> [OPTIONS]
Options:
--apply Apply the merge result
--format <FORMAT> Output: json, human
writ converge-all
Merge all diverged branches.
writ converge-all [OPTIONS]
Options:
--apply Apply merge results
--dry-run Preview without applying
--strategy <STRAT> escalate, three-way-merge, most-recent, orchestrator
--format <FORMAT> Output: json, human
writ converge-workspaces
Merge across named workspaces.
writ converge-workspaces <WORKSPACE>... [OPTIONS]
Options:
--apply Apply merge results
--dry-run Preview without applying
--strategy <STRAT> escalate, three-way-merge, most-recent, orchestrator
--format <FORMAT> Output: json, human
Non-overlapping changes merge cleanly. Overlapping changes go through the convergence engine.
Spec Management
writ spec add
Register a new spec.
writ spec add "<DESCRIPTION>" [OPTIONS]
Options:
--id <ID> Custom spec ID (default: auto-generated hash)
--title <TITLE> Custom title (default: derived from description)
writ spec status
Show specs, optionally filtered by lifecycle state.
writ spec status [OPTIONS]
Options:
--state <STATE> Filter: active, stale, completed, cancelled, archived
writ spec claim
Claim an unclaimed spec for the current agent. Prevents other agents from picking up the same task.
writ spec claim <ID> [OPTIONS]
Options:
--agent <AGENT> Agent identifier (auto-detected if not provided)
If the spec is already claimed by another agent, returns an error with the claiming agent’s ID.
Specs are also auto-claimed on first seal: when writ seal --spec X runs and spec X has no owner, it is automatically claimed by the sealing agent.
writ spec done
Mark a spec as completed. Creates a final seal, transitions the spec from active to completed, and prints hints for the user.
writ spec done <ID> [OPTIONS]
Options:
-s, --summary <MSG> Completion summary (used in commit message by writ finish)
If the agent has exactly one active spec, the ID can be omitted: writ spec done.
writ spec complete
Mark a spec as completed (same as writ spec done without the final seal creation).
writ spec complete <ID>
writ spec cancel
Cancel a spec.
writ spec cancel <ID>
writ spec assign
Assign a spec to a workspace. Assigned specs are visible only in their workspace and the main workspace.
writ spec assign <SPEC-ID> --workspace <NAME>
writ spec unassign
Remove a spec’s workspace assignment. The spec becomes globally visible in all workspaces.
writ spec unassign <SPEC-ID>
writ spec reopen
Reopen a completed spec for continued work. Sets the spec back to active. Seal history is preserved.
writ spec reopen <ID>
Workspace Commands
writ workspace create
Create a new isolated workspace with its own directory and files.
writ workspace create <NAME> [OPTIONS]
Options:
--path <DIR> Directory for the workspace (default: workspaces/<name>/)
--specs <FILTER> Assign matching specs (glob or comma-separated IDs)
--from <WORKSPACE> Create from another workspace's state instead of main
Creates a parallel directory with a full copy of the project files, its own index and HEAD, and a .writ-workspace pointer file back to the main project. All writ commands work from the workspace directory automatically.
writ workspace list
List all workspaces with paths and spec counts.
writ workspace list
# Output:
# main . 0 specs base workspace
# auth-team ../ws-auth 3 specs
# payments-team ../ws-payments 4 specs
writ workspace status
Show detailed status for a workspace, including spec progress and seal counts.
writ workspace status [NAME]
writ workspace delete
Delete a workspace. Removes workspace state and parallel directory. Does NOT delete seals, specs, or objects from the shared store.
writ workspace delete <NAME> [OPTIONS]
Options:
--force Skip confirmation prompt
--keep-files Preserve the parallel directory on disk
Cannot delete the main workspace.
Security Commands
writ verify
Verify seal chain integrity and signatures.
writ verify [OPTIONS] # Full chain verification (default)
writ verify --seal <ID> [OPTIONS] # Single seal verification
Options:
--format <FORMAT> Output: json, human
writ security events
View the security event audit log.
writ security events [OPTIONS]
Options:
--severity <LEVEL> Filter: info, warning, critical
--event-type <TYPE> Filter by event type
Agent Management
writ agent register
Register an agent identity.
writ agent register --id <ID> [OPTIONS]
Options:
--role <ROLE> Agent role
--trust-level <LEVEL> full, standard, restricted, untrusted
Git Bridge
writ bridge import
Import git working tree as a baseline seal.
writ bridge import [OPTIONS]
Options:
--git-ref <REF> Git reference to import
writ bridge export
Export seals as git commits.
writ bridge export [OPTIONS]
Options:
--branch <BRANCH> Target git branch
--pr-body Include PR-style metadata
Garbage Collection
writ gc status
Storage breakdown and stale spec warnings.
writ gc status
writ gc run
Generate and execute a cleanup plan.
writ gc run [OPTIONS]
Options:
--dry-run Preview without executing
--yes Skip confirmation prompt
writ gc storage
Detailed storage usage by category.
writ gc storage
writ gc log
GC audit history.
writ gc log [OPTIONS]
Options:
--limit <N> Maximum entries
Maintenance Commands
writ doctor
Run health checks on a .writ/ repository. Read only by default.
writ doctor [OPTIONS]
Options:
--json Machine-readable JSON output (DoctorReport)
--fix Reserved for future use (auto-repair common issues)
Runs 8 checks:
| Check | What it verifies |
|---|---|
version_file | .writ/version.toml exists and parses |
schema_version | Schema version matches current binary |
directories | Required dirs: objects, seals, specs, heads, keys, agents |
index | .writ/index.json exists and deserializes |
config | .writ/config.toml parses (if present) |
master_key | keys/.master exists |
specs | All spec JSON files deserialize |
seals | First 50 seal JSON files deserialize |
Example output:
✓ version_file — version.toml exists and parses
✓ schema_version — schema version 1 is current
✓ directories — all expected directories present
✓ index — index.json exists and deserializes
✓ config — config.toml exists and parses
✓ master_key — master key present
✓ specs — 3 spec file(s) OK
✓ seals — 12 seal file(s) sampled, all OK
8 passed, 0 failed, 0 warnings
Exit code 0 when all checks pass, 1 if any check fails.
See Version Compatibility for details on schema versioning, auto-migration, and troubleshooting.
MCP Server
writ mcp-serve
Start the MCP server. Communicates over stdio using the standard MCP protocol. Normally started automatically by the MCP client (Claude Code or Claude Desktop) using the config in .mcp.json.
writ mcp-serve
The server exposes 21 tools matching the full writ CLI. Each tool calls the CLI via subprocess: same behavior, same output, same enforcement. See the MCP server guide for the full tool list and setup instructions.
writ mcp-install
Generate MCP configuration for agent frameworks.
writ mcp-install [OPTIONS]
Options:
--desktop Write config for Claude Desktop instead of project .mcp.json
--global Write config to global Claude Code settings
Default (no flags): Writes .mcp.json to project root. Commit this to git for zero setup team adoption.
--desktop: Writes to Claude Desktop’s config file (~/Library/Application Support/Claude/claude_desktop_config.json on macOS).
Note:
writ initautomatically generates.mcp.jsonwhen Claude Code is detected. Usewrit mcp-installfor manual setup or when adding MCP to an existing project.
Python SDK Reference
Full API reference coming soon.
Installation
pip install writ-vcs
Quick Reference
Repository
import writ
# Open an existing writ repository
repo = writ.Repository.open(".")
# Initialize a new repository
repo = writ.Repository.init("/path/to/project")
Sealing
result = repo.seal(
summary="added auth endpoint",
agent_id="dev-1",
agent_type="agent",
spec_id="auth",
status="in-progress",
tests_passed=12,
tests_failed=0,
)
Context
# Full context
ctx = repo.context()
# Scoped to a spec
ctx = repo.context(spec="auth")
Convergence
report = repo.converge_all(strategy="escalate", apply=True)
Verification
result = repo.verify_chain()
result = repo.verify_seal("a7c2e8f4b31a")
See the Getting Started guide for usage examples.
Configuration
Writ uses a two tier configuration system that mirrors git: a global config for your defaults and a project config for per repository overrides. Any setting in the global config can be overridden at the project level.
Configuration Hierarchy
~/.writ/config Global defaults (like ~/.gitconfig)
.writ/config.toml Project-level overrides
CLI flags Per-invocation override (highest priority)
Environment variables WRIT_FORMAT, WRIT_COMMIT_MODE, etc.
Resolution order (highest priority first):
- CLI flag (
--format toon) - Environment variable (
WRIT_FORMAT=toon) - Project config (
.writ/config.toml) - Global config (
~/.writ/config) - Built-in default
Global Config
Created automatically on first writ init. Located at ~/.writ/config.
[user]
name = "Andrew" # your name for seal attribution
[output]
format = "toon" # default output format: json | toon | json-compact
[init]
frameworks = ["claude", "codex", "generic"] # default frameworks to configure
[workflow]
commit_mode = "user" # user | propose | auto
commit_strategy = "single" # single | per-spec | grouped
Project Config
Created by writ init in each project. Located at .writ/config.toml.
[project]
name = "my-project"
profile = "development" # deployment profile
[output]
format = "toon" # override global format for this project
[workflow]
commit_mode = "user" # user | propose | auto
commit_strategy = "single" # single | per-spec | grouped
stale_timeout = 3600 # seconds before a spec is flagged stale (0 to disable)
[workflow.auto]
verify_command = "cargo test --quiet" # must exit 0 for auto-commit to proceed
max_specs_per_commit = 10 # prevent mega-commits
branch = "writ/auto" # target branch for auto commits (not main)
notify = "log" # log | stdout | none
[gc]
profile = "development" # storage profile
max_storage_bytes = 5368709120 # 5 GB
retention_days = 30
compression_level = 3
Output Formats
Three output formats for writ context and other structured commands:
| Format | Flag | Description |
|---|---|---|
json | --format json | Standard JSON. Maximum compatibility. Default if no config is set. |
toon | --format toon | Token Oriented Object Notation. Field names declared once, rows streamed as values. Recommended for LLM agents. |
json-compact | --format json-compact | Minified JSON. No whitespace. Good for CI pipelines. |
Set your default format globally:
# During first writ init, you'll be prompted:
# Default output format for agent context:
# (1) toon Token Oriented Object Notation (optimized for LLM agents)
# (2) json Standard JSON (maximum compatibility)
# (3) json-compact Minified JSON
Or set it directly in config:
# ~/.writ/config (global)
[output]
format = "toon"
# .writ/config.toml (project override)
[output]
format = "json"
Override per command:
writ context --format toon
writ status --format json
writ diff --format json-compact
Workflow Modes
Three modes that scale from solo developer to enterprise fleet. Set during writ init or in config.
user (Default)
The user runs writ finish manually. Maximum control, maximum safety.
[workflow]
commit_mode = "user"
Flow: agents complete specs → user runs writ status → user runs writ finish → git commit created.
propose
An orchestrator proposes commits. The user reviews and accepts.
[workflow]
commit_mode = "propose"
Flow: agents complete specs → orchestrator runs writ finish --propose → user reviews with writ finish --review → user accepts with writ finish --accept.
Good for medium teams (10-50 agents) with supervised autonomous work.
auto
Fully autonomous. Orchestrator commits directly. Requires high trust.
[workflow]
commit_mode = "auto"
[workflow.auto]
verify_command = "cargo test --quiet" # must exit 0 to commit
max_specs_per_commit = 10 # prevent mega-commits
branch = "writ/auto" # commit to a branch, not main
notify = "log" # log | stdout | none
Flow: agents complete specs → orchestrator runs writ finish --auto → verification runs → git commit created on target branch.
Safety rails: test verification blocks bad commits. Branch targeting keeps auto commits off main. Max specs per commit prevents runaway batches.
Deployment Profiles
Set during initialization with writ init --profile <name>. Controls storage budgets, retention periods, and compression levels.
| Profile | Storage Budget | Retention | Compression | Use Case |
|---|---|---|---|---|
raspberry-pi | 500 MB | 7 days | Level 1 | Constrained environments |
development | 5 GB | 30 days | Level 3 | Local development |
production | 100 GB | 90 days | Level 3 | Production systems |
enterprise | Unlimited | 365 days | Level 6 | Enterprise deployments |
Files
| File | Location | Purpose |
|---|---|---|
~/.writ/config | Home directory | Global defaults (format, frameworks, workflow mode) |
.writ/ | Project root | Writ repository data (seals, objects, heads, keys, config) |
.writ/config.toml | Inside .writ | Project-level configuration overrides |
.writ/gc-config.json | Inside .writ | Garbage collection settings (profile, budgets, retention) |
.writ/keys/ | Inside .writ | Cryptographic keys (Ed25519 convergence keypair, AES-GCM encrypted) |
.writignore | Project root | Files to exclude from tracking (gitignore-compatible syntax) |
.writignore
Same syntax as .gitignore. Created automatically by writ init with sensible defaults:
.git/
node_modules/
__pycache__/
*.pyc
.env
target/
dist/
build/
Add project specific exclusions as needed. Writ respects nested .writignore files the same way git respects nested .gitignore files.
Environment Variables
| Variable | Effect | Example |
|---|---|---|
WRIT_FORMAT | Override output format | WRIT_FORMAT=toon writ context |
WRIT_COMMIT_MODE | Override workflow mode | WRIT_COMMIT_MODE=auto writ finish |
WRIT_PROFILE | Override deployment profile | WRIT_PROFILE=production writ init |
Version Compatibility
Writ tracks the on-disk format of .writ/ repositories separately from the binary version. This page explains how schema versioning, auto-migration, and health checks work.
Schema Version vs Binary Version
The .writ/version.toml file tracks two distinct things:
- Schema version (
schema_version): An integer that describes the on-disk layout of.writ/. Increments when the directory structure or file formats change. Migrations upgrade old schemas to current. - Binary version (
created_by,last_opened_by): The writ binary version (from Cargo.toml / PyPI). Can move independently of schema version.
# .writ/version.toml
schema_version = 1
created_by = "0.1.0"
last_opened_by = "0.1.0"
created_at = "2026-03-06T10:00:00Z"
last_opened_at = "2026-03-06T14:30:00Z"
The schema version is what matters for compatibility. Two different binary versions with the same schema version are fully compatible.
How Auto-Migration Works
When you run any writ command that opens a repository, writ checks the schema version:
- Load
.writ/version.toml(if it exists) - Missing file? Treat the repo as schema version 0 (pre-versioning legacy repo)
- Schema is current? Continue normally, update
last_opened_by - Schema is behind? Run migrations automatically (v0 to v1, v1 to v2, etc.)
- Schema is ahead? Return an error: the repo was created by a newer version of writ
Migration guarantees
- Sequential: Migrations run one step at a time (v0 to v1, then v1 to v2). No steps are skipped.
- Idempotent: Running a migration twice produces the same result. Safe to retry after interruption.
- Backup:
.writ/version.tomlis backed up to.writ/version.toml.bakbefore any migration starts. - Atomic per-step: After each step succeeds, the version file is updated. A failure mid-chain leaves the repo at the last successful step.
- Append-only: Migration functions are never removed. Future versions accumulate steps.
v0 to v1 migration
The first migration handles all repositories created before schema versioning existed:
- Creates
.writ/version.tomlwith schema_version = 1 - Ensures all expected directories exist:
objects/,seals/,specs/,heads/,keys/,agents/,proposals/,security/,security/events/ - Creates the
HEADfile if missing - If a legacy
settings.jsonexists without aconfig.toml, creates a defaultconfig.toml
This migration runs silently. You may see a brief message in stderr:
writ: migrating .writ schema v0 → v1
Checking Repo Version
To see a repository’s current version and health status:
writ doctor
Example output for a healthy repo:
✓ version_file — version.toml exists and parses
✓ schema_version — schema version 1 is current
✓ directories — all expected directories present
✓ index — index.json exists and deserializes
✓ config — config.toml exists and parses
✓ master_key — master key present
✓ specs — 3 spec file(s) OK
✓ seals — 12 seal file(s) sampled, all OK
8 passed, 0 failed, 0 warnings
For machine-readable output:
writ doctor --json
Returns a structured DoctorReport with all check results, statuses, and messages.
Health Checks
writ doctor runs 8 checks:
| Check | What it verifies | Fail means |
|---|---|---|
version_file | .writ/version.toml exists and parses | Corrupt version file (Warning if missing) |
schema_version | Schema version matches current binary | Newer writ created this repo, or migration needed |
directories | Required dirs: objects, seals, specs, heads, keys, agents | Structural damage to .writ/ |
index | .writ/index.json exists and deserializes | Index corruption, rebuild needed |
config | .writ/config.toml parses (if present) | Invalid TOML in config |
master_key | keys/.master exists | Missing cryptographic key |
specs | All spec JSON files deserialize | Corrupt spec data |
seals | First 50 seal JSON files deserialize | Corrupt seal data |
Exit code is 0 when all checks pass, 1 if any check fails.
Troubleshooting
“this repo was created by a newer version of writ”
Your writ binary is older than the one that last touched this repo. The on-disk schema has features your binary doesn’t understand.
Fix: Update writ to the latest version.
pip install --upgrade writ-vcs # if installed via pip
brew upgrade writ # if installed via Homebrew
cargo install writ-cli # if installed from source
Missing .writ/version.toml
This means the repo was created before schema versioning existed (pre-beta). Any writ command will auto-migrate it to the current schema. You can also run writ doctor to confirm the repo is healthy after migration.
writ doctor reports failures
Doctor is read-only by default. It tells you what’s wrong but doesn’t fix anything. Common issues:
- Missing directories: A partial init or interrupted migration. Re-running
writ init --yesin the project will recreate missing structure. - Corrupt index.json: The file tracking index is damaged. A fresh
writ bridge importfrom git can rebuild it. - Missing master key: The Ed25519 keypair was lost. Seals can still be created (they’ll lack signatures) but verification will fail for new seals.
The --fix flag is reserved for a future release that will automate common repairs.
Error Codes
Full error reference coming soon.
Common Errors
“No writ repository found”
You’re not inside a writ project. Run writ init to set one up, or navigate to a directory that has a .writ/ folder.
“Seal chain verification failed”
A seal in the history has been tampered with or corrupted. Run writ verify for details on which seal failed and why.
“File outside agent scope”
An agent tried to seal changes to files outside its declared scope. Either update the agent’s scope constraints or use a different agent.
“Invalid lifecycle transition”
A spec can’t move from its current state to the requested state. For example, you can’t complete a spec that’s been cancelled. Check the spec’s current state with writ spec status.
Troubleshooting
Something Broke During Agent Work
Every seal is an immutable snapshot. This is the safety net, whether an agent went off the rails, a model update produced unexpected behavior, or a convergence produced bad output.
# Find the last known good seal
writ log --all
# Inspect it to confirm
writ show <SEAL_ID> --diff
# Restore to that state
writ restore <SEAL_ID>
# Seal the restored state
writ seal -s "restored to pre-breakage state" --agent human
Restoring doesn’t delete history. All previous seals remain in the log. And since writ works alongside git, you always have the git safety net underneath.
Convergence Produced Unexpected Results
Check the convergence report for details:
writ converge-all --dry-run --strategy escalate
Common causes:
- Low confidence scores: The engine wasn’t confident about the merge. Review the escalated files manually.
- Unexpected merge result: The convergence engine produced an incorrect merge. File a bug report with the seal IDs involved.
- Overlapping function edits: Two agents modified the same function body. This is a real conflict. Review and resolve.
If the merged output is wrong, restore to the pre convergence seal and try again with a different strategy, or resolve the escalated conflicts manually.
Seal Chain Verification Fails
writ verify
If verification fails, it reports the exact seal where the chain broke. Common causes:
- A seal file was manually edited (don’t do this)
- Disk corruption
- An interrupted write operation
Recovery: the seals before the break point are still valid. You can restore to the last valid seal.
Context Shows Stale Specs
writ context automatically detects specs with no recent activity. If a spec is stale:
# If the spec is done, complete it
writ spec complete <ID>
# If the spec is abandoned, cancel it
writ spec cancel <ID>
Stale detection keeps nothing from falling through the cracks. Specs that go inactive surface automatically in context output.
Storage Growing Too Large
Check storage usage:
writ gc status
writ gc storage
Run garbage collection:
writ gc run --dry-run # Preview what would be cleaned
writ gc run # Execute with confirmation
GC never deletes seals. Immutable history is sacred. It only cleans expired working state, archived specs past retention, and old security events.
Git and Writ Out of Sync
If your git repo has moved ahead of writ’s baseline:
writ bridge import
This re-imports the current git state as a new baseline seal. Git is the storage layer, writ is the intelligence layer. They stay in sync through the bridge.
Getting Help
- Bug reports: github.com/andrew-garfield101/writ/issues
- Source code: github.com/andrew-garfield101/writ