Files
kandev/docs/plans/threads-multi-session-windowing/plan.md
T
Jérémy Coatelen 538813de02 feat(web): add Threads view for live agent conversations (#3112)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Carlos Florencio <carlosmflorencio@gmail.com>
2026-09-03 08:07:16 +00:00

188 lines
9.2 KiB
Markdown

---
created: 2026-08-28
status: completed
requirements:
- ../../specs/ui/requirements/threads-conversation-deck.md
- ../../specs/platform/requirements/viewport-bounded-session-delivery.md
system_design:
- ../../specs/ui/system-design/threads-conversation-deck.md
- ../../specs/platform/system-design/viewport-bounded-session-delivery.md
legacy_specs: []
---
# Implementation Plan: Threads Multi-session Windowing
## Overview
Extend the contributor Threads view so each task column can switch among its
existing agent sessions without adding management actions or another header
row. Correct the question icon so only an explicit clarification or permission
means that a person must act. Bound expensive conversation work by the
horizontal viewport: task shells stay stable, nearby session lists preload,
and only the selected session in a visible column owns a transcript and full
session subscription.
This package targets PR #3112. It preserves the contributor's first-class
Threads route, stable task order, shared composer path, task-page round trip,
and mobile snap-paged deck.
## Scope
### In scope
- A compact, workspace-scoped per-session pending-action event with revision
ordering and no transcript content.
- Stable task shells with separate preload and detail activation windows.
- One selected transcript subscription per visible desktop task column and one
total selected transcript on phone.
- Column-local session selection, exact task/session deep links, and
deterministic fallback when membership changes.
- Desktop switch-only tabs on the right of the existing metadata row.
- A phone pill and bottom-sheet session picker with 44-pixel rows.
- Correct clarification, permission, working, turn-finished, and review-ready
status semantics.
- Unit, component, desktop E2E, mobile E2E, and WebSocket traffic evidence.
### Out of scope
- Session creation, deletion, rename, reorder, pin, primary-session mutation,
or persistence of Threads selection.
- Full task-shell DOM virtualization.
- Task status summary session arrays or transcript projections.
- Changes to the task workbench's global active-session behavior.
- Plugin API changes.
## Technical approach
### Compact sibling status
Keep `session.message.*` events session-scoped. Add
`session.pending_action_changed` as a workspace-scoped semantic event derived
from the current authoritative pending-action projection. Reuse
`pending_action_revision` for cross-channel stale-event rejection. Route the
event fail-closed by workspace and merge it into the existing compact
`TaskSession` store fields. Existing global `session.state_changed` continues
to supply lifecycle and foreground activity.
### Viewport activation
Keep all `ActiveThread` task shells mounted so stable order, flexible column
width, horizontal scroll, deep-link focus, and accessibility order do not
change. Add an `IntersectionObserver` rooted at `ThreadsBoard`.
- The preload set contains intersecting desktop columns plus one adjacent
column on each side. It mounts `useTaskSessions` only.
- The desktop detail set contains intersecting columns. It mounts one
`TaskChatPanel` for each column's selected session.
- The phone detail set contains only the nearest snapped column. The next
column can preload membership but cannot subscribe to messages.
- Leaving the detail set unmounts the selected chat and releases its session
registration. The task shell and local session selection remain.
A component test with 30 task shells is the main resource-bound oracle. It
asserts that initial session-list and chat mounts follow the two viewport sets,
not the task count.
### Session selection and presentation
Load session membership through `useTaskSessions` only after preload
activation. Build a pure session view model from existing session sort/profile
helpers and compact pending status. Keep selected IDs in Threads-local state
keyed by task ID.
Desktop renders switch-only `SessionTabs` in the right side of the existing
metadata row. The right region scrolls internally if names do not fit. Do not
pass add, close, context-menu, double-click, drag, or management callbacks.
Phone renders a `MobilePillButton` in the same metadata row and a
`MobilePickerSheet` for the session list. This avoids a nested horizontal tab
gesture inside the horizontally paged task deck.
`ThreadConversation` continues to omit `onSend`, so `TaskChatPanel` owns queue
mode, model, plan mode, context, attachments, optimistic insertion, and send
reconciliation.
### Status semantics
Use a shared pure helper with explicit precedence: permission, clarification,
starting, working, terminal error, then neutral settled state. Never infer a
question from `WAITING_FOR_INPUT`. Use task review outcome for the
`Ready for review` completion treatment. Task-wide pending action can rank or
decorate a task shell, but only compact per-session projection can decorate a
specific tab.
Add every new label to all five locale catalogs. Generate Traditional Chinese
translations with the existing `i18n:zh-hant` workflow when implementation
adds the keys.
## Tests
| Acceptance criteria | Evidence |
| --- | --- |
| `AC-PLATFORM-VIEWPORT-SESSION-DELIVERY-002.1` through `.6` | Backend event routing/payload tests and frontend revision-aware reducer tests. |
| `AC-PLATFORM-VIEWPORT-SESSION-DELIVERY-001.1` through `.6` | Controllable IntersectionObserver component tests with 30 shells and mocked conversation/list mounts. |
| `AC-UI-THREADS-DECK-001.1` through `.7` | Pure selection tests, component switcher tests, and desktop task/session deep-link E2E. |
| `AC-UI-THREADS-DECK-002.1` through `.6` | Status precedence unit matrix plus inactive-tab live-update E2E. |
| `AC-UI-THREADS-DECK-003.1` through `.3` | Thirty-shell component budget and desktop scroll/subscription E2E. |
| `AC-UI-THREADS-DECK-003.4` through `.7` | Mobile picker component tests and mobile-chrome geometry, touch, failure, and scroll assertions. |
## E2E tests
- Extend `apps/web/e2e/tests/task/threads-view.spec.ts` with one real primary
session plus existing test-harness sibling sessions. Verify same-row tabs,
switch-only controls, selected transcript changes, inactive-tab status,
exact `taskId` plus `sessionId` round trip, and stable task-column order.
- Add a multi-column traffic scenario that records outgoing WebSocket actions.
Verify initial `session.subscribe` IDs equal selected sessions in visible
columns, unselected sibling IDs never subscribe, scrolling activates the new
selected session, and the departed offscreen session unsubscribes.
- Extend `apps/web/e2e/tests/task/mobile-threads-view.spec.ts` with a
multi-session task. Verify one snapped detail owner, the metadata-row pill,
bottom-sheet selection, 44-pixel rows, status parity, safe-area containment,
and zero document overflow.
- Keep the exact 30-task resource bound in deterministic component coverage.
The E2E fixture does not need to run 30 real agent turns to prove the same
mount and subscription invariant.
## Mobile design contract
- **Desktop outcome:** compact existing-session tabs share the metadata row;
visible columns can each show one selected conversation.
- **Mobile entry and hierarchy:** the task deck remains horizontally
snap-paged. A compact session pill in the metadata row opens a bottom sheet.
- **Surface rationale:** a bottom sheet avoids nested horizontal gestures and
gives long labels and status text a full-width 44-pixel row.
- **Scroll ownership:** the board owns horizontal task paging; the picker owns
vertical session scrolling; the transcript owns its internal vertical
scroll. The page document gains no horizontal overflow.
- **Shared behavior:** membership, selection fallback, session status, and
conversation submission are shared. Only the selector presentation and
phone detail-window rule differ.
- **Mobile proof:** mobile-chrome validates one active detail, picker geometry,
touch targets, status parity, safe area, and zero document overflow.
## Work orders
- [x] [Task 01: Publish compact session attention](task-01-publish-compact-session-attention.md)
- [x] [Task 02: Activate task columns from the viewport](task-02-activate-task-columns-from-viewport.md)
- [x] [Task 03: Add the Threads session switcher](task-03-add-threads-session-switcher.md)
- [x] [Task 04: Prove the Threads resource budget](task-04-prove-threads-resource-budget.md)
All work orders are sequential. They share event types, task-session state,
Threads components, and E2E fixtures, so this plan has no parallel-safe
implementation candidates.
## Risks
- A pending-action event must not broaden delivery of message content. Assert
the exact payload and workspace routing.
- React effect cleanup during fast horizontal scroll can briefly overlap old
and new chat mounts. Assertions must permit transition overlap only while
guaranteeing the settled subscription set.
- A partially visible phone neighbor must not become a second detail owner.
Use the nearest snap target instead of raw intersection on phone.
- A task-wide pending action has no session identity. Never assign it to the
primary tab before the task-session list identifies the owner.
- Many compact tabs can compete with metadata. Keep one row, bound the right
region, and test long labels at compact desktop widths.