|
| 1 | +// SPDX-License-Identifier: PMPL-1.0-or-later |
| 2 | +// Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk> |
| 3 | + |
| 4 | += local-coord-mcp message envelope — design rationale |
| 5 | +:author: Jonathan D.A. Jewell |
| 6 | +:date: 2026-04-20 |
| 7 | +:toc: macro |
| 8 | +:version: v1 |
| 9 | + |
| 10 | +Companion document to `schemas/coord-messages.ncl`. |
| 11 | +Architectural memory: `project_coord_supervision_architecture.md`. |
| 12 | + |
| 13 | +toc::[] |
| 14 | + |
| 15 | +== Motivation |
| 16 | + |
| 17 | +The first version of local-coord-mcp treated messages as opaque strings. |
| 18 | +That worked for a toy two-peer ping but doesn't survive a real multi-agent |
| 19 | +workspace with mixed-trust peers (Claude + vibe + codex + gemini), where |
| 20 | +one agent is prone to confabulation ("Gemini is often nuts") and the user |
| 21 | +needs a firewall between runaway outputs and the system. |
| 22 | + |
| 23 | +The envelope defined here: |
| 24 | + |
| 25 | +* Makes every message typed, routable, and auditable. |
| 26 | +* Carries enough metadata for the server to gate risky ops without |
| 27 | + interrupting free ones. |
| 28 | +* Uses Byzantine-style cross-verification to catch the specifically |
| 29 | + dangerous failure modes of a low-trust peer. |
| 30 | +* Keeps the supervisor (Opus + user) as the single locus of attention |
| 31 | + while the other agents work in parallel. |
| 32 | + |
| 33 | +== Design principles |
| 34 | + |
| 35 | +. *Proportional gating.* Tier 0/1 flow free, Tier 2 light auto-approve, |
| 36 | + Tier 3 hard gate, Tier 4 schema-level reject. Coordination overhead |
| 37 | + should never be the rate-determining step for routine work. |
| 38 | +. *Awareness scales with blast radius.* Drone behaviour on small ops, |
| 39 | + strategic awareness on big ones. Enforced by requiring |
| 40 | + `context_fetch_id` on Tier 2+. |
| 41 | +. *Never trust self-assessment alone.* Self-declared confidence is a |
| 42 | + signal, never a decision. Server cross-checks against track record |
| 43 | + and, for Tier 2+ from supervised peers, requires an independent |
| 44 | + attester. |
| 45 | +. *Supervision without bottleneck.* Auto-approve where safe, batch |
| 46 | + where feasible, preserve Opus attention for genuine judgement calls. |
| 47 | +. *Single user locus.* All agent-to-user traffic filters through the |
| 48 | + supervisor terminal. Supervised peers never reach the user directly. |
| 49 | + |
| 50 | +== Trust tiers |
| 51 | + |
| 52 | +|=== |
| 53 | +| Role | Assigned to | Default for client_kind |
| 54 | + |
| 55 | +| `supervisor` | Opus | (explicit only) |
| 56 | +| `executor` | Claude Sonnet / Haiku | `claude` |
| 57 | +| `supervised` | vibe, codex, gemini | `gemini`, `copilot`, `custom` |
| 58 | +|=== |
| 59 | + |
| 60 | +`supervisor` must be requested explicitly on register; the server |
| 61 | +SHOULD restrict the role to Opus-tier Claude clients. |
| 62 | + |
| 63 | +== Risk ladder |
| 64 | + |
| 65 | +|=== |
| 66 | +| Tier | Examples | Gate |
| 67 | + |
| 68 | +| 0 | status, query, read, peer discovery, context_query | none |
| 69 | +| 1 | single-file edit (non-critical), test, lint, fyi, release | logged |
| 70 | +| 2 | multi-file edit, dep bump, new file, claim (local/repo) | auto-approve 60s |
| 71 | +| 3 | commit, push, mirror, secret-adjacent, deletion, schema migration | supervisor approval, no timeout |
| 72 | +| 4 | force-push, branch delete, public-repo create, license change, always-private-repo touch | schema-level reject for `supervised` |
| 73 | +|=== |
| 74 | + |
| 75 | +== Byzantine safety mechanisms |
| 76 | + |
| 77 | +Five layers, each catching a specific failure mode: |
| 78 | + |
| 79 | +=== 1. Hash chain per sender — catches retroactive rewriting |
| 80 | + |
| 81 | +Each envelope's `prev_msg_hash` is the SHA-256 of the sender's previous |
| 82 | +envelope. First-ever message uses 64 zeros. Server tracks chain head per |
| 83 | +peer; any break is an instant reject. Cost: 32 bytes per message. |
| 84 | + |
| 85 | +=== 2. M-of-N attestation — catches unilateral bad Tier 2+ ops |
| 86 | + |
| 87 | +Before a Tier 2+ envelope from `role=supervised` executes, server routes |
| 88 | +`attest_req` in parallel to a second executor (Sonnet or Codex). That |
| 89 | +executor independently reads the payload, HMAC-signs the envelope with |
| 90 | +its session token, and returns `attest_resp`. Opus sees both the |
| 91 | +supervise_req and the attestation when deciding. |
| 92 | + |
| 93 | +This is PBFT-lite for *f=1* byzantine tolerance. |
| 94 | + |
| 95 | +=== 3. Watchdog claims — catches claim-squatting |
| 96 | + |
| 97 | +Every `claim` op has a TTL. Claimant must emit `progress` or `release` |
| 98 | +before expiry or server auto-releases the claim. |
| 99 | + |
| 100 | +* `supervised`: 30s TTL |
| 101 | +* `executor`: 5min TTL |
| 102 | +* `supervisor`: unlimited |
| 103 | + |
| 104 | +=== 4. Content sanity gate — catches confabulation + tier underclaiming |
| 105 | + |
| 106 | +Server runs heuristics over payload content: |
| 107 | + |
| 108 | +* File references validated against recent-FS-state cache — nonexistent |
| 109 | + path = reject. |
| 110 | +* Self-contradiction detector (simple heuristic within payload). |
| 111 | +* Risk-tier escalator — content matching `sanity_auto_promote` patterns |
| 112 | + forces the minimum tier regardless of declared value. |
| 113 | + |
| 114 | +=== 5. Tamper-evident audit log — forensic recovery |
| 115 | + |
| 116 | +Every envelope + every supervision decision logged to VeriSimDB with |
| 117 | +hash-chain continuity. Even when a bad message slips through, post-hoc |
| 118 | +audit reconstructs exactly what happened and who signed what. |
| 119 | + |
| 120 | +== Self-assessment — four layers |
| 121 | + |
| 122 | +.Ranked by strength of evidence |
| 123 | +|=== |
| 124 | +| Layer | Source | Catches |
| 125 | + |
| 126 | +| A. Static affinity | Peer declares on register | Coarse mismatch |
| 127 | +| B. Per-claim confidence | Peer self-reports on `claim` | Laziness / overreach |
| 128 | +| C. Track record | Server-computed from outcomes | Pattern miscalibration |
| 129 | +| D. Drift detector | Server alerts on confidence vs track-record gap | Specific overclaim ("Gemini is nuts" case) |
| 130 | +|=== |
| 131 | + |
| 132 | +Tie-break on concurrent claims = `max(sender_confidence * effective_affinity)`. |
| 133 | +The second factor is server-attached and cannot be forged. |
| 134 | + |
| 135 | +== Adaptive awareness |
| 136 | + |
| 137 | +`context_fetch_id` is the load-bearing field here. Required for Tier 2+ |
| 138 | +envelopes. Obtained via `context_query` + `context_reply`. Forces every |
| 139 | +high-risk action to be preceded by a context read. |
| 140 | + |
| 141 | +Context summary depth scales with requester role: |
| 142 | + |
| 143 | +* `supervised` → summary only (never raw state — prevents hallucinated |
| 144 | + connections). |
| 145 | +* `executor` → summary by default, raw on request. |
| 146 | +* `supervisor` → full raw on request, always. |
| 147 | + |
| 148 | +== User-interaction routing |
| 149 | + |
| 150 | +Three op_kinds for agent-to-user communication: |
| 151 | + |
| 152 | +[cols="1,2,2",options="header"] |
| 153 | +|=== |
| 154 | +| op_kind | Behaviour | Allowed setters |
| 155 | + |
| 156 | +| `fyi` | Log-only. Never interrupts. | any role |
| 157 | +| `clarify` | Batch through supervisor terminal. `urgent_direct` breakout allowed for executor/supervisor. | any role (supervised can't set urgent_direct) |
| 158 | +| `blocker` | Inline interrupt on supervisor terminal. Stops the task. | any role |
| 159 | +|=== |
| 160 | + |
| 161 | +The supervisor (Opus) deduplicates near-identical questions from multiple |
| 162 | +peers, synthesises with context, and relays to the user in natural |
| 163 | +language. Rejected confabulated questions from supervised peers never |
| 164 | +reach the user. |
| 165 | + |
| 166 | +== op_kind quick reference |
| 167 | + |
| 168 | +[cols="1,1,3",options="header"] |
| 169 | +|=== |
| 170 | +| op_kind | Default tier | Purpose |
| 171 | + |
| 172 | +| status | 0 | Current work status, visible via list_peers |
| 173 | +| query | 0 | Read-only Q&A between peers |
| 174 | +| context_query | 0 | Request big-picture context (required for Tier 2+) |
| 175 | +| context_reply | 0 | Response to context_query |
| 176 | +| supervise_req | 0 | Supervised peer requests gated-op approval |
| 177 | +| supervise_resp | 0 | Supervisor decides approve/reject |
| 178 | +| attest_req | 0 | Server requests independent attestation (BFT) |
| 179 | +| attest_resp | 0 | Executor attests/rejects |
| 180 | +| fyi | 1 | Log-only observation |
| 181 | +| progress | 1 | Task percent + note |
| 182 | +| warn_drift | 1 | Rule / invariant / expected-state drift |
| 183 | +| release | 1 | Release claim with outcome |
| 184 | +| claim | 2 | Claim task, affinity-routed (3 when scope=estate) |
| 185 | +| handoff | 2 | Transfer task to peer (3 when scope=estate) |
| 186 | +| clarify | 2 | Question for user, batched |
| 187 | +| blocker | 3 | Peer cannot proceed without user |
| 188 | +| gated_op | 3 | Wrapper for arbitrary high-risk action |
| 189 | +|=== |
| 190 | + |
| 191 | +== What this does NOT cover |
| 192 | + |
| 193 | +* *Cross-machine transport* — this envelope is localhost-only. Umoja |
| 194 | + federation (separate design) handles multi-host. |
| 195 | +* *Model attestation* — peer identity is `(client_kind, session token, |
| 196 | + suffix)`. No proof the peer is actually the model it claims. |
| 197 | + Out-of-scope until we have hardware attestation. |
| 198 | +* *Privacy between peers* — all messages are server-visible. Encrypted |
| 199 | + peer-to-peer is a later addition (not needed for local use). |
| 200 | + |
| 201 | +== Implementation sequence |
| 202 | + |
| 203 | +. Schema + design doc (this commit). |
| 204 | +. Supervision tier + role field in FFI (Task #5). |
| 205 | +. Supervisor tools: coord_review / coord_approve / coord_reject (Task #6). |
| 206 | +. VeriSimDB sidecar for durable state + track record (Task #7). |
| 207 | +. E2E test: 2-instance with supervisor gate + durability (Task #8). |
| 208 | + |
| 209 | +Only after those land do we wire the full envelope into the FFI's |
| 210 | +coord_send path. Today's coord_send accepts raw strings; in the |
| 211 | +transition period, strings without the envelope shape are treated as |
| 212 | +implicit `{op_kind: "query", risk_tier: 0}` for backward compatibility. |
| 213 | + |
| 214 | +== References |
| 215 | + |
| 216 | +* Nickel source: `../schemas/coord-messages.ncl` |
| 217 | +* JSON export: `../schemas/coord-messages.json` |
| 218 | +* Supervision architecture memory: `memory/project_coord_supervision_architecture.md` |
| 219 | +* Cartridge manifest: `../cartridge.ncl` |
| 220 | +* ADR-0006 (five-symbol cartridge ABI): `boj-server/docs/adr/ADR-0006.adoc` |
0 commit comments