Skip to content

Commit 2cd5312

Browse files
authored
release: cut v2.0.5 "Harbor" ("Landfall" — first iOS finalization; iOS re-port onto Timebase) (#244)
Opens the iOS finalization window (v2.0.5→v2.0.8) by re-porting the frozen v1.9.9 SwiftUI/Metal app onto the v2.0.0 Timebase core. Host/iOS-only; core byte-identical to v2.0.4 (AccuracyCoin 141/141). Surfaces + localizes (EN+ES) the pre-Timebase movie warning via a single multiplexed alert; UniFFI-Swift surface re-confirmed; workspace 2.0.4→2.0.5, iOS MARKETING_VERSION 1.9.1→2.0.5. Copilot review adopted + resolved.
1 parent f17f1aa commit 2cd5312

13 files changed

Lines changed: 320 additions & 29 deletions

File tree

‎.github/release-notes/v2.0.5.md‎

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# RustyNES v2.0.5 — "Harbor" ("Landfall")
2+
3+
**The iOS re-port onto Timebase.** The fifth release of the v2.0.x "Harbor" mobile-finalization train opens the **iOS finalization window (v2.0.5 → v2.0.8)** — the iOS/iPadOS analogue of the Android v2.0.1 → v2.0.4 window. It brings the frozen v1.9.9 SwiftUI / Metal app onto the **v2.0.0 "Timebase"** core. The cycle-accurate emulation core is **unchanged and byte-identical to v2.0.4** — **AccuracyCoin 141/141 (100.00%)**, nestest 0-diff, the deterministic `#![no_std]` chip stack untouched — so **no accuracy, save-state, or determinism number moves in this release**. This is a host / iOS-only cut.
4+
5+
No store submission happens here (that is the future v2.1.0 joint Google Play + Apple App Store + AltStore PAL + F-Droid launch); v2.0.5 establishes the green iOS build path on the v2.0.0 core and re-ports the first host-side behaviour the timebase break requires.
6+
7+
---
8+
9+
## The core is unchanged (141/141)
10+
11+
Nothing in the emulation core changed. The 2-cycle-ALE PPU fetch model promoted to default in v2.0.3 (ADR 0030) is the substrate here too; AccuracyCoin holds **141/141 (100.00%, RAM-authoritative)**, nestest is 0-diff, and the 60-ROM commercial byte-identity oracle is unmoved. If you only care about emulation accuracy, v2.0.5 is v2.0.4 with a bumped version number.
12+
13+
## Host-side iOS re-port (what actually landed)
14+
15+
- **The pre-Timebase movie warning, surfaced + localized on iOS.** The v2.0.0
16+
"Timebase" rewrite bumped the `.rns`/`.rnm` save-state + movie epoch (ADR 0028):
17+
a pre-v2.0.0 `.rnm` still **replays its input stream** on the new core, but exact
18+
framebuffer / audio reproduction is no longer guaranteed across the timebase
19+
change. The v2.0.3 bridge began queuing a machine-readable `HostWarning` code for
20+
this so hosts can localize it; **v2.0.5 surfaces it on iOS** — a new, non-blocking
21+
notice on its own channel (multiplexed through a single alert that prefers an error
22+
when both are queued, so neither is dropped) presented after a movie load, drained
23+
via `EmulatorCore.drainWarnings()` → `NesController.drainWarningCodes()`, localized
24+
**EN + ES** with wording byte-identical to the Android v2.0.4 string (shared ES
25+
copy). Loading a pre-Timebase movie now tells the user exactly what to expect
26+
instead of silently diverging.
27+
- **UniFFI-Swift binding surface re-confirmed against the v2.0.0 bridge.** The
28+
generated Swift surface (`drainWarningCodes() -> [HostWarning]`,
29+
`HostWarning.preTimebaseMovie`, `moviePlay(bytes:)`) was regenerated from the
30+
v2.0.0 `rustynes-mobile` and confirmed to match the host code — the iOS analogue
31+
of the wasm-bindgen-pin discipline that keeps the Rust library and the generated
32+
bindings in lockstep.
33+
- **Version realignment.** Workspace `2.0.4 → 2.0.5` (version-only `Cargo.lock`
34+
cascade). The iOS `MARKETING_VERSION` is realigned from the frozen v1.9.x
35+
checked-in default (`1.9.1`) to `2.0.5`, back in step with the workspace version;
36+
the build number stays CI-injected by the fastlane `beta` lane.
37+
38+
## Verification (host)
39+
40+
- **Core unchanged / byte-identical to v2.0.4** — AccuracyCoin **141/141**, nestest
41+
0-diff (not re-run for this host-only cut; the chip stack is untouched).
42+
- `cargo check --workspace` green after the version bump + `Cargo.lock` regen
43+
(version-only lock diff, 18 crates); `cargo fmt --all --check` clean.
44+
- **UniFFI-Swift emit host-verified:** the Swift bindings generated from a host
45+
build of `rustynes-mobile` contain `drainWarningCodes`, `HostWarning`
46+
(`case preTimebaseMovie`), and `moviePlay(bytes:)` — the exact surface the new
47+
Swift code references.
48+
- `Localizable.xcstrings` remains valid JSON (EN source + the added ES entry).
49+
- markdownlint (CI-pinned) clean on the new / edited docs.
50+
51+
## Maintainer / on-device closeout (deferred to v2.0.9 / the TestFlight upload)
52+
53+
These are inherently macOS / on-device / maintainer steps and are **not** done by
54+
this release (there is no Xcode / device in the build environment):
55+
56+
- Regenerate the `RustyNESFFI.xcframework` on macOS (`scripts/build-ios-xcframework.sh`)
57+
and archive the app with **Xcode 26 / the iOS 26 SDK** (the App Store submission
58+
floor lands as a hard gate at v2.0.7).
59+
- On-device **save-state migration** from a v1.9.x install and the **AccuracyCoin /
60+
SMB / Zelda determinism smoke** on Apple silicon.
61+
- A physical-device **TestFlight boot smoke** (iPhone 14+ / iPad); confirm the
62+
pre-Timebase-movie notice renders correctly under both locales.
63+
64+
## What's next — the rest of "Harbor"
65+
66+
- **v2.0.6** — iOS feature-parity finalization against the v2.0.0 behaviour.
67+
- **v2.0.7** — iOS polish + performance + the **iOS 26 SDK / Xcode 26** App Store
68+
submission-floor readiness + the privacy-manifest / required-reason-API audit.
69+
- **v2.0.8** — the iOS release candidate (App Store Connect listing staged,
70+
TestFlight external beta, App-Review self-audit).
71+
- **v2.0.9** — joint on-device readiness verification for both apps.
72+
- **v2.1.0** — the **joint Google Play + Apple App Store + AltStore PAL + F-Droid launch**.
73+
74+
Full detail: `CHANGELOG.md` `[2.0.5]` · `docs/STATUS.md` (single source of truth) · `docs/ios-v2.0.5-readiness.md` · `to-dos/plans/v2.0.5-v2.0.8-ios-finalization-plan.md`.

‎CHANGELOG.md‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,25 @@ cycle-accurate core later replaced.
1414

1515
## [Unreleased]
1616

17+
## [2.0.5] - 2026-07-09 - "Harbor" (iOS re-port onto Timebase — "Landfall")
18+
19+
- Opens the iOS finalization window (v2.0.5–v2.0.8) of the v2.0.x "Harbor" train:
20+
the iOS/iPadOS app is re-ported onto the v2.0.0 "Timebase" core — the iOS
21+
analogue of the Android v2.0.1 re-port. Host/iOS-only; the emulation core is
22+
unchanged and byte-identical to v2.0.4 (AccuracyCoin 141/141, nestest 0-diff).
23+
- The iOS host now localizes bridge warnings (device-locale strings, EN + ES) for
24+
the pre-Timebase movie notice: loading a pre-v2.0.0 `.rnm` still replays its
25+
input, but surfaces a non-blocking notice that byte-exact framebuffer/audio
26+
reproduction is not guaranteed across the ADR-0028 timebase change — the iOS
27+
analogue of the Android v2.0.4 warning, verbatim wording and shared ES copy.
28+
- The UniFFI-Swift binding surface is re-confirmed against the v2.0.0 bridge
29+
(`drainWarningCodes` / `HostWarning.preTimebaseMovie`); the iOS
30+
`MARKETING_VERSION` is realigned from the frozen v1.9.x default to `2.0.5`.
31+
- TestFlight-only; the App Store + AltStore PAL launch stays deferred to the
32+
v2.1.0 joint milestone. On-device re-port verification (save-state migration +
33+
the AccuracyCoin / SMB / Zelda determinism smoke on Apple silicon) is flagged
34+
for the v2.0.9 dual-app readiness pass.
35+
1736
## [2.0.4] - 2026-07-08 - "Harbor" (Android release candidate)
1837

1938
- Android release-candidate milestone; the emulation core is unchanged and

‎Cargo.lock‎

Lines changed: 18 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Cargo.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ members = [
2121
]
2222

2323
[workspace.package]
24-
version = "2.0.4"
24+
version = "2.0.5"
2525
edition = "2024"
2626
rust-version = "1.96"
2727
license = "MIT OR Apache-2.0"

‎docs/STATUS.md‎

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,30 @@
11
# RustyNES — Project Status Matrix
22

3-
> **Current release: v2.0.4 "Harbor"** (2026-07-08) — the fourth release of the v2.0.x
3+
> **Current release: v2.0.5 "Harbor"** (2026-07-09) — the fifth release of the v2.0.x
4+
> mobile-finalization train, and the **first iOS finalization release** ("Landfall"),
5+
> opening the iOS window (v2.0.5–v2.0.8) that mirrors the Android v2.0.1–v2.0.4 window.
6+
> This is a **host / iOS-only** cut: the cycle-accurate emulation core is **unchanged and
7+
> byte-identical to v2.0.4** — AccuracyCoin still **141/141 (100.00%, RAM-authoritative)**,
8+
> nestest 0-diff, the deterministic `#![no_std]` chip stack untouched — so no accuracy /
9+
> save-state / determinism number moves. It re-ports the frozen v1.9.9 SwiftUI / Metal app
10+
> onto the v2.0.0 "Timebase" core: **(1)** the pre-Timebase movie warning is surfaced +
11+
> localized on iOS (a non-blocking notice on its own channel — not an error, **EN + ES**,
12+
> drained via `EmulatorCore.drainWarnings()` → `NesController.drainWarningCodes()`, wording
13+
> byte-identical to the Android v2.0.4 string) so loading a pre-v2.0.0 `.rnm` tells the user
14+
> byte-exact framebuffer/audio reproduction isn't guaranteed across the ADR-0028 timebase
15+
> change; **(2)** the UniFFI-Swift binding surface (`drainWarningCodes` /
16+
> `HostWarning.preTimebaseMovie` / `moviePlay`) is re-confirmed against the v2.0.0 bridge
17+
> (host-verified Swift emit); and the **version bump** (workspace `2.0.4 -> 2.0.5`; iOS
18+
> `MARKETING_VERSION 1.9.1 -> 2.0.5`, realigned from the frozen v1.9.x default). The core is
19+
> untouched, so the chip stack / `no_std` / AccuracyCoin contract is unaffected.
20+
> **TestFlight-only**; App Store + AltStore PAL stay deferred to the v2.1.0 joint launch.
21+
> The **on-device closeout is a maintainer / v2.0.9 step** — the xcframework build on macOS
22+
> (**Xcode 26 / iOS 26 SDK**, the App Store submission floor that lands as a hard gate at
23+
> v2.0.7), save-state migration from a v1.9.x install, and the AccuracyCoin / SMB / Zelda
24+
> determinism smoke on Apple silicon. See `CHANGELOG.md` `[2.0.5]` +
25+
> `docs/ios-v2.0.5-readiness.md` + `to-dos/plans/v2.0.5-v2.0.8-ios-finalization-plan.md`.
26+
>
27+
> **The preceding release: v2.0.4 "Harbor"** (2026-07-08) — the fourth release of the v2.0.x
428
> mobile-finalization train, and the **Android release-candidate** milestone. This is a
529
> **host / Android-only** cut: the cycle-accurate emulation core is **unchanged and
630
> byte-identical to v2.0.3** — AccuracyCoin still **141/141 (100.00%, RAM-authoritative)**,
@@ -28,7 +52,7 @@
2852
> them to the monetization `feature_enabled` façade without on-device validation). See
2953
> `CHANGELOG.md` `[2.0.4]` + `to-dos/plans/v2.0.4-android-rc-plan.md`.
3054
>
31-
> **The preceding release: v2.0.3 "Harbor"** (2026-07-08) — the third release of the v2.0.x
55+
> **The release before that: v2.0.3 "Harbor"** (2026-07-08) — the third release of the v2.0.x
3256
> mobile-finalization train, and the one that makes the octal-latch accuracy work
3357
> real at the shipped default. The **2-cycle-ALE PPU fetch model is promoted from the
3458
> experimental `mc-ppu-2cycle-ale` flag to the unconditional, only PPU fetch path**

