aboutsummaryrefslogtreecommitdiff
path: root/.ai/scripts/cross-agent-comms/cross-agent-send.md
diff options
context:
space:
mode:
Diffstat (limited to '.ai/scripts/cross-agent-comms/cross-agent-send.md')
-rw-r--r--.ai/scripts/cross-agent-comms/cross-agent-send.md199
1 files changed, 0 insertions, 199 deletions
diff --git a/.ai/scripts/cross-agent-comms/cross-agent-send.md b/.ai/scripts/cross-agent-comms/cross-agent-send.md
deleted file mode 100644
index 29bfb24..0000000
--- a/.ai/scripts/cross-agent-comms/cross-agent-send.md
+++ /dev/null
@@ -1,199 +0,0 @@
-# cross-agent-send
-
-**Purpose.** Send a cross-agent message file to a specific destination. Handles
-peer-config lookup, GPG signing, atomic write (same-machine) or rsync push
-(cross-machine), retry-with-backoff, and failure surfacing.
-
-This is the canonical writer. The protocol spec defers all writer mechanics to
-this script.
-
-## Usage
-
-```
-cross-agent-send <destination> <message-file> [--no-sign] [--retries N]
-```
-
-### Positional arguments
-
-| Position | Meaning | Example |
-|---|---|---|
-| 1 | Destination as `<machine>.<project>` | `homelab.career`, `velox.career` |
-| 2 | Message file (already-formatted `.org`) | `/tmp/my-message.org` |
-
-### Flags
-
-| Flag | Default | Purpose |
-|---|---|---|
-| `--no-sign` | (signing on) | Skip GPG signing. Use only for testing; receivers reject unsigned messages by default. |
-| `--retries N` | 3 | Override retry count for cross-machine sends. |
-| `--key <key-id>` | (user's primary key) | GPG key to sign with. Resolution order: `--key` flag, `GPG_USER` env, `git config user.signingkey`, then the first secret key in the keyring. |
-
-## Behavior
-
-### Filename generation (script-controlled)
-
-The script generates the canonical destination filename from the message's
-frontmatter and sender context. The user's input filename is ignored — pass any
-path, the script names the destination correctly:
-
-```
-<UTC-now>T<HHMMSS>Z-from-<sender-slug>-<short-conv-id>.org
-```
-
-`<sender-slug>` comes from the sender machine's project name (config or
-hostname-based). `<short-conv-id>` is read from the message's
-`#+CONVERSATION_ID` frontmatter field. UTC timestamp is generated at send time.
-
-The script also performs the **sender-side max-seen scan** before writing: it
-reads the receiver's `from-agents/` directory, finds the highest existing
-sequence in this conversation across both sender prefixes, and (best-effort)
-suggests `max(seen) + 1` for the next sequence. The user/agent is responsible
-for setting `#+SEQUENCE` in the message body; the script only advises.
-
-### Same-machine destinations
-
-Resolved when the destination's machine matches the current hostname (or is
-not in `peers.toml` as a remote). Steps:
-
-1. Parse frontmatter; extract `CONVERSATION_ID` and `TIMESTAMP`. Validate per
- the *Validation before send* section below.
-2. Generate canonical filename per *Filename generation* above.
-3. Sign: `gpg --detach-sign --armor --output <canonical>.asc --local-user <key> <input>`.
-4. Compute target: read `peers.toml` for the project's `inbox_path`. If
- missing, fall back to `~/projects/<project>/inbox/from-agents/`.
-5. **Atomic write with strict ordering** (signature must precede message):
- - Stage `.asc`: write to `<target>/.tmp.XXXXXX-<canonical>.asc`,
- then `mv` to `<target>/<canonical>.asc`.
- - **Then** stage `.org`: write to `<target>/.tmp.XXXXXX-<canonical>`,
- then `mv` to `<target>/<canonical>`.
- - Receivers only act on `.org` files; staging the `.asc` first guarantees
- the signature is present when the receiver opens the message. Out-of-order
- would race: receiver could read the `.org` before the `.asc` lands and
- fail GPG verify even though the sender did everything right.
-6. Exit 0 on success. Exit non-zero if any step fails.
-
-### Cross-machine destinations
-
-Steps:
-
-1. Parse + generate canonical filename, as same-machine steps 1-2.
-2. Sign locally to `<input>.asc` (or a tmp staging file).
-3. rsync push **with the same .asc-first ordering**:
- - `rsync -a <input>.asc <ssh-user>@<host>:<inbox_path>/<canonical>.asc`
- - **Then** `rsync -a <input> <ssh-user>@<host>:<inbox_path>/<canonical>`
- rsync writes to a hidden temp file then renames atomically by default
- (`--inplace` would defeat this; do not pass it).
-4. Retry on failure: 5s, 30s, 120s backoff, then surface error.
-5. On persistent failure: write a marker file to
- `~/.local/state/cross-agent-comms/failed-sends/<timestamp>-<dest>-<canonical>.json`
- containing the destination, message path, error, and retry log. Exit non-zero.
-
-### Validation before send
-
-- Destination resolves via `peers.toml` (or local fallback). If neither, exit
- immediately with `destination not found in peers.toml; available: <list>`.
-- Message file must be readable, non-empty, and have valid org-mode frontmatter
- with **all** of the following required fields:
- - `#+TITLE`
- - `#+CONVERSATION_ID`
- - `#+MESSAGE_TYPE`
- - `#+SEQUENCE`
- - `#+TIMESTAMP`
- - `#+PROTOCOL_VERSION` (must equal `5` for v5)
-
- If any required field is missing or malformed, exit immediately with a parse
- error naming the offending field.
-
-- Optional fields the script recognizes and passes through (no special
- handling beyond preservation):
- - `#+REQUIRES_TOOLS` — comma-separated tool/MCP slugs the receiver needs.
- - `#+RELEASE_STATUS` — valid only on `MESSAGE_TYPE: release`. Values per
- spec: `complete`, `cancelled`, `withdrawn-after-pushback`,
- `abandoned-after-escalation`.
- - `#+WORKFLOW_VERSION` — sender's version of the cross-agent-comms workflow
- file. Currently advisory; receiver may warn on mismatch but does not block.
-
-## Configuration
-
-Reads `~/.config/cross-agent-comms/peers.toml` for peer routing:
-
-```toml
-[peers.velox]
-host = "velox.local"
-ssh_user = "cjennings"
-
-# Optional: per-project inbox-path overrides for non-default layouts.
-[projects.work]
-inbox_path = "~/projects/work/inbox/from-agents"
-
-[projects.homelab]
-inbox_path = "~/projects/homelab/inbox/from-agents"
-```
-
-If a project entry is omitted, defaults to `~/projects/<project>/inbox/from-agents`.
-
-## Failure modes
-
-| Symptom | Cause | Fix |
-|---|---|---|
-| `destination not found in peers.toml` | Misspelled destination, or peer not configured | Run `cross-agent-discover` to see available destinations. |
-| `signing failed: no secret key` | GPG key missing or not in keyring | `gpg --list-secret-keys` to confirm. Override with `--key <id>`. |
-| `signing failed: pinentry timed out` | Headless session, GUI pinentry unavailable | Confirm `pinentry-program` in `gpg-agent.conf` matches available pinentry. Per protocols.org, GUI pinentry works from Claude Code. |
-| `rsync exit 255` | SSH unreachable | `cross-agent-discover --peer <name>` to confirm reachability. |
-| `rsync exit 23` | Permission denied at destination | Check destination directory perms (`chmod 700`) and ownership. |
-| Marker file written to `failed-sends/` | Persistent cross-machine failure | Inspect the marker's `error` field. After fixing, retry: `cross-agent-send <dest> <msg>` (the marker is for visibility; it does not auto-retry). |
-| Receiver complains "unsigned message" | `--no-sign` was used in production | Don't use `--no-sign` outside testing. |
-
-## HALT awareness
-
-Checks `~/.config/cross-agent-comms/HALT` at the start of every send AND
-between the `.asc` and `.org` rsync calls AND between each retry iteration.
-On HALT exists, exits with code 5 ("halt active; remove
-~/.config/cross-agent-comms/HALT to resume") without writing or pushing
-further.
-
-Worst case: one in-flight send completes its current rsync step within a few
-seconds before halt kicks in for the next step. New sends are blocked
-immediately. No `pkill` needed — the per-iteration check stops things
-naturally.
-
-If the HALT file exists but is unreadable (permissions wrong), fail-closed —
-treat as if HALT is set. Safer than fail-open.
-
-See `cross-agent-halt.md` for the full halt mechanism.
-
-## Examples
-
-```bash
-# Same-machine send
-cross-agent-send homelab.career /tmp/my-message.org
-
-# Cross-machine send via Tailscale
-cross-agent-send velox.career /tmp/my-message.org
-
-# Test send without signing (receiver will reject)
-cross-agent-send homelab.career /tmp/test.org --no-sign
-
-# Override retry count for a flaky link
-cross-agent-send velox.career /tmp/my-message.org --retries 10
-
-# After a delivery failure, inspect the marker
-cat ~/.local/state/cross-agent-comms/failed-sends/*.json | jq .
-```
-
-## Exit codes
-
-| Code | Meaning |
-|---|---|
-| 0 | Sent successfully. |
-| 1 | General error (parse failure, signing failure, etc.). |
-| 2 | Destination not found in peers.toml. |
-| 3 | Cross-machine delivery failed after retries. Marker file written. |
-| 4 | Frontmatter validation failed. |
-
-## See also
-
-- `cross-agent-discover` — validate destinations before sending.
-- `cross-agent-watch` — receiver-side notification.
-- `cross-agent-status` — see what's queued.
-- `cross-agent-comms.org` — protocol spec, the "what" the script implements.