Skip to content

fix: recover peanut.me technical SEO on main - #2725

Draft
0xkkonrad wants to merge 13 commits into
mainfrom
hotfix/seo-technical-prod
Draft

fix: recover peanut.me technical SEO on main#2725
0xkkonrad wants to merge 13 commits into
mainfrom
hotfix/seo-technical-prod

Conversation

@0xkkonrad

@0xkkonrad 0xkkonrad commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Summary

Draft status: this evidence pins reviewed base 500109ea. main has since advanced to 759db4d1 and now points src/content at 3c2d4cf. After the paired API deploy, this branch must be refreshed onto that main and the exact-head build/runtime matrix rerun before human review.

Task

TASK-21499 — SEO fixes peanut.me

Paired API gate

This UI PR must not deploy until peanut-api-ts #1359 has merged, fully deployed, and rejected both content and stories case-insensitively in production. Both names were unclaimed in the read-only preflight. If the API needs rollback, roll this UI back first. API #1359 replaces stale-base draft #1355 without rewriting history.

Provenance and scope

  • Base: main at 500109ea49762cb38ea7492685125706d77b76ac.
  • Source: all 11 commits from seo: technical fix bundle — robots, canonicals, receive-from, noindex catch-all, real 404s, redirects, sitemap lastmod #2685, replayed chronologically with -x -S.
  • Every replay commit has the same stable patch ID as its source; the only range-diff changes are the cherry-pick provenance lines and removal of AI co-author trailers.
  • Two signed extension commits add the named-bot fix, durable regression coverage, and the independent review's verifier fixes.
  • All 13 commits verify locally. The 24-path diff is the reviewed 14-path bundle plus ten explicit test/hardening paths.
  • Preserved from the reviewed base: both Notion press-kit redirects, removal of Team content types, package/lockfiles, and content gitlink 9101046215f3eb24c447176d3e99de883c0aacd6. Refresh must preserve and revalidate current main's newer 3c2d4cf content gitlink.

Behavioral decisions

  • Invalid recipient-shaped garbage now returns a real 404. A syntactically valid but nonexistent username still returns 200 with noindex,nofollow, because existence is resolved client-side.
  • Twitterbot remains unrestricted so user-shared request and receipt cards can unfurl. Googlebot can fetch /api/og but retains the full private/app disallow list.
  • es-ar remains routable through es-419 content fallback but is omitted from receive-from sitemap entries unless an exact locale file exists.
  • docs.peanut.to DNS/Vercel attachment and legacy peanut.to path preservation are external infrastructure work and are not claimed fixed by this code PR.

Local QA

  • Prettier check: pass.
  • next typegen + full TypeScript: pass.
  • Full Jest: 236 suites, 2,996 tests (2,993 passed, 3 skipped).
  • Focused SEO/verifier regression suite: 10 suites / 86 tests passed with no open handles.
  • pnpm validate-links: all 11 passes; 762 published files and 936 independently expected routable URLs.
  • ESLint ratchet: exact main and branch both have 0 errors / 64 warnings; all changed code has 0 errors.
  • Production-domain webpack build: pass in supported LOCAL_BUILD=true mode; TypeScript passed and 1,088 static pages generated. Full Sentry-wrapped compilation exceeded this shared host's memory and was terminated by earlyoom, so the Vercel preview build remains a hard gate.
  • Pinned sitemap audit: 742 unique URLs, 27 distinct lastmods, and 57 receive-from entries (19 each for en/es-419/pt-br; no es-ar fallback duplicates).

