Skip to content

RG-T117 Chat feature flag - #35

Merged
ucswift merged 2 commits into
masterfrom
develop
Aug 8, 2026
Merged

RG-T117 Chat feature flag#35
ucswift merged 2 commits into
masterfrom
develop

Conversation

@ucswift

@ucswift ucswift commented Aug 8, 2026

Copy link
Copy Markdown
Member

Summary

This PR introduces a feature flag system and uses it to gate the entire Chat feature behind a department-scoped Chat.System flag. When the flag is disabled for a department, all chat functionality is hidden and its background services are skipped.

What was added

  • Feature Flags API client (src/api/feature-flags/feature-flags.ts): API functions (getAllFeatureFlags, getFeatureFlagState) that call the v4 FeatureToggles endpoints.
  • Feature Flags store (src/stores/feature-flags/store.ts): A persisted Zustand store that fetches, caches, and exposes feature flags. Provides reactive hooks (useFeatureFlag, useIsChatEnabled). Unknown flags default to disabled, and cached flags are retained on fetch failure for offline stability.

What was changed

  • App initialization (_layout.tsx): Feature flags are now fetched during startup. The SignalR chat hub only connects when the ChatSystem flag is enabled.
  • Chat screens (chat.tsx, chatbot.tsx, chat/[channelId].tsx, chat/thread/[messageId].tsx): Each screen checks useIsChatEnabled() and redirects to the home screen if chat is disabled. Data-fetching effects are also guarded by the flag.
  • Sidebar navigation (sidebar-content.tsx): The Chat and Assistant/Bot menu entries are filtered out when the flag is off.

Functional impact

Departments that don't have the Chat.System feature flag enabled will no longer see chat-related UI, won't connect to the chat SignalR hub, and won't trigger chat data fetches — effectively removing chat from the app experience for those departments.

Summary by CodeRabbit

  • New Features
    • Added feature-flag support for retrieving and evaluating application settings.
    • Chat and chatbot availability now responds to the ChatSystem setting.
  • Improvements
    • Chat-related screens display loading states while availability is determined and redirect when disabled.
    • Chat navigation options and real-time connections are hidden or skipped when unavailable.
    • Feature settings persist safely and reset during logout.
  • Bug Fixes
    • Prevented chat data loading and initialization when chat is disabled.
  • Tests
    • Added coverage for feature-flag loading, persistence, reset behavior, errors, and chat availability.

@Resgrid-Bot

This comment has been minimized.

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds a feature-flags API and persisted Zustand store. App initialization loads flags before chat connection. Chat screens, chatbot flows, and sidebar entries use ChatSystem status to control rendering, operations, navigation, and SignalR connection.

Changes

Chat feature flag integration

Layer / File(s) Summary
Feature-flag contract and store
src/api/feature-flags/feature-flags.ts, src/stores/feature-flags/store.ts, src/stores/feature-flags/__tests__/store.test.ts
Adds feature-toggle response models, API helpers, persisted identity-aware flag state, tri-state status hooks, and tests for loading, stale data, failures, and lookups.
Feature-flag reset integration
src/services/app-reset.service.ts, src/services/__tests__/app-reset.service.test.ts
Defines the initial feature-flag state, resets it during resetAllStores, and verifies the reset behavior.
Flag-aware app initialization
src/app/(app)/_layout.tsx, src/stores/signalr/signalr-store.ts
Loads feature flags before startup and skips chat hub connection when ChatSystem is disabled.
Chat surface gating
src/app/(app)/chat.tsx, src/app/(app)/chatbot.tsx, src/app/chat/[channelId].tsx, src/app/chat/thread/[messageId].tsx, src/components/sidebar/sidebar-content.tsx
Shows loading while chat status is unresolved, redirects disabled chat routes to /, skips chat operations, and filters chat navigation entries.

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

Sequence Diagram(s)

