Skip to content

Commit 79afabc

Browse files
committed
FE-1321: Refresh session-host topology notes and share the editor helper
1 parent 5be2495 commit 79afabc

3 files changed

Lines changed: 7 additions & 15 deletions

File tree

β€Žsrc/app/TOPOLOGY.mdβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Current entrypoints:
1515

1616
## Live migration: session runtime contract convergence
1717

18-
FE-1200 materialized `brunch-web.ts` as a target-addressed standalone host. FE-1321's first tracer falsified the stronger plan to attach `InteractiveMode` to one independently-lived host; D141-L instead keeps two legitimate composition roots. Normal TUI owns its sealed Pi runtime and real `InteractiveMode`, while standalone web owns its `LiveSessionHost` runtimes. The active arc now gives both roots fail-closed per-target writer acquisition before runtime construction. Normal TUI also exposes its exact live session through the canonical target-addressed hosted-session RPC and semantic event contract while retaining the real `InteractiveMode`; the old raw relay and `/rpc/driver` remain only until the cutover deletion sweep. The production PTY witness (FE-1321) now drives the real `runBrunchTui` β†’ `launchPiInteractive` β†’ `InteractiveMode` composition deterministically β€” boot chrome, one ordinary turn, canonical-JSONL readback, bounded quit β€” so real-TUI behavior is pinned by test. Its companion sibling attaches the production React app to that running TUI's sidecar over a real WebSocket and proves the browser observes the TUI-typed turn through target-addressed semantic deltas alone, converges with a fresh canonical-JSONL projection at settlement, and detaches inertly. Both witnesses share one PTY choreography module and one child entry (`src/app/__tests__/session-runtime-contract-pty-journey.ts`, `-tracer-child.ts`); a structured ask over this composition, rival refusal, and shutdown/reopen witnesses remain. Do not add new sidecar-only browser semantics. Design history: [`docs/design/WEB_UI_ARCHITECTURE.md`](../../docs/design/WEB_UI_ARCHITECTURE.md).
18+
FE-1200 materialized `brunch-web.ts` as a target-addressed standalone host. FE-1321's first tracer falsified the stronger plan to attach `InteractiveMode` to one independently-lived host; D141-L instead keeps two legitimate composition roots. Normal TUI owns its sealed Pi runtime and real `InteractiveMode`, while standalone web owns its `LiveSessionHost` runtimes. The active arc now gives both roots fail-closed per-target writer acquisition before runtime construction. Normal TUI also exposes its exact live session through the canonical target-addressed hosted-session RPC and semantic event contract while retaining the real `InteractiveMode`; the old raw relay and `/rpc/driver` remain only until the cutover deletion sweep. The production PTY witness (FE-1321) now drives the real `runBrunchTui` β†’ `launchPiInteractive` β†’ `InteractiveMode` composition deterministically β€” boot chrome, one ordinary turn, canonical-JSONL readback, bounded quit β€” so real-TUI behavior is pinned by test. Its companion sibling attaches the production React app to that running TUI's sidecar over a real WebSocket and proves the browser observes the TUI-typed turn through target-addressed semantic deltas alone, converges with a fresh canonical-JSONL projection at settlement, and detaches inertly. Every automated witness this arc owed has now landed on that same PTY choreography module and child entry (`src/app/__tests__/session-runtime-contract-pty-journey.ts`, `-tracer-child.ts`): a structured ask over this composition, proved through the observe-only announcement channel so the companion sees the ask but the TUI stays its sole answering authority; rival refusal, where a second `runBrunchWeb` composition is turned away from a TUI-owned target with the writer guard's own `-32020` / "already has a writer" identity before any second runtime exists; and shutdown/reopen, where standalone web takes the released target over and extends the TUI's own canonical JSONL, proved by literal prefix-extension of that file. The frontier's only open evidence is A51-L's colleague walkthrough. Do not add new sidecar-only browser semantics. Design history: [`docs/design/WEB_UI_ARCHITECTURE.md`](../../docs/design/WEB_UI_ARCHITECTURE.md).
1919

