Skip to content

Latest commit

 

History

History
326 lines (238 loc) · 13.5 KB

commands.md

File metadata and controls

326 lines (238 loc) · 13.5 KB

Command Reference

Machine-readable output

status, sync, verify, seal, and validate accept --format=json for consumers that run SpecMan as a subprocess. --format=human is the default; any other value is a usage error and the command exits 1 without doing work.

Every JSON-mode run that reaches a result writes exactly one document to stdout:

{
  "schemaVersion": 1,
  "command": "sync",
  "result": {}
}
  • schemaVersion is one integer for the whole tool. Adding a field, or a new member of an existing set of values, does not bump it; renaming a field, removing one, or changing what a value means does.
  • Nothing else goes to stdout in JSON mode. When a run fails before a result exists (no specs/, missing FEAT-ID, dirty tree), the error goes to stderr, stdout stays empty, and the exit code is the one documented for that failure.
  • Exit codes are identical in human and JSON mode. JSON changes the output channel, not the semantics.
  • message fields are human-oriented and may be reworded at any time. Branch on discriminators (outcome, reason, status, type), never on prose.

Per-command payloads are described under each command below.


specman init

Initialize SpecMan in the current directory.

specman init

Creates specs/, .specman/, .specman/implemented/, .specman/plans/. Purely additive — never deletes or overwrites. Idempotent — safe to run multiple times.

Condition Behavior
Clean directory Creates all 4 directories, exits 0
Already initialized Prints "Already initialized", exits 0
Partial state Creates only missing directories, exits 0
Path conflict (file where dir expected) Error, exits 1, creates nothing
No .git Warning (sync/status need git), still creates, exits 0

specman new

Create a new spec scaffold.

specman new "<title>" [--group <name>] [--id FEAT-NNNN]
Flag Description
--group <name> Place file in specs/<name>/, creating the dir if needed
--id FEAT-NNNN Force a specific ID instead of auto-assigning

ID assignment: Scans specs/ recursively for the highest existing FEAT-NNNN and assigns max + 1. IDs are never reused.

Slug: Derived from title — lowercase, non-alphanumeric → hyphens, no leading/trailing hyphens.

Output: Prints the created file path to stdout (single line, nothing else).

Scaffold: Required frontmatter + empty ## Intent and ## Acceptance criteria. Not yet valid per specman validate — the author fills in content.

Exit code Meaning
0 File created
1 Missing title, ID collision, or other error

specman validate

Check specs for errors and warnings.

specman validate [--format=json] [--strict]
Flag Description
--format=json Machine-readable output: result is { summary, findings }
--strict Warnings cause non-zero exit (normally only errors do)

Checks performed:

Code Severity What
E000 error Spec file fails to parse (malformed YAML, no frontmatter)
E001 error Duplicate id across files
E002 error Missing required frontmatter field (id, title, status, depends_on)
E003 error Wrong type for frontmatter field
E005 error Required section (Intent, Acceptance criteria) empty or missing
E006 error depends_on references nonexistent spec
E007 error Dependency cycle (length ≥ 2; self-refs ignored)
E008 error Duplicate AC ID within a single spec
E009 error Orphan snapshot (no matching spec)
E010 error Snapshot ID mismatch (filename vs content)
E011 error Orphan plan (no matching spec)
E012 error Invalid status value
W001 warning Filename doesn't match <FEAT-ID>-<slug>.md convention
Exit code Meaning
0 No errors (warnings allowed unless --strict)
1 Validation errors found
2 No specs/ directory found

specman status

Show drift status of all specs.

specman status [--verbose | -v] [--diff] [--format=json]
Flag Description
--verbose, -v Show all specs including in-sync ones
--diff Include unified diff for drifted specs
--format=json Machine-readable output (see below)

Output format:

FEAT-0001 drifted  (changed since last sync)
FEAT-0002 new      (no snapshot yet)
3 specs in-sync

Drift detection uses canonical-form comparison: the spec is parsed and re-serialized before comparing against the snapshot. This means formatting-only differences (frontmatter key order, blank lines) don't count as drift.

JSON payload: result.entries lists every spec (--verbose and --diff shape the human view only) with id, status, specPath, snapshotPath, and hint when there is one. new and drifted entries also carry driftSet: the acceptance criteria that changed, each with id, type (added, modified, removed), and text. When the drift set cannot be computed (unparseable snapshot), the entry carries driftSet: null and driftError instead; the exit code stays 0.

Exit code Meaning
0 Always (status is informational)

specman sync

Sync drifted specs — generate plans, run verification, seal snapshots.

specman sync [<FEAT-ID>] [--dry-run] [--regenerate] [--format=json]
Flag Description
<FEAT-ID> Sync a single spec. Without it, syncs all drifted/new specs
--dry-run Show what would be synced without generating plans
--regenerate Discard a plan with uncommitted changes and write a fresh scaffold
--format=json Machine-readable output (see below)

Single-spec sync: Generates a plan scaffold targeting only changed ACs, writes it to .specman/plans/<FEAT-ID>.md.

Multi-spec sync: Processes specs in dependency order. If a spec fails, it and all transitive dependents are skipped; independent specs continue.

Clean working tree required at start, except uncommitted plan files in the sync scope.

One sync, two invocations. Execution happens outside SpecMan, so a sync normally takes two runs: one writes the plan, and a later one closes it out. The second run attempts to seal when the plan declares at least one verification command and at least one commit exists since the plan's base commit (recorded in the plan header). Then, in order: the scope check, the trailer check, the plan's verification commands, and finally the snapshot commit.

