Skip to content

chore: back-merge main → dev (2026-08-20) - #2769

Merged
jjramirezn merged 21 commits into
devfrom
chore/backmerge-main-into-dev-20260820
Aug 20, 2026
Merged

chore: back-merge main → dev (2026-08-20)#2769
jjramirezn merged 21 commits into
devfrom
chore/backmerge-main-into-dev-20260820

Conversation

@jjramirezn

Copy link
Copy Markdown
Contributor

Back-merge of main hotfixes into dev so dev is a clean superset before the next release.

Brings over (main-only):

Conflict resolution (4 files, all QRScanner/QRBottomDrawer):

  • QRBottomDrawer/index.tsx, QRScanner/index.tsx: dev side kept — it is the refined superset of main's hotfix (deterministic peek, QR_DRAWER_EXPANDED_PX CSS var, iOS 15.0–15.3 h-screen fallback, pointer-events wrappers, paste-chip copy useCopiedCode).
  • QRScanner/useQRScanner.ts: union of imports — dev's ensureNativeCameraPermission + main's reportQrScanError; both call sites auto-merged.
  • QRScanner/__tests__/index.test.tsx: dev constants kept (CHIP_LABEL matches dev copy) + main's PIX_PAYLOAD re-added for the Sentry-report tests.

Verified locally: prettier clean, jest QRScanner 32/32 pass, tsc --noEmit clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QniCxBnNTbMzj4QCQHAwWT

claude and others added 21 commits August 19, 2026 16:26
The site directory had no route to any legal document, and the two links
that existed (Terms, Privacy in the footer nav) sat apart from the five
card-programme documents, which were reachable only mid-application from
CardTermsScreen. App-store review and the card issuer both expect those
permanently reachable, so all seven now live in one Legal column.

- Legal column: Terms of Service, Privacy Policy, Card Terms (U.S.),
  Card Terms (International), E-Sign Consent, Account Opening Privacy
  Notice, Prohibited Activities Policy — locale-routed like the manifest
  entries, so a Spanish reader lands on Spanish prose.
- Resources column is gone; Help Center, Pricing and Supported Networks
  move to the top of Learn More. The manifest's "Terms" entry is dropped
  (a stale Notion export superseded by the /terms page) along with
  "Jobs", which already sits in the footer nav above.
- Footer nav drops Terms and Privacy — one home per document.
- footerTerms/footerPrivacy now read "Terms of Service"/"Privacy Policy",
  the labels a legal column wants; footerResources gives up its slot to
  the new Legal keys across all four catalogs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018GWaH5h1Prm6zpeXkKWcZR
`apt-get update`, run inside `npx playwright install-deps chromium`, stalls
intermittently on the runner's Azure mirrors. On 2026-08-19 it killed the
e2e job three consecutive times on one commit — each run ignoring
azure.archive.ubuntu.com, then hanging on archive.ubuntu.com noble-security
until the 6-minute cap — while sibling runs minutes apart cleared the same
step in seconds. Unrelated branches hit it the same afternoon, so it is not
branch-specific.

The stall is transient, so retry rather than mask: three attempts with a
150s per-attempt cap, dropping to the canonical archive after the first
failure. One bad mirror now costs an attempt instead of the whole job.

Deliberately still exits non-zero once the attempts are spent. Since
`Run E2E tests` is continue-on-error, a setup step is the only thing that
can red this job — making the install non-fatal would leave a genuinely
missing system library silently unreported.

The job cap moves 6 → 9 minutes to fit three bounded attempts; it stays well
under the job's own 20-minute ceiling, so the fail-fast property that
`ci-success` depends on is preserved.

Verified by extracting the run block and exercising it under `bash -e` with
fakes for npx/sudo/timeout: succeeds on first pass, recovers on a third
attempt, and exits 1 when all three stall. The guarded pkill/sed cleanups
return non-zero without aborting the script.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018GWaH5h1Prm6zpeXkKWcZR
Review feedback on the retry loop. `timeout` already runs the attempt in its
own process group and signals that whole group, so the apt children do receive
the SIGTERM — `--kill-after=15s` escalates to SIGKILL for any that ignore it.

That makes `sudo pkill -9 -f '[a]pt-get'` both redundant and unsafe: it matched
by command line across the entire runner, so it could have killed unrelated
package work rather than only this step's descendants. Removed.

The apt partial-list cleanup stays — that clears state a killed attempt leaves
behind, which is what the next attempt trips over.

Re-verified under `bash -e` with fakes: succeeds first pass, recovers on the
third attempt, exits 1 when all three stall.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018GWaH5h1Prm6zpeXkKWcZR
The two QR-scanner catch blocks only console.error'd. Sentry's console
integration already forwards that as captureException(err), but the
event is titled by the inner error, untagged and payload-less, so a
live support case ("Error processing QR code" on Itaú Pix terminals)
left no findable trace.

Capture explicitly with error_type=qr_scan_processing plus the first
64 chars of the payload, and downgrade the console call to info so the
console integration does not emit a second event for the same throw.
The paste path forwards arbitrary clipboard text and a scanned Peanut
claim link carries a bearer secret, so a blanket 64-char excerpt could
ship a user's secret to Sentry (CodeRabbit). Gate the excerpt on the
EMVCo "000201" payload-format indicator — Pix / Mercado Pago / QR3 are
machine-generated merchant data and the family this report exists for.
Everything else keeps qrLength only. One helper now owns the rule.
A static Pix QR with a personal key embeds the payee's CPF/phone inside
the first 64 chars, so even an EMVCo-gated excerpt ships PII; no excerpt
is safe by construction. Replace it with qrKind (pix/emv/url/other) —
a closed enum derived on-device — next to qrLength. Tests assert the
exact extra object and that a claim-link secret never leaves the device.
…ps-retry

