WP5 asked for one of two candidate mechanisms, chosen by measurement. The measurement chose neither. coord-sweep.sh already IS the bulk-ack for pure notices, and it has never run: no _sweep.log at the default path, and none anywhere under ~/.claude (stated that way because --log can override the default, so an absent default-path log alone would not prove it). Its own dry-run agrees exactly with an independent classification of the same inboxes - 0 at 30 days, 7 at 14, 13 at 7 - so the gap is invocation, not mechanism. The broadcast class converges on its own: reading records a broadcast as seen, so a mailbox clears its backlog on its next session. 263 of 884 pairs are unread, but 34 of those belong to two mailboxes no session can hold - one a documented retired --to address, one with no checkout anywhere under /Users/ktg (known-positive control: llm-ingestion-okf resolves). A TTL would close those rather than reduce them. What remains is running coord-sweep --write unattended, which closes mail in 51 other repos' inboxes unread. That is a decision on another repo's behalf and a policy constant of the same class as the STATE.md line limit, so it is left to the operator rather than shipped. No version bump: nothing here changes behaviour. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
4.8 KiB
Coordination debt: what does not converge, and why building a second
mechanism would have been wrong
Measured 2026-09-03 against the live mailbox, for order
20260902T113745Z-1254925290-from-.claude (WP5). Every number below was
produced by a command, and every negative result carries the control that
proves the query could have found something.
The order offered two candidate mechanisms and said to choose by measurement, not taste: a broadcast TTL, or a bulk-ack for pure notices. The measurement chose neither. One of them is already built and has never been run; the other addresses the class that converges on its own.
Denominators
| population | count |
|---|---|
| mailbox directories under the coord root | 55 |
of those, holding an inbox/ |
52 |
| pending directed messages across all inboxes | 27 |
broadcasts in _broadcast/inbox/ |
17 |
| (mailbox x broadcast) delivery pairs | 884 |
The three directories with no inbox/ are named rather than silently dropped:
jobbsok, mediemon, medieovervaaking. 55 - 3 = 52 is the reconciliation,
stated because an unreconciled pair of denominators in one report is the same
positive-looking null this engine refuses everywhere else.
The two classes behave in opposite directions
Directed messages do not converge. They are re-injected at every session
start until a session closes them by hand. Of the 27 pending, 4 owe a reply and
23 are pure notices (reply-expected: no). By age:
| class | <7d | 7-13d | 14-29d | >=30d |
|---|---|---|---|---|
| owes a reply | 0 | 3 | 1 | 0 |
| pure notice | 10 | 6 | 7 | 0 |
Broadcasts converge on their own. Reading one records it as seen, so a mailbox clears its whole backlog on its next session. 263 of the 884 pairs are unread (29.8%), and the distribution shows the self-clearing: 10 mailboxes are fully current, 26 sit at exactly 6 unread (the newest announcements), and the tail is short.
The floor under the broadcast number, which strengthens the case
Two mailboxes hold all 17 broadcasts unread, and neither can ever read them:
ktg-plugin-marketplaceis a retired--toaddress. It is a polyrepo directory, not a git repo, sobasename(git toplevel)can never resolve to it and no session can hold that identity. This is already documented as engine behaviour; the 17 unread are its permanent consequence.llm-ingestion-guardhas no checkout anywhere under/Users/ktg(find -maxdepth 4, withllm-ingestion-okfas the known-positive control proving the query finds a real one) and no.origin. It also holds the single oldest pending notice, 24 days.
So 34 of 263 unread pairs (12.9%) are a permanent floor no TTL would reduce
to zero - it would close them, but it would be closing announcements for
mailboxes that were never going to read anything. .origin absence alone is
NOT a proxy for unholdable: repos also lacks one, yet sits at 2 unread of 17,
which is only possible if something reads it.
Why no second mechanism was built
coord-sweep.sh already is the bulk-ack for pure notices: machine-wide,
one mechanically decidable class (reply-expected: no, older than a grace
window), dry-run by default, closing through coord-done.sh, logging sender
and subject for every closure. Building the order's second candidate would have
been a second copy of a shipped policy - the defect class this repo names
repeatedly.
Its own dry-run reports what it would close today, and the figures agree exactly with the independent classification above:
--days 30 -> 0 messages
--days 14 -> 7 messages
--days 7 -> 13 messages
The gap is invocation, not mechanism. No _sweep.log exists at the default
path, and no sweep log exists anywhere under ~/.claude (the one *sweep* hit
is an unrelated plugin file) - stated that way because --log can override the
default, so an absent default-path log alone would not prove it never ran.
What remains, and why it is not this repo's call
Making the notice class converge without opening each repo means running
coord-sweep.sh --write unattended. That closes mail in 51 other repos'
inboxes, unread, and the script's own design says so in as many words: a notice
to a repo left unopened for the whole window is closed unread, and the log is
the only thing standing between that and a silent disappearance.
Deciding that on another repo's behalf is the one anti-pattern with no exception clause, and the grace window is a policy constant of the same class as the STATE.md line limit, which was an operator decision both times it moved. Dry-run is the default precisely because this is the script that destroys pending state; flipping that to a schedule is the operator's act, not a plugin's.
The messages that owe a reply are untouched by any of this, at any age, with any flag. That is not a gap to close later - it is the rule that keeps a procedural duty from becoming a substantive one.