sequenceDiagram
  participant AppLayout
  participant featureFlagsStore
  participant ChatSignalRHub
  participant ChatScreens
  AppLayout->>featureFlagsStore: fetchFlags()
  featureFlagsStore-->>AppLayout: Return ChatSystem status
  AppLayout->>ChatSignalRHub: connectChatHub() when enabled
  ChatScreens->>featureFlagsStore: Read ChatSystem status
  ChatScreens->>ChatScreens: Show spinner, redirect, or run chat operations
Loading

Possibly related PRs

  • Resgrid/IC#30: Adds the chat entry points and SignalR integration that this PR gates with ChatSystem.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: introducing a feature flag for the Chat feature.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch develop

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

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (5)
src/api/feature-flags/feature-flags.ts (3)

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

Use the configured alias for the API client.

Replace the relative import with @/api/common/client.

As per coding guidelines, src/**/*.{ts,tsx} must use configured path aliases instead of relative imports.

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

In `@src/api/feature-flags/feature-flags.ts` at line 1, Update the API client
import in feature-flags.ts to use the configured "`@/api/common/client`" alias
instead of the relative path, preserving the existing api symbol.

Source: Coding guidelines


28-36: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the API endpoint abstraction.

Define both requests through createApiEndpoint or createCachedApiEndpoint. Do not call api.get directly from this API module.

As per coding guidelines, src/api/**/*.ts must define API endpoints through createApiEndpoint or createCachedApiEndpoint.

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

In `@src/api/feature-flags/feature-flags.ts` around lines 28 - 36, Replace the
direct api.get calls in getAllFeatureFlags and getFeatureFlagState with endpoint
definitions created through createApiEndpoint or createCachedApiEndpoint, then
invoke those abstractions while preserving the existing paths, response types,
parameters, and AbortSignal support.

Source: Coding guidelines


10-25: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move feature-toggle response models to the v4 model layer.

Move FeatureToggleData, FeatureTogglesResult, and FeatureToggleResult into src/models/v4/, organized by domain. Import those models with import type.

As per coding guidelines, API response models must remain in src/models/v4/, organized by domain.

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

In `@src/api/feature-flags/feature-flags.ts` around lines 10 - 25, Move the
FeatureToggleData, FeatureTogglesResult, and FeatureToggleResult interfaces out
of the feature-flags API module into the appropriate domain-organized files
under src/models/v4/. Update the feature-flags implementation to import these
models using import type, preserving their existing shapes and usage.

Source: Coding guidelines

src/app/(app)/chat.tsx (1)

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

Separate Href into a type-only import.

  • src/app/(app)/chat.tsx#L1-L1: import Href with import type after value imports.
  • src/app/chat/[channelId].tsx#L2-L2: import Href with import type after value imports.

As per coding guidelines, type-only imports must use import type, and type imports must follow value imports.

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

In `@src/app/`(app)/chat.tsx at line 1, Update the imports in
src/app/(app)/chat.tsx at lines 1-1 and src/app/chat/[channelId].tsx at lines
2-2 so Href is removed from value imports and added via a separate import type
statement after the remaining value imports.

Source: Coding guidelines

src/stores/feature-flags/store.ts (1)

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

Use the configured alias for storage.

Replace ../../lib/storage with @/lib/storage.

As per coding guidelines, src/**/*.{ts,tsx} must use configured path aliases instead of relative imports.

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

In `@src/stores/feature-flags/store.ts` at line 7, Update the storage import in
the feature-flag store to use the configured "`@/lib/storage`" alias instead of
the relative "../../lib/storage" path, while leaving the zustandStorage usage
unchanged.

Source: Coding guidelines

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