fix(ci): retry Playwright system-deps install past apt mirror stalls
Scan failures cannot be diagnosed from a family enum; the report needs
the payload itself. Send it in full as extra.qrPayload next to qrLength
and qrKind. This carries payee/merchant data, a claim link's fragment,
or whatever the paste path hands in — a privacy trade the code owner
accepted on 2026-08-19 (PR #2757); the helper is the single place to
change if that call is reversed.
…pture

fix(qr-scanner): make onScan throws findable in Sentry
…s-audit-z4ed53

feat(footer): Update legal links in Footer
…0820-104554

content: publish latest to production (src/content → peanut-content@cebac1c)
The eslint job has been advisory since it was split out of `format`, so a
lint error could land on main unnoticed. `pnpm lint` is at 0 errors today
(64 warnings), so there is nothing to pay down before turning the gate on.

Drop `continue-on-error` — with it set the check reports green whatever
eslint says, which makes any "required check" flag a no-op — and add the
job to `ci-success.needs`. The branch ruleset requires only `ci-success`,
so this makes lint blocking on main and dev with no ruleset edit.

Warnings still do not gate. Ratchet to `--max-warnings` separately.

Claude-Session: https://claude.ai/code/session_01P6CXdiGhc3io26wXXDsjjk
ci: make eslint blocking on peanut-ui
…s and screen heights

The paste link was positioned from the top of the viewport while the drawer's
collapsed peek grew from the bottom, and vaul resolves a fractional snap point
against the drawer's own content height. The peek therefore changed with both
the locale (pt-BR wraps the drawer's body text to two lines) and the screen
height, so the two met and the link went under the drawer — every locale below
667px, and pt-BR on tall phones such as the S24 Ultra.

Give the drawer a full-height content box so px snap points are exact visible
heights, and anchor the paste actions a fixed gap above that known peek.
jsdom has no layout, so the geometry was verified in a browser. What this
pins is the coupling that would silently regress: the anchor offset must be
derived from QR_DRAWER_PEEK_PX, so changing the peek can never leave the
paste link behind again.
The PR body references them by commit SHA, which keeps resolving after the
files leave the tree.
…taining block

Review findings on the first pass:

h-full resolves a percentage height against the initial containing block,
which on a mobile browser with a retractable toolbar is the LARGE viewport.
vaul resolves a snap point against window.innerHeight, so the two disagreed by
the height of the toolbar and the collapsed peek grew by that much — putting
the drawer back over the paste link on exactly the Chrome Android phones the
bug was reported from. A headless browser has no toolbar, so the measurement
matrix could not see it. h-[100dvh] tracks innerHeight.

The expanded snap also became a fixed height on a drawer that no longer grows
with its content, so a longer translation or a larger font-size setting cut
the share button off with nothing to scroll: the shared max-h-[80vh] cap is
never reached inside a 520px window. Cap the scroll area to the expanded
window instead, less the rem-based drag-handle block, so the scroll region
lands on the bottom of the viewport at any font size.

Also move the shared geometry into a constants module so the scanner no longer
imports the drawer component, and drop min-h-[200px], which h-[100dvh] made
dead.
…k-drawer-overlap

fix(qr-scanner): keep paste link above the My QR drawer on all locales and screen heights
…into-dev-20260820

# Conflicts:
#	src/components/Global/QRBottomDrawer/index.tsx
#	src/components/Global/QRScanner/__tests__/index.test.tsx
#	src/components/Global/QRScanner/index.tsx
#	src/components/Global/QRScanner/useQRScanner.ts
@vercel

vercel Bot commented Aug 20, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
peanut-wallet Building Building Preview Aug 20, 2026 4:12pm

Request Review

@coderabbitai

coderabbitai Bot commented Aug 20, 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: Pro

Run ID: 57fad620-fbe7-4378-95f7-81eb33a2f362

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

Comment @coderabbitai help to get the list of available commands.

@jjramirezn
jjramirezn enabled auto-merge August 20, 2026 16:12
@github-actions

Copy link
Copy Markdown
Contributor

Code-analysis diff

Painscore total: 7292.27 → 7299 (+6.73)
Findings: 0 net (+21 new, -21 resolved)

🆕 New findings (21)

  • critical complexity — src/components/Global/QRScanner/useQRScanner.ts — CC 64, MI 55.98, SLOC 305
  • high complexity — src/components/Global/QRScanner/index.tsx — CC 37, MI 63.58, SLOC 153
  • high hotspot — src/components/Global/QRScanner/index.tsx — 30 commits, +798/-422 lines since 6 months ago
  • medium high-mdd — src/components/Global/QRScanner/useQRScanner.ts:96 — useQRScanner: MDD 124.8 (uses across many lines from declarations)
  • medium high-mdd — src/components/LandingPage/Footer.tsx:57 — Footer: MDD 46.4 (uses across many lines from declarations)
  • medium high-dlt — src/components/Global/QRScanner/useQRScanner.ts:96 — useQRScanner: DLT 44 (calls 44 distinct functions — high context load)
  • medium high-mdd — src/components/Global/QRScanner/index.tsx:246 — QRScanner: MDD 27.7 (uses across many lines from declarations)
  • medium complexity — src/components/LandingPage/SEOFooter.tsx — CC 28, MI 67.67, SLOC 99
  • medium high-mdd — src/components/LandingPage/SEOFooter.tsx:90 — SEOFooter: MDD 26.2 (uses across many lines from declarations)
  • medium high-mdd — src/components/Global/QRScanner/index.tsx:149 — ScanRegionOverlay: MDD 24.5 (uses across many lines from declarations)
  • medium method-complexity — src/components/Global/QRScanner/useQRScanner.ts:226 — CC 15 SLOC 72
  • low high-dlt — src/components/Global/QRScanner/index.tsx:246 — QRScanner: DLT 23 (calls 23 distinct functions — high context load)
  • low high-mdd — src/components/Global/QRScanner/useQRScanner.ts:226 — : MDD 20.0 (uses across many lines from declarations)
  • low high-dlt — src/components/Global/QRScanner/useQRScanner.ts:226 — : DLT 20 (calls 20 distinct functions — high context load)
  • low structural-dup — components/Global/QRScanner/index.tsx:105 — 17 duplicate lines / 57 tokens with components/Global/QRScanner/index.tsx:149
  • low high-mdd — src/components/Global/QRScanner/index.tsx:105 — PasteActions: MDD 16.0 (uses across many lines from declarations)
  • low high-mdd — src/components/Global/QRScanner/useQRScanner.ts:392 — : MDD 15.5 (uses across many lines from declarations)
  • low high-mdd — src/components/Global/QRScanner/index.tsx:81 — ScannerControls: MDD 11.0 (uses across many lines from declarations)
  • low missing-return-type — src/components/Global/QRScanner/index.tsx:246 — QRScanner: exported fn missing return type annotation
  • low missing-return-type — src/components/Global/QRScanner/useQRScanner.ts:96 — useQRScanner: exported fn missing return type annotation

…and 1 more.

✅ Resolved (21)

  • src/components/Global/QRScanner/useQRScanner.ts — CC 64, MI 56.02, SLOC 304
  • src/components/Global/QRScanner/index.tsx — CC 37, MI 63.65, SLOC 152
  • src/components/Global/QRScanner/useQRScanner.ts:95 — useQRScanner: MDD 123.8 (uses across many lines from declarations)
  • src/components/LandingPage/Footer.tsx:57 — Footer: MDD 49.2 (uses across many lines from declarations)
  • src/components/Global/QRScanner/useQRScanner.ts:95 — useQRScanner: DLT 43 (calls 43 distinct functions — high context load)
  • src/components/Invites/InvitesPage.tsx — 29 commits, +548/-347 lines since 6 months ago
  • src/components/Global/QRScanner/index.tsx:245 — QRScanner: MDD 27.5 (uses across many lines from declarations)
  • src/components/Global/QRScanner/index.tsx:148 — ScanRegionOverlay: MDD 24.5 (uses across many lines from declarations)
  • src/components/LandingPage/SEOFooter.tsx:55 — SEOFooter: MDD 24.6 (uses across many lines from declarations)
  • src/components/LandingPage/SEOFooter.tsx — CC 19, MI 68.05, SLOC 52
  • src/components/Global/QRScanner/useQRScanner.ts:222 — CC 15 SLOC 72
  • src/components/Global/QRScanner/index.tsx:245 — QRScanner: DLT 21 (calls 21 distinct functions — high context load)
  • src/components/Global/QRScanner/useQRScanner.ts:222 — : MDD 20.0 (uses across many lines from declarations)
  • src/components/Global/QRScanner/useQRScanner.ts:222 — : DLT 20 (calls 20 distinct functions — high context load)
  • components/Global/QRScanner/index.tsx:104 — 17 duplicate lines / 57 tokens with components/Global/QRScanner/index.tsx:148
  • src/components/Global/QRScanner/index.tsx:104 — PasteActions: MDD 16.0 (uses across many lines from declarations)
  • src/components/Global/QRScanner/useQRScanner.ts:388 — : MDD 15.5 (uses across many lines from declarations)
  • src/components/Global/QRScanner/index.tsx:80 — ScannerControls: MDD 11.0 (uses across many lines from declarations)
  • src/components/Global/QRScanner/index.tsx:245 — QRScanner: exported fn missing return type annotation
  • src/components/Global/QRScanner/useQRScanner.ts:95 — useQRScanner: exported fn missing return type annotation

…and 1 more.

📈 Painscore deltas (top movers)

File Before After Δ
src/components/Global/QRScanner/utils.ts 0.0 5.0 +5.0
src/components/Global/QRScanner/index.tsx 10.7 11.2 +0.5

@github-actions

Copy link
Copy Markdown
Contributor

🧪 UI test report — ✅ all green

Suites

  • unit: 3369 ran, 0 failed, 0 skipped, 59.2s

📊 Coverage (unit)

metric %
statements 68.3%
branches 53.4%
functions 59.3%
lines 69.2%
⏱ 10 slowest test cases
time test
4.4s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › never places two stickers in heavy overlap (broad seed sweep)
1.0s src/hooks/query/__tests__/user.test.tsx › does NOT clear a token that rotated mid-request (stale 401 racing a fresh login)
0.7s src/utils/__tests__/demo-api.test.ts › isDemoMode() is false when not running under Capacitor
0.5s src/utils/__tests__/crisp.test.ts › settles, and hands back a usable plugin, against a real-shaped plugin proxy
0.5s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › every sticker stays within canvas at any count
0.5s src/utils/__tests__/sentry.utils.test.ts › defaults to the client budget under a browser global
0.4s src/utils/__tests__/crisp.test.ts › configures once across repeated support opens
0.4s src/utils/__tests__/crisp.test.ts › retries configuration on the next open after a failure
0.4s src/app/actions/__tests__/api-headers.test.ts › should include Content-Type in validateInviteCode
0.4s src/utils/__tests__/crisp.test.ts › resets the native session on logout once support has been opened
📍 Inline annotations are in the **Unit test report** check above. Coverage artifact: `coverage-unit`. Generated by `.github/workflows/tests.yml`.

@jjramirezn
jjramirezn merged commit 01ac0d4 into dev Aug 20, 2026
17 of 18 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.

5 participants