Skip to content

feat: complete Chat Workspace live flow#2595

Merged
rmusser01 merged 1 commit into
devfrom
codex/chat-workspace-live-flow
Jul 3, 2026
Merged

feat: complete Chat Workspace live flow#2595
rmusser01 merged 1 commit into
devfrom
codex/chat-workspace-live-flow

Conversation

@rmusser01

@rmusser01 rmusser01 commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Summary

  • Makes Chat Workspace create and resume workspace-scoped server chats across normal, persona, character, and staged-RAG send paths.
  • Completes the source rail and staged-context lifecycle for real workspace sources, including browse, stage, unstage, source states, fallback summaries, and workspace-switch clearing.
  • Adds deterministic Chat Workspace live-backend Playwright smoke coverage and wires the Stage 5 release gate to that focused proof.

Change summary

AI-generated PR. Before merge, a maintainer must add a human-written Change summary explaining what changed and why these implementation choices were made, per Docs/superpowers/AI_GENERATED_PR_CHANGE_SUMMARY_POLICY_2026_04_17.md.

Test Plan

  • bunx vitest run src/components/Option/ChatWorkspace/__tests__ src/hooks/chat/__tests__/useChatActions.character.integration.test.tsx src/hooks/chat/__tests__/useChatActions.persona.integration.test.tsx src/services/__tests__/chat-settings.sync.test.ts --maxWorkers=1
  • npx playwright test e2e/smoke/chat-workspace-live-backend.spec.ts --project=chromium
  • npx playwright test e2e/smoke/stage5-release-gate.spec.ts --project=chromium --grep "Chat Workspace"
  • git diff --check origin/dev...HEAD
  • Bandit not applicable: no Python files touched.

Closes #2031
Closes #2032
Closes #2035
Refs #1239


Summary by cubic

Chat Workspace now runs as a live, workspace-scoped chat surface. It completes the source rail lifecycle and adds a focused Playwright proof wired into the Stage 5 release gate.

  • New Features
    • Create/resume workspace-scoped server chats across normal, persona, character, and staged‑RAG sends; preserves drafts, supports streaming/stop, and clears state on workspace switch.
    • Source rail: browse, stage, unstage, per‑source status, staged summary insert/send, remove a single staged source; routes Add source/Open library to canonical surfaces; shows loading/error states.
    • Server chat settings sync honors workspace scope and passes scope through API calls for chat settings.
    • Focused Playwright smoke for /chat-workspace against a live backend; Stage 5 release gate links to this proof and allowlists route‑only startup probes.

Written for commit f580e39. Summary will update on new commits.

Review in cubic

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 70716151-cc7d-4c9e-aff8-ad56301cdab2

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/chat-workspace-live-flow

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

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Complete Chat Workspace live flow (scoped chats, source lifecycle, smoke gate)

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Ensure /chat-workspace creates/resumes workspace-scoped server chats across all send modes.
• Complete source rail lifecycle: browse focus, stage/unstage, states, empties, workspace-switch
 guards.
• Add deterministic Playwright live-backend smoke and wire Stage 5 release gate to it.
Diagram

graph TD
  A["ChatWorkspacePage"] --> B["ChatWorkspaceConsole"] --> C["WorkspaceRail"]
  B --> D["WorkspaceChatPanel"] --> E(["useChatActions"])
  E --> F["TldwApiClient"] --> G{{"Backend API"}}
  H["Playwright smoke"] --> I["Stage 5 gate"] --> A
  subgraph Legend
    direction LR
    _ui["UI Component"] ~~~ _hook(["Hook/Service"]) ~~~ _ext{{"External"}}
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Scope-bound API client instance
  • ➕ Eliminates repeated scope ? {scope} : undefined call sites
  • ➕ Reduces risk of missing scope propagation in future additions
  • ➖ Requires broader refactor of client creation/DI across hooks and services
  • ➖ Harder to keep global vs workspace behavior explicit in call sites
2. Central request middleware to inject scope query/body
  • ➕ Single enforcement point for scope_type/workspace_id serialization
  • ➕ Keeps higher-level code focused on business logic
  • ➖ Can obscure which requests are truly scoped, complicating debugging
  • ➖ Requires careful exclusions for endpoints that must remain unscoped

