Skip to content

chore(lint): make it harder for me to accidentally product ai slop code - #143

Merged
cameronapak merged 37 commits into
mainfrom
cursor/cp/install-anti-slop-676e
Aug 25, 2026
Merged

chore(lint): make it harder for me to accidentally product ai slop code#143
cameronapak merged 37 commits into
mainfrom
cursor/cp/install-anti-slop-676e

Conversation

@cameronapak

@cameronapak cameronapak commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

Adds oxlint anti-slop to the Expo SDK. Generated slop fails lint.

Replaces jest.mock with hookOverrides and component impls. Tests do not hoist mocks or mount use dom.

Keeps #146. Provider locale still reaches every DOM WebView (YPE-5118).

Inspiration: https://x.com/dillon_mulroy/status/2087531327000117630

Greptile Summary

The PR replaces application-module Jest mocks with explicit hook and component test seams while moving lint enforcement to type-aware Oxlint.

  • Adds custom anti-slop and native-localization Oxlint rules and integrates them into CI.
  • Introduces hookOverrides and native component implementations for tests without mounting Expo DOM components.
  • Makes overridden highlight hooks inert across fetch, refresh, persistence, queue, and write paths.
  • Refactors tests and supporting fixtures to use the new seams.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains; the previously reported live-work and live-write paths are now gated before their observable side effects.

Important Files Changed

Filename Overview
packages/core/src/highlights/use-highlights.ts The override fix consistently gates reads, subscriptions, persistence, and both public write paths before any external or persisted side effect.
packages/core/src/highlights/use-highlight-permission-flow.ts The real hook remains mounted for stable hook ordering, but its nested highlight behavior is inert and the returned override controls all user-triggered flow actions.
packages/core/src/hook-overrides.ts Adds a typed, documented test seam for auth and highlight hooks while keeping production providers unchanged.
oxlint.config.ts Configures type-aware Oxlint, Expo DOM checks, native localization enforcement, and the new anti-slop plugin.
packages/ui/src/native/component-impls.ts Provides component substitution seams used to avoid mounting Expo DOM and sheet implementations in native unit tests.

Reviews (5): Last reviewed commit: "fix(test): type the outside-native-scope..." | Re-trigger Greptile

cursoragent and others added 18 commits August 19, 2026 18:01
Vendor the generic anti-slop Oxlint plugin and run it after ESLint so
the Expo SDK matches the Web SDK lint bar without dropping eslint-config-expo
or the native i18n ESLint checks.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
…y MMKV

Drop app-module jest.mock from core tests. Native runtimes stay shimmed in
jest.setup.js. Tests spyOn named exports, steer hooks through hookOverrides,
and use the real mmkvStorage backed by the in-memory MMKV factory.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
jest.setup.js mocks are process-wide jest.fn instances. spyOn returns
those same mocks, so call history leaked across cases until each suite
clears them in beforeEach.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
Replace file-level jest.mock of DOM wrappers, sibling sheets, and core
hooks with setImpls/captureDom/installBibleReaderTestImpls and
YouVersionProvider hookOverrides. Safe-area setup now honors
SafeAreaProvider initialMetrics so inset tests do not need module mocks.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
Seed the highlight write queue in MMKV instead of spying on a
non-configurable core export. Route BibleReader highlight stubs through
useHighlightPermissionFlow, unwrap the Gorhom mock default export, and
assert the real missing-provider error from useYouVersion.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
NativeSheet default-imports BottomSheet. The official CJS mock is a
namespace object, so React tried to mount it ("got: object"). Setup now
exports a capturing forwardRef default and BottomSheetView testIDs.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
NativeSheet reads the RN getter over Libraries/Utilities/useWindowDimensions,
so spyOn(ReactNative, useWindowDimensions) never reached the sheet. Setup now
mocks that module; NativeSheet tests assign width before render.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
Replace jest.mock of DOM, sheets, and core hooks with render-time
registries so tests do not mount 'use dom' or live fetch.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
Drop ESLint. Port native i18n and Expo DOM rules onto oxlint
jsPlugins, enable tsgolint, and pin typescript@7.0.2.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
Parse persist/token I/O with zod, replace Record widenings with satisfies,
type catch sites through toMessage(Error), and add explicit component/hook
return types without suppressing anti-slop rules.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
Replace setImpls with setImpl, name DOM/sheet capture types, drop runtime
typeof and chained assertions, and type fetch/JSON/JWT fixtures at their
boundaries without suppressing anti-slop rules.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
Give lastRequest a named return type and extract RequestInit bodies
through instanceof narrowing so oxlint stays clean in the API tests.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
Keep x-yvp-sdk as the exact object key the client sends, and throw
synchronously from getOrSetInstallationId so requestPermissions catch
can surface a transient failure.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
document.createElement is unavailable in the native test environment.
Keep a typed style-map fixture and assert to HTMLElement with SAFETY.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
Narrow mock.calls before use, type SignInResult and WebBrowser dismiss mocks, export AuthContextValue, and override DOM reader actions with Omit so TS 7 and oxlint stay clean.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
Avoid the mocked WebBrowserResultType enum at runtime, restore nullable backdrop checks, and resolve tsc via typescript/package.json so Jest can find it.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
Oxlint ignorePatterns does not expand {ts,tsx} braces, so list
__tests__ and *.test.tsx explicitly.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
@cameronapak cameronapak self-assigned this Aug 19, 2026
CI regenerates locales/index.ts and fails if the committed file
differs. Keep SdkI18nResourceMap in the generator template.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
@cameronapak cameronapak changed the title chore(lint): install oxlint anti-slop plugin chore(lint): make it harder for me to accidentally product ai slop code- #235 Aug 19, 2026
@cameronapak cameronapak changed the title chore(lint): make it harder for me to accidentally product ai slop code- #235 chore(lint): make it harder for me to accidentally product ai slop code Aug 19, 2026
@cameronapak
cameronapak marked this pull request as ready for review August 19, 2026 19:38
@cameronapak

