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

@ -1,6 +1,6 @@
{
"name": "repo-mailbox",
"version": "0.24.0",
"version": "0.25.0",
"description": "Local mailbox for coordination between Claude Code sessions in different repositories. Directed messages and broadcasts as plain Markdown files on your own disk, injected as context at session start. Local, private, no network.",
"author": {
"name": "Kjell Tore Guttormsen"

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

View file

@ -10,11 +10,11 @@ Session A in repo X leaves a message for repo Y; the next session in repo Y gets
*AI-generated: all code produced by Claude Code through dialog-driven development.*
![Version](https://img.shields.io/badge/version-0.24.0-blue)
![Version](https://img.shields.io/badge/version-0.25.0-blue)
![Hooks](https://img.shields.io/badge/hooks-1-green)
![Skills](https://img.shields.io/badge/skills-4-orange)
![CLI scripts](https://img.shields.io/badge/CLI_scripts-8-blue)
![Selftest checks](https://img.shields.io/badge/selftest_checks-433-blue)
![Selftest checks](https://img.shields.io/badge/selftest_checks-529-blue)
---

View file

@ -1,6 +1,6 @@
{
"name": "repo-mailbox",
"version": "0.24.0",
"version": "0.25.0",
"private": true,
"type": "module",
"engines": {

View file

@ -20,7 +20,7 @@ description: >-
user names no repo and no tool — choosing *between* repos is this skill. Not for
"where were we" inside the current repo: that is this repo's own STATE.md,
already injected at session start.
version: "0.24.0"
version: "0.25.0"
---
# board — which repo deserves the next session

View file

@ -15,7 +15,7 @@ description: >-
covers retiring a broadcast that has become wrong or obsolete: "retract that
broadcast", "that announcement is outdated, pull it", "trekk tilbake kringkastingen",
"den broadcasten er utdatert".
version: "0.24.0"
version: "0.25.0"
---
# coord-send — natural-language front door for inter-repo messages

View file

@ -17,7 +17,7 @@ description: >-
skill. Not for choosing WHICH repo deserves the next session (that is `board`),
not for scoring model and effort alone (that is `route`), and not for sending a
message to another repo (that is `coord-send`).
version: "0.24.0"
version: "0.25.0"
---
# dispatch — hand a session a task it can actually start on

View file

@ -14,7 +14,7 @@ description: >-
the operator names no model and no tool — choosing the model for the next
session IS this skill. Not for choosing which REPO gets the next session:
that is the `board` skill.
version: "0.24.0"
version: "0.25.0"
---
# route — what the next session should run with