feat(engine): retire a broadcast with coord-send --retract
Nothing could remove a message from _broadcast/inbox/. coord-done is directed-only and never touches the broadcast queue, so the backlog could only grow: every new repo received the entire standing history at its first session, including announcements that had since become false. --retract <filename> archives the message into _broadcast/archive/, so no future repo is served it. Three deliberate limits, all pinned by tests: - Un-send, not recall. Repos that already received it keep it; _broadcast/seen/ is delivery history and is left untouched. - Only the sender may retract (from: must match the repo identity). --from overrides it, as everywhere else in the engine, which makes the check an accident guard rather than a security boundary. - Nothing is deleted, mirroring coord-done. Retracting twice is a no-op. The branch runs before every send-side validation and before the stdin body read, since a retract carries no subject and no body. Selftest 70 -> 82 (new section 19). Also fixes two README defects the feature exposed: the install command still named coord@ after the v0.3.0 rename, and the docs advised pruning _broadcast/inbox/ by hand, which contradicted the rule that the script owns mailbox files.
This commit is contained in:
parent
c27b20fc62
commit
316b8acdd2
8 changed files with 184 additions and 15 deletions
14
README.md
14
README.md
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
*AI-generated: all code produced by Claude Code through dialog-driven development.*
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
|
@ -31,6 +31,7 @@ Mailbox layout (default `~/.claude/coord/`, override with `CLAUDE_COORD_DIR`):
|
|||
<repo>/inbox/ pending directed messages TO <repo>
|
||||
<repo>/archive/ handled messages (kept, never deleted)
|
||||
_broadcast/inbox/ messages to ALL repos (accumulate)
|
||||
_broadcast/archive/ retracted broadcasts (kept, never deleted)
|
||||
_broadcast/seen/<repo> per-repo seen set: delivered broadcast filenames, one per line
|
||||
|
||||
Repo identity is the basename of the git toplevel (fallback: the working directory). There is no registration — a repo joins the moment something is sent to it, or when it first reads a broadcast.
|
||||
|
|
@ -45,12 +46,14 @@ Message format (filename `<UTC-timestamp>-<uniq>-from-<sender>.md`):
|
|||
---
|
||||
<body>
|
||||
|
||||
**Lifecycle — deliver until done.** Directed messages are NOT archived on read. They stay pending and are re-injected at every session start (startup, `/clear`, resume) until explicitly marked handled: replying (`--reply-to`) archives the original, or `coord-done <file>` archives it without a reply. `/clear` never loses a message. Broadcasts are delivered once per repo via the seen set, and never back to their own sender — the announcing repo's seen entry is written at delivery time, so it is not told its own news. Broadcasts otherwise accumulate; prune `_broadcast/inbox/` manually when a notice stops being relevant to future first-time repos.
|
||||
**Lifecycle — deliver until done.** Directed messages are NOT archived on read. They stay pending and are re-injected at every session start (startup, `/clear`, resume) until explicitly marked handled: replying (`--reply-to`) archives the original, or `coord-done <file>` archives it without a reply. `/clear` never loses a message. Broadcasts are delivered once per repo via the seen set, and never back to their own sender — the announcing repo's seen entry is written at delivery time, so it is not told its own news.
|
||||
|
||||
**Retracting a broadcast.** Broadcasts otherwise accumulate, and every future first-time repo receives the whole standing backlog — including announcements that have since become false. `coord-send --retract <filename>` retires one: it moves the message out of `_broadcast/inbox/` into `_broadcast/archive/`, so no future repo is served it. This is un-send, **not recall** — repos that already received it are unaffected. Only the original sender may retract (`from:` must match your repo identity); `--from <sender>` overrides that, which makes it an accident guard rather than a security boundary. Retracting twice is a no-op.
|
||||
|
||||
## Install
|
||||
|
||||
claude plugin marketplace add https://git.fromaitochitta.com/open/ktg-plugin-marketplace.git
|
||||
claude plugin install coord@ktg-plugin-marketplace
|
||||
claude plugin install repo-mailbox@ktg-plugin-marketplace
|
||||
|
||||
The plugin ships empty: your mailbox is created lazily on first send, on your machine, and stays there.
|
||||
|
||||
|
|
@ -65,6 +68,7 @@ The plugin ships empty: your mailbox is created lazily on first send, on your ma
|
|||
coord-send.sh --to <repo> --subject "<subject>" [--message "<text>"] # or body on stdin
|
||||
coord-send.sh --broadcast --subject "<subject>" <<'BODY' ... BODY
|
||||
coord-send.sh --reply-to <filename> [--subject "Re: ..."] # routes + closes original
|
||||
coord-send.sh --retract <filename> [--from <sender>] # retire your own broadcast
|
||||
coord-inbox.sh [--repo <name>] # print pending (what the hook injects)
|
||||
coord-done.sh <filename>... | --all # archive without replying
|
||||
|
||||
|
|
@ -81,7 +85,7 @@ Cross-repo message content is untrusted input by design:
|
|||
|
||||
- **Atomic delivery:** the temp file is created inside the destination directory (dot-prefixed, invisible to the inbox glob), so the final rename never crosses filesystems and readers never observe a half-written message.
|
||||
|
||||
Every guarantee above is pinned by the 70-check selftest, including forgery-resistance regressions.
|
||||
Every guarantee above is pinned by the 82-check selftest, including forgery-resistance regressions.
|
||||
|
||||
## The Six Rules
|
||||
|
||||
|
|
@ -100,7 +104,7 @@ Every guarantee above is pinned by the 70-check selftest, including forgery-resi
|
|||
|
||||
## Development
|
||||
|
||||
bash scripts/coord-selftest.sh # 70 checks against a throwaway mailbox
|
||||
bash scripts/coord-selftest.sh # 82 checks against a throwaway mailbox
|
||||
npm test # same selftest via node --test
|
||||
|
||||
TDD is the house rule: every behavior change lands with a failing selftest check first.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue