RFD D58: Multi-Participant Conversations
- Status: Draft
- Category: Design
- Authors: Jean Mertz git@jeanmertz.com
- Date: 2026-06-06
- Requires: RFD 097
- Extends: RFD 076, RFD 078
WARNING
This RFD is in the process of being split up into several smaller RFDs valuable on their own and independently implementable:
- RFD D51: migrating
conversation.tools.*toassistant.tools.* - RFD 098: explicit
event_idreference from each response to request event - RFD D53: parse inline URIs from query prompts
- RFD D54: The main RFD implementing multi-participant conversations
This RFD is kept for historical purposes until all four of the above RFDs are promoted, after which this one can be removed.
Summary
This RFD generalizes JP conversations from one human and one assistant to many participants sharing one conversation. Humans are observed through event authorship; assistant participants are captured in the conversation config and activated through conversation.participants. The design preserves the current single-assistant behavior, keeps reusable assistant authoring as normal config files, and projects the shared conversation into each assistant's provider request with explicit speaker labels.
Motivation
Several JP workflows need more than one assistant voice in one shared context. A pull-request panel is the motivating example: a reviewer assistant, a triager assistant, and the human should deliberate in one room instead of maintaining separate conversations and manually relaying context.
JP also stores conversations as self-contained data: base_config.json captures the conversation's starting config, and events.json carries later events and config deltas. When a teammate pulls a conversation from Git, they get the same snapshotted config and deltas. Multi-participant state should use that existing model rather than adding a metadata roster or sidecar store.
The goal is a narrow core:
- named assistant participants;
- event-level authorship, addressing, and response attribution;
- deterministic responder selection;
- a provider projection that preserves who said what;
- no autonomous arbitration in core.
Design
Terms
A Participant is a named member of a conversation. There are two kinds:
- Human participants are observed. Their identity comes from
user.name, stored as theauthorof eachChatRequest. Humans are not configured inconversation.participants. - Assistant participants are configured. Their behavior is stored as an assistant config and their active membership is listed in
conversation.participants.
The name assistant is reserved for the existing single assistant backed by the top-level assistant.* config. Named assistant participants other than assistant are backed by assistants.<name>.*.
The participant identifier is the stable identity used for addressing, config paths, and event attribution. Participant identifiers must match:
ParticipantIdentifier = [A-Za-z_][A-Za-z0-9_-]*conversation.participants entries, assistants.<name> keys, --invite, --at, and inline @name mentions all use this grammar. The identifier assistant is reserved and cannot appear under assistants.
assistant.name is a display label only. It may contain whitespace and punctuation. If assistant.name is unset, displays fall back to the participant identifier. Renaming assistant.name does not rename the participant.
Terminal rendering shows both when they differ, for example Software Architect (@architect), so the human can discover the addressable participant name.
Conversation config shape
The final logical config shape is:
# Existing single-assistant config. Also backs participant "assistant".
[assistant]
name = "JP"
model.id = "opus"
# Active assistant participants in this conversation, in response order.
[conversation]
participants = ["assistant", "dev", "architect"]
# Captured config for named participants.
[assistants.dev]
name = "Dev"
model.id = "sonnet"
[assistants.architect]
name = "Architect"
model.id = "opus"conversation.participants is the current active assistant roster. Array order is significant: it defines deterministic response, render, and append order for broadcast requests. The array contains assistant participant names only. Humans remain event authors, not config entries.
If conversation.participants is absent, readers treat the roster as ["assistant"] for compatibility with existing conversations. assistants.assistant is invalid; the reserved assistant participant always maps to top-level assistant.*.
New conversations follow the same rule unless participants are explicitly invited:
jp q --new "hello"createsconversation.participants = ["assistant"].jp q --new --invite dev "hello"createsconversation.participants = ["dev"].jp q --new --at dev "hello"resolves, invites, and selectsdev, creatingconversation.participants = ["dev"].jp q --new --invite assistant --invite dev "hello"createsconversation.participants = ["assistant", "dev"].
Explicit --at during conversation creation replaces the implicit assistant roster. This supports starting directly with a named assistant participant:
jp q --new --at dev "hello world" # participants: ["dev"]
jp q "how are you?" # broadcasts to ["dev"]Continuing an existing conversation with --at adds the participant when needed and preserves the existing roster:
jp q --new "hello world" # participants: ["assistant"]
jp q --at dev "how are you?" # participants: ["assistant", "dev"]A name in conversation.participants must resolve to config:
assistantresolves toassistant.*;- every other name resolves to
assistants.<name>.*.
A resolved query config is invalid if a participant name has no assistant config. JP reports the broken name and suggests inviting it or removing it from the roster. Raw inspection and repair commands may still load the stored conversation so users can fix the config.
Runtime code must access assistant participant config through a single helper, not by open-coding the assistant branch at each call site. The reserved assistant participant seam is accepted to preserve assistant.* as the canonical composable authoring shape and to keep existing base_config.json snapshots valid without introducing custom aliasing in v1.
Authoring reusable assistants
Reusable assistants are normal JP config files shaped around [assistant]. There is no new file type and no special inheritance syntax.
Example:
# .jp/config/assistants/dev.toml
extends = [
"../knowledge/project-structure",
"../knowledge/software-engineering",
"../roles/dev",
]
[assistant]
name = "Dev"
model.id = "sonnet"A shared knowledge fragment remains unchanged:
# .jp/config/knowledge/project-structure.toml
[[assistant.system_prompt_sections]]
tag = "Project Structure"
content = """\
You are an expert at understanding the structure of the project.
"""
[[assistant.instructions]]
title = "Project Structure: Individual Crates"
items = [
"The project is structured as a Cargo workspace, with all code in individual crates in `crates/`.",
"Crates are organized into their logical domains, e.g. `jp_attachment`, `jp_conversation`, etc.",
"The Rust code in `.config/jp/tools` is used for project maintenance tooling and is NOT part of the project's main codebase.",
]Both assistants/dev.toml and assistants/architect.toml can extend the same fragment. The fragment stays generic because it targets assistant.*, the same shape the config loader already understands.
Config composition is not invitation
Loading config with --cfg does not invite participants.
A command like:
jp q -c dev -c architect "Please investigate this issue"continues to compose both config files into the current assistant configuration, exactly as it does today. It produces one assistant response from the reserved assistant participant, not a multi-participant conversation.
Invitation is explicit. --invite <name>, jp c invite <name>, --at <name>, or an allowed inline @name mention resolves a config source and captures its assistant-facing configuration under a participant identifier.
The same config file can be used either way: --cfg composes it into the current effective config; invite extracts its resolved assistant.* subtree and captures it as a named participant.
Inviting assistants
Inviting resolves a normal config source, extracts the resolved assistant-facing config, and captures it into the conversation config. --invite uses the same config-source resolution as --cfg: direct paths, configured config_load_paths, supported extensions, and extends behave the same. The difference is what JP does with the resolved config. --cfg merges it into the current effective config. --invite extracts the resolved assistant.* subtree, captures it under a participant name, and adds that participant to conversation.participants.
jp c invite devResolution:
- If
devis already inconversation.participants, the command is a no-op. - If
assistants.devalready exists, appenddevtoconversation.participants. - Otherwise resolve
devas a normal config source through the existing config load paths, applying itsextendschain. - Extract the resolved assistant-facing config and store it as
assistants.devin a config delta. - Append
devtoconversation.participantsin the same effective config change.
The loaded file is not special. It is the same kind of config file that can be loaded with --cfg. The special operation is the invite command: it captures the file's assistant-facing result under a participant name.
Inviting assistant adds the reserved participant name to the roster and uses the top-level assistant.* config. It does not create assistants.assistant.
A participant identifier can be supplied separately when the source path does not imply the desired identifier or when two sources have the same file stem:
jp c invite reviewer --source personas/pr-reviewer
jp q --new --invite reviewer=personas/pr-reviewer "hello"Both forms capture personas/pr-reviewer as participant reviewer. --cfg remains reserved for ordinary config composition and is never used as the invite source syntax.
Uninviting and re-inviting
Uninviting removes the participant name from conversation.participants and leaves assistants.<name> intact.
jp c uninvite devFor a roster ["assistant", "dev", "architect"], the command writes a replacement delta for the small participant array:
[conversation]
participants = ["assistant", "architect"]The captured assistants.dev config remains in the conversation snapshot and deltas. Re-inviting dev adds the name back to conversation.participants and uses the existing captured config, including conversation-local overrides.
This avoids tombstone state, sidecar files, and negative deltas. The event stream remains the join/leave record: deltas show when the participant was added or removed, and event attribution shows who said what.
A future refresh command may discard the captured config and reload from the source file:
jp c participant refresh devThat is not part of v1.
Assistant-facing tool configuration
Tool bindings are assistant-facing. The current conversation.tools field is de facto scoped to the single assistant. Multi-participant conversations make that scope explicit.
Final shape:
[assistant.tools.fs_read_file]
source = "local"
[assistants.dev.tools.fs_modify_file]
source = "local"
run = "ask"providers.mcp remains global because MCP server configuration describes shared processes. Participants capture the tool entries they may use. ToolConfig is not split into definition, behavior, display, and access sub-objects. Per-participant differences are represented by different captured tool entries.
This RFD migrates conversation.tools to assistant-facing config:
- old config files may still use
conversation.toolsas legacy input; - for the reserved
assistantparticipant, legacyconversation.toolsmaps toassistant.tools; - when inviting a named assistant from a source file, legacy
conversation.toolsin that source maps toassistants.<name>.tools; - query setup, tool rendering, tool enable/disable flags, and
ToolCoordinatoruse the selected participant's assistant-facing tool config.
New config should use assistant.tools in reusable assistant files and assistants.<name>.tools in captured conversation config.
Relationship to tool configuration RFDs
This RFD extends the tool configuration surfaces from RFD 076 and RFD 078 from one assistant to multiple assistant participants.
Legacy input:
conversation.toolsremains accepted as legacy input.- For the reserved
assistantparticipant, legacyconversation.toolsmaps toassistant.tools. - During invite, legacy
conversation.toolsin the source maps toassistants.<name>.tools.
Runtime config writes are participant-relative. A tool running for participant dev that writes assistant.model.id stores the delta at assistants.dev.model.id. A tool running for the reserved assistant participant writes to top-level assistant.model.id. Tools must not mutate another participant's assistant config unless granted an explicit absolute config path by RFD 078's access model.
Sensitive tool access paths include the participant-scoped locations:
assistant.tools.*.access;assistants.*.tools.*.access;- legacy
conversation.tools.*.accesswhile compatibility remains.
At-mention policy
An assistant controls whether mentioning it can invite or rejoin it.
[assistant.at_mention]
invite = "error_if_known" # allow | error_if_known | ignore
rejoin = "error_if_known" # allow | error_if_known | ignoreCaptured examples:
[assistants.architect.at_mention]
invite = "allow"
rejoin = "allow"
[assistants.dev.at_mention]
invite = "error_if_known"
rejoin = "error_if_known"Defaults are error_if_known.
Mentions are parsed from ChatRequest.content with a small lexer rule:
- a mention starts at the beginning of the string or after whitespace;
- the mention starts with
@; - the participant name matches
[A-Za-z_][A-Za-z0-9_-]*; \@nameis literal text;email@example.comandfoo@barare not mentions.
When JP parses @name inside a query:
- If
nameis active inconversation.participants, select it as a responder. - If
assistants.<name>exists butnameis not active, apply that assistant'srejoinpolicy. - If no captured config exists but
nameresolves as a config source, load it and apply itsassistant.at_mention.invitepolicy. - If no captured config or source resolves, treat
@nameas plain text.
Unknown inline mentions are plain text. The invite and rejoin policies apply only after the mention resolves to a captured assistant or loadable assistant source.
The explicit --at <name> flag is stricter and not governed by assistant.at_mention:
- If
nameis active inconversation.participants, select it as a responder. - If
assistants.<name>exists butnameis not active, add it back toconversation.participantsand select it. - If no captured config exists but
nameresolves as a config source, load and capture it, add it toconversation.participants, and select it. - If no captured config or source resolves, error with a hint to invite the participant or check the spelling.
Policy outcomes:
allow: mutate the roster as needed and select the participant;error_if_known: fail with a hint to usejp c invite <name>;ignore: treat the mention as plain text.
The mention is parsed from ChatRequest.content. This is valid:
jp q "@dev please review this"This is not the at-mention form:
jp q @dev "please review this"The explicit CLI equivalent is:
jp q --at dev "please review this"Inline @name mentions replace the previous query-text @path shorthand. File references in query text use a file: prefix instead:
jp q "@dev please review file:README.md"The file: form is an attachment candidate. Future attachment handlers may add other inline references, such as HTTP URLs or jp:// conversation links.
Addressing and visibility
Addressing and visibility are separate axes.
- Visibility: who sees the event in assistant projections. v1 events are public to all assistant participants.
- Addressing: who is expected to respond.
A ChatRequest carries recipients.responders:
{
"type": "chat_request",
"author": "jean",
"recipients": {
"responders": [
"dev"
]
},
"content": "@dev please review this"
}If no @name or --at <name> selects responders, the responder set is all active assistant participants. This means jp q "hello" is a room-visible request and all active assistant participants are expected to answer.
If one or more responders are selected, only those assistant participants are expected to answer. The request is still visible to everyone in v1.
Responder order is:
- For unaddressed broadcast requests,
conversation.participantsorder. - For addressed requests, explicit order:
--atvalues in flag order, followed by inline@namementions in content order for names not already selected.
Duplicate responders are deduped by first occurrence.
Future private messages can extend recipients with projection visibility:
{
"recipients": {
"responders": [
"dev"
],
"visibility": {
"only": [
"dev"
]
}
}
}Humans are never listed in recipients. Stored events are human-visible by being present in the conversation store. Visibility metadata controls assistant provider projections only.
Query behavior
A single ChatRequest addressed to N assistant participants produces one turn containing N participant execution substreams. TurnStart marks the turn boundary. A normal query appends TurnStart, then a ChatRequest, then the participant-originated events that answer that request.
For a broadcast request:
jp q "hello"all active assistant participants respond to the same request. Responders generate sequentially in conversation.participants order. Each responder's projection includes the original ChatRequest and any prior assistant responses already produced for that turn. The terminal renders each response as it streams, then the next responder begins. Appends to the event stream use the same order under the conversation lock (RFD 020).
Sequential generation preserves the room model: later responders can react to earlier responders in the same turn. Parallel independent responder execution is future work because it changes semantics, not only performance.
For an addressed request:
jp q "@dev hello"only dev responds.
A future reply-order graph may allow branching plans such as "dev and architect respond independently, then reviewer summarizes both". v1 defines a single linear responder order.
Participant execution scope
Participant execution produces a substream of events while answering a specific ChatRequest. Every participant-originated event carries:
participant: the assistant participant that produced the event;request_id: the stable event ID of theChatRequestbeing answered.
This applies to:
ChatResponse;ToolCallRequest;ToolCallResponse;InquiryRequestcreated during that participant's tool execution;InquiryResponseanswering an inquiry created during that participant's tool execution.
Tool-call matching uses (participant, tool_call_id) rather than tool_call_id alone. Inquiry matching uses (participant, inquiry_id) rather than inquiry_id alone. This keeps provider-generated IDs scoped to the participant that produced them and lets projection distinguish own tool/inquiry events from peer events.
request_id depends on stable event identifiers from RFD 097.
Assistant-scoped CLI flags
Current query flags such as --model, --param, --tool, and --no-tools mutate assistant-facing config. In a multi-participant conversation, JP resolves @name and --at <name> into a responder set before applying these flags. Assistant-scoped flags apply only when the request has exactly one assistant responder.
Allowed:
jp q --model gpt "@dev hello"
jp q --at dev --model gpt "hello"Both mutate only dev:
[assistants.dev.model]
id = "gpt"For the reserved assistant participant:
jp q --at assistant --model gpt "hello"mutates:
[assistant.model]
id = "gpt"A broadcast request with assistant-scoped mutation is ambiguous:
jp q --model gpt "hello"If more than one assistant participant is active, this fails:
error: `--model` is ambiguous for a broadcast request
hint: use `--at dev --model gpt` or `-c @dev:model.id=gpt`Participant-scoped config sugar maps @name: to the captured config path:
jp q -c @dev:model.parameters.reasoning.effort=max "@dev try again"maps to:
[assistants.dev.model.parameters.reasoning]
effort = "max"For assistant, @assistant: maps to top-level assistant.*. This is a --cfg parser addition: values matching @<participant>:<key>=<value> are parsed as participant-scoped assignments before the existing leading-@ path form. Values such as @config/dev.toml remain path references.
Event attribution
ChatRequest gains:
author: existing human display name fromuser.name;recipients.responders: assistant participant identifiers expected to respond.
Attribution is event-level, not turn-level. A turn can contain more than one request, and requests can address different participants.
Attribution is stored even in 1:1 conversations. Provider projections can omit labels for strict 1:1 history, but stored history remains attributable if more participants join later.
Speaker-aware projection
Projection maps the stored multi-voice event stream into the dyadic user/assistant message format each provider expects. The projection is built separately for each assistant participant.
Rules:
- The generating participant's own prior responses map to
assistant. - Every human request and every peer assistant response maps to
user. - External turns are labeled in-band with the speaker name when the projection has more than one external speaker.
- Labels are omitted for strict 1:1 projections so existing conversations remain byte-for-byte equivalent at the provider boundary.
- Peer reasoning is not projected to other assistants.
- Peer tool calls, tool responses, and inquiry events are not projected to other assistants. Only peer chat responses are shared across assistant projections. Tool and inquiry internals remain visible in the stored conversation and to the producing participant.
Example projecting for dev:
system: <dev prompt>
user: [jean]: should we refactor this?
assistant: yes, I would refactor it
user: [architect]: I disagree; the boundary is still unstableThe provider API remains strictly dyadic. In-band labels carry participant identity because providers may merge consecutive same-role turns.
Drawbacks
- Two assistant config shapes exist.
assistant.*remains the canonical assistant authoring shape and backs the reservedassistantparticipant;assistants.<name>.*backs captured named participants. The seam is accepted to preserve composableassistant.*fragments and keep existingbase_config.jsonsnapshots valid without custom aliasing in v1. Runtime code must contain the seam behind config accessors. - Captured participant config can be verbose. Large assistant prompts and instructions may appear in config deltas. This is accepted:
events.jsonis the event store. Readability should be improved with tooling, not by splitting storage into side files. - Assistant-scoped flags become conditional. Flags such as
--modelremain ergonomic for one responder, but error for broadcasts. - Broadcast latency scales with responders. A broadcast to
Kassistant participants runsKmodel turns sequentially in v1. This preserves the room model because later responders can react to earlier responders in the same turn, but it makes multi-assistant broadcasts slower than parallel independent execution. - Invite does more than append a name. It resolves a config source with the same loader used by
--cfg, captures assistant-facing config, and updates the participant roster.
Alternatives
Separate conversations with relay
Rejected as the core model. Separate conversations keep each provider request simple, but the shared room becomes an illusion maintained by an orchestrator. That duplicates context, loses a single authoritative event stream, and pushes speaker attribution and projection into workflow code. A relay workflow remains buildable on top of this RFD; it should not be JP's core conversation model.
Hierarchical sub-agents
Rejected for this use case. RFD 051 describes delegation: a main agent sends scoped work to sub-agents and receives summaries. This RFD describes peer participation: multiple assistants are first-class voices in one conversation. Delegation remains useful for research and task fan-out, but it does not solve participants deliberating in the same room.
Store participants in metadata
Rejected. Conversations already store a snapshotted config plus deltas. Participant membership and captured config belong in that self-contained config state, not in metadata.json.
Store participant snapshots in sidecar files
Rejected. Mid-stream participant changes are ordered config changes. Sidecar files would need references, versioning, and extra failure handling. Large config deltas are simpler and keep the event store authoritative.
Store captured config under conversation.participants.<name>.assistant
Rejected. It makes membership and captured config one map entry, so uninviting removes the captured config unless tombstones or negative deltas are introduced. Keeping active membership (conversation.participants) separate from captured config (assistants.<name>) preserves re-invite behavior without extra state.
Tombstone participant state
Rejected. state = "uninvited" leaves non-participants in the participant map. The roster should list current participants only.
Per-field conditions
Rejected. Conditions make shared fragments know who consumes them, which couples orthogonal config fragments and grows special cases as more assistants are added. Existing file-level extends keeps fragments independent.
Group-transcript projection
Rejected. Rendering the whole history as one transcript loses native assistant continuity, prompt caching, and tool-call structure for the generating participant.
Non-Goals
- Autonomous arbitration. Deciding who should chime in, whether to stay silent, and how bots converse without a human request belongs in a plugin built on query delegation, not in v1 core.
- Reply-order graphs. v1 selects responders for one request. Arbitrary sequential and fan-out/fan-in plans are future work.
- Private messages.
--dm <participant>is future work. v1 events are public to all assistant projections. - Deleting captured assistant configs. Uninvite removes a name from the active roster and preserves
assistants.<name>. - Refreshing from source. Reloading a captured assistant from its source file is future work.
- Wider config namespace cleanup. Moving unrelated
conversation.*fields is not part of this RFD.
The at-mention policy and assistant-scoped CLI flag scoping are included because they define the minimum coherent v1 query surface. If review stalls on either, they can be split into follow-up RFDs without changing the storage or projection model.
Future Work
Direct messages
A future --dm <participant> sends a request only to that assistant participant and hides both request and response from other assistant projections. The events remain visible to humans in the stored conversation.
Future event shape:
{
"type": "chat_request",
"author": "jean",
"recipients": {
"responders": [
"dev"
],
"visibility": {
"only": [
"dev"
]
}
},
"content": "private question"
}Humans are not configured recipients. Visibility controls assistant provider projections only.
Reply-order graph and parallel responses
A future RFD may add a reply plan for cases where responders should branch, merge, or run independently. The internal model can be a dependency graph over responders; v1 exposes only a single linear responder order.
Parallel independent responder execution is also future work. It can reduce latency, but it means co-responders do not see each other's outputs until the next turn, which is different from the room model v1 provides.
Participant refresh
Re-invite uses the existing captured assistant config. This follows the same rule as all conversation config: source config changes do not affect an existing conversation unless explicitly applied. Use -c @name:... for conversation-local tweaks.
A future command may intentionally replace a captured assistant config by resolving its source again:
jp c participant refresh devRisks and Open Questions
- Tool migration. Moving
conversation.toolsinto assistant-facing config is necessary, but it touches query execution, tool rendering, and config docs. - At-mention source resolution. Checking a resolvable
@namemay need to load a source config to readassistant.at_mention.invite. Cache source-resolution results per command invocation. Unknown inline mentions are plain text; unknown--atvalues are errors.--inviteand--atuse the same config-source resolution as--cfg, but they capture the resolved assistant-facing config instead of merging it into the current assistant. - Large config deltas. This worsens raw
events.jsonreadability. The answer is editor folding and JP tooling for viewing/editing event streams. - Projection behavior.
user+ labels is the v1 projection. It should be tested against real multi-assistant review conversations before acceptance. - Backwards compatibility. Existing conversations without
conversation.participantsmust behave as a singleassistantparticipant.
Implementation Plan
Config schema and accessors. Add
assistantsas a top-level map with the same assistant-facing shape asassistant. Addconversation.participantsas an ordered array of assistant participant identifiers. Addassistant.at_mentionpolicy and validation linking participants to captured configs. Add helper accessors so runtime code does not open-code the reservedassistantbranch. Depends on nothing.Stable event identifiers. Implement or require RFD 097, so
ChatRequestentries can be referenced by stableevent_id. Depends on nothing in this RFD.Participant execution attribution. Add
participantandrequest_idto participant-originated chat, tool, and inquiry events. Tool and inquiry matching move to(participant, id)keys. Depends on Phases 1 and 2.Single-responder query path. Resolve one assistant participant and run a normal query through that participant's assistant config. Depends on Phases 1 and 3.
Tool migration and participant-aware tool execution. Move assistant-facing tool bindings to assistant participant config while preserving compatibility for config sources that still use
conversation.tools. Query setup, tool rendering, tool enable/disable flags, andToolCoordinatormove to the selected participant's assistant-facing tool config. Depends on Phases 1, 3, and 4.Invite and uninvite commands. Implement
jp c invite <name>andjp c uninvite <name>. Invite resolves normal config sources, captures assistant-facing config intoassistants.<name>, and appends the name toconversation.participants. Uninvite replaces the participant array without the removed name. Depends on Phases 1 and 5.Query responder resolution. Parse
@namementions insideChatRequest.content, implement--at, apply at-mention invite/rejoin policy, and compute the responder set. Depends on Phases 3 and 6.Broadcast sequential responders. Execute multiple responders in a single turn using the linear responder order. Depends on Phases 3, 5, and 7.
Speaker-aware projection. Implement speaker-aware projection with conditional labels, peer reasoning stripping, and peer tool/inquiry omission. Depends on Phases 3 and 7.
Assistant-scoped CLI flag scoping. Rewrite existing assistant-mutating query flags to the selected single responder. Error for broadcasts unless the user uses participant-scoped
--cfg @name:. Depends on Phases 1 and 7.Documentation and tooling. Document reusable assistants as normal config files loaded by invite. Add event-viewing/editing affordances so large config deltas do not make raw
events.jsonthe only practical editing surface. Depends on all prior phases.
References
- RFD 020: conversation locks and concurrent mutation control.
- RFD 031: durable conversation storage and workspace projection.
- RFD 051: hierarchical sub-agent workflows, contrasted with peer participation.
- RFD 054: conversation config snapshots and deltas.
- RFD 070: future negative config deltas, not required by this RFD.
- RFD 072: command plugin system, relevant to future arbitration plugins.
- RFD 076: tool access grants, extended by participant-scoped tool paths.
- RFD 078: tool config mutation, extended by participant-relative config paths.
- RFD 097: stable event identifiers, required for
request_idreferences.