Repository navigation
Handoff: state of the tracker and what's left (2026-10-17) #202
Copy link
Copy link
Closed
Description
Where the work stands, and how to pick it up on another machine.
State of master (v2.42.0)
- Logic:
@alltp/engineand the complete ALttPR v31 pack (packs/alttpr).- All 2,969 official
tests/NoGlitchescases, all 3,571tests/Invertedcases and the 32RetroBowTestcases pass (sporchia/alttp_vt_randomizer@219fcafd). - Worlds: Open, Standard, Inverted and Retro; and the entrance randomizer's own world for Crosskeys, its 510 official dungeon and Death Mountain cases passing (
cassidoxa/ALttPEntranceRandomizer@8a1ac4d4). - Settings not known yet (a Mystery run's) read both ways by the engine, sound by property test.
- Key model for untracked keys; save v7; a "seen" map state; keysanity maps and compasses.
- All 2,969 official
- Autotracking end to end:
@alltp/autotrack, the usb2snes link to SNI / QUsb2Snes;- the ALttPR memory map, with Race Mode by construction;
- an in-app setup wizard per OS, and "What I read";
- the board follows the game, one tab at a time;
- outside races, a Crosskeys run notes each door or hole Link goes through, and the door he comes out by;
- outside races, a dungeon's prize is read from the ROM's prize tables and shown when the game would show it (dungeon finished, its map held under map mode, Sahasrahla's or the Bomb Merchant's mark), listed in "What I read";
- Race Mode follows the ALTTPR Auto-tracker Rules v2.0, deliberately strict pending TARC; a setting Auto (the ROM's flag) / Always on; no alttpr.com seed file in races.
- Reading is confirmed on a real setup: CachyOS, RetroArch + bsnes-mercury, SNI, Firefox.
- Archipelago (
packages/archipelago,games/archipelago/): a read-only Tracker on the player's slot of an AP room, througharchipelago.js2.1.0 (pinned) behind an outgoing whitelist; AP's server is the authority (no logic, never mixed with our packs). Items (ALttP, SM and SMZ3 skins with our art on AP's item names; a generic list for any other AP game), checks by location group, the room's feed, hints, players, while-you-were-away, the run timer from the room's countdown to the slot's goal, the stream view (&feed=1,&timer=1), items-only overlay; self-hosted rooms from the site (Local Network Access flow) and from the desktop app (Linux verified); join links. Pinned to AP 0.6.8, with a weekly real-server CI job and anap-driftwatch. Checked live against archipelago.gg from the site. - Three games, one module each (the device's game, loaded on demand):
- A Link to the Past Randomizer (alttpr.com), as above;
- Super Metroid as Map Rando plays it: items, the seed's objectives, autotracking (no logic: the rooms are reconnected);
- SMZ3 (samus.link): both games' logic from
tewtal/SMZ3Randomizer, held to its four test sets (5,490 cases); the maps of Hyrule and Zebes; keysanity's keys and keycards; autotracking in both games, race-safe (never the rewards, never a race seed's thresholds or medallions).
- Desktop app (Tauri 2): Windows (NSIS), macOS (arm64 and x64
.dmg, ad-hoc signed), Linux (AppImage,.deb,.rpm), built and attached to each release; signed updates (latest.json, minisign key); autotracking: RetroArch read directly over UDP with no SNI, SNI as the fallback for every other device (and for Safari's users on the web); global hotkeys (Windows, macOS and X11 through the global-shortcut plugin, Wayland through the xdg GlobalShortcuts portal,alltp-tracker --step <action>for any window manager); Export through the native Save dialog; live follow on the LAN (the app relays the run to?view=followpages on phones on the same network); on Linux, the AppImage offers to add itself to the application menu. - Web: PWA (offline, install, F5 always latest), a Share dialog with the link and its QR code (lean-qr, lazy chunk), OBS stream view (
?view=obs), configurable item shortcuts, measured-fit toolbar with a More menu, greyed missing items, transparent art with 1-game-pixel outlines. - Run analysis: what each check is missing (tooltip and the Checks list's "Why?"), a Checks tab beside the map, go mode per goal beside the map's tabs.
- Run timer: beside go mode, started and stopped by the game while autotracking (race-legal), the ROM's play time outside races, optional on stream.
- Run tab: splits at each boss, a timeline of every find timed by the run timer (saved with the run, undo-exact), and outside races the ROM's own end-credits stats.
- Voice commands: "tracker hook shot" marks a find while the game has focus, recognized on the device (Vosk, WASM), opt-in.
- The ROM picks the run type: a fresh board switches to the seed's; otherwise a prompt offers Switch or Keep mine. Outside Race Mode, settings the ROM does not state come from the seed's file on alttpr.com; in Race Mode the racer picks the run type.
- Start from a seed: an alttpr.com link or hash in the run type dialog names the run type.
- CI and releases:
- one pipeline per merge (
ci.yml; the deploy job publishes the e2e-tested build to Pages); - release-please, with the release PR merged at the end of each PR series;
- a weekly logic-drift workflow;
pnpm check:docs(scripts/check-docs.mjs, owndocs.ymlworkflow sinceci.ymlignores Markdown);- actions pinned by commit SHA; Dependabot sends one weekly pull request for npm, actions and the desktop crates (
cargoin the all-dependencies group, ci: let Dependabot update the desktop app's crates #315; unconfirmed until its first weekly run accepts it).
- one pipeline per merge (
Waiting on the owner
- SignPath Foundation application (Windows signing): the owner's step, next on the roadmap.
- Social preview: the upload (GitHub repository settings) is still pending.
- Announcement (the first drafts were lost with a wiped
/tmp; write them again when it is due): first ask the ALttPR racing council whether race mode's two uncovered behaviours are OK (the seed's file fetched from alttpr.com for its settings; a Mystery race seed switching the board to Mystery); then review the texts; decide whether GitHub Discussions stay open. - Stream view and overlay API on other setups: passing in real OBS 32.2.2 and 33.0.0 on Linux (Flathub; CEF 127 and 150), 2026-10-23/24 (docs: record the overlay API and stream view run in a real OBS #327, fix(desktop): serve the web manifest instead of a 404 #328). Still to do by the owner: another computer on the network, and the stream server on macOS and Windows.
- TARC questions (ALTTPR Auto-tracker Rules v2.0): nine points to ask on the TARC Discord: the seed file in races, Mystery logic, the timeline, the overlay's timer, prize classes, play time, WRAM, a prize the map shows, the override. The text is ready in the owner's home (
~/tarc-questions.md). The answers decide the series that loosens race reads. - Archipelago on macOS and Windows desktop, and Safari/iOS: unverified.
- Live follow on a real phone: scan the QR with an iPhone on the same Wi-Fi (Safari iOS on a plain-http LAN page is unverified); check the Windows firewall prompt.
On master, not yet released
Nothing.
Done in v2.38.2 to v2.42.0 (2026-10-24/25): Race Mode v2.0, Archipelago, ROM prizes
- v2.38.2, fix(web): bring Race Mode in line with the ALTTPR Auto-tracker Rules v2.0 #330: Race Mode on the ALTTPR Auto-tracker Rules v2.0: "Zelda rescued" hidden in races, a Race Mode setting (Auto / Always on), no alttpr.com seed file in races, docs cite v2.0.
- Archipelago, plan and decisions recorded in
docs/archipelago.mdand the package README: S1 gate on the bench (AP 0.6.8, fixtures recorded, wss to archipelago.gg and ws:// from the desktop app passing); v2.39.0 feat(archipelago): add the read-only Archipelago tracker package #332 package, feat(web): add the Archipelago board #334 board and ALttP skin, ci(archipelago): real-server run and Archipelago release drift #335 real-server CI +ap-drift; v2.40.0 feat(web): Archipelago feed, hints and players (S2) #336 feed, hints, players, countdown timer; v2.41.0 feat(web): Super Metroid and SMZ3 skins for the Archipelago board (S3) #339 SM and SMZ3 skins, overlay, the AP-ROM pointer in the SNI guide; v2.42.0 feat: Archipelago self-hosted rooms from the site, and join links (S4) #341 self-hosted rooms (Local Network Access), join links. - v2.42.0, feat(alttpr): show a dungeon's prize when the game shows it #342: ALttPR dungeon prizes from the ROM outside races, shown when the game would show them; SMZ3's "rewards never read" wording corrected. build(desktop): bump tokio to 1.54.1 #343 tokio 1.54.1 (Dependabot alert 120).
- Decided 2026-10-24: door mapping and the PopTracker runtime are dropped.
- Lessons:
- CI's branded Chrome headless reads a refused local network permission as "denied", Playwright's headless shell as "prompt": the Archipelago self-hosted e2e runs Chrome's headless mode locally too;
- rtk rewrote
pnpm exec biomeas an ESLint run and exited 1 on clean files: excluded in its config.
Done in v2.38.1
- fix(desktop): serve the web manifest instead of a 404 #328: the app's servers answered 404 for
manifest.webmanifest, because Tauri types it as HTML and the relay took that for its index fallback. OBS 33 logged that failed load with the page's address, which carries the stream token. The manifest is now served, and the fallback is told apart by its bytes. - docs: record the overlay API and stream view run in a real OBS #327: the real-OBS runs recorded in the docs.
Done in the series before (v2.37.0 to v2.38.0): stream server, overlay API
- v2.38.0 released, its 14 desktop installers and
latest.jsonattached (15 assets, checked once the Desktop release run finished). - refactor(desktop): split the relay's building blocks out of follow.rs #322: the relay's building blocks split out of
follow.rsintorelay.rs. - feat: serve the stream view to OBS from the desktop app #325: the desktop app serves the OBS stream view (
?view=obs&token=) from a second token-gated server on port 23076 (16 seats), loopback by default, with an opt-in "Allow other devices on the network" switch. Settings, port and tokens are kept instream.json(0600). Port order: saved, 23076, 23077-23085, any. Exclusive bind on Windows, with a 2 s same-port retry on restart. The token travels as a WebSocket subprotocol. The Stream view toolbar button is back in the desktop app, with the Stream tools dialog. Windows CI now runscargo test --locked -- stream:: relay::. - feat: the overlay WebSocket API #326: the overlay WebSocket API (
/overlayon the same server, its own API token; protocol v1 withstate,updateandbeat,seq; a race-safe whitelist per game, SMZ3 sending items only, no boss tokens);docs/overlay-api.md. - fix(web): put the visible text in the accessible name of four controls #323: a11y fixes for axe-core 4.14's
label-content-name-mismatch. Dependabot build(deps): Bump the "all-dependencies" group with 2 updates across multiple ecosystems #316 (axe-core 4.14 group) waits on a Dependabot recreate. - Lessons:
- validate workflow YAML after every edit: an unquoted
:brokedesktop.ymlsilently (the job never started, so the PR looked green); - a test that holds a port must bind the address the server binds (Windows lets an exclusive
127.0.0.1bind share a port with a plain0.0.0.0one); - a stale-build e2e used
?v=2, which loads the entry module twice.
- validate workflow YAML after every edit: an unquoted
Done in the series before (v2.36.0 to v2.37.0): QR share, docs audit, deduplication, live follow
- v2.36.0: feat(web): show the share link as a QR code #314 the Share dialog with a QR code of the link (lean-qr, lazy chunk); fix(web): fold the secondary toolbar actions to icons before every label #313 the staged toolbar fold (secondary actions go to icons before any primary label; SM and SMZ3 bars too); docs: refresh the README screenshots for the Half Magic cell #311 README screenshots with the Half Magic cell and the staged toolbar.
- Series 0: Dependabot updates the desktop crates (
cargoentry in the all-dependencies group, ci: let Dependabot update the desktop app's crates #315; unconfirmed until its first weekly run accepts cargo in the group). Dependabot alert Ci/bump GitHub actions #119 (glib 0.18 via Tauri's GTK 3 stack) dismissed as tolerable risk, with the reason. - v2.36.1: docs audit and fixes (docs: audit fixes and a check for broken links and paths #317) with
pnpm check:docs(scripts/check-docs.mjs, owndocs.ymlworkflow sinceci.ymlignores Markdown); deduplication (Share what the three games' boards duplicated, and fix three drifts #319): RunToolbar, GameShell, runHistory, saveFormat, useObsStage, syncAcrossTabs, copyText, ToggleList, board.css, locationId in@alltp/engine; three drift bugs fixed (SM/SMZ3 undo shortcut altKey, tab sync storageArea, clipboard fallback); the SM store bounds counts to the pool. jscpd 1.18% to 0.73%. - v2.37.0: live follow on the LAN (feat: follow the desktop app's run live on the local network #320): the desktop app relays the run (axum/hyper on 0.0.0.0:23075, token, 8 viewers, 32 connections, timeouts, pings) to
?view=followpages on phones on the same network; Share dialog section with QR; reviewed by a 25-agent multi-axis review (20 findings, all fixed). Decided: LAN only, no Cloudflare or hosted relay.
Done in the series before (v2.35.0): Half Magic, durable share links, test flakes
- feat(web): a Half Magic cell on the ALttPR board #306 Half Magic on the ALttPR board (equipment row; Agahnim moved to the castles' row; Slash by default, added to saved keymaps only where free; voice "half magic"), and its logic fix:
PACK_ITEMSlackedmagic, so the logic never saw it (Mothula swordless with the Fire Rod and no bottle, Trinexx inverted with the Fighter's Sword). - feat(web): shorter share links for ALttPR and SMZ3 #302 shorter share links (a bitset and a table digest), and in feat(web): a Half Magic cell on the ALttPR board #306 links carry each table's size, so a link stays valid while the tables only grow; reorders and removals are refused.
- fix(web): stop flashing the landing text before the board on reload #304 no flash of the crawler text before the board on reload (it sits in
<noscript>); docs: state the Map Rando race rule as its source has it #305 the guide states the Map Rando race rule as racetime's category info has it. - test(web): wait for the update takeover instead of polling the old worker #307 to test(web): split the SMZ3 axe tests and load the page once #310 four flakes fixed at their cause (PWA takeover poll, lazy dialog import, in-flight analytics routes, SMZ3 axe page loads), no retry or timeout raised.
- Release PR chore(master): release 2.35.0 #303: guard accepted, 4 desktop targets green, 15 assets with
latest.json, the.debsignature checked against the app's key (a tampered copy rejected). - Investigated, no bug: a Mini Moldorm Cave NPC check greyed late in a live run; replaying the real WRAM through the poller, decoder and follower greys it on the next poll. Left open: when the game writes that bit (trace it on the rig if it recurs).
Done in the series before (v2.34.0): Rando Atlas, the public repo, release after the deploy
- Product: feat(web): call the tracker Rando Atlas #293 the tracker is called Rando Atlas (technical ids unchanged), feat(web): the Rando Atlas icon #295 its icon, feat(web): describe each game to search engines and link previews #297 per-game titles and link previews for search engines, feat(web): count visits with GoatCounter #298 GoatCounter visit counts, feat(web): share a run through its URL #301 share a run through its URL.
- Perf: perf(web): serve the maps as lossless WebP #292 the maps as lossless WebP, perf(web): precache only the active game #299 precache only the active game, test(web): run the unit tests in vmThreads #290 unit tests in vmThreads.
- Repo and CI: docs: a README for the public, CONTRIBUTING and SECURITY #300 README with screenshots, CONTRIBUTING and SECURITY; ci: release after the deploy, in the same pipeline #289 release-please runs in
ci.ymlafter the deploy, behind.github/scripts/release-guard.sh, and adesktopjob dispatchesdesktop-release.ymlfor the new tag (release-please.ymlis gone). - Release PR chore(master): release 2.34.0 #294, the first run of that flow: the plan answered
skip(ci, E2E, build and deploy skipped); the guard logged "accepted: this run's own commit"; release-please cut v2.34.0 and the PR gotautorelease: tagged; thedesktopjob dispatchedDesktop release, whose 4 legs succeeded (14 assets pluslatest.jsonat 2.34.0, every platform with a URL and a signature; the AppImage's signature verified and rejected after a one-byte change). No Pages deploy for the release commit; the live site is the previous master commit (97936be) and shows "Rando Atlas".
Done in the series before (v2.33.0): the application menu entry, and a faster CI
- PR feat(desktop): add the AppImage to the application menu #287: the Linux AppImage offers to add itself to the application menu (a notice once on the first launch, and a More menu item, desktop build only).
launcher.rsis the one writer ofio.github.kqesar.alltp-tracker.desktop, the record of the choice; it installs the bundled icons (32, 128, 256 px) and names them, which fixes the generic KDE icon. A launch repoints an existing entry'sExecat the running$APPIMAGE. Checked on a private Xvfb bench; the real KDE and GNOME menus are the owner's checklist. Known limit: GLib refuses an entry whose path holds a . - CI PRs: ci: promote a tree a pull request already tested #286 promotes a tree a pull request already tested (master to the live site in 65 s, with no second build and no second E2E run), ci: three browser-test shards #284 three browser-test shards, ci: skip everything for a release commit #283 a release commit skips ci, E2E, deploy and CodeQL, ci: cancel superseded CodeQL and commitlint runs #282 cancels superseded CodeQL and commitlint runs, ci: save the pnpm store once, from master #280 saves the pnpm store once, from master.
- Release PR chore(master): release 2.33.0 #288: the plan answered "release" and skipped ci, E2E, the site build and deploy; CodeQL's plan skipped too (no wait on a deployed parent, since ci: promote a tree a pull request already tested #286). The 5 jobs of the Release workflow succeeded (release-please plus the 4-target matrix); 14 assets plus
latest.jsonat 2.33.0, every platform with a URL and a signature; the AppImage's signature verified against the app's key and rejected after a one-byte change. No Pages deploy for the release commit, as intended: the live site is the previous master commit (9343373). - Not yet seen: the owner's 2.32.0 → 2.33.0 update on the real desktop.
Done in the series before (v2.32.0): desktop global hotkeys (phase 7, series 3)
- PR feat(desktop): global shortcuts in the desktop app #281: the desktop app steps the tracker from a key that works while RetroArch has the focus, the same actions as the page's shortcuts, opt-in per action, off by default. A mechanism is picked once per process (
shortcuts/detect.rs, a pure function):hotkeys(tauri-plugin-global-shortcut) on Windows, macOS and X11;portal(the xdg GlobalShortcuts portal through ashpd) on Wayland (KDE, GNOME 48+, Hyprland), touching nothing until the first binding;command(alltp-tracker --step <action>, bound in the window manager) anywhere else. The command works under every mechanism, handed to the running app bytauri-plugin-single-instance. - A Global tab in the Shortcuts dialog (desktop only; the site gets no byte of it), key labels from the system keymap on Linux. No
global-shortcut:*permission is granted: the page registers a key only through the app's command, which keeps a floor (a character key needs Control, Alt or Meta), so a compromised page cannot make the app a keylogger. - Fixes on the way: X11 keys released in one request, the Exec line of the desktop file escaped, a
--stepthat finds no page dropped, report types compiled without the portal; clippy now also runs on Windows and macOS. CI: a release commit skips ci, E2E, deploy and CodeQL (ci: skip everything for a release commit #283). - Release PR chore(master): release 2.32.0 #285: the 4-target matrix succeeded, 14 assets plus
latest.json, every platform with a URL and a signature, the AppImage's checked against the app's key (a tampered copy rejected). No Pages deploy for the release commit, as intended: the live site is still the previous master commit (1c5e889). - Update test (2026-10-20), v2.31.0 AppImage on Linux in the private bench (Xvfb kept alive this time): the notice appeared, Restart replaced the file (sha256 equal to the v2.32.0 asset) and relaunched at once: one
alltp-trackerprocess, one window, the app working on 2.32.0.<appimage> --step advance:fluteagainst it reached the running instance (the flute cell changed). This proves the update and the relaunch from 2.31.0, which has no single-instance plugin, so no D-Bus name was held: whether a 2.32.0 holding the name relaunches cleanly is seen at the next release. - Not tested by hand: Windows, macOS, GNOME, Hyprland (built and linted in CI); KDE Wayland is the owner's checklist above.
Done in the series before (v2.31.0): RetroArch read directly (phase 7, series 2)
- PR feat(desktop): read RetroArch without SNI #277: the desktop app reads RetroArch itself over its UDP network commands (
VERSION,GET_STATUS,READ_CORE_MEMORY), with SNI as the fallback for every other device (FX Pak Pro, BizHawk, NWA).RetroArchTransportin@alltp/autotrack(eight pieces in flight, short replies read on,CONTENTLESStold apart from a core without a memory map); aretroarchTauri command (a socket per command, only the three read commands pass). FirstAnsweringTransportand thesupersededrule: a fallback is kept only once it lists a device; a fallback with none gives way to RetroArch the moment it answers, and the session retries at once.- Real rig (RetroArch 1.22.2, release build, private Xvfb): ALttPR Crosskeys on bsnes-mercury connected directly; app opened before RetroArch moves from SNI to RetroArch; no content gives "No game is running"; Snes9x gives "Cannot read the game's memory"; SMZ3 title read at
$C0:FFC0. The player's saves were left unchanged. Measured: no read-size cap, one command per frame; theretroarchskill's 4096-byte cap was false and is corrected. Site bundle: startup chunk unchanged, lazy chunks +477 B. - Two web fixes on the way: the wrong-ROM message names the game it is checked against; the Safari wording points at the desktop app.
- Release: the 4-target matrix succeeded, 14 assets plus
latest.json, every signature valid against the app's key (and a tampered file rejected), Pages deployed on the release commit. - Update test (2026-10-20), v2.30.0 AppImage on Linux in the private bench: it offered "A new version is available"; Restart downloaded, verified and replaced the AppImage in place (sha256 equal to the v2.31.0 release asset). The relaunch found no display only because the bench kills Xvfb when the first process exits; launching the updated file again showed the app with no update notice.
Done in the series before (v2.30.0): the desktop app (phase 7, series 1)
apps/desktop(feat(desktop): the tracker as a desktop app for Windows, macOS and Linux #275): Tauri 2.12 aroundapps/webbuilt in adesktopmode (base/, no service worker, no speech model: it is fetched from the site); no byte of desktop code in the site (inlineimport.meta.env.MODEtests, checked on the bundle).- Autotracking through SNI as on the web: the app's origin is not https, so loopback is open;
detectEnvironmentnow gateshttps:pages only (feat(desktop): the tracker as a desktop app for Windows, macOS and Linux #275's first commit). - Updates:
tauri-plugin-updaterreadsreleases/latest/download/latest.json; the notice offers "Restart". The minisign private key is in the repo secrets (TAURI_SIGNING_PRIVATE_KEY,..._PASSWORD) and with the owner: losing it ends updates. - Releases: the release-please workflow builds 4 targets in parallel when it cuts a release (macOS arm64 and x64, Ubuntu 22.04, Windows), about 10 min, and attaches 14 files plus
latest.json. Verified on v2.30.0: every signature checks out against the app's key, the.debdownloads through the API URLlatest.jsongives. - PR CI: a
rustjob (fmt, clippy, tests) only whenapps/desktopchanges. - Tested on Linux in a private Xvfb bench: rendering, SNI autotracking with RetroArch, links opened in the system browser, Export, persistence across relaunches. macOS and Windows are built in CI, not tested by hand yet.
- Docs:
docs/desktop.md(architecture, updates, releases, signing), the README's per-OS first run (SmartScreen, Gatekeeper).
Done in the series before (v2.27.0 to v2.29.1): Super Metroid and SMZ3 (phase 6, series 2 and 3)
- v2.27.0: Super Metroid (Map Rando): items, objectives, autotracking, tested on a real seed.
- v2.28.0: SMZ3's board, logic and maps; v2.29.0: its autotracking, tested on a real samus.link seed (three bugs found and fixed); v2.29.1: ALttPR reads everything every 500 ms (one cadence, so the state and the saves are read together).
Done in the series before (v2.26.1): one module per game (phase 6, series 1)
- ALttPR's board, map, logic, store, autotracking and voice moved under
apps/web/src/games/alttpr/(refactor(web): one module per game, loaded on demand #259);src/keeps what every game shares. games/registry.ts: the device's game (alltp-tracker-game, ALttPR by default) and its module, a chunk loaded on demand, so a player of one game never downloads another's board.- Startup JS unchanged for ALttPR (571 kB against 569 kB, 169 kB gzip against 167); CI time unchanged (e2e 292 s, ci 129 s).
Done in the series before (v2.26.0): Crosskeys entrance autotracking
- The pack reads where Link went (feat(alttpr): read from memory the doors and holes Link goes through #256):
decodeEntrancesreads the door or hole Link went in by and the door he came out by, as slots and entrance numbers, andlinksOfnames them.- The randomizer writes where each door and hole leads into the ROM's door and hole tables (
Rom.py:490-539). The base patch keeps the last overworld door's slot ($7F:5099) as the game loads its entrance ($7E:010E). The exit is told by the room Link leaves (zelda3overworld.c:1784-1789). - Every range is the race profile's
entrancescategory, so Race Mode never reads one: no new switch, autotracking's casual mode is the opt-in. - The importer adds door slots, hole bytes,
exit_idsand the base ROM's exit rooms, cross-checked against the game's own tables (59 pairs).
- The randomizer writes where each door and hole leads into the ROM's door and hole tables (
- The board notes them (feat(web): note the doors Link goes through in a Crosskeys run #257): as the game's news, once per change, loading the entrance tables only in a Crosskeys run. A note the game contradicts gives way. Of rooms alike (the healer fairies), the one noted there, else one noted nowhere.
- Checked on the real rig with
0yAObedRMV:- offline, the decoder read the rebuilt ROM's 126 doors, 12 holes and 58 exits as the spoiler has them;
- in game, three doors were noted as Link went through them, the Elder House crossed to its east side included;
- deaths and a save and quit noted nothing.
- Found on the way: a respawn keeps the last door's slot (it was said to clear it); the docs now say what the game does.
Done in the series before (v2.25.0): Crosskeys logic
- The entrance randomizer's rules (feat(alttpr): the entrance randomizer's rules in the engine's language #250):
scripts/er-rules.mjswalksRules.pyon vt's path (No Glitches, Open or Standard) and translates each rule into the engine's language: items, the collection state's methods as@er…helpers, bosses fromBosses.py, branches on settings assetting(), placements asmaybe.- The big bomb's route is cases by door. Turtle Rock's key rules and the swamp patch are derived settings.
- Only code stays unported, listed with its lines: the save-and-quit points and the bunny's paths, both ported by hand in the world.
- Its world, held to its tests (feat(alttpr): the entrance randomizer's world, held to its own tests #252):
erWorldbuilds the world from the run's connections.solveFactssolves until no event or reached region is gained.- The randomizer's own tests (510 cases) pass under the pack's contract.
- On a real Crosskeys seed with every door noted, everything held reaches all 233 locations.
- Crosskeys in logic (feat(web): Crosskeys checks follow the entrance randomizer's logic #253, feat(web): say what a Crosskeys check misses, through the doors noted #254):
- the map, the go mode, Why? and the tooltips follow that world through the doors noted;
- a check behind a door not noted says "a door you have not noted";
- the logic loads with a Crosskeys run alone (10 kB gzip).
- Checked on the real rig with the Crosskeys seed: the seed switched the board to Crosskeys and started the timer; a noted door put Blind's Hideout in logic.
- Fixed on the way (test(web): let each shortcuts dialog fetch land before the next test #255): a flaky shortcuts dialog test, reproduced with a 50 ms delay.
Done in the series before (v2.24.0): Crosskeys, the entrance assistant
- The entrance randomizer's tables (feat(alttpr): the entrance randomizer's entrances and targets #246):
packs/alttpr/scripts/import-er.mjsreadscassidoxa/ALttPEntranceRandomizer@8a1ac4d4(the commit vt pins) as data, never runs it.- Door positions come from
door_addresses. Hole positions come from the base ROM's hole table, read via--romand checked by its MD5, 7 rows down asCreatePyramidHoleshows. crossedPoollists what alttpr.com's crossed shuffle mixes (126 doors, 12 holes; Ganon's Tower is shuffled by default).- It is held to a real Crosskeys seed's spoiler.
- Door positions come from
- The assistant (feat(web): note where each shuffled entrance leads #248, feat(web): checks follow the entrance noted to their place #249):
- shuffled doors and holes are on the map (dashed ring until noted, then a glyph per kind of place);
- a searchable picker offers only what the shuffle allows, and notes a hole with its cave's door;
- an Entrances tab lists them all;
- the checks inside a place stand above the first door noted to lead there;
- pointing at a door rings the other sides of the same place.
- The tables load only for such runs.
- Checked on the real rig with the Crosskeys seed: the lake shop's door led into Elder House's west side and its east side out of Good Bee Cave, as the spoiler says. Fixed on the way: overlapping door markers.
Done in the series before (v2.23.0): the "logic soon" run types, part 1
- No more Open logic under "logic soon" (fix(web): no Open logic on a run type the pack does not model #242): a run whose rules the pack does not model (Crosskeys today) shows every check as maybe, with a note over the map;
logicModelledfollows the settings. - Retro (feat(alttpr): the retro world, its rupee bow and generic keys #243, feat(web): Retro runs in logic, with their arrow and key shops #245):
- the pack ports vt's retro world: the rupee bow (an arrow bought,
shopArrow), generic keys (every door once a key is for sale,shopKey), wild keys. The officialRetroBowTestis imported and passes; - the key model never vouches for a generic key: a key door is maybe until a key shop is found;
- the board's last row holds an Arrow shop and a Key shop in Retro runs (click, shortcut, voice "tracker arrow shop" / "tracker key shop"), drawn in the grid's own pixel scale;
- checked on the real rig with a Retro seed generated on alttpr.com: the tracker switched itself to Retro, and the generic key from Link's House stayed no dungeon's.
- the pack ports vt's retro world: the rupee bow (an arrow bought,
Done in the series before (v2.22.0): Mystery
- Settings not known yet (feat(engine): settings not known yet, read both ways #237, engine):
{ unknown: true }and{ between: [lo, hi] }settings, a flag read both ways (Shannon fold), a threshold yes at its top and maybe from its bottom; soundness properties. - The pack (feat(alttpr): a Mystery run's settings, hidden until found #238): a Mystery run's hidden config values derive unknown settings by Kleene's logic.
- The web (feat(web): a Mystery run type, its settings found as the run goes #240): the Mystery run type (Ambrosia's place), eight settings hidden ("? Unknown"), set as found with the run kept, dungeon items changeable mid-run, "goal unknown" go mode; a seed read outside a race reveals them.
- Found on the real rig with seeds generated on alttpr.com, fixed (feat(web): a Mystery run type, its settings found as the run goes #240, fix(web): tell a seed with shuffled entrances from one without #241): a Mystery race seed's file reads as hidden, not unrecognized, and switches a fresh board to Mystery; a Crosskeys seed was taken for Keysanity (the file's
shuffleis now read).
Done in the series before (v2.21.0)
- Voice commands (feat(web): the voice commands' words, grammar and meaning #234, feat(web): mark finds by voice, recognized on the device #236), Phase 4 series 3:
- Evaluated first:
- The Web Speech API is missing in Firefox (the owner's browser), and Chrome sends the audio to a server by default.
- OBS docks have no reliable microphone.
- So: Vosk on the device, opt-in, light and free (the owner's conditions).
- The vocabulary (
logic/voice.ts):- every command starts with the wake word "tracker", so talking about an item marks nothing;
- a command says a find, not a click (held stays held, a level up never wraps, bits apart, a named level never lowers);
- the grammar is the commands, then
[unk]; - names the model's dictionary lacks are said as sound-alikes ("samaria", "bom boss", "a gannon", "gannon tower"), checked against synthesized speech.
- 1,393/1,395 commands were recognized right in three synthesized voices.
- The engine:
@lichess-org/vosk-browserruns in a Web Worker, fed by an AudioWorklet;- the 40 MB model is kept in IndexedDB, and "Remove the speech model" frees it;
- it reached "Listening" in Firefox, and recognized the commands end to end in Chromium;
- about 15 % of one core while hearing speech.
- Weight:
- about +5 kB gzipped on first load;
- the engine is lazy, its worker and WASM are cached on demand by the service worker, and the model is never precached;
- CI fetches the model with its pinned SHA-256.
- The UX:
- the Voice button beside the run timer, always in sight while listening, shows each command for 3 s;
- a live region says each command;
- the dialog has the microphone, "What to say", and the failures with what to do;
- one tab listens (Web Lock).
- Still to do: the owner's test with a real microphone, Firefox included.
- Evaluated first:
Done in the series before (v2.20.0)
- Run timeline and stats (feat(alttpr): read the ROM's own stats outside races #230, feat(web): keep when each find of the run happened #232, feat(web): show the run's splits, timeline and the ROM's stats in a Run tab #233), Phase 4 series 2:
- The pack reads the ROM's own stats, those its end credits show: first sword, boots, flute and mirror times, boss kills by sword, damage, magic, bonks, save and quits, deaths, faerie revivals, menu and lag time, items collected, play time.
- Three ranges of category
other, so Race Mode never reads them; the race read is unchanged at 364 bytes. - Checked on the real rig:
MenuFramescounts one per frame (the credits' 8-bit shift is not its unit), and a save and quit adds one.
- Three ranges of category
- The timeline (
logic/timeline.ts): every item level, bit (the bow's, the boomerangs') and location found is stamped with its instant and the run timer's reading, by the player and by autotracking.- It is part of the run: undo and redo give back the very stamps, and exports carry it.
- No save version: older saves restore none, and 2.19 still imports a 2.20 export.
- The Run tab beside Map | Checks has four parts:
- the run at a glance;
- splits (a table of the bosses in the order they fell, with segments, then the goal);
- the timeline, newest first, with sprites;
- "From the game", outside races once in play.
- Three view tabs: a narrow panel lays them out as a full-width segmented control. The toolbar e2e caught them pushing a 320 px page wider at 150 % text.
- Checked on the real rig: the game's check stamped at 0:00 as the auto-timer started, and the ROM's stats read live.
- The pack reads the ROM's own stats, those its end credits show: first sword, boots, flute and mirror times, boss kills by sword, damage, magic, bonks, save and quits, deaths, faerie revivals, menu and lag time, items collected, play time.
Done in the series before (v2.19.0)
- Run timer (feat(alttpr): read when a game starts, and its play time outside races #226, feat(web): time the run, started and stopped with the game #228, feat(web): show the run timer on stream, in the counters' pixel font #229), Phase 4 series 1:
- The pack reads when a game starts (
beforePlay: the title, the file select, the spawn select, all in the race-legalgame-startcategory). Outside races, it also reads the ROM's frame counter (NMIFrames, categoryother, never read in Race Mode). - The map panel's timer sits beside go mode: start, pause, resume, reset. It is saved with the run but stays out of undo.
- It starts when play follows the screens before it (connecting mid-run starts nothing). It stops at the goal.
- It survives a reload and follows the other tabs and the stream view. The automatic switch to the seed's run type keeps it.
- Outside races, its popover shows the in-game time (frames / 60.0988).
- On stream, optional (
&timer=1, a checkbox in the Stream view dialog): one row under the grid, in the chest counters' pixel font and frame. - Checked on the real rig (RetroArch + bsnes-mercury + SNI): title → file select → spawn select → play started the timer; a menu in play did not restart it; the IGT was read.
- The pack reads when a game starts (
- Fix: the README's stream view sizes now count the grid's eighth row.
Done in the series before (v2.18.0)
- Ganon's Tower's counter (feat(web): count Ganon's Tower's items on its own cell #225):
- Its boss cell (Agahnim 2 and its items left: 20, or 27 in Full Shuffle) ends the 8th row, which now always shows. In standard mode the grid is 7 rows, the map's height.
- Its map square uses the pigeonhole rule, and autotracking derives the counter from its checks.
- The chest counters run to 27, drawn in the font of 10 to 14.
- Node pinned once (ci: set up pnpm, Node.js and the install in one step #223):
devEngines.runtimeinpackage.json.pnpm installfetches that Node (exact version and checksums in the lockfile), and every script runs with it. CI sets up pnpm, Node and the frozen install in onepnpm/setupstep. - Fix (fix(web): name the locations only an Inverted world has #222): Inverted-only locations (the Bomb Merchant) had no name in the Checks list.
Done in the series before (v2.17.0)
- Keysanity polish (feat(web): record the castle dungeons' keys on the grid #220):
- 8th grid row, shown with the key cells: Hyrule Castle's, Castle Tower's and Ganon's Tower's key cells, each beside its short name (HC, CT, GT), as the reference keysanity pack lays its key rows out (CodeTracker).
- Key cells show what their dungeon's pool holds.
- Logic and autotracking: Ganon's Tower goes into logic in MCS / Full Shuffle once its keys are recorded, and autotracking lays those keys too.
- Save v11: 13 small-key counts.
- pnpm 12.14.0 (build: move to pnpm 12.14.0, pinned in devEngines too #219), pinned in
packageManageranddevEngines.packageManager. The lockfile records each pnpm binary's sha512 (pnpm 12 does it frompackageManageralone).
Done in the series before (v2.16.0)
- Inverted (feat(alttpr): lay out the Inverted world and import its tests #214, feat(alttpr): port the Inverted overworld and escape #216, feat(alttpr): port the Inverted dungeons #217, feat(web): track Inverted runs #218):
- Scaffold. The importer reads each world's locations through its class chain and the
removeItems on it, and importstests/Inverted. - The 24
app/Region/Inverted/**files. Classes that keep their parent's rules (parent::initalize()) derive from it withextendModule: the escape and eleven dungeons. The overworld and Turtle Rock replace them all. - Contract. All 22 Inverted test files run in the contract. A test's "Dummy Boss" is given to its region.
- App.
markersOfgives each world its markers: Link's House sits at the Dark World's Bomb Shop, the Bomb Merchant takes Link's House, and Castle Tower and Ganon's Tower swap doors.- The big bomb comes from checking the Bomb Merchant (
EVENT_CHECKS), which opens Inverted's Pyramid Fairy. - The Inverted run type now has logic.
- Upstream quirks, ported as written:
config('cantTakeDamage')has noregion.prefix, so it is never set;has('PegasusBoot')names no item;hasABottle(2)ignores its argument;turtlerock.wastekeyis never set.
- Real setup. Checked with the Oct 16 Daily (Inverted, race): the board switched to Inverted, and Link's House read as done in the Dark World.
- Scaffold. The importer reads each world's locations through its class chain and the
Done in the series before (v2.15.0)
- Logic settings (feat(alttpr): model the item pool and the item functionality #209, feat: a seed's logic settings, from its file on alttpr.com #211, feat(web): tune a run's crystals and logic settings in place #212, feat(web): dungeon items by alttpr.com's levels (MC, MCS, Full Shuffle) #213): the pack models the item pool and functionality (
@canExtendMagic*per bottle refill, held to upstream's formula); a run's settings carry item placement, accessibility, item pool and functionality (save v9) and its dungeon items as alttpr.com names them, Standard / MC / MCS / Full Shuffle (save v10, replacingkeysanity). The seed's file on alttpr.com gives them for every seed whose name is a hash; the run type is decided once the file has settled. The run type dialog's summary tunes crystals, placement, pool, functionality and accessibility in place, one undo step. Checked on the real setup with the Oct 16 Daily (hard pool, hard functionality, basic placement) and the Open seed.
Done in the series before (v2.14.0)
- The seed's file on alttpr.com (feat: read a seed's settings from its file on alttpr.com #207):
packs/alttpr/src/seed-file.tsreadshashandspoiler.metafrom the file alttpr.com keeps per seed, through azod/minischema (5.7 kB gzipped, in its own chunk), into the ROM decoder's terms. Race seeds ask for it by the hash in their header, once per connection; the run type dialog takes a link or hash ("Start from a seed"). Checked on the real setup with the Oct 16 Daily (race, Inverted): settings from alttpr.com, a fresh board switched to Inverted. - CI (ci: pin actions by SHA and unblock Dependabot #205, build(deps): Bump the "all-dependencies" group with 2 updates across multiple ecosystems #206): actions pinned by commit SHA; Dependabot's grouped pull request opens again (its
dockerentry, with no Dockerfile, had blocked it since 2026-09-07).
Done in the series before (v2.13.0)
- PR F (feat(web): say what each check is missing, and define go mode per goal #199, feat(web): Checks panel and go mode (PR F, second half) #203): what each check is missing, worked out at idle; a Checks tab beside the map (filters with counts, a zone select, search, "Why?", a skip link); go mode beside the map's tabs, what it lacks in a popover. Two deviations from the plan, for simplicity: go mode left the toolbar (there it cost every button its label at 1440 px), and the list has no sort control (filters, zone and search cover it).
- The ROM picks the run settings (feat: read a seed's settings from its ROM #200, feat(web): the ROM picks the run settings (second half) #204): a fresh board switches to the seed's run type and says so; otherwise a prompt offers Switch (one undo step) or Keep mine (remembered per seed). The dialog and "What I read" show the seed's settings. "New game" now keeps the run's own settings. Checked on the real setup: an Open seed (settings equal to alttpr.com's), the prompt, Switch then Undo, Keep mine across a reload, and a Daily Challenge in Race Mode.
- Known oddities:
in keysanity the grid cannot record Hyrule Castle's, Castle Tower's or Ganon's Tower's keys(fixed in v2.17.0); Hyrule Castle's key spots show unprefixed names. - Follow-up idea: a manual crystal-count picker in
PresetSelector. - Platform decision (2026-10-17): no proxy, the seed's file read straight from alttpr.com's bucket.
- Why it changed:
alttpr.com/hash/{hash}sends no CORS header, but the file the site's own seed page loads,https://alttpr-patch-data.s3.us-east-2.amazonaws.com/{hash}.json, sendsAccess-Control-Allow-Origin: *(checked withcurl -H "Origin: https://kqesar.github.io", then in Chromium against the real bucket). - What is read:
hashandspoiler.metaonly; the patch in the same file is downloaded and never read (no schema names it). A tournament seed's spoiler holds only its meta; a "mystery" one drops its world and goal, which then read as unrecognized. Approved by the user on 2026-10-17. - Behaviour: non-race seeds still get their settings from the ROM; a race seed's ROM gives only its name (the hash) and logic, and the file gives the rest. Offline, or for a seed alttpr.com does not have, the run type stays the player's.
- Why it changed:
Next on the roadmap
Finish the two second halves, then merge the release-please PR.Done: v2.13.0.- Phase 3 — modes (plan approved 2026-10-17, one release per series):
series 1: the seed's file on alttpr.com, seed import from a linkDone: v2.14.0;series 2: the logic settings as run settings, read from the seed's file; pickers; MC / MCS / Full ShuffleDone: v2.15.0.series 3: Inverted (scaffold, overworld, dungeons, map)Done: v2.16.0;series 4: keysanity polish (the castle dungeons' keys on the grid)Done: v2.17.0.- Follow-ups of series 4:
Ganon's Tower's counter(v2.18.0). Hyrule Castle's checks keep their own map markers. Castle Tower holds two checks. Labels on the ten other key cells were dropped: their boss portraits name them. swordless as a run type(preset since feat: read a seed's settings from its ROM #200).- CI on Node 26 once it is LTS (2026-10-28), straight from 22: approved by the user.
- Phase 4:
timer and auto-timerDone: v2.19.0;a run timeline and statsDone: v2.20.0;voice commandsDone: v2.21.0 (a real-microphone test by the owner pending).
- Phase 5:
Mystery(v2.22.0);Retro logic(v2.23.0); the entrance randomizer (Crosskeys):series 2, the entrance assistant without logic(v2.24.0);series 3, the ER logic ported from(v2.25.0);cassidoxa/ALttPEntranceRandomizer@8a1ac4d4, held to its testsseries 4, entrance autotracking outside races(v2.26.0). - Phase 6: Super Metroid alone, then SMZ3, each on the randomizer the community plays most (measured on racetime.gg race links, 2026-10-19): Map Rando for SM alone (~99 % of 600 races, Aug-Oct 2026; rooms reconnected, logic in sm-json-data, so an item and objective tracker without logic), samus.link for SMZ3 (~97 %; logic ported from
tewtal/SMZ3Randomizerand held to its tests).series 1, one module per game(v2.26.1);series 2, Super Metroid (Map Rando): items, the seed's objectives, autotracking(v2.27.0; tested on a real Map Rando seed through SNI); series 3, SMZ3 (samus.link):the ExHiROM mapping (feat(autotrack): read ExHiROM games, as the SMZ3 combined ROM #266), the logic of(v2.28.0);tewtal/SMZ3Randomizer@productionheld to its four official test sets, 5,490 cases (feat(smz3): port SMZ3's logic, held to its upstream tests #268), its weekly drift watch (ci: watch SMZ3's upstream for logic drift too #269), the board: both games' items, the maps of Hyrule and Zebes by that logic, keysanity's keys and keycards (feat(web): track SMZ3 runs #270)SMZ3 autotracking in both games, race-safe: never the rewards, never a race seed's thresholds or medallions (feat: autotrack SMZ3 seeds #271)(v2.29.0; tested through SNI on a real samus.link seed, which found and fixed three bugs). CI: the browser tests run on two runners since ci: split the browser tests over two runners #264 (about 3 min, from 5.9). - Phase 7: a Tauri desktop app for Windows, macOS and Linux, no app stores, no paid signing:
series 1, the shell, distributed and updated(v2.30.0);series 2, a direct RetroArch UDP transport, SNI as the fallback for other devices(v2.31.0);series 3, global hotkeys: the plugin on Windows, macOS and X11, the xdg GlobalShortcuts portal on Wayland (KDE, GNOME 48+, Hyprland), a CLI fallback for any WM(v2.32.0; KDE Wayland checked by the owner, all 8 points, 2026-10-20);- then: apply to SignPath Foundation for Windows signing (the owner's step).
Planned features, with their status
Legend: ✅ done (on master) · 🚧 in progress (draft PR) · ⬜ to do.
Retained with the user
| Feature | Status | Notes |
|---|---|---|
| Offline PWA, installable | ✅ | F5 always loads the latest version; a stale tab is offered the update; never reloads mid-run (except the OBS view) |
| OBS stream view | ✅ | ?view=obs: grid only, transparent, crisp scale; dock + browser source sync to verify in real OBS |
| Per-item keyboard shortcuts, configurable keymap | ✅ | physical keys (AZERTY-safe), rebinding dialog, master switch; global hotkeys need the desktop app |
| Zero-config autotracking | ✅ | SNI / QUsb2Snes, setup wizard per OS, diagnostics, auto-reconnect, one tab at a time |
| Race Mode compliant with spec v1.2 | ✅ | reads only permitted memory; turns on from the ROM's race flag; race-legal badge |
| "What's blocking me?" (blockers) | ✅ | "Missing: …" in the tooltip and the Checks list's "Why?" |
| "Doable now" Checks panel | ✅ | a Checks tab beside the map: filters with counts, zone, search, "Why?" |
| Go mode indicator | ✅ | per goal (Ganon, fast Ganon, pedestal), beside the map's tabs |
| The ROM picks the run settings | ✅ | auto-switch on a fresh board, else a prompt; a race seed's settings from its file on alttpr.com |
| Auto timer | ✅ | beside go mode; starts at play, stops at the goal (race-legal); the ROM's play time outside races; optional on stream (&timer=1) |
| Run timeline and stats | ✅ | the Run tab: splits, every find timed by the run timer (saved with the run), the ROM's end-credits stats outside races |
| Voice commands | ✅ | "tracker ", recognized on the device (Vosk), opt-in; English; not in OBS docks |
| Seed import | ✅ | an alttpr.com link or hash in the run type dialog: world, goal, swords, crystals, dungeon items |
| QR / state sharing | ✅ | share link (#301/#302), QR code (#314), live follow on the LAN from the desktop app (#320) |
| Entrance randomizer assistant | ✅ | v2.24.0: doors and holes on the map, a picker per door, an Entrances tab, checks following their door; v2.25.0: the logic through the doors noted |
| Mystery seeds | ✅ | settings hidden until found, the run kept; a race seed's file known as Mystery (v2.22.0) |
Modes and games
| Feature | Status | Notes |
|---|---|---|
| Open / Standard, NoGlitches v31 | ✅ | all official cases pass; weekly logic-drift watch |
| Keysanity, MC and MCS Shuffle | ✅ | maps, compasses and keys tracked per level, the castle dungeons' too (v2.17.0) |
| Inverted | ✅ | ported from app/Region/Inverted/**, all official cases pass (v2.16.0) |
| Swordless run type | ✅ | a preset since #200 |
| Retro | ✅ | rupee bow, generic keys, the arrow and key shops on the grid; RetroBowTest passes (v2.23.0) |
| Crosskeys (entrance shuffle) | ✅ | recognized from the seed's file; the entrance assistant (v2.24.0); the entrance randomizer's logic, go mode and Why? (v2.25.0); entrance autotracking outside races (v2.26.0). Inverted crossed and other shuffles stay maybe |
| Item placement, pool, functionality, accessibility, crystals | ✅ | run settings, from the seed's file or tuned in the run type dialog |
| Super Metroid alone | ✅ | Map Rando (the community's): items and the seed's objectives (v2.27.0), autotracking read from the ROM and the game (v2.27.0); the app loads one game's module at a time (v2.26.1) |
| SMZ3 (one combined ROM) | ✅ | samus.link (the community's): pack smz3 from tewtal's source, held to its tests; the board with both maps and keysanity (v2.28.0); autotracking in both games, race-safe (v2.29.0); ALttPR alone, SM alone and SMZ3 are all first-class |
| Desktop app (Tauri), Windows / macOS / Linux | 🚧 | the shell, releases and signed updates (v2.30.0); direct RetroArch UDP, no SNI (v2.31.0); global hotkeys, portal / X11 / CLI fallback (v2.32.0); live follow on the LAN (v2.37.0); stream view and overlay API served by the app (v2.38.0); next: SignPath application; later a BizHawk bridge (Zelda 1, Metroid, OoT) |
Ideas from the competitor research
Race-illegal assists in casual mode:entrance autotracking(v2.26.0); door mapping dropped (2026-10-24).An overlay / event WebSocket API for stream tools(v2.38.0). Next after Race Mode v2.0: Archipelago as its own randomizer.A browser runtime for PopTracker packs: dropped (2026-10-24).
Picking it up with Claude Code
git clone, thenpnpm install(pnpm is pinned inpackage.json).CLAUDE.mdand.claude/rules/*.mdcarry the conventions;packs/alttpr/README.mdandpackages/*/README.mdare the design docs.- Checks:
pnpm lint && pnpm typecheck && pnpm test:coverage && pnpm build && pnpm test:e2e. Judge them by exit code, and gate pushes on them. - CI and local versions: CI runs Node 22, so run Node-API code (WebSocket…) on 22 too.
- Docs:
pnpm check:docsbefore a Markdown change (its owndocs.ymlworkflow). - rtk hook: rtk rewrote
pnpm lintas ESLint and exited 1 on a clean tree; fixed machine-locally withexclude_commands = ["git", "pnpm lint"]in~/.config/rtk/config.toml(add it on another machine). - After a Playwright bump (Dependabot), run
pnpm exec playwright install chromiuminapps/webbefore the e2e. - e2e in parallel: a second worktree takes
E2E_PORT=<free port>. - Desktop app: Rust 1.90+ (rustup) and Tauri's system libraries (Linux:
webkit2gtk-4.1,base-devel,librsvg…), thenpnpm --filter @alltp/desktop dev(HMR on the Vite server) orpnpm --filter @alltp/desktop tauri build.docs/desktop.mdcovers the rest. A local build is unsigned for updates unlessTAURI_SIGNING_PRIVATE_KEYis set. - Home test checklist (autotracking):
- Open seed: items and chests update within ~1 s.
- A manual mark is kept; "Sync from game" overwrites it, and undo brings it back.
- A finished dungeon fills its prize.
- Keysanity key count; maps and compasses.
- A second tab shows "Running in another tab".
- A race seed ("Generate Race ROM") shows the race-legal badge, and the withheld fields stay manual.
Working agreements (for a Claude Code session on another machine)
These come from the previous machine's session memory, which is not in the repo.
- Language: the user writes in French; code, comments, commits and PRs are in English. Never add a Co-Authored-By trailer, nor a "Generated with Claude Code" line anywhere (commits, pull requests, issues).
- Merging: merge your own and Dependabot PRs with
gh pr merge <n> --rebaseonce every check is green and you have reviewed the diff. The owner granted a local permission rule so Claude merges its own green, reviewed PRs (in.claude/settings.local.json, not committed: add it on another machine). - Releases: merge the release-please PR at the end of each PR series, after reading its changelog.
- Checks: judge every check by its exit code, never by its last line. Gate every push on the checks (
ok=1; …; [ $ok -eq 1 ] && git push). - Quality bar (CLAUDE.md):
- fix what you find, in its own commit;
- verify UI in a real browser with Playwright at 390 / 820 / 1440 / 1920, plus axe and keyboard;
- add a dependency only when it earns its place.
- Art and look:
- every image has a transparent background (through
apps/web/scripts/transparent-icons.mjs); - everything not yet obtained (items, bosses, keys, maps, compasses) uses one shared greyed style;
- item icons carry a 1-game-pixel black outline.
- every image has a transparent background (through
- Updates (PWA):
- F5 shows the latest version when online;
- a stale tab is offered the update;
- never reload on its own mid-run, except in the OBS view.
- Platform: front-end only on GitHub Pages. When that blocks something worth building, say so and propose options. The CORS question for race seeds was settled as option 3.
- The user's setup:
- CachyOS with KDE (maybe GNOME or Hyprland later), RetroArch with the bsnes-mercury core, SNI, Firefox;
- plays with a gamepad;
- wants ALttPR alone, Super Metroid alone and SMZ3, and a desktop app for Windows, macOS and Linux with no paid store.
- Parallel work: worktree agents are welcome. Give each its own
E2E_PORT; agents in worktrees may need/usr/bin/gitbecause of the rtk hook.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity
kqesar commentedon Oct 25, 2026
Superseded by #347, which keeps only what is left. This issue stays as the history.