From 3abc68004888776c909c8a06866a46d37a027855 Mon Sep 17 00:00:00 2001 From: Ziya <199893125+ZiyaZhang@users.noreply.github.com> Date: Fri, 10 Jul 2026 02:38:26 -0700 Subject: [PATCH] rfc: derive handback visibility from the log, not a live-only mirror --- .../proposed/feature/2026-07-08-interactive-side-sessions.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/rfc/proposed/feature/2026-07-08-interactive-side-sessions.md b/docs/rfc/proposed/feature/2026-07-08-interactive-side-sessions.md index 986c15ae66..2ed5008356 100644 --- a/docs/rfc/proposed/feature/2026-07-08-interactive-side-sessions.md +++ b/docs/rfc/proposed/feature/2026-07-08-interactive-side-sessions.md @@ -17,7 +17,7 @@ A **side session** is an ordinary live session forked from a source session at i - **Fork + attach composes existing primitives.** The child is created via `ctx.agents.create({ seed, meta })` with the parent's balanced completed-turn prefix (the same slice the fork subagent takes) and `parentSession`/`seedLength` lineage stamped in `meta`. No new core service and no session-store change, for the same reasons [the fork API RFC](../../implemented/feature/2026-06-30-session-store-fork-api.md) rejected a standalone fork service. - **Advisor framing rides the log, not the system prompt.** The rules ("you are a read-only side advisor; explain, do not mutate; refuse task continuation") are `inject()`ed as a `context/message` with source `{ kind: 'plugin', plugin: 'sidechat' }` immediately after creation. The child's system prompt stays byte-identical to the parent's, so the provider's prefix cache covers the inherited history. - **Merge-back is one condense turn plus one injection.** `/merge` prompts the child for a bounded handback note (a hard length cap), then `inject()`s the note into the PARENT as a `context/message` with the same plugin source. The parent's next request sees it at its chronological position; replay and [reconstructability](../../implemented/architecture/2026-07-05-reconstructable-requests.md) hold by construction; no new event type enters [the session vocabulary](../../../core-data-structures/session.md). -- **The v1 surface is the ACP bridge, with no new protocol capability.** The bridge intercepts `/side [question]` on a prompt: it stages the fork (bridge-local state, nothing appended to the source log) and the next `session/new` in the same workspace claims it, so the child appears to the editor as an ordinary second session under [multi-session](./2026-06-14-acp-multi-session.md). Because `context/message` produces no client update, the bridge mirrors the handback as an `agent_message_chunk` in the parent session so the merge is visible. +- **The v1 surface is the ACP bridge, with no new protocol capability.** The bridge intercepts `/side [question]` on a prompt: it stages the fork (bridge-local state, nothing appended to the source log) and the next `session/new` in the same workspace claims it, so the child appears to the editor as an ordinary second session under [multi-session](./2026-06-14-acp-multi-session.md). A plugin-sourced `context/message` renders as a visible message through the bridge's shared event translator, so the live stream and `session/load` replay derive the identical visible record from the same durable event — the handback's visibility is a projection of the log, never a live-only mirror. v1 scope excludes: the stdio-agent surface (single-session readline today), rewind productization (mechanically the same fork-to-an-earlier-boundary, an entirely different product question), session tree views, a model-facing side-session tool, `forkName`/`mergedInto` metadata, and cross-connection persistence of a staged fork. @@ -31,12 +31,13 @@ A bridge-level spike (branch `spike/side-sessions-b`) runs the full loop against - **Persona via `system-prompt/assemble` section filtering.** Rejected as the default path: any system-prompt byte change invalidates the provider prefix cache from token 0, forfeiting the cheap fork that makes side sessions attractive on long histories. The filter seam remains available for deployments that prefer hard prompt separation over cache reuse. - **A dedicated `sidechat/*` event family for the handback.** Deferred: `context/message` with a mandatory plugin source already satisfies durability, provenance, and replay. A first-class event earns its catalog, persistence, and snapshot costs only if a UI needs to render handbacks as dedicated cards. - **An ACP `session/fork` method now.** Deferred: the fork API RFC gates any ACP surface on transcript/snapshot coverage before the capability is advertised, and today's clients offer no fork affordance to call it from. Stage-and-claim ships the experience with zero protocol additions and can be replaced by the method transparently later. +- **A live-only transcript mirror for the handback.** Rejected in review: load replay deliberately ignores `context/message`, so a bridge-emitted `agent_message_chunk` would make the visible note vanish on `session/load` while the model still sees it. Rendering the durable `context/message` through the shared translator keeps live streaming and replay byte-identical. ## Acceptance criteria - `/side` on a live ACP session appends nothing to that session's log; staging is bridge-local and consumed by exactly one claim. - The claiming `session/new` yields a child seeded with the source's completed-turn prefix, with `parentSession` and `seedLength` in its header and a system prompt byte-identical to the parent's. -- `/merge` appends exactly one length-capped `context/message` to the parent with source `plugin: sidechat`; the parent's next request sees it, and `session/load` replays both sessions faithfully. +- `/merge` appends exactly one length-capped `context/message` to the parent with source `plugin: sidechat`; the parent's next request sees it, and `session/load` reproduces the visible handback note identically to the live stream — both derive from the same durable event. - Parent and child stream concurrently without interleaving their `session/update` feeds (existing multi-session isolation). - Coverage: bridge unit tests for the stage/claim/merge state machine over the in-memory transport, plus a keyless snapshot scenario exercising fork-claim and merge-back end to end.