Inline comments:
In `@src/stores/feature-flags/store.ts`:
- Around line 56-59: Update the Zustand persistence configuration for
feature-flags-storage to scope persisted data by the authenticated department
identity or clear it whenever that identity changes, preventing reuse across
sessions. Add a partialize option that persists only flags and excludes isLoaded
and error, while preserving the existing storage behavior.
- Around line 63-67: The feature-flag hook currently conflates unresolved flags
with disabled flags, causing premature chat redirects. In
src/stores/feature-flags/store.ts lines 63-67, update useFeatureFlag to expose
an unresolved state while preserving an explicitly disabled result; in
src/app/(app)/chat.tsx lines 109-112, src/app/(app)/chatbot.tsx lines 67-70,
src/app/chat/[channelId].tsx lines 254-257, and
src/app/chat/thread/[messageId].tsx lines 106-109, defer redirects until the
flag resolves as disabled, while continuing to allow fetches and chat actions
only when the flag is explicitly enabled.

---

Nitpick comments:
In `@src/api/feature-flags/feature-flags.ts`:
- Line 1: Update the API client import in feature-flags.ts to use the configured
"`@/api/common/client`" alias instead of the relative path, preserving the
existing api symbol.
- Around line 28-36: Replace the direct api.get calls in getAllFeatureFlags and
getFeatureFlagState with endpoint definitions created through createApiEndpoint
or createCachedApiEndpoint, then invoke those abstractions while preserving the
existing paths, response types, parameters, and AbortSignal support.
- Around line 10-25: Move the FeatureToggleData, FeatureTogglesResult, and
FeatureToggleResult interfaces out of the feature-flags API module into the
appropriate domain-organized files under src/models/v4/. Update the
feature-flags implementation to import these models using import type,
preserving their existing shapes and usage.

In `@src/app/`(app)/chat.tsx:
- Line 1: Update the imports in src/app/(app)/chat.tsx at lines 1-1 and
src/app/chat/[channelId].tsx at lines 2-2 so Href is removed from value imports
and added via a separate import type statement after the remaining value
imports.

In `@src/stores/feature-flags/store.ts`:
- Line 7: Update the storage import in the feature-flag store to use the
configured "`@/lib/storage`" alias instead of the relative "../../lib/storage"
path, while leaving the zustandStorage usage unchanged.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: bc276eb6-58e5-4db0-a6f1-d29a0d1a9084

📥 Commits

Reviewing files that changed from the base of the PR and between 88a9d2f and 3db6b35.

📒 Files selected for processing (8)
  • src/api/feature-flags/feature-flags.ts
  • src/app/(app)/_layout.tsx
  • src/app/(app)/chat.tsx
  • src/app/(app)/chatbot.tsx
  • src/app/chat/[channelId].tsx
  • src/app/chat/thread/[messageId].tsx
  • src/components/sidebar/sidebar-content.tsx
  • src/stores/feature-flags/store.ts

