Skip to content

Ai-settings: invalidate inherited org caches on tenant-level saves - #147

Open
romanivan-flamingo wants to merge 2 commits into
mainfrom
hotfix/live-customization-cache-invalidation
Open

Ai-settings: invalidate inherited org caches on tenant-level saves#147
romanivan-flamingo wants to merge 2 commits into
mainfrom
hotfix/live-customization-cache-invalidation

Conversation

@romanivan-flamingo

@romanivan-flamingo romanivan-flamingo commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Part of the live-customization work (ClickUp 86ajn8hnq): tenant-level AI-settings saves invalidated only their own react-query key, while org-scoped queries return the effective config (tenant defaults while inheritDefault is on) - so mounted customer pages kept showing pre-save values until a full reload.

Changes

  • Tenant CLIENT AI config save (tone, quick actions, model) also drops all organization-client-ai-config caches
  • Tenant-default ClientView save drops the whole client-view family, including the manual invalidation on the avatar-commit path (invalidateOnSuccess: false)
  • Guardrails template activation and custom policy save also drop organization-guardrails caches
  • Added all keys to the org-scoped query-key factories; updated the two sidecar docs

Out of scope

Server push (NATS config.changed) and per-setting in-flight semantics are a BE spec delivered separately; FE subscription and UI hints follow once BE lands.

Testing

  • npm run type-check clean
  • biome check clean on changed files

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Updated AI settings so changes to tenant-wide defaults refresh inherited organization settings.
    • Organization-specific AI configurations and guardrails now update immediately after related changes.
    • Improved cache refresh behavior for client views, AI configurations, and guardrail policies.
  • Documentation

    • Clarified when organization settings are refreshed after tenant-default updates.

Tenant-level saves (CLIENT AI config, default ClientView, guardrails
template activation/custom policy) now also drop the org-scoped query
caches that resolve effective values from the tenant default, so mounted
customer pages update without a reload (ClickUp 86ajn8hnq).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@romanivan-flamingo
romanivan-flamingo requested review from a team as code owners August 7, 2026 13:46
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The changes add shared query-key prefixes and broaden cache invalidation for tenant-default and CLIENT AI settings. Organization guardrails policy mutations also invalidate organization guardrails caches. Hook documentation reflects the updated behavior.

Changes

AI settings cache invalidation

Layer / File(s) Summary
Client view invalidation
src/app/(app)/settings/ai-settings/hooks/use-client-view.ts, src/app/(app)/settings/ai-settings/components/ai-settings-view.tsx, src/app/(app)/settings/ai-settings/hooks/.use-client-view.md
clientViewQueryKeys now exposes a shared root. Tenant-default saves invalidate all client-view queries. Organization-specific saves retain detail-only invalidation.
Organization AI configuration invalidation
src/app/(app)/settings/ai-settings/hooks/use-organization-ai-config.ts, src/app/(app)/settings/ai-settings/hooks/use-agent-ai-config.ts, src/app/(app)/settings/ai-settings/hooks/.use-agent-ai-config.md
CLIENT configuration updates invalidate all organization AI configuration queries. ADMIN updates retain existing invalidation behavior.
Organization guardrails invalidation
src/app/(app)/settings/ai-settings/components/guardrails/use-organization-guardrails.ts, src/app/(app)/settings/ai-settings/components/guardrails/use-guardrails-policies.ts
Guardrails query keys now use a shared root. Policy activation and custom-policy saves invalidate organization guardrails caches.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested reviewers: oleksandr-blip

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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: invalidating inherited organization caches after tenant-level AI settings saves.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch hotfix/live-customization-cache-invalidation

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.

@romanivan-flamingo romanivan-flamingo changed the title fix(ai-settings): invalidate inherited org caches on tenant-level saves Ai-settings: invalidate inherited org caches on tenant-level saves Aug 7, 2026
@romanivan-flamingo romanivan-flamingo self-assigned this Aug 7, 2026