‎docs/ios-v2.0.5-readiness.md‎

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# RustyNES v2.0.5 "Harbor" ("Landfall") — iOS re-port readiness
2+
3+
This is the readiness record for **v2.0.5 "Landfall"**, the first iOS/iPadOS release of
4+
the v2.0.x "Harbor" mobile-finalization train — the iOS re-port of the frozen v1.9.9 app
5+
onto the **v2.0.0 "Timebase"** core. It is the iOS analogue of the Android v2.0.1
6+
re-port, and opens the iOS finalization window (**v2.0.5 → v2.0.8**). `docs/STATUS.md`
7+
remains the per-suite source of truth; this file is the authoritative v2.0.5 summary.
8+
9+
v2.0.5 is a **host / iOS-only** cut. The deterministic core and chip crates are
10+
untouched; the shipped / native / `no_std` / wasm core stays **byte-identical to
11+
v2.0.4** and **AccuracyCoin holds 141/141 (100.00%, RAM-authoritative)**, nestest is
12+
0-diff. Every change is presentation / host-side.
13+
14+
## 1. What landed
15+
16+
- **The pre-Timebase movie warning, surfaced + localized on iOS.** The v2.0.0 timebase
17+
rewrite bumped the `.rns`/`.rnm` epoch (ADR 0028): a pre-v2.0.0 `.rnm` still replays
18+
its input on the new core, but byte-exact framebuffer / audio reproduction is no
19+
longer guaranteed. The v2.0.3 bridge queues a machine-readable `HostWarning` code for
20+
this; v2.0.5 surfaces it on iOS:
21+
- `EmulatorCore.drainWarnings() -> [String]` drains `NesController.drainWarningCodes()`
22+
and maps each `HostWarning` to a localized string (idempotent — the queue empties on
23+
read, so each warning surfaces exactly once).
24+
- `AppModel.surfaceWarnings()` is called after every successful `moviePlay` (both the
25+
saved-`.rnm` `playMovie(at:)` path and the foreign-movie `importForeignMovie(at:)`
26+
path) and routes the text to a new `@Published var warningMessage`.
27+
- `ContentView` presents `warningMessage` through a **single alert that multiplexes
28+
the error + warning channels** — it prefers the error when both are queued and
29+
clears only the visible channel on dismissal, so neither is dropped (two chained
30+
`.alert` modifiers on one view would race — SwiftUI presents only one) and a
31+
warning still never reads as a failure.
32+
- `Localizable.xcstrings` gains the EN source string + its ES translation; the English
33+
key and the ES copy are **byte-identical to the Android `host_warning_pre_timebase_movie`
34+
resource**, so both platforms surface the same wording.
35+
- **UniFFI-Swift binding surface re-confirmed against the v2.0.0 bridge.** The generated
36+
Swift surface (`drainWarningCodes() -> [HostWarning]`, `HostWarning.preTimebaseMovie`,
37+
`moviePlay(bytes:)`) matches the new host code, verified by a host emit (see §2).
38+
- **Version realignment.** Workspace `2.0.4 → 2.0.5` (version-only `Cargo.lock` cascade,
39+
18 crates). iOS `MARKETING_VERSION 1.9.1 → 2.0.5` in `ios/project.yml` (the frozen
40+
v1.9.x default is realigned to the workspace version); the build number stays
41+
CI-injected by the fastlane `beta` lane.
42+
43+
## 2. Verification / validation
44+
45+
### Host gate suite
46+
47+
| Gate | Result |
48+
|---|---|
49+
| `cargo check --workspace` (version bump + lock regen) | PASS (46 crates; version-only lock diff) |
50+
| `cargo fmt --all --check` | PASS |
51+
| `cargo clippy --workspace --all-targets -- -D warnings` | PASS |
52+
| `RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps` | PASS |
53+
| `cargo build -p rustynes-core --target thumbv7em-none-eabihf --no-default-features` (`no_std`) | PASS |
54+
| both wasm clippy gates | PASS |
55+
| `markdownlint` (pinned v0.39.0, changed docs) | PASS |
56+
| **UniFFI-Swift emit** (`uniffi-bindgen generate --library <host build> --language swift`) | PASS — emits `drainWarningCodes`, `HostWarning.preTimebaseMovie`, `moviePlay(bytes:)` |
57+
| `Localizable.xcstrings` JSON validity (EN source + added ES entry) | PASS |
58+
59+
The Swift-side change references only symbols confirmed present in the regenerated
60+
bridge surface; the exhaustive `switch` over `HostWarning` is sound (the generated enum
61+
is compiled into the app module).
62+
63+
### Not host-certifiable (macOS / device)
64+
65+
The Xcode build, the `RustyNESFFI.xcframework` assembly (`scripts/build-ios-xcframework.sh`),
66+
and any on-device run are **not** reproducible on the Linux host and remain the documented
67+
TestFlight carryover (§3).
68+
69+
## 3. On-device / maintainer carryovers (v2.0.9 / the TestFlight upload)
70+
71+
- Regenerate the xcframework on macOS and archive with **Xcode 26 / the iOS 26 SDK** (the
72+
App Store submission floor — a hard gate at v2.0.7; deployment target may stay iOS 16/17).
73+
- **Save-state migration** from a v1.9.x install onto the v2.0.0 epoch, and the
74+
**AccuracyCoin / SMB / Zelda determinism smoke** on Apple silicon.
75+
- A physical-device **TestFlight boot smoke** (iPhone 14+ / iPad); confirm the
76+
pre-Timebase-movie notice renders correctly under both EN and ES locales, and that a
77+
post-v2.0.0 `.rnm` produces **no** warning.
78+
- The full v1.9.9 on-device TestFlight checklist (ROM import, save / rewind, MFi
79+
controller, audio interruptions, ProMotion pacing, an accurate privacy label).
80+
81+
## 4. Carryovers to v2.0.6 → v2.0.8 (unchanged from v1.9.9 §5)
82+
83+
Feature-parity re-verify against the v2.0.0 behaviour (Game Center, CloudKit save sync,
84+
controllers, capture/PiP, accessibility) lands in **v2.0.6**; polish / performance + the
85+
**privacy-manifest + required-reason-API audit** + the submission-floor wiring in
86+
**v2.0.7**; the App Store Connect listing + signing scaffold + §4.7 self-audit in the
87+
**v2.0.8** iOS RC. The post-Timebase feature carryovers (FDS/NSF, the 20-band EQ, `.dbg`
88+
source maps, a cheats DB, box-art scraping, a dedicated external-display output) remain
89+
deferred for re-evaluation at/after v2.1.0. Full phasing:
90+
`to-dos/plans/v2.0.5-v2.0.8-ios-finalization-plan.md`.

0 commit comments

Comments
 (0)