Skip to content

Move memory settings into the settings page - #1438

Merged
rohan-pandeyy merged 9 commits into
AOSSIE-Org:mainfrom
rohan-pandeyy:update/memories-setting-page
Aug 2, 2026
Merged

Move memory settings into the settings page#1438
rohan-pandeyy merged 9 commits into
AOSSIE-Org:mainfrom
rohan-pandeyy:update/memories-setting-page

Conversation

@rohan-pandeyy

@rohan-pandeyy rohan-pandeyy commented Jul 30, 2026

Copy link
Copy Markdown
Member

Follow-up to #1429.

Memory preferences had their own page at memories/settings, reachable only from a gear icon on the Memories grid. Every other preference in the app lives in /settings, so this moves them there and deletes the separate page.

What changed

A collapsible Memories group now sits in User Preferences, beside Video Tagging and behaving the same way:

Control Type Default
Generate Memories toggle on
Desktop Notifications toggle off, disabled while Generate is off
Seconds Per Photo dropdown, 3 / 5 / 7 / 10 s 5
Minimum Photos dropdown, 3 / 5 / 8 / 10 5
Maximum Photos dropdown, 20 / 30 / 50 / 100 30

The three sliders became dropdowns to match Keyframe Interval in the video group. Discrete options also keep the min and max pair valid by construction, though the cross-clamp is kept because a value saved by the old sliders can sit outside these sets.

MemorySettings.tsx, the memories/settings route and ROUTES.MEMORIES_SETTINGS are gone. The gear button on the Memories grid went with them, since the sidebar Settings button is on screen at the same time and does the same thing. The story viewer keeps its gear, now opening /settings, because the sidebar is covered while a memory is playing.

Notable decisions

Background music was dropped from the UI. frontend/public/memory-theme.mp3 is not bundled yet, so the toggle controlled nothing. story_music_enabled stays in the schema and the story viewer, it just has no control until the theme ships.

Desktop notifications now default to off. Flipped in MemoriesPreferences, in the MemoryStatusData echo and in the frontend defaults. Alerts are opt-in; the memory is waiting on the page either way. openapi.json is regenerated to match, and the one assertion covering the default was updated.

Preference writes were not concurrency safe

Reviewing the new dropdowns turned up a problem that predates them and affects every control in the panel, so it is fixed here.

useUserPreferences applied each change optimistically, snapshotted the whole preferences object, and restored that snapshot on failure. Two overlapping saves meant the second snapshot was taken before the first had applied, so a failure rolled back over an unrelated successful change. The YOLO, GPU and video writes also sent the entire object, so the server merge could clobber a concurrent edit even when nothing failed.

  • Writes queue. One request at a time.
  • State is read when the write is sent, not when it was requested. A queued write builds from what actually landed before it, and rolls back to that. Two GPU toggles in one render now send true then false rather than true twice.
  • Each request carries only the keys it changed. The route already does a deep partial merge with exclude_unset, so this is what it expects.

Behind that sat a read/write race. The mutation refetched on success, outside the queue, and the load effect applied whatever came back, so a response describing the server from before a newer write could land on top of it and hand the next queued write a stale base. Three parts to the fix:

  • The PUT already returns the merged result, so the write path adopts that instead of refetching. Reconciliation happens inside the queue, where nothing else is in flight, and it drops a redundant GET per save.
  • The load effect stands down while writes are pending.
  • Reads capture a write counter when they start and are dropped if a write has been queued since. Arrival order is not enough on its own: a read issued before a write can still resolve after it settles, once the pending guard is back to zero.

updatePreference, the unused whole-object entry point, is removed.

Tests

16 new, 303 total.

Hook tests cover per-key request bodies, a queued write building on the previous one, a failed write rolling back only its own change, the queue surviving a rejection, the server response being adopted, and both stale-read cases. Every one was run against the code before its fix, and each fails there.

Component tests cover both directions of the min and max cross-clamp, a stored value outside the dropdown options, and every control disabled while a save runs.

Verification

  • GITHUB_ACTIONS=true python -m pytest tests/ -q — 969 passed
  • npx jest --maxWorkers=2 — 303 passed
  • npx tsc --noEmit — clean
  • ruff and black clean

Summary by CodeRabbit

  • New Features

    • Added a collapsible Memories section to the main Settings page with controls for memory generation, desktop notifications, story pacing, and image limits.
    • Updated the Memories page action label from Refresh to Regenerate.
  • Bug Fixes

    • Desktop notifications are now off by default and require opt-in.
    • Notification controls remain disabled until memory generation is enabled.
    • Preference updates are now queued and rolled back safely if a save fails.

