Skip to content

release: cut v2.0.4 "Harbor" (Android release candidate — host-side RC scaffolding on the byte-identical v2.0.3 core) - #241

Merged
doublegate merged 4 commits into
mainfrom
feat/v2.0.4-android-rc
Jul 9, 2026
Merged

doublegate merged 4 commits into
mainfrom
feat/v2.0.4-android-rc

Conversation

@doublegate

Copy link
Copy Markdown
Owner

v2.0.4 "Harbor" — Android release candidate (host-side RC scaffolding)

Stacked on #240 (v2.0.3). Base is release/v2.0.3; GitHub will auto-retarget this to main once #240 merges. The diff below is v2.0.4-only.

The fourth release of the v2.0.x "Harbor" train — the Android release candidate. The emulation core is byte-identical to v2.0.3 (AccuracyCoin still 141/141, no crates/rustynes-{cpu,ppu,apu,mappers,core} changes); this is a host/Android-only RC cut that stages everything a maintainer needs to push the RC to a Play Console testing track.

Host-side RC work (this PR)

  • Version: workspace 2.0.3 → 2.0.4; Android versionCode 20003 → 20004, versionName "2.0.4".
  • Release signing: buildTypes.release now always resolves a signing config — the upload keystore (keystore.properties / RUSTYNES_UPLOAD_* env) when present, else a graceful fallback to debug signing so CI + local builds without the secret still assemble. Keystore files stay gitignored (maintainer secret).
  • Crash/ANR hardening: new debug-only DebugStrictMode (thread + VM policy, log-only, BuildConfig.DEBUG-guarded, release-stripped), installed first in MainActivity.onCreate. The existing opt-in CrashReporter already chains the uncaught-exception handler (unchanged).
  • R8: proguard keep-set audited for the RC (complete; nothing loosened).
  • Fastlane metadata: fastlane/metadata/android/{en-US,es-ES}/ title / short / full description + changelogs/20004.txt, staged as version-controlled Play listing (maintainer uploads).
  • Docs: CHANGELOG [2.0.4], STATUS (single source of truth — new current-release block), ROADMAP, .github/release-notes/v2.0.4.md, to-dos/plans/v2.0.4-android-rc-plan.md, finalization-plan v2.0.4 bullet.

Flagged for the maintainer / on-device (v2.0.9)

Play Console AAB upload; signing with the real keystore; internal/closed testing track; crash-free-rate + ANR gate on real hardware; live monetization runtime; on-device Baseline Profile regen; and the deferred per-feature feature_enabled gate migration (existing LicenseManager freemium gates left as-is — a product decision needing runtime validation).

Verification

cargo check --workspace exit 0 (lock diff version-only); cargo fmt --all --check clean; ./gradlew :app:compileFossDebugKotlin :app:compilePlayDebugKotlin BUILD SUCCESSFUL (both flavors; signing-fallback validated at configuration time); pre-commit run markdownlint passed.

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings July 9, 2026 01:02

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.

Pull request overview

Cuts the v2.0.4 “Harbor” Android release-candidate by bumping workspace/Android versions and adding host-side scaffolding (release signing fallback + debug-only StrictMode), plus release documentation and Play Console fastlane metadata. No emulation-core changes are included (intended byte-identity with v2.0.3).

Changes:

  • Bump workspace version to 2.0.4 (Cargo.toml + Cargo.lock) and Android versionCode/versionName to 20004/2.0.4.
  • Android RC scaffolding: release signing config now always resolves (upload keystore when present, else debug signing) and install debug-only StrictMode early in MainActivity.
  • Add version-controlled fastlane Play listing metadata (en-US/es-ES) and update release docs/status/roadmaps.

Reviewed changes

