# Modern Runtime Architecture — detailed execution plan **Date:** 2026-07-24 **Status:** Slices A–I, J0–J4, and J5.1–J5.5 are complete. Flat-authoritative collision, zero- allocation resolve scratch, parsed-graph removal, connected correctness/ lifetime acceptance, and Slice-I closeout landed 2026-07-25. Slices F–L were explicitly approved 2026-07-24. Slice J0–J1 established the enforced Runtime boundary and borrowed contract seam; J2 moved canonical session/transport lifetime and ordered inbound routing; J3.1–J3.6 moved canonical entity identity and object-table lifetime to Runtime, exact graphical projection identity to App, and one ordered canonical entity/object stream plus direct views to all hosts and closed the failure-safe owner ledger. J4.1 moved communication and social ownership into Runtime at `c9d25ade`; J4.2 moved inventory transaction ownership into Runtime at `011efbea`; J4.3 moved the coupled magic/player-sheet owner into Runtime at `d02a12ce`; J4.4 moved character settings/projections and the shared typed state-command seam at `dcb61efb`; J4.5 closed canonical gameplay-state ownership at `89e6b207`. J5 movement, physics, interaction, and combat ownership is active at J5.6 after canonical action state (`b298f99f`), interaction transactions (`f5f7b417`), and combat/magic intent (`20df9d15`), local movement/outbound cadence (`aa3f4a60`), and per-session physics/remote simulation (`7e6033d0`) moved to Runtime. **Scope:** Reconcile and sequence the existing Modern Pipeline (`MP`) and Linux/headless (`LH`) tracks using the 2026-07-24 connected performance audit. **Primary objective:** Minimize CPU, managed/native memory, GPU memory, allocation, and frame-time variance without reducing view distance, particles, world detail, or retail behavior. This plan refines rather than replaces [`docs/superpowers/specs/2026-07-05-modern-pipeline-design.md`](../superpowers/specs/2026-07-05-modern-pipeline-design.md). It preserves the shipped N.5 bindless/MDI renderer, the completed `GameWindow` decomposition, canonical Runtime entity ownership with exact-key App projections, the retail PView/cell visibility ports, and all behavior already accepted by the user. --- ## 0. Governance and standing-decision interlocks (2026-07-24 review) Added by the 2026-07-24 adversarial review. These interlocks are binding on every executor of this plan; the audit's correction record is [`../research/2026-07-24-runtime-performance-audit.md`](../research/2026-07-24-runtime-performance-audit.md) section 13. 1. **The ECS deferral stands until the user lifts it.** On 2026-07-05 the user deferred the ECS/MP3 render-world rewrite ("revisit ONLY if the user wants a higher raw FPS number and accepts the rewrite risk" — `claude-memory/project_mp_track_findings.md`), and the follow-up measurement note concluded MP3 was not warranted for town FPS. Slices A–E do not touch that deferral. **Slices F and G resume the deferred MP3 work and MUST NOT start without explicit user approval recorded against this section**, taken on the post-Slice-E corrected uncapped-local profile (does partition/submission cost still dominate?). **Approval recorded 2026-07-24:** after reviewing the post-E result and rewrite risk, the user authorized "all slices." F/G may proceed from the pre-runtime rollback anchor `e7d9d6fa`. Note the prior "GPU-bound at ~200 FPS" Sawato reading is itself invalidated by the `GpuFrameTimer` boundary artifact this plan's Slice A corrects — the question is genuinely open until re-measured. 2. **Session discipline.** This plan executes in dedicated Track MP/LH side-track sessions only. The ACTIVE milestone's critical path wins every conflict; do not begin any slice inside a milestone session. 3. **Freeze-exception scope.** The recorded 2026-07-05 Track MP exception covers streaming and WB rendering only. Slice H's network-I/O item and Slice J's gameplay-owner moves touch otherwise-frozen subsystems (network, chat, input) and each requires its own recorded user authorization before starting. **Approval recorded 2026-07-24:** the user's authorization for "all slices" lifts the Slice J gate as well. (The 2026-07-24 receive-thread robustness fix was separately reviewed and user-approved as a bug fix, not a rework.) 4. **Evidence-base corrections.** The §1 exception-traffic row previously cited "4,520 + 1,168" — figures with no derivation in the audit or its committed artifacts; the verifiable figures are the focused-trace 1,587 (1,335 invalid DAT probes + 252 receive timeouts). And the audit's 204 MiB single-frame allocation is a FRAME-THREAD measurement that cannot include worker-side mesh decode: **Slice E owns the frame-spike symptom; Slices B/C own the process-wide allocation rate and GC pressure.** Win attribution must respect that split. 5. **Baseline prerequisite.** No behavior-changing slice (C onward) starts until the Slice A exit criteria in §10 are met: committed uncapped-local AND capped-local nine-stop baselines, a pinned dense-town uncapped baseline, and per-portal allocation/frame timelines, all captured with the corrected tooling and a committed reference-hardware definition. --- ## 1. Why the architecture should change The complete methodology, attribution, sanitized checkpoint data, and raw- artifact retention policy are recorded in [`../research/2026-07-24-runtime-performance-audit.md`](../research/2026-07-24-runtime-performance-audit.md). The 2026-07-24 six-minute, nine-stop connected route established: | Observation | Measurement | Architectural implication | |---|---:|---| | Steady capped update work | approximately 0.4–1.3 ms median | The gameplay/update architecture is not the steady-state crisis | | External GPU use | 7.6% median, 15.9% p95 | The tested route was not GPU-bound | | Portal allocation | up to approximately 204 MiB in one frame | Runtime content preparation and publication must leave the frame transaction | | Managed allocation rate | approximately 40.8 MB/s average, 2.22 GiB/s maximum | Portal work is creating short-lived graphs and pixel/mesh arrays faster than GC can absorb | | GC | 38 Gen-2 collections; 256 ms longest pause | Frame spikes are dominated by allocation/publication, not upload time alone | | Longest frame | 276 ms | Landblock-count budgets are not real cost budgets | | Final live GC heap | approximately 194 MiB | The 1–2 GiB process high-water is not an unbounded live-object leak | | Dedicated GPU memory | 633–818 MiB | Texture arrays/caches are bounded but expensive and insufficiently observable | | Revisited locations | entity/emitter/particle counts returned to the same plateaus | Logical lifetime is broadly convergent; optimize rather than replace it | | Exception traffic | 1,587 focused-trace exceptions (1,335 invalid DAT probes, 252 receive timeouts); full-route totals pending re-derivation (see §0.4) | Exceptions are being used as normal control flow | Allocation attribution names the runtime mesh/texture path (`MeshExtractor.PrepareMeshData` → `PrepareGfxObjMeshData` → `AddSurfaceToBatch`) as the largest portal source. The update-thread publication/retirement graph then amplifies it with complete dictionaries, sorts, effect profiles, and unmetered per-entity teardown. The conclusion is: > Keep retail-shaped gameplay and the modern GPU backend. Replace the runtime > content, scene-projection, residency, and work-scheduling architecture that > feeds the renderer. --- ## 2. Fixed architectural decisions These decisions are binding unless a later measured gate disproves one. 1. **No whole-client rewrite.** Existing retail ports, network behavior, physics, animation, UI, plugins, and the N.5 renderer remain in place. 2. **No simulation ECS.** Retail-shaped object-oriented code remains the oracle- traceable form for physics, motion, sequencers, combat, inventory, and magic. 3. **A data-oriented render world only.** Arch remains the selected storage implementation, hidden behind acdream-owned interfaces so it can be replaced without changing simulation or rendering contracts. 4. **One authoritative live-object owner.** `LiveEntityRuntime` is evolved and later extracted; no second GUID map, gameplay world, or mirrored entity state may be introduced during migration. 5. **Headless is not a hidden window.** A headless session constructs no Silk.NET window, GL context, render assets, particles, audio, or gameplay UI. 6. **One content interpretation.** Bake and runtime validation use the exact existing `AcDream.Content` interpretation code. A baked artifact must be byte/field equivalent before runtime uses it. 7. **No visual-quality optimization by default.** View horizon, retail PView, particles, effects, and texture pixels stay unchanged. Retail distance LOD is a faithful mechanism and remains separately configurable. 8. **Work is budgeted by cost, not object count.** Streaming stages use elapsed microseconds, prepared bytes, uploaded bytes, and entity operations. “Four landblocks” is not a meaningful budget. 9. **GL remains render-thread owned.** Workers may perform I/O, parsing, decompression, mesh preparation, and collision preparation, but never mutate GL or live scene state. 10. **Frame products are borrowed immutable views, not object graphs.** Snapshot boundaries use double-buffered arrays/spans and generation stamps; they do not allocate a deep immutable tree each frame. 11. **Parallelism follows data cleanup.** Do not add a job system to compensate for repeated decode, sorting, allocation, or whole-world scans. 12. **Every cutover deletes the replaced production path.** Temporary shadow execution is allowed for comparison, but no permanent dual renderer, duplicate world, or silent decode fallback remains after its gate. --- ## 3. Target dependency architecture ```text AcDream.GraphicalHost (current AcDream.App) ├── window/input/OpenGL/OpenAL/retail UI ├── render scene projection + renderer └── owns one AcDream.Runtime.GameRuntime AcDream.Headless ├── deterministic host clock ├── bot/plugin command surface └── owns one or more AcDream.Runtime.GameRuntime instances AcDream.Runtime [new, extracted incrementally] ├── session lifecycle and inbound ordering ├── authoritative live world and properties ├── movement/physics/interaction/combat/magic/inventory/chat ├── instance-scoped clocks, queues, commands, and events └── no Silk.NET, GL, OpenAL, retained UI, or OS window dependency AcDream.Content [already exists] ├── DAT interpretation ├── deterministic bake pipeline ├── mapped pak/content manifest ├── immutable prepared render/collision metadata └── shared read-only content store for multiple sessions AcDream.Core / AcDream.Core.Net [existing retail-shaped logic] AcDream.Plugin.Abstractions [existing BCL-only contracts] ``` `AcDream.Runtime` is a target boundary, not the first implementation step. App types move only after their presentation dependencies have been removed and parity tests prove that the graphical host is still driving the same owner. ### Dependency rules - `Core` never depends on Runtime, App, Content, or a backend. - `Core.Net` depends on Core only. - `Content` depends on Core, never App or a graphics backend. - `Runtime` may depend on Core, Core.Net, Content, and Plugin.Abstractions. - The graphical and headless hosts depend on Runtime; Runtime never depends on either host. - UI and rendering consume read-only runtime views plus explicit commands/events. - Shared multi-session caches contain immutable content only. Credentials, GUIDs, clocks, packets, plugins, object state, and automation remain instance-scoped. --- ## 4. Runtime ownership model | Concern | Canonical owner | Consumers | |---|---|---| | Server GUID/incarnation/timestamps/properties | `GameRuntime` evolved from `LiveEntityRuntime` | physics, UI, render projection, plugins, bots | | Static prepared content | `ContentStore` | streaming, collision, render projection | | Desired spatial window | `StreamingRegion` | streaming scheduler | | Landblock generation and stage receipts | `StreamingWorkScheduler` | reveal, physics publication, render publication | | Render entity/component data | `IRenderScene` / Arch implementation | visibility, snapshot builder | | CPU/GPU asset residence | `ResidencyManager` | render scene, upload owner | | GL names, fences, physical retirement | renderer resource owners | renderer only | | Retail PView/visible-cell result | existing PView owner | render-scene query/snapshot builder | | UI state | focused UI controllers/ViewModels | retained UI and optional dev UI | Identity types must remain explicit: ```text ServerGuid authoritative network identity ObjectIncarnation ServerGuid + generation RuntimeEntityId runtime-local logical identity RenderEntityId presentation projection identity AssetHandle immutable content identity + generation GpuHandle render-thread physical resource identity ``` No layer may infer one identity from another through unchecked integer casts. --- ## 5. Content and asset architecture ### 5.1 Prepared assets The runtime world hot path should consume prepared records, not DBObj graphs: - Packed mesh vertices, indices, batch/material records, bounds, and part tables. - Exact decoded texture pixels used by the current renderer. - EnvCell geometry and portal records with content-deduplicated aliases. - Terrain and deterministic scenery placement. - Flattened collision/BSP arrays. - Compact Setup presentation metadata: default script, script table, animation, part availability, sound table, collision references, and effect-relevant flags. - Retail degrade tables. Dynamic character palette/appearance composition, low-volume UI assets, audio, and motion/animation tables may remain runtime DAT consumers until separately measured and migrated. ### 5.2 Content-addressed pak manifest Finish the existing pak implementation rather than replacing it: ```text Source asset key -> manifest entry -> content hash/shared blob ``` - EnvCell file IDs may alias the same blob offset. - Blob contents are deterministic and 64-byte aligned. - DAT iteration stamps, serializer version, and bake-tool version invalidate stale content. - The writer builds a temporary artifact, validates it, then atomically replaces the prior artifact. - The reader memory-maps immutable data and exposes typed borrowed views. - Corrupt/stale entries fail loudly and name the required rebake. - Production streaming does not silently fall back to runtime decode after the cutover. Developer equivalence tools retain both sources explicitly. ### 5.3 Asset handles and residence Callers receive typed handles rather than retaining decoded arrays: ```csharp readonly record struct AssetHandle(uint Index, ushort Generation); readonly record struct AssetLease(AssetHandle Handle, OwnerToken Owner); ``` The eventual `ResidencyManager` tracks: - CPU prepared bytes. - Decoded/pinned/staging bytes. - GPU buffer and texture bytes. - Current owners and reference count. - Last used generation/frame. - Rebuild/reload cost. - Priority: destination-critical, visible, near, far, speculative. - State: absent, requested, prepared, upload-pending, resident, retiring. Eviction is generation-safe and owner-scoped. A stale completion cannot revive a retired world generation or release the replacement generation's resource. --- ## 6. Streaming and reveal architecture ### 6.1 Staged pipeline ```text Request -> I/O/map lookup -> parse/borrow prepared records -> build landblock publication -> physics/collision publish -> render asset request -> GL upload -> render-scene publish -> reveal-ready ``` Every item carries: - World/session generation. - Landblock/cell identity. - Stage and priority. - Estimated and actual byte cost. - Entity-operation count. - Cancellation token. - Retryable stage receipt. Bounded queues provide back-pressure. Background stages may run concurrently; authoritative world, render-scene publication, and GL upload remain ordered on their owning thread. ### 6.2 Cost budget Each frame receives independently configurable budgets: - Update-thread publication time. - Entity create/retire operations. - CPU bytes adopted. - GPU bytes uploaded. - GL resource retire operations. Immediate work: - Remove old landblocks from visibility, collision, picking, radar, and target eligibility. - Mark the old generation unavailable to new consumers. Budgeted work: - Walk old presentation owners. - Release scripts/effects/lights/plugin snapshots. - Retire CPU/GPU resources after fences permit. - Publish far-ring content. This preserves correct visible lifetime while preventing a 600-landblock retirement from becoming one frame transaction. ### 6.3 Portal generation and reveal A destination generation progresses through: ```text Requested -> Prepared -> CollisionReady -> NearSceneReady -> Revealed | +-> FarSceneConverging ``` Reveal is an atomic edge. The world cannot become visible until the destination near ring, collision root, camera identity, and required scene publication all belong to the same generation. Far content continues under normal budgets after reveal. This replaces “priority work bypasses the budget” with a prepared-behind-portal contract and eliminates `viewport-before-ready`. --- ## 7. Data-oriented render scene ### 7.1 Scope Arch stores render projections only: - Static landblock objects and scenery. - EnvCell objects. - Live entities and equipped children. - Lights and effect anchors where a packed projection is beneficial. It never owns gameplay properties, network sequencing, physics authority, inventory, combat, or interaction state. ### 7.2 Components Initial components are blittable or stable handles: ```text RenderTransform PreviousRenderTransform MeshAsset MaterialVariant SpatialResidency (landblock, cell) WorldBounds RenderFlags DegradeState SortKey OwnerIncarnation DirtyMask ``` Separate archetypes cover static, dynamic, equipped-child, translucent, and light-bearing projections. Do not force every entity to carry every component. ### 7.3 Incremental indices Creation, rebucketing, mutation, and removal maintain: - Outdoor-static set. - Per-cell static sets. - Dynamic set. - Translucent set. - Selectable/pickable spatial index. - Light candidates. - Dirty transform/material/mesh ranges. The frame loop no longer partitions every loaded entity. Existing retail PView continues to determine visible cells; the render scene enumerates only those cell buckets plus the dynamic set. ### 7.4 Simulation-to-render seam `LiveEntityRuntime` and static publication emit ordered projection deltas: ```text Register UpdateTransform UpdateAppearance UpdateFlags Rebucket Unregister ``` Deltas contain exact incarnation identity. The render world rejects stale generation updates. It never calls back into simulation dictionaries while drawing. --- ## 8. Render-frame product and GPU submission The snapshot is a double-buffered, generation-stamped borrowed view: ```text RenderFrameView Visible opaque instance ranges Visible alpha instance ranges Dynamic transforms Light set Effect draw records Selection records Existing PView/clip products ``` The update/render order initially remains on the accepted host thread. The snapshot seam prevents mutation during draw and permits later thread separation without requiring it now. GPU submission evolves in measured steps: 1. Persistent global mesh/instance buffers remain. 2. Dirty ranges update only changed instance records. 3. MDI command buffers are reused and rewritten in place. 4. Static command templates are cached by scene generation/cell visibility. 5. Accurate timestamp queries bracket actual render passes. 6. GPU culling, command compaction, GPU particles, or GPU light selection are introduced only when the corrected profile names them as the next bottleneck. The existing portal/PView pass graph, clipping, translucency ordering, and bindless material behavior remain authoritative. --- ## 9. Presentation-independent runtime and headless host ### 9.1 Extraction rule Do not create a new parallel `GameRuntime` and synchronize it with `LiveEntityRuntime`. Instead: 1. Define narrow runtime read/command/event contracts around the current owner. 2. Remove presentation-specific fields from that owner into App projections. 3. Move the now presentation-independent owner and collaborators into `AcDream.Runtime`. 4. Keep the graphical host using the same instance throughout the move. ### 9.2 `GameRuntime` One instance owns: - Connection/authentication/character/session lifetime. - Packet receive ordering and retail update phases. - Authoritative objects, properties, containers, inventory, spell state, enchantments, vitals, targets, and combat state. - Movement, collision state required for gameplay, and interaction commands. - Instance clock, random sources where applicable, queues, plugins/behaviors, diagnostics identity, and teardown. It exposes: - Immutable or borrowed read views. - Typed commands. - Ordered events/deltas. - Deterministic `Tick`. - Retryable, complete shutdown. #### 9.2.1 Structural teardown protocol Every graphical or headless host follows one retryable, generation-scoped teardown transaction. New runtime owners register through the same `CompositionAcquisitionScope` lease discipline already used by the graphical composition root; release order is encoded by leases and acknowledgements, never remembered by callers. 1. **Cancel scheduler generations.** Make commands inert, cancel the active session/runtime generation, stop new receive publication, and prevent any timer, bot, plugin, or worker callback from creating more work for it. 2. **Drain or poison journals and queues.** Detach inbound producers, drain already-accepted ordered events through the generation boundary, then poison every command/event/delta queue so delayed callbacks fail against their exact generation instead of entering a replacement session. 3. **Unpublish presentation.** The graphical host consumes the final ordered withdrawals and acknowledges that the render scene, retained gameplay UI, audio/effect projections, selection, radar, and private viewports no longer expose the runtime generation. Headless hosts acknowledge this stage immediately because they construct no presentation owners. 4. **Release residency after fences.** Withdraw content owners, finish streaming/landblock retirement, and allow GPU-backed stores to retire only after their existing frame-flight fences signal. A failed fence or release retains the exact unfinished lease and resumes without replaying completed stages. 5. **Release the shared `ContentStore` last.** Session-scoped collision/content leases go away before the runtime instance. The immutable mapped content store outlives every runtime and presentation host and is disposed only by the process host after all registered sessions have acknowledged teardown. Logout, session reset, mid-portal disconnect, reconnect replacement, ordinary window close, and construction rollback all use this protocol. No stage may clear another generation's state, and no teardown failure may be converted into a best-effort purge. ### 9.3 Hosts `GraphicalGameHost` provides input, camera, rendering, UI, audio, frame pacing, and a single runtime instance. `HeadlessGameHost` provides a monotonic scheduler, navigation collision/content, bot actions, diagnostics, and one or more runtime instances. It performs no render-content bake lookup unless navigation/collision requires that content. For 30 clients: - Immutable content and flattened collision assets are shared. - Every mutable session structure is instance-scoped. - One process may host many sessions, but one-process-per-session remains a supported diagnostic/isolation mode. - Scheduling uses a deterministic round-robin/time-wheel rather than 30 busy loops. - Server-safe outbound rate limits are per session. --- ## 10. Detailed implementation sequence Each slice is independently buildable, testable, bisectable, and behavior- preserving. Each gets a focused implementation plan when it starts. ### Slice A — Measurement and contract correction **Purpose:** Make every later gate trustworthy. - Correct `GpuFrameTimer` so queries cover actual GL submission/pass intervals, not frame pacing. - Extend canonical checkpoint JSON with CPU/GPU resident bytes, staging bytes, per-stage queue depth, stage work time, exception counts, GC pause/heap fields, and render-scene generation. - Add a tracked analysis script that summarizes the existing nine-stop route. - Record capped-RDP and uncapped-local results separately. - Add a deterministic allocation/exception attribution recipe without enabling developer UI or changing gameplay. **Gate:** repeated idle measurements have low observer effect; CPU stage sums reconcile with active frame time; external GPU engine direction agrees with GL timestamps. **Amendment (2026-07-24 review).** Slice A additionally includes, and its gate requires: - Per-frame history export (frame index, stage times, frame-thread alloc, gpu time) — landed post-review via `ACDREAM_FRAME_HISTORY`; route-wide p50/p95/p99 populations must come from this export. Until it is the gate population, every percentile gate reads "no 5-second window with p99 > X". - Process-wide allocation rate per scene (dotnet-counters) alongside the frame-thread profiler, plus a per-thread split recipe — the frame-thread gates alone are gameable by moving work to workers (§0.4). - One contention capture WITH STACKS on the route (the 4,614 contentions are unattributed; `_datLock` build-vs-hydration and `ObjectMeshManager._pendingRequests` are the leading candidates, not only the DAT typed-object lock). - Cache hit/miss/evict counters in the checkpoint JSON (landed post-review) so Slice B/C wins are attributable to cold-vs-warm portals. - LOH size + fragmentation (+POH) checkpoint fields (landed post-review; the audit's own JSON showed 65% LOH fragmentation, never analyzed). - Fixed-camera screenshot COMPARISON tooling (pulled forward from Slice G) with a defined match rule (per-channel tolerance + max differing-pixel fraction; pinned `/time`, pinned camera, particles pinned-seed or masked). Slice C's visual gate depends on it existing. - A committed reference-configuration block (CPU, GPU, driver, OS build, display path, resolution, refresh, VSync state, re-baseline rule) — the §11 phrase "reference local hardware" is undefined without it. **Exit criterion (blocks Slice C, per §0.5):** committed uncapped-local and capped-local nine-stop baselines, a pinned dense-town uncapped baseline (name the town, pin the camera), and per-portal allocation/frame timelines around each teleport materialization. **Progress (2026-07-24):** the corrected GL timer, per-frame history, process counter/trace capture, screenshot comparison, reference configuration, binary provenance, and route-bound stationary sampling have landed. Corrected capped and uncapped nine-stop RDP routes, a pinned dense-Arwic RDP sample, observer effect, portal windows, and contention stacks are committed in [`../research/2026-07-24-slice-a-rdp-baselines.md`](../research/2026-07-24-slice-a-rdp-baselines.md) and its machine-readable companion. They prove the tooling is usable but are diagnostic-only. The authoritative capped/uncapped nine-stop and pinned dense-Arwic repetitions are committed in [`../research/2026-07-24-slice-a-physical-local-baselines.md`](../research/2026-07-24-slice-a-physical-local-baselines.md) and its machine-readable companion. All routes completed and disconnected gracefully. They also preserve the two repeated-portal memory plateau failures, ~200 MiB portal allocation frames, and typed-Setup exception storm as Slice C's before-state instead of weakening the gates. Slice A and Slice B are complete; Slice C is unblocked. ### Slice B — Finish MP1b EnvCell dedup and full bake **Purpose:** Make the existing pak physically usable. - Add shared-blob/alias support to `PakWriter` while retaining unique source keys. - Compute the existing runtime EnvCell geometry identity before extraction. - Extract each unique geometry once; map all source file IDs to the shared blob. - Make side-staged particle preloads and ordinary assets use the same deterministic content table. - Add alias, collision, determinism, stale-version, corruption, cancellation, and atomic-replace tests. - Run the complete bake and publish counts, unique ratios, size, time, failures, and peak memory. **Gate:** full bake finishes in practical time/space; all fixture and random sample equivalence tests pass; duplicate EnvCells share blob offsets; no runtime code consumes the pak yet. **Completed 2026-07-24:** 729,888 EnvCell keys collapsed to 17,117 unique geometries plus 712,771 aliases (42.6×), with 751,141 total keys, 38,370 physical blobs, zero failures, a 28,192.4 MiB artifact, and an 81.4-second validated atomic publication. The gate also exposed and corrected a real collision in WorldBuilder's legacy 31× geometry hash. Evidence: [`../research/2026-07-24-slice-b-full-bake-report.md`](../research/2026-07-24-slice-b-full-bake-report.md). ### Slice C — Runtime prepared-asset source and MP1c cutover **Purpose:** Remove mesh/texture decode from portal frames. - Introduce `IPreparedAssetSource` in Content and inject it into App streaming. - Teach `ObjectMeshManager`/landblock builders to adopt pak-backed prepared payloads without reconstructing complete intermediate object graphs. - Add compact Setup presentation metadata to the bake or a keyed prepared cache so static activation does not parse arbitrary IDs as Setup. - Replace `ResolveId().ToList().OrderBy...` with typed non-allocating lookup. - Add explicit negative/type metadata; exceptions are not type tests. - Retain live extraction only in bake/equivalence tooling and required dynamic appearance paths. - Remove production streaming fallback at the gate. **Gate:** byte/field equivalence; no invalid Setup exception storm; portal single-frame allocation and p99 materially improve from the 2026-07-24 baseline; connected nine-stop route and screenshots pass. **Completed 2026-07-24:** production streaming now consumes the validated format-1/bake-tool-3 package through `IPreparedAssetSource`, with no implicit live-DAT mesh fallback. Capped, uncapped, and dense-Arwic physical routes, installed-DAT equivalence, lifecycle assertions, graceful teardown, the full Release suite, and the user's visual gate passed. Process allocation fell 41.8–61.9%, GC pause time fell 48.4–54.1%, uncapped CPU/GPU p99 improved 17.0%/20.6%, and invalid typed-Setup exceptions fell to zero. Evidence: [`../research/2026-07-24-slice-c-prepared-asset-cutover-report.md`](../research/2026-07-24-slice-c-prepared-asset-cutover-report.md). **Amendment (2026-07-24 review).** Binding contracts for this slice: - **Precedence contract.** The LINQ being replaced is load-bearing content selection, not just allocation: `OrderByDescending(r => r.Database == _dats.Portal)` means **Portal-dat-wins** when an id resolves in more than one database. The typed non-allocating lookup must encode that precedence explicitly, and the equivalence fixtures must include at least one dual-dat id (an id shadowed by `client_highres.dat`) so a precedence regression fails the gate instead of silently changing pixels. - **Seam fixtures.** The DAT-equivalence suite must include ObjDesc-composed fixtures that straddle the bake/runtime split: a drudge, a robed player, and palette-dyed armor (baked base mesh + runtime appearance overlay). - **Cache identity rule.** Pak-backed default-palette pixels and runtime-composed palette-overlaid pixels for the same surface id are distinct cache identities and must never share a key. - **Exception semantics.** When the probe storm is removed, also narrow `ResolveActivation`'s bare `catch { return null; }` (and the sibling `SequencerFactory`, which today has NO catch at all): catch only the specific corrupt-data exception types, rethrow `OperationCanceledException`, log anything unexpected. Otherwise the "no exception storm" gate can pass while real activation failures stay invisible — the pattern CLAUDE.md's no-workarounds rule names explicitly. - **Baseline comparison validity.** "p99 materially improve" compares against the post-Slice-A uncapped-local baseline, not the capped-RDP 2026-07-24 capture (whose steady p99 sits at the 31.2 ms display floor). Portal single-frame allocation is FPS-independent and may compare against either. ### Slice D — Typed asset handles and unified residency **Purpose:** Bound and explain CPU/GPU memory. - Add `AssetHandle`, owner tokens, leases, generations, and accounting. - Place object mesh, standalone texture, composite texture-array, staging, and prepared-content residence behind one policy owner while retaining specialized physical caches. - Track logical, CPU, staging, GPU-requested, GPU-resident, and retiring bytes separately. - Add configurable budgets through `RuntimeOptions`/settings with current visual behavior as the default. - Implement generation-safe LRU/cost-aware eviction and fence-delayed physical release. - Expose exact budget/occupancy/fragmentation facts to diagnostics. **Gate:** same-location third-visit residence plateaus; no stale-generation release; no missing textures; no resource growth after repeated portal loops. **Completed 2026-07-24:** typed asset/owner generations, leases, immutable budget profiles, specialized-owner adapters, exact aggregate accounting, bounded animation/audio/alpha-scratch policies, forced-pressure coverage, and fence-aware retirement diagnostics are live. The capped/uncapped physical nine-stop routes and dense Arwic passed with graceful shutdown. The third Caul checkpoint reduced cache-attributed CPU/GPU residence from the second visit, animations plateaued at 55 entries, scratch stayed below budget, and no checkpoint retained staged or retiring bytes. Evidence: [`../research/2026-07-24-slice-d-unified-residency-report.md`](../research/2026-07-24-slice-d-unified-residency-report.md). **Amendment (2026-07-24 review).** Two corrections to this slice's scope: - **Do not rebuild what exists.** Fence-gated, budgeted eviction already runs per frame in three of the four systems this slice names: `ObjectMeshManager` (1 GiB / 50-object LRU, reclaimed every frame), `CompositeTextureArrayCache` (128/64 MiB budgets, per-frame throttled GL deletion), and `GlobalMeshBuffer` (capacity shrink with hysteresis). The 2026-07-24 route never pushed those ceilings, so its "bounded" residency was bounded-by-route, not policy-exercised. Slice D's job is to UNIFY accounting and observability over the existing physical caches, add a forced-pressure eviction test (deliberately exceed the ceilings), and retune the budgets for multi-session load — plus bring policy to the items that genuinely have none: deferred-alpha scratch arrays, `ObjectMeshManager._boundsCache`, and the audio/animation content caches (audio caches bounded post-review). - **Concurrency contract before code.** The `ResidencyManager` design in §5.3 lists states with no transitions. Before this slice starts, its plan must specify: a transition table including failure/cancellation/corrupt edges; which fields are single-thread-owned vs shared (policy decisions on the update thread, physical release render-thread-only, accounting via atomics or a single-writer journal); the rule that eviction never blocks the render thread and never runs under a lock the render thread takes; and that `ResidencyManager` never touches GL names (only the renderer resource owners row of §4 may). ### Slice E — Cost-budgeted streaming and retirement **Completed plan:** [`2026-07-24-modern-runtime-slice-e-cost-budgeted-streaming.md`](2026-07-24-modern-runtime-slice-e-cost-budgeted-streaming.md). **Completed 2026-07-24:** E0 fixed the retail/adaptation contract and current owner inventory. E1 landed the validated work profile, deterministic meter and completion charge model. E2 cut execution over to stable priority FIFOs. E3 added immediate generation quiescence and cursored retirement. E4 advances render/physics/static publication from retained exact receipts. E5 replaced mutable priority-radius state with the reveal coordinator's exact generation-scoped destination reservation, protected its share across every typed budget dimension, removed forced early materialization, and added retail's centered five-second portal wait cue. E6's deterministic suites, Release build, complete tests, capped/uncapped nine-stop routes, and pinned dense-Arwic route pass. The connected gate corrected stale cache-hit staging and separated quiesced destination spatial residency from gameplay visibility. Every canonical checkpoint converged with zero pending publication/retirement/backlog, staged upload, composite warmup, or retiring resource. Against Slice A, capped/uncapped CPU p99 improved 34.7%/30.4% and largest frame allocation fell 84.7%/82.2%; against Slice C, CPU p99 improved 21.4%/16.1% and allocation fell 33.5%/9.7%. Evidence: [`../research/2026-07-24-slice-e-cost-budgeted-streaming-report.md`](../research/2026-07-24-slice-e-cost-budgeted-streaming-report.md). **Purpose:** Remove update-thread portal transactions. - Introduce explicit stage queues and `StreamingWorkBudget`. - Split immediate logical/spatial detach from budgeted owner/resource teardown. - Make per-entity retirement cursor/time bounded. - Make publication cursor/time/byte bounded. - Reserve destination-critical work across portal frames rather than bypassing all budgets on one frame. - Preserve FIFO within priority/generation and exact retry receipts. - Connect reveal to the single destination-generation readiness state. **Gate (rescoped, 2026-07-24 review; completed):** zero `viewport-before-ready`; no scheduler continuation after a configured time/byte/entity limit (the documented first-indivisible-operation rule is named explicitly); portal-window frame p99 and single-frame allocation materially improve versus the post-Slice-A baseline; no stranded old generation, staged upload, collision, effect, or GPU owner. The program-level 16.67 ms p99 / 33.3 ms maximum targets are §11 gates verified at Slice H — Slice E cannot own them while the audit's own P2 costs (whole-world partition, unconditional UI/diagnostic work) are assigned to Slices G and H. **Amendment (2026-07-24 review).** Binding contracts for this slice: - **Release-later never means run-later.** Retired-generation owners are tick-frozen and audio-silenced at the immediate detach edge even when their memory release is budgeted across later frames. Audio is added to the immediate set (or bounded at ≤1 frame): a looping emitter on a retired landblock is user-audible during portal transit, and audio appears in neither of §6.2's lists today. Retail teardown anchors for the slice plan: `CLandBlock::Destroy` 0x0052faa0, `destroy_static_objects` 0x0052fa50, `CObjectMaint::DestroyObjects` 0x00508c30 (retail teardown is one synchronous transaction; deferral has retail precedent only via the `AddObjectToBeDestroyed` queue, whose drain is complete-when-run). - **Reveal stall behavior.** A reveal hold longer than retail's 5-second fade ceiling keeps the tunnel animating, surfaces the retail "In Portal Space - Please Wait" cue (AP-115), and emits a diagnostic — never a silent early reveal. The `IsUnhydratable` loud-failure escape stays. Retail anchor: `SmartBox::UseTime` 0x00455410 `blocking_for_cells`; the 1/2/5 s fade thresholds are recorded in `claude-memory/project_portal_space.md`. - **All count constants convert.** `MaxCompletionsPerFrame` is not the only count-shaped budget: `MaxDrainIterations` (the ×64 outer drain ceiling) and the priority-ring radius are part of the same redesign; and the uncapped one-frame paths (`BeginFullWindowRetirement`, `TryAdvanceOriginRecenterPreparation` — the ~600-landblock teleport retire) are the primary offenders, not the 9-landblock eager ring. - Deferred loads retained in `_deferredApply` during a burst are a retained-memory high-water source; the staged pipeline must account for them under the CPU-bytes budget. ### Slice F — Incremental render scene foundation **Prepared plan:** [`2026-07-24-modern-runtime-slices-f-g-render-scene.md`](2026-07-24-modern-runtime-slices-f-g-render-scene.md). **Purpose:** Stop rebuilding/partitioning the world each frame. - Add acdream-owned `IRenderScene`, identifiers, components, and delta journal. - Implement it with Arch in App only. - Mirror static publication and live projection into a non-drawing shadow world. - Add deterministic scene digest and compare it with current owners at checkpoints. - Maintain cell/outdoor/dynamic/translucent/light indices incrementally. - Add stale-incarnation, duplicate-create, rebucket, delete/recreate, hidden, parent-child, and session-reset tests. **Gate:** shadow scene matches canonical world/resource checkpoints through the nine-stop route with bounded memory and zero authoritative ownership. **Amendment (2026-07-24 review).** Preconditions and contracts: - **User approval required before this slice starts** (§0.1 — F/G resume the deferred MP3 work). - **Referee before the match.** The deterministic scene digest is built and validated against the CURRENT pipeline in steady state as its own preceding sub-slice, so the comparison tool has a proven track record before it gates a mirror. During soak/dev runs the digest also runs continuously through portal windows (every N frames), not only at the nine checkpoints — checkpoint-only comparison is blind to drift that self-corrects between stops, which peaks exactly when churn does. - **Journal discipline.** The delta journal is drained to empty every host tick while single-threaded (assert on carryover); deltas coalesce last-writer per (entity, kind) between drains, bounding the journal at O(live entities) instead of O(mutations); on any future thread separation it adopts §6.1's bounded-queue discipline with an explicit no-block rule for the update thread. - **Interface home.** `IRenderScene`, its components, and the delta journal types live in `AcDream.App` for Slices F–I. At Slice J the seam becomes Runtime-emitted ordered entity events (incarnation + transform + flags, no render vocabulary) that an App-side projection adapter maps to `IRenderScene` deltas — write Slice F against that end-state so no render type bakes into a contract Slice J must break. - **Identity enforcement.** The §4 no-unchecked-cast rule gets a concrete mechanism here: wrapper structs with a single `.Value` accessor plus a grep-based architecture test (or analyzer) restricting that accessor to owning assemblies. ### Slice G — Render snapshot and delta submission cutover **Purpose:** Make render cost proportional to visible/changed data. - Build the double-buffered `RenderFrameView`. - Feed it the existing PView visible-cell/clip product. - Replace full `InteriorEntityPartition` scans with render-scene bucket queries. - Upload only dirty persistent instance ranges. - Reuse MDI command/sort buffers. - Build instance-set comparison and fixed-camera screenshot comparison. - Run old and new submissions in compare mode without drawing twice. - At the gate, delete the replaced production enumeration/submission path. **Gate:** instance sets and screenshots match; dense-town uncapped target is 300 FPS or the corrected profile identifies a new dominant stage; steady frame allocation is near zero. **Amendment (2026-07-24 review).** The 300 FPS / 3.33 ms figure is provisional until Slice A's corrected pass timing re-measures the dense-town CPU-vs-GPU split on the local display (the prior "GPU-bound ~200 FPS" reading was produced by the boundary-spanning `GpuFrameTimer` this plan corrects, and the 2026-07-24 capture cannot settle it). Bucket queries must preserve the retail dynamics contract: retail drops out-of-flood dynamics via the per-dynamic viewcone CULL, not by set membership (`RetailPViewRenderer.DrawDynamicsLast`) — replacing the whole-world walk with buckets must not change which dynamics get culled. This slice also retires the sibling per-frame rebuilds the audit missed: `EnvCellRenderer.PrepareRenderBatches` (gated post-review) and the nearby-building gather walk, both of which otherwise keep steady render cost proportional to resident content. ### Slice H — Event-driven UI, diagnostics, lights, and frame cleanup **Purpose:** Remove remaining work that scales with uncapped FPS. **Execution plan (active 2026-07-25):** [`2026-07-25-modern-runtime-slice-h.md`](2026-07-25-modern-runtime-slice-h.md). The plan separates H-a text/UI/attachment/frame work, H-b's exact light-set optimization, and H-c's ordered network-I/O change into independently revertible gates. - Skip diagnostics with no consumer and reuse renderer visibility facts. - Dirty-layout retained UI: apply anchors only after geometry changes. - Maintain an overlay-participant registry instead of a second full-tree walk. - Track active cooldown items/effects/dialogs through events. - Replace per-frame frame-input objects, LINQ arrays, iterator objects, and liveness collections with reusable storage or borrowed views. - Make light candidates spatial/cell-driven and select top-k without a complete per-frame sort. - Replace socket-timeout exceptions and datagram `ToArray` copies with normal cancellable/poll/span-based I/O. **Gate:** no steady-state Gen-2 collections; steady allocation target ≤ 4 KiB/frame initially and zero for the core world/render loop; UI and network behavior tests unchanged; process-wide steady allocation rate within the §11 per-scene target (frame-thread numbers alone are gameable, §0.4); the program-level traversal frame-time targets from §11 are verified here. **Amendment (2026-07-24 review).** This slice splits into three separately gated pieces: - **H-a — UI/diagnostics/frame cleanup.** Includes: `UiText` shaping cache (appraisal/character-info/effects panels re-run full word-wrap every visible frame; apply the existing `ChatWindowController.GetTranscriptLines` revision+width+font cache pattern — it is already proven in-tree), the cooldown scan's visibility scoping (it is O(all mounted item-list slots) regardless of open windows — the worst-scoped of the audited UI items), diagnostics consumer-gating (reuse the AABB results as the partition's landblock frustum cull rather than deleting them — the same test is computed and discarded one call away from where it is needed), `EnvCellRenderer` prepare gating (landed post-review), and the per-frame scratch items (RetailPViewFrameInput reuse, iterator layers). - **H-b — Lights (pinned; two-failure regression history, register AP-85).** The candidate pool remains the resident registry (`_all`); any narrowing is the existing latency-buffered visible-cell FILTER only; the over-cap ranking stays dynamics-first + nearest-PLAYER; the only change is replacing the >MaxGlobalLights overflow `List.Sort` with a top-k partial select that produces the IDENTICAL selected set. Retail anchors to cite in the slice plan: `CEnvCell::add_dynamic_lights` 0x0052d410, `Render::insert_light` 0x0054d1b0, `Render::add_dynamic_light` 0x0054d420, caps 0x0081ec94/98. Gate adds: selected-set equivalence on captured Town Network fixtures (463 lights) + fixed-camera screenshot comparison. Note the audit overstated this item: per-object/per-cell selection is already an allocation-free top-8 insertion; only the overflow path sorts. - **H-c — Network I/O (requires §0.3 authorization).** Scope includes the receive-side per-packet allocation chain the audit missed (fresh `UdpClient.Receive` array + `BodyBytes` copy + per-fragment `ToArray` + per-packet `Packet`/`List`/`Optional` objects), not only the outbound `ToArray`. Invariants pinned: single outstanding receive, kernel arrival order preserved into the inbound queue, ack-per-received-packet at decode acceptance (holtburger pattern), `LinkStatusHolder::OnHeartbeat` 0x004113D0 last-heard placement unchanged, `PumpOnce` handshake pacing preserved. (Receive-thread death robustness landed post-review as an approved bug fix.) ### Slice I — Flat collision assets and residual zero-allocation work **Purpose:** Complete MP4 without changing retail math. - Bake flattened index-based BSP/collision records. - Port traversal data access line-for-line while preserving ordering and arithmetic. - Remove parsed DBObj graphs from streaming collision publication. - Address remaining measured allocation sites, including any physics transition pooling only after identity/lifetime tests prove it safe. **Gate:** trajectory and retail conformance suites remain bit-equivalent; navigation/collision fixtures match; portal and steady allocation targets pass. **Amendment (2026-07-24 review).** Bit-equivalence across the layout change is achievable (RyuJIT SSE scalar IEEE-754; the x87 concerns in project memory are decomp-READING lessons, not runtime determinism) — but only under two conditions the slice plan must state: (a) the bake stores dat-parsed floats VERBATIM, zero arithmetic transformation (no plane renormalization, no quantization, no double round-trips); (b) traversal preserves front/back child visit order and every epsilon comparison. The existing suites are scenario-bounded, so add an old-vs-flat DIFFERENTIAL sweep during the shadow phase: mass BSPQuery/point-in-cell/sweep queries across a large cell sample, outputs compared bit-for-bit between graph and flat implementations while both coexist. Physics transition pooling stays behind its identity/lifetime-test condition. This slice floats — it depends only on Slice B's bake infrastructure, not on F–H. ### Slice J — Presentation-independent `AcDream.Runtime` **Purpose:** Establish the graphical/headless shared client kernel. - Add the Runtime project and dependency guards. - Define `IGameRuntimeView`, commands, ordered events, clock, and lifecycle. - Adapt the current graphical host to those interfaces first. - Remove App presentation dependencies from canonical gameplay owners. - Move owners by coherent lifetime groups; never mirror state. - Add host-parity and no-backend construction tests after each move. - End with `GameWindow`/App composing one `GameRuntime`. **Gate:** the connected graphical route is unchanged; a no-window integration test connects, enters world, moves, receives inventory/chat/world updates, portals, logs out, reconnects, and tears down without loading App/Silk/OpenAL. **Amendment (2026-07-24 review).** Requires §0.3 authorization (moves frozen-subsystem owners). Add a **teardown protocol** subsection to §9.2 before this slice starts — session reset, logout, and mid-portal disconnect traverse five new owners, and tests without a specified order mean the order gets invented per-slice: (1) cancel scheduler generations; (2) drain or poison journals/queues; (3) unpublish the render scene; (4) release residency, fence-gated; (5) `ContentStore` last (shared, outlives sessions). Every new owner registers with the existing `CompositionAcquisitionScope` lease discipline so disposal order is structural, not remembered. This slice is a multi-plan track (the comparable `GameWindow` decomposition took 8 slices for 14K lines), not a single bisectable slice — plan it as lifetime-group sub-slices with host-parity tests after each move. ### Slice K — Linux headless and multi-session host **Purpose:** Deliver efficient automated bots. - Add Linux CI for Core, Core.Net, Content, Runtime, and Headless. - Implement portable path/config/credential handling. - Add `AcDream.Headless` CLI and deterministic scheduler. - Add bot command/event APIs for movement, selection, use, combat, spells, looting, chat, and commands. - Share immutable content/collision stores across sessions. - Audit and eliminate mutable statics and process-wide session state. - Add clean cancellation, reconnect, plugin isolation, and credential-safe logs. - Stress 1, 5, 10, and 30 sessions. **Gate:** 30 local-server sessions in one Linux process, no GPU/display/audio dependency, bounded incremental memory per session, no busy-loop CPU, clean teardown/reconnect, and parity with graphical runtime command/event behavior. **Amendment (2026-07-24 review).** The parity gate covers packet ORDER but not inter-packet TIMING — and AutonomousPosition cadence is already a tracked divergence (register row TS-33, `claude-memory/project_retail_motion_outbound.md`). Add a wire-timing assertion: AutonomousPosition inter-packet intervals and MoveToState-on-change emission points within a stated tolerance band versus the graphical host on the same scenario; the time-wheel scheduler must derive wire cadence from the instance clock, never from tick count. The 30-session stress gets an explicit duration measured in HOURS (the characteristic 30-bot failure is slow per-session creep — the #193 class leaked ~1 GiB/min and was found by OOM, not by a 6-minute route), with per-session incremental memory given a numeric ceiling at gate time. ### Slice L — Linux graphical host and evidence-gated GPU work **Purpose:** Finish platform portability and only then pursue remaining GPU opportunities. - Validate Linux OpenGL extension/driver matrix and package native dependencies. - Port paths, frame pacing, input, audio, and packaging without renderer fallback. - Reprofile locally with accurate pass timing. - Add GPU culling, command compaction, particle simulation, or light selection only for stages proven dominant. **Gate:** graphical Linux connected route passes the same lifecycle, screenshot, resource, and performance checks; every GPU migration has a CPU/GPU before/after and visual equivalence result. --- ## 11. Performance gates Reference measurements use a Release build, the same account/route, stable camera scripts, and both capped and uncapped modes. RDP results are kept separate from local-display results. | Metric | Target | |---|---:| | Traversal frame p99 | ≤ 16.67 ms on reference local hardware | | Traversal maximum | ≤ 33.3 ms after warm process startup | | Dense-town uncapped frame p50 | ≤ 3.33 ms or a newly attributed blocker | | Steady update p95 | ≤ 2 ms | | Core world/render allocation | 0 B/frame after warmup | | Whole-client steady allocation | initial gate ≤ 4 KiB/frame | | Portal single-frame allocation | ≤ 4 MiB, then tighten from evidence | | Gen-2 collections during canonical route | 0 after startup/bake warmup | | Same-location third-visit resource growth | ≤ 5% and explained | | World-visible-before-ready events | 0 | | Staged resources at stable checkpoints | 0 | | Exception-as-control-flow | 0 known sites | | Headless renderer/audio/window allocations | 0 | Memory budgets are measured by category rather than only process working set: - Live managed heap. - GC committed/fragmented. - Native prepared/staging buffers. - Mapped content pages. - Tracked GPU buffers/textures. - Driver-reported dedicated/shared GPU memory. Absolute process/VRAM targets are fixed after Slice D produces trustworthy category accounting; until then, the binding rule is plateau plus no regression in pixels/range. **Amendment (2026-07-24 review).** Gate-table corrections and additions: - **Percentile populations.** Until the Slice A per-frame history export is the population, every p99 row reads "no 5-second window with p99 > X" — the windowed profiler cannot produce route-wide percentiles. - **Anti-gaming complement.** New row: process-wide steady allocation rate per scene (dotnet-counters), alongside the frame-thread rows. The frame-thread gates alone reward moving garbage to workers, which is this plan's own remediation shape. - **Provisional budgets.** "≤ 4 MiB portal" and "≤ 4 KiB steady" are round-number initial budgets, not derived values; tighten from post-A/C evidence. "0 B/frame" means alloc p50 = 0 with every nonzero frame attributable to the profiler/report path (the instrument itself allocates its 5-second report on the frame thread). - **Third-visit growth ≤ 5%** is scoped per metric class: same-location tracked CPU/GPU residence measured against the location's own asset set (possible after Slice D accounting); entity/emitter/particle counts stay warnings on a live ACE world; absolute byte deltas reported alongside percentages. The audit's own data shows legitimate +31.7% Sawato tracked-GPU growth from route-wide cache warming — the unscoped gate false-fails on exactly that. - **New row — long-duration soak:** ≥ 60 minutes looping the route; managed heap, LOH size + fragmentation, tracked GPU, handles, and threads all plateau within stated bands. (The canonical route is 359 s; the #193 leak class is invisible to it.) - **New row — frame-pacing jitter** (stddev / 99.9th) once the per-frame export exists. - **Scheduled harness.** Slice A or H tasks a scheduled local perf harness: run the committed route against the last committed baseline JSON with the soak script's relative-limit pattern applied cross-commit, failing loudly. Manual-and-unscheduled is not a regression net. - "Reference local hardware" refers to the committed reference-configuration block required by Slice A; gates against an undefined machine are unfalsifiable. --- ## 12. Test and review matrix Every slice must pass the tests relevant to its boundary: 1. **Pure unit tests:** handles, generations, budgets, queues, manifest, deduplication, stale/corrupt data, snapshot buffers. 2. **DAT equivalence:** live extractor versus prepared asset for representative outdoor, dungeon, town, portal, animated, translucent, and particle assets. 3. **Scene equivalence:** canonical entity/instance digest at fixed checkpoints. 4. **Render equivalence:** instance-set diff plus fixed-camera screenshots. 5. **Retail conformance:** named-retail citations for any AC-specific selection, degradation, visibility, collision, or timing behavior touched. 6. **Lifecycle:** fresh login, same-location revisit, world edge, dungeon, repeated recalls, rapid generation replacement, logout/reconnect, graceful close, cancellation, and failure injection. 7. **Resource:** exact create/retire counts, heap diff, GPU accounting, no stale owner or queued work at stable checkpoints. 8. **Performance:** capped and uncapped CPU/GPU/allocation/GC percentiles. 9. **Headless parity:** same runtime packet/order/command results with presentation attached or absent. 10. **Platform:** Windows graphical/headless plus Linux headless; Linux graphical begins in Slice L. For a replacement cutover: - Shadow/compare mode may exist only during development. - Confirmed differences are fixed at their source. - The gate commit removes the old production path. - No test threshold is loosened to accept a regression. **Adjudication protocol (2026-07-24 review).** When a cutover comparison disagrees: (1) any confirmed OLD-path bug fixed during the cutover gets its divergence-register row or `docs/ISSUES.md` entry in the same commit; (2) visual differences are adjudicated by the user (visual verification is the user's role per CLAUDE.md), never self-certified; (3) any threshold change requires a written rationale in the gate commit — "the new output is an improvement" carries the same evidence burden as a regression claim. --- ## 13. Risks and controls | Risk | Control | |---|---| | Pak repeats the 865 GB failure | content identity and aliasing land before another full bake; bake reports unique/shared ratios continuously | | Prepared data changes pixels/geometry | byte/field equivalence against the current extractor; no compression in the equivalence phase | | Streaming budget reveals an incomplete world | one destination-generation readiness barrier; reveal is an atomic state edge | | Deferred retirement leaks or revives stale resources | incarnation/generation owner tokens and stable-checkpoint zero-outstanding assertions | | Render ECS becomes a second game world | render-only component contract; no network properties or authoritative GUID map; digest checks against canonical owner | | Snapshot adds a frame of latency | same-thread borrowed snapshot first; frame-number assertions; concurrency remains deferred | | Arch dependency constrains the design | acdream-owned `IRenderScene`; Arch only in App; no Arch types cross the boundary | | Headless extraction duplicates state | move the existing canonical owner in lifetime groups; adapters first, never synchronize two stores | | Shared 30-client cache gains mutable state | immutable content only; tests create conflicting sessions/characters concurrently | | Linux work forces renderer fallback | graphical host retains mandatory modern extensions and fails clearly on unsupported drivers | | “Optimization” changes retail behavior | unchanged behavior is a hard gate; retail ports remain OO and traceable; visual/connected gates stay mandatory | --- ## 14. Current execution point Slices A–H are complete. The retained render scene is now the sole production entity source: G5 removed the old whole-world `InteriorEntityPartition` from normal mesh and attached-particle routing while preserving it only for standalone tests and explicit diagnostic/oracle probes. The user accepted the retained-path visual matrix. Capped/uncapped/dense routes and the exact seven-checkpoint lifecycle/reconnect gate pass with graceful shutdown. The final ordinary-production minute on exact `14fbe92b` sustained 519.7 FPS: CPU p50/p95/p99 1.869/2.306/2.484 ms and GPU 1.096/1.108/1.136 ms. Working/private memory was 652.1/928.3 MiB. Frame-thread allocation at the G5 checkpoint was 22.34 KiB median. I1 has now replaced fresh per-resolve transition graphs and query temporaries with reset-complete retained scratch: player, remote, projectile, camera, and grounded walkable-publication profiles each measure 0 B/resolve, while fresh-vs-reused output and body state remain bit-identical. I2 now supplies validated immutable flat collision records: synthetic and installed-DAT source identity, exact float bits, deterministic pre-order, and corruption tripwires pass without changing production traversal. I3 now packages all flat collision/topology records through a shared typed mmap source: two complete 2,232,170-key bakes are byte-identical with zero failures. I4–I6 then proved exact flat traversal, strict dual publication, and flat-authoritative gameplay before removing every production parsed collision graph. Slice I7 passes 8,413 Release tests / 5 skips plus both exact-binary connected routes; every stable checkpoint reports zero retained parsed collision graphs. J1 is complete with borrowed contracts, and J2 owns canonical session/transport lifetime plus ordered inbound routing in Runtime. J3.1–J3.6 are complete: Runtime owns the only entity GUID/incarnation/local-ID directory, and App's materialized sidecars plus spatial/presentation worksets carry exact `RuntimeEntityKey`. One `RuntimeEntityObjectLifetime` also owns the exact live `ClientObjectTable`; App is a synchronous borrower. Exact `ce3ac310` adds the one per-generation canonical entity/object delta stream, issues Runtime identity before App hydration, and gives graphical/no-window hosts the same direct borrowed views. J3.6 at `119b7c11` retains exact teardown receipts before fallible callbacks, serializes re-entrant observation within the committing call, protects every accepted channel by mutation version, and requires reset/direct disposal to converge the complete ownership ledger to zero. Its 8,484-test / 5-skip Release suite, exact-binary lifecycle/reconnect gate, and canonical nine-stop route pass. J4 is complete at `89e6b207`: the single Runtime shortcut/spellbook/inventory owners, graphical/no-window command parity, failure-safe combined ledger, 8,544-test Release suite, exact-binary lifecycle/reconnect gate, and canonical nine-stop route pass. J5 movement/physics/interaction/combat ownership is the current execution point. J5.1–J5.5 are complete; J5.6 projectile runtime is active. Evidence: [`../research/2026-07-25-slice-g5-production-profile.md`](../research/2026-07-25-slice-g5-production-profile.md) and [`../research/2026-07-25-slice-i1-transition-scratch.md`](../research/2026-07-25-slice-i1-transition-scratch.md) and [`../research/2026-07-25-slice-i2-flat-collision-schema.md`](../research/2026-07-25-slice-i2-flat-collision-schema.md) and [`../research/2026-07-25-slice-i3-prepared-collision-package.md`](../research/2026-07-25-slice-i3-prepared-collision-package.md) and [`../research/2026-07-25-slice-i7-closeout.md`](../research/2026-07-25-slice-i7-closeout.md) and [`../research/2026-07-25-slice-j2-session-lifetime-closeout.md`](../research/2026-07-25-slice-j2-session-lifetime-closeout.md) and [`../research/2026-07-25-slice-j3-3-exact-projection-store.md`](../research/2026-07-25-slice-j3-3-exact-projection-store.md) and [`../research/2026-07-26-slice-j3-5-canonical-delta-stream.md`](../research/2026-07-26-slice-j3-5-canonical-delta-stream.md) and [`../research/2026-07-26-slice-j3-6-lifetime-closeout.md`](../research/2026-07-26-slice-j3-6-lifetime-closeout.md) and [`2026-07-25-modern-runtime-slice-j.md`](2026-07-25-modern-runtime-slice-j.md). The intended order is therefore: ```text honest metrics + committed baselines (A — exit criteria block C) -> prepared content (B, C) -> typed residence (D — unify/retune, not rebuild) -> cost-budgeted streaming (E) -> [APPROVED 2026-07-24: ECS deferral lifted under §0.1] -> incremental render scene (F) -> delta GPU submission (G) -> residual frame cleanup (H-a, H-b, H-c) -> flat collision assets (I — COMPLETE) -> presentation-independent runtime (J — CURRENT at J5) -> Linux/headless/multi-session (K) -> evidence-gated GPU jobs (L) ``` This order improves the current graphical client first while laying clean boundaries for Linux and approximately 30 automated headless clients later. **Execution discipline (2026-07-24 review):** this plan runs in dedicated Track MP/LH side-track sessions only; the active milestone's critical path wins every conflict (§0.2). Slice I appears in this list explicitly because both earlier order summaries omitted it. The 2026-07-24 review also landed a set of independent fixes ahead of the plan (receive-thread robustness, bounded audio caches, dead ParticleBatcher chain deletion, CellStruct clone guard, solid-color texture cache, EnvCellRenderer prepare gating, measurement tooling) — those are recorded in the relevant slices above as "landed post-review" and must not be re-done.