Collapsible group beside Video Tagging, holding the generate and desktop
notification toggles plus dropdowns for seconds per photo and the photo
count bounds. The min and max pair still cross-clamps, since a value saved
by the old sliders can fall outside these options.
Its controls now live in the settings page, so the page, the
memories/settings route and ROUTES.MEMORIES_SETTINGS all go. The gear
buttons on the grid and in the story viewer point at /settings instead.
Alerts are opt-in now; the memory is waiting on the page either way.
Flipped in MemoriesPreferences, the MemoryStatusData echo and the frontend
defaults, with openapi.json regenerated to match.
@github-actions github-actions Bot added GSoC 2026 enhancement New feature or request labels Jul 30, 2026
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Memory notifications now default to opt-in. Memory controls moved into the main settings page. The dedicated memory settings route was removed. The preference hook now serializes writes with optimistic updates and rollback.

Changes

Memory settings consolidation

Layer / File(s) Summary
Opt-in notification defaults
backend/app/schemas/memories.py, backend/app/schemas/user_preferences.py, backend/tests/test_user_preferences.py, docs/backend/backend_python/openapi.json, frontend/src/hooks/useUserPreferences.tsx
Memory notification defaults now resolve to false across backend schemas, frontend defaults, OpenAPI documentation, and tests.
Preference update serialization and queuing
frontend/src/hooks/useUserPreferences.tsx, frontend/src/hooks/__tests__/useUserPreferences.test.tsx
The hook queues writes, sends partial updates, applies optimistic state, adopts server responses, rolls back failed writes, and continues after rejection.
Memories settings panel
frontend/src/pages/SettingsPage/components/UserPreferencesCard.tsx, frontend/src/pages/SettingsPage/components/__tests__/UserPreferencesCard.test.tsx
The settings page adds Memories controls for generation, notifications, pacing, and image-count bounds. The controls clamp invalid bounds and disable during saves.
Settings route consolidation
frontend/src/routes/AppRoutes.tsx, frontend/src/constants/routes.ts, frontend/src/components/Memories/MemoryStoryViewer.tsx, frontend/src/pages/Memories/Memories.tsx
The dedicated route wiring was removed. Memory settings navigation now targets the main settings route. The action label changed from “Refresh” to “Regenerate.”

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

Sequence Diagram(s)

sequenceDiagram
  actor User
  participant UserPreferencesCard
  participant useUserPreferences
  participant PreferencesAPI
  User->>UserPreferencesCard: Change a Memories setting
  UserPreferencesCard->>useUserPreferences: Submit partial update
  useUserPreferences->>useUserPreferences: Queue and apply optimistic update
  useUserPreferences->>PreferencesAPI: Send changed fields
  alt API succeeds
    PreferencesAPI-->>useUserPreferences: Return updated preferences
    useUserPreferences->>useUserPreferences: Process next queued write
  else API fails
    PreferencesAPI-->>useUserPreferences: Return error
    useUserPreferences->>useUserPreferences: Roll back failed update
  end
Loading

Possibly related PRs

  • AOSSIE-Org/PictoPy#1440: Shares the memory preference schemas, notification defaults, routes, settings UI, and useUserPreferences changes.

Suggested labels: Python, TypeScript/JavaScript, Documentation

Poem

