Skip to content

Feat/group chat followup ux - #113

Merged
ginccc merged 11 commits into
mainfrom
feat/group-chat-followup-ux
Jul 24, 2026
Merged

Feat/group chat followup ux#113
ginccc merged 11 commits into
mainfrom
feat/group-chat-followup-ux

Conversation

@ginccc

@ginccc ginccc commented Jul 23, 2026

Copy link
Copy Markdown
Member

This pull request refactors how the "continue discussion" action is handled in group discussions. Previously, the "Continue" action was managed by a dedicated button and composer in the DiscussionActions bar. Now, it is consistently handled by the context-aware input field (DiscussionInput), improving the user experience and simplifying the code. The changes also enhance test coverage and accessibility for the new behavior.

Refactor: Move "Continue" action to input field

Enhancement: Context-aware input for "Continue"

Consistency: Board input updates

API integration:

Overall, these changes make the "continue discussion" flow more intuitive and maintainable by consolidating the UI and logic into the main input component, while improving test coverage and accessibility.

Summary by CodeRabbit

  • New Features
    • Added follow-up “continue” flow for eligible completed conversations, including mode-aware discussion input with a “Continue” submit action.
    • Added “new” vs “continue” behavior for the workforce board input, including improved disabled messaging.
    • Enabled continuation via live streaming, with stream reset when starting fresh.
    • Post-completion action bar now shows follow-up and close only.
    • Introduced a sidebar mode switcher and persisted workforce chat panel visibility.
  • Localization
    • Updated continuation, new discussion, and disabled-input texts across supported languages.
  • Tests
    • Expanded coverage for continuation streaming, input modes, and the mode switcher UI.

ginccc added 2 commits July 23, 2026 21:21
…nels

- Input mode switches between New/Continue/Disabled based on conversation state
- Continue uses SSE streaming (continueStream) instead of REST mutation
- DiscussionActions bar shows followup + close (continue moved to input)
- New Discussion button in sidebar header + mobile dropdown
- Workforce board: full lifecycle parity (actions, continue, new discussion)
- Right-side panels default to open and persist state in localStorage
- i18n: 12 new keys across all 11 locales
- Tests updated: 3918 pass, 269 files
… resetStream

- group_start SSE handler now appends question on continuation instead
  of replacing the entire transcript (was wiping prior round context)
- Discussion list items: outer <button> → <div role='button'> to fix
  nested <button> HTML spec violation
- Input shows 'Loading…' placeholder while conversation data loads
  (was briefly showing default placeholder on a disabled input)
- Standardized workforce board disabled-messages to groups.* namespace
- Added resetStream() to SSE hook — fully resets state (transcript,
  synthesis, etc) on 'New Discussion' instead of just stopping the stream
@ginccc
ginccc requested a review from Copilot July 23, 2026 19:33
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ab900641-9204-40d4-885d-34df1396fa62

📥 Commits

Reviewing files that changed from the base of the PR and between 1f6fcad and b019a00.

📒 Files selected for processing (6)
  • e2e/resource-editor.spec.ts
  • src/components/shared/__tests__/mode-switcher.test.tsx
  • src/components/workforce/__tests__/workforce-bottom-tabs.test.tsx
  • src/components/workforce/__tests__/workforce-shortcuts.test.tsx
  • src/components/workforce/__tests__/workforce-topbar.test.tsx
  • src/lib/api/__tests__/hitl.test.ts

📝 Walkthrough

Walkthrough

The PR moves discussion continuation into SSE streaming, adds reset and transcript preservation, introduces context-aware input modes and disabled states, simplifies action bars, adds new-discussion controls, introduces workspace switching and persisted panels, updates log streaming and translations, and expands tests.

Changes

Discussion continuation and workspace controls