Recommendation: Current approach is appropriate for a minimal, auditable correctness fix: scope is threaded explicitly through create/message/stream/persist and settings sync, with regression tests and an E2E proof to prevent global-chat leakage. Consider a scope-bound client only if scope propagation expands significantly beyond chat + settings endpoints.

Files changed (25) +2125 / -55

Enhancement (9) +337 / -47
ChatWorkspaceConsole.tsxThread source loading/error and unstage callbacks through console composition +10/-0

Thread source loading/error and unstage callbacks through console composition

• Extends props to pass workspace source loading/error state into the rail and to support individual unstage/removal from staged context via the panel.

apps/packages/ui/src/components/Option/ChatWorkspace/ChatWorkspaceConsole.tsx

ChatWorkspacePage.tsxPrime source focus on browse and guard individual unstage by active workspace +27/-2

Prime source focus on browse and guard individual unstage by active workspace

• Uses workspace store loading/error + focusSourceById when browsing. Adds an unstage handler that only mutates staged sources when the callback matches the current workspace id.

apps/packages/ui/src/components/Option/ChatWorkspace/ChatWorkspacePage.tsx

ContextStagingCard.tsxAdd per-source Remove action for staged context +18/-4

Add per-source Remove action for staged context

• Adds optional onRemoveSource and renders an accessible Remove button per staged source without clearing all context.

apps/packages/ui/src/components/Option/ChatWorkspace/ContextStagingCard.tsx

WorkspaceChatPanel.tsxExpose optional staged-source removal into ContextStagingCard +3/-0

Expose optional staged-source removal into ContextStagingCard

• Extends panel props with onRemoveStagedSource and forwards it to ContextStagingCard as onRemoveSource.

apps/packages/ui/src/components/Option/ChatWorkspace/WorkspaceChatPanel.tsx

WorkspaceRail.tsxComplete source rail lifecycle (links, status states, stage/unstage, empties) +67/-22

Complete source rail lifecycle (links, status states, stage/unstage, empties)

• Adds canonical Add/Open links, loading/error/empty messaging, explicit unavailable status, and toggles Stage vs Unstage actions while keeping non-actionable states disabled.

apps/packages/ui/src/components/Option/ChatWorkspace/WorkspaceRail.tsx

staging.tsAdd unstageWorkspaceSource helper +6/-0

Add unstageWorkspaceSource helper

• Introduces a small helper that removes a staged source by sourceId, enabling per-item unstage flows.

apps/packages/ui/src/components/Option/ChatWorkspace/staging.ts

useChatActions.tsEnsure workspace turns create/resume scoped server chats and thread scope throughout persistence +181/-11

Ensure workspace turns create/resume scoped server chats and thread scope throughout persistence

• Adds ensureWorkspaceServerChatForTurn to create/resume workspace-scoped server chats for normal and RAG turns, and threads scope into character chat create/message/stream/persist plus settings sync.

apps/packages/ui/src/hooks/chat/useChatActions.ts

chat-settings.tsAdd optional scope to syncChatSettingsForServerChat and pass through to API client +15/-4

Add optional scope to syncChatSettingsForServerChat and pass through to API client

• Extends syncChatSettingsForServerChat params with optional ChatScope and conditionally supplies scope options to get/update settings calls.

apps/packages/ui/src/services/chat-settings.ts

TldwApiClient.tsMake chat settings endpoints scope-aware via query parameters +10/-4

Make chat settings endpoints scope-aware via query parameters

• Extends getChatSettings/updateChatSettings signatures to accept optional scope and appends scope query params to the settings endpoint path.

apps/packages/ui/src/services/tldw/TldwApiClient.ts

Tests (9) +1350 / -8
ChatWorkspacePage.test.tsxCover browse focus and stale unstage callback behavior across workspace switches +60/-1

Cover browse focus and stale unstage callback behavior across workspace switches

• Mocks workspace store focusSourceById and verifies it's called on browse. Adds a regression test ensuring unstage callbacks from a previous workspace do not mutate the current workspace staged context.

apps/packages/ui/src/components/Option/ChatWorkspace/tests/ChatWorkspacePage.test.tsx

ContextStagingCard.test.tsxTest per-source removal from staged context +34/-0

Test per-source removal from staged context

• Adds coverage asserting Remove calls onRemoveSource for the targeted source and does not trigger a full clear.

