Skip to content
archmax.ai

The OpenSpec workflow

Development on archmax automation is spec-driven. A substantive change flows through OpenSpec artifacts under openspec/ before and while it is implemented, and the specs under openspec/specs/ say what the platform does now.

Each open change lives at openspec/changes/<change-name>/:

Artifact Purpose
proposal.md Why the change exists, what changes, which specs it adds or modifies, its impact
design.md Context, goals and non-goals, decisions with the alternatives considered, risks, rollout
specs/<spec>/spec.md Delta specs: added, modified or removed requirements, each with scenarios
tasks.md The implementation checklist, each task with how it is verified

A requirement says what the platform SHALL do (the validator reads the first line of a requirement’s body for SHALL or MUST), and each has at least one #### Scenario: with WHEN and THEN bullets. A modified requirement is restated in full, keeping every scenario title it already has.

  1. Propose: describe the change and generate the proposal, the design, the delta specs and the tasks.
  2. Apply: implement the tasks and check them off as they land. When the work shows the design was wrong, the artifacts change with it.
  3. Archive: merge the delta specs into openspec/specs/ and move the change under openspec/changes/archive/. Then git add the archive: the spec gate refuses an untracked one.

The openspec CLI drives this:

Terminal window
openspec list # open changes
openspec status --change <name> # which artifacts are done
openspec validate <name> # the change's artifacts and deltas
openspec archive <name> --yes # merge the deltas and archive

Agent-assisted work has matching slash commands in .claude/commands/: /opsx:propose, /opsx:apply, /opsx:sync and /opsx:archive.

The vocabulary table in openspec/config.yaml is normative: one word per concept, shared with the SDK, used in code identifiers, collection and route names, UI strings, specs and these pages. Its “not called” column lists the spellings that must not appear. When a concept has no word yet, the row is added to the table first. The glossary is that table, rendered when this site is built.

pnpm check:specs (tools/check-specs.ts) runs in CI beside the typecheck and before every push. It fails on:

  • a spec without exactly one ## Purpose, or one that starts with TBD;
  • a requirement without SHALL or MUST, without a scenario, or a scenario without WHEN and THEN;
  • a requirement title used twice anywhere under openspec/specs/;
  • a “not called” spelling in a spec or on a page of this site (fenced code blocks are skipped);
  • a spec directory missing from openspec/spec-map.yaml, or a mapped path that does not exist;
  • an archived change left untracked under openspec/changes/archive/;
  • the product’s former codename on any line of the repository outside the archive.

openspec/spec-map.yaml maps each spec to the modules that implement it, under the keys core, api, frontend, docs (this site) and ci (the workflows, scripts and image). A new spec’s entry is added when the change is archived, because the map is checked against openspec/specs/.

openspec/config.yaml injects two rules into artifact generation: a proposal for a change owners, authors or operators see or configure names the pages of this site that change, and its tasks include updating them. Write a page about the behaviour as it is, from the spec it describes, and link the harness docs for the SDK’s own concepts instead of restating them.