Skip to content

Tie-up: one tested product (2026-10-27, branch claude/tie-up-2026-10-27)

One long Mac session tied Studio, the recompiler output, 32-bit, DLC/updates, the overlay, amiibo, Mii, camera, the online hook, packaging, docs and CI into one product and tested it end to end with real runs. iOS untouched. Decisions were taken without asking (the user was away): they are marked Decision.

Seams

# Seam Result
4 One system data root Done, tested. g_cfg.support_dir / napple_support_dir() (runtime/src/napple.h): NAPPLE_SUPPORT_DIR or ~/Library/Application Support/Napple, independent of --save-root; the Mii database, fonts, icon, amiibo store and applet scratch use it (mii.c, pl.c, account.c, applet.c, offline.c); the launcher installs fonts there (not under --save-root); Studio uses SystemData.supportDir; boot_test.sh/golden.py pass only --save-root, so they resolve the same root. test_svc_sysroot.c (8 checks), Studio SystemDataTests. The second game reads the same MiiDatabase.dat (MK8D: mii: list ... wrote 2 after LAS-side creation, in the e2e table).
3 Amiibo list source Done, tested. The C loader (amiibo_lib_core.c) now reads the repository's full database (amiibos + characters/amiibo_series/game_series/types, icon URL derived) as well as the API shape; the frontend converter is gone (simulators.m); default source is the AmiiboAPI repository's raw database/amiibo.json (documented configuration, no data bundled; amiiboapi.com answers 404); cache + local folder remain the offline fallback; test_svc_amiibo 124 checks.
2 Overlay amiibo settings, lazy reader Done. The overlay's amiibo section has list source and key fields saved to system.json (see seam 1); the reader is installed on first Place/Import or NAPPLE_AMIIBO, never at boot. The Break 0x35f202: not an nfp problem. It is SetTerminateResult 0x35f202 (fs 2-6905) raised by LAS itself 0.4-0.8 s after start, about 1 start in 5 (headless 1/7, windowed 2/7 seen; 0/25 and 0/40 with FS/IPC tracing, so timing dependent), with no nfp service ever opened by LAS. Reproduced with IPC error tracing: the game probes saves/files with concurrent GetEntryType/OpenFile (PathNotFound, expected on first use) and aborts after the burst on some starts. Not fixed: no deterministic reproduction; documented as KNOWN flaky in tools/e2e (retried up to 3 times) and in the LAS title note.
1 Studio panes Done, tested. Settings > Mii (list, delete, Back Up, Export, Restore, Open Folder), Amiibo (source, key, store), Online (provider module; empty = NullProvider) and Library > Play with Log… (live runtime log, filter, copy, stop) in studio/Sources/NappleStudio/Views/SystemSettingsViews.swift; logic in NappleCLI.swift (SystemConfig, MiiStore, GameRun, SystemData): no second code path. They write ONE contract, <support>/system.json, read by the runtime at boot (sysconfig_core.c, env wins). Swift tests in SystemDataTests.swift (64 tests green). Screenshots: img/tie-up-studio-settings-{mii,amiibo,online}.png, img/tie-up-overlay-game-las.png.
5 Mii Maker child Done, verified. The child exits when its parent is gone (NAPPLE_APPLET_PARENT + watch thread in main.c); libnapple-applet.dylib found in a dev tree (../build-lib) and bundled in Mac apps (pipeline.rs Contents/Frameworks, build_app.sh, make_release.sh, EngineInstaller); game frames are paced (60 Hz per swap interval) while an applet hides the game (gfx_glue.c; the presenter dropped them without pacing: 17-20k frames/60 s); scripted exit NAPPLE_APPLET_SECONDS. Verified end to end: tools/e2e drives the real Mii Maker (firmware applet in the app) with NAPPLE_INPUT_SCRIPT + NAPPLE_SWKBD_ANSWER, the new Mii (UTF-16 name) is in MiiDatabase.dat, and MK8D's mii:u lists it after restart.
6 Camera (and microphone) Done, tested. Generated apps are ad-hoc signed with com.apple.security.device.camera and com.apple.security.device.audio-input (bundle.rs mac_entitlements()), Info.plist has NSCameraUsageDescription and NSMicrophoneUsageDescription (for NAPPLE_AUDIN_SOURCE=mic); Studio's entitlements.plist has both. Decision: no App Sandbox (the system folders need an unsandboxed app); Rust tests assert the keys, no duplicates (codesign rejects a repeated key) and no app-sandbox. Verified with codesign -d --entitlements in every e2e run.
7 Online provider Done. Already: a bad path/ABI/symbol falls back to the NullProvider with one online: log line (test_svc_online); now also a Studio setting and system.json key.
8 Tooling studio/build_app.sh honours CARGO_TARGET_DIR and bundles the applet lib; test_timescale.c takes the best of five samples (robust under load); swift test note for ~/Documents.
9 A32 through Studio Verified. MK8D built through Studio's pipeline (napple-studio-build, the exact Add Game code path with the three files): modules/a32jit.dylib embedded, update + DLC in the app (3 version dirs, 1 DLC), boots 45 s.
10 E2E Done: tools/e2e/run.sh <las|smo|acnh|mk8d|all> (e2e.py), table below.
11 CI + docs tests.yml: applet-lib, the golden selftest, py_compile/bash -n of the e2e script, and a studio-app job (build_app.sh smoke + helpers, applet dylib and entitlements checks); release.yml smoke checks the applet dylib (in Studio and the zip's bin/) and the entitlements; ci_build.sh builds the applet lib. Docs: new system data folders, updated hardware-simulators, network-providers, Studio, environment variables, testing guide (end-to-end section), the four title notes. gen_support.py --check and check_docs.py green. CI YAML was not run (no runner): validated by reading only.

SMO threads.c stall

Not fixed (blocked: no reproduction). In the last two end-to-end runs SMO's golden route went past the old stall point (14143 and 15451 frames, frames 8000/9000 captured, 0-1 gfx warnings); the stall after ~7,500 frames (a spinning guest thread holding a virtual core, no timer preemption) did not occur, so there was nothing to test a preemption fix against. The cores model (threads.c: a guest thread owns one of three virtual cores until it blocks; preempt is honoured only at svc entry) is the place; a fix needs a signal-based preemption point and a deterministic repro first.

tools/e2e results (this branch, final end-to-end run, MacBook, real titles)

LAS was run first with everything (build ... golden); mk8d/acnh/smo in a second invocation on the same support root. Overlay rows say tabs [0..6] in the first invocation: the overlay has 4 tabs (0 Audio/Video, 1 FPS/Speed, 2 Controls, 3 Game/System, which holds the amiibo section) and higher numbers fall back to tab 0; e2e now renders [0,1,2,3] / [3]. Golden diffs are report only (non deterministic run to run even on main), goldens approved by the user are used for the comparison.

title step status detail
las build PASS The Legend of Zelda Link's Awakening.app in 56 s
las bundle PASS 64-bit; 1 version dir(s); 0 dlc
las camera-mic PASS usage strings + camera/audio-input entitlements, no sandbox
las mii-db PASS Mii Maker scripted: Mii 'E2E099770' (swkbd answer) is in the shared database (0 -> 2 Miis); every later title reads this file
las boot PASS exit 0, 2582 frames in 45 s (floor 1200)
las audio PASS 4263840 frames pushed, 2067135 above the noise floor, peak 0.273
las input PASS 3 of 3 scripted presses reached the guest
las amiibo N/A the title never opened nfp:user/nfc:user in this route (the reader installs lazily: no abort)
las save PASS 2 save file(s) written, second boot mounted them again (exit 0)
las overlay PASS tabs rendered [0, 1, 2, 3, 4, 5, 6] (PNGs in $TMPDIR/napple-e2e-all/overlay/las)
las golden REPORT report only: boot-to-village: exit 0, 8539 frames, warnings 0, crash False, frames {'1500': 'differs', '3000': 'differs', '4500': 'differs', '6000': 'ok'}
smo build PASS SUPER MARIO ODYSSEY.app in 49 s
smo bundle PASS 64-bit; 1 version dir(s); 0 dlc
smo camera-mic PASS usage strings + camera/audio-input entitlements, no sandbox
smo boot PASS exit 0, 5396 frames in 100 s (floor 1000)
smo audio PASS 4786320 frames pushed, 2835880 above the noise floor, peak 1.000
smo input PASS 3 of 3 scripted presses reached the guest
smo amiibo N/A nfp:user opened, but the title never started tag detection in this route (the scripted amiibo fires at the first StartDetection; nfp itself is covered by test_svc_nfp / test_svc_amiibo)
smo save PASS 2 save file(s) written, second boot mounted them again (exit 0)
smo overlay PASS tabs rendered [4] (PNGs in $TMPDIR/napple-e2e-all/overlay/smo)
smo golden REPORT report only: title-and-kingdom: exit 0, 15451 frames, warnings 1, crash False, frames {'1500': 'ok', '4000': 'ok', '8000': 'new', '9000': 'new'}
acnh build PASS Animal Crossing New Horizons.app in 238 s
acnh bundle PASS 64-bit; 4 version dir(s); 3 dlc
acnh camera-mic PASS usage strings + camera/audio-input entitlements, no sandbox
acnh boot PASS exit 0, 1193 frames in 45 s (floor 1000)
acnh audio PASS 2145120 frames pushed, 1593672 above the noise floor, peak 0.430
acnh input PASS 3 of 3 scripted presses reached the guest
acnh amiibo N/A nfp:user opened, but the title never started tag detection in this route (the scripted amiibo fires at the first StartDetection; nfp itself is covered by test_svc_nfp / test_svc_amiibo)
acnh save PASS 2 save file(s) written, second boot mounted them again (exit 0)
acnh overlay PASS tabs rendered [4] (PNGs in $TMPDIR/napple-e2e-all/overlay/acnh)
acnh golden REPORT report only: airport-counter: exit 0, 2624 frames, warnings 0, crash False, frames {'300': 'ok', '600': 'ok', '1200': 'differs'}
mk8d build PASS Mario Kart 8 Deluxe.app in 198 s
mk8d bundle PASS a32jit.dylib embedded; 3 version dir(s); 1 dlc
mk8d camera-mic PASS usage strings + camera/audio-input entitlements, no sandbox
mk8d boot PASS exit 0, 2442 frames in 45 s (floor 1000)
mk8d audio PASS 2135280 frames pushed, 2115305 above the noise floor, peak 0.253
mk8d input PASS 8 of 8 scripted presses reached the guest
mk8d amiibo N/A the title never opened nfp:user/nfc:user in this route (the reader installs lazily: no abort)
mk8d save PASS 6 save file(s) written, second boot mounted them again (exit 0)
mk8d mii-serve PASS mii:u opened, mii: list kind 0 flags 0x1: output buffer 9200 bytes (100 elements of 92), wrote 2
mk8d overlay PASS tabs rendered [0, 1, 2, 3, 4, 5, 6] (PNGs in $TMPDIR/napple-e2e-all/overlay/mk8d)
mk8d golden REPORT report only: title-menus-race: exit 0, 8362 frames, warnings 0, crash False, frames {'600': 'ok', '1500': 'differs', '3000': 'differs', '4500': 'differs'}

Known flaky (tagged KNOWN, never hit in the final run): LAS 0x35f202 early abort (above), ACNH Break at sdk+0xc80730. Findings: SMO is silent for the first 45 s (boot step uses 100 s for it); SMO and ACNH open nfp:user but the 45 s routes never call StartDetection (amiibo step N/A for them; nfp state machine and picker are unit tested); NAPPLE_CLI in the user's shell points at the installed engine, e2e overrides it with this checkout's CLI.

Files

runtime (napple.h, main.c, gfx_glue.c, services/{applet,mii,pl,account,offline,amiibo_lib_core,sysconfig_core}, tests), frontend/src/simulators.m, audio (PCM counter), packaging/launcher.c, crates/napple-build (bundle.rs, pipeline.rs), studio (Sources, Tests, build_app.sh, packaging), tools/e2e (new), tools/ci_build.sh, .github/workflows, docs.