refactor(lifetime): own render resource prefixes

Give terrain, sky, retained UI, portal preparation, and the update/render frame pair explicit single owners. Make shader, texture, text, bindless, and GL construction prefixes checked and retryable so partial failure cannot lose or replay resource ownership.

Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-22 14:42:14 +02:00
parent fec0d94148
commit c87b15303d
41 changed files with 3768 additions and 355 deletions

View file

@ -829,15 +829,18 @@ Current movement/collision ownership:
routers; Slice 4 moved hydration, teardown, inbound authority, network
updates, and reusable motion-runtime policy over canonical
`LiveEntityRuntime`. Slices 57 complete landblock presentation and the
update/render frame graphs. Slice 8 checkpoints AG now own the native
update/render frame graphs. Slice 8 checkpoints AH now own the native
callback shell, live-session composition, world environment, reversible
input/pointer lifetime, physical framebuffer publication, the sole gameplay
action-priority graph, typed commands, the retained-root item-drop edge, and
two-phase runtime settings. `RuntimeSettingsController` is the sole settings
two-phase runtime settings, sole terrain/sky render-resource lifetime,
retained Host/runtime lease, atomic frame-root pair, and prepared portal
fallback/transfer. `RuntimeSettingsController` is the sole settings
persistence/current-state owner, supplies the immutable pre-window snapshot,
applies startup pacing/display/FOV/audio once, and later borrows complete
runtime targets without replay. Explicit resource ownership and frame slots
are the next checkpoint. The reconciled ledger and sequence live in
runtime targets without replay. Checked GL construction/state boundaries
retain exact cleanup obligations across failure. Ordered production
composition is the next checkpoint. The reconciled ledger and sequence live in
`docs/architecture/code-structure.md`. This decision does not reclassify the
remaining L.2 conformance work or Track LH.

View file

@ -694,7 +694,7 @@ slices. `OnClosing` delegates to the existing retryable shutdown transaction.
Silk callbacks become narrow calls into the input, update, render, resize,
focus, and shutdown owners.
Checkpoints E, F, and G are complete. `CameraPointerInputController` owns raw pointer,
Checkpoints E, F, G, and H are complete. `CameraPointerInputController` owns raw pointer,
camera cursor/focus/scroll/sensitivity behavior; `FramebufferResizeController`
alone publishes physical framebuffer changes. All Silk, dispatcher, retained
UI, and optional devtools device edges are reversible, transactional, and
@ -715,10 +715,16 @@ then borrows late-bound runtime targets without replay. Saved FOV now applies at
startup even when devtools are disabled; this is a deliberate correction of the
old accidental SettingsVM gate. The controller also preserves unrelated unsaved
Gameplay draft fields when combat preferences change. `GameWindow` is now 3,663
raw lines / 162 fields / 37 methods. Checkpoints HL remain active, with explicit
resource ownership and frame slots next. The Checkpoint G production App build
is warning-free, all three corrected-diff reviews are clean, and the complete
Release suite passes 7,553 tests / 5 intentional skips.
raw lines / 162 fields / 37 methods at G. H adds sole lifetime roots for the
terrain atlas and dedicated sky shader, one retained Host/runtime lease, an
atomic update/render frame-root slot, and a prepare-aware portal fallback and
transfer slot. GL construction and state mutations now use checked commit
boundaries with exact retry ownership for failed names, bindless residency,
and texture-binding restoration. `GameWindow` is 3,689 raw lines / 162 fields /
37 methods. Checkpoints IL remain active, with ordered production composition
next. The Checkpoint H production App build is warning-free, its 61 focused
ownership tests pass, the complete Release suite passes 7,606 tests / 5 skips,
and all three final corrected-diff reviews are clean.
### 4.4 Exit criteria

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 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.
**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 AH 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 AG now own
Final Slice 8 composition/shutdown cleanup is active. Checkpoints AH 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,
@ -94,10 +94,15 @@ retained-root item-drop binding. `GameWindow` is now 4,057 lines / 198 fields /
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.
`GameWindow` is 3,663 lines / 162 fields / 37 methods at G. Checkpoint H adds
sole terrain-atlas and sky-shader roots, one retained Host/runtime lease, an
atomic update/render frame-root pair, prepared portal fallback/transfer, and
checked retryable GL leaf construction and state mutation. `GameWindow` is now
3,689 lines / 162 fields / 37 methods. Ordered composition, shutdown, canonical
soak snapshots, and closeout checkpoints IL remain. Checkpoint H passes 61
focused ownership tests and 3,236 App tests / 3 intentional skips; the complete
Release suite passes 7,606 tests / 5 skips and all three final 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 AG are complete:
preserve Slice 6 presentation. Final Slice 8 checkpoints AH 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,
@ -50,10 +50,15 @@ gameplay action-priority owner, typed combat/diagnostic/general commands, and a
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
lines / 162 fields / 37 methods at G. Checkpoint H adds sole terrain/sky
resource roots, one retained Host/runtime lease, an atomic update/render root
pair, a prepared portal fallback/transfer transaction, and checked retryable GL
leaf construction. `GameWindow` is 3,689 lines / 162 fields / 37 methods.
Checkpoints IL 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.
threshold. Checkpoint H passes 61 focused ownership tests and 3,236 App tests /
3 skips; the complete Release suite passes 7,606 tests / 5 skips and all three
final corrected-diff reviews are clean.
Carried:
#153 far-teleport residual, #116 slide-response, the remaining R6 ownership cleanup plus

