Skip to content

Add Workspace Assistant Defaults V1#2316

Merged
rmusser01 merged 12 commits into
devfrom
codex/workspace-assistant-defaults-web-client
Jul 3, 2026
Merged

Add Workspace Assistant Defaults V1#2316
rmusser01 merged 12 commits into
devfrom
codex/workspace-assistant-defaults-web-client

Conversation

@rmusser01

@rmusser01 rmusser01 commented Jun 8, 2026

Copy link
Copy Markdown
Owner

PR Summary by Qodo

Add Workspace Assistant Defaults V1
✨ Enhancement 🧪 Tests 🕐 40+ Minutes

Grey Divider

Walkthroughs

User Description

Summary

  • Adds Workspace Assistant Defaults V1 across backend effective-state API, WebUI client/store mapping, and Research Workspace settings.
  • Applies available Workspace Persona defaults to fresh Chat Workspace startup while preserving explicit assistant selections and existing chat assistant metadata.
  • Adds inspector/status copy for explicit, inherited, unavailable, and unset assistant states.
  • Closes out the Workspace Assistant Defaults backlog task chain and PRD V1 status.

Change summary

Maintainer note: this PR is materially AI-authored. Please replace or supplement this section with the required human-owned change summary before merge.

Verification

  • python -m pytest tldw_Server_API/tests/Workspaces/test_workspace_assistant_defaults_api.py tldw_Server_API/tests/Chat/unit/test_chat_conversations_api.py -q -> 22 passed.
  • python -m pytest tldw_Server_API/tests/ChaChaNotesDB/test_workspace_assistant_defaults_db.py tldw_Server_API/tests/Workspaces/test_workspace_assistant_defaults_api.py tldw_Server_API/tests/Workspaces/test_workspaces_api.py tldw_Server_API/tests/ChaChaNotesDB/test_workspace_sub_resources_db.py -q -> 115 passed.
  • vitest run focused Workspace Assistant Defaults frontend suite -> 120 passed.
  • tsc --noEmit in apps/packages/ui -> passed.
  • git diff --check -> passed.
  • bandit on touched backend workspace schema/endpoint/DB scope -> no findings.
AI Description
• Adds WorkspaceAssistantDefaults and EffectiveWorkspaceAssistantDefault types, API
  normalization/serialization, and workspace store state to carry and persist Persona default
  references without leaking resolved labels.
• Exposes effective_assistant_default on all Workspace API responses (get/list/patch/context);
  validates Persona references on PATCH and rejects missing/deleted/inactive Personas with a 422.
• Applies available Workspace Persona defaults to fresh Chat Workspace startup via
  useMessageOption/useChatActions, preserving explicit assistant selections and existing chat
  session metadata.
• Adds a "Default assistant" settings modal in WorkspaceHeader for selecting, configuring memory
  mode (read-only/read-write with confirmation), saving, and clearing the Workspace Persona default.
• Adds inspector/status copy in InspectorRail distinguishing explicit, inherited-from-workspace,
  unavailable, and no-persona states.
• Closes out the Workspace Assistant Defaults backlog task chain (TASK-2318.1–2318.5) and updates
  the PRD status to V1.
Diagram
graph TD
    subgraph Backend["Backend (Python)"]
        WS_EP["workspaces.py\nEndpoint"] --> WS_SCH["workspace_schemas.py\nSchemas"]
        WS_EP --> DB[("CharactersRAGDB")]
    end

    subgraph APIClient["API Client (TS)"]
        WS_API["workspace-api.ts\nNormalize / Serialize"]
    end

    subgraph Store["Workspace Store"]
        WS_STORE["workspace.ts\nState + Snapshots"]
        WS_LIST["workspace-list-slice.ts"]
    end

    subgraph ChatWorkspace["Chat Workspace UI"]
        CWP["ChatWorkspacePage"] --> CWC["ChatWorkspaceConsole"]
        CWC --> WCP["WorkspaceChatPanel"]
        CWC --> IR["InspectorRail"]
        WCP --> UMO["useMessageOption"]
        UMO --> UCA["useChatActions"]
        UCA --> PSC["personaServerChat"]
    end

    subgraph ResearchWorkspace["Research Workspace UI"]
        WH["WorkspaceHeader\nDefault Assistant Modal"]
    end

    DB -- "effective_assistant_default" --> WS_EP
    WS_EP -- "WorkspaceResponse" --> WS_API
    WS_API -- "normalized response" --> WS_STORE
    WS_STORE -- "effectiveAssistantDefault" --> CWP
    WS_STORE -- "assistantDefaults" --> WH
    WH -- "PATCH assistantDefaults" --> WS_API

    subgraph Legend
        direction LR
        _db[("Database")] ~~~ _svc["Service/Hook"] ~~~ _ui(["UI Component"])
    end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. React context for effectiveAssistantDefault
  • ➕ Eliminates prop-drilling through ChatWorkspaceConsole
  • ➕ Easier to consume in deeply nested components without prop threading
  • ➖ Inconsistent with existing workspace prop-threading pattern
  • ➖ Adds a new context boundary that may complicate testing

Recommendation: The PR's approach — threading effectiveAssistantDefault from the store through props into WorkspaceChatPanel and useMessageOption — is sound and keeps the inheritance logic close to the chat submission path. One alternative worth considering is a dedicated React context for workspace assistant defaults rather than prop-drilling through Console → Panel, which would reduce the prop surface area. However, the current approach is consistent with how other workspace state (e.g., backendAvailable, stagedSources) is already threaded, so deviating would be inconsistent. The decision to exclude effectiveAssistantDefault from persisted snapshots (only persisting assistantDefaults) is correct and avoids stale resolved labels in localStorage.

Grey Divider

File Changes

Enhancement (14)
workspace.ts Add Workspace Assistant Defaults type definitions +34/-0

Add Workspace Assistant Defaults type definitions

• Adds 'WorkspaceAssistantKind', 'WorkspacePersonaMemoryMode', 'WorkspaceAssistantDefaults', 'EffectiveWorkspaceAssistantDefault', and related status/source/degraded-reason union types to the shared workspace type module.

apps/packages/ui/src/types/workspace.ts


workspace-api.ts Normalize and serialize Workspace Assistant Defaults in API client +210/-5

Normalize and serialize Workspace Assistant Defaults in API client

• Adds 'normalizeWorkspaceAssistantDefaults', 'normalizeEffectiveWorkspaceAssistantDefault', 'normalizeWorkspaceApiResponse', and 'serializeWorkspacePatchRequest' helpers. All workspace API methods (list, get, upsert, patch) now run responses through 'normalizeWorkspaceApiResponse' and patch payloads through 'serializeWorkspacePatchRequest' to translate between snake_case backend and camelCase frontend.

apps/packages/ui/src/services/tldw/domains/workspace-api.ts


workspace.ts Add assistantDefaults and effectiveAssistantDefault to workspace store +62/-1

Add assistantDefaults and effectiveAssistantDefault to workspace store

• Extends 'WorkspaceIdentityState', 'WorkspaceSnapshot', and 'WorkspaceUndoSnapshot' with 'assistantDefaults' and 'effectiveAssistantDefault' fields. Adds 'coerceWorkspaceAssistantDefaultsForRehydrate' for safe deserialization from localStorage. Snapshot build/apply/revive/duplicate paths all carry 'assistantDefaults'; 'effectiveAssistantDefault' is intentionally excluded from persisted snapshots.

