docs(runtime): close J3.3 and hand off J3.4

Record exact projection-store ownership, automated and connected evidence, rollback chain, and the executor-ready canonical object-table plan. Reconcile milestones, roadmap, architecture, divergence pointers, AGENTS/CLAUDE, and durable handoff state.

Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-25 22:29:34 +02:00
parent e937cc36df
commit d9bf4c4960
15 changed files with 757 additions and 114 deletions

View file

@ -213,8 +213,14 @@ operation maps; `RuntimeEntityRecord` is presentation-free and App records are
graphical sidecars. Runtime tests pass 110/110, App tests pass 3,758/3 skips,
the complete Release suite passes 8,441/5 skips, and the exact-binary seven-
checkpoint lifecycle/reconnect gate passes with graceful exits, zero render-
shadow mismatches, and zero pending deltas. J3.3 exact-key App projection-store
cutover is next. J0's rollback is
shadow mismatches, and zero pending deltas. J3.3 is complete at `420e5eea`
plus exact-spatial correction `e937cc36`: `LiveEntityProjectionStore` and every
materialized presentation/spatial owner now use exact `RuntimeEntityKey`; the
temporary GUID-shaped compatibility view is deleted. App tests pass 3,761/3
skips, the complete Release suite passes 8,444/5 skips, and the final exact-
binary seven-checkpoint gate passes with two graceful exits, zero failures,
zero render-shadow mismatches, and zero pending deltas. J3.4 canonical
`ClientObjectTable` ownership is next. J0's rollback is
`git revert b632672e5ccabfb44c551e08f1c411ab2669c44a`.
J1's rollback is
`git revert 854d9e9cd13092bd5aaa3cf025d73eeb4600e9f8`.
@ -222,11 +228,15 @@ J2's rollback is
`git revert 75930787741db40a83eab8663e4464dce8d687ba`.
J3.2's rollback is
`git revert f46ddb5cdb1e145752bea49aeb1d62bfe71284d3`.
J3.3's rollback is, newest first,
`git revert e937cc36df39cf6ea1eaba2f9c0243d1929da702` then
`git revert 420e5eea70fd2c29cf9c8614e6298a1f84d64045`.
Slice H's retained UI/frame, bounded-light, and pooled/borrowed/direct network
work is complete; the exact seven-checkpoint connected lifecycle/reconnect gate
passes. Slice I is closed in
`docs/plans/2026-07-25-modern-runtime-slice-i.md`; the coherent Slice J campaign
executes from `docs/plans/2026-07-25-modern-runtime-slice-j.md`. The exact G4 visual rollback
executes from `docs/plans/2026-07-25-modern-runtime-slice-j.md`; J3.4 starts at
`docs/plans/2026-07-25-modern-runtime-slice-j3-object-table.md`. The exact G4 visual rollback
is `git revert ef1d263337997bb030eadb7b8e71d73dc659907a`; do not revert G3 or
the portal-warmup corrections. Evidence:
`docs/research/2026-07-25-slice-h-closeout.md` and
@ -239,7 +249,10 @@ the portal-warmup corrections. Evidence:
`docs/research/2026-07-25-slice-i6-flat-production-cutover.md` and
`docs/research/2026-07-25-slice-i7-closeout.md` and
`docs/research/2026-07-25-slice-j1-runtime-contract-closeout.md` and
`docs/research/2026-07-25-slice-j2-session-lifetime-closeout.md`.
`docs/research/2026-07-25-slice-j2-session-lifetime-closeout.md` and
`docs/research/2026-07-25-slice-j3-2-canonical-entity-directory-closeout.md` and
`docs/research/2026-07-25-slice-j3-3-exact-projection-store.md`. Agent handoff:
`docs/research/2026-07-25-slice-j3-3-complete-j3-4-handoff.md`.
**Structural prerequisite before new M4 subsystem work:** all eight
behavior-preserving `GameWindow` decomposition slices and the automated

View file

@ -211,8 +211,14 @@ operation maps; `RuntimeEntityRecord` is presentation-free and App records are
graphical sidecars. Runtime tests pass 110/110, App tests pass 3,758/3 skips,
the complete Release suite passes 8,441/5 skips, and the exact-binary seven-
checkpoint lifecycle/reconnect gate passes with graceful exits, zero render-
shadow mismatches, and zero pending deltas. J3.3 exact-key App projection-store
cutover is next. J0's rollback is
shadow mismatches, and zero pending deltas. J3.3 is complete at `420e5eea`
plus exact-spatial correction `e937cc36`: `LiveEntityProjectionStore` and every
materialized presentation/spatial owner now use exact `RuntimeEntityKey`; the
temporary GUID-shaped compatibility view is deleted. App tests pass 3,761/3
skips, the complete Release suite passes 8,444/5 skips, and the final exact-
binary seven-checkpoint gate passes with two graceful exits, zero failures,
zero render-shadow mismatches, and zero pending deltas. J3.4 canonical
`ClientObjectTable` ownership is next. J0's rollback is
`git revert b632672e5ccabfb44c551e08f1c411ab2669c44a`.
J1's rollback is
`git revert 854d9e9cd13092bd5aaa3cf025d73eeb4600e9f8`.
@ -220,11 +226,15 @@ J2's rollback is
`git revert 75930787741db40a83eab8663e4464dce8d687ba`.
J3.2's rollback is
`git revert f46ddb5cdb1e145752bea49aeb1d62bfe71284d3`.
J3.3's rollback is, newest first,
`git revert e937cc36df39cf6ea1eaba2f9c0243d1929da702` then
`git revert 420e5eea70fd2c29cf9c8614e6298a1f84d64045`.
Slice H's retained UI/frame, bounded-light, and pooled/borrowed/direct network
work is complete; the exact seven-checkpoint connected lifecycle/reconnect gate
passes. Slice I is closed in
`docs/plans/2026-07-25-modern-runtime-slice-i.md`; the coherent Slice J campaign
executes from `docs/plans/2026-07-25-modern-runtime-slice-j.md`. The exact G4 visual rollback
executes from `docs/plans/2026-07-25-modern-runtime-slice-j.md`; J3.4 starts at
`docs/plans/2026-07-25-modern-runtime-slice-j3-object-table.md`. The exact G4 visual rollback
is `git revert ef1d263337997bb030eadb7b8e71d73dc659907a`; do not revert G3 or
the portal-warmup corrections. Evidence:
`docs/research/2026-07-25-slice-h-closeout.md` and
@ -237,7 +247,10 @@ the portal-warmup corrections. Evidence:
`docs/research/2026-07-25-slice-i6-flat-production-cutover.md` and
`docs/research/2026-07-25-slice-i7-closeout.md` and
`docs/research/2026-07-25-slice-j1-runtime-contract-closeout.md` and
`docs/research/2026-07-25-slice-j2-session-lifetime-closeout.md`.
`docs/research/2026-07-25-slice-j2-session-lifetime-closeout.md` and
`docs/research/2026-07-25-slice-j3-2-canonical-entity-directory-closeout.md` and
`docs/research/2026-07-25-slice-j3-3-exact-projection-store.md`. Agent handoff:
`docs/research/2026-07-25-slice-j3-3-complete-j3-4-handoff.md`.
**Structural prerequisite before new M4 subsystem work:** all eight
behavior-preserving `GameWindow` decomposition slices and the automated

View file

