alterspec client
@alterset/alterspec-client brings the work of an alterspec product
spec into the repositories that build the product. With it, a developer working in Claude Code takes a task from the
alterspec dispatcher, reads only the part of the spec that task needs, builds it, and reports progress and completion
back. The client never edits the spec.
It also carries the format of the export's work.json (@alterset/alterspec-client/work) for the dispatcher.
New to the dispatcher? Start with the step-by-step developer guide (developer-guide.md in
this package): the SSH key, setting a repository up, signing in, and a task from start to finish. This README is the
reference.
What you need
- Node.js 20 or later, git, and Claude Code.
- The dispatcher's URL from your admin.
- A clone of the export repository: the signed-off product spec your team builds from. The client pulls it. Never edit it by hand.
- An
ssh-ed25519key: in ssh-agent, or at~/.ssh/id_ed25519. Signing in to the dispatcher uses it. There is no password or token to manage.
Words
- Task: one signed-off change on one track, written
CHG-012/BE. - Track: a stream of build work. It is not a channel.
BEis the backend (every API channel included). Every other track is one design, named by the code of the channel that owns it (BO,MOB). A channel that shares another's design is built in that channel's track. - Checklist: a task's items, such as acceptance criteria (
CAP-CAT-001-AC-03), screen actions (SCR-PRC-01.A02) and experience states (UX-SCR-PRC-01-WEB#error). Progress counts the items your commits cover. - Then and Shown: a criterion's
Thenis what the system guarantees (the backend's), itsShownwhat the screen shows (a UI track's). A task's checklist holds a criterion for its own part only; one withoutShownis all the backend's. - Feedback: when a task's checklist is covered, the agent tells the dispatcher what in the spec was unclear, what it had to open beyond the brief and what it decided itself, naming IDs, so the product side can fix the spec.
- Question: when the spec doesn't decide something, the agent asks with
/alterspec-spec-ask. The answer is from the spec (citing IDs the export holds), an assumption to work on until the product side confirms or corrects it, or open (that item waits). The dispatcher keeps the assumptions and the open ones (an answer from the spec is not sent); a product person answers there and marks each reconciled with the spec once the spec says it.
Set up a repository
In the root of the repository that builds one or more tracks:
npx @alterset/alterspec-client init --dispatcher https://<dispatcher> --export ../<export-clone> --track BE
| Option | Meaning |
|---|---|
--dispatcher <url> | The dispatcher's URL. |
--export <path> | Your clone of the export repository, relative to this repository or absolute. |
--track <code...> | The tracks this repository builds (BE, BO, …). Several are allowed: --track BE BO. |
--worktrees <path> | Where task worktrees go. Default: ../<repo>-tasks. |
--key <key> | The public key to sign with, as a .pub path or the key line. Default: the first ssh-ed25519 key in ssh-agent, then ~/.ssh/id_ed25519.pub. |
--branch <name> | The track's main branch. Default: the remote's HEAD, else main. |
--force | Replace skills you modified. |
init writes:
.alterspec-client.json: the configuration above..mcp.json: thedispatcherMCP server (npx @alterset/alterspec-client mcp)..claude/settings.json: a hook that reports progress after eachgit commit..claude/settings.local.json: your approval of thedispatcherserver ("enabledMcpjsonServers": ["dispatcher"]), merged with whatever else the file holds. Claude Code loads a server from a project's.mcp.jsononly once it is approved, and honours this approval only while git doesn't track the file, soinitadds the file to.gitignorewhen git doesn't ignore it yet..claude/skills/alterspec-*: the skills, which are thin wrappers..alterspec-client/prompts/: the full instructions behind the skills.
It is safe to run again. Running it again also updates the client: it adds the skills of the new version, removes
the ones it dropped, and keeps any skill you edited unless you pass --force. Commit everything it writes except
.claude/settings.local.json, so task worktrees have it too. worktree writes the approval into each new task
worktree itself. If you keep the setup out of git instead, as top-level symlinks into another repository (.claude,
.mcp.json, .alterspec-client), git-ignored, worktree links each of them into the new worktree too. If such a link is committed with a relative target, so that
it would point nowhere from a worktree one folder deeper, worktree links the folder it misses in the worktrees'
folder to the one beside your checkout (../<repo>-tasks/ops → ../ops), which all task worktrees share; git sees
no change.
When .claude is a symlink to another repository, git can't tell whether the file is ignored there: init says so,
and you add .claude/settings.local.json to that repository's .gitignore.
At the end it prints your public key and fingerprint. Send both to the dispatcher's admin. Nothing works until the admin adds your key. To print them again:
npx @alterset/alterspec-client key
The dispatcher's tools appear in the next Claude Code session started in the repository.
init doesn't touch your CLAUDE.md. So that Claude knows where the export is and how to treat it outside the
skills too, copy the block in agent-instructions.md (in this package) into your agent's
instructions, wherever you keep them.
Working day
You talk to Claude Code; the skills call the dispatcher and the CLI for you.
-
Take work.
/alterspec-task-next, or just "give me the next task". Name a track or ask for several at once:/alterspec-task-next BE 2. It only assigns: with one task it continues with the brief, with several it lists them; type/alterspec-task-context <task>wherever you work on each. It creates no branch or worktree; where you work is yours to choose (worktree <task>makes one when you want it). -
Build from the brief.
/alterspec-task-context [task]reads the task's brief from the export:- the checklist, with the items the dispatcher already holds covered and what changed in each;
- why the change was made;
- the before and after of each object;
- for
BE: the changed acceptance criteria on the checklist and the operations, whole; the rules and events the checklist's criteria or the change's text name, whole; the others the capabilities name, as paths; - for
BE: the entities the capabilities work with, as paths; - the decisions and glossary terms the change's text names, whole; the other decisions its objects name, as paths;
- for a UI track: the screens, their experience contracts, the mockup pages and the design system.
You build it in your own technology. When the spec is silent or disagrees with the code, Claude asks you; it does not invent business rules. A mockup page shows the agreed look and behaviour. It is not code to copy.
-
Commit with trailers. Every commit of the task ends with:
Alterspec-Task: CHG-012/BE Alterspec-Covers: CAP-CAT-001-AC-03, CAP-CAT-002Alterspec-Taskgoes on every commit.Alterspec-Coversgoes only on the commit that makes an item's tests pass, naming items exactly as the checklist writes them. Name each item once; a commit that covers nothing has noAlterspec-Coversline. After each commit the hook reports the covered items to the dispatcher. It works in the background and never blocks you. It skips the report when nothing changed or the last report is under 15 minutes old. -
Merge, then report done. Once the task's work is on the track's main branch on the remote, type
/alterspec-task-done CHG-012/BE. It sends the last progress, finds the commit that brought the task in, and reports it done. It refuses while the work is not merged.
Other skills:
| Skill | What it does |
|---|---|
/alterspec-task-status [track] | Your tasks with progress and ETA, what is available, what is blocked and by what, and the open changes per level. |
/alterspec-task-eta <task> <YYYY-MM-DD|clear> <reason> | Set your own date for a task, or clear it so the dispatcher's computed ETA applies again. |
/alterspec-task-release <task> [reason] | Give a task back. Your code and branches stay as they are. |
/alterspec-spec-ask <question> | Answer a question about the product from the export, citing IDs. Read-only. |
You only ever type task-done, task-eta and task-release yourself, because each is your word. Claude starts
the others from plain language as well.
Commands
The skills run these; you rarely need them yourself. Run them in the repository or in a task worktree. In a task worktree, the configuration is read from the main checkout.
| Command | What it does |
|---|---|
init [options] | Set the repository up (see above). |
key | Print the public key and fingerprint you sign with. |
open dispatcher | Sign in and open the dispatcher's pages in the browser. The link works once, for a minute. |
context <task> [--json] [--stats] | Print the task's brief from the export, starting with its questions and its checklist. |
worktree <task> [--here] | Create the branch task/<chg>-<track> from origin/<main> in its own worktree, with the checkout's git-ignored top-level symlinks linked in and its committed relative ones made to resolve. --here uses this checkout instead. |
progress [task] [--json] | Report the items your commits cover. Default: the task of the current branch. |
lookup [ref...] [--question <text>] [--task <task>] [--item <item>] | Where in the export a question is answered: what to read first, what to open before calling the export silent, the neighbours. --unread <path...> says what of that was not opened. |
cites <ref...> | Check that every ID or path is in the export; what an answer "from the spec" must pass. |
questions [task] [--about <ID...>] [--open] [--json] | Print a task's questions with the product side's answers, the ones naming some IDs, or without either your own. |
merged <task> | Print the commit on origin/<main> that brought the task in. |
export-head [--latest <sha>] | Pull the export clone and print the commit to send to the dispatcher. |
mcp | The MCP server Claude Code starts from .mcp.json. It forwards to the dispatcher. |
Each runs as npx @alterset/alterspec-client <command>.
Troubleshooting
- "The dispatcher refused your SSH key": the admin hasn't added your key yet, or you are signing with a
different key. Send the two lines it prints (or the output of
key) to the admin. - "No SSH key to sign with": add an
ssh-ed25519key to ssh-agent, create~/.ssh/id_ed25519, or setkeyin.alterspec-client.json. - "ssh-agent doesn't hold the key": run
ssh-addfor the configured key. - No dispatcher tools in Claude Code: the
dispatcherserver isn't approved or loaded. Runinitagain (it approves it in.claude/settings.local.json), make sure git doesn't track that file (git rm --cachedit ifinitsays it is committed), then start a new session. In an interactiveclaudeterminal,/mcpshows the server's state and approves it too. - "No .alterspec-client.json here": run
initin this repository, or commit the configuration so worktrees find it. - "The export is N commits behind": the clone couldn't catch up. The task's change is already in it, so work goes on. Check the clone's remote if it keeps happening.
- An item reported as unknown: an
Alterspec-Coversline named something the checklist lacks. A pushed commit can't be corrected; name the right item on a later commit. task-donesays the work isn't merged: merge the task's branch into the main branch, or push the commits that carry itsAlterspec-Tasktrailer, then run it again.
For the dispatcher: the work.json format
import { parseWork, WorkSchema, AnyWorkSchema, WorkError } from '@alterset/alterspec-client/work';
parseWork(text) reads a work.json in format 1 or 2 and throws WorkError when it isn't one. The JSON Schema is
at @alterset/alterspec-client/work.schema.json.
License
MIT, Alterset d.o.o.