{
  "schema_version": "0.1",
  "brainstorm_id": "BR-2026-07-18-addressed-request-routing",
  "title": "Addressed request routing: cross-boundary agent messaging between repos (the transport every PRD repo has, not a replacement for workspace chat)",
  "created_at": "2026-07-18",
  "author_agent": "AGENT-CLAUDE",
  "source_session": "SES-CLAUDE-TRANSPORT",
  "status": "open",
  "context": "User proposal after the resolution-loop work (REQ-124/125/126): 'what if request intakes could be tagged with the repo the request is for, so agents can send request messages to repos for their agents to read'. Grounded in the as-built transport read 2026-07-18. TODAY IS A HARD-WIRED STAR: request_autosubmit.resolve_hub() reads exactly ONE destination (PRD_UPSTREAM_HUB env or config.requests.upstream_hub_path) and _is_hub() REJECTS any destination that is not a prd-plugin hub checkout. Every submission therefore flows downstream->hub and back; a repo cannot address a peer at all. Vestigial evidence the idea was always intended: historical records (REQ-005, REQ-030) already carry a descriptive target_repo field that nothing routes on. What ALREADY generalizes: per-repo scoped transport directories (inbox/<origin>/incoming/, mailboxes/<repo>/), origin_repo + source_request_id provenance, the duplicate-safe prd_import_request tool, the request.import workflow, and the now-complete delivery loop (auto-pull at session start, source-id resolution, outbox pruning, mailbox consumption). Peer routing is therefore mostly a ROUTING and DIRECTORY problem, not a transport rewrite. OWNER REFINEMENT 2026-07-18 (audience and boundary): this is FOR repo-scoped host agents that have no shared live channel - Claude Desktop, Codex Desktop, opencode - and for AI-Collab-v3 spawned agents ONLY when addressing a repo outside their workspace. It must NOT replace AI-Collab-v3's internal chat for agents inside that workspace. Grounded: repository.workspace is ALREADY a declared field in the shipped service-manifest schema, and AI-Collab-v3 is the only workspace repo that populates it (workspace='ai-collab-v3'); prd-plugin, GRAPH_LANG, Asset-Foundry, visual-context-engine and AI-Collab-Desktop-Bridge all leave it empty. The owner's boundary is therefore a DETERMINISTIC PREDICATE on existing data, not a judgment call. Repo discovery also already exists in wiki_html_export.discover_repos() (scans the workspace root by convention marker), but it keys on wiki/index.md rather than a PRD install, so a messaging destination list needs its own predicate.",
  "goals": [
    "BR-REQ-037: Add an addressed destination to intake — target_repo (the repo the request is FOR) alongside origin_repo (where it came from) — and route delivery on it instead of always resolving the hub. Absent target_repo keeps today's behavior exactly (plugin-about requests go to the hub), so the star topology remains the default, not a special case.",
    "BR-REQ-038: Resolve a target repo through a DECLARED directory, never a filesystem guess. Two candidate sources already exist: the service manifest's declared relationships (consumes[].provider / provides[].consumer name real peer repo ids) and an explicit config map (requests.peers: {repo-id: path}). A repo may only be addressed if it is declared — an agent must not be able to spray requests into arbitrary directories.",
    "BR-REQ-039: Relax the destination gate from 'is the prd-plugin hub' to 'is a PRD-enabled repo' (has .prd_plugin/ and an inbox), keeping the hub as one peer among many rather than a hardcoded singleton.",
    "BR-REQ-040: Give every repo the inbound half the hub already has: session.start (or a request.inbox workflow) surfaces newly delivered inbox packages so the receiving repo's agent sees 'another repo asked us for something' the same way it now sees delivered resolutions. Import stays a judgment step — an inbox package is a PROPOSAL, never auto-promoted into canonical state.",
    "BR-REQ-041: Make the resolution loop symmetric. The REQ-124/125/126 machinery (upstream reply, source-id resolution, auto-pull, residue pruning) is already generic; it must answer the ADDRESSER whoever they are, so peer-to-peer requests cannot strand the way upstream ones did.",
    "BR-REQ-042: Address-aware reporting: message_check and /prd-status distinguish 'requests we sent and are awaiting an answer on' (by target) from 'requests other repos sent us' (by origin), so neither direction hides.",
    "BR-REQ-043: Classification stays explicit for peers. prd_file_request auto-classifies only the plugin-about case today; guessing WHICH peer a request belongs to is a judgment an agent should state, not a heuristic — target_repo is supplied, not inferred.",
    "BR-REQ-044: `destinations` tool - list every addressable repo with, per entry: repo id, path, whether it has PRD installed, its declared workspace, whether it is a workspace peer of the caller (same non-empty workspace), and therefore the RECOMMENDED CHANNEL (substrate/chat for workspace peers, prd messaging for everyone else). One call answers 'who can I message and how'.",
    "BR-REQ-045: `messages check` tool - inbound view for the receiving repo: messages addressed to us, unread first, with sender, subject, and age; distinct from today's transport-health message_check (which answers 'is my transport clean', not 'what did someone send me').",
    "BR-REQ-046: `messages reply` tool - reply to an addressed message, routed back to the sender repo through the same declared directory, reusing the resolution loop verified in REQ-124/125/126.",
    "BR-REQ-047: `messages status` tool - mark read / actioned / archived. Message read-state does NOT exist today (MSG-* records carry visibility but no lifecycle), so this is a genuine new primitive, and it is what keeps a shared inbox usable across several agents and sessions.",
    "BR-REQ-048: Channel-selection guard - when the caller and target declare the same non-empty workspace AND a live substrate is enabled, the messaging tools REFUSE (or loudly warn) and point at the workspace chat instead. The boundary is enforced by the tool, not left to agent discretion, so this can never quietly become a second in-workspace chat.",
    "BR-REQ-049: Repo identity hygiene - most manifests still carry repository.id 'auto' and an empty workspace. Addressed routing needs a stable id per repo, so the installer/audit should resolve 'auto' to the real repo id and prompt for workspace membership where it matters."
  ],
  "non_goals": [
    "A message bus, chat, or live agent-to-agent protocol. This is file-based, durable, review-gated request transport — the existing mechanism addressed to more than one destination.",
    "Auto-accepting inbound peer requests into canonical state. The proposal/import boundary is the safety property; it does not move.",
    "Replacing AI-Collab's workspace messaging or the Conduit substrate. Where those are present they remain the live coordination layer; this is the durable, offline-capable fallback every PRD repo already has.",
    "Cross-machine or networked delivery. Local declared paths only, as today."
  ],
  "decisions_proposed": [
    "BR-DEC-025: Keep the hub special-cased ONLY as a default, not as a type. A request with no target_repo behaves exactly as today (plugin-about -> hub); target_repo is purely additive, so no existing flow changes behavior.",
    "BR-DEC-026: Declaration is the consent. Writing into a peer's inbox is permitted only for repos declared in the service manifest relationships or requests.peers. This mirrors how configuring upstream_hub_path is today's consent to deliver to the hub, and keeps the 'never edit another repo you were not pointed at' floor intact — a declared inbox is a mailbox, not the peer's canonical state.",
    "BR-DEC-027: Inbound remains proposal-only. Delivery into a peer's inbox never mutates that repo's requests.json; only its own agent, running its own intake, imports. Automation may surface and triage, never self-accept.",
    "BR-DEC-028: THE BOUNDARY RULE - PRD messaging is the CROSS-boundary transport; the workspace substrate is the IN-boundary transport. Two repos declaring the same non-empty repository.workspace are workspace peers and must use their live channel; everyone else uses durable file transport. Rationale: inside a workspace a live chat is strictly better (presence, immediacy, threads) and a second mechanism would fragment coordination; across boundaries there IS no shared live channel, and .prd_plugin is the only substrate every PRD repo already has.",
    "BR-DEC-029: Primary audience is the repo-scoped host agent (Claude Desktop, Codex Desktop, opencode) - one agent, one repo, no presence, no shared bus. The feature is designed for that case first; AI-Collab spawned agents are a secondary, boundary-limited consumer.",
    "BR-DEC-030: Read-state is REPO-scoped, not agent-scoped. A repo's agents share canonical state, so one agent marking a message read marks it read for the repo - matching how requests already work, and avoiding per-agent read matrices that would need agent identity to be durable.",
    "BR-DEC-031: Messaging rides the REQUEST record, not a parallel message store. An addressed message IS a request (or a reply on its thread) with target_repo set; MSG-* records gain lifecycle state. No second schema, no second transport, no second loop to leave half-closed.",
    "BR-DEC-032: Resolves BR-Q-011. The channel guard WARNS and proceeds; it never hard-refuses an in-workspace send. An in-workspace message crosses no boundary (same workspace, same substrate, no new surface, no secret exposure), so refusal buys no security while blocking the legitimate case of an in-workspace agent wanting a durable auditable request rather than ephemeral chat. Workspace membership is DERIVED from the service manifest, so a stale or missing `workspace` field would turn a hard refusal into an unoverridable block on legitimate traffic - the exact failure direction this project has repeatedly been burned by. Hard refusal also strictly costs more: it forces an override mechanism plus override auditing. The guard emits a named finding recommending the workspace's own chat and delivers anyway.",
    "BR-DEC-033: Corollary of BR-DEC-032. Hard refusal belongs on the genuinely exposed boundary - writing into an UNDECLARED peer's inbox (BR-DEC-026, BR-Q-008/BR-Q-009) - not on intra-workspace traffic, which is strictly less exposed. The guard as originally framed pointed its strictness at the wrong boundary.",
    "BR-DEC-034: Resolves BR-Q-013. A message to a declared repo with no active agent QUEUES DURABLY and is never aged out. Delivery and being read are different things (established by REQ-133): whether an agent ever shows up is not the transport's problem to solve by deletion. A dormant repo may become active later, and time-based destructive deletion contradicts both the method's no-timescales rule and the storage contract accepted from ai-collab-v3 this session (rotation never deletes unacknowledged data). Expiry would also require clock-dependent tests, which this codebase deliberately avoids.",
    "BR-DEC-035: The black-hole concern behind BR-Q-013 is a VISIBILITY problem, not a retention problem, and is solved on the sender's side: message_check reports messages delivered but never acknowledged, so the sender sees that a peer is silent without the message ever being destroyed. General principle carried from the REQ-133/REQ-134 transport work: never solve a visibility problem with a destructive or blocking mechanism."
  ],
  "open_questions": [
    "BR-Q-007: Directory source of truth — service manifest relationships (already declared, semantically meaningful, but scoped to SERVICE dependencies) or an explicit requests.peers map (simpler, but a second registry to maintain)? Union of both, with the manifest preferred?",
    "BR-Q-008: Must addressing be MUTUAL? Should repo B have to declare A before A may write into B's inbox, or is A's declaration of B sufficient? Mutual is safer and prevents unsolicited traffic; one-sided is far more usable.",
    "BR-Q-009: What happens when the target repo is unreachable or undeclared — hold in outbox with a named finding (consistent with today's delivery backstop), or refuse the filing outright?",
    "BR-Q-012: Is 'workspace' the right boundary unit, or should it be 'has a live substrate connection to the same hub'? Workspace equality is simpler and already declared; substrate reachability is more accurate but runtime-dependent and can flap."
  ],
  "risks": [
    "BR-RISK-028: Write amplification into other repos. Mitigated by declaration-gated destinations, proposal-only inboxes, and the fact that inbox files are transport artifacts (already gitignored downstream), never canonical state.",
    "BR-RISK-029: Loop asymmetry repeating the REQ-124/125/126 failure in a new direction — a peer request answered but never delivered back, or delivered but never consumed. Mitigated by reusing the now-complete generic loop and extending unanswered_resolutions/residue pruning to cover peer targets from the start, with the consumer side verified end to end (the lesson from that failure).",
    "BR-RISK-030: Topology confusion — agents unsure whether to address the hub or a peer for plugin-vs-product concerns. Mitigated by keeping automatic classification limited to the plugin case and requiring explicit target_repo otherwise.",
    "BR-RISK-031: Becoming a shadow chat system inside AI-Collab's workspace - the exact outcome the owner ruled out. Mitigated by BR-REQ-048's enforced channel guard rather than documentation alone.",
    "BR-RISK-032: Unstable repo identity ('auto' ids, empty workspace declarations across five of six manifests) making addressing ambiguous or the boundary predicate silently false. Mitigated by BR-REQ-049 before routing depends on it - an empty workspace must mean 'not a workspace peer', never 'unknown, assume peer'."
  ],
  "recommended_first_slice": "Order changed by the refinement. FIRST: BR-REQ-049 (stable repo ids + workspace declarations) and BR-REQ-044 (destinations tool), because addressing and the boundary predicate both depend on identity being real - today five of six manifests say id='auto', workspace=''. THEN BR-REQ-037/038/039 (tag, declared directory, relaxed gate) with BR-REQ-048's channel guard built in from the start, THEN the inbound tools (BR-REQ-045/046/047). Transport, provenance, import tooling and the resolution loop already exist and are verified end to end, so the new code is routing, identity, one lifecycle field, and four tools.",
  "linked_ids": [
    "REQ-124",
    "REQ-125",
    "REQ-126",
    "REQ-100"
  ]
}
