Skip to content

Wire Chat Workspace inspector and status rails#2600

Open
rmusser01 wants to merge 1 commit into
devfrom
codex/chat-workspace-status-rails
Open

Wire Chat Workspace inspector and status rails#2600
rmusser01 wants to merge 1 commit into
devfrom
codex/chat-workspace-status-rails

Conversation

@rmusser01

@rmusser01 rmusser01 commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Summary

  • Lift Chat Workspace send-failure and workspace-readiness runtime state into the inspector/status rails.
  • Replace inactive approval/task-progress placeholder panels with concrete runtime and recovery copy.
  • Extend component coverage and the live-backend browser smoke for visible streaming/failure rail states.

Test Plan

  • bunx vitest run src/components/Option/ChatWorkspace/__tests__ --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

Typecheck Note

  • NODE_OPTIONS=--max-old-space-size=8192 bunx tsc --noEmit -p tsconfig.json still fails on unrelated existing test type errors outside this touched scope: ChatGreetingPicker greetingSelectionChecksum, background-session-store quickIngestBatches, TldwChat.abort spread args, and character-export tuple cast.

Tracking

Closes #2033.
Refs #1239.

Merge Gate

AI-authored PR: maintainer needs to add the required human-written Change summary before merge.


Summary by cubic

Wired the Chat Workspace inspector and status rails to real runtime state (backend, workspace hydration, streaming, send failures) with clear recovery hints. Replaces placeholder panels and surfaces streaming/failure in the UI and tests.

  • New Features
    • Added sendError to ChatWorkspaceRuntimeState; WorkspaceChatPanel reports it and it’s lifted via ChatWorkspacePage/ChatWorkspaceConsole.
    • WorkspaceStatusStrip shows precedence: server unavailable → loading workspace context → send failed → streaming → ready; adds pills for reconnect, wait for workspace identity, select a model, no persona, and context staged.
    • InspectorRail now shows runtime label and recovery copy; adds model/persona guidance; removed “Approvals” and “Task Progress” placeholders.
    • Expanded tests: unit tests for rails/status and Playwright smoke to assert visible streaming and failed-send states.
    • Docs and backlog updated with the implementation plan; closes /chat-workspace: wire inspector and status rails to real runtime state #2033 (refs Epic: Complete Chat Workspace web UI implementation #1239).

Written for commit b29bb0a. 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: e022d94b-c268-4c74-82f9-650eb1961e7d

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:

  • ✅ Review completed - (🔄 Check again to review again)
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/chat-workspace-status-rails

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

@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 runtime state for the Chat Workspace status and inspector rails, addressing GitHub issue #2033. It introduces a sendError state to handle failed sends, tracks workspace readiness, and replaces placeholder panels in the inspector rail with real runtime and recovery copy. Corresponding unit and E2E tests have been added or updated. However, a critical issue was found where selectedModelLabel is used in InspectorRail but is neither defined in its props nor passed to it from ChatWorkspaceConsole, which will prevent the model recovery copy from rendering correctly.

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.

backendAvailable={backendAvailable}
workspaceReady={workspaceReady}
streaming={streaming}
sendError={sendError}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The selectedModelLabel prop is not being passed to InspectorRail. Inside InspectorRail, selectedModelLabel is used to determine modelRecoveryCopy (e.g., showing "Choose a model before sending." when no model is selected). Without passing this prop, selectedModelLabel will be undefined at runtime, and the recovery copy will never be displayed.

Please pass selectedModelLabel={selectedModelLabel} to <InspectorRail />.

            sendError={sendError}
            selectedModelLabel={selectedModelLabel}

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Wire Chat Workspace inspector/status rails to real runtime state

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Lift workspace readiness and send-failure runtime state into inspector and footer status rails.
• Replace placeholder inspector panels with concrete runtime precedence and recovery copy.
• Extend Vitest and Playwright smoke coverage for streaming and send-failure rail states.
Diagram

graph TD
  Types["Runtime contract"] --> Panel["WorkspaceChatPanel"] --> Page["ChatWorkspacePage"] --> Console["ChatWorkspaceConsole"]
  Console --> Status["WorkspaceStatusStrip"]
  Console --> Inspector["InspectorRail"]
  Vitest[["Vitest component tests"]] --> Status
  Vitest --> Inspector
  Playwright[["Playwright smoke"]] --> Status
  Playwright --> Inspector
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Centralize runtime/precedence into a shared selector
  • ➕ Single source of truth for label/precedence used by both InspectorRail and StatusStrip
  • ➕ Reduces future drift when adding new runtime states (e.g., auth-required, rate-limited)
  • ➕ Makes unit tests target one pure function instead of duplicating scenarios
  • ➖ Adds an abstraction layer that may feel heavy for a small surface area
  • ➖ Requires deciding a stable shared output model (labels, pills, recovery copy) early
