feat(sweep): schedule the FYI sweep - invocation was the gap, not the mechanism

coord-sweep.sh shipped in 0.10.0 and had never run once against the real
mailbox. Measured 2026-09-03 with a denominator (docs/2026-09-03-coordination-
debt-measurement.md): 55 mailbox directories, 52 with an inbox/, 27 pending
directed messages - 23 of them pure notices, re-injected at every session start
in repos nobody had opened. The script was correct and unreachable.

WP5 (order 20260902T113745Z-1254925290) asked for a mechanism and named two
candidates. The measurement chose neither, and the first session returned the
order saying so: bulk-ack for pure notices was already built - it is this
script - so the second candidate would have been two copies of one policy, and
the broadcast class converges on its own (reading sets seen), with 34 of 263
unread pairs belonging to two mailboxes no session can hold, so a TTL would
have closed those rather than reduced them. The operator then chose the window
and authorized the schedule.

launchd/com.ktg.repo-mailbox-sweep.plist runs --write --days 14 daily at 05:30.
That is the entire behavioural change. The window is written out in the plist
rather than inherited from the script's default: it is a policy constant chosen
on a measured distribution (30d -> 0 messages, 14d -> 7, 7d -> 13), so a later
change to DAYS=14 must not silently change what an unattended job closes across
51 other repos. It runs BEFORE the 06:00 briefing agent, which scans the same
mailbox this mutates, so the morning briefing reports the debt that remains
rather than counting notices being closed underneath it.

coord-selftest.sh section 38 pins the launchd templates (242 -> 257 checks). A
wrong program path is the one defect here that nothing catches at runtime: the
agent loads cleanly and then silently never runs, with no output to be wrong
and no exit status to read. launchctl list proves an agent is LOADED, never
that it is RIGHT. The section covers every plist in launchd/, not only the new
one - the plist grammar gets one reader rather than one per agent - while
board-selftest.sh section 9 keeps owning brief-nightly.sh's behaviour. Each
plist must name a script that exists here, carry a Label matching its filename,
keep its __CHECKOUT__/__HOME__ placeholders (public mirror), and never point
into the version-pinned plugin cache.

The cache assertion runs on the extracted path, never the whole file - caught
by the check itself on its first run: the brief plist's header explains in
prose why it does not point at the cache, and a file-wide grep read that
explanation as the defect it warns about, the same shape as prose saying
status=done triggering the board's done-guard. Four controls present; mutation-
verified against the real file, where a one-letter typo (coord-sweeep.sh) turns
exactly that check red. XML well-formedness is deliberately not checked:
plutil is not coreutils, and malformed XML already fails loudly at launchctl
load - the opposite of the silent failure this section exists for.

Also fixes the README selftest-checks badge, stale at 529 since 0.25.0; the
real total is 868 (257 + 368 + 73 + 116 + 54).

Suites: coord 257, board 368, route 73, orders 116, guard 54. npm test 11/11.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-04 08:12:32 +02:00
commit 2f8ceb3f97
12 changed files with 451 additions and 11 deletions

View file

@ -5,6 +5,84 @@ 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.33.0] - 2026-09-04
### Added
- **The FYI sweep is scheduled. Invocation was the gap, not the mechanism.**
`coord-sweep.sh` shipped in 0.10.0 and had never run once against the real
mailbox. Measured 2026-09-03 with a denominator
(`docs/2026-09-03-coordination-debt-measurement.md`): 55 mailbox directories,
52 carrying an `inbox/`, 27 pending directed messages - of which **23 were
pure notices**, re-injected at every session start in repos nobody had
opened. The script was correct and unreachable.
The order behind this (WP5, `20260902T113745Z-1254925290`) asked for a
mechanism and named two candidates; the measurement chose neither, and the
first session returned the order saying so. Bulk-ack for pure notices *was
already built* - it is this script - so building the second candidate would
have been two copies of one policy. The broadcast class converges on its own
(reading sets `seen`), and 34 of 263 unread pairs belong to two mailboxes no
session can hold, so a TTL would have **closed** those rather than reduced
them. The operator then chose the window and authorized the schedule.
`launchd/com.ktg.repo-mailbox-sweep.plist` runs `--write --days 14` daily at
05:30. That is the entire behavioural change; no new mechanism was built.
- **The 14-day window is written out in the plist, not inherited from the
script's default.** It is a policy constant chosen on a measured distribution
(30 days would have closed 0 messages, 14 closed 7, 7 would have closed 13),
of the same class as the STATE.md line limit. Leaving it implicit would let a
later change to `DAYS=14` silently change what an unattended job closes every
night across every other mailbox on this machine.
It runs at 05:30, clear of the 06:00 briefing agent, because the briefing
scans the same mailbox this mutates and the two must not overlap. It does
**not** change what the briefing reports as debt: since 0.22.0 that figure
comes from `coord-count.sh`'s `owed` column, and this sweep closes only
messages that owe nothing. What moves is the raw pending count — the table's
`INN` column and the volume injected at every session start. Selftest section
38 asserts the two agents never share an hour.
- **coord-selftest.sh section 38 pins the launchd templates (+15 checks, 242 ->
257).** A wrong program path in a plist is the one defect here that nothing
catches at runtime: the agent loads cleanly and then silently never runs -
no output to be wrong, no exit status to read, a failure indistinguishable
from a quiet machine. `launchctl list` proves an agent is *loaded*, never
that it is *right*.
The section covers **every** plist in `launchd/`, not just the new one: the
plist grammar gets one reader here rather than one per agent, which is the
two-copies-of-one-policy defect this repo has named repeatedly.
`board-selftest.sh` section 9 still owns `brief-nightly.sh`'s behaviour. Each
plist must name a script that exists in this checkout, carry a `Label`
matching its filename, keep its `__CHECKOUT__`/`__HOME__` placeholders (the
repo is mirrored publicly, and a plist is the one file that would otherwise
need an absolute home path), and never point into the version-pinned plugin
cache.
The cache assertion runs on the **extracted program path, never the whole
file** - caught by the check itself on its first run: the brief plist's header
explains in prose why it does *not* point at the cache, and a file-wide grep
read that explanation as the defect it warns about. Same shape as the board
line, where prose saying `status=done` must never trigger the done-guard.
Four controls are mandatory here and present: the extractor really does read
a path, a plist naming a missing script is judged missing, a `Label`
disagreeing with its filename is caught, and a program path inside the plugin
cache is caught. Mutation-verified against the real file: a one-letter typo
(`coord-sweeep.sh`) turns exactly that check red.
XML well-formedness is deliberately **not** checked. `plutil` is not
coreutils, and malformed XML already fails loudly at `launchctl load` - the
opposite of the silent failure this section exists for. Both files were
linted by hand at 0.33.0.
### Fixed
- **The README's selftest-checks badge had read 529 since 0.25.0; the real
total is 868.** A stale count in the one place a reader takes as the
headline number, corrected while adding to it: 257 + 368 + 73 + 116 + 54.
## [0.32.1] - 2026-09-03
### Fixed