Layer / File(s) Summary
Continuation stream lifecycle
src/hooks/use-group-discussion-stream.ts, src/hooks/__tests__/use-group-discussion-stream.test.ts
Continuation uses streamGroupContinue, preserves transcripts, resets per-round state, and adds full stream reset coverage.
Shared input and action controls
src/components/groups/*, src/components/workforce/board-input.tsx, src/components/groups/__tests__/*
Inputs support new and continue modes with disabled messages; action bars now provide follow-up and close actions.
Group discussion page flow
src/pages/group-detail.tsx, src/pages/__tests__/group-detail.test.tsx
Submissions select new or continuation streaming, new discussions reset state, and history surfaces provide new-discussion controls.
Workforce board integration
src/pages/workforce/workforce-board.tsx, src/pages/workforce/__tests__/workforce-pages.test.tsx
The board integrates continuation, reset, lifecycle mutations, contextual input states, new-discussion controls, and persisted configuration visibility.
Workspace switching and persisted panels
src/components/shared/mode-switcher.tsx, src/components/layout/sidebar.tsx, src/components/workforce/workforce-sidebar.tsx, src/hooks/use-persisted-boolean.ts, src/pages/workforce/workforce-chat.tsx
Manager and Workforce navigation is added with keyboard handling, local-storage persistence, and persisted workforce panel state.
Log routing, query access, localized states, and validation
src/hooks/use-logs.ts, src/hooks/use-groups.ts, src/hooks/use-persisted-boolean.ts, src/i18n/locales/*.json, src/lib/api/__tests__/hitl.test.ts, src/components/workforce/__tests__/*, src/components/ui/__tests__/*
Filtered logs use dedicated SSE state, the group conversations query key is exported, locales add workspace and discussion strings, and shared API, workforce, and UI behavior receives tests.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant GroupDetailPage
  participant useGroupDiscussionStream
  participant streamGroupContinue
  User->>GroupDetailPage: Submit continuation question
  GroupDetailPage->>useGroupDiscussionStream: continueStream(groupId, conversationId, question)
  useGroupDiscussionStream->>streamGroupContinue: Start continuation SSE stream
  streamGroupContinue-->>useGroupDiscussionStream: Stream group_start and progress events
  useGroupDiscussionStream-->>GroupDetailPage: Update transcript and stream state
  GroupDetailPage-->>User: Render continued discussion
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and relevant, and it captures the group chat follow-up UX focus of the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 81.82% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/group-chat-followup-ux

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors the group “continue discussion” flow so continuation is initiated from the context-aware input (instead of the DiscussionActions bar), and wires that pattern into both Group Detail and Workforce Board experiences. It also adds panel-state persistence and expands i18n coverage for the new UX.

Changes:

  • Move “continue” UX from DiscussionActions to DiscussionInput/BoardInput via a mode + disabledMessage API and updated tests.
  • Add SSE-based continuation (continueStream) + “New discussion” reset actions in Group Detail and Workforce Board.
  • Persist Workforce panel open/close state to localStorage, with test updates for deterministic defaults.

Reviewed changes

Copilot reviewed 22 out of 22 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/pages/workforce/workforce-chat.tsx Persists details panel open state to localStorage.
src/pages/workforce/workforce-board.tsx Adds context-aware input mode (new/continue/disabled), SSE continuation, lifecycle mutations, and “New Discussion”.
src/pages/workforce/tests/workforce-pages.test.tsx Stabilizes tests around new default-open panel behavior and duplicate text occurrences.
src/pages/group-detail.tsx Uses input-driven continuation/new discussion reset; adjusts discussion list item semantics; updates history UI.
src/pages/tests/group-detail.test.tsx Updates assertions for continue being handled by input; adds disabled-input coverage.
src/hooks/use-group-discussion-stream.ts Adds continueStream + resetStream; updates group_start transcript handling.
src/components/workforce/board-input.tsx Adds mode/disabledMessage, hides attachments in continue mode, updates placeholders/a11y.
src/components/groups/discussion-input.tsx Adds mode/disabledMessage, continue icon/label/placeholder behavior.
src/components/groups/discussion-actions.tsx Removes continue-related UI/props; keeps followup + close actions only.
src/components/groups/tests/discussion-input.test.tsx Adds coverage for continue mode and disabled placeholder behavior.
src/components/groups/tests/discussion-actions.test.tsx Updates coverage to reflect continue removal from the action bar.
src/i18n/locales/en.json Adds new group/workforce strings for continue mode, disabled messaging, and “New”.
src/i18n/locales/de.json Propagates new keys for continue mode and disabled messaging.
src/i18n/locales/fr.json Propagates new keys for continue mode and disabled messaging.
src/i18n/locales/es.json Propagates new keys for continue mode and disabled messaging.
src/i18n/locales/ar.json Propagates new keys for continue mode and disabled messaging.
src/i18n/locales/zh.json Propagates new keys for continue mode and disabled messaging.
src/i18n/locales/th.json Propagates new keys for continue mode and disabled messaging.
src/i18n/locales/ja.json Propagates new keys for continue mode and disabled messaging.
src/i18n/locales/ko.json Propagates new keys for continue mode and disabled messaging.
src/i18n/locales/pt.json Propagates new keys for continue mode and disabled messaging.
src/i18n/locales/hi.json Propagates new keys for continue mode and disabled messaging.
Comments suppressed due to low confidence (2)

src/components/groups/discussion-input.tsx:151

  • The expanded dialog textarea stays enabled when disabled is true, and the Ctrl/Cmd+Enter handler will still call handleSubmit(). This lets users attempt submissions in states where the inline input is disabled (e.g., CLOSED discussion). Disable the dialog textarea when disabled is true and gate the shortcut accordingly.

This issue also appears on line 171 of the same file.

            disabled={isLoading}
            onKeyDown={(e) => {
              if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
                e.preventDefault();
                handleSubmit();
              }
            }}

src/components/groups/discussion-input.tsx:171

  • The expanded dialog submit button does not account for the disabled prop, so it can appear clickable even when the discussion is not accepting input. Include disabled in the button's disabled condition.
            >

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/components/workforce/board-input.tsx
Comment thread src/hooks/use-group-discussion-stream.ts
Comment thread src/pages/workforce/workforce-chat.tsx Outdated
Comment thread src/pages/workforce/workforce-board.tsx Outdated
ginccc added 2 commits July 23, 2026 22:50
…Storage guards

- board-input: aria-label now mirrors placeholder logic so assistive
  tech announces the disabled reason (not the stale normal label)
- continueStream: reset per-round derived fields (synthesizedAnswer,
  currentPhase, taskPlan, HITL, cancel, task sets) while keeping the
  transcript — prevents stale synthesis/task state during continuation
- workforce-board + workforce-chat: wrap localStorage read/write in
  try/catch so pages remain usable when storage is blocked
- 6 new tests covering: continueStream per-round state reset,
  group_start transcript append vs replace on continuation,
  resetStream full state clear, callback stability for
  continueStream and resetStream

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/components/groups/discussion-input.tsx (1)

51-58: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

disabled state is bypassable via the expand dialog — submissions can slip through when the discussion is disabled.

handleSubmit never checks the disabled prop, and the "Expand" trigger button, the dialog's textarea, and the dialog's submit button only gate on isLoading, not disabled. A user can click "Expand" while disabled is true (e.g. discussion closed, surfaced via disabledMessage), type in the dialog, and submit anyway — undermining the very feature this PR adds.

🐛 Proposed fix to thread `disabled` through the expand path
   function handleSubmit(e?: React.FormEvent) {
     e?.preventDefault();
-    if (question.trim() && !isLoading) {
+    if (question.trim() && !isLoading && !disabled) {
       onSubmit(question.trim());
       setQuestion("");
       setDialogOpen(false);
     }
   }
           <button
             type="button"
             onClick={() => setDialogOpen(true)}
+            disabled={disabled}
             className="absolute end-2 inset-y-0 my-auto h-fit rounded p-0.5 text-muted-foreground hover:text-foreground hover:bg-secondary/50 transition-colors"
             title={t("groups.expandInput", "Expand input")}
           >
-            disabled={isLoading}
+            disabled={isLoading || disabled}
             onKeyDown={(e) => {
             <Button
               onClick={() => handleSubmit()}
-              disabled={!question.trim() || isLoading}
+              disabled={!question.trim() || isLoading || disabled}
             >

Also applies to: 90-97, 138-142, 145-145, 170-181

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/groups/discussion-input.tsx` around lines 51 - 58, Thread the
disabled prop through the entire expand-dialog submission flow: update
handleSubmit to reject submissions when disabled, and gate the Expand trigger,
dialog textarea, and dialog submit button on both disabled and isLoading.
Preserve disabledMessage behavior while ensuring no interaction or submission
can bypass the disabled state.
src/pages/workforce/workforce-board.tsx (1)

139-147: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Stale selectedConversation cache after continueStream completes.

The completion effect (unchanged, lines 143-147) only calls setSelectedConvId(streamState.conversationId). For a brand-new discussion this triggers a fresh useGroupConversation fetch (previously disabled since selectedConvId was null), but for a continuation, selectedConvId is already set to the same id, so the state update is a no-op and the underlying useGroupConversation query is never invalidated. inputMode (Lines 210-217) and the DiscussionActions gating (Lines 493-504) both read selectedConversation.availableActions, so after a continue round finishes, the UI keeps deciding "continue"/"followup"/"close" availability from pre-continuation data.

🔧 Proposed fix
   useEffect(() => {
     if (streamState.state === "COMPLETED" && streamState.conversationId) {
       setSelectedConvId(streamState.conversationId);
+      queryClient.invalidateQueries({
+        queryKey: ["groupConversations", boardId, streamState.conversationId],
+      });
     }
-  }, [streamState.state, streamState.conversationId]);
+  }, [streamState.state, streamState.conversationId, boardId, queryClient]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/workforce/workforce-board.tsx` around lines 139 - 147, Update the
stream completion effect around useGroupDiscussionStream so it invalidates or
refreshes the selected conversation query through queryClient whenever
streamState reaches COMPLETED with a conversationId, including continuation
completions where setSelectedConvId is unchanged. Preserve the existing
auto-selection behavior for new discussions and ensure consumers such as
inputMode and DiscussionActions receive the refreshed availableActions data.
🧹 Nitpick comments (4)
src/pages/group-detail.tsx (2)

189-193: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

No test coverage for handleNewDiscussion / the "New Discussion" control.

This is a new user-facing entry point (resets stream state and clears selection) but the companion test file doesn't exercise new-discussion-btn / new-discussion-btn-mobile or assert resetStream + selection-clearing behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/group-detail.tsx` around lines 189 - 193, Add test coverage for
handleNewDiscussion in the companion group-detail tests by exercising both
new-discussion-btn and new-discussion-btn-mobile. Assert that activating either
control invokes resetStream, clears the selected conversation, and resets the
pending decision state as implemented by the handler.

592-613: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated "New Discussion" button markup.

The Plus-icon "New" button here duplicates the one in HistoryDropdown (lines 777-792) almost verbatim. Consider extracting a small shared NewDiscussionButton component to avoid drift between the two.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/group-detail.tsx` around lines 592 - 613, Extract the duplicated
Plus-icon “New Discussion” button into a shared NewDiscussionButton component
and use it both here and in HistoryDropdown. Preserve the existing click
handler, translations, accessibility attributes, test identifier, and styling so
both locations remain behaviorally identical.
src/pages/workforce/__tests__/workforce-pages.test.tsx (1)

70-103: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

No coverage for the new continue/new-discussion/followup/close flows.

WorkforceBoard gained a "New Discussion" reset handler, context-aware inputMode/disabledMessage, and followup/close mutations wired to DiscussionActions, but this suite still only exercises the pre-existing render/members-panel-toggle paths.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/workforce/__tests__/workforce-pages.test.tsx` around lines 70 -
103, The WorkforceBoard test suite lacks coverage for the new discussion and
action flows. Extend the WorkforceBoard tests to verify New Discussion resets
the board state, inputMode and disabledMessage reflect the active context, and
DiscussionActions trigger the followup and close mutations with the expected
behavior; preserve the existing render and members-panel tests.
src/pages/workforce/workforce-board.tsx (1)

113-130: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated localStorage-backed boolean state pattern across two files. Both sites implement the same "read with try/catch, default true, persist on change with try/catch" logic for a panel-visibility flag; extracting a shared usePersistedBoolean(key, defaultValue) hook would remove the duplication and keep the storage-guard logic in one place.

  • src/pages/workforce/workforce-board.tsx#L113-L130: replace the inline showConfig lazy-init/persist-effect pair with const [showConfig, setShowConfig] = usePersistedBoolean("workforce-board-config-panel", true);.
  • src/pages/workforce/workforce-chat.tsx#L32-L48: replace the inline showDetails lazy-init/persist-effect pair with const [showDetails, setShowDetails] = usePersistedBoolean("workforce-chat-details-panel", true);.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/workforce/workforce-board.tsx` around lines 113 - 130, The
duplicated localStorage-backed boolean logic should be centralized in a shared
usePersistedBoolean(key, defaultValue) hook. In
src/pages/workforce/workforce-board.tsx#L113-L130, replace the inline showConfig
initialization and persistence effect with
usePersistedBoolean("workforce-board-config-panel", true); apply the same
replacement to showDetails in src/pages/workforce/workforce-chat.tsx#L32-L48
using "workforce-chat-details-panel" and true, while preserving guarded storage
access in the shared hook.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/pages/group-detail.tsx`:
- Around line 174-187: Update handleInputSubmit so the continuation and
new-discussion success toasts are shown only after continueStream or startStream
confirms the stream was established, and add matching error handling for
immediate failures (including 404/409/network errors). Preserve the existing
HITL failure behavior while ensuring a failed request never leaves an optimistic
“started” toast visible.
- Around line 162-172: Update the disabledMessage useMemo to handle COMPLETED
conversations when availableActions does not include "continue", returning the
appropriate ended/disabled translation instead of undefined. Preserve the
existing state-specific messages and ensure the condition matches the inputMode
disabled behavior.
- Around line 399-409: Update the conversation container’s onKeyDown handler
around handleSelectConversation so Enter/Space events originating from nested
button controls are ignored, allowing their native keyboard activation to
proceed. Preserve the existing preventDefault and conversation-selection
behavior for activation events on the outer role="button" itself.

In `@src/pages/workforce/workforce-board.tsx`:
- Around line 370-380: Guard the New Discussion Button around
handleNewDiscussion so it cannot reset an active stream without confirmation.
When isStreaming is true, require an AlertDialog confirmation before invoking
handleNewDiscussion; preserve the existing direct action when no stream is
active and keep the Stop and Close discussion behavior unchanged.
- Around line 219-229: Update the disabledMessage useMemo to handle COMPLETED
discussions that lack the "continue" action, returning the appropriate
disabled/end-state translation instead of undefined. Keep the existing
state-specific messages unchanged and ensure BoardInput receives a non-empty
disabled message whenever inputMode is "disabled" for this case.
- Around line 232-244: The handleSend callback in workforce-board.tsx drops
attachment metadata because it only accepts a question string. Update handleSend
and the BoardInput onSend contract to either process attachments through the
existing upload/attachment API and pass the resulting context into
startStream/continueStream, or explicitly reject and notify attachment-only
submissions before invoking either stream API.
- Around line 257-262: Export GROUP_CONVERSATIONS_KEY from use-groups.ts, then
update WorkforceBoard’s invalidateConversations callback to build the
invalidation key with [...GROUP_CONVERSATIONS_KEY, boardId] instead of the
hard-coded "groupConversations" prefix, preserving the existing boardId guard.

---

Outside diff comments:
In `@src/components/groups/discussion-input.tsx`:
- Around line 51-58: Thread the disabled prop through the entire expand-dialog
submission flow: update handleSubmit to reject submissions when disabled, and
gate the Expand trigger, dialog textarea, and dialog submit button on both
disabled and isLoading. Preserve disabledMessage behavior while ensuring no
interaction or submission can bypass the disabled state.

In `@src/pages/workforce/workforce-board.tsx`:
- Around line 139-147: Update the stream completion effect around
useGroupDiscussionStream so it invalidates or refreshes the selected
conversation query through queryClient whenever streamState reaches COMPLETED
with a conversationId, including continuation completions where
setSelectedConvId is unchanged. Preserve the existing auto-selection behavior
for new discussions and ensure consumers such as inputMode and DiscussionActions
receive the refreshed availableActions data.

---

Nitpick comments:
In `@src/pages/group-detail.tsx`:
- Around line 189-193: Add test coverage for handleNewDiscussion in the
companion group-detail tests by exercising both new-discussion-btn and
new-discussion-btn-mobile. Assert that activating either control invokes
resetStream, clears the selected conversation, and resets the pending decision
state as implemented by the handler.
- Around line 592-613: Extract the duplicated Plus-icon “New Discussion” button
into a shared NewDiscussionButton component and use it both here and in
HistoryDropdown. Preserve the existing click handler, translations,
accessibility attributes, test identifier, and styling so both locations remain
behaviorally identical.

In `@src/pages/workforce/__tests__/workforce-pages.test.tsx`:
- Around line 70-103: The WorkforceBoard test suite lacks coverage for the new
discussion and action flows. Extend the WorkforceBoard tests to verify New
Discussion resets the board state, inputMode and disabledMessage reflect the
active context, and DiscussionActions trigger the followup and close mutations
with the expected behavior; preserve the existing render and members-panel
tests.

In `@src/pages/workforce/workforce-board.tsx`:
- Around line 113-130: The duplicated localStorage-backed boolean logic should
be centralized in a shared usePersistedBoolean(key, defaultValue) hook. In
src/pages/workforce/workforce-board.tsx#L113-L130, replace the inline showConfig
initialization and persistence effect with
usePersistedBoolean("workforce-board-config-panel", true); apply the same
replacement to showDetails in src/pages/workforce/workforce-chat.tsx#L32-L48
using "workforce-chat-details-panel" and true, while preserving guarded storage
access in the shared hook.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c69fabd6-d022-4055-a3f3-172099fb1217

📥 Commits

Reviewing files that changed from the base of the PR and between 2fa96bb and 3277022.

📒 Files selected for processing (23)
  • src/components/groups/__tests__/discussion-actions.test.tsx
  • src/components/groups/__tests__/discussion-input.test.tsx
  • src/components/groups/discussion-actions.tsx
  • src/components/groups/discussion-input.tsx
  • src/components/workforce/board-input.tsx
  • src/hooks/__tests__/use-group-discussion-stream.test.ts
  • src/hooks/use-group-discussion-stream.ts
  • src/i18n/locales/ar.json
  • src/i18n/locales/de.json
  • src/i18n/locales/en.json
  • src/i18n/locales/es.json
  • src/i18n/locales/fr.json
  • src/i18n/locales/hi.json
  • src/i18n/locales/ja.json
  • src/i18n/locales/ko.json
  • src/i18n/locales/pt.json
  • src/i18n/locales/th.json
  • src/i18n/locales/zh.json
  • src/pages/__tests__/group-detail.test.tsx
  • src/pages/group-detail.tsx
  • src/pages/workforce/__tests__/workforce-pages.test.tsx
  • src/pages/workforce/workforce-board.tsx
  • src/pages/workforce/workforce-chat.tsx

Comment thread src/pages/group-detail.tsx
Comment thread src/pages/group-detail.tsx
Comment thread src/pages/group-detail.tsx
Comment thread src/pages/workforce/workforce-board.tsx
Comment thread src/pages/workforce/workforce-board.tsx
Comment thread src/pages/workforce/workforce-board.tsx
Comment thread src/pages/workforce/workforce-board.tsx
ginccc added 3 commits July 23, 2026 23:24
- New ModeSwitcher component: dropdown-style workspace switcher
  placed in both sidebars below the logo. Shows current mode with
  icon + label + chevron; opens a dropdown with both options.
  Active mode gets a checkmark. Updates localStorage landing
  preference on switch.
- Follows the same dropdown pattern as sidebar's HelpMenu
  (outside-click close, Escape, arrow-key nav, focus management)
- Collapsed sidebar: icon-only trigger, dropdown positioned to
  the side
- i18n: 3 new keys (modeManager, modeWorkforce, switchMode) ×
  11 locales
Bug fixes:
- discussion-input: thread disabled prop through expand dialog
  (expand button, dialog textarea, dialog submit, handleSubmit)
- group-detail: fix keyboard event propagation on conversation list
  items — only handle Enter/Space on the container div itself, not
  nested buttons (fixes delete/cancel keyboard activation)
- group-detail: add COMPLETED branch to disabledMessage so disabled
  input shows explanation instead of generic placeholder
- workforce-board: invalidate conversation query on stream complete
  to refresh availableActions after continuation rounds
- workforce-board: add COMPLETED branch to disabledMessage
- group-detail: downgrade optimistic stream toasts from success→info

Code quality:
- Extract usePersistedBoolean hook to DRY up duplicated localStorage
  boolean pattern (workforce-board + workforce-chat)
- Export GROUP_CONVERSATIONS_KEY from use-groups.ts and use it in
  workforce-board invalidation (type-safe query key)

Live logging:
- useLogStream now delegates entirely to the session store when
  unfiltered — no redundant second SSE connection. The Logs page
  shows data collected since app boot immediately.
- Only opens a dedicated SSE when filters are set.

Tests:
- usePersistedBoolean: 6 tests (read/write, error handling)
- ModeSwitcher: 5 tests (render, collapsed, dropdown, keyboard)
- DiscussionInput: 2 new tests (disabled expand, disabled bypass)

i18n:
- groups.inputDisabledCompleted × 11 locales

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
src/components/shared/__tests__/mode-switcher.test.tsx (1)

41-62: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover mode selection side effects.

Add a test that selects Workforce and asserts navigation to /workforce plus persistence of eddi-landing-preference=workforce; the current suite never exercises the primary action.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/shared/__tests__/mode-switcher.test.tsx` around lines 41 - 62,
Add a test alongside the existing ModeSwitcher interaction tests that opens the
menu, selects the Workforce menu item, and asserts navigation to /workforce and
persistence of eddi-landing-preference=workforce. Reuse the existing
renderWithProviders and userEvent setup, and target the Workforce option through
its accessible menu item name.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/groups/__tests__/discussion-input.test.tsx`:
- Around line 169-179: Update the “does not submit via handleSubmit when
disabled” test to rerender DiscussionInput with disabled enabled after entering
text, then trigger submission through the button or form. Assert onSubmit was
not called, and remove the assertion that only checks the button is initially
enabled.

In `@src/components/shared/mode-switcher.tsx`:
- Around line 20-37: Update the MODES definitions and mode-switcher component to
remove labelKey and fallback fields, then add an in-component mode-label helper
that returns literal t("key", "Fallback") calls for each mode. Replace
downstream dynamic translation usage with this helper while preserving the
existing Manager and Workforce labels.

In `@src/hooks/use-logs.ts`:
- Around line 129-133: Update the filter-change useEffect to reset
filteredConnected to false before invoking connect(), alongside clearing
filtered entries, so consumers never observe the stale connected state while the
replacement SSE stream is being established.

In `@src/hooks/use-persisted-boolean.ts`:
- Around line 12-27: The persisted boolean hook must rehydrate its state
whenever key changes before persisting. Update the usePersistedBoolean flow to
read the new key and reset value to its stored boolean or defaultValue, then
ensure the persistence effect does not write the stale value under the new key
before rehydration completes.
- Line 11: Update the return type of the persisted-boolean hook to use
explicitly imported React state types, ensuring the references to React.Dispatch
and React.SetStateAction resolve without relying on a global React namespace.
Add the necessary type imports and preserve the hook’s existing behavior.

---

Nitpick comments:
In `@src/components/shared/__tests__/mode-switcher.test.tsx`:
- Around line 41-62: Add a test alongside the existing ModeSwitcher interaction
tests that opens the menu, selects the Workforce menu item, and asserts
navigation to /workforce and persistence of eddi-landing-preference=workforce.
Reuse the existing renderWithProviders and userEvent setup, and target the
Workforce option through its accessible menu item name.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2a49eb49-fd23-4a67-b0cd-70d98d4440a9

📥 Commits

Reviewing files that changed from the base of the PR and between 3277022 and 4408465.

📒 Files selected for processing (24)
  • src/components/groups/__tests__/discussion-input.test.tsx
  • src/components/groups/discussion-input.tsx
  • src/components/layout/sidebar.tsx
  • src/components/shared/__tests__/mode-switcher.test.tsx
  • src/components/shared/mode-switcher.tsx
  • src/components/workforce/workforce-sidebar.tsx
  • src/hooks/__tests__/use-persisted-boolean.test.ts
  • src/hooks/use-groups.ts
  • src/hooks/use-logs.ts
  • src/hooks/use-persisted-boolean.ts
  • src/i18n/locales/ar.json
  • src/i18n/locales/de.json
  • src/i18n/locales/en.json
  • src/i18n/locales/es.json
  • src/i18n/locales/fr.json
  • src/i18n/locales/hi.json
  • src/i18n/locales/ja.json
  • src/i18n/locales/ko.json
  • src/i18n/locales/pt.json
  • src/i18n/locales/th.json
  • src/i18n/locales/zh.json
  • src/pages/group-detail.tsx
  • src/pages/workforce/workforce-board.tsx
  • src/pages/workforce/workforce-chat.tsx
🚧 Files skipped from review as they are similar to previous changes (9)
  • src/pages/workforce/workforce-chat.tsx
  • src/i18n/locales/ko.json
  • src/i18n/locales/pt.json
  • src/i18n/locales/de.json
  • src/i18n/locales/fr.json
  • src/i18n/locales/ar.json
  • src/components/groups/discussion-input.tsx
  • src/pages/workforce/workforce-board.tsx
  • src/pages/group-detail.tsx

Comment thread src/components/groups/__tests__/discussion-input.test.tsx Outdated
Comment thread src/components/shared/mode-switcher.tsx
Comment thread src/hooks/use-logs.ts
Comment thread src/hooks/use-persisted-boolean.ts Outdated
Comment thread src/hooks/use-persisted-boolean.ts Outdated
ginccc added 2 commits July 24, 2026 00:45
Code fixes (CodeRabbitAI feedback):
- usePersistedBoolean: rehydrate on key change via useEffect + explicit
  Dispatch/SetStateAction imports (no bare React namespace)
- use-logs: reset filteredConnected before reconnecting SSE
- mode-switcher: replace dynamic t(labelKey) with getModeLabel() helper
  using literal t() calls per coding guidelines

Test fixes:
- discussion-input: rewrote disabled-bypass test to properly verify
  all disabled gating (textarea, button, expand, dialog)
- mode-switcher: added tests for selection side effects (navigation +
  localStorage persistence + no-op on active mode + checkmark)

New test files for coverage (>85% lines, >70% functions):
- hitl.ts API: 13 tests covering 8 exported functions
- view-mode.ts: 9 tests covering get/set functions + error handling
- workforce-topbar: 6 tests (render, back, menu, title, right content)
- workforce-bottom-tabs: 4 tests (render, tab count, active, navigate)
- workforce-shortcuts: 5 tests (null render, n key, ? key, modifier, cleanup)
- stream-badge: 4 tests (connected, disconnected, a11y, className)
- use-persisted-boolean: 3 additional tests for key-change rehydration

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/workforce/__tests__/workforce-bottom-tabs.test.tsx`:
- Around line 6-14: Update the router mocks to create mockNavigate through
vi.hoisted so the hoisted vi.mock factories can safely access it. Apply this
change in src/components/workforce/__tests__/workforce-bottom-tabs.test.tsx
lines 6-14, src/components/workforce/__tests__/workforce-shortcuts.test.tsx
lines 5-13, and src/components/workforce/__tests__/workforce-topbar.test.tsx
lines 6-10, preserving each test’s existing useNavigate and useLocation
behavior.

In `@src/lib/api/__tests__/hitl.test.ts`:
- Around line 29-30: Update the getBaseUrl mock in the hitl test setup to return
window.location.origin instead of a hardcoded localhost URL, while leaving
getAuthHeader unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 335fdaff-8d76-4b89-bf24-d02b146ed613

📥 Commits

Reviewing files that changed from the base of the PR and between 4408465 and 1f6fcad.

📒 Files selected for processing (13)
  • HANDOFF.md
  • src/components/groups/__tests__/discussion-input.test.tsx
  • src/components/shared/__tests__/mode-switcher.test.tsx
  • src/components/shared/__tests__/view-mode.test.ts
  • src/components/shared/mode-switcher.tsx
  • src/components/ui/__tests__/stream-badge.test.tsx
  • src/components/workforce/__tests__/workforce-bottom-tabs.test.tsx
  • src/components/workforce/__tests__/workforce-shortcuts.test.tsx
  • src/components/workforce/__tests__/workforce-topbar.test.tsx
  • src/hooks/__tests__/use-persisted-boolean.test.ts
  • src/hooks/use-logs.ts
  • src/hooks/use-persisted-boolean.ts
  • src/lib/api/__tests__/hitl.test.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/hooks/use-persisted-boolean.ts
  • src/components/groups/tests/discussion-input.test.tsx
  • src/components/shared/mode-switcher.tsx
  • src/hooks/use-logs.ts

Comment thread src/components/workforce/__tests__/workforce-bottom-tabs.test.tsx Outdated
Comment thread src/lib/api/__tests__/hitl.test.ts Outdated
ginccc added 2 commits July 24, 2026 08:21
…bles

- 4 test files: replace bare const mockNavigate = vi.fn() with
  �i.hoisted(() => ...) so the mock is available when vi.mock
  factory runs (CodeRabbitAI critical feedback)
- hitl.test.ts: use window.location.origin instead of hardcoded
  localhost:7070 per API URL coding guidelines
The config-editor-layout uses custom tab buttons with aria-selected,
not Radix Tabs data-state. The E2E test was checking for data-state
which doesn't exist on these elements.
@ginccc
ginccc merged commit c706693 into main Jul 24, 2026
2 of 4 checks passed
@ginccc
ginccc deleted the feat/group-chat-followup-ux branch July 24, 2026 07:39
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.

2 participants