@ -4,7 +4,7 @@ This page is the entry point for project documentation. It distinguishes
current sources of truth from implementation history so an old plan or issue
banner cannot silently override the current program state.
## Current snapshot — 2026-07-24
## Current snapshot — 2026-07-25
- **Milestone state:** M3, “Cast a spell,” landed 2026-07-21. M4, “Live in the
world,” is active.
@ -14,38 +14,32 @@ banner cannot silently override the current program state.
[`plans/2026-07-23-world-interaction-completion.md`](plans/2026-07-23-world-interaction-completion.md).
The eight-slice `GameWindow` decomposition and its connected visual matrix
are complete.
- **Current active engineering program:** Slices AE of
- **Current active engineering program:** Slices AI and J0J3.3 of
[`plans/2026-07-24-modern-runtime-architecture.md`](plans/2026-07-24-modern-runtime-architecture.md)
are complete. Production world-mesh streaming consumes the validated
prepared package, typed residency is bounded/accounted, and streaming now
has typed admission plus immediate old-generation quiescence, metered
retirement, and retained cursor-budgeted publication across render, physics,
statics, building registries, collision/reflood, and EnvCells. The canonical
reveal generation now owns an exact destination reservation across every
typed budget dimension; incomplete destinations remain in portal space and
receive retail's centered wait cue instead of forced early reveal. E6's
deterministic/resource gates and capped, uncapped, and dense-Arwic
physical-local routes pass. Slices FL, including the F/G and J approval
gates, were explicitly authorized 2026-07-24; Slice F is active.
Their post-E evidence, ownership map, shadow-comparison design, cutover
sequence, and acceptance matrix are implementation-ready in the
[F/G render-scene plan](plans/2026-07-24-modern-runtime-slices-f-g-render-scene.md).
See also the [Slice E streaming report](research/2026-07-24-slice-e-cost-budgeted-streaming-report.md).
are complete. Production uses prepared world/collision packages, typed
residence, retained render-scene/delta submission, bounded frame owners, and
flat-authoritative collision without parsed-graph production residency.
Runtime owns canonical session/transport and the sole live-entity
GUID/incarnation/local-ID directory. App materialized projections,
presentation worksets, and spatial visibility carry exact
`RuntimeEntityKey`. J3.4 canonical object-table ownership is next in
[`plans/2026-07-25-modern-runtime-slice-j3-object-table.md`](plans/2026-07-25-modern-runtime-slice-j3-object-table.md).
- **Completed gates:** R6 locomotion/collision/projectile/teleport/radar and
the final two-client portal-out/materialization observer comparison (`#218`).
- **Separate visual verification:** the shared-alpha lifestone/particle result
in `#225`; its connected resource-lifetime and performance routes pass.
- **Carried behavior debt:** `#153` (far teleport onto an unstreamed edge),
`#116` (two narrowed slide-response cases), and divergence rows TS-50/TS-51.
- **Active engineering track:** render ECS/MP3 is executing from the
implementation-ready F/G plan, followed by the approved Linux/headless
automation slices. The pre-F/G runtime rollback anchor is `e7d9d6fa`.
- **Active engineering track:** complete Slice J's coherent Runtime lifetime
groups, then build the approved Linux/headless multi-session host in Slice K.
J3.3's exact rollback is `git revert e937cc36` followed by
`git revert 420e5eea`.
- **Divergence audit:** 178 active rows — IA 17, AD 37, AP 85, TS 34,
and UN 5 — plus the retained retired TS-37 history note.
- **Latest automated baseline:** Release build succeeds; 8,164 tests pass and
five are intentionally skipped. The capped, uncapped, and dense-Arwic
physical prepared-asset routes all completed their portal/input/lifetime
assertions and disconnected gracefully.
- **Latest automated baseline:** Release build succeeds; 8,444 tests pass and
five are intentionally skipped. The exact J3.3 connected lifecycle/reconnect
gate completed two sessions and seven checkpoints with graceful zero-code
exits, zero failures, zero render-shadow mismatches, and zero pending deltas.
## Sources of truth

View file

@ -209,7 +209,12 @@ src/
ordered deltas, clock, generation + teardown
Session/ -> J2 canonical session lifetime, ordered
inbound routing + retryable teardown
-> J3 identity, properties, and object-table extraction active
Entities/
RuntimeEntityDirectory.cs -> sole GUID/incarnation/local-ID authority
RuntimeEntityRecord.cs -> presentation-free accepted entity state
InboundPhysicsStateController.cs -> retail timestamp/snapshot authority
ParentAttachmentState.cs -> generation-exact parent relations
-> J3.1-J3.3 complete; J3.4 canonical object-table ownership next
-> may reference Core, Core.Net, Content, and Plugin.Abstractions only
-> must never reference App, UI, Silk.NET, OpenAL, Arch, or ImGui
@ -230,7 +235,8 @@ src/
RemoteTeleportHook.cs -> ordered retail teleport teardown seam
RemoteTeleportPlacement.cs -> collision-seated SetPosition transition commit
World/
LiveEntityRuntime.cs -> canonical identity/state/body/projectile/spatial ownership
LiveEntityRuntime.cs -> exact-key App projection/lifecycle host
LiveEntityProjectionStore.cs -> materialized sidecars by RuntimeEntityKey
RetailInboundEventDispatcher.cs -> update-thread packet/frame FIFO barrier
LiveEntityPresentationController.cs -> Hidden/NoDraw/effect/collision presentation
LiveEntityTeardown.cs -> failure-isolated multi-owner lifecycle drain
@ -447,26 +453,38 @@ Ownership by phase:
## GameEntity: The Unified Entity (target refactor)
`LiveEntityRuntime` is now the shipped bridge to this target. It owns one
`LiveEntityRecord` per accepted server-object incarnation, ServerGuid-to-local-id
translation, accepted snapshots/timestamp gates, the canonical `PhysicsBody`,
animation/remote-motion/projectile components, parent-event state,
effect-profile defaults, and exact logical
teardown. Live `PhysicsDesc` effect fields replace Setup defaults on that same
record; rebucketing never recreates them. `GpuWorldState`
owns spatial buckets only: register/rebucket/unregister are separate operations,
logical teardown removes the exact `WorldEntity` incarnation rather than every
projection sharing its server GUID, and per-GUID/session mutation epochs prevent
a callback from resurrecting an outer CreateObject after delete/reset. Per-GUID
epoch tombstones remain until session clear, so delete-then-create cannot repeat
an outer operation's epoch (the ABA problem). Resource registration is an atomic
boundary, superseded cleanup failures surface at the runtime boundary, and
visibility observers are failure-isolated without interrupting canonical commits.
Landblock reloads reuse the same `WorldEntity` without replaying renderer or
script creation. Its canonical materialized view remains stable across pending
landblocks, while a separate visible-only view feeds radar, picking, status, and
targeting. Raw server PhysicsState and the final state produced by retail's
ordered Lighting/NoDraw/Hidden side effects are stored separately.
The shipped bridge is now split at the Runtime/App boundary.
`AcDream.Runtime.Entities.RuntimeEntityDirectory` owns the only current
server-GUID/incarnation map, Runtime local-ID allocation/reverse lookup,
accepted snapshots and timestamp gates, parent state, session/operation
versions, and exact tombstones. `RuntimeEntityRecord` is presentation-free.
`LiveEntityRuntime` is the App projection/lifecycle host.
`RegisterLiveEntity` first creates or refreshes canonical Runtime state without
an App record. `MaterializeLiveEntity` claims the Runtime local ID and creates
one graphical `LiveEntityRecord` sidecar in `LiveEntityProjectionStore`, keyed
by exact `RuntimeEntityKey` (local ID plus `INSTANCE_TS`). The sidecar owns the
App animation, remote-motion, projectile, hydration, and effect presentation
state. `RuntimeEntityRecord` deliberately still carries the canonical
`PhysicsBody`/physics-host facts until their coherent J5 move.
Live `PhysicsDesc` effect fields replace Setup defaults on that same exact
sidecar; rebucketing never recreates it.
`GpuWorldState` owns spatial buckets only and retains the exact projection key
through loaded/pending movement, landblock retirement/reload, origin recenter,
quiescence, and visibility transition delivery. Register, rebucket, withdraw,
and unregister remain separate operations. Logical teardown removes the exact
`WorldEntity` incarnation rather than every projection sharing its server GUID.
Runtime per-GUID/session operation versions prevent a callback from
resurrecting an outer CreateObject after delete/reset, and exact tombstones
prevent ABA reuse. Resource registration is an atomic boundary, superseded
cleanup failures surface at the runtime boundary, and visibility observers are
failure-isolated without interrupting canonical commits. Landblock reloads
reuse the same `WorldEntity` without replaying renderer or script creation.
Focused exact-key materialized queries remain stable across pending landblocks;
focused visible queries feed radar, picking, status, and targeting. Raw server
PhysicsState and the final state produced by retail's ordered
Lighting/NoDraw/Hidden side effects are stored separately.
When retail's 25-second leave-visibility deadline expires, active ownership
still ends completely. The ACE adapter retains only the accepted
`EntitySpawn` in `DormantLiveEntityStore`, because ACE can keep that GUID in
@ -494,10 +512,11 @@ does not depend on Content or a database implementation.
Runtime effect delivery is owned by App-layer `EntityEffectController`. It
retains pre-materialization F754/F755 packets in one mixed FIFO per server GUID,
resolves the canonical local ID through `LiveEntityRuntime` only after
renderer/resources/profile are ready, and resolves typed/default/part-default
hooks through the live owner's current `EntityEffectProfile`. It does not own a
second GUID map. The same profile publishes Setup/static or network/live
asks Runtime for the current canonical incarnation, then resolves the exact
App projection only after renderer/resources/profile are ready. It resolves
typed/default/part-default hooks through that projection's current
`EntityEffectProfile`; it does not own a second GUID map. The same profile
publishes Setup/static or network/live
SoundTable ownership at readiness and on every same-generation PhysicsDesc
replacement, including present-zero clearing. Core `PhysicsScriptRunner` mirrors retail's one
serial FIFO per owner: duplicate plays append, owners progress independently,
@ -920,7 +939,8 @@ Current movement/collision ownership:
- **B.3** is shipped MVP history: first collision resolver foundation.
- **L.1/R6** own animation/motion parity and the complete retail object-frame
update order over canonical `LiveEntityRuntime` records.
update order over canonical Runtime records plus exact-key App projection
sidecars.
- **L.2** owns the remaining movement and collision conformance:
`docs/plans/2026-04-29-movement-collision-conformance.md`.
- **G.3 is shipped**: dungeon streaming, portal-space presentation, collision,

View file