Copilot reviewed 19 out of 20 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
to-dos/ROADMAP.md Updates “Current release” status block to v2.0.4 and shifts v2.0.3 to preceding.
to-dos/plans/v2.0.x-mobile-finalization-plan.md Reconciles v2.0.4 plan bullet to reflect host-side RC scaffolding vs on-device maintainer steps.
to-dos/plans/v2.0.4-android-rc-plan.md Adds a dedicated v2.0.4 Android RC sprint plan doc.
fastlane/metadata/android/es-ES/title.txt Adds Spanish Play title.
fastlane/metadata/android/es-ES/short_description.txt Adds Spanish short description.
fastlane/metadata/android/es-ES/full_description.txt Adds Spanish full description.
fastlane/metadata/android/es-ES/changelogs/20004.txt Adds Spanish changelog for versionCode 20004.
fastlane/metadata/android/en-US/title.txt Adds English Play title.
fastlane/metadata/android/en-US/short_description.txt Adds English short description.
fastlane/metadata/android/en-US/full_description.txt Adds English full description.
fastlane/metadata/android/en-US/changelogs/20004.txt Adds English changelog for versionCode 20004.
docs/STATUS.md Updates current release block to v2.0.4 and records RC scope.
CHANGELOG.md Adds v2.0.4 release notes section (host/Android-only RC scaffolding).
Cargo.toml Workspace version bump 2.0.3 → 2.0.4.
Cargo.lock Propagates workspace crate versions to 2.0.4.
android/app/src/main/java/com/doublegate/rustynes/MainActivity.kt Installs DebugStrictMode at the start of onCreate.
android/app/src/main/java/com/doublegate/rustynes/Crash.kt Adds DebugStrictMode implementation (debug-only StrictMode policies).
android/app/proguard-rules.pro Records the v2.0.4 RC keep-set audit outcome in comments.
android/app/build.gradle.kts Bumps Android versionCode/versionName; adds release signing-config fallback logic.
.github/release-notes/v2.0.4.md Adds GitHub Release notes for v2.0.4.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread fastlane/metadata/android/es-ES/short_description.txt Outdated
Comment thread fastlane/metadata/android/es-ES/changelogs/20004.txt Outdated
Comment thread fastlane/metadata/android/es-ES/changelogs/20004.txt Outdated
Comment thread fastlane/metadata/android/es-ES/full_description.txt Outdated
Comment thread fastlane/metadata/android/es-ES/full_description.txt Outdated
Comment thread fastlane/metadata/android/es-ES/full_description.txt Outdated
Comment thread fastlane/metadata/android/es-ES/full_description.txt Outdated
Comment thread fastlane/metadata/android/es-ES/full_description.txt Outdated
Comment thread fastlane/metadata/android/es-ES/full_description.txt Outdated
doublegate added a commit that referenced this pull request Jul 9, 2026
…copilot #241)

Resolves all 9 copilot review threads on PR #241: the es-ES fastlane store copy
(short_description, full_description, changelogs/20004) was written without Spanish
diacritics, so the customer-facing Play listing read as misspelled Spanish. Restored
the correct orthography throughout — precisión, núcleo, emulación, rápido, versión,
público, código, características, batería, línea, teléfonos, diseños, etc. — and
tidied two phrasings to the idiomatic form ("de ciclo exacto", "versión candidata").
Play field limits still respected (short <= 80, full <= 4000, changelog <= 500).
No functional/code change; en-US copy unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

Gemini encountered an error creating the review. You can try again by commenting /gemini review.

@doublegate doublegate self-assigned this Jul 9, 2026
Base automatically changed from release/v2.0.3 to main July 9, 2026 01:44
doublegate and others added 3 commits July 8, 2026 21:55
…C scaffolding on the byte-identical v2.0.3 core)

The fourth release of the v2.0.x "Harbor" mobile-finalization train, and the
Android release-candidate milestone. This is a HOST / ANDROID-ONLY cut: the
cycle-accurate emulation core is unchanged and byte-identical to v2.0.3 —
AccuracyCoin still 141/141 (100.00%), nestest 0-diff, the deterministic
`#![no_std]` chip stack untouched — so no accuracy / save-state / determinism
number moves. What lands is the release-candidate scaffolding a maintainer needs
to stage the Android app onto a Play Console testing track, with every on-device
step explicitly flagged for the maintainer / v2.0.9. The `foss`
(F-Droid / GitHub-sideload) flavor stays behaviour-identical.

