acdream/docs/plans/2026-07-20-automated-world-lifecycle-gate.md
Erik e02acb870c docs(runtime): close deterministic world lifecycle gate
Record the passing capped and uncapped connected evidence, coordinator ownership, graceful reconnect contract, current test baseline, and the remaining human-only visual gates. Keep AGENTS.md synchronized with CLAUDE.md and distill the durable reveal-lifecycle lesson into project memory.
2026-07-21 00:00:31 +02:00

177 lines
8.7 KiB
Markdown

# Automated world-lifecycle gate
**Status:** Complete (2026-07-20)
**Owner:** M3 stabilization / R6 rendering-lifecycle rebaseline
**Depends on:** `WorldRevealReadinessBarrier`, retained-UI automation,
`FrameProfiler`, connected local ACE
## Outcome
Create a deterministic connected Release gate that catches the structural
failures which previously required someone to stare at the client for several
minutes:
- the first login frame cannot expose world geometry before the destination's
render meshes, composite textures, and collision data are all ready;
- every portal/recall lifetime crosses the same readiness edge, materializes
once, and returns to the normal viewport once;
- dungeon entry and exit use the same lifecycle without grey frames,
stale-source geometry, or an uncollidable destination;
- graceful logout followed by a fresh reconnect starts a new reveal lifetime
and cannot inherit readiness or resources from the previous process;
- repeated destinations do not cause cumulative memory, owner-count,
allocation, update-time, or GPU-time growth;
- each stable checkpoint produces machine-readable state plus a screenshot for
later human comparison.
This is a correctness and resource-lifetime gate. Screenshots preserve visual
evidence, but automation does **not** claim retail visual equivalence. Color,
composition, animation feel, and subtle geometry artifacts remain user gates.
## Canonical runtime contract
`WorldRevealReadinessBarrier` remains the only definition of destination
readiness. It will expose a structured snapshot rather than forcing telemetry
to repeat the readiness formula. A snapshot records the destination cell,
indoor/outdoor classification, required radius, render publication, composite
texture readiness, collision readiness, an impossible/unhydratable claim, and
the final `Ready` result.
A focused App-layer `WorldRevealLifecycleTelemetry` owns diagnostic reveal
generations. It has no GL calls and no knowledge of `GameWindow`. For each
login or portal generation it records:
1. begin;
2. destination acquisition;
3. each distinct readiness transition;
4. the first frame where the normal world viewport is actually eligible to
draw;
5. completion or cancellation.
Making the normal viewport visible before `Ready` is a hard invariant failure,
not a warning. A newer generation retires the older one, so stale asynchronous
completion cannot satisfy the new destination.
`GameWindow` only supplies facts and invokes the controller at existing
lifecycle seams. It does not gain a new feature body.
## Production automation seam
The retained-UI automation runner gains runtime commands through an injected
interface:
- `wait world-ready [timeout-ms]` waits on canonical lifecycle state instead
of a guessed sleep;
- `wait materialized <occurrence> [timeout-ms]` waits for the authoritative
portal completion count;
- `checkpoint <name>` emits one JSON record containing reveal state,
landblock/entity/animation and resource-owner counts, managed memory, and
the latest frame-profiler summary;
- `screenshot <name> [timeout-ms]` queues a default-framebuffer capture after
the complete world and retained UI have drawn, then waits for the render
thread to finish the PNG;
- existing `input` commands perform deterministic turns and movement through
`InputDispatcher`.
All state mutation and GL readback stay on the update/render thread. The
external gate only launches the process, observes files/logs and OS process
metrics, and requests normal `WM_CLOSE` shutdown.
## Connected route
The gate runs two capped/uncapped-aware sessions against local ACE:
1. fresh login → wait for canonical readiness → checkpoint + screenshot;
2. outdoor dense scene → turn → stable checkpoint + screenshot;
3. world-edge scene → turn → stable checkpoint;
4. known dungeon destination → checkpoint + screenshot → local movement;
5. return outdoors → checkpoint + screenshot;
6. revisit the first dense scene → stable resource comparison;
7. graceful close;
8. reconnect the same account → repeat the fresh-login readiness and
screenshot gate → graceful close.
The existing seven-destination R6 soak remains the longer performance route.
The lifecycle gate is shorter and more diagnostic; it composes with that soak
rather than replacing it.
## Hard failures
- world viewport observed before the active reveal snapshot is ready;
- destination never reaches readiness or materialization within the bounded
connected test timeout;
- missing or duplicate reveal/materialization/completion edges;
- missing checkpoint JSON or screenshot, zero-sized/corrupt screenshot;
- absent required destination landblock/EnvCell/collision readiness;
- retained per-owner resources after delete/session teardown in deterministic
tests;
- same-location resource counts or memory/per-frame allocation grow beyond the
route's documented relative tolerance;
- unhandled exception, access violation, OOM, GL/device loss, disconnect,
unexpected WeenieError, nonzero exit, or failure of graceful shutdown.
Absolute FPS is recorded, never compared across local and RDP sessions. The
gate compares like-for-like samples within one process and reports CPU and GPU
frame time separately.
## Implementation sequence
1. Add a structured readiness snapshot and exhaustive barrier tests.
2. Add lifecycle telemetry as a pure App owner with stale-generation,
cancellation, duplicate, and early-visible tests.
3. Add the automation runtime interface, deterministic waits, checkpoints, and
screenshot request/completion protocol.
4. Add the render-thread screenshot owner and structured resource snapshots.
5. Wire the owners at login, F751 portal start, TAS placement/completion, world
draw eligibility, and session teardown.
6. Add the connected route and PowerShell report/gate orchestration.
7. Run capped and uncapped Release gates, fix root causes, and retain artifacts.
8. Extract the now-protected streaming/portal/reveal frame orchestration from
`GameWindow` without changing the accepted lifecycle trace.
9. Run focused tests, full Release build/test, connected gates, graceful-close
verification, and documentation reconciliation.
## Completion evidence
- Focused App tests cover readiness composition, stale generations,
cancellation, duplicate lifecycle edges, early-visible rejection, artifact
capture, automation timeouts, and resource snapshots.
- `dotnet build AcDream.slnx -c Release` succeeds. The complete repository
suite is 6,480 passed / 5 skipped; the 17 existing test-project warnings
remain tracked by issue #228.
- The final two-process connected report passed with no failures. The capped
process completed fresh login, Aerlinthe, Rynthid, Facility Hub, Holtburg,
and an Aerlinthe revisit in 244.424 seconds. The uncapped process then
reconnected the same account and completed in 61.717 seconds.
- Both processes closed through `WM_CLOSE`, received the authoritative
character-logoff confirmation, exited with code 0, and were matched to
ACE's exact client UDP endpoint and `PacketHeader Disconnect` record.
- Every checkpoint had zero pending live teardowns, landblock retirements,
staged mesh uploads, composite warmups, and reveal invariant failures.
- All six PNG artifacts were valid and inspected. Login, indoor dungeon,
dungeon exit, Aerlinthe revisit, and reconnect showed complete world
geometry rather than a grey or partially revealed destination.
- The uncapped reconnect sampled 172.7 FPS / 5.79 ms at the checkpoint; its
latest profiler window measured 5.7 ms CPU p50, 6.1 ms p95 and 5.7 ms GPU
p50, 6.0 ms p95 with zero pacing time. This is machine-specific throughput
evidence, not a cross-display FPS requirement.
- The Aerlinthe revisit retained balanced owners and stayed inside the
documented relative memory/resource tolerances. One circuit is not claimed
as a permanent memory plateau proof; the longer seven-destination R6 soak
remains the endurance gate.
- `WorldRevealCoordinator` now owns the protected lifecycle outside
`GameWindow`; the accepted trace did not change after extraction.
## Landed commits
- `db45b81f` — expose canonical readiness snapshots and lifecycle telemetry;
- `354c2adc` — add deterministic checkpoints and framebuffer screenshots;
- `b03371c0` — add the connected lifecycle/reconnect gate;
- `68578fa5` — port retail graceful character logout and transport disconnect;
- `a4ef5788` — centralize the reveal lifetime in `WorldRevealCoordinator`;
- `f7b09617` — preserve singleton endpoint/checkpoint collections in the gate.
The remaining visual work is deliberately outside this gate: the R6
locomotion/collision/projectile/teleport retail comparison, the two-client
portal-out/materialization observer comparison, and issue #225's translucent
lifestone/particle ordering comparison.