Preview / runtime QA

  • Exact-head Vercel deployment dpl_JCdGvVUm3rR6DsDa7aU5kCwDH9Rq is Ready at https://peanut-wallet-26mylgkfs-squirrellabs.vercel.app; GitHub deployment 5961963369 pins 4f531377a9ebde4733dec5696fbda4fad3e4bb4a.
  • Current production renders 9/19 English receive sources and soft-404s the other 10 as indexable homepage-canonical catch-all pages. The immutable preview renders all 19/19 with the expected title/H1; the exact local build passes all 19 × 4 routable locale variants (76/76) with the intended es-ar fallback canonicals.
  • Browser, Googlebot, and Twitterbot requests to dashed/nested/unknown-locale garbage finish as real 404s with noindex; valid username/address/ENS/request shapes remain 200 with noindex,nofollow and no canonical.
  • Redirect/query/max-hop checks pass for help, pricing, stories/content, retired locale aliases, and locale slash normalization. /relay/decide/ GET/POST is not redirected.
  • Preview correctly uses the non-production Disallow: / policy. The production robots branch is covered by parser-level crawler-group tests and must be rechecked on the real deployment.
  • Twitterbot request metadata passes on /qaalice001/7USDC. Request and receipt OG endpoints return visually correct 1200×630 PNGs; local and preview hashes match byte-for-byte.
  • Mobile 375×667 before/after captures verify recovered en, es-419, and pt-br pages, the true 404, and an unchanged valid-address payment state. The sanitized captures are embedded below from a signed, verified orphan asset commit.
  • Direct preview 404 paint is typically 0.98–1.36 s, comparable to current production at 1.27 s. Cold App Router navigation completed in 0.92–2.05 s and warm navigation in 0.20–0.31 s, without page errors or hydration mismatches.
  • Vercel rejects syntactically malformed raw request targets at ingress with HTTP/1.1 400 (or HTTP/2 stream rejection); syntactically valid /%25 reaches the app and returns 404. Direct local next start instead returns 500 for malformed raw targets, but that path does not ship behind Vercel. A global Proxy expansion was independently rejected because it cannot alter ingress behavior and would broaden middleware over the whole site.

Screenshots

Captured at 375×667 on 2026-08-18. The first three rows show the production 404 repaired in the exact-head preview. The last two rows are non-regression controls.

Check Production before Exact-head preview after
Australia receive page — English Production English Australia receive page returns 404 Preview English Australia receive page renders
Australia receive page — Spanish (Latin America) Production Spanish Australia receive page returns 404 Preview Spanish Australia receive page renders
Australia receive page — Portuguese (Brazil) Production Portuguese Australia receive page returns 404 Preview Portuguese Australia receive page renders
Invalid path remains on the 404 surface Production invalid path 404 Preview invalid path 404
Valid catch-all address remains on the payment surface Production valid catch-all payment page Preview valid catch-all payment page

Assets live on temporary branch pr-assets-2725; delete it after merge.

Paid charge-backed request/receipt E2E is explicitly partial: no sanctioned synthetic completed-P2P fixture exists. The exact completed-charge metadata branch has focused unit coverage and both unchanged renderers pass HTTP/visual/hash checks. No production charge was created and no customer receipt capability token was exposed.

Isolated E2E compatibility

  • Nutcracker preflight passed against an isolated testdb Postgres instance, exact UI/API worktrees, source API, and production-built UI.
  • Backend flow 10-user-profile-and-lookup passed its happy and auth-adversarial paths.
  • e2e-profile-variants passed 6/6, including a real /update-user call and exact DB assertion. Its screenshots are not counted as layout proof because the fresh fixture displayed the expected re-consent modal over every screen; the SEO visual proof uses the clean public preview captures above.
  • The isolated API/UI processes and database were stopped after QA.

Architectural review

No blocking smell or hotfix-specific refactor was found. This deliberately preserves client-side username existence lookup: syntactically valid unknown handles remain 200 but are noindex,nofollow with no canonical, while invalid shapes return real 404s. Removing the catch-all route-level loading boundary lets Next emit those 404s before streaming; the child payment/profile flows keep their own loaders. Existing cross-repo route ownership and the gray-matter generated_at type/runtime mismatch are contained follow-ups, not reasons to widen this production hotfix.

Release blockers

  • Paired API Peanut wallet dev #1359 exact-head CI/review, human merge, full deploy, and post-deploy username rejection proof.
  • Human code-owner approval and human merge.

Required GitHub checks, Vercel, exact-head CodeRabbit (No actionable comments were generated), independent review, runtime matrix, and visual review are green. The PR remains draft until the API-first and human-approval gates clear.

