Skip to content

refactor: extract URL state and agent conversation helpers - #101

Merged
2witstudios merged 5 commits into
masterfrom
fix/header-missing-cleanup-dead-store-code
Dec 19, 2025
Merged

2witstudios merged 5 commits into
masterfrom
fix/header-missing-cleanup-dead-store-code

Conversation

@2witstudios

@2witstudios 2witstudios commented Dec 19, 2025 •

Copy link
Copy Markdown
Owner

Summary

  • Centralize URL state management for chat params (agentId, conversationId) into url-state.ts
  • Extract shared agent conversation API helpers (fetchAgentConversationMessages, fetchMostRecentAgentConversation, createAgentConversation) into agent-conversations.ts
  • Add UI refresh protection (isPaused: () => isAnyActive) to useBreadcrumbs, usePageTree, and useConversations SWR hooks to prevent unwanted revalidation during editing/streaming
  • Update state management docs with AI assistant state boundaries

Test plan

  • Verify agent selection updates URL correctly in dashboard
  • Verify conversation loading/creation works in sidebar and dashboard modes
  • Verify breadcrumbs and page tree don't refetch while editing or AI streaming
  • Verify browser back/forward navigation with conversation URLs

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • URL-based navigation and centralized conversation URL handling for more reliable browser history and shareable links.
    • Debug panel: new "Rehydrated" Yes/No indicator.
  • Bug Fixes & Improvements

    • Pause background data fetching while editing to reduce interruptions.
    • Simplified header visibility logic for more consistent display.
  • Refactor

    • Streamlined UI/state surface—navigation/cache controls reduced for a leaner layout and persistence model.
  • Documentation

    • Updated architecture and guides to reflect URL-driven navigation and state boundaries.

✏️ Tip: You can customize this high-level summary in your review settings.

2witstudios and others added 3 commits December 19, 2025 09:38
… code

The page header (Share button, breadcrumbs, etc.) was missing from all pages
because OptimizedViewHeader checked layoutStore.activePageId which was always
null - the navigation system was designed but never wired up.

Changes:
- Fix OptimizedViewHeader to use useParams() instead of dead store state
- Remove all dead navigation code from useLayoutStore (352→60 lines)
- Remove duplicate sidebar state from useUIStore (97→50 lines)
- Remove dead cleanup code from NavigationProvider.tsx
- Remove dead sidebar hooks from useUI.ts (keep only useTreeState)
- Update store tests to match new simplified state
- Update state-management.md documentation

The two stores now have clear, non-overlapping responsibilities:
- useLayoutStore: sidebar open/closed state (persisted)
- useUIStore: tree expansion and scroll state (persisted)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The layout store was simplified to only manage sidebar state, but
DebugPanel and LayoutErrorBoundary still referenced the deleted
clearCache() method and other removed properties (viewCache,
activeDriveId, activePageId, centerViewType).

These were all part of a navigation system that was designed but
never wired up (dead code). The useful functionality is preserved:
- Clear Cache button still clears localStorage/sessionStorage
- Error boundary still clears storage on error

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add centralized url-state.ts for chat URL param management
- Add shared agent-conversations.ts API helpers (DRY up fetch calls)
- Add UI refresh protection (isPaused) to useBreadcrumbs, usePageTree, useConversations
- Refactor usePageAgentDashboardStore and usePageAgentSidebarState to use shared helpers
- Update state-management.md with AI assistant state boundaries documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Dec 19, 2025 •

Copy link
Copy Markdown
Contributor

Walkthrough

This PR shifts navigation state from Zustand stores to URL parameters, consolidates UI hooks into a tree-focused hook, extracts shared agent-conversation API helpers and a URL-state utility, and strips navigation/caching responsibilities from layout stores and several layout components.

Changes

Cohort / File(s) Summary
Store Refactoring
apps/web/src/stores/useLayoutStore.ts, apps/web/src/stores/useUIStore.ts
Removed navigation pointers, view caching, center view type, and many public APIs. UI store reduced to treeExpanded/treeScrollPosition persistence; layout store reduced to sidebar toggles and rehydrated.
Layout Components
Debug & Boundaries
apps/web/src/components/layout/DebugPanel.tsx, apps/web/src/components/layout/LayoutErrorBoundary.tsx, apps/web/src/components/layout/NavigationProvider.tsx, apps/web/src/components/layout/middle-content/CenterPanel.tsx
Removed useLayoutStore imports/usages and cache-clearing calls; DebugPanel UI trimmed (View Cache removed, Rehydrated badge added). CenterPanel header visibility now relies on URL params (useParams) instead of store state.
URL State Manager (new)
apps/web/src/lib/url-state.ts
Added centralized URL param utilities (get/set/clear for conversationId and agentId) with push/replace modes and SSR guards.
URL Adoption
apps/web/src/contexts/GlobalChatContext.tsx, apps/web/src/components/layout/right-sidebar/ai-assistant/SidebarHistoryTab.tsx, apps/web/src/stores/page-agents/usePageAgentDashboardStore.ts
Replaced manual URLSearchParams/history manipulation with getConversationId/getAgentId and setConversationId/setChatParams; adjusted control flow to use URL-state helpers.
Agent Conversation Helpers (new)
apps/web/src/lib/ai/shared/agent-conversations.ts, apps/web/src/lib/ai/shared/index.ts
New module exposing fetchMostRecentAgentConversation, fetchAgentConversationMessages, and createAgentConversation plus related interfaces; re-exported from shared index.
Agent & Sidebar Hooks
apps/web/src/hooks/page-agents/usePageAgentSidebarState.ts, apps/web/src/stores/page-agents/usePageAgentDashboardStore.ts
Replaced inline fetch calls with new agent-conversation helpers and integrated URL-state usage for agent/conversation syncing.
Fetch Control Integration
apps/web/src/hooks/useBreadcrumbs.ts, apps/web/src/hooks/usePageTree.ts, apps/web/src/lib/ai/shared/hooks/useConversations.ts
Integrated isEditingActive to SWR via isPaused to pause fetching during active edits.
UI Hook Consolidation
apps/web/src/hooks/useUI.ts
Removed several small UI hooks (useLeftSidebar, useRightSidebar, useCenterView, useNavigationState, useResponsiveLayout) and introduced useTreeState exposing tree expansion and scroll controls.
Tests
apps/web/src/stores/__tests__/useUIStore.test.ts
Simplified/rewrote tests to focus on tree expansion and scroll position independence; removed sidebar/view-type assertions.
Docs
docs/2.0-architecture/2.1-frontend/state-management.md, docs/3.0-guides-and-tools/ui-refresh-protection.md
Updated architecture and guidance to reflect URL-driven navigation, separated AI chat boundaries, and using isEditingActive for SWR pause semantics.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~55 minutes

  • Areas needing extra attention:
    • useLayoutStore.ts — many public fields/methods removed; verify all consumers updated.
    • useUIStore.ts / useUI.ts — removed hooks and introduced useTreeState; ensure imports across codebase replaced.
    • url-state.ts — verify push/replace semantics, SSR guards, and param naming/collisions.
    • Agent conversation helpers — confirm response parsing matches backend shapes and error paths.
    • Layout components (DebugPanel, ErrorBoundary, NavigationProvider, CenterPanel) — ensure no residual expectations of layout store state.

Possibly related PRs

Poem

🐰 I hopped through stores and tidied the trail,
Params now guide pages where pointers would fail.
Trees keep their leaves, sidebars whisper "peek",
Conversations live in the URL we seek.
A tiny rabbit nods — the change is neat and hale.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and specifically describes the main changes: extracting URL state management and agent conversation API helpers into dedicated modules.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/header-missing-cleanup-dead-store-code

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c5ef62e and 00fdbbb.

