@ct-builders/commercetools-spec-templates
v0.10.0
Published
Industry vertical spec templates for commercetools, rendered into GitHub Spec Kit or OpenSpec projects.
Maintainers
Readme
commercetools-spec-templates
Industry vertical specs for commercetools, rendered into your spec-driven development project.
Freely available, AS IS and UNSUPPORTED. This is reference code published by commercetools: no SLA, no security patching, not covered by commercetools Support. Issues are answered best-effort and it may be archived without notice. See SUPPORT.md before you build on it.
This repository is public, and it is the only one. The engine, the catalog, the taxonomy and the rendered specs all live here under MIT. Collector answers do not: they arrive through a Google Form, never touch GitHub, and are ingested into a gitignored
inbox/. What gets committed is the reviewed capability, never the raw response.
Prerequisites
commercetools AI Plugins. Every
spec this tool writes carries [SKILL: commercetools-*] annotations, and the skills those name
— commercetools-storefront, commercetools-platform, commercetools-checkout and the rest —
come from that bundle. Without it the annotations point at nothing, and an agent implementing a
task will invent API surface instead of loading the playbook for it.
/plugin install commercetools@commercetools # Claude Code
codex plugin marketplace add commercetools/commercetools-ai-plugins # Codex
npx skills add commercetools/commercetools-ai-plugins # skills only, any toolNode >= 20.19. A spec framework is not a prerequisite — init offers to run openspec init
for you if there is none.
Quick start
npx @ct-builders/commercetools-spec-templates init- Run it in the project that will hold the specs — not in this repo.
- Answer the questions. Which framework, who the storefront sells to, which industry, how much of it, and finally whether to copy the skills that work the specs for you. Every answer has a default; nothing is written until you have seen the file list and confirmed.
/commercetools-spec-planin your agent. It reads the specs it just wrote, works out what depends on what, and writesopenspec/PLAN.md— the build order. Read that file before going further: the order it chose and any foundation rows it invented are the two things worth correcting by hand, and correcting them later means unpicking changes./commercetools-spec-run. One spec at a time, from proposal to archived, until the plan is exhausted. Stops on its own; invoke it again to resume.
Steps 3 and 4 need the skills from step 2 — answer Yes, copy them. Skip them and you have the specs and nothing that works through them, which is a perfectly good place to stop.
Already know the answers, or scripting it?
npx @ct-builders/commercetools-spec-templates plan --industry grocery --model B2C # writes nothing
npx @ct-builders/commercetools-spec-templates apply --industry grocery --model B2C --skills allThat writes the grocery B2C spec set into openspec/specs/, runs the
commercetools SDD overlay
so every commercetools-touching task carries its [SKILL: …] annotation, and leaves a receipt at
.commercetools/spec-templates.lock.json so status, update and remove all work.
Step-by-step procedures for every task — bringing specs into a project, opening a collection
round, adding an industry, authoring a vertical — are in docs/runbook.md.
This README explains why things are shaped the way they are.
Three ways to answer the questions
The questions live in questions/developer-intake.yaml and are asked by whichever surface you use.
There is one question set, not three.
| Surface | How you answer | Use when |
| :--- | :--- | :--- |
| cts init | Numbered choices at a terminal | You are a human with a shell |
| The plugin skill | AskUserQuestion buttons in Claude Code, Cursor, Codex or Copilot | You are working with an agent |
| cts questions --answers '<json>' | A JSON request/response loop | You are building your own front end |
The third is the protocol the other two are built on: pass the answers you have, get back the next
question or the resolved outputs. cts init refuses to run without a TTY and names the flag-based
alternative, so it can never hang a CI job.
A question that has only one real answer is not asked — it is reported. If the project already has
a framework, you see Using OpenSpec — already set up in this project. rather than a prompt with
one option. That is prefill in the question file, not a special case in the code.
Prompts are a product surface. Nothing in a question may show a developer our internal
vocabulary — no _base, no P1, no "capability", no raw kebab-case ids, no unrendered {{...}}.
A test walks every question the flow can reach and fails the build on any of them.
What's in the catalog
| Bundle | Capabilities | What it is |
| :--- | ---: | :--- |
| _base x B2C | 30 | The industry-agnostic storefront: 8 journeys + 22 pages |
| _base x B2B | 48 | Adds the 9 B2B journeys and the 6 B2B-specific pages |
| grocery x B2C | 33 | The base plus 3 grocery capabilities |
| grocery x B2B | 50 | The base plus 2 grocery capabilities that apply to business buyers |
Every industry inherits _base, so a new vertical starts from a complete storefront and adds only
what makes it that industry. An industry with no vertical.yaml yet resolves to the base alone —
cts coverage labels those rows base only so they cannot be mistaken for industry content.
How it fits together
One hand-authored source, compiled into per-framework output:
catalog/verticals/<industry>/capabilities/*.yaml authored: framework-agnostic capability YAML
│ ctsx build
▼
rendered/<industry>/<model>/<framework>-<placement>/ committed: the exact bytes cts copies
registry.json committed: the single discovery root
│ cts apply
▼
your project's openspec/specs/ or specs/NNN-<slug>/Authoring finished spec files per framework would mean up to industries × models × frameworks
copies of one requirement. Authoring one neutral source and committing the rendered output gets
both: no duplication, and a PR diff that shows the exact bytes a developer will receive.
Commands
cts detect what framework is here, and is it complete
cts list [--industry <i>] what content exists
cts questions [--answers '<json>'] [--surface agent|cli]
drive the intake flow (data-driven)
cts plan --industry <i> --model <m> [...] preview what would be written, and its hash
cts apply [--plan <f>] [...] write it, atomically, and leave a receipt
cts status | cts remove inspect or undo exactly what we wrote
cts why --industry <i> --model <m> explain why a combination resolves as it doesOptions: --framework openspec|speckit · --placement specs|change (OpenSpec only) · --scope all|mvp ·
--cwd <dir> · --force · --dry-run · --json · --no-overlay ·
--skills all|none|<name,name>
cts init offers a third answer to "how much": picking the industry's specs one by one. The
general storefront always comes with them, so the list is the industry delta only — 19 at its
longest today. It is a terminal affordance; an assistant driving cts questions keeps the
all/must-haves choice, because AskUserQuestion renders six options at most.
Exit codes: 0 ok · 2 bad args · 4 no or incomplete framework · 5 unsupported combination ·
6 blocked by conflicts · 7 renderer not implemented.
What lands where
| Framework | Placement | Destination |
| :--- | :--- | :--- |
| OpenSpec | specs (default) | openspec/specs/<capability>/spec.md — a browsable baseline catalog |
| OpenSpec | change | openspec/changes/add-<i>-<m>-<epic>/{proposal,tasks}.md + delta specs |
| Spec Kit | — | specs/<NNN>-<slug>/spec.md, one feature per capability, rebased onto max(NNN)+n |
Four placement notes that are easy to get wrong, and that this tool gets right:
- Spec Kit numbering comes from disk, not from git.
create-new-feature.shscansspecs/*for^[0-9]{3,}-and takes max+1, so seeded feature directories are safe and the next/speckit.specifycontinues the sequence. - A paired model puts the side in the slug, not in a directory.
specs/001-seller-portal-cart-page/, notspecs/seller-portal/001-cart-page/— Spec Kit's numbering scan and our apply-time rebase both look exactly one level underspecs/, so a nested shop would be invisible to both. - Nothing dated or numbered goes in the body.
**Feature Branch**,**Created**and**Input**are omitted: the first embeds a number the rebase reassigns, the second would churn the committed goldens on every build, and the third has no source. No Spec Kit script reads them. - We never write a
plan.mdor a pre-tickedchecklists/file.setup-plan.shskips its template copy whenplan.mdalready exists, which would strip the overlay's Platform Skills Resolution table out of the feature. A pre-ticked checklist would forge Spec Kit's own content-quality attestations.
Working the backlog on its own
A spec set is a backlog, and OpenSpec has no queue: it knows changes in flight and specs that are
done, and nothing that says which of fifty to do next. The last two questions — or
--skills — copy the skills that add one.
| Skill | Wires rules | Does |
| :--- | :--- | :--- |
| commercetools-spec-plan | | Derives openspec/PLAN.md — the rows, what each depends on, the gate |
| commercetools-spec-run | yes | Works one row at a time: propose → apply → sync → gate → archive → tick |
| commercetools-spec-templates | | Adds another industry's specs later, or takes these back out |
They are offered individually, because wanting a build order is not the same as wanting something
to work through it unattended. Picking commercetools-spec-run adds commercetools-spec-plan
too and says so — it reads a plan that nothing else would write. The one rule in
openspec/config.yaml follows the selection: it is written only when a skill that reads it was
chosen.
The list comes from the skills' own frontmatter (metadata.ships), so publishing a fourth adds
it to the question with no code change.
The split is forced by what OpenSpec can hold. A workflow schema is scoped to a single change —
its requires is a DAG among artifacts, and the format has no second change, no conditionals
and no shell execution — so nothing in the framework can express a queue. Meanwhile the
operations: key that the scaffolded config documents is not read by any version: OpenSpec 1.6
parses schema, context, rules, references and store, and drops the rest. So the loop
lives in the skills, and the one rule that has to reach an agent which never loads a skill goes
into rules.specs and rules.design — the two artifact ids the commercetools SDD overlay leaves
free. ADR 9 has the reasoning and what was
rejected.
openspec/PLAN.md is the only durable state, so a run that stops is resumed by invoking the skill
again. The skills are receipt-tracked like any other file and the config region by its own hash,
so cts status and cts remove cover both.
Nothing is written until you have seen it
cts plan produces a file list, an action per path, and a plan_hash. cts apply --plan <file>
recomputes that hash and refuses if the project moved on. Files are staged in a temp directory,
fsync'd, then atomically renamed, and the receipt is written last — so a crash leaves either the
old state or a complete new one. A path we did not write (foreign), or one we wrote and you then
edited (ours-edited), blocks the run until you pass --force.
Coverage, and honesty about gaps
industry × business model resolves three ways. A model the vertical explicitly excludes is a
hard refusal, not a guess. A model reached only through inheritance is reported as derived,
and whatever that model structurally needs but no capability covers is rendered as an open
question — never as invented content.
$ cts why --industry grocery --model B2B2C
grocery|B2B2C: derived match
3 capabilit(ies): 3 native to B2B2C, 0 inherited
4 model gap(s) with no published content: seller-onboarding, seller-scoped-assortment, …
These are rendered as open questions, never as invented content.Authoring a vertical
bin/ctsx.mjs is the authoring tool; it is not shipped to developers.
node bin/ctsx.mjs build # regenerate registry.json, dist/, rendered/, collector/forms/
node bin/ctsx.mjs lint --strict # 0 ok · 1 errors · 3 golden drift
node bin/ctsx.mjs coverage # the industry × model × side matrix
node bin/ctsx.mjs collect:render # regenerate the collector form (build does this too)
npm test # offline; includes the generated Apps Script, run against a fake Forms APIThe full procedure is .claude/skills/commercetools-vertical-authoring/SKILL.md, including how to
turn a source PDF into capability YAML without ever committing the PDF. The step before it —
getting a ranked, de-identified candidate list out of a folder of RFPs, demo agendas and briefing
decks — is .claude/skills/commercetools-spec-harvesting/SKILL.md.
Support
There is none, and that is deliberate — see SUPPORT.md. No SLA, no security patching, not covered by commercetools Support, issues answered best-effort, and it may be archived without notice. If you need it changed for a project, fork it: that is the intended use.
Licence
MIT throughout — see LICENSE. That covers the engine and the rendered specs the npm package
ships, and every rendered spec carries an SPDX-License-Identifier comment of its own so the
licence travels with the file once it is copied into your project. If the catalog is ever opened up
under a separate content licence, that is a decision to take then, with a licence file to match;
there is no second licence today.