View file

@ -0,0 +1,208 @@
# GameWindow Slice 8 — Checkpoint H Resource Ownership Plan
## Objective
Replace the remaining implicit, constructor-local, and nullable-field resource
ownership in `GameWindow` with explicit single-owner transactions. This is a
behavior-preserving lifetime cutover: draw/update order, portal presentation,
retained UI behavior, settings behavior, and the frozen shutdown order do not
change.
Checkpoint H does not move the complete shutdown manifest; that is Checkpoint
J. It supplies the typed roots and retry semantics that J will consume.
## Frozen invariants
1. Every GL/kernel-owning object is published to exactly one owner immediately
after successful construction.
2. Renderers borrow the terrain atlas and dedicated sky shader. They never
dispose those resources. The renderer borrower retires before the sole
resource owner releases the borrowed atlas/shader.
3. A leaf factory that throws before returning must release every GL name it
created. Outer phase/lifetime rollback cannot recover a name that was never
published.
4. Bindless residency is a resource prefix: partial handle acquisition rolls
back, successful non-residency is not replayed after a later failure, and
textures are not deleted while a resident handle remains.
5. Retained UI input is quiesced before the live-session barrier and physically
detached afterward, independently of runtime/host disposal.
6. Exactly one retained-UI ownership chain exists. Before runtime construction
the lease owns the `UiHost`; after a runtime object is published the lease
retains that exact partial/full runtime and delegates final disposal to it.
Constructor or initialization failure never leaves a host in a local only.
7. Update and render roots publish atomically as one frame graph. Silk callbacks
resolve only through the slot; an empty/withdrawn slot is safely inert.
8. Portal tunnel ownership starts in one fallback slot. Transfer clears the
fallback only after the complete local-teleport controller factory returns;
any earlier failure leaves the same tunnel reachable by shutdown.
9. Successful disposal is never replayed. Failed disposal remains retryable.
Terminal abandonment is explicit and retains references for native/process
fallback rather than silently losing ownership.
## Typed owners
### `OwnedResourceSlot<T>` and `GameRenderResourceLifetime`
- `OwnedResourceSlot<T>` acquires through a factory, rejects duplicate
publication, exposes a typed borrower, and clears ownership only after
`Dispose` succeeds.
- `GameRenderResourceLifetime` contains separate slots for `TerrainAtlas` and
the dedicated sky `Shader`.
- `GameWindow` acquires both through this owner. Shutdown releases the sky
renderer and terrain renderer first, then the two resource slots, then GL.
### `RetailUiRuntimeLease`
- The lease exists before `OnLoad` and acquires the `UiHost` through a factory.
- Runtime construction and initialization are separate. The runtime is stored
in the lease before initialization begins.
- Initialization failure attempts immediate cleanup. Successful cleanup
rethrows the original initialization error; cleanup failure reports both and
leaves the exact runtime in the lease for lifetime retry.
- The lease exposes idempotent `QuiesceInput` and retryable `DeactivateInput`
operations against the retained host.
- If no runtime was constructed, lease disposal directly disposes the host. If
a runtime exists, runtime disposal remains the one path that disposes the
host. There are never two host disposers.
- Explicit terminal abandonment is allowed only after a failed disposal pass;
it makes later disposal inert while retaining the unresolved references for
the native/process fallback handled by Checkpoint J.
### `GameFrameGraphSlot`
- `IGameUpdateFrameRoot` and `IGameRenderFrameRoot` are the narrow frame seams.
- `UpdateFrameOrchestrator` and `RenderFrameOrchestrator` implement those seams.
- `Publish(update, render)` is atomic and one-shot until withdrawal.
- `Tick`/`Render` are inert before publication and after withdrawal.
- Shutdown withdraws the pair only after live-session convergence and before
any frame borrower retires.
### `TransferableResourceSlot<T>`
- `Acquire` owns one fallback resource.
- `Transfer(factory)` retains fallback ownership while the complete destination
factory runs and clears it only after that factory returns successfully.
- Failed acquisition, resource preparation, destination construction, binding,
and normal shutdown prefixes are covered without double disposal.
## Exception-safe GL leaves
### Shader program construction
`ShaderProgramConstruction` uses a narrow `IShaderProgramBuildApi`. It tracks
the vertex shader, fragment shader, and linked program as soon as each name is
returned. Compile, attach, link, detach, or cleanup failure attempts every
remaining rollback operation. A clean rollback preserves the original failure;
rollback failures are aggregated with it. `Shader.Dispose` becomes idempotent
and retryable.
### Terrain atlas construction and residency
`GlTextureConstructionTransaction` tracks every texture name allocated by
terrain, alpha, and fallback paths. `TerrainAtlas.Build`, `BuildAlphaAtlas`, and
`BuildFallback` allocate only through that transaction. The transaction commits
only after the complete `TerrainAtlas` object exists; failure rolls back all
names in reverse acquisition order and reports cleanup failures with the
original error.
`BindlessTexturePair` owns the terrain/alpha residency pair. Partial acquisition
releases the first handle. Release attempts both handles, remembers successful
substeps, and is retryable. `TerrainAtlas.Dispose` releases residency first,
then independently deletes both texture names through the existing retryable
shutdown transaction.
## Production integration order
1. Construct render-resource lifetime, UI lease, frame slot, and portal
fallback slot before `Window.Create`/`OnLoad` acquisitions.
2. Acquire the atlas through `GameRenderResourceLifetime`; construct
`TerrainModernRenderer` as a borrower.
3. Acquire `UiHost` through `RetailUiRuntimeLease`, wire input/assets, construct
and publish `RetailUiRuntime` before initialization, then expose host/runtime
only as borrower aliases.
4. Acquire/prepare portal presentation in the fallback slot.
5. Acquire the dedicated sky shader through the render-resource lifetime, then
construct `SkyRenderer` as a borrower.
6. Transfer the portal fallback only through the complete
`LocalPlayerTeleportController` factory.
7. Construct update/render roots as locals and publish them atomically through
`GameFrameGraphSlot`.
8. Update/render callbacks resolve only through the slot.
9. Shutdown quiesces/deactivates UI through the lease, withdraws frame roots,
disposes the UI lease and renderer borrowers, releases atlas/sky roots, then
reaches GL.
## Automated gates
- owned-resource acquire/borrow/release, duplicate acquire, transient failure,
retry, and no replay;
- UI host acquisition, runtime constructor failure, Initialize failure with
successful cleanup, transient cleanup then lifetime retry, persistent cleanup
then explicit abandonment, separate input quiescence/deactivation, and exact
host disposal count;
- frame pair atomic publication, duplicate publication, update/render routing,
withdrawal, pre-publication/post-withdrawal silence, and republish policy;
- portal fallback acquisition/preparation/transfer failures plus successful
transfer and every shutdown prefix, with exact one-resource disposal;
- vertex/fragment compile failure, program creation/link failure, and cleanup
failure after every created shader/program name;
- terrain/alpha/fallback texture allocation and upload failure after every
created name, reverse rollback, cleanup aggregation, and committed-name
non-deletion;
- bindless first/second acquisition failure, release partial failure/retry, and
no deletion before residency release;
- source boundary proves direct frame-root fields, constructor-local atlas/sky
ownership, direct UI host/runtime ownership, and nullable portal fallback are
removed from `GameWindow`;
- focused App tests, production App Release build, solution Release build, and
complete Release suite.
## Review and commit gate
The existing architecture/integration, retail-conformance, and adversarial
reviewers inspect the complete tracked and untracked diff independently. Every
confirmed finding is corrected and all three repeat until clean. Architecture,
roadmap, milestones, issues, memory, `CLAUDE.md`, and `AGENTS.md` are reconciled
in the same bisectable Checkpoint H commit.
## Implementation result — 2026-07-22
Checkpoint H landed the planned ownership graph without changing the accepted
update/render order or gameplay behavior:
- `GameRenderResourceLifetime` owns the terrain atlas and dedicated sky shader;
renderers borrow them and retire first.
- `RetailUiRuntimeLease` owns Host/runtime construction, initialization, input
cutoff, retryable disposal, and explicit terminal abandonment as one chain.
- `GameFrameGraphSlot` publishes update/render roots atomically and makes empty
or withdrawn callbacks inert.
- `TransferableResourceSlot.AcquirePrepared` publishes the portal presentation
before mesh preparation, retries the same partial resource, and refuses
transfer until preparation has committed.
- GL name creation records the returned name before post-command validation.
Shader/program, texture, text-renderer, buffer/VAO, bindless-residency, and
texture-binding operations advance ownership only after an always-on checked
GL commit boundary.
- Failed construction cleanup is retained by `GlConstructionCleanupLedger`;
exact pending names and binding-restore obligations remain retryable, while
successful cleanup stages never replay.
Four corrected-diff cycles closed issues found by the architecture, retail,
and adversarial reviewers: partial bindless mutation, lost GL rollback names,
constructor-local text resources, reentrant UI ownership, portal preparation,
unchecked production GL calls, leaked texture binding, and the bindless post-
commit verification gap. All three final reviews are clean.
Automated acceptance:
- focused Checkpoint H ownership/lifetime gate: 61 passed;
- App Release suite: 3,236 passed / 3 intentional skips;
- production App Release build: zero warnings and zero errors;
- complete solution Release suite: 7,606 passed / 5 intentional skips; solution
build has zero errors and the 17 existing test-project warnings tracked by
#228;
- `GameWindow.cs`: 3,689 raw lines / 162 fields / 37 methods, versus 15,723 /
278 / 205 at the campaign baseline.
No AC-specific algorithm or intended retail behavior changed, so Checkpoint H
adds no retail-divergence row. Checkpoint I is the next active unit.

View file

@ -26,7 +26,7 @@ audit.
- [x] F — extract the sole gameplay input-action router plus focused combat and
diagnostic command owners.
- [x] G — extract two-phase persisted settings/display/quality ownership.
- [ ] H — give terrain atlas, sky shader, retained `UiHost`, and both frame roots
- [x] 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
transactional partial-acquisition rollback.
@ -598,6 +598,22 @@ intentional skips (17 existing test-project warnings remain tracked by #228).
Inject GL compile/link/texture-allocation failure after every internal name
and prove exact rollback/residency release.
Checkpoint H is complete. `GameRenderResourceLifetime` is the sole terrain-
atlas and dedicated-sky-shader owner; renderers borrow and retire before those
roots. `RetailUiRuntimeLease` retains the Host/runtime chain through partial
construction, input cutoff, retryable disposal, and explicit terminal
abandonment. `GameFrameGraphSlot` publishes update/render roots atomically, and
the portal tunnel now uses one prepare-aware fallback/transfer transaction.
Shader, terrain, text-renderer, buffer/VAO, bindless, and texture-binding
construction/mutation paths use always-on checked GL commit boundaries. A
failed cleanup retains the exact name/handle/binding obligation for retry, and
successful substeps never replay. `GameWindow` is 3,689 raw lines / 162 fields
/ 37 methods. The focused ownership gate passes 61 tests, the App Release suite
passes 3,236 tests / 3 intentional skips, and all three independent final
corrected-diff reviews are clean. The complete Release suite passes 7,606 tests
/ 5 intentional skips; the 17 existing test-project warnings remain tracked by
#228.
### I — ordered production composition
- Split the 2,289-line `OnLoad` body at §3.6 boundaries using the fixed typed