📒 Files selected for processing (7)
  • apps/web/src/hooks/useBreadcrumbs.ts (2 hunks)
  • apps/web/src/hooks/usePageTree.ts (3 hunks)
  • apps/web/src/lib/ai/shared/agent-conversations.ts (1 hunks)
  • apps/web/src/lib/ai/shared/hooks/useConversations.ts (2 hunks)
  • apps/web/src/stores/page-agents/usePageAgentDashboardStore.ts (7 hunks)
  • docs/2.0-architecture/2.1-frontend/state-management.md (15 hunks)
  • docs/3.0-guides-and-tools/ui-refresh-protection.md (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/web/src/stores/page-agents/usePageAgentDashboardStore.ts
  • apps/web/src/lib/ai/shared/agent-conversations.ts
  • apps/web/src/hooks/usePageTree.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Never use any types in TypeScript code - always use proper TypeScript types

**/*.{ts,tsx}: No any types - always use proper TypeScript types
Use kebab-case for filenames (e.g., image-processor.ts)
Use camelCase for variables and functions
Use UPPER_SNAKE_CASE for constants
Use PascalCase for types and enums
Use centralized permission logic: import getUserAccessLevel and canUserEditPage from @pagespace/lib/permissions
Use Drizzle client from @pagespace/db for all database access
Always structure message content using the message parts structure: { parts: [{ type: 'text', text: '...' }] }
Use ESM modules and TypeScript strict mode

Files:

  • apps/web/src/hooks/useBreadcrumbs.ts
  • apps/web/src/lib/ai/shared/hooks/useConversations.ts
apps/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/**/*.{ts,tsx}: Use centralized permission functions from @pagespace/lib/permissions for access control, such as getUserAccessLevel() and canUserEditPage()
Always use Drizzle client from @pagespace/db for database access instead of direct database connections
Always use message parts structure with parts array containing objects with type and text fields when constructing messages for AI

Files:

  • apps/web/src/hooks/useBreadcrumbs.ts
  • apps/web/src/lib/ai/shared/hooks/useConversations.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Format code with Prettier and lint with ESLint using the configuration at apps/web/eslint.config.mjs

Files:

  • apps/web/src/hooks/useBreadcrumbs.ts
  • apps/web/src/lib/ai/shared/hooks/useConversations.ts
apps/web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/src/**/*.{ts,tsx}: Use Zustand for client-side state management
Use SWR for server state and caching

Files:

  • apps/web/src/hooks/useBreadcrumbs.ts
  • apps/web/src/lib/ai/shared/hooks/useConversations.ts
🧠 Learnings (9)
📓 Common learnings
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:47.122Z
Learning: Applies to apps/web/src/**/*.{ts,tsx} : Use Zustand for client-side state management
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:45.713Z
Learning: Use Zustand for client state management and SWR for server state and caching
📚 Learning: 2025-12-14T14:54:47.122Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:47.122Z
Learning: Applies to apps/web/src/**/*.{ts,tsx} : Use Zustand for client-side state management

Applied to files:

  • docs/2.0-architecture/2.1-frontend/state-management.md
📚 Learning: 2025-12-14T14:54:15.319Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T14:54:15.319Z
Learning: Applies to apps/web/src/**/*.tsx : For document editing, register editing state using `useEditingStore.getState().startEditing()` and `endEditing()` to prevent unwanted UI refreshes

Applied to files:

  • docs/2.0-architecture/2.1-frontend/state-management.md
  • apps/web/src/hooks/useBreadcrumbs.ts
  • apps/web/src/lib/ai/shared/hooks/useConversations.ts
  • docs/3.0-guides-and-tools/ui-refresh-protection.md
📚 Learning: 2025-12-14T14:54:15.319Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T14:54:15.319Z
Learning: Applies to apps/web/src/**/*.tsx : For AI streaming operations, register streaming state using `useEditingStore.getState().startStreaming()` and `endStreaming()` to prevent unwanted UI refreshes

Applied to files:

  • docs/2.0-architecture/2.1-frontend/state-management.md
  • apps/web/src/hooks/useBreadcrumbs.ts
  • apps/web/src/lib/ai/shared/hooks/useConversations.ts
  • docs/3.0-guides-and-tools/ui-refresh-protection.md
📚 Learning: 2025-12-14T14:54:47.122Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:47.122Z
Learning: Applies to apps/web/src/**/*.{ts,tsx} : Use SWR for server state and caching

Applied to files:

  • docs/2.0-architecture/2.1-frontend/state-management.md
  • apps/web/src/hooks/useBreadcrumbs.ts
  • apps/web/src/lib/ai/shared/hooks/useConversations.ts
  • docs/3.0-guides-and-tools/ui-refresh-protection.md
📚 Learning: 2025-12-14T14:54:45.713Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:45.713Z
Learning: Use Zustand for client state management and SWR for server state and caching

Applied to files:

  • docs/2.0-architecture/2.1-frontend/state-management.md
  • docs/3.0-guides-and-tools/ui-refresh-protection.md
📚 Learning: 2025-12-14T14:54:15.319Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T14:54:15.319Z
Learning: Applies to apps/web/src/**/*.tsx : When using SWR, check `useEditingStore` state with `isAnyActive()` and set `isPaused` to prevent data refreshes during editing or streaming

Applied to files:

  • docs/2.0-architecture/2.1-frontend/state-management.md
  • apps/web/src/hooks/useBreadcrumbs.ts
  • apps/web/src/lib/ai/shared/hooks/useConversations.ts
  • docs/3.0-guides-and-tools/ui-refresh-protection.md
📚 Learning: 2025-12-14T14:54:47.122Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:47.122Z
Learning: Tech stack: Next.js 15 App Router + TypeScript + Tailwind + shadcn/ui

Applied to files:

  • docs/2.0-architecture/2.1-frontend/state-management.md
📚 Learning: 2025-12-14T14:54:38.009Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:38.009Z
Learning: Applies to apps/web/app/**/{route,route.ts,route.js} : In Next.js 15, `params` in dynamic routes are Promise objects. You MUST await `context.params` before destructuring.

Applied to files:

  • docs/2.0-architecture/2.1-frontend/state-management.md
🧬 Code graph analysis (2)
apps/web/src/hooks/useBreadcrumbs.ts (1)
apps/web/src/stores/useEditingStore.ts (1)
  • isEditingActive (134-134)
apps/web/src/lib/ai/shared/hooks/useConversations.ts (1)
apps/web/src/stores/useEditingStore.ts (1)
  • isEditingActive (134-134)
🔇 Additional comments (8)
apps/web/src/hooks/useBreadcrumbs.ts (1)

3-3: LGTM! Past concern about stale state has been resolved.

The isEditingActive helper function correctly reads live store state via getState() when SWR calls it, ensuring the pause decision always uses the current editing state rather than a stale value captured at render time. This addresses the concern raised in the previous review.

Based on learnings about using isAnyActive() with SWR's isPaused to prevent data refreshes during editing or streaming.

Also applies to: 27-27

apps/web/src/lib/ai/shared/hooks/useConversations.ts (1)

11-11: LGTM! Consistent implementation of UI refresh protection.

The hook correctly uses the isEditingActive helper to pause conversation fetching during editing or AI streaming. This implementation is consistent with the pattern used in useBreadcrumbs and usePageTree, ensuring conversations don't refresh and interrupt active user work.

Based on learnings about using isAnyActive() with SWR's isPaused to prevent data refreshes during editing or streaming.

Also applies to: 89-89

docs/3.0-guides-and-tools/ui-refresh-protection.md (2)

63-73: Excellent documentation of the pattern.

The added explanation clearly articulates why the isEditingActive helper function is used directly rather than a React hook selector. This guidance will help developers avoid the stale-closure pitfall and correctly implement UI refresh protection in new components.


332-339: Migration guide correctly demonstrates the pattern.

The migration examples consistently show using the isEditingActive helper directly, reinforcing the best practice documented earlier in the file.

docs/2.0-architecture/2.1-frontend/state-management.md (4)

50-60: Clear documentation of AI assistant state boundaries.

This new section effectively explains the intentional separation between different AI assistant contexts (Global Assistant, Dashboard Agent Mode, Sidebar Agent Mode, and AI_CHAT Pages) and references the centralized utilities that maintain consistency across these boundaries. The explanation reinforces good architectural separation.


89-106: Well-documented shift to URL-based navigation.

The updated Application State section clearly explains the architectural decision to use Next.js App Router's useParams() for navigation state rather than Zustand stores. This follows Next.js best practices and provides better browser integration (history, shareable URLs).


171-189: Store renaming reflects focused responsibilities.

Renaming "Layout Store" to "Sidebar State" and "UI Store" to "Tree State" accurately reflects their narrowed scope after navigation state moved to URL params. The updated documentation clearly defines each store's focused responsibilities.

Also applies to: 285-312


489-520: Architecture diagram clearly shows the separation.

The updated solution architecture effectively illustrates the URL-based navigation pattern alongside the specialized stores (Layout for sidebar visibility, UI for tree state, Document Manager for content). This visual reinforces the architectural separation documented throughout.


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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (6)
docs/2.0-architecture/2.1-frontend/state-management.md (1)

493-520: Add language identifier to code block.

The code block starting at line 493 is missing a language specifier for the Markdown renderer. Add typescript to the opening fence.

-```
+```typescript
 ✅ CURRENT ARCHITECTURE:
apps/web/src/lib/ai/shared/hooks/useConversations.ts (1)

71-71: Optimize isPaused to avoid unnecessary re-renders

Line 71 subscribes to store changes via the Zustand selector, which causes this component to re-render whenever isAnyActive changes. Since isPaused only needs to check the current state when SWR evaluates it (not trigger re-renders), you can avoid the subscription.

🔎 Recommended optimization

Remove the subscription and query the store directly in the isPaused callback:

- const isAnyActive = useEditingStore(state => state.isAnyActive());
-
  // SWR key for conversations list
  const swrKey = useMemo(() => {
    if (!enabled) return null;
    return isAgentMode
      ? `/api/ai/page-agents/${agentId}/conversations`
      : `/api/ai/global`;
  }, [enabled, isAgentMode, agentId]);

  // Fetch conversations with SWR
  const { data, isLoading } = useSWR(
    swrKey,
    async (url) => {
      const response = await fetchWithAuth(url);
      if (!response.ok) throw new Error('Failed to load conversations');
      return response.json();
    },
    {
-     isPaused: () => isAnyActive,
+     isPaused: () => useEditingStore.getState().isAnyActive(),
      revalidateOnFocus: false,
      revalidateOnReconnect: false,
      dedupingInterval: 5000,
    }
  );

This prevents the component from subscribing to editing store changes while still allowing SWR to check the current editing state when needed.

Based on learnings: When using SWR, check useEditingStore state with isAnyActive() and set isPaused to prevent data refreshes during editing or streaming.

apps/web/src/stores/page-agents/usePageAgentDashboardStore.ts (1)

254-306: Consider whether 'push' is appropriate when loading most recent conversation on agent switch.

At line 294, using 'push' mode when auto-loading the most recent conversation creates a history entry. This could lead to confusing back-button behavior when the user switches agents (they'd navigate back to the previous agent's conversation URL rather than exiting the dashboard).

Consider using 'replace' mode for automatic conversation loading during agent initialization, reserving 'push' for explicit user actions like clicking a conversation in the history list.

🔎 Proposed fix
       // Update URL
-      setChatParams({ agentId: agent.id, conversationId: mostRecent.id }, 'push');
+      setChatParams({ agentId: agent.id, conversationId: mostRecent.id }, 'replace');
       return;
apps/web/src/lib/ai/shared/agent-conversations.ts (3)

24-39: Defensive dual-format handling is good, but consider adding context to error messages.

The function correctly handles both UIMessage[] and wrapped AgentMessagesResponse formats, which is a good defensive pattern. However, the error message could include the agentId and conversationId for easier debugging.

💡 Optional: Add context to error messages for debugging
  if (!response.ok) {
-   throw new Error('Failed to load conversation messages');
+   throw new Error(`Failed to load conversation messages for agent ${agentId}, conversation ${conversationId}`);
  }

41-52: LGTM: Clean implementation with appropriate null handling.

The function correctly fetches the most recent conversation and returns null when none exist. Consider enhancing the error message with agentId context for debugging.

💡 Optional: Add agentId to error message
  if (!response.ok) {
-   throw new Error('Failed to load conversations');
+   throw new Error(`Failed to load conversations for agent ${agentId}`);
  }

54-69: Empty POST body is intentional and correct.

The API handler at apps/web/src/app/api/ai/page-agents/[agentId]/conversations/route.ts explicitly accepts an optional title field: const customTitle = body.title;. The empty body {} matches this contract—if no title is provided, the server defaults to 'New conversation'. No changes needed.

The error message enhancement suggestion remains valid: adding agentId context to error messages would improve debuggability, though it's not critical since the codebase generally uses generic messages.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 60e4275 and c5ef62e.

📒 Files selected for processing (19)
  • apps/web/src/components/layout/DebugPanel.tsx (2 hunks)
  • apps/web/src/components/layout/LayoutErrorBoundary.tsx (1 hunks)
  • apps/web/src/components/layout/NavigationProvider.tsx (1 hunks)
  • apps/web/src/components/layout/middle-content/CenterPanel.tsx (1 hunks)
  • apps/web/src/components/layout/right-sidebar/ai-assistant/SidebarHistoryTab.tsx (2 hunks)
  • apps/web/src/contexts/GlobalChatContext.tsx (4 hunks)
  • apps/web/src/hooks/page-agents/usePageAgentSidebarState.ts (5 hunks)
  • apps/web/src/hooks/useBreadcrumbs.ts (2 hunks)
  • apps/web/src/hooks/usePageTree.ts (2 hunks)
  • apps/web/src/hooks/useUI.ts (1 hunks)
  • apps/web/src/lib/ai/shared/agent-conversations.ts (1 hunks)
  • apps/web/src/lib/ai/shared/hooks/useConversations.ts (3 hunks)
  • apps/web/src/lib/ai/shared/index.ts (1 hunks)
  • apps/web/src/lib/url-state.ts (1 hunks)
  • apps/web/src/stores/__tests__/useUIStore.test.ts (3 hunks)
  • apps/web/src/stores/page-agents/usePageAgentDashboardStore.ts (7 hunks)
  • apps/web/src/stores/useLayoutStore.ts (1 hunks)
  • apps/web/src/stores/useUIStore.ts (3 hunks)
  • docs/2.0-architecture/2.1-frontend/state-management.md (15 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Never use any types in TypeScript code - always use proper TypeScript types

**/*.{ts,tsx}: No any types - always use proper TypeScript types
Use kebab-case for filenames (e.g., image-processor.ts)
Use camelCase for variables and functions
Use UPPER_SNAKE_CASE for constants
Use PascalCase for types and enums
Use centralized permission logic: import getUserAccessLevel and canUserEditPage from @pagespace/lib/permissions
Use Drizzle client from @pagespace/db for all database access
Always structure message content using the message parts structure: { parts: [{ type: 'text', text: '...' }] }
Use ESM modules and TypeScript strict mode

Files:

  • apps/web/src/contexts/GlobalChatContext.tsx
  • apps/web/src/lib/ai/shared/hooks/useConversations.ts
  • apps/web/src/components/layout/right-sidebar/ai-assistant/SidebarHistoryTab.tsx
  • apps/web/src/lib/url-state.ts
  • apps/web/src/lib/ai/shared/index.ts
  • apps/web/src/components/layout/NavigationProvider.tsx
  • apps/web/src/hooks/useBreadcrumbs.ts
  • apps/web/src/hooks/usePageTree.ts
  • apps/web/src/lib/ai/shared/agent-conversations.ts
  • apps/web/src/stores/__tests__/useUIStore.test.ts
  • apps/web/src/stores/page-agents/usePageAgentDashboardStore.ts
  • apps/web/src/hooks/useUI.ts
  • apps/web/src/components/layout/middle-content/CenterPanel.tsx
  • apps/web/src/components/layout/DebugPanel.tsx
  • apps/web/src/stores/useUIStore.ts
  • apps/web/src/components/layout/LayoutErrorBoundary.tsx
  • apps/web/src/hooks/page-agents/usePageAgentSidebarState.ts
  • apps/web/src/stores/useLayoutStore.ts
apps/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/**/*.{ts,tsx}: Use centralized permission functions from @pagespace/lib/permissions for access control, such as getUserAccessLevel() and canUserEditPage()
Always use Drizzle client from @pagespace/db for database access instead of direct database connections
Always use message parts structure with parts array containing objects with type and text fields when constructing messages for AI

Files:

  • apps/web/src/contexts/GlobalChatContext.tsx
  • apps/web/src/lib/ai/shared/hooks/useConversations.ts
  • apps/web/src/components/layout/right-sidebar/ai-assistant/SidebarHistoryTab.tsx
  • apps/web/src/lib/url-state.ts
  • apps/web/src/lib/ai/shared/index.ts
  • apps/web/src/components/layout/NavigationProvider.tsx
  • apps/web/src/hooks/useBreadcrumbs.ts
  • apps/web/src/hooks/usePageTree.ts
  • apps/web/src/lib/ai/shared/agent-conversations.ts
  • apps/web/src/stores/__tests__/useUIStore.test.ts
  • apps/web/src/stores/page-agents/usePageAgentDashboardStore.ts
  • apps/web/src/hooks/useUI.ts
  • apps/web/src/components/layout/middle-content/CenterPanel.tsx
  • apps/web/src/components/layout/DebugPanel.tsx
  • apps/web/src/stores/useUIStore.ts
  • apps/web/src/components/layout/LayoutErrorBoundary.tsx
  • apps/web/src/hooks/page-agents/usePageAgentSidebarState.ts
  • apps/web/src/stores/useLayoutStore.ts
apps/web/src/**/*.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/src/**/*.tsx: For document editing, register editing state using useEditingStore.getState().startEditing() and endEditing() to prevent unwanted UI refreshes
For AI streaming operations, register streaming state using useEditingStore.getState().startStreaming() and endStreaming() to prevent unwanted UI refreshes
When using SWR, check useEditingStore state with isAnyActive() and set isPaused to prevent data refreshes during editing or streaming

Files:

  • apps/web/src/contexts/GlobalChatContext.tsx
  • apps/web/src/components/layout/right-sidebar/ai-assistant/SidebarHistoryTab.tsx
  • apps/web/src/components/layout/NavigationProvider.tsx
  • apps/web/src/components/layout/middle-content/CenterPanel.tsx
  • apps/web/src/components/layout/DebugPanel.tsx
  • apps/web/src/components/layout/LayoutErrorBoundary.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Format code with Prettier and lint with ESLint using the configuration at apps/web/eslint.config.mjs

Files:

  • apps/web/src/contexts/GlobalChatContext.tsx
  • apps/web/src/lib/ai/shared/hooks/useConversations.ts
  • apps/web/src/components/layout/right-sidebar/ai-assistant/SidebarHistoryTab.tsx
  • apps/web/src/lib/url-state.ts
  • apps/web/src/lib/ai/shared/index.ts
  • apps/web/src/components/layout/NavigationProvider.tsx
  • apps/web/src/hooks/useBreadcrumbs.ts
  • apps/web/src/hooks/usePageTree.ts
  • apps/web/src/lib/ai/shared/agent-conversations.ts
  • apps/web/src/stores/__tests__/useUIStore.test.ts
  • apps/web/src/stores/page-agents/usePageAgentDashboardStore.ts
  • apps/web/src/hooks/useUI.ts
  • apps/web/src/components/layout/middle-content/CenterPanel.tsx
  • apps/web/src/components/layout/DebugPanel.tsx
  • apps/web/src/stores/useUIStore.ts
  • apps/web/src/components/layout/LayoutErrorBoundary.tsx
  • apps/web/src/hooks/page-agents/usePageAgentSidebarState.ts
  • apps/web/src/stores/useLayoutStore.ts
apps/web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/src/**/*.{ts,tsx}: Use Zustand for client-side state management
Use SWR for server state and caching

Files:

  • apps/web/src/contexts/GlobalChatContext.tsx
  • apps/web/src/lib/ai/shared/hooks/useConversations.ts
  • apps/web/src/components/layout/right-sidebar/ai-assistant/SidebarHistoryTab.tsx
  • apps/web/src/lib/url-state.ts
  • apps/web/src/lib/ai/shared/index.ts
  • apps/web/src/components/layout/NavigationProvider.tsx
  • apps/web/src/hooks/useBreadcrumbs.ts
  • apps/web/src/hooks/usePageTree.ts
  • apps/web/src/lib/ai/shared/agent-conversations.ts
  • apps/web/src/stores/__tests__/useUIStore.test.ts
  • apps/web/src/stores/page-agents/usePageAgentDashboardStore.ts
  • apps/web/src/hooks/useUI.ts
  • apps/web/src/components/layout/middle-content/CenterPanel.tsx
  • apps/web/src/components/layout/DebugPanel.tsx
  • apps/web/src/stores/useUIStore.ts
  • apps/web/src/components/layout/LayoutErrorBoundary.tsx
  • apps/web/src/hooks/page-agents/usePageAgentSidebarState.ts
  • apps/web/src/stores/useLayoutStore.ts
**/{components,src/**/components}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use PascalCase for React component names and filenames

Files:

  • apps/web/src/components/layout/right-sidebar/ai-assistant/SidebarHistoryTab.tsx
  • apps/web/src/components/layout/NavigationProvider.tsx
  • apps/web/src/components/layout/middle-content/CenterPanel.tsx
  • apps/web/src/components/layout/DebugPanel.tsx
  • apps/web/src/components/layout/LayoutErrorBoundary.tsx
🧠 Learnings (17)
📓 Common learnings
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:45.713Z
Learning: Use Zustand for client state management and SWR for server state and caching
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:47.122Z
Learning: Applies to apps/web/src/**/*.{ts,tsx} : Use Zustand for client-side state management
📚 Learning: 2025-12-16T19:03:59.870Z
Learnt from: 2witstudios
Repo: 2witstudios/PageSpace PR: 91
File: apps/web/src/components/ai/shared/chat/tool-calls/CompactToolCallRenderer.tsx:253-277
Timestamp: 2025-12-16T19:03:59.870Z
Learning: In apps/web/src/components/ai/shared/chat/tool-calls/CompactToolCallRenderer.tsx (TypeScript/React), use the `getLanguageFromPath` utility from `formatters.ts` to infer syntax highlighting language from file paths instead of hardcoding language values in DocumentRenderer calls.

Applied to files:

  • apps/web/src/contexts/GlobalChatContext.tsx
📚 Learning: 2025-12-14T14:54:15.319Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T14:54:15.319Z
Learning: Applies to apps/web/src/**/*.tsx : When using SWR, check `useEditingStore` state with `isAnyActive()` and set `isPaused` to prevent data refreshes during editing or streaming

Applied to files:

  • apps/web/src/lib/ai/shared/hooks/useConversations.ts
  • apps/web/src/hooks/useBreadcrumbs.ts
  • apps/web/src/hooks/usePageTree.ts
  • docs/2.0-architecture/2.1-frontend/state-management.md
📚 Learning: 2025-12-14T14:54:15.319Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T14:54:15.319Z
Learning: Applies to apps/web/src/**/*.tsx : For AI streaming operations, register streaming state using `useEditingStore.getState().startStreaming()` and `endStreaming()` to prevent unwanted UI refreshes

Applied to files:

  • apps/web/src/lib/ai/shared/hooks/useConversations.ts
  • apps/web/src/hooks/useBreadcrumbs.ts
  • apps/web/src/hooks/usePageTree.ts
  • apps/web/src/stores/__tests__/useUIStore.test.ts
  • apps/web/src/hooks/useUI.ts
  • docs/2.0-architecture/2.1-frontend/state-management.md
  • apps/web/src/stores/useUIStore.ts
📚 Learning: 2025-12-14T14:54:15.319Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T14:54:15.319Z
Learning: Applies to apps/web/src/**/*.tsx : For document editing, register editing state using `useEditingStore.getState().startEditing()` and `endEditing()` to prevent unwanted UI refreshes

Applied to files:

  • apps/web/src/lib/ai/shared/hooks/useConversations.ts
  • apps/web/src/hooks/useBreadcrumbs.ts
  • apps/web/src/hooks/usePageTree.ts
  • apps/web/src/stores/__tests__/useUIStore.test.ts
  • apps/web/src/hooks/useUI.ts
  • docs/2.0-architecture/2.1-frontend/state-management.md
  • apps/web/src/stores/useUIStore.ts
📚 Learning: 2025-12-18T05:22:42.263Z
Learnt from: 2witstudios
Repo: 2witstudios/PageSpace PR: 96
File: apps/web/src/components/layout/right-sidebar/ai-assistant/SidebarChatTab.tsx:641-657
Timestamp: 2025-12-18T05:22:42.263Z
Learning: In apps/web/src/components/layout/right-sidebar/ai-assistant/SidebarChatTab.tsx, the provider/model selector buttons are intentionally non-functional placeholders in the compact sidebar view. The `hideModelSelector={true}` prop is passed to ChatInput to hide the full ProviderModelSelector. Users are expected to use the full GlobalAssistantView for model selection. A settings link may be added in a future iteration.

Applied to files:

  • apps/web/src/components/layout/right-sidebar/ai-assistant/SidebarHistoryTab.tsx
📚 Learning: 2025-12-14T14:54:15.319Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T14:54:15.319Z
Learning: Applies to apps/web/src/app/**/*.ts : Get search params using `const { searchParams } = new URL(request.url);`

Applied to files:

  • apps/web/src/lib/url-state.ts
📚 Learning: 2025-12-14T14:54:38.009Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:38.009Z
Learning: Applies to apps/web/app/**/*.ts : Use `new URL(request.url).searchParams` to access URL search parameters in route handlers

Applied to files:

  • apps/web/src/lib/url-state.ts
📚 Learning: 2025-12-14T14:54:47.122Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:47.122Z
Learning: Tech stack: Next.js 15 App Router + TypeScript + Tailwind + shadcn/ui

Applied to files:

  • apps/web/src/components/layout/NavigationProvider.tsx
  • docs/2.0-architecture/2.1-frontend/state-management.md
  • apps/web/src/components/layout/middle-content/CenterPanel.tsx
📚 Learning: 2025-12-14T14:54:47.122Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:47.122Z
Learning: Applies to apps/web/src/**/*.{ts,tsx} : Use SWR for server state and caching

Applied to files:

  • apps/web/src/hooks/useBreadcrumbs.ts
  • apps/web/src/hooks/usePageTree.ts
  • docs/2.0-architecture/2.1-frontend/state-management.md
📚 Learning: 2025-12-14T14:54:45.713Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:45.713Z
Learning: Use Zustand for client state management and SWR for server state and caching

Applied to files:

  • apps/web/src/hooks/useBreadcrumbs.ts
  • docs/2.0-architecture/2.1-frontend/state-management.md
  • apps/web/src/stores/useLayoutStore.ts
📚 Learning: 2025-12-14T14:54:15.319Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T14:54:15.319Z
Learning: Applies to apps/**/*.{ts,tsx} : Always use message parts structure with `parts` array containing objects with `type` and `text` fields when constructing messages for AI

Applied to files:

  • apps/web/src/lib/ai/shared/agent-conversations.ts
📚 Learning: 2025-12-14T14:54:47.122Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:47.122Z
Learning: Applies to apps/web/src/**/*.{ts,tsx} : Use Zustand for client-side state management

Applied to files:

  • apps/web/src/hooks/useUI.ts
  • docs/2.0-architecture/2.1-frontend/state-management.md
  • apps/web/src/stores/useUIStore.ts
  • apps/web/src/stores/useLayoutStore.ts
📚 Learning: 2025-12-14T14:54:38.009Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:38.009Z
Learning: Applies to apps/web/app/**/{route,route.ts,route.js} : In Next.js 15, `params` in dynamic routes are Promise objects. You MUST await `context.params` before destructuring.

Applied to files:

  • docs/2.0-architecture/2.1-frontend/state-management.md
📚 Learning: 2025-12-14T14:54:47.122Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:47.122Z
Learning: Applies to **/*.{ts,tsx} : Use centralized permission logic: import `getUserAccessLevel` and `canUserEditPage` from `pagespace/lib/permissions`

Applied to files:

  • apps/web/src/components/layout/middle-content/CenterPanel.tsx
📚 Learning: 2025-12-14T14:54:45.713Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:45.713Z
Learning: Applies to app/**/*.{ts,tsx} : Import and use `getUserAccessLevel()` and `canUserEditPage()` from `pagespace/lib/permissions` for centralized permission logic

Applied to files:

  • apps/web/src/components/layout/middle-content/CenterPanel.tsx
📚 Learning: 2025-12-14T14:54:15.319Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T14:54:15.319Z
Learning: Applies to apps/**/*.{ts,tsx} : Use centralized permission functions from `pagespace/lib/permissions` for access control, such as `getUserAccessLevel()` and `canUserEditPage()`

Applied to files:

  • apps/web/src/components/layout/middle-content/CenterPanel.tsx
🧬 Code graph analysis (10)
apps/web/src/contexts/GlobalChatContext.tsx (1)
apps/web/src/lib/url-state.ts (3)
  • getAgentId (41-44)
  • setConversationId (46-48)
  • getConversationId (36-39)
apps/web/src/lib/ai/shared/hooks/useConversations.ts (1)
apps/web/src/stores/useEditingStore.ts (1)
  • useEditingStore (50-128)
apps/web/src/components/layout/right-sidebar/ai-assistant/SidebarHistoryTab.tsx (1)
apps/web/src/lib/url-state.ts (1)
  • setConversationId (46-48)
apps/web/src/hooks/usePageTree.ts (1)
apps/web/src/stores/useEditingStore.ts (1)
  • useEditingStore (50-128)
apps/web/src/lib/ai/shared/agent-conversations.ts (1)
apps/web/src/lib/auth/auth-fetch.ts (1)
  • fetchWithAuth (704-705)
apps/web/src/stores/__tests__/useUIStore.test.ts (1)
apps/web/src/stores/useUIStore.ts (1)
  • useUIStore (14-50)
apps/web/src/stores/page-agents/usePageAgentDashboardStore.ts (2)
apps/web/src/lib/url-state.ts (3)
  • setChatParams (62-64)
  • getConversationId (36-39)
  • getAgentId (41-44)
apps/web/src/lib/ai/shared/agent-conversations.ts (3)
  • fetchAgentConversationMessages (24-39)
  • createAgentConversation (54-69)
  • fetchMostRecentAgentConversation (41-52)
apps/web/src/hooks/useUI.ts (1)
apps/web/src/stores/useUIStore.ts (1)
  • useUIStore (14-50)
apps/web/src/components/layout/DebugPanel.tsx (1)
apps/web/src/components/ui/badge.tsx (1)
  • Badge (46-46)
apps/web/src/hooks/page-agents/usePageAgentSidebarState.ts (2)
apps/web/src/lib/ai/shared/agent-conversations.ts (3)
  • fetchMostRecentAgentConversation (41-52)
  • fetchAgentConversationMessages (24-39)
  • createAgentConversation (54-69)
packages/db/src/schema/conversations.ts (1)
  • messages (30-46)
🪛 markdownlint-cli2 (0.18.1)
docs/2.0-architecture/2.1-frontend/state-management.md

493-493: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Unit Tests
🔇 Additional comments (34)
docs/2.0-architecture/2.1-frontend/state-management.md (8)

50-59: Clear and well-scoped AI assistant state boundaries section.

The new "AI Assistant State Boundaries" section correctly documents the intentional separation of assistant state by context (global, dashboard, sidebar, AI_CHAT pages) and points to the agent-conversations.ts shared helpers and url-state.ts utilities. This aligns well with the PR's goal of centralizing URL state mechanics and keeping conversation fetch/create behavior consistent without merging state.


89-105: Excellent refocus on URL-based navigation.

The renamed section "Application State (URL-based Navigation)" and updated patterns clearly establish that navigation state is derived from the URL via useParams() rather than stored in Zustand. The explicit note about PageSpace using Next.js App Router best practices with automatic browser history and shareable URLs is helpful for developers onboarding to the new architecture. Based on learnings, this aligns with the established pattern that navigation state should be URL-driven.


171-188: Layout Store scope clarified.

The narrowed responsibilities (sidebar visibility and hydration state only) and explicit note that navigation state is read from URL params via useParams() are important clarifications. This prevents developers from attempting to store navigation pointers in the layout store, which was a source of re-render cascades in earlier iterations.


285-311: UI Store refocused to tree state with clean access pattern.

Renaming to "UI Store (Tree State)" and introducing the useTreeState() hook pattern (lines 303-309) provides a cleaner, more intentional API than accessing the store directly. The note clarifying that sidebar visibility remains a layout store concern (line 311) is important for preventing scope creep. Based on learnings, this supports the pattern of using dedicated tree state management while keeping layout concerns separate.


489-520: Architecture summary clearly illustrates decoupled concerns.

The visual architecture breakdown showing navigation via URL params, layout/UI/document isolation, and the SWR layer is an excellent reference. The comment "Layout changes do NOT affect document state" (lines 397-398 in diagram) reinforces the critical design decision that prevents re-render cascades.


611-625: Document state protection pattern well-documented.

The code example for registering editing state with useEditingStore and the explicit note that this "prevents SWR revalidations during active editing" is directly aligned with the learnings about using useEditingStore.getState().startEditing() / endEditing() and checking isPaused: () => isAnyActive on SWR hooks. This is critical guidance for preventing editing interruptions.


532-543: Migration guide clearly shows the old vs. new pattern.

The side-by-side comparison of store-based navigation (❌ OLD) versus URL params (✅ NEW) provides a clear path for developers updating old code. The pattern is concise and unambiguous.


555-571: Best practices table and guidelines are comprehensive.

The reorganized "When to Use Each State Type" section with explicit checkmarks for each state category, combined with the updated "Navigation State (URL params)" section, provides clear decision trees for developers. The SWR configuration section (lines 573-586) emphasizing conservative polling intervals and isPaused: () => isAnyActive is particularly important for stability. As per learnings and coding guidelines, this matches the established patterns for editing/streaming state protection.

apps/web/src/stores/useLayoutStore.ts (2)

1-18: Clean refactoring to a minimal layout store.

The simplified interface correctly separates concerns: UI panel states that warrant persistence vs. runtime hydration tracking. This aligns with the PR objective of moving navigation state to URL parameters. Based on learnings, the use of Zustand for client-side UI state is appropriate here.


20-59: Well-structured persist configuration.

The partialize function correctly limits persistence to only the sidebar visibility states, excluding the runtime rehydrated flag. The onRehydrateStorage callback with optional chaining (state?.setRehydrated()) properly handles the hydration lifecycle. This is a clean Zustand persist pattern.

apps/web/src/lib/ai/shared/index.ts (1)

10-10: LGTM - Clean module export

The re-export of agent-conversations helpers through the shared AI utilities index is appropriate and follows the module organization pattern.

apps/web/src/hooks/useUI.ts (1)

1-32: LGTM - Excellent consolidation of UI state hooks

The refactor successfully consolidates multiple small selector hooks into a single, focused useTreeState hook. The use of useCallback for derived functions (isExpanded, toggleExpanded) ensures stable references and prevents unnecessary re-renders.

apps/web/src/stores/useUIStore.ts (1)

1-50: LGTM - Clean state reduction with correct Set persistence

The refactor successfully narrows the UI store to focus solely on tree state. The persistence logic correctly handles Set serialization (converting to Array for storage) and deserialization (converting back to Set on rehydration), with defensive handling for both array and set types.

apps/web/src/stores/__tests__/useUIStore.test.ts (1)

1-137: LGTM - Tests correctly reflect the refactored store

The test suite has been appropriately updated to focus on tree state management. The state independence tests (lines 116-136) are particularly valuable, ensuring that tree expansion and scroll position remain properly isolated.

apps/web/src/lib/url-state.ts (1)

1-64: LGTM - Clean URL state management utility

This new utility provides a centralized, type-safe approach to managing chat-related URL parameters. The implementation correctly:

  • Guards against SSR environments
  • Preserves the current URL path while updating params
  • Supports both push and replace modes for history manipulation
  • Uses concise param names ('c', 'agent') for cleaner URLs
apps/web/src/components/layout/LayoutErrorBoundary.tsx (1)

65-75: LGTM - Simplified error recovery

The removal of layoutStore.clearCache() aligns with the broader refactor to eliminate layout store dependencies. Error boundary cleanup now focuses on clearing potentially corrupted storage without side effects on layout state.

apps/web/src/components/layout/NavigationProvider.tsx (1)

21-58: LGTM - Simplified navigation provider

The removal of layout store dependencies successfully simplifies the NavigationProvider. The component now focuses on its core responsibilities: providing an error boundary and handling unsaved changes warnings, without coupling to layout state management.

apps/web/src/components/layout/right-sidebar/ai-assistant/SidebarHistoryTab.tsx (2)

17-17: LGTM!

The import of setConversationId from the centralized URL state module aligns with the PR's objective to consolidate URL parameter management.


139-145: LGTM!

Good refactor replacing manual window.history manipulation with the centralized setConversationId helper. The 'push' mode correctly creates a browser history entry for conversation navigation.

apps/web/src/components/layout/middle-content/CenterPanel.tsx (1)

112-130: LGTM!

Good refactor deriving header visibility directly from URL params via useParams() instead of relying on the layout store. This aligns with Next.js App Router patterns and the PR's goal of URL-driven navigation.

apps/web/src/contexts/GlobalChatContext.tsx (3)

7-7: LGTM!

Centralized URL state imports align with the PR's refactoring goals.


116-119: LGTM!

Correct usage of getAgentId() to check agent selection before updating URL, and setConversationId() with 'push' mode for new conversation creation.


141-172: LGTM!

Good refactor replacing manual URLSearchParams parsing with centralized getConversationId() and getAgentId() helpers. The 'replace' mode at line 171 is appropriate for restoring existing conversation state without creating extra history entries.

apps/web/src/stores/page-agents/usePageAgentDashboardStore.ts (4)

4-11: LGTM!

Good consolidation of imports: agent conversation helpers from @/lib/ai/shared and URL state helpers from @/lib/url-state. This aligns with the PR's objective to extract shared functionality.


96-113: LGTM!

Clean refactor of selectAgent using setChatParams for URL synchronization. The logic correctly clears conversationId when switching agents and clears both params when switching back to Global Assistant.


178-200: LGTM!

Good refactor replacing inline fetch logic with the fetchAgentConversationMessages helper. Error handling is preserved with toast notifications.


205-231: LGTM!

Clean extraction using createAgentConversation helper. The helper handles response parsing and error cases internally.

apps/web/src/components/layout/DebugPanel.tsx (3)

12-23: LGTM!

Icon imports updated appropriately: Activity for the performance tab, X for the close button. Removed unused MemoryStick import.


226-231: LGTM!

Good addition of the "Rehydrated" status badge. This provides useful visibility into Zustand's hydration state for debugging purposes.


39-43: Remove this review comment - there is no clearCache() method to call in useLayoutStore.

The current implementation of the clearCache() function in DebugPanel.tsx is correct. The layout store only contains UI state management methods (toggleLeftSidebar, toggleRightSidebar, etc.) and no clearCache() method exists. The function properly clears the persisted layout state by removing the 'layout-storage' key (matching Zustand's persistence configuration), clears session storage, and reloads the page—this is the intended behavior.

Likely an incorrect or invalid review comment.

apps/web/src/hooks/page-agents/usePageAgentSidebarState.ts (3)

7-11: LGTM: Clean refactoring to shared API helpers.

The import and usage of centralized conversation helpers eliminates code duplication and improves maintainability across the codebase.


238-268: LGTM: Load/create flow correctly uses shared helpers.

The refactored logic properly:

  • Attempts to load the most recent conversation
  • Falls back to creating a new conversation if none exists
  • Preserves race condition protection with loadingAgentIdRef
  • Handles errors appropriately with logging and toast notifications

297-299: LGTM: Action functions correctly delegate to shared helpers.

Both createNewConversation and refreshConversation properly use the centralized API helpers with appropriate error handling.

Also applies to: 316-317

apps/web/src/lib/ai/shared/agent-conversations.ts (1)

1-22: LGTM: Clean type definitions with defensive optional fields.

The imports and type definitions are well-structured. Using optional fields in the response types provides good defensive programming against API shape variations.

Comment thread apps/web/src/hooks/useBreadcrumbs.ts Outdated
Comment thread apps/web/src/hooks/usePageTree.ts Outdated
2witstudios and others added 2 commits December 19, 2025 10:41
- Fix stale isPaused closure in useBreadcrumbs, usePageTree, useConversations
  (use isEditingActive helper that reads live state via getState())
- Change 'push' to 'replace' when auto-loading most recent conversation
- Add agentId/conversationId context to error messages for debugging
- Update ui-refresh-protection.md docs with correct isPaused pattern
- Add language identifier to code block in state-management.md

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Keep language identifier (```text) for code block as per review feedback.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@2witstudios
2witstudios merged commit ca507e7 into master Dec 19, 2025
2 of 3 checks passed
2witstudios added a commit that referenced this pull request Dec 19, 2025
* feat(db): add activityLogs table for audit trail

Add new database schema for enterprise activity monitoring:
- New enums: activity_operation, activity_resource
- New activityLogs table with:
  - User attribution with AI context (isAiGenerated, aiProvider, aiModel)
  - Full content snapshots for future rollback support
  - Hierarchical context (driveId, pageId) for filtering
  - Indexed for efficient queries by timestamp, user, drive, page

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(lib): add activity logger service

Add fire-and-forget activity logging functions:
- logActivity(): Core logging function
- logPageActivity(): Page CRUD operations
- logPermissionActivity(): Permission changes
- logDriveActivity(): Drive operations
- logAgentConfigActivity(): Agent configuration changes

Designed to never block user operations while maintaining
comprehensive audit trail for enterprise compliance.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(api): add /api/activities endpoint

Add context-aware activity fetching endpoint:
- user context: User's own activity (dashboard view)
- drive context: All drive activity (drive view)
- page context: All page edits (page view)

Includes permission checks (canUserViewPage, isUserDriveMember)
and pagination support for large activity lists.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(ui): replace Settings tab with Activity tab in sidebar

- Add SidebarActivityTab component with context-aware activity display
- Update right sidebar to use Activity tab instead of Settings
- Update GlobalAssistantView to open Activity tab

Activity tab features:
- Search filtering
- User avatars with AI indicator (Bot icon)
- Operation icons (create, update, delete, etc.)
- Relative timestamps
- Loading skeletons

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor(store): update SidebarTab type from 'settings' to 'activity'

Update usePageAgentDashboardStore and tests to use 'activity'
tab instead of 'settings' to match new sidebar structure.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(api): inject activity logging into page CRUD routes

Add logPageActivity calls to track:
- Page creation (POST /api/pages)
- Page updates (PATCH /api/pages/[pageId])
- Page deletion/trash (DELETE /api/pages/[pageId])
- Page restoration (POST /api/pages/[pageId]/restore)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(api): inject activity logging into permission routes

Add logPermissionActivity calls to track:
- Permission grants (POST /api/pages/[pageId]/permissions)
- Permission updates (POST with existing permission)
- Permission revocations (DELETE /api/pages/[pageId]/permissions)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(api): handle null values in activities query params

Zod's .optional() and .default() only work with undefined, not null.
searchParams.get() returns null for missing params, causing validation
errors. Convert null → undefined with ?? operator.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(ai): add activity logging to all AI tool operations

AI tools now log to the activity system with full attribution:
- Extended ToolExecutionContext with aiProvider, aiModel fields
- Added logging to 11 write tools across 4 tool files
- Pass AI context through experimental_context in 3 API routes
- Export monitoring module from @pagespace/lib/server

Tools now logged: replace_lines, create_page, rename_page, trash,
restore, move_page, edit_sheet_cells, update_agent_config,
update_task, create_drive, rename_drive

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address PR review comments

- Wrap switch case declarations in blocks (/api/activities/route.ts)
- Change driveId FK to 'set null' for audit trail preservation
- Fix ProviderSetupCard to use inline mode instead of broken handler
- Fix conversationId mapping to use session ID not page ID

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test: fix mocks for activity logging functions

Add missing mock functions for the new activity logging exports:
- agent-tools.test.ts: add logAgentConfigActivity mock
- page-write-tools.test.ts: add logPageActivity, logDriveActivity mocks
- permissions/route.test.ts: add logPermissionActivity mock (moved to
  @pagespace/lib), add @pagespace/db mock for db.query.pages.findFirst

All 2073 tests now pass.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: prefix unused provider param with underscore

* Rewire iOS app after web refactor (#97)

* fix(stores): restore missing page header and clean up dead navigation code (#100)

* fix(stores): restore missing page header and clean up dead navigation code

The page header (Share button, breadcrumbs, etc.) was missing from all pages
because OptimizedViewHeader checked layoutStore.activePageId which was always
null - the navigation system was designed but never wired up.

Changes:
- Fix OptimizedViewHeader to use useParams() instead of dead store state
- Remove all dead navigation code from useLayoutStore (352→60 lines)
- Remove duplicate sidebar state from useUIStore (97→50 lines)
- Remove dead cleanup code from NavigationProvider.tsx
- Remove dead sidebar hooks from useUI.ts (keep only useTreeState)
- Update store tests to match new simplified state
- Update state-management.md documentation

The two stores now have clear, non-overlapping responsibilities:
- useLayoutStore: sidebar open/closed state (persisted)
- useUIStore: tree expansion and scroll state (persisted)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: remove references to deleted clearCache method in layout store

The layout store was simplified to only manage sidebar state, but
DebugPanel and LayoutErrorBoundary still referenced the deleted
clearCache() method and other removed properties (viewCache,
activeDriveId, activePageId, centerViewType).

These were all part of a navigation system that was designed but
never wired up (dead code). The useful functionality is preserved:
- Clear Cache button still clears localStorage/sessionStorage
- Error boundary still clears storage on error

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: extract URL state and agent conversation helpers (#101)

* fix(stores): restore missing page header and clean up dead navigation code

The page header (Share button, breadcrumbs, etc.) was missing from all pages
because OptimizedViewHeader checked layoutStore.activePageId which was always
null - the navigation system was designed but never wired up.

Changes:
- Fix OptimizedViewHeader to use useParams() instead of dead store state
- Remove all dead navigation code from useLayoutStore (352→60 lines)
- Remove duplicate sidebar state from useUIStore (97→50 lines)
- Remove dead cleanup code from NavigationProvider.tsx
- Remove dead sidebar hooks from useUI.ts (keep only useTreeState)
- Update store tests to match new simplified state
- Update state-management.md documentation

The two stores now have clear, non-overlapping responsibilities:
- useLayoutStore: sidebar open/closed state (persisted)
- useUIStore: tree expansion and scroll state (persisted)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: remove references to deleted clearCache method in layout store

The layout store was simplified to only manage sidebar state, but
DebugPanel and LayoutErrorBoundary still referenced the deleted
clearCache() method and other removed properties (viewCache,
activeDriveId, activePageId, centerViewType).

These were all part of a navigation system that was designed but
never wired up (dead code). The useful functionality is preserved:
- Clear Cache button still clears localStorage/sessionStorage
- Error boundary still clears storage on error

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: extract URL state and agent conversation helpers

- Add centralized url-state.ts for chat URL param management
- Add shared agent-conversations.ts API helpers (DRY up fetch calls)
- Add UI refresh protection (isPaused) to useBreadcrumbs, usePageTree, useConversations
- Refactor usePageAgentDashboardStore and usePageAgentSidebarState to use shared helpers
- Update state-management.md with AI assistant state boundaries documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address PR review comments from CodeRabbit

- Fix stale isPaused closure in useBreadcrumbs, usePageTree, useConversations
  (use isEditingActive helper that reads live state via getState())
- Change 'push' to 'replace' when auto-loading most recent conversation
- Add agentId/conversationId context to error messages for debugging
- Update ui-refresh-protection.md docs with correct isPaused pattern
- Add language identifier to code block in state-management.md

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* feat(db): add activityLogs table for audit trail

Add new database schema for enterprise activity monitoring:
- New enums: activity_operation, activity_resource
- New activityLogs table with:
  - User attribution with AI context (isAiGenerated, aiProvider, aiModel)
  - Full content snapshots for future rollback support
  - Hierarchical context (driveId, pageId) for filtering
  - Indexed for efficient queries by timestamp, user, drive, page

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(lib): add activity logger service

Add fire-and-forget activity logging functions:
- logActivity(): Core logging function
- logPageActivity(): Page CRUD operations
- logPermissionActivity(): Permission changes
- logDriveActivity(): Drive operations
- logAgentConfigActivity(): Agent configuration changes

Designed to never block user operations while maintaining
comprehensive audit trail for enterprise compliance.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(api): add /api/activities endpoint

Add context-aware activity fetching endpoint:
- user context: User's own activity (dashboard view)
- drive context: All drive activity (drive view)
- page context: All page edits (page view)

Includes permission checks (canUserViewPage, isUserDriveMember)
and pagination support for large activity lists.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(ui): replace Settings tab with Activity tab in sidebar

- Add SidebarActivityTab component with context-aware activity display
- Update right sidebar to use Activity tab instead of Settings
- Update GlobalAssistantView to open Activity tab

Activity tab features:
- Search filtering
- User avatars with AI indicator (Bot icon)
- Operation icons (create, update, delete, etc.)
- Relative timestamps
- Loading skeletons

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor(store): update SidebarTab type from 'settings' to 'activity'

Update usePageAgentDashboardStore and tests to use 'activity'
tab instead of 'settings' to match new sidebar structure.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(api): inject activity logging into page CRUD routes

Add logPageActivity calls to track:
- Page creation (POST /api/pages)
- Page updates (PATCH /api/pages/[pageId])
- Page deletion/trash (DELETE /api/pages/[pageId])
- Page restoration (POST /api/pages/[pageId]/restore)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(api): inject activity logging into permission routes

Add logPermissionActivity calls to track:
- Permission grants (POST /api/pages/[pageId]/permissions)
- Permission updates (POST with existing permission)
- Permission revocations (DELETE /api/pages/[pageId]/permissions)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(api): handle null values in activities query params

Zod's .optional() and .default() only work with undefined, not null.
searchParams.get() returns null for missing params, causing validation
errors. Convert null → undefined with ?? operator.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(ai): add activity logging to all AI tool operations

AI tools now log to the activity system with full attribution:
- Extended ToolExecutionContext with aiProvider, aiModel fields
- Added logging to 11 write tools across 4 tool files
- Pass AI context through experimental_context in 3 API routes
- Export monitoring module from @pagespace/lib/server

Tools now logged: replace_lines, create_page, rename_page, trash,
restore, move_page, edit_sheet_cells, update_agent_config,
update_task, create_drive, rename_drive

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address PR review comments

- Wrap switch case declarations in blocks (/api/activities/route.ts)
- Change driveId FK to 'set null' for audit trail preservation
- Fix ProviderSetupCard to use inline mode instead of broken handler
- Fix conversationId mapping to use session ID not page ID

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test: fix mocks for activity logging functions

Add missing mock functions for the new activity logging exports:
- agent-tools.test.ts: add logAgentConfigActivity mock
- page-write-tools.test.ts: add logPageActivity, logDriveActivity mocks
- permissions/route.test.ts: add logPermissionActivity mock (moved to
  @pagespace/lib), add @pagespace/db mock for db.query.pages.findFirst

All 2073 tests now pass.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: prefix unused provider param with underscore

* refactor: replace ORM chain mocks with repository seams

Create proper architectural boundaries for database operations:

- Add pageRepository with 11 methods (findById, create, update, trash, etc.)
- Add driveRepository with 5 methods (findById, findByIdBasic, etc.)
- Add agentRepository with 2 methods (findById, updateConfig)

Refactor AI tools to use repository seams:
- agent-tools.ts now uses agentRepository
- page-write-tools.ts now uses pageRepository + driveRepository

Rewrite tests to mock repository boundaries:
- agent-tools.test.ts: removed @scaffold label, 9 tests passing
- page-write-tools.test.ts: removed @scaffold label, 23 tests passing

Test scores improved from 6/10 to 9/10 per testing rubric.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(test): add missing isEditingActive export to usePageTree test mock

The test mock for @/stores/useEditingStore was missing the
isEditingActive named export that usePageTree.ts imports.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: remove unused db imports from page-write-tools

Removed leftover imports (db, pages, drives, eq, and) that were
replaced by repository seams in the refactor.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(test): add missing getActorInfo and logPageActivity mocks

Added missing mocks for activity logging functions that were added
to the API routes:
- getActorInfo in @pagespace/lib/server mock
- logPageActivity in @pagespace/lib mock

Fixes 8 failing tests in pages route tests.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: implement fire-and-forget activity logging pattern

Replace blocking `await getActorInfo()` calls with non-blocking helpers
to avoid blocking user operations during activity logging:

- Add logPageActivityAsync() for fire-and-forget page activity logging
- Add logDriveActivityAsync() for fire-and-forget drive activity logging
- Update all 9 logging call sites in page-write-tools.ts
- Handle errors gracefully (still log activity even if actor lookup fails)
- Use nullish coalescing for optional context fields
- Fix TypeScript errors in test file with proper type assertions

Addresses CodeRabbit review comment about blocking activity logging
defeating the fire-and-forget design goal.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: use null instead of undefined for parentId when moving to root

Also add activity logging test assertions to verify logging is called
after successful page operations (replace_lines, create_page, rename_page).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: prefix unused mockLogDriveActivity with underscore

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
@2witstudios
2witstudios deleted the fix/header-missing-cleanup-dead-store-code branch January 29, 2026 02:24
2witstudios added a commit that referenced this pull request Apr 8, 2026
…ts (#98-101)

Add Zod schema validation and log sanitization to break CodeQL taint
chains in the integration OAuth callback. Alert #101 dismissed as S4
false positive — verifySignedState() provides HMAC-SHA256 verification.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2witstudios added a commit that referenced this pull request Apr 8, 2026
…ts (#98-101)

Add Zod schema validation and log sanitization to break CodeQL taint
chains in the integration OAuth callback. Alert #101 dismissed as S4
false positive — verifySignedState() provides HMAC-SHA256 verification.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2witstudios added a commit that referenced this pull request Apr 8, 2026
)

* fix(security): harden integration OAuth callback against CWE-807 alerts (#98-101)

Add Zod schema validation and log sanitization to break CodeQL taint
chains in the integration OAuth callback. Alert #101 dismissed as S4
false positive — verifySignedState() provides HMAC-SHA256 verification.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(security): validate visibility enum and restore env in tests

Replace unsafe type assertion for visibility with runtime validation
against allowed values. Add afterEach env var cleanup in callback tests
to prevent cross-test pollution.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(security): address CodeRabbit review feedback

- Persist visibility on reconnect by passing it to updateConnectionCredentials
- Add optional visibility parameter to updateConnectionCredentials repository fn
- Replace vi.clearAllMocks with vi.resetAllMocks for proper mock isolation
- Use mockReturnValueOnce/mockResolvedValueOnce for per-test overrides
- Add test coverage for visibility enum validation (valid, invalid, default)
- Fix file count mismatches in CODEQL_ALERT_LOG.md subsection headings

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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