Skip to content
Closed
Show file tree
Hide file tree
Changes from 131 commits
Commits
Show all changes
138 commits
Select commit Hold shift + click to select a range
34e3c2c
feat(support): surface live verification state to Crisp agents
innolope-dev Aug 18, 2026
216dab7
fix(sentry): match ignore patterns against every exception in a chain
innolope-dev Aug 18, 2026
c45f765
chore(native): retire the transport-migration probes
innolope-dev Aug 18, 2026
dac4777
fix(sentry): treat PasskeyError as already reported
innolope-dev Aug 18, 2026
8f5a9f5
fix(passkey): stop reporting a cancelled passkey prompt as an error
innolope-dev Aug 18, 2026
69c103c
fix(support): don't report an absent capability read-model as needs-i…
innolope-dev Aug 18, 2026
ce103db
Merge origin/dev: reconcile with the critical-flow exemption
innolope-dev Aug 18, 2026
b78b3f4
fix(native): accept Bridge ToS via the system browser on android
innolope-dev Aug 19, 2026
f05f0b1
fix: skip Browser.open when the effect was cleaned up mid-registration
innolope-dev Aug 19, 2026
2d27be7
fix(request): unblur the QR code as soon as a positive amount is entered
claude Aug 20, 2026
29b3b17
feat(pull-to-refresh): make a native refresh visibly land
claude Aug 20, 2026
6b30067
fix(profile): make back work after the exchange-rate widget CTA
claude Aug 20, 2026
0829ae8
chore: back-merge main into dev (2026-08-20, release + hotfixes)
Hugo0 Aug 20, 2026
3b7cc18
Merge branch 'claude/pull-refresh-visual-feedback-djj41t' into mobile…
claude Aug 20, 2026
134a221
feat(rewards): iOS-only cashback copy, referral surfaces restored
innolope-dev Aug 20, 2026
c242eaa
Merge pull request #2772 from peanutprotocol/chore/backmerge-main-int…
Hugo0 Aug 20, 2026
e9064bd
fix(rewards): drop useEffect import the invites page no longer uses
innolope-dev Aug 20, 2026
eade15f
Merge pull request #2773 from peanutprotocol/feat/cashback-ios-copy
innolope-dev Aug 20, 2026
f20e053
fix(mobile): nav active state, withdraw re-suspend, receipt rule, can…
claude Aug 20, 2026
d260a29
Merge branch 'claude/profile-exchange-rates-back-button-d3lnpy' into …
claude Aug 20, 2026
fa4b486
fix(native): paint the status-bar strip in the app background, not black
innolope-dev Aug 18, 2026
fbfca52
Merge pull request #2774 from peanutprotocol/mobile-app-fixes
innolope-dev Aug 20, 2026
8016c68
Merge pull request #2777 from peanutprotocol/claude/send-claim-verifi…
innolope-dev Aug 20, 2026
b846119
feat(badges): PEANUT_SHAPER asset + share line
abalinda Aug 21, 2026
0609f6c
Merge pull request #2752 from peanutprotocol/fix/native-bridge-tos-if…
abalinda Aug 21, 2026
20029ef
Merge pull request #2784 from peanutprotocol/badge/peanut-shaper
Hugo0 Aug 21, 2026
59e8736
feat: mirror the user's app locale to the BE for localized emails
abalinda Aug 21, 2026
356e1dc
fix: catch a rejected updateUserById so locale sync stays best-effort
abalinda Aug 21, 2026
78003c6
Merge pull request #2785 from peanutprotocol/feat/sync-app-locale
abalinda Aug 21, 2026
462a2dc
perf(landing): keep the wallet provider tree off the marketing site
innolope-dev Aug 22, 2026
d04557a
perf(landing): keep preloading the decorative fonts
innolope-dev Aug 22, 2026
6d7636c
fix(landing): position the hero mascot before paint, not on image load
innolope-dev Aug 22, 2026
2df653c
Revert the hero-mascot layout-effect fix; it did not work
innolope-dev Aug 22, 2026
0569071
perf(landing): drop the chain registry, framer-motion's rAF loop and …
innolope-dev Aug 22, 2026
6c8191b
Revert the lazy Sentry SDK split; it cost more bytes than it saved
innolope-dev Aug 22, 2026
19ba687
perf(sentry): tree-shake the unused Replay code out of the client SDK
innolope-dev Aug 22, 2026
bcc0490
Revert "perf(sentry): tree-shake the unused Replay code out of the cl…
innolope-dev Aug 22, 2026
ff50eaf
perf(landing): load Sentry only where it's wanted, drop framer-motion…
innolope-dev Aug 22, 2026
b1c98e7
fix(history): respect hidden-balance preference on Activity
innolope-dev Aug 22, 2026
7b2ccc4
perf(i18n): ship the marketing site only the copy it renders
innolope-dev Aug 22, 2026
32bf6d8
test(history): name mocked TransactionCard for react/display-name
innolope-dev Aug 22, 2026
d7a1c4b
Merge pull request #2792 from peanutprotocol/fix/history-respect-hidd…
innolope-dev Aug 22, 2026
97da923
fix: block account deletion while the wallet still holds funds
innolope-dev Aug 22, 2026
a187dc4
Merge pull request #2739 from peanutprotocol/fix/passkey-cancel-unhan…
innolope-dev Aug 22, 2026
ee70497
fix(capgo): give every OTA upload a unique bundle version
innolope-dev Aug 22, 2026
0c64648
perf(landing): keep the chain/token catalogs and app utils off the ma…
innolope-dev Aug 22, 2026
b3958ed
test: cover the balance refusal in the requestDeletion service test
innolope-dev Aug 22, 2026
41407fc
refactor: drive the delete modal from one descriptor per step
innolope-dev Aug 22, 2026
d60d726
Merge pull request #2795 from peanutprotocol/fix/capgo-ota-bundle-ver…
innolope-dev Aug 22, 2026
78ee69c
perf(sentry): stop bundling the SDK into every page for a native-only…
innolope-dev Aug 22, 2026
d2560f3
perf(landing): stop prefetching app routes from the marketing page
innolope-dev Aug 22, 2026
676b67d
perf(landing): render the word strips on the server with a CSS marquee
innolope-dev Aug 22, 2026
dfb7af4
perf(landing): stop mounting the app's state layer on the marketing site
innolope-dev Aug 22, 2026
8a71b6b
perf(landing): build the FAQ on the server instead of in the client c…
innolope-dev Aug 22, 2026
496b048
Revert the CSS marquee; it bought bytes, not score
innolope-dev Aug 22, 2026
6eaa47b
Merge remote-tracking branch 'origin/main' into chore/backmerge-main-…
jjramirezn Aug 24, 2026
f5dfbc5
Merge pull request #2803 from peanutprotocol/chore/backmerge-main-int…
jjramirezn Aug 24, 2026
e29f650
fix(lint): drop the type imports left behind by the catalog split
innolope-dev Aug 24, 2026
db48da7
ci: require human-authored commits on every PR
Hugo0 Aug 24, 2026
41e5539
ci: grant human-authors job pull-requests read (403 without it)
Hugo0 Aug 24, 2026
f49807c
ci(release): couple native releases to the production OTA lane
innolope-dev Aug 24, 2026
412973b
Merge origin/dev into perf/lp-marketing-bundle
innolope-dev Aug 24, 2026
8a54e2d
Merge pull request #2737 from peanutprotocol/chore/drop-native-migrat…
abalinda Aug 24, 2026
efe071b
fix(ens): match the peanut domain on a label boundary, after the root…
innolope-dev Aug 24, 2026
82b1319
merge dev: keep native camera-permission + configure wrapper, re-appl…
abalinda Aug 24, 2026
ba5cf65
Merge origin/dev again; drop native-canary as dev did
innolope-dev Aug 24, 2026
72e49b8
Merge pull request #2736 from peanutprotocol/feat/crisp-support-verif…
abalinda Aug 24, 2026
9d47eea
Merge pull request #2806 from peanutprotocol/ci/couple-native-ota-lanes
abalinda Aug 24, 2026
6d0aad4
Merge pull request #2791 from peanutprotocol/perf/lp-marketing-bundle
innolope-dev Aug 24, 2026
5fca94a
ci: derive the author allowlist from repo collaborators (no list to m…
Hugo0 Aug 24, 2026
a057216
ci: check author write-access per user (collaborator listing is parti…
Hugo0 Aug 24, 2026
ec6cfbf
Merge pull request #2805 from peanutprotocol/ci/human-authors-only
Hugo0 Aug 24, 2026
3801dce
fix(sentry): give stackless console events a stack and stop issue exp…
innolope-dev Aug 24, 2026
6aa258a
Merge pull request #2810 from peanutprotocol/fix/sentry-stackless-exc…
innolope-dev Aug 24, 2026
cc7be9c
Merge origin/dev: keep both third-party noise handlers
innolope-dev Aug 24, 2026
c17f7eb
fix(sentry): rescue actionable Capgo failures before the generic patt…
innolope-dev Aug 24, 2026
1681ad1
Merge pull request #2735 from peanutprotocol/fix/sentry-chained-error…
innolope-dev Aug 24, 2026
9278729
fix(send): stop over-promising account-less claims and surface the $5…
innolope-dev Aug 25, 2026
0574c9b
review: say crypto wallet, not wallet, in the link-card claim caveat
innolope-dev Aug 25, 2026
bfaf8ea
feat(status): public status page at /status
innolope-dev Aug 25, 2026
74bc46e
ci: stop double-running Tests on every PR commit
innolope-dev Aug 25, 2026
9b5512d
feat(status): say what the user lost, not what the provider said
innolope-dev Aug 25, 2026
c7b6ff0
fix(status): move Status into the site directory, dedupe the pricing …
innolope-dev Aug 25, 2026
6f783ad
Merge pull request #2817 from peanutprotocol/ci/limit-push-trigger
Hugo0 Aug 25, 2026
24f8c58
review: drop bank from the no-account promise, and single-source the …
innolope-dev Aug 25, 2026
1935708
feat(status): read the staging feed on preview deployments
innolope-dev Aug 25, 2026
a0ff6d4
feat(status): Peanut-style header, and fix a name collision in feed.test
innolope-dev Aug 25, 2026
1f2e4f2
feat(status): shared Hero, weighted donut, and hide the summary card
innolope-dev Aug 25, 2026
9b048b1
feat(status): full title, OG and hreflang metadata
innolope-dev Aug 25, 2026
6e3d817
feat(status): always read the production API
innolope-dev Aug 25, 2026
6a34851
Merge pull request #2815 from peanutprotocol/feat/status-page
innolope-dev Aug 25, 2026
e03ae55
fix(startup): survive a null or throwing localStorage
innolope-dev Aug 26, 2026
8cdb561
fix(qr-scanner): recover the camera when the app returns to foreground
innolope-dev Aug 26, 2026
0116296
Merge pull request #2822 from peanutprotocol/fix/task-21835-startup-s…
innolope-dev Aug 26, 2026
402ca25
fix(qr-scanner): rework resume recovery after review
innolope-dev Aug 26, 2026
ed2e6bd
fix(startup): keep a signal when locale resolution falls back
innolope-dev Aug 26, 2026
b722b8c
Merge pull request #2821 from peanutprotocol/fix/task-21862-qr-camera…
abalinda Aug 26, 2026
b0657f2
ci: resolve release versions as <major>.<build>.<ota> instead of typi…
innolope-dev Aug 26, 2026
094e2de
Merge pull request #2824 from peanutprotocol/fix/startup-storage-revi…
innolope-dev Aug 26, 2026
2487dce
ci: hold one mutex across a production release
innolope-dev Aug 26, 2026
0566de6
fix(kyc): launch Manteca RFIs via start-action instead of the generic…
innolope-dev Aug 22, 2026
2ace3fc
fix(kyc): pass nextActions to deriveProviderRejection so legacy respo…
innolope-dev Aug 22, 2026
7a087cd
fix(kyc): refresh an RFI token through start-action, not POST /users/…
innolope-dev Aug 24, 2026
46e0a2f
review(coderabbit): shallow-clone detection, idempotent uploads, doc …
innolope-dev Aug 26, 2026
d96b6d2
feat(native): make the deferred-link match rate measurable
innolope-dev Jul 29, 2026
c400222
perf(capgo): stop logging transient update failures at error level
innolope-dev Aug 18, 2026
1b6326a
review: reattach the one-shot restore doc to its function
innolope-dev Aug 26, 2026
4e3c927
review: escalate persistent OTA failures after 3 consecutive launches
innolope-dev Aug 26, 2026
edd3aef
Merge pull request #2812 from peanutprotocol/fix/task-21724-send-copy…
innolope-dev Aug 26, 2026
e4867a0
feat(native): count hand-off creation at the store bounce, giving the…
innolope-dev Aug 26, 2026
1c9f298
fix(native): route every link entry and exit through a path the app c…
innolope-dev Aug 26, 2026
86043c6
chore(types): drop the deleted /manteca/initiate-onboarding route fro…
innolope-dev Aug 26, 2026
d41834e
ci: stop re-judging authorship of commits already merged to main/dev
innolope-dev Aug 26, 2026
34fb99e
review: close-then-navigate with a bounded wait, RootRedirect dedupe,…
innolope-dev Aug 26, 2026
074d493
Merge pull request #2839 from peanutprotocol/ci/human-authors-landed-…
Hugo0 Aug 27, 2026
d862e0c
fix(native): stamp the launch url before the already-navigated check
innolope-dev Aug 27, 2026
06d5eaa
Merge pull request #2838 from peanutprotocol/fix/native-links-routing
innolope-dev Aug 27, 2026
9393040
fix(fx): stop the add-money and withdraw screens freezing on a rate f…
innolope-dev Aug 27, 2026
630a3a6
fix(i18n): regenerate the marketing catalogs for the moved rateUnavai…
innolope-dev Aug 27, 2026
233fbd5
fix(fx): keep the rate retry reachable in every blocked state
innolope-dev Aug 27, 2026
779b598
Merge pull request #2831 from peanutprotocol/feat/deferred-link-telem…
innolope-dev Aug 27, 2026
5c04c5e
Merge pull request #2825 from peanutprotocol/ci/version-scheme
innolope-dev Aug 27, 2026
9177c38
Merge pull request #2794 from peanutprotocol/fix/block-delete-with-ba…
innolope-dev Aug 27, 2026
6659763
Merge pull request #2843 from peanutprotocol/fix/fx-rate-failure-free…
innolope-dev Aug 27, 2026
6902e35
Merge pull request #2832 from peanutprotocol/perf/capgo-transient-log…
innolope-dev Aug 27, 2026
cefb535
Merge pull request #2827 from peanutprotocol/fix/kyc-additional-docs-…
innolope-dev Aug 27, 2026
c245291
fix(native): stop the Android splash handoff crashing on a resumed la…
innolope-dev Aug 27, 2026
cf2343e
fix(claim): stop rendering a failed optimistic claim as a success
innolope-dev Aug 27, 2026
ebe2b0d
review: register the resume listener before reading app state
innolope-dev Aug 27, 2026
1d780b3
i18n(limits): stage copy for the Manteca cap-nudge and its under-revi…
innolope-dev Aug 27, 2026
c23b1e4
fix(claim): hold the optimistic claim in processing until it is confi…
innolope-dev Aug 27, 2026
3f97056
test: make the listener-cleanup double return a promise
innolope-dev Aug 27, 2026
b63692f
i18n(limits): add the es-AR voseo override for the cap-nudge prompt
innolope-dev Aug 27, 2026
04a4f1b
copy(i18n): use 'Recibir' for the Spanish request action
jjramirezn Aug 27, 2026
c16fbe9
Merge pull request #2857 from peanutprotocol/copy/es-request-recibir
jjramirezn Aug 27, 2026
09f8d68
Merge pull request #2852 from peanutprotocol/fix/manteca-cap-nudge-un…
innolope-dev Aug 27, 2026
cd5dafa
Merge pull request #2848 from peanutprotocol/fix/android-splash-resum…
innolope-dev Aug 27, 2026
0800132
Merge pull request #2850 from peanutprotocol/fix/task-21724-claim-pol…
innolope-dev Aug 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 116 additions & 13 deletions .github/workflows/android-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,22 @@ name: Android Release (Play)
# Plus the production NEXT_PUBLIC_* the static export bakes in (see the env step).

on:
workflow_call:
inputs:
versionName:
description: 'Resolved <major>.<build>.0 version to ship'
required: true
type: string
track:
description: 'Play track'
required: false
default: 'internal'
type: string
pushDebug:
description: 'Enable OneSignal verbose logging in this build'
required: false
default: false
type: boolean
push:
tags: ['v*']
workflow_dispatch:
Expand All @@ -29,7 +45,7 @@ on:
type: choice
options: [internal, alpha, beta, production]
versionName:
description: 'versionName override (optional; defaults to package.json)'
description: 'Break-glass <major>.<build>.0 version (required; there is no package.json fallback)'
required: false
type: string
pushDebug:
Expand All @@ -42,7 +58,16 @@ permissions:
contents: read

concurrency:
group: android-release-${{ github.ref }}
# A break-glass `v*` tag push runs outside release-native.yml's mutex, so it joins
# `production-release` here (which serialises iOS and Android for that path only —
# the price of not racing a concurrent OTA). When release-native.yml calls this
# workflow the parent already holds the mutex, and github.event_name is the
# caller's `workflow_dispatch`, so the called run keeps its own group instead of
# deadlocking against its parent. A direct dispatch of this workflow is
# indistinguishable from that, so it sits outside the mutex — deepest break-glass.
group: >-
${{ github.event_name == 'push' && 'production-release'
|| format('android-release-{0}', github.ref) }}
cancel-in-progress: false

jobs:
Expand Down Expand Up @@ -109,26 +134,68 @@ jobs:
NEXT_PUBLIC_SAFARI_WEB_ID=${{ vars.NEXT_PUBLIC_SAFARI_WEB_ID }}
NEXT_PUBLIC_ONESIGNAL_WEBHOOK=${{ vars.NEXT_PUBLIC_ONESIGNAL_WEBHOOK }}
EOF
if [ "${{ github.event.inputs.pushDebug }}" = "true" ]; then
if [ "${{ inputs.pushDebug }}" = "true" ]; then
echo "NEXT_PUBLIC_ONESIGNAL_DEBUG=true" >> .env.production.local
fi

- name: Resolve release versionName
id: version
env:
INPUT_VERSION: ${{ inputs.versionName || '' }}
run: |
# release-native.yml resolves the version and passes it here, so the
# input wins; a break-glass tag push falls back to the tag name minus
# its leading 'v' (v1.1.0 -> 1.1.0). Either way it is validated against
# the release scheme before the build starts. Resolved once, up front,
# so the OTA floor check and the bundle publish below see the exact
# version the binary ships with. Keep in lockstep with ios-release.yml.
VERSION_NAME="$INPUT_VERSION"
if [ -z "$VERSION_NAME" ] && [ "$GITHUB_REF_TYPE" = "tag" ]; then
VERSION_NAME="${GITHUB_REF_NAME#v}"
fi
if [ -z "$VERSION_NAME" ]; then
echo "::error::no versionName — call this from release-native.yml, or push a vX.Y.0 tag" >&2
exit 1
fi
node scripts/release-version.mjs validate "$VERSION_NAME" --kind native
echo "name=$VERSION_NAME" >> "$GITHUB_OUTPUT"
echo "Release versionName: $VERSION_NAME"

# A binary whose versionName is ahead of the newest production OTA bundle
# makes Capgo's disable_auto_update_under_native rule reject every bundle
# on those devices — OTA goes silently dead for the whole fleet while CI
# stays green (TASK-21793: 102 devices, a month of refused updates). Read
# the floor before the long build; the publish step after the store
# upload closes the gap this detects. Keep in lockstep with
# ios-release.yml.
- name: Check production OTA floor
id: ota_floor
env:
CAPGO_API_KEY: ${{ secrets.CAPGO_API_KEY }}
VERSION_NAME: ${{ steps.version.outputs.name }}
run: |
CURRENT="$(npx @capgo/cli@latest channel currentBundle production \
--apikey "$CAPGO_API_KEY" --quiet | grep -oE '[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?' | tail -n1 || true)"
if [ -z "$CURRENT" ]; then
echo "::error::cannot read the production channel's current bundle from Capgo — refusing to ship a binary that may strand the fleet's OTA path" >&2
exit 1
fi
NEEDS_OTA="$(node scripts/semver-newer.mjs "$VERSION_NAME" "$CURRENT")"
echo "needs_ota=$NEEDS_OTA" >> "$GITHUB_OUTPUT"
if [ "$NEEDS_OTA" = "true" ]; then
echo "versionName $VERSION_NAME is ahead of production bundle $CURRENT — a matching bundle ships after the store upload"
else
echo "versionName $VERSION_NAME is covered by production bundle $CURRENT"
fi

- name: Build signed AAB
env:
# native sentry sdk dsn — read by android/app/build.gradle into a
# manifest placeholder. same project dsn the js layer bakes into
# the static export above; no new secret needed.
SENTRY_DSN_ANDROID: ${{ secrets.NEXT_PUBLIC_SENTRY_DSN }}
ANDROID_VERSION_NAME: ${{ steps.version.outputs.name }}
run: |
# versionName: manual dispatch input wins; else the tag name minus
# its leading 'v' (v1.0.10 -> 1.0.10); else build.gradle's
# package.json fallback. Keeps the Play versionName in lockstep
# with the release tag without a manual package.json bump.
VERSION_NAME="${{ github.event.inputs.versionName }}"
if [ -z "$VERSION_NAME" ] && [ "$GITHUB_REF_TYPE" = "tag" ]; then
VERSION_NAME="${GITHUB_REF_NAME#v}"
fi
export ANDROID_VERSION_NAME="$VERSION_NAME"
# Monotonic (run_number always increases). +10000 clears legacy
# codes already on Play from earlier manual/local uploads (small
# console codes plus git-commit-count builds up to ~8600).
Expand All @@ -141,11 +208,47 @@ jobs:
serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }}
packageName: me.peanut.wallet
releaseFiles: android/app/build/outputs/bundle/release/app-release.aab
tracks: ${{ github.event.inputs.track || 'internal' }}
tracks: ${{ inputs.track || 'internal' }}
status: completed
# Staged production rollout: set status: inProgress + userFraction: 0.1,
# then promote in Play Console once crash/error rates look clean.

