Amend a spec¶
A spec is rarely right the first time. Thresholds move, an edge case turns up, a sentence turns out to be ambiguous. This guide is about changing a spec that already exists — and about the one decision you have to make first, because getting it wrong breaks citations.
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. And if you own the rules but not the tooling, let a coding agent run this page for you — see Manage product specs with a coding agent.
Prerequisites¶
- The hadron CLI installed and signed in — see
Install the hadron CLI. Check with
hadron auth status. - Write access to the specs memory. The examples use
acme.com:specsand the ruleapi:010:04.
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 number |
If the spec has no Durable vs tunable section, that is the real bug. Add one
while you are in there — spec lint will keep asking for it.
Superseding is covered in Replace a spec without renumbering. The rest of this page is the amend path.
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.
spec lintenforces the mandatory "What invalidates this spec" statement, the abstract's presence and length, the table-of-contents and inheritance edges, anddata.version. 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¶
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.
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.