2020
Current runtime support modules:
2121

β€Žsrc/app/__tests__/session-runtime-contract-structured-ask.slow.test.tsβ€Ž

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import { afterAll, beforeAll, describe, expect, it } from 'vitest';
3434
import { builtinEnvironments } from 'vitest/runtime';
3535
import { WebSocket } from 'ws';
3636

37-
import { removeSession, sendKeys, sendText, stopSession } from '../../dev/tui-driver.js';
37+
import { removeSession, stopSession } from '../../dev/tui-driver.js';
3838
import type { SessionPresentationEntry } from '../../projections/session/session-presentation.js';
3939
import { projectSessionPresentationFile } from '../../projections/session/session-presentation.js';
4040
import type { WorkspaceState } from '../../projections/workspace/workspace-state.js';
@@ -58,6 +58,7 @@ import {
5858
requireScreen,
5959
sessionWriterLockExists,
6060
startProductionTui,
61+
typeAndSubmit,
6162
waitForScreen,
6263
} from './session-runtime-contract-pty-journey.js';
6364
import {
@@ -188,15 +189,6 @@ function askOpenedEvents(
188189
});
189190
}
190191

191-
/** Type into the real Pi editor and submit only once it has echoed the text back. */
192-
async function typeAndSubmit(name: string, text: string): Promise<void> {
193-
sendText(name, text);
194-
if (!(await waitForScreen(name, text, ATTACH_TIMEOUT_MS)).matched) {
195-
throw new Error(`production Pi editor never echoed ${text}`);
196-
}
197-
sendKeys(name, ['Enter']);
198-
}
199-
200192
async function waitForSettled(
201193
notifications: readonly WebSocketRpcNotification[],
202194
alreadySeen: number,
@@ -247,7 +239,7 @@ async function driveStructuredAskJourney(): Promise<StructuredAskJourney> {
247239
await waitFor(() => expect(transcriptList()).toBeDefined(), { timeout: ATTACH_TIMEOUT_MS });
248240

249241
// The assistant's ask is provoked from the PTY keyboard; the browser watches.
250-
await typeAndSubmit(name, TRACER_ASK_PROMPT);
242+
await typeAndSubmit(name, TRACER_ASK_PROMPT, ATTACH_TIMEOUT_MS);
251243
await requireScreen(name, TRACER_ASK_BODY, TURN_TIMEOUT_MS);
252244

253245
await waitFor(() => expect(askOpenedEvents(notifications).length).toBeGreaterThan(0), {
@@ -278,7 +270,7 @@ async function driveStructuredAskJourney(): Promise<StructuredAskJourney> {
278270
const settledBeforeAnswer = liveSessionDeltas(notifications).filter(
279271
(delta) => delta.type === 'agent_settled',
280272
).length;
281-
await typeAndSubmit(name, TRACER_ASK_ANSWER);
273+
await typeAndSubmit(name, TRACER_ASK_ANSWER, ATTACH_TIMEOUT_MS);
282274
await requireScreen(name, TRACER_ASK_REPLY, TURN_TIMEOUT_MS);
283275
await waitForSettled(notifications, settledBeforeAnswer);
284276

@@ -310,7 +302,7 @@ async function driveStructuredAskJourney(): Promise<StructuredAskJourney> {
310302
const settledBeforeStyle = liveSessionDeltas(notifications).filter(
311303
(delta) => delta.type === 'agent_settled',
312304
).length;
313-
await typeAndSubmit(name, CONSULT_COMMAND);
305+
await typeAndSubmit(name, CONSULT_COMMAND, ATTACH_TIMEOUT_MS);
314306
await commitModeChoice(name);
315307
await waitForSettled(notifications, settledBeforeStyle);
316308

β€Žsrc/rpc/TOPOLOGY.mdβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ rpc handler surfaces:
2323

2424
The full CLI/RPC host includes mutation-capable workspace/session methods. The TUI-started web sidecar is an attachment to the TUI-owned runtime: canonical `/rpc` exposes the same target-addressed hosted-session registry and validated `brunch.liveSessionEvent` stream as standalone web, adapting the exact `InteractiveMode` session rather than constructing another runtime. The transitional `/rpc/driver` connection preserves the older targetless process-local handles and raw `brunch.sessionEvent` stream only until `shared-session-host-cutover`; new browser work must not depend on it. Browser clients, CLI probes, and TUI adapters speak Brunch method names; they do not coordinate raw Pi RPC plus Brunch product RPC themselves.
2525

26-
**Migration state:** the sidecar registry, `/rpc/driver`, and raw `brunch.sessionEvent` relay are transitional D84-L surfaces. FE-1200's target-addressed registry and `brunch.liveSessionEvent` remain the canonical browser contracts, but D141-L no longer requires TUI and standalone web to share one physical host. `shared-session-host-tracer` now adapts the real TUI-owned runtime into those same semantic contracts and acquires cross-process writer authority before runtime construction. Its production PTY witness has landed (real `runBrunchTui` boot, one ordinary turn, canonical-JSONL readback, bounded quit), and a companion witness now proves a real browser client on that sidecar's `/rpc` receives only `brunch.liveSessionEvent`/`brunch.updated` β€” never `brunch.sessionEvent` β€” for a TUI-driven turn and converges with fresh JSONL at settlement; a structured ask over the same composition, rival refusal, and shutdown/reopen witnesses remain open; `shared-session-host-cutover` later removes the still-present raw sidecar-only surface. Do not add a third relay, compatibility alias, or new sidecar-only browser method during this transition. See [`docs/design/WEB_UI_ARCHITECTURE.md`](../../docs/design/WEB_UI_ARCHITECTURE.md).
26+
**Migration state:** the sidecar registry, `/rpc/driver`, and raw `brunch.sessionEvent` relay are transitional D84-L surfaces. FE-1200's target-addressed registry and `brunch.liveSessionEvent` remain the canonical browser contracts, but D141-L no longer requires TUI and standalone web to share one physical host. `shared-session-host-tracer` now adapts the real TUI-owned runtime into those same semantic contracts and acquires cross-process writer authority before runtime construction. Its production PTY witness has landed (real `runBrunchTui` boot, one ordinary turn, canonical-JSONL readback, bounded quit), and a companion witness now proves a real browser client on that sidecar's `/rpc` receives only `brunch.liveSessionEvent`/`brunch.updated` β€” never `brunch.sessionEvent` β€” for a TUI-driven turn and converges with fresh JSONL at settlement; the structured-ask, rival-refusal, and shutdown/reopen witnesses have since landed as well β€” the ask is announced observe-only so a browser answer is refused `ask_closed`, a rival standalone-web `session.open` against a TUI-owned target is refused `-32020` "already has a writer" before runtime construction, and after Ctrl-D a standalone composition reopens that same target and extends its one canonical JSONL; only A51-L's colleague walkthrough is still open, and `shared-session-host-cutover` later removes the still-present raw sidecar-only surface. Do not add a third relay, compatibility alias, or new sidecar-only browser method during this transition. See [`docs/design/WEB_UI_ARCHITECTURE.md`](../../docs/design/WEB_UI_ARCHITECTURE.md).
2727

2828
`session.submitExchangeResponse` (this directory) and `session.answerExchange` (the hosted-session path, with a transitional `/rpc/driver` adapter) are two structurally distinct paths, not variants of one mechanism β€” the former never touches Pi's `ctx.ui.*`/tool-execution layer at all; the latter answers a genuinely live tool call through the process-local broker for a browser driver. See [`docs/design/STRUCTURED_EXCHANGE_ANSWERING_PATHS.md`](../../docs/design/STRUCTURED_EXCHANGE_ANSWERING_PATHS.md) for the full mechanism and per-response-kind coverage.
2929

0 commit comments

Comments
Β (0)