apps/packages/ui/src/store/workspace.ts


workspace-list-slice.ts Rehydrate assistantDefaults when switching workspaces +5/-0

Rehydrate assistantDefaults when switching workspaces

• Calls 'coerceWorkspaceAssistantDefaultsForRehydrate' when applying a cloned workspace snapshot so that assistant defaults are safely coerced on workspace switch.

apps/packages/ui/src/store/workspace-slices/workspace-list-slice.ts


useMessageOption.tsx Accept inheritedAssistant/inheritedPersonaMemoryMode options and expose selectedAssistantSource +69/-3

Accept inheritedAssistant/inheritedPersonaMemoryMode options and expose selectedAssistantSource

• Adds 'UseMessageOptionOptions' type with 'inheritedAssistant' and 'inheritedPersonaMemoryMode' fields. Computes an 'inheritedAssistant' memo that activates only for fresh plain-mode chats with no existing session or explicit selection. Exposes 'selectedAssistantSource' ('workspace' | 'explicit' | 'none') and passes inherited assistant/memory-mode through to 'useChatActions'.

apps/packages/ui/src/hooks/useMessageOption.tsx


useChatActions.ts Route inherited workspace persona default through chat send path +89/-17

Route inherited workspace persona default through chat send path

• Adds 'inheritedAssistant' and 'inheritedPersonaMemoryMode' options. Computes 'inheritedTrackedAssistant' (active only for fresh chats with no existing session/character/history) and 'selectedPersonaMemoryMode'. Replaces 'selectedAssistant' with 'routingSelectedAssistant' throughout send-mode resolution and persona server-chat creation. Passes 'requestedPersonaMemoryMode' to 'ensurePersonaServerChatWithState'.

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


personaServerChat.ts Accept requestedPersonaMemoryMode when creating persona server chats +11/-2

Accept requestedPersonaMemoryMode when creating persona server chats

• Adds 'requestedPersonaMemoryMode' parameter to 'EnsurePersonaServerChatArgs' and 'ensurePersonaServerChat'. New chats use the requested mode (defaulting to 'read_only') instead of always defaulting; existing matching chats preserve their stored mode.

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


types.ts Add ChatWorkspaceAssistantSource type and extend runtime state +12/-1

Add ChatWorkspaceAssistantSource type and extend runtime state

• Defines 'ChatWorkspaceAssistantSource' union ('explicit' | 'workspace' | 'none' | 'unavailable') and adds 'assistantSource' and 'workspaceAssistantDegradedReason' fields to 'ChatWorkspaceRuntimeState'.

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


WorkspaceChatPanel.tsx Apply workspace persona default on first submit and report assistant source +118/-6

Apply workspace persona default on first submit and report assistant source

• Accepts 'effectiveAssistantDefault' prop and builds an 'inheritedAssistant' from it when the workspace is ready and no explicit assistant or existing session exists. Computes 'assistantSource' and 'runtimeSelectedPersonaLabel' for the inspector. Injects 'assistant_kind', 'assistant_id', and 'persona_memory_mode' into the submit payload when using the workspace default.

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


ChatWorkspacePage.tsx Thread effectiveAssistantDefault from store into ChatWorkspaceConsole +19/-3

Thread effectiveAssistantDefault from store into ChatWorkspaceConsole

• Reads 'effectiveAssistantDefault' from the workspace store and passes it to 'createInitialRuntimeState' (setting initial 'assistantSource' to 'unavailable' when appropriate) and down to 'ChatWorkspaceConsole'.

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


ChatWorkspaceConsole.tsx Forward assistantSource and effectiveAssistantDefault to child panels +16/-1

Forward assistantSource and effectiveAssistantDefault to child panels

• Adds 'assistantSource', 'workspaceAssistantDegradedReason', and 'effectiveAssistantDefault' props and threads them to 'InspectorRail' and 'WorkspaceChatPanel' respectively.

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


InspectorRail.tsx Show assistant source and degraded-reason labels in the inspector +47/-1

Show assistant source and degraded-reason labels in the inspector

• Adds 'assistantSource' and 'workspaceAssistantDegradedReason' props. Renders 'Inherited from workspace', 'Explicit persona', 'Workspace default unavailable', or a degraded-reason label depending on the source state, replacing the previous single persona-name line.

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


workspaces.py Validate Persona references and emit effective_assistant_default on all workspace responses +135/-7

Validate Persona references and emit effective_assistant_default on all workspace responses

• Adds '_parse_workspace_assistant_defaults', '_effective_workspace_assistant_default', and '_validate_workspace_assistant_default_reference' helpers. Updates '_ws_to_response' to accept 'db' and 'current_user', resolve the effective default, and include it in every response. PATCH now validates that the referenced Persona is active and accessible before writing.

tldw_Server_API/app/api/v1/endpoints/workspaces.py


workspace_schemas.py Make effective_assistant_default a required field on WorkspaceResponse +1/-1

Make effective_assistant_default a required field on WorkspaceResponse

• Changes 'effective_assistant_default' from 'Optional' to a required non-nullable field on 'WorkspaceResponse', enforcing that all response paths always include the effective default.

tldw_Server_API/app/api/v1/schemas/workspace_schemas.py


Bug fix (1)
task-2318 - Plan-Workspace-Assistant-Defaults-implementation.md Fix task ID typo in planning task +1/-1

Fix task ID typo in planning task

• Corrects the task 'id' field from TASK-2276 to TASK-2318.

backlog/tasks/task-2318 - Plan-Workspace-Assistant-Defaults-implementation.md


Tests (10)
test_workspace_assistant_defaults_api.py New API integration tests for Workspace Assistant Defaults effective state +241/-0

New API integration tests for Workspace Assistant Defaults effective state

• Adds four integration tests covering: PATCH returning available effective default for an existing Persona, GET/LIST including effective default, PATCH rejecting a missing Persona reference, and GET redacting a deleted Persona as 'persona_deleted' drift.

tldw_Server_API/tests/Workspaces/test_workspace_assistant_defaults_api.py


test_workspaces_api.py Create real Persona profiles in existing workspace assistant default tests +24/-0

Create real Persona profiles in existing workspace assistant default tests

• Adds '_create_workspace_test_persona' helper and calls it in existing assistant-defaults test cases so they reference real Persona records instead of dangling IDs.

tldw_Server_API/tests/Workspaces/test_workspaces_api.py


tldw-api-client.workspace-api.test.ts Test workspace assistant defaults response mapping and patch serialization +75/-0

Test workspace assistant defaults response mapping and patch serialization

• Adds a test verifying that 'patchWorkspace' correctly maps 'assistant_defaults' and 'effective_assistant_default' response fields to camelCase and serializes the patch payload body to snake_case including 'confirm_read_write_assistant_default'.

apps/packages/ui/src/services/tests/tldw-api-client.workspace-api.test.ts


workspace.test.ts Test that effective assistant labels are not persisted in workspace snapshots +46/-0

Test that effective assistant labels are not persisted in workspace snapshots

• Adds a test confirming that 'assistantDefaults' is persisted in workspace snapshots while 'effectiveAssistantDefault' (including resolved labels) is excluded from localStorage.

