Copied from the agreed story (STORY.md) and the rule sheets
(2026-10-07); those two win if this drifts.
The Orange Portal (Portal A, shot from the front or pre-loaded through Up3) and the Blue
Portal (Portal B, its own VUK, a new hole and up-kicker (owner, 2026-10-19; needs a playfield change); was shot from the Mid-Flipper). The two VUKs
swap: one ball is always "in transit", and a ball entering one portal releases the other's - the
portal transfer. "Now you're thinking with portals." The exit [s-exit-success] behind the turret
bank is the test chamber's exit.
story_p2,sequence portal_sequence — CORRECTED from the original "accrual" choice: accruals complete
in ANY order (verified against the installed MPF's mpf/devices/logic_blocks.py source directly) -
the dropper -> portal-r/m -> exit-success ordering needs sequences: instead, same correction
already applied to dropbank.md's insinerator_finisher. Implemented in
modes/portal/config/portal.yaml.
achievement exit_open_1..5 (x5, chained by config - NOT wrapped in an achievement_group) —
IMPLEMENTED (2026-08-27), correcting two earlier findings on re-verification against
mpf/devices/achievement.py and mpf/devices/achievement_group.py directly: (1) achievements: DOES
default events_when_completed to a clean, distinctly-named achievement_<name>_state_completed
event whenever it's not explicitly set (validate_and_parse_config fills it in) - the earlier "no
clean completed event" finding only looked at the generic changed_state event and missed this.
Each stage's enable_events chains off the previous stage's default completion event. (2)
achievement_group is the wrong device type for a fixed linear chain - it's built for "select one
achievement from several and pursue it" (rotation/random-select semantics), not sequential gating,
and wrapping the chain in one would risk the group's own enable()/select() logic fighting the
manual enable_events chain for no benefit. A plain set of 5 achievements chained by their own
default completion events is both simpler and the better semantic fit. Real bug found and fixed
along the way: disable_on_complete defaults to true for ALL logic_blocks
(mpf/config_spec.yaml's logic_blocks_common) - portal_sequence needed disable_on_complete: false
explicitly, or it would only ever complete once per ball (permanently disabling itself right after
its one completion), which would have capped this chain at stage 1 forever. Implemented in
modes/portal/config/portal.yaml, verified in tests/test_portal.py (5 repeat completions light all
5 stages; progress persists across balls since Achievement state lives on the player, not the
mode). led-exit-open-1..5 are lit via light_player: on each stage's completion event (purple,
matching the slide's violet accent) - the first light_player: usage in this codebase. Since
2026-10-10 (CHANGES #92) the story's exit light trail (modes/major_shots, priority 120) takes
these 5 LEDs over during the chapters that open the exit (1C, 2C, period 4's dial) and hands
them back afterwards.
DRAFT, needs a real game-balance pass with the user: 200/hit for dropper/portal-r/portal-m, 500/hit
for exit-success, +5000 for the full ordered sequence completing, +1000 per exit_open stage (1-4),
+10000 for the 5th/final stage (portal_all_exits_open - now consumed by
modes/progression/config/progression.yaml's wizard_gate accrual as the Phase 5 wizard-mode
gate's deliberate final required key, alongside the 3 feature tiers). Implemented in
modes/portal/config/portal.yaml.
From rule-sheets.md, not built yet: the rules above are what the mode does
today. Counts, timers and scores are drafts for the balance pass.
sfx_Portal2_sfx_portal_gun_fire_blue for a Blue Portal..._orange for an Orange Portal shot (design/callouts.md's effects table).Most involved feature in Phase 3 (multi-switch ball-tracking state machine) - build it last, once
the simpler shot_group pattern is proven out by lanes/orbits, per the resumption roadmap (CHANGES.md
#3). The playfield schematic (design/research/onshape-cad-findings.md) showed three "Duplicate Exit"
fragments near the top-left - confirmed by the user to just be one Exit part split into 3 pieces for
3D-printing, not three separate mechanisms. That was a dead end, not a lead: the ball-lock "portal"
hole-pairing mechanic the user described (hole A holds a ball, hole B capturing a new ball instantly
releases A - giving a teleport effect) is now RESOLVED (2026-08-27): the CAD does not support it.
Opened balldropper/VUK/Exit/ DropperAssy directly - VUK/VUK-high/Exit are unused orphaned stub part
studios (a single bracket or cover each), never instanced in the real top-level assembly. The actual
built machine has exactly one DropperAssy (single valve/riser actuator) feeding a passive Subway/
Tube path to a single Portal Vuk exit - one linear path, no second capture point, no linkage that
could release one ball when another is captured. See design/research/onshape-cad-findings.md's
"Resolved" section for full detail. This confirms (rather than contradicts) the sequence-based rules
below: a strictly ordered dropper -> transfer -> exit path is what the physical machine actually is.
If the instant-teleport pairing effect is still wanted, it needs new physical design (a second
capture point + coordinating mechanism), not something achievable via MPF config alone.
launch-lane-diverter-2) and a diverterorbit-top-diverter); which portal each feeds is open. Portal A's