@ -47,8 +47,9 @@ At the campaign baseline, `GameWindow` was the single object that:
- Composes the shipped `LiveSessionController`/host/router boundary; it no
longer owns a parallel session, command bus, subscription lifetime, connect,
character-entry, reconnect, or graceful-close body.
- Owns the adapters that hydrate canonical `LiveEntityRuntime` records into
animation, collision, rendering, and DAT-backed appearance resources.
- Owns the adapters that hydrate canonical Runtime entity records into
exact-key App projection sidecars and their animation, collision, rendering,
and DAT-backed appearance resources.
- Composes the shipped `WorldSelectionQuery` and
`SelectionInteractionController`; it no longer owns world-picking,
selection intent, Use/PickUp, or auto-walk deferral algorithms.
@ -298,7 +299,8 @@ What `GameWindow` loses:
- Live connect/enter/logout and event-subscription bodies → completed inside
`LiveSessionController` / `LiveSessionEventRouter`.
- Live-object hydration adapters and final animated-part presentation → focused
world/render owners over canonical `LiveEntityRuntime` records.
world/render owners over exact-key App projection sidecars backed by
canonical Runtime records.
- `WorldPicker`, target queries, and selection-driven Use/PickUp/auto-walk →
`WorldSelectionQuery` + `SelectionInteractionController`. Core
`SelectionState` remains the injected session owner.
@ -310,27 +312,33 @@ What `GameWindow` loses:
The eventual `GameEntity` aggregation (target state described in
`acdream-architecture.md` §"GameEntity: The Unified Entity") happens
**after** `LiveEntityRuntime` is the single owner of entity state.
Until then, the parallel-dicts problem is bounded inside one class
instead of spread across `GameWindow`.
**after** `RuntimeEntityDirectory` is the single owner of canonical entity
state. That boundary is now shipped. App's former parallel presentation
dictionaries are bounded by one exact-key projection store rather than spread
across `GameWindow`.
`LiveEntityRuntime` is now that single boundary. It composes
`InboundPhysicsStateController` for the nine-channel retail timestamp gates and
latest accepted immutable CreateObject snapshot, owns the canonical local ID
and optional runtime components, and separates logical registration from
spatial projection. It also retains raw PhysicsState separately from the final
state produced by retail's ordered side effects;
`LiveEntityPresentationController` projects those transitions into draw,
collision, effect, child-NoDraw, and target visibility without becoming a
second lifetime or GUID owner. `ParentAttachmentState` is runtime-owned and keys unresolved
relations by child and parent generation. `Rendering/Vfx/EntityEffectController`
owns the focused mixed F754/F755 pending FIFO, effect profiles, typed-table
resolution, and a readiness set; canonical ServerGuid-to-local-ID translation
always stays in `LiveEntityRuntime`. `EntityScriptActivator` uses the same
canonical `WorldEntity.Id` as rendering and physics; the disjoint static ID
allocators fail fast instead of wrapping into another landblock's namespace.
All other non-Parent packet
families still need the future general queue tracked by divergence AD-32.
`RuntimeEntityDirectory` owns the only active server-GUID/incarnation map,
Runtime local-ID allocation/reverse lookup, accepted immutable CreateObject
snapshot and nine-channel timestamp gates, parent relations, operation
versions, and exact teardown tombstones. `RuntimeEntityRecord` is
presentation-free. `LiveEntityRuntime` is the App projection/lifecycle host:
it creates a `LiveEntityRecord` sidecar only after Runtime local-ID claim and
stores it in `LiveEntityProjectionStore` by exact `RuntimeEntityKey`.
Hydration, animation, effects, lights, equipped children, render identity,
movement observations, liveness, teardown, and spatial worksets borrow that
same exact identity. `LiveEntityPresentationController` projects Runtime state
transitions into draw, collision, effects, child-NoDraw, and target visibility
without becoming a second canonical owner.
`Rendering/Vfx/EntityEffectController` owns the focused mixed F754/F755 pending
FIFO, effect profiles, typed-table resolution, and readiness markers. A GUID
is retained there only as pending wire-delivery metadata; current-incarnation
resolution always asks `RuntimeEntityDirectory` and then the exact projection
store. `EntityScriptActivator` uses the same Runtime-issued `WorldEntity.Id` as
rendering and physics; disjoint static ID allocators fail fast instead of
wrapping into another landblock's namespace. All other non-Parent pre-create
packet families still need the future general queue tracked by divergence
AD-32.
The per-frame object scheduler is extracted, but final animated-part
presentation is not. `LiveEntityAnimationScheduler` snapshots canonical spatial
@ -348,8 +356,8 @@ is deliberately separate: `RetailStaticAnimatingObjectScheduler` owns the
Static owners with Setup DefaultAnimation. Both schedulers are wired by
`GameWindow`, but neither owns GUID identity or logical resources.
The typed animation view fills its reusable snapshot and render-ID set from
`LiveEntityRuntime`'s concrete spatial dictionary, avoiding interface-enumerator
boxing on both update and render hot paths.
the exact-key projection workset, avoiding interface-enumerator boxing on both
update and render hot paths.
`RemoteInboundMotionDispatcher` similarly keeps UpdateMotion protocol behavior
outside `GameWindow`: GameWindow resolves the canonical record/body and the
optional PartArray sink, while one dispatcher owns retail's interrupt, style,
@ -408,7 +416,8 @@ not `GameWindow`: it retains at most one pending request per materialized
incarnation, scopes it by the live generation and accepted PositionSequence,
and asks `RemoteTeleportPlacement` to collision-seat the current body when the
destination projection is available. `GameWindow` supplies lifecycle and
shadow-sync callbacks only; canonical identity remains in `LiveEntityRuntime`.
shadow-sync callbacks only; canonical identity remains in
`RuntimeEntityDirectory`, and App placement retains the exact projection key.
Failed hydration restores the captured source and delegates an
incarnation-scoped shadow restore to `LiveEntityPresentationController` while
that source is unloaded, so Hidden/UnHide and teleport never become competing
@ -459,16 +468,16 @@ useful ordering seam, but its ownership status is **partial**.
| Area | Status | Current truth |
|---|---|---|
| Startup options | **Complete** | `RuntimeOptions` owns startup configuration (`eda936dc`). Remaining direct environment reads are legacy runtime diagnostics, not startup configuration. |
| Network session | **Complete** | `LiveSessionController` remains the sole resolve/create/Connect/selection/EnterWorld/Tick/stop/reconnect/disposal and current-session owner. `LiveSessionHost` owns App reset/selection/entry ordering plus create→attach route factories; `LiveSessionLifecycleHost` guards exact borrowed-session attachment. `LiveSessionEventRouter` and `LiveSessionCommandRouter` own exact inbound/outbound lifetimes, with retryable per-edge teardown before reset or replacement. `GameWindow` retains the controller, focused host, and typed domain binding factories—no mirrored session or reset plan. |
| Network session | **Complete Runtime ownership** | `RuntimeLiveSessionController` owns the sole `WorldSession` generation and resolve/create/Connect/selection/EnterWorld/Tick/stop/reconnect/disposal transaction. Runtime route owners preserve exact inbound/outbound ordering and retryable teardown. App supplies immutable options, graphical/domain callbacks, and one borrowed inertable UI command projection—no mirrored session or reset plan (`75930787`). |
| World environment | **Complete ownership** | `WorldEnvironmentController` solely owns WorldTime, the loaded DAT sky, current day group, Weather, server synchronization, AdminEnvirons bridging, and debug time/weather cycles. `GameWindow` keeps ABI-compatible readonly aliases to the same clock/weather instances and composes one render source plus the two live-session callbacks. TS-54/TS-55 register the remaining centered UI sound and full fog/ambient/radar behavior gaps. |
| Live identity/lifetime | **Complete App integration** | `LiveEntityRuntime` owns incarnation identity, accepted snapshots/timestamps, runtime components, and logical/spatial lifetime. `LiveEntityHydrationController`, `LiveEntityRuntimeTeardownController`, and `LiveEntityNetworkUpdateController` own DAT-backed hydration, retryable cleanup, and accepted Position/Vector/State/Movement presentation without a second GUID owner (`aa90c646`). |
| Live identity/lifetime | **J3.3 complete** | `RuntimeEntityDirectory` owns the sole GUID/incarnation/local-ID identity, accepted snapshots/timestamps, parent state, operation versions, and tombstones. `LiveEntityProjectionStore` owns App graphical sidecars by exact `RuntimeEntityKey`; hydration, presentation components, `GpuWorldState` residence/visibility, and retryable teardown preserve that key without another GUID authority (`f46ddb5c`, `420e5eea`, `e937cc36`). |
| Inbound/object-frame order | **Complete App orchestration** | `UpdateFrameOrchestrator` owns the complete typed host phase graph; `RetailInboundEventDispatcher`, `RetailLiveFrameCoordinator`, `LiveObjectFrameController`, `LiveSpatialPresentationReconciler`, streaming/input/teleport/player-mode/camera owners preserve the accepted order. `GameWindow.OnUpdate` is one profiler-scoped handoff (`e91f3102`). |
| World reveal | **Complete** | `WorldRevealCoordinator` owns login/portal readiness, generation quiescence, and the exact streaming destination reservation. Login auto-entry and portal completion close only their active generation; stale completion cannot clear its replacement. Delayed portals retain the authored tunnel and centered retail wait notice rather than force-revealing incomplete content. Quiesced destination projections may become spatially resident for renderer preparation while all availability-gated gameplay consumers stay closed. |
| Retained gameplay UI | **Mostly complete feature ownership** | `RetailUiRuntime` and focused panel/controllers own layout and behavior. `GameWindow.OnLoad` still performs substantial service composition, which is allowed until the final composition cleanup. |
| Selection/interaction | **Complete** | `WorldSelectionQuery` owns read-only picking/classification/range queries; `SelectionInteractionController` owns selection intent, Use/PickUp transport, exact-incarnation queues, and auto-walk deferral; `ItemInteractionController` owns ItemHolder policy plus the shared retail inventory-request transaction. `GameWindow` retains construction and narrow lifecycle forwarding only. |
| Landblock presentation | **Complete** | `LandblockBuildFactory` owns the captured-origin DAT transaction; concrete render/physics/DAT-static publishers and `LandblockPresentationPipeline` own typed-meter publication and exact retryable retirement. `StreamingController` owns stable destination/control/unload/Near/Far queues and destination reservation. CPU mesh-cache restaging requires an exact live owner. `StreamingOriginRecenterCoordinator` serializes old-window retirement with teleport/session origin lifetimes. `GameWindow` retains construction and one pipeline field only (`c79d0a49`, closeout `4a205a3e`; Slice E closeout `91e82c3c`). |
| Render-frame orchestration | **Complete** | `RenderFrameOrchestrator` owns the GPU-flight, resource, world/PView/shared-alpha, private-presentation, diagnostics, screenshot, and recovery graph. `GameWindow.OnRender` takes one logical window-size snapshot and performs one immutable handoff (`9d7df1bf`). |
| Unified `GameEntity` | **Deferred target** | `LiveEntityRuntime` already supplies the critical canonical owner. Full type aggregation is a separate high-risk migration and is not required to make `GameWindow` thin. |
| Unified `GameEntity` | **Slice J active** | Canonical identity is now in Runtime and exact graphical sidecars are in App. J3.4 next moves `ClientObjectTable`; later J4J7 move coherent gameplay lifetime groups before one Runtime composition root. |
### 4.3 Revised extraction sequence

View file

@ -92,7 +92,7 @@ accepted-divergence entries (#96, #49, #50).
| AD-28 | Chat transcript (`UiText`) and input (`UiChatInput`) are two separate widget classes placed inside their dat-authored container panels; retail's `ChatInterface` uses a single mode-flagged `UIElement_Text` (Type-12) that switches between read and edit mode | `src/AcDream.App/UI/Layout/ChatWindowController.cs:135` (transcript) + `:150` (input) | `UIElement_Text` is inside keystone.dll with no PDB/decomp; a two-widget split is functionally equivalent (read-only scroll, editable input) and is the structural adaptation required by our UiElement architecture | A future consumer expecting a single widget for both read/write (e.g. a plugin calling the chat API and getting one widget back) must be written to the two-widget contract | `UIElement_Text` (Type-12) @ keystone.dll; `gmMainChatUI::PostInit` @0x4ce130 |
| AD-29 | `ClientObjectTable` fires global `ObjectAdded`/`ObjectUpdated`/`ObjectRemoved` events; consumers filter by guid on their end. Retail dispatches per-object via `NoticeRegistrar` observer dispatch — each UI cell observes only its specific object guid | `src/AcDream.Core/Items/ClientObjectTable.cs:48` (events); `src/AcDream.App/UI/Layout/ToolbarController.cs:115` (guid filter) | `NoticeRegistrar` is inside keystone.dll with no PDB/decomp; global broadcast + consumer-side filter is functionally equivalent for the current panel count and object volumes seen in practice | At high object counts (>1 000 objects), every `ObjectUpdated` wakes every subscribed consumer — O(n·m) notification cost instead of retail's O(1) per-observer dispatch; a consumer that forgets the guid filter processes all objects (a latent correctness bug) | `NoticeRegistrar` (keystone.dll, no PDB); retail per-object observer registration in `CObjectMaint` |
| AD-30 | Cell-march preserves seed landblock id when `TryGetTerrainOrigin` returns false for an outdoor seed (#145 D, 2026-06-22): `BuildCellSetAndPickContaining` returns `currentCellId` verbatim rather than marching via `blockOrigin=(0,0,0)`; retail never encounters this state (cells stored block-local, no streaming-gap concept) | `src/AcDream.Core/Physics/CellTransit.cs:765` | Equivalence argument: "preserve-verbatim when unregistered" is the same contract as `PhysicsEngine.Resolve`'s NO-LANDBLOCK branch; the player's cell stays the last known-correct cell until the landblock's terrain registers — no march, no lbX=0 wire | A body whose seed landblock is genuinely absent for >1 physics tick holds its last-known cell rather than discovering the true containing cell; transient only — corrects the instant terrain registers; an indoor seed is explicitly excluded from the guard (outdoor low < 0x100 gate) | `CObjCell::find_cell_list` + block-local storage (retail has no streaming gap); `TryGetTerrainOrigin` pc path |
| AD-32 | Movement, Position, State, Vector, Pickup, Delete, and ObjDesc packets for a FUTURE object incarnation are dropped until its CreateObject arrives; retail queues each blob for the not-yet-created object (`SmartBox::QueueBlobForObject`, dispatch return 4) and replays it after construction. F754/F755 and ParentEvent are no longer part of this divergence: `EntityEffectController` preserves one mixed effect FIFO per server GUID until the canonical local owner is ready, while runtime-owned `ParentAttachmentState` retains parent relations by generation. Older-incarnation state packets drop in both clients. | `src/AcDream.Core/Physics/PhysicsTimestampGate.cs` (`TryAcceptInstance`); `src/AcDream.App/World/InboundPhysicsStateController.cs`; `src/AcDream.App/World/LiveEntityRuntime.cs`; effect exception `src/AcDream.App/Rendering/Vfx/EntityEffectController.cs`; Parent exception `src/AcDream.App/World/ParentAttachmentState.cs` | acdream has no general per-object mixed queue for non-effect state packets yet. Dropping those state families is generation-safe: a future packet cannot mutate the old body or advance its stamps, and the newer CreateObject wholesale-seeds all nine channels. The canonical runtime remains the seam for widening the queue later. | The first queued non-effect state can be absent until a later authoritative update; effect and Parent packets retain retail order and ownership | `SmartBox::HandleSetState` 0x004533E0; `HandleVectorUpdate` 0x00453480; `HandleParentEvent` 0x004535D0; `HandlePickupEvent` 0x00453530; `HandleDeleteObject` 0x00451EA0; `HandleObjDescEvent` 0x00453340; `UnpackPositionEvent` 0x004542C0; F754/F755 dispatch and pending replay pc:357214-357239 |
| AD-32 | Movement, Position, State, Vector, Pickup, Delete, and ObjDesc packets for a FUTURE object incarnation are dropped until its CreateObject arrives; retail queues each blob for the not-yet-created object (`SmartBox::QueueBlobForObject`, dispatch return 4) and replays it after construction. F754/F755 and ParentEvent are no longer part of this divergence: `EntityEffectController` preserves one mixed effect FIFO per server GUID until the canonical local owner is ready, while runtime-owned `ParentAttachmentState` retains parent relations by generation. Older-incarnation state packets drop in both clients. | `src/AcDream.Core/Physics/PhysicsTimestampGate.cs` (`TryAcceptInstance`); `src/AcDream.Runtime/Entities/InboundPhysicsStateController.cs`; `src/AcDream.App/World/LiveEntityRuntime.cs`; effect exception `src/AcDream.App/Rendering/Vfx/EntityEffectController.cs`; Parent exception `src/AcDream.Runtime/Entities/ParentAttachmentState.cs` | acdream has no general per-object mixed queue for non-effect state packets yet. Dropping those state families is generation-safe: a future packet cannot mutate the old body or advance its stamps, and the newer CreateObject wholesale-seeds all nine channels. The canonical runtime remains the seam for widening the queue later. | The first queued non-effect state can be absent until a later authoritative update; effect and Parent packets retain retail order and ownership | `SmartBox::HandleSetState` 0x004533E0; `HandleVectorUpdate` 0x00453480; `HandleParentEvent` 0x004535D0; `HandlePickupEvent` 0x00453530; `HandleDeleteObject` 0x00451EA0; `HandleObjDescEvent` 0x00453340; `UnpackPositionEvent` 0x004542C0; F754/F755 dispatch and pending replay pc:357214-357239 |
| AD-33 | `CSequence.frame_number` at C# `double` (64-bit); retail is x87 `long double` (80-bit extended) — every frame-boundary comparison ran at extended precision on retail (Phase R1, 2026-07-02) | `src/AcDream.Core/Physics/Motion/CSequence.cs` (`FrameNumber`) | `double` is the widest C# float type; the R1 port removes ACE-style boundary epsilons so comparisons are exact-int against bare boundaries, minimizing ULP sensitivity (ACE's `float` is far worse) | A frame landing within 1 double-ULP of an integer boundary could classify differently than retail's 80-bit compare — sub-frame timing skew at pathological framerate×dt combinations | `acclient.h:30747` (`long double frame_number`) |
| AD-34 | Retail's intrusive `DLListBase`/`DLListData` lists are managed `LinkedList<T>`s; node identity via `LinkedListNode<>` references (Phase R1 anim list, 2026-07-02; extended R2-Q3 to `MotionTableManager.pending_animations`; extended R4-V2 to `MoveToManager.pending_actions` — whose node type, retail `MoveToManager::MovementNode`, is RENAMED `MoveToNode` to avoid colliding with R2's `Motion/MotionNode.cs` pending_motions node) | `src/AcDream.Core/Physics/Motion/CSequence.cs` (`_animList`); `src/AcDream.Core/Physics/Motion/MotionTableManager.cs` (`_pendingAnimations`); `src/AcDream.Core/Physics/Motion/MoveToManager.cs` (`_pendingActions`) + `MoveToNode.cs` | Same topology + cursor semantics (curr_anim/first_cyclic/tail-anchored scans are node references); unlink/delete becomes `Remove(node)`; conformance tests pin the surgery state tables | Any retail behavior depending on the 4 pointer adjustment or node memory reuse (none observed in the decomp) would diverge; a reader grepping for retail's `MovementNode` name must find it via this row | `acclient.h` DLListBase; `r1-csequence-decomp.md` §0; `r2-motiontable-decomp.md` §11; `r4-moveto-decomp.md` node factories §4a |
| AD-35 | `MotionTableManager.PerformMovement`'s unhandled-type default case returns the named sentinel `0xFFFFFFFF` (`MotionTableManagerError.NotHandled`); retail's compiled code leaks the `CSequence*` pointer reinterpreted as the return code (BN-confirmed artifact, dead/unreachable — callers gate on type first) (R2-Q3, 2026-07-02) | `src/AcDream.Core/Physics/Motion/MotionTableManager.cs` (`PerformMovement` default case) | No retail caller consults the return value for unhandled types (RawCommand/StopRawCommand/MoveTo\*/TurnTo\* route elsewhere); returning a stable non-zero sentinel preserves the only observable contract (non-zero = not success) without fabricating a pointer-shaped number | If a future port wires a caller that passes unhandled types AND branches on the exact return value, it would see `0xFFFFFFFF` where retail saw an arbitrary pointer — flag at that port | `PerformMovement` 0x0051c0b0 (`r2-motiontable-decomp.md` §11 default-case note) |

View file

@ -1687,8 +1687,14 @@ port in any phase — no separate listing here.
> tombstone, local-ID/reverse, accepted-wire, parent, and operation-version
> maps. Its 110 Runtime tests, 3,758 App tests / 3 skips, 8,441-test / 5-skip
> complete Release suite, and exact-binary seven-checkpoint connected gate pass
> with graceful exits and zero render-shadow mismatch. J3.3 exact-key App
> projection storage is next.
> with graceful exits and zero render-shadow mismatch. J3.3 is complete at
> `420e5eea` plus `e937cc36`: App's materialized sidecars, presentation
> worksets, spatial residence, quiescence, and visibility edges now carry the
> exact `RuntimeEntityKey`; the compatibility GUID view is deleted. Its 3,761
> App tests / 3 skips, 8,444-test / 5-skip complete Release suite, and exact-
> binary seven-checkpoint gate pass with two graceful exits, zero failures,
> zero render-shadow mismatches, and zero pending deltas. J3.4 canonical object-
> table ownership is next.
> The historical exact cutover
> rollback remains
> `git revert ef1d263337997bb030eadb7b8e71d73dc659907a`. Slice H is complete:

View file

@ -162,8 +162,14 @@ residency is `0/0/0` at every stable checkpoint and teardown converges. Slice I
local-ID/reverse, accepted-wire, parent, and operation-version maps in
Runtime. Its 110 Runtime tests, 3,758 App tests / 3 skips, 8,441 full Release
tests / 5 skips, and exact-binary seven-checkpoint connected gate pass with
graceful exits and zero render-shadow mismatch. J3.3 exact-key App projection
storage is next under
graceful exits and zero render-shadow mismatch. J3.3 is complete at
`420e5eea` plus exact-spatial correction `e937cc36`:
`LiveEntityProjectionStore` and every materialized presentation/spatial
workset now use exact `RuntimeEntityKey`; the GUID-shaped compatibility view
is deleted. Its 3,761 App tests / 3 skips, 8,444 full Release tests / 5 skips,
and exact-binary seven-checkpoint connected gate pass with two graceful
exits, zero failures, zero render-shadow mismatches, and zero pending deltas.
J3.4 canonical `ClientObjectTable` ownership is next under
[`2026-07-25-modern-runtime-slice-j.md`](2026-07-25-modern-runtime-slice-j.md).
Slice-I closeout evidence is
[`../research/2026-07-25-slice-i7-closeout.md`](../research/2026-07-25-slice-i7-closeout.md).
@ -171,6 +177,8 @@ Slice-J2 closeout evidence is
[`../research/2026-07-25-slice-j2-session-lifetime-closeout.md`](../research/2026-07-25-slice-j2-session-lifetime-closeout.md).
Slice-J3.2 closeout evidence is
[`../research/2026-07-25-slice-j3-2-canonical-entity-directory-closeout.md`](../research/2026-07-25-slice-j3-2-canonical-entity-directory-closeout.md).
Slice-J3.3 closeout evidence is
[`../research/2026-07-25-slice-j3-3-exact-projection-store.md`](../research/2026-07-25-slice-j3-3-exact-projection-store.md).
The historical exact G4 visual rollback remains
`git revert ef1d263337997bb030eadb7b8e71d73dc659907a`.

View file

@ -2,12 +2,14 @@
**Date:** 2026-07-24
**Status:** Slices AI and J0J2 are complete. Flat-authoritative collision, zero-
**Status:** Slices AI and J0J3.3 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 FL were
explicitly approved 2026-07-24. Slice J0J1 established the enforced Runtime
boundary and borrowed contract seam; J2 moved canonical session/transport
lifetime and ordered inbound routing. J3 is the active execution point.
lifetime and ordered inbound routing; J3.1J3.3 moved canonical entity identity
to Runtime and exact graphical projection identity to App. J3.4 is the active
execution point.
**Scope:** Reconcile and sequence the existing Modern Pipeline (`MP`) and
Linux/headless (`LH`) tracks using the 2026-07-24 connected performance audit.
@ -19,8 +21,9 @@ 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 `LiveEntityRuntime` ownership, the retail PView/cell
visibility ports, and all behavior already accepted by the user.
decomposition, canonical Runtime entity ownership with exact-key App
projections, the retail PView/cell visibility ports, and all behavior already
accepted by the user.
---
@ -1246,9 +1249,12 @@ 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 now owns canonical session/transport
lifetime plus ordered inbound routing in Runtime. J3 is the current execution
point.
complete with borrowed contracts, and J2 owns canonical session/transport
lifetime plus ordered inbound routing in Runtime. J3.1J3.3 are complete:
Runtime owns the only entity GUID/incarnation/local-ID directory, and App's
materialized sidecars plus spatial/presentation worksets carry exact
`RuntimeEntityKey`. J3.4 canonical `ClientObjectTable` ownership is the current
execution point.
Evidence:
[`../research/2026-07-25-slice-g5-production-profile.md`](../research/2026-07-25-slice-g5-production-profile.md)
and
@ -1262,6 +1268,8 @@ and
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
[`2026-07-25-modern-runtime-slice-j.md`](2026-07-25-modern-runtime-slice-j.md).
The intended order is therefore:
@ -1276,7 +1284,7 @@ honest metrics + committed baselines (A — exit criteria block C)
-> delta GPU submission (G)
-> residual frame cleanup (H-a, H-b, H-c)
-> flat collision assets (I — COMPLETE)
-> presentation-independent runtime (J — CURRENT at J3)
-> presentation-independent runtime (J — CURRENT at J3.4)
-> Linux/headless/multi-session (K)
-> evidence-gated GPU jobs (L)
```

View file

@ -1,7 +1,7 @@
# Modern runtime Slice J — presentation-independent runtime
**Status:** J0J2 COMPLETE — boundary, borrowed contracts, and canonical
session/transport lifetime; J3 is ACTIVE
**Status:** J0J2 COMPLETE; J3.1J3.3 COMPLETE; J3.4 canonical object-table
ownership is next
**Parent:** `2026-07-24-modern-runtime-architecture.md`, Slice J
**Authorization:** the user approved Slices FL, including the otherwise-frozen
Slice J gameplay-owner moves, on 2026-07-24
@ -15,8 +15,9 @@ deletes the replaced App production path before the next group moves.
## 1. Fixed invariants
1. `LiveEntityRuntime` remains the sole server-GUID/incarnation owner. There is
never a second GUID map or synchronized gameplay world.
1. `RuntimeEntityDirectory` is the sole server-GUID/incarnation/local-ID owner.
`LiveEntityRuntime` is the exact-key App projection host. There is never a
second GUID map or synchronized gameplay world.
2. Runtime types do not reference `AcDream.App`, either UI assembly, Silk.NET,
OpenAL, Arch, ImGui, a native window, or an OpenGL resource.
3. App projection state is keyed by runtime-issued local entity identity plus
@ -182,6 +183,15 @@ git revert 75930787741db40a83eab8663e4464dce8d687ba
**Detailed execution plan:**
[`2026-07-25-modern-runtime-slice-j3.md`](2026-07-25-modern-runtime-slice-j3.md).
**Current checkpoint:** J3.1J3.3 are complete. Runtime owns accepted entity
wire state and the only canonical identity/incarnation/local-ID directory.
App's `LiveEntityProjectionStore` and every materialized presentation/spatial
workset are keyed by exact `RuntimeEntityKey`; the temporary GUID-shaped
compatibility view is deleted. The final J3.3 binary `e937cc36` passes 8,444
Release tests / 5 skips and the exact seven-checkpoint connected gate at
`logs/connected-world-gate-20260725-221008/report.json`. J3.4 now moves the
existing `ClientObjectTable` under the Runtime entity/object lifetime root.
- Strip renderer, particle, Wb, and streaming components from the canonical
record into an App projection store keyed only by local identity/incarnation.
- Move `LiveEntityRuntime`, accepted spawn/state/property timestamps,

View file

@ -0,0 +1,237 @@
# Modern runtime J3.4 — canonical object-table ownership
**Status:** READY
**Parent:** `2026-07-25-modern-runtime-slice-j3.md`
**Required production base:** `e937cc36df39cf6ea1eaba2f9c0243d1929da702`
**Prior closeout:** `docs/research/2026-07-25-slice-j3-3-exact-projection-store.md`
## Objective
Make one presentation-independent Runtime lifetime-group root own both:
- the canonical `RuntimeEntityDirectory`; and
- the existing canonical `ClientObjectTable`.
App, retained UI, interaction controllers, and the graphical projection host
must borrow those exact same instances. There must be no copied inventory
model, second object table, asynchronous delivery seam, extra input frame, or
change to retail event order.
This slice moves ownership and transaction boundaries, not the
`ClientObjectTable` implementation out of `AcDream.Core`. The type is already
presentation-free and has extensive Core conformance coverage.
## Current ownership inventory
At the required base:
- `LiveEntityRuntime` constructs its own `RuntimeEntityDirectory`.
- `GameWindow` constructs a public readonly `ClientObjectTable Objects`.
- App composition records pass that table to retained UI, selection,
interaction, magic, combat, paperdoll, appraisal, cooldown, and diagnostics.
- Runtime `LiveSessionEventRouter` already applies property, stack, inventory,
player-description, container, and appraisal traffic directly to the
borrowed table through `ObjectTableWiring`.
- App `LiveEntityHydrationController` applies CreateObject to the table only
after canonical Runtime create acceptance and revalidates
`CreateIntegrationVersion`.
- App `LiveEntityDeletionController` applies logical DeleteObject before the
exact App projection teardown suffix.
- App `LiveSessionResetManifest` clears the object table before clearing live
graphical projections.
- Optimistic moves, confirmations, rollback, stack updates, container
replacement, and every table event are synchronous on the current thread.
Retail behavior and citations already live in:
- `src/AcDream.Core/Items/ClientObjectTable.cs`;
- `src/AcDream.Core.Net/ObjectTableWiring.cs`;
- `docs/research/2026-07-13-retail-give-item-pseudocode.md`;
- the named-retail symbols cited beside `ACCObjectMaint::CreateObject`,
`ACCObjectMaint::DeleteObject`, and
`ACCWeenieObject::ServerSaysMoveItem`.
J3.4 does not reinterpret those algorithms.
## Fixed target
Add a presentation-free Runtime owner equivalent to:
```csharp
public sealed class RuntimeEntityObjectLifetime
{
public RuntimeEntityDirectory Entities { get; }
public ClientObjectTable Objects { get; }
}
```
The concrete name may change, but these invariants do not:
1. The owner constructs each canonical instance exactly once.
2. `LiveEntityRuntime` receives `Entities`; it may not construct a directory.
3. Runtime session routing receives `Objects` from the same owner.
4. App projection, UI, and interaction composition receive borrowed references
only.
5. Accepted CreateObject/DeleteObject object-table mutation is implemented by
the Runtime group and revalidates the exact canonical record/version.
6. Reset preserves the current order: object-table clear before graphical live
projection teardown, with completed stages never replayed.
7. Studio/sample-data object tables remain independent fixture owners and are
not part of the live session.
## Execution
### 1. Pin parity traces and construction identity
- Capture normalized traces for:
- initial player description and equipment manifest;
- item CreateObject add/update;
- same-incarnation refresh;
- new-generation replacement;
- property Int/Int64/String/Float/DataID updates;
- stack-size update;
- optimistic move, authoritative confirm, and rejection rollback;
- wield/unwield;
- ViewContents replacement and close;
- logical delete, GUID reuse, and session clear.
- Add a construction-identity test proving the router, App projection host,
retained UI source, and interaction source all receive one table instance.
- Preserve current synchronous event order in the golden trace.
### 2. Introduce the Runtime lifetime-group root
- Add the owner under `src/AcDream.Runtime/Entities/`.
- Construct `RuntimeEntityDirectory` and `ClientObjectTable` there.
- Expose borrowed properties only; no collection mirror or event repeater.
- Add Runtime-only tests that instantiate it without loading App, UI, Silk,
OpenAL, Arch, or ImGui assemblies.
- Keep mutable state instance-scoped so two roots can hold conflicting GUIDs
and inventory contents without interference.
### 3. Inject canonical identity into the App projection host
- Change `LiveEntityRuntime` to require the exact
`RuntimeEntityDirectory` instance from the group.
- Remove its internal `new RuntimeEntityDirectory(...)`.
- Preserve the existing local-ID start override for focused wraparound tests
through an explicit fixture/group construction seam, not a second production
allocator.
- Assert that App cannot replace the directory after projection construction.
- Preserve J3.3 `RuntimeEntityKey` identity and teardown behavior unchanged.
### 4. Replace the `GameWindow` object-table owner
- Construct the Runtime group before retained UI and live presentation
composition.
- Delete `GameWindow`'s public readonly `ClientObjectTable Objects` field.
- Pass `runtimeGroup.Objects` through the existing typed composition records.
- Keep App controllers as direct synchronous borrowers; do not wrap every read
in a copied DTO or event bus.
- Add source guards rejecting a production App `new ClientObjectTable()` and a
second Runtime entity directory. Exempt Studio/sample-data and tests.
### 5. Move accepted object mutation to the Runtime group
- Move the generation/version-sensitive
`ObjectTableWiring.ApplyEntitySpawn/Delete` transaction boundary out of App.
- Runtime must apply a CreateObject only after the canonical directory accepts
that exact incarnation and while its create-integration version remains
current.
- New-generation replacement must remove the old object generation and publish
the same add/update/removal sequence as today.
- Logical delete must mutate the table before App graphical teardown, exactly
as the current `beforeTeardown` callback does.
- Pickup/parent leave-world must not delete the object-table entry.
- Dormant liveness pruning must retain the existing object-table semantics;
only an authoritative logical delete removes the retained object generation.
- Keep all callbacks synchronous and revalidate after every callback that can
replace, delete, or clear the incarnation.
### 6. Route session and reset ownership
- Bind J2 `LiveSessionEventRouter` directly from the Runtime group rather than
an App-owned table field.
- Keep the current pre-Connect subscription order.
- Keep local-player property projection to `LocalPlayerState` and the same
table instance until J4 moves the coherent player/gameplay state group.
- Replace the App reset field with a Runtime-group object-table reset
acknowledgement at the same manifest position.
- Failure injection must retain the unfinished reset suffix and never clear a
replacement session's table.
### 7. Delete superseded ownership seams
- Remove public ownership comments and aliases that describe the table as a
GameWindow state owner.
- Remove any App construction helper whose only purpose was to allocate the
live table.
- Keep direct table parameters on UI/controllers for now: they are explicit
borrowed dependencies and J4 will move higher-level inventory transactions.
- Update architecture, plans, durable memory, and exact rollback in the same
closeout commit.
## Adversarial cases
- Reentrant `ObjectAdded` deletes or replaces the same GUID.
- Reentrant `ObjectMoved` starts another optimistic move.
- Delete arrives during CreateObject callbacks.
- Same GUID with a new `INSTANCE_TS` while old projection teardown is pending.
- Unknown-object authoritative move publishes the same nullable item event.
- Equal, stale, and wrapped object/physics generations.
- Multiple in-flight optimistic moves followed by confirm/reject in either
order.
- Container replacement while item events mutate the same container.
- Reset during object-table event dispatch.
- Failed graphical teardown after canonical object removal.
- Two Runtime roots use identical GUIDs with different contents.
- No-window root executes the full entity/object lifecycle without loading App
or backend assemblies.
## Mandatory gates
Run in this order:
1. Runtime entity/object lifetime tests.
2. Core `ClientObjectTable*` and `ObjectTableWiring*` tests.
3. Focused App hydration, deletion, reset, inventory, interaction, paperdoll,
magic, cooldown, and runtime-adapter tests.
4. All Runtime tests.
5. All App tests.
6. `dotnet build AcDream.slnx -c Release`.
7. `dotnet test AcDream.slnx -c Release --no-build`.
8. Exact-binary connected lifecycle/reconnect gate.
9. At every connected checkpoint: zero render-shadow mismatches, zero pending
deltas, graceful process exit, and object/projection teardown convergence.
A user visual pause is not required unless an automated or connected gate
exposes an inventory, paperdoll, selection, or interaction symptom.
## Acceptance
- One Runtime lifetime-group root owns the exact entity directory and object
table.
- `GameWindow` owns neither instance directly.
- App and Runtime routing borrow the same object table.
- Runtime remains the only GUID/incarnation/local-ID authority.
- Create/delete/property/container ordering matches the pinned trace.
- Optimistic move/rollback and press-time UI remain synchronous.
- No App/UI/backend dependency enters Runtime.
- No mirror, queue, frame delay, or steady-state traversal allocation is added.
- Runtime-only entity/object lifecycle and multiple-instance isolation pass.
- Complete Release and exact connected gates pass on one committed binary.
## Rollback
Record the exact J3.4 production commit here before its connected gate. J3.3
remains independently reversible; do not use its rollback for a J3.4 failure.
## Final J3 goal
J3.4 is not the end of J3. J3.5 must publish one ordered per-session
entity/object delta stream and make graphical/direct hosts consume the same
committed facts without a second queue. J3.6 then failure-injects and proves
the complete no-window entity/object lifecycle, zero leaked records,
tombstones, projections, container entries, queued deltas, or subscriptions,
plus Release and connected parity. That J3.6 proof is the clean boundary before
J4 moves inventory transactions, vitals, enchantments, magic, and other
gameplay-state services.

View file

@ -1,8 +1,12 @@
# Modern runtime J3.3 — exact-key App projection store
**Status:** READY AFTER J3.2 CONNECTED GATE
**Status:** COMPLETE 2026-07-25
**Parent:** `2026-07-25-modern-runtime-slice-j3.md`
**Required base:** `f46ddb5cdb1e145752bea49aeb1d62bfe71284d3`
**Production commits:** `420e5eea70fd2c29cf9c8614e6298a1f84d64045`,
then exact-spatial correction
`e937cc36df39cf6ea1eaba2f9c0243d1929da702`
**Closeout:** `docs/research/2026-07-25-slice-j3-3-exact-projection-store.md`
## Objective
@ -131,3 +135,46 @@ Run:
- No new allocation appears in steady-state live-object traversal.
- No callback is delayed to another frame.
- All automated and connected gates pass on the exact commit.
## Completion result
All acceptance conditions pass on final exact binary `e937cc36`.
- `LiveEntityProjectionStore` is the only materialized App sidecar store and is
keyed by exact `RuntimeEntityKey`.
- The temporary `ActiveRecordView`, `_activeRecords`, detached App record
constructor, and broad materialized/visible `WorldEntity` collection
surfaces are deleted. Borrowed exact-key record views remain for hot
traversal and tests.
- Hydration, animation, effects, lights, equipped children, remote teleport,
render journal, liveness, movement observations, teardown, spatial
residence, and visibility transitions carry the exact key.
- `GpuWorldState` retains the exact key through loaded/pending rebucketing,
landblock retirement, origin recentering, quiescence, and reentrant
visibility delivery. DAT-static entities remain outside live Runtime
identity.
- Runtime remains the only current-GUID/incarnation/local-ID authority. App
resolves GUID through Runtime and never decides which incarnation is current.
- Synchronous callbacks, allocation order, render ordering, and frame timing
are unchanged.
Evidence:
- 104 focused App ownership/streaming tests passed.
- 3,761 App tests passed / 3 skipped.
- 8,444 complete Release tests passed / 5 skipped.
- Release solution build passed.
- `logs/connected-world-gate-20260725-221008/report.json` passed on exact commit
`e937cc36`: two sessions, seven checkpoints, two graceful zero-code exits,
zero failures, zero render-shadow mismatches, and zero pending deltas.
Exact rollback, newest commit first:
```text
git revert e937cc36df39cf6ea1eaba2f9c0243d1929da702
git revert 420e5eea70fd2c29cf9c8614e6298a1f84d64045
```
J3.4 is the next clean slice. It moves canonical `ClientObjectTable` ownership
under the Runtime entity/object lifetime group without moving retained UI,
icons, paperdolls, grids, cooldown drawing, or interaction presentation.

View file

@ -12,16 +12,21 @@
| J3.0 ownership inventory | complete | `b0fecc5f65ed26042c61a1651fb134aeb432c141`; `docs/research/2026-07-25-slice-j3-entity-ownership-inventory.md` |
| J3.1 accepted-wire owners | complete | `f7442d13e9ae7d5b077ffecb1f8a10899ad46edd`; 110 Runtime tests and complete Release parity remain green after J3.2 |
| J3.2 canonical Runtime entity directory | complete | `f46ddb5cdb1e145752bea49aeb1d62bfe71284d3`; 8,441 Release tests / 5 skips and `logs/connected-world-gate-20260725-200749/report.json` pass |
| J3.3 App projection store | next | [`2026-07-25-modern-runtime-slice-j3-projection-store.md`](2026-07-25-modern-runtime-slice-j3-projection-store.md): key every graphical sidecar/workset by `RuntimeEntityKey` and remove the compatibility view |
| J3.3 App projection store | complete | `420e5eea70fd2c29cf9c8614e6298a1f84d64045` + `e937cc36df39cf6ea1eaba2f9c0243d1929da702`; 8,444 Release tests / 5 skips and `logs/connected-world-gate-20260725-221008/report.json` pass |
| J3.4 canonical object-table ownership | next | [`2026-07-25-modern-runtime-slice-j3-object-table.md`](2026-07-25-modern-runtime-slice-j3-object-table.md): move the existing `ClientObjectTable` into the Runtime entity/object lifetime group while App remains a synchronous borrower |
J3.2 passes the Release solution build and 8,441 complete tests with five
expected skips. Its exact-binary seven-checkpoint connected
lifecycle/reconnect route also passes with graceful exits, zero render-shadow
mismatches, and zero pending deltas. It adds no frame queue, backend
dependency, or gameplay behavior change. The exact rollback for J3.2 is:
J3.3 passes the Release solution build, 3,761 App tests with three skips, and
8,444 complete tests with five expected skips. Its final exact-binary
seven-checkpoint connected lifecycle/reconnect route also passes with graceful
exits, zero render-shadow mismatches, and zero pending deltas. Runtime is now
the sole GUID/incarnation/local-ID authority; every materialized App sidecar,
presentation workset, and live spatial visibility edge uses the exact
`RuntimeEntityKey`. It adds no frame queue, backend dependency, allocation-order
change, or gameplay behavior change. The exact rollback for J3.3 is:
```text
git revert f46ddb5cdb1e145752bea49aeb1d62bfe71284d3
git revert e937cc36df39cf6ea1eaba2f9c0243d1929da702
git revert 420e5eea70fd2c29cf9c8614e6298a1f84d64045
```
## 1. Objective
@ -197,6 +202,9 @@ Source guards reject any App server-GUID authority map.
### J3.4 — canonical object-table ownership
**Detailed execution plan:**
[`2026-07-25-modern-runtime-slice-j3-object-table.md`](2026-07-25-modern-runtime-slice-j3-object-table.md).
- Add a Runtime entity/object lifetime-group root that owns the canonical
entity directory and the existing Core `ClientObjectTable`.
- Construct it before either graphical UI or live presentation; both borrow
@ -286,8 +294,12 @@ failure; record and revert only the exact failing J3 commit.
J3 sub-slices are independently reversible:
```text
git revert e937cc36df39cf6ea1eaba2f9c0243d1929da702
git revert 420e5eea70fd2c29cf9c8614e6298a1f84d64045
git revert f46ddb5cdb1e145752bea49aeb1d62bfe71284d3
git revert f7442d13e9ae7d5b077ffecb1f8a10899ad46edd
```
Revert J3.2 before J3.1 when rolling back both.
Revert J3.3's exact-spatial correction before its main projection-store
commit. Revert J3.3 before J3.2, and J3.2 before J3.1, when rolling back more
than one sub-slice.

View file

@ -0,0 +1,138 @@
# HANDOFF — J3.3 complete; start J3.4 canonical object-table ownership
## Outcome
Stop here. J3.3 is complete and certified.
Final code commits:
1. `420e5eea70fd2c29cf9c8614e6298a1f84d64045`
— App projection sidecars and presentation worksets use exact
`RuntimeEntityKey`.
2. `e937cc36df39cf6ea1eaba2f9c0243d1929da702`
— spatial residence, visibility, quiescence, rebucketing, retirement, and
origin recenter preserve that same exact key.
Do not reopen J3.3 unless a reproducible gate fails. The next slice is J3.4.
## Read in this order
1. `AGENTS.md`
2. `docs/plans/2026-07-25-modern-runtime-slice-j3-object-table.md`
3. `docs/plans/2026-07-25-modern-runtime-slice-j3.md`
4. `docs/research/2026-07-25-slice-j3-3-exact-projection-store.md`
5. `claude-memory/project_modern_runtime_architecture.md`
6. `claude-memory/project_live_entity_runtime.md`
7. `docs/architecture/acdream-architecture.md`
8. `docs/architecture/code-structure.md`
The first plan is the authoritative J3.4 execution plan. The parent plan
defines J3.5/J3.6 and final acceptance.
## Current architecture
- `RuntimeEntityDirectory` is the only server-GUID/incarnation/local-ID
authority.
- `RuntimeEntityRecord` is presentation-free.
- `LiveEntityProjectionStore` is the only materialized App sidecar store.
- Every App materialized owner uses exact `RuntimeEntityKey`.
- `GpuWorldState` retains the exact key across all spatial lifetime changes.
- `LiveEntityRuntime` is an App projection/lifecycle host, not canonical
identity.
- No queue, copied gameplay world, extra frame, or rendering behavior change
was introduced.
The remaining J3.4 split is visible in two places:
- `LiveEntityRuntime` still constructs `RuntimeEntityDirectory`.
- `GameWindow` still constructs the live `ClientObjectTable`.
J3.4 replaces those two independent construction points with one
presentation-free Runtime entity/object lifetime-group root, then makes all App
controllers and Runtime session routing borrow its exact instances.
## Required behavior
Preserve verbatim:
- canonical create acceptance before object-table CreateObject mutation;
- `CreateIntegrationVersion` revalidation after synchronous callbacks;
- logical DeleteObject mutation before App projection teardown;
- pickup/parent leave-world without object-table deletion;
- optimistic move, authoritative confirm, and rejection rollback;
- ViewContents/container replacement and nullable unknown-object move notices;
- current pre-Connect subscription order;
- object-table clear before graphical projection reset;
- synchronous press-time UI and controller reads.
Do not move retained UI, item icons, paperdolls, grids, cooldown drawing,
selection presentation, or higher-level inventory transactions into Runtime in
J3.4. Those are borrowers now and later J4 work.
## Baseline and gates
Final J3.3 baseline:
- focused exact ownership/streaming: 104 passed;
- App: 3,761 passed / 3 skipped;
- complete Release: 8,444 passed / 5 skipped;
- Release build: pass;
- connected report:
`logs/connected-world-gate-20260725-221008/report.json`;
- connected result: two sessions, seven checkpoints, both graceful exit code
zero, zero failures, zero render-shadow mismatches, zero pending deltas.
Standard commands:
```powershell
dotnet build AcDream.slnx -c Release --no-restore
dotnet test AcDream.slnx -c Release --no-build
```
Run the focused Runtime/Core/App matrix listed in the J3.4 plan before the full
suite. Commit the J3.4 code before running the connected gate so the report
identifies one immutable binary. Use credentials supplied by the user/session;
never write them into source or documentation.
## Worktree cautions
These tracked paths can appear modified from Windows metadata/line-ending
behavior while remaining content-identical to the index:
- `src/AcDream.App/Interaction/PlayerInteractionMovementSink.cs`
- `src/AcDream.App/Rendering/LiveAnimationPresentationContext.cs`
- `src/AcDream.Core/Physics/CellArray.cs`
- `src/AcDream.Core/Vfx/ParticleSystem.cs`
- `tests/AcDream.Core.Tests/Vfx/ParticleSystemTests.cs`
Do not stage or rewrite them unless `git diff -- <path>` shows a real task
change.
`.test-out/`, nested `.test-out/`, and `logs/` contain local test/evidence
artifacts, including very large prepared packages. Do not stage them and do not
recursively delete them without first verifying the exact absolute target.
## Exact rollback
If J3.3 itself must be rolled back, revert newest first:
```text
git revert e937cc36df39cf6ea1eaba2f9c0243d1929da702
git revert 420e5eea70fd2c29cf9c8614e6298a1f84d64045
```
For a J3.4 failure, revert only the new J3.4 commit. Do not use the J3.3
rollback as a shortcut.
## Final J3 goal
J3.4 establishes one Runtime entity/object ownership root. J3.5 publishes one
ordered per-session entity/object delta stream consumed by graphical and
direct hosts without a second queue or mirror. J3.6 proves failure recovery,
no-window lifecycle, multiple-instance isolation, exact teardown convergence,
Release parity, and the connected route.
The J3 campaign is complete only when J3.6 proves zero leaked canonical
records, tombstones, App projections, resources, container entries, parent
candidates, deltas, and subscriptions. That is the next clean boundary before
J4 gameplay-state services and later Slice K Linux/headless multi-session work.

View file

@ -0,0 +1,128 @@
# Modern runtime J3.3 exact projection-store closeout
## Result
J3.3 is complete in two production commits:
1. `420e5eea70fd2c29cf9c8614e6298a1f84d64045` — exact-key App
projection store and presentation-owner cutover.
2. `e937cc36df39cf6ea1eaba2f9c0243d1929da702` — exact-key spatial
residence, visibility, quiescence, rebucketing, and retirement correction.
The second commit is the final exact binary certified by the connected gate.
`AcDream.Runtime.Entities.RuntimeEntityDirectory` remains the sole owner of
current server GUID, `INSTANCE_TS` incarnation, Runtime local ID, reverse
lookup, accepted snapshot/timestamps, parent state, session epoch, tombstones,
and per-GUID operation versions.
`AcDream.App.World.LiveEntityProjectionStore` now owns materialized graphical
sidecars only. Its key is the complete `RuntimeEntityKey`:
```text
Runtime local ID + INSTANCE_TS incarnation
```
There are three explicit states:
1. canonical-only Runtime registration with no App sidecar;
2. one materialized App sidecar under its exact key;
3. retryable teardown retaining only the unfinished suffix for that exact key.
The temporary `ActiveRecordView`, `_activeRecords`, detached
`LiveEntityRecord(EntitySpawn)` fixture path, and broad materialized/visible
`WorldEntity` collection APIs are deleted. Consumers use focused borrowed
queries or allocation-free exact-key record views.
## Exact owner cutover
The following App owners now carry the exact key:
- world projection and loaded/pending spatial residence;
- spatial visibility transitions and world-generation quiescence;
- hydration and recovery;
- animation and spatial-animation schedules;
- remote motion, remote teleport, and movement observations;
- projectile and ordinary-root worksets;
- entity effect profiles and moving lights;
- equipped-child roots and render registrations;
- render-scene journal identity;
- radar, targeting, selection, liveness, and teardown lookups.
`GpuWorldState` preserves the exact key through loaded-to-loaded rebucketing,
pending residence, landblock retirement/reload, persistent rescue, and
full-origin recenter. A delayed visibility edge therefore cannot match a later
incarnation that reused the same numeric local ID. DAT-static entities retain
their existing non-Runtime path and are not assigned a fabricated server-live
identity.
No asynchronous queue, copied gameplay world, extra input frame, local-ID
allocation-order change, renderer order change, or gameplay behavior change
was introduced.
## Automated acceptance
At final commit `e937cc36`:
- focused ownership/streaming tests: 104 passed;
- App tests: 3,761 passed / 3 skipped;
- complete Release solution: 8,444 passed / 5 skipped;
- Release solution build: pass.
Coverage includes canonical-only registration, exact sidecar claim, same-
incarnation refresh, factory/resource rollback, local-ID wrap and stale-key
rejection, loaded/pending rebucketing, landblock detach/reload, origin
recenter, reentrant visibility callbacks, GUID replacement, hydration,
animation/effect/light/equipped-child ownership, and retryable teardown.
Source guards reject a GUID-keyed App `LiveEntityRecord` authority map and
local-ID-only ownership in the materialized presentation/spatial worksets.
The divergence-register audit found no new behavioral divergence: J3.3 changes
ownership and identity plumbing only. AD-32's moved Runtime source pointers
were refreshed without changing its scope.
## Connected lifecycle acceptance
Raw evidence:
`logs/connected-world-gate-20260725-221008/report.json`.
Result: PASS on exact commit
`e937cc36df39cf6ea1eaba2f9c0243d1929da702`.
- Two fresh processes/sessions completed.
- Seven of seven checkpoints passed.
- Both processes exited gracefully with exit code zero.
- The report contains zero failures.
- Every checkpoint has zero render-scene shadow mismatches.
- Every checkpoint converged with zero pending render deltas.
- The only warning is 25 expected world-edge landblock misses.
This certifies fresh login, outdoor/dungeon travel, same-location revisit,
graceful logout, and fresh-process reconnect on the exact final binary.
## Exact rollback
Revert newest first:
```text
git revert e937cc36df39cf6ea1eaba2f9c0243d1929da702
git revert 420e5eea70fd2c29cf9c8614e6298a1f84d64045
```
Do not revert J3.2 unless canonical Runtime identity itself is also being
rolled back.
## Next slice
Start J3.4 in
`docs/plans/2026-07-25-modern-runtime-slice-j3.md`.
The executor-ready transfer prompt is
`docs/research/2026-07-25-slice-j3-3-complete-j3-4-handoff.md`.
J3.4 moves the existing canonical `ClientObjectTable` and its placement/
container lifetime under one Runtime entity/object group. App remains a
synchronous borrower for retained UI, item icons, paperdolls, grids, cooldown
drawing, and interaction presentation. It must preserve object add/update/
remove, container replacement, optimistic move rollback, stack responses,
GUID reuse, session clear, and press-time event ordering without adding a
queue or copied inventory model.