Skip to content

FE-716: Chat runtime β€” unified chat surface with inline secondary chats#141

Draft
kostandinang wants to merge 11 commits into
ln/fe-709-reconciliationsfrom
ka/fe-716-chat-runtime-unified-secondary-chats
Draft

FE-716: Chat runtime β€” unified chat surface with inline secondary chats#141
kostandinang wants to merge 11 commits into
ln/fe-709-reconciliationsfrom
ka/fe-716-chat-runtime-unified-secondary-chats

Conversation

@kostandinang
Copy link
Copy Markdown
Contributor

@kostandinang kostandinang commented May 15, 2026

Stack context

Stacked on ln/fe-709-reconciliations (PR #139). Part of the chat-runtime track that replaces the side-chat popover and other disconnected chat surfaces with one unified workspace chat where every secondary conversation (side, reconciliation, QA, strategy) renders inline as a collapsible secondary chat alongside the primary interview.

What

V1 of inline secondary chats on the existing chat/turn substrate (no thread table):

  • Substrate (C1, C2): chat table gains parent_chat_id, invoked_in_turn_id, pinned_item_id, pinned_span_hint, and mode; createSecondaryChat + createKickoffTurn helpers.
  • Bundle + API (C3a, C3c-route): secondary chats surface in the spec bundle; POST /api/specifications/:id/secondary-chats and PATCH /.../secondary-chats/:chatId/mode.
  • Standalone UI (C3b): SecondaryChatCollapsible, collapsed by default, kickoff turn visible on expand.
  • Inline rendering (C3c-mount): controller projects secondaryChatsByInvokedTurnId; transcript renders chats under their anchor turn (secondary-chats-for-turn-{id} slot) with no-orphan invariant.
  • Trigger (C3c-wire): SecondaryChatTriggerProvider derives parent + anchor from the spec bundle; Open inline chat button on the structured list-view rail.
  • Ask/Edit mode (C4): nullable chat.mode ('explore' | 'edit'); inline toggle on the collapsible (read-only when no onSetMode); helper rejects non-secondary chats.

Out of scope

  • Streaming-with-tools wiring for secondary chats (C5)
  • #-mention knowledge-item injection (C6)
  • Agent-run flavor + chat.kind projection (C7)
  • SideChatPopover retirement (C8)
  • Lightweight reconciliation-element view (C9)
  • V1 closure + frontier closeout (C10)

Verification

npm run verify green: 103 test files / 1317 tests pass; build clean.

Draft β€” opening for visibility while C5–C10 land.

kostandinang and others added 10 commits May 15, 2026 18:34
Promote chat-runtime-secondary-chats to Active in PLAN.md (Linear=FE-716)
with V1 narrowing and deferred list. CARDS.md decomposes FE-716 into
commit-sized slices C0–C10. Adopt 'secondary chat' lexicon throughout
(matches PR #139). Retire HANDOFF.md per its contract.

Co-authored-by: Amp <amp@ampcode.com>
Copy docs/design/UNIFIED_CHAT_UX.md verbatim from PR #138 as the in-tree
UX ceiling. Body unedited per user directive; a prepended FE-716 reading
note documents vocabulary (thread = secondary chat) and substrate (D153
defers a schema-level thread table; FE-716 uses chat columns + existing
chat.kind).

Co-authored-by: Amp <amp@ampcode.com>
Add nullable parent_chat_id, invoked_in_turn_id, pinned_item_id,
pinned_span_hint to the chat table plus indexes on parent_chat_id and
invoked_in_turn_id (migration 0020). Zero enum changes β€” chat.kind stays
['interview', 'side_chat']. Tests cover column shape, index presence,
FK rejection, and active_turn_id preservation.

Co-authored-by: Amp <amp@ampcode.com>
createSecondaryChat inserts a chat row with kind='side_chat' and the
four C1 columns. createKickoffTurn inserts a turn with
turn_kind='kickoff' and resolves specification_id from the chat. POST
route deferred to C3 (no client consumer yet). Tests cover happy paths,
optional columns, and FK rejection.

Co-authored-by: Amp <amp@ampcode.com>
listSecondaryChatsForSpecification returns chat rows where
parent_chat_id IS NOT NULL with their earliest kickoff turn.
readSpecificationStateProjection includes a new secondaryChats field;
secondaryChatStateSchema added to api-types.ts (additive). Tests cover
empty, single-with-kickoff, missing-kickoff, primary-chat exclusion,
multi-spec scoping, and bundle round-trip.

Co-authored-by: Amp <amp@ampcode.com>
Radix Collapsible-backed surface keyed off the secondaryChats wire
shape. Header always renders; body collapsed by default and reveals the
kickoff turn's assistant_parts on expand; tolerates kickoffTurn=null.
Mounting in the workspace view is deferred to C3c-mount. Tests cover
header render, default collapsed, expand on click, and empty body
fallback.

Co-authored-by: Amp <amp@ampcode.com>
handleCreateSecondaryChatRequest validates spec + body (parentChatId,
invokedInTurnId, itemKind, itemId, spanHint?), resolves the item via
getKnowledgeItem (rejects when missing or kind/spec mismatch), then
calls createSecondaryChat + createKickoffTurn and returns {chatId,
kickoffTurnId}. Existing POST /side-chat untouched (deletion is C8).
Per-mode kickoff templates land in C4. Tests cover happy path with
bundle round-trip, span-hint persistence, 400 on bad body, 404 on
missing spec, 404 on missing item.

Co-authored-by: Amp <amp@ampcode.com>
…turn

Project specificationState.secondaryChats into a
secondaryChatsByInvokedTurnId map on ContinuousWorkspaceController and
thread it through ContinuousWorkspaceView into
WorkspaceTranscriptArtifacts. getArtifactAnchorTurnId covers all
turn-bearing artifact kinds. SecondaryChatCollapsible renders inline
under each matching anchor in a secondary-chats-for-turn-{id} slot;
chats without a matching artifact never render (no-orphan invariant).

Co-authored-by: Amp <amp@ampcode.com>
…button

SecondaryChatTriggerProvider derives parentChatId (= primary_chat_id)
and invokedInTurnId (= active_turn_id) from the spec bundle and exposes
create({kind, id}). useCreateSecondaryChatMutation POSTs to
/api/specifications/:id/secondary-chats and invalidates the bundle.
Mount the provider in the specification route. Add an 'Open inline
chat' button on the structured list-view ItemActionRail.
specificationSchema gains primary_chat_id?: number for transition.
Mode wiring follows in C4.

Co-authored-by: Amp <amp@ampcode.com>
Add nullable chat.mode column ('explore' | 'edit') via migration 0021.
createSecondaryChat defaults to 'explore'; setSecondaryChatMode rejects
non-secondary chats. Project mode through the bundle. Expose via PATCH
/api/specifications/:id/secondary-chats/:chatId/mode. Client adds
useSetSecondaryChatModeMutation; SecondaryChatCollapsible gets an
Ask/Edit toggle (sibling of CollapsibleTrigger to avoid nested
buttons); WorkspaceTranscriptArtifacts wires it via a thin
SecondaryChatCollapsibleWithMode wrapper. Streaming-with-tools wiring
deferred to C5. Update CARDS.md to mark C3c-mount, C3c-wire, C4 done.

Co-authored-by: Amp <amp@ampcode.com>
@kostandinang kostandinang changed the title FE-716: Plan inline-secondary-chats frontier with V1 card queue FE-716: Chat runtime β€” unified chat surface with inline secondary chats May 15, 2026
Copy link
Copy Markdown
Contributor Author

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@kostandinang kostandinang force-pushed the ka/fe-716-chat-runtime-unified-secondary-chats branch 2 times, most recently from b5e5c0e to 52ed431 Compare May 15, 2026 18:25
C5 in CARDS.md is split into three sub-cards (server streaming β†’ client
composer + host β†’ per-chat staging strip). Cross-cutting: Shape A
partition seam adds producerChatId to PatchBase + a usePatchListForChat
hook; reducer logic unchanged. Documents rejected alternatives (Shape B
nested providers, Shape C map-of-lists, Shape D no-shared-abstraction)
and order discipline (C5a β†’ C5b β†’ C5c sequential).

Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019e2cd7-2c2f-70d7-9362-d17899d44ea5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant