title: Channel streaming message flow scenario: id: channel-message-flows surface: channels coverage: primary: - channels.streaming secondary: - runtime.delivery objective: Verify streaming channel replies produce visible previews that resolve to one final answer. gatewayConfigPatch: channels: telegram: streaming: mode: partial successCriteria: - The selected transport exposes at least one preview event before final delivery. - The final answer replaces or follows the preview without losing the requested text. docsRefs: - docs/channels/qa-channel.md - docs/channels/telegram.md - docs/concepts/qa-e2e-automation.md codeRefs: - extensions/qa-channel/src/inbound.ts - extensions/qa-lab/src/crabline-transport.ts - extensions/qa-lab/src/providers/mock-openai/server.ts - extensions/qa-lab/src/qa-transport.ts - extensions/telegram/src/draft-stream.ts execution: kind: flow channel: telegram summary: Stream a deterministic answer through QA Channel or Crabline Telegram and assert its preview lifecycle. config: requiredProviderMode: mock-openai conversationId: "-1001234567890" senderId: "100001" finalMarker: QA-CHANNEL-STREAMING-PREVIEW-FINAL-OK-1234567890 prompt: "Final-only marker streaming QA check. Reply exactly: QA-CHANNEL-STREAMING-PREVIEW-FINAL-OK-1234567890" flow: steps: - name: streams a preview into one final reply actions: - assert: expr: env.providerMode === config.requiredProviderMode message: this deterministic streaming proof requires mock-openai - call: waitForGatewayHealthy args: - ref: env - 60000 - call: waitForTransportReady args: - ref: env - 60000 - resetTransport: true - sendInbound: conversation: id: ref: config.conversationId kind: group senderId: ref: config.senderId senderName: QA Streaming Operator text: ref: config.prompt - waitForOutboundSequence: conversationId: ref: config.conversationId finalTextIncludes: ref: config.finalMarker finalSettleMs: 500 minimumPreviewEvents: 1 timeoutMs: expr: liveTurnTimeoutMs(env, 45000) saveAs: sequence - assert: expr: sequence.events.length >= 2 message: expr: "`expected a preview followed by the final marker; events=${JSON.stringify(sequence.events)}`" detailsExpr: "`${sequence.events.map((event) => event.kind).join(' -> ')}: ${sequence.final.text}`"