What to build: Every implemented card from the two smaller packs (~15) plays through the registry system, same shape as the System Gateway migration: one contiguous block per card in its pack's module, match arms deleted as each card moves.
Blocked by: 16 — Expand: registry infrastructure + pilot card. (Can run in parallel with 17.)
Status: fixed
All 15 implemented cards — 8 Elevation, 7 Voices of the Past (every
implemented entry in game/cards_impl/elev.json / vp.json, none of them
vanilla) — now live in cards/elev.rs and cards/vp.rs as one contiguous
reg.card("…") block each, in NRDB code order, followed by their hook
implementations in the same order. 26 registrations (14 elev + 12 vp), which
takes the live registry to 72 cards / 133 registrations.
Hook bodies moved verbatim; the only edits were signature adjustments where a
dispatcher passed arguments the port-era function didn't take
(methuselah_run_end gains _successful, sipa_outermost_broken gains
_prog_id), and inline match-arm bodies getting names
(azimat_install, devadatta_install, side_hustle_install). hiram_peek is
registered on both on_hardware_install and on_hardware_trash, which is what
the two arms did.
Two cards needed more than a move:
RUN_EVENT_PRE was inline in
engine::play_event because beta_build_pre_run needs start_run_tagged.
That helper is now pub(crate) (same precedent as Diviner in 02), so the
hook moved to cards/vp.rs unchanged and play_event consults only the
registry.
credits to pay trash costs" is *not* a hook registry on either side of the
port (none of the 45 mirrored registries covers it), so it stays inline in
engine::trash_card and legal.rs's Access affordability check. Both sites
now carry a comment saying so, and cards/elev.rs's module doc names it, so
it doesn't read as a stray un-migrated arm.
With this, no dispatcher has a match-arm fallback left. Seventeen
dispatchers in hooks.rs plus the RUN_EVENT_PRE site in engine::play_event
lost their arms; sixteen of those collapsed to a registry lookup plus their
documented default (run_event_spec → None = "not a run event",
pre_access_prompt / outermost_ice_broken_prompt → false, trojan_codes →
contains, and so on), and runner_install_cost is now purely the
INSTALL_COST_MODS loop. hooks.rs is 894 → 631 lines and holds dispatchers
only.
Ordering: INSTALL_COST_MODS is applied in registration order, so
registry::build calls sg → elev → vp, which keeps Carmen, DZMZ, Principia in
the order the port had them. That constraint is documented at build.
registry_manifest.rs is unchanged apart from its doc comment — a migration
moves *where* a code is registered, not whether — so the frozen-fixture parity
test passes untouched.
Testing: 02's every_system_gateway_registration_is_in_the_registry was a
pack-specific check living in cards/sg.rs; with every pack migrated it is a
registry-wide invariant, so it moved to cards/registry.rs as
every_manifest_registration_is_in_the_registry, with the 30-prefix filter
dropped and the guard counts raised to 72 cards / 133 registrations. Since no
fallback arms remain, that test is now the machine-checked form of "every
implemented card runs from its pack module" — a card left out of a block would
be missing outright, not silently handled elsewhere. Verified non-vacuous by
commenting out Knickknack's registration (test fails). No new per-card tests:
these 15 cards are has_tests: false in their manifests, i.e. the same
grandfathered set issue 05 addresses.
cargo fmt --all -- --check: clean. `cargo clippy --workspace --all-targets-- -D warnings`: clean.
cargo test --workspace: green — 56 core unit tests + 5 AI invariants (samecount as before; the parity test moved rather than being added).
make rust-py, including
test_registry_parity.py against the frozen golden manifest.
afterwards) played 40 deterministic epsilon-heuristic games — 20 as Tāo
Salonga, 20 as Hiram, whose identity hooks only fire in that slot — with a
runner deck containing all 15 migrated cards against the System Gateway
upgraded starter corp, dumping each game's full log. Every one of the 15
cards appears in the output (27–162 log lines each), and the 13,356-line dump
is byte-identical before and after the migration. selfplay 12 200 (24 games
of the two SG starter decks) is also byte-identical, confirming the shared
dispatchers and the INSTALL_COST_MODS order are unchanged for the cards
migrated in 02.