@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: 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 `@src/app/`(app)/settings/ai-settings/components/ai-settings-view.tsx:
- Around line 158-160: Update the save flow containing the client-view query
invalidation to await both save mutations settling before invalidating
clientViewQueryKeys.all, replacing the fail-fast Promise.all behavior with
Promise.allSettled or equivalent. Preserve the existing cleanup/invalidation
behavior, and await queryClient.invalidateQueries if the flow must complete
after active queries refetch.
🪄 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: 0585b8e6-a28f-437c-a08b-ac7aa4576f2f

📥 Commits

Reviewing files that changed from the base of the PR and between 0b875b1 and 481ca15.

📒 Files selected for processing (8)
  • src/app/(app)/settings/ai-settings/components/ai-settings-view.tsx
  • src/app/(app)/settings/ai-settings/components/guardrails/use-guardrails-policies.ts
  • src/app/(app)/settings/ai-settings/components/guardrails/use-organization-guardrails.ts
  • src/app/(app)/settings/ai-settings/hooks/.use-agent-ai-config.md
  • src/app/(app)/settings/ai-settings/hooks/.use-client-view.md
  • src/app/(app)/settings/ai-settings/hooks/use-agent-ai-config.ts
  • src/app/(app)/settings/ai-settings/hooks/use-client-view.ts
  • src/app/(app)/settings/ai-settings/hooks/use-organization-ai-config.ts

Comment on lines +158 to +160
// Tenant-default save: also drops per-org entries so customer pages
// inheriting the default appearance refresh without a reload.
queryClient.invalidateQueries({ queryKey: clientViewQueryKeys.all });

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 | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== candidate files =="
fd -a 'ai-settings-view\.tsx|client-view-query|useUpdateClientView|clientViewQueryKeys' . | sed 's#^\./##' | head -200

echo
echo "== target file outline/size =="
wc -l src/app/'(app)'/settings/ai-settings/components/ai-settings-view.tsx
ast-grep outline 'src/app/(app)/settings/ai-settings/components/ai-settings-view.tsx' --view compact || true

echo
echo "== relevant target snippets =="
sed -n '1,220p' 'src/app/(app)/settings/ai-settings/components/ai-settings-view.tsx'

echo
echo "== search for clientViewQueryKeys/useUpdateClientView definitions/usages =="
rg -n "clientViewQueryKeys|useUpdateClientView|invalidateOnSuccess|updateClientView|client-view|clientView" src -S

Repository: flamingo-stack/openframe-oss-frontend

Length of output: 18297


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== use-client-view hook =="
sed -n '1,180p' 'src/app/(app)/settings/ai-settings/hooks/use-client-view.ts'

echo
echo "== customers usage snippets =="
for f in \
  'src/app/(app)/customers/components/ai-assistant-appearance/customer-ai-assistant-appearance.tsx' \
  'src/app/(app)/customers/components/customer-ai-configuration/customer-ai-configuration.tsx' \
  'src/app/(app)/customers/components/customer-details-view.tsx'
do
  echo "--- $f ---"
  wc -l "$f"
  rg -n "useUpdateClientView|useClientView|invalidateOnSuccess|commitAvatar|clientView" "$f" -C 8
done

echo
echo "== async sequencing behavioral probe =="
node - <<'JS'
function racePromiseAllBehavior() {
  let viewMutationPending = false;
  let invalidateCalled = false;

  const aiSave = new Promise((resolve, reject) => {
    setTimeout(() => reject(new Error('AI save failed')), 10);
  });
  const viewSave = new Promise((resolve) => {
    viewMutationPending = true;
    setTimeout(() => {
      viewMutationPending = false;
      resolve({ id: 'view-id' });
    }, 50);
  });

  Promise.all([aiSave, viewSave])
    .then(() => {
      console.log('Promise.all resolved');
    })
    .catch(() => {
      // This finally runs once aiSave rejects, while viewMutationPending is still true.
      invalidateCalled = true;
      console.log('finally ran while client-view mutation still pending:', viewMutationPending);
    });

  viewSave.then(() => {
    // useUpdateClientView success handler with invalidateOnSuccess=false does not invalidate again.
    console.log('client-view mutation later resolved; no invalidate from later success path');
  });

  setTimeout(() => console.log('invalidation called:', invalidateCalled), 120);
}