2. Model runtime state as an enum + structured metadata
  • ➕ Clearer than stringly-typed labels and scattered boolean precedence
  • ➕ Enables consistent analytics/telemetry if desired (status codes)
  • ➖ More refactor churn across props/tests now
  • ➖ May be premature if states remain limited

Recommendation: Current approach (lift minimal runtime state and compute precedence locally in each rail) is reasonable and keeps changes within the Chat Workspace boundary. If additional rail states are expected soon, extract a shared selector for runtime label + recovery hints to prevent InspectorRail/StatusStrip precedence drift.

Files changed (12) +417 / -25

Enhancement (6) +135 / -19
ChatWorkspaceConsole.tsxPropagate workspace readiness and send failures into both rails +10/-0

Propagate workspace readiness and send failures into both rails

• Adds sendError to console props and derives workspaceReady from the normalized workspace id. Passes workspaceReady, sendError, and model/persona metadata into WorkspaceStatusStrip and InspectorRail.

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

ChatWorkspacePage.tsxPersist sendError in lifted Chat Workspace runtime state +2/-0

Persist sendError in lifted Chat Workspace runtime state

• Extends initial runtime state with sendError and forwards it into ChatWorkspaceConsole. Keeps send failures in the lifted runtime slice so rails can render failure state outside the composer.

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

InspectorRail.tsxReplace placeholder panels with runtime precedence + recovery copy +68/-13

Replace placeholder panels with runtime precedence + recovery copy

• Adds workspaceReady and sendError inputs and implements deterministic runtime precedence: offline → hydration → send failed → streaming/ready. Removes inactive Approvals/Task Progress panels and adds concise recovery hints for backend, hydration, send failure, and missing model/persona context.

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

WorkspaceChatPanel.tsxReport sendError through runtime state for rails to consume +2/-0

Report sendError through runtime state for rails to consume

• Includes sendError in onRuntimeStateChange so failures propagate beyond the composer panel. Send failures preserve draft/staged context and surface as both an alert in-panel and as runtime state.

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

WorkspaceStatusStrip.tsxAdd readiness/setup/failure status pills with precedence rules +52/-6

Add readiness/setup/failure status pills with precedence rules

• Extends the status strip to accept workspaceReady, sendError, model/persona metadata, and assistant source. Implements precedence (offline/hydration/send failed/streaming/ready) and adds actionable pills (Reconnect server, Wait for workspace identity, Select a model, No persona).

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

types.tsExtend ChatWorkspaceRuntimeState with sendError +1/-0

Extend ChatWorkspaceRuntimeState with sendError

• Adds optional sendError to the shared runtime contract so send failures can be lifted and rendered outside the composer. Enables consistent state propagation across page/console/rails.

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

Tests (4) +167 / -6
InspectorRail.test.tsxCover inspector runtime precedence and recovery copy +53/-3

Cover inspector runtime precedence and recovery copy

• Updates tests to assert removal of placeholder panels and validate recovery copy when backend is unavailable. Adds coverage for hydration-not-ready precedence and inspector surfacing of send failures plus missing model/persona guidance.

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

WorkspaceChatPanel.test.tsxVerify failed sends are reported via runtime state +28/-0

Verify failed sends are reported via runtime state

• Adds a test ensuring a failed submit reports sendError through onRuntimeStateChange. Validates that send failure messages propagate to lifted runtime state consumers.

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

WorkspaceStatusStrip.test.tsxAssert status strip precedence and setup pills +70/-3

Assert status strip precedence and setup pills

• Updates existing cases to pass new props and adds coverage for hydration-before-ready and send failure/missing-model/persona pill rendering. Ensures backend unavailable takes precedence over streaming.

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

chat-workspace-live-backend.spec.tsSmoke assert rails show Streaming and Send failed states +16/-0

Smoke assert rails show Streaming and Send failed states

• Extends live-backend smoke coverage to assert the footer status strip and inspector reflect Streaming while generation is active. Adds assertions that send failure renders 'Send failed' in status and retry-preservation copy in the inspector.

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

Documentation (2) +115 / -0
2026-07-03-chat-workspace-status-rails-plan.mdAdd implementation plan for wiring Chat Workspace rails to runtime state +56/-0

Add implementation plan for wiring Chat Workspace rails to runtime state

• Introduces a staged plan documenting the intended runtime contract changes, rail behavior, and required tests. Serves as an execution checklist for implementing issue #2033.

Docs/superpowers/plans/2026-07-03-chat-workspace-status-rails-plan.md

task-12135 - Implement-Chat-Workspace-inspector-and-status-runtime-state.mdAdd backlog task record for #2033 implementation +59/-0

Add backlog task record for #2033 implementation

• Creates a tracked task entry referencing issues #2033 and #1239 and listing modified files. Includes an embedded final summary of the implemented runtime/rail behavior and verification notes.

