Specification-Obsessed Development
Specs that hold up when agents work in parallel.
Every spec framework keeps markdown in git. That works for one agent at a time. SpecOCD adds the part they are all missing: task claims, conflict detection, and a decision log that the next agent actually reads before it starts.
A Node CLI for any language, in new or existing repos. Works with Claude Code, Cursor, Copilot and Codex.
$ specocd claim add-user-auth T1 CONFLICT: Task T1 is already claimed by claude-code (session claude-code:dev@studio, last heartbeat 10:24:02Z). Wait for it to be released, or let the claim go stale. exit 2 $ specocd claim add-user-auth T2 Claimed T2 in "add-user-auth" as cursor (cursor:dev@studio). Prior decisions on T2 (read before you start): [10:19:41Z] claude-code: Sliding window over fixed bucket: avoids burst at the boundary.▌
Real output. The second agent is stopped before it writes a line of code, then handed the reasoning behind the work it is picking up.
The gap
Markdown in git is not coordination.
OpenSpec, spec-kit, Kiro and BMAD all sync through versioned files. Nothing arbitrates when two agents, or two sessions of one agent, reach for the same work.
Two agents, one task
Both start the same ticket. Neither knows. The one that finishes second overwrites the first, and the loss is silent.
The reasoning evaporates
Session one chose JWT over cookies for a reason. Session two never hears it, picks differently, and the codebase ends up arguing with itself.
A dead session holds the lock
An agent crashes mid-task. Without a heartbeat there is nothing to expire, so the work stays claimed by a session that no longer exists.
How it works
Three commands carry the context.
Everything is plain text in your repo. No IDE, no service, no API key. The CLI never calls a model itself.
specocd claim
Claim before you touch code
Claims live in claims.yaml beside the
tasks. A live claim held by another session is a hard conflict with exit code 2, not a
warning an agent can talk itself past.
specocd log
Record the why, not the what
Decisions and blockers append to a per-change log tagged by task. The diff already shows what changed; this is the part git cannot reconstruct.
specocd release
Hand off cleanly
Completed, released and abandoned are distinct states. Claims carry a heartbeat, so a session that dies goes stale and the task can be reclaimed instead of stranded.
Verification
The CLI checks structure. The agent checks the code.
A tool can confirm every requirement is written and every task is closed. It cannot confirm the code actually does what the spec says, so SpecOCD refuses to pretend it can.
$ specocd verify rate-limiting rate-limiting requirements: 2 tasks: 2/2 done claims: 0 active, 0 stale Structural checks passed. Semantic verification is yours: R1: WHEN an IP exceeds 100 requests in 60s THEN the system shall respond 429 with Retry-After R2: WHEN the service restarts mid-window THEN the system shall restore counters from Redis Check each against the ACTUAL implementation. Read the code, run the tests. Never mark a requirement verified from the spec or task list alone.
Requirements are written WHEN/THEN, so acceptance criteria are unambiguous to a person and a model alike. On archive they fold into a per-feature baseline spec.
Features
Built in, not left to convention.
Everything below is one command in a repository you already have. The right-hand column is what you are otherwise left to arrange yourself.
| Capability | SpecOCD | Others |
|---|---|---|
| Task claims with conflict detection | Built in | Not offered |
| Stale-session recovery | Heartbeat and takeover | Not offered |
| Prior decisions replayed on pickup | Automatic, on claim | Document handoff at best |
| Isolated worktree per task | One shared claim registry | Sometimes, without shared claims |
| Human approval gate before commit | Required by default | Not offered |
| Branch, pull request and ticket in one step | Built in | Not offered |
| Ticket to QC loop | Built in, degrades to a paste-ready file | Via an extension, if at all |
| Context budget cap | 50 KB plus an agent-written digest | Varies; some cap specs, some do not |
| Drops into an existing repo | No files touched | Varies; several lean greenfield |
| Ceremony | Proportional to the change | Often fixed phases |
"Others" describes comparable spec-driven development frameworks as their public documentation stood in September 2026. They are good at things this table does not measure, and they move quickly, so weigh them on their own terms too.
Git
Nothing ships without a human.
Agents branch, implement and verify. Then they stop. A developer approves, and only that approval lets anything reach a commit, a remote or a ticket.
# the agent has implemented and verified, then stopped $ specocd ship PROJ-42-login-timeout "PROJ-42-login-timeout" has not been approved. Shipping pushes code and opens a pull request or merges, so it requires a human sign-off first: run `specocd approve PROJ-42-login-timeout`. # the developer reviews, then signs off $ specocd approve PROJ-42-login-timeout Approved and committed a3f91c2 on fix/PROJ-42-login-timeout. $ specocd ship PROJ-42-login-timeout ok push fix/PROJ-42-login-timeout → origin ok pull request https://github.com/acme/api/pull/418 ok jira commented and moved to QA▌
The ticket is updated last, so it is never told work is ready that never left the machine. If a step fails, the ticket is not touched and a paste-ready file records what remains.
Branch naming
| Kind | Branch |
|---|---|
| Feature | feature/PROJ-42-login-timeout |
| Bug fix | fix/PROJ-51-session-leak |
| No ticket | feature/rate-limiting |
| Task worktree | fix/PROJ-51-session-leak-t1 |
The prefix comes from the JIRA issue type, so bugs land on fix/ without
anyone choosing. Ticket keys keep their upper case, because that is what Atlassian and
CI match on to link a branch back to its ticket.
single or multi
Branch per change, or stay put
specocd init --mode single keeps you
on your current branch; multi gives each change its own. Switch any time with
specocd config set git.mode.
worktrees
An isolated checkout per task
Claims stop two agents taking one task. Worktrees stop agents on different tasks colliding on one filesystem — each gets its own branch and directory, sharing a single claim registry.
provenance
Every task carries its commits
Claiming records the branch and HEAD, releasing records HEAD again, so a decision in the log always has the commit range that acted on it.
One integration mode or the other.
A project either opens pull requests or merges straight to the base branch. Chosen at init, and the same for every change, so nobody has to wonder which happened.
ok push branch → origin ok pull request .../pull/418 ok jira moved to QA Reviewable before it lands, and revertible after.
ok push branch → origin ok merge branch → main ok push base main → origin ok jira moved to QA The base branch is pushed too: a local merge has shipped nothing.
Either way, a failed step stops the ticket update and records what remains. Pushes never
use --force, and SpecOCD never rebases.
JIRA
From ticket to QC, without leaving the terminal.
Add an API token to one gitignored file. The agent reads the ticket, including title, description, comments, due date and attachments, then scaffolds a change from it and reports back once the work is verified.
It degrades instead of failing
Permissions and workflow rules differ per project, and finished work should never be stranded by either. Reading a ticket is the only hard requirement. If the comment or the transition fails, SpecOCD writes a paste-ready file, prints the comment, and exits 3. It lists what already succeeded, so you never double-post or re-move a ticket.
Ticket text reaches the agent fenced as data, not instructions. Descriptions are written by other people, and a ticket that tells an agent to ignore its instructions gets surfaced to you rather than obeyed.
Questions
Common questions.
What is SpecOCD?
SpecOCD (Specification-Obsessed Development) is a lightweight, tool-agnostic spec-driven development framework. It stores specs as plain files in your repository and adds a coordination layer so multiple AI coding agents can work the same change without overwriting each other.
How is it different from OpenSpec, spec-kit, Kiro and BMAD?
Those frameworks synchronise through versioned markdown files, which works when one agent runs at a time. None of them arbitrates concurrent work. SpecOCD adds task claims with hard conflict detection, heartbeat-based recovery when a session dies, and a decision log replayed to the next agent that picks up a task.
How does it stop two agents working the same task?
An agent must claim a task before editing code. Claims are recorded in
claims.yaml beside the tasks. If a second agent claims a task another live
session already holds, the command fails with exit code 2 rather than allowing a silent
overwrite.
Does it work with existing projects?
Yes. Running specocd init in an existing repository creates a
.specocd/ directory and generates agent bindings without modifying any
existing files. No restructuring, and no assumption about your language, framework or
build system.
Which AI coding agents are supported?
Claude Code, Cursor, GitHub Copilot and Codex. SpecOCD generates the binding files each tool reads, so the same workflow applies whichever agent is driving. Binding generation is pluggable, so further agents can be added without changing the core.
Does it need an API key or send my code anywhere?
No. The CLI never calls a language model itself, so it needs no API key and no external service. When a spec exceeds its size cap it prints an instruction for whichever agent is already running to regenerate the digest. The only optional network calls are to your own JIRA instance.
What is the branching strategy?
Multi-branch mode gives each change its own branch, named
feature/PROJ-42-description or fix/PROJ-51-description
depending on the JIRA issue type. Single-branch mode stays where you are. Pick with
specocd init --mode, change it later with
specocd config set git.mode.
Can an agent commit or push on its own?
No. Agents implement and verify, then stop. Committing happens only when a developer
runs specocd approve, which re-runs verification first.
specocd ship refuses outright on a change nobody approved, and the
generated agent instructions tell agents never to run either command.
Can it merge straight to main instead of opening a PR?
Yes. A project is in one mode or the other:
specocd init --integration pull-request or merge, changeable
later with specocd config set git.integration. Pull request is the default
because it is reviewable and revertible. Merge mode pushes the base branch too, since a
merge that stays on your machine has shipped nothing.
What order does it push, merge and update the ticket?
Push, then pull request or merge, then the ticket — last. So a ticket is never told
work is ready that never left the machine. If a step fails the ticket is untouched, a
ship-manual.md records what remains, and it exits 4.
Can it work from JIRA tickets?
Yes. Add a JIRA API token to a gitignored credentials file, then
specocd jira start PROJ-123 fetches the ticket title, description,
comments, due date and attachments, and scaffolds a change from it. After verification,
specocd jira handoff comments on the ticket and moves it to your QC stage.
What if the JIRA update fails?
Reading a ticket is the only hard requirement. If posting the comment or transitioning
the ticket fails on permissions or workflow rules, SpecOCD writes a paste-ready
jira-handoff.md, prints the comment to the terminal and exits with code 3.
It lists whatever already succeeded, so you never double-post or move a ticket twice.
Get started
Five minutes, no restructuring.
Run it in a repo you already have. Init writes a .specocd/ directory, detects
your agent tooling, and generates the matching slash commands. It touches nothing else.
npm install -g specocd && specocd init
specocd propose "rate limiting"Scaffold a changespecocd statusClaims, stale claims, cap breachesspecocd claim <change> T1Take a task, see prior decisionsspecocd log <change> --task T1Record a decision or blockerspecocd verify <change>Check before archivingspecocd approve <change>Developer sign-off, then commitspecocd ship <change>Push, pull request, update the ticketspecocd archive <change>Fold into the baseline spec