refactor(settings): own two-phase runtime settings

Move pre-window loading, startup application, live settings mutation, toon context, quality reapply, and SettingsVM loans behind one RuntimeSettingsController. Preserve retail command behavior, ordered target publication, draft semantics, and retryable failure convergence while removing duplicate GameWindow state and feature bodies.

Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-22 13:30:22 +02:00
parent 4eae9b4f5a
commit fec0d94148
24 changed files with 2379 additions and 599 deletions

View file

@ -1,6 +1,6 @@
# acdream — strategic roadmap
**Status:** Living document. Updated 2026-07-22. **M3 landed; M4 is next after the active `GameWindow` structural prerequisite.** M3's retail casting/UI, R6 locomotion/collision/projectile/teleport/radar rebaseline, deterministic fresh-login/portal world lifecycle, and final two-client portal observer flow are user-gated. The current program is the behavior-preserving ownership campaign in [`docs/architecture/code-structure.md`](../architecture/code-structure.md); Slices 17 and Slice 8 checkpoints AF are complete, with the remaining composition/shutdown checkpoints active. New M4 quest/emote/character-creation feature bodies wait until that campaign is complete. Issue #225's lifestone/particle alpha comparison remains a separate rendering visual gate.
**Status:** Living document. Updated 2026-07-22. **M3 landed; M4 is next after the active `GameWindow` structural prerequisite.** M3's retail casting/UI, R6 locomotion/collision/projectile/teleport/radar rebaseline, deterministic fresh-login/portal world lifecycle, and final two-client portal observer flow are user-gated. The current program is the behavior-preserving ownership campaign in [`docs/architecture/code-structure.md`](../architecture/code-structure.md); Slices 17 and Slice 8 checkpoints AG are complete, with the remaining composition/shutdown checkpoints active. New M4 quest/emote/character-creation feature bodies wait until that campaign is complete. Issue #225's lifestone/particle alpha comparison remains a separate rendering visual gate.
**Purpose:** One source of truth for where the project is and where it's going. Every observed defect or missing feature has a named phase that owns it; when something looks wrong in-game, look here to find the phase that'll address it. Implementation details live in per-phase specs under `docs/superpowers/specs/`, not in this file.
---
@ -79,7 +79,7 @@ second synchronized soak pass with graceful exits, and the six stable PNG
checkpoints preserve Slice 6 presentation. Issue #232 tracks the coarse
process-residency gate's diagnostic blind spot; no tolerance was loosened.
Final Slice 8 composition/shutdown cleanup is active. Checkpoints AF now own
Final Slice 8 composition/shutdown cleanup is active. Checkpoints AG now own
the frozen shell boundary, all nine native callback edges, live-session
reset/selection/entry/route composition, and world clock/sky/day/weather/
AdminEnvirons state. Checkpoint E adds reversible, transactional Silk,
@ -91,8 +91,13 @@ fields / 65 methods. Checkpoint F adds the sole gameplay action-priority
subscriber, typed combat/diagnostic/general commands, and a reversible
retained-root item-drop binding. `GameWindow` is now 4,057 lines / 198 fields /
54 methods; the Checkpoint F Release suite passes 7,524 tests / 5 skips.
Settings, startup/resource transfer, shutdown, soak, and closeout checkpoints
GL remain.
Checkpoint G adds the pre-window `RuntimeSettingsController`, one immutable
startup snapshot, ordered startup pacing/display/FOV/audio application, late
runtime-target binding without replay, and one current/toon settings owner.
`GameWindow` is now 3,663 lines / 162 fields / 37 methods. Resource transfer,
ordered composition, shutdown, soak, and closeout checkpoints HL remain. The
Checkpoint G Release suite passes 7,553 tests / 5 intentional skips, and all
three corrected-diff reviews are clean.
This is a behavior-preserving structural program. Severe regressions still get
root-cause fixes in separate commits; ordinary feature work resumes with M4

View file

@ -39,7 +39,7 @@ diagnostic owners preserve the accepted draw/failure graph. `GameWindow` is
campaign baseline. The full Release suite passes 7,341 tests / 5 skips; the
315.6-second capped/reconnect lifecycle gate and 395.2-second synchronized
nine-destination soak pass with graceful exits, and six stable PNG checkpoints
preserve Slice 6 presentation. Final Slice 8 checkpoints AF are complete:
preserve Slice 6 presentation. Final Slice 8 checkpoints AG are complete:
native callbacks and live-session composition now have explicit retryable
owners. Checkpoint D adds the sole world-environment owner for clock, DAT sky,
day group, weather, and AdminEnvirons state. Checkpoint E adds terminal,
@ -47,10 +47,13 @@ transactional ownership for Silk/dispatcher/retained/devtools input, the raw
camera pointer, focus, and physical framebuffer size. Graceful live-session
retirement now precedes physical callback removal. Checkpoint F adds the sole
gameplay action-priority owner, typed combat/diagnostic/general commands, and a
reversible retained-root item-drop binding. `GameWindow` is 4,057 lines / 198
fields / 54 methods; 7,524 Release tests pass / 5 skip. Checkpoints GL remain
active. Issue #232 tracks process-residency variance in the soak without
loosening its leak threshold.
reversible retained-root item-drop binding. Checkpoint G adds the sole
pre-window/runtime settings owner, one startup snapshot, ordered one-shot
startup application, and inert late target binding. `GameWindow` is 3,663
lines / 162 fields / 37 methods. Checkpoints HL remain active. Issue #232
tracks process-residency variance in the soak without loosening its leak
threshold. The Checkpoint G Release suite passes 7,553 tests / 5 skips and all
three corrected-diff reviews are clean.
Carried:
#153 far-teleport residual, #116 slide-response, the remaining R6 ownership cleanup plus

View file

@ -0,0 +1,132 @@
# GameWindow Slice 8 — Checkpoint G Runtime Settings Plan
## Objective
Replace `GameWindow`'s duplicated pre-window and `OnLoad` settings paths with
one two-phase `RuntimeSettingsController`. The controller owns persistence and
live settings state; render, window, audio, UI, and streaming objects remain
borrowed targets. This is a structural cutover, not a redesign of the settings
format or retail option behavior.
Two carried behaviors are now explicit rather than accidental. Saved FOV is
applied during startup even when developer tools are disabled; previously it
was incorrectly gated on construction of `SettingsVM`. The named-retail
`/framerate` live toggle and notice remain intact, while acdream's already
shipped cross-launch ShowFps persistence is documented as AP-121 rather than
being attributed to the retail command body.
## Frozen behavior and ordering
1. Construct the controller before `Window.Create` and load Display, Audio,
Gameplay, Chat, and the `default` Character bag exactly once.
2. Resolve the preset plus environment overrides once for the immutable startup
snapshot. `WindowOptions.VSync` comes from the pacing policy initialized with
that snapshot; `WindowOptions.Samples` comes from the same resolved quality.
3. After window/GL input, camera, DAT effects, and optional audio exist, but
before devtools or world/render factories, apply startup settings in this
order: monitor-aware pacing, resolution/fullscreen, field of view, then audio.
4. Downstream terrain, dispatcher, render-range, and streaming factories read
the same startup quality snapshot. The legacy startup stream-radius override
remains at its current composition boundary.
5. Bind one complete `RuntimeSettingsTargets` only after window, UI (when
enabled), terrain atlas, dispatcher, render-range, and streaming exist.
Binding performs no display, quality, UI-lock, or audio replay.
6. Settings-panel Save preserves its callback order: keybindings, Display,
Audio, Gameplay, Chat, Character. Each section retains its existing
persistence/error boundary.
7. Display Save persists first, applies resolution/fullscreen, publishes the
new display snapshot, then reapplies quality. A changed MSAA sample count is
logged as restart-required and is not applied to the live GL context.
8. Runtime quality reapply preserves the existing order: alpha-to-coverage,
terrain anisotropy, render-range publication, streaming reconciliation,
completion budget.
9. `/framerate`, UI lock, loot permits, and combat preferences mutate the
controller's canonical state, synchronize the optional Settings draft using
the existing draft-preservation rules, and retain their existing save-error
behavior.
10. Enter-world changes the active toon key, loads that character bag even when
devtools are disabled, and publishes it to the optional Settings view model.
Session reset restores the startup/default character context before the
later identity-reset operation restores the key to `default`.
11. Per-frame display/audio preview reads the optional Settings draft through a
typed source. Particle range and FPS visibility read the same source so an
unsaved draft still previews exactly as it does today.
12. Shutdown first withdraws the Settings view-model and runtime-target loans;
the controller never disposes the window, renderer, UI, streaming, audio, or
input objects it borrows.
## Types and ownership
- `IRuntimeSettingsStorage` is the test seam for the five settings bags.
`JsonRuntimeSettingsStorage` is the production owner of the one concrete
`SettingsStore` and exposes that store only for retained-window layout
persistence.
- `RuntimeSettingsSnapshot` is the immutable startup value.
- `IRuntimeSettingsStartupTarget` receives the one startup Display/Audio apply.
- `IRuntimeSettingsTargets` receives future display-window, quality, and UI-lock
changes. `BindRuntimeTargets` stores it without invoking it.
- `IRuntimeSettingsPreviewSource` exposes current Display/Audio preview values
and whether a live draft exists.
- `RuntimeSettingsController` owns persistence, current values, active toon,
resolved quality, and the optional `SettingsVM` binding. It also implements
the combat settings source; no second gameplay-state mirror remains.
- `SilkRuntimeDisplayWindowTarget`, `RuntimeSettingsStartupTargets`, and
`RuntimeSettingsTargets` are App-layer adapters over borrowed runtime objects.
## Automated gates
- Construction loads every bag once and populates one immutable snapshot.
- Startup apply is ordered, exactly once, and independent of GL in controller
tests.
- Runtime target binding produces zero calls; unbinding prevents future target
calls while state and persistence still advance.
- Display Save ordering, quality transition, and restart-required MSAA logging.
- Quality target receives one complete resolved value per reapply.
- Draft preview/cancel, external FPS mutation, UI lock, loot permits, and combat
option synchronization.
- Toon activate/load/reset/save behavior with and without a Settings view model.
- Save failures preserve each established state/target boundary.
- Source-boundary test proves `GameWindow` no longer constructs or loads a
`SettingsStore`, owns persisted settings fields, or contains quality/display
feature bodies.
- Focused App tests, full App tests, Release solution build, and full Release
suite.
## Review and commit gate
After the focused tests pass, the existing retail-conformance,
architecture/integration, and adversarial reviewers inspect the complete diff
in parallel. Confirmed findings are corrected and the same reviewers repeat
until clean. Documentation, roadmap, issues, memory, `CLAUDE.md`, and
`AGENTS.md` are reconciled in the same bisectable Checkpoint G commit.
## Implementation result
- `RuntimeSettingsController` is constructed before `Window.Create`; all five
bags load once and `WindowOptions` consumes its immutable startup snapshot.
- Startup substeps commit independently, so an audio failure retries audio
without replaying already successful pacing/display/FOV work.
- Late target binding is inert and reversible; expected-instance SettingsVM
withdrawal cannot detach a replacement binding.
- The concrete quality adapter preserves all five application steps and the
unchanged MSAA restart boundary.
- Combat changes merge their three fields into a live Gameplay draft rather
than overwriting unrelated unsaved fields; successful external changes also
advance the SettingsVM persisted baseline so Cancel cannot resurrect stale
command state.
- UI-lock target, persistence, and baseline publication form one explicit
convergence state. A transient failure leaves the same requested value
retryable instead of letting the equality fast-path strand unfinished work.
- Enter/reset/toon changes load and publish Character state independently of
developer-tool availability.
- `GameWindow` no longer constructs or loads `SettingsStore`, owns persisted
settings mirrors, or contains display/quality/settings feature bodies.
- Candidate metrics: 3,663 raw lines / 162 fields / 37 methods, down 394 lines /
36 fields / 17 methods from Checkpoint F and 12,060 lines (76.7%) from the
campaign baseline.
- Thirty-five focused App settings/boundary tests, all 43 `SettingsVMTests`, the
UI Abstractions Release suite (534 pass), and the App Release suite (3,183
pass / 3 intentional skips) pass. All three independent corrected-diff
reviews are clean. The production App Release build has zero warnings/errors;
the solution Release build retains the 17 existing #228 test-project warnings;
and the complete Release suite passes 7,553 tests / 5 intentional skips.

View file

@ -25,7 +25,7 @@ audit.
resize behind symmetric named subscriptions.
- [x] F — extract the sole gameplay input-action router plus focused combat and
diagnostic command owners.
- [ ] G — extract two-phase persisted settings/display/quality ownership.
- [x] G — extract two-phase persisted settings/display/quality ownership.
- [ ] H — give terrain atlas, sky shader, retained `UiHost`, and both frame roots
explicit single ownership and transfer seams.
- [ ] I — group `OnLoad` into small ordered, fakeable composition phases with
@ -557,6 +557,29 @@ fields / 54 methods.
- Preserve restart-required quality behavior and test transitions/previews
without requiring GL.
**Implementation result:** one pre-window `RuntimeSettingsController` now owns
the concrete settings store, the immutable startup snapshot, current Display /
Audio / Gameplay / Chat / Character values, the active toon, resolved quality,
and the optional Settings draft. Startup applies pacing, window state, saved
FOV, and audio in order exactly once; a failed later substep retries without
replaying a successful earlier substep. Complete runtime targets bind only
after their borrowers exist and binding performs no replay. Display saves keep
the persistence → live window → snapshot → quality order; quality applies
alpha-to-coverage → anisotropy → render range → streaming → completion budget.
Combat preference changes merge only their three fields into a live draft, so
unrelated unsaved Gameplay edits survive. Saved FOV now applies without
devtools, correcting the old accidental SettingsVM gate. `/framerate` retains
the named-retail live toggle/notice and keeps acdream's shipped cross-launch
persistence as documented AP-121. UI-lock convergence requires target,
persistence, and Settings baseline publication; a failed substep keeps the
same-value command retryable. `GameWindow` is 3,663 raw lines / 162 fields / 37
methods. Thirty-five focused App settings/boundary tests, all 43
`SettingsVMTests`, the UI Abstractions Release suite (534 pass), and the App
Release suite (3,183 pass / 3 intentional skips) pass. All three independent
corrected-diff reviews are clean; the production App Release build has zero
warnings/errors, and the complete Release suite passes 7,553 tests / 5
intentional skips (17 existing test-project warnings remain tracked by #228).
### H — explicit single resource ownership and frame slots
- Publish TerrainAtlas and the dedicated sky Shader as separate lifetime roots;