Skip to content

feat(review): native app-store review sheet + drop the custom prompt (TASK-22193, TASK-21044) - #2995

Merged
innolope-dev merged 6 commits into
devfrom
feat/app-store-review-sheet
Sep 5, 2026
Merged

feat(review): native app-store review sheet + drop the custom prompt (TASK-22193, TASK-21044)#2995
innolope-dev merged 6 commits into
devfrom
feat/app-store-review-sheet

Conversation

@innolope-dev

Copy link
Copy Markdown
Collaborator

Summary

Re-author of #2945 (+ its stacked #2952, already merged into that branch) onto current dev. The old branch forked before the claim-settle, web-vitals and About-view changes landed and could no longer merge; nothing in it was superseded — ReviewPromptModal is still live on dev today.

1. Canonical store URLs (TASK-21044). Pins the iOS store + write-review URLs to the official /us/ listing form (https://apps.apple.com/us/app/id6786373552); Google Play unchanged (id=me.peanut.wallet).

2. Removes the custom app-review prompt (TASK-22193). ReviewPromptModal ("Loving Peanut so far?" → store deep link / "Could be better" → support) is a custom review prompt, disallowed verbatim by App Store guideline 5.6.1. It also routed unhappy users away from the store (5.6.3; Google's In-App Review guidelines forbid the pre-question by name) and fired on the first home view after open. The APP_REVIEW modal type, reviewPromptShownAt pref, migration.review.* copy (three locales + regenerated marketing subsets) and the surface-gallery entry go with it.

3. The compliant replacement: the OS review sheet on happy moments. useAppReviewNudge mounts on four confirmed-success surfaces (payment success, claim success, QR payment, perk claimed) and calls requestAppReview, which:

  • banks every happy moment and asks only after 2 of them;
  • stays silent for 7 days after any money-flow failure event (tapped in PostHog's before_send, so no call sites);
  • enforces a 120-day cooldown and a 2/365 ceiling, one under Apple's quota;
  • never records a request as spent when the plugin rejects.

Profile → About gains a user-initiated Rate Peanut row (native only) that hands the write-review URL to the OS via AppLauncher — a tap, never a prompt, and the release valve when the OS quietly swallows the sheet.

Conflict resolution vs the old branch

OTA vs binary

The JS is OTA-safe: both plugins are dynamically imported and every call is guarded, so a binary predating them just never shows the sheet and falls back to the in-app browser on the Rate row. The sheet itself only fires from a binary that includes @capgo/capacitor-in-app-review and @capacitor/app-launcher (Package.swift / gradle wiring included here).

Tasks

QA

  • pnpm typecheck clean; related suites green (app-review, app-review-friction, About, Success.view, HomeModals, migration — 6 suites / 74 tests); prettier + eslint clean on touched files.
  • Store listings: re-verify both URLs return 200 before pwa-sunset flips.

kushagrasarathe and others added 4 commits September 5, 2026 09:16
… launch

the real store ids already landed on dev (ca45419); this pins the ios
store + write-review urls to the canonical /us/ listing form
(apps.apple.com/us/app/id6786373552) so they match the published listing
exactly. play url confirmed unchanged. launch-blocker per TASK-21044 §2.

branch doubles as the handoff branch for slava's review-nudge changes.
App Store Review Guidelines 5.6.1 disallows custom review prompts outright:
"Use the provided API to prompt users to review your app ... we will disallow
custom review prompts." ReviewPromptModal is one — our own copy, our own CTA,
deep-linking out to the store's write-review page.

Two further problems on the same modal: routing "Could be better" to the
support drawer filters unhappy users away from the store (5.6.3 Discovery
Fraud; Google's In-App Review guidelines name the "Do you like the app?"
pre-question explicitly), and it fired on the first home view after app open,
which is the moment the HIG tells you not to ask.

Removing it rather than relocating it: the native review sheet
(SKStoreReviewController / Play In-App Review) needs a Capacitor plugin, so it
cannot ride an OTA bundle. This deletion is JS-only and unblocks flipping
pwa-sunset to 100% without carrying a 5.6.1 violation into App Review; the
happy-moment replacement lands with the next binary.

REVIEW_URL, the reviewPromptShownAt preference and the APP_REVIEW modal type
lose their only consumer and go with it. migration.review.* is removed from the
three source catalogs and the marketing subsets regenerated.
…193)

Replaces the deleted custom prompt with the OS review sheet
(@capgo/capacitor-in-app-review: SKStoreReviewController on iOS, Play In-App
Review on Android), which is the only ask guideline 5.6.1 permits. This is the
binary half of the review-nudge work — it adds native plugins and so cannot ship
over OTA; the JS-only removal is TASK-22193's other PR.

requestAppReview(userId, trigger) is the single entry point. It carries the
whole policy so call sites stay one line:

- native only, skipped in demo mode
- an engagement floor (>=2 qualifying moments) before the first ask, so a
  day-0 depositor is never asked — HIG's "demonstrated engagement"
- a 120-day floor between requests and at most 2 per 365 days, one under
  Apple's silent 3/365 ceiling so a strong moment always has budget. The OS
  reports nothing back, so this stamp is the only control we have.
- a 7-day friction quiet period: any money-flow failure (observed for free in
  posthog before_send) suppresses the ask. This is the honest version of what
  the old "Could be better" branch reached for — suppress the question, never
  filter the answer toward or away from the store.

Wired on the tail of four happy moments, each gated on the success being real
and fired ~2.5s after the celebration settles (never mid-flow, never layered on
our own modal): reward/perk claim (a friend joined + money landed — the
strongest moment), shared payment success, claim-link money-received, and QR
pay. review_requested is captured with the trigger and budget state; there is
deliberately no shown/rated counterpart because neither OS exposes one — measure
against App Store Connect / Play Console.

Also adds a user-initiated "Leave a review" row in Profile -> About. A prompt
built from a write-review deep link would breach 5.6.1, but Apple documents that
exact link for a review the user starts themselves, and it is the release valve
for anyone the OS quota silently skipped. Opens via @capacitor/app-launcher, not
the in-app browser: SFSafariViewController does not follow the universal link, so
?action=write-review would otherwise land on the web listing with no composer.
@vercel

vercel Bot commented Sep 5, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated
peanut-wallet Ready Ready Preview Sep 5, 2026 4:20pm UTC

Request Review

@coderabbitai

coderabbitai Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Team

Run ID: 48c07faa-7007-491b-a68e-032d740a6473

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@notion-workspace

Copy link
Copy Markdown

@notion-workspace

Copy link
Copy Markdown

1 similar comment
@notion-workspace

Copy link
Copy Markdown

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Code-analysis diff

Painscore total: 7233.57 → 7237.64 (+4.07)
Findings: -3 net (+78 new, -81 resolved)

🆕 New findings (78)

  • critical complexity — src/app/(mobile-ui)/qr-pay/page.tsx — CC 329, MI 52.65, SLOC 1209
  • critical method-complexity — src/app/(mobile-ui)/qr-pay/page.tsx:103 — QRPayPage CC 84 SLOC 447
  • critical complexity — src/dev/surfaces/registry.tsx — CC 71, MI 65.5, SLOC 477
  • critical complexity — src/features/payments/shared/components/PaymentSuccessView.tsx — CC 58, MI 55.64, SLOC 174
  • critical complexity — src/dev/surfaces/list.ts — CC 1, MI 29.33, SLOC 206
  • high hotspot — src/app/(mobile-ui)/qr-pay/page.tsx — 144 commits, +1961/-1653 lines since 6 months ago
  • high hotspot — src/constants/analytics.consts.ts — 69 commits, +482/-28 lines since 6 months ago
  • high hotspot — src/utils/general.utils.ts — 49 commits, +280/-389 lines since 6 months ago
  • high complexity — src/features/home/components/HomeModals.tsx — CC 46, MI 63.08, SLOC 123
  • high complexity — src/components/Claim/Link/Onchain/Success.view.tsx — CC 39, MI 59.69, SLOC 161
  • medium react-long-component — src/app/(mobile-ui)/qr-pay/page.tsx:103 — QRPayPage is 1791 lines — split it
  • medium high-mdd — src/app/(mobile-ui)/qr-pay/page.tsx:103 — QRPayPage: MDD 487.3 (uses across many lines from declarations)
  • medium high-dlt — src/app/(mobile-ui)/qr-pay/page.tsx:103 — QRPayPage: DLT 138 (calls 138 distinct functions — high context load)
  • medium high-mdd — src/features/payments/shared/components/PaymentSuccessView.tsx:91 — PaymentSuccessView: MDD 106.2 (uses across many lines from declarations)
  • medium high-mdd — src/components/Claim/Link/Onchain/Success.view.tsx:33 — SuccessClaimLinkView: MDD 80.6 (uses across many lines from declarations)
  • medium high-mdd — src/components/Profile/views/About.view.tsx:26 — AboutView: MDD 52.9 (uses across many lines from declarations)
  • medium high-mdd — src/app/(mobile-ui)/qr-pay/page.tsx:821 — : MDD 48.2 (uses across many lines from declarations)
  • medium structural-dup — app/(mobile-ui)/dev/shake-test/page.tsx:29 — 46 duplicate lines / 204 tokens with app/(mobile-ui)/qr-pay/page.tsx:1124
  • medium high-dlt — src/components/Claim/Link/Onchain/Success.view.tsx:33 — SuccessClaimLinkView: DLT 39 (calls 39 distinct functions — high context load)
  • medium high-dlt — src/features/payments/shared/components/PaymentSuccessView.tsx:91 — PaymentSuccessView: DLT 32 (calls 32 distinct functions — high context load)

…and 58 more.

✅ Resolved (81)

  • src/app/(mobile-ui)/qr-pay/page.tsx — CC 328, MI 52.66, SLOC 1208
  • src/app/(mobile-ui)/qr-pay/page.tsx:102 — QRPayPage CC 83 SLOC 446
  • src/dev/surfaces/registry.tsx — CC 71, MI 65.4, SLOC 481
  • src/features/payments/shared/components/PaymentSuccessView.tsx — CC 58, MI 55.73, SLOC 173
  • src/features/home/components/HomeModals.tsx — CC 52, MI 63.21, SLOC 128
  • src/dev/surfaces/list.ts — CC 1, MI 29.1, SLOC 210
  • src/app/(mobile-ui)/qr-pay/page.tsx — 143 commits, +1956/-1653 lines since 6 months ago
  • src/constants/analytics.consts.ts — 67 commits, +475/-27 lines since 6 months ago
  • src/utils/general.utils.ts — 47 commits, +276/-387 lines since 6 months ago
  • src/components/Claim/Link/Onchain/Success.view.tsx — CC 38, MI 59.82, SLOC 160
  • src/app/(mobile-ui)/qr-pay/page.tsx:102 — QRPayPage is 1787 lines — split it
  • src/app/(mobile-ui)/qr-pay/page.tsx:102 — QRPayPage: MDD 486.3 (uses across many lines from declarations)
  • src/app/(mobile-ui)/qr-pay/page.tsx:102 — QRPayPage: DLT 137 (calls 137 distinct functions — high context load)
  • src/features/payments/shared/components/PaymentSuccessView.tsx:90 — PaymentSuccessView: MDD 105.2 (uses across many lines from declarations)
  • src/components/Claim/Link/Onchain/Success.view.tsx:32 — SuccessClaimLinkView: MDD 79.7 (uses across many lines from declarations)
  • src/app/(mobile-ui)/qr-pay/page.tsx:820 — : MDD 48.2 (uses across many lines from declarations)
  • app/(mobile-ui)/dev/shake-test/page.tsx:29 — 46 duplicate lines / 204 tokens with app/(mobile-ui)/qr-pay/page.tsx:1123
  • src/components/Profile/views/About.view.tsx:23 — AboutView: MDD 40.6 (uses across many lines from declarations)
  • src/components/Claim/Link/Onchain/Success.view.tsx:32 — SuccessClaimLinkView: DLT 38 (calls 38 distinct functions — high context load)
  • src/features/payments/shared/components/PaymentSuccessView.tsx:90 — PaymentSuccessView: DLT 31 (calls 31 distinct functions — high context load)

…and 61 more.

📈 Painscore deltas (top movers)

File Before After Δ
src/utils/app-review.ts 0.0 6.8 +6.8
src/utils/app-review-friction.ts 0.0 4.6 +4.6
src/hooks/useAppReviewNudge.ts 0.0 3.5 +3.5
src/components/Migration/ReviewPromptModal.tsx 6.8 0.0 -6.8

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

🧪 UI test report — ✅ all green

Suites

  • unit: 5951 ran, 0 failed, 0 skipped, 1.9m

📊 Coverage (unit)

metric %
statements 74.8%
branches 60.1%
functions 68.6%
lines 75.7%
⏱ 10 slowest test cases
time test
🐢 9.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › Network failure keeps loading while retries remain, then shows the generic error
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › User KYC not approved fails fast with copy that names the real cause
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › MANTECA_SOURCE_OVER_MONTHLY_CAP fails fast with copy that names the real cause
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › routes the KYC rejection on its wire code, and does not retry it
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › MANTECA_MERCHANT_RECENT_REFUND fails fast with copy that names the real cause
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › MANTECA_MERCHANT_VOLUME_NEAR_CAP fails fast with copy that names the real cause
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › a refused idempotency key tells the user to scan again, not to contact support
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › MANTECA_USER_NOT_PROVISIONED fails fast with copy that names the real cause
3.1s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › never places two stickers in heavy overlap (broad seed sweep)
3.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › Going offline blames the connection, and reconnecting clears it for the recovered scan
📍 Inline annotations are in the **Unit test report** check above. Coverage artifact: `coverage-unit`. Generated by `.github/workflows/tests.yml`.

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

🖼 Visual diff — 6 screens moved

9 of 66 shots changed · 57 identical · baseline d5f8d49 → head 5d1482c

worst % screen widths
3.63% avatar-picker 320, 430
0.70% huge-limits 320, 430
0.59% badges 320, 430
0.07% withdraw 320
0.03% unverified 430
0.01% empty-accounts 320

job summary · before/after/diff images — artifact

Fixture screenshots, no backend. Advisory — this check never blocks a merge. Posted from the default branch by ds-shots-comment.yml; the report it renders is untrusted data.

@chip-peanut-bot chip-peanut-bot Bot left a comment

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.

Chip review — no blocking findings — this is not an approval

The native review flow is narrowly scoped and its primary budget tests pass, but the exact-head unit gate is red and two edge paths bypass the intended native-only and post-failure guards.

Findings

  • MINOR · package.json:55 · Register both plugins in the native fingerprint list
    The exact-head unit job fails native-fingerprint › lists every plugin Capacitor generated in NATIVE_DEPENDENCIES: the generated manifests now contain both new packages, while scripts/native-fingerprint.mjs omits them from NATIVE_DEPENDENCIES. Add @capacitor/app-launcher and @capgo/capacitor-in-app-review to that list so the repository's native-surface contract passes and remains complete when generated manifests are stale.

  • MINOR · src/utils/app-review.ts:69 · Require a live bridge before spending a review request
    isCapacitor() is intentionally true for NEXT_PUBLIC_CAPACITOR_BUILD=true browser previews even when no native bridge exists. In that reachable environment the dependency's web adapter resolves requestReview() without displaying anything, so this code appends requestedAt and emits review_requested for a nonexistent prompt; the About row is exposed there for the same reason. Gate native review APIs and native-only UI with isNativeBridge() and cover the env-only/no-bridge case.

  • MINOR · src/utils/app-review-friction.ts:33 · Treat collateral spend failures as review friction
    QR pay can fail in resolveSpendStrategy or useSignSpendBundle, where the only analytics terminal is CARD_WITHDRAW_FAILED; this set ignores that event. If the user hits such a collateral/signing failure and then succeeds on retry, no quiet-period stamp exists and the second qualifying moment can open the review sheet 2.5 seconds later despite the immediately preceding money-flow failure. Include CARD_WITHDRAW_FAILED and pin this retry scenario in the friction test.

  • MINOR · src/hooks/useAppReviewNudge.ts:25 · [claude-opus] New gating hook useAppReviewNudge ships with no test
    CONTRIBUTING.md (mono, "Testing & verifiability" → Frontend): "Every custom hook that fetches data, gates a flow, or holds persistent state needs a test." useAppReviewNudge gates the review flow behind a 2.5s timer and drives the only write to the persistent reviewNudge budget in localStorage, and there is no src/hooks/tests/useAppReviewNudge.test.tsx — while ~30 sibling hooks in that directory do have one. app-review.test.ts covers requestAppReview's budget arithmetic but mocks nothing about the hook, so the hook's own contract is entirely unexercised.

Exact untested cases, both of which are behaviours the hook's own docstring promises:

  1. Unmount (or enabled flipping back to false, e.g. a post-factum error on qr-pay) before SETTLE_MS elapses must cancel the timer and never call requestAppReview — "a user who navigated away is no longer at a stopping point". Nothing asserts the cleanup.
  2. "Fires at most once per mount." fired.current is only set inside the timeout callback, so any dep change during the 2.5s window (userId resolving late, enabled toggling) tears down and restarts the timer rather than being a no-op; a re-render storm can push the ask arbitrarily late or, after the first fire, the guard is what stops a second banked moment. A render-hook test pinning "one requestAppReview call across N re-renders" would lock this down.

Fix: add src/hooks/tests/useAppReviewNudge.test.tsx with jest fake timers and @testing-library/react's renderHook — mock @/utils/app-review, assert (a) no call when unmounted at 2.4s, (b) exactly one call at 2.5s with (userId, trigger), (c) no call when enabled is false or userId is undefined, (d) one call only across repeated re-renders.

Checked clean

  • Verified the detached worktree, trusted author, base ref/base SHA, and exact head SHA.
  • Traced all four success-surface call sites; request creation and unconfirmed claim states are excluded.
  • Checked the engagement floor, 120-day cooldown, two-per-year budget, friction expiry, and plugin-rejection behavior; the new focused tests pass in exact-head CI.
  • Inspected the pinned review plugin's web, Android, and iOS implementations and the AppLauncher web fallback; no caller-controlled URL or sensitive-data path was introduced.
  • Checked removal of the custom prompt, modal orchestration, translations, store URLs, and surface-gallery references.
  • Exact-head format, typecheck, eslint, native-export, and analyze checks passed; unit failed only on the native fingerprint registry assertion, and ds-shots was still running when reviewed.

Security review by moonshotai/kimi-k3: 0 finding(s), marked with the model name. It reads the diff only and answers only security, privacy and money, so treat its findings as advice.

Third opinion by claude-opus: 1 finding(s), marked with the model name. It answers only product truth, missing tests and the cross-repo contract, so treat its findings as advice.

Exact head: 1d3803a2f64d · Context: repo, dependency · Took 11m

Comment thread package.json
Comment thread src/utils/app-review.ts Outdated
Comment thread src/utils/app-review-friction.ts
…es, register the plugins

- requestAppReview and the About row use isNativeBridge(): a capacitor-flavoured
  web preview resolves the plugin's web adapter without showing anything and
  would spend a request from the budget
- CARD_WITHDRAW_FAILED is friction: a failed collateral spend that succeeds on
  retry must not open the sheet 2.5s later
- both new plugins in NATIVE_DEPENDENCIES, which the fingerprint test enforces

@chip-peanut-bot chip-peanut-bot Bot left a comment

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.

Chip review — no blocking findings — this is not an approval

The exact head fixes P1-P3. P4 remains as a minor regression-test gap around the delayed review hook; required exact-head CI is green.

Findings

  • MINOR · src/hooks/useAppReviewNudge.ts:30 · Cover the delayed review hook itself
    The utility tests call requestAppReview directly, so they never exercise the hook that owns the 2.5-second delay, cancellation when the success surface disappears, and once-per-mount guard. A regression that drops the cleanup could open the OS sheet after the user has left the success screen, while a guard regression could schedule it more than once, without failing the current suite. Add a renderHook test with fake timers that covers the delay, unmount/disable cancellation, and a single call across rerenders.

  • MINOR · src/hooks/useAppReviewNudge.ts:25 · [claude-opus] New gating hook useAppReviewNudge ships with no test
    useAppReviewNudge is the only production caller of requestAppReview, and it decides whether the persisted reviewNudge state in localStorage gets mutated at all — yet it has no test, while app-review.ts and app-review-friction.ts both got thorough ones (src/utils/__tests__/app-review.test.ts, src/utils/__tests__/app-review-friction.test.ts).

Exact untested cases:

  1. Unmount inside the 2.5s settle window must cancel the ask — a user who taps away from PaymentSuccessView/Success.view.tsx within SETTLE_MS should bank no moment. Nothing asserts the clearTimeout cleanup, so a future edit that moves fired.current = true out of the timer callback, or drops the cleanup, would silently start banking moments (and eventually spending one of the two requests/year) for users who never stayed on the screen.
  2. Fire-at-most-once-per-mount when the effect re-runs. HomeCarouselCTA passes enabled = claimedPerkIds.size > 0 && !selectedPerk, which toggles as the perk modal opens and closes; only fired.current stops each toggle from banking another moment, and that guard is unasserted.

Fix: a small @testing-library/react renderHook test with fake timers mocking @/utils/app-review — assert (a) no call when unmounted before 2500ms, (b) exactly one call after toggling enabled false→true→false→true across a full settle, (c) no call for userId === undefined.

Checked clean

  • Verified the detached worktree head, trusted author, exact base ref and SHA, merge base, and PR metadata match the requested target.
  • Reviewed the complete base-to-head diff across native dependency wiring, review budgeting, friction capture, success-surface gates, store links, custom-prompt removal, localization, and dev-surface cleanup.
  • Rechecked P1: both new Capacitor plugins are listed in NATIVE_DEPENDENCIES, and the native fingerprint manifest includes the changed plugin-version input.
  • Rechecked P2: requestAppReview returns before reading or writing reviewNudge state unless isNativeBridge() is true; the exact case is covered by the utility test.
  • Rechecked P3: CARD_WITHDRAW_FAILED is now a friction event, is emitted by collateral-spend failure paths, and the retry-then-success case is covered.
  • Traced all four nudge call sites: each is gated on a completed money or reward outcome, with request creation excluded and claim/QR failures cancelling the pending timer.
  • The deleted custom modal still appears in two design-system audit inventories, but the PR explicitly scopes that known cleanup to TASK-22226 rather than leaving it untracked.
  • Required exact-head CI is green, including aggregate CI, unit, typecheck, native export, lint, format, analysis, provenance, and preview deployment; advisory ds-shots was still running when checked. A focused local Jest run was unavailable because dependencies are not installed in the detached worktree.
  • Correctness, security, adversarial, and slop passes found no additional actionable defect and no security, privacy, credential, authorization, or money-control surface requiring a dedicated security review.

Security review by moonshotai/kimi-k3: 0 finding(s), marked with the model name. It reads the diff only and answers only security, privacy and money, so treat its findings as advice.

Third opinion by claude-opus: 1 finding(s), marked with the model name. It answers only product truth, missing tests and the cross-repo contract, so treat its findings as advice.

Exact head: c36ec0423a93 · Context: repo, github_ci · Took 11m

Comment thread src/hooks/useAppReviewNudge.ts

@chip-peanut-bot chip-peanut-bot Bot left a comment

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.

Chip review — no blocking findings — this is not an approval

The app-review nudge is correctly gated, budgeted, and covered at this head; all supplied prior findings are fixed and exact-head required CI is green.

Checked clean

  • Confirmed the detached worktree, trusted author, dev base, base SHA, and head SHA match the supplied review target.
  • Reviewed the app-review budget, native-bridge and demo gates, friction suppression, plugin failure handling, manual store fallback, and all four success-surface call sites.
  • Verified both native plugins appear in package metadata, generated Android/iOS integration files, and the explicit native fingerprint dependency list.
  • Reviewed the delayed hook suite covering timer threshold, unmount and disabled cancellation, once-per-mount behavior, missing users, and disabled success state.
  • Exact-head aggregate CI, unit, native export, typecheck, lint, format, analysis, design-system lint, provenance, and deploy preview checks passed; the non-gating ds-shots job was still in progress.
  • A local targeted Jest rerun could not start because the detached worktree has no installed node_modules; exact-head unit CI passed the committed suites.

Security review by moonshotai/kimi-k3: 0 finding(s), marked with the model name. It reads the diff only and answers only security, privacy and money, so treat its findings as advice.

Third opinion by claude-opus: 0 finding(s), marked with the model name. It answers only product truth, missing tests and the cross-repo contract, so treat its findings as advice.

Exact head: 5d1482c9e9db · Context: repo · Took 9m

@innolope-dev
innolope-dev merged commit 234708d into dev Sep 5, 2026
24 checks passed
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.

2 participants