Amend a spec¶
Fix a spec's wording, sharpen an example, or record an edge case you've
found — without breaking anyone's reference to it: the citation stays the
same, so every ticket, code comment and other spec that points at it keeps
pointing at the right rule. You'll need an agent that can run commands — a
coding agent such as Claude Code, Codex or Cursor, with the hadron CLI
(v0.17.0 or later) installed and signed in — or a terminal of your own.
A chat-only app connected to Hadron can read specs and draft the change with you, but can't save it through the spec tooling. The one decision that stays yours is the first one below.
If you are creating a spec, or curating the corpus as a whole, see Maintain product specs. If you only need to find and read one, see Read and cite product specs.
First: amend or supersede?¶
A citation is a permanent handle. Someone has written api:010:04 in a code
comment, a ticket, or another spec, and that reference has to keep meaning what
it meant when they wrote it.
So the test is not "how big is this change?" but does it change what the spec guarantees? Every spec's rubric has a Durable vs tunable section, and it is exactly the answer:
| The change touches… | Do this |
|---|---|
| Wording, a clearer example, a newly-discovered edge case, a value the spec itself labels tunable | Amend in place |
| The durable half — what the spec promises, an enum's members, who the rule applies to, a guarantee being withdrawn | Supersede: mint a new citation (the next number, or a path you name with --to) |
If the spec has no Durable vs tunable section, that is the real bug. Add one
while you are in there. No check will ask for it — spec lint doesn't look for
that section, on any spec — so this is on you.
Superseding is covered in Replace a spec without renumbering. The rest of this page is the amend path.
Ask your agent to amend it¶
Say which spec, what to change, and that you want to see the change before it is saved:
In acme.com:specs, spec api:010:04's second example says "one week" where
the rule itself says "7 days". Make the example match the rule — it's a
wording fix, so amend it, don't supersede it. Show me exactly what will
change, before and after, and wait for my OK before you save.
What you get back: the spec's current text, the proposed text, and which parts change — the body, the abstract, or both. "Wait for my OK" matters: a coding agent working on its own can otherwise show you the change and save it in the same step. Ask for the before and after explicitly: the tool's own preview only lists which fields would change, so a good agent pulls the text and shows you the difference itself.
What to check before you approve:
- It's still an amendment. If the change alters what the spec promises, stop — that's a supersede.
- The abstract still describes the body. If the body changed, the abstract usually should too; they are one unit.
- Nothing else moved. The preview should report only the fields you meant to change.
When you approve, the agent saves the change and re-reads the spec to confirm it. The tooling doesn't warn you if someone else changed the spec after the preview, and saving replaces what's there — so if anyone else might be editing it, ask the agent to re-read it just before saving, compare it with the version it previewed, and stop and show you a fresh before-and-after if anything differs.
What you need for this path: write access to the specs memory, and the agent set up as described above. To do it by hand instead, read on.
Why not just edit it in the portal?¶
You can open a spec node in the portal and edit it, and for a typo that is fine. But the portal edits a node; it knows nothing about specs. Three things it will not do for you:
- Keep the abstract honest. The portal has an abstract field, so you can
change it — but nothing connects it to the body you just rewrote. Change the
body, leave the abstract, and the spec quietly drifts: its abstract was
authored against different content, and the corpus checks below start
reporting it as
stale-abstract. That matters more than it sounds, because the abstract is the vector-search retrieval surface — a stale one means a reader finds a spec that no longer says what they just read. - Check the rubric. On numbered rules and flows,
spec lintenforces the mandatory "What invalidates this spec" statement, the abstract's presence, anddata.version; on every spec it checks the abstract's length. A spec named by path gets none of the rubric checks, so read its sections yourself. A portal edit runs none of it. - Stop you renumbering. Nothing in a node editor knows that a citation is permanent, or that the change you are making needed a new number.
hadron spec edit exists because the body and the abstract are one logical
unit. It opens both.
Amend it with the hadron CLI¶
You'll need the hadron CLI, v0.17.0 or later for specs named by path and
for supersede --to, installed and signed in — see
Install the hadron CLI — and write access to the
specs memory. The examples use acme.com:specs and the rule api:010:04.
Read the current state first — spec get prints the abstract, the body, the
edges, and a lint summary in one go:
Then edit:
This opens $EDITOR (then $VISUAL, else vi) pre-loaded with the current
abstract and body in a single buffer, divided by sentinel comment lines:
<!-- === ABSTRACT === one paragraph; the spec's RAG retrieval surface. Edit below this line. -->
Re-engage users who signed up but never finished setup, 7 days in.
<!-- === BODY === the spec markdown. Edit below this line. -->
# api:010:04 — W4 — 7-day check-in
...
Pre-loading is the point: you change the lines you mean to, instead of
reconstructing the node in a temp file and risking a transcription slip on a
full replace. Keep the body divider — it is how the buffer is split back
apart, and spec edit refuses to write without it rather than guessing. A
field you do not touch is not rewritten; an unchanged buffer writes nothing.
A worked example¶
Say api:010:04 sets a 7-day check-in, its Durable vs tunable section lists
the window as tunable, and product wants 14 days. Tunable → amend.
In the buffer, the body changes:
## Rule & examples
-A user who has not completed setup is sent a check-in **7 days** after signup.
+A user who has not completed setup is sent a check-in **14 days** after signup.
and — in the same pass — so does the abstract:
-Re-engage users who signed up but never finished setup, 7 days in.
+Re-engage users who signed up but never finished setup, 14 days in.
Updating both together is the habit worth building. It is not bookkeeping: the abstract is what semantic search matches on, so an abstract still promising "7 days" will keep answering questions about a rule that now says 14.
Save, and confirm:
Editing without an editor¶
For scripts, CI, or an agent, replace the fields non-interactively — in one call, so the body and abstract land together:
echo "Re-engage users who never finished setup, 14 days in." \
| hadron spec edit api:010:04 -m acme.com:specs \
--content-file rule.md \
--abstract -
That is a single write: spec edit sends both fields in one updateNode
mutation, so the spec is never briefly left with a new body and its old
abstract. Two sequential calls would have exactly that window — and if the
second one fails, the drift is permanent.
--content / --abstract take the value inline; --content-file /
--abstract-file read from a path; - reads stdin (only one field can use it,
which is why the example pipes the shorter one). A field whose flag you omit is
preserved untouched, and a field that did not actually change is not rewritten.
--dry-run previews without writing.
To change the description shown by spec list and search, pass
--description "New summary" or --description-file summary.md. It uses the
same guarded write and can be combined with body and abstract changes. The
description is not in the default editor buffer. A replacement abstract may
contain at most 2000 UTF-16 code units, counting whitespace and newlines;
--dry-run rejects an over-limit abstract before showing a proposal.
Save a proposal approved earlier¶
If someone reviews the proposed change before you save it, preview the exact files that you plan to use and keep the three values reported by the preview:
hadron spec edit api:010:04 -m acme.com:specs \
--content-file rule.md --abstract-file abstract.md \
--dry-run --json
Review the diff in changes, then save with the same files and the preview's
nodeId, revision, and proposalHash. Replace the uppercase placeholders
with those three values from the same preview:
hadron spec edit api:010:04 -m acme.com:specs \
--content-file rule.md --abstract-file abstract.md \
--expected-node-id NODE_ID_FROM_PREVIEW \
--expected-revision REVISION_FROM_PREVIEW \
--expected-proposal-hash PROPOSAL_HASH_FROM_PREVIEW
Every save checks the node ID and revision, including an ordinary editor
save. The three --expected-* flags also ensure that a later save matches
the proposal that was reviewed. If the spec or files changed, the command
exits 5 without writing. Re-read the spec, reconcile the change, and get a
new preview approved before saving.
Changing only the body leaves the abstract stale
Passing --content-file alone is legitimate — for a typo, or a change that
does not alter what the spec says. But if the meaning moved, refresh the
abstract in the same call. Otherwise you have created the drift the next
section is about.
Find specs whose abstract has drifted¶
Two tools, for two different questions.
Auditing the corpus — every drifted spec in the memory:
Checking the specs your code points at — spec citations scans a source
tree for the // Spec: <citation> pointers the authoring workflow asks for,
and normally reports only broken or superseded ones. --stale-abstracts adds
drift on the specs it found:
That is the narrower, more useful check when you are working in a codebase: it flags drift in exactly the specs this code depends on, rather than the whole corpus.
Read this signal narrowly
stale-abstract is a hash comparison — the abstract's origin hash
against the current content hash. It fires on any body edit made since
the abstract was last written, including one that changed nothing the
abstract says. It means "the body moved under this abstract", not "this
abstract is wrong".
On a corpus that has never been audited it will flag a large fraction of
the specs, and most of those abstracts are fine. Treat it as a worklist to
skim, not a defect count — and reach for it when you are already editing a
spec, rather than as a cleanup project. spec citations keeps it behind an
opt-in flag for exactly this reason.
Related¶
- Maintain product specs — scaffolding a corpus, sharing provisions through contracts, superseding, and the lint gates.
- Read and cite product specs — the read side.
- What makes a node findable — why the abstract carries so much weight, and what actually governs whether a spec is found.
- hadron CLI reference → Product specs — the full command surface.