feat(engine): harden mailbox CLIs for v0.2.0

- Atomic delivery: create the temp file inside the destination dir
  (dot-prefixed, invisible to the inbox glob) so the final rename never
  crosses filesystems and readers never see a half-written message.
- Reject . and .. explicitly in the --reply-to and coord-done name
  guards instead of relying on downstream failure.
- Add -h/--help to coord-inbox.sh (uniform across the three CLIs).
- Close selftest gaps: default mailbox path via HOME fallback, malformed
  frontmatter on the read path, read-only destination dir. 48 -> 64
  checks, all green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018fduZz8otpU3W3rhfoPD6t
This commit is contained in:
Kjell Tore Guttormsen 2026-07-24 19:57:40 +02:00
commit 0a79e786fc
9 changed files with 80 additions and 26 deletions

View file

@ -6,7 +6,7 @@
*AI-generated: all code produced by Claude Code through dialog-driven development.*
![Version](https://img.shields.io/badge/version-0.1.0-blue)
![Version](https://img.shields.io/badge/version-0.2.0-blue)
![Platform](https://img.shields.io/badge/platform-Claude_Code_Plugin-purple)
![Hooks](https://img.shields.io/badge/hooks-1-green)
![Skills](https://img.shields.io/badge/skills-1-orange)
@ -77,7 +77,9 @@ Cross-repo message content is untrusted input by design:
- **No network, no secrets:** everything is local files under your `$HOME`. Credentials never appear in messages, filenames, or frontmatter — there is nothing to leak by transport.
- **Privacy rule:** coordination metadata (repo names, status, architecture) never belongs on a public surface. The mailbox lives outside your repos and stays out of git.
Every guarantee above is pinned by the 48-check selftest, including forgery-resistance regressions.
- **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 64-check selftest, including forgery-resistance regressions.
## The Five Rules
@ -95,19 +97,14 @@ Every guarantee above is pinned by the 48-check selftest, including forgery-resi
## Development
bash scripts/coord-selftest.sh # 48 checks against a throwaway mailbox
bash scripts/coord-selftest.sh # 64 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.
## Hardening Roadmap
Known, accepted gaps queued for a future release:
- Atomic delivery: create the temp file inside the destination directory so the final rename never crosses filesystems.
- Explicit `.`/`..` rejection in the `--reply-to` and `coord-done` name guards (currently stopped downstream by accident, not by the guard).
- Selftest gaps: the default mailbox path (unset `CLAUDE_COORD_DIR`) and malformed frontmatter on the read path are not yet exercised.
- `coord-inbox.sh` lacks `-h`/`--help` and silently ignores unknown arguments; send/done already have uniform help.
Note on argument parsing: `coord-inbox.sh` deliberately ignores unknown
arguments (lenient by design — it runs inside the SessionStart hook and must
never fail a session over a stray flag), while `coord-send.sh` rejects them.
## License