chore(release): 0.25.0

Version bumped across plugin.json, package.json, the README version badge and
all four skill frontmatters (coord-send, board, route, dispatch), plus the
README skills (3 -> 4) and selftest-checks (433 -> 529) badges. CHANGELOG
entry for --dispatch/the dispatch skill and the coord-send reply-mode fix.

NOT released: no tag, no catalog ref bump. Both require pushing to Forgejo,
and the operator has held the push window closed. The catalog gate is clear
(check-versions.mjs: 12 plugins, 0 ERROR; the single WARN is this bump
itself), so the release runs the moment the window opens.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ett8uHMDLir6trFaMzrYRu
This commit is contained in:
Kjell Tore Guttormsen 2026-08-16 16:19:26 +02:00
commit 05e56eb2a5
8 changed files with 68 additions and 8 deletions

View file

@ -5,6 +5,66 @@ 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.25.0] - 2026-08-16
### Added
- **`board.sh --dispatch` and the `dispatch` skill: the startup command a
handed-over session can actually act on.** "Start a session in repo X, on
order Y, at cost Z" was produced by hand, and it misfired four times in one
day (2026-08-16) across two repos. Three distinct holes, all measured:
a bare `claude --model X --effort Y` forces the operator to type Go and
leaves the session guessing its task out of `STATE.md`; `--no-go` stops only
the follow-up Go message, never the work (`morning:806`), so a dispatched
session starts working by itself; and a session dispatching its own next
session gets an empty plan, because `plan_drop_open` (`morning:1788`) drops a
block whose repo already has a pane and the dry-run then reports "0 of 1",
which reads as a broken plan file. `--dispatch` closes all three: the command
carries the prompt in argv (`... "$(cat <file>)"`), the `--no-go` semantics
are stated in the emitted output rather than only in a comment, and there are
two output forms chosen by `--target-pane` — a plan block, or a bare paste
line for the tab that already exists, carrying no `cd` and **no `tab=` key at
all**, so it can never be fed to `morning` as a plan.
It lives in `board.sh` rather than in a script of its own because the block
format has exactly ONE generator and `board.sh` already is it; a second
emitter of `tab=`/`repo=`/`dir=`/`command=`/`paste=` would be two copies of
one file format. Read-only is untouched — every check is a read, and the two
writes a dispatch needs (the prompt file, the plan file) stay with the
caller, the same split `brief-nightly.sh` already carries.
`--target-pane yes|no` is required with **no default**, the same rule
`route.sh`'s `--last-effort` carries: it is a measurement of the world
(`morning --probe-panes`, which works without a tty), and the dry-run cannot
substitute for it — measured 2026-08-16, run from a Claude session `morning`
reports `window: unknown ... assuming an empty window` and `plan_drop_open`
never fires, so a dry-run gate would pass the self-dispatch case every time.
Cost comes from `route.sh`'s row table; `--dispatch` deliberately takes no
`--model`/`--effort`, because `--advisor opus` is a property of the ROW.
Also measured: `"$(cat f)"` passes the file's bytes as one argv element with
no re-evaluation, so `$(...)`, backticks, quotes and UTF-8 in the prompt BODY
are inert — only the PATH is expanded, and it is required to be absolute and
shell-clean. `board-selftest.sh` sections 18-19 (183 -> 217 checks).
### Fixed
- **`coord-send --reply-to` claimed "marked handled" without checking.** It ran
`coord-done` under `>/dev/null 2>&1` and printed the handled claim
unconditionally: measured with a stub `coord-done` exiting 1, the original
stayed in the inbox, no `archive/` was created, and `coord-send` still exited
0 saying the message was handled — a false success in the message transport
itself, which is why every reply had to be verified by hand afterwards. The
predicate is deliberately wider than the exit code: `coord-done` exits 0 when
it archives nothing (an unknown name is idempotently fine by its own
contract), so an exit-code-only fix would still certify a message that never
moved. The check is exit 0 **and** the original no longer being at
`$COORD/$FROM/inbox/$REPLYTO` — recomputed rather than reusing `$REPLY_ORIG`,
which resolves to the inbox OR the archive, so replying to an already-archived
original moves nothing and must not warn. Failure is **exit 1**, a new status:
the reply WAS delivered and re-sending it would duplicate it, so 2 stays the
nothing-was-written status. `coord-selftest.sh` section 34 (206 -> 220
checks).
## [0.24.0] - 2026-08-14
### Added