> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cfosilvia.com/llms.txt
> Use this file to discover all available pages before exploring further.

# QUERY CACHE MIGRATION PLAN

# Query Cache Migration Plan

## Goal

Standardize TanStack Query usage across the app so that:

* every query key comes from one shared factory
* every user-scoped query includes `userId` at index `[1]`
* every read uses a `useQuery` hook under `hooks/queries/`
* every write uses a `useMutation` hook under `hooks/mutations/`
* invalidation is intentional, minimal, and type-safe
* cache behavior is documented in `AGENTS.md` and becomes the default pattern for future work

## Problem Summary

The current codebase has several related issues:

* query keys are scattered as inline arrays across hooks, components, and pages
* many user-specific queries are not user-scoped, which creates cross-user cache leakage risk
* `queryClient.clear()` is being used as a broad safety net in `lib/react-query.tsx`
* writes are implemented inconsistently, with many raw `fetch` calls inside components/pages
* invalidation blocks often overlap and refetch the same query multiple times
* there is no single registry for root cache key names, which increases the chance of typos and drift

## Target Architecture

### 1. Uppercase cache key registry

Create `lib/cache-keys.ts` as the single source of truth for root key names.

This should be an uppercase registry such as `CACHE_KEYS`, used in an enum-like way. We should keep it as a constant object rather than a TypeScript `enum` so it stays aligned with the project rule to prefer unions/constants over enums.

Example:

```ts theme={null}
export const CACHE_KEYS = {
  ACCOUNTS: 'accounts',
  MANUAL_ACCOUNTS: 'manual-accounts',
  HOLDINGS: 'holdings',
  NET_WORTH: 'net-worth',
  TRANSACTIONS: 'transactions',
} as const
```

### 2. Shared query key factory

Create `lib/query-keys.ts` with:

* `globalKeys` for non-user data
* `userKeys(userId)` for user-scoped data

Every user-scoped query key must place `userId` at index `[1]`.

### 3. Hook-only data layer

* reads belong in `hooks/queries/`
* writes belong in `hooks/mutations/`
* pages and components should not call `fetch` directly for app data

### 4. Standard mutation policy

Use the same mutation pattern across the repo:

```ts theme={null}
const mutation = useMutation({
  mutationFn,
  onSuccess: async () => {
    await queryClient.invalidateQueries({
      queryKey: userKeys(userId).accounts.all,
    })
  },
})
```

We are explicitly choosing this invalidation-based approach over `setQueryData` and `setQueriesData` to keep mutation behavior consistent and easier to audit.

## Workstreams

## Phase 0: Foundations

Deliverables:

* `lib/cache-keys.ts`
* `lib/query-keys.ts`
* documentation set under `docs/react-query/`

Acceptance criteria:

* all root key names come from `CACHE_KEYS`
* new key arrays are composed only in `lib/query-keys.ts`

## Phase 1: Query hook migration

Scope:

* migrate all query hooks to `globalKeys` or `userKeys(userId)`
* remove local key factories such as `createQueryKeys`
* ensure every user-scoped query includes `userId`

Acceptance criteria:

* no inline `queryKey: [...]` in query hooks
* no user-specific query missing `userId` in the key

## Phase 2: Mutation and fetch standardization

Scope:

* move all raw write-side `fetch` calls into `hooks/mutations/`
* move all raw read-side `fetch` calls into `hooks/queries/`
* replace component/page mutation logic with hook usage
* replace `setQueryData` and `setQueriesData` with `invalidateQueries` in mutation flows

Acceptance criteria:

* no page/component should own app-data mutation logic directly
* no mutation should rely on `setQueryData` or `setQueriesData`

## Phase 3: Invalidation audit and cleanup

Scope:

* review every `invalidateQueries` usage file by file
* remove overlapping invalidations
* keep `refetchQueries` only where immediate UX requires it

Acceptance criteria:

* each invalidation block is justified by dependent data
* redundant cases like multiple invalidations for the same active query are removed

## Phase 4: Query client provider cleanup

Scope:

* replace full-cache `queryClient.clear()` with targeted removal of user-scoped queries
* preserve global cache where appropriate across sign-in/sign-out transitions

Acceptance criteria:

* user-scoped cache is removed safely on user switch
* global/public cache is not unnecessarily destroyed

## Phase 5: Documentation and guardrails

Scope:

* document the rules in `AGENTS.md`
* add grep-based or lint-style checks where practical

Acceptance criteria:

* future query/mutation work follows the documented pattern
* regressions are easier to catch during review

## PR Breakdown

Suggested PR order:

1. Foundation files and docs
2. Core query hook migration
3. Remaining query hook migration by domain
4. Mutation hook migration
5. Component/page fetch extraction
6. Invalidation audit and cleanup
7. Query client provider cleanup
8. `AGENTS.md` update and guardrails

## Detailed Docs

* `docs/react-query/01-CACHE-KEY-REGISTRY.md`
* `docs/react-query/02-QUERY-HOOK-MIGRATION.md`
* `docs/react-query/03-MUTATION-STANDARDIZATION.md`
* `docs/react-query/04-INVALIDATION-AUDIT.md`
* `docs/react-query/05-PROVIDER-AND-AGENTS-UPDATE.md`

## Verification

* `npm run build`
* smoke test authenticated flows
* mutation test on accounts/holdings/net-worth paths
* React Query DevTools inspection of key shapes
* multi-user sign-out/sign-in validation
* grep review for inline keys and raw component/page `fetch` usage


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.