Version bump
------------
Workspace `2.0.3 -> 2.0.4` (cascades version-only through Cargo.lock — 18 crate
entries, no dependency graph change). Android `versionCode 20003 -> 20004`,
`versionName "2.0.3" -> "2.0.4"`: the monotonic versionCode is the Play upload
ordinal for the internal/closed testing (RC) track.

Release signing wired with a graceful debug-signing fallback
------------------------------------------------------------
The `release` build type now ALWAYS resolves a signing config instead of leaving
the artifact unsigned when the upload keystore is absent. When the maintainer
secret is present (a gitignored `keystore.properties` file or the
`RUSTYNES_UPLOAD_*` env vars, CI-injected, never committed) the release is signed
with the real upload key so the produced AAB is store-uploadable; when ABSENT (a
keyless CI or local RC build) it falls back to the debug signing config so
`assemble{Foss,Play}Release` still yields an INSTALLABLE artifact for smoke
testing. Debug-signed is never shippable (Play App Signing rejects the debug key),
so this fallback only ever produces a locally-installable RC artifact — the real
keystore path is what a store upload uses. `keystore.properties` / `*.jks` /
`*.keystore` were already gitignored since v2.0.3 (verified, no change needed).
The signing block + `buildTypes.release` evaluate at Gradle configuration time,
which the green foss/play compile exercised end-to-end.

Crash / ANR hardening (DebugStrictMode)
---------------------------------------
A debug-only StrictMode installer (`DebugStrictMode` in `Crash.kt`, shared
`src/main` across foss+play) surfaces the two most common ANR / jank sources
during development: thread policy (main-thread disk / network I/O + custom slow
calls — the single most dangerous ANR class for an emulator that must never block
the UI thread on the native bridge) and VM policy (leaked `Closeable`s / SQLite
cursors / registered receivers, relevant to the wgpu SurfaceView + UniFFI object
graph spanning the JNI boundary). It is log-only (never penaltyDeath, so a false
positive can never crash a dev build) and guarded on `BuildConfig.DEBUG`, so a
release / RC / store build constructs no policy and carries zero runtime cost or
behavioural change. Installed as the first statement of `MainActivity.onCreate`
so launch-path violations are caught too. Complements the existing opt-in,
privacy-first `CrashReporter` (uncaught-exception handler chaining to the previous
default handler; unchanged). The authoritative crash-free-rate + ANR measurement
is an on-device gate (real hardware / Play vitals) and is a maintainer / v2.0.9
step; this installer only makes the host build fail loud so those numbers start
clean.

R8 / ProGuard final hardening review
------------------------------------
Re-audited the release keep set for the RC and confirmed it COMPLETE — the UniFFI
(rustynes_mobile + monetization) + JNA surface, the NativeRenderer JNI seam, and
the play-flavor Google / AppLovin / RevenueCat SDKs (added in v2.0.3) are all
covered. The v2.0.4 host additions need no new keeps by construction:
DebugStrictMode is stripped from release (BuildConfig.DEBUG-guarded), CrashReporter
is reached by a direct Kotlin call, and the ResumeWidgetReceiver / ResumeTileService
are manifest-declared components R8 keeps automatically. No existing keep was
loosened; the audit outcome is recorded in `proguard-rules.pro`.

Fastlane / Play Console listing metadata (version-controlled)
-------------------------------------------------------------
A `fastlane/metadata/android/{en-US,es-ES}/` tree — title / short + full
description / `changelogs/20004.txt` — stages the store listing as tracked source
a maintainer uploads via fastlane / the Play Console (no upload is performed).
Copy is accurate to the app (a cycle-accurate NES emulator, bring-your-own-ROMs,
open source) and flags the RC / testing-track status; fields respect Play's limits
(title <= 30, short <= 80, full <= 4000, changelog <= 500).

Docs
----
CHANGELOG.md `[2.0.4]` (Added/Changed; fresh empty `[Unreleased]`); docs/STATUS.md
gains a v2.0.4 current-release block (v2.0.3 demoted to "The preceding release");
to-dos/ROADMAP.md status updated; `.github/release-notes/v2.0.4.md` authored;
to-dos/plans/v2.0.4-android-rc-plan.md authored (host-done vs maintainer/on-device
split + plan reconciliation: ADR 0030 "Option 1" already landed in v2.0.3, so
v2.0.4 is the pure Android RC closeout); the mobile-finalization plan's v2.0.4
bullet marks the host-doable items done and the on-device ones maintainer/v2.0.9.