Out of scope / still open on the parent task

  • GSC/Ahrefs ownership and weekly reporting.
  • docs.peanut.to and peanut.to DNS/redirect infrastructure.
  • Brazil IOF/counsel copy remediation and content regeneration.
  • Brazil/Argentina content expansion, Spendnode outreach, and bot-wave mitigation ownership.

…m pages

Three independent SEO hygiene defects.

robots.ts — the Googlebot group declared only `allow: ['/api/og']` with no
disallow key. A crawler obeys the single most specific group that matches it,
so Googlebot was reading that group INSTEAD of `*` and treating every
auth-gated route (/home, /profile, /settings, /kyc, ...) as crawlable. Extract
the `*` disallow list into a shared DISALLOWED_PATHS const and apply it to the
Googlebot group too, mirroring how the AI-crawler group is built. The narrower
/api/og allow still wins over /api/ by longest-match, so OG images keep
working.

careers + lp/card — both call the metadata helper without `canonical`, so they
inherited the root layout's `alternates: { canonical: '/' }` and declared the
homepage as their canonical while sitting in the sitemap. Pass the real path,
matching the existing exchange/layout.tsx workaround.

corridors.ts — loadReceiveSources seeded from CORRIDORS.from and intersected
with the receive-from content tree. receive-from is authored independently of
corridors, so the intersection silently dropped 10 authored countries
(australia, india, kenya, malaysia, netherlands, pakistan, philippines,
saudi-arabia, singapore, united-arab-emirates) whose articles were live but
unreachable. Enumerate published receive-from content directly via the
existing listPublishedSlugs helper. RECEIVE_SOURCES goes 9 -> 19; colombia and
mexico stay out, correctly, as origins with no article.

corridors.test.ts was a checked-in regression guard from the May 2026 no-content
incident asserting RECEIVE_SOURCES subset-of CORRIDORS.from. That subset relation
was an artifact of how the list was built, not what protected us — the real
invariant is "every entry has a published article". Rewritten to assert that in
both directions: no entry without content (no 404s) and no published article
left out (no orphans). Verified the new guard fails against the old loader.

(cherry picked from commit 2648b5a)
…comments

Adversarial-review findings on this PR:
- scripts/verify-content.ts gateReceiveSources() was a stale mirror of the
  old corridor-intersection loader — Pass 11 (blocking CI check) silently
  stopped covering the 10 recovered receive-from slugs. Now enumerates
  published receive-from articles directly, matching RECEIVE_SOURCES.
- robots.ts: DISALLOWED_PATHS comment overclaimed ('every named crawler
  group'); reworded to what the file actually does.
- lp/card: canonical comment misattributed the inherited '/' to the root
  layout; it comes from lp/layout.tsx's deliberate alias policy.

(cherry picked from commit 936b012)
The [...recipient] catch-all renders an indexable "X on Peanut" shell for
ANY username-shaped string — existence is only checked client-side, so a
non-existent handle still returns HTTP 200 with full metadata. Google has
been indexing those as thin/soft-404 pages.

Set robots: { index: false, follow: false } on all four generateMetadata
return paths (reserved route, missing recipient, !couldBeRecipient, and the
full metadata object). No carve-outs: real profiles, ETH addresses, .eth ENS
names and request/receipt links are app surface, not search landing pages.

(cherry picked from commit 153c6ba)
Adversarial-review catch: every branch of this route inherits
alternates.canonical '/' from the root layout, so the noindex was
shipping on pages that canonicalize to the homepage - a combination
Google's canonicalization guidance warns can bleed the noindex into
the cluster head (the homepage itself, sitemap priority 1.0).
alternates: { canonical: null } suppresses the inherited value; the
noindex pages now carry no canonical at all.

(cherry picked from commit 69b3e5d)
The [...recipient] route's loading.tsx created a Suspense boundary at the
segment. Page() suspends on use(props.params), so React rendered the
fallback, flushed the shell with HTTP 200, and only then resolved
notFound() — which landed as a NEXT_HTTP_ERROR_FALLBACK;404 marker inside
an already-200 response body.

Removing the boundary makes the segment's suspension propagate to the
shell, so notFound() throws before headers flush and Next sets a real 404
status. The route's render is otherwise synchronous (params guard checks
only), and PaymentPage is a client component, so there is no server data
fetch left to cover with a fallback.

(cherry picked from commit 5cb458a)
…e trailing-slash dupes

Four routing defects, all in the redirect/reserved-route layer:

1. /pricing, /stories and /content are real [locale]/(marketing) pages, but
   the bare paths are 7 lowercase letters, so couldBeRecipient() accepted them
   and the [...recipient] catch-all served a payment-profile shell on HTTP 200.
   Reserved in DEDICATED_ROUTES + 301'd to /en/....

2. /help/delete-account (and every other help article at its bare path)
   soft-404'd the same way: only bare /help was redirected, never /help/:path*.
   That URL is the account-deletion link Google Play requires. Added
   /help/:path* and /stories/:path* so the newly-reserved prefixes keep
   resolving instead of hard-404ing.

3. skipTrailingSlashRedirect is on (it must stay — the PostHog /relay proxy is
   called with trailing slashes), so /en/help/ and /en/help both returned 200.
   Added one slash-stripping redirect scoped to the locale-prefixed tree only.

4. docs.peanut.to had no host rule; only docs.peanut.me did.

(cherry picked from commit 74ca6e5)
Adversarial-review blocker: :path* matches zero segments, so bare
locale homepages (/es-ar/, /es-419/, /pt-br/) compiled a 308 redirect
to themselves - an infinite loop, cached permanently by browsers.
Measured live on the preview; /en/ survived only because the /en -> /
rule sits earlier in the array. :path+ requires a segment, so bare
locale + slash falls through to its current 200 (matches production).
Verified with next's checkCustomRoutes + compiled-rule replay of the
loop paths and the intended redirect paths.

(cherry picked from commit f5034b2)
Every sitemap URL carried lastModified: BUILD_DATE, so each deploy told
crawlers all 709 pages had just changed. That is noise, and it costs the
signal on pages that genuinely did change.

Content-backed URLs now report the generated_at of the exact file that
serves them. 684 of 709 URLs get a real date across 21 distinct values;
the remaining 25 (hand-built pages and index pages with no single backing
file) keep BUILD_DATE as the fallback.

contentGeneratedAt() coerces the frontmatter value to a Date. Note the
type/runtime mismatch it guards: ContentFrontmatter declares generated_at
as a string, but gray-matter runs js-yaml, which parses unquoted YAML
timestamps into Date objects — so both shapes have to work.

The lookups read through the cache the has*Content() guards already
populate, so they add no file reads.

(cherry picked from commit 8ce2b76)
…itterbot exemption

Review findings 1+2 from #2676 (abalinda): the AI-crawler group's
6-entry list left claim links, receipts, QR, card-payment, invite and
withdraw crawlable by GPTBot/ClaudeBot/PerplexityBot while every other
crawler was blocked. They now share DISALLOWED_PATHS - AI engines
should read (and cite) content pages, not transactional surface.
Twitterbot's empty disallow is deliberate (card unfurls on shared
claim/payment links) and now documented as such.

(cherry picked from commit 8057851)
)

- loader + verify-content gate skip a future 'index' meta directory
  (same guard sitemap.ts already applies to other intents)
- test parses frontmatter with gray-matter instead of a regex, so YAML
  edge cases (published: False, trailing comments) agree with the loader
- test fails with an actionable message when the src/content submodule
  is not initialized, instead of a raw ENOENT

(cherry picked from commit 31bced6)
@vercel

vercel Bot commented Aug 18, 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 18, 2026 11:43am

Request Review

@coderabbitai

coderabbitai Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: c0685f7d-2939-491f-85e9-5999c5b33bd5

📥 Commits

Reviewing files that changed from the base of the PR and between 500109e and 4f53137.

📒 Files selected for processing (24)
  • next.config.js
  • redirects.json
  • scripts/__tests__/verify-content-frontmatter.test.ts
  • scripts/__tests__/verify-content-routes.test.ts
  • scripts/verify-content-frontmatter.ts
  • scripts/verify-content-routes.ts
  • scripts/verify-content.ts
  • src/__tests__/seo-redirects.test.ts
  • src/app/[...recipient]/loading.tsx
  • src/app/[...recipient]/page.test.ts
  • src/app/[...recipient]/page.tsx
  • src/app/__tests__/seo-metadata.test.ts
  • src/app/careers/page.tsx
  • src/app/lp/card/page.tsx
  • src/app/robots.test.ts
  • src/app/robots.ts
  • src/app/sitemap.test.ts
  • src/app/sitemap.ts
  • src/constants/__tests__/routes.test.ts
  • src/constants/routes.ts
  • src/data/seo/corridors.test.ts
  • src/data/seo/corridors.ts
  • src/lib/content.test.ts
  • src/lib/content.ts
💤 Files with no reviewable changes (1)
  • src/app/[...recipient]/loading.tsx

Included review availability: Your plan includes up to 1 review per rolling hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The PR adds SEO redirects, reserves marketing routes, centralizes content validation, updates crawler and page metadata, derives sitemap dates from frontmatter, and adds tests for these behaviors.

Changes

SEO and content integrity

Layer / File(s) Summary
Redirects and reserved routes
next.config.js, redirects.json, src/__tests__/seo-redirects.test.ts, src/constants/routes.ts, src/constants/__tests__/routes.test.ts
Adds help, marketing, host-specific, locale, and trailing-slash redirects. Reserves pricing, stories, and content from catch-all recipient routing.
Shared content and route validation
scripts/verify-content-frontmatter.ts, scripts/verify-content-routes.ts, scripts/verify-content.ts, src/data/seo/corridors.ts, scripts/__tests__/*, src/data/seo/corridors.test.ts
Centralizes frontmatter publication checks and locale redirect validation. Uses published receive-from content for route and source discovery.
Page metadata and crawler policies
src/app/[...recipient]/page.tsx, src/app/[...recipient]/page.test.ts, src/app/careers/page.tsx, src/app/lp/card/page.tsx, src/app/__tests__/seo-metadata.test.ts, src/app/robots.ts, src/app/robots.test.ts
Adds noindex metadata for invalid recipient routes, page-specific canonicals, shared crawler restrictions, and test coverage for robots policies.
Content dates and sitemap generation
src/lib/content.ts, src/lib/content.test.ts, src/app/sitemap.ts, src/app/sitemap.test.ts
Normalizes generated_at frontmatter dates and applies them to content-backed sitemap entries, with BUILD_DATE fallback behavior.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk: 🔴 Critical · up to 4f531

The SEO changes have passing validation, but this PR must not merge or deploy until the paired API rejects the reserved names in production and the required approvals are complete; otherwise the UI/API contract could remain exposed.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.95% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: restoring technical SEO fixes for peanut.me on the main branch.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch hotfix/seo-technical-prod

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.

@0xkkonrad

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@github-actions

Copy link
Copy Markdown
Contributor

Code-analysis diff

Painscore total: 7096.17 → 7096.21 (+0.04)
Findings: -1 net (+17 new, -18 resolved)

🆕 New findings (17)

  • critical complexity — src/lib/content.ts — CC 64, MI 61.39, SLOC 252
  • high complexity — src/app/[...recipient]/page.tsx — CC 49, MI 43.64, SLOC 145
  • high method-complexity — src/app/[...recipient]/page.tsx:30 — generateMetadata CC 42 SLOC 124
  • high complexity — src/app/sitemap.ts — CC 26, MI 43.05, SLOC 219
  • medium high-mdd — src/app/sitemap.ts:49 — generateSitemap: MDD 80.0 (uses across many lines from declarations)
  • medium high-mdd — src/app/[...recipient]/page.tsx:30 — generateMetadata: MDD 46.5 (uses across many lines from declarations)
  • medium structural-dup — app/[...recipient]/page.tsx:175 — 28 duplicate lines / 101 tokens with app/invite/page.tsx:58
  • medium method-complexity — src/app/sitemap.ts:49 — generateSitemap CC 21 SLOC 197
  • medium complexity — src/constants/routes.ts — CC 14, MI 61.4, SLOC 46
  • medium complexity — src/data/seo/corridors.ts — CC 12, MI 62.58, SLOC 48
  • medium complexity — src/app/robots.ts — CC 5, MI 61.67, SLOC 38
  • low high-dlt — src/app/[...recipient]/page.tsx:30 — generateMetadata: DLT 18 (calls 18 distinct functions — high context load)
  • low high-mdd — src/lib/content.ts:350 — listAllContent: MDD 11.3 (uses across many lines from declarations)
  • low missing-return-type — src/app/[...recipient]/page.tsx:30 — generateMetadata: exported fn missing return type annotation
  • low missing-return-type — src/app/[...recipient]/page.tsx:202 — Page: exported fn missing return type annotation
  • low missing-return-type — src/app/careers/page.tsx:14 — CareersPage: exported fn missing return type annotation
  • low missing-return-type — src/app/lp/card/page.tsx:18 — CardLPPage: exported fn missing return type annotation

✅ Resolved (18)

  • src/lib/content.ts — CC 58, MI 61.74, SLOC 239
  • src/app/[...recipient]/page.tsx — CC 49, MI 44.48, SLOC 135
  • src/app/[...recipient]/page.tsx:19 — generateMetadata CC 42 SLOC 121
  • src/app/sitemap.ts — CC 23, MI 37.57, SLOC 174
  • src/app/sitemap.ts:27 — generateSitemap: MDD 73.8 (uses across many lines from declarations)
  • src/app/[...recipient]/page.tsx:19 — generateMetadata: MDD 46.3 (uses across many lines from declarations)
  • app/[...recipient]/page.tsx:158 — 32 duplicate lines / 107 tokens with app/invite/page.tsx:54
  • src/app/sitemap.ts:27 — generateSitemap CC 21 SLOC 167
  • src/constants/routes.ts — CC 14, MI 61.44, SLOC 46
  • src/data/seo/corridors.ts — CC 14, MI 62.7, SLOC 53
  • src/app/robots.ts — CC 4, MI 60.85, SLOC 30
  • src/app/[...recipient]/page.tsx:19 — generateMetadata: DLT 18 (calls 18 distinct functions — high context load)
  • src/lib/content.ts:329 — listAllContent: MDD 11.3 (uses across many lines from declarations)
  • src/app/[...recipient]/loading.tsx:3 — Loading: exported fn missing return type annotation
  • src/app/[...recipient]/page.tsx:19 — generateMetadata: exported fn missing return type annotation
  • src/app/[...recipient]/page.tsx:189 — Page: exported fn missing return type annotation
  • src/app/careers/page.tsx:11 — CareersPage: exported fn missing return type annotation
  • src/app/lp/card/page.tsx:14 — CardLPPage: exported fn missing return type annotation

📈 Painscore deltas (top movers)

File Before After Δ
src/app/robots.ts 4.8 5.3 +0.6
src/app/sitemap.ts 14.1 11.8 -2.2

@github-actions

Copy link
Copy Markdown
Contributor

🧪 UI test report — ✅ all green

Suites

  • unit: 2996 ran, 0 failed, 0 skipped, 37.8s

📊 Coverage (unit)

metric %
statements 66.7%
branches 51.7%
functions 56.8%
lines 67.5%
⏱ 10 slowest test cases
time test
2.4s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › never places two stickers in heavy overlap (broad seed sweep)
0.8s 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/hooks/__tests__/useCrispTokenId.test.ts › retries then stays undefined when the endpoint keeps failing (no fallback token)
0.3s src/utils/__tests__/sentry.utils.test.ts › still lets a per-call timeoutMs win over the default
0.2s src/app/actions/__tests__/api-headers.test.ts › should include Content-Type in validateInviteCode
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 › authReady does not park — hydrates the plain token without an unlock
0.2s src/utils/__tests__/auth-token.test.ts › ignores the guarded marker and falls back to the plain token
📍 Inline annotations are in the **Unit test report** check above. Coverage artifact: `coverage-unit`. Generated by `.github/workflows/tests.yml`.

@0xkkonrad

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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.

1 participant