apps/packages/ui/src/components/Option/ChatWorkspace/tests/ContextStagingCard.test.tsx

WorkspaceRail.test.tsxExpand rail tests for routing links, unstage, source states, and empty-state distinctions +191/-0

Expand rail tests for routing links, unstage, source states, and empty-state distinctions

• Adds tests for canonical source-management links, unstage behavior, ready-with-invalid-media stageability, disabled staging for non-ready states, and distinct loading/error/no-source/filter-empty copy.

apps/packages/ui/src/components/Option/ChatWorkspace/tests/WorkspaceRail.test.tsx

staging.test.tsAdd unit coverage for unstageWorkspaceSource +12/-1

Add unit coverage for unstageWorkspaceSource

• Adds a test verifying unstaging one source preserves other staged sources.

apps/packages/ui/src/components/Option/ChatWorkspace/tests/staging.test.ts

useChatActions.character.integration.test.tsxAssert workspace scope is propagated through character chat create/message/stream/persist +73/-4

Assert workspace scope is propagated through character chat create/message/stream/persist

• Reuses addChatMessage mock for assertions and adds a dedicated test proving workspace scope is passed to createChat, addChatMessage, streamCharacterChatCompletion, and persistCharacterCompletion.

apps/packages/ui/src/hooks/chat/tests/useChatActions.character.integration.test.tsx

useChatActions.persona.integration.test.tsxAdd regression coverage for workspace-scoped server chat bootstrap and global safety +238/-1

Add regression coverage for workspace-scoped server chat bootstrap and global safety

• Adds tests ensuring workspace-scoped persona creation, workspace server chat bootstrap for plain and staged-RAG sends, and a regression that global sends do not enter the workspace bootstrap path.

apps/packages/ui/src/hooks/chat/tests/useChatActions.persona.integration.test.tsx

chat-settings.sync.test.tsTest scope propagation in server chat settings sync +30/-0

Test scope propagation in server chat settings sync

• Adds a test verifying getChatSettings/updateChatSettings are invoked with { scope } during settings synchronization.

apps/packages/ui/src/services/tests/chat-settings.sync.test.ts

chat-workspace-live-backend.spec.tsAdd deterministic Playwright smoke spec for Chat Workspace live-backend flows +694/-0

Add deterministic Playwright smoke spec for Chat Workspace live-backend flows

• Introduces a full backend fixture that captures requests and simulates health/config/models/chats/RAG/streaming. Covers scoped sends with staged media, browse/unstage/re-stage workflow, stop generation during streaming, and error recovery preserving draft + fallback context.

apps/tldw-frontend/e2e/smoke/chat-workspace-live-backend.spec.ts

stage5-release-gate.spec.tsRequire focused smoke proof reference for /chat-workspace in Stage 5 gate +18/-1

Require focused smoke proof reference for /chat-workspace in Stage 5 gate

• Adds focusedProof metadata and asserts /chat-workspace explicitly references the dedicated Playwright smoke spec.

apps/tldw-frontend/e2e/smoke/stage5-release-gate.spec.ts

Documentation (6) +417 / -0
2026-06-26-chat-workspace-live-flow-implementation-plan.mdDocument staged implementation plan for workspace-scoped live chat flow +40/-0

Document staged implementation plan for workspace-scoped live chat flow

• Adds a six-stage plan describing audit, regression coverage, scoped persistence fixes, and verification steps for Chat Workspace live flow.

Docs/superpowers/plans/2026-06-26-chat-workspace-live-flow-implementation-plan.md

2026-06-30-chat-workspace-live-backend-smoke-plan.mdDocument plan for deterministic Chat Workspace live-backend Playwright smoke +46/-0

Document plan for deterministic Chat Workspace live-backend Playwright smoke

• Adds a staged plan to implement a realistic backend fixture, capture scoped requests, and require a release-gate reference for /chat-workspace proof.

Docs/superpowers/plans/2026-06-30-chat-workspace-live-backend-smoke-plan.md

2026-07-02-chat-workspace-source-rail-lifecycle-plan.mdDocument plan for source rail browse/stage/unstage lifecycle completion +75/-0

Document plan for source rail browse/stage/unstage lifecycle completion