Comment on lines +56 to +59
{
name: 'feature-flags-storage',
storage: createJSONStorage(() => zustandStorage),
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Scope and reduce persisted feature-flag state.

feature-flags-storage persists department-scoped flags under one device-wide key. After an account or department change, or after a failed refresh, the new session can reuse the prior department's Chat.System value.

Scope the storage by authenticated department identity, or clear it on identity changes. Add partialize so persistence retains only flags; do not persist isLoaded or error.

As per coding guidelines, src/stores/**/*.ts must use Zustand persistence with partialize to exclude transient state such as loading and error flags.

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

In `@src/stores/feature-flags/store.ts` around lines 56 - 59, Update the Zustand
persistence configuration for feature-flags-storage to scope persisted data by
the authenticated department identity or clear it whenever that identity
changes, preventing reuse across sessions. Add a partialize option that persists
only flags and excludes isLoaded and error, while preserving the existing
storage behavior.

Source: Coding guidelines

Comment thread src/stores/feature-flags/store.ts

/** Evaluates every active flag for the caller's department. */
export const getAllFeatureFlags = async (signal?: AbortSignal) => {
const response = await api.get<FeatureTogglesResult>(`${FEATURE_TOGGLES}/GetAll`, { signal });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Unhandled promise rejection: the awaited api.get call at src/api/feature-flags/feature-flags.ts:35 propagates to the caller with no structured context on network errors or non-2xx responses. Wrap the call in try { ... } catch (e) { /* log with context or rethrow mapped error */ }.

Kody rule violation: Handle async operations with proper error handling

Prompt for LLM

File src/api/feature-flags/feature-flags.ts:

Line 29:

Unhandled promise rejection: the awaited `api.get` call at `src/api/feature-flags/feature-flags.ts:35` propagates to the caller with no structured context on network errors or non-2xx responses. Wrap the call in `try { ... } catch (e) { /* log with context or rethrow mapped error */ }`.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.


/** Evaluates every active flag for the caller's department. */
export const getAllFeatureFlags = async (signal?: AbortSignal) => {
const response = await api.get<FeatureTogglesResult>(`${FEATURE_TOGGLES}/GetAll`, { signal });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Unhandled exception: the external HTTP call at src/api/feature-flags/feature-flags.ts:35 is not wrapped in try/catch, so network/file/external failures propagate unhandled with no context or application-level error mapping. Wrap the call in try { ... } catch (e) { /* add context (endpoint, operation) and map to app error */ }.

Kody rule violation: Add try-catch blocks for external calls

Prompt for LLM

File src/api/feature-flags/feature-flags.ts:

Line 29:

Unhandled exception: the external HTTP call at `src/api/feature-flags/feature-flags.ts:35` is not wrapped in try/catch, so network/file/external failures propagate unhandled with no context or application-level error mapping. Wrap the call in `try { ... } catch (e) { /* add context (endpoint, operation) and map to app error */ }`.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

}

/** Evaluates every active flag for the caller's department. */
export const getAllFeatureFlags = async (signal?: AbortSignal) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Incomplete JSDoc: getAllFeatureFlags (line 27) omits @returns {Promise<FeatureTogglesResult>} and rejection conditions, leaving callers without knowledge of the resolved type, rejection scenarios, or that it must be used with await. Add @returns {Promise<FeatureTogglesResult>} and document rejection scenarios (e.g., network failure, auth error).

Kody rule violation: Document async/Promise behavior and errors

Prompt for LLM

File src/api/feature-flags/feature-flags.ts:

Line 28:

Incomplete JSDoc: `getAllFeatureFlags` (line 27) omits `@returns {Promise<FeatureTogglesResult>}` and rejection conditions, leaving callers without knowledge of the resolved type, rejection scenarios, or that it must be used with `await`. Add `@returns {Promise<FeatureTogglesResult>}` and document rejection scenarios (e.g., network failure, auth error).

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

const isChatEnabled = useIsChatEnabled();

// Chat.System feature flag off: hide the chat and assistant entries entirely.
const menuItems = MENU_ITEMS.filter((item) => (item.key === 'chat' || item.key === 'chatbot' ? isChatEnabled : true));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Magic strings: the literals 'chat' and 'chatbot' are used as inline item-key comparisons but are shared domain identifiers also referenced in MENU_ITEMS definitions and route configs. Define constants (e.g., MENU_KEY.CHAT = 'chat', MENU_KEY.CHATBOT = 'chatbot') or a const tuple and reference them in both the filter and the MENU_ITEMS array.

Kody rule violation: Centralize string constants

Prompt for LLM

File src/components/sidebar/sidebar-content.tsx:

Line 42:

Magic strings: the literals `'chat'` and `'chatbot'` are used as inline item-key comparisons but are shared domain identifiers also referenced in `MENU_ITEMS` definitions and route configs. Define constants (e.g., `MENU_KEY.CHAT = 'chat'`, `MENU_KEY.CHATBOT = 'chatbot'`) or a const tuple and reference them in both the filter and the `MENU_ITEMS` array.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

const isChatEnabled = useIsChatEnabled();

// Chat.System feature flag off: hide the chat and assistant entries entirely.
const menuItems = MENU_ITEMS.filter((item) => (item.key === 'chat' || item.key === 'chatbot' ? isChatEnabled : true));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Magic strings: the literals 'chat' and 'chatbot' represent a finite set of menu item keys but lack compile-time safety, making refactoring error-prone. Declare an enum or as-const tuple (e.g., const MENU_KEYS = ['chat','chatbot',...] as const) and compare against MENU_KEYS.Chat / MENU_KEYS.Chatbot instead of raw strings.

Kody rule violation: Use enums instead of magic strings

Prompt for LLM

File src/components/sidebar/sidebar-content.tsx:

Line 42:

Magic strings: the literals `'chat'` and `'chatbot'` represent a finite set of menu item keys but lack compile-time safety, making refactoring error-prone. Declare an enum or as-const tuple (e.g., `const MENU_KEYS = ['chat','chatbot',...] as const`) and compare against `MENU_KEYS.Chat` / `MENU_KEYS.Chatbot` instead of raw strings.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

isEnabled: (key: string, defaultValue?: boolean) => boolean;
}

export const featureFlagsStore = create<FeatureFlagsState>()(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Bug high

Security vulnerability: resetAllStores() in app-reset.service.ts omits featureFlagsStore, so the in-memory zustand state retains the previous department's flags after logout even though the MMKV key is deleted. Since fetchFlags() preserves existing flags on network failure (store.ts:46), an offline re-login as a different department leaves the previous department's Chat.System flag active — register the store in resetAllStores() by adding featureFlagsStore.setState({ flags: {}, isLoaded: false, error: null });.

// Add to resetAllStores() in src/services/app-reset.service.ts:
// import { featureFlagsStore } from '@/stores/feature-flags/store';
// ...
// featureFlagsStore.setState({ flags: {}, isLoaded: false, error: null });
Prompt for LLM

File src/stores/feature-flags/store.ts:

Line 29:

Security vulnerability: `resetAllStores()` in `app-reset.service.ts` omits `featureFlagsStore`, so the in-memory zustand state retains the previous department's flags after logout even though the MMKV key is deleted. Since `fetchFlags()` preserves existing flags on network failure (`store.ts:46`), an offline re-login as a different department leaves the previous department's `Chat.System` flag active — register the store in `resetAllStores()` by adding `featureFlagsStore.setState({ flags: {}, isLoaded: false, error: null });`.

Suggested Code:

// Add to resetAllStores() in src/services/app-reset.service.ts:
// import { featureFlagsStore } from '@/stores/feature-flags/store';
// ...
// featureFlagsStore.setState({ flags: {}, isLoaded: false, error: null });

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

@Resgrid-Bot

Resgrid-Bot commented Aug 8, 2026

Copy link
Copy Markdown

Code Review Completed! 🔥

The code review was successfully completed based on your current configurations.

Kody Guide: Usage and Configuration
Interacting with Kody
  • Request a Review: Ask Kody to review your PR manually by adding a comment with the @kody start-review command at the root of your PR.

  • Validate Business Logic: Ask Kody to validate your code against business rules by adding a comment with the @kody -v business-logic command.

  • Provide Feedback: Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

Current Kody Configuration
Review Options

The following review options are enabled or disabled:

Options Enabled
Bug
Performance
Security
Business Logic

Access your configuration settings here.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (1)
src/stores/feature-flags/store.ts (1)

66-91: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Invalidate active feature-flag requests during reset.

fetchFlags() can complete after resetAllStores() clears the store. Its success or failure path can then restore the prior session state and temporarily enable Chat.System for the wrong session.

  • src/stores/feature-flags/store.ts#L66-L91: associate each fetch with a request generation and apply success or failure state only when that generation remains current. Add a deferred-promise test for this case.
  • src/services/app-reset.service.ts#L205-L205: invalidate the active feature-flag request generation before resetting the store state.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/stores/feature-flags/store.ts` around lines 66 - 91, Associate each fetch
started by fetchFlags in src/stores/feature-flags/store.ts:66-91 with a request
generation, and apply its success or failure state only if that generation is
still current; add a deferred-promise test covering reset during an active
fetch. In src/services/app-reset.service.ts:205-205, invalidate the active
feature-flag request generation before resetting store state.
🧹 Nitpick comments (3)
src/stores/feature-flags/__tests__/store.test.ts (3)

43-45: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Replace untyped require calls.

require() returns any. Use typed Jest mocks or typed alias imports for getAllFeatureFlags, useAuthStore, and securityStore.

As per coding guidelines, **/*.{ts,tsx} must use strict TypeScript and never use any.

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

In `@src/stores/feature-flags/__tests__/store.test.ts` around lines 43 - 45,
Replace the untyped require calls for getAllFeatureFlags, useAuthStore, and
securityStore with typed ES module imports or properly typed Jest mock imports.
Preserve each symbol’s existing default or named import shape, and ensure no
any-typed require usage remains in the test.

Source: Coding guidelines


1-3: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Declare the storage mock before importing the store.

Line 3 imports ../store before Line 21 mocks its storage dependency. Move native storage mocks before store imports so tests cannot initialize the real storage implementation.

As per coding guidelines, src/**/__tests__/**/*.{ts,tsx} must mock native modules before imports.

Also applies to: 21-27

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

In `@src/stores/feature-flags/__tests__/store.test.ts` around lines 1 - 3, Move
the native storage mock setup ahead of the `../store` import in `store.test.ts`,
ensuring the mock is declared before `FeatureFlagKeys`, `featureFlagsStore`, and
`useChatSystemStatus` are imported. Preserve the existing mock behavior while
preventing the store from initializing the real storage implementation.

Source: Coding guidelines


175-212: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Unmount each rendered hook.

Each renderHook() call creates a mounted subscription to the Zustand store. Call unmount() during test cleanup.

As per coding guidelines, src/**/__tests__/**/*.{ts,tsx} must always call unmount() during cleanup.

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

In `@src/stores/feature-flags/__tests__/store.test.ts` around lines 175 - 212,
Update each useChatSystemStatus test’s renderHook usage to retain the returned
unmount function and invoke it during cleanup before the test completes,
including both hooks created in the enabled/disabled test. Ensure every mounted
hook subscription is explicitly unmounted while preserving the existing
assertions.

Source: Coding guidelines

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

Inline comments:
In `@src/stores/feature-flags/store.ts`:
- Around line 8-9: Replace all listed internal relative imports with configured
aliases: in src/stores/feature-flags/store.ts lines 8-9, alias the auth and
security store imports; in src/stores/feature-flags/__tests__/store.test.ts
lines 3, 21, 30, and 37, alias the feature-flags store, storage, auth-store, and
security-store imports; and in src/stores/signalr/signalr-store.ts line 11,
alias the feature-flags store import. Use the existing `@/stores/`... and
`@/lib/storage` paths without changing behavior.

In `@src/stores/signalr/signalr-store.ts`:
- Around line 424-429: Add a test in signalr-store.test.ts for connectChatHub()
with featureFlagsStore.isEnabled(FeatureFlagKeys.ChatSystem) mocked to false;
assert signalRService.connectToHubWithEventingUrl is not called and
isChatHubConnected remains false.

---

Outside diff comments:
In `@src/stores/feature-flags/store.ts`:
- Around line 66-91: Associate each fetch started by fetchFlags in
src/stores/feature-flags/store.ts:66-91 with a request generation, and apply its
success or failure state only if that generation is still current; add a
deferred-promise test covering reset during an active fetch. In
src/services/app-reset.service.ts:205-205, invalidate the active feature-flag
request generation before resetting store state.

---

Nitpick comments:
In `@src/stores/feature-flags/__tests__/store.test.ts`:
- Around line 43-45: Replace the untyped require calls for getAllFeatureFlags,
useAuthStore, and securityStore with typed ES module imports or properly typed
Jest mock imports. Preserve each symbol’s existing default or named import
shape, and ensure no any-typed require usage remains in the test.
- Around line 1-3: Move the native storage mock setup ahead of the `../store`
import in `store.test.ts`, ensuring the mock is declared before
`FeatureFlagKeys`, `featureFlagsStore`, and `useChatSystemStatus` are imported.
Preserve the existing mock behavior while preventing the store from initializing
the real storage implementation.
- Around line 175-212: Update each useChatSystemStatus test’s renderHook usage
to retain the returned unmount function and invoke it during cleanup before the
test completes, including both hooks created in the enabled/disabled test.
Ensure every mounted hook subscription is explicitly unmounted while preserving
the existing assertions.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3c89b632-dc87-4cd7-ac74-f043d934e994

📥 Commits

Reviewing files that changed from the base of the PR and between 3db6b35 and 6ee2dd1.

📒 Files selected for processing (9)
  • src/app/(app)/chat.tsx
  • src/app/(app)/chatbot.tsx
  • src/app/chat/[channelId].tsx
  • src/app/chat/thread/[messageId].tsx
  • src/services/__tests__/app-reset.service.test.ts
  • src/services/app-reset.service.ts
  • src/stores/feature-flags/__tests__/store.test.ts
  • src/stores/feature-flags/store.ts
  • src/stores/signalr/signalr-store.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/app/(app)/chat.tsx
  • src/app/(app)/chatbot.tsx

Comment on lines +8 to +9
import useAuthStore from '../auth/store';
import { securityStore } from '../security/store';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Use configured aliases for internal imports.

These new internal imports bypass the configured aliases.

  • src/stores/feature-flags/store.ts#L8-L9: replace the auth and security relative imports with @/stores/... aliases.
  • src/stores/feature-flags/__tests__/store.test.ts#L3-L3: replace ../store with the feature-flags store alias.
  • src/stores/feature-flags/__tests__/store.test.ts#L21-L21: replace the storage mock path with @/lib/storage.
  • src/stores/feature-flags/__tests__/store.test.ts#L30-L30: replace the auth-store mock path with its alias.
  • src/stores/feature-flags/__tests__/store.test.ts#L37-L37: replace the security-store mock path with its alias.
  • src/stores/signalr/signalr-store.ts#L11-L11: replace the feature-flags store relative import with its alias.

As per coding guidelines, src/**/*.{ts,tsx} must use configured path aliases (@/*, @env, @assets/*) instead of relative imports.

📍 Affects 3 files
  • src/stores/feature-flags/store.ts#L8-L9 (this comment)
  • src/stores/feature-flags/__tests__/store.test.ts#L3-L3
  • src/stores/feature-flags/__tests__/store.test.ts#L21-L21
  • src/stores/feature-flags/__tests__/store.test.ts#L30-L30
  • src/stores/feature-flags/__tests__/store.test.ts#L37-L37
  • src/stores/signalr/signalr-store.ts#L11-L11
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/stores/feature-flags/store.ts` around lines 8 - 9, Replace all listed
internal relative imports with configured aliases: in
src/stores/feature-flags/store.ts lines 8-9, alias the auth and security store
imports; in src/stores/feature-flags/__tests__/store.test.ts lines 3, 21, 30,
and 37, alias the feature-flags store, storage, auth-store, and security-store
imports; and in src/stores/signalr/signalr-store.ts line 11, alias the
feature-flags store import. Use the existing `@/stores/`... and `@/lib/storage`
paths without changing behavior.

Source: Coding guidelines

Comment on lines +424 to +429
// Guard here so every call path (init, app-resume reconnect) honors the flag.
if (!featureFlagsStore.getState().isEnabled(FeatureFlagKeys.ChatSystem)) {
logger.info({ message: 'Chat disabled by feature flag; skipping chat hub connection' });
return;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 6 'connectChatHub|ChatSystem|connectToHubWithEventingUrl|isChatHubConnected' \
  src/stores/signalr/__tests__/signalr-store.test.ts

Repository: Resgrid/IC

Length of output: 3112


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Locate signalr store and tests"
git ls-files | rg 'src/stores/signalr/(signalr-store|.*test).*\.ts$'

echo
echo "Relevant test file around feature flag/chat-related tests"
wc -l src/stores/signalr/__tests__/signalr-store.test.ts
rg -n -C 8 'chat|Chat|connectChatHub|connectUpdateHub|connectGeolocationHub|isEnabled|ChatSystem' src/stores/signalr/__tests__/signalr-store.test.ts

echo
echo "signalr-store relevant implementation around connectChatHub"
rg -n -C 20 'connectChatHub|FeatureFlagKeys\.ChatSystem|isChatHubConnected|connectToHubWithEventingUrl' src/stores/signalr/signalr-store.ts

Repository: Resgrid/IC

Length of output: 16943


Add a test for disabled chat-hub connection.

signalr-store.test.ts covers the update and geolocation hubs, but it does not cover connectChatHub() when Chat.System is disabled. Add a test that mocks featureFlagsStore.isEnabled(FeatureFlagKeys.ChatSystem) to false, calls connectChatHub(), and asserts signalRService.connectToHubWithEventingUrl is not called while isChatHubConnected remains false.

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

In `@src/stores/signalr/signalr-store.ts` around lines 424 - 429, Add a test in
signalr-store.test.ts for connectChatHub() with
featureFlagsStore.isEnabled(FeatureFlagKeys.ChatSystem) mocked to false; assert
signalRService.connectToHubWithEventingUrl is not called and isChatHubConnected
remains false.

Source: Coding guidelines

@ucswift
ucswift merged commit f489fdd into master Aug 8, 2026
9 checks passed
Comment thread src/app/(app)/chat.tsx
const [fabOpen, setFabOpen] = useState(false);
const [newMode, setNewMode] = useState<'dm' | 'group' | null>(null);
const chatStatus = useChatSystemStatus();
const isChatEnabled = chatStatus === 'enabled';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Hardcoded string literal 'enabled' represents a finite set of chat system statuses and risks typos. Export named constants like CHAT_STATUS from the feature-flags store and compare against CHAT_STATUS.ENABLED.

Kody rule violation: Use enums instead of magic strings

Prompt for LLM

File src/app/(app)/chat.tsx:

Line 92:

Hardcoded string literal 'enabled' represents a finite set of chat system statuses and risks typos. Export named constants like `CHAT_STATUS` from the feature-flags store and compare against `CHAT_STATUS.ENABLED`.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

const channelMessages = useChatStore((s) => (channelId ? s.messagesByChannel[channelId] : undefined));
const [fetchedReplies, setFetchedReplies] = useState<ChatMessageResultData[]>([]);
const chatStatus = useChatSystemStatus();
const isChatEnabled = chatStatus === 'enabled';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Hardcoded string literal 'enabled' checks chat system status inline, creating synchronization risks across components. Define a constant like CHAT_STATUS.ENABLED or derive it from the feature-flag store's return type.

Kody rule violation: Centralize string constants

Prompt for LLM

File src/app/chat/thread/[messageId].tsx:

Line 32:

Hardcoded string literal 'enabled' checks chat system status inline, creating synchronization risks across components. Define a constant like `CHAT_STATUS.ENABLED` or derive it from the feature-flag store's return type.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

if (!persistedKey) {
return false;
}
const userId = useAuthStore.getState().userId;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Duplicated store-access logic fetches userId identically in getCurrentIdentityKey and isPersistedIdentityStale. Extract a shared helper like getCurrentIdentity() to centralize identity retrieval.

Kody rule violation: Extract duplicated logic into functions

Prompt for LLM

File src/stores/feature-flags/store.ts:

Line 39:

Duplicated store-access logic fetches `userId` identically in `getCurrentIdentityKey` and `isPersistedIdentityStale`. Extract a shared helper like `getCurrentIdentity()` to centralize identity retrieval.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants