Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Sigil — the writ-vcs mascot

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 context returns 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 merge smarter. 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

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

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:

  1. Initialized writ with automatic environment detection and format configuration
  2. Sealed a checkpoint with structured metadata (agent, spec, status)
  3. Checked context in token optimized TOON format, one call for full project state
  4. Defined a spec that agents can work against
  5. Completed the spec with writ spec done, final seal and clean lifecycle transition
  6. Checked status to see fleet progress at a glance
  7. 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

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("<", "&lt;").replace(">", "&gt;")
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:

  1. Pool filter selects both specs for convergence (both have sealed changes to the same file)
  2. 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
  3. 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("<", "&lt;").replace(">", "&gt;")

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:

  1. Optimizing merge order. Disjoint specs merge first, reducing conflict surface for later merges
  2. Composing additive changes. The common case in multi agent work where agents build complementary features
  3. 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

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 CommitWrit Seal
IdentityEmail addressAgent ID with trust level and role
Task linkageBranch name (convention)First class spec reference
StatusNonein-progress, complete, blocked
Test resultsNone (CI runs after the fact)tests_passed, tests_failed, linted
IntegritySHA-1 hashBLAKE3 content hash + chain hash + Ed25519 signature
Scope awarenessNoneCan enforce file scope constraints per agent
Machine readableNeeds parsingStructured 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:

  1. Content hash: BLAKE3 hash of the seal’s own data (files, metadata). Proves the seal hasn’t been modified.
  2. Parent seal hash: BLAKE3 hash of the previous seal. Links seals into a chain.
  3. 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

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

FieldPurpose
idUnique identifier, used in --spec flags
titleHuman readable name
descriptionWhat needs to be built
statusCurrent state (in-progress, complete, blocked)
depends_onOther spec IDs this depends on
file_scopeWhich 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 context warns 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

LevelDescriptionConvergence Impact
fullUnrestricted. Typically the project owner or lead agent.Full confidence scoring
standardNormal working agent. The default for most agents.Standard confidence scoring
restrictedLimited scope. For agents that should only touch specific files.Reduced confidence caps
untrustedNew 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:

  1. Epoch boundary: only specs from the current session (since the last writ finish)
  2. Commit state: only uncommitted specs
  3. 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 --apply for explicit control

Confidence Thresholds

RangeAction
>= 0.85Auto resolve. The merge is highly confident. Apply without human review.
0.60 to 0.84Suggest. Present as a recommendation for review.
< 0.60Escalate. 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

StrategyBehavior
escalateAuto resolve high confidence merges, escalate the rest. Recommended for most workflows.
three-way-mergeStandard three way merge. Leaves conflict markers where resolution fails.
most-recentPrefers the most recently sealed version on conflict.
orchestratorReports 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:

  1. Specs that touch disjoint files merge first (zero conflict risk)
  2. Specs with minimal overlap merge next
  3. 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

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

FormatFlagDescription
JSONjsonStandard JSON with indentation. Maximum compatibility. Human readable.
JSON Compactjson-compactMinified JSON with no whitespace. Smaller than pretty JSON, still parseable by any JSON library.
TOONtoonToken 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.

