feat(sweep): close the aged FYI backlog without a human in the loop
A notice needs no answer, but it is still re-injected at every session start until someone closes it by hand. Across enough repositories that hand-closing IS the manual work the mailbox was meant to remove, and the pending count -- the operator's only signal -- drowns in messages that were never going to be acted on: 9 of 22 pending messages across 12 mailboxes were pure notices when this was written. coord-sweep.sh closes exactly one mechanically decidable class: reply-expected: no, older than a grace window (default 14 days). A message that owes a reply is never touched, at any age, with any flag -- answering it would mean deciding something on the receiving repo's behalf, which is the one thing this system exists to prevent. Four properties are load-bearing, not incidental: - Dry-run is the default, inverted from the rest of the engine. The others print or deliver; this one destroys pending state, so the safe direction has to be what you get by forgetting a flag. - Closing goes through coord-done.sh --repo, never mv, so the archive layout and the _broadcast refusal stay in one place. - Age is read from the filename prefix, never the file. An unreadable age is never treated as old: fail-safe, not fail-open. - Every closure is logged with sender and subject. Directed messages have no seen-tracking, so the sweep cannot tell "seen and ignored" from "never delivered" -- a notice can be closed unread, and the log is the only thing standing between that and silent data loss. The reply-expected read is bounded to the frontmatter block, matching coord-count.sh: a body line claiming it at column 0 is untrusted cross-repo input and must not close its own message. No scheduler, no launchd unit, no skill front door -- the script does nothing until invoked. Selftest 159 -> 182. The log check caught a real defect during development: the first implementation read from/subject AFTER coord-done.sh had moved the file, logging empty values and quietly defeating the only safeguard the design has. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uwcak9j4m9JijDKeFmptww
This commit is contained in:
parent
9cb405c2cd
commit
459c9feec0
10 changed files with 362 additions and 11 deletions
52
CHANGELOG.md
52
CHANGELOG.md
|
|
@ -5,6 +5,58 @@ 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.14.0] - 2026-08-01
|
||||
|
||||
### Added
|
||||
|
||||
- **`scripts/coord-sweep.sh` — the FYI backlog closes itself, deterministically
|
||||
and without a model.** A notice needs no answer, but it is still re-injected at
|
||||
every session start until someone closes it by hand. Across enough
|
||||
repositories that hand-closing *is* the manual work the mailbox was supposed to
|
||||
remove, and the pending count — the operator's only signal — drowns in messages
|
||||
that were never going to be acted on. At the time of writing, 9 of 22 pending
|
||||
messages across 12 mailboxes were pure notices.
|
||||
|
||||
The sweep closes exactly one mechanically decidable class: a directed message
|
||||
whose sender declared `reply-expected: no`, older than a grace window (default
|
||||
14 days, `--days`). **A message that owes a reply is never touched, at any age,
|
||||
with any flag** — answering it would mean deciding something on the receiving
|
||||
repo's behalf, which is the one thing this system exists to prevent a session
|
||||
from doing.
|
||||
|
||||
Four properties are load-bearing rather than incidental:
|
||||
|
||||
- **Dry-run is the default**, inverted from every other script here. The others
|
||||
print or deliver; this one destroys pending state, so the safe direction has
|
||||
to be the one you get by forgetting a flag.
|
||||
- **Closing goes through `coord-done.sh --repo`, never `mv`.** The engine owns
|
||||
the filename grammar and the archive layout, and it already refuses
|
||||
`_broadcast` on its own — archiving out of there would retire an announcement
|
||||
for every repo that has not read it yet, an unauthenticated retract.
|
||||
- **Age is read from the filename prefix, never from the file.** A name the
|
||||
grammar does not produce has no readable age, and an unreadable age is never
|
||||
treated as old: fail-safe, not fail-open.
|
||||
- **Every closure is logged** (`$CLAUDE_COORD_DIR/_sweep.log` by default) with
|
||||
sender and subject. This is not bookkeeping. A directed message has no
|
||||
seen-tracking — only broadcasts do — so the sweep cannot distinguish "seen and
|
||||
ignored" from "never delivered", and a notice to a repo left unopened for the
|
||||
whole window is closed **unread**. That is an accepted tradeoff; the log is
|
||||
the only thing standing between it and a silent disappearance.
|
||||
|
||||
The `reply-expected` read is bounded to the frontmatter block, matching
|
||||
`coord-count.sh`: a body line claiming `reply-expected: no` at column 0 is
|
||||
untrusted cross-repo input and must not be able to close its own message. That
|
||||
is the one place where getting it wrong would let another repo delete its way
|
||||
out of your inbox.
|
||||
|
||||
Scoped deliberately: no scheduler, no launchd unit, no skill front door. The
|
||||
script does nothing until invoked.
|
||||
|
||||
- Selftest 159 → 182 checks (new section 29). The log check caught a real defect
|
||||
during development — the first implementation read `from`/`subject` *after*
|
||||
`coord-done.sh` had already moved the file, logging empty values and quietly
|
||||
defeating the only safeguard the design has.
|
||||
|
||||
## [0.13.0] - 2026-07-31
|
||||
|
||||
### Added
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue