aboutsummaryrefslogtreecommitdiff
path: root/.ai/scripts/cross-agent-comms/cross-agent-recv.md
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-06 21:59:52 -0500
committerCraig Jennings <c@cjennings.net>2026-05-06 21:59:52 -0500
commitd81b23ad6b6e437dfe3c338a00a4be39bc555146 (patch)
tree2d4b0d7890fd1fc70d81282b81fed2808c28a106 /.ai/scripts/cross-agent-comms/cross-agent-recv.md
parent201377f57430ef28d02e703a2191434bbee55c75 (diff)
downloadrulesets-d81b23ad6b6e437dfe3c338a00a4be39bc555146.tar.gz
rulesets-d81b23ad6b6e437dfe3c338a00a4be39bc555146.zip
chore(ai): initialize project notes and Claude tooling surfaces
Replace the seed notes.org with project-specific context (layout, install modes, task tracker location, recent inflection point). Bring in the synced template surfaces (protocols, workflows, scripts, references, retrospectives, someday-maybe) as tracked content for this content/documentation project.
Diffstat (limited to '.ai/scripts/cross-agent-comms/cross-agent-recv.md')
-rw-r--r--.ai/scripts/cross-agent-comms/cross-agent-recv.md218
1 files changed, 218 insertions, 0 deletions
diff --git a/.ai/scripts/cross-agent-comms/cross-agent-recv.md b/.ai/scripts/cross-agent-comms/cross-agent-recv.md
new file mode 100644
index 0000000..247a27a
--- /dev/null
+++ b/.ai/scripts/cross-agent-comms/cross-agent-recv.md
@@ -0,0 +1,218 @@
+# cross-agent-recv
+
+**Purpose.** The canonical receiver-side processor. Reads a single incoming
+message file and reports a structured decision the agent acts on:
+process / dedup / query / reject.
+
+The script handles only mechanical checks (frontmatter, signature, dedup,
+version, tools). Substance-level decisions like `pushback` ("I disagree with
+this request") happen one layer up — after the agent reads the message body
+the script returns as `process`-able.
+
+This is the read-side counterpart to `cross-agent-send`. Together they are the
+two halves of the per-message contract. The agent's polling loop calls
+`cross-agent-recv` on every new file in `inbox/from-agents/` and dispatches on
+the decision.
+
+Without this script, every receiver implementation re-invents GPG verify +
+frontmatter sanity-check + SHA-256 dedup. With it, behavior is consistent
+across projects.
+
+## Usage
+
+```
+cross-agent-recv <message-file>
+```
+
+Single positional argument: a `.org` file in `inbox/from-agents/`. The matching
+`.asc` signature file must be present alongside it.
+
+### Flags
+
+| Flag | Default | Purpose |
+|---|---|---|
+| `--no-verify` | (verify on) | Skip GPG verification. Testing only. |
+| `--no-dedup` | (dedup on) | Skip SHA-256 dedup against existing files. Testing only. |
+| `--protocol-version <N>` | 5 | Override the expected protocol version. Useful for testing forward-compatibility checks. |
+| `--json` | off | Output decision as JSON for easier parsing by the agent. |
+
+## Behavior
+
+Runs the receiver checks in order. First failure determines the decision.
+
+### Step 1 — Frontmatter sanity-check
+
+Parse the message's org-mode frontmatter. Required fields:
+
+- `#+TITLE`
+- `#+CONVERSATION_ID`
+- `#+MESSAGE_TYPE` (must be one of: `request`, `progress`, `query`, `pushback`,
+ `complete`, `release`, `escalate`)
+- `#+SEQUENCE` (integer)
+- `#+TIMESTAMP` (ISO 8601 with explicit offset)
+- `#+PROTOCOL_VERSION` (must match the expected version; default 5)
+
+Any required field missing, malformed, or the protocol version mismatched →
+decision = `reject` (frontmatter) or `query` (version mismatch — see below).
+
+### Step 2 — Protocol-version check
+
+If `PROTOCOL_VERSION` doesn't match the expected:
+
+- Decision = `query`. Action: receiver should write a `query` reply asking the
+ sender to upgrade to the expected protocol version.
+
+### Step 3 — Signature verification
+
+Look for `<message-file>.asc` alongside the `.org`. If missing or `gpg
+--verify` fails:
+
+- Decision = `reject` (signature). Surface to user; do not act.
+
+The `.asc` file MUST be present when the `.org` is — `cross-agent-send`
+guarantees this with its strict ordering (`.asc` lands first). If the `.asc`
+is missing despite the `.org` being present, the sender violated atomic-write
+ordering or the file was tampered with in transit.
+
+### Step 4 — SHA-256 dedup
+
+Compute SHA-256 of the message file. Scan the same directory for existing
+files matching `CONVERSATION_ID + SEQUENCE`:
+
+- No match → decision = `process` (new message, dispatch by type).
+- Match with **identical** SHA-256 → decision = `dedup` (silent retry; do not
+ reprocess).
+- Match with **different** SHA-256 → decision = `process` (sequence collision
+ with non-identical content; both are legitimate, ordered by `#+TIMESTAMP`).
+
+### Step 5 — REQUIRES_TOOLS optional check
+
+If the message has a `#+REQUIRES_TOOLS` field, verify each named tool/MCP is
+available in the receiver's environment.
+
+- All available → `process`.
+- One or more missing → decision = `query`. The agent should write a `query`
+ reply naming the missing tools, asking the sender to reframe the request to
+ avoid them.
+
+### Step 6 — Dispatch decision
+
+If all checks pass, decision = `process` with the parsed `MESSAGE_TYPE` so the
+agent's main loop knows which handler to invoke.
+
+## Output
+
+### Default (human-readable)
+
+```
+$ cross-agent-recv inbox/from-agents/20260427T091015Z-from-homelab-prep-fixup.org
+decision: process
+message_type: request
+conversation_id: prep-fixup
+sequence: 6
+sha256: a1b2c3d4...
+```
+
+### `--json`
+
+```json
+{
+ "decision": "process",
+ "reason": null,
+ "message_type": "request",
+ "conversation_id": "prep-fixup",
+ "sequence": 6,
+ "timestamp": "2026-04-27T04:11:42-05:00",
+ "sha256": "a1b2c3d4..."
+}
+```
+
+For decisions other than `process`, `reason` carries a human-readable
+explanation:
+
+```json
+{
+ "decision": "query",
+ "reason": "PROTOCOL_VERSION mismatch: expected 5, got 4",
+ "conversation_id": "prep-fixup",
+ "sequence": 6
+}
+```
+
+## Decision exit codes
+
+| Decision | Exit code | Agent action |
+|---|---|---|
+| `process` | 0 | Dispatch to the message-type handler |
+| `dedup` | 1 | Silent — do nothing further |
+| `query` | 2 | Write a `query` reply (see `reason` for what to ask) |
+| `reject` | 3 | Surface to user; do not auto-reply |
+
+The agent reads stdout/JSON to learn the decision; it can also key off exit
+code for simpler bash-style dispatching.
+
+## Failure modes
+
+| Symptom | Cause | Fix |
+|---|---|---|
+| `decision: reject (frontmatter)` | Required field missing or malformed | Open the message; fix or surface to user. The sender should not have produced this file. |
+| `decision: reject (signature)` | `.asc` missing, GPG verify failed, or signer unknown | Check that `.asc` exists alongside `.org`. If yes, run `gpg --verify <msg>.asc <msg>` manually for diagnostic output. |
+| `decision: query (PROTOCOL_VERSION)` | Sender on older/newer protocol | Reply with a `query` asking sender to upgrade. Both sides should align before continuing. |
+| `decision: query (REQUIRES_TOOLS)` | Receiver lacks one of the named tools | Reply with a `query` naming the missing tools; sender should reframe to avoid. |
+| `decision: dedup` | Already-processed identical retry | No action. The script handled it correctly. |
+
+## HALT awareness
+
+Checks `~/.config/cross-agent-comms/HALT` at the start of every invocation. If
+HALT exists, exits with code 5 ("halt active; remove
+~/.config/cross-agent-comms/HALT to resume") without verifying, deduping, or
+returning a decision.
+
+**The inbound file is left in place** — not moved, not rejected, not
+deduped. When HALT clears and polling resumes, the file gets picked up via
+the normal cold-start handling (whichever surfaces first: watcher
+notification, startup workflow check, or the next agent poll). Reversibility
+is preserved.
+
+If the HALT file exists but is unreadable, fail-closed — treat as if HALT is
+set.
+
+See `cross-agent-halt.md` for the full halt mechanism.
+
+## Examples
+
+```bash
+# Basic invocation in an agent's polling loop
+for msg in inbox/from-agents/*.org; do
+ decision=$(cross-agent-recv --json "$msg")
+ case "$(echo "$decision" | jq -r '.decision')" in
+ process) handle_message "$msg" ;;
+ dedup) ;; # silent
+ query) write_query_reply "$msg" "$decision" ;;
+ reject) surface_to_user "$msg" "$decision" ;;
+ esac
+done
+
+# Test signature verification only
+cross-agent-recv --no-dedup inbox/from-agents/test-msg.org
+
+# Test against a future protocol version
+cross-agent-recv --protocol-version 6 inbox/from-agents/future-msg.org
+```
+
+## Performance
+
+The script is fast (single SHA-256 compute, single GPG verify, frontmatter
+parse). For typical messages (single-digit KB), runs in well under 100ms.
+Dedup-scan is O(N) over files in the directory; if a project's
+`inbox/from-agents/` accumulates hundreds of files, archive released
+conversations to keep the scan fast.
+
+## See also
+
+- `cross-agent-send` — counterpart writer.
+- `cross-agent-watch` — fires when a new message arrives; agent then calls
+ `cross-agent-recv` to process it.
+- `cross-agent-status` — pending-message snapshot (uses similar
+ released-vs-unreleased logic, but doesn't process individual messages).
+- `cross-agent-comms.org` — protocol spec, the "what" the script implements.