Skip to content

chore: back-merge dev → main (ships production OTA 1.6.3) - #3112

Closed
innolope-dev wants to merge 5 commits into
mainfrom
dev
Closed

innolope-dev wants to merge 5 commits into
mainfrom
dev

Conversation

@innolope-dev

Copy link
Copy Markdown
Collaborator

Back-merge dev → main. Merging this ships production OTA 1.6.3 — that is the point of it, not a side effect.

Why this is the ship

main is behind dev and is missing both halves of what unblocks the OTA:

on main today after this merge
/app App Links claim still present → surface check fails reverted → fingerprint matches v1.6.0
release-ota.yml is dispatch-only runs on every push to main, main only
no provenance guard refuses a ref that lags the newest native release
no per-platform floors floors resolved and written into the bundle

Once the trigger change is on main, the merge commit itself fires App Release OTA, which resolves 1.6.3 off the channel (1.6.2) and publishes it. No dispatch needed.

Every gate dry-run against this tree

floors            → android 1.6.0, ios 1.5.0   (channel floor 1.5.0)
capabilities      → v1.6.0: provisioning compiled on Android and iOS
native surface    → matches original v1.6.0 (48586271a56340ed)
provenance guard  → HEAD contains v1.6.0
version resolver  → 1.6.3
tag ota-1.6.3     → clear, no collision

The surface check is the step that failed the 18:18 and 20:52 runs. It passes here.

Order of operations

  1. Merge feat(ota): verify main releases before promotion and gate delivery per platform #3111 into dev (per-platform floors + the main-only trigger). This PR picks it up automatically.
  2. Merge this PR.
  3. App Release OTA fires on the merge commit and ships 1.6.3. Watch the run.

Merging this before #3111 lands still works — main would get the /app revert and the provenance guard, and 1.6.3 could be shipped by a manual dispatch on main. It just would not be automatic.

What 1.6.3 fixes for whom

  • Devices still on bundle 1.6.1 (no gate in their JS) take 1.6.3 freely and land on the candidate-floor logic. Healthy from then on.
  • Android on the 1.6.0 binary running 1.6.2: build 6 > 6 is false, so they were never blocked. They take 1.6.3 and stay correct.
  • Any binary older than 1.6.0 already running 1.6.2 cannot be reached over the air — proven, not assumed: Capgo only offers a bundle sorting above 1.6.2, which forces build ≥ 6, and that bundle's own gate refuses it against a build 5 binary. Android sees the store prompt and can self-rescue if a ≥1.6.0 build is in its Play track; iOS has no prompt (listing not live) and needs a TestFlight build.

Shipping this promptly is what stops that last group growing — every device that adopts 1.6.2 before 1.6.3 is published becomes unreachable.

innolope-dev and others added 5 commits September 10, 2026 21:23
Both resolvers produce a number that outranks the binary whether or not the
commit contains it: `ota` lands the next bundle inside the newest native
build's range, `native` returns latestBuild + 1. Neither asks about the code.
A bundle numbered above the binary is accepted by every device, so an OTA
published from a lagging ref is a silent downgrade of the JS that binary
shipped with — and a store build in that state is worse, since only another
store release undoes it.

On 2026-09-10 production bundle 1.6.1 was published from ff34896, the tip
of main, which did not contain the v1.6.0 release cut from dev the day
before. Every install on the 1.6.0 binary took it and ran older JS.

check-native-ota-surface already asserted this ancestry, but only as a
precondition of its fingerprint diff — in the deploy job, after a full
install and native build, reported as "v1.6.0 is not an ancestor of HEAD".
A lagging ref whose native surface happened to match would have passed it
outright. Both lanes now assert it before anything is built, and name the
back-merge as the remedy. The native lane tolerates the first release of a
new major, which has no earlier build to contain.