Situation What sync does
Plan declares no commands Leaves the plan alone; it is still an unfilled scaffold
No commits since the base commit Reports the plan as awaiting implementation; nothing is sealed
A commit lacks a Spec: <FEAT-ID>/AC-N trailer trailer-check-failed, naming the commits; nothing is sealed
A verification command fails, or dirties the tree verification-failed, with the command detail; nothing is sealed
The plan's ACs no longer match the spec's drift set Scope error; nothing runs
The plan has no base commit (written by an older SpecMan) Refuses, directing to --regenerate
Everything passes sealed: the snapshot commit is the last commit of the invocation

On any of those failures the plan stays on disk untouched and the spec stays drifted, so a later invocation retries from the same plan. --regenerate takes precedence over closing out.

Resume, scaffold, or regenerate. If the plan file has uncommitted changes (including an untracked file), sync resumes from it as-is and never overwrites it. --regenerate is the only way to discard that content. If the plan is absent or byte-identical to HEAD, a fresh scaffold is written; the committed plan it replaced is recoverable with git restore. Sync is non-interactive: it never asks which path to take, and it always says which one it took.

Path planSource What happened
resume resumed An uncommitted plan was kept verbatim
scaffold scaffolded No plan existed, or the existing one matched HEAD
regenerate regenerated --regenerate discarded an uncommitted plan

JSON payload: three shapes under one "command": "sync", told apart by their fields.

Invocation result
single spec featId, outcome, message, plus planPath / planSource / verificationResult / trailerResult when they apply
all specs results (one per spec, same shape as above) and skipped (featId, reason)
--dry-run dryRun: true and entries (featId, status, summary)

outcome is one of in-sync, no-ac-drift, plan-written, sealed, verification-failed, trailer-check-failed, error.

Exit code Meaning
0 Sync completed: plan written, spec sealed, or nothing to sync
1 Error (dirty tree, parse failure, scope mismatch), failed verification, or failed trailer check

specman seal

Seal a snapshot for editorial or initial changes.

specman seal <FEAT-ID> [--initial] [--verify] [--format=json]
Flag Description
--initial Create first snapshot for a new spec (no prior sync needed)
--verify Run the plan's verification, then seal if it passes (allows AC-level drift)
--format=json Machine-readable output (see below)

Without flags: Updates the snapshot for a drifted spec. Requires no AC-level drift (only prose/metadata changes). Refuses if ACs were added, removed, or changed.

With --initial: Creates the first snapshot for a new spec. Use when implementation was done outside SpecMan.

With --verify: Seals a spec whose acceptance criteria did change, on the strength of its plan's verification. Runs the commands in the plan's ## Verification section exactly as specman verify does, and writes the snapshot only if all of them pass on a clean tree.

The AC-drift refusal asks for evidence rather than permission: the snapshot is SpecMan's only assertion that a spec is implemented, so it has to mean "verification passed" and not "someone said so". --verify produces that evidence at seal time, from commands the plan declares. There is deliberately no flag that simply skips the gate.

Consequences worth knowing:

  • A plan with at least one runnable command is required. SpecMan never infers verification commands (FEAT-0009), so with no plan the honest path is to run specman sync <FEAT-ID> and declare what would convince a reader the work is done.
  • The clean-tree requirement is not relaxed, before or after the commands run. Only this spec's own plan file may be uncommitted.
  • Works on a new spec too, where it does what --initial does but with evidence attached.

Clean working tree required.

JSON payload: outcome (sealed or error) and message. A refusal also carries reason:

reason Meaning
ac-drift Acceptance criteria changed; the spec needs specman sync. Carries driftSet
wrong-status The spec's status does not match the operation (e.g. --initial on a spec that already has a snapshot)
dirty-tree Uncommitted changes present. Carries dirtyPaths
no-spec No spec exists for that id
drift-error The drift set could not be computed (e.g. unparseable snapshot)
commit-failed The snapshot commit itself failed
verification-failed --verify only: a command exited non-zero. Carries verificationResult
no-plan --verify only: no plan file to read verification commands from
empty-verification --verify only: the plan declares no runnable commands
plan-parse-error --verify only: the plan file could not be parsed
Exit code Meaning
0 Snapshot sealed
1 Error (AC drift, wrong status, dirty tree)

specman verify

Run a plan's verification commands standalone.

specman verify <FEAT-ID> [--plan <path>] [--format=json]
Flag Description
--plan <path> Use a plan file at an arbitrary path
--format=json Machine-readable output (see below)

Reads verification commands from the plan's ## Verification section. Runs each sequentially via sh -c. Stops on first failure. Checks for dirty working tree after each command.

Does not write snapshots or create commits — purely diagnostic.

JSON payload: outcome (passed, command-failed, dirty-tree, no-plan, empty-verification, parse-error), message, and planPath. When commands ran, verificationResult carries passed and one entry per command with command, exitCode, stdout, and stderr (every command that ran, not just the failing one), plus dirtyPaths on a dirty-tree failure.

Exit code Meaning
0 All commands passed, tree clean
1 Command failed, dirty tree, or no plan found

specman delete

Remove a spec and all tracked artifacts.

specman delete <FEAT-ID> [--dry-run]
Flag Description
--dry-run Show what would be removed without deleting

Removes:

  • Spec file (specs/**/<FEAT-ID>-*.md)
  • Snapshot (.specman/implemented/<FEAT-ID>.md)
  • Plan (.specman/plans/<FEAT-ID>.md)
  • Assets directory (specs/assets/<FEAT-ID>/)

Warns about dependent specs but proceeds. Non-interactive — no confirmation prompt (git is the undo mechanism).

Exit code Meaning
0 Deleted (or dry-run completed)
1 Spec not found