spexcode
v0.4.1
Published
SpexCode — a spec-driven, self-developing dev tool. The `spex` CLI + spec server reads the .spec tree and its git history, and serves the dashboard.
Readme
Spec-driven development with AI agents in the loop. SpexCode keeps a versioned tree of specs inside your git repo, links every spec to the code it governs, and runs a session manager that dispatches coding agents into isolated worktrees. You review and merge; the tool keeps intent and implementation from drifting apart.
▶ This repo's own spec tree, replayed from its git history — 160 spec nodes growing over three weeks. Click for the full video.
English | 中文 · Docs: spexcode.net · License: MIT
Quick links: the model · quick start · agents · eval · config
The model
A spec node is a directory under .spec/ containing a spec.md: frontmatter (title, status, a
code: pointer to the file it governs, a related: list for files it references) plus a prose
body stating what that part of the system is supposed to do, right now. Nodes nest, so the tree
mirrors how you think about the project rather than the file layout. The body can split into two
labelled parts. The short raw source states the intent; changing it takes explicit
human approval (an agent can draft it, a human signs off). The expanded spec is the agent's
detailed reading of that intent; it iterates freely but must always match the raw source.
Two rules make this workable:
Git is the database. There is no separate store. A node's version count is the number of commits that changed its
spec.md, its history view isgit logon that file, and each version is attributed to an agent session through aSession:commit trailer. This is also why a spec body always describes present intent and gets rewritten in place: changelog headings inside the body are banned (the linter enforces it), because git already keeps the history.Spec and code land together. A change is one commit that updates both the
spec.mdand the code it justifies. When code moves without its spec, the linter flags it,drift: spec-cli/src/graph.ts is 1 commit(s) ahead of spec 'graph-lean' (v12) — may be staleand keeps flagging until the spec catches up.
The optimization loop
Specs, commits, and evals compose into one loop. The spec is the loss function: it states what you want, and it's the half a human signs off on. Commits are the optimizer. eval, the measurement subsystem, scores how far live behavior currently sits from the spec, and the score's history lives in git like everything else.
It also settles where the human stands day to day: nobody reads a neural net by staring at its weights, and between merge gates you don't have to stare at agent diffs either. Attention goes to the spec and the evals; the diff gets read once, at merge time.
Quick start
Requires Node ≥ 22 and git. This part is plain tooling — no AI involved yet.
npm i -g spexcode # installs the `spex` command
cd your-repo
spex init # seeds .spec/, installs git hooks, materializes the agent contract
spex serve # API backend on :8787
spex serve ui # dashboard on :5173, proxying to the backendspex init is additive. It works on any existing git repo and never overwrites your files: it
creates a root .spec/project/spec.md and a starter spexcode.json, installs the git hooks, and
writes a managed block into CLAUDE.md/AGENTS.md so any agent working in the repo
discovers the workflow on its own.
Then grow the tree:
- Edit
.spec/project/spec.mdto describe the project. - Add child nodes for the parts you want governed, each with a
code:entry pointing at an existing file (related:for the files it touches but doesn't own). - Run
spex spec lint. Coverage warnings list the source files no spec claims yet; that list is your adoption TODO.
You are not expected to hand-author all of this. The intended workflow is to have an agent do most
of the spec writing; spex guide spec prints the exact file format it needs.
Getting started on the docs site walks the setup end to
end.
SpexCode's own repo on its own board; the sessions top-left are agents building the tool.
Working with agents
This part needs tmux and a logged-in Claude Code or Codex on the machine.
spex session new "make the settings page remember the last tab" --node settingslaunches a worker session in its own worktree on branch node/settings-…. The --node flag (or a
[[settings]] mention in the prompt, same effect) sets the branch name and board attribution; the
worker still finds and reads the governing spec itself before touching code. It makes the change,
rewrites the spec body to match, commits
both (a hook stamps the Session: trailer), then proposes a merge and stops. Workers never merge
themselves. The merge stays with the manager: when you fire it, the session's own agent runs the
actual git merge, so conflicts land on the one who knows the work. The same dispatch is a
button on the dashboard (the new-session box on the board); the command form is what agents
themselves use when they delegate.
You supervise from outside — on the board, or with the same commands your agent uses:
spex session watch # stream session transitions: launched / review / done / needs-input ...
spex session review settings # commits ahead of trunk, merge-base diff, merge-conflict/lint gates
spex session merge settings # gated merge into the trunk
spex session close settingsIndependent tasks run in parallel. Each worker is isolated in its own worktree, git serializes the merges, and a pre-commit guard blocks direct commits on the trunk, so everything flows through reviewable node branches.
The process is enforced by mechanism, not prompt engineering: the backend creates the branch and a hook stamps the attribution; the materialized contract block carries the rest, so your dispatch prompt stays task-only. More on this mode of working: working with agents.
Measuring behavior: eval
eval is the measuring half of
the loop, built on the YATU discipline (You As The User): you measure
behavior from the product's real surface, the way a real end user would touch it, not through an
internal helper or shortcut that makes the
proof easy. A spec says what a part should do; an
eval.md beside it says how to check. Each scenario is a plain description plus an expected
result. eval itself runs nothing (no DSL, no runner). An agent runs the scenario however it can:
a test file, a real browser, or just clicking through by hand and screenshotting. It compares
actual to expected and files the eval with evidence:
spex eval add settings --scenario remembers-tab --pass --image evidence.pngEvals live in a git-tracked ndjson next to the spec, so measurements get the same attribution and history as spec versions. Bug fixes are expected to bracket: file a failing eval that reproduces the bug, fix, then file a passing eval on the same scenario.
The eval view: scenario evals on the left; the selected eval's expected result, staleness, and recorded video evidence in the middle.
What's in the repo
| Package | Role |
|---|---|
| spec-cli | The spex CLI and the HTTP backend (Hono, runs via tsx, no build step). Reads .spec and git live; owns the session state machine and the linter. |
| spec-dashboard | React board: the node graph, per-node spec/history/issues panes, and a real terminal onto each live agent session. |
| spec-eval | Scenario definitions, evals, evidence. |
| spec-forge | Read-only tracer that resolves a forge's open issues and PRs to the spec nodes they serve (GitHub and GitLab drivers today). An issue links itself with a Spec: <node-id> line in its body; a PR from a node/<id> branch links for free. |
The linter
spex spec lint checks the spec↔code graph and is the real gate (the git hook is fast local feedback):
- integrity (error): a
code:orrelated:path that doesn't exist - living (error): a changelog heading in a spec body
- altitude (warn): a body that slid from contract prose into an implementation dump. The usual smell is a numbered step list or a wall of function names; this rule is why spec bodies stay short enough to actually read
- coverage (warn): unclaimed source files
- drift (warn): governed code changed after its spec's last version, derived live from git
plus naming and ownership rules (one-govern, id-format, mention as errors; breadth,
related-drift, owners, confusable-id as warns) — spex guide spec lists them all.
Configuration
spexcode.json (committed, portable: layout, lint budgets, dashboard identity, launcher names) and
spexcode.local.json (gitignored, host-specific: absolute launcher paths, cert paths) cover every
setting. There is no imperative settings verb: you edit the two files by hand (or ask your agent
to), and spex guide settings documents every field. The other
manuals are spex guide (the workflow), spex guide spec, spex guide eval, and
spex guide footprint; spex help maps the commands.
Contributing
docs/CONTRIBUTING.md gets you from a clone to a first merged change.
docs/AGENT_GUIDE.md has the full mechanics of the node model and the
reflexive plugin system.
Credit
First introduced on the LINUX DO community — thanks to everyone there for the first round of discussion.
License
MIT.

