Pre-flight Checks
Problem Description
apps/mobile/doctor.config.ts currently carries 11 override groups. A few are covered by #363; the rest are undocumented debt. Every row is justified with evidence (react-doctor is at 100/100 with them), but the goal is to shrink the table with real fixes — not keep it forever.
Full override inventory
| # |
Files |
Rules ignored |
Why |
Covered by |
| 1 |
src/app/_layout.tsx |
deslop/unused-file |
Expo Router loads _layout.tsx by convention; deslop does not understand file-based routing |
new |
| 2 |
**/experiences.tsx |
rn-no-inline-flatlist-renderitem, rn-list-callback-per-row |
Horizontal category selector, small static items |
new |
| 3 |
package.json |
deslop/unused-dependency, expo-lockfile |
Workspace/monorepo false positives |
new |
| 4 |
experiences.tsx, explore.tsx, track-detail-view.tsx, trip-detail-view.tsx, track-map.tsx |
react-compiler, react-hooks-js/todo, prefer-useReducer, set-state-in-effect, no-initialize-state, no-giant-component |
try/finally data-fetching + React Compiler limitation; independent loading/error/data state by design |
#363 (react-hooks-js/todo only) — the other 4 rules are new to this inventory |
| 5 |
config-cache, translation-cache, device-service (+ .web variants) |
deslop/unused-export |
Platform-split files (Metro resolves .web.ts dynamically) |
#363 (device-service) |
| 6 |
use-track-download.ts, download-manager-store.ts |
no-create-object-url-without-revoke |
Blob URLs are the live expo-audio web playback source; lifecycle-correct revocation implemented |
#363 |
| 7 |
download-manager-store.ts |
async-await-in-loop |
Sequential stream-reader chunk reads require await in a loop |
new |
| 8 |
use-track-download.ts, use-immersion-player.ts |
no-fetch-in-effect, no-event-handler |
Custom background ETag fetching + audio player sync with external subscriptions |
new |
| 9 |
use-purchase.ts |
no-effect-with-fresh-deps, exhaustive-deps, react-compiler-no-manual-memoization |
Hooks optimized by React Compiler; useCallback used to satisfy standard exhaustive-deps |
new |
| 10 |
dist/** |
artifact-baas-authority-surface |
Static build artifacts |
new |
| 11 |
src/app/poetics/[id].tsx |
no-loading-flag-reset-outside-finally |
Genuine rule conflict: a real finally breaks React Compiler (BuildHIR::lowerStatement — see #147); without it the rule flags even though the reset is mirrored on every catch path (the catch does not re-throw) |
new |
Detailed notes on rows not covered by #363
Row 4 (extra rules) — prefer-useReducer, set-state-in-effect, no-initialize-state, no-giant-component: these detail screens use independent loading/error/data state and fetch via useEffect by design. A real fix would extract a shared data-fetching hook (see #147) — that is the refactor that would retire several of these rows at once.
Row 11 (poetics/[id].tsx) — added during the web-checkout fix (audioUrl refresh after purchase). The mirrored reset (setRefreshingExperience(false) at the end of try and in catch) is semantically identical to finally because the catch does not re-throw. React Doctor's static analysis does not recognize the mirror, so the rule fires on the trailing setState inside try regardless. No code shape satisfies both rules while React Compiler is enabled. Fix path: (a) upstream analyzer support for mirrored resets, (b) React Compiler support for finalizers, or (c) a shared loading-flag helper the analyzer can prove correct.
Row 1 (_layout.tsx) — Expo Router convention file; deslop's unused-file rule does not model the router's implicit require. Fix: verify newer deslop handles _layout.tsx or add a convention-aware rule exemption upstream.
Row 2 (experiences.tsx FlatList) — 8 static items; extracting renderItem would add indirection without benefit. Fix: document a size threshold that the analyzer accepts, or a small CategoryChip component.
Row 7 (async-await-in-loop) — Streams API reader.read() must be awaited sequentially; there is no parallel-safe variant. Fix: analyzer should whitelist ReadableStreamDefaultReader loops.
Row 8 (no-fetch-in-effect / no-event-handler) — use-track-download does background ETag validation; use-immersion-player subscribes to the audio player store. These are the established architecture. Fix: extract the ETag worker + player sync into lib modules with tests, then re-evaluate.
Row 9 (use-purchase.ts) — React Compiler memoizes the hook; the manual useCallback exists to satisfy exhaustive-deps. Fix: revisit when compiler-aware eslint rules stabilize (react-hooks-js 0.9+).
Row 10 (dist/)** — build output; the rule has no business scanning it. Fix: analyzer should ignore dist by default.
Verification & Current State
Acceptance Criteria
Affected Area
Code quality / linting — apps/mobile/doctor.config.ts
Pre-flight Checks
apps/mobile/doctor.config.tsand links each row to its fix path.Problem Description
apps/mobile/doctor.config.tscurrently carries 11 override groups. A few are covered by #363; the rest are undocumented debt. Every row is justified with evidence (react-doctor is at 100/100 with them), but the goal is to shrink the table with real fixes — not keep it forever.Full override inventory
src/app/_layout.tsxdeslop/unused-file_layout.tsxby convention; deslop does not understand file-based routing**/experiences.tsxrn-no-inline-flatlist-renderitem,rn-list-callback-per-rowpackage.jsondeslop/unused-dependency,expo-lockfileexperiences.tsx,explore.tsx,track-detail-view.tsx,trip-detail-view.tsx,track-map.tsxreact-compiler,react-hooks-js/todo,prefer-useReducer,set-state-in-effect,no-initialize-state,no-giant-componentconfig-cache,translation-cache,device-service(+ .web variants)deslop/unused-exportuse-track-download.ts,download-manager-store.tsno-create-object-url-without-revokedownload-manager-store.tsasync-await-in-loopuse-track-download.ts,use-immersion-player.tsno-fetch-in-effect,no-event-handleruse-purchase.tsno-effect-with-fresh-deps,exhaustive-deps,react-compiler-no-manual-memoizationuseCallbackused to satisfy standard exhaustive-depsdist/**artifact-baas-authority-surfacesrc/app/poetics/[id].tsxno-loading-flag-reset-outside-finallyfinallybreaks React Compiler (BuildHIR::lowerStatement— see #147); without it the rule flags even though the reset is mirrored on every catch path (the catch does not re-throw)Detailed notes on rows not covered by #363
Row 4 (extra rules) —
prefer-useReducer,set-state-in-effect,no-initialize-state,no-giant-component: these detail screens use independent loading/error/data state and fetch viauseEffectby design. A real fix would extract a shared data-fetching hook (see #147) — that is the refactor that would retire several of these rows at once.Row 11 (poetics/[id].tsx) — added during the web-checkout fix (audioUrl refresh after purchase). The mirrored reset (
setRefreshingExperience(false)at the end oftryand incatch) is semantically identical tofinallybecause the catch does not re-throw. React Doctor's static analysis does not recognize the mirror, so the rule fires on the trailing setState insidetryregardless. No code shape satisfies both rules while React Compiler is enabled. Fix path: (a) upstream analyzer support for mirrored resets, (b) React Compiler support for finalizers, or (c) a shared loading-flag helper the analyzer can prove correct.Row 1 (_layout.tsx) — Expo Router convention file; deslop's unused-file rule does not model the router's implicit require. Fix: verify newer deslop handles
_layout.tsxor add a convention-aware rule exemption upstream.Row 2 (experiences.tsx FlatList) — 8 static items; extracting renderItem would add indirection without benefit. Fix: document a size threshold that the analyzer accepts, or a small
CategoryChipcomponent.Row 7 (async-await-in-loop) — Streams API
reader.read()must be awaited sequentially; there is no parallel-safe variant. Fix: analyzer should whitelistReadableStreamDefaultReaderloops.Row 8 (no-fetch-in-effect / no-event-handler) —
use-track-downloaddoes background ETag validation;use-immersion-playersubscribes to the audio player store. These are the established architecture. Fix: extract the ETag worker + player sync into lib modules with tests, then re-evaluate.Row 9 (use-purchase.ts) — React Compiler memoizes the hook; the manual
useCallbackexists to satisfyexhaustive-deps. Fix: revisit when compiler-aware eslint rules stabilize (react-hooks-js 0.9+).Row 10 (dist/)** — build output; the rule has no business scanning it. Fix: analyzer should ignore
distby default.Verification & Current State
react-doctor0.9.3 (repo-pinned,bun run doctor): 100/100, No issues found with the documented exceptions.tsc --noEmitclean; prettier clean.react-hooks-js/todopart of row 4: see chore(code-health): resolve remaining react-doctor exceptions introduced by 0.9.3 cleanup (blob URLs, platform-split, try/finally) #363 for detailed fix paths.Acceptance Criteria
bun run doctorstays atNo issues foundafter each row is touched.Affected Area
Code quality / linting —
apps/mobile/doctor.config.ts