Also recorded in docs/NATIVE-RELEASE.md: that bundle shipped through a tag
push, and a push-triggered workflow runs the workflow file as it existed at
the pushed ref — so it ran the long-retired capgo-deploy.yml still present
at that commit, with no floor check and no surface check. Retiring a
workflow does not retire it at older commits, and the same applies to `v*`.
Production is stuck on bundle 1.6.1 — JS older than the 1.6.0 binary it runs
on — and every attempt to replace it fails the native-surface check. The whole
drift between `dev` and v1.6.0 is two lines: the `/app` and `/app/` App Links
paths d91ea7c added to AndroidManifest.xml.

An intent filter cannot reach a device over the air. Those two lines were
therefore inert on every shipped binary while blocking every bundle that could
have fixed the fleet, so they buy nothing where they are and cost everything.
Reverted byte-exact to v1.6.0 so the fingerprint matches and the OTA can ship;
a comment marking the revert would itself change the bytes and fail the check,
which is why the note lives in docs/NATIVE-RELEASE.md instead — at the top of
"Cutting a release", where whoever cuts the next one will read it.

iOS is untouched: apple-app-site-association still claims `/app` and
native-routes.ts still maps `/app/*` → `/app`, so only Android `/app` deep
links are missing, exactly as they already are on every install in the field.
Production is serving bundle 1.6.1 — JS older than the 1.6.0 binary running
it — and every replacement fails check-native-ota-surface. The whole native
drift between dev and v1.6.0 is d91ea7c's `/app` claim: two lines in
AndroidManifest.xml, three in the AASA.

v1.6.0 predates that commit, so no shipped binary and no shipped AASA ever
carried the claim, and an intent filter cannot be shipped over the air. The
manifest half did move the native fingerprint, which is what the surface check
compares against the binary an OTA targets — so those lines blocked every
bundle that could have fixed the fleet while delivering nothing to anyone.

Now that the client-side store-update gate has landed, the version this
unblocks matters as much as the unblocking. A native release would number its
bundle 1.7.0, which the gate reads as needing a newer binary on every 1.6.0
install — pinning the fleet behind a store update that only exists in
TestFlight and Play internal, invisibly on iOS where the listing is not live.
1.6.2 shares the binary's build number, so the gate passes it and the OTA lane
stays open.

Both platforms are reverted together. Dropping only the Android half would
manufacture exactly the drift app-links.test.ts pins, so the parity and shape
cases stay enforced and only the two `/app`-presence cases are `it.skip`, with
the reason and the restore condition inline. docs/NATIVE-RELEASE.md carries the
same note at the top of "Cutting a release", where whoever cuts the next one
will read it: restore the AASA entries, the manifest entries and the two cases
together, in that PR, never on dev alone.
fix(release): refuse to ship a ref that lags the newest native release
@innolope-dev innolope-dev self-assigned this Sep 10, 2026
@innolope-dev
innolope-dev deployed to content-publish September 10, 2026 22:00 — with GitHub Actions Active
@vercel

vercel Bot commented Sep 10, 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 10, 2026 10:00pm UTC

Request Review

@coderabbitai

coderabbitai Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

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.

@github-actions

Copy link
Copy Markdown
Contributor

Code-analysis diff

Painscore total: 8992.04 → 8992.04 (0)
Findings: 0 net (+0 new, -0 resolved)

@github-actions

Copy link
Copy Markdown
Contributor

🧪 UI test report — ✅ all green

Suites

  • ✅ unit: 7067 ran, 0 failed, 0 skipped, 2.2m

📊 Coverage (unit)

metric %
statements 77.1%
branches 63.5%
functions 71.1%
lines 78.1%
⏱ 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 › 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_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 › MANTECA_USER_NOT_PROVISIONED 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 › User KYC not approved fails fast with copy that names the real cause
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
3.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › Scan that recovers on the retry lands on the payment screen, not an error
📍 Inline annotations are in the **Unit test report** check above. Coverage artifact: `coverage-unit`. Generated by `.github/workflows/tests.yml`.

This branch was successfully deployed

3 active deployments
content-publish — f22a759e Deployed Sep 10, 2026 by innolope-dev via approve-and-merge #3618
Production — f22a759e Deployed Sep 10, 2026 by innolope-dev via deploy #12
Preview — f22a759e Deployed Sep 10, 2026 by vercel[bot]
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