Skip to main content

Metadata and ledger reference

An increment is a folder, .specweave/increments/NNNN-slug/. You write spec.md. The CLI writes everything else. Agents do not need to read metadata.json; specweave pickup and specweave task next give them what they need.

Files in an increment folder​

FileWritten byHolds
spec.mdYou or the agentProblem, Scope, Acceptance Criteria, Approach, Open questions, Tasks.
metadata.jsonThe CLIStatus, type, timestamps, tracker links.
ledger.jsonlspecweave task, note, handoff, pickupEvery claim, completion and message, one JSON object per line. The only task state.
plan.mdcreate-increment --with-planOptional long design notes.
rubric.mdspecweave generate-rubricOptional quality contract tied to the acceptance criteria.
handoff.md, handoff.diffspecweave handoffThe last handoff and the uncommitted diff at that moment.
reports/verify.md, reports/verify.jsonspecweave verifyTest, lint and build results. complete checks verify.json.
reports/task-T-01.txttask done --runFull output of the task's test command.
reports/handoff-report.htmlspecweave report, handoff, pickupHTML timeline of who did what: tools, sessions, handoffs, pickups and evidence.
tasks.md2.x onlyTask definitions in increments created before 3.0. Still read.

metadata.json​

specweave create-increment writes this:

{
"id": "0042-add-login-form",
"status": "active",
"type": "feature",
"priority": "P1",
"created": "2026-09-20T09:12:00.000Z",
"updated": "2026-09-20T09:12:00.000Z",
"lastActivity": "2026-09-20T09:12:00.000Z",
"title": "Add login form",
"project": "my-app"
}

Never edit status by hand. Use start, pause, resume, abandon and complete.

Fields​

FieldNotes
idFolder name, such as 0042-add-login-form.
statusSee the statuses below.
typefeature (default), bug, hotfix, refactor, change-request or experiment.
priorityP1 by default; set with --priority.
createdISO timestamp.
updatedLast change. Every ledger append also updates it. lastActivity is the same value, kept for 2.x increments.
titleThe title given to create-increment.
project, boardWhich workspace repo or board the increment belongs to, when there is more than one.
planning.parallelPresent when created with --parallel.
testMode, coverageTargetPer-increment overrides of the testing config. Filled in with defaults when missing.
externalLinksTracker issue numbers and URLs, written by specweave sync push or an import. Never hand-edit.

Lifecycle fields​

Set by the CLI during status changes:

FieldSet when
pausedAt, pausedReasonspecweave pause
abandonedAt, abandonedReasonspecweave abandon
approvedAtspecweave complete
readyForReviewAtcomplete passes through ready_for_review on the way to completed.
closeReasoncomplete --reason, or when another increment supersedes this one.
supersedescreate-increment --supersedes NNNN. The old increment is abandoned with closeReason: "superseded by <new id>".
parentcreate-increment --parent NNNN.

Statuses​

StatusMeaning
plannedSpec exists, work has not started. create-increment --planned or an import.
activeBeing worked on. The default for a new increment, and what task, verify and handoff use when you give no id.
pausedStopped for now.
completedClosed by specweave complete.
abandonedDropped or superseded.

backlog and ready_for_review are 2.x states that still load. Older spellings are mapped when read: planning, new and todo become planned; in_progress and started become active; done and closed become completed; cancelled and superseded become abandoned. See the increment status reference.

ledger.jsonl​

The ledger is append-only. Each line is one JSON object, UTF-8, LF line endings:

{"t":"T-01","e":"claim","by":"claude@laptop","at":"2026-09-20T10:00:00.000Z"}
{"t":"T-01","e":"done","by":"claude@laptop","at":"2026-09-20T10:40:00.000Z","evidence":"npm test -- login → exit 0\n12 passing"}
{"t":"*","e":"note","by":"codex@cloud","at":"2026-09-20T11:05:00.000Z","note":"Staging API key rotated; ask before running e2e"}
{"t":"*","e":"handoff","by":"claude@laptop","at":"2026-09-20T12:00:00.000Z","note":"out of tokens · next: T-02 restore draft"}
{"t":"*","e":"pickup","by":"codex@cloud","at":"2026-09-20T12:10:00.000Z","note":"from claude@laptop, snapshot 3f9c2a1"}
KeyRequiredMeaning
tyesTask id such as T-01, or * for an increment-level event.
eyesEvent type, below.
byyesAgent id.
atyesISO 8601 timestamp.
notefor skip and blockReason or message.
evidencefor doneCommit sha, test command and output tail.

Lines that do not parse are counted and skipped, never fatal. A byte-order mark and CRLF endings are tolerated.

Task events​

EventEffect
claimThe task becomes claimed by by. Ignored if someone else holds a claim that is still within the lease; the earliest claim wins.
doneThe task is done. Ignored without evidence, or if someone else holds a live claim.
releaseThe owner gives the task back; it becomes open.
blockThe task is blocked, with a reason in note.
skipThe task will not be done. Final. Ignored without a note.

A claim older than the lease (tasks.leaseHours, default 2 hours) is stale, and another agent may take the task over. Task state is derived by sorting events by time and replaying them, so the order of lines in the file does not matter.

An acceptance criterion counts as met when every task that lists it under AC: is done. Nobody ticks boxes in spec.md.

Increment events​

These use "t": "*" and never change task state.

EventWritten by
notespecweave note "<text>". A message for whoever works on the increment next.
sessiontask claim, task done and pickup, once per tool session, when the tool exposes a session id.
handoffspecweave handoff. note carries the reason and the next step.
pickupspecweave pickup when it applies a handoff. note says who it came from and which snapshot.

pickup shows the latest notes and the last handoff. Secrets are scrubbed from note and handoff text before they are written.

Agent id​

by is <tool>@<host>, for example claude@laptop, codex@build-box or claude@cloud. The tool is detected from the environment (Claude Code, Codex, Grok, Cursor, Gemini CLI, Copilot, OpenCode; cli otherwise). Cloud sessions use cloud as the host so a new container keeps its own claims. Override with SPECWEAVE_TOOL and SPECWEAVE_HOST, or set the whole id with SPECWEAVE_AGENT. specweave task whoami prints it.

Writing the ledger without the CLI​

Any tool can append a line itself:

echo '{"t":"T-01","e":"claim","by":"grok@laptop","at":"2026-09-20T10:00:00Z"}' >> .specweave/increments/0042-add-login-form/ledger.jsonl

Appends are single-line writes, so two agents in one working tree do not corrupt each other. Across branches, .gitattributes sets **/ledger.jsonl merge=union, so a merge keeps every line from both sides. See cross-tool handoff for the full multi-agent flow.