# The binary that just shipped is ahead of every production OTA bundle, so
# its devices would refuse all existing bundles. Publish the exact JS baked
# into this binary (native:release leaves it in out/) under the binary's own
# version — the lanes can no longer drift apart. A failure here is the floor
# guard firing: the job goes red instead of the fleet going silently dark.
# Upload flags in lockstep with capgo-deploy.yml; ios-release.yml publishes
# the same version from the same tag, which --version-exists-ok turns into a
# verified no-op.
- name: Publish matching production OTA bundle
if: steps.ota_floor.outputs.needs_ota == 'true'
env:
CAPGO_API_KEY: ${{ secrets.CAPGO_API_KEY }}
CAPGO_PRIVATE_KEY: ${{ secrets.CAPGO_PRIVATE_KEY }}
VERSION: ${{ steps.version.outputs.name }}
run: |
if [ ! -f out/index.html ]; then
echo "::error::out/ is missing its static export — cannot publish the matching OTA bundle, and without it every device on the $VERSION binary refuses OTA updates" >&2
exit 1
fi
npx @capgo/cli@latest bundle upload \
--channel production \
--apikey "$CAPGO_API_KEY" \
--key-data-v2 "$CAPGO_PRIVATE_KEY" \
--path ./out \
--bundle "$VERSION" \
--auto-min-update-version \
--version-exists-ok \
--comment "${GITHUB_SHA:0:7} — auto-published with the $VERSION native release"
CURRENT="$(npx @capgo/cli@latest channel currentBundle production \
--apikey "$CAPGO_API_KEY" --quiet | grep -oE '[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?' | tail -n1 || true)"
if [ "$CURRENT" != "$VERSION" ]; then
echo "::error::production channel serves '${CURRENT:-<none>}', expected $VERSION — devices on the $VERSION binary have no OTA path" >&2
exit 1
fi
echo "Production channel now serves $VERSION"

- name: Upload AAB as build artifact
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
Expand Down
Loading
Loading