Releases: MatanKoby/specflow
Release list
v0.1.10
Action required
Run specflow upgrade in every repo that has specflow installed, and update the binary. This
release changes the CLI and four managed files: the claim-batch, finish-batch, and
prune-ledgers procedures, and their skill triggers.
curl -fsSL https://raw.githubusercontent.com/MatanKoby/specflow/main/install.sh | sh
specflow upgrade
specflow status # `stale` should read `none` afterwards
One thing upgrade deliberately will not do for you: add the new pointer markers to your
BUILD_QUEUE.md. Your queue is a seed file that no verb rewrites, so that edit is yours, once. See
Wrap your pick-order pointer, below. A queue with no markers keeps working and is simply not
measured separately.
Four batches, one subject: the queue preamble
| Batch | What |
|---|---|
| QD | the preamble warning says what is wrong, not just how long: an address, a staleness split, a stale claimable line, a near-miss heading |
| PD | prune-ledgers section 3 greps before it judges, and asks once instead of once per paragraph |
| FL | a finish leaves the queue smaller: the queue edit is subtractive, and the pick-order pointer is a bounded, replace-only block |
| OD | a batch can wait on an outcome, not just on a finish: the new Blocked on: line |
All four came out of one downstream install whose BUILD_QUEUE.md held 446 lines of prose above
its first batch, in a file whose own header says it lists un-done batches only. The 0.1.8 cap
reported that as a number and stopped there. QD makes the report diagnostic, PD makes the cleanup
affordable, and FL and OD close the two inlets that filled it: about 9 lines per finish, which puts
a freshly pruned 45-line preamble back over its cap inside five batches.
The preamble warning is now a diagnosis
specflow next used to print BUILD_QUEUE.md preamble is 467 lines, over its 45-line cap, and the
user who saw it read it as a bug in specflow. It was not, but the message gave a number with no
anchor. Four read-only diagnostics, all in Weigh, so verify reports them too.
- The count carries an address. Preamble lines are attributed to the nearest heading above them:
467 lines above the first "## Batch" heading (446 of them under "## Un-done batches"). - The staleness split is the signal the length never was. The distinct batch ids a preamble
names, split by whether each is still in the queue or already archived inBUILD_QUEUE_DONE.md.
The install above named 43 archived against 2 live. That fires on a short rotten preamble and
stays quiet on a long current one, which is exactly what a line count cannot do. The floor is 5
archived, and archived must outnumber live, so an honest pointer naming a shipped dependency stays
silent. - A stale
claimableline is named with its line number. It is the one preamble defect that
misdirects rather than merely reading old: the field case saidClaimable now: 50while the queue
held 49, 51 and 58 and Batch 50 had shipped weeks earlier. The check reads the batch named in the
sentence the word sits in, so "Claimable: nothing. QD shipped" claims nothing and reports one. - A near-miss heading is named, and this one guards the count itself. A heading that misses the
declared shape (an h3, a lower-case "batch", a colon after it) is not a batch anywhere: it inflates
the preamble count and vanishes fromnextat the same time.
Over-counting is noisy but visible, so the boundary was deliberately not loosened to accept a
near-miss heading: under-counting is silent.
prune-ledgers section 3 greps before it judges
Section 3 asked the agent to sort every preamble paragraph into keep / relocate / delete and put the
three piles to the user. At the 59 lines this repo dogfooded it on, that is right. At 446 lines and
73 paragraphs it produced two findings.
The relocate pile came back empty. Every durable fact in that preamble was already carried by
BUILD_QUEUE_DONE.md or spec/, many of them citing their own destination in their own text. The
expensive half of the section was answering a question a grep had already settled 73 times over.
And 73 asks is an audit that gets abandoned.
So 3a greps first: take a distinctive phrase from each paragraph and look for a home in
specflow/history/, CLAIMS.md, and spec/. A paragraph with a citation is a delete, mechanically,
with the file and line as the evidence, and no ask. Only uncited paragraphs reach the three piles,
and the stop-and-ask is put once, as piles with counts. 3b prescribes the report: one row
per paragraph (line range, opening words, pile, evidence or destination, reason), the content that
lives nowhere else, and the projected line count. Relocation is explicitly verbatim, since a
paragraph reworded on its way into spec/ is no longer the thing that was reviewed.
The section also states the ordering QD's four signals imply: length is the weakest of them. A
40-line preamble naming a dozen shipped batches is rotten; a 60-line one that is all live pick-order
may be fine.
A finish leaves the queue smaller
The finish-time queue edit is subtractive. finish-batch.md step 4 now says so outright: delete
the batch's section, rewrite the pick-order pointer, append nothing. The batch's narrative already
has a home in specflow/history/BUILD_QUEUE_DONE.md and specflow finish already files it there,
so a status paragraph added to the top of the queue is a second copy of prose that already exists,
sitting in the file every future agent reads, that no retention rule ever reaches.
The pick-order pointer is bounded. It is the one region of the preamble a finish is expected
to rewrite, which is exactly why it is the region that grows a line per batch when a finish appends
instead. It is now a marker-delimited block capped at 20 lines of content, measured by Weigh
and reported beside the preamble count:
ledger weight: BUILD_QUEUE.md 125 lines (preamble 34/45, pointer 8/20, ...) · CLAIMS.md 142 lines (5 completed)
Over the cap, the warning names the rule rather than just the number: it is replace-only, rewrite
it, do not append a line per batch. 20 is set where an honest pointer (what is claimable, what it
waits on, what is not ready) stays silent, while one status paragraph per batch clears it
immediately.
specflow finish now prints the ledger weight on its way out. This is the last moment the agent
can still fix a preamble that grew: the meta: complete commit is the next thing it does, and after
that the growth is someone else's to find.
Wrap your pick-order pointer
Once, by hand, in each repo's BUILD_QUEUE.md:
<!-- specflow:pointer:start - replace-only, 20 lines max: rewrite this block, never append to it. -->
> **Pick-order pointer.** ... your existing pointer, unchanged ...
<!-- specflow:pointer:end -->
The markers use the same specflow:<tag>:start shape as the render regions and deliberately not the
specflow:start token the managed-file check matches, so wrapping your pointer does not turn
your queue into a managed region: upgrade and verify go on leaving it alone. A start marker with
no end is reported rather than guessed at. Absent markers mean no separate measurement, not an
error.
Blocked on: gates a batch on an outcome
Depends on: takes batch ids and means completed, so there was no way to say a batch waits on
what an earlier batch found. Downstream, two batches declared they depended on "Batch 46 having
come back answers"; Batch 46 shipped, reported package presence only, and specflow next went on
offering both as claimable. The only thing standing between an agent and a wrong claim was a prose
caveat in the queue preamble: the format could not express the fact, so it got parked where nothing
reads it and nothing prunes it.
A batch section now takes an optional free-text line:
## Batch 47 - the thing that waits
**Blocked on:** Batch 46 having come back with the package answer, which it did not.
- It is a hard gate.
nextnever offers the batch, andclaimrefuses it and writes nothing. - The reason printed is the author's own text, not a gloss over it, folded to the terminal
width. Every other reasonnextprints is a string the CLI wrote and kept short; this one is
whatever you typed. - It coexists with a tag, whose canned gloss the stated reason replaces:
[NOT READY] blocked on Batch 46 having come back with the package answer. The tag is the
category, the line is the specific why, and printing both would say the same thing twice. - Deleting the line clears it, and that is for whoever can answer it, not for an agent on the
way past.Blocked on: noneis accepted too, so an agent that writes the word does not strand a
batch, but the procedures say delete: a leftover "none" is a line that states nothing and that
nothing ever prunes. - An empty
Blocked on:line still blocks, with "no reason given" as the reason. A gate nobody
can act on is a defect in the queue, not a claimable batch, and reading it as absent would let a
truncated line quietly re-open a batch someone meant to close.
Depends on: is untouched: a batch id still means completed, and the two gates are evaluated
separately, so a batch can be waiting on both.
Behavior changes
nextandverifyreport apointer N/20count when the queue carries the markers, and warn
past the cap or on an unclosed block.finishprints the ledger weight after it writes, which it did not before.nextapplies a new blocked-on check, prints aBlocked on:reason as its author wrote it, and
folds a long one to 72 columns.claimrefuses a blocked batch.next --jsoncarriesblockedandblockedOnper item, beside the renderedreason, and
weightcarriespointerBlock,pointerLines, andpointerLimit.templates/base/BUILD_QUEUE.mddecl...
v0.1.9
Action required
Run specflow upgrade in every repo that has specflow installed, and update the binary. This
release changes the CLI, every managed procedure file, and every agent adapter.
curl -fsSL https://raw.githubusercontent.com/MatanKoby/specflow/main/install.sh | sh
specflow upgrade
specflow status # `stale` should read `none` afterwards
If a managed file shows as drifted and you have edited it on purpose, specflow waive <file>
is new in this release: see Drift is a state you can leave, below.
Five batches
v0.1.9 is the largest line so far. Two of the five fix things a downstream install reported, one
adds a verb for repos arriving from 0.1.8, and two are a punctuation sweep with an enforcement rule
behind it.
| Batch | What |
|---|---|
| RC | drift is a state you can leave: a reconcilable sidecar, adoption on identical bytes, and specflow waive |
| MC | specflow migrate-claims, retrofitting 0.1.8's stub shape onto entries written before it |
| FS | the stub contract now says what the code already does, and finish supplies and checks the pointer |
| ED | no em dash in anything specflow ships as files |
| EM | no em dash in anything specflow writes at runtime |
Drift is a state you can leave
Two defects, both reported from an install running 0.1.8, and both about the same thing: editing a
managed file used to be a one-way door.
The sidecar held the wrong bytes. When upgrade declined to overwrite a file you had edited, it
wrote the fresh template whole to <file>.specflow-new. For an adapter that is right, because
every byte is specflow's. For a marker-delimited file such as CLAUDE.md it was a footgun: the
warning invites you to reconcile, the obvious reconciliation is mv, and mv threw away everything
outside the region. One install carried 27 managed lines and 73 lines of its own project guidance
below them. The sidecar now holds before + markers + fresh region + after, so mv is correct
everywhere, and the CLI says mv outright.
Drift was terminal. Upgrade carried the old baseline forward for a drifted file, so a user who
followed the printed advice ended up with a region matching the new template and a baseline still
holding the old hash: re-drifted on every upgrade, warned on in every verify, with no exit but
discarding the edit. Upgrade now adopts on identical bytes, which self-heals every already
reconciled install on its next run, with no verb involved.
specflow waive <file> keeps a deliberate edit.
specflow waive AGENTS.md # this edit is on purpose; stop reporting it
specflow waive --all # every currently drifted file
specflow waive --clear AGENTS.md # go back to reporting it as drift
A waiver changes no file bytes. It records the exact bytes waived (so a later edit resurfaces as
drift) and the template version it was taken against (so upgrade can tell you when specflow has
moved past your waiver rather than sitting silent forever) in a new waived map in
specflow/config.json. upgrade leaves a waived file alone and writes no sidecar, verify reports
it as a choice rather than a warning, and status counts it on its own row. Only a drifted file
can be waived: waiving a clean one would silently opt it out of every future refresh for nothing.
This is deliberately a waiver and not the specflow adopt the queue entry originally proposed.
Re-recording your edited bytes as the baseline would make the region read as clean, so the very next
upgrade would refresh it and destroy the edit being blessed.
specflow migrate-claims
Retention bounds how many entries a ledger holds. The stub cap that shipped in 0.1.8 bounds how big
one entry gets, but it only reached entries written after the upgrade, so a repo arriving at 0.1.8
still carried every legacy essay in the file that is re-read on every claim, finish, and prune.
specflow migrate-claims --dry-run # report only
specflow migrate-claims
Each over-cap entry in CLAIMS.md ## Completed and in specflow/history/CLAIMS_DONE.md is
rewritten to its metadata, a stub within the cap, and the Full narrative pointer; the narrative
relocates to specflow/history/BUILD_QUEUE_DONE.md under that batch's heading.
Three things it deliberately does not do. The body relocates whole, not just the part that did
not fit, so the archive section reads from its first sentence and the stub is a copy of its head.
An entry already within the cap is left exactly as it is, including one with no pointer, because
emitting a pointer at a section that may not exist would fabricate a reference. And ## In progress
is never touched: a live claim has no archived narrative to point at yet. If any ledger fails to
parse, nothing is written.
Run against this repo's own ledgers it took CLAIMS.md from 201 to 137 lines and CLAIMS_DONE.md
from 696 to 328, losing no prose.
The stub contract
finish has always skipped blank lines and the pointer when counting a stub against the 8-line cap,
which is why a 10-line stub file is accepted. Nothing said so, so an agent budgeting against the
bound budgeted against the wrong number and the natural response to a rejection was to delete prose
that was never costing anything. finish-batch.md step 3, templates/base/CLAIMS.md, and
specflow finish --help now all say the cap counts prose lines.
The Full narrative pointer is now checked rather than merely skipped. finish supplies it
when your stub omits one (gated on --done-file, since a pointer at a section that was never filed
is worse than no pointer) and refuses one that names a different batch, which is what a stub
copy-pasted from the entry above it looks like. That failure is silent otherwise: it sends the next
reader to someone else's narrative with nothing visibly wrong.
No em dashes
specflow's shipped text no longer contains an em dash (or an en dash, or a double hyphen in prose).
This matters because a downstream sweep is drift: upgrade would stop refreshing the swept files,
and the next clean upgrade would put the dashes back. Fixing it upstream is the only version that
holds.
Batch ED swept the files: 214 em dashes and 2 en dashes across templates/** and
specflow/procedures/*.md. Punctuation only, nothing reworded. A fresh specflow init now writes
an install with no em dash in it.
Batch EM swept what the CLI writes. specflow claim used to put ### Batch N — title into
your CLAIMS.md and finish put ## Batch N — title into your BUILD_QUEUE_DONE.md, so a repo
that had just been swept clean got a fresh em dash back on its next verb. Both now write a plain
hyphen, matching the entry format AGENTS.md and the CLAIMS.md template have always documented.
Console output, --help text, and every error and warning message went the same way.
Your existing ledgers keep working. The parsers still accept the em dash, and always will:
finish, next, and migrate-claims locate entries by heading, and every install that ran 0.1.9
or earlier has ledgers full of em-dash headings. Nothing needs rewriting, and mixed ledgers are
fine. Three tests pin this from both sides.
Behavior changes
- New:
specflow waive <file>... [--all] [--clear]. - New:
specflow migrate-claims [--dry-run]. upgradewrites a reconcilable sidecar for marker-managed files, and adopts a region whose bytes
already match the template instead of reporting it as drift forever.upgrade,verify, andstatusall report waived files distinctly from drifted ones.finishsupplies a missingFull narrativepointer and refuses one naming another batch.claimandfinishwrite hyphen headings into downstream ledgers; the parsers still accept the
em dash.
Install
curl -fsSL https://raw.githubusercontent.com/MatanKoby/specflow/main/install.sh | sh
# or
go install github.com/MatanKoby/specflow/cmd/specflow@v0.1.9
Full diff: v0.1.8...v0.1.9
v0.1.8
Action required
Run specflow upgrade in every repo that has specflow installed, and update the binary. This
release changes both the procedures and the CLI: specflow finish takes a new flag, and next /
verify print a new line.
curl -fsSL https://raw.githubusercontent.com/MatanKoby/specflow/main/install.sh | sh
specflow upgrade
specflow status # `stale` should read `none` afterwards
Why
Pruning bounded the ledgers by count — CLAIMS.md to its 5 newest completed entries,
BUILD_QUEUE.md to zero completed batches. Both rules work. Neither bounds the size of a single
entry, or the prose that is not an entry at all. An install running at its prescribed retention of 5
still carried a 27 KB CLAIMS.md, and both ledgers are re-read on every claim, finish, and prune.
What it changes on disk
Four managed files are refreshed. Nothing you own is touched: spec/, BUILD_QUEUE.md, CLAIMS.md,
and your own text outside specflow's markers are unaffected, and upgrade still never commits.
| Path | Change |
|---|---|
specflow/procedures/finish-batch.md |
steps 3 and 4 rewritten: stub in CLAIMS.md, narrative in the archive |
specflow/procedures/prune-ledgers.md |
new section 3: audit the BUILD_QUEUE.md preamble |
.claude/skills/finish-batch/SKILL.md |
trigger text follows the new split |
.claude/skills/prune-ledgers/SKILL.md |
trigger text names the preamble audit and the weight warning |
A managed file you have edited yourself is still left alone, with its new version written to
<file>.specflow-new.
The narrative is written once
Finishing a batch used to ask for prose about that batch twice: the "What shipped" summary in
CLAIMS.md and the paragraph in specflow/history/BUILD_QUEUE_DONE.md. They were authored
independently, and neither was a superset of the other — so the batch's story ended up split across a
hot file and an archive, with the hot copy carrying most of it.
Now CLAIMS.md keeps a stub: the metadata fields, at most 8 lines of "What shipped", and a
pointer. The full narrative goes to BUILD_QUEUE_DONE.md, which nothing reads on the hot path.
A resuming agent gets enough to know whether it needs to read further; the unabridged record is one
file away.
specflow finish <N> --commit <sha> --stub-file <path> --done-file <path>
--stub-file is the new name for --summary-file, which still works. An over-length stub is
refused before anything is written, so the fix is to move prose into the done-file and retry
against an untouched repo. It is a hard reject, not a stop-and-ask: unlike splitting a spec file,
moving a paragraph into the archive loses nothing, so there is no judgment to put to the user. Blank
lines and the pointer line don't count against the cap.
The queue preamble is capped
Everything above the first ## Batch heading in BUILD_QUEUE.md is not an entry, so no retention
rule ever reached it. That preamble is where an agent parks a durable fact when it cannot decide
which spec/ file owns it: at finish time the queue is already open, writing there is one edit, and
nothing ever prunes it. It fills.
The preamble is now capped at 45 lines (the shipped template is 33), using the same
specflow:size-ok waiver as the 600-line spec cap and re-asking every +15. prune-ledgers section 3
audits the paragraphs into delete / relocate via spec-edit / keep. That section does
stop and ask, unlike the rest of pruning, because deciding which spec file should own a stranded
paragraph is a judgment call about concerns.
next and verify report ledger weight
ledger weight: BUILD_QUEUE.md 119 lines (preamble 28/45) · CLAIMS.md 228 lines (5 completed)
Printed every time, with a warning past a bound — more completed entries than the retention of 5, or
a preamble over its cap (honoring a waiver). specflow next --json carries the same numbers under
weight. This is reporting, not a second retention rule: the cut stays a count, for the
determinism reasons in spec/architecture.md → Ledger lifecycle. What a count cannot reveal is a
file that grew heavy while staying correct.
Behavior changes
specflow finishgains--stub-fileand refuses a stub over 8 prose lines (exit 1, nothing
written).--summary-filekeeps working as the old name.specflow nextandspecflow verifyprint aledger weightline;verifyskips it for
--spec-onlyinstalls, and it never changes verify's verdict.specflow next --jsongains aweightobject.
Install
curl -fsSL https://raw.githubusercontent.com/MatanKoby/specflow/main/install.sh | sh
# or
go install github.com/MatanKoby/specflow/cmd/specflow@v0.1.8
Full diff: v0.1.7...v0.1.8
v0.1.7
Action required
Run specflow upgrade in every repo that has specflow installed. This release is guidance only —
two rules added to files specflow manages, no CLI behavior change.
specflow upgrade
specflow status # `stale` should read `none` afterwards
What it changes on disk
Three managed files are refreshed. Nothing you own is touched: spec/, BUILD_QUEUE.md,
CLAIMS.md, and your own text outside specflow's markers are all unaffected, and upgrade still
never commits.
| Path | Change |
|---|---|
AGENTS.md |
one paragraph added to The work queue (full installs only) |
specflow/procedures/spec-edit.md |
one paragraph added to Persisting a design decision (full installs only) |
specflow/procedures/claim-batch.md |
one check added to Eligibility |
A managed file you have edited yourself is still left alone and its new version written to
<file>.specflow-new, as in v0.1.6.
What the rules say
Size a batch by the layers it crosses, not the deliverables it lists. Three edits inside one
layer is a small batch; one edit each to the spec, a template, the code, and its tests is a wide one,
however short each item reads in the queue. When a batch's declared file list spans more layers than
its goal needs, it gets split on the layer seam — and the split pieces then declare disjoint file
lists, which is exactly what the existing parallelism rule needs to let them run at the same time.
What counts as a layer is per project, and specflow does not enumerate it. Seams differ per repo;
a shipped list would be wrong in most installs and would read as a contract rather than a heuristic.
Prune before claiming, not only at finish. claim-batch.md now tests the retention rule in its
eligibility section:
sed -n '/^## Completed/,$p' CLAIMS.md | grep -c '^### ' # more than 5 → prune first
Pruning only at finish does nothing for whoever claims next: they still read the overgrown ledger on
the way in, and that read is the cost. The threshold is the same 5 that specflow finish already
enforces, so there is no second number and no stop-and-ask.
Both additions are gated to full installs where they name queue machinery, so a --spec-only install
still never mentions the queue.
Behavior changes
None. No verb, flag, or output line changed; specflow status, next, claim, finish, and
verify behave exactly as in v0.1.6.
Install
curl -fsSL https://raw.githubusercontent.com/MatanKoby/specflow/main/install.sh | sh
# or
go install github.com/MatanKoby/specflow/cmd/specflow@v0.1.7
Full diff: v0.1.6...v0.1.7
v0.1.6
Changelog
- f5f578f batch-AF: manage the adapter files as whole files
v0.1.5
Changelog
- 25f419e batch-CE: read-shape steps, an economy section, and config.check
- a7df418 batch-QV: archives are newest-first, so prepend above the first entry
- 2346480 batch-QV: procedures name the verbs as the fast path
- 80df6c4 batch-QV: queue verbs next, claim, and finish
- 9b20c36 batch-RD: a pushed tag publishes the release directly
- 1f70f84 batch-RD: the agent needs approval for every release
v0.1.4
v0.1.3
Changelog
- 3b265e0 batch-SL: make upgrade a complete fix path for existing spec-only installs
- d9ee716 batch-SL: propagate the refreshed CLAUDE.md to the dogfood install
- ee4b6b7 batch-SL: stop spec-only installs naming machinery they omit
- f9fa57b batch-SZ: make the 600-line spec-file cap a stop-and-ask
- 02e6a41 batch-SZ: propagate the size cap to the dogfood install
v0.1.2
v0.1.0
Changelog
- fa3aa3d batch-1: specflow add-agent
- 3f67292 batch-2: specflow status
- 7f5f70b batch-5: --dry-run preview for init and upgrade
- ce60443 batch-BI: bring per-agent instruction files under management
- b57bd50 batch-BI: idempotent injection + tier-aware decline notices
- ec31a2d batch-BI: per-subcommand help for init and upgrade
- 6d14a90 batch-BI: relocate _DONE archives to specflow/history/
- f583f39 batch-BI: specflow verify (install-integrity check)
- b766da2 batch-BI: two-phase, consent-gated brownfield init
- 5204656 batch-CFG: procedures honor commit/push levers
- ea3bffb batch-CFG: rename stamp to specflow/config.json with a config block
- 48ac469 batch-CFG: safety + CLI fixes (risk-A, git-required, NO_COLOR, friendly errors)
- 422284b batch-G1: add Go CLI at functional parity with the Node bin
- c2df02c batch-G1: remove Node CLI, switch CI to Go
- 2be98a5 batch-G1: rewrite README install/usage for the Go binary
- ac82cec batch-G1: self-host via the Go binary (stamp bump)
- 50c59aa batch-G2: GoReleaser cross-compile + release workflow + curl|sh installer
- 93519b0 batch-SO: spec-only install mode via section composition
- 66442e0 batch-U2: self-documenting, token-matched region markers
- 42cd047 batch-U: non-destructive upgrade via marked regions + drift detection
- ce72aec chore: harden for npm — LICENSE, metadata, smoke tests, CI, error handling, --version
- c822a3a ci: bump checkout/setup-node to v5 (Node 24), add Node 24 to matrix
- 7fb3c61 feat: initial specflow kit — cross-agent spec/batch/claim protocol + CLI