Move the exact retryable shutdown manifest, typed root snapshot, terminal reporting, and native-window-last release out of GameWindow. Keep session and GPU convergence as hard barriers while reporting persistent physical callback cleanup without stranding dependent owners. Co-authored-by: Codex <codex@openai.com>
180 lines
9.2 KiB
Markdown
180 lines
9.2 KiB
Markdown
# GameWindow Slice 8 Checkpoint J — lifetime and shutdown
|
|
|
|
**Status:** Complete 2026-07-22.
|
|
**Parent:**
|
|
[`2026-07-22-gamewindow-slice-8-composition-lifecycle.md`](2026-07-22-gamewindow-slice-8-composition-lifecycle.md),
|
|
Checkpoint J.
|
|
**Integrated baseline:** `530b4bd8`; Checkpoint I is complete, the production
|
|
startup path uses the tested nine-phase pipeline, `GameWindow.cs` is 1,945 raw
|
|
lines, App Release passes 3,431 tests / 3 skips, and the complete Release suite
|
|
passes 7,803 tests / 5 skips.
|
|
**Behavior rule:** preserve the accepted disconnect, reset, callback cutoff,
|
|
frame withdrawal, GL dependency, and native-window-last order. This checkpoint
|
|
changes lifetime ownership and failure reporting, not gameplay behavior.
|
|
|
|
**Result:** `GameWindowLifetime` now owns the immutable typed root snapshot,
|
|
retryable staged transaction, structured terminal report, and native window.
|
|
Persistent physical-detach failures are retried and reported without stranding
|
|
later owners; session convergence and GPU drain remain hard barriers. Clean
|
|
native release drops the completed transaction/root graph, while terminal
|
|
abandonment retains it. `GameWindow.cs` is 1,625 raw lines, down 14,098 lines
|
|
(89.7%) from the campaign baseline. The App Release gate passes 3,441 tests / 3
|
|
skips; the complete Release gate passes 7,813 / 5 skips; the solution build has
|
|
only #228's 17 existing test warnings. Three corrected-diff review passes are
|
|
clean and no retail-divergence row changed.
|
|
|
|
## 1. Outcome
|
|
|
|
Replace `GameWindow.CreateShutdownTransaction` and the window-owned transaction
|
|
field with one pre-window `GameWindowLifetime`. The lifetime owns:
|
|
|
|
- the single staged `ResourceShutdownTransaction`;
|
|
- the first immutable typed shutdown-root snapshot;
|
|
- the native window root once `Window.Create` succeeds;
|
|
- explicit `Active`, `RetryableIncomplete`, `Complete`,
|
|
`CompleteWithCleanupFailures`, and `AbandonedIncomplete` state;
|
|
- one immutable report returned by every terminal repeated call.
|
|
|
|
`GameWindow.OnClosing` performs a synchronous non-terminal attempt while the GL
|
|
context is current. `GameWindow.Dispose` retries the retained transaction and
|
|
then releases the native window. A persistent hard barrier forces the named,
|
|
logged last-resort native fallback and terminal abandonment; it never reports a
|
|
clean shutdown.
|
|
|
|
## 2. Typed ownership boundary
|
|
|
|
`GameWindowShutdownRoots` is a teardown-only aggregate of cohesive records, not
|
|
a runtime service locator. It exposes no gameplay operations and is captured
|
|
exactly once when shutdown first starts:
|
|
|
|
1. `IngressShutdownRoots` — quiescence, command/input logical cutoff, physical
|
|
callback owners, session controller, and native callback binding.
|
|
2. `FrameShutdownRoots` — atomic frame publication, frame/session bindings, and
|
|
UI/session late bindings.
|
|
3. `LiveShutdownRoots` — retained UI, interaction/streaming owners, canonical
|
|
live runtime, hook/effect/audio owners, and effect state.
|
|
4. `RenderShutdownRoots` — GPU-flight barrier, private/render frontends,
|
|
texture/mesh/render resources, dedicated atlas/sky roots, and construction
|
|
cleanup.
|
|
5. `PlatformShutdownRoots` — DAT mapping, input context, and GL.
|
|
|
|
The records retain exact object identities. Operations call those identities
|
|
directly; no stage stores `GameWindow`, a callback facade into it, a dictionary,
|
|
or an untyped service provider. The window may retain borrowed fields until the
|
|
host itself dies, but only `GameWindowLifetime` drives release after capture.
|
|
|
|
## 3. Transaction policy
|
|
|
|
Extend `ResourceShutdownOperation` with a defaulted policy:
|
|
|
|
```csharp
|
|
ResourceShutdownOperationPolicy.HardBarrier
|
|
ResourceShutdownOperationPolicy.ReportAndContinue
|
|
```
|
|
|
|
Existing call sites remain hard by default. All operations in a stage are
|
|
attempted independently. Successful operations never replay. A hard failure
|
|
remains pending and protects later stages. A report-and-continue operation is
|
|
retried once; a second failure becomes a structured cleanup failure, marks that
|
|
operation settled, and allows the stage to converge. The transaction exposes
|
|
its current stage and immutable cleanup failures without swallowing exceptions.
|
|
|
|
Only physical ingress removal is soft. Logical quiescence, live-session
|
|
convergence, frame withdrawal, owner disposal, GPU drain, DAT/input/GL release,
|
|
and all dependency barriers remain hard.
|
|
|
|
## 4. Frozen stage order
|
|
|
|
The manifest outside `GameWindow` uses this exact order:
|
|
|
|
1. `host and session barriers`
|
|
- stop accepting callbacks;
|
|
- deactivate combat/diagnostic/retained/gameplay/pointer/dispatcher/device
|
|
paths;
|
|
- quiesce retained/devtools input;
|
|
- dispose the live-session controller and require converged disposal.
|
|
2. `physical ingress cleanup` — settings VM, retained/gameplay/pointer/
|
|
dispatcher/device bindings, retained/devtools input, and native window
|
|
callbacks; every operation is report-and-continue.
|
|
3. `frame borrowers` — withdraw the exact atomic frame pair, then frame-root and
|
|
session/player bindings.
|
|
4. `session dependents` — late bindings, mouse presentation, retained UI,
|
|
combat/item/container owners, streamer, and equipped children.
|
|
5. `live entities` — clear canonical runtime while teardown callbacks live.
|
|
6. `effect dispatch edges` — live bindings, effect advance, hook registrations.
|
|
7. `live entity dependents` — lights, presentation, remote teleport, effect
|
|
state, and audio.
|
|
8. `submitted GPU work` — hard wait barrier.
|
|
9. `render frontends`.
|
|
10. `shared texture owners`.
|
|
11. `mesh adapter`.
|
|
12. `remaining render owners`.
|
|
13. `dedicated render resources` — sky shader then terrain atlas.
|
|
14. `failed render construction cleanup`.
|
|
15. `frame flight owner`.
|
|
16. `content mappings`.
|
|
17. `input context`.
|
|
18. `OpenGL context`.
|
|
19. Native window release/fallback outside the transaction and last.
|
|
|
|
The former late native-callback operation moves to stage 2. Logical cutoff makes
|
|
copied callbacks inert before the potentially long session close, so a broken
|
|
event remove cannot prevent F653/transport teardown or strand unrelated owners.
|
|
|
|
## 5. State and reporting
|
|
|
|
`GameWindowLifetimeReport` contains status, blocked stage, structured soft
|
|
cleanup failures, and the retained hard/native error when present.
|
|
|
|
- `TryComplete(roots)` captures roots once and never performs native fallback.
|
|
It is used by `OnClosing`.
|
|
- `CompleteAndReleaseNativeWindow(roots)` retries hard work, then disposes the
|
|
native window. If hard convergence still fails, it retains the blocked-stage
|
|
report, marks eligible partial owners abandoned, releases the native fallback,
|
|
and becomes `AbandonedIncomplete`.
|
|
- Clean transaction + no soft failures becomes `Complete`.
|
|
- Clean transaction + soft failures becomes `CompleteWithCleanupFailures`.
|
|
- Reentrant completion is inert while the outer call owns progress.
|
|
- Every terminal repeated call returns the same report and touches no owner.
|
|
- Native window publication is single-owner, pre-Run-safe, and released once.
|
|
|
|
## 6. Implementation sequence
|
|
|
|
1. Add operation policy, structured cleanup-failure exposure, and focused
|
|
transaction tests for transient/persistent soft cleanup, mixed hard/soft
|
|
stages, retry/no-replay, and reentrancy.
|
|
2. Add `GameWindowLifetime`, report/state types, typed root records, and the
|
|
manifest factory. Test every stage trace using fakes, optional-null roots,
|
|
persistent session/GPU barriers, persistent soft detach, clean/native-failure
|
|
finalization, terminal abandonment, and repeated/reentrant calls.
|
|
3. Publish the native window immediately after creation; replace the window
|
|
transaction/manifest with one typed root capture and the two narrow lifetime
|
|
calls. Remove all shutdown feature bodies and update source-shape tests to
|
|
inspect the focused owner.
|
|
4. Exercise constructed-never-run, Load-never-fired, partial composition
|
|
prefixes, normal close then Dispose, direct Dispose, transient retry, and
|
|
optional audio/devtools/retained-UI absence.
|
|
5. Run behavior-order, architecture/ownership, and adversarial failure review
|
|
passes; correct every finding. Run focused tests, App Release, solution
|
|
Release build, and complete Release tests.
|
|
6. Reconcile architecture, roadmap, milestones, issues, AGENTS/CLAUDE, and
|
|
durable memory; commit Checkpoint J as one bisectable ownership unit.
|
|
|
|
## 7. Acceptance
|
|
|
|
- No shutdown stage or substantial release body remains in `GameWindow`.
|
|
- No lifetime/manifest type stores `GameWindow` or a general callback facade.
|
|
- Session and GPU hard failures never run protected dependent stages.
|
|
- Persistent physical-detach failure does not strand frame/live/render/content/
|
|
input/GL owners and produces `CompleteWithCleanupFailures`.
|
|
- Both frame roots become unreachable before any borrower retires.
|
|
- Every acquired owner releases once; absent owners are no-ops; failed owners
|
|
retry without replaying completed work.
|
|
- Direct, repeated, concurrent-safe/reentrant, close-then-dispose, and every
|
|
partial-load path are deterministic.
|
|
- Native window release is last. Hard non-convergence becomes named terminal
|
|
abandonment; repeated calls are inert and preserve the same report.
|
|
- App production builds without warnings; only #228's 17 test warnings remain;
|
|
focused, App, and complete Release suites pass.
|
|
- No retail-divergence row is added unless review discovers an actual behavior
|
|
change; this ownership correction itself is behavior-preserving.
|