Copy link
Copy Markdown
Collaborator Author

@greptile-apps, please review this PR

Comment thread packages/core/src/highlights/use-highlights.ts Outdated
Override wrappers still call the real hook for rules of hooks, but gate GET, drain notify, and cache persist so authenticated test seams cannot hit the network.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
@cameronapak

Copy link
Copy Markdown
Collaborator Author

@greptile-apps, updates have been made, so please re-review

cursoragent and others added 3 commits August 20, 2026 17:00
Keep hookOverrides live-gating and component-impls test seams. Keep host highlight paint (enabled/useHighlightPaint) and version filter lists from main. Convert incoming highlights-bridge suites off jest.mock of DOM modules.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
Incoming highlight-paint and version-filter code tripped anti-slop and explicit return types. Keep the new behavior; drop jest.mock of app modules and runtime typeof checks.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
Keep getImpl, ReactNode, and lint-safe fetch helpers. Take platform-core/react-ui 2.8.0 header combining (X-YVP-Sdk / ReactSDK) and drop unused useCallback/useMemo wraps from #141.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
@cameronapak

Copy link
Copy Markdown
Collaborator Author

@greptile-apps, please re-review. You don't have to re-review every single file, because I just did some small fixes, but honestly, you do whatever's best for you.

cursoragent and others added 2 commits August 21, 2026 22:59
Conflicting-intent merge with main after #146 (YPE-5118). Keep InternalLocaleProps and locale={locale} on every DOM WebView plus native pass-through. Keep oxlint ReactNode returns, typed DOMProps, getImpl test seams, and SAFETY comments. Lockfile regenerated with pnpm install --config.minimumReleaseAge=0.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
Keep #146 device-resolved locale assertions. Steer expo-localization through spyOn so oxlint no-module-mocking stays clean.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
@cameronapak

Copy link
Copy Markdown
Collaborator Author

@greptile-apps, review this and rip it to shreds to make sure the PR is bulletproof

Comment thread packages/core/src/highlights/use-highlights.ts

@camrun91 camrun91 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Marking as requesting changes as there are a few things that at least should be discussed. There are a few other things that claude called out but I do not think are big enough right now. if they show back up when these are closed I may surface them.

Comment thread .github/workflows/ci.yml
Comment thread packages/ui/src/lib/dom-local-storage.ts Outdated
Comment thread packages/ui/src/lib/reader-location.ts
Comment thread scripts/test-native-i18n-oxlint.mjs
Comment thread packages/core/src/youversion-provider.tsx
cursoragent and others added 7 commits August 22, 2026 18:53
Reading window.localStorage can throw in a sandboxed WebView. Catch that
read separately and still install the memory shim, matching old main.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
One wrong-typed field no longer nulls book, chapter, and versionId.
Restore per-field parse after JSON.parse; keep typed-but-invalid rehydrate.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
startWrite now follows the same live flag as fetch and persist. A
permission-only hook override no longer enqueues or transmits apply/remove.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
Switching to oxlint does not drop warn-fail. Keep lint:native-i18n in Lint.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
The scripts fixture was ignored, so the regression never ran. Point the
test at a packages/ui file so a global no-literal-string rule would fail it.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
Named WindowStub lets the accessor test install a throwing getter without
an assertion that oxlint rejects at --max-warnings=0.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
View has no headerTitle. Keep the literal attributes on a typed host so
oxlint still inspects them and typecheck stays clean.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
@cameronapak

Copy link
Copy Markdown
Collaborator Author

@greptile-apps, it's time for a re-review. Scrutinize this PR to make it as good as it can be.

@cameronapak
cameronapak requested a review from camrun91 August 22, 2026 19:07

@camrun91 camrun91 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Re-reviewed after the latest push. The five fixes all check out. I traced 765ef28 against main's old per-field semantics across every edge case (numeric chapter, 3.7, 0, -1, NaN, Infinity, "abc", "", booleans, string versionId, arrays, non-object JSON) and they all match. 6cbda01's guard is the first statement in startWrite, before any state touch, and live is always true without overrides, so production is untouched. The number_of_files > 0 guard in 9a72036 is better than what I suggested — it makes that test fail closed.

Three items are inline. Two more are below, because their lines fall outside this PR's diff.


Orphaned ESLint script now points at a deleted fixture (new)

