Skip to content

chore: back-merge main into dev (2026-08-11) - #2665

Closed
abalinda wants to merge 21 commits into
devfrom
chore/backmerge-main-into-dev-20260811
Closed

chore: back-merge main into dev (2026-08-11)#2665
abalinda wants to merge 21 commits into
devfrom
chore/backmerge-main-into-dev-20260811

Conversation

@abalinda

Copy link
Copy Markdown
Contributor

Routine back-merge. main was 20 commits ahead of dev.

Carries #2658 (the /fx/card-markup consumer, which deletes both duplicate card-comparison modules) plus the hotfixes that shipped straight to main.

No conflicts. Verified locally: typecheck clean, 229 suites / 2925 tests green.

Hugo0 and others added 21 commits August 9, 2026 19:53
#2647 fixed this defect at useZeroDev.ts:222 but missed its sibling here, so
half the campaign failures are still undiagnosable in production.

Sentry's console integration serializes each console argument. Passing an
object holding an array of claims lands in the issue as the literal
"[object Object]" — so the two facts the warning exists to carry, which
campaign and why, were both unreadable. PEANUT-UI-SJC is a live example from
today; the registration-side sibling now reads "utm:pix=unknown" instead.

Pre-join the pairs into one string, exactly as useZeroDev.ts does. The message
stays constant so Sentry keeps grouping these into a single issue rather than
opening one per campaign combination.

This matters right now: three live marketing tags (brazil, pix,
faster-payments) currently resolve to no campaign, and the invite page was the
surface that could not say which.

Test pins the payload is a string, names both campaigns and reasons, and pins
the message constant. Verified non-vacuous: reverting to the object payload
fails it, restoring the join passes.
Ask our most active users for a 15-min founder call (TASK-21189).
The cohort lives in the PostHog flag user-interviews-invite (release
condition on username), never in code - list edits and the kill
switch are one click in PostHog and no usernames ship in the bundle.
The card leads the carousel for targeted users, opens the shared
cal.com event via the Capacitor-safe helper, and inherits the
standard 7-day X-dismissal. Temporary campaign: delete the card,
flag, and i18n keys when it ends.
…n-payload

fix(badges): make the invite-page campaign warning readable in Sentry
…rdown checklist

Applied from the automated review pass: (1) await openExternalUrl so a
native Browser.open failure surfaces in CarouselCTA's onClick catch
instead of a detached rejection; (2) the analytics comment no longer
claims flag exposures equal impressions (they fire per evaluation,
prod-only, dismissed users included) and points readers at cal.com
bookings + a $host filter; (3) the delete-together checklist now names
the dev/home-ctas preview entry so teardown removes all three copies.
The committed aleks-n-hugo/15min event 404s (CodeRabbit catch, verified).
Replaced with the hugo0+abalinda dynamic group link, which resolves and
books both hosts' mutual availability.
Aleks isn't a founder; only Hugo is. All four locales, the dev preview,
and the code comments now say team.
feat(home): flag-gated user-interview invite card in the carousel
The shared icon container is size-8, and the Tailwind preflight img
max-width clamps any larger logo back to 32px - so the size bump rides
the existing iconContainerClassName prop (tailwind-merge lets size-11
override size-8). The asset already animates: it is the animated webp,
which Next serves untouched (ANIM chunk verified through /_next/image).
ui: render the waving mascot at 44px (follow-up to #2654)
…end copies

The card-vs-Peanut markup was modeled twice in the frontend — once as a server
action, once copied into the merchant page with a header saying to delete it
when #2108 landed. #2108 landed. Both computed their own dolarapi call and
their own issuer fee, and neither could agree with the rate on screen.

The hook keeps its signature and its never-fails contract: a backend outage
falls back to the same static table as before, so a frontend deploy that
precedes the backend degrades to today's numbers instead of blanking a
surface.
…compute

Also splits the two failure kinds: a 404 is the backend proving there is no
comparison to publish, so the row is hidden rather than filled with the static
assumption, which exists for an unreachable backend.
Review found the distinction collapsing at four points, each publishing a
saving claim on evidence there is none:

- a well-formed zero markup threw, so the hook fell back to the static 9.13%
- an out-of-band locked-price recompute silently returned the market markup,
  still tagged live — the exact failure the recompute exists to prevent
- the merchant page and the post-card-spend nudge both turned the hook's null
  into the static table, striking through every menu price by 9.13%

fetchCardMarkup now returns null for 'the backend published no comparison' and
throws only when the response could not be obtained or trusted. Also bounds the
live observation age client-side, formats the CompareSavings date in UTC (a
hydration mismatch on statically generated pages for any viewer west of
Greenwich), rejects a negative percent instead of publishing it as a range, and
formats amounts in the page locale.
…0811-100504

content: publish latest to production (src/content → peanut-content@062cdba)
…dates

Both CodeRabbit findings are the same defect class as the last round — a
literal check that misses an equivalent representation and falls through to the
static claim.

The wire pattern admits "0.0" and "0.00", not just "0". Matching the text
sent those down the invalid path, so a backend saying 'no gap to show' produced
a 9.13% saving claim. The check now compares the parsed value.

new Date('2026-02-30') is 2 March, so a typo in a hand-authored MDX date would
publish a verification date that never happened. verifiedAt is now round-tripped
against an exact YYYY-MM-DD.
…0811-102159

content: publish latest to production (src/content → peanut-content@c708cdd)
…0811-103319

content: publish latest to production (src/content → peanut-content@5954b68)
feat(fx): consume the shared card-markup endpoint, delete both frontend copies
Carries the /fx/card-markup consumer (#2658) plus the hotfixes that went
straight to main.
@vercel

vercel Bot commented Aug 11, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
peanut-wallet Ready Ready Preview Aug 11, 2026 11:11am

Request Review

Copilot AI lite review requested due to automatic review settings August 11, 2026 11:04

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Aug 11, 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: 8c75b3cc-a8a6-4969-bb0a-dcd43e7237ba

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.

@github-actions

Copy link
Copy Markdown
Contributor

Code-analysis diff

Painscore total: 7157.18 → 7155.4 (-1.78)
Findings: +6 net (+267 new, -261 resolved)

🆕 New findings (267)

  • critical complexity — src/components/Invites/InvitesPage.tsx — CC 80, MI 55.27, SLOC 199
  • critical complexity — src/hooks/useHomeCarouselCTAs.tsx — CC 69, MI 57.11, SLOC 316
  • critical complexity — src/app/m/[slug]/MerchantLandingPage.tsx — CC 51, MI 59.94, SLOC 184
  • high structural-dup — types/api.generated.ts:10266 — 83 duplicate lines / 388 tokens with types/api.generated.ts:10854
  • high structural-dup — types/api.generated.ts:10276 — 74 duplicate lines / 348 tokens with types/api.generated.ts:10864
  • high structural-dup — types/api.generated.ts:5041 — 64 duplicate lines / 198 tokens with types/api.generated.ts:5143
  • high structural-dup — types/api.generated.ts:5044 — 61 duplicate lines / 188 tokens with types/api.generated.ts:5248
  • high hotspot — src/constants/analytics.consts.ts — 43 commits, +352/-13 lines since 6 months ago
  • high hotspot — src/hooks/useHomeCarouselCTAs.tsx — 38 commits, +451/-270 lines since 6 months ago
  • high method-complexity — src/hooks/useHomeCarouselCTAs.tsx:147 — CC 30 SLOC 143
  • high complexity — src/constants/analytics.consts.ts — CC 1, MI 32.24, SLOC 184
  • medium high-mdd — src/hooks/useHomeCarouselCTAs.tsx:82 — useHomeCarouselCTAs: MDD 170.8 (uses across many lines from declarations)
  • medium high-mdd — src/hooks/useHomeCarouselCTAs.tsx:147 — : MDD 122.8 (uses across many lines from declarations)
  • medium high-mdd — src/components/Invites/InvitesPage.tsx:37 — InvitePageContent: MDD 101.5 (uses across many lines from declarations)
  • medium structural-dup — types/api.generated.ts:8149 — 49 duplicate lines / 149 tokens with types/api.generated.ts:8573
  • medium high-dlt — src/hooks/useHomeCarouselCTAs.tsx:82 — useHomeCarouselCTAs: DLT 48 (calls 48 distinct functions — high context load)
  • medium structural-dup — types/api.generated.ts:7 — 48 duplicate lines / 185 tokens with types/api.generated.ts:10994
  • medium high-dlt — src/components/Invites/InvitesPage.tsx:37 — InvitePageContent: DLT 45 (calls 45 distinct functions — high context load)
  • medium structural-dup — types/api.generated.ts:8652 — 43 duplicate lines / 123 tokens with types/api.generated.ts:8933
  • medium structural-dup — types/api.generated.ts:8652 — 42 duplicate lines / 125 tokens with types/api.generated.ts:9011

…and 247 more.

✅ Resolved (261)

  • src/components/Invites/InvitesPage.tsx — CC 79, MI 54.61, SLOC 196
  • src/hooks/useHomeCarouselCTAs.tsx — CC 67, MI 57.4, SLOC 295
  • src/app/m/[slug]/MerchantLandingPage.tsx — CC 53, MI 60.34, SLOC 196
  • types/api.generated.ts:10166 — 83 duplicate lines / 388 tokens with types/api.generated.ts:10754
  • types/api.generated.ts:10176 — 74 duplicate lines / 348 tokens with types/api.generated.ts:10764
  • types/api.generated.ts:5046 — 61 duplicate lines / 188 tokens with types/api.generated.ts:5148
  • src/constants/analytics.consts.ts — 41 commits, +345/-12 lines since 6 months ago
  • src/hooks/useHomeCarouselCTAs.tsx — 34 commits, +405/-262 lines since 6 months ago
  • src/constants/analytics.consts.ts — CC 1, MI 32.3, SLOC 183
  • src/hooks/useHomeCarouselCTAs.tsx:78 — useHomeCarouselCTAs: MDD 150.9 (uses across many lines from declarations)
  • src/hooks/useHomeCarouselCTAs.tsx:137 — : MDD 106.1 (uses across many lines from declarations)
  • src/components/Invites/InvitesPage.tsx:37 — InvitePageContent: MDD 98.7 (uses across many lines from declarations)
  • types/api.generated.ts:8049 — 49 duplicate lines / 149 tokens with types/api.generated.ts:8473
  • types/api.generated.ts:7 — 48 duplicate lines / 185 tokens with types/api.generated.ts:10894
  • src/components/Invites/InvitesPage.tsx:37 — InvitePageContent: DLT 44 (calls 44 distinct functions — high context load)
  • src/hooks/useHomeCarouselCTAs.tsx:78 — useHomeCarouselCTAs: DLT 44 (calls 44 distinct functions — high context load)
  • types/api.generated.ts:8552 — 43 duplicate lines / 123 tokens with types/api.generated.ts:8833
  • types/api.generated.ts:8552 — 42 duplicate lines / 125 tokens with types/api.generated.ts:8911
  • types/api.generated.ts:7471 — 40 duplicate lines / 116 tokens with types/api.generated.ts:8049
  • types/api.generated.ts:8049 — 40 duplicate lines / 118 tokens with types/api.generated.ts:8887

…and 241 more.

📈 Painscore deltas (top movers)

File Before After Δ
src/components/Marketing/mdx/CompareSavings.tsx 0.0 8.7 +8.7
src/hooks/useCardMarkupRate.ts 3.9 5.5 +1.7
src/utils/fx.utils.ts 2.3 3.1 +0.8
src/hooks/useHomeCarouselCTAs.tsx 13.7 14.5 +0.8
src/app/m/[slug]/card-comparison.ts 7.5 0.0 -7.5
src/app/actions/card-comparison.ts 8.3 0.0 -8.3

@github-actions

Copy link
Copy Markdown
Contributor

🧪 UI test report — ✅ all green

Suites

  • unit: 2928 ran, 0 failed, 0 skipped, 33.6s

📊 Coverage (unit)

metric %
statements 66.3%
branches 51.2%
functions 56.4%
lines 67.0%
⏱ 10 slowest test cases
time test
2.2s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › never places two stickers in heavy overlap (broad seed sweep)
0.5s src/utils/__tests__/demo-api.test.ts › isDemoMode() is false when not running under Capacitor
0.3s src/utils/__tests__/sentry.utils.test.ts › defaults to the client budget under a browser global
0.3s src/app/(mobile-ui)/withdraw/__tests__/withdraw-states.test.tsx › Bank withdrawal keeps the $1 minimum for sub-$1 amounts
0.3s src/utils/__tests__/sentry.utils.test.ts › still lets a per-call timeoutMs win over the default
0.2s src/utils/__tests__/auth-token.test.ts › authReady does not park — hydrates the plain token without an unlock
0.2s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › every sticker stays within canvas at any count
0.2s src/utils/__tests__/auth-token.test.ts › is none — never guarded — when only the guarded marker is present
0.2s src/app/actions/__tests__/api-headers.test.ts › should include Content-Type in validateInviteCode
0.2s src/app/actions/__tests__/api-headers-extended.test.ts › should not include apiKey in validateInviteCode body
📍 Inline annotations are in the **Unit test report** check above. Coverage artifact: `coverage-unit`. Generated by `.github/workflows/tests.yml`.

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@abalinda

Copy link
Copy Markdown
Contributor Author

Superseded by #2668, which is CLEAN and ready to merge.

This PR was never failing CI — every check on its head SHA was green (ci-success included), dev requires 0 approvals, and there were no unresolved review threads. GitHub still reported BLOCKED.

The cause: this branch carries three unsigned commits from the content-publish bot (Update content submodule to latest main at 10:05 / 10:22 / 10:33Z). The All branches ruleset (6457257) requires signed commits on every ref and has no bypass actors. The ruleset audit log shows the bot reaches main by bypassing that rule — a back-merge PR has no such bypass, so those commits cannot flow into dev through an ordinary merge.

#2668 is the same back-merge collapsed into one signed commit, off the current dev tip. Proof the diagnosis is right: identical content, mergeStateStatus: CLEAN.

Follow-up worth filing: the content-publish workflow should sign its commits (or author them through the GitHub API, which signs automatically). Until then every main→dev back-merge will hit this, and each one has to be squashed — which costs the merge ancestry and makes the next back-merge re-present the same delta.

@abalinda abalinda closed this Aug 11, 2026
auto-merge was automatically disabled August 11, 2026 13:48

Pull request was closed

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