• Adds test-first plan and design adjustments for source lifecycle states, canonical routing links, and workspace-switch stale callback guards.

Docs/superpowers/plans/2026-07-02-chat-workspace-source-rail-lifecycle-plan.md

task-12130 - Implement-Chat-Workspace-live-backend-flow-and-scoped-persistence.mdAdd tracker for scoped persistence + live flow completion (#2031) +73/-0

Add tracker for scoped persistence + live flow completion (#2031)

• Creates a completed backlog task with acceptance criteria, implementation notes, and verification evidence for the scoped chat flow work.

backlog/tasks/task-12130 - Implement-Chat-Workspace-live-backend-flow-and-scoped-persistence.md

task-12131 - Add-Chat-Workspace-live-backend-browser-smoke-coverage.mdAdd tracker for Playwright live-backend smoke + gate reference (#2035) +83/-0

Add tracker for Playwright live-backend smoke + gate reference (#2035)

• Creates a completed backlog task documenting the focused smoke spec, backend fixture expectations, and verification commands/results.

backlog/tasks/task-12131 - Add-Chat-Workspace-live-backend-browser-smoke-coverage.md

task-12132 - Complete-Chat-Workspace-source-rail-and-staged-context-lifecycle.mdAdd tracker for source rail lifecycle completion (#2032) +100/-0

Add tracker for source rail lifecycle completion (#2032)

• Creates a completed backlog task describing lifecycle requirements, touched files, test-first verification, and final behavior summary.

backlog/tasks/task-12132 - Complete-Chat-Workspace-source-rail-and-staged-context-lifecycle.md

Other (1) +21 / -0
smoke.setup.tsAllowlist expected /chat-workspace route-only startup errors pending focused proof +21/-0

Allowlist expected /chat-workspace route-only startup errors pending focused proof

• Adds time-bounded allowlist entries for connection-refused console/request noise on /chat-workspace when running route-only smoke without a backend.

apps/tldw-frontend/e2e/smoke/smoke.setup.ts

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request implements the Chat Workspace live backend flow, scoped persistence, and source rail lifecycle management, accompanied by extensive unit, integration, and Playwright smoke tests. The feedback identifies a missing tldwClient dependency in a React callback hook, warns against using standard anchor tags that trigger full page reloads and reset SPA state, and suggests simplifying redundant branching logic when passing optional scope parameters to the API client settings methods.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread apps/packages/ui/src/hooks/chat/useChatActions.ts Outdated
Comment thread apps/packages/ui/src/components/Option/ChatWorkspace/WorkspaceRail.tsx Outdated
Comment thread apps/packages/ui/src/services/chat-settings.ts Outdated
Comment thread apps/packages/ui/src/services/chat-settings.ts Outdated
Comment thread apps/packages/ui/src/services/chat-settings.ts Outdated
@qodo-code-review

qodo-code-review Bot commented Jul 3, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 74 rules

Grey Divider


Action required

1. Stale chat id reused ✓ Resolved 🐞 Bug ≡ Correctness
Description
ensureWorkspaceServerChatForTurn reuses any existing serverChatId in workspace scope without
confirming it belongs to the active workspace, so a previously-loaded global/other-workspace chat
can be reused instead of creating a workspace-scoped chat. This can write workspace turns into the
wrong server conversation and break scoped persistence guarantees.
Code

apps/packages/ui/src/hooks/chat/useChatActions.ts[R1375-1381]

+      if (resolvedServerChatId) {
+        const ensuredHistoryId = await ensureServerChatHistoryId(
+          resolvedServerChatId,
+          serverChatTitle || undefined
+        )
+        return { chatId: resolvedServerChatId, historyId: ensuredHistoryId }
+      }
Evidence
The new workspace bootstrap explicitly reuses an existing serverChatId without any scope check,
and the only scope-validation call site shown (validateCachedServerChatId) is guarded behind
!serverChatMetaLoaded, meaning a previously-loaded chat can remain set when scope changes and then
be reused by the new helper.

apps/packages/ui/src/hooks/chat/useChatActions.ts[1356-1381]
apps/packages/ui/src/hooks/useMessageOption.tsx[255-270]
apps/packages/ui/src/hooks/useMessageOption.tsx[435-516]
apps/packages/ui/src/hooks/chat/useServerChatLoader.ts[745-768]
apps/packages/ui/src/store/workspace-sync-contract.ts[151-173]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`ensureWorkspaceServerChatForTurn` returns an existing `serverChatId` for workspace scope without validating that the server chat’s `scope_type/workspace_id` matches the current `scope`. If `serverChatMetaLoaded` is already true from a prior route/scope, the loader’s scope validation may be skipped and the stale chat id persists.

### Issue Context
- Workspace bootstrap: reuses `resolvedServerChatId` directly.
- The chat-id scope validation in `useServerChatLoader` only runs when `serverChatMetaLoaded` is false.
- `useMessageOption` passes the same persisted `serverChatId` into `useChatActions` across scopes.

### Fix Focus Areas
- apps/packages/ui/src/hooks/chat/useChatActions.ts[1356-1381]
- apps/packages/ui/src/hooks/chat/useServerChatLoader.ts[745-768]
- apps/packages/ui/src/hooks/useMessageOption.tsx[255-270]

### Implementation direction
- Before reusing `resolvedServerChatId` in `ensureWorkspaceServerChatForTurn`, validate that it matches the current `scope` (e.g., `tldwClient.getChat(resolvedServerChatId, { scope })` + `validateCachedServerChatId`). If it fails, clear/reset the server chat state and proceed to create a new workspace chat.
- Alternatively (or additionally), make `useServerChatLoader` validate scope whenever `scope` changes even if `serverChatMetaLoaded` is true.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Workspace mirroring wrong scope ✓ Resolved 🐞 Bug ≡ Correctness
Description
Workspace sends now set conversationId/serverChatId to a workspace chat id, which activates
saveMessageOnSuccess server mirroring via tldwClient.addChatMessage. Those mirroring calls omit
{ scope }, so the API client defaults to scope_type=global, which can cause workspace chats to
be persisted under the wrong scope or fail to persist at all.
Code

apps/packages/ui/src/hooks/chat/useChatActions.ts[R3258-3274]

+          const workspaceServerChat = await ensureWorkspaceServerChatForTurn({
+            message,
+            serverChatIdOverride
+          })
+          const scopedNormalModeParams = workspaceServerChat.chatId
+            ? {
+                ...normalModeParams,
+                historyId:
+                  workspaceServerChat.historyId ?? normalModeParams.historyId,
+                serverChatId: workspaceServerChat.chatId,
+                conversationId: workspaceServerChat.chatId,
+                saveMessageOnSuccess: (data: SaveMessageData) =>
+                  saveMessageOnSuccess({
+                    ...data,
+                    conversationId: workspaceServerChat.chatId
+                  })
+              }
Evidence
The new workspace turn path explicitly sets conversationId/serverChatId to the workspace chat
id, which triggers the existing server mirroring logic. That logic calls addChatMessage without
options, while the API client derives scope from toChatScopeParams(options?.scope) which defaults
to global—so workspace turns are mirrored using the wrong scope.

apps/packages/ui/src/hooks/chat/useChatActions.ts[3258-3274]
apps/packages/ui/src/hooks/chat/useChatActions.ts[1126-1181]
apps/packages/ui/src/services/tldw/TldwApiClient.ts[5297-5310]
apps/packages/ui/src/types/chat-scope.ts[13-18]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
When the new workspace bootstrap forces a server conversation id for normal/RAG turns, `saveMessageOnSuccess` mirrors user/assistant messages to `/api/v1/chats/{id}/messages`. The mirroring path calls `tldwClient.addChatMessage(cid, payload)` without the `options` argument, but the API client always appends `scope_type` based on `toChatScopeParams(options?.scope)` (defaults to `global`). For workspace chats this produces the wrong scope.

### Issue Context
- Workspace bootstrap injects `conversationId` and a `serverChatId` for normal sends.
- The mirroring path uses `tldwClient.addChatMessage` without passing `{ scope }`.
- `toChatScopeParams(undefined)` defaults to `{ scope_type: "global" }`.

### Fix Focus Areas
- apps/packages/ui/src/hooks/chat/useChatActions.ts[3236-3285]
- apps/packages/ui/src/hooks/chat/useChatActions.ts[1126-1181]
- apps/packages/ui/src/services/tldw/TldwApiClient.ts[5297-5310]
- apps/packages/ui/src/types/chat-scope.ts[13-18]

### Implementation direction
- Update all server-mirroring `addChatMessage` calls inside `saveMessageOnSuccess` to pass scope:
 - `tldwClient.addChatMessage(cid, payload, scope ? { scope } : undefined)`
 - This includes the image-event mirror call (`mirroredMessage`) and the normal user/assistant mirroring calls.
- (Optional) Add a focused unit/integration test asserting workspace mirroring calls include `scope_type=workspace` + `workspace_id` in the request query/body as expected.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Date.now() makes test nondeterministic ✓ Resolved 📘 Rule violation ▣ Testability
Description
The new Playwright smoke test seeds tldwModelsCache using Date.now(), which is a
non-deterministic time source and can introduce timing-dependent/flaky behavior. The compliance
checklist requires tests to avoid non-deterministic APIs like Date.now unless fixed/mocked.
Code

apps/tldw-frontend/e2e/smoke/chat-workspace-live-backend.spec.ts[459]

+        timestamp: Date.now(),
Evidence
PR Compliance ID 380653 requires new/modified tests to avoid non-deterministic APIs such as
Date.now. The added smoke test uses timestamp: Date.now() when seeding tldwModelsCache.

Rule 380653: Tests must be deterministic and avoid non-deterministic sources
apps/tldw-frontend/e2e/smoke/chat-workspace-live-backend.spec.ts[457-460]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The Playwright smoke test uses `Date.now()` when seeding state, violating the requirement that tests avoid non-deterministic time sources.

## Issue Context
This file already uses a fixed ISO timestamp (`now = "2026-06-30T12:00:00.000Z"`) for deterministic fixture values; the cache seed should follow the same pattern.

## Fix Focus Areas
- apps/tldw-frontend/e2e/smoke/chat-workspace-live-backend.spec.ts[457-461]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

4. Hard navigation links ✓ Resolved 🐞 Bug ➹ Performance
Description
WorkspaceRail uses plain <a href> for internal routes (/research-workspace, /media), causing
full-page navigations instead of SPA routing. This adds reload cost and can drop in-memory state
more aggressively than router navigation.
Code

apps/packages/ui/src/components/Option/ChatWorkspace/WorkspaceRail.tsx[R105-110]

+          <a href={addSourceHref} className={buttonClass}>
+            Add source
+          </a>
+          <a href={openLibraryHref} className={buttonClass}>
+            Open library
+          </a>
Evidence
The new source-management actions are rendered as raw anchors, while other internal-navigation UI
uses React Router Link, indicating SPA navigation is the standard approach and that these anchors
will force a reload.

apps/packages/ui/src/components/Option/ChatWorkspace/WorkspaceRail.tsx[102-110]
apps/packages/ui/src/components/Common/NoProviderBanner.tsx[4-50]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Internal navigation in `WorkspaceRail` is implemented with `<a href=...>`, which triggers a full document navigation in a React Router SPA.

### Issue Context
Elsewhere in the codebase, internal navigation typically uses `react-router-dom`’s `Link` (or `useNavigate`) to avoid reloads.

### Fix Focus Areas
- apps/packages/ui/src/components/Option/ChatWorkspace/WorkspaceRail.tsx[102-112]

### Implementation direction
- Replace `<a href={...}>` with `<Link to={...}>` when a router context is present, or use `useNavigate` handlers.
- If this component must also render outside a router context, consider a small wrapper that uses `Link` when `useInRouterContext()` is true and falls back to `<a>` otherwise.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread apps/tldw-frontend/e2e/smoke/chat-workspace-live-backend.spec.ts Outdated
Comment thread apps/packages/ui/src/hooks/chat/useChatActions.ts
Comment thread apps/packages/ui/src/hooks/chat/useChatActions.ts
Comment thread apps/packages/ui/src/components/Option/ChatWorkspace/WorkspaceRail.tsx Outdated
@rmusser01 rmusser01 force-pushed the codex/chat-workspace-live-flow branch from 8ed28b5 to f580e39 Compare July 3, 2026 23:22
@rmusser01 rmusser01 merged commit 23044c0 into dev Jul 3, 2026
3 of 21 checks passed
@rmusser01 rmusser01 deleted the codex/chat-workspace-live-flow branch July 3, 2026 23:26
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