Deferred (maintainer / on-device, per the plan)
-----------------------------------------------
The actual Play Console listing upload, signing with the real keystore, the
internal/closed testing track, the crash-free-rate + ANR gate on real hardware,
live monetization runtime verification, on-device Baseline Profile regeneration,
and the per-feature `feature_enabled` gate migration (the existing Play-Billing
LicenseManager freemium gates are intentionally left as-is — rewiring them to the
monetization façade is a v2.0.9 product decision needing on-device validation).

Verification (host gates, all green)
------------------------------------
- `cargo check --workspace` clean (2m22s); Cargo.lock diff is version-only.
- `cargo fmt --all --check` clean.
- `./gradlew :app:compileFossDebugKotlin :app:compilePlayDebugKotlin` BUILD
  SUCCESSFUL (both flavors; the two showAd() deprecation warnings are pre-existing
  v2.0.3 monetization glue, not from this change). assembleFossRelease not re-run
  here (10m+ compile alone) — relied on the green compile + config-time validation
  of the signing block + CI.
- `pre-commit run markdownlint --all-files` Passed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…copilot #241)

Resolves all 9 copilot review threads on PR #241: the es-ES fastlane store copy
(short_description, full_description, changelogs/20004) was written without Spanish
diacritics, so the customer-facing Play listing read as misspelled Spanish. Restored
the correct orthography throughout — precisión, núcleo, emulación, rápido, versión,
público, código, características, batería, línea, teléfonos, diseños, etc. — and
tidied two phrasings to the idiomatic form ("de ciclo exacto", "versión candidata").
Play field limits still respected (short <= 80, full <= 4000, changelog <= 500).
No functional/code change; en-US copy unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The accent-corrected es-ES changelog reached 565 chars (Spanish is more verbose); Play rejects a changelog over 500. Tightened to mirror the en-US length while keeping correct orthography and all the RC points.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@doublegate
doublegate force-pushed the feat/v2.0.4-android-rc branch from 54e9eb3 to 896d093 Compare July 9, 2026 01:55
… resources

Completes the Android half of the "host-localizable bridge warnings" carryover
(target v2.0.2-v2.0.4, DEFERRED-AND-CARRYOVER-FEATURES.md). v2.0.3 added the stable
HostWarning code/enum + drain_warning_codes() to the rustynes-mobile bridge; this
wires the Android host to actually consume it.

Previously the bridge queued a HostWarning::PreTimebaseMovie on a pre-v2.0.0 .rnm
load, but the Android app never drained it -- so the ADR-0028 movie-epoch notice was
silently dropped on-device (grep confirmed no drain call anywhere in the app). Now,
after a successful moviePlay in MainActivity's SAF picker, the app drains the warning
CODES and maps each through a device-locale Android string resource
(host_warning_pre_timebase_movie, en + es) rather than the bridge's baked-in English,
surfacing it via the existing status line. The `when` over HostWarning is exhaustive,
so a future bridge warning is a compile error here until it is localized -- the
intended end state for keeping the frontends in locale parity without embedding
locale text in the deterministic-adjacent core bridge.

Scope: Android host only (src/main -> both foss + play). No core / bridge change
(HostWarning + drain_warning_codes already shipped in v2.0.3), so AccuracyCoin stays
141/141 byte-identical. The iOS half (Localizable) lands in the v2.0.5-v2.0.8 iOS
train; the DEFERRED item is updated to [~] (bridge + Android done, iOS remaining).

Verified: ./gradlew :app:compileFossDebugKotlin :app:compilePlayDebugKotlin BUILD
SUCCESSFUL (resources parsed, R.string resolves, both flavors compile).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@doublegate
doublegate merged commit 4232afe into main Jul 9, 2026
23 checks passed
@doublegate
doublegate deleted the feat/v2.0.4-android-rc branch July 9, 2026 02:40
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.

2 participants