Skip to content

fix(react-query): close the lint's blind spots, and the drift they hid - #7020

Merged
waleedlatif1 merged 1 commit into
stagingfrom
deslop-query
Aug 24, 2026
Merged

fix(react-query): close the lint's blind spots, and the drift they hid#7020
waleedlatif1 merged 1 commit into
stagingfrom
deslop-query

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Stacked on #7019#7018#7016#7015.

The gate was partly reporting on what it could see

scripts/check-react-query-patterns.ts has a zero-tolerance strict zone (apps/sim/hooks/queries/**) and an empty baseline, and it passes clean. Two gaps in one regex explain part of that.

1. Type arguments. \buseQuery\s*\( does not match useQuery<Row[]>({ ... }) — the type argument sits between the name and the paren. 20 query calls carry one, 10 inside the strict zone. That zone's "0 violations" was, for those ten, a statement about the scan rather than the code.

2. useQueries. Absent from both the call pattern and the file pre-filter, where \buse(Query|...)\b rejects it on the trailing s. All 16 call sites were unscanned. It also nests its options one level deeper, inside a queries array — so a naive fix that reads the wrapper would take a single staleTime anywhere inside as covering every entry. It gets its own per-entry pass.

Closing both surfaced exactly three violations.

The drift they hid

knowledge-base-selector.tsx served knowledgeKeys.detail(id) with an inline 60 * 1000, while useKnowledgeBaseQuery serves the same cache key from KNOWLEDGE_BASE_DETAIL_STALE_TIME. They agree today by coincidence. TanStack resolves staleTime per observer, so tuning the constant would have left this component on the old freshness window for the same cache entry — the precise failure the CLAUDE.md rule exists to prevent.

The same call also dropped the AbortSignal that fetchKnowledgeBase accepts, and use-permission-config.ts gave staleTime as a bare literal.

New category: stale-time-literal

The rule reads "assigned from a named exported constant, never an inline numeric literal" — but only the presence of staleTime was ever checked. That is the half that let all three through, so it is now enforced.

0 is exempt. It is the sentinel for "always refetch", not a window anyone keeps in step with a prefetch, and the two documented staleTime: 0 sites in the strict zone are correct as written.

Verified the rules can fail

Reverted each of the three fixes, confirmed the audit reported it, then restored:

ratchet zone violations: 3 {"stale-time-literal":2,"queryfn-no-signal":1}
  knowledge-base-selector.tsx:75  [stale-time-literal]  useQueries entry staleTime is the literal 60 * 1000
  knowledge-base-selector.tsx:75  [queryfn-no-signal]   useQueries entry queryFn takes no args
  use-permission-config.ts:56     [stale-time-literal]  useQuery staleTime is the literal 5 * 60 * 1000

With the fixes in place both zones are 0, so no baseline entries were needed.

Testing

  • 3809 tests passing across hooks and app/workspace
  • bun run type-check clean; the audit passes under --check

Follow-up not included

use-permission-config.ts also breaks "all React Query hooks live in hooks/queries/" — its private key factory and hook sit outside the strict zone, which is why the zone never saw them. Moving the file would put it under strict enforcement but touches imports across the permission surface, so it wants its own PR.

@vercel

vercel Bot commented Aug 23, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview Aug 24, 2026 2:08am

Request Review

@cursor

cursor Bot commented Aug 23, 2026

Copy link
Copy Markdown

PR Summary

Low Risk
CI lint and cache-freshness alignment only; no auth, data, or API contract changes. Regex-based scanning can still miss edge syntax, but the three previously hidden violations are now fixed.

Overview
Closes two blind spots in check-react-query-patterns.ts so the React Query audit actually sees generic useQuery<T>(...) calls and every useQueries entry (previously skipped by the regex and the file pre-filter).

Adds a stale-time-literal rule: staleTime must be a named exported constant (0 still allowed as always-refetch). That surfaced the real drift: knowledge-base-selector used an inline 60 * 1000 on knowledgeKeys.detail while useKnowledgeBaseQuery uses KNOWLEDGE_BASE_DETAIL_STALE_TIME for the same key. The selector now shares that constant and forwards AbortSignal. use-permission-config exports ALLOWED_INTEGRATIONS_STALE_TIME instead of a bare literal.

Reviewed by Cursor Bugbot for commit 132b178. Configure here.

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 132b178. Configure here.

if (queriesValue === null) continue

for (const entry of splitObjectLiterals(queriesValue)) {
if (/\.\.\.\w/.test(entry)) continue

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

useQueries spread skips entries

Medium Severity

The new useQueries pass does continue on any entry matching /\.\.\.\w/, which also hits normal array spreads in queryKey and object spreads in queryFn args. That skips every check for those entries. The single-query path only softens missing-stale-time when options are spread, so this reopens a blind spot—including the existing useQueries site in use-selector-query.ts.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 132b178. Configure here.

@greptile-apps

greptile-apps Bot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR aligns two React Query callers with shared stale-time constants, forwards cancellation into knowledge-base fetches, and expands the React Query audit to inspect generically typed hooks and individual useQueries entries.

  • Reuses the knowledge-base detail freshness constant and forwards AbortSignal.
  • Exports and reuses the allowed-integrations freshness constant.
  • Adds useQueries scanning and stale-time-literal diagnostics to the audit.

Confidence Score: 4/5

The PR appears safe to merge, with non-blocking gaps remaining in how comprehensively the expanded lint gate recognizes generic calls and exported stale-time constants.

The application changes preserve query behavior while improving cancellation and freshness consistency; the remaining concerns affect the audit’s ability to enforce those conventions across additional valid source forms.

Files Needing Attention: scripts/check-react-query-patterns.ts

Important Files Changed

Filename Overview
scripts/check-react-query-patterns.ts Expands static query-pattern coverage, but the generic and stale-time classifiers retain specific syntax and enforcement blind spots.
apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/knowledge-base-selector/knowledge-base-selector.tsx Correctly reuses the shared detail stale time and propagates query cancellation.
apps/sim/hooks/use-permission-config.ts Extracts the inline freshness duration into an exported reusable constant.

Reviews (1): Last reviewed commit: "fix(react-query): close the lint's blind..." | Re-trigger Greptile

* reported zero violations while never having looked at them. One level of nesting is enough
* for the shapes that occur here (`useQuery<Record<string, T>>`).
*/
const TYPE_ARGS = String.raw`(?:\s*<[^<>()]*(?:<[^<>()]*>[^<>()]*)*>)?`

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.

P2 Generic matcher retains blind spots

TYPE_ARGS rejects parentheses and supports only one nested angle-bracket level, so valid query generics containing function types, parenthesized conditional types, or deeper nested generics remain unscanned and can bypass the React Query checks.

Comment on lines +205 to +209
*/
function isNamedStaleTime(value: string): boolean {
const trimmed = value.trim()
if (trimmed === '0') return true
return !/^[0-9]/.test(trimmed)

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.

P2 Classifier does not verify exports

isNamedStaleTime accepts every expression not beginning with a digit, including local identifiers, parenthesized literals, and unary numeric expressions. The audit therefore accepts values that are not named exported constants and cannot provide the intended shared freshness source for prefetch callers.

`check-react-query-patterns.ts` reported a clean strict zone while never
looking at part of it. Two gaps in one regex:

`\buseQuery\s*\(` does not match `useQuery<Row[]>({ ... })` — a type argument
sits between the name and the paren. Twenty query calls carry one, ten of them
inside the zero-tolerance zone, so that zone's "0 violations" was partly a
statement about what the scan could see.

`useQueries` was absent from both the call pattern and the file pre-filter,
where `\buse(Query|...)\b` rejects it on the trailing `s`. All sixteen call
sites were unscanned, and its options nest one level deeper — inside a
`queries` array — so it needs its own pass per entry rather than one that reads
the wrapper and takes a single `staleTime` anywhere inside as covering them all.

With both closed, three real violations surfaced:

- `knowledge-base-selector` served `knowledgeKeys.detail(id)` with an inline
  `60 * 1000` while `useKnowledgeBaseQuery` serves the same cache key from
  `KNOWLEDGE_BASE_DETAIL_STALE_TIME`. The two agree only by coincidence, and
  TanStack resolves staleTime per observer, so tuning the constant would have
  left this component on the old window for the same entry.
- The same call dropped the `AbortSignal`, which `fetchKnowledgeBase` accepts.
- `use-permission-config` gave `staleTime` as a literal with no named constant.

The new `stale-time-literal` category makes the second half of the CLAUDE.md
rule enforceable — it required a named constant, and only the presence of
`staleTime` was ever checked. `0` is exempt: it is the sentinel for "always
refetch", not a window anyone keeps in step with a prefetch.

Verified the new rules can fail by reverting each fix and watching the audit
report it, then restoring.
@waleedlatif1
waleedlatif1 merged commit cc16d23 into staging Aug 24, 2026
28 checks passed
@waleedlatif1
waleedlatif1 deleted the deslop-query branch August 24, 2026 02:09
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