A rabbit set alerts to opt in,
Then queued each preference spin.
Settings moved to one place,
With bounds kept in valid space.
“Regenerate” now begins. 🐰

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: moving memory settings into the main settings page.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@frontend/src/pages/SettingsPage/components/UserPreferencesCard.tsx`:
- Around line 484-508: Disable the dropdown trigger Buttons for the memories
duration and related preference dropdowns by adding disabled={isUpdating},
matching the existing switch behavior. Update the buttons around the memories
duration control and the corresponding controls near the second and third
dropdown sections so they cannot start overlapping patchMemories mutations while
an update is active.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a058ddde-e3e8-423d-9ab4-ba673f5e960f

📥 Commits

Reviewing files that changed from the base of the PR and between c7323dc and b7d0b7e.

📒 Files selected for processing (11)
  • backend/app/schemas/memories.py
  • backend/app/schemas/user_preferences.py
  • backend/tests/test_user_preferences.py
  • docs/backend/backend_python/openapi.json
  • frontend/src/components/Memories/MemoryStoryViewer.tsx
  • frontend/src/constants/routes.ts
  • frontend/src/hooks/useUserPreferences.tsx
  • frontend/src/pages/Memories/Memories.tsx
  • frontend/src/pages/Memories/MemorySettings.tsx
  • frontend/src/pages/SettingsPage/components/UserPreferencesCard.tsx
  • frontend/src/routes/AppRoutes.tsx
💤 Files with no reviewable changes (3)
  • frontend/src/constants/routes.ts
  • frontend/src/pages/Memories/MemorySettings.tsx
  • frontend/src/routes/AppRoutes.tsx

Comment thread frontend/src/pages/SettingsPage/components/UserPreferencesCard.tsx
The switches already did this, the three dropdown triggers did not. Rapid
selections could overlap, and each mutation rolls back to its own snapshot,
so an out-of-order failure could leave the min/max pair inconsistent.

@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.

Caution

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

⚠️ Outside diff range comments (1)
frontend/src/pages/SettingsPage/components/UserPreferencesCard.tsx (1)

73-78: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Serialize all preference mutations or disable every dependent control.

isUpdating disables only the Memories controls, while YOLO, GPU, and video controls can still start mutations through the same updatePreferencesMutation. Because both update paths snapshot and restore the entire preferences object, a concurrent non-Memory update can be lost from local state when the Memory request fails or completes out of order. Serialize these writes in useUserPreferences, or disable all controls sharing this mutation until it settles.

🤖 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 `@frontend/src/pages/SettingsPage/components/UserPreferencesCard.tsx` around
lines 73 - 78, Serialize all preference mutations in useUserPreferences,
including the patchMemories path and the YOLO, GPU, and video update paths, so
only one updatePreferencesMutation can run at a time. Ensure dependent controls
cannot trigger concurrent writes, preserving the existing snapshot/restore
behavior without allowing out-of-order updates to overwrite local preferences.
🧹 Nitpick comments (1)
frontend/src/pages/SettingsPage/components/UserPreferencesCard.tsx (1)

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

Add automated coverage for the Memories panel.

Please test min/max cross-clamping, legacy values outside the dropdown options, and that all controls remain disabled while a save is pending. These are the core behaviors introduced by this panel.

🤖 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 `@frontend/src/pages/SettingsPage/components/UserPreferencesCard.tsx` around
lines 381 - 611, Add automated tests for the Memories controls in
UserPreferencesCard, covering min/max cross-clamping in both update directions,
rendering and preserving legacy values not present in the dropdown options, and
disabling every Memories control while a save is pending. Reuse the existing
component test patterns and exercise the relevant patchMemories interactions.

Source: Path instructions

🤖 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.

Outside diff comments:
In `@frontend/src/pages/SettingsPage/components/UserPreferencesCard.tsx`:
- Around line 73-78: Serialize all preference mutations in useUserPreferences,
including the patchMemories path and the YOLO, GPU, and video update paths, so
only one updatePreferencesMutation can run at a time. Ensure dependent controls
cannot trigger concurrent writes, preserving the existing snapshot/restore
behavior without allowing out-of-order updates to overwrite local preferences.

---

Nitpick comments:
In `@frontend/src/pages/SettingsPage/components/UserPreferencesCard.tsx`:
- Around line 381-611: Add automated tests for the Memories controls in
UserPreferencesCard, covering min/max cross-clamping in both update directions,
rendering and preserving legacy values not present in the dropdown options, and
disabling every Memories control while a save is pending. Reuse the existing
component test patterns and exercise the relevant patchMemories interactions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: dc60ba56-192d-4fa1-9ccb-079370180dd4

📥 Commits

Reviewing files that changed from the base of the PR and between b7d0b7e and 02dc31c.

📒 Files selected for processing (1)
  • frontend/src/pages/SettingsPage/components/UserPreferencesCard.tsx

Every write snapshotted and restored the whole preferences object, so two
overlapping saves would have the later one roll back over the earlier one.
Writes now queue, each builds from the state that actually landed before it,
and each sends only the keys it changed so the server merge cannot clobber a
concurrent edit either. Drops the unused updatePreference, which was the
whole-object path.
Both directions of the min and max cross-clamp, a stored value that is not
one of the dropdown options, and every control disabled while a save runs.

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

🧹 Nitpick comments (2)
frontend/src/pages/SettingsPage/components/__tests__/UserPreferencesCard.test.tsx (2)

85-87: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Assert one update call for each interaction.

toHaveBeenCalledWith accepts any matching call. It does not fail when the handler emits an additional stale or duplicate patch. Add toHaveBeenCalledTimes(1) before each payload assertion, or assert the complete mock.calls array.

This protects the queued-save contract and verifies that each user action sends only the intended patch.

Suggested assertion
+    expect(mockUpdateMemoriesPreferences).toHaveBeenCalledTimes(1);
     expect(mockUpdateMemoriesPreferences).toHaveBeenCalledWith({

As per path instructions, ensure test code is automated and comprehensive.

Also applies to: 112-115, 126-129, 139-142, 167-169

🤖 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
`@frontend/src/pages/SettingsPage/components/__tests__/UserPreferencesCard.test.tsx`
around lines 85 - 87, Strengthen the interaction assertions in
UserPreferencesCard tests by verifying mockUpdateMemoriesPreferences is called
exactly once before each existing payload assertion at all referenced cases.
Preserve the current expected patch payloads while ensuring each user action
produces only one update call.

Source: Path instructions


90-100: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add coverage for enabling desktop notifications.

This test only checks the disabled state. It does not verify that the control is enabled when memory generation is active or that clicking it sends { notifications_enabled: true }. A permanently disabled or miswired switch could pass.

Add a test for the default fixture that checks the enabled state, the default unchecked state, and the update payload.

Suggested test
+  it('enables desktop notifications', async () => {
+    const user = userEvent.setup();
+    render(<UserPreferencesCard />);
+    await openPanel(user);
+
+    const notifications = screen.getByRole('switch', {
+      name: /Desktop Notifications/i,
+    });
+    expect(notifications).not.toBeDisabled();
+    expect(notifications).not.toBeChecked();
+
+    await user.click(notifications);
+
+    expect(mockUpdateMemoriesPreferences).toHaveBeenCalledWith({
+      notifications_enabled: true,
+    });
+  });

As per path instructions, ensure test code is automated, comprehensive, and covers critical functionality.

🤖 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
`@frontend/src/pages/SettingsPage/components/__tests__/UserPreferencesCard.test.tsx`
around lines 90 - 100, Add coverage alongside the existing `locks notifications
behind the generate toggle` test for the default memory fixture: render and open
`UserPreferencesCard`, verify the Desktop Notifications switch is enabled and
initially unchecked, click it, and assert the update handler receives `{
notifications_enabled: true }`. Ensure the test exercises the real user
interaction and preserves the existing disabled-state coverage.

Source: Path instructions

🤖 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 `@frontend/src/hooks/__tests__/useUserPreferences.test.tsx`:
- Around line 75-92: Update updatePreferencesMutation’s onSuccess/refetch
coordination in useUserPreferences so a preferences fetch resolving while writes
remain queued cannot overwrite an optimistically applied or pending write. Add a
regression test in the useUserPreferences test suite that resolves
getUserPreferences mid-queue and verifies the concurrent write remains applied,
while preserving existing optimistic apply and rollback assertions.

In `@frontend/src/hooks/useUserPreferences.tsx`:
- Around line 63-65: The write-success refetch in updatePreferencesMutation can
overwrite newer queued optimistic updates through the preferencesQuery data
effect. Update updatePreferencesMutation and the surrounding writeQueue/refetch
flow so stale refetch responses cannot reach applyPreferences while newer writes
are pending, applying reconciliation only after the queue drains or using the
mutation response; preserve queued writes’ use of preferencesRef.current as
their base state.

In `@frontend/src/pages/Memories/Memories.tsx`:
- Around line 2-6: Restore the Memories page settings entry by retaining the
Settings import, navigation hook, and page-level settings button in the Memories
component. Configure the button to navigate to ROUTES.SETTINGS, or preserve an
equivalent always-visible control that provides the same access.

---

Nitpick comments:
In
`@frontend/src/pages/SettingsPage/components/__tests__/UserPreferencesCard.test.tsx`:
- Around line 85-87: Strengthen the interaction assertions in
UserPreferencesCard tests by verifying mockUpdateMemoriesPreferences is called
exactly once before each existing payload assertion at all referenced cases.
Preserve the current expected patch payloads while ensuring each user action
produces only one update call.
- Around line 90-100: Add coverage alongside the existing `locks notifications
behind the generate toggle` test for the default memory fixture: render and open
`UserPreferencesCard`, verify the Desktop Notifications switch is enabled and
initially unchecked, click it, and assert the update handler receives `{
notifications_enabled: true }`. Ensure the test exercises the real user
interaction and preserves the existing disabled-state coverage.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: c8fcfcd1-c2c8-42de-8244-f46712949bf8

📥 Commits

Reviewing files that changed from the base of the PR and between 02dc31c and 3e4eb2e.

📒 Files selected for processing (4)
  • frontend/src/hooks/__tests__/useUserPreferences.test.tsx
  • frontend/src/hooks/useUserPreferences.tsx
  • frontend/src/pages/Memories/Memories.tsx
  • frontend/src/pages/SettingsPage/components/__tests__/UserPreferencesCard.test.tsx

Comment thread frontend/src/hooks/__tests__/useUserPreferences.test.tsx
Comment thread frontend/src/hooks/useUserPreferences.tsx
Comment thread frontend/src/pages/Memories/Memories.tsx
The mutation refetched on success, outside the queue, so a response that
predated a newer write could land on top of it and hand the next queued
write a stale base. The PUT already returns the merged result, so the write
path adopts that instead, and the load effect stands down while writes are
pending. Tests drop the never-resolving read they used to need.

@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.

Caution

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

⚠️ Outside diff range comments (1)
frontend/src/hooks/useUserPreferences.tsx (1)

57-73: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Guard stale query data after a write settles.

pendingWrites.current stops loads only while a write is queued. If an earlier GET/refetch() from mount resolves after the PUT completes, pendingWrites.current is already 0, so the effect can apply the stale preferences and revert the fast write. Track lastCommittedAt on successful apply/rollback and reject loads received after that timestamp. usePictoQuery spreads the underlying UseQueryResult, so dataUpdatedAt is available for this comparison.

🤖 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 `@frontend/src/hooks/useUserPreferences.tsx` around lines 57 - 73, The
preferences synchronization effect around pendingWrites and applyPreferences
must reject query results older than the most recent successful write or
rollback. Track a lastCommittedAt timestamp when a write is successfully applied
or rolled back, then only apply preferencesQuery.data when its dataUpdatedAt is
newer than that timestamp, while retaining the pendingWrites guard and existing
valid-data checks.
🧹 Nitpick comments (1)
frontend/src/hooks/__tests__/useUserPreferences.test.tsx (1)

76-87: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Replace any with explicit types in deepMerge.

base, updates, and the return type are typed any. Since deepMerge mirrors the server's merge over UserPreferencesData and UpdateUserPreferencesRequest, a generic constrained to a plain-object shape gives type safety without hardcoding the domain type.

♻️ Proposed typing for `deepMerge`
-const deepMerge = (base: any, updates: any): any => {
+const deepMerge = <T extends Record<string, unknown>>(
+  base: T,
+  updates: Partial<T>,
+): T => {
   const merged = { ...base };
   for (const [key, value] of Object.entries(updates ?? {})) {
     const isPlainObject = (v: unknown) =>
       typeof v === 'object' && v !== null && !Array.isArray(v);
-    merged[key] =
+    (merged as Record<string, unknown>)[key] =
       isPlainObject(value) && isPlainObject(merged[key])
-        ? deepMerge(merged[key], value)
+        ? deepMerge(merged[key] as Record<string, unknown>, value as Record<string, unknown>)
         : value;
   }
   return merged;
 };

As per path instructions, "Avoid 'any', use explicit types."

🤖 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 `@frontend/src/hooks/__tests__/useUserPreferences.test.tsx` around lines 76 -
87, Update deepMerge to remove all any types and use explicit generic types
constrained to a plain-object shape, preserving recursive merging for nested
plain objects and allowing updates to override values. Type the return value
consistently with the generic merge result without hardcoding
UserPreferencesData or UpdateUserPreferencesRequest.

Source: Path instructions

🤖 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.

Outside diff comments:
In `@frontend/src/hooks/useUserPreferences.tsx`:
- Around line 57-73: The preferences synchronization effect around pendingWrites
and applyPreferences must reject query results older than the most recent
successful write or rollback. Track a lastCommittedAt timestamp when a write is
successfully applied or rolled back, then only apply preferencesQuery.data when
its dataUpdatedAt is newer than that timestamp, while retaining the
pendingWrites guard and existing valid-data checks.

---

Nitpick comments:
In `@frontend/src/hooks/__tests__/useUserPreferences.test.tsx`:
- Around line 76-87: Update deepMerge to remove all any types and use explicit
generic types constrained to a plain-object shape, preserving recursive merging
for nested plain objects and allowing updates to override values. Type the
return value consistently with the generic merge result without hardcoding
UserPreferencesData or UpdateUserPreferencesRequest.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ba14b9b5-0e0c-46ef-978d-4983661d22b9

📥 Commits

Reviewing files that changed from the base of the PR and between 3e4eb2e and e6e81c7.

📒 Files selected for processing (2)
  • frontend/src/hooks/__tests__/useUserPreferences.test.tsx
  • frontend/src/hooks/useUserPreferences.tsx

The pending-write guard only covered reads that arrived mid-write. A read
issued earlier can resolve after the write settles, and it carries pre-write
state, so it reverted the change. Reads now capture a write counter when they
start and are dropped if a write has been queued since.
@rohan-pandeyy
rohan-pandeyy merged commit 76f7e2c into AOSSIE-Org:main Aug 2, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request GSoC 2026

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant