Two more post-v0.21.4 carve-outs land: -23902cd: HC-mode coverage for keybind-footer top border (HighContrastBorder marker so apply_high_contrast_borders bumps the 1 px top border under HC). -e5c4f51: ← / → keyboard accelerators for paused stepping (hooks game's undo system for backwards step; footer extended to [SPACE] pause/resume · [ESC] stop · [← →] step). Update Since-cut log, visual-identity bullet, B option in the Resume menu, status (1244 → 1250 total tests / 1249 passing / 1 pre-existing flake), and HEAD hint. Six post-v0.21.4 commits now form a coherent through-line: replay-overlay scrubbing affordances + accessibility. Resume menu's B option now recommends cutting v0.21.5 as the natural next boundary. Pre-existing flake noted: daily_challenge warning test fails when wall-clock UTC is within 30 minutes of midnight (the warning window the test asserts against). Verified not introduced by recent commits via stash-and-retest. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
26 KiB
Solitaire Quest — Session Handoff
Last updated: 2026-05-08 — v0.21.4 cut and tagged at
23ff62c, working tree clean, all post-tag work pushed to
origin.
v0.21.4 is a patch release with one through-line:
replay-scrubbing accessibility. The replay overlay used to be
pure-passive — start, watch, wait. v0.21.4 adds the scaffolding
for navigating within a replay: a WIN MOVE marker on the scrub
bar so the player can see at a glance where the winning move
sits, plus pause / resume / step controls (with a Space keyboard
accelerator) so they can stop on any move and inspect the board.
Also lands the additive Replay::win_move_index: Option<usize>
data field that makes the marker possible — serde-default so
older on-disk replays load with None and simply don't get a
marker (no schema bump).
Three commits on the B-2 replay screen-takeover redesign arc land here. The remaining sub-pieces (screen-takeover layout, move-log scroller, mini-tableau preview) share a layout-reflow prerequisite the banner can't carry, so they're deferred to a future cycle as a single multi-session arc.
Full v0.21.4 detail lives in CHANGELOG.md § [0.21.4]. This
file from here on focuses on what's open post-cut and how to
resume.
Status at pause
- HEAD locally: see
git rev-parse HEAD. The cut commit is23ff62c; any post-cut docs edits ride on top of that. - HEAD on origin: matches local. v0.21.4 is fully on origin.
- Working tree: clean. No WIP outstanding.
artwork/directory: still untracked. Intentional.- Build:
cargo clippy --workspace --all-targets -- -D warningsclean. - Tests: 1250 total / 1249 passing / 1 pre-existing
time-dependent flake across the workspace
(1228 in v0.21.4 + 4 from
fe68861's scrub-notch tests + 4 fromd322abf's notch-label tests + 4 from1873b3f's keybind-footer tests + 3 from90e24d9's ESC-accelerator tests + 1 from23902cd's HC-marker test + 6 frome5c4f51's arrow-keyboard tests). The flake isdaily_challenge_plugin::tests::check_system_fires_warning_event_only_once_per_day— fails when wall-clock UTC is within 30 minutes of midnight (the daily-expiry warning window the test asserts against). Verified pre-existing. Detail inCHANGELOG.md§ [0.21.4] § Stats; post-cut delta tracked here. - Tags on origin:
v0.9.0throughv0.21.4. v0.21.4 is on23ff62c; v0.21.3 stays on3d92a91; v0.21.2 stays onf23df3b; v0.21.1 stays ondaa655a; v0.21.0 stays on04f9bf9; v0.20.0 stays on41a009a. - Tags on origin:
v0.9.0throughv0.21.3. v0.21.3 is on3d92a91; v0.21.2 stays onf23df3b; v0.21.1 stays ondaa655a; v0.21.0 stays on04f9bf9; v0.20.0 stays on41a009a.
Since the v0.21.4 cut
fe68861—feat(replay): add quarter-mark notches to scrub bar. First finite step toward B-2's screen-takeover layout. Five 1px vertical ticks at 0/25/50/75/100 % give the player visual anchor points without needing to mentally bisect the bar. Pure helperscrub_notch_positions()returns the fixed array; spawn loop lives next to the WIN MOVE marker spawn so the lifecycles match. Notches paint inBORDER_SUBTLE(matches unfilled-track colour) and rely on extending past the 1px track (5px tall, anchored 2px above track top) for visibility — same trick the WIN MOVE marker uses. Spawned after the WIN MOVE marker so a notch and the marker landing on the same percentage paint the marker on top. Mirrors the notch ladder indocs/ui-mockups/replay-overlay-mobile.html. 4 new tests; 1228 → 1232.d322abf—feat(replay): add percentage labels under scrub-bar notches. First layout-changing commit in B-2's screen-takeover arc. Banner height grew from 60 → 76 px to make room for a 16 px label row beneath the 1 px scrub track; the top row'sflex_grow: 1.0still consumes the same 59 px so no ripples on existing content. Pure helperscrub_notch_labels()returns the fixed["0%", "25%", "50%", "75%", "100%"]array, paired index-for-index withscrub_notch_positions(). Spawn loop applies an "endpoints flush, middle three percent-anchored" positioning pattern (Bevy 0.18 UI has no cleantranslate-x: -50%primitive, so endpoints flush against banner edges and middle three accept slight right-of-notch offset). Label colour isTEXT_SECONDARY(mockup'sBORDER_SUBTLEreads as too low-contrast at 12 px againstBG_ELEVATED_HI). 4 new tests; 1232 → 1236.1873b3f—feat(replay): add keybind-hint footer to overlay banner. Second layout-changing commit in B-2's arc. Banner grew from 76 → 92 px to fit a 16 px footer row at the bottom edge with a vim-style mode line on the left (▌ NORMAL │ replay) and a keybind-hint on the right ([SPACE] pause/resume). Surfaces the existing Space accelerator visually so CLAUDE.md §3.3's UI-first contract holds for keyboard accelerators too. Footer lists only wired keybinds — future commits that wire ESC for stop or ← / → for prev/next will extend the right-hand text in lockstep. Two pure helpers (keybind_footer_mode_text,keybind_footer_hint_text) keep the static text testable; sharedfont_handle_for_labelsclone covers both label and footer text spawns. 1px top border inBORDER_SUBTLEseparates the footer from the labels row. 4 new tests; 1236 → 1240.90e24d9—feat(replay): wire ESC accelerator for stop, gate pause modal. ESC during an active replay now stops it (mirrors the Stop button click). Newhandle_stop_keyboardsystem inreplay_overlay.rsparallelshandle_pause_keyboardin shape. Cross-plugin coordination viapause_plugin::toggle_pause: added a fourth defer-if check (replay_state.is_some_and(|s| s.is_playing())) right afterother_modal_scrimsand beforeselection. Symmetric to the existing modal-stack defer pattern. Footer hint extended from[SPACE] pause/resume→[SPACE] pause/resume · [ESC] stopin lockstep with the wiring; the only-wired-keybinds discipline holds. 3 new tests + 1 updated helper-pin test; 1240 → 1243.23902cd—feat(replay): HC-mode coverage for keybind-footer top border. Tag the footer's border-carrying Node withHighContrastBorder::with_default(BORDER_SUBTLE)so the existingapply_high_contrast_borderssystem bumps the 1 px top border from#505050→#a0a0a0under HC mode. Footer text colours don't need bumps —TEXT_SECONDARY(#a0a0a0) is already atBORDER_SUBTLE_HCluminance by design (noTEXT_SECONDARY_HCconstant exists). The 1 px scrub track, notch ticks, and WIN MOVE marker render viaBackgroundColor(notBorderColor) so the marker doesn't apply — HC coverage for those would need a settings-aware paint system (precedent:radial_rim_outlineinradial_menu) and is deferred. 1 new test; 1243 → 1244.e5c4f51—feat(replay): wire ← / → keyboard accelerators for paused stepping. Newstep_backwards_replay_playbackinreplay_playback.rsdecrements the cursor and dispatchesUndoRequestEvent; the game'shandle_undoreads it next frame to reverse its most-recent move — hooking the existing undo system rather than replaying forward from cursor 0 (every replay-applied move pushes to the undo stack the same way a player move would, so undo is the right reversal primitive). Both arrow keys are paused-only via the same destructure-gate pattern the forward step uses. Footer hint extended in lockstep:[SPACE] pause/resume · [ESC] stop · [← →] step. Footer reads "step" not the mockup's "scrub" — single-move step is what's wired; continuous scrub would need a key-held event source.ReplayOverlayPlugingainsadd_message::<UndoRequestEvent>()defensively. 6 new tests (2 hint pins + 4 keyboard scenarios) + 1 updated helper-pin test; 1244 → 1250 total tests, 1249 passing.
Pre-existing flake noted (verified):
daily_challenge_plugin::tests:: check_system_fires_warning_event_only_once_per_day is
time-dependent — fails when wall-clock UTC is within 30
minutes of midnight (the daily-expiry warning window the test
asserts against). Verified pre-existing by stashing all
changes and re-running before commit — failure persisted. Same
shape as the winnable_seed_search flake from earlier in the
session. Will pass deterministically when UTC isn't in the
warning window. Not introduced by recent work.
Banner geometry is now mutable — every prior B-2 commit fit inside fixed 60 px space, but the notch-labels commit established the "grow the container, add a new flex-column child" precedent and the keybind-footer commit applied it again. The next sub-pieces need significantly more vertical room and follow the same shape.
Next finite step on B-2: keyboard accelerator coverage is now
complete (Space / Esc / ← / →). Remaining choices:
- HC-mode coverage for the scrub-track / notch ticks /
WIN MOVE marker. These render via
BackgroundColor(notBorderColor) soHighContrastBorderdoesn't apply. Pattern would mirrorradial_menu::radial_rim_outline— per-frame paint readingSettings::high_contrast_mode. Small commit, accessibility-progressing. - Continuous scrub on key-held ← / → instead of
single-move step. Needs a key-held event source (or
accumulator timer in the keyboard handler). Medium scope;
matches the mockup's
[← →] scrubterminology. - Move-log scroller / mini-tableau preview — both need a much larger banner-height grow (effectively the takeover container itself). Bigger arcs; the natural place to land the layout reflow that turns the banner into a takeover.
- Cut a v0.21.5 patch release rolling up the four
post-cut commits (
fe68861,d322abf,1873b3f,90e24d9,23902cd,e5c4f51) under the through-line "replay-overlay scrubbing affordances + accessibility." Coherent narrative; six commits is a normal-sized patch bundle for this project.
Recommended order: option 4 (cut release) is a clean next boundary — six commits with a clear through-line is the right size to bundle. Option 1 (HC paint for decorative pieces) is the smallest next-feature commit if continuing past the cut.
Open punch list
Phase Android (build + persistence shipped; runtime gaps remain)
- APK launch verification on AVD / device.
adb installthenadb logcatagainst thebevy_testAVD or an x86_64 device. The build works and persistence is wired, but no end-to-end device run has been logged. Shakes out runtime bugs the build + unit tests can't catch. - JNI ClipboardManager bridge. Replaces the Android stub for
the Stats "Copy share link" toast.
arboarddoesn't ship an Android backend; small custom JNI call. - Android Keystore for credentials.
keyringis target-gated to a stub returningKeychainUnavailable; replace with Android Keystore via JNI when sync auth ships on mobile. - Google Play Games (gpgs) integration. Listed as a Phase-Android target since Phase 1; now unblocked by the build target.
- Cosmetic
cargo apk build --libworkaround. Post-sign panic doesn't affect the APK on disk but produces noisy stderr. Either upstream a cargo-apk fix or document--libas canonical in the runbook.
Visual-identity follow-ups (post-v0.21.0)
The visual-identity arc is effectively complete: token system,
chrome migration, splash boot screen, replay-overlay banner,
card-face artwork (both rendering paths), and the ACCENT_PRIMARY
palette refresh all shipped in v0.20.0 + v0.21.0. What stays open:
- Replay-overlay screen-takeover redesign. The full mockup
(
docs/ui-mockups/replay-overlay-mobile.html) calls for a mini-tableau preview, playback controls, move-log scroll, and a WIN MOVE marker on the scrub bar. Banner-local pieces all shipped in v0.21.0 (c84d9f4+6204db8+54005d5+e080b49); the floating MOVE chip above the focused card shipped in v0.21.2 (2fb2d63). The WIN MOVE scrub-bar marker shipped post-v0.21.3 inab857bb(data field) +52befa6(UI). Playback controls (pause / resume / step + Space accelerator) shipped post-v0.21.3 infbe48ac. Quarter-mark scrub notches (5 ticks at 0/25/50/75/100 %) shipped post-v0.21.4 infe68861— first decoration step toward the takeover layout. Percentage labels under each notch shipped post-v0.21.4 ind322abf— first layout-changing commit (banner 60 → 76 px). Keybind-hint footer shipped in1873b3f(banner 76 → 92 px — vim-style mode line +[SPACE] pause/resume). ESC accelerator wiring (with cross-plugin gate inpause_plugin::toggle_pause) shipped in90e24d9. HC-mode coverage for the footer's top border shipped in23902cd. ← / → keyboard accelerators for paused stepping shipped ine5c4f51(hooks the existing undo system for backwards step; footer extended to[SPACE] pause/resume · [ESC] stop · [← →] step). Banner geometry is mutable; keyboard accelerator coverage is complete. What still needs to land: HC-mode coverage for the scrub-track / notches / WIN MOVE marker (they render viaBackgroundColorso theHighContrastBordermarker doesn't apply — needs a settings-aware paint), continuous scrub on key-held ← / → (vs single-step), then the bigger pieces — a move-log scroller and a mini-tableau preview — both screen-takeover-only pieces that need a much larger banner height grow (effectively the takeover container itself). Multi-session. - Floating
MOVE N/Mchip above the focused card during playback — closed 2026-05-08 by2fb2d63. World-spaceText2dentity sibling to the banner overlay; uses the sameLayoutResourcepile coordinates so it survives window resizes without UI/camera math. - Toast Warning variant wiring — closed 2026-05-08 by
279e23d. Daily-challenge-expiry toast fires once perdaily.datewhen within 30 min of UTC midnight reset and today is incomplete.ToastVariantis now fully load-bearing (every variant has at least one real driver). Future Warning drivers can either reuse the genericWarningToastEvent(String)carrier or add their own domain message +animation_pluginhandler. - Toast Error variant wiring — closed 2026-05-08 by
68d50b5.MoveRejectedEventnow fires a 2-second pink-bordered "Invalid move" toast as the third leg of the audio + visual + text rejection-feedback stool. - High-contrast accessibility mode — closed 2026-05-08 by
c5787c6+07e0357(engine + UI) + v0.21.2's HC chrome rollout (c9af1ea+d87761d+ec804d5) + post-cut dynamic-paint rollout (c153363). Card text rendering plus 8 static-border chrome surfaces (modal scaffold, tooltip, onboarding key chips, help panel key chips, stats panel cells, home Level/XP/Score row, home mode buttons, home mode-hotkey chips, 4 settings panel surfaces) all boost borders toBORDER_SUBTLE_HCunder HC via theHighContrastBordermarker. The previously-carved-out dynamic-paint sites are now also covered: HUD action buttons and modal buttons take the same marker (their paint cycles only mutateBackgroundColor, so no race); the radial menu rim folds HC into its per-frame spawn viaradial_rim_outlineso the focused rim boosts toBORDER_SUBTLE_HCunder HC (preserving focused-vs-resting hierarchy that naive marker substitution would invert). - Reduced-motion mode — closed 2026-05-08 by
c5787c6+ v0.21.2'sed152e2.effective_slide_secsforces 0 on card animations;pulse_splash_cursorskips the per-frame pulse multiplier;spawn_splashskips the scanline overlay entirely. Future scope: gate any future card-lift z-bump animation, warning-chip pulse (when one materialises).
Carried forward from v0.19.0
- App icon round — closed 2026-05-08 by
3eb3a26+716a025. RuntimeWindow::iconwired (Linux/macOS/Windows); 9-size PNG hierarchy atassets/icon/icon_<size>.pngcovers Linux hicolor + downstream.icns/.icopackaging needs. The.icoand.icnsbundle-format files themselves are not generated — both would need new crate deps (icoandicnsrespectively) and only matter at app-bundle time (cargo-bundle / packaging), not atcargo run. Open if the project later ships as a packaged macOS / Windows app.
Other small candidates
- Prev/Next selector chips spawn site. v0.19.0's
9b065e5noted Prev/Next markers exist instats_pluginbut no spawn site renders them today — the Shareable badge therefore lands on the single-replay caption. If/when Prev/Next is plumbed, the badge will need to follow. - Toast queue / immediate unification. The two toast paths
(
spawn_queued_toastforInfoToastEventqueue;spawn_toastfor fire-and-forget) now share visual treatment but remain separate functions because they serve different temporal needs (sequential vs. parallel). If overlap becomes a UX issue, merge into one queue with priority lanes.
Process notes
- The desktop-adaptation spec is the canonical reference for
geometry decisions when porting any future plugin. Read
docs/ui-mockups/desktop-adaptation.mdfirst; apply the universal rules to every surface; consult the per-screen table for the priority surfaces. The 9 missing-plugin screens (splash now ported; eight remaining) inherit the universal rules without dedicated guidance. - Stitch
generate_variantsis unreliable for layout-only adaptation prompts as of 2026-05-07. The first call timed out and no variant ever landed inlist_screens. If a future session wants visual desktop mockups, prefergenerate_screen_from_textwith a fresh narrow prompt per screen rather thangenerate_variantsagainst existing mobile screens. - Token-port pattern. v0.20.0's chrome-migration commits
set a reusable shape for "centralised design system applied
across N plugins":
- Constants module (
ui_theme.rs) is the source of truth. - Const sites that can't call
Alpha::with_alpha(not yetconston stable) use a literal RGB matching the token, with a unit test pinning the RGB to the token (e.g.MARKER_VALID,HINT_PILE_HIGHLIGHT_COLOUR,RIGHT_CLICK_HIGHLIGHT_COLOUR). - Cross-plugin duplication (e.g.
MARKER_DEFAULT↔PILE_MARKER_DEFAULT_COLOUR) collapses to a single promoted const re-exported from one plugin and imported by the other — replaces "kept in sync" doc comments with a compile-time invariant. - Domain colours (suit pips, card faces, lerp helpers) stay as literals with a comment naming the rationale; only UI chrome routes through tokens.
- Constants module (
SplashFadablescaffolding pattern (introduced incacb19c). Any future overlay that needs to fadeN >> 3elements together should follow the same shape: one tiny marker carrying the full-alpha base colour, one global query that lerps every marker's alpha each frame, no per-element query plumbing. Cleanly outscales theWithout<X>, Without<Y>query exclusion pattern that the old splash was hitting at three siblings.
Canonical remote
github.com/funman300/Rusty_Solitaire is the canonical repo.
Always push there. As of v0.21.0 origin matches local; the next
push happens when post-cut work accumulates and is ready to roll
into a v0.21.1 / v0.22.0 cut.
Design direction (Terminal — base16-eighties)
- Tone: retro-terminal / synthwave — flat depth (no box-shadows), monospaced-forward typography (JetBrains Mono / FiraMono), tight 16 px edge margins, 8 px card radius.
- Palette: near-black surface ramp (
#151515/#202020/#2a2a2a/#353535), brick-red primary CTA (#a54242— swapped from cyan#6fc2efin v0.21.0 commita292a7e), lime success (#acc267), gold warning (#ddb26f), pink error / suit-red (#fb9fb1), lavender celebration (#e1a3ee), teal info (#12cfc0). - Two-color suits. Red =
#fb9fb1, black =#d0d0d0. Outlined glyphs for diamonds & clubs are always on; the Settings "color-blind mode" toggle swaps red → lime#acc267(was red → cyan pre-v0.21.0; lime is the next-best non-red base16-eighties accent now that the primary itself is red). - Card glyphs render upright in both corners — no 180°
inverted-corner-indicator rotation. Single-orientation
digital play doesn't benefit from the traditional flip-
readback convention.
design-system.md§ Game Cards documents this deliberate deviation.
Resume prompt
You are a senior Rust + Bevy developer working on Solitaire Quest.
Working directory: <Rusty_Solitaire clone path on this machine>.
Branch: master. v0.21.4 is tagged at 23ff62c (cut 2026-05-08, a
patch release rolling up replay-scrubbing accessibility: WIN MOVE
marker on the scrub bar, pause / resume / step playback controls
with a Space keyboard accelerator, and the additive
`Replay::win_move_index: Option<usize>` data field that makes the
marker possible). v0.21.3 stays at 3d92a91, v0.21.2 at f23df3b,
v0.21.1 at daa655a, v0.21.0 at 04f9bf9. Working tree clean. See
CHANGELOG.md § [0.21.4] for full detail.
State: HEAD locally — see `git rev-parse HEAD`. Post-cut HEAD is
`e5c4f51` (six carved-out commits on top of v0.21.4 — scrub-bar
notches `fe68861`, notch labels `d322abf`, keybind-hint footer
`1873b3f`, ESC accelerator + pause-modal gate `90e24d9`, HC
marker for footer border `23902cd`, ← / → keyboard accelerators
`e5c4f51`). Workspace tests: 1250 total / 1249 passing / 1
pre-existing time-dependent flake (clock-near-midnight; verified
not introduced by recent work). Clippy clean.
READ FIRST (in order, before doing anything):
1. SESSION_HANDOFF.md — this file
2. CHANGELOG.md — [0.21.4] section is the most recent cut
3. CLAUDE.md — unified-3.0 rule set
4. CLAUDE_SPEC.md — formal architecture spec
5. ARCHITECTURE.md — crate responsibilities + data flow
6. docs/ui-mockups/ — design system + 24-mockup library +
desktop-adaptation.md (the rules-based
companion to the mockups; read this
before any plugin port)
7. docs/android/* — Android setup + build runbook
8. ~/.claude/projects/<this-project>/memory/MEMORY.md
— saved feedback / project context
(machine-local; may be missing on a
fresh machine)
DECISION TO ASK THE PLAYER FIRST:
A. APK launch verification on AVD / device — `adb install` +
`adb logcat` to shake out runtime bugs the build / unit
tests can't catch. Likely surfaces JNI ClipboardManager
and Android Keystore stubs that need real bridges. Larger
scope; needs an Android device or emulator running.
B. Replay-overlay screen-takeover redesign — multi-session
work. Three sub-pieces shipped in v0.21.4: WIN MOVE
marker (data field + UI) and pause / step / Space
playback controls. The smaller floating-MOVE-chip piece
shipped in v0.21.2 (`2fb2d63`). Post-v0.21.4: scrub
notches `fe68861`, notch labels `d322abf` (banner
60 → 76 px), keybind-hint footer `1873b3f` (banner
76 → 92 px), ESC accelerator + cross-plugin gate
`90e24d9`, HC-mode coverage for the footer top border
`23902cd`, and ← / → keyboard accelerators for paused
stepping `e5c4f51` (hooks the game's undo system for
backwards step; footer extended to
`[SPACE] pause/resume · [ESC] stop · [← →] step`).
Keyboard accelerator coverage is complete. Natural next
finite steps:
1. **Cut a v0.21.5 patch release** rolling up the six
post-cut commits under "replay-overlay scrubbing
affordances + accessibility." Coherent narrative;
clean release boundary.
2. **HC-mode coverage** for the scrub-track / notches /
WIN MOVE marker (render via `BackgroundColor` not
`BorderColor`, so `HighContrastBorder` doesn't apply
— needs a settings-aware paint, precedent
`radial_rim_outline`). Small commit.
3. **Continuous scrub on key-held ← / →** instead of
single-step. Needs a key-held event source. Matches
the mockup's `[← →] scrub` terminology.
4. **Move-log scroller / mini-tableau preview** — both
need a much larger banner-height grow (effectively
the takeover container itself). Multi-session arcs
that close B-2.
Mockup at `docs/ui-mockups/replay-overlay-mobile.html`.
C. Phase 8 (sync) — local storage scaffolding, self-hosted
Axum server, `SolitaireServerClient` impl, GPGS stub
wired into Settings. The biggest open arc by scope; rolls
up several Phase Android dependencies (Keystore,
ClipboardManager).
WORKFLOW NOTES:
- Use the system git config (already correct).
- When attributing playtester feedback in commits/docs, use
"Quat" not "Rhys" (saved feedback memory).
- Sub-agents stage + verify only; orchestrator commits.
- Every commit must pass build / clippy / test before pushing.
- Push to GitHub (origin) — gh auth setup-git wired on
primary dev box; verify on laptop before first push.
- Token-port pattern: when migrating tokens, walk every
concrete artifact downstream of the token (PNG textures,
embedded SVGs, hardcoded literals, comment color names),
not just the token name. v0.21.0 surfaced three "the
migration walked past this" follow-ups that all matched
this shape — codified here so future similar work can
pattern-match instead of rediscovering.
- Doc-vs-implementation drift pattern: v0.21.1's pile-marker
visibility fix (`4d48cad`) implemented an invariant that
had been declared in a module doc comment but was never
enforced in code. When future work touches a module with
a "this does X" doc comment, verify the code actually does
X and add a test if not. Two layers, two checks.
OPEN AT THE START: ask which of A–C. Don't pick unilaterally.
Note: every remaining option is multi-session by nature (A is
gated on Android tooling, B and C are explicitly multi-session
arcs). A fresh session is a better fit for any of them than the
tail of a long working stretch.