racePromiseAllBehavior();
JS

Repository: flamingo-stack/openframe-oss-frontend

Length of output: 18433


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== AI config mutation hook definitions =="
fd -a 'use-agent-ai-config.tsx|use-ai-configuration' src/\(app\)/settings/ai-settings/hooks src | sed 's#^\./##'
for f in 'src/app/(app)/settings/ai-settings/hooks/use-agent-ai-config.tsx' 'src/app/(app)/settings/ai-settings/hooks/use-update-ai-configuration.tsx'
do
  echo "--- $f ---"
  if [ -f "$f" ]; then
    wc -l "$f"
    rg -n "useUpdateClientAiConfig|useUpdate.*AiConfig|useMutation|invalidate|onSuccess" "$f" -C 5
  fi
done

echo
echo "== queryClient.invalidateQueries behavior probe =="
sed -n '1,220p' node_modules/@tanstack/query-core/build/legacy/queryClient.js 2>/dev/null || true
node - <<'JS'
const fs = require('fs');
const pkg = fs.readFileSync('package.json', 'utf8');
console.log('query package from lock/package context unavailable in read-only source inspection');
JS

Repository: flamingo-stack/openframe-oss-frontend

Length of output: 8798


🌐 Web query:

TanStack Query invalidateQueries refetchQueries active queries background Promise returns

💡 Result:

In TanStack Query, invalidateQueries and refetchQueries serve different purposes, though both interact with the query cache [1][2]. invalidateQueries When you call invalidateQueries, TanStack Query marks the matching queries as stale [3][4]. This stale state immediately overrides any existing staleTime configuration [3][5]. By default, it also automatically triggers a background refetch for any queries currently marked as "active" (those with mounted observers) [3][1][6]. You can control which queries are refetched using the refetchType option, which defaults to 'active' [1][7][6]. refetchQueries Unlike invalidation, which is a "smarter" approach that merely marks data as stale and triggers refetching, refetchQueries forces an immediate refetch of the matching queries regardless of their stale state or whether they have active observers [8]. It is a more imperative command to update data [1][2]. Behavior with Active/Background Queries - Active Queries: Both methods can target active queries [3][1][9]. When refetching occurs in the background, TanStack Query continues to show the existing (stale) data until the new data is fetched, avoiding a "hard" loading state [10]. - Promise Returns: Both invalidateQueries and refetchQueries return a Promise [10][6]. If you await these calls, the Promise will resolve only after the triggered refetches have finished [10]. Summary of Options - refetchType: Use this option in invalidateQueries to change the default behavior (e.g., set to 'all' to refetch inactive queries too, or 'none' to only mark as invalid without refetching) [1][7][6]. - Filters: Both methods support QueryFilters to precisely target which queries should be acted upon (e.g., using queryKey, predicate functions, or activity status) [3][1][9].

Citations:


Wait for both saves before invalidating client-view queries.

Promise.all rejects on the first failure, so this finally can invalidate clientViewQueryKeys.all while updateClientView is still pending. Later, useUpdateClientView(..., { invalidateOnSuccess: false }) does not invalidate when the view save resolves. As a result, active customer pages can refetch the old client view from an invalidation triggered before the pending save completes.

Use Promise.allSettled or equivalent so both mutations decide before invalidating. Await the invalidation if the save flow needs wait completion after active query refetches.

🤖 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)/settings/ai-settings/components/ai-settings-view.tsx around
lines 158 - 160, Update the save flow containing the client-view query
invalidation to await both save mutations settling before invalidating
clientViewQueryKeys.all, replacing the fail-fast Promise.all behavior with
Promise.allSettled or equivalent. Preserve the existing cleanup/invalidation
behavior, and await queryClient.invalidateQueries if the flow must complete
after active queries refetch.

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