SourceTokensCapabilitiesTokens per Capability
git (5 commands)8954224
writ context (TOON)1,68510168

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 TypeSavings (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

CommandNotes
writ contextPrimary use case for TOON. Full project state.
writ logSeal history. Highly tabular, ideal for TOON.
writ spec statusActive specs and their state.
writ statusFleet overview: agents, specs, progress.
writ showSingle 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

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:

FieldWhat It HashesWhat It Proves
content_hashThe seal’s own data (files, summary, metadata)This seal hasn’t been modified
parent_seal_hashThe previous seal’s contentThe previous seal is the real predecessor
chain_hashCombination of content hash and parent seal hashThe 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

LevelDescriptionImpact
FullProject owner or lead. Unrestricted access.Full confidence scoring in convergence.
StandardNormal working agent. The default.Standard confidence scoring.
RestrictedLimited scope. Constrained to specific files.Reduced confidence caps. Changes more likely to be reviewed.
UntrustedNew 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

  1. A spec declares its file scope: src/auth/*, tests/test_auth.py
  2. An agent is registered with scope constraints
  3. 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:

  1. writ context output (under scope violations)
  2. Security event log (writ security events)
  3. 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 TypeWhen It Fires
scope_violationAgent seals changes outside its declared scope
chain_hash_failureSeal chain verification detects tampering
authentication_failureSignature verification fails
agent_revokedAn agent is revoked or suspended
convergence_low_confidenceConvergence produces results below the confidence threshold
unrecognized_agentA 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

LayerWhat It Protects
Hash chainsHistory integrity. No seal can be modified after creation.
SignaturesAuthorship. Every seal can be verified back to its creator.
Trust levelsConvergence quality. Lower trust agents get more scrutiny.
Scope enforcementFile boundaries. Agents stay in their lane.
Content traceabilityMerge integrity. No hallucinated or injected content survives. (In development, part of LLM convergence pipeline)
Event monitoringAuditability. Every security relevant action is logged.

Next Steps

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 ConceptWrit ConceptWhat Improves
(nothing)SpecTask tracking with lifecycle, agent claiming, genesis snapshots. No git equivalent
CommitSealAgent identity, spec linkage, immutable chain
MergeConvergeWrit’s convergence engine merges seal trees. Auto resolves independent changes
WorktreeWorkspaceShared object store, scoped context, native convergence (Level 2 only)
git statuswrit contextOne call, structured output, token optimized
git logwrit logFilterable by spec, agent, workspace
(nothing)writ planBatch 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:

  1. writ spec done: when an agent marks its task complete, convergence checks for overlapping work with other completed specs
  2. writ 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 --spec correctly? 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

SituationModeWhy
Learning writuserFull control, see everything
Solo developeruserSimple, direct
Small team (2-5 agents)userEasy to track manually
Medium team (10-50 agents)userConvergence runs at spec done and finish, user controls commits
CI pipelineautoNeeds to commit without waiting
Overnight batchautoNo 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:

  1. Detects the git branch and HEAD commit
  2. Imports the current working tree as a baseline seal (bridge import)
  3. Configures agent frameworks (CLAUDE.md, AGENTS.md, etc.)
  4. 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:

  1. Shows all completed specs
  2. Lets you select which to include
  3. Offers commit strategies (single, per spec, grouped)
  4. Auto generates commit messages from seal history
  5. Runs git add and git 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 finish manually. 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 init once per project, at the start of each session
  • Let agents work entirely in writ (seal, spec done, context)
  • Use writ status to monitor progress
  • Run writ finish when 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 ConceptWrit ConceptWhat Improves
BranchTaskStructured metadata: title, status, agent assignment, file scope
CommitSealAgent identity, spec linkage, workspace tagging, immutable chain
MergeConvergeGenesis tree based, confidence scoring, auto resolves independent changes
git statuswrit contextOne call, structured output, token optimized, agent ready
git logwrit logFilterable 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:

  1. Writ detects all workspaces with changes
  2. Runs convergence: files changed in only one workspace merge cleanly, overlapping changes go through the convergence engine
  3. If convergence is clean, proceeds to git commit
  4. If there are escalations that need human resolution, writ stops and shows you what needs attention
  5. 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:

  1. Spec: a structured task definition (ID derived from title, or --id to override)
  2. Workspace: a full project copy at workspaces/<id>/ with its own index and HEAD
  3. Assignment: the spec is scoped to the workspace so writ context surfaces 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

ScenarioRecommendation
Single agentNo workspace needed. Work directly in the project.
Multiple agents, different filesNo workspace needed. Same directory, spec-scoped sealing. See Multi Agent Workflow.
Multiple agents, additive changes to same fileNo workspace needed. Convergence runs at spec done and writ finish.
Multiple agents, competing rewrites of same codewrit task per approach. Physical isolation prevents in-progress breakage.
CI pipeline with parallel jobswrit 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

ToolParametersWhat It Does
writ_contextspec (optional), format (optional, default: toon)Full project state. Run this FIRST at the start of every task.
writ_sealsummary (required), spec (optional), agent (optional, default: claude-code), paths (optional)Checkpoint current work. Auto-scoped when agent has one claimed spec.
writ_spec_adddescription (required), id (optional), title (optional)Create a new task/spec. Hash ID auto-generated from description.
writ_spec_doneid (optional), summary (optional)Mark a spec complete. Creates a final seal.

Status and Review

ToolParametersWhat It Does
writ_statuscompleted (optional), active (optional), agent (optional), spec (optional)Fleet overview: agents, specs, progress.
writ_diffspec (optional), agent (optional), stat (optional)View file changes, optionally scoped to a spec or agent.
writ_logspec (optional), agent (optional), all (optional), limit (optional)Seal history. all includes diverged branches.
writ_showseal_id (required), diff (optional)Inspect a specific seal in detail.

Spec Management

ToolParametersWhat It Does
writ_spec_statusstate (optional), format (optional)List specs, optionally filtered by lifecycle state.
writ_spec_showid (required)Detailed view of a single spec.
writ_spec_reopenid (required)Reopen a completed spec for continued work.

Round Trip

ToolParametersWhat It Does
writ_finishspecs (optional), message (optional), strategy (optional), dry_run (optional)Promote completed specs to git commits.
writ_summaryformat (optional, default: commit)Generate commit messages or PR descriptions from seal history.

Recovery and Convergence

ToolParametersWhat It Does
writ_restoreseal_id (required)Restore working directory to any seal’s state.
writ_convergestrategy (optional), dry_run (optional)Merge diverged branches. Strategies: escalate, three-way-merge, most-recent, orchestrator.

Diagnostics

ToolParametersWhat It Does
writ_verifychain (optional), all_chains (optional), seal (optional)Verify cryptographic integrity of seals and chains.
writ_doctorfix (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 --agent on every seal.
  • Output format: Defaults to toon for writ_context. Agents get token optimized output without asking.
  • Auto-scoping: When an agent has exactly one claimed spec, spec is optional on writ_seal and writ_spec_done. The MCP server resolves the claimed spec automatically.
  • Context token: writ_context via 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:

LayerWhatHow
1. InstructionsWrit workflow in CLAUDE.md / AGENTS.mdGenerated by writ init
2. Slash Commands20 commands in .claude/commands/Generated by writ init
3. MCP Tools21 native tools via MCP protocol.mcp.json generated by writ init
4. HooksSessionStart hook runs writ context automaticallyConfigured by writ init
5. EnforcementSeal 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

CommandWhat It Does
/writ-contextGet structured project state. Run this FIRST at the start of every task.
/writ-sealCheckpoint current work with a summary and spec linkage.
/writ-spec-addCreate a new task/spec with an ID and title.
/writ-spec-doneMark a spec complete. Creates a final seal.

Status and Review

CommandWhat It Does
/writ-statusFleet overview: agents, specs, progress, commit readiness.
/writ-diffView file changes. Supports --spec, --agent, --stat, --name-only.
/writ-logSeal history. --all includes diverged branches.
/writ-showInspect a specific seal. --diff shows file changes.

Spec Management

CommandWhat It Does
/writ-spec-statusList specs, optionally filtered by lifecycle state.
/writ-spec-showDetailed view of a single spec.
/writ-spec-reopenReopen a completed spec for continued work.

Round Trip

CommandWhat It Does
/writ-finishPromote completed specs to git commits. Supports --strategy, --dry-run.
/writ-summaryGenerate commit messages (--format commit) or PR descriptions (--format pr).

Recovery and Convergence

CommandWhat It Does
/writ-restoreRestore working directory to any seal’s state.
/writ-convergeMerge diverged branches. Strategies: escalate, three-way-merge, most-recent, orchestrator.

Diagnostics

CommandWhat It Does
/writ-verifyVerify cryptographic integrity of the seal chain.
/writ-doctorRun 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 CommandsMCP Tools
How agent finds themCommand palette, / prefixTool palette, native MCP discovery
How agent invokes themRuns the CLI command in a shellCalls the MCP tool function
OutputCLI stdout (text or TOON)MCP tool result (text)
Works without MCPYes, just files on diskNo, requires MCP connection
Works without Claude CodeNo, Claude Code specificYes, 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 install and writ uninstall are deprecated aliases for writ init and writ uninit respectively. 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:

  1. Detects all non-main workspaces
  2. Runs convergence (prints merge report)
  3. If convergence has unresolved escalations, errors out before committing
  4. If convergence is clean, proceeds to git commit
  5. 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:

CheckWhat it verifies
version_file.writ/version.toml exists and parses
schema_versionSchema version matches current binary
directoriesRequired dirs: objects, seals, specs, heads, keys, agents
index.writ/index.json exists and deserializes
config.writ/config.toml parses (if present)
master_keykeys/.master exists
specsAll spec JSON files deserialize
sealsFirst 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 init automatically generates .mcp.json when Claude Code is detected. Use writ mcp-install for 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):

  1. CLI flag (--format toon)
  2. Environment variable (WRIT_FORMAT=toon)
  3. Project config (.writ/config.toml)
  4. Global config (~/.writ/config)
  5. 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:

FormatFlagDescription
json--format jsonStandard JSON. Maximum compatibility. Default if no config is set.
toon--format toonToken Oriented Object Notation. Field names declared once, rows streamed as values. Recommended for LLM agents.
json-compact--format json-compactMinified 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.

ProfileStorage BudgetRetentionCompressionUse Case
raspberry-pi500 MB7 daysLevel 1Constrained environments
development5 GB30 daysLevel 3Local development
production100 GB90 daysLevel 3Production systems
enterpriseUnlimited365 daysLevel 6Enterprise deployments

Files

FileLocationPurpose
~/.writ/configHome directoryGlobal defaults (format, frameworks, workflow mode)
.writ/Project rootWrit repository data (seals, objects, heads, keys, config)
.writ/config.tomlInside .writProject-level configuration overrides
.writ/gc-config.jsonInside .writGarbage collection settings (profile, budgets, retention)
.writ/keys/Inside .writCryptographic keys (Ed25519 convergence keypair, AES-GCM encrypted)
.writignoreProject rootFiles 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

VariableEffectExample
WRIT_FORMATOverride output formatWRIT_FORMAT=toon writ context
WRIT_COMMIT_MODEOverride workflow modeWRIT_COMMIT_MODE=auto writ finish
WRIT_PROFILEOverride deployment profileWRIT_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:

  1. Load .writ/version.toml (if it exists)
  2. Missing file? Treat the repo as schema version 0 (pre-versioning legacy repo)
  3. Schema is current? Continue normally, update last_opened_by
  4. Schema is behind? Run migrations automatically (v0 to v1, v1 to v2, etc.)
  5. 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.toml is backed up to .writ/version.toml.bak before 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.toml with schema_version = 1
  • Ensures all expected directories exist: objects/, seals/, specs/, heads/, keys/, agents/, proposals/, security/, security/events/
  • Creates the HEAD file if missing
  • If a legacy settings.json exists without a config.toml, creates a default config.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:

CheckWhat it verifiesFail means
version_file.writ/version.toml exists and parsesCorrupt version file (Warning if missing)
schema_versionSchema version matches current binaryNewer writ created this repo, or migration needed
directoriesRequired dirs: objects, seals, specs, heads, keys, agentsStructural damage to .writ/
index.writ/index.json exists and deserializesIndex corruption, rebuild needed
config.writ/config.toml parses (if present)Invalid TOML in config
master_keykeys/.master existsMissing cryptographic key
specsAll spec JSON files deserializeCorrupt spec data
sealsFirst 50 seal JSON files deserializeCorrupt 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 --yes in the project will recreate missing structure.
  • Corrupt index.json: The file tracking index is damaged. A fresh writ bridge import from 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