# 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-marketplace` is a **retired `--to` address**. It is a polyrepo directory, not a git repo, so `basename(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-guard` has no checkout anywhere under `/Users/ktg` (`find -maxdepth 4`, with `llm-ingestion-okf` as 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. --- ## Appendix: the before-state, captured 2026-09-04 08:0x UTC The operator authorized the sweep on 2026-09-03 (14-day window, scheduled via launchd). This is `coord-count.sh` immediately before the authorized `--write --days 14` run, recorded here because a before-state stops existing the moment the write happens, and the order asks for debt before/after with a denominator. mailbox pending owed origin-age app-creator 1 1 - claude-playlist-corpus 1 0 - graceful-handoff 3 0 - human-friendly-style 1 0 - ki-produktivitetsmodell 1 0 - llm-ingestion-guard 1 0 25 llm-security 2 0 - mcp-servere 2 0 - okr 1 0 - org-ops 5 2 - portfolio-optimiser-commons 3 0 - repo-standard 1 1 - wiki-advise 2 0 - .claude 1 0 - .profile 1 0 - ------------------------------------------------------ 15 mailboxes with pending mail 26 4 `$CLAUDE_COORD_DIR/_sweep.log` did not exist: the sweep had still never run. **The control this file exists to make runnable:** after the write, the `owed` column must be BYTE-IDENTICAL (total 4), because the sweep spares every message that owes a reply at any age. A changed `owed` figure means the sweep closed something it must never touch, and is a defect, not a result. **The dry-run said 11, not the 7 this document measured a day earlier, and the gap is entirely the moving cutoff.** Verified two ways rather than assumed. Yesterday's 14-day cutoff was ~20260820T184902; today's is 20260821T055442. Four notices timestamped 2026-08-20 between those two instants (`human-friendly-style` T210113Z, `ki-produktivitetsmodell` T205611Z, `org-ops` T210955Z and T211358Z) crossed the boundary in one calendar day: 7 + 4 = 11. Independently, `--days 15` today yields 6, and those 6 plus `mcp-servere/20260820T104644Z` - which sits between the 15-day cutoff and yesterday's 14-day one - reconstruct yesterday's 7 exactly. The engine is consistent; the 7 was a measurement of a moment, never a constant, and reading it as one would have been face 3 of the verification law pointed at our own report.