Working to a specification
Name the spec folder a change answers, send an agent the tasks you choose, and make your own gates the verdict inside Spec Kit's workflow.
Write the specification with Spec Kit, OpenSpec, Kiro, or a folder of Markdown. Devplane runs the agent, watches it and decides whether the result is done, and adopts no spec format.
devplane change start "password reset" --spec specs/001-password-reset
devplane change show <id>What Devplane reads
- the spec is Markdown, committed to the repository;
- the unit is a folder;
- the outline is the headings;
- progress is
- [ ]/- [x]in the folder’stasks.mdonly (achecklists/folder is not progress).
No section name is recognised. Layouts found automatically:
| Tool | Detected by | Spec folders |
|---|---|---|
| Spec Kit | .specify/ | specs/NNN-name/ |
| OpenSpec | openspec/config.yaml | openspec/changes/<id>/ (not archive/) |
| Kiro | .kiro/ | .kiro/specs/<feature>/ |
Any other layout is one line of configuration:
# devplane.toml
[spec]
plans = "docs/plans" # where this repository keeps its specs
open_questions = ["NEEDS CLARIFICATION", "TBD"]open_questions are the words that mark an unanswered question, matched per line, case-insensitively; there is no default. The Specifications view in the workbench lists each project’s spec folders, task counts, and the lines still carrying one of those words.
Naming the spec a change answers
devplane change start "password reset" --spec specs/001-password-reset
devplane change adopt feat/password-reset --spec specs/001-password-resetThe path is relative to the repository and must exist in the base the worktree branches from; an uncommitted spec folder is not in the agent’s checkout. Every gate run stamps a fingerprint of the folder’s Markdown onto the change, so the certificate says checked against specs/001-password-reset at 3f9a….
$ devplane change show c-3f9a
spec specs/001-password-reset at 3f9a1c40b7e2d518 over 4 documents
tasks 11 ticked · 9 seen by a passing checkSending chosen tasks
--task picks which lines of the task list go to the agent. Repeat it, or mix the two forms:
# every task line that cites the requirement token REQ-3
devplane change start "reset: token expiry" \
--spec specs/001-password-reset --task REQ-3
# one exact line, file:line relative to the spec folder
devplane change start "reset: email copy" \
--spec specs/001-password-reset --task tasks.md:14The selected lines travel in the prompt and on the run’s record. A selector that matches nothing refuses before anything is created.
Requirement tokens are a prefix followed by digits: by default FR-, NFR-, SC-, REQ-, US- and AC-; [spec] tokens replaces the list. A token in a heading and in a task line is an edge; a token on only one side is reported as a gap. Spec Kit’s [US1] marker and Kiro’s _Requirements: 1.1, 2.3_ line are read as citations.
Ticked is not verified
A ticked box is the agent’s claim about its own work, so a change that names a spec reports two counts and never combines them. Neither is verified: only a change is.
tasks 11 ticked · 9 seen by a passing check
ticked, sent to nobody: tasks.md:31, tasks.md:32
REQ-3 3 tasks · 2 ticked · 0 seen by a passing check- ticked —
- [x]in the task file, read in the change’s worktree. - seen by a passing check — ticked, sent to a run of this change with
--task, and that run finished before acheckthat passed, so the check ran over its work. It stays true if a later check fails, which is why it is never called verified. - A box ticked by hand is listed as ticked, sent to nobody. With no gates declared the line reads 11 ticked · no gates declared, never 0 seen. No percentage is computed anywhere.
When the spec moves under a run
If the spec folder is edited while an agent works from it, the inbox says so when the run ends (the specification changed 18m into run r-9f2 and the run never saw it), with two answers:
devplane change drift <id> --run r-9f2 --tell # tell it what changed
devplane change drift <id> --run r-9f2 --accept # work to what the run saw--tell prompts the run with the changed files named; --accept makes the change work to what the run saw. Either is recorded as your decision.
A spec tool’s CLI is already a gate
If your spec tool validates from the command line, put it beside your tests:
# devplane.toml
[gates]
check = ["openspec validate --strict", "pnpm test -- --run"]That checks the specs against each other; your tests compare spec and code.
The gate inside Spec Kit’s workflow
Spec Kit’s commands look in .specify/extensions.yml for hooks, and run and wait for a hook marked optional: false. Devplane’s hook names the skill devplane-gate, which runs devplane gate and reports what it exited with. The agent has to find that skill as a file, so copy it into the project (or into ~/.claude/skills/ for every project) first:
mkdir -p .claude/skills/devplane-gate
curl -LsSf -o .claude/skills/devplane-gate/SKILL.md https://raw.githubusercontent.com/hupe1980/devplane/main/plugin/skills/devplane-gate/SKILL.md
devplane speckit # writes .specify/extensions.yml
devplane speckit --dry-run # print it, write nothing
devplane gate # what the skill runsThe Claude Code plugin ships the same skill, but devplane speckit does not look inside a plugin install, so it still refuses until the file is at .claude/skills/devplane-gate/SKILL.md in the repository or your home.
speckit needs Spec Kit initialised here (.specify/). If .specify/extensions.yml already exists, it prints the entry and where to add it instead of editing the file. The hook goes on after_implement unless you pass --event. It refuses when the repository declares no gate (declare [gates] check first) and when the skill file is missing; --anyway writes it regardless, and --dry-run applies the same refusals.
devplane gate runs [gates] check and exits 0 only when it passed; no gates declared and an unreadable devplane.toml exit non-zero. It records nothing, and the hook never waits on a person. States and flags: devplane gate.
Next
- Verified done — what the gate verdict means, and the certificate.
- Configuration:
[spec]— every key.
Something wrong or out of date? Edit this page.