apps/packages/ui/src/store/tests/workspace.test.ts


WorkspaceChatPanel.test.tsx Add tests for workspace persona default inheritance and override logic +185/-5

Add tests for workspace persona default inheritance and override logic

• Adds four new test cases: inheriting an available workspace default on first submit, explicit persona overriding the workspace default, not inheriting an unavailable default, and not mutating existing chat assistant metadata when the workspace default changes.

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


InspectorRail.test.tsx Add tests for inherited workspace and unavailable assistant source labels +42/-0

Add tests for inherited workspace and unavailable assistant source labels

• Adds tests verifying 'Inherited from workspace' label for workspace-sourced persona, 'Explicit persona' label for explicit selection, and 'Workspace default unavailable' + degraded reason for unavailable state without duplicating persona content.

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


ChatWorkspacePage.test.tsx Update ChatWorkspacePage tests with effectiveAssistantDefault fixture data +30/-4

Update ChatWorkspacePage tests with effectiveAssistantDefault fixture data

• Adds 'effectiveAssistantDefault' to workspace state fixtures and verifies that the effective assistant ID is forwarded to the chat panel via a data attribute.

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


WorkspaceHeader.test.tsx Add WorkspaceHeader tests for default assistant settings modal +271/-0

Add WorkspaceHeader tests for default assistant settings modal

• Adds four integration tests covering: opening the modal and saving a read-only Persona default, requiring confirmation before saving read-write, clearing the default, and redacting unavailable default assistant labels in the modal.

apps/packages/ui/src/components/Option/ResearchWorkspace/tests/WorkspaceHeader.test.tsx


personaServerChat.test.ts Test requestedPersonaMemoryMode propagation in persona server chat creation +50/-0

Test requestedPersonaMemoryMode propagation in persona server chat creation

• Adds a test verifying that 'ensurePersonaServerChat' uses the 'requestedPersonaMemoryMode' ('read_write') when creating a new persona-backed chat and sets the store state accordingly.

apps/packages/ui/src/hooks/chat/tests/personaServerChat.test.ts


useChatActions.persona.integration.test.tsx Test inherited workspace persona default routing through useChatActions +72/-0

Test inherited workspace persona default routing through useChatActions

• Adds an integration test verifying that an inherited workspace persona default (with no explicit assistant selected) is routed through 'createChat' with the correct 'assistant_kind', 'assistant_id', and 'persona_memory_mode', and that the playground session is persisted with the workspace persona metadata.

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


Documentation (7)
Workspace_Persona_Defaults_PRD.md Update PRD status to V1 implemented +1/-1

Update PRD status to V1 implemented

• Changes the PRD status from 'Draft' to 'V1 implemented for Workspace schema/API, Workspace settings, and Chat Workspace startup. Later Workspace-bound surfaces remain future adoption stages.'

Docs/Product/Workspace_Persona_Defaults_PRD.md


task-2318.1 - Map-Workspace-Assistant-Defaults-in-WebUI-client-and-store.md Add completed backlog task for WebUI client/store mapping +64/-0

Add completed backlog task for WebUI client/store mapping

• New backlog task document (TASK-2318.1) recording acceptance criteria, implementation notes, and final summary for the WebUI client and store mapping slice.

backlog/tasks/task-2318.1 - Map-Workspace-Assistant-Defaults-in-WebUI-client-and-store.md


task-2318.2 - Expose-Workspace-Assistant-Defaults-API-validation-and-effective-state.md Add completed backlog task for backend API validation and effective state +63/-0

Add completed backlog task for backend API validation and effective state

• New backlog task document (TASK-2318.2) recording acceptance criteria, implementation notes, and final summary for the backend API slice.

backlog/tasks/task-2318.2 - Expose-Workspace-Assistant-Defaults-API-validation-and-effective-state.md


task-2318.3 - Add-Workspace-default-assistant-settings-UI.md Add completed backlog task for default assistant settings UI +68/-0

Add completed backlog task for default assistant settings UI

• New backlog task document (TASK-2318.3) recording acceptance criteria, implementation notes, and final summary for the settings UI slice.

backlog/tasks/task-2318.3 - Add-Workspace-default-assistant-settings-UI.md


task-2318.4 - Apply-Workspace-Persona-defaults-to-Chat-Workspace-startup.md Add completed backlog task for Chat Workspace startup defaults +64/-0

Add completed backlog task for Chat Workspace startup defaults

• New backlog task document (TASK-2318.4) recording acceptance criteria, implementation notes, and final summary for the Chat Workspace startup slice.

backlog/tasks/task-2318.4 - Apply-Workspace-Persona-defaults-to-Chat-Workspace-startup.md


task-2318.5 - Close-out-Workspace-Assistant-Defaults-V1-verification.md Add completed backlog task for V1 closeout verification +70/-0

Add completed backlog task for V1 closeout verification

• New backlog task document (TASK-2318.5) recording closeout verification commands, results, and final summary for the full V1 implementation.

backlog/tasks/task-2318.5 - Close-out-Workspace-Assistant-Defaults-V1-verification.md


task-2278 - Implement-Workspace-Assistant-Defaults-backend-storage.md Mark backend storage task as Done +2/-2

Mark backend storage task as Done

• Updates TASK-2278 status from 'In Progress' to 'Done' and updates the modification timestamp.

backlog/tasks/task-2278 - Implement-Workspace-Assistant-Defaults-backend-storage.md


Other (1)
WorkspaceHeader.tsx Add Default Assistant settings modal to workspace header +417/-1

Add Default Assistant settings modal to workspace header

• Adds a 'Default assistant' entry to the workspace settings menu that opens a modal for selecting a Persona, configuring memory mode (read-only/read-write with explicit confirmation checkbox), saving, and clearing the workspace default. Handles loading, saving, error, and redacted-unavailable states. Calls 'tldwClient.getWorkspace', 'listPersonaProfiles', and 'patchWorkspace'.

apps/packages/ui/src/components/Option/ResearchWorkspace/WorkspaceHeader.tsx


Grey Divider

Qodo Logo

@coderabbitai

coderabbitai Bot commented Jun 8, 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: 0b90a5b6-1763-4046-9695-c196b162537c

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/workspace-assistant-defaults-web-client

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

@qodo-code-review

qodo-code-review Bot commented Jun 8, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Stale defaults modal state ✓ Resolved 🐞 Bug ☼ Reliability
Description
WorkspaceHeader.handleOpenDefaultAssistantModal does not clear prior modal state on open/fetch
failure, so handleSaveDefaultAssistant can PATCH using a stale
defaultAssistantWorkspace.version/persona selection from a previous open. This can lead to incorrect
saves (or avoidable optimistic-lock conflicts) when the modal is reopened during transient API
errors or after switching workspaces.
Code

apps/packages/ui/src/components/Option/ResearchWorkspace/WorkspaceHeader.tsx[R604-706]

