acdream/docs/plans/2026-07-22-gamewindow-slice-8-checkpoint-k-canonical-soak.md
Erik 5c955c36ec docs(architecture): record GameWindow automated closeout
Reconcile the architecture, roadmap, milestones, issue ledger, session instructions, and durable memory with the 1,622-line native shell, canonical soak evidence, connected framebuffer comparison, and sole remaining user visual gate.

Co-authored-by: Codex <codex@openai.com>
2026-07-22 20:52:25 +02:00

11 KiB
Raw Blame History

GameWindow Slice 8 Checkpoint K — canonical soak snapshots

Status: Complete 2026-07-22 (bca41487, corrected gate semantics 6c5e0604). Parent: 2026-07-22-gamewindow-slice-8-composition-lifecycle.md, Checkpoint K. Integrated result: GameWindow.cs is 1,622 raw lines, App Release passes 3,451 tests / 3 skips, and the complete Release suite passes 7,823 tests / 5 skips. Issue: #232. Behavior rule: this checkpoint changes diagnostic capture and connected-gate evidence only. It does not change rendering, streaming, gameplay, resource budgets, or the existing process working/private-memory thresholds.

1. Outcome

Make every scripted checkpoint <name> a deferred, acknowledged render-frame barrier. A checkpoint is written only after private presentation and the normal render diagnostics phase have both observed the same immutable RenderFrameOutcome. Its JSON contains the canonical world/reveal/resource owner snapshot and that exact frame outcome. The script cannot execute the next command until serialization and both writes succeed, fail, or are explicitly cancelled during shutdown.

The nine-stop soak then consumes exactly nine ordered checkpoints, gates canonical owner stability at the Caul return → Caul plateau same-location pair, labels server-controlled entity/animation population changes as workload warnings, and retains process residency as an unchanged secondary guard.

2. Runtime contract

2.1 Acknowledgement token

Replace the synchronous automation method with a request contract equivalent to:

enum RetailUiAutomationCheckpointStatus
{
    Pending,
    Succeeded,
    Failed,
    Cancelled,
}

interface IRetailUiAutomationCheckpoint
{
    RetailUiAutomationCheckpointStatus Status { get; }
    string? Error { get; }
}

bool TryRequestCheckpoint(
    string name,
    out IRetailUiAutomationCheckpoint? checkpoint,
    out string error);

The production token also retains the assigned request sequence and validated name across the update → render edge. Only the controller can transition it, exactly once, from Pending to a terminal state.

RetailUiAutomationScriptRunner stores one token together with the active command index. While Pending, repeated ticks return without enqueueing again or advancing the command. Success clears the token and advances once. Failure or cancellation clears it and stops on the token's exact error. Disposal still releases held input; runtime shutdown is responsible for cancelling queued tokens.

2.2 FIFO request owner

WorldLifecycleAutomationController becomes the sole FIFO owner and an IDisposable frame-phase participant:

  • validation failure creates no request and consumes no sequence;
  • accepted requests receive one monotonic sequence and enter one FIFO;
  • a successful render drains accepted requests in order;
  • each request serializes one WorldLifecycleCheckpoint containing its sequence/name, current reveal state, the exact current RenderFrameOutcome, and one canonical resource snapshot;
  • Succeeded is published only after the JSONL append and named JSON write both complete;
  • capture/serialization/I/O failure marks only that request Failed with contextual sequence/name error and does not throw out of rendering;
  • shutdown marks every still-pending request Cancelled with an explicit error;
  • enqueue after shutdown fails synchronously and cannot create an orphan token.

The controller is adopted by FrameRootRuntimeBindings separately from its retained-UI late binding, so the binding detaches and pending requests cancel under the existing retryable frame-root lifetime.

2.3 Post-diagnostics render phase

Add a narrow IRenderFramePostDiagnosticsPhase with a no-op implementation. RenderFrameOrchestrator calls it exactly once after:

  1. resource preparation;
  2. world rendering;
  3. private presentation and screenshots;
  4. IRenderFrameDiagnosticsPhase.Publish;
  5. then checkpoint drain;
  6. finally the existing GPU-flight close.

If an earlier render phase fails, post-diagnostics is not called and the FIFO remains pending for the next successful frame. An unexpected post-phase throw uses the existing render failure/recovery path, although production capture and I/O failures are represented on tokens rather than thrown. No previous-frame diagnostic value is paired with a current-frame owner snapshot: WorldLifecycleResourceSnapshotSource.Capture(RenderFrameOutcome) takes visible/total landblocks from the supplied outcome and reads the already- published aggregate FPS/frame-time snapshot only after diagnostics runs.

3. Artifact and soak schema

WorldLifecycleCheckpoint adds the exact frame outcome. Existing reveal and resource fields retain their names. The route adds these nine commands in this order at the end of each settled destination block:

  1. caul-baseline
  2. sawato-baseline
  3. rynthid
  4. aerlinthe
  5. sawato-return
  6. holtburg
  7. caul-return
  8. sawato-plateau
  9. caul-plateau

run-connected-r6-soak.ps1 reads the JSONL only after the route completes and requires one row for every expected name, exact sequence 19, exact order, and the current process id. It embeds those records in the report.

Every checkpoint hard-gates zero:

  • pending live teardowns;
  • pending landblock retirements;
  • staged mesh uploads and bytes;
  • composite warmup work.

Every route teleport supplies an explicit identity quaternion so the timed turn begins from a reproducible heading. The same-location Caul return → Caul plateau comparison requires exact equality for the deterministic loaded/total world domain, while visible-landblock and authoritative entity/animation/live- entity changes are named workload warnings. Teardown/retirement queues, staged work, and composite warmup remain hard zero at every checkpoint. Mesh, atlas, tracked-GPU, composite/particle-texture, and VFX owner counts may retire and shrink normally; any growth is a hard leak failure when the visible and authoritative workload is stable, and a named owner-growth warning when that workload changed. Particles, emitters, and active scripts are always transient workload warnings rather than silent exclusions. The report therefore says which canonical owner grew without mistaking legitimate retirement for a leak. Managed used/committed deltas are reported diagnostically. Existing working/private-memory, update-p95, and allocation-p50 thresholds remain byte- for-byte unchanged as the secondary residency/performance guard.

4. Implementation sequence

  1. Add token states and change the automation runtime/script runner to the one-token-per-command polling contract. Cover delayed success, repeated pending ticks, failure, cancellation, invalid request, and disposal while pending.
  2. Refactor WorldLifecycleAutomationController into the FIFO request owner; pass the frame outcome into snapshot capture and write it into artifacts. Cover FIFO order, sequence stability, write failure, no duplicate drain, shutdown cancellation, and enqueue-after-dispose.
  3. Add the post-diagnostics phase to RenderFrameOrchestrator, wire the controller through FrameRootCompositionPhase, and adopt its lifetime separately from the late UI binding. Pin exact phase order, skipped drain on prior failure, post-phase failure recovery, and partial-composition cleanup.
  4. Add the nine route commands. Parse/validate JSONL in the soak, attach the canonical checkpoints to the report, add zero-work and same-location owner gates, and preserve the old process/performance formulas verbatim.
  5. Run behavior/order, architecture/ownership, and adversarial failure review passes. Correct findings and re-run focused App tests, App Release, Release build, and the complete Release suite.
  6. Run two fresh-process connected nine-stop routes before closing #232. Both must exit gracefully, produce exact ordered checkpoints, pass canonical owner/cache gates, and pass the unchanged process residency guard.
  7. Reconcile architecture, roadmap, milestones, issues, AGENTS/CLAUDE, and durable memory; commit Checkpoint K as its own bisectable diagnostic unit.

5. Automated acceptance

  • one checkpoint command creates one request, one sequence, and one artifact;
  • a pending token blocks all later script commands without duplicate enqueue;
  • render failure delays rather than loses or duplicates the request;
  • diagnostics publication precedes same-frame capture;
  • independent accepted requests drain FIFO and retain name/sequence identity;
  • capture/write failure reaches the script as the exact failed-token error;
  • shutdown reaches pending scripts as explicit cancellation;
  • no controller/runtime reference points back to GameWindow;
  • all nine route names and sequences are exact and appear in the soak report;
  • canonical pending/staging/warmup work is zero at every checkpoint;
  • Caul return → plateau deterministic owners/caches are unchanged, while authoritative workload deltas are explicitly warned;
  • working/private-memory thresholds and update/allocation formulas are unchanged;
  • focused, App, and complete Release suites pass with no new warnings.

No visual gate is required for K because it changes diagnostic observation only. Checkpoint L owns the final connected framebuffer and user visual gate.

6. Completion evidence

The focused checkpoint suite passes 74 tests. The Release build succeeds with only the 17 pre-existing test-project warnings tracked by #228. Two independent fresh-process routes produced all nine ordered render-frame checkpoints and closed gracefully:

  • connected-r6-soak-20260722-201335.report.json: 403.139 seconds; Caul return-to-plateau working/private deltas +20.2/+10.3 MiB; update p95 1.5 ms.
  • connected-r6-soak-20260722-202025.report.json: 403.021 seconds; Caul return-to-plateau working/private deltas +165.0/+157.2 MiB; update p95 1.3 ms.

Every checkpoint had zero pending teardown/retirement, staged upload work, and composite warmup work. Neither run found unconfounded canonical owner growth, and both passed the unchanged process-residency and performance limits.

The first connected implementation run also proved that exact cache equality was the wrong leak predicate: visible landblocks and authoritative entities changed while mesh/GPU/cache owner counts decreased through legitimate retirement. The corrected contract therefore hard-fails owner growth when the workload is stable, reports named growth warnings when the authoritative or visible workload changed, and permits shrinking. It does not weaken the process limits or hide an identified owner leak.