chore(release): bump to 0.30.0

Two commits landed after v0.29.0 and neither is reachable: every entry point
on this machine runs the version-pinned plugin cache, not the tree.

Minor rather than patch because the behaviour is EXTENDED, not merely
repaired - coord-count.sh and board.sh gained a new exit status (3, "the
world you named is not there"), and pre-state-line-guard.mjs gained
CLAUDE_STATE_MAX_LINES. Exit codes are a contract, and the installed cache
still runs the old one.

Version synced across all seven version-bearing files - plugin.json,
package.json, the README badge, and the four SKILL.md frontmatters - so
release-plugin.mjs's plugin.json == README-badge == target check passes.
The three remaining 0.29.0 mentions (CLAUDE.md:915, CLAUDE.md:960,
board.sh:1388) are historical prose about when things shipped and must keep
saying 0.29.0.

CHANGELOG covers BOTH unreleased commits, not just the newest: d8fdeaa's
five defects were one class - a failed measurement rendering as a
reassuring value - and the entry carries what that session found about
itself, that --plan's free-capacity test had to move from "$7 + 0" to a
string comparison against "0", because "?" coerces to 0 in arithmetic and
would have certified an UNMEASURED tree as free capacity. The defect
reappearing one layer down, wearing the fix as a disguise.

No tag and no catalog change: that is the catalog session's order, which
runs release-plugin.mjs to do the tag and the ref bump atomically. Doing
both here would take a decision on the catalog's behalf without its
context.

Suites verified under system bash 3.2, not Homebrew 5.3: coord 242, board
300, route 69, orders 110, guard 54; npm test 11/11.

Order 20260826T115026Z-998059640-from-.claude
This commit is contained in:
Kjell Tore Guttormsen 2026-08-26 14:09:37 +02:00
commit 3e4a3bc62a
8 changed files with 112 additions and 7 deletions

View file

@ -5,6 +5,111 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.30.0] - 2026-08-26
Two commits landed after `v0.29.0` and neither is reachable: every entry point
on this machine runs the version-pinned plugin cache, not the tree. Minor
rather than patch because the behaviour is EXTENDED, not merely repaired -
`coord-count.sh` and `board.sh` gained a new exit status and the state-line
guard gained an environment variable. Exit codes are a contract, and the
installed cache still runs the old one.
All five defects here are the same class: **a failed measurement rendering as
a reassuring value** - a broken or uninstrumented query returning a
positive-looking null, consumed as a fact about the world.
### Fixed
- **`coord-count.sh`: a mailbox root that does not exist was byte-identical to
one where nobody has pending mail** - zero lines on stdout, exit 0, nothing
on stderr. `board.sh` consumes that TSV. The contract is now three statuses:
0 = counted, 2 = usage error, **3 = mailbox root absent**. 3 rather than 2 on
purpose: 2 already means "you called me wrong" and this means "the world you
named is not there" - two different repairs, and a consumer that only ever
sees one integer cannot tell them apart. Both nonzero paths print NOTHING on
stdout, so neither can be mistaken for a count of zero. What the old
"always 0" claim protected is kept and made precise: no state OF THE MAILBOX
can produce a nonzero exit, so a SessionStart still cannot be failed by mail.
An existing-but-empty root stays a silent, clean 0, and the selftest pins
that silence as hard as it pins the failure.
- **`coord-count.sh`'s header promised exit 0 unconditionally, and that was
already false** - `--exclude` with no value exited 2 - so the documented
contract contradicted code a reader could run. The header text is now pinned
by a check of its own: a doc line nothing tests is a doc line that drifts,
which is how this one drifted.
- **`board.sh`: `git status --porcelain | wc -l` yields zero lines whether the
tree is CLEAN or git refused to answer**, so DRT printed **0** and every
reader took it as "nothing uncommitted here". Reachable, not theoretical:
discovery tests `.git` with `-e` so worktrees are found, and a worktree whose
parent checkout was deleted exits 128. The exit status is now the
discriminator and an unmeasured tree reads **`?`** - the same token
`coord-count.sh` already uses for an age it could not compute.
**The fix was worthless without its two awk consumers, and one of them is
the point:** `--plan`'s free-capacity test compared `$7 + 0`, and `"?"`
coerces to **0** in arithmetic - so an UNMEASURED tree would have been
certified as free capacity. That is the defect reappearing one layer down,
wearing the fix as a disguise. It now compares the field as a STRING against
exactly `"0"`; only a tree measured clean is clean. The SUM roll-up, where a
`?` would silently add 0, now NAMES the repos it could not add.
- **`board.sh`: a scan root that does not exist was skipped in silence**, so a
typo in `--roots`, a moved home directory and a genuinely empty tree were one
single output with the denominator never reported. Every bad root is now
named on stderr; the exit status changes only when NO root was scanned at all
(**3**, for the same reason as above). A mix still exits 0 and still prints
the board - some repos really were scanned. This REPLACES an older assertion
reading `check "missing root is a clean no-op"`: the defect encoded as a
passing test, which is why nothing ever caught it. `brief-nightly.sh` needs
no change and gains from this - it already treats a nonzero `--brief` as
failure and keeps yesterday's briefing, so a mistyped root now preserves the
file instead of overwriting it with an empty render.
### Added
- **`CLAUDE_STATE_MAX_LINES` overrides the state-line guard's limit**, the same
kind of knob `CLAUDE_COORD_DIR` is for the mailbox root. As a bare constant
the limit forced every boundary fixture to hardcode the number the code
carried - two copies of one policy, both rewritten by hand the last time the
operator moved it (60 -> 120). It is **not** a bypass claim; this guard has
always been escapable by writing the file another way. The one thing it must
never do is silently fail to take effect, so an UNUSABLE value (`""`, `"0"`,
negatives, anything not made of digits) **denies with a message naming the
variable** instead of falling back to 120 - a caller who set it and got the
default anyway would be reading a limit that was never in force.
### Changed
- **The README's Install block is now fenced (` ```bash `)** while the rest of
the README keeps its indented blocks. The org-side D-census reported this
repo as the only plugin of 12 with no start command; the premise was
imprecise - the lines were present, indented - but the finding held, because
the machine-readable entry point keys on a fenced block and 10 of 11 sibling
plugin READMEs use one. Measured before and after: 11 of 11 now match. Only
the Install block was converted; the other 39 indented blocks were not
ordered changed and converting them would bury a two-line fix in a
whole-file diff.
### Known gaps, stated rather than closed
- `coord-inbox.sh:57` and `coord-order-inbox.sh:60/64` carry the identical
`[ -d "$COORD" ] || exit 0` shape that was fixed in `coord-count.sh`. The
denominator was measured, not estimated, and they were deliberately left
alone: they are the INJECTION path, their output is prose a session reads
rather than a TSV a program parses, and their contract really is "never fail
a SessionStart".
- `board.sh` does not yet CONSUME `coord-count.sh`'s new exit 3. The signal
exists; the `HAVE_COUNT` machinery does not read it. Tracked as separate
work rather than widened into this release in silence.
### Testing
Suites at this release, verified under system bash 3.2 (not Homebrew 5.3):
coord 242, board 300, route 69, orders 110, state-line-guard 54; `npm test`
11/11. Every design choice above was mutation-tested, and every negative check
carries a known-positive control - one selftest section's first cut was
VACUOUSLY green (wrong basename, plus a fixture path that was never exported)
and is recorded in `CLAUDE.md` rather than quietly fixed.
## [0.29.0] - 2026-08-23
Answers a question the board could not answer before: which repos are finished