+  const handleOpenDefaultAssistantModal = async () => {
+    if (!workspaceId) return
+
+    setDefaultAssistantModalOpen(true)
+    setDefaultAssistantLoading(true)
+    setDefaultAssistantError(null)
+    try {
+      const [workspace, personas] = await Promise.all([
+        tldwClient.getWorkspace(workspaceId),
+        tldwClient.listPersonaProfiles()
+      ])
+      applyDefaultAssistantWorkspaceState(workspace)
+      setPersonaProfiles(personas)
+    } catch {
+      setDefaultAssistantError(
+        t(
+          "playground:workspace.defaultAssistantLoadError",
+          "Could not load default assistant settings."
+        )
+      )
+    } finally {
+      setDefaultAssistantLoading(false)
+    }
+  }
+
+  const handleCloseDefaultAssistantModal = () => {
+    setDefaultAssistantModalOpen(false)
+    setDefaultAssistantError(null)
+    setDefaultAssistantSaving(false)
+  }
+
+  const handleDefaultAssistantMemoryModeChange = (
+    event: React.ChangeEvent<HTMLSelectElement>
+  ) => {
+    const nextMode = event.target.value as WorkspacePersonaMemoryMode
+    setDefaultAssistantMemoryMode(nextMode)
+    if (nextMode !== "read_write") {
+      setDefaultAssistantConfirmReadWrite(false)
+    }
+  }
+
+  const buildDefaultAssistantPayload = (): WorkspaceAssistantDefaults => ({
+    assistantKind: "persona",
+    assistantId: defaultAssistantPersonaId.trim(),
+    personaMemoryMode: defaultAssistantMemoryMode,
+    voice: null,
+    style: null,
+    toolPolicyProfileId: null
+  })
+
+  const handleSaveDefaultAssistant = async () => {
+    if (!workspaceId || !defaultAssistantWorkspace) return
+    if (!defaultAssistantPersonaId.trim()) {
+      setDefaultAssistantError(
+        t(
+          "playground:workspace.defaultAssistantSelectRequired",
+          "Select a Persona before saving."
+        )
+      )
+      return
+    }
+    if (
+      defaultAssistantMemoryMode === "read_write" &&
+      !defaultAssistantConfirmReadWrite
+    ) {
+      setDefaultAssistantError(
+        t(
+          "playground:workspace.defaultAssistantReadWriteConfirmRequired",
+          "Confirm read-write memory access before saving."
+        )
+      )
+      return
+    }
+
+    setDefaultAssistantSaving(true)
+    setDefaultAssistantError(null)
+    try {
+      const updatedWorkspace = await tldwClient.patchWorkspace(workspaceId, {
+        version: defaultAssistantWorkspace.version,
+        assistantDefaults: buildDefaultAssistantPayload(),
+        ...(defaultAssistantMemoryMode === "read_write"
+          ? { confirmReadWriteAssistantDefault: true }
+          : {})
+      })
+      applyDefaultAssistantWorkspaceState(updatedWorkspace)
+      setDefaultAssistantModalOpen(false)
+      messageApi.success(
+        t(
+          "playground:workspace.defaultAssistantSaved",
+          "Default assistant updated"
+        )
+      )
+    } catch {
+      setDefaultAssistantError(
+        t(
+          "playground:workspace.defaultAssistantSaveError",
+          "Could not save default assistant settings."
+        )
+      )
+    } finally {
+      setDefaultAssistantSaving(false)
+    }
+  }
Evidence
The modal open handler sets loading/error but does not clear previously fetched workspace/persona
state; the save handler then uses defaultAssistantWorkspace.version for PATCH, which can be stale
if the open fetch fails or is skipped.

apps/packages/ui/src/components/Option/ResearchWorkspace/WorkspaceHeader.tsx[593-706]

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

### Issue description
`handleOpenDefaultAssistantModal()` opens the modal and starts loading, but it does **not** reset `defaultAssistantWorkspace`, `personaProfiles`, `defaultAssistantPersonaId`, and related fields before fetching. If the fetch fails (or the user switches workspaces and reopens), the modal can display/use stale state and allow `handleSaveDefaultAssistant()` to submit a PATCH with an outdated `version` and/or persona selection.

### Issue Context
This is a real-world failure mode because the code sets `defaultAssistantLoading=false` in `finally` even on errors, and the save handler only checks that `defaultAssistantWorkspace` is non-null (which may be left over from a previous successful open).

### Fix Focus Areas
- apps/packages/ui/src/components/Option/ResearchWorkspace/WorkspaceHeader.tsx[593-706]

### Suggested fix
- At the start of `handleOpenDefaultAssistantModal`:
 - `setDefaultAssistantWorkspace(null)`
 - `setPersonaProfiles([])`
 - `setDefaultAssistantPersonaId("")`
 - `setDefaultAssistantMemoryMode("read_only")`
 - `setDefaultAssistantConfirmReadWrite(false)`
- In the `catch` block, also clear `defaultAssistantWorkspace`/`personaProfiles` to prevent stale UI.
- Add `!defaultAssistantWorkspace` as an additional `defaultAssistantSaveDisabled` condition (defense in depth).

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



Remediation recommended

2. logger.warning lacks context ✓ Resolved 📘 Rule violation ◔ Observability
Description
The new warning log for invalid stored workspace assistant defaults omits actionable context (e.g.,
workspace id and the validation exception details). This makes production debugging and incident
triage significantly harder.
Code

tldw_Server_API/app/api/v1/endpoints/workspaces.py[R137-139]

+    except ValueError:
+        logger.warning("Ignoring invalid stored workspace assistant defaults.")
+        return None, True
Evidence
The checklist requires error logging to include useful context. The added log line
logger.warning("Ignoring invalid stored workspace assistant defaults.") provides no identifying or
diagnostic details about what failed or where.

AGENTS.md: Use Loguru for Logging and Log Errors With Context
tldw_Server_API/app/api/v1/endpoints/workspaces.py[129-139]

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

## Issue description
A new warning log is emitted without enough context to debug the underlying issue.

## Issue Context
`_parse_workspace_assistant_defaults()` catches validation errors and logs a generic warning message, but does not include the workspace identifier, the invalid payload shape, or the exception details/stack.

## Fix Focus Areas
- tldw_Server_API/app/api/v1/endpoints/workspaces.py[129-139]
- tldw_Server_API/app/api/v1/endpoints/workspaces.py[236-240]

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


3. degraded_reason line too long ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
The assignment to degraded_reason is written as a single long line, which is an obvious PEP 8
style violation and can trigger formatter/linter failures. This reduces readability and increases
review friction.
Code

tldw_Server_API/app/api/v1/endpoints/workspaces.py[195]

+    degraded_reason = "persona_deleted" if deleted_profile is not None else "permission_denied"
Evidence
PEP 8 compliance requires avoiding obvious style issues like overly long lines. The new
degraded_reason = ... ternary expression is a single long line in the modified code.

AGENTS.md: Python Code Must Follow PEP 8 Style
tldw_Server_API/app/api/v1/endpoints/workspaces.py[190-196]

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

## Issue description
A new line exceeds typical PEP 8 max line length, making the code harder to read and likely to trigger lint/format checks.

## Issue Context
The `degraded_reason` conditional assignment is currently on one long line.

## Fix Focus Areas
- tldw_Server_API/app/api/v1/endpoints/workspaces.py[195-195]

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


