diff options
| author | Craig Jennings <c@cjennings.net> | 2026-05-06 21:59:52 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-05-06 21:59:52 -0500 |
| commit | d81b23ad6b6e437dfe3c338a00a4be39bc555146 (patch) | |
| tree | 2d4b0d7890fd1fc70d81282b81fed2808c28a106 /.ai/scripts/cross-agent-comms/cross-agent-recv.md | |
| parent | 201377f57430ef28d02e703a2191434bbee55c75 (diff) | |
| download | rulesets-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.md | 218 |
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. |