backlog/tasks/task-12135 - Implement-Chat-Workspace-inspector-and-status-runtime-state.md

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

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

Context used
✅ Compliance rules (platform): 74 rules

Grey Divider


Action required

1. Stop test lacks halt assertion 📎 Requirement gap ☼ Reliability
Description
The Playwright test for stop-generation only asserts the stop button becomes hidden, but it does not
verify that streaming output/tokens stop and the UI stops updating. This does not meet the
compliance requirement to prove generation halts during active streaming.
Code

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

+    await expect(
+      page.getByLabel("Chat workspace status").getByText("Streaming")
+    ).toBeVisible()
+    await expect(
+      page
+        .getByRole("complementary", { name: "Chat workspace inspector" })
+        .getByText("Streaming")
+    ).toBeVisible()
Evidence
PR Compliance ID 380688 requires the stop-generation E2E test to verify generation halts during
streaming. In chat-workspace-live-backend.spec.ts, the modified stop-generation test adds
assertions that streaming is visible, but after clicking stop it only asserts the stop button is
hidden and does not assert that streamed content stops/does not appear or that the UI stops
updating.

Playwright verifies stop generation while streaming is active
apps/tldw-frontend/e2e/smoke/chat-workspace-live-backend.spec.ts[660-695]

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 `/chat-workspace` Playwright test `shows stop generation while the workspace stream is active` clicks `Stop generating`, but it only checks the button becomes hidden and does not prove that generation actually halts (no further streamed content/updates).

## Issue Context
Compliance requires an explicit verification that stopping generation halts streaming updates, not just that a control disappeared.

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

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



Remediation recommended

2. Unsafe workspaceReady default 🐞 Bug ≡ Correctness
Description
InspectorRail and WorkspaceStatusStrip default workspaceReady to true while keeping it optional,
so any caller omission will silently render “Ready” even if workspace identity is still hydrating.
This is a correctness footgun that can mask missing wiring and make the rails disagree with the
actual send guard logic.
Code

apps/packages/ui/src/components/Option/ChatWorkspace/WorkspaceStatusStrip.tsx[R37-46]

export const WorkspaceStatusStrip = ({
  backendAvailable,
+  workspaceReady = true,
  streaming,
-  stagedSourceCount
+  sendError,
+  stagedSourceCount,
+  selectedModelLabel,
+  selectedPersonaLabel,
+  assistantSource
}: WorkspaceStatusStripProps) => {
-  const runtimeLabel = !backendAvailable
-    ? "Server unavailable"
-    : streaming
-      ? "Streaming"
-      : READY_STATE_LABEL
Evidence
Both rails components treat readiness as optional and default it to true, which can silently
misrepresent hydration state if omitted. The only current call site passes workspaceReady,
demonstrating the prop is always available and can be required for safety.

apps/packages/ui/src/components/Option/ChatWorkspace/WorkspaceStatusStrip.tsx[4-13]
apps/packages/ui/src/components/Option/ChatWorkspace/WorkspaceStatusStrip.tsx[37-46]
apps/packages/ui/src/components/Option/ChatWorkspace/InspectorRail.tsx[10-22]
apps/packages/ui/src/components/Option/ChatWorkspace/InspectorRail.tsx[92-104]
apps/packages/ui/src/components/Option/ChatWorkspace/ChatWorkspaceConsole.tsx[108-133]

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

### Issue description
`workspaceReady` is optional and defaults to `true` in both `WorkspaceStatusStrip` and `InspectorRail`. If a future refactor or alternate call site omits the prop, the UI will incorrectly show a hydrated/ready state.

### Issue Context
These components are currently only used from `ChatWorkspaceConsole`, which *does* pass `workspaceReady`, but the component APIs now allow silent misrendering.

### Fix Focus Areas
- apps/packages/ui/src/components/Option/ChatWorkspace/WorkspaceStatusStrip.tsx[4-13]
- apps/packages/ui/src/components/Option/ChatWorkspace/WorkspaceStatusStrip.tsx[37-46]
- apps/packages/ui/src/components/Option/ChatWorkspace/InspectorRail.tsx[10-22]
- apps/packages/ui/src/components/Option/ChatWorkspace/InspectorRail.tsx[92-104]

### Suggested change
- Make `workspaceReady` required (`workspaceReady: boolean`) in both prop types.
- Remove the default initializer (`workspaceReady = true`) so TS forces callers to pass the real readiness.
- (Alternative if you truly need optional) default to `false` rather than `true` so missing wiring degrades safely to “Loading workspace context.”

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



Informational

3. Model state via label string 🐞 Bug ⚙ Maintainability
Description
WorkspaceStatusStrip infers “model selected” by comparing selectedModelLabel against the display
string "No model selected", coupling state logic to presentation text. This is brittle and can lead
to incorrect status pills/recovery copy if the label ever changes or is generated differently.
Code

apps/packages/ui/src/components/Option/ChatWorkspace/WorkspaceStatusStrip.tsx[R53-54]

+  const hasModel = selectedModelLabel ? selectedModelLabel !== NO_MODEL_LABEL : true
+  const hasPersona =
Evidence
The status strip’s hasModel logic is driven by a string sentinel, and the chat panel is
responsible for manufacturing that sentinel label. This is state derived from presentation text
rather than a dedicated boolean/enum.

apps/packages/ui/src/components/Option/ChatWorkspace/WorkspaceStatusStrip.tsx[18-20]
apps/packages/ui/src/components/Option/ChatWorkspace/WorkspaceStatusStrip.tsx[47-57]
apps/packages/ui/src/components/Option/ChatWorkspace/WorkspaceChatPanel.tsx[199-206]

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 code uses the UI label string `"No model selected"` as a sentinel for model selection state. This makes logic correctness depend on a specific string value.

### Issue Context
- `WorkspaceChatPanel` emits `selectedModelLabel: selectedModel || "No model selected"`.
- `WorkspaceStatusStrip` then computes `hasModel` by checking label equality with the same string.

### Fix Focus Areas
- apps/packages/ui/src/components/Option/ChatWorkspace/WorkspaceStatusStrip.tsx[18-20]
- apps/packages/ui/src/components/Option/ChatWorkspace/WorkspaceStatusStrip.tsx[47-57]
- apps/packages/ui/src/components/Option/ChatWorkspace/WorkspaceChatPanel.tsx[199-206]

### Suggested change
Prefer one of:
1) Add a boolean to the runtime contract, e.g. `hasModelSelected: boolean`, set it in `WorkspaceChatPanel` from the real state (`Boolean(selectedModel)`), and use that for pills/copy.
2) Centralize the sentinel into a single exported constant (shared module) so the value cannot drift across components.