4. Inherited source flips explicit ✓ Resolved 🐞 Bug ◔ Observability
Description
WorkspaceChatPanel derives assistantSource as "explicit" as soon as any server chat assistant
metadata exists, even when the chat was created via the inherited workspace default, and the persona
label can immediately fall back to the generic string "Persona". This makes the inspector/runtime
state unstable (inherited→explicit) and can misreport the origin/label of the assistant right after
the first submit.
Code

apps/packages/ui/src/components/Option/ChatWorkspace/WorkspaceChatPanel.tsx[R160-186]

+  const hasExistingChatSession = Boolean(serverChatId)
+  const hasExistingAssistantMetadata = Boolean(
+    serverChatAssistantKind || serverChatAssistantId
+  )
+  const usingWorkspaceAssistant =
+    Boolean(inheritedAssistant) &&
+    !hasExistingChatSession &&
+    !hasExistingAssistantMetadata &&
+    (selectedAssistantSource === "workspace" ||
+      (!selectedAssistant && messages.length === 0))
+  const assistantSource: ChatWorkspaceAssistantSource = usingWorkspaceAssistant
+    ? "workspace"
+    : selectedAssistant || hasExistingAssistantMetadata
+      ? "explicit"
+      : effectiveAssistantDefault?.status === "unavailable"
+        ? "unavailable"
+        : "none"
+  const runtimeSelectedPersonaLabel =
+    assistantSource === "workspace"
+      ? inheritedAssistant?.name ?? selectedAssistant?.name ?? null
+      : selectedAssistant?.name ??
+        (assistantSource === "explicit" && serverChatAssistantKind === "persona"
+          ? "Persona"
+          : null)

  React.useEffect(() => {
    setDraft("")
Evidence
WorkspaceChatPanel flips to "explicit" whenever serverChat assistant metadata exists and falls back
to the literal string "Persona" for persona sessions; effective assistant state resolution also
defaults tracked persona displayName to "Persona" when neither tracked displayName nor matching
draft selection exists.

apps/packages/ui/src/components/Option/ChatWorkspace/WorkspaceChatPanel.tsx[160-201]
apps/packages/ui/src/hooks/chat/effective-assistant-state.ts[115-139]
apps/packages/ui/src/hooks/useMessageOption.tsx[245-346]

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

### Issue description
After the first message creates a server chat, `serverChatAssistantKind/id` become set, which forces `assistantSource` to "explicit" in `WorkspaceChatPanel` and can cause `selectedPersonaLabel` to fall back to the generic "Persona" (because the tracked persona display name defaults to "Persona" when no draft selection name is available).

### Issue Context
This behavior is triggered specifically by the new workspace-default inheritance path where the user may never have a draft selected assistant in the store, so the tracked persona display name has no draft metadata to reuse.

### Fix Focus Areas
- apps/packages/ui/src/components/Option/ChatWorkspace/WorkspaceChatPanel.tsx[160-201]
- apps/packages/ui/src/hooks/useMessageOption.tsx[245-346]
- apps/packages/ui/src/hooks/chat/effective-assistant-state.ts[115-139]

### Suggested fix
Implement one of the following (pick the least invasive that matches product intent):
1) **Track origin explicitly for the session**: when a chat is created due to workspace inheritance, persist a session flag (e.g., `trackedAssistantSource: "workspace"`) and use that for `assistantSource` instead of treating any server metadata as "explicit".
2) **Preserve display name for tracked persona**: when resolving `effectiveAssistantState` for tracked personas, allow a one-time fallback to the inherited assistant name **only if** it matches the tracked assistant id and the chat is newly created.
3) Ensure that the first tracked persona chat created from workspace inheritance carries a draftSelection-like displayName into the tracked resolution (without changing the user's global assistant selection).

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


View more (1)
5. Workspace list N+1 queries ✓ Resolved 🐞 Bug ➹ Performance
Description
The backend list_workspaces endpoint now calls _ws_to_response per workspace, and _ws_to_response
resolves effective_assistant_default via db.get_persona_profile (and potentially a second
include_deleted lookup). This creates an N+1 query pattern that can significantly slow listing when
a user has many workspaces.
Code

tldw_Server_API/app/api/v1/endpoints/workspaces.py[R1045-1050]

    except (ConflictError, InputError, CharactersRAGDBError) as exc:
        raise map_db_error_to_http(exc, default_detail="Failed to fetch workspaces") from exc
    return WorkspaceListResponse(
-        items=[_ws_to_response(w) for w in items],
+        items=[_ws_to_response(w, db=db, current_user=current_user) for w in items],
        total=len(items),
    )
Evidence
The list endpoint maps each workspace through _ws_to_response, which resolves effective assistant
default via persona profile lookups, yielding per-item DB calls.

tldw_Server_API/app/api/v1/endpoints/workspaces.py[1032-1050]
tldw_Server_API/app/api/v1/endpoints/workspaces.py[142-203]

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

### Issue description
`GET /api/v1/workspaces/` now computes `effective_assistant_default` for each workspace individually, causing per-workspace persona profile DB lookups (potentially two). This is an N+1 pattern and can degrade latency for large workspace lists.

### Issue Context
The effective default resolver calls `db.get_persona_profile(...)` for the stored persona id (and then again with `include_deleted=True` if missing).

### Fix Focus Areas
- tldw_Server_API/app/api/v1/endpoints/workspaces.py[1032-1050]
- tldw_Server_API/app/api/v1/endpoints/workspaces.py[142-203]

### Suggested fix
- Add a bulk-fetch path for persona profiles used by workspace defaults:
 - Collect all `assistant_id`s from `assistant_defaults_json` for the listed workspaces.
 - Fetch profiles in one query (or cache results in-memory for the duration of the request).
 - Resolve `effective_assistant_default` from that preloaded map.
- If the DB layer cannot bulk-fetch yet, add a simple per-request LRU/dict cache in the endpoint to avoid duplicate lookups (same persona used by multiple workspaces).

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


Grey Divider

Qodo Logo

@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 Workspace Assistant Defaults V1, introducing backend storage, validation, and effective state resolution for Persona-backed defaults, alongside frontend store mappings, a settings UI modal in the Research Workspace header, and automatic inheritance of defaults in the Chat Workspace startup. The review feedback identifies a critical state synchronization issue where updating the default assistant in the settings modal only updates local component state instead of syncing with the global workspace store, which causes other workspace-scoped views to display stale defaults until a full reload.

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 tldw_Server_API/app/api/v1/endpoints/workspaces.py Outdated
Comment thread tldw_Server_API/app/api/v1/endpoints/workspaces.py Outdated
Comment thread apps/packages/ui/src/components/Option/ChatWorkspace/WorkspaceChatPanel.tsx Outdated
Comment thread tldw_Server_API/app/api/v1/endpoints/workspaces.py

@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: 9

🤖 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
`@apps/packages/ui/src/components/Option/ResearchWorkspace/__tests__/WorkspaceHeader.test.tsx`:
- Around line 187-217: The test helper createWorkspaceApiResponse mixes
snake_case and camelCase; update it to mirror the client shape returned by
tldwClient.getWorkspace() by renaming all snake_case fields to camelCase (e.g.,
studyMaterialsPolicy, bannerTitle, bannerSubtitle, bannerColor, audioProvider,
audioModel, audioVoice, audioSpeed, createdAt, lastModified) while keeping
existing camelCase keys (assistantDefaults, effectiveAssistantDefault) and the
...overrides spread so tests remain able to override values.

In
`@apps/packages/ui/src/components/Option/ResearchWorkspace/WorkspaceHeader.tsx`:
- Around line 604-626: The modal leaves stale state between opens causing
mismatches; in handleOpenDefaultAssistantModal before starting the fetch (after
the workspaceId check) clear/invalidate the modal-specific state by calling
setDefaultAssistantWorkspace(null), setDefaultAssistantPersonaId(null) and
setPersonaProfiles([]) (and any version field like
defaultAssistantWorkspace?.version if present) so the UI and save logic cannot
reuse previous workspace data; keep existing applyDefaultAssistantWorkspaceState
and setPersonaProfiles calls to repopulate on success and ensure the same reset
is applied in the error path/when closing the modal so failed or out-of-order
responses cannot apply the wrong persona/version.
- Around line 688-719: The success path after patching defaults must sync the
returned updatedWorkspace into the shared workspace store so runtime consumers
see the new assistantDefaults/effectiveAssistantDefault before the modal closes;
in handleClearDefaultAssistant (and the save path that calls
tldwClient.patchWorkspace) ensure you call the store updater (e.g.,
applyDefaultAssistantWorkspaceState(updatedWorkspace) or the appropriate
useWorkspaceStore setter) to write updatedWorkspace.assistantDefaults and
updatedWorkspace.effectiveAssistantDefault into the global store prior to
calling setDefaultAssistantModalOpen(false) and ending the flow.

In
`@apps/packages/ui/src/hooks/chat/__tests__/useChatActions.persona.integration.test.tsx`:
- Around line 345-360: The test is constructing a state shape that never occurs
in production because useMessageOption invokes useChatActions with
selectedAssistant set to effectiveSelectedAssistant; update the test to mirror
that payload by setting selectedAssistant to the inherited assistant (i.e. set
selectedAssistant to the same object as
inheritedAssistant/effectiveSelectedAssistant) rather than null, and keep
inheritedAssistant present so the hook sees the real-world shape; locate
references to useMessageOption, useChatActions, selectedAssistant and
inheritedAssistant in the test and make this substitution.

In `@apps/packages/ui/src/hooks/chat/personaServerChat.ts`:
- Around line 148-159: The personaMemoryMode computation incorrectly falls back
to newChatPersonaMemoryMode when serverChatId exists but serverChatMetaLoaded is
false, causing restored read_write mode to be overwritten; modify the logic in
the personaMemoryMode assignment (referencing newChatPersonaMemoryMode,
isMatchingPersonaChat, serverChatMetaLoaded, serverChatPersonaMemoryMode, and
resolvedServerChatId) so that if a resolvedServerChatId exists we preserve
serverChatPersonaMemoryMode (even while serverChatMetaLoaded is false) unless it
is explicitly undefined—i.e., treat the presence of a matching serverChatId as
sufficient to use serverChatPersonaMemoryMode and only fall back to
newChatPersonaMemoryMode when serverChatPersonaMemoryMode is null/undefined or
no matching server chat exists.

In `@apps/packages/ui/src/hooks/useMessageOption.tsx`:
- Around line 285-316: The current inheritedAssistant useMemo drops the
workspace-derived assistant as soon as serverChatId/history/messages appear,
losing provenance; change the logic to persist the first valid inherited
assistant instead of clearing it on subsequent state changes: create a ref
(e.g., inheritedAssistantRef) that captures opts.inheritedAssistant when it
first satisfies kind === "persona" and id != null and
effectiveAssistantState.mode === "plain" and no selectedAssistant, and then have
the inheritedAssistant memo return ref.current (if present) regardless of later
serverChatId/history/messages so long as selectedAssistant remains null; apply
the same ref-based preservation to the other similar memo referenced at lines
342-346 (the other inherited selection computation) so workspace provenance is
retained across renders.

In
`@apps/packages/ui/src/services/__tests__/tldw-api-client.workspace-api.test.ts`:
- Around line 513-565: Add a regression unit test for
workspaceApiMethods.patchWorkspace that verifies malformed assistantDefaults
input does not cause the client to send assistant_defaults: null or silently
succeed: call workspaceApiMethods.patchWorkspace with an invalid
assistantDefaults payload (e.g., wrong shape or null) and assert that either the
method throws or mocks.bgRequest was not called with a body containing
assistant_defaults: null; reference the existing test logic (mocks.bgRequest and
expected request object shape) and reuse the same pattern of
expect(mocks.bgRequest).toHaveBeenCalledWith/expect(...).toThrow to validate the
client prevents accidental clearing of assistant defaults.

In `@apps/packages/ui/src/services/tldw/domains/workspace-api.ts`:
- Around line 783-789: serializeWorkspaceAssistantDefaults currently converts
malformed non-null inputs into null via normalizeWorkspaceAssistantDefaults,
which leads callers to emit assistant_defaults: null and inadvertently clear
persisted defaults; instead, when normalizeWorkspaceAssistantDefaults(value)
returns a falsy/invalid result for a non-null input, fail fast by throwing a
descriptive error (or returning a distinct non-null sentinel that callers treat
as an error) rather than returning null. Update
serializeWorkspaceAssistantDefaults (and the similar serialization path around
the 875–890 region) to validate the normalized result and raise an exception
with context (including the original value and mention of assistant_defaults) so
invalid payloads cannot be coerced into a clear operation. Ensure callers that
assemble assistant_defaults do not treat thrown errors as nulls.

In `@tldw_Server_API/tests/Workspaces/test_workspaces_api.py`:
- Around line 69-89: Add a clear docstring to the helper function
_create_workspace_test_persona describing its purpose (create a test persona for
workspace tests), parameter types and meanings (db: CharactersRAGDB, persona_id,
user_id, name), return value (the created persona id/record returned by
db.create_persona_profile), and preconditions (expects
db.get_character_card_by_name("Test Char") to return a non-None character).
Include any side effects and note that it asserts the character exists before
calling db.create_persona_profile.
🪄 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: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 84018666-52d2-44da-9637-3ee9a568510a

📥 Commits

Reviewing files that changed from the base of the PR and between 9ad04ff and f46681c.

⛔ Files ignored due to path filters (1)
  • Docs/Product/Workspace_Persona_Defaults_PRD.md is excluded by !docs/**
📒 Files selected for processing (32)
  • apps/packages/ui/src/components/Option/ChatWorkspace/ChatWorkspaceConsole.tsx
  • apps/packages/ui/src/components/Option/ChatWorkspace/ChatWorkspacePage.tsx
  • apps/packages/ui/src/components/Option/ChatWorkspace/InspectorRail.tsx
  • apps/packages/ui/src/components/Option/ChatWorkspace/WorkspaceChatPanel.tsx
  • apps/packages/ui/src/components/Option/ChatWorkspace/__tests__/ChatWorkspacePage.test.tsx
  • apps/packages/ui/src/components/Option/ChatWorkspace/__tests__/InspectorRail.test.tsx
  • apps/packages/ui/src/components/Option/ChatWorkspace/__tests__/WorkspaceChatPanel.test.tsx
  • apps/packages/ui/src/components/Option/ChatWorkspace/types.ts
  • apps/packages/ui/src/components/Option/ResearchWorkspace/WorkspaceHeader.tsx
  • apps/packages/ui/src/components/Option/ResearchWorkspace/__tests__/WorkspaceHeader.test.tsx
  • apps/packages/ui/src/hooks/chat/__tests__/personaServerChat.test.ts
  • apps/packages/ui/src/hooks/chat/__tests__/useChatActions.persona.integration.test.tsx
  • apps/packages/ui/src/hooks/chat/personaServerChat.ts
  • apps/packages/ui/src/hooks/chat/useChatActions.ts
  • apps/packages/ui/src/hooks/useMessageOption.tsx
  • apps/packages/ui/src/services/__tests__/tldw-api-client.workspace-api.test.ts
  • apps/packages/ui/src/services/tldw/domains/workspace-api.ts
  • apps/packages/ui/src/store/__tests__/workspace.test.ts
  • apps/packages/ui/src/store/workspace-slices/workspace-list-slice.ts
  • apps/packages/ui/src/store/workspace.ts
  • apps/packages/ui/src/types/workspace.ts
  • backlog/tasks/task-2278 - Implement-Workspace-Assistant-Defaults-backend-storage.md
  • backlog/tasks/task-2318 - Plan-Workspace-Assistant-Defaults-implementation.md
  • backlog/tasks/task-2318.1 - Map-Workspace-Assistant-Defaults-in-WebUI-client-and-store.md
  • backlog/tasks/task-2318.2 - Expose-Workspace-Assistant-Defaults-API-validation-and-effective-state.md
  • backlog/tasks/task-2318.3 - Add-Workspace-default-assistant-settings-UI.md
  • backlog/tasks/task-2318.4 - Apply-Workspace-Persona-defaults-to-Chat-Workspace-startup.md
  • backlog/tasks/task-2318.5 - Close-out-Workspace-Assistant-Defaults-V1-verification.md
  • tldw_Server_API/app/api/v1/endpoints/workspaces.py
  • tldw_Server_API/app/api/v1/schemas/workspace_schemas.py
  • tldw_Server_API/tests/Workspaces/test_workspace_assistant_defaults_api.py
  • tldw_Server_API/tests/Workspaces/test_workspaces_api.py

Comment thread apps/packages/ui/src/hooks/chat/personaServerChat.ts Outdated
Comment thread apps/packages/ui/src/hooks/useMessageOption.tsx Outdated
Comment thread apps/packages/ui/src/services/tldw/domains/workspace-api.ts
Comment thread tldw_Server_API/tests/Workspaces/test_workspaces_api.py
@rmusser01 rmusser01 force-pushed the codex/workspace-assistant-defaults-web-client branch 2 times, most recently from 05f8414 to d7f2987 Compare June 9, 2026 01:19
@rmusser01

Copy link
Copy Markdown
Owner Author

Addressed the open review feedback in d7f2987:

  • Reset/invalidate Workspace default assistant modal state on open, close, failed load, and stale request completion.
  • Synced saved/cleared assistant defaults into the shared workspace store before closing the modal.
  • Preserved workspace-inherited Persona provenance and display label after server chat creation.
  • Preserved restored Persona memory mode while chat metadata is still hydrating.
  • Made malformed non-null assistantDefaults patch payloads throw instead of serializing to assistant_defaults: null.
  • Added contextual backend logging for invalid stored defaults and formatted degraded_reason assignment.
  • Added request-scoped Persona profile caching for workspace list effective-default resolution.
  • Normalized the WorkspaceHeader test helper shape and documented the backend test helper.

Verification after latest rebase onto dev:

  • Focused frontend review-fix suite: 101 passed.
  • Broader Workspace Assistant Defaults frontend suite: 125 passed.
  • Backend Workspace suite: 116 passed before the final rebase; post-rebase focused backend suite: 83 passed.
  • git diff --check: passed.
  • Bandit production endpoint scope: zero findings.

Known unrelated verification blocker: full UI tsc with 8GB heap still fails in existing Notes test files for missing/changed props such as onCreateNote, listError, ServerCapabilities, and onSyncFolder; no Workspace Assistant Defaults files were implicated.

@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: 4

Caution

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

⚠️ Outside diff range comments (1)
apps/packages/ui/src/hooks/chat/personaServerChat.ts (1)

151-167: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Unhydrated chat reuse can attach a new persona turn to the wrong server chat.

Lines 151-163 treat any resolvedServerChatId with serverChatMetaLoaded === false as reusable. If a restored chat exists and the user switches personas before hydration finishes, this helper returns the stale chatId, rewrites local assistant state to the new persona, and useChatActions sends the next turn into the old conversation. Reuse should require a verified persisted assistant match; otherwise reset/create a new chat first.

🤖 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 `@apps/packages/ui/src/hooks/chat/personaServerChat.ts` around lines 151 - 167,
The unhydrated chat reuse path in personaServerChat’s persona memory/chat
selection logic is too permissive and can route a new persona turn into the
wrong persisted conversation. Update the resolved chat handling around
isReusingUnhydratedChat, isMatchingPersonaChat, personaMemoryMode, and
shouldResetServerChat so reuse only happens after a verified persisted assistant
match; otherwise force a reset or create a new chat before useChatActions sends
the next turn.
🤖 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 `@apps/packages/ui/src/components/Option/ChatWorkspace/WorkspaceChatPanel.tsx`:
- Around line 160-184: The assistant provenance logic in WorkspaceChatPanel is
reclassifying a restored inherited workspace persona as "explicit" when
useMessageOption no longer has a draft selection and only
serverChatAssistantKind/serverChatAssistantId remain. Update the assistantSource
determination to preserve "workspace" whenever the current chat is backed by the
inherited workspace persona, not just when selectedAssistantSource is
"workspace"; use the existing selectedMatchesWorkspaceAssistant,
usingWorkspaceAssistant, inheritedAssistant, and
serverChatAssistantKind/serverChatAssistantId checks together so reopened
sessions keep the workspace label and runtimeSelectedPersonaLabel stays
inherited.

In
`@apps/packages/ui/src/components/Option/ResearchWorkspace/WorkspaceHeader.tsx`:
- Around line 655-657: In WorkspaceHeader.tsx, the finally block in the default
assistant loading flow uses an early return for the stale-request guard, which
triggers noUnsafeFinally. Update the finally logic around
defaultAssistantRequestIdRef.current and setDefaultAssistantLoading so it uses a
conditional check instead of returning, while preserving the guard that only
clears loading for the active request.

In `@apps/packages/ui/src/hooks/useMessageOption.tsx`:
- Around line 255-271: The inherited assistant ref is being mutated during
render in useMessageOption, which can leave stale state behind if React abandons
a render. Move the write to inheritedAssistantRef.current out of the render path
and into an effect or other commit-safe place, keeping the
canCaptureInheritedAssistant logic in useMessageOption as the gate for when to
capture or clear the inherited assistant. Ensure assistantDraftSelection and
selectedAssistantSource only read from committed state, and preserve the
existing reset behavior when selectedAssistant is present.

In `@tldw_Server_API/app/api/v1/endpoints/workspaces.py`:
- Around line 169-176: The new db.get_persona_profile() calls in the workspace
endpoints are bypassing the existing db-to-HTTP error mapping and can leak
generic 500s. Wrap the Persona lookup in the same map_db_error_to_http(...)
handling used elsewhere in list_workspaces, get_workspace, patch_workspace, and
get_workspace_context, so ConflictError, InputError, and CharactersRAGDBError
are translated consistently before caching or returning the profile.

---

Outside diff comments:
In `@apps/packages/ui/src/hooks/chat/personaServerChat.ts`:
- Around line 151-167: The unhydrated chat reuse path in personaServerChat’s
persona memory/chat selection logic is too permissive and can route a new
persona turn into the wrong persisted conversation. Update the resolved chat
handling around isReusingUnhydratedChat, isMatchingPersonaChat,
personaMemoryMode, and shouldResetServerChat so reuse only happens after a
verified persisted assistant match; otherwise force a reset or create a new chat
before useChatActions sends the next turn.
🪄 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: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2f2fe600-cf1f-4711-a4dd-7b9d6d87c133

📥 Commits

Reviewing files that changed from the base of the PR and between f46681c and d7f2987.

⛔ Files ignored due to path filters (1)
  • Docs/Product/Workspace_Persona_Defaults_PRD.md is excluded by !docs/**
📒 Files selected for processing (34)
  • apps/packages/ui/src/components/Option/ChatWorkspace/ChatWorkspaceConsole.tsx
  • apps/packages/ui/src/components/Option/ChatWorkspace/ChatWorkspacePage.tsx
  • apps/packages/ui/src/components/Option/ChatWorkspace/InspectorRail.tsx
  • apps/packages/ui/src/components/Option/ChatWorkspace/WorkspaceChatPanel.tsx
  • apps/packages/ui/src/components/Option/ChatWorkspace/__tests__/ChatWorkspacePage.test.tsx
  • apps/packages/ui/src/components/Option/ChatWorkspace/__tests__/InspectorRail.test.tsx
  • apps/packages/ui/src/components/Option/ChatWorkspace/__tests__/WorkspaceChatPanel.test.tsx
  • apps/packages/ui/src/components/Option/ChatWorkspace/types.ts
  • apps/packages/ui/src/components/Option/ResearchWorkspace/WorkspaceHeader.tsx
  • apps/packages/ui/src/components/Option/ResearchWorkspace/__tests__/WorkspaceHeader.test.tsx
  • apps/packages/ui/src/hooks/__tests__/useMessageOption.assistant-overlay.test.tsx
  • apps/packages/ui/src/hooks/chat/__tests__/personaServerChat.test.ts
  • apps/packages/ui/src/hooks/chat/__tests__/useChatActions.persona.integration.test.tsx
  • apps/packages/ui/src/hooks/chat/personaServerChat.ts
  • apps/packages/ui/src/hooks/chat/useChatActions.ts
  • apps/packages/ui/src/hooks/useMessageOption.tsx
  • apps/packages/ui/src/services/__tests__/tldw-api-client.workspace-api.test.ts
  • apps/packages/ui/src/services/tldw/domains/workspace-api.ts
  • apps/packages/ui/src/store/__tests__/workspace.test.ts
  • apps/packages/ui/src/store/workspace-slices/workspace-list-slice.ts
  • apps/packages/ui/src/store/workspace.ts
  • apps/packages/ui/src/types/workspace.ts
  • backlog/tasks/task-2278 - Implement-Workspace-Assistant-Defaults-backend-storage.md
  • backlog/tasks/task-2318 - Plan-Workspace-Assistant-Defaults-implementation.md
  • backlog/tasks/task-2318.1 - Map-Workspace-Assistant-Defaults-in-WebUI-client-and-store.md
  • backlog/tasks/task-2318.2 - Expose-Workspace-Assistant-Defaults-API-validation-and-effective-state.md
  • backlog/tasks/task-2318.3 - Add-Workspace-default-assistant-settings-UI.md
  • backlog/tasks/task-2318.4 - Apply-Workspace-Persona-defaults-to-Chat-Workspace-startup.md
  • backlog/tasks/task-2318.5 - Close-out-Workspace-Assistant-Defaults-V1-verification.md
  • backlog/tasks/task-2318.6 - Address-Workspace-Assistant-Defaults-PR-review-feedback.md
  • tldw_Server_API/app/api/v1/endpoints/workspaces.py
  • tldw_Server_API/app/api/v1/schemas/workspace_schemas.py
  • tldw_Server_API/tests/Workspaces/test_workspace_assistant_defaults_api.py
  • tldw_Server_API/tests/Workspaces/test_workspaces_api.py

Comment thread apps/packages/ui/src/components/Option/ResearchWorkspace/WorkspaceHeader.tsx Outdated
Comment thread apps/packages/ui/src/hooks/useMessageOption.tsx Outdated
Comment thread tldw_Server_API/app/api/v1/endpoints/workspaces.py Outdated
@rmusser01 rmusser01 force-pushed the codex/workspace-assistant-defaults-web-client branch from d7f2987 to 4ab486d Compare July 1, 2026 06:32
@rmusser01

Copy link
Copy Markdown
Owner Author

Rebased PR #2316 onto current origin/dev (9445a17) and force-pushed head 4ab486d. GitHub now reports the PR as mergeable; fresh CI is pending.

Local verification after rebase:

  • Backend focused: python -m pytest tldw_Server_API/tests/Workspaces/test_workspace_assistant_defaults_api.py tldw_Server_API/tests/Chat/unit/test_chat_conversations_api.py -q -> 23 passed.
  • Backend broader Workspace defaults: python -m pytest tldw_Server_API/tests/ChaChaNotesDB/test_workspace_assistant_defaults_db.py tldw_Server_API/tests/Workspaces/test_workspace_assistant_defaults_api.py tldw_Server_API/tests/Workspaces/test_workspaces_api.py tldw_Server_API/tests/ChaChaNotesDB/test_workspace_sub_resources_db.py -q -> 118 passed.
  • Frontend focused Workspace defaults suite: vitest run ...workspace-api/workspace-store/WorkspaceHeader/ChatWorkspace/persona tests... -> 9 files, 184 passed.
  • Bandit on touched backend Workspace defaults scope -> 0 results, 0 errors.
  • git diff --check -> clean.

Typecheck note: plain tsc --noEmit OOMs at the default Node heap. With NODE_OPTIONS=--max-old-space-size=8192, it completes but still reports existing broad package errors outside this Workspace Assistant Defaults slice, including Notes tests, AudioStudio, ScheduledTasks, background response typing, MCP hub path typing, and voice-cloning ArrayBuffer typing.

@cubic-dev-ai cubic-dev-ai 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.

3 issues found across 35 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread apps/packages/ui/src/components/Option/ResearchWorkspace/WorkspaceHeader.tsx Outdated
Comment thread apps/packages/ui/src/components/Option/ResearchWorkspace/WorkspaceHeader.tsx Outdated
Comment thread apps/packages/ui/src/store/workspace.ts Outdated
@rmusser01 rmusser01 force-pushed the codex/workspace-assistant-defaults-web-client branch from 671e808 to 0ee4797 Compare July 3, 2026 03:12
@rmusser01 rmusser01 merged commit 4ad2090 into dev Jul 3, 2026
21 of 38 checks passed
@rmusser01 rmusser01 deleted the codex/workspace-assistant-defaults-web-client branch July 3, 2026 04:10
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