Numbered log of key decisions and milestones, with current status (active /
superseded-by-<entry> / rejected). Reconstructs why the project looks the way it does.
Initial hardware bring-up (Sept–Oct 2024, commits ce51795..0afd57b). Wired and
configured the OPP gen2 platform across 3 serial chains, mapped ~35 switches, 8 coils, and 40
LEDs, built minimal base/attract modes with flat per-switch scoring, and play-tested on
real hardware for 23 games (see machinefolder/data/audits.yaml). Flippers were never wired
into this pass. — Status: active, this is the current baseline.
~22-month dormancy (Oct 2024 – Sept 2025). Only activity was an accidental commit
(d8e97eb) that added a 126k-line stray log file with no real changes. — Status:
superseded-by-3, project resumed.
Resumption + repo hygiene (2026-08-25). Assessed the dormant project, wrote a phased
resumption roadmap (flippers → rules architecture → feature modes → display/audio), and did
Phase 0 cleanup: stopped tracking machinefolder/logs/ in git (kept existing history intact),
added the standard project docs (README.md, TODO.md, IDEAS.md, this file) and a
project-level CLAUDE.md. — Status: active; the roadmap itself is finished and deleted, see
#48.
Story→shots→modes design docs, live Onshape research, and config naming cleanup
(2026-08-25 – 2026-08-26, commits 4b848f5..2ce9fd1). Added the schema-tracked
design/features/*.yaml workflow (one file per Phase 3 feature, validated by
tests/test_design_docs.py) and design/README.md's story→feature→shots→rules→presentation
layer model, replacing board Overviews.xlsx's "Modes" sheet as the living feature reference.
Connected directly to the "Portal Playfield" Onshape document via onshape-mcp and folded
real CAD findings into all 8 feature files (design/research/onshape-cad-findings.md),
confirming aerial/ramps/portal/orbits/slings against real geometry and honestly flagging
lanes/skillshot/dropbank's remaining gaps rather than forcing matches. Standardized every
switch/coil/device name in machinefolder/config/hardware-*.yaml on hyphens (was a mix of
underscore and hyphen), fixing the pre-existing popbumber/popbumper spelling mismatch
along the way, verified via a clean mpf -X -t -b boot and a green test suite. Added
design/onshape-mpf-overview.md, a single table cross-referencing every feature's Onshape
CAD name(s) against its MPF switch/coil/LED names. — Status: active.
Software-side gaps closed while flipper/tilt/diverter wiring stays hardware-blocked
(2026-08-27). With the physical cabinet unavailable, worked through every gap that's fixable
in config/tests alone: enabled MPF's built-in service mode (found the installed 0.80.0
hardcodes its nav switch names in _get_key(), ignoring its own mode_settings:
config_spec — the sw_service_* switch names in hardware-switches.yaml are named to match
that hardcoding, not this project's usual s- convention, deliberately) and ball_search
(playfields: enable_ball_search: true); gave the sling combo a real time window via a
timers: device that restarts on every hit (logic_block_timeout can't do this — it starts
from mode enable, not the first hit); made dropbank's bank-completion score escalate per
repeat within a ball; implemented portal's 5-stage exit_open achievement chain (both
earlier "can't be done" findings for this were wrong — achievements: does default a clean
per-stage completion event, and achievement_group turned out to be the wrong device type
for a fixed chain); and electrically drafted the CAD-confirmed right ramp diverter/subway
hardware (same draft-ahead-of-wiring pattern as the Phase 1 flippers). Root-caused a
previously-unexplained stuck full-game test: not a bug, the test drained a ball before it was
ever launched onto the playfield, desyncing playfield.available_balls and hanging every
later ball_starting. See plans/testing-strategy.md for the reusable testing-methodology
findings from this pass (trough-fill mismatch, plunger eject-confirm delay, etc.).
— Status: active.
Phase 5 stretch goals: bonus tally, multiball, tiered wizard mode (2026-08-27), per two
user decisions gathered mid-session: multiball's trigger ("complete a feature bank" →
db-dropbank, the one Phase 3 feature that's literally a bank) and the wizard mode's shape
(tiered mini-wizards — 3 feature tiers plus portal's own exit_open chain as the deliberate
final required key, not one single gate). All scoring is explicitly DRAFT, and super_wizard
mode implements the gating only, not real wizard-mode gameplay — both flagged as open
design questions, not oversights. — Status: active.
MPF config formatting unified on a 2-space indent (2026-08-27). hardware-devices.yaml
and hardware-switches.yaml were the last two files still using a 4-space step (inherited
from before this project's Sept 2024 bring-up); reindented to match every other config/mode
file, whitespace-only, no config values touched. No mpf format/lint tool exists to automate
this (plans/testing-strategy.md Tier 3) — done by hand, boot-tested clean. — Status:
active.
Visual/audio polish pass: lane LEDs, LED shows for all 8 Phase 3 features, the multiball
slide, and a first real-sound baseline (2026-08-27). Drafted a 5th bottom-lane LED
(led-lane-b5, a previously-unused LED chain) and wired simple hit feedback for all 8 lane
shots; gave every Phase 3 feature a real MPF shows: LED flourish matching its design doc's
described motif; built the multiball slide (user chose a dropbank fire tie-in over a
feature-independent treatment, rendered and visually verified in Godot 4.3). Sound had been
left untouched (no suitable assets existed) until the user located their own full Portal 2
sound extract on this machine — picked one clip per feature from it, wired via
sound_player:, explicitly as a DRAFT baseline pending the user's own listen-through and
adjustment (tracked as an open manual action in TODO.md). — Status: active.
Hardware bring-up console (2026-08-30), ahead of starting real-hardware reconnection
(boards first, then one component at a time). Built tools/hw_console/ — a local Flask +
vanilla-JS web tool tracking per-board and per-component wiring status (planned/wired/
tested), a wiring checklist per component (flyback diode / common ground / same-board rule,
from TODO.md's OPP checklist), and a collision check against hardware-switches.yaml/
hardware-coils.yaml before a new component's numbers get assigned. It's a separate tracking
layer, not a replacement for MPF's own config — seeded from the current state of
hardware-*.yaml/TODO.md (DRAFT items as planned, everything else as wired, reflecting
the 23-real-games history from entry 1). board Overviews.xlsx is deliberately not
auto-parsed — numbers are transcribed by hand as each component gets planned, same as before
this tool existed. Found two real gaps while seeding it (recorded in the registry's notes, not
fixed here): the VUK switches and the portal dropper switch have no matching eject coil
configured anywhere. — Status: active; its per-component status field was later
replaced by parallel flags, see #32.
tools/mpf-session.ps1 for running mpf from a non-interactive session (2026-08-30),
found while doing the first real-hardware bring-up test this laptop. Plain mpf (real
hardware, full text UI) crashes immediately when run from a Claude Code tool session —
asciimatics can't open a console screen buffer without a real console attached; MPF's own
-t flag fixes that (confirmed working: connected cleanly to all 3 OPP chains, correctly
read all 6 trough switches + jam as active). Separately, a backgrounded mpf process's PID
as seen from a Bash/MSYS shell doesn't match its real Windows PID, so kill from Bash
silently failed to find it — it was still running, undetected, until located via
Get-Process/Stop-Process from PowerShell. tools/mpf-session.ps1 wraps both fixes:
always passes -t, tracks the real Windows PID via Start-Process -PassThru, and gives
Start/Stop/Status/Log actions with log tailing and stale-state cleanup. Stop is a
plain tracked Stop-Process, not a true graceful Ctrl+C — verified safe on this hardware
(OPP boards de-energize drivers on serial disconnect, COM ports release cleanly, confirmed
via mpf hardware scan immediately after a hard-stop); a true graceful shutdown was
considered and deliberately deferred, see TODO.md. — Status: active.
First real-hardware flipper bring-up: wiring confirmed, tools/wiring_test.py built,
and a live gameplay test (2026-09-06). With the flipper switches/coils wired for real
(see TODO.md), built tools/wiring_test.py — a no-HV wiring check reusing MPF's own
service-mode BCP commands (list_switches/list_coils/coil_pulse) instead of talking to
the hardware directly, generic via --switches/--coils so it covers future components
(tilt, diverter, VUKs) too. See plans/wiring-test-tool.md for the design and findings.
Ran it end to end: both flipper switches PASS (open→closed→open), both coils PASS (LED
confirmed with 50V off, real mechanical travel confirmed with 50V on). Then went further and
actually played a real game — with neither the start nor launch button physically wired yet,
used MPF's inbound BCP switch command ({"name": ..., "state": 1/0}, the same mechanism
the Godot display's [keyboard] mapping uses) to inject those two switch presses, confirming
the flippers: device (enables on ball_started) holds correctly under real button
press/release during actual gameplay, not just isolated pulses. Three real findings along
the way, all logged in TODO.md: (1) mpf's service BCP server needs -NoBcp to start at
all - without it, mpf blocks forever on a required outbound connection to a display that
isn't running; (2) Windows USB selective suspend caused a genuine mid-session serial
disconnect on an idle Cobra board, fixed via powercfg; (3) the flipper coils' 25ms
configured default pulse is too weak for real travel with HV on (200ms worked, untuned) -
real tuning is still an open decision, and separately, the trough→plunger ball transfer is
failing/retrying in a real game - a new, unrelated gap. — Status: active.
Start/launch switches wired onto the flipper Cobra board, both confirmed working
(2026-09-06). Moved s-start/s-launch off their earlier DRAFT 2-0-17/2-0-18 PSOC-chain
placeholders onto the same Cobra chain 0 board (0x20) the flippers use: s-launch →
0-0-3 (PASSED first try), s-start → initially 0-0-8, which read stuck CLOSED regardless
of press/release. Root-caused by reading the actual CobraPin board-config toolchain
(C:\Users\dorus\Documents\Pinball-Code\Tools\Boardconfig files\Cobra\):
CobraPin_Board0_servos.py vs. the plain CobraPin_Board0.py differ only in the input
config array, where indices 8-11 get a special \x96 byte instead of the normal
CFG_INP_STATE — that board's servo config dedicates inputs 8-11 to servo/PWM use, not
switch inputs. Moved s-start to 0-0-27 (outside that range) and PASSED. Inputs 0-0-8
through 0-0-11 on this board are now off-limits for future switches unless it's re-flashed
back to the plain config. Both confirmed live via the same BCP mechanism
tools/wiring_test.py uses. Also hit a more severe repeat of the flipper bring-up's USB
disconnect issue: all 3 OPP boards' COM ports wedged into Status: Unknown after one
ClearCommError, not just the affected port — the earlier powercfg selective-suspend fix
didn't fully prevent it this time. Only a physical USB unplug/replug at the PC end recovered
it (board-side reseat and an unelevated PnP disable/enable both failed). See TODO.md.
— Status: active.
Plunger lane and trough-eject coil moved to the Cobra board (2026-09-12). Moved
s-plunger-lane (2-0-16 → 0-0-26), c-plunger (2-0-2 → 0-0-10), and c-trough-eject
(2-0-3 → 0-0-11) from the PSOC chain to Cobra chain0-0x20, both coils on Bank A (HV_A,
brown) alongside the flippers, per user decision. Trough switches (s-trough1..6/
s-trough-jam) were unaffected, staying on 2-1-16..22 (PSOC chain2-0x21) pending the opto
board repair (see TODO.md). Wiring confirmed 2026-09-12: s-plunger-lane correctly
tracks physical ball motion at 0-0-26; both coils PASSED the no-HV activation-LED check,
then PASSED again with real 50V present — c-plunger launched a ball onto the playfield
correctly (twice), c-trough-eject fed a ball into the plunger lane correctly.
— Status: active.
Slings moved to the Cobra board; new ball-saver post component wired and tuned
(2026-09-13). Migrated the autofire slings from the PSOC red board (2-0-0/2-0-1) to Cobra
chain0-0x20 Bank A, same pattern as entry 13, and added a brand-new ball-saver post component
(c-ball-saver/s-ball-saver) — a software-triggered short-pulse coil (no hold power) that
kicks a post between two positions, with a switch reporting which position it's in. Real
hardware testing with tools/wiring_test.py found two switch mislabelings in a row (the wire
assigned to s-left-sling actually landed on the right sling; then s-left-sling and
s-ball-saver turned out crossed too) and one coil mislabeling (c-sling-left/
c-sling-right were also swapped relative to their names) — all four numbers corrected to
match physical reality: s-left-sling=0-0-19, s-right-sling=0-0-25,
s-ball-saver=0-0-24, c-sling-left=0-0-12, c-sling-right=0-0-0. All switches and
coils PASSED their no-HV and real-50V checks after the corrections. Tuned the ball-saver's
pulse empirically (25ms → 15ms → 20ms, all wrong, then 40ms confirmed correct) — it needs
far more than this board's other short-pulse coils (15-25ms) because the small spring holding
its mechanism against its pin (which connects to the plunger) is too weak, leaving too much
play before the coil's pull translates into motion; noted as the first thing to check if this
coil ever needs re-tuning again. Also confirmed the ball-saver's position mapping live via
MPF's BCP service: down/not-saving = active/closed/pressed, up/saving = inactive/open. Added
docs/wiring-pin-map.md, a single readable pin-map/status table across every component,
cross-linked from README.md and docs/opp-hardware-reference.md. — Status: active.
Knowledge-management overhaul, part 1: wiring-guide.html/wiring-pin-map.md become
generated, not hand-transcribed (2026-09-13). wiring-guide.html's own footer had claimed
to be "generated from" components.yaml since it was written, but no generator ever existed —
722 lines of hand-transcribed HTML, and TODO.md had flagged its status-badge vocabulary as
drifted from components.yaml's newer 1-6 scale as a result. Considered against MkDocs (a
full static-site toolchain — rejected: heaviest maintenance for a one-reader audience, no
benefit to the AI collaborator since Claude already reads the source YAML directly) and
against leaving it hand-maintained (rejected: the thing this decision exists to fix). Built
tools/hw_console/generate_docs.py — Jinja2 templates reusing registry.py's existing
loaders, rendering both files from components.yaml/hardware-*.yaml, with atomic
all-or-nothing writes (a bad harness or missing Graphviz leaves the previously-committed files
untouched, verified with a deliberately broken harness reference). The "04 Known components"
badges (wired/planned/gap) are now a documented, single-source-of-truth mapping
(status >= 5 → wired, notes containing GAP: → gap, else planned) instead of manual
per-row judgment — this alone fixed a real, already-stale badge (Pop bumpers showed wired
despite status: 4, unwired). Also folded in real generated wiring diagrams via
WireViz: a new tools/hw_console/data/harnesses/
(native WireViz YAML) replaces the flipper coil-bank's hand-drawn SVG sketch with one rendered
from real, sourced data (docs/opp-hardware-reference.md's per-pin bank/HV-feed/color table).
Wired into the existing sync points — the PostToolUse hook now runs the generator after
check_registry.py passes, and the wire-component skill's step 5 points at it instead of a
hand-edit. Proven for real, not just locally: merging in parallel real-hardware bring-up work
from main (entry after this one, bottom-lane wiring) produced a clean 3-way merge conflict
only in the two generated files — components.yaml/hardware-switches.yaml merged
automatically with zero conflicts, and regenerating from the merged data resolved the rest,
exactly the property this decision was chosen for. — Status: active.
Knowledge-management overhaul, part 2: external reference archive, CI, branch protection
(2026-09-13). docs/opp-hardware-reference.md, docs/stern-spike-trough-opto.md, and
plans/OutsidePerspective.md all cited external docs (MPF doc site, CobraPin/OPP hardware
docs, pinballmakers.com wiki, PinWiki) by live URL only. Archived 22 already-cited sources
under docs/references/raw/<slug>/content.md in a one-time sweep; docs/references/index.md
is itself generated (via the same generate_docs.py from entry 15) from
docs/references/index.yaml, applying the same "generate, don't duplicate" principle to the
archive's own index rather than hand-maintaining a fourth table. Also added .github/ workflows/ci.yml (this repo had no .github/ directory at all, and TODO.md already had an
open, unowned item requesting unittest discover on push) — runs the test suite,
check_registry.py, and a doc-freshness check (re-runs the generator, diffs against
committed output). The doc-freshness check needed a real fix post-launch, not just
config: WireViz's embedded SVG isn't byte-reproducible across Graphviz/font environments (this
Windows/Arial dev machine vs. the Ubuntu runner's older apt-packaged Graphviz and different
font substitution both shifted text-layout coordinates) — fixed by stripping
Graphviz-generated <svg> blocks from both sides before comparing, verifying the actual
data-driven content without false-failing on rendering-environment variance; confirmed via a
real push (not just reasoning about it) that this correctly still fails on a genuine
hand-edit. Enabled branch protection on main (required CI check, PR required) once CI was
green — deliberately enforce_admins: false after discussing the tradeoff: the repo owner's
own pushes still bypass it (confirmed via a real, immediately-reverted test push), while a
separately-scoped credential (a future Wiki.js deploy key, if that gets built) would not.
— Status: active.
Datasette (for Component_database) scoped in, then correctly walked back (2026-09-13,
part of the knowledge-management overhaul). An earlier pass of this work scoped in a Datasette
instance on the NAS to browse the sibling Component_database repo's SQLite parts inventory,
reasoning the same NAS/Docker infrastructure being stood up for a planned wiki made a second
small service low-cost. Independent review found this broken two ways before anything was
built: that repo's .gitignore excludes data/*.db entirely, so a git-pull sync would never
receive the database file; and more fundamentally, Component_database already has its own
deployed FastAPI app (auth, browse/search/filter/gallery, QR labels, CSV export, a JSON API)
live at 192.168.1.2:8091 and parts.famvanderlinden.nl. Building Datasette on top would
have been a second, less-capable, differently-authenticated surface for data that already has
a purpose-built one — recorded here so this doesn't get silently re-proposed later without
re-discovering the same two reasons. Added docs/online-services.md instead, linking the
existing app directly. — Status: rejected (Datasette specifically; docs/online-services.md
itself is active, see entry 15/16 for the broader initiative it belongs to).
Wiki.js deployed to the NAS; deploy keys needed an org-level setting change
(2026-09-13). Deployed Postgres + Wiki.js containers to
weather-reader-nas:/volume1/docker/portal-pinball-wikijs/ (see nas-deploy/wikijs/),
reachable at 192.168.1.2:8095 and indexed on nas-dashboard. Generated an SSH deploy
keypair for Wiki.js's git storage module, per the design in entry 16 — GitHub initially
rejected registering it: Dorus-Pinball is a GitHub Organization, and the org had deploy
keys disabled repo-wide as a default security policy (HTTP 422: Deploy keys are disabled for this repository, i.e. deploy_keys_enabled_for_repositories: false org-wide). Not a bug —
correctly treated as a decision needing the user, not something to override unilaterally
(this is an org-wide policy, not scoped to just this repo). A fine-grained Personal Access
Token was drafted as the standard alternative, but Dorus instead chose to enable the org
setting directly (Organization Settings, confirmed via deploy_keys_enabled_for_repositories: true after) — the originally-generated deploy key was then registered successfully
(gh repo deploy-key add --allow-write, confirmed via gh repo deploy-key list), so the PAT
path wasn't needed after all. Remaining setup (Wiki.js's own first-run wizard, git module
config with the now-registered deploy key, search engine config) is still manual — genuinely
can't be scripted, not a credential issue — and tracked in nas-deploy/wikijs/README.md's
checklist. — Status: active.
Board silkscreen ↔ MPF number reference, extracted from board Overviews.xlsx
(2026-09-13). The xlsx has always been treated as a legacy, never-auto-parsed artifact (per
tools/hw_console/README.md) — true for its role as a live tracker, but it turned out to
also contain real, never-extracted value: 7 embedded images (a real CobraPin board photo with
MPF numbers printed on the silkscreen, a full system wiring diagram, a screenshot of
CobraPin's own published STM32 connector-mapping PDF, a CY8CKIT-049-42XX chaining diagram, and
3 generic OPP wing wiring diagrams) and complete physical-pin-to-MPF-number tables for all
four red boards (PSOC4200 - Game Function sheet) that were never pulled into the repo's own
docs. Extracted the images into docs/board-photos/ (plus a real cabinet photo of this
project's own red board, shared during this session) and built
docs/board-silkscreen-reference.md, cross-referencing every silkscreen-visible pin label
(J1-J12 on CobraPin, P0.x-P3.x on the red boards) against its MPF number — lets a
future wiring/debugging session identify a pin from a photo without re-deriving the mapping.
Explicitly labeled as reflecting the sheet's original/historical assignments, not live
status (several components have since moved — see entries 11-14) — components.yaml stays
the source for what's actually wired today. Added a generated SVG pinout diagram per red
board (docs/board-photos/pinout-0x2{0,1,2,3}.svg) alongside each table — silkscreen pin,
MPF number, and device name per cell, color-coded by function (switch/coil/autofire/light) —
for a quick visual glance at the bench, with the table kept alongside for searching/scanning.
— Status: active.
Hooks/skills caught up to the KM overhaul, and wiring_test.py made low-friction for the
full rewire (2026-09-13). Two independent gaps closed in one pass, both raised while the
user is mid-rewire (new pieces going to new board locations, old wiring not considered).
First: the KM overhaul (entries 15-19) left docs/references/index.yaml unwatched by the
PostToolUse hook that already auto-regenerates wiring docs from hardware config — added it
to tools/hw_console/post_edit_hook.py's watched paths, and added a cite-reference skill
(mirroring wire-component's structure) so citing a new external source and archiving it
under docs/references/raw/<slug>/ happen in the same pass, same as the existing convention
already asked for in prose. Second: wiring_test.py (entry from plans/wiring-test-tool.md,
2026-09-06) required typing an exact safety phrase before any run, even switch-only ones —
confirmed MPF 0.80 core has no built-in coil-pulse confirmation anywhere (checked
commands/service.py, core/bcp/bcp_interface.py, modes/service/code/service.py), so the
phrase was entirely this project's own friction. Replaced it with a single Enter press, gated
only on --coils actually being non-empty, and added a --monitor mode (MPF's BCP
monitor_start/"switches" live push) that snapshots and then streams every switch's state
so a freshly-wired switch's MPF name can be read off by tapping it, instead of needing to be
named up front. — Status: active.
Wiki home page rewritten to explain the KM concept; new docs/wiring-workflow.md for the
human-facing version of the rewire process (2026-09-14). home.md linked to every doc but
never explained why the docs are organized the way they are — rewrote it to introduce the
KM concept up front (generated vs. hand-written vs. reference-archive vs. decision-log pages)
and reorganized its link groups to match, adding two pages that existed but were never linked
from it (docs/board-silkscreen-reference.md, docs/references/index.md). Added
docs/wiring-workflow.md, a plain-language walkthrough of how wiring actually happens now
that the rewire is underway: name the component, name its coil link, identify the switches
together using wiring_test.py --monitor (entry 20) rather than assuming names up front,
then test low-voltage before high-voltage. Cross-linked from
docs/opp-hardware-reference.md and .claude/skills/wire-component/SKILL.md so the
human-facing and Claude-facing process descriptions don't drift apart. — Status: active.
docs/wiring-guide.md — the wiring guide, browsable in the wiki (2026-09-14).
design/physical-checklists/wiring-guide.html is a full standalone HTML document (its own
<html>/<head>/<style>); synced into Wiki.js as a page it nests badly inside Wiki.js's
own page chrome. Rather than exclude it from the wiki entirely, added a second generated
output — same generator, a new wiring_guide.md.j2 template — covering the parts that are
genuinely data-driven (board map, known-components table) plus the WireViz harness diagrams,
now also written to real docs/wiring-diagrams/*.svg files (markdown image, not inline SVG —
more reliable through a wiki's Markdown renderer). Deliberately does not duplicate
wiring-guide.html's sections 02-03/05-13: those are hand-authored prose baked directly into
the HTML template (dated pass/fail notes, pulse-ms tuning history), not derived from
components.yaml — copying ~400 lines of that into a second hand-maintained file would be
exactly the kind of drift-prone duplication this project's generated-docs discipline exists
to avoid. The new page links to opp-hardware-reference.md for the rulebook content already
documented there, and points to the printable HTML checklist for the dated log rather than
forking a copy of it. CI's doc-freshness check (.github/workflows/ci.yml) extended to cover
the new file; the harness SVG files themselves stay unchecked there, same as the existing
exemption for wiring-guide.html's embedded copy (WireViz/Graphviz output isn't
byte-reproducible across environments). — Status: active.
flipper-bank-a.yaml → bank-a.yaml: one harness diagram per physical bank, not per
component (2026-09-14). Bank A actually has 7 of its 9 pins wired & tested — flippers,
both slings, ball saver, plunger, and trough eject — but only the flipper pair ever got a
WireViz harness. Rather than add a separate small harness file per component (the same
physical HV bus and driver header drawn over and over), replaced flipper-bank-a.yaml with
a single bank-a.yaml covering the whole populated connector. Confirmed with the user first
that the other 5 coils are each independently wired (their own 2-wire pigtail to the shared
bus/header) rather than sharing a connector the way the flipper pair does — this project's
harness files are meant to be real, sourced data, not a guess, so that physical-topology
question got asked rather than assumed. docs/wiring-diagrams/flipper-bank-a.svg removed
(superseded by bank-a.svg); wiring_guide.html.j2's section 05 and
wire-component/SKILL.md's worked-example pointer updated to match. — Status: active.
Wiring diagrams simplified to driver-signal-only; silkscreen added as a real
components.yaml field (2026-09-14). The Bank A diagram modeled every coil's full cable
run including the shared HV_A bus and its brown 2-wire feed - already covered once, clearly,
by docs/opp-hardware-reference.md's bank/HV-feed table and the printable checklist's
section 02, so repeating it per coil was noise. Per the user's explicit spec (board name,
silkscreen label, MPF number, connection name - nothing else), rewrote bank-a.yaml to model
only the driver-signal side: one pin per coil, no HV/ground. Added silkscreen as a real
field on switch/coil entries in components.yaml (populated only where status ≥5 and
sourced from docs/board-silkscreen-reference.md - never guessed) rather than typing it only
into the harness YAML, since the user also wanted it in docs/wiring-pin-map.md's table -
_pin_str() now shows it there too, skipping the parenthetical when it's identical to the
MPF number (true for every CobraPin coil bank pin, where the silkscreen prints the number
directly) to avoid showing the same text twice. .claude/skills/wire-component/SKILL.md
updated with both new conventions for future components. — Status: active.
Row-per-pin wiring table; CobraPin switch silkscreen correction; bottom-lanes/rollover
split (2026-09-14). The user shared example table.xlsx specifying a new table shape -
one row per switch/coil pin (Component | Switches & Coils | Type | Board | Pin Silk Screen |
Other Pin | MPF | Status) - replacing the one-row-per-component Components table across
docs/wiring-pin-map.md, docs/wiring-guide.md, and wiring-guide.html section 04.
Verifying it against real hardware (photos of this cabinet's own boards, cross-checked
against CobraPin's official wiki) found entry #24's CobraPin switch silkscreen values
("J1 pin 2" style) wrong - every CobraPin pin, switches included, silkscreens the MPF
number directly, not just coil banks; docs/board-silkscreen-reference.md's claim to the
contrary was corrected, backed by a new cabinet photo and a cobrapin-official-wiki
citation. Also extracted the red-board silkscreen↔MPF formula (wing×8/wing×4, MPF's own OPP
convention, plus this board's specific physical-port/direction mapping) into
docs/opp-hardware-reference.md, verified by reproducing every row of the existing per-pin
tables before writing it down. bottom-lanes split into two components.yaml entries
(bottom-lanes, now status 6 - the 4 real lanes were already individually confirmed; a new
rollover entry, status 5, for s-bottomlane5's still-unresolved hardware fault) -
registry-level only, not the full TODO.md rename (shot-group/scoring logic left alone, a
real gameplay-design decision). Silkscreen data now populated whenever a pin's board+MPF
number is assigned, not gated on physical-test status (a hardware-layout fact, not a
live-wiring one) - added for the trough switches accordingly. Verified against a real xlsx
demo sheet (example table.xlsx's new "Claude verification" tab) before committing any of
it. — Status: active.
docs/wiring-pin-map.md retired, merged into docs/wiring-guide.md (2026-09-14).
Extending both pages to the new row-per-pin table (entry #25) left them showing an
identical Board map + Components table side by side - the two pages had converged into
duplicated generated content, a real design smell the user caught. wiring-guide.md is a
strict superset (it already had harness diagrams; picked up pin-map's Lights and Known-gaps
sections too), so wiring-pin-map.md and its wiring_pin_map.md.j2 template are removed
entirely rather than kept as a thin duplicate - generate_docs.py, the CI doc-freshness
check, and every cross-reference (home.md, README.md, CLAUDE.md, opp-hardware- reference.md, board-silkscreen-reference.md, wire-component/SKILL.md) updated to point
at wiring-guide.md instead. Entry #15 first introduced wiring-pin-map.md alongside
wiring-guide.html; that entry's infrastructure decision (generated, not hand-transcribed)
stays active, only the separate page it produced is gone. Historical mentions in CHANGES.md
and one checked-off TODO.md item are left as-is, per this project's own convention of not
rewriting the record. — Status: active.
Trough opto bridge, new 520-8516-00 board: extensive diagnostic session, root cause still
unresolved (2026-09-26, branch bench/trough-opto-mosi-diag). A genuinely new replacement
board (confirmed by the owner to work in a real Stern machine) turned out to be the current
520-8516-00 revision rather than the 520-7001-00A the original plan was built against —
real differences: two connectors carrying independently different fixed values (not a shared
bus), a third conditioning IC, and MOSI actually broken out (though proven to have zero
effect on the read). Found and fixed two real bugs (an Arduino MISO/MOSI pin swap; a Bus
Pirate RCK line that never reached a valid 5V logic-high, worked around by hand-wiring RCK
directly to the board's own power rails). Discovered a genuinely new, datasheet-confirmed fact
— the serial output floats during the register's load phase and only actively drives during
shift mode, which a bare 74HC165 cannot do unassisted, proving a second, still-unidentified
active component sits between the shift register and the connector. Despite that discovery
being properly exploited and an exhaustive signal-level test matrix (logic analyzer plus a
Bus Pirate, all MOSI values, both clock polarities, a verified-clean 5V RCK), the output
stayed completely flat and unresponsive to all 7 sensors. Used two rounds of independent
multi-agent review (5 agents total) to sanity-check the reasoning rather than let one long
session's assumptions go unchecked — surfaced real gaps (U2's own input legs never directly
probed; the per-channel test matrix never fully redone after the RCK fix) rather than a
resolution. Full writeup: plans/read-opto.md's "Bench findings (2026-09-26)" section; concrete
next steps captured in TODO.md. — Status: superseded-by-28 (the root cause was found the
same day; this entry's "float during load / hidden component" discovery was an artifact).
Trough opto board reads correctly: RCK is inverted on the board (2026-09-26, branch
fix/trough-rck-polarity). A fresh review of #27 went back to Stern's schematic for the older
board (already in this repo) and saw that RCK doesn't reach the 74HC165's SH/LD directly:
it passes through a 220Ω resistor and a spare channel of the 74HC540 inverting buffer.
Confirmed on the 520-8516-00 with a meter in Ω mode (219.5Ω RCK→540 pin 9, 0.1Ω 540 pin
11→165 pin 1). So RCK HIGH = load, LOW = shift — the reverse of what every earlier read did,
which clocked the register while it was held in load mode and produced a constant
all-same-bits byte. With the firmware changed to idle RCK low and pulse it high, the Uno read
the board correctly on the first try, and blocking each sensor in turn gave the bit map (bit 6
jam, bits 5→0 trough 1→6, bit 7 unused; 1 = clear), now set as BIT_CHANNEL. Retracted from
#27: the "output floats during load" finding (most likely a Bus Pirate pull-up also raising the
undriven clock line, shifting the register) and the "hidden component" theory built on it.
The old 520-7001-00A board's "dead U1" diagnosis (2026-09-11) is most likely the same bug.
Docs corrected in plans/read-opto.md, docs/stern-spike-trough-opto.md, the build sheet,
TODO.md, and the diagnostic sketches. Lesson: trace where each control line actually goes
before experimenting with timing and protocol. — Status: active; remaining work (set
BIT_INVERT on the cabinet, permanent bridge, wire into OPP) tracked in TODO.md.
Trough bridge: bare ATmega328P chosen over MPF's spi_bit_bang (2026-09-26). MPF 0.80's
built-in spi_bit_bang platform could read the Stern board straight from OPP (2 outputs with
pull-ups + 1 input, no microcontroller or firmware), but at roughly 0.5s per read, only while
MPF runs, and untested. The owner chose the bench-proven ATmega bridge instead (~10ms reads,
independent of MPF). Both are written up in plans/read-opto.md. — Status: active;
spi_bit_bang rejected for now, kept on file as the fallback if the bridge ever needs
replacing.
plans/read-opto.md cut down to the build plan; its knowledge moved to
docs/stern-spike-trough-opto.md (2026-09-26). The plan had grown to ~530 lines, mostly bench
history and retracted theories. It now holds only the ATmega bridge build (goal, status, parts,
wiring, programming, build order, verification). The doc gained a probing reference (chip
pinouts and measured values), the MPF spi_bit_bang write-up, a bench-tools section (Uno,
logic analyzer, Bus Pirate v3.6 limits vs. Bus Pirate 5), the Stern "node extension" finding,
and a condensed bench history with lessons. The full original text is preserved in git at
commit 21a9e8b; entries #27–#29 above still name the old plan sections (left as-is, per this
log's convention). — Status: active.
Trough bridge wired into OPP on chain2-0x20, not chain2-0x21 as planned (2026-09-27,
branch feature/trough-bridge-breadboard). plans/read-opto.md and board Overviews.xlsx's
design sheet both called for chain2-0x21 (2-1-16...22) — the same slot the old soldered taps
used, and where entry #13 above left the trough switches. The bridge's 7 outputs were
physically landed on chain2-0x20 (2-0-16...22) instead, discovered when tools/wiring_test.py --monitor showed those exact addresses firing together while sweeping a hand across all 7
sensors — not the chain2-0x21 addresses the plan expected, which stayed completely quiet.
Per the user, live hardware is authoritative over the design sheet. Renumbered
s-trough1...6/s-trough-jam to 2-0-16...22 in hardware-switches.yaml/components.yaml,
which in turn reclaimed s-toplane1/2/3's and s-orbit-r's DRAFT placeholder numbers
(2-0-19...22) — moved to 2-0-0/1/2/3 (still not physically wired). s-trough6 (2-0-21)
didn't fire during the hand-sweep; flagged in TODO.md for the per-switch verification pass.
— Status: active; per-switch cabinet test still open, tracked in TODO.md.
hw_console: parallel status flags replace the 1-6 build stage (2026-10-04, branch
feature/parallel-status-flags, plan plans/parallel-status-flags.md). A single stage number
couldn't express that build steps happen out of order (wired before cleaned, configured before
installed). Each component now carries independent yes/no/n/a flags: obtained, story
function, restored, wires & plug, on playfield, lights fitted, wired to controls, bench-tested
and tested in game, plus two derived flags that are never stored: configured (pins present
in hardware-*.yaml with matching numbers, none marked draft: true) and in_code (names
referenced in config/modes/shows, or pins bound into a device in hardware-devices.yaml).
DRAFT placeholder pins got an explicit draft: true marker, since their status previously
lived only in free-text comments. The Status tab is now a sortable, filterable per-flag table,
and the wiring guide's Status column shows done/total · next: <step>. Its wired badge now
follows the wired flag; the badges came out identical after conversion. Decisions D1-D6 and
the rejected options are recorded in the plan. Conversion script:
tools/hw_console/migrate_status_flags.py. Also fixed two pre-existing concurrency bugs in
the console: a ruamel parser shared across request threads, and unserialized
read-modify-write saves. — Status: active; supersedes the 1-6 scale described in
components.yaml's 2026-09-06 header (never logged as its own entry).
Status-flag review with the owner; unwired parts' switches parked on DRAFT placeholders
(2026-10-04, branch feature/parallel-status-flags). All 20 registry parts were walked
through flag by flag, replacing the conversion's guesses. Several "wired" assumptions turned
out wrong: the trough's and rollover's wiring to the controllers isn't done (their wiring-guide
badges are now planned), and parts like the targets, orbits and ramps are less far along than
the old scale implied. At the owner's request, every switch of a part not yet wired to a
controller (top lanes, orbits, standup targets, ramps, VUKs, portal transfer, aerial/insinerator,
cabinet button: 28 switches) moved off its numbers onto placeholder numbers on the
lowest-priority boards' unused inputs (2-3-20...31, 2-2-16...31), all marked draft: true.
That frees the slots they held, including chain2-0x20 wing 0's dedicated autofire inputs
2-0-0...3, for the CobraPin-first rewire. Names are unchanged, since modes and tests use them.
The ramp diverter/subway and service switches were already DRAFT placeholders on chain2-0x23
and stay put. Verified with check_registry.py, a registry-vs-MPF number cross-check, the
49-test suite and a virtual MPF boot to attract. — Status: active for the flag review;
the switch parking is superseded by #34. Real-hardware boot not
re-run: every new number is an input the 2026-09 hardware scan lists as present.
Not-wired parts get no pin number: hardware-draft.yaml on MPF's virtual platform
(2026-10-04, branch feature/draft-pins-virtual, plan plans/draft-pins-without-numbers.md).
#33's placeholder numbers still occupied real inputs, so every real addition collided with
numbers that meant nothing. The switches and coils of every part not wired to a controller
(45 devices: the DRAFT ones plus, per the owner, the pop bumpers and drop bank) moved into a
new hardware-draft.yaml with platform: virtual and their own name as number:. Modes,
devices and tests keep working on the names, but no board input is used. hardware-*.yaml
now holds only real wiring; chain2 boards 0x21-0x23 are entirely free.
check_registry.py enforces the rule: draft entries must be virtual and number-less, and
can't also be defined for real. Proven first on the real cabinet: one switch on the virtual
platform next to OPP, all three chains initialized and attract reached.
wiring_test.py --monitor can no longer see a switch wired onto a freeTODO.md.Probe mode for finding a new switch's address (2026-10-04, branch
feature/wiring-test-probe). Since #34 most inputs have no switch configured, and
wiring_test.py --monitor (which reads through MPF) can't see a switch on an unconfigured
input. tools/mpf-session.ps1 -Action Start -NoBcp -Probe now runs tools/wiring_probe.py
first. That generates a git-ignored machinefolder/config/probe-switches.yaml with a
probe-<number> switch on every input the recorded OPP scan reports (hardware-basic.yaml,
"# Input cards:") that no real switch uses and no board config reserves: 110 today. MPF is
started with -c config.yaml,probe-switches.yaml, so every switch position is watched.
--monitor prints a probe hit as NEW INPUT <number>, summarizes the probes in its snapshot
(flagging any already closed), hides not-wired virtual switches on real hardware, and lists
every new input that fired on Ctrl+C.
debug: true raw logging. It works but buries hits in log noise and needs aPhysical labels on parts; single drop target added (2026-10-04, branch
feature/component-labels). Components get an optional label:, the label physically
written on the part (e.g. Drp2). It shows as a sortable column before the part name in the
console, right of the index on the printable checklist, and is editable in the detail pane and
the add dialog.
Drp2, with its story role recorded as theDrp1, is renovated with wiress-drop-single / c-drop-single are placeholder names, defined inhardware-draft.yaml (virtual, no pin number) per #34.design/features/dropbank.yaml, which is theRule: one registry part per physical label (2026-10-04, owner). On the printable
checklist and in the console every separately labelled part gets its own row, so
components.yaml holds one component per label, never a combined label.
autofire-slings became sling-left (SlingL) and sling-rightSlingR), each with its own switch, coil, autofire device and flags. The shared history ischeck_registry.py (registry.label_violations) rejects a combined label/, ,, + or &) and a label used by two parts. The console API refusespop-bumper-1..3 (Pop1-Pop3), and thestart-button (S-Start) and launch-button (S-Launch), the toptop-lane-1..3 and the bottom lanes into bottom-lane-1..4 (both labelled withCLAUDE.md, the wire-component skill, the console README and thecomponents.yaml header. Grouped entries (e.g. bottom lanes, targets) get split the sameStory column moved; in_code understands MPF switch tags (2026-10-04, owner request).
registry.STATUS_FLAGS.in_code: splitting the cabinet buttons exposed a false "not in game code"S-Start. MPF starts games from the switch's start tag, not its name. in_code nowstart, tilt, tilt_warning,slam_tilt), or whose sw_<tag> event the config uses. — Status: active.No top orbit switch: s-orbit-top removed; orbits split per label (2026-10-04, owner:
"I have only the left & right orbit switches").
s-orbit-top from hardware-draft.yaml and the registry, along with what usedshot_orbit_top (100 pts), its show and its sound;test_orbits; test_progression updated to match.design/features/orbits.yaml and onshape-mpf-overview.md updated, andSTORY.md question resolved.led-orbit-t stays defined (only the switch was reported missing), but nothingorbits became orbit-left / orbit-right, labelled with their config namess-orbit-l / s-orbit-r. — Status: active.Standup targets: 7 real targets replace 9 guessed ones (2026-10-04, owner). The config had 9
target switches in E/R/M/L banks (s-target-e1 … s-target-l1), never located on the machine.
The cabinet has 7, labelled S-Target1..7.
s-target1..s-target7 in hardware-draft.yaml (not wired), one registrystandup-target-1..7): obtained and renovated, not plugged/installed/wired.s-target-l1 was the only one used, as the 3rd step of the dropbankSTORY.md itemled-target-* LEDs are still defined. — Status: active.Spinner added: S-Spin1 (2026-10-04, owner). The top-loop spinner from the CAD is real:
renovated with wires & plug, not on the playfield yet. It's switch s-spin1 in
hardware-draft.yaml with debounce: quick (one toggle per rotation) and registry part
spinner. MPF 0.80's spinners: device gets added once a mode scores it. STORY.md's "needs
confirmation" note on the spinner is resolved. — Status: active.
Pop-up post Up1 at the top of the orbit (2026-10-04, owner). When raised (e.g. during a
2nd launch) it stops the ball at the top of the loop, which rolls back to the top lanes or the
left orbit entry. Wires & plug, on the playfield, not wired. Coil c-up1 in
hardware-draft.yaml; registry part popup-post-top.
STORY.md: the pop-up-blocker item is confirmed. — Status: active.Right-side toys modelled as two feature groups (2026-10-04, owner description). A ramp
leads up to the lightbridge, a plexiglass gantry. Rolling down, diverter Div1 either sends
the ball into the ball dropper, a ball lock where VUK6 kicks it straight up into a
see-through box held shut by a servo gate, or lets it return above the inner right bottom
lane.
group: field on components. Rows are grouped under a header in the console ands-ramp-r1 (mid-way) and s-ramp-r2 (just past the top), splitramps entry, plus the lightbridge (no pins).c-ramp-diverter), the CAD "subway" switches (confirmed ass-dropper (moved out of the portal transfer), VUK6c-vuk6) and the box gate servo (no pin; a future MPF servos: device).No dropper-path switches (2026-10-04, owner correction). The CAD "subway" switches
s-subway-entry / s-subway-exit were confirmed in #43 as the ball dropper's path, but they
don't exist: there are no switches between Div1 and the dropper, and none on the lightbridge.
Both were removed from hardware-draft.yaml and the registry. Nothing in the game used them.
— Status: active; corrects #43.
Ball dropper corrected: VUK6 is the lock, the box + gate is the dropper (2026-10-04, owner).
A ball diverted by Div1 drops straight into VUK6, the ball lock, with no switch or other
measurable step in between. s-dropper is VUK6's lock switch, so it moved into the VUK6 row
alongside coil c-vuk6; the name stays because the portal mode uses it. VUK6 kicks the ball
up into the see-through box. That box, with its servo gate, is the ball dropper: opening
the gate drops the ball onto the playfield. — Status: active; corrects #43.
s-dropper renamed s-vuk6 (2026-10-04, owner). The switch is VUK6's lock switch; the
ball dropper box has no switch, so the old name misled. Renamed in hardware-draft.yaml, the
portal mode (the shot's switch and the sequence's first step) and test_portal /
test_progression. Design docs and the registry are updated, and TODO's "dropper coil
missing" is closed. Kept as they were: the shot and event names (shot_dropper*) and the LED
led-dropper. — Status: active.
Finished plans folded into the docs, then deleted (2026-10-04, owner). plans/ now holds
only work still in progress (resumption-roadmap.md). Each finished plan's lasting knowledge
moved to the doc for its topic; the step-by-step build history stays in git. Where older
entries above point at a deleted plan, look here:
plans/draft-pins-without-numbers.md → docs/wiring-workflow.md, "Parts not wired yet".plans/parallel-status-flags.md → tools/hw_console/README.md, "How the flags work".plans/wiring-test-tool.md → new docs/wiring-test-tool.md.plans/read-opto.md → docs/stern-spike-trough-opto.md, "The ATmega328P bridge as built".plans/story-to-shots-to-modes-workflow.md → design/README.md (already the live version).plans/testing-strategy.md → moved whole to docs/testing-strategy.md.plans/OutsidePerspective.md (research, not a plan) → moved whole todesign/research/outside-perspective.md.Resumption roadmap finished and deleted (2026-10-04, owner). plans/resumption-roadmap.md
(the 2026-08 phased plan from #3) is done: flippers on CobraPin and in real games (phase 1); rules
foundation — shots, logic blocks, achievements, service mode, ball search (2); all 8 feature
modes built and tested (3); per-mode slides and shows (4); bonus, multiball and tiered wizards
(5). Its two unbuilt items went to TODO.md: a software ball save and service-mode slides;
everything else open was already there (tilt, flipper pulse tuning, game balance, the wizard
payoff, playtests as parts get wired). Its lasting pieces already live elsewhere: the flipper
EOS reasoning in hardware-coils.yaml/hardware-devices.yaml, device-choice guidance in
design/README.md, testing in docs/testing-strategy.md. References now point at #3, and the
feature schema's phase field (it only pointed at roadmap phases) is gone. plans/ is empty
until the next planned piece of work. — Status: active.
Design data: one home per fact, readable feature files, generated views (2026-10-04,
owner). The design facts lived in four unlinked places (registry, feature YAML, STORY.md prose,
a hand-kept table). Now: physical parts in the registry, which gains a one-line summary:, a
playfield area: (named zones: bottom, left, left-top, middle, right, right-top, top-loop,
shooter, cabinet) and category: machine for machine basics; game features in
design/features/<id>.md — a schema-checked YAML header (id, name, status, mode,
cad, parts by registry id, leds, devices) over a Markdown body (Story, Rules, Scoring,
Presentation, Notes); the narrative alone in design/STORY.md. design/overview.md (features
× parts × CAD × status, parts in no feature, machine basics) and design/playfield.md (parts
by area) are generated, replacing onshape-mpf-overview.md and STORY.md's hand-written layout.
Owner decisions: named zones (not a grid or CAD coordinates); LEDs stay per feature, not
per part (so the lights flag stays manual); machine basics as a category rather than feature
files; a summary per part; the generated pages and STORY.md to be added to the wiki's path
scope (a manual Wiki.js setting). Rejected: keeping YAML with prose blocks (unreadable in the
wiki, hard to diff); per-part LED lists (owner's choice). The feature files were converted
mechanically, keeping all prose; trimming text that repeats the mode configs is a follow-up
(TODO.md). The plan (plans/design-data-structure.md) was deleted once built, per #47's
convention. — Status: active.
Sound library: every Portal sound tagged, voice lines in the repo; repo made private
(2026-10-05, owner). To pick callouts per period, every line in the owner's Portal rip needed a
speaker and its text. tools/sounds/catalog.py scans the rip (9,359 files, 7,984 unique after
folding 1,648 duplicate copies) into design/sounds/catalog.csv, tagging kind
(voice/sfx/music), speaker, setting (Portal 1 / Portal 2 modern / old Aperture / co-op —
owner's request, it maps onto the periods' voices) and purpose (the owner's sorted folders;
a tag, not folders, owner's choice). Speakers come from the rip's file prefixes, then from the
per-character zips (which name most unprefixed lines), then name rules; hand corrections go in
tag-overrides.yaml. The scan found the rip does include Portal 1 (308 GLaDOS lines).
build_library.py converts the 4,689 voice lines to OGG (~190 MB) in assets/sounds/voice/;
sound effects and music stay local (gitignored). Owner decisions: commit the voice lines as OGG;
text from Portal 2's caption file plus speech-to-text for the rest; make the repo private
so Valve's audio and script text can be committed — done with gh repo edit. Consequence: on
GitHub Free, branch protection (the required test check on main) isn't enforced on a
private repo, so the PR routine now holds by habit (TODO.md). Rejected: committing all 4 GB
(too big for GitHub, every clone huge); keeping everything out of git (the owner wanted the
files in the repo). See docs/sound-library.md. — Status: active.
Sound library finished: Valve's own text for every spoken line; plan folded and deleted
(2026-10-05, owner: "execute the sounds library plan"). The owner installed Portal and Portal 2.
tools/sounds/captions.py + valve_files.py read the games' caption files: VPK archives,
compiled VCCD captions keyed by CRC32 of the sound name (verified 506/506 against Portal 1's
.txt), and sound scripts mapping sound names to wave files. Portal 2's dialogue is in
subtitles_english (its closecaption_english only has sound effects), and each line names
its speaker, which now cross-checks the catalogue's tags. Result: 3,140 of 3,216 spoken lines
carry Valve's text, 76 speech-to-text (which can invent text on beeps, so stt is a guess).
Scanner fixes found on the way: duplicates are paired by duration when two prefixed copies
share a name (sizes differ between exports); zip files are compared per character, recovering
11 Space Core lines an Adventure Core line of the same name had hidden; captions are picked
per speaker for the same reason. The review list went from 81 to 11 (unused content with no
captions, TODO.md). Checking plans/callouts.md against the captions corrected one file the
Portal Wiki lookup had wrong and filled six more. Speech-to-text stayed a fallback: running it
first would have cost ~2 hours of CPU that the captions made unnecessary. Not done (optional,
TODO.md): local copies of sound effects and music. plans/sound-library.md was folded into
docs/sound-library.md and deleted, per #47. — Status: active.
Sound effects committed as OGG, tagged by use; music and commentary sorted out (2026-10-05,
owner: "add sound effects to git repo", "music stays ignored", "just convert to ogg"). All
2,951 sound effects are committed as OGG in assets/sounds/sfx/ (100 MB, from 1.2 GB WAV),
revising #50's S1 (which kept them local). A new sfx_use column tags the 441 most likely to
be useful by the playfield feature they'd serve (portal, button, light_bridge, funnel,
faith_plate, turret, gel, incinerator, dropper, tube, elevator, door, alarm, explosion,
sparks, fizzle, ui, glados_core, ambience) - a suggestion; the owner's picks stay in
purpose. Re-sorting on the way: sp_a1..a4_* are Portal 2's dynamic-music layers and
portal2_* soundtrack files (music: 161 -> 297); com-* is developer commentary, a new local-
only kind (83 files, 286 MB) so it doesn't bloat the repo. Music stays gitignored: as OGG its
WAV part would be ~157 MB, the MP3s 486 MB as they are. Checked: mpf-gmc plays mp3/wav/ogg
(not m4a). Rejected (owner): dropping the "useless" sound effects. — Status: active
(music part superseded by #53).
Music committed as OGG too (2026-10-05, owner: "convert music to ogg and add to repo").
Revises #52's "music stays gitignored": all 297 music files (Portal 2's dynamic-music layers
and soundtrack) are now OGG in assets/sounds/music/, including the 64 MP3s (lossy-to-lossy,
inaudible on a cabinet; one format keeps the config simple) and the 25 M4As, which
libsndfile can't read - build_library.py decodes those with PyAV (av, now pinned in
install.ps1). Only developer commentary stays local. — Status: active.
Both games' scripts and developer commentary in the repo, in story order (2026-10-06,
owner: "store the captioned stories of both games in the repo"). tools/sounds/scripts.py
builds design/sounds/script-portal1.md, script-portal2.md, script-portal2-coop.md and
commentary-portal1.md / -portal2.md from the installed games. Chapter, chamber and map
order are exact: they come from the games' own files, since Portal Wiki's lists (which are in
play order) returned HTTP 429. The order within a map is approximate. Each line carries its
catalogue file name so it can be found and played. Coverage: Portal 1 91%, Portal 2 78%,
co-op 79% placed on a map; the rest are listed per speaker, not dropped. Made for the story
brainstorm (design/research/story-brainstorm/), which uses them as a source. —
Status: active.
Portal 1's turret voice and soundtrack in the sound library (2026-10-06, owner: "can you
get Portal 1's turret voice?"). The 64 turret_turret_* files were Portal 1's turret voice all
along (Portal 2 reused them unchanged, same frames), but carried Valve's group captions
("Activated!" for eight different lines). captions.py now skips a caption shared by a
multi-file sound entry for the floor turret, and speech-to-text gave the real lines ("Are you
still there?", "I don't hate you", "No hard feelings"); one was corrected by hand. Portal 1's
12 music tracks, including Still Alive, were extracted from the installed game's VPK into the
rip and built as music_portal_* OGGs. Story decision D12 (design/research/story-brainstorm/owner-decisions.md):
floor-turret lines mix freely, the defective turret stays with the sabotage and the finale.
The same day (owner: "extract in same way"): the Anger core's 24 growls, GLaDOS's death
scream and pain sound, the 26 ARG radio sounds and the rocket turret's sounds were extracted
too, with catalogue rules tagging the rockets turret and the Anger core incinerator.
— Status: active.
The story decided and consolidated in design/STORY.md (2026-10-06, owner: "yes to all").
The story brainstorm and its convergence plan (plans/story-convergence.md) ended in one agreed
story: five periods in 10 chapters (GLaDOS, Cave Johnson, frantic GLaDOS, Cave's brain-mapped
ghost, Rattmann), a Wheatley branch by choice, seven choices, a tree of eight endings plus a
small ending per period, three play modes (co-op with a shared score and its own courses), and
a test chamber sign on half the screen. design/STORY.md is now the single source of truth;
the brainstorm files and the owner's decisions D1-D24 are its history; round 2 scoring is
replaced by a per-act review of STORY.md. Supersedes the story parts of
plans/story-and-shots.md (R1.3, R1.4, R2.1, R2.3, the round 6 periods). — Status: active.
Final review of the agreed story done (2026-10-07, in chat, branch design/final-review-a).
All three tabs of the final review page (plans/story-to-build.md step 0) are answered and
saved in design/research/story-brainstorm/scoring/review.json: tab A (STORY.md as a
checklist) 159 OK and 2 changes (D27), tab B (the story's 334 beats and arcs) 248 OK and 86
changes (D28), tab C (brought back) as D26. Story changes in design/STORY.md: Rattmann's art
gets a ~1 s ball hold, the chapter recap card lists its defaults, attract wakes with the "nine
nine nine..." courtesy call, the jackpot tour sits before the climax as super_wizard's
ruleset, and Drp1 is the rocket turret in the climax only. The other per-item changes, mostly
aligning older brainstorm items with STORY.md, are listed in
design/research/story-brainstorm/review-b-changes.md; where a research file differs from
STORY.md or that list, they win. — Status: active.
Toy table and chapter rule sheets drafted (2026-10-07, branch design/rule-sheets):
design/rule-sheets.md, step 1 of plans/story-to-build.md. A toy table (every shot and story
toy against the 10 chapters and co-op, with the overloaded and idle toys) and a rule sheet per
chapter: the goal, the tests with their chamber-sign rule text, timers, what lights the
multiball, the layers and the small ending; plus the Wheatley branch and co-op's courses.
Built from design/STORY.md and the final review's changes (D28). All numbers are drafts for
the balance pass. The owner answered its 8 open questions the same day: the Left Orbit test
moves to 1A; the cores use the 3 multiball locks until the 3 core locks are built; a real
knock-down coil (HW-9) for sabotaged turrets; the decade locks as drafted; dial clicks light
period 4's lock; four arrow dens and two secret dens; the magnet is the secret Adhesion Gel and
the "SPACE!" grab; co-op's vault press is both flipper buttons. — Status: active.
The callout script and the story's audio edits (2026-10-07, branch design/callout-script,
step 2 of plans/story-to-build.md): design/callouts.md picks the real file for every
callout per period and event (the machine's moments, the turret bank, each chapter, the
endings, the Wheatley branch, co-op and attract), sets the playback rules (one voice at a
time in three priorities, 4 s in live play and longer only in held moments, music ducking,
subtitles), lists the lines the rip doesn't have, and the audio edits.
tools/sounds/edits.py makes the mechanical edits into assets/sounds/edits/
(design/sounds/edits.csv) and lists 819 Cave bite candidates (design/sounds/cave-bites.csv).
Supersedes plans/callouts.md as the working document. The owner answered its 4 questions
the same day: subtitles for story lines only; chattiness as an operator setting (quiet /
normal / chatty); no fat, adopted, racist or sexist jokes; period 4 mixes whole long lines at
held moments with short clips in play. All picks and cuts still wait for the owner's ears.
— Status: active.
The rule sheets copied into the feature files (2026-10-07, branch design/feature-stories):
each of the 8 features (aerial, dropbank, lanes, orbits, portal, ramps,
skillshot, slings) now has the agreed story in ## Story (replacing the early theme-research
proposals, several of which the story overturned: the top lanes aren't the turret gauntlet, the
orbits aren't blue/orange) and a ### Story rules (design target) section with its rule-sheet
rules, next to the built rules. A dated note in each ## Notes records what changed. Status
fields are unchanged (the modes still do what they did). — Status: active.
Playthrough of the agreed story, as a model (2026-10-07, branch design/playthrough, step 4
of plans/story-to-build.md): tools/sim/playthrough.py plays the rule sheets 2,000 times per
skill (beginner, average, expert) on stated assumptions, and
design/research/playthrough-agreed-story.md reports how far games get, where the time goes,
three typical games and seven findings: a gap in the jackpot tour (the Turret Gauntlet can't
be made), 2A and 3B as the slow chapters, beginners stuck in 1A, an expert's full run at
~27-31 min instead of ~20, and periods 1-2 as the game for most players. The owner's answers
(the bank counts as the tour's Turret Gauntlet shot, 2A at 30 hits, only the first core
refused, 1A any 3 of 4, a ~25-30 min run) are in the rule sheets and STORY.md. — Status:
active.
Period 1 of the story as an MPF mode (2026-10-07, branch build/period1, step 5 of
plans/story-to-build.md): machinefolder/modes/story_p1 runs chapters 1A (any 3 of 4 tests,
the orbit test lit after the first), 1B (6 tests, start to qualify and finish for +1x, the cube
chain and choice C1) and 1C (lock 1, lock 2, the exit, the 4-ball escape multiball), with all
progress kept per player across balls. tests/test_story_p1.py (7 tests) on the virtual
platform; all 67 tests pass. The placeholder mb-dropbank multiball no longer starts in 1C.
Period 1's callouts are wired too: 17 picked lines copied into
machinefolder/sounds/voice/<purpose>/, played on GMC's sequential voice bus, with
random pools that never repeat the last pick. Screens too (placeholder look): the chamber
sign widget and 12 overlays, generated by tools/display/story_p1_slides.py. Not yet:
lights, scoring, physical
locks, C0; not run on the cabinet.
— Status: active.
Chamber sign icon states, and only "== non-zero" conditions (2026-10-07, branch
build/icon-states): the sign's hazard icons have three looks (not in use = light grey, in
use = black, done = light blue). Rendering the sign in Godot with fake player variables
showed that mpf-gmc's MPFConditional hides itself whenever its variable is 0 or unset
(evaluate() starts with if not t: return false), so every "== 0" / "!= 1" condition never
showed: no 1A sign at all (p1_chapter == 0), no grey tick boxes, no "not in use"/"in use"
icons, no "cube incinerated" line. tools/display/story_p1_slides.py now draws the 0 look
unconditionally and stacks opaque "== 1/2/3" layers on top (1A is the bottom layer; 1B, 1C
and a new "period 1 complete" layer cover it). The mpf-gmc addon is left stock (rejected:
patching its one line, an addon update would quietly undo it). Renders in
design/research/story-brainstorm/display/icon-states/ (*.png = the bug, *-v2.png =
fixed). — Status: active.
The pre-recorded emergency messages, every option switchable (2026-10-07, branch
build/pa-callouts): the owner's requests in design/sounds/pa-snippets.md built as
operator settings instead of picking one: pa_attract_style (A1-A6, timers in
modes/attract), pa_p17_style (B1-B3), pa_multiball (P24/P23) and pa_taunts (P16 same
outlane twice, P20/P93 at initials entry, the P86 flipper code), in the new
modes/pa_callouts and an addition to MPF's high_score mode. 11 cuts plus a static beat as
edit group A9, cut between words using faster-whisper word timings (the first time
on the PC; edits.py --only keeps the other edits untouched). tests/test_pa_callouts.py.
Lines whose game event doesn't exist yet are cut but not wired (TODO.md).
— Status: active (options to compare on the cabinet).
Period 2 of the story as an MPF mode, and a fix for period 1's silent events (2026-10-08,
branch build/period2): machinefolder/modes/story_p2 takes over from story_p1 when the
escape multiball ends (story_period_2; story_p1 now stops there and only starts while
p1_chapter < 3). It runs 2A-2C's gel tests (30 pop/sling hits, 6 orbits, 4 portal
transfers as an Orange-then-Blue sequence), each decade's lock (the dropper, the Incinerator,
the turret bank's exit), and the 4-ball Combustible Lemons multiball: either ramp scores a
lemon jackpot, the 5th lights the super jackpot (the Super Button until the lemon target
Pull1 has a switch). The placeholder mb-dropbank also stays out of 2C. 17 Cave and
Caroline lines in sounds/voice/ (chapter, test_complete, lock, lemons); the opening plays
once (p2_intro). Screens from tools/display/story_p2_slides.py, which reuses period 1's
sign builder (now story_p1_slides.sign(); period 1's output is byte-identical).
The fix: MPF posts logicblock_<name>_complete only while a counter/accrual has no
events_when_complete of its own - setting one replaces it. Period 1 listened for those
defaults, so in a real game its sign ticks never set, "Now you're thinking with portals",
two callouts and the cube chain's step 3 tick never fired, and choice C1 didn't lock out
the other option. Every such block now lists its default event too, and
tests/test_logic_block_events.py checks all modes for it. Not yet: see
plans/story-to-build.md step 5. — Status: active.
Period 3 of the story as an MPF mode (2026-10-08, branch build/period3):
machinefolder/modes/story_p3 takes over from story_p2 when the lemons multiball ends
(story_period_3; story_p2 now stops there and only starts while p2_chapter < 3). 3A: six
timed tests on period 1's shots, 3 lit at a time, each with a 30 s neurotoxin clock that
restarts on a time-out (the test stays lit) and after a drain; 4 done light the cores, seated
in the 3 multiball locks (rule sheets Q 2), which start the 4-ball Her Cores multiball
(jackpots on the portals and ramps). 3B: three turret waves (the drop bank, then two sets of
three standups; each followed by a side orbit within 5 s, a time-out resets that wave), the
jackpot tour (D28: the 8 major shots, the bank counting for the Turret Gauntlet, then the
super jackpot at the Super Button), and the climax (the Stalemate button, the rocket from
Drp1 through the portals and a major shot within 5 s, 4 cores into the Incinerator with the
first refused once). It ends in E1. The comply/defy meter isn't built anywhere yet, so the
climax always takes comply and C3 always escape (superseded by #68: the meter decides). mb-dropbank is now off from 2C on.
21 lines in sounds/voice/, the sign and 8 overlays from tools/display/story_p3_slides.py.
The bank-guard tests of periods 1 and 2 now really knock the bank down (they released the
switches before, so the bank never counted as down). — Status: active.
TODO cleanup (2026-10-08, owner's choice of three passes): settled items closed with a
pointer to what settled them (the lanes' LIE/CAKE and the Turret Gauntlet, the skill-shot rule,
the Companion Cube incineration hook, the dropbank multiball's repeat question); duplicates
merged (one Godot pass, one cabinet run for the story, one Graphviz item for the PC, the hook
and the cloud); and every checked-off item, plus the solved trough-opto saga, moved verbatim
to the new TODO-done.md, leaving the one optional retest open. TODO.md went from 712 to
~440 lines; no open item was dropped. — Status: active.
The comply/defy meter (2026-10-08, branch build/meter, choice C2): a new
machinefolder/modes/story_meter keeps one player variable, meter, for the whole game:
+1 for every finished story test (1A, 1B, the gel tests, 3A's timed tests: "tests in order
fill comply") and -1 for every hit on Rattmann's panel Drp1 outside the climax (following
his hints). Period 3's last core reads it: 0 or more is comply (GLaDOS survives, E1); below
0 is defy (E8, GLaDOS destroyed, then E2, the escort bot drags you back, posting
story_period_4 for when period 4 exists). New player variables p3_ending and
glados_destroyed, the E8/E2 overlays and two voice lines. Sabotage, the shortcuts, the
plunge side, GLaDOS's tone and the sign-display setting aren't built yet, so Drp1 is the only
defy source for now. tests/test_story_meter.py; period 3's test helpers moved into a base
class it shares. — Status: active.
CLAUDE.md for cloud sessions (2026-10-08, branch docs/claude-md-proposal, the owner's
choices from the 2026-10-08 review): the global ~/.claude/CLAUDE.md workflow is now copied
into .claude/workflow.md (minus its section about maintaining the collaboration repo itself)
and imported by CLAUDE.md, because cloud sessions never see the PC's global file. CLAUDE.md
also gained a Cloud sessions section, a Testing section, and key facts on the generated story
screens, the story's sources of truth and the voice lines/settings. The two copies of the
workflow have to be kept in sync by hand. The plan (plans/claude-md-proposal.md) is deleted.
— Status: active.
Gamebook printed in story order (2026-10-08, branch docs/gamebook-story-order, owner's
request): tools/story/print_gamebook.py no longer shuffles the scenes (it used a fixed seed,
like a classic gamebook); they're numbered in the interactive page's order, which is the
story's chronological order, so most choices turn to a scene on the same or the next page.
Each drain scene already sits next to the scene it returns to. Render:
design/research/gamebook-renders/story-order/. — Status: active.
Three finished plans retired (2026-10-08, branch docs/retire-finished-plans, owner's
request; the CHANGES #47 rule): plans/ now holds only the two plans still in progress
(art-and-screens.md, story-to-build.md).
plans/story-and-shots.md (last at 6536cd7): its story was superseded by design/STORY.mddesign/shots.md: the shotplans/story-convergence.md (last at 309b0f0): finished when the story was chosen (D25);story-to-build.md's step 0, done 2026-10-07.plans/callouts.md (last at bc54fbb): superseded by design/callouts.md on 2026-10-07.design/research/story-brainstorm/ still cite the old plan pathsTODO.md: implied changes nothing tracked yet, including balls_installed: 4 while thestory-to-build.md lost its stale design/story-brainstorm branch line.The story's screens seen live, two widget bugs fixed (2026-10-08, branch
fix/chamber-sign-on-base): a virtual mpf
run with the Godot display, switches pressed over BCP and the viewport captured inside
Godot (method in docs/testing-strategy.md, Tier 2.5). Found:
{slide: base} in all three periods.update action. Each period now sends one on every player variable its sign shows;tests/test_chamber_signs.py keeps that list in step with the generated signs.design/research/live-screen-test/2026-10-08/, before and after). The 85 story assets got.import files (--import), and GMC played 38 voice lines with none failing toGraphviz found on the PC (2026-10-08, owner's request; branch feature/attract-mode):
Graphviz 16.1 was already installed in C:\Program Files\Graphviz, but its installer never
added it to PATH, so generate_docs.py (and the PostToolUse hook) failed with
ExecutableNotFound: dot and sessions fell back to a portable zip. Its bin is now on the
user PATH, and the generator looks in that folder itself when dot isn't on PATH (an
editor and its hooks keep the PATH they were started with). The machine-wide PATH needs
an admin prompt and was left as is. — Status: active; supersedes the portable-zip
workaround in TODO.md.
The attract mode (2026-10-08, branch feature/attract-mode, planned in Mode 1 and
confirmed by the owner; the plan is folded in here and deleted). The owner's choices:
design/STORY.md's Attract with a large pool of pages anddesign/attract-pages.yaml.attract_callouts off / rare / normal.tools/art/extract_textures.py, design/art/), the Relaxation Vault from the Portal Wikidocs/references).custom/idle_days.py, the project's first custom code (shows themodes/attract_pages (generated with its 76 slides by tools/display/attract_slides.py),config/machine-stats.yaml (endings found, period reached, elevendocs/gmc-bugs.md #1, #4); every GMC issue so far is collected there. Checked: unit teststest_machine_stats, test_attract_pages, test_attract_rotation), all 76 slides renderedTODO.md). — Status: active.mpf-gmc upgraded to 1.0.0; the GMC findings sorted (2026-10-09, branch feature/gmc-1.0,
owner's request). The repo had mpf-gmc 0.1.0.dev2, while MPF 0.80.0 "requires MPF-GMC 1.0.0
at minimum" (its release notes). Upgraded to 1.0.0 (still Godot 4.3; it imports and runs):
MPFConditional extends Control, not Node2D - the sign andControl nodes; all slides regenerated.==,config/story-player-vars.yaml gives every conditional variable anscripts/machine_var_conditional.gd (1.0.0 reads machineupdate, a widget without slide: joins the current slide.value; notdocs/gmc-repros/10-*. docs/gmc-bugs.md is rewritten around that.Four test stages (2026-10-09, branch feature/test-stages, planned in Mode 1 and confirmed
by the owner; the plan is folded in here and into docs/testing-strategy.md, and deleted).
The owner's four stages, with choices TS1-TS4:
tools/test/stage1.py, ~15 s, also in CI): the unit tests plus threempf boots to attract with no error or warning (test_boot), everytest_config_refs),test_fuzz, MPF's own advice; MPF 0.80tools/test/playtest.py): scenariosmachinefolder/playtests/: attract, period_1..3, full) run inside the real MPF throughmpf -pit) with GMC connected; screens confirmed byslide_..._active events, screenshots, expected sounds from GMC's log; an HTML reportfull passes in ~85 s.--scenario hardware_check --hardware): guided on the--auto-answer (62 checks pass); not yet run on the real boards.tools/test/session.py): Godot + MPF + MPF Monitor (TS3; the layout isgmc.cfg, notes with screenshots in a session log..ui filestools/test/setup_monitor.py restores them); MPF doesn't echo an event that came in over BCPname. attract_pages gainedattract_cycle_hold / _resume, a new hwcheck mode shows the hardware-check prompts.session.py --hardware had noPeriod 4 and the shared major-shots layer (2026-10-09, branch feature/period-4-and-shots,
planned in Mode 1 and confirmed by the owner; the plan is folded in here, into
design/rule-sheets.md, design/callouts.md, design/SCREENS.md and TODO.md, and deleted).
The owner's choices: period 4 next (period 3's defy ending posted story_period_4 with nothing
listening - a dead end), "overarching shots" = a shared layer every period can use, and all
automatic tests.
modes/major_shots (always on in a game, priority 120): one MPF shot per major shot withlight_<shot>, arrow_<shot>, unlight_<shot> or unlight_all_major_shots and listens for<shot>_lit_hit. Periods 1-3 still use raw switches (TODO).modes/story_p4, Back to Your Own Time: the opening (card 4, "Is anyone there?", Cavestory_period_5 10 s later (after the ending's screen).story_period_4, posted by a 10 s mode timer afterp3_ending 2).tools/display/story_p4_slides.py - the EPILOGUE sign (the year on the dial,tests/test_story_p4.py (14), tests/test_major_shots.py (5), a 4th fuzz stormperiod_4 playtest scenario (40 checks: events,full through period 4.story_period_4|10s), which live in a machine-wide delay manager and survive the ball andevent|delay in event_player for anything a player's ball depends on - useThe small endings (2026-10-09, branch feature/period-4-small-parts, owner: "pick up the
smaller to-be-done parts"): a game over inside a story period now plays that period's small
ending (design/STORY.md, "The ending tree"; the lines picked in design/callouts.md): the
line and a GAME OVER screen with "NEXT: CHAPTER ..." per chapter (1A-4, 9 screens from
tools/display/small_ending_slides.py). None after the final endings (E1, E3/E4).
modes/small_ending starts with a player's last ball (game.balls_per_game) and holdsball_ending with a queue_relay_player until the line has played (GMC'sevents_when_stopped; a 15 s safety timer). It's a non-game mode (game_mode: false, thestop_on_ball_end: false).ball_ending (a queue event) withuse_wait_queue - its start never finished and the bonus never ran. Found: MPF runs a queuebus_player stop on the voice bus (it clearstests/test_small_ending.py (9: per period, none before the last ball or after asmall_ending playtest (period 4, live in GMC), all 9 screens rendereddesign/research/small-endings/2026-10-09/). test_full_game now waits out the ending.Period 4: the dial turns during the ride (2026-10-09, branch feature/period-4-small-parts;
rule sheets, period 4): each Era-Dial Ride jackpot moves the dial a decade (1950 ... 2000,
round again; p4_ride_era, with the dial click), and the super jackpot stops it in the
eighties (1980, shown on the C5 sign). Test in tests/test_story_p4.py; seen on the sign in
the period_4 playtest (1980 after 4 jackpots, 1990 after 5, 1980 on C5).
— Status: active.
A software ball save; the small endings' lines really play (2026-10-09, branch
feature/ball-save-and-shot-layer, owner: "do 1 & 2"):
modes/base, ball_saves: ball_save_start): a ball lost within 8 s of itss-plunger-lane_inactive, once per ball viaball_save_armed) - with a mechanical plunger MPF's ejecting_ball fires as the balltests/test_ball_save.py (4).Periods 1-3 light their inserts (2026-10-09, branch feature/ball-save-and-shot-layer):
their lit shots now show on the major shots' inserts through the shared layer
(modes/major_shots, #77). Each period's pN_lights redraws the layer from the player
variables the signs already use - unlight all, then light each shot whose condition holds -
on mode start and on every change of one of those variables (player_<var>); shot states
persist per player, so a drain keeps them. The counting stays on the raw switches, so the
periods' logic and tests are unchanged. 3B's jackpot tour has no per-shot variables: all 8
light at its start and each goes out on its hit. Period 4 now clears the layer at its first
start. Generated with a throwaway script; the blocks sit at the top of each period's
event_player. Tests: tests/test_story_inserts.py (6); the major-shots and period 4 tests
start from a clean layer / hand over through periods 2-3 (a test-only artifact: jumping
straight to period 4 left period 1 running, which kept redrawing the layer).
Fresh-context review, both CONFIRMED and fixed: a weak plunge (the ball rolling back into the
lane) used up the ball save while the ball sat there - now it switches off and re-arms for the
real launch, only while unused; inserts stayed lit after they stopped counting - period 3's
cores now have a variable each (p3_core_bridge/_fire/_exit), 1A's orange insert follows the
orange-portal test only, the blue one goes out once the transfer's blue half is made
(t1a_xfer_blue, the accrual has no per-step variable), and wave 1's defend window lights the
side orbits. A test confirms the jackpot tour's inserts survive the period's restart.
— Status: active.
The signs use the games' real icons (2026-10-09, branch
feature/sign-icons-and-screen-review, owner's IDEAS.md "Art": "use the real icons not the text
only in the row of warning signs"): the story signs' hazard icons were a word in a square. Now
17 of Portal 2's own chamber-sign icons (materials/signage/signage_*: button, portals, fling,
pellet, faith plate, funnel, cake, turret, cube, incinerator, bridge, exit, gels, rocket) as
white glyphs (tools/art/sign_icons.py: the inverted brightness becomes the alpha, the tile's
frame cropped, the single portal dot centred), drawn as a TextureRect tinted per look by
story_p1_slides.icon_nodes(), used by all four signs. Words with no fitting icon stay words.
Seen in the period 1 and 4 playtests (design/research/sign-icons/2026-10-09/).
— Status: active.
A screen review page (2026-10-09, branch feature/sign-icons-and-screen-review, owner's
IDEAS.md "Art": "create an artifact showing all individual screens, letting me add notes per
screen"): all 156 screens (139 slides rendered offline in Godot, the 4 signs as captured in the
full playtest) on one private claude.ai page, grouped per period / endings / features /
attract, each with a note box saved to the page's shared database (db, collection notes)
for Claude to read back. Kept for later rounds: tools/display/screen_review.py renders
everything into a new dated folder with the page (tools/display/screen_review/index.html), to
republish to the same URL so the notes stay; how-to in design/research/screen-review/README.md.
— Status: active.
The shot map (2026-10-09, branch feature/sign-icons-and-screen-review, owner's IDEAS.md
"Gameplay": "create an overview of all shots that are existing all game long"):
design/shot-map.md, generated from the mode configs by tools/story/shot_map.py - a matrix of
the 21 playfield shots against every mode, every use per shot (counters, accruals, jackpots,
lights, callouts, screens, with their conditions) and "Scoring today", every score a shot gives
(the starting point for the IDEAS.md scoring calibration). Generated rather than written, so it
can't drift: tests/test_shot_map.py fails while it's stale. Found on the way: the spinner
(s-spin1, not installed yet) is used by no mode.
Then, owner: "include hardware and stories using the shots": each shot also lists its
registry parts (label, area, pins with numbers or draft, status flags, next step) and insert
LED, and every rule-sheet passage naming it, per chapter (tables split per row, the toy table
as "chapter: role"); the matrix gains Hardware and Chapters columns. generate_docs.py now
runs it too. It shows the Orange and Blue Portals have no switch yet: s-portal-r/-m are old
names, the portals are two of VUK1-VUK4 (listed as the candidates).
— Status: active.
Period 5 - The True Story (2026-10-09, away session, branch away/period-5; owner: "build
the next set of chapters"): modes/story_p5, Rattmann's period, on the shared layer. His red
arrow (flashing insert) points at 4 den shots in turn; making it opens the passage, Drp1 then
finds the den (his mural and scrawl). The PA chimes a round of official shots (500 each). Two
secret dens (50 spins; Up3 while the arrow is dark) and the 7th standup are dots too. 4 dots
light A Clear Head (2 balls, the Super Button); 4 jackpots, then Drp1's passage is the super
jackpot; draining to one ball brings the scrawl back. The box: the bridge loads VUK6, the
pedestal opens it - E6. No voice: den music, chimes, radio slices, I Saw a Deer Today. His
wall sign and 18 screens (tools/display/story_p5_slides.py), period 5's small ending (a
crackle). Tests: tests/test_story_p5.py (9), a period 5 fuzz storm, the period_5 playtest and
full through E6. Drafts and the hardware it waits for: TODO.md "Period 5's open points".
— Status: active.
Choice C3 after period 3's climax (2026-10-09, away session, branch away/period-5): the
climax used to end on its own (comply: E1; defy: E8 then E2). Now, after the 4th core (and E8
with defy), C3 opens ("It's not too late to turn back."): ESCAPE = the Turret Gauntlet exit (E1
with comply, E2 with defy - dragged back to period 4) or DIVE DEEPER = the Blue Portal (period 4,
p3_ending 3). Both shots are drafts; "follow the arrow" (period 5 first) waits for Rattmann's
hints. Period 4 and the small endings now take p3_ending 2 or 3. Tests updated (the climax
and meter tests make their C3 shot) plus C3's own (dive, the lights over a restart).
— Status: active.
Period 5's review fixes; Drp1 stays down until its step is done (2026-10-09, away session,
branch away/period-5). The fresh-context review of period 5: a single shot could make all 4
A Clear Head jackpots (now each cleaned shot goes dark; CONFIRMED there) and nothing reset Drp1,
so on the cabinet the first den would have left it down for good. Owner, asked: Drp1 stays
down until the step it was knocked down for is done, and "the end of the chapter" means each
stage in period 5. Now: a drop_targets: d-drp1 device (reset coil c-drop-single), reset at
ball start, at every chapter start and when a step ends (period 5: a den found, the super
jackpot lit, each stage; period 3: each rocket's step); drp1_down tracks it, and in period
5 a den is found at once if the passage opens with Drp1 already down. Also: restarts show A
Clear Head lit / the loaded box (no shot can). Tried and dropped the same evening: resetting
Drp1 3 s after every drop (the owner wants it down until the step ends).
— Status: active.
The CAROLINE letters and E5 (2026-10-09, away session, branch away/period-5): the hidden
layer C4 (modes/caroline): from 1B on, the 8 major shots in a hidden order (draft: Orange
Portal, Faith Plate, Left Orbit, Incinerator, Propulsion Run, Excursion Funnel, Hard Light
Bridge, Turret Gauntlet exit), one faint letter each (a glitch and the letters so far shown
for a moment, tools/display/caroline_slides.py), kept across balls and periods. All 8 turn
period 4's C5 "up to GLaDOS" into E5, she remembers ("Being Caroline taught me a valuable
lesson...", "It's been fun. Don't come back."), then period 5 as after E3/E4. It reads the
shared layer's <shot>_hit (every hit), so it changes nothing a period lights. Not yet: C1b
(siding with Caroline makes it easier), the letters on the signs, the stuttered glitch line.
tests/test_caroline.py (6).
— Status: active.
The scoring, calibrated (2026-10-09, away session, branch away/scoring, owner's IDEAS.md
"let us calibrate the shot scoring"): the August values were flat placeholders (100-3,000)
and the story scored nothing. Now a table calibrated against the Monte Carlo playthrough:
tools/sim/playthrough.py tallies what happens in each simulated game, tools/sim/scoring.py
applies a table and checks three targets (the story pays 55-75% of an average game, toys at
most 15%, expert/beginner 8-40x) - all pass (67%, 13%, 35x). Shots by difficulty (orbits 5,000,
ramps 6,000, Faith Plate 7,000, exit 10,000...), and a new modes/scoring that pays the story's
tests, locks, multiballs, jackpots, choices and endings, growing x1-x2 by period, plus the
standups and Drp1 - it only listens to the story's events. Tried and dropped: x the period
number (story 95% of an expert's score, spread 79x). 23 unit tests asserted the old values;
each new value was checked by hand against the table (several include a 1A story test's
10,000, since the tests play in 1A). Details and how to re-check: docs/scoring.md.
Then (owner): docs/scoring.md became the one place for how the game scores, the choices and
what they change, and the game and operator settings (it replaces design/scoring.md).
Review fixes: Drp1, the climax, C3 (2026-10-10, away session, branch away/period-5; the
fresh-context review of #86-#88, all CONFIRMED there): drp1_down is per player but Drp1 is
one target - another player's reset left it stale, so a period-5 den could be found without
Drp1 (now 0 at every ball start, when Drp1 is reset). A Drp1 hit inside the reset's 500 ms
switch mute, or after a failed reset, was lost and could stall the rocket - the story now reads
drop_target_d-drp1_down (MPF re-checks after the mute) and the device retries a reset
(max_reset_attempts: 3). A drain in the rocket's 5 s window locked the climax (older bug:
the rocket now re-arms when the climax restarts), and the 4th burned core re-enabled the
rocket during C3 (now off once C3 opens). stat_comply counts when C3 opens with comply, so a
comply player who dives counts too. Left for the owner: with Drp1 staying down, the meter's
defy can count only once per Drp1 reset while comply counts every test, so E8/E2 get much
harder to reach.
— Status: active.
The sign icons, per the owner's picks; the exit's light trail (2026-10-10, branch
docs/sign-icons, plans/sign-icons.md; #91 is on docs/human-todo): the owner walked through
the games' 64 sign icons (design/sign-icons.html, generated by
tools/art/sign_icon_catalogue.py) and set the rule: one icon per physical shot, since the
icons will become the playfield inserts, changing only for a big story moment. Built: the
portals are the game's ring drawn thicker (orange, or blue + orange, in the "in use" look - a
per-glyph colour, ICON_LAYERS), replacing the dots (picked by eye from mock-ups: the ring
over the figure-into-portal 5c the owner first chose); the turret drop bank is laser_burn,
the standups stay the turret; a cube-dispenser square next to the bridge while the dropper has
a lock lit or a ball held; the exit is the closed-door X until it opens; SPEED/FLING per orbit
everywhere (also the era dial), 2B's gel ORBIT the one exception. The sign code gained per-icon
options (closed, show, alt). The icon extraction no longer crops a fixed 8% (it clipped
the bridge's portals, the fling and funnel rings): only the tile's 4-pixel frame, its shading
and the step numbers go, and each drawing keeps the game's composition - a first version that
trimmed and centred every icon was dropped on the owner's word ("slight nudges only").
Hardware: no moving exit door (the Drp2 bank stays the door); HW-20's small screen becomes the
door sign; led-exit-open-1..5 become Portal's indicator-light trail (modes/major_shots): blue,
one dot orange per step of the chapter that opens the exit (1C's locks, 2C's 4 transfers -
new p2c_transfers - period 4's 6 dial clicks), all orange once open; the feature-phase
portal mode's purple stages show outside those chapters. Recorded only: co-op's team-door
icons (rule sheets). Left: the cube's fate as an icon waits for a chapter with a cube task and
the file screens; five words keep text (owner's list). Not yet run: stage 1 and the playtests
(owner: build first).
— Status: active.
A calmer chamber sign: icons appear on unlock, and four looks as a setting (2026-10-10,
branch feature/sign-layout off docs/sign-icons, plans/sign-layout.md (retired, #158); owner's picks in
plan mode): a locked sign icon is no longer a grey square from the start - it appears when its
test unlocks, in its own fixed place (an empty gap until then, so the row's order matches the
test list; the owner picked that over appending at the row's end). Only a closed exit keeps
its square from the start, since the X is the "closed" message (#92). New operator setting
sign_style (owner: "all four options"): 0 full (today's sign), 1 number + title +
big icons (the default - owner, after comparing them on the screens page), 2 like the games (number + big icons), 3 story lines without the test list. Every
sign widget is generated in four versions (<sign>, <sign>_s1.._s3,
story_p1_slides.STYLES); each story mode shows the picked one at its start via
mode_story_pN_started{settings.sign_style==n} and sends every update to all four - mpf-gmc
ignores an update for a widget that isn't on screen (MPFSceneBase.process_action), so no
per-style update entries are needed. Draft answers to the plan's open points, built as such:
big icons wrap at 5 to a row into even rows (1B: 4 + 4); period 4's year/dial and period 5's
dots/scrawl stay in every look, their text lines follow the style; overlays unchanged. Tools:
playtest.py --setting NAME=VALUE (runs land in ...-full-sign_style2), the playtest base
checks that every sign GMC showed is the picked look, and the screen review shows each sign's
four looks next to each other (it also left period 5's signs out by a p[1-4] glob - fixed).
After comparing: style 1 is the default, and period 2 ("too little icons") shows one square
per lit shot in its gel tests - 2B SPEED + FLING (ending #92's gel ORBIT exception), 2C the
orange and blue portals; 2A keeps one bumper square (the slings count too, no game icon).
Icons for text-only squares (owner): CORE is the pellet catcher (overlay_catcher), UP the
game's down arrow flipped (sign_icons.py FLIPPED); TAPE has two drawn candidates
(tools/art/tape_icon.py: Lucide's cassette, ISC, or a reel-to-reel); the owner picked the
reel-to-reel, without the head block under the tape (sign_tape).
New generated list design/sign-icons-per-toy.md (tools/art/sign_icons_per_toy.py).
— Status: active.
C5's words: STAY WITH CAVE / FIND CAROLINE (2026-10-10, branch feature/sign-layout;
owner's pick from six lore-based options, IDEAS.md): "HOME / UP" didn't read as Portal. The
choice now names the people, not the directions: the tape has just told you Caroline became
GLaDOS, so going up to GLaDOS is finding Caroline. Only the words changed - the shots
(Left Orbit / Hard Light Bridge), the endings (E3 / E4-E5) and p4_ending stay. The sign's
squares are CAVE (was HOME, still a word) and UP (the arrow, now "up to her"). The sign's title
is "4 CAVE OR CAROLINE?"; the choice
overlay's big text is drawn at 84 px instead of 110 to fit (overlay(..., big_size)). The
other options, kept for the record: LEMONS / CAKE (the runner-up), CAVE'S ORDERS / CAROLINE'S
CALL (echoing C1b), TURN THE DIAL / TAKE THE LIFT, 200- / NOW, BRING YOUR DAUGHTER / WAKE HER UP.
— Status: active.
The real choices' lines show in every sign look (2026-10-10, branch feature/sign-layout;
owner: "all the real choices" should show in style 1, then picked in a walk-through): sign
style 1, the default, hid the test list, and with it the only place a choice says which shot
picks what. Now, while a choice is open, each option gets one line <WORD>: <THE SHOT> in
all four looks, covering the test list where there is one: C1 (BURN IT / KEEP IT - the owner
picked these over the overlay's BURN / KEEP), C3 (ESCAPE: THE EXIT - the short name over THE
TURRET GAUNTLET EXIT), C5. Look 2 ("like the games") shows them too (owner: a choice you
can't read isn't a choice). C0 and C1b get the same format once built. sign(..., choices=)
in story_p1_slides.py, each script's CHOICES; period 3's sign now updates on p3_c3
(tests/test_chamber_signs.py caught the missing entry). New captures: p1_05b_sign_c1,
p3_04_sign_c3 (period 3's playtest now skips ahead to C3 at its end). Details in
design/SCREENS.md; the plan (plans/sign-choice-lines.md) is folded in and deleted (#47).
— Status: active.
C5's squares are Cave's and Caroline's faces (2026-10-10, branch feature/sign-layout;
owner's pick): the owner wanted the up arrow gone. Drawn candidates came first (the shots' own
icons, lemon / heart core, burning lemon / GLaDOS, era dial / Caroline in a core, man / woman -
tools/art/cave_caroline_icons.py, sheet in design/research/sign-layout/2026-10-10/), then
the owner asked for their faces. Portal 2 has them: cavejohnson_portrait01..04 in
materials/models/props_underground/, 04 being Cave with Caroline - the only picture of her in
the game files. The owner picked both faces from that painting (over the 1950s and 1970s Caves).
cave_caroline_icons.py faces crops them to sign_face_cave.png / sign_face_caroline.png;
the signs show a photo for a word in story_p1_slides.PHOTOS (a glyph's tint doesn't work on
a photo: as is in use, faded when not, blue-washed when done). The icon docs list them as
photos. Then (owner): the faces are grey on the sign (sign_face_*_grey.png) and the
picked one shows in colour above its ending (E3 Cave, E4/E5 Caroline), since a "done"
look on the sign is never seen (the period-complete layer covers it); and each face sits
with its shot's icon, a "/" between: CAVE SPEED / CAROLINE BRIDGE (SEPARATOR, no square). Like the drawn options they don't match the Left Orbit's and the bridge's inserts - a
story-moment exception (#92). The same faces are ready for the speaker bar and the C5 choice
screen ("two faces, two words", not built yet).
— Status: active.
The difficulty setting, phase 1: counts, clocks and the start card (2026-10-10, branch
feature/sign-layout; owner's IDEAS.md item, decided in a walk-through,
plans/difficulty-levels.md): operator setting difficulty ("Testing protocol"), four
levels named in Portal's voice - 0 Bring Your Daughter to Work Day (the default), 1
Standard Testing, 2 Advanced Testing (the build as it was), 3 Military Android.
Points are the same at every level. The difficulty mode starts with each ball and copies
the level's value of every count and clock into a player variable (dl_*, declared in
config/difficulty.yaml); the story counters (C1-C9, C11), the neurotoxin, defend and
rocket clocks and the ball save read them, and the signs show them ("PROPULSION: 3 ORBITS").
MPF 0.80 templates take a if settings.difficulty == 0 else ... but not a tuple index
(_eval_tuple doesn't return a subscription, and a non-constant subscript raises on
Python 3.9+), hence the chains; variable_player isn't allowed outside a game mode, hence
the mode. 1A's card shows "TESTING PROTOCOL: " (dl_level = level + 1, as
MPFConditional hides on 0). The existing story tests are pinned to Advanced Testing;
tests/test_difficulty.py checks every level's goals and plays 1A/1B at levels 0 and 3; the
playtests default to level 2 (TODO.md). The values and their tracker: docs/scoring.md
section 6 - a design/scoring.md made for this on the same day duplicated that doc (which
had replaced an older design/scoring.md in #89) and was folded into it. C10
stays 6 at every level (the dial's six decades are its art). Not yet: the gates and the
rejects (phases 2-3, TODO.md).
— Status: active.
The difficulty setting, phase 2: the gates (2026-10-10, branch feature/sign-layout;
plans/difficulty-levels.md, docs/scoring.md section 6): each gate opens through an event
posted at the chapter start (and on a restart) when the level allows it, which enables the
gated counter and sets the "in use" variable the lights and the sign already follow.
G1 1A's Left Orbit test lit from the start on levels 0-1 (p1a_orbit_open sets i_orbit).
G2 Military Android only: 1B's tests 4-6 open one at a time as tests finish (counters
p1b_slot_4..6 on p1b_test_finished), the rule sheet's "3 lit at a time"; their sign squares
follow new in-use variables i1b_funnel/i1b_cake/i1b_standups (set at the chapter start
below level 3). G4 level 0: 1C's turret bank and exit open with the locks; G5 level 0:
each decade's lock opens with its gel test (i2a_lock..i2c_lock, also the sign's in-use
variables now). An opened lock is parallel, not skippable: the escape and each next decade
now start from an accrual of all the parts (p1c_ready, p2a_ready..p2c_ready), which in
the gated order completes on the same shot as before. G9 the era dial: level 0 keeps the
Faith Plate's "next one too" lit after every click (two dial shots lit), Military Android gets
no plate help; correcting the proposal, Advanced Testing keeps today's plate help. Tests in
tests/test_difficulty.py (16, one or two per gate); tests/test_orbits.py pinned to level 2
(at level 0 the first orbit also finishes 1A's open orbit test). Not yet: the rejects (phase 3).
— Status: active.
The difficulty setting, phase 3: the rejects; the plan is done (2026-10-10, branch
feature/sign-layout; docs/scoring.md section 6): R1 the climax's cores take
dl_core_first / dl_core_rest Incinerator hits - the first core is never refused on Bring
Your Daughter to Work Day, refused once on Standard and Advanced Testing (the build), and every
core is refused once on Military Android. R3 on Bring Your Daughter to Work Day a missed
defend restarts the defend clock (p3b_defend_again) instead of resetting the wave. Checking
the build against the proposal: R2 (a timed-out 3A test) already stays lit with a new clock -
the rule sheet's "back in the backlog" was never built - and R6 (the drain resume) isn't built
either (the clock restarts in full), so both are the gentle version at every level; R4 waits
for the hatch clock. Tests: tests/test_difficulty.py (20). With the three phases built,
plans/difficulty-levels.md is deleted (#47): its decisions are in docs/scoring.md section
6 and #97-#99, its research items in TODO.md.
— Status: active.
Review fixes for the difficulty setting: a test or lock counts once (2026-10-10, branch
feature/sign-layout; fresh-context review of #97-#99): MPF resets a completed counter by
default (reset_on_complete), so a later enable lets it count and complete again. That hit
three counters whose enable event can now fire after they're done: 1A's Left Orbit test
(each completion posts p1a_test_done, whose hit re-enabled it - the orbit alone could finish
1A; this was already possible at Advanced Testing with the button + orbit + orbit, G1 made it
the first shot), period 2's locks on level 0 (a lock made before its gel test was
re-enabled by the test and scored p2_locked again later - breaking "the same points at
every level") and 1C's exit on level 0 (harmless today). All three now have
reset_on_complete: false (complete() returns early while a block is marked completed).
A first try - guarding the enable with {current_player.<done>==0} - didn't hold for the
orbit: the re-enabling hit event is handled before the variable_player sets the done flag.
Also noted by the review, kept: at Advanced Testing the chapter events from the new accruals
(story_chapter_2b/2c, story_lemons_start, story_escape_start) now come after the
lock's own complete event, so the decade's outro callout plays before the next decade's
intro (the other way round before) - arguably the better order. Tests in
tests/test_difficulty.py (23).
— Status: active.
The final story review, phase 1: the story files agree (2026-10-13, branch
story/final-review-fixes; plans/story-final-review.md). The owner's answers to the final
critic panel (2026-10-10, design/story-final-review.md) are logged as D29 in
owner-decisions.md and written into design/STORY.md, rule-sheets.md, callouts.md and
the feature docs (orbits, dropbank). The main changes: the Portal 1 finale in its canon order
(H83: "You are kidding me." is her reaction to the first core, the Morality Core, burning;
"Time out for a second..." is the first core falling; comply's line is spheredestroy4-04,
weakened rather than threatening; E8 is the explosion, the shaker and her death scream) -
this changes D23's E8 line; a core falls after every rocket and only the first is
refused (H82, the built rule); the cube burn uses the canon line and "COMPANION CUBE
EUTHANIZED" ("Goodbye, my only friend" is in neither game); C1 gets a ~30 s way out to the
keep side (H105); 1B's layers drip by difficulty (H103); no Space Core in 2B (H98); the
meter shows as one word on C3's screen; Portal A/B are the Orange/Blue Portal; all six dens
via Drp1; E3's card names level 0's joke (H84 c); E3-E5 hand over to period 5 and E6 closes
every game that reaches it (H86, already the build); period 5 is "no character speaks"
(H94); period 4's opening per way in; attract alternates the radio mix and the Portal 2
playlist; the lemon speech is the 4 s rule's one exception and the momentum line is split;
period 1's ball save no longer spends E1's "Killing you? Is hard."; new content H88, H89,
H91, H92 (attract only) and H101 (branch). Not now: H85, H87, H90, H93, H99, H100, H102,
H104; skipped: H95; later (TODO.md): H96, H97. D22's ~20 min run length is marked
superseded by the owner's 25-30 min. Docs only: the built modes follow in phase 2.
Interpretation to confirm: "hidden the rest of the game"
was read as dropping D20's operator setting that would show the meter as a needle on the
sign (never built). — Status: active.
The story chain review: tooling and the open forks (2026-10-16, branch
review/story-chain; plans/story-review-chain.md). From IDEAS.md's story and gameplay
questions. The owner reviews story -> chapters -> story modes -> shots on one page,
design/research/story-chain/review.html (generated by tools/story/chain_review.py, served
by serve.py on port 8766, answers in answers.json): per part of the game its story, rules,
callouts, each toy's role next to what the mode does, and the mode's build notes. The shot map
got a Per chapter section (the toy table's role vs the config, with a "not in the config
yet" status) - the answer to "where is the overview of shots per story", next to the toy table
in rule-sheets.md (design) and the per-shot section (build). The shot map now also counts each
drop target's own events (Drp1's dens in period 5 were missing). The open forks are listed side
by side in design/research/story-chain/options.md for the owner to pick on the page: the
comply/defy meter (M1-M4, M3+M4; today comply fills on every test and defy rarely, so E8 and
E2 are rarely seen), the hidden room's dens (H1-H3, H1+H2) and its display (D1-D3), and scoring for
beginners (S0, S2-S4). The scoring benchmark (scoring-benchmark.md): no published split of
story/trick/toy scoring exists; designers and players call a game balanced when the intended
path pays most and warn against one dominant repeatable shot (good for beginners, dull in
competition) - our simulated story share (54/67/85% for beginner/average/expert) fits; the
simulation has no combo/trick category, so that third of the question stays unmeasured. The
beginner setting asked for in IDEAS.md was already built (#97-#99). — Status: active.
All printables in print/; a printable toys-and-hardware overview (2026-10-18, branch
review/story-chain). The owner asked for a printable overview of the hardware and the shots
that use it, including what's still to build, and for every printable in one folder so they're
easy to find. tools/story/toys_print.py writes print/toys-and-hardware.html (A4 landscape,
run by generate_docs.py too): every toy of the rule sheets' toy table with its registry parts,
each part's build progress and next step, and dots for the chapters that use it, grouped by the
least-finished part (wired / installed / to build / to get) - including the story toys without
a switch (posts, magnet, LED strips, shaker), the 3 core locks and the proposed den display.
design/physical-checklists/* (wiring guide, status checklist, trough opto bridge) and the
gamebook print (design/short-story-paths-print.html/.pdf) moved to print/, with an index
(print/README.md); every generator, the CI freshness check and the references point there.
Older CHANGES/TODO-done entries keep the old paths as history. — Status: active.
Feature groups for the status checklist (2026-10-18, branch review/story-chain; the
owner's picks, walked through one by one). 26 parts that sat under "Individual parts" got a
group: Slingshots; Trough and launch (trough, launch, launch-lane diverter); Ball savers (BSv1,
and BSv2 the kickback - not Bottom lanes); Incinerator (its lock and Up4); GLaDOS's cores
(CoreLock1-3); Rattmann's den (Drp1, the den display, the hint LED row); Super Button;
Shortcut gate (Up2); Light effects (the two strips); Feel effects (shaker, magnet); Tilt. KD1
joined Turret Gauntlet; Up3 joined the VUKs, renamed Portals and VUKs (the shot map and the
toys overview still take only the VUKs as portal candidates). Only Screen-Small stays
individual. components.yaml entries were reordered so each group is adjacent (the registry
check's rule); nothing but the group: fields changed. Rejected alternatives: slings with the
pops, one "Ball handling" or "Machine basics" group, the incinerator with the core locks, the
hint row with the light effects, the tilts with the service buttons, a "Screens" group.
— Status: active.
Nothing is tested in game yet; printed groups stay on one page (2026-10-18, branch
review/story-chain). The owner: "slings are not tested in game yet, nothing is." The 11
parts marked game_tested: "yes" (slings, bottom lanes, flippers, Launch1, the Start and Launch
buttons) are back to "no", so no part prints as complete. The printed status checklist puts
each feature group in its own tbody that won't break across pages (checked in an Edge PDF: 8
pages, every page starts on a group). Also: chain_review.py used an f-string that only
Python 3.12+ parses; CI runs 3.11, so PR #72's first check failed - fixed, and the new tools
now compile under 3.11. — Status: active.
The chain review's first answers; the toy table split for review (2026-10-18, branch
review/story-chain-answers). From the owner's answers so far (answers.json): only period 5
is the EPILOGUE now - removed from period 4's headings in STORY.md, rule-sheets.md and
callouts.md (the built period 4 sign still shows it: TODO.md, phase 2); after a drain ~1 s of
silence, then a mock from the chapter's antagonist (callouts playback rule 3, was ~2 s of
silence; period 5 stays voiceless); the meter must feel like a choice, with the tests needed to
progress weighing little (input to the meter decision in options.md); two callout sections go
to a sound-by-sound review (IDEAS.md's actions). The review page now splits the toy table into
one item per chapter per toy, plus each chapter's idle toys (chain_review.py, owner
request). — Status: active.
1B's cube chain loses the pedestal: the Super Button drops the cube (2026-10-18, branch
review/story-chain-answers; the owner's review, then two picks). The chain is now dropper lock
-> Super Button (Servo1 drops the cube onto it, with "The Vital Apparatus Vent will deliver a
Weighted Companion Cube...") -> choice C1; still in order at every level (G6). Built in
story_p1 (chain2_pedestal gone, the button's step plays the vent line and the "cube delivered"
card before C1's), the sign (story_p1_slides.py), the tests, the period 1 playtest and the
sim (story 68% of an average game, all targets pass). The pedestal keeps its period 5 job (the
box). Rejected: the Faith Plate or another shot as the drop step; opening the chain's order on
the beginner level. Also answered: 1B's 6 tests run in parallel at levels 0-2 (3 at a time on
Military Android), next to the chain. — Status: active.
The chain review, round 2: D30's picks; sounds and screenshots on the review page
(2026-10-18, branch review/story-chain-answers). Logged as D30 in owner-decisions.md.
The owner picked H1+H2 (the hidden room shows a den per period plus the fragments of the
hints found; his full wall in period 5 - STORY.md, the rule sheets' toy table and period 5),
D2 (the den display is a Pi Zero/ESP32 with a small LCD - the registry's DenDisplay) and
S0 (beginner scoring stays as built). The meter stays open: the owner's M6, its fork's
place (M6a/M6b) to pick. The review page now ends every "Callouts" block with the section's
sounds, one per row: a play button, what's said, when it plays, a select checkbox and a note
(330 sounds; the files come from assets/sounds and machinefolder/sounds, which serve.py
now serves read-only), and attract's tab shows its 76 pages as screenshots (a fresh Godot
render, design/research/screen-review/2026-10-18-chain-review/) with their voice lines, plus
the music playlist. The per-tab "This tab, overall" checks are gone (owner). — Status: active.
The "lost you" lines: ball search's lines play at more moments (2026-10-18, branch
review/story-chain-answers; owner: "they are really cool"). Callouts rule 12 and a "Lost
you" row per period: rarely (1 in ~4, at most once a ball) when the ball is in Rattmann's
passage ("Where are you?", "Sir, the testing?", "I know you're there. I can feel you
here."), on a portal transfer ("Target lost", "Where are you?") and when the ball is held
~8 s with nothing happening ("Are you still there?", "We're still doing science, sir.",
"Searching", "Can you hear me?"). Not picked: period 4's opening. Not built yet (TODO.md, with
ball search's own lines). — Status: active.
A drain: the fizzle at once, the mock 1 s later (2026-10-18, owner). Refines #106's
"~1 s of silence, then a mock": the Emancipation Grill's fizzle (sfx_portal_fizzle_01)
plays straight away in every period with a voice, and the chapter's antagonist's mock starts
~1 s after the drain, over the fizzle's tail. Callouts rule 3 and the drain row, the rule
sheets' period 1 and STORY.md follow; not built yet (TODO.md). — Status: active.
The comply/defy meter, decided (D31): choices fill it, the last core is your shot
(2026-10-18, branch review/story-chain-answers; M6a with Rattmann's trail, the details walked
through with the owner). The meter counts C1 (burn +3 / keep -3, a made shot only) and each of
Rattmann's arrows made (-1; 2 set moments per chapter, 1B-3A); tests count nothing. At the
last core the sign shows BURN THE CORE / SPARE HER with the meter's word (moved from C3): the
meter's side flashes (an even meter: burn), the other still works; the Super Button spares
GLaDOS, the Incinerator destroys her (E8); after 40 s (a new operator setting,
climax_choice_time) the lit side happens. GLaDOS pleads (candidates picked, to listen),
Rattmann's scrawl points at the fire. E1/E2 now follow spared/destroyed. The same at every
difficulty level; the plunge side no longer nudges the meter. Docs only - story_meter and
story_p3 change in phase 2 (TODO.md). Rejected in the walk-through: the Blue Portal or a
time-out as the spare, tests at 0.5 or arrows counting both ways, random or hint-tied arrows,
waiting forever, an even meter lighting both or spare, C1's time-out counting as keep.
— Status: active.
The chain review, round 3: period 1's sounds (2026-10-18, branch review/story-chain-answers).
From the owner's sound-by-sound answers: two trims (A14 "You can create your own portals.",
A15 "...it can now manufacture two linked portals at once."), ball 1's praise cut at "oh
wait" plus "I can drop the fake praise." (A16), live fire with its lead-in line ("Due to
mandatory scheduled maintenance..."), the incinerator escort line always plays, H91's "never
threaten to stab you" in the dropper's hold is dropped (owner's pick; the period 3 callback
stays), Caroline's "Mr. Johnson!" (period 2 tilt) and "I am!" (period 2 bonus) dropped.
Candidates to listen to on the review page: the fling ("For instance, the floor here will kill
you..."), the funnel (two), the laser relay (the laser switching on, no voice), the LIE sting
(Ghost of Rattman or the den fan) and period 2's drain mock (three Cave bites).
— Status: active.
HumanToDo.md: the owner's items split out of TODO.md (2026-10-18, branch
docs/human-todo-v2; the owner's IDEAS.md request of 2026-10-10, redone on today's TODO).
33 open items waiting on the owner move to HumanToDo.md under Read and look, Listen,
Decide, At the cabinet and Outside the repo; TODO.md keeps what Claude builds or fixes and
points there. Two TODO items were checked off instead: the meter vs Drp1 (settled by D31) and
merging the old branches (docs/sign-icons merged and deleted). The first try,
docs/human-todo (2026-10-10), no longer merged with main and is deleted.
CLAUDE.md and README.md name the new file. — Status: active.
The chain review, round 4: period 1's cuts made, picks and pools, two toy changes
(2026-10-19, branch sound/p1-review-cuts). Edits A14-A16 made with faster-whisper word
timings (A16 in a short and a long version; the owner picked the long one, and reopened
"cut later, add the insult" to get it); A14 swapped into story_p1. LIE complete gets a
voice pool of 6 instead of a music sting (all stings dropped; "the cake is a lie" still
never spoken), with A18 "Have I lied to you?"; the funnel becomes a pool of 2, the fling one
line, the laser relay's three sounds accepted one per moment. Toys: the Blue Portal gets its
own VUK (a playfield change, no longer the Mid-Flipper's shot); the skill shot is a top
lane from the plunge with Up1 down, and LIE completion is its own parallel mode, running
from 1A; later the same day the owner moved CAKE to 1A too (both lanes from 1A; the other
layers still start in 1B). The
review page now shows how each sound plays (in a row / pool / candidate, from callouts.md's
"->", "pool" and "candidates") in both the table and the sound list, and candidates get Pick,
Add to pool or Drop. — Status: active.
The lemon rant in two (A19) (2026-10-19, branch sound/p1-review-cuts). Cave's
cave_eighties_outro11 played whole when the super jackpot lit, and its last sentence was
planned again for collecting it - the punchline twice within a minute, giving the payoff away.
Now the rant (12.6 s) plays when it lights and "...a combustible lemon that burns your house
down!" (5.5 s) when it's collected, both wired in story_p2. The lift up's "Make life take the
lemons back!" stays a deliberate callback to the multiball's opening line. — Status: active.
Cave's firing joke played whole, as one file (A20) (2026-10-19, branch
sound/p1-review-cuts). "Not you, test subject, you're doing fine." is the middle of a three-line
joke (cave_fifties_fifth_test_complete09 "...because you are fired." -> -03 -> -08 "Yes, you.
Box..."), but it played alone in period 2's test-done pool, period 4's tape pool and an attract
quote, answering a firing nobody heard. Owner's pick (option A of three): the three lines joined
into one file (A20, 21 s) as period 2's small ending (its safety timer 15 -> 25 s), out of the two
pools; and, on the owner's follow-up, attract's quote q_cave_fine keeps the joke whole - text and
voice. A new long: true page flag in attract_slides.py holds the cycle for the extra seconds
when that quote is drawn, so the other quotes keep their 8 s slot. Rejected: parts 1+2 at the 2B
card with part 3 as a callback (B), dropping the halves only (C). — Status: active.
Meet yourself on the testing track, built (2026-10-19, branch feature/meet-yourself).
2C's easter egg was "a portal swap with another ball on the same side", which the machine
can't sense. Owner's pick: two portal swaps within 3 s during the lemons multiball, once per
multiball, in place of the infinite fall's quick-swap award there. story_p2: counter p2_met
with a 3 s window timer -> p2_met_yourself: the slide "YOU MET YOURSELF / DON'T MAKE EYE
CONTACT", Cave's warning cut to 3.8 s (edit A21), 12,500 points (like a test done). On the
placeholder portal switches s-portal-r/s-portal-m, like 1A's transfer, until the portal
redesign. — Status: active.
The lift up's callback (A22) (2026-10-19, branch feature/meet-yourself). The end of the
lemons multiball (the lift up to 3A) plays "Make life take the lemons back!", cut from the
multiball's opening speech (cave_eighties_outro09, 2.2 s). Hearing that line twice is on
purpose (owner, option A): it closes the chapter. Rejected: Cave's "Allright, test's over. You
can head on back to your desk." there (B), no voice (C). — Status: active.
E1 no longer ends the game: the radio route (2026-10-19, branch story/e1-goes-on). Escaping
with GLaDOS spared (E1) ended the game after period 3, so the player who spared her lost
periods 4 and 5. Now E1 plays as before (the cake, Still Alive), then Portal 1's radio breaks
into Rattmann's signal - SOMEONE IS STILL DOWN THERE - and the game goes on: period 5, the box
opens empty onto a shaft, the fall into period 4 (as on the arrow route), C5 and its ending,
then E6's file with "ESCAPED. CAME BACK ANYWAY.". Owner's picks: going back is the default and
leaving is the hard choice (making the Turret Gauntlet exit in a short LEAVE FOR GOOD window
ends the game on E1); period 4 stays a real place you fall into (C5 still a present choice),
only the tape reveal plays as a sepia memory. Considered: E1 then period 5 only (A, the base of
this), a surface victory mode (B), a lap-2 loop (C), E1 moved to the very end (D), A+C (E); the
radio as an opt-in choice, and period 4 as a whole flashback. Docs only - build steps in
TODO.md. — Status: active.
Rattmann's one return in the dark 3B (2026-10-19, branch review/rattmann-tab). RA-9 has
every Rattmann feature go dark from the turret finale on; D31 (#111) later put his scrawl at
the last core, pointing at the fire - a contradiction found while building his review tab.
Owner's pick (A): keep the scrawl as his one exception, on purpose - after the silence his
return lands, and GLaDOS's plea and his scrawl argue over the last shot. Rejected: remove the
scrawl (B), his last arrow pointing at the Incinerator instead (C). — Status: active.
The finale's turret bank is the last wave (2026-10-19, branch review/rattmann-tab). The
owner's review: with the drop bank as wave 1, the Turret Gauntlet exit opened for waves 2 and 3.
Now waves 1 and 2 are the standups and wave 3 is the drop bank, so the exit stays blocked until
the end. Built in story_p3 (the counters swap, the Gauntlet insert lights at wave 3, the sign's
texts and icons follow); the sabotaged turret's knock-down coil now fires in wave 3. Also from
the review: three period 3 pools (timed test done, Turret Air's launches, GLaDOS's plea at the
fork). — Status: active.
The Wheatley branch's open points (2026-10-24, branch story/wheatley-chapters-always-offered).
The branch's two chapters (3A The Itch, 3B The Part Where He Kills You) are always in the chapter
select (the hidden-until-played unlock of H81/PT-3 dropped) and start on the plug-in path. Four
gaps answered: (Q1) after "SPACE!" (E7), escape is E1 with Portal 2's own finish (Want You Gone,
the opera, the cube thrown out); E2 and E8 don't exist on the branch. Rejected: plain E1, the
meter deciding E1/E2, a new branch-only E9. (Q2) PotatOS reads your file at E7: a comply/defy/even
line with the meter's word, before C3. Rejected: the word alone, at the corrupted cores, no
moment. (Q3) The Escape multiball runs on both paths; on plug-in its end is the core transfer.
Rejected: the transfer as its super jackpot, the transfer replacing it. (Q4) The corrupted-core
multiball is 4 balls (3 or 3 + add-a-ball rejected). Text only; the branch is still a stretch
goal. — Status: active.
The review page's Choices tab (2026-10-24, branch story/wheatley-chapters-always-offered).
The owner asked for every choice in one tree, with descriptions and how each is made, to review
them: a new tab after Endings, from design/choice-tree.yaml (a summary of STORY.md and the
rule sheets, which stay the source of truth). Each node tags how it's made (shot, held choice,
meter, collected, time-out, story), what it means, its source and the gaps the docs leave; the
collected layers (meter, arrows, hints, letters, singing turret, moon rock) come first, then one
section per period, linked by "on to" jumps instead of nesting. test_chain_review fails when a
choice or ending code in STORY.md has no node. Also: a branch chapter name ("3A The Itch") no
longer gets the trunk's gloss on any tab. — Status: active.
Options: the meter and what the game remembers (2026-10-24, branch
story/wheatley-chapters-always-offered). The owner asked (plan mode) for mini plans to sway
the meter with deliberate choices, to let it influence more, and to improve the other things
the game remembers. Three decisions on the review page's Decisions tab
(design/research/story-chain/options.md): A1-A5 (every route choice has a side; GLaDOS's
order against Rattmann's arrow; cake or lie; GLaDOS's yes/no questions; +1 per arrow-free
chapter), B1-B5 (it leans C3/C5 and their defaults; who helps you; the characters' tone; your
file and K2; GLaDOS remembers between games), R1-R6 (C1b matters; Wheatley's visits feed C0;
the turrets remember; the cube comes back; one file showing everything; per-machine unlocks).
Plus a table, Everything the story tracks, from design/choice-tree.yaml's layers, now
also listing the cube, the path, C1b picks, Wheatley's visits, sabotage, the cake, your file's
counters and the four machine-wide records. Options only: nothing changes until the owner
picks. — Status: active (awaiting picks).
The critic panel on the meter and memory options (2026-10-24, branch
story/wheatley-chapters-always-offered). Four invented critics reviewed A1-A5, B1-B5, R1-R6 and
the tracked table against STORY.md, the rule sheets and callouts
(design/research/story-chain/critics-meter-memory.md; every voice line checked in the catalogue,
spot-checked by the lead session). Champions: A2, B4, R5, R1, B1 at C3. Worries: A5 repeats a rule
rejected in D31, A3 counts a progression test, A4 overloads chapter ends, C1b on GLaDOS's meter
is the wrong ledger, period 2 has no GLaDOS. Their hybrids H106-H119 are extra options on the three
decisions (options.md); two findings went to TODO.md (the meter almost always lights BURN; the
visits' triggers at levels 0-2). Nothing decided changes until the owner picks. — Status: active.
D32: the meter and what the game remembers, picked (2026-10-24, branch
story/wheatley-chapters-always-offered; walked through one by one after the critic panel, #125).
Sway: H106 (GLaDOS's order shot +1 against Rattmann's arrow -1 in 1B, 1C, 3A; period 2 keeps
lone arrows; range +9 to -15, so keeping the cube no longer always lights BURN). Two ledgers:
H108 (the meter stays GLaDOS's; C1b becomes Cave's record). Forks: H115 (C3 leans and defaults
by the meter, C5 by Cave's record - settles the open C3/C5 defaults). Payoffs: H113 (the
two-page file, co-op-credits adjectives + R5's checklist), H111 (after an E8 the next game opens
with "I've been really busy being dead..."), H118 (time-outs voiced), H116 (compete points).
Memory: H117 (the cube beside BURN), R3 (the singing turret sits out the finale, the sabotage
count named), H110 (GLaDOS quotes your file in play), R4 as scrawl only, and the attract stats
counting the last core's shot. Held: H112, R2. Not picked (kept as options): A1, A5, B2, B3,
A3/H114, A4/H107, H109, H119. Decision recorded only - the design docs and the build follow
plans/meter-and-memory.md. — Status: active.
D32 written into the design docs (2026-10-24, branch story/wheatley-chapters-always-offered;
plans/meter-and-memory.md phase 1). STORY.md (the meter as GLaDOS's ledger with H106's order
shot; Cave's record; the file; C1b; the finale's turrets; the last core's cube icon; C3's and C5's
lean; Cave's ghost; the wall's cube scrawl; compete's points; GLaDOS remembering an E8; D22
reworded: a shot picks, the leaned side is the time-out default, voiced), the rule sheets (the
same per chapter; C3/C5 time-outs 20 s, draft), callouts.md (rows for every new line, marked D32),
SCREENS.md (planned screens), the Choices tree (meter card, Cave's record, C3/C5 leans). The
attract page's "COMPLY OR DEFY" text moves to phase 2 (a generated slide, needs a playtest).
Docs only; the build is phase 2. — Status: active.
The review page's Time-outs tab, and music's picks (2026-10-24, branch
story/wheatley-chapters-always-offered). The owner asked for a tab to adjust every time-out: a
card per story choice (C1, C0, C1b, the last core, C3, LEAVE FOR GOOD, C5) with today's rule and
fields for the wait, what happens when it runs out (the default, or keep waiting), the default side
(or "the side the meter / Cave's record lit", and what happens when nothing is lit), the voice, and
whether a time-out counts as the player's choice; plus the live-play windows' lengths. Source:
design/timeouts.yaml (a summary of the rule sheets); answers save as one record per card. On top,
the owner's question as a decision, "Time-outs: the standard": T1 a 10 s quick-decision bonus
then a grace by difficulty (45/30/20/15 s in all), T2 one length per difficulty, T3 bonus + one 30 s
grace, T4 beginners never time out; every option keeps a time-out from counting as a choice. Also
recorded from the walk-through: music pool P1 (a new track per chapter) and borrowing Bombs for
Throwing at You for the main path's finale (music-plan.md). — Status: active (awaiting picks).
Decided decisions leave the review page (2026-10-24, branch
story/wheatley-chapters-always-offered). The owner asked to remove the decisions already made
from the tab and keep their alternatives as documentation for later review: the nine picked
decisions (the meter D31; the dens and their display, beginner scoring D30; the endings' colours
K1; the three D32 decisions; the music) moved whole, with every unpicked option, to
design/research/story-chain/decided-options.md. options.md now holds only open decisions (today:
the time-outs' standard, on the Time-outs tab) and says that a pick moves the decision out.
References updated (owner-decisions D31, docs/scoring.md, the meter plan, the critic panel's note).
— Status: active.
D33: the choice window - beginners never time out (2026-10-24, branch
story/wheatley-chapters-always-offered; option T4 of "Time-outs: the standard"). Every held choice
(C0, C1b, the last core, C3, C5): a 10 s decisive window (a pick scores a DECISIVE bonus, value to
set), then a grace with one nag; 30 / 20 / 15 s in all on levels 1-3, and on level 0 (Bring Your
Daughter to Work Day) no time-out - the ball stays held, the nag every 20 s. A time-out never counts
as a choice. C1 and LEAVE FOR GOOD keep their own clocks. It replaces the planned 40 s
climax_choice_time setting and the 20 s drafts. Written into STORY.md, the rule sheets (a new
"Every choice" section), callouts.md (the grace's nag row), docs/scoring.md (timer T7) and
design/timeouts.yaml; the decision moved to decided-options.md. Open: C1b's time-out default,
the DECISIVE bonus's value, a nag line per fork. — Status: active.
The choice window's grace nags, proposed (2026-10-24, branch
story/wheatley-chapters-always-offered). One nag per fork when the grace starts (D33), from the
catalogue, in the fork's own voices, unused elsewhere: C0 Wheatley's "Come on, stop muckin' around!
Plug me in!" answered by GLaDOS's "Don't do it."; C1b Cave's "...test as fast as possible." (a cut,
A24) and Caroline's "We're still doing science, sir."; the last core GLaDOS's "Don't. Do it."; C3
GLaDOS's "What are you doing?" / "Anything? Take your time." (the announcer's evacuation call after
E8, PotatOS on the branch); C5 Cave's ghost's "Just a heads-up that the Cloud's still waiting." (a
cut). In callouts.md, the rule sheets, timeouts.yaml and the Time-outs tab (a field to keep, drop
or swap each fork's nag). To listen. — Status: active.
D33's details: C1b's default and the DECISIVE bonus (2026-10-24, branch
story/wheatley-chapters-always-offered; walked through). C1b's time-out plays Cave's orders
(the announcer's yes goes to the boss), not counted on Cave's record. The DECISIVE bonus is 50,000
base times the period's multiplier, the same as the choice award (recommended was 25,000; the
owner picked equal). A timed-out choice scores no choice points (a timed-out C3/C5 still scores
its ending). C1b gets the DECISIVE bonus but still no choice award of its own. In the rule sheets,
docs/scoring.md, timeouts.yaml. — Status: active.
Review answers: pools, the launch sound, pool buttons (2026-10-24, branch
story/wheatley-chapters-always-offered). The owner's answers: period 2's drain (the three Cave bites
and Caroline's line), period 3's drain taunts, 1C's escape nags, C0's two Wheatley nags and period 1's
two small-ending takes are now pools; the portal gun's shot on the ball launch is dropped
(removed from base.yaml and its test) and three auto-launch candidates are listed to pick. Then the
owner asked that pooled sounds get the right options: a sound in a settled pool now shows Keep in
pool / Only this one / Change / Remove from pool (SOUND_POOL), and an audit of every callouts
cell that says "pool" fixed three rows the page misread (a semicolon in a note, the cores' bickering
exchange, the Fact Core's risky-shot line). — Status: active.
To-do files cleaned up (2026-10-24, owner's IDEAS.md request "Clean up all todo files. move
what is done, check if it is in the right file, group and update actions"). Every checked item
(25 from TODO.md incl. four found done but unchecked - the shot overview and scoring benchmark,
the stale "work through the list" step, the sound library answers, the Godot sound check; 3 from
HumanToDo.md) moved verbatim to TODO-done.md under "Moved 2026-10-24". TODO.md regrouped by topic
(story build, sound and music, screens and playtests, hardware, scoring and balance, dev tooling)
instead of the 2026-10-07 cloud session's sections; the phase 2 list split into its own items;
stale ones updated (the D31 bullet became plans/meter-and-memory.md phase 2; the drain mocks are
picked); new build items for D33's choice window, the music plan and today's sound picks. Owner
work moved to HumanToDo.md (flipper pulse tuning, the hand-over listening check, the cloud Graphviz
setup), which got a new item (pick the auto-launch sound) and updated ones (the review's status, the
old meter default, the grace nags, C3's and the CAROLINE letters' open points, the sfx allocation).
A script checked that no open item was dropped. — Status: active.
Review answers worked in; pools boxed and named; plain-words summaries on every tab
(2026-10-24, owner: "process new input first", "sounds I pooled do not need reconfirmation",
"rewrite all tabs for readability... keep the answers"). The answers: C0 and the last core
never time out at any level (the ball waits, the nags repeat; a default only should one ever be
needed), C1b's time-out is voiced by Cave himself (line to pick), C1's way out gets a clear pause
between GLaDOS's last nag and "kept"; C3's grace nag is a pool of three, with "It's not too late to
turn back." moved there from C3's opening (taken out of story_p3); new pools: period 2's ball
start, the file's defiant word; E3's Halls of Science 4 dropped (taken out of story_p4); the
C1b and last-core grace nags OK'd; C0's build is a TODO item to scope with the owner; STORY.md's
audio open points are pointed at their TODO entries. Pools: every settled pool's sounds sit
together in one purple-bordered box named in words ("Hurry-up nag at the choice ...", "The
Reunion's card / first plunge" - codes and bracketed notes left out, moment_name), and show as
Pooled and answered without a click (data-settled); the buttons only change one.
Readability: seven fresh-context agents wrote a plain-words summary (no codes, no file names,
context first, nothing dropped that is still open) for all 176 text blocks and rewrote every
tab's "In plain words" intro; each block now shows its summary first and folds the design text
under it. The summaries live in design/research/story-chain/plain.yaml, keyed by the same block
keys, so every answer stays; each holds a hash of the text it was written from, and the page and
the run flag a stale one (rejected alternative: rewriting STORY.md / the rule sheets / the
callouts themselves - they stay the detailed sources of truth, and their headings are the answer
keys). The agents' list of contradictions already in the docs went to HumanToDo.md (owner
calls) and TODO.md (mode headers behind the design). — Status: active.
The readability pass's contradictions, decided on the Decisions tab (2026-10-24, owner
clicked through the nine). Still Alive: a short slice at E1's surface, cut off by the radio, the
full song in the credits lap (a slice to cut, TODO). E3: Cave's verdict, then GLaDOS's Bring Your
Daughter line as the punchline - wired in story_p4 (cave_eighties_intro03 copied into
sounds/voice/ending). C5: with no side lit (a tied record) it never times out; its time-out is
voiced by Cave's ghost (line to pick). The first core: refused once on levels 1-2 (the docs follow
the build). The LIE pool loses the ball save's "Quit now and cake..." and Wheatley's whisper. The
Wheatley branch's chapters show greyed out, "COMING SOON", until built; its E1 music follows the
callouts (the opera at the surface, Want You Gone on the way out and the credits lap). The 7th
standup reads "not placed yet". Caroline's two "We're still doing science, sir." files stay a
listen (HumanToDo). The decisions and their alternatives moved to decided-options.md; the
affected plain-words summaries were refreshed. — Status: active.
Round 2 of the contradictions decided; the jackpot tour's sign shows its 8 shots (2026-10-24).
The owner's picks (the Decisions tab): E1's surface plays three of GLaDOS's lines and the radio,
"It's been fun. Don't come back." is saved for making LEAVE FOR GOOD (the build still plays it at
E1 until the radio route exists, TODO); the Wheatley branch's E1 always plays the turret opera and
has no Still Alive slice; E3's three music candidates stay, to listen under Cave's verdict; the
era dial follows the design (the build already does: every ride shot is lit and a jackpot); C5's
nag repeats on level 0 and on any level while no side is lit; the core refusal and the last core's
level sentence reworded; three time-out candidates each for C1b (Cave) and C5 (Cave's ghost) are
on the review page to pick. The tour's sign (the owner's IDEAS.md "Art" item, answered "yes"):
the JACKPOT word is replaced by the 8 tour shots' own Portal 2 sign icons - the same ones as their
toys' inserts - each turning blue as its jackpot lands (story_p3's p3j_* flags, started at 0
in story-player-vars.yaml and updating the sign), then the Super Button's square once the tour
is done; a 9-icon row shrinks its squares to fit (icon_place). Every sign word now has an icon
(design/sign-icons.md: none left as text). Seen in the period 3 playtest, which now stops at the
tour (playtest-runs/2026-10-24-jackpot-icons-period_3-tour-v3/). — Status: active.
Away session: the single-player periods built to the design's end (2026-10-24/25, branch
away/single-player-periods, owner: "build the remaining single players periods, to full
completion, push in between steps"; the decision log with every draft and alternative is
plans/away-single-player-periods.md). Built on the virtual platform, tested (stage 1 grew from
~245 to 313 tests) and playtested (period 1-5, small ending, full), not on the cabinet:
- The meter from choices only and Rattmann's arrows / GLaDOS's orders (modes/arrows,
two moments per chapter 1B-3A, lone arrows in period 2), his hints (following an arrow lights
the chapter's; his panel finds it - six, pinning period 5's dens).
- The choice window (modes/choice_window, D33) for every held choice: the last core as
the player's shot (never times out), C3 leaned by the meter (with follow the arrow at 3+
hints), C1b at each period 2 lock with Cave's record and Caroline's letter hint, C5
leaned by the record (a tie never times out), C1's way out. DECISIVE bonus; choice awards
only for a made shot.
- E1's radio route and LEAVE FOR GOOD, the arrow route, period 5's empty box, period 4
after it and E6's radio file; the credits lap after a final ending.
- Period 5 with six dens (the secret dens gone), your file counted and read out at game
over, GLaDOS's file quotes and H91, period 4's openings per way in, Cave's bonus, the
tape's sepia, the C5 overlay finally seen.
- Side features: period 2's Fine Print, Voucher Button, science fair, loop combos, Conversion
Gel window, Adhesion Gel stand-in, red phone (story_p2_side); period 3's Turret Air, Turret
Frenzy, Bridge Shield (story_p3_side); periods 1-3's Wheatley's visits, sabotage, the singing
turret, the laser relay, The Surprise, LIE / CAKE (story_layers).
- Drains and ball saves finally sound (modes/drains: the fizzle, the period's mock).
- Two fresh-context reviews; the first found five bugs (all fixed with regression tests).
Left for the owner (HumanToDo.md/TODO.md): the drafts (shots, counts, values), the lines to
pick and cut (C1b/C5 time-outs, A5/A24 Cave cuts), the hardware-bound parts (ball holds, the core
locks, KD1, Up2/Up4, the magnet, the lemon target), the climax's neurotoxin clock (what running out
does), Still Alive, lights and the cabinet test. — Status: active.
The away session's drafts confirmed; the neurotoxin clock by level; HumanToDo's decisions on the
review page (2026-10-25, the review page's Decisions tab).
- Confirmed, as built: Rattmann's arrows, the Cave-or-Caroline shots, the hints and period 5's
dens, your file read out, period 2-3's extras, the layers, the draft values (CHANGES #138).
- The goodbye lines stay as built: LEAVE FOR GOOD's plain "It's been fun. Don't come back."
(LF1), E5's laughing take (E5a, the two rhyme on purpose).
- The climax's neurotoxin clock (N4): per core at the level's 3A length (draft); running out
gives a line on the easiest level, the last burned core back on Standard and Advanced, and dead
flippers and a drain on Military Android. Rejected: one rule for every level (N1-N3).
- C1b's grace: Cave nags at 10 s, Caroline answers halfway to the time-out (20 / 15 / 12.5 s;
level 0: 10 s after each nag) - her line is a reply to him.
- Cave's cuts (CC1): all five made (A5: period 2's three drain mocks, now its pool with
Caroline's line; A24: the C1b and C5 nags) and in the game, each with its own row on the review
page to OK; the full files stay in the sound library (owner, 2026-10-25).
- The rest of HumanToDo.md's decisions are now cards on the Decisions tab (checked off there,
not deleted). The cards and every unpicked option are kept in decided-options.md. — Status: active.
The to-do files cleaned up; every review item on the review page (2026-10-25, owner: "cleanup
all todo files, (re)moving all things done, moving all review items to the review tool").
- Done items moved verbatim to TODO-done.md ("Moved 2026-10-25"), each with a note on where it
went: 27 from TODO.md (fully done, or replaced by a short line with only what's left), 29 from
HumanToDo.md.
- A To review tab on the review page (design/research/story-chain/to-review.yaml): what's left
to listen to, read or decide that isn't a decision card, each entry with buttons to the tabs where
it's answered and Done / a note. Decisions with options stay cards on the Decisions tab.
- HumanToDo.md now holds only the cabinet sessions, physical work and things outside the repo;
CLAUDE.md and the README say review items go to the review page, never into HumanToDo.md.
- A Reload button next to Download answers: it saves a note still being typed, waits for every
save, then reloads on the same tab and scroll position (checked in Edge on a scratch server).
— Status: active.
The owner's hardware notes, round 2026-10-25 (four notes described one by one with a
reference image and two drawings, then decided in chat; the plan is done and deleted, its record is HW-26..30 in hardware-ideas.md).
- Portal loading: three ball feeders - the existing launch-lane diverter keeps the dropper, a
2nd launch-lane diverter and a diverter at the top of the orbit each load a portal (which one
is open).
- New idea parts: a moving-panel ramp (Portal 2's panels, sunken pivot, lifted by a new
BSv1-type post), a high post at the Excursion Funnel's exit choosing a drop hole or the guide
rail (extra to the left ramp's diverter), and a hidden side-panel button with an inductive
proximity sensor (picked over a Hall sensor, which needs a bias magnet).
- Corrected: the Excursion Funnel is two PVC tubes wound with LED wire, not one transparent
tube (registry, STORY.md, shots.md, ramps.md).
- All five are ideas in components.yaml with draft pins in hardware-draft.yaml (4 coils, 1
switch: ~11 free coil outputs down to ~7); HW-26..30 in hardware-ideas.md; the images in
design/research/hardware-notes-2026-10-25/; what's open is one To review entry.
— Status: active.
The playfield's technical drawing as the hole-position reference (2026-10-25, owner):
design/drawings/playfield-drawing-1.pdf (drawing 001, A0 1:1, multiplex, 1100 x 540 mm) holds
the basics - lanes, flippers, slings, posts, main holes - with the toys to follow in a later
version (a new file; older versions stay). Pointed to from CLAUDE.md and the README, next to
board Overviews.xlsx. — Status: active.
Review answers, 2026-10-25 afternoon: eleven decisions, the To review tab cleared, two new page
buttons (owner: "review answers and update the tool accordingly, remove done items from the to
review tab"). Also logs the morning's review commits that had no entry (C1b's time-out pool, the
oracle turret's and the bonus / ball-search pools, the auto launch's whoosh).
- C5's time-out: Cave's ghost, one of three ("Continue testing." / "Chariots, chariots, okay." /
"That's an alternate Cave. Okay?"), as on the Period 4 sound list (asked: the card's later "T5a
with sigh" lost to the list); wired in story_p4, tested.
- Period 3's sign climbs 15/19 -> 19/19 per stage, and the climax's neurotoxin clock counts
down on the sign (the timer's own player variable, shown while p3c_toxin_on; 3A's clocks as
they were). The other build defaults kept (BD2).
- Caroline's last line on the tape's screen: "Sir, I don't want this." (P4a). C3: the ball
is never held (C3b). Period 5: counts and start kept; Claude drafted the six den scrawls and
his wall line, to read on the To review tab (P5a). CAROLINE letters: order and sound kept; the
letters found show faintly at the top right of periods 1-4's signs (CL1).
- Caroline's "We're still doing science, sir." stays her C1b reply only: out of period 2's
test-done pool (Cave's line plays alone) and the ball-search / lost-you callouts (CS-b).
- Recorded: E3 no music (M4; Halls of Science stays in the folder for the per-period music),
PotatOS's three lines (PO1, not wired), the playfield decisions at the cabinet (HW1, HumanToDo.md,
with the hardware notes' placements), the PR habit without branch protection (GH1).
- To review: the 13 answered entries removed (answers marked processed); the sign words were
already icons (HOME and UP no longer on a sign); plans/sound-review.md dropped. New: the scrawls.
- Review page: every Sound library sound gets Unselected / Use somewhere / Drop (owner; values
keep the earlier answers), every attract page in rotation a Drop page button (owner). The picked
cards moved to decided-options.md; 33 plain summaries refreshed.
— Status: active.
The attract screen review worked in (2026-10-25, owner: "process the attract screen reviews",
79 answers on the review page's Attract tab).
- 26 pages dropped from the rotation: the high-score files (out of the fixed cycle too), the
certificate wall, the three chamber-sign tours, and 21 quotes and posters (Rattmann's four scrawl
cards, five posters, the facts, the cake mix, some GLaDOS, Cave and Wheatley lines). 76 -> 50
pages; their slides removed. STORY.md's attract section, the choice tree and the attract playtest
follow.
- Pictures: 12 quotes got one (from the attract folder); the neurotoxin quote now shows Portal 2's
own NEUROTOXIN SOURCE sign, "the cake is a lie" Rattmann's wall from Portal 1's den, the symptoms
quote his Companion Cube collage (three textures added to extract_textures.py; the two decals put
onto a light wall by the new tools/art/attract_derived.py). The Bring-Your-Daughter banner's
strips are joined into two readable rows (same script).
- Layouts: a page aspect draws a picture at its game shape (Portal 2's square chapter textures
at 16:9 - "the picture looks skewed" on rules_p1, also rules_p2/p3 and Wheatley's quote); a quote's
wide picture goes across the top (the banner, the neurotoxin sign); "It's been a long time" is
now its words on the full picture; the portals and comply-or-defy rules cards lost the text under
the line; the trusted-friend poster reads "Multi Player Co-op".
- Renders kept in design/research/screen-review/2026-10-25-attract-review/ (first pass) and
-v2 (the decals on a light wall). — Status: active.
The attract review's second pass (2026-10-25, owner): 20 of #144's page changes confirmed;
"The next test is impossible" and Wheatley's "This is the part where I kill you" now show their
words on the full-screen picture, as "It's been a long time" does. The scene layout takes a
two-line title in a taller band for a longer quote, and aspect for its picture (Wheatley's square
chapter texture at 16:9). The impossible test's picture swapped for Portal 2's sharp menu background 3 (the
Portal 1 chapter thumbnail, 152x86, was blurry full screen; owner: "swap"). Renders:
design/research/screen-review/2026-10-25-attract-review-v3/ and -v4 (the swap).
— Status: active.
A Screens tab on the review page (2026-10-25, owner: "add a tab to review all other screens
per period"): every screen but attract's from the newest screen render, a block per period, then
the small endings and the feature modes (182 screens), each with its words, its source and OK /
Change with a note (keys screens:<id>). latest_render() now picks the render for both the
Attract and the Screens tab. Rendered after a fresh full playtest so the signs show today's
changes: design/research/screen-review/2026-10-25-screens-tab/. The Sound library's explainer
now names its new buttons. — Status: active.
Attract's sounds and callouts reviewed (2026-10-25, owner): the greeting, idle, party and
turret pools and the vault, jukebox and cube-speak pages confirmed as built; the high-score files'
lines confirmed although their page was dropped in #144 (they wait for a new place, callouts.md);
attract's music the owner picks separately, later (callouts.md, TODO A3). — Status: active.
Review answers, 2026-10-25 evening (174: the GLaDOS, Wheatley and co-op tabs, attract's sounds
and music). Nearly all confirmed as written. Changed:
- Period 1's small ending plays one of its two lines at random, never both (the owner's
2026-10-24 note "they are too similar"): each cut after its first "You are a horrible person."
(A27, word timestamps, checked with speech-to-text), a pool in small_ending, tested; the
uncut file left the game folder. The audio edits table gains A25-A27.
- Attract: the high-score files' lines dropped (their page went in #144); the playlist trimmed
to seven tracks and static bursts 02-03 - five songs left the game's music folder, static 01
stays (six modes use it).
- Pools on paper (not built yet): Wheatley's 3B boss fight and button lines one pool of five;
co-op's small endings a pool of two; compete's between-turn lines a pool of three.
— Status: active.
An IN USE badge in the sound library (2026-10-25, owner): every song or effect a built mode
plays shows a green IN USE badge by its name (30 today); the "Built" use label and the block counts
say "in use" too. — Status: active.
The day's last answers and a review-page audit (2026-10-25, owner: "make sure the review tool
has all the right formats & buttons everywhere").
- Audit (every tab, block, button set and media file on the page): all 21 tabs have their
explainer, all 284 blocks a review, each kind of item its own consistent buttons, no missing
sound or picture (3,909 refs). Keys that repeat across tabs are on purpose (one answer shows
on every tab with that sound list).
- Fixed: struck-through files in the design docs no longer get a sound row (the dropped
high-score lines kept coming back to review); the attract and decisions explainers describe
today's pages and buttons. Two answers cut loose by a row's text edit carried to the new key.
- Lesson: a sound's answer key includes its row's moment text, so notes go in the text cell,
never the moment column.
— Status: active.
Away session: three config reviews (2026-10-25, owner's IDEAS.md items; branch
away/config-reviews, reports in docs/reviews/). Review only - no config changed.
- 1, non-native OPP / workarounds: chain 2's PSOC boards run OPP firmware 1.0.0.4, so MPF
takes its legacy paths there (the "wing" pairing rule and software lamp fading are firmware
limits, not hardware); the trough's ATmega328P bridge is the one custom hardware piece; draft
hold coils sit at full hold power (risky once wired); servos on the CobraPin are native MPF.
- 2, YAML: structurally healthy (no duplicate keys, consistent values); uneven documentation,
commented-out templates, two debug flags, a d- prefix clash.
- 3, documentation standard: research (Home Assistant's YAML guide, helm-docs, yamllint;
archived in docs/references/), nine previews of hardware-devices.yaml (S1-S9, same settings,
different comments), a recommendation (S9), a draft guideline and questions - a card on the
Decisions tab for the owner's pick. — Status: active (the standard waits on the pick).
The to-do files cleaned up, second pass (2026-10-25 away session, owner: "clean up all todo
files"): B2, B5 and two A8 items (period 2/3's sign renders done in today's render; the attract
sign pages dropped in #144) moved verbatim to TODO-done.md; B3 cut to what's left (the COMPLY OR
DEFY page text); the small endings' and screens-review items trimmed to their open parts; B7 notes
that mb-dropbank is retired rather than renamed. B1's scope card, meant to be on the Decisions
tab, had gone missing - added (W1-W3). HumanToDo.md had nothing done. — Status: active.
Naming conventions for the game config (2026-10-25 away session, owner: "clean up game config
with standard naming conventions"). docs/naming-conventions.md written from the names in use:
hardware <type>-<part>-<side><n> with dashes (s-, c-, led-, bd-, ac-, d-, db-, dv-, fl-), game logic
with underscores (MPF builds events from those names). Renamed only what broke its own pattern:
s-left/right-flipper and s-left/right-sling to s-flipper-left etc. (matching their coils),
d-ramp-diverter to dv-ramp-diverter (d- is drop targets), the flippers to fl-left/-right,
mb-dropbank to mb_dropbank; the unused tag ledtag3 removed. Kept on purpose: every name that's
a physical label (s-toplane1, s-orbit-l, s-insinerator...), MPF's sw_service_*, the dashed
number families. 186 references in 40 files; audits.yaml (MPF's) and the history files untouched.
The wire-component skill now names new parts by the doc. — Status: active.
Documentation cleanup (2026-10-25 away session, owner: "cleanup documentations"). Every link
and file path in the current docs checked against the repo (50 hits): the wiki-style /docs/...
links, .claude/workflow.md's example names, the planned files and the git-ignored probe config
are intended; the retired plans are named as retired (with their CHANGES entry or commit). Fixed:
testing-strategy.md (the config path, the ball save that does exist, CI that does run) and the
README's layout (images and sounds as they are now, the docs folder, the design folder's review page
and drawing, the review page's generator and the newer tools). — Status: active.
Design folder cleanup (2026-10-25 away session, owner: "cleanup design folder, delete stale
outdated files and parts"). Inventory of every file in design/ (age, size, references). The
brainstorm folder is the deliberate record behind STORY.md (its README) and the screen-review
renders are artifacts, so both stay. Done: two finished plans folded and deleted -
plans/away-single-player-periods.md (recorded in #138-#139) and plans/story-review-chain.md
(paused since 2026-10-18; its lasting knowledge is the new design/research/story-chain/README.md:
running the page, each tab's sources, processing answers), references repointed; three
zero-byte stub files in the brainstorm's _partial/lore-pages/ deleted. Bigger candidates are left
for the owner (the old brainstorm scoring tool and its ~1.9 MB of data, the raw scraped lore pages,
the 2026-10-07 story review): docs/reviews/2026-10-25-4-cleanup-session.md. — Status: active.
The second final critic panel on the story (2026-10-25 away session, owner: "last full critics
story review, adding a dystopian scifi writer"): five invented critics incl. the new dystopian
writer Orla Brennick, design/research/story-brainstorm/critics-final-story-2.md; six checked
facts to fix, shared champions and worries, hybrids H120-H143 (extra options, none changes a
decision), a gap table (91 items, 24 new). Short version in design/story-final-review.md, on the
review page's To review tab for the owner. — Status: active (waiting on the owner's read).
The config documentation standard, rolled out (owner's picks 2026-10-26 on the review page's
card, then "continue in away mode"). S9 with the owner's changes: a 1-3 line header (story modes
3-6, pointing to the rule sheets), # --- Name --- banners per group, one sentence above a setting
only when it needs a reason, no history or dates in comments (that's this file's job), never a
comment at a line end, only the markers TEMPORARY:, WARNING: and TODO:, true/false, no
commented-out config, no line-length limit, MPF config only. All 61 files rewritten with every
setting verified unchanged (the only code-line changes: debug: yes to true, the draft posts
regrouped, 75 commented-out coil templates and one dead show comment removed). The story modes'
long "built / not built" headers were stale, so accurate build notes went into
design/rule-sheets.md under each period instead. Guideline docs/config-documentation.md; the
stage 1 check tests/test_config_comments.py (header, space after #, no dates, markers, no
line-end comments, booleans; show files may use off as a colour); CLAUDE.md points at it for
every new edit. The draft and the S1-S9 previews stay in docs/reviews/. — Status: active.
plans/sign-layout.md retired (2026-10-31, branch docs/retire-sign-layout-plan, owner: "do 1"
after a check of the plans folder). Built and tested in #93, with the owner's compare done (style 1
the default). Nothing left to fold in: the looks, the unlock rule and the wrap are in
design/SCREENS.md, the setting in docs/scoring.md, --setting in docs/testing-strategy.md.
The plan's text stays in git history. — Status: active.
The open plans checked against the newer decisions (2026-10-31, branch docs/plans-accuracy,
owner: "test the remain plans for accuracy against recent decisions", then "fix"). Each plan's
status and lists caught up with #111-#157: meter-and-memory.md phase 2 split into what #138
built and what's left (TODO A7: COMPLY OR DEFY, H117, H111, the file's second page, R3 to check);
sign-icons.md tested since #138, its crop step as built (#92), decision 4 names H117 as the
cube's first later place; story-final-review.md phase 2 noted as built in #138 except attract's
music and H92 and the cabinet runs, with four answers marked superseded in place (the meter's word
at the last core, D31; E1 goes on, #119; C1's way out in the choice window, D33; attract's music
open again, #147); art-and-screens.md's "nothing picked yet" and period 1-only sign corrected.
Also: design/SCREENS.md no longer calls JACKPOT a word without an icon (#137) and points period
4's EPILOGUE frame at its removal; HumanToDo.md gets the signs-and-light-trail cabinet check
the sign plan's step 7 pointed to. — Status: active.
plans/story-to-build.md retired (2026-10-31, branch docs/plans-accuracy, owner: "fix" after the
plans check). Steps 0-4 were done; step 5's period-by-period status was out of date (#138 built far
more than its "not yet" lists), and its open work already lives elsewhere: lights, scoring and the
skill shot as TODO.md A1, A2 and A5, the build notes per period in design/rule-sheets.md, the
part positions in HumanToDo.md's playfield session (HW1). TODO.md's pointers now go to those
docs; older mentions in the design docs and the registry notes name the plan as retired. The art
list's plan (plans/art-and-screens.md, split out of its step 3) stays. — Status: active.
The meter's memory and the cube, built; Phase B's picks on the review page (2026-10-31, branch
build/memory-and-cube, owner: "start Phase A now", "put Phase B's cards on the review page";
plans/finish-open-plans.md). Built on the virtual platform:
- H117: at the last core the climax sign shows your cube beside BURN - the broken cube with
"SAME INCINERATOR" if C1 burned it, the kept cube with "FOR THE CUBE" (also after C1's time-out),
in all four sign looks. New player variable cube_burned (a sign can't show on cube_kept == 0).
- H111: after an E8 the next game's first ball plays "I've been really busy being dead. You know,
after you MURDERED ME." once (machine variable e8_last, kept across power-off; your_file).
Compete's "the winner's game" waits for compete.
- Your file's second page (H113 + R5): small_ending's file_page_2, 6 s after the read-out and
after period 5's silent file: the cube, the singing turret, her letters as C A R _ _ _ _ _, his
hints, the sabotaged turrets, the path (without Wheatley until the branch), the meter's word,
Cave's orders and Caroline's advice. The moon rock's row waits for the moon rock.
- R3 was already built in #138 (story_p3_side). Period 4's sign lost its EPILOGUE label.
- The review page: two Decisions cards - the COMPLY OR DEFY page's text (CD1-CD3) and attract's
radio mix (AM1 as D29, AM2 every third track, AM3 dropped) - and a To review entry with H92's
commentary candidates per toy (none covers the bridge or the lasers).
Seven player variables now start at 0 for page 2 (config/story-player-vars.yaml). Tests: H111,
the cube flag, page 2 on both paths. A fresh-context review found no correctness gaps (it caught
the stale design/sign-icons-per-toy.md, regenerated with the catalogue). Stage 1 and the period_3,
period_4, small_ending, attract and full playtests pass; the small_ending scenario now waits for page 2
and captures it, period_3 burns the cube before the last core to capture its fate. Screens:
design/research/screen-review/2026-10-31-memory-and-cube/. — Status: active.
The draft sound effects as one row per sound (2026-10-31, branch review/draft-sfx-rows, owner:
the To review entry "is not very clear, what is to be reviewed here?"). The entry now explains what
the drafts are (the feature modes' first placeholder .wavs, still playing) and lists the ten as rows:
where each plays (checked against the mode configs), a candidate to swap in - the owner's two library
picks (slings sfx_chicken_tube, Faith Plate sfx_longfall_land_01) or Claude's suggestion, none for
the ramps - and Keep / Swap / Drop per row (chain_review.py's swap_rows, keys
todo:sfx-allocation:<draft>). To review entries can carry such rows from now on. — Status: active.
The draft sound effects, decided: six swapped, four kept (2026-10-31, branch review/draft-sfx-rows,
owner's answers on the To review tab, #162's rows). Swapped in their modes for library sounds: the skill
shot and the sling combo sfx_Portal2_sfx_button_positive, the Incinerator shot sfx_fire_flareup_01,
every lane sfx_turret_scanner_01, the drop bank multiball's start sfx_door_round_blue_lock_01, a
dropper shot sfx_dropper_iris_open_01, an exit shot sfx_door_open_chime_01 (already in the game
folder for Rattmann). Five copied in from assets/sounds/sfx/ and imported by Godot. Kept: the slings',
the Faith Plate's, the orbits' and the ramps' drafts - so the owner's 2026-10-24 library picks for the
slings (sfx_chicken_tube) and a Faith Plate launch (sfx_longfall_land_01) aren't used there
(callouts.md, TODO A4). The six replaced drafts no longer play; deleting them waits on the owner (TODO
A4). The entry left the To review tab; its answers are marked processed. — Status: active.
Cave's audio cuts made; the sound lists name their codes (2026-10-31, branch review/draft-sfx-rows,
owner: "do the Cave Johnson audio editing work", and on the Cave tab "make the cut" and "do not only use
codes, add some context"). Fourteen cuts from word timestamps (faster-whisper), each cut point moved to
the quietest spot nearby and checked by transcribing the result (the gel line's first end fell inside
"test-ing"): A12 "Cave Johnson, we're done here." (not wired: no dismissal moment yet); A28 the
script's named ✂ lines - the tilt warning, extra ball lit, 2B's gel test done, the risky shot (waiting
for their moments), and period 4's boot (28 s down to 6.7 s), the time machine's door and both glitches,
wired in story_p4 in place of the whole files (which left the game folder); A29 the Fine Print -
four of Cave's promises on the CAKE clauses and his "don't go spending it all" as LIE's catch, wired in
story_p2_side. All "to listen" on the Cave tab's audio edits list. The review page: a sound row named
only by a code ("A12 - Source") or a bare file now shows its row's description under it, and the codes
D29-D33, H108, TS-13 and "rule 3" get a short name in the sound lists (not page-wide, which would stale
every plain summary); answer keys unchanged (checked: no answer came loose). Old answers on the replaced
whole files are marked processed. — Status: active.
The Cave tab's notes worked in; the momentum line cut (2026-10-31, branch review/draft-sfx-rows,
owner's notes on the Cave tab). "Are these still open?" (the rule sheets' open questions): no, all eight
were answered 2026-10-07; one stale pointer to the retired plan fixed. "Is this a separate toy?" (the
toy table on the Cave tab): its summary now says the table is a grid of every toy against every
chapter, and that the lemon target is a real part (Pull1's pull-down coil, not installed) used only
in 2C. "Do the edits" (the story's open audio edits): the Cave cuts (#164) plus A11, the momentum line
in two bites ("Speedy thing goes in, speedy thing comes out." live, the definition held), made and
checked by transcription, not wired yet (TODO D5); STORY.md's open points and three plain summaries
updated. — Status: active.
Two new review tabs: Cave's bites and the second critic panel; rows for the scrawls and the
commentary (2026-10-31, branch review/draft-sfx-rows, owner: "also build the proposed tabs").
- Cave's bites: the 819 candidates (design/sounds/cave-bites.csv) as a fold per source line, a row
per bite that plays just that slice of the line (#t=start,end), its words and length, Pick / Skip
(keys bites:<line>:<start>-<end>). Its explainer lists the moments still waiting for a Cave bite.
serve.py now answers byte-range requests (206), so the browser can start a slice mid-file.
- Critics (2nd panel): the panel's 11 checked facts (Fix it / Leave it as is / a note) and its 24
new options H120-H143 (Build it / Later / No), read from critics-final-story-2.md.
- To review rows: an entry can carry items (a draft line each, OK / my own words) or picks
(candidate sounds per thing, one button each or none): Rattmann's six den scrawls and his wall line,
and H92's commentary clips per toy. The bites and critics entries point to their tabs.
— Status: active.
A proposed Cave bite per moment; the first cuts heard (2026-10-31, branch review/draft-sfx-rows,
owner's answers: on the Cave's bites tab "Propose a pick for each of the mentioned moments", and OKs on
the Fine Print's five bites and period 4's boot and door cuts). design/sounds/cave-bite-proposals.yaml
holds 18 proposals, each a row of cave-bites.csv with why it fits, shown first on the tab with OK /
another: period 4's skill shot ("Oh, that's much better.") and extra ball lit ("I'll be back."), a pool
of three for 2B's orbits, Cave's side at each C1b lock (one per decade), period 4's six dial clicks
(1950-2000), the ride's three tapes (fifties, seventies - "we're gonna [tape cuts out]" - eighties), and
the obedient record's line ("Test subject, can you hear me? Congratulations!"). The explainer's "skill
shot in period 2" was wrong (period 2 has its line; period 4's skill shot and extra ball lit need a
bite) - fixed. The seven OK'd cuts are "heard" in edits.csv and callouts.md. — Status: active.
The critics tab says what each fix is (2026-10-31, branch review/draft-sfx-rows, owner: "the critics
panel tab is not clear everywhere, what type of fixes are they and more context is needed sometimes").
design/research/story-chain/critics2-context.yaml gives every checked fact its type (story text, a voice
line, screen text, a game rule, a tool, or just information), how it stands today (checked against the
docs and the build: all eight fixable facts are still true; 9-11 are information), what Fix it and Leave it
as is would each do; and every option H120-H143 its type, what you'd notice in a game, and what it builds
on in plain words. The tab shows that first and folds the panel's own text (files, timings, codes) under
it; answer keys unchanged. — Status: active.
Cave's bites tab: the proposals, then his lines in full (2026-10-31, branch review/draft-sfx-rows,
owner: "rework the cave bytes into the selection part and the list of full text ... most of the other
snippets are not useful ... so I do not have to click 800 lines"). The 819 machine-found snippet rows are
gone from the page (they stay in design/sounds/cave-bites.csv); none had an answer. The tab is now the
18 proposed picks (#167), then each of Cave's 156 lines once, full text and play button, in five folds
(the fifties, seventies and eighties tapes, the DLC Caves, other lines), with one button, "I want a bite
from this", and a note for which words and what for (keys bites:line:<file>). Claude cuts marked lines
from word timings. — Status: active.
D34: the second critic panel's options, picked (2026-10-31, branch review/draft-sfx-rows, the
owner's answers on the review page's Critics (2nd panel) tab). Build (20): H120 the last subject, H121
the facility without her, H122 Caroline's voice taken (form b, "including cut voice"), H123 the
unmonitored chamber, H124 the official testing record, H125 chamber 17's long nags, H126 the roster, H127
instructions ignored, H128 the intake form, H130 the cabinet as Aperture, H131 signal ignored, H132 C3
named and one shot to leave, H134 the Curiosity Core speaks, H136 Wheatley Laboratories, H137 the ARG's
paper trail, H138 the grill names what it took, H139 three on the sign, H140 say what a choice does, H141
mercy leaves a mark, H143 endings on record. Not picked: H129, H135, H142. Asked: H133 ("I do not
understand this one") - re-explained in plain words on the tab, still open. Facts: 9 to fix (the Blue
Portal's kicker as a registry part), 1 and 11 left as is (fact 1's wording is still corrected through
H120), 2-8 and 10 unanswered. Decision recorded only - the design docs and the build follow
plans/critics2-picks.md (TODO A10). — Status: active.
D34 completed: H133 picked, fact 10 to fix (2026-10-31, branch review/draft-sfx-rows, the owner's
answers after #170's re-explanation). H133, the held-time budget, is in: the playthrough model will sum the
held seconds per level so the new held lines can be checked against a 25-30 minute game. Fact 10's unused
lines go where the picked options place them (H123-H128, H134); the rail and the fizzle stay unused (H129,
H135 not picked). plans/critics2-picks.md updated; facts 2-8 remain open on the tab. — Status: active.
CI fixes for #162-#171 (2026-10-31, branch review/draft-sfx-rows, the PR's test check). The
developer commentary stays on the PC (.gitignore), so the review page's commentary picks crashed the
build on CI; the sound index now adds the commentary clips from the committed catalogue at their fixed
folder, so the page builds the same with or without the files (checked by building with them hidden).
design/shot-map.md was stale after #163's sound swaps - regenerated. — Status: active.
The lighting plan, drafted (2026-10-31, away session, branch away/lighting-plan; owner: "create a
lighting plan ... research pinball lighting and draft a few options ... create a review tab ... the plan
for the light shows, including the attract mode and all game modes"). design/LIGHTING.md: a light
language (off / solid / pulse / flash, faster as time runs out; blue waiting, orange done, white
official, red arrow), a palette from Portal's own colours, the lights (86 elements: the 40 existing, an
icon insert per shot, the story elements, a light per toy, LED-filament portal rings, the funnel's blue
and orange wires, an exit cross and indicator dot trails, GI, the cabinet), the hardware (two CobraPins'
LED chains, the lamp wing's 16 outputs at 0.5 A, power and inserts), each period's look, and 80 light
shows over attract, the standard moments, every period, the endings, the branch, co-op and the
machine's own modes. The forks are 11 decision cards; the review page's new Lighting tab shows the
cards, every light and every show with OK / change / drop (design/lighting/options.md,
elements.yaml, shows.yaml). Research by a background agent:
design/research/lighting/research-findings.md, its source excerpts archived in
docs/references/raw/lighting-*. Proposal only - nothing wired or built; the decision log is
plans/away-lighting-plan.md. — Status: active, its picks in #174.
The lighting plan's picks (owner, 2026-11-01, the review page's Lighting tab). The owner picked
L3 (Portal's indicator dot trails instead of arrows: each shot's existing light becomes its trail's
head dot, +3 dots for 8 shots), K2 with the portals a fixed bright blue and orange in every period,
G1 addressable GI, P1 filament portal rings, M2 the comply/defy meter shown all game as a
very small VU-meter-style LED bar, X1 one exit cross, D1 den markers in 4-5 mm LED holes, C1
the buttons only, H1 split by function, I1 clear-frosted RGB inserts everywhere (not the
recommended I2), S3 calm in play, big in the moments. M2 changes D29's "meter hidden the rest of
the game" for the playfield: the bar shows it live, the screen still shows only its word at the last
core (STORY.md, rule-sheets.md). Applied to design/LIGHTING.md, elements.yaml (the unpicked
options' rows - the arrow ladders, exit portal rings, a second cross, speaker rings - are dropped;
83 lights, ~190 addressable LEDs and 10 lamp outputs) and shows.yaml (the trails replace the arrows; a
new show for the meter moving, 81 shows). Not taken: L1, L2, K1, K3, G2, G3, P2-P4, M1, M3, X2, D2, C2,
C3, H2, I2, I3, S1, S2. — Status: active (the lights and shows still wait on OK / change / drop).
The review page: a simpler sound library and plain "When it plays" moments (owner, 2026-11-01: "way too
many buttons" in the sound library; the When-it-plays column "sometimes still only has codes and the text of
the sound"). Sound library: every file is kept for record unless picked; only a shortlist of 129 asks for
an answer (the famous bites, the Portal 2 albums, the sounds a doc plans that no mode plays yet - Use
somewhere / Keep for record / Not for this machine), the other ~3,300 are a folded reference list with one
optional "Use this" per row, left out of the open counts (2,056 required review points instead of 5,366). The
answer keys are unchanged; the ten bite drops whose notes said "keep for future" are Keep for record. When
it plays: all 242 distinct entries over 850 sound rows now show a plain gameplay moment from
design/research/story-chain/moments.yaml (where in the game, what just happened, a per-sound line where
a row's sounds play at different points; no quotes, codes or dates), the raw text on hover; answer keys are
unchanged. tests/test_chain_review.py fails on a row without a moment or an entry no row uses
(chain_review.py --moments-todo <file> lists them). Audio-edit rows (A5, A28...) no longer show as
unsettled alternatives. A24 was split into A24a/A24b (C1b's and C5's nags) the same day. — Status: active.
The review page reads pools right (owner, 2026-11-01: "fix" TODO D7). Every sound row that showed as
"alternatives, not settled" was a pool or a single: the turret bank's table (a column headed "Pool" now makes
every cell a pool), C5's time-out (a ";" inside its bracketed note ended the pool early), the 3B fight's "one
pool of five" (a label inside it ended it after three), the attract greetings and the lemons' turret echoes
(random pools in the built configs; callouts.md now says so) and a struck-through, dropped row. The period 4
era dial's sentence in callouts.md's "Built so far" is reworded so each list says when it plays (a bite on
most clicks, a tape on each jackpot - the old wording put two bites under "the tapes"); its six answers carried
over to the new rows. No sound row shows "alternatives" any more. — Status: active.
Only actual sound moments get a review line (owner, 2026-11-01). An audio edit's row in callouts.md now gives
the sound lists only its cuts (the Status column), not the full original files they're cut from, which never
play in the game; the originals stay named in the folded design text. 23 rows gone (2,043 review points); their
11 moment entries left moments.yaml; answers given on them stay saved. — Status: active.
The Cave Johnson tab's answers processed (owner, 2026-11-01). 119 answers: every block and sound OK'd as is,
including the cuts heard today - A12's "We're done here" (its moment, with the drain mocks or only on the last
drain, is still open), A24a/A24b's two hurry-up nags, all eight A28 live-play cuts, four of the five Fine Print
clauses - and period 2's drain mocks kept as a pool. One change: the Fine Print's clause E
(cave_seventies_test_b_complete01_bindle, owner: "need a bit longer cut") now runs through the catch, "...if
you let us take you apart." (4.55-9.46 s of the source, 4.9 s), copied into the game; to listen. — Status: active.
The sound library tab checked against what's in use (owner, 2026-11-01). A sound a built mode already plays
needs no pick, so it left "Pick these" for the reference list (with its IN USE badge): the attract playlist's seven
Portal 2 tracks, period 5's Robot Waiting Room #1, Technical Difficulties (attract and period 4) and the five
picked effects now in the game (the two portal-gun shots, the button blip, the last core's plea, the drain
taunt). The shortlist is 112 sounds that still need a decision (39 famous bites, 56 album tracks, 17 planned but
not playing). Of the 33 "Use somewhere" picks, 5 are in the game and the rest are placed in the voice script
but not wired (mostly the Wheatley branch, a stretch goal). The answer to "Is it not in use already?" on
glados_sp_laser_powered_lift_completion01 is now yes (its cut is in period 1's small-ending pool). — Status: active.
The background music carousel, planned (owner, 2026-11-01, plan mode: "up tempo for fast multiball modes
or time critical missions, soft for Rattmann's period ... a separate tab for me to review and listen"; branch
plan/music-carousel). Each period's pool (music-plan.md, 2026-10-24) becomes three carousels - soft, normal,
fast - switched by the game state; the fixed cues stay. Every soundtrack track was measured
(tools/sounds/measure_music.py -> design/music/measurements.csv: rough tempo, loudness, onset density,
brightness, an energy score) and its quiet lead-in and fade-out tail found (owner: "cut some of the silent or
low volume sections"; only two tracks have a low passage inside). The review page's new Music tab: nine
cards (design/music/options.md), 34 tracks over six carousels with Soft / Normal / Fast / Drop, ten fixed
cues and 24 game moments with their level (design/music/carousel.yaml). The plan is
plans/music-carousel.md; nothing in the game changes until it's confirmed. Also: the sound library's
reference rows got Not for this machine next to Use this (owner: the short cuts "have no drop").
— Status: active (a plan, waiting on the owner's picks).
The To review tab processed and removed; no developer commentary in the explanation mode (owner,
2026-11-01: "process the to review tab and delete it completely afterwards, all is captured elsewhere"; "I will
not be using the explanation mode's developer commentary tracks ... per toy"). Processed: Rattmann's six den
scrawls and his wall's line OK'd, now final (story_p5_slides.py, rule sheets); the explanation mode (H92) keeps
HOW TO PLAY with each period's sign rules but plays no commentary clips (STORY.md, callouts.md, the attract
explainer, plans/finish-open-plans.md); the critics, lighting, music and bites items live on their own tabs, the
11 uncaptioned library files on the sound library's own review page. Removed: to-review.yaml, the tab and its
code. Review items now go on their topic's tab or as a card (CLAUDE.md updated). — Status: active.
The Decisions tab processed (owner, 2026-11-01). W2: the Wheatley plug-in choice and the branch's period
3 get built (planned next, plans/wheatley-branch.md). CD2: the COMPLY OR DEFY attract page reads "Comply, and
we will get along. Defy, and I will remember. The last shot is yours." (attract-pages.yaml, its slide
regenerated). AM1 built: attract's music now runs a Portal 2 track, a static burst, one loop of Portal 1's
radio mix, another burst, the next track (attract_pages.yaml; D29's alternating music). The three cards moved
to decided-options.md; the tab holds no open decisions. — Status: active.
The Screens and Decisions tabs processed (owner, 2026-11-01). Decisions: the Wheatley branch's five cards,
all picked as recommended - SH1 (plug in on the Super Button, refuse on the Turret Gauntlet exit), IT1 (four
scrap tests, then the three core modes for the locks), BF1 (three turret waves, a portal transfer stuns him),
PO-a (PotatOS's lines in period 2 for branch games), AR1 (no arrows in the branch's 3A); moved to
decided-options.md, the build is plans/wheatley-branch.md. Screens: 180 answers grouped into
plans/screens-round-2.md - text callouts as a brief widget at the top left (~130 screens; Caroline's at the
left middle; a mock-up first), text that doesn't fit, the dens' text on a bottom bar, an A/B cube-icon option,
new backgrounds for periods 2-5, the antagonist on each small ending, a picture of the toy on the feature
screens (3D renders to research), E3's "200-" text; 26 OK'd, two questions answered (the C1 sign vs its callout;
the hardware check's screen), 1A's "overlap" is the renderer drawing every conditional layer. — Status: active.
The callout-widget mock-up (owner, 2026-11-01: "do the callout-widget mock-up first, before the Wheatley build").
tools/display/callout_widget.py makes a story callout a small panel - top left, or left middle for Caroline's
letters - over the left half's art, with the accent stripe, an optional icon and a big and a small line, the chamber
sign and the score staying visible. tools/display/callout_mockup.py renders four samples (TEST COMPLETE, THE CUBE
with its icon, C1b's long line, a Caroline letter) over in-play captures, next to today's full-screen slides
(design/research/screen-review/2026-11-01-callout-widget-v2/; v1 used a capture with the old callout on it). The
Screens tab opens with the A / B pairs to pick. Nothing in the game changed. — Status: active (waiting on the pick).
The Music tab processed (owner, 2026-11-01; "fixed cues can also be used in the music pools"). Cards: E4 four
levels (a tension loop per period for the held choices), W1, T1, R1, B1 (each multiball its own track every time),
H2, P1, V1, Q1. A fixed cue may now also play in a carousel. Tracks: the owner's moves applied (I Made It All Up to
soft, Control Group to soft and Bring Your Daughter to normal in period 2, Overgrowth to period 5, the den loop out of
period 5); proposed, to listen: Hard Sunshine as period 1's third normal track, You Know Her? / Almost at Fifty
Percent / Reconstructing More Science for the three dropped period 3 tracks, the den loop and Robot Ghost Story in
period 4's soft, a tension loop per period from the in-game stems, Omg What Has He Done as the fixed cue when
Wheatley takes over. Cut (edit group M1, stereo, to listen): period 1's three fast tracks from where they turn heavy
(0:34.8, 0:18.6, 0:17.9) and 9999999 as a medium part (normal) and a fast part (fast). A drain plays soft.
edits.py trims can now keep stereo (music). — Status: active (round 2 of the tracks to listen).
Answers on every tab processed; the lighting plan's notes applied (owner, 2026-11-01: the IDEAS.md list).
Lighting: 165 answers - seven lights marked "idea only, needs a hardware idea" (the turret eyes, the rocket
eye, Wheatley's spot, the lemon and den lamps, the laser line; the hint row OK'd, to build into a toy); the exit's
cross and running man become icon inserts in the wall with a yellow/blue dot trail up to them; the GI confirmed
RGB by period; the back wall's characters - a backlit picture of GLaDOS, Cave, Caroline, Wheatley, PotatOS
and Rattmann, lit when they matter, dark when not, brighter as they speak (six elements, four shows; ~228
addressable LEDs now, a 15 A supply). Music: the nine picked cards fold into one group; every track gets
"Change"; Hard Sunshine ("too much buzz") replaced by The Friendly Faith Plate (to listen); the den loop to
period 4's normal. Library: the songs the carousel uses now show as planned (37 of the 64 album tracks);
the owner's music picks get proposed uses in plans/music-carousel.md; Party Escort cut (edit M2, its quiet
first 7.5 s, to listen); the infinite fall's sound ruled out. Cave's bites: every bite listed under its
line, one OK per group; two proposals recut by word timings (the skill shot now "Oh, that's much better.",
the eighties ride from "When life gives you lemons"). Caroline: a new Caroline's lines tab (her 20
speech-to-text lines to check, edit A10); her last line exists spoken (responses82), noted; the letters in any
order queued (TODO D8). Screens: the art candidates now show (the server didn't serve their folder).
HumanToDo: a playfield photo for MPF Monitor's LED places. — Status: active.
The screens, round 2 (owner, 2026-11-01: "execute the screens round 2 plan"; plans/screens-round-2.md).
Every story callout is now a panel at the top left over the art (Caroline's at the left middle, E3-E5 with
their face, the cube moments with the cube icon), at most 3 s, the score and the sign staying visible
(tools/display/callout_widget.py's callout_slide(); the generators switched from overlay()); the dens get
a bottom bar; C1b's line fits; the screen renderer draws one conditional layer and an in-play frame behind each
slide. Candidates on the Screens tab for new backgrounds (periods 2-5) and the antagonists' pictures; cards for
the toy pictures and E3's text. The full playtest caught two callouts close together stacking (the older one
showing through the 90% panel), so every panel is opaque and one height (224 px) - a newer one always covers.
— Status: active.
The CAROLINE letters in any order (owner, 2026-11-01: "not in a mandatory order"; TODO D8). Each of the 8
major shots gives its own letter once, in any order (car_c ... car_e, the count caroline kept for E5 and the
rest); Caroline's advice in period 2 lights the first letter still missing, and the hint ends only when that
letter lands (choosing her advice on the bridge can land the N at the same moment - the playtest-free unit test
caught it ending the hint at once). The signs show the letters found in their places, the callout one slide
with each letter or a dash (caroline_letters, replacing caroline_1-8). Rule sheets, explainer, tests
(test_caroline: any order, reverse order; test_story_p2: the hint) updated. — Status: active.
The art candidates checked against the story (owner, 2026-11-01: "check if the image matches the story
phase (state of GLaDOS & location) and indicate those properties per period"; and the picks' notes). Each
period's block now states where you are and GLaDOS's state there, and each candidate what it shows and how well
it matches. GLaDOS's dormant head (a1_1) moved from period 3 (she is awake there) to period 4's ruins; today's
art joined period 3. Round 3 (art-candidates/2026-11-01-screens-v3): two more of her working chambers, the
overgrown ruins, a den put together from the games' art (not a screenshot), his murals, GLaDOS damaged for the
endings, all of Cave's portraits. The Lab Rat comic isn't in the game files and its site loads by script - its
pages need saving by hand. The candidate buttons light up when picked (they didn't). — Status: active.
E3's text and the toy pictures picked (owner, 2026-11-01, the Decisions tab). EH2: E3's screen reads "THE
DAY IT ALL BEGAN" over "BRING YOUR DAUGHTER TO WORK DAY" instead of "200-" (story_p4_slides.py, the rule sheets,
STORY.md). TP2: the feature screens get 3D renders of the toys from the games' models - next, after installing
Blender and Crowbar (plans/screens-round-2.md). — Status: active.
Rattmann from the Lab Rat comic (owner, 2026-11-01: "comic pages are in design/comics" - design/Comics/ Portal2_Lab_Rat.pdf, Valve's 2011 comic). Its panels became round 4 of the candidates
(art-candidates/2026-11-01-screens-v4): for period 5's small ending, Rattmann's face, his gaunt close-up, his
first look, him carrying the cube between turrets, holding it, wounded beside it, and the last page - asleep in a
relaxation vault with the cube; for period 5's background, that vault, the hidden architecture behind the walls
and a wrecked room. The panels are cropped from the PDF (pymupdf), the contact sheets kept in
art-candidates/2026-11-01-comic/. — Status: active (waiting on the picks).
Answers on every tab processed, round 2; the toy renders planned (owner, 2026-11-01: the IDEAS.md list).
Caroline's lines: her 20 lines heard and checked (catalog.csv: text_source: listened, checked);
responses80 is "Sir, the test." (was "Serve the test"), responses12 spelled "Caroline"; the game scripts
regenerated. Her last line: the speaker bar after the upload tape now shows her spoken words, "NO, LISTEN TO
ME! SIR, I DO NOT WANT THIS!" (p4_caroline; was "SIR, I DON'T WANT THIS.", P4a, #143). Cave's bites: a
second round of ten proposals from the owner's group notes (2A's first gel hit, 2A's gel test done, the blue line
at 2A's ball start, the yellow line at 2C's exit, a period 2 drain, ball search, ball start in 2B, the bonus, 2B's
gel test done instead of A28's cut, eighties_intro04 before 2C's card line), cut points from word timings - all
OK'd the same session; the 28 picks fold under "Picked earlier", to cut and wire (TODO D10). Music: The
Friendly Faith Plate kept as period 1's normal track; sp_a2_bts3_b1 dropped ("soft tension"), sp_a2_bts5_b0 is
period 3's tension loop (to listen). Lighting: the back wall's characters, the GI, the exit inserts and the
idea-only lights confirmed; the eleven picked cards fold into one group, like the music's (option_cards()).
41 answers on rows whose text changed since were marked as handled under their new keys. Next build:
plans/toy-renders.md - the nine feature screens get the toy rendered in Blender from the games' models, in
two looks (game-textured and a technical drawing), as candidates on the Screens tab. — Status: active.
Afterwards (owner, 2026-11-01): the Sound library's shortlist drops the tracks the music carousel uses
(carousel_stems()): the albums 56 -> 21, the planned list 24 -> 17; they're in the reference list.
Then (owner, 2026-11-01: "use sfx_light_flicker when a lighting show can use it"): twelve light shows get a
sound from the light family nothing used yet (shows.yaml's sound, a Sound column on the Lighting tab with
its own answer, lighting:sound:<id>): flickers with attract's waking rows, the sabotaged turret, 2C's GI, period
4's glitch, the den lamp and Wheatley's cheap blue (with the neon hum under it); power-ons at the game start
and Cave's boot; power-offs at the fall, GLaDOS waking, E4 and the tilt. Fire, the radio's crackle and the tape
stay silent. They count as planned in the Sound library, off its shortlist.
Then the Sound library's 103 new answers (owner, 2026-11-01: "reprocess sound library, add options to the
other tabs"), each "use somewhere" now a proposal to listen on its own tab. Music tab: 40 tracks into the
carousels (Music of the Spheres and Your Precious Moon to Cave's, Caroline Deleted to period 4's soft, Wheatley's
two tracks to the branch, old Aperture's chapter 6-8 loops spread over 2A-2C and the ruins, Portal 1's tracks to
periods 1 and 3, the slow intros marked to cut), Portal 2's finale loops as the branch's tension sequence in the
game's order, a co-op carousel of the six hallway loops, the chapter 1 loop sp_a1_intro5_b1 as period 4's other tension loop, the core loop as a Her Cores alternative; the level loops
aren't measured yet. Effects: period 2's gel tubes and elevator, 3A's neurotoxin hiss and vent fan, period
5's den opening, E2's party escort, and a new section (All game tab) for every period: the side-panel sensor's
impacts, the portal gun's power-up at game start and its other shot pair, the dropper's servo and cube, a container
alarm, a no-music background pool. Sounds already in or planned were answered as such (the radio mix, Want You
Gone, the two short cuts). A moments.yaml entry may name its sound (stem), since file-only rows share their
text. Owner, the same day: a drop of a sound in use or planned waits for the owner (Cara Mia Addio: it stays planned, its answer reset).
The toy renders, steps 1-4 (owner, 2026-11-01: "next build plan to execute: the 3d renders";
plans/toy-renders.md). The games' own models now render in Blender: tools/art/render_toys.py runs Blender 5.2
headless per candidate in design/art/toy-renders.yaml and writes each toy in two looks, game-textured and a
technical drawing (Freestyle lines on a flat blueprint fill), as transparent PNGs; 15 candidates for the nine
feature screens are in art-candidates/2026-11-01-toy-renders/. The importer is SourceIO (REDxEYE's add-on,
5.5.4), picked over Crowbar + Blender Source Tools and over a reader of our own because it reads the .mdl and
its materials straight from the VPK archives (so the plan's model-pulling step fell away) and handles skins (the
Companion Cube is metal_box skin 1); the owner OK'd running it, kept outside Blender's profile. Many models are
stored on their side, so each candidate carries its turn; SourceIO's animation import fails on the turret, so
every toy is in its bind pose. The period tint and the multi-model scenes are not done yet. Each render is also
placed on its own slide (the line drawing painted over) and shown on the Screens tab as nine picks, one button
per model and look; the candidate images there now really shrink to fit (a later table.pages rule overrode
table.cand's). Then (owner, 2026-11-01: "add a rendering of the panels on the hinges", "add all arms
(multiple)", "is there also an open / activated Faith Plate?"): Portal 2's chamber panels on their hinge arms - one
swung out, one folded, a wall of six (grid:), a row and a field of the arms - on RAMP! and INCINERATOR!, and
the Faith Plate activated, its paddle bone turned by hand (pose:). — Status: active (waiting on the picks;
then step 5 wires them in).
GLaDOS rendered angry and damaged (owner, 2026-11-01, the Screens tab: "do we have another shot of GLaDOS
damaged?" for period 1's small endings, "a few extra screenshots of GLaDOS angry?" for period 3's background).
The games hold no more pictures of her up close (Portal 2's chapter pictures show her small), so she's rendered
from her models with #193's tool, which gained light moods: angry (a hard red key from below, an orange rim)
for Portal 2's GLaDOS and Portal 1's (period 3 is Portal 1's finale), wrecked (dim, cold, one ember) for her
opened-up heads from Portal 2 and Portal 1's body fallen on its side (design/art/glados-renders.yaml).
screen_candidates.py sets each over a darkened, softened backdrop - her armed chamber, or the ruins - as round
5 (art-candidates/2026-11-01-screens-v5): three new options per group, the rest as before. Her eye doesn't
glow in the renders (the models' bind poses, no eye light). — Status: active (waiting on the picks).
Characters and props rendered, to place (owner, 2026-11-01: "any other cool game entities to render? Any key
elements?" - yes to the five proposed). Wheatley, PotatOS (also in the angry light), Atlas and P-body, Portal 1's
radio and the cake, rendered from their models (design/art/character-renders.yaml) and shown on the Screens tab
as "Characters and props: where to use them" - pick a render and say where it goes, or not needed (keys
screens:render:<item>); render_toys.py --gallery puts each on a dark square. Picked over the other entities the
games hold (the rocket turret, the laser kit, Chell, the bird, the elevator...) because the story already uses
each of the five. Wheatley's eye doesn't glow and Atlas and P-body keep their arms-out default pose. Then (owner,
the same day: "any more we can add? The real incinerator?"): Portal 1's Emergency Intelligence Incinerator has no
model of its own - it's built into test chamber 17 (testchmb_a_13) and her chamber (escape_02) - but its iris
door is one (props_bts/glados_aperturedoor), now on INCINERATOR!; and to the gallery: her four cores (the one
model's skins: Morality, Curiosity, Anger, Intelligence), the rocket turret and the reflection cube. Left out: the
laser emitter (mostly a glass panel), the pellet launcher (in pieces in its default pose) and Chell (a T-pose).
— Status: active (waiting on the owner's places).
GLaDOS in white and blue, in her chamber; the elevators, Cave's portrait, the bird (owner, 2026-11-01: "a
GLaDOS version without the red light, more blue/white and without the background, perhaps also one of her in her
room?"; "do the next set"). A cool light mood (white key, pale blue fill, cyan rim) for both GLaDOS models on a
plain dark ground, and a view of her in her chamber rendered from Portal 1's last map (tools/art/render_map.py,
escape_02): three new options in period 3's background and the period 1 and 3 small endings (round 6,
art-candidates/2026-11-01-screens-v6). In the chamber view her upper rings render black - not a missing texture;
the cause wasn't found - so she's half silhouette. The next set: Cave Johnson's portrait as a 3D frame, Portal 1's
round and Portal 2's glass elevator and the bird join the characters to place; the observation window, the fizzler
(only its post - the field is an effect) and the number sign were too plain. The whole incinerator room was tried
from test chamber 17's map and dropped: it's a dark pit there, the fire glow an effect the import doesn't bring -
the door model stays the incinerator. — Status: active (waiting on the picks).
Screenshots from the games: key moments (owner, 2026-11-01: plan mode "what would be a good way to get
screenshots from key moments?", then "away mode: go ahead" - plans/game-screenshots.md). tools/art/game_shots.py
takes them in Portal and Portal 2 themselves: the game started once, then each console command handed over with
-hijack (one call per step, a pause after it), the camera placed with setpos/setang, the map's own events set
off (ent_fire relay_incinerator_open trigger, the gel sprayers' start), no HUD or gun. 28 shots on the Screens
tab ("Screenshots from the games: key moments"): the incinerator burning in test chamber 17 and in GLaDOS's
chamber, GLaDOS in her chamber (Portal 1's finale) and her ruined chamber (Portal 2), old Aperture (where you land,
the conversion and propulsion gel), Rattmann's dens (Portal 1's scrawled dens in chamber 17 and the escape, Portal
2's five murals and "Don't even try"), a turret and the Faith Plates in action. Found on the way: a console script
on the launch line runs entirely before any map can load and wait does nothing in these builds (so -hijack
instead); Portal 2 refuses to start from its own exe and ignores con_logfile on its command line (Steam's
-applaunch and -condebug); Rattmann's murals are decals, found through the maps' overlay data; guessing dens
from where his music plays didn't work. The big black panels over GLaDOS in her chamber are dark monitors in the
game itself, so #196's render wasn't wrong there. Not got: the cube in its dropper, the portal gun on its pedestal,
the laser beam, repulsion gel and Wheatley (the camera landed in walls or the moment is scripted) - their misses
kept in rejected/; online shots (plan step 6) not done yet. — Status: active (waiting on the picks).
Period 4's backgrounds: Cave's ruins, not GLaDOS's (owner, 2026-11-02: "period 4 background candidates I
think are wrong", after asking whether its ruins are Cave's 1950s-80s Aperture or the newer GLaDOS-era one).
The candidates were all Portal 2's overgrown modern ruins (her chamber, the relaxation vault, the observation
window); now they're old Aperture abandoned and decaying - the chapter 6 and 7 pictures (the 1971 silo under
Cave's sign), Portal 2's old-Aperture menu background, two of #197's game shots where you land, and three old
Aperture loading screens (also period 2's candidates; the two periods' picks should differ). Round 7,
art-candidates/2026-11-02-screens-v7; the earlier rounds keep the GLaDOS-era ones. Still to settle: STORY.md
calls the act "Back to Your Own Time", and its "Powerup initiated." and Technical Difficulties music are Portal
2's GLaDOS-era wake-up cues. — Status: active (waiting on the pick).
Where and when, for every part of the story (owner, 2026-11-02: "make the location and timing in each part
of the story explicit and let me check on the review tool, in the period tab, unchecked"). STORY.md has a new
"Where and when" table: per part (the acts, their chapters, the Wheatley branch, the endings) where it happens,
when, and the past it shows. One timing model throughout: the game happens today, in one continuous present -
the past appears only as Cave's tapes, his ghost's era dial, Caroline's upload tape and Rattmann's scrawls.
chain_review.py reads the table and puts each tab's rows at its top as their own review points (where:<tab>: <part>, OK / change), 30 in all, unchecked. A separate section rather than a line in each act, so the acts' plain
summaries stay current. To settle with it: period 4's title "Back to Your Own Time" and its GLaDOS-era sound cues
against Cave's ruins (#198), and E4/E5 in GLaDOS's chamber after E8 destroyed her. — Status: active (waiting
on the owner's checks).
The game shots moved into the screen candidates (owner, 2026-11-02, the Screens tab: GLaDOS's chamber shots
"move all as background option for period 3 and as options for small endings, period 1"; the dens "add all as
options to small ending, period 5 and to period 5's left-half background"). Round 8
(art-candidates/2026-11-02-screens-v8): #197's five GLaDOS's chamber shots join period 3's background and period
1's small ending, with the overgrown ruins of her chamber (loadingscreen_a1_2, the owner's note); its 13 den shots
join period 5's background and its small ending. Only the moves this round (owner: "do only the moves for now"):
the night's other Screens answers - the toy and character picks, more Cave and comic options, the dropper's
iris, Wheatley's eye, the lanes' cake and LIE, speed and repulsion gel - stay open. — Status: active (waiting
on the picks).
Screenshots from the wikis (owner, 2026-11-02: "execute step 6 the internet screenshot search";
plans/game-screenshots.md). tools/art/online_shots.py lists every image on the listed theportalwiki.com and
combineoverwiki.net pages through their MediaWiki API (the incinerator, GLaDOS, her chamber, Cave Johnson, Aperture's
history, Rattmann, the toys and gels, Wheatley) and downloads the screenshot-shaped ones - 282 - to a scratch folder;
48 picked by eye from numbered contact sheets, re-encoded into art-candidates/2026-11-02-online-shots/ with
sources.csv, and shown on the Screens tab as "Screenshots from the wikis" (keys screens:online:<moment>). The
finds fill the game run's gaps: Wheatley on his rail, glowing, and in space by the moon (E7); GLaDOS damaged in the
ruins, her cores close up, her chamber exploding (E8), the neurotoxin clock; Cave's portraits in old Aperture;
repulsion and propulsion gel pouring; Valve's own "testing element" diagrams of the Faith Plate, the gels and the
laser, and the portal gun as a blueprint; Atlas and P-body. One is from Aperture Desk Job (Cave's head), noted so.
The 12 Combine OverWiki source pages are archived (docs/references/); the Portal Wiki's rate-limited, its pages
are in TODO.md; four Valve Developer Community pages are skipped on purpose (behind a bot check). — Status:
active (waiting on the picks).
Period 3's background picked; a Wheatley branch background (owner, 2026-11-02, chat). Period 3's left half:
the Portal Wiki's shot of GLaDOS's head lying damaged in the ruins of her chamber (File:Sp_a1_wakeup0061.jpg, from
#201) added as an option and selected (answers.json, replacing the round 8 pick of the ruins wide). A new group,
the Wheatley branch's period 3 background (screens:background:wheatley), with two of #201's shots: Wheatley with his
blue eye glowing (Portal Wiki, File:BetaWheatleyChamber.jpg) and Wheatley on his management rail (Combine OverWiki,
File:Wheatley_close-up.jpg); shown beside period 3's sign until a playtest captures the branch's own. Round 9,
art-candidates/2026-11-02-screens-v9; screen_candidates.py now also finds the wikis' screenshots. —
Status: active.
"Keep for record" on the screenshots and renders (owner, 2026-11-02: 'add a "keep for record" option to screen
captures & renders'). The toy picks and the three galleries on the Screens tab (the characters and props, the game
shots, the wikis' shots) get a third answer beside the picks and "not needed": keep - not used now, kept as a
reference. The background and small-ending candidates stay one pick per screen. — Status: active.
GLaDOS recovering, for period 3's background (owner, 2026-11-02: "is there a picture of GLaDOS recovering?",
"add a few as options"). Five more of #201's wiki downloads - Portal 2's opening, where she wakes in the ruins - are
re-encoded into art-candidates/2026-11-02-online-shots/ (sources and gallery updated) and added to period 3's
left-half background: her powering up, sparks crackling (File:Sp_a1_wakeup0027.jpg), her body rising (from the
side, and with her head still hanging), her head up towards you, and her risen in the overgrown chamber talking -
3A's "Oh... it's you". Round 10, art-candidates/2026-11-02-screens-v10; the side view's square crop loses her
head. The pick (#202's damaged head) stands until changed. — Status: active.
The Faith Plate close up and activated (owner, 2026-11-02: "add a closeup of a faith plate to the screenshot
part", "and can we add one when activated (from game and from web)?"). In the game shots: two close-ups (the plate
on its pedestal over the flooded floor in sp_a2_catapult_intro, one set in the floor between its signs in
sp_a2_trust_fling) and two activated - flung straight up and angled - caught by slowing the game to a tenth
(host_timescale 0.1) while the plate's own animation plays (a shot's before); game_shots.py now resets
host_timescale 1 with each map's setup. From the web: Valve's Faith Plate diagram with the plates flipped up, and
a small (250x267) in-game shot of an activated plate - better kept for record; their Combine OverWiki page archived.
— Status: active (waiting on the picks).
The review page shows the newest round again (owner, 2026-11-02, asking for #204's five recovering shots as
period 3 options - they were in round 10 but not on the page). chain_review.py picked the newest candidates
folder by its name as text, so 2026-11-02-screens-v10 sorted before -v9 and the page kept showing round 9;
the five folder sorts now compare the numbers in a name as numbers (folder_order()). — Status: active.
Review round 2026-11-02: the answers on every tab, the picks built, the signs on the game's sign wall
(owner, 2026-11-02: "process new answers on all tabs, pay attention to the notes, remove decisions that are
completed, recreate all changed screens (and uncheck those), provide options where needed"). 112 open answers:
106 marked done (the Where-and-when rows, the threads, the lighting sounds, 13 sound proposals OK'd and 4 pooled,
"keep for record" and "not needed" on the renders and screenshots); sfx_amb_boomer_02 out of the den-opening
proposal; Cara Mia Addio's old drop, back from a browser copy, cleared again (it stays planned). Built: each
period's left-half background (tools/art/apply_screen_picks.py -> images/periods/; periods 2-5 add their
widget p<N>_background - the picture and the score - to the base slide at mode start,
tools/display/period_backgrounds.py; a conditional on the base slide was tried first and failed: mpf-gmc shows a
conditional while its variable is unset) and each small ending's picture, framed left of its text
(images/small_endings/); the CAKE lanes' callout shows the rendered cake. Atlas and P-body (co-op) and the Wheatley
branch's background are kept as picks until those exist. The signs (owner, the same day): every icon glyph
trimmed, sized and centred the same (replaces 2026-10-10's "no centring"; the hint dots and the left/right portal
rings stay as drawn); the big icons in two left-aligned rows of five slots filled row by row from the top (the full
look keeps its one bottom row under the story lines, period 4's era dial stays one row - exceptions); the panel is
the surface of Portal 2's chamber sign, newsignage_back02, without its steel frame (sign_panel.png; the worn
_dirty one built as an option). Options: for the toy notes - the cake (rendered and Portal 1's picture) and
Rattmann's cake-is-a-lie wall for the lanes, the dropper turned iris-down, speed gel and repulsion gel (Valve's
diagrams, screenshots) - toy candidates can now be pictures (image:); those four groups are back to unchecked.
Screens: the full playtest and a screen render (screen-review/2026-11-02-round-v2); the 33 screens whose own
content changed - 22 sign looks, the 10 small endings, the CAKE callout - unchecked. Found on the way: new images
weren't imported by Godot, so the playtest drew without them and still passed; the playtest runner and the screen
render now run Godot's import first. — Status: active (the owner's final review).
The Screens tab's answers, round 2: the right screen behind each callout, the feature screens as callouts
(owner, 2026-11-02: "some screens still use the wrong background on the left, and process all screen answers").
160 open answers: 94 screens back to unchecked because they changed, 64 done, 2 open (period 1's unfinished
note "Some icons still", and Wheatley's render, now a decision card). The left background: the screen render
drew every slide over period 1's sign capture; each period's slides now render over their own period's
(screen_review.py's BACKDROPS), the layers and Rattmann's arrows - which run through periods 1-3 - over
period 1's. The feature screens are callouts at the top left with an icon in front of the text, like the cube
moments (tools/display/feature_slides.py; the nine hand-made slides replaced, the same slide names): AERIAL!
shows the Faith Plate in action (our game screenshot, cropped), INCINERATOR! the incinerator (a Portal Wiki
screenshot, cropped; the old "INSINERATOR!" typo gone), LANES COMPLETE the cake; the other six have four icon
candidates each on the Screens tab (tools/art/callout_icon_candidates.py: the game's glyphs, renders, a
screenshot). DECISIVE is a callout too. The exit and portals callouts carry their glyph; the dens show the whole
mural above a slimmer bar. Four decision cards for the rest of the round's questions: Wheatley's glow, the big
button on the Super Button callouts, period 4's dial, the sign's worn surface. The sepia memory look (owner:
"where are the sepia screens") had never been on the review: it is the p4_memory widget over the whole screen
during the upload tape, not a slide. The screen render can now draw a widget over a slide (MEMORY), so the two
tape screens also show "as a memory". Render screen-review/2026-11-02-round-v5 (over the full playtest
20261102-123217-full, which passed with the new callouts). — Status: active (the owner's review).
The Screens tab's answers, round 3: the picked icons and the four screen decisions built (owner, 2026-11-02:
"process new answers on all tabs ... remove decisions that are completed"). 125 answers done, 9 screens unchecked
because they changed. Icons: the six feature callouts carry the picked game glyphs (dropper, speed gel, beam
through portals, light bridge, catapult, repulsion gel; feature_slides.py). SB1: the big red button render,
cut out (images/signs/sign_super_button.png), is the icon of VOUCHER READY, SUPER JACKPOT LIT and A CLEAR HEAD.
P2: periods 2 and 4's signs stand on the worn surface (sign(..., panel="panel_dirty")), the rest clean.
D1: period 4's dial stays one row. W2: Wheatley's screens will use the glowing screenshot once the branch
has them. The four cards moved to decided-options.md; no decision card is open. Still open: the drop answer on
Cara Mia Addio, a song still planned (asked, not processed). Render screen-review/2026-11-02-round-v6 (full
playtest 20261102-132903-full passed). — Status: active (the owner's review).
The proposed music measured (owner, 2026-11-02: "measure the pace and intensity of songs proposed but not
measured yet, so I can do final music review"). 28 carousel tracks and fixed cues had no numbers on the Music tab;
tools/sounds/measure_music.py --add measures only the files not in design/music/measurements.csv yet and
scores their energy on the existing rows' scale, so the numbers already reviewed don't move (a full re-run
would re-standardise them all). — Status: active.
The final music review processed, the carousel's silences cut (owner, 2026-11-02: "on selected music for
the carousel, please cut out silent parts, as they do not work on a pinball machine"; "process the final review
comments"). The owner's levels: five tracks dropped (Self Esteem Fund, Taste of Blood, Caroline Deleted,
sp_a1_intro5_b1, finale 1 - kept for record in a dropped: list per period of carousel.yaml), finale 4's two
parts moved from tension to fast, the rest kept. Silence: a new edit group M3 in tools/sounds/edits.py, built
from carousel.yaml, cuts each kept track's silent lead-in and tail (silent = 25 dB under the track's median
level, so a quiet track's own quiet parts stay; 0.4 s of fade kept) and shortens a silence inside to 0.3 s (only
Some Assembly Required has one). 46 tracks got a _tight cut, which their cut: now names; the 20 loops have
no silence. VOUCHER READY's icon (owner: "icon of voucher"): no game has a voucher, so three drawn candidates
(callout_icon_candidates.py --round 2). The hardware check screen's question answered. Open: a "change" without
a note on Cara Mia Addio. — Status: active.
The review tool split in two: a final review of only what's in the game, the workshop as backup (owner,
2026-11-02: "a much simpler tool ... a final review of everything in a simple way, only the selected items. keep
the rest in backup"; layout A, fresh keys with the old note shown, the backup on its own page). The old page had
grown to 25 tabs, 5.2 MB and ~5,600 review points (the character tabs repeating the periods, the whole sound
library, every candidate round, critics). review.html is now the final review (tools/story/final_review.py):
12 tabs - Overview, Attract, Period 1, The Fall, Period 2, Period 3, Wheatley branch, Period 4, Period 5, Endings,
Co-op, Machine - each item once (1,122 review points, 1.4 MB), inside each part the same order: what happens,
the chapters, what you hear, what you see. Its keys are final: + the backup's key, so the pass starts fresh and
each item shows its earlier answer in grey. GLaDOS / Cave / Caroline / Rattmann / Wheatley are filter buttons, not
tabs. A Still open list on the Overview holds the undecided cards, the unsettled sounds and the newest callout
icons. backup.html (tools/story/chain_review.py, unchanged otherwise) keeps the whole workshop, still
rebuilt. serve.py serves both (/backup.html); generate_docs.py rebuilds both; a new test counts the selected
items from their sources (each screen of the newest render, each kept carousel track, each light show, each
callouts sound outside the workshop sections) and fails if one is missing, shown twice, or a workshop item shows.
Built differently from the plan: the collectors stay in chain_review.py (the final page imports them) instead of
moving to a review_data.py - the same separation without moving 2,000 lines; the plan's step 0 needed nothing
for voice (callouts.md already holds the settled picks). The structure was reviewed by a fresh-context agent
before building (its findings folded in), and the built change by a second one: it found three game sections
on no final tab (STORY.md's "The playfield as a test chamber" and "The chapter select and length", the rules'
"Every choice"), now on the Overview, and the test widened to every story and rules section and every sound row
at its own moment - the workshop sections named in final_review.py (BACKUP_*). Plan
plans/review-tool-v2.md done and deleted. Then (owner, 2026-11-02: "preapprove all callouts and lighting
shows, the rest I will review manually"): 434 final-page items - every callouts block, every sound row in them
(OK, or "Pooled" for a settled pool's sound) and every light show - answered for the owner with a done note
saying so; the 301 items the owner had already answered there kept as they were. — Status: active.
Live-play windows are 20 s at least (owner, 2026-11-02: "live-play windows seem short, can we not have
them 20 sec minimum?"). Rattmann's arrow and GLaDOS's order (8 s), Wheatley's hiding spot, the laser relay
(10 s), sabotage (15 s), the Conversion Gel window (12 s) and the red phone (5 s) are 20 s; the Super Button
argument (10 s, not built yet) too. The finale's defend shot and the rocket's follow-up kept their scaling by
testing level, shifted up from 15 / 8 / 5 / 4 to 35 / 30 / 25 / 20 s; the escape hatch's planned Expert value
15 -> 20 s. Configs, timeouts.yaml, the rule sheets, the feature docs, docs/scoring.md (with the rule), the
choice tree and the plain summaries updated; the unit tests that waited out the old windows wait out the new
ones. One of them, a drain closing an arrow moment, had passed only because the 8 s timer ran out while the
ball save held the ball; it now drains after the ball save. Caroline's lit-letter hint (10 s) isn't in the
live-play list and is unchanged. Full playtest passed. — Status: active.
The final review's first answers processed (owner, 2026-11-03: "process all feedback, when it says change,
execute the change, update the table and mark as unchecked"). 660 answers done (OK, kept in pool, drops); the 30
changed items back to unchecked with a note on what changed. Music: four carousel tracks dropped (Love as a
Construct, Triple Laser Phase, sp_a2_laser_intro_b1, Almost at Fifty Percent - to each period's dropped: list);
twelve tracks start where they get going ("cut the slow buildup" / "remove more of the lead-in") - a lead_cut
per track in carousel.yaml, picked from where loudness plus onset density first rise to the track's level, which
edit group M3 (edits.py) honours with a short fade in. Screens: the signs' covers (the year on period 4's
dial, a choice's lines, the cube's line, period 5's lines) were flat boxes in the panel's colour, visible on the
worn surface - now panel_cover(), a clipped window onto the panel texture that lines up with it; VOUCHER READY
shows the picked paper voucher (instead of the button, SB1); the cake callout reads LANES COMPLETE / THE CAKE IS A
LIE with the game's scrawl as its icon (sign_cake_lie.png); the three file small endings lose "HOW YOU PLAYED";
NOW YOU'RE THINKING WITH PORTALS gets four icon candidates (callout_icon_candidates.py --round 3). Render
screen-review/2026-11-03-round-v7 (full playtest 20261103-210319-full passed). Then the two asked: Ghost of
Rattman out of attract's playlist (confirmed; period 5 keeps it) - which showed attract_slides.py never
learnt the radio-mix interlude between tracks (#182 had hand-edited the generated config), so a regeneration
would have dropped it: the generator now writes it from design/attract-pages.yaml's new radio:. Cara Mia
Addio trimmed too (TIGHT_ALSO in edits.py: a 2.1 s silent tail), E1's callouts row naming the trimmed file.
Then (owner: "render a portal from the game or find one online"): a portal is a shader, not a model, so it can't
be rendered from the model files; game_shots.py took four in the games (moment portal_open: Portal's
relaxation-vault pair and Portal 2's first pair, opened by console, no gun), and online_shots.py found three on
Combine OverWiki (copied to 2026-11-02-online-shots/, their sources in its sources.csv); seven icon
candidates, callout_icon_candidates.py --round 4. — Status: active.
The final review's next answers (owner, 2026-11-03): 31 done, 6 changed back to unchecked. NOW YOU'RE
THINKING WITH PORTALS shows Portal 1's blue portal from the game (sign_portal_open.png); the file small endings
lose their bottom line (compliant, marginally compliant, defiant, marginally defiant, arsonist) - so the two
PERFECTIONIST. screens and the two PROBLEMS WITH AUTHORITY. screens now read the same. Render
screen-review/2026-11-03-round-v8. — Status: active.
The plans folder cleaned up (owner, 2026-11-03, IDEAS.md: "review the status of the plans ... cleanup").
Of 13 plans, 9 retired (CHANGES #47's rule), their lasting parts folded in: done - screens-round-2
(its callout panel, period backgrounds, antagonists and feature callouts now in design/SCREENS.md),
toy-renders (step 5 overtaken by #208: the feature screens took game glyphs, not renders; the renders stay
in design/art/README.md), game-screenshots (design/art/README.md already held it), away-lighting-plan
(its decisions are in design/LIGHTING.md and #173-#174; the shows are TODO A1), next-build-order
(steps 0-1 done, the rest are their own plans); software done, only the cabinet left - sign-icons,
story-final-review, meter-and-memory, finish-open-plans: their cabinet checks moved to HumanToDo.md's
story run, H92's HOW TO PLAY to TODO A11, H112 and R2 to the Wheatley plan, the cube-fate rule to SCREENS.md.
TODO B3 (COMPLY OR DEFY, picked CD2 in #182) and D9 (the typo, gone in #208) checked off. Open:
critics2-picks (not started), music-carousel (cuts made, mode not built), wheatley-branch (not started),
each with a status line; art-and-screens (the owner's own) left for the owner. — Status: active.
The music carousel, built (owner, 2026-11-03, away mode: "do all 3, starting with music";
plans/music-carousel.md, done and deleted). A music mode runs all game: each period's soft / normal /
fast / tension carousels from design/music/carousel.yaml, the level from the game state (tension in a held
choice, fast in a hurry-up's last 30 s, soft on a chapter card, a waiting ball and a drain, else normal), a
1.5 s crossfade, a 10 s hold before a calmer level, resume where a track stopped, and 11 fixed cues (the five
multiballs, the finale, the fall, the science fair, E1's radio, E6, an extra ball) that win while they play.
The mode code (modes/music/code/music_carousel.py) keeps the state and posts each track's event with its
start position, which the sound_player entry's start_at takes - mpf-gmc drops a stopped track's position, so
MPF keeps it. tools/sounds/music_mode.py generates the config, the table and the 54 track copies (levelled to
the median loudness, at most 8 dB, a limiter; 123 MB in sounds/music/carousel/). The story modes' own music
lines (period 4's Technical Difficulties, period 5's den loop, A Clear Head, E1, E6, the science fair) moved
into the music mode, so one place owns the bus; tests/test_music.py fails if another game mode plays on it.
Judgement calls: Her Cores plays the owner's core-transfer loop, not Don't Do It; the Faith Plate and bridge
cues wait for a moment (their "while lit" is most of 1B); a card inside the hold waits. Docs: design/MUSIC.md. A fresh-context
review found five playback gaps, fixed the same day: a third play inside the 1.5 s fade found no free channel on
the two-channel solo bus and went silent (plays are now 1.6 s apart at least, a newer request replacing a
waiting one); the fall cue restarted on every period 2 ball (it starts on story_period_2 now, tied to
period 2); a sting during a multiball dropped the multiball's track (cues stack); no extra ball device exists,
so its cue went; every one-shot cue ends by its length at the latest. The red phone counts as a hurry-up.
— Status: active (to hear in a playtest and on the cabinet).
D34, the second critic panel's picks, built (owner, 2026-11-03, away mode: "do all 3 ... then D34";
plans/critics2-picks.md). Asked first because D34 (picked 2026-10-31) came before the final review: H122
(Caroline's voice cut mid-word) is superseded by #192, her full line stays; the file's new rows go on its second
page; every D34 change to an item the owner OK'd is built and shown as a card, A as reviewed / B the D34 version,
with the item back to unchecked (11 cards D34-1..D34-11 in options.md, 27 answers to the archive's
final-changed-2026-11-03-d34). Docs: the picks in STORY.md, the rule sheets and callouts (with their
moments and the plain summaries). Built: the held lines at the next ball start, one per ball after H110's
commendation and only in their speaker's period (your_file: H128's intake, with an INTAKE FORM callout; H124's
record after the first drain; H123's unmonitored chamber; H120's confession; H125's absolution or warning); H141
(the machine remembers a spare: spare_last, the greeting "It's been a long time. How have you been?" to listen;
GLADOS: 12% POWER on period 4's card); H126's 2B/2C card lines; H121's opening after E8; H134's Curiosity Core;
H131's crackle; H127's counter and "Ignores directions." in the read-out; H132's and H137's screen texts and
H140's two words; H130's SUBJECTS RELEASED page; H137's rare BBS page; the file's second page in two columns with
five rows (H124, H125, H127, H131, H143 - endings_on_record counts each ending once); H133 in
tools/sim/playthrough.py (held time: 4% of a beginner's game, 8% average, 10% expert); fact 9's registry part
(blue-portal-vuk). Not built (TODO A12): H123's camera answer and blind spot (no camera line or CAMERAS
BLIND yet), H126's sign words, H138's drain word (no drain screen), H139's sign cycling, H130's high-score and
service titles (GMC's default screens), H132's bank-stays-down rule, H136 (with the Wheatley branch).
tests/test_d34.py; stage 1, the full playtest and the screen render screen-review/2026-11-03-d34 pass.
— Status: active (the owner's cards and listening).
The Wheatley branch, built (owner, 2026-11-03, away mode: "... last the Wheatley branch";
plans/wheatley-branch.md, its cards SH1, IT1, BF1, PO-a, AR1). C0: when 1C's escape lock is full, fork 5 of
the choice window (never times out, Wheatley's nag pool every 20 s): plug him in on the Super Button, refuse on
the exit; the meter -2 / +2; wheatley 1 on the branch. The end of 1C: the escape runs; its end lights the
Super Button for the core transfer (it waits across balls), then the takeover and "NOW WHO'S A MORON?" and the pit
into period 2, with Omg What Has He Done. PotatOS joins in period 2 (the fall, the 2B card with a defective
turret, 2C's "why do I know this woman?"). story_p3w, instead of story_p3 / story_p3_side: 3A's scrap tests
under the reactor clock, the three cores' modes (kept in the same mode, not a story_p3w_side), the corrupted
cores' multiball, 3B's three waves ended by a portal transfer's stun, What Wheatley is, the booby-trapped button,
the moon portal, E7 (SPACE!) and PotatOS's verdict 4 s later; then it hands over to story_p3 at C3, so the
branch's C3, E1, the radio route, the dive and the arrow are the trunk's own (GLaDOS alive: no E2 or E8). The
shared modes learn the flag: drains (her mock and ball-save line not on the branch; a Wheatley / PotatOS pool),
the small ending ("NOW WHO'S A MORON?" with his picture, end_3wa / end_3wb), your file (WITH WHEATLEY;
GLaDOS's held lines not in the branch's period 3), the music (the branch's wb carousels). Screens:
tools/display/story_p3w_slides.py (the sign headed WHEATLEY LABORATORIES - D34 H136 - the background from the
owner's pick, the callouts). Tests: tests/test_story_p3w.py, C0's in test_story_p1.py; the wheatley
playtest (machinefolder/playtests/wheatley.py) passes. Left: TODO E1b. The plan is done and deleted. A fresh-context
review found five gaps, fixed the same day: a drain in the verdict's 9 s left no period 3 mode (C3's stage is set
at E7 now), a ball ended inside the corrupted cores' shoot-again stuck before 3B (a mode start in chapter 2 goes
on to 3B), a drain in the pit's 15 s skipped story_period_2 (posted at the next ball now), the Surprise could
fire on C0's or the transfer's press, and C0's DECISIVE bonus was missing (50,000). Also: the boss fight's music
is fast from its card, the branch's C3 drains are GLaDOS's again, the branch's variables are declared.
— Status: active (to play and listen on the cabinet).
A choice's callout: the story's words on top, the shot below (owner, 2026-11-04: "for all decision
making shot callouts, the top line should reflect the story wording, the bottom line should be the actual shot
to make"). C0, C1, C1b, the last core, C3 (with the arrow side: THE DROP TARGET), C5 and the arrow moments now
read e.g. CAVE'S ORDERS / CAROLINE'S ADVICE over THE LEFT ORBIT / THE HARD LIGHT BRIDGE. The arrow moments get one
callout per moment (their shots differ; arrow_both_<chapter>_<n>, period 2's lone arrow arrow_lone_<shot>),
replacing arrow_both / arrow_lone; the last core keeps the meter's word as a third line. This settles cards
D34-1..3 (moved to decided-options.md) and replaces H140's two words (CAVE: BONUS ..., HOME / HER) from #218.
— Status: active.
The Wheatley branch keeps his background through C3 (owner, 2026-11-04). story_p3 runs the branch's C3
after E7 and showed GLaDOS's picture; with wheatley 1 it now shows p3w_background instead (one
widget_player condition in story_p3.yaml), so the escape, the dive and the arrow stay in his facility. The
wheatley playtest's C3 capture shows it. — Status: active.
Review round, 2026-11-04 (owner: "process all review questions, update the review tool with the changes,
uncheck only changes"). The D34 cards, all picked (moved to decided-options.md): D34-4A E2's card back to
ASSUME THE PARTY ESCORT SUBMISSION POSITION, and (the Screens tab) over a screenshot of the surface - Portal's
parking lot with her remains (images/endings/web_e2_surface.jpg, the Combine OverWiki; callout_slide takes a
full-screen background); D34-5A the radio screen's LEAVE FOR GOOD: THE EXIT; D34-6B..11B stay as built;
D34-1..3 were settled by #220 (the owner's screen notes on them were answered by the render). The attract pages:
the BBS quote page (H137) dropped; SUBJECTS RELEASED's icon is the exit sign (man in a door). The other 62 answers
were OKs. Only the items this changed are unchecked (their answers to the archive's
final-changed-2026-11-04-review). — Status: active.
Review round 2 of 2026-11-04, and CI runs the music check (owner: "process answers, fix the ci and merge
again"). The 17 open answers were OKs (the items #222 unchecked, the Wheatley branch's cards over his
background), marked done. CI installs ffmpeg, so tests/test_music.py's generated-files check runs there instead
of skipping (the fix that got PR #98 green had made it skip); edits.duration always decodes, so the PC and CI
measure a track the same. — Status: active.
The art list built: the speaker bar, your file's dossier, the decade signs; what the ball is dropped
(owner, 2026-11-04: "can we build the rest in that plan?"; plans/art-and-screens.md, done and deleted).
Picked first (chat): the bar on the sign, faces picked by Claude for review, a drawn Aperture dossier. The speaker bar (callouts rule 5): a mode speaker_bar runs all game and
puts the bar on GMC's overlay slide; its script watches the voice bus and shows the speaker's face and name, and
a story line's words page by page (tools/display/speaker_bar.py generates the tables from the voice folders,
the catalogue and edits.csv: 289 files, 164 with words; story lines by folder). The text-only lines follow
their line for 3 s (COMPANION CUBE EUTHANIZED, I'M SORRY.). The owner asked mid-build for a switch to take it
off the main screen or put it elsewhere: the setting speaker_bar (off / on the sign / the left half), a new
place being one line in PLACES. Faces: tools/art/speaker_faces.py from the renders (card SB1). What the
ball is (OW-3) was built per chapter in the sign's top right corner (the gels, the cubes, you, a core, a
turret in Turret Frenzy), then dropped by the owner the same day ("drop the ball is... part"): removed with
its icons and rule-sheet table; STORY.md records the drop, and H138's drain word, which was to follow it, is
open again (TODO D11). Your file (TODO
D3): tools/art/file_dossier.py draws the folder; the verdicts are stamps on it, page 2 typed on the paper,
E6's callouts on the same file with Rattmann's marker. The sign's per-period looks: period 2's decades tint
the worn panel, period 3's sign flickers (sign_flicker.gd). Found already built: CUBE STATUS: UNKNOWN and
the small endings' drafts (TODO A8). Left (TODO D11): the recap cards wait for the chapter select, co-op's
courses for co-op, the small playfield screen for its hardware; the P2 cores' own faces. tests/test_speaker_bar.py.
— Status: active (to see and hear on the cabinet; card SB1).
The speaker bar on the left by default, its review fixes, and a Speaker bar tab (owner, 2026-11-04:
"speaker bar is at the wrong side of the screen"; "can you add a tab to the review tool to review the speaker
bar screens?"). The setting speaker_bar defaults to 2, the left half above the score; the sign stays setting
1. A fresh-context review of #224 found two gaps, fixed: leaving service mode ended a game without game_ended,
so the next game had no bar (the mode also stops on service_mode_entered, game_start, as small_ending does;
the music mode has the same gap, TODO E5); a text-only line was skipped when the sequential bus started the next
line at once, and shown when its line was cut off (the cue now arms when its line played to its end, and holds
the bar for its seconds). The final review page has a Speaker bar tab: the bar as seen in playtests in each
place (tools/display/speaker_bar_review.py keeps the captures with the bar in
design/research/speaker-bar/<date>/), the faces, the text-only lines and every voice line's speaker and words
(289 lines), one answer each. — Status: active.
The speaker bar back on the sign by default (owner, 2026-11-04: "make the speaker bar on the right
default"). After trying the left half (#225), the setting speaker_bar defaults to 1 again, the chamber sign on
the right half; the left half stays setting 2. tools/display/speaker_bar_review.py now reads each capture's
place from the picture, so a run made under an older default still lands in the right group of the Speaker bar
tab. — Status: active, superseding #225's default.
The announcer's face: a 1940s microphone (owner, 2026-11-04: "I do not like the announcer icon, can we
have an old school mic icon?", then "option A"). Three drawn candidates (tools/art/announcer_mics.py, the
sheet design/art/speaker-faces/announcer-mics-2026-11-04.png: A the 1940s capsule, B the radio ribbon mic, C
the 1920s carbon ring mic; a fourth, a desk mic, was dropped as badly drawn); A replaces the speaker grille,
kept as face_announcer-grille-2026-11-04.png. — Status: active.
The speaker faces reviewed (owner, 2026-11-04, the Speaker bar tab): nine faces OK (GLaDOS, PotatOS, Cave,
Caroline, the announcer's mic, the turret, the curiosity and cake cores, the Space/Fact/Adventure stand-in);
Wheatley's changed to "the image used in other places": the Combine OverWiki close-up on his rail that the
branch's background and his small ending use, cropped square (the render is kept in
design/art/speaker-faces/), back to unanswered for a look. Also: the Space Core's four faster copies on the
Wheatley branch are story lines but showed no words (their edit notes read "Orbit 1 of 5"); they take their
source line's words now. — Status: active.
The Speaker bar tab's lines start OK where the sound was approved (owner, 2026-11-04: "if the lines were
approved before (in sound) then mark as okay already"). 233 of its 289 voice lines got an OK in answers.json,
each noting the earlier sound answer it came from (the newest answer for that sound was OK, pool or pick; none
had a later change or drop); the 56 never answered as a sound stay open. — Status: active.
No empty speaker bars (owner, 2026-11-04: "can we agree not to show empty speaker bars?"). The bar shows
only for a line with words - the story lines; event and flavour lines play without it (callouts rule 5 had the
bar show just the face for them). The Speaker bar tab marks those lines "no bar (no words)", and the bar's
captures are retaken (design/research/speaker-bar/2026-11-04-words/). — Status: active, changing rule 5.
The Speaker bar tab shows the sign's screens only (owner, 2026-11-04: "I see speakerbar cards left and
right, can we have them all to the right please"): the tab lists the bar as seen on the chamber sign, the
default; the left half stays setting 2, its captures off the tab. — Status: active.
The Speaker bar tab's answers processed (owner, 2026-11-04: "process the final review data"). 72 answers:
card SB1 picked A (the faces as built) and moved to decided-options.md; the 56 lines not pre-approved from the
sound review, the nine sign screens, both text-only lines and Wheatley's new face OK; the three left-half screens
marked "select the cards on the right side" were already done by #231. All marked done; nothing left open on
the tab. — Status: active.
TODO.md cleaned up and a new order proposed (owner, 2026-11-04, IDEAS.md: "Review the status of the todo
actions. Cleanup ... and propose what we complete next"). Every open item checked against the code. Moved to
TODO-done.md: 9 finished items (A10, B1, B3, B6, D2, D3, D7, D8, E1) and 7 finished sub-items, plus A3, whose
leftovers are now A3b. Found done in the code: the attract playtest already accepts a greeting from any voice
folder (since 2026-10-19), story_p2's header no longer names the Space Core, and B7's coil templates were
removed by #157. Out of date: the header's To review tab (removed in #181), C6b's placement questions (the owner
sent them to the cabinet session in #143), A12's H138 (dropped in #224, its question is in D11) and the
proposed order. Narrowed: A9's header item (only story_p4's start-after-period-5 is left) and B7 (the
trough's debug: true after C0). CLAUDE.md's playtest list now includes wheatley. The new order: the
cabinet smoke test (C0) first, then the picked-but-unwired sounds (A4 + D10), the A9 cleanup with the A8
playtest gaps, A1 lights, then A5/A6/A11/A12. — Status: active.
The hardware lists follow the built and reviewed game; ideas the game doesn't use print last (owner,
2026-11-04: "The story, music, sounds, screen and structure as now build and reviewed is the leading source of
information ... review the hardware html lists, to match that and move all toys that are ideas only, not in
the review or build to a group at the end of the list"). A new registry field, idea_only: true, marks an idea
that the story, the rule sheets, the final review and the game config don't use. It's on 4 parts so far, all from
the owner's hardware notes of 2026-10-25: the 2nd launch-lane diverter, the orbit-top diverter, the funnel-exit
post and the panel ramp. print/status-checklist.html and the wiring guide's tables (HTML and Markdown) list
them last, under "Ideas only". check_registry.py rejects the field on an obtained part. The other
"(idea)" parts stay where they are, because the reviewed game uses them: the LED strips, hint row, magnet,
shaker, core locks, KD1, the den display, the co-op and era-select buttons, tilt, the small screen (a STORY
stretch goal), the launch-lane diverter (the dropper auto-reload) and the hidden side sensor (a sound on the
review). Changed to match the review: the toys print gives the Blue Portal its own new VUK instead of the
Mid-Flipper (rule sheets, 2026-10-19), puts the Mid-Flipper with the Faith Plate it feeds, and adds the
left-ramp diverter to the Excursion Funnel (2C, the diverter to the Faith Plate). The registry's summaries say
Orange/Blue Portal instead of Portal A/B (D29), and the left-ramp diverter's "(destination open)" is now "(to the
Faith Plate, period 2)". The review pages came out unchanged. Screenshots:
print/screenshots/ideas-only-last/. — Status: active.
A top-view sketch of Portal 2's Aerial Faith Plate, for Onshape (owner, 2026-11-07: "extract a top view
sketch (including dimensions) for the aerial faith plate ... from the game assets to onshape"). New
tools/art/faith_plate_topview.py (portal-art venv) reads models/props/faith_plate from Portal 2's
pak01_dir.vpk (MDL + VVD + VTX, LOD 0, bind pose) and writes design/research/aerial-plate-sketch/: the mesh
as STL, the top view as DXF for an Onshape sketch (game-top-view.dxf in game units marked as inches,
game-top-view-mm.dxf at game scale in mm, centred on the origin, straight runs joined) and an SVG/PNG with the
main dimensions. Only what is visible from above is drawn (a depth buffer hides covered edges). Source models
wind their triangles clockwise; reading them counter-clockwise flipped every normal, so the first version
(game-top-view-v1.*, kept) showed mostly the underside. Considered and dropped: a sketch from the
playfield CAD's own "Faithplate walls" part - the owner wanted the game's model. The plate model is 128 x 64
game units; faith_plate_128 was skipped (its bind pose has the plates at odd angles). — Status: active.