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.

cursor: two agents, one task
$ 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
$ 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.

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 gate
# 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
Featurefeature/PROJ-42-login-timeout
Bug fixfix/PROJ-51-session-leak
No ticketfeature/rate-limiting
Task worktreefix/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.

integration: pull-request  (default)
ok  push           branch → origin
ok  pull request   .../pull/418
ok  jira           moved to QA

Reviewable before it lands, and
revertible after.
integration: merge
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.

Compare

What the others leave to convention.

Each of these is good at something. None of them arbitrates concurrent work, which is the only reason SpecOCD exists.

Capability SpecOCD OpenSpec spec-kit Kiro BMAD
Task claims with conflict detection Built in Worktree isolation (beta) None None Sequential relay
Stale-session recovery Heartbeat and takeover None None None None
Prior decisions replayed on pickup Automatic None None None Document handoff
Context budget cap 50 KB plus digest 50 KB per spec Not addressed Steering scope Persona scope
Drops into an existing repo Yes Yes Greenfield-leaning Partial Partial
Works with any AI tool 4 agents 20+ agents 30+ agents Its own IDE Tool-agnostic
Branch, approval gate and pull request Built in None None None None
JIRA ticket to QC loop Built in None Via extension None None
Ceremony Proportional Low High Medium High

Based on public documentation and community reports, September 2026. OpenSpec is the closest prior art. Its git-worktree isolation and cross-repo Stores are documented as beta. spec-kit's constitution step is genuinely stronger governance than anything here; it is also the framework most often reported as heavy for small changes.

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.

specocd jira start PROJ-123 Ticket becomes a change
specocd claim / log Agent plans and implements
specocd verify Criteria checked against code
specocd jira handoff Comments, moves to QC
jira-handoff.md If a write fails, paste-ready

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 change
specocd statusClaims, stale claims, cap breaches
specocd claim <change> T1Take a task, see prior decisions
specocd log <change> --task T1Record a decision or blocker
specocd verify <change>Check before archiving
specocd approve <change>Developer sign-off, then commit
specocd ship <change>Push, pull request, update the ticket
specocd archive <change>Fold into the baseline spec