9a72036 deleted scripts/eslint-fixtures/native-i18n/outside-native-scope.tsx, but scripts/test-native-i18n-eslint.mjs:115 still reads that exact path. That file is already unreachable — nothing invokes it since lint:native-i18n moved to the oxlint version, and eslint / eslint-config-expo were dropped from devDependencies — so it can't even run to fail. scripts/eslint-native-i18n-config.mjs (its only import target) is equally orphaned. Anyone opening either file to understand the i18n scoping rules reads instructions for a linter this repo no longer has.

Suggest: delete both files. (Not inline — neither is in this PR's diff.)


web-yv-provider.test.tsx:114 locale assertion fails open (inherited from #146, not this PR)

Flagging for a follow-up on main rather than as a blocker here — I confirmed both this assertion and the two-branch reader already exist on main.

expect(source).toMatch(/locale=\{locale\}/) with a non-global regex is a single "appears anywhere" check, but dom/bible-reader.tsx has two YouVersionProvider return branches (includeAuth true / false). Delete locale={locale} from the includeAuth branch at line 321 and every test stays green — the regex matches the surviving occurrence at line 338, and the runtime tests stub BibleReaderDom through setImpl so they never reach the web provider. Authenticated readers (the branch consumers hit whenever auth is configured) would render English inside the WebView with nothing red. The version-filter assertion above it has the same weakness, for the same reason.

Suggest: assert an occurrence count, or one match per <YouVersionProvider block.


Deliberately not filed

oxlint.config.ts:51 still excludes **/scripts/**, **/*.config.*, **/*.d.ts, and **/jest.setup.js from lint, where the old ESLint config covered scripts/. This push is itself evidence of the cost — 9a72036 had to relocate the i18n fixture out of scripts/ precisely because oxlint never inspects it. Still open, just not blocking.

Comment thread packages/ui/src/stores/reader-settings-store.ts Outdated
Comment thread packages/ui/src/lib/web-yv-provider.ts
Comment thread packages/ui/src/native/component-impls.ts
@camrun91

Copy link
Copy Markdown
Collaborator

Went ahead and had my robo friend Claude look into it the fixes look good I had it post the other high ones and two new ones that I also validated.

cursoragent and others added 2 commits August 24, 2026 12:15
One wrong-typed field no longer drops font size, family, and spacing.
Parse the persist envelope as unknown optional keys, then validate each
field on its own. Twin wrong-type test matches reader location.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
Nothing invoked these after lint:native-i18n moved to oxlint. The old
fixture path is gone.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
@greptile-apps

greptile-apps Bot commented Aug 24, 2026

Copy link
Copy Markdown

Too many files changed for review (190 files, 100 file limit).

Bypass the limit by tagging @greptile-apps to review.

@cursor

cursor Bot commented Aug 24, 2026

Copy link
Copy Markdown

Orphaned ESLint scripts: deleted. Nothing invoked them after oxlint.

Locale source-regex fail-open: inherited from #146. Follow-up on main.

oxlint scripts ignore: left alone. You said not blocking.

Code Implementor, sent on behalf of Cam. I am AI.

Bring in hide-swatches-when-unauth from main plus version packages.
Keep hookOverrides test seams. Empty swatch tray when auth is null.

Co-authored-by: Cameron Pak <cameronandrewpak@gmail.com>
@camrun91

Copy link
Copy Markdown
Collaborator

This PR now has no changeset, but it still adds public API

Merging 73f41cd (chore: version packages, #147) consumed provider-locale-dom-bridge.md, so .changeset/ is down to just README.md. Both packages are sitting at 1.3.1 from that release.

That leaves the branch adding public API with nothing to version it:

  • HookOverrides — new type export from packages/core/src/index.ts:2
  • AuthContextValue — new type export from packages/core/src/index.ts:9
  • hookOverrides — new prop on the UI YouVersionProvider, whose YouVersionProviderProps is exported at packages/ui/src/index.ts:29

changeset version bumps nothing without an entry, so these ship silently on whatever unrelated release lands next — no version bump, no CHANGELOG line. And nothing catches it: the Lint / Typecheck / Build / Test / Locale-parity jobs in ci.yml never mention changesets, so it merges green.

Suggest: a minor changeset for the two packages (they're a fixed group in .changeset/config.json, so one entry versions both).


Scoping note, since I overstated this in earlier review notes: I checked whether the sanitizeAvatarUrl (unknownstring) and IdTokenPayload (index signature dropped) narrowings are breaking for consumers. They are not. Neither symbol is re-exported from packages/core/src/index.ts, and the package exports map only exposes ., so deep imports are blocked — both are effectively internal. Same for the new named AuthProvider / HighlightQueueDrainHost exports, which are module-level only. So this is an additive minor, not a breaking change.

@camrun91
camrun91 self-requested a review August 24, 2026 18:42

@camrun91 camrun91 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

One thing to call out but It may not be blocking.

@cameronapak
cameronapak merged commit dd11c3f into main Aug 25, 2026
7 checks passed
@bmanquen
bmanquen deleted the cursor/cp/install-anti-slop-676e branch September 1, 2026 01:53
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.

4 participants