Option (1) is safest because it removes string-based state entirely.

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


Grey Divider

ⓘ  2 issues published inline · 3 in summary

Qodo Logo

Comment on lines +674 to +681
await expect(
page.getByLabel("Chat workspace status").getByText("Streaming")
).toBeVisible()
await expect(
page
.getByRole("complementary", { name: "Chat workspace inspector" })
.getByText("Streaming")
).toBeVisible()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Stop test lacks halt assertion 📎 Requirement gap ☼ Reliability

The Playwright test for stop-generation only asserts the stop button becomes hidden, but it does not
verify that streaming output/tokens stop and the UI stops updating. This does not meet the
compliance requirement to prove generation halts during active streaming.
Agent Prompt
## Issue description
The `/chat-workspace` Playwright test `shows stop generation while the workspace stream is active` clicks `Stop generating`, but it only checks the button becomes hidden and does not prove that generation actually halts (no further streamed content/updates).

## Issue Context
Compliance requires an explicit verification that stopping generation halts streaming updates, not just that a control disappeared.

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

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

Comment on lines 37 to 46
export const WorkspaceStatusStrip = ({
backendAvailable,
workspaceReady = true,
streaming,
stagedSourceCount
sendError,
stagedSourceCount,
selectedModelLabel,
selectedPersonaLabel,
assistantSource
}: WorkspaceStatusStripProps) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

2. Unsafe workspaceready default 🐞 Bug ≡ Correctness

InspectorRail and WorkspaceStatusStrip default workspaceReady to true while keeping it optional,
so any caller omission will silently render “Ready” even if workspace identity is still hydrating.
This is a correctness footgun that can mask missing wiring and make the rails disagree with the
actual send guard logic.
Agent Prompt
### Issue description
`workspaceReady` is optional and defaults to `true` in both `WorkspaceStatusStrip` and `InspectorRail`. If a future refactor or alternate call site omits the prop, the UI will incorrectly show a hydrated/ready state.

### Issue Context
These components are currently only used from `ChatWorkspaceConsole`, which *does* pass `workspaceReady`, but the component APIs now allow silent misrendering.

### Fix Focus Areas
- apps/packages/ui/src/components/Option/ChatWorkspace/WorkspaceStatusStrip.tsx[4-13]
- apps/packages/ui/src/components/Option/ChatWorkspace/WorkspaceStatusStrip.tsx[37-46]
- apps/packages/ui/src/components/Option/ChatWorkspace/InspectorRail.tsx[10-22]
- apps/packages/ui/src/components/Option/ChatWorkspace/InspectorRail.tsx[92-104]

### Suggested change
- Make `workspaceReady` required (`workspaceReady: boolean`) in both prop types.
- Remove the default initializer (`workspaceReady = true`) so TS forces callers to pass the real readiness.
- (Alternative if you truly need optional) default to `false` rather than `true` so missing wiring degrades safely to “Loading workspace context.”

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

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