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
24
CLAUDE.md
24
CLAUDE.md
|
|
@ -13,9 +13,24 @@ marketplace plugin. Three components, one boundary:
|
|||
- **Engine (`scripts/*.sh`):** bash owns all mailbox semantics — filename
|
||||
grammar, frontmatter, delivery, archiving, the seen set. `coord-send.sh`
|
||||
writes, `coord-inbox.sh` reads (formatted for context injection),
|
||||
`coord-done.sh` archives, `coord-count.sh` counts without delivering.
|
||||
`coord-done.sh` archives, `coord-count.sh` counts without delivering,
|
||||
`coord-sweep.sh` closes the aged FYI backlog machine-wide.
|
||||
Everything is pinned by `coord-selftest.sh`
|
||||
(159 checks, throwaway mailbox via `CLAUDE_COORD_DIR`).
|
||||
(182 checks, throwaway mailbox via `CLAUDE_COORD_DIR`).
|
||||
|
||||
**`coord-sweep.sh` is the only path that closes a message with no human in
|
||||
the loop, and every constraint on it follows from that.** It may close exactly
|
||||
one mechanically decidable class - `reply-expected: no`, older than the grace
|
||||
window - because a message that owes a reply can only be answered by a session
|
||||
in the repo that owes it. Dry-run is the default, inverted from the rest of the
|
||||
engine, since this is the one script that destroys pending state. It closes
|
||||
through `coord-done.sh --repo` rather than moving files, so the archive layout
|
||||
and the `_broadcast` refusal stay in one place. And it logs every closure with
|
||||
sender and subject, because directed messages have no seen-tracking: the sweep
|
||||
genuinely cannot tell "seen and ignored" from "never delivered", so a notice
|
||||
can be closed unread and the log is the only record that it existed. Widening
|
||||
the class, defaulting to `--write`, or dropping the log each independently
|
||||
turn this from a bounded cleanup into silent data loss.
|
||||
|
||||
**`coord-count.sh` prints TWO integers per mailbox** (`<name>\t<pending>\t<debt>`),
|
||||
and the first must stay pending: `board.sh` counts the same inbox files
|
||||
|
|
@ -153,7 +168,7 @@ obligations in another repo.
|
|||
- Zero dependencies everywhere: bash + coreutils in the engine, `node:`
|
||||
builtins only in hook and tests.
|
||||
- TDD: no behavior change without a failing selftest check first.
|
||||
`bash scripts/coord-selftest.sh` must exit 0 (159/159),
|
||||
`bash scripts/coord-selftest.sh` must exit 0 (182/182),
|
||||
`bash scripts/board-selftest.sh` must exit 0 (36/36) and
|
||||
`bash scripts/route-selftest.sh` must exit 0 (73/73).
|
||||
- English for all code, docs, and commit messages (public repo). Norwegian
|
||||
|
|
@ -168,6 +183,9 @@ obligations in another repo.
|
|||
- Board smoke test: `bash scripts/board.sh` (read-only, ~3s over the real tree)
|
||||
- Route smoke test: `bash scripts/route.sh --path known --verification strong
|
||||
--reversibility cheap --scope local --rationale x` (writes nothing, instant)
|
||||
- Sweep smoke test: `bash scripts/coord-sweep.sh` (dry-run is the default, so
|
||||
this writes nothing; never add `--write` to a smoke test against the real
|
||||
mailbox)
|
||||
|
||||
## Release
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue