M-7: Turn surface keystone (per-turn batcher + listTurns hydration; B-06 partial) #11

Open
opened 2026-05-12 19:02:52 +00:00 by KofTwentyTwo · 0 comments
KofTwentyTwo commented 2026-05-12 19:02:52 +00:00 (Migrated from github.com)

Goal

Land Turn surface (keystone). Partition OutboundBatcher by turnId. Webview retires sessionHistory push and calls listTurns on connect (R-015). bargeIn lives on Turn (UQ-4). Confirmation roundtrip. Closes B-06 partial (turnTextComplete API event; DOM scroll ack remains in webview vitest territory). Worst rollback in plan; dual-channel transport (M-0.5) stays active until M-7 verified across N≥3 days.

Scope — surfaces in this phase

  • JarvisAPI.Turn commands: submitTurn(text:source:) (no images: — F-007), cancelTurn(turnId:), bargeIn(text:source:), respondToConfirmation(confirmationId:outcome:)
  • Events: turnStarted, tokenStreamed(turnId, seq), thinkingStreamed(turnId, seq), toolCallStarted/Updated/Ended, confirmationRequested/Resolved, turnEnded(terminator:), turnError(code:), turnTextComplete (G-006 / B-06)
  • Queries: getActiveTurn (exposes toolCalls + accumulatedThinking per F-019), listTurns
  • seq: Int per-turn monotonicity (F-018)
  • OutboundBatcher.postToken(turnId:) — partitioned pending buffer; on bargeIn, superseded turn's tokens dropped (R-002)
  • ConfirmationBroker reads timeout from injected ConfirmationPolicy (R-008)
  • Turn.bargeIn triggers Voice.cancelTTS (R-018 surface wiring; substrate from M-6.8)
  • Webview retires sessionHistory inbound push; calls Turn.listTurns(limit:) on connect (R-015) — atomic with Swift
  • Retire BusOutbound.hudState + sessionHistory cases
  • Bump BUS_PROTOCOL_VERSION = "2.4.0" strict (drops 2.3.0) — LAST commit in branch; lands after N≥3-day soak

Files (created/modified)

  • packages/JarvisAPI/Sources/JarvisAPI/Surfaces/Turn+Adapter.swift (new)
  • packages/Bus/Sources/Bus/OutboundBatcher.swift — partition by turnId
  • packages/MCP/Sources/MCP/ConfirmationBroker.swift — injected ConfirmationPolicy
  • packages/AgentCore/Sources/AgentOrchestrator/AgentOrchestrator.swift — wire bargeIn → cancelTTS
  • webview/src/... — drop sessionHistory inbound handler; add listTurns() on connect
  • webview/dist/ — rebuild + commit atomic with Swift
  • packages/Bus/Sources/Bus/Protocol.swift — strict 2.4.0
  • packages/Harness/Tests/HarnessTests/TurnScenarioTests.swift (new)

Test contract

Scenarios S-TURN-001..S-TURN-018. Both transport modes mandatory.

  • R-002 BLOCKING: submit-cancel-submit within 30 ms — no turn-1 token appears under turn-2's turnId
  • F-005 post-cancel silence: after cancelTurn(t1) succeeds, no further t1 events; turnEnded(t1, .cancelled) is last
  • F-018 seq monotonicity: seq == prevSeq + 1 across tokenStreamed/thinkingStreamed per turn
  • B-06 partial gate: turnTextComplete emitted between last tokenStreamed and turnEnded (DOM scroll ack remains vitest)
  • R-018 bargeIn cancels TTS: bargeIn during ttsStarted ⇒ ttsEnded(.cancelled) then turnStarted(new)
  • R-008 ConfirmationBroker timeout: configured timeout respected (was hardcoded 60s)
  • All M-1..M-6 harness scenarios still pass (regression)

Acceptance criteria

  • bash scripts/check-orchestrator-events-single-consumer.sh (single fanout drainer) green
  • bash scripts/check-bus-protocol-version.sh green for 2.4.0 strict
  • bash scripts/check-bus-harness-parity.sh green
  • bash scripts/check-app-builds.sh green
  • bash scripts/check-no-evaluate-javascript.sh green
  • bash scripts/check-no-schema-migration-in-turn.sh green (no schema delta in M-7 — Failure Mode 5)
  • All Turn scenarios green via .inProcessActor runner
  • All Turn scenarios green via .jsonRoundTripWebView runner
  • R-002 BLOCKING: submit-cancel-submit within 30 ms — no turn-1 token under turn-2's turnId
  • F-005: post-cancel silence
  • F-018: seq monotonicity
  • B-06 partial: turnTextComplete emitted between last tokenStreamed and turnEnded
  • All M-1..M-6 harness scenarios still pass (regression)
  • N≥3 days of live use with dual-channel transport active before M-7.8 strict-version commit
  • PR merged to develop

Carry-forward closures

  • Closes #51 partial (B-06: chat auto-scroll) — turnTextComplete API event lands; DOM scroll ack out of harness scope (R-014 / G-006)

Dependencies

  • Blocked by: #10 (M-6)
  • Last in sequence — v1.0 lock follows N≥3-day soak

Sub-issues

  • #135 — M-7: OutboundBatcher.postToken takes turnId; per-turn flush (R-002 BLOCKING)
  • #136 — M-7: Turn surface — submitTurn/cancelTurn/bargeIn/respondToConfirmation
  • #137 — M-7: Turn events — tokenStreamed(seq)/thinkingStreamed(seq)/turnTextComplete (F-018, B-06)
  • #138 — M-7: ConfirmationBroker reads timeout from ConfirmationPolicy (R-008, F-013)
  • #139 — M-7: Turn.getActiveTurn exposes toolCalls + accumulatedThinking (F-019 late-connect)
  • #140 — M-7: webview retires sessionHistory push; calls Turn.listTurns on connect (R-015)
  • #141 — M-7: Turn.bargeIn triggers Voice.cancelTTS (R-018 surface wiring)
  • #142 — M-7: retire BusOutbound.hudState/sessionHistory; bump BUS_PROTOCOL_VERSION=2.4.0 strict (post-soak)
  • #143 — M-7: harness scenarios S-TURN-001..018 (R-002 BLOCKING, B-06 partial, full regression)

Estimate

5.0–7.0 engineer-days

Source

  • .planning/architecture/JARVIS-API-MIGRATION-PLAN.md §2.8 + §4 (webview lockstep) + §5.5 (Failure Mode 5 no schema migration)
  • .planning/architecture/JARVIS-API-DESIGN-v0.1.md — Turn surface
  • .planning/architecture/JARVIS-API-DESIGN-v0.2.md — F-004, F-005, F-007, F-013, F-018, F-019, UQ-4 bargeIn on Turn
  • .planning/architecture/REVIEW-MIGRATION-RISK.md R-002, R-008, R-009, R-014, R-015, R-018
  • Parent epic: v1.0 API Migration — UI/Backend separation (M-0..M-7) (#76)
## Goal Land Turn surface (keystone). Partition `OutboundBatcher` by `turnId`. Webview retires `sessionHistory` push and calls `listTurns` on connect (R-015). `bargeIn` lives on Turn (UQ-4). Confirmation roundtrip. Closes B-06 partial (`turnTextComplete` API event; DOM scroll ack remains in webview vitest territory). **Worst rollback in plan**; dual-channel transport (M-0.5) stays active until M-7 verified across N≥3 days. ## Scope — surfaces in this phase - **`JarvisAPI.Turn`** commands: `submitTurn(text:source:)` (no `images:` — F-007), `cancelTurn(turnId:)`, `bargeIn(text:source:)`, `respondToConfirmation(confirmationId:outcome:)` - Events: `turnStarted`, `tokenStreamed(turnId, seq)`, `thinkingStreamed(turnId, seq)`, `toolCallStarted/Updated/Ended`, `confirmationRequested/Resolved`, `turnEnded(terminator:)`, `turnError(code:)`, `turnTextComplete` (G-006 / B-06) - Queries: `getActiveTurn` (exposes `toolCalls` + `accumulatedThinking` per F-019), `listTurns` - `seq: Int` per-turn monotonicity (F-018) - `OutboundBatcher.postToken(turnId:)` — partitioned pending buffer; on `bargeIn`, superseded turn's tokens dropped (R-002) - `ConfirmationBroker` reads timeout from injected `ConfirmationPolicy` (R-008) - `Turn.bargeIn` triggers `Voice.cancelTTS` (R-018 surface wiring; substrate from M-6.8) - Webview retires `sessionHistory` inbound push; calls `Turn.listTurns(limit:)` on connect (R-015) — atomic with Swift - Retire `BusOutbound.hudState` + `sessionHistory` cases - Bump `BUS_PROTOCOL_VERSION = "2.4.0"` strict (drops `2.3.0`) — LAST commit in branch; lands after N≥3-day soak ## Files (created/modified) - `packages/JarvisAPI/Sources/JarvisAPI/Surfaces/Turn+Adapter.swift` (new) - `packages/Bus/Sources/Bus/OutboundBatcher.swift` — partition by turnId - `packages/MCP/Sources/MCP/ConfirmationBroker.swift` — injected ConfirmationPolicy - `packages/AgentCore/Sources/AgentOrchestrator/AgentOrchestrator.swift` — wire bargeIn → cancelTTS - `webview/src/...` — drop `sessionHistory` inbound handler; add `listTurns()` on connect - `webview/dist/` — rebuild + commit atomic with Swift - `packages/Bus/Sources/Bus/Protocol.swift` — strict 2.4.0 - `packages/Harness/Tests/HarnessTests/TurnScenarioTests.swift` (new) ## Test contract Scenarios `S-TURN-001..S-TURN-018`. Both transport modes mandatory. - **R-002 BLOCKING:** submit-cancel-submit within 30 ms — no turn-1 token appears under turn-2's turnId - **F-005 post-cancel silence:** after `cancelTurn(t1)` succeeds, no further `t1` events; `turnEnded(t1, .cancelled)` is last - **F-018 seq monotonicity:** `seq == prevSeq + 1` across `tokenStreamed`/`thinkingStreamed` per turn - **B-06 partial gate:** `turnTextComplete` emitted between last `tokenStreamed` and `turnEnded` (DOM scroll ack remains vitest) - **R-018 bargeIn cancels TTS:** bargeIn during ttsStarted ⇒ `ttsEnded(.cancelled)` then `turnStarted(new)` - **R-008 ConfirmationBroker timeout:** configured timeout respected (was hardcoded 60s) - All M-1..M-6 harness scenarios still pass (regression) ## Acceptance criteria - [ ] `bash scripts/check-orchestrator-events-single-consumer.sh` (single fanout drainer) green - [ ] `bash scripts/check-bus-protocol-version.sh` green for `2.4.0` strict - [ ] `bash scripts/check-bus-harness-parity.sh` green - [ ] `bash scripts/check-app-builds.sh` green - [ ] `bash scripts/check-no-evaluate-javascript.sh` green - [ ] `bash scripts/check-no-schema-migration-in-turn.sh` green (no schema delta in M-7 — Failure Mode 5) - [ ] All Turn scenarios green via `.inProcessActor` runner - [ ] All Turn scenarios green via `.jsonRoundTripWebView` runner - [ ] **R-002 BLOCKING:** submit-cancel-submit within 30 ms — no turn-1 token under turn-2's turnId - [ ] **F-005:** post-cancel silence - [ ] **F-018:** seq monotonicity - [ ] **B-06 partial:** `turnTextComplete` emitted between last `tokenStreamed` and `turnEnded` - [ ] All M-1..M-6 harness scenarios still pass (regression) - [ ] N≥3 days of live use with dual-channel transport active before M-7.8 strict-version commit - [ ] PR merged to develop ## Carry-forward closures - Closes #51 partial (B-06: chat auto-scroll) — `turnTextComplete` API event lands; DOM scroll ack out of harness scope (R-014 / G-006) ## Dependencies - Blocked by: #10 (M-6) - Last in sequence — v1.0 lock follows N≥3-day soak ## Sub-issues - #135 — M-7: OutboundBatcher.postToken takes turnId; per-turn flush (R-002 BLOCKING) - #136 — M-7: Turn surface — submitTurn/cancelTurn/bargeIn/respondToConfirmation - #137 — M-7: Turn events — tokenStreamed(seq)/thinkingStreamed(seq)/turnTextComplete (F-018, B-06) - #138 — M-7: ConfirmationBroker reads timeout from ConfirmationPolicy (R-008, F-013) - #139 — M-7: Turn.getActiveTurn exposes toolCalls + accumulatedThinking (F-019 late-connect) - #140 — M-7: webview retires sessionHistory push; calls Turn.listTurns on connect (R-015) - #141 — M-7: Turn.bargeIn triggers Voice.cancelTTS (R-018 surface wiring) - #142 — M-7: retire BusOutbound.hudState/sessionHistory; bump BUS_PROTOCOL_VERSION=2.4.0 strict (post-soak) - #143 — M-7: harness scenarios S-TURN-001..018 (R-002 BLOCKING, B-06 partial, full regression) ## Estimate 5.0–7.0 engineer-days ## Source - `.planning/architecture/JARVIS-API-MIGRATION-PLAN.md` §2.8 + §4 (webview lockstep) + §5.5 (Failure Mode 5 no schema migration) - `.planning/architecture/JARVIS-API-DESIGN-v0.1.md` — `Turn` surface - `.planning/architecture/JARVIS-API-DESIGN-v0.2.md` — F-004, F-005, F-007, F-013, F-018, F-019, UQ-4 bargeIn on Turn - `.planning/architecture/REVIEW-MIGRATION-RISK.md` R-002, R-008, R-009, R-014, R-015, R-018 - Parent epic: #76
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: KofTwentyTwo/Jarvis#11