Skip to content

[DECISION] /turn-stream is not retirable — the broadcast half is, the turn lock is not #254

Description

@drewstone

Answering "can /turn-stream + TurnStreamDO be retired now that both platform lanes fan out to the session gateway?"

Verdict: NO. Not fully, and not yet even partly.

/turn-stream carries four responsibilities. The session gateway replaces exactly one.

Responsibility Gateway replacement?
Live turn-event broadcast + reconnect replay Yes — for sandbox turns, once #4114 reaches production
Single-flight turn lock (createDurableTurnLock) No — the gateway has no mutual exclusion
Durable turn-event store (createDurableObjectTurnEventStore) Partly — durable chat rows (#250) serve history, but the store also backs the app-side replay endpoint
Product extension seams (handleProductRequest / shouldDeferLockRelease / productSyncEvents) No — product domain, no analogue

The lock has no substitute

The only thing named lock in the gateway is SubscriptionMutex (apps/orchestrator/src/session-gateway/sse-bridge.ts:42) — private locks = new Map<string, Promise<void>>(), an in-process guard against duplicate SSE subscriptions on one instance. It goes silent across instances, the same defect as the platform's in-process workflow-run-events.ts emitter. It is not a distributed turn lock and was never meant to be.

Sizing the retirable slice

gtm-agent is the sole consumer. src/lib/.server/chat/session-broadcast.ts imports 11 symbols from /turn-stream; only 3 are broadcast (broadcastTurnStreamEvent, broadcastWorkspaceActivity, broadcastThreadCreated). The other 8 are the lock and its types. SessionStreamDO (758 lines) additionally hosts the Vault mutation/head endpoints, the interaction-answer rendezvous, and the vault-turn persistence queue.

Retiring the broadcast half removes 3 of 11 imports from one file. The Durable Object stays.

Preconditions before even the broadcast half can go

  1. agent-dev-container #4114 reaches production. Merged to develop (a0adc3580); git merge-base --is-ancestor a0adc3580 origin/main -> NO. Production sandbox.tangle.tools still delivers 0 turn events on the run/stream (detach) lane. The main <- develop release PR is adc #4108, open.
  2. gtm adopts agent-app >= 0.43.62. Currently pinned ^0.43.60 — before feat(chat-routes): persist assistant parts incrementally as a turn streams #250, so it has no incremental persistence and no durable history for late viewers.
  3. The 2.000x lane-switch defect is located. Measured on production: feeding session-bus events to the shell's producer wrote a durable row exactly 2.000x the true transcript. It is not in createSandboxChatProducertests/chat-routes/lane-portability.test.ts pins that at 1.000x across eight lane shapes, and removing the reconciliation makes it 3.133x, so the probe is sensitive. The defect is somewhere at the gateway-frame -> producer boundary and is currently unlocated. Switching gtm to the message lane before finding it doubles every stored transcript.

Docs

The measured model is recorded in AGENTS.md § "Live viewing vs history" (#252).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions