docs(runtime): pin J3 entity ownership migration

Reconcile J3 with later gameplay lifetime groups, inventory the current mixed entity/object ownership, and define the staged canonical identity, projection, object-table, delta, teardown, and no-window gates.

Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-25 19:51:50 +02:00
parent 9496c01be1
commit b0fecc5f65
3 changed files with 365 additions and 0 deletions

View file

@ -179,6 +179,9 @@ git revert 75930787741db40a83eab8663e4464dce8d687ba
### J3 — canonical identity, properties, and object-table group
**Detailed execution plan:**
[`2026-07-25-modern-runtime-slice-j3.md`](2026-07-25-modern-runtime-slice-j3.md).
- 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,265 @@
# Modern runtime Slice J3 — canonical identity, properties, and object table
**Status:** ACTIVE
**Parent:** `2026-07-25-modern-runtime-slice-j.md`, J3
**Production base:** `9496c01b`
**J2 production commit:** `75930787741db40a83eab8663e4464dce8d687ba`
## 1. Objective
Move the one canonical server-object identity/incarnation owner, accepted
CreateObject/property/timestamp state, parent relation state, and
`ClientObjectTable` lifetime into `AcDream.Runtime`.
App becomes a projection host. Its world entity, animation, effect, light,
selection, paperdoll, spatial bucket, and hydration state is keyed by a
Runtime-issued local identity plus exact incarnation. App must not retain a
second server-GUID map or decide whether a packet belongs to the current
incarnation.
This is a structural move. Existing named-retail CreateObject, timestamp,
parenting, pickup, Hidden, delete, and generation-replacement behavior remains
unchanged. No renderer, particle, streaming, GL, or retained-UI type may enter
Runtime.
## 2. Scope reconciliation
The umbrella J3 text overlaps later lifetime groups. This detailed plan is the
authoritative split:
- J3 moves raw accepted entity identity, immutable spawn/property snapshots,
timestamp gates, parent relations, and the canonical client object/container
table.
- J4 moves higher-level inventory transactions, vitals, enchantments,
spell/component state, cooldown services, chat, and their ViewModel-facing
revisions.
- J5 moves movement/physics/interaction/combat authority.
J3 may retain existing Core physics references already stored on the entity
record while separating presentation, but it does not redesign or relocate
their behavior. That coherent move remains J5.
## 3. Fixed ownership after J3
`AcDream.Runtime` owns:
- server GUID to current incarnation;
- exact Runtime local identity allocation and reverse lookup;
- accepted `INSTANCE_TS` and every retail packet-channel timestamp;
- the latest accepted immutable `WorldSession.EntitySpawn`;
- logical create, same-incarnation refresh, pickup, parent, Hidden/state,
delete, generation replacement, tombstone, and session-clear state;
- unresolved/staged/committed parent relations;
- `ClientObjectTable`, containers, placement indices, pending authoritative
property state, and generation-aware removal;
- ordered immutable entity/object deltas;
- retryable logical teardown state and the acknowledgement from an attached
projection host.
`AcDream.App` owns:
- `WorldEntity` and render-scene projection;
- loaded/pending spatial buckets and visibility derived from streaming;
- animation, remote-motion presentation, projectile presentation, effects,
lights, selection geometry, radar presentation, and private paperdolls;
- projection hydration/recovery transaction state;
- render/resource registration and its exact retryable teardown suffix.
App projection keys are:
```text
RuntimeLocalEntityId + INSTANCE_TS
```
Server GUID may be carried as immutable metadata for diagnostics and outbound
commands, but it is not an App lookup authority.
## 4. Runtime contracts
Introduce or finish contracts equivalent to:
```csharp
public readonly record struct RuntimeEntityKey(
uint LocalEntityId,
ushort Incarnation);
public readonly record struct RuntimeEntityDelta(
ulong Sequence,
RuntimeGenerationToken SessionGeneration,
RuntimeEntityKey Entity,
RuntimeEntityDeltaKind Kind,
RuntimeEntitySnapshot Snapshot);
public interface IRuntimeEntityProjectionHost
{
RuntimeProjectionAcknowledgement Register(RuntimeEntitySnapshot entity);
RuntimeProjectionAcknowledgement Update(RuntimeEntityDelta delta);
RuntimeProjectionAcknowledgement Withdraw(RuntimeEntityKey entity);
RuntimeProjectionAcknowledgement TearDown(RuntimeEntityKey entity);
}
```
The concrete names may follow existing project vocabulary, but these
invariants are mandatory:
- Runtime issues identity before App creates a projection.
- Every App callback carries the exact local identity and incarnation.
- Callback acknowledgement is exact and retryable.
- Completed teardown work never replays.
- App cannot query or mutate Runtime by a stale local identity/incarnation.
- Runtime emits deltas only after canonical state commits.
- Callback reentrancy may supersede the outer operation; the outer operation
must revalidate generation/incarnation/operation version before continuing.
## 5. Execution slices
### J3.0 — oracle and ownership inventory
- Pin the current field/method ownership table for `LiveEntityRecord`,
`LiveEntityRuntime`, `InboundPhysicsStateController`,
`ParentAttachmentState`, `ClientObjectTable`, `GpuWorldState`, hydration,
and teardown owners.
- Reuse the existing retail pseudocode and citations; no behavior is being
invented. Add research only where a current branch lacks a cited oracle.
- Capture normalized existing traces for create, same-generation refresh,
parent/unparent, pickup, Hidden, delete/recreate, GUID reuse, container
placement, and session clear.
- Record baseline render-scene identity/digest at deterministic fixtures.
### J3.1 — move narrow accepted-wire owners
- Move `InboundPhysicsStateController`, `ParentAttachmentState`, their result
types, and their complete tests into Runtime.
- Preserve timestamp cursor/order behavior and parent candidate ordering
line-for-line.
- App's existing `LiveEntityRuntime` temporarily composes those Runtime owners;
no second state is introduced.
- Add Runtime dependency/load guards around the moved types.
Gate: all timestamp, malformed, wraparound, parent-generation, and packet-order
tests pass in Runtime; App behavior and complete suite remain unchanged.
### J3.2 — canonical Runtime entity directory
- Extract current GUID/incarnation maps, accepted snapshot state, session
lifetime version, operation versions, tombstones, delete/generation
replacement, and local-ID allocation into one Runtime owner.
- Split `LiveEntityRecord` into canonical Runtime state and an App projection
sidecar. Canonical fields never reference App interfaces or `WorldEntity`.
- Preserve current local-ID allocation order during graphical hydration for
parity; Runtime owns the allocator and claim, while a no-window host may
claim immediately.
- Retain exact reentrant supersession and retryable tombstone behavior.
- Keep physics/movement fields structurally in place where required for J5,
but remove every renderer/effect/hydration reference from the canonical
record.
Gate: the complete existing `LiveEntityRuntimeTests` identity/timestamp/
teardown subset moves to Runtime and produces the same normalized trace.
### J3.3 — App projection store and cutover
- Add one App projection store keyed only by Runtime local identity plus
incarnation.
- Move `WorldEntity`, spatial visibility, hydration/recovery flags, animation,
remote-motion presentation, projectile presentation, effect profiles,
resource flags, and active presentation worksets into that store.
- Convert `GpuWorldState`, hydration, equipped children, render journal,
animation/effect/light owners, selection/radar queries, and teardown to the
exact projection key.
- Replace App's mixed `LiveEntityRuntime` facade with focused Runtime directory
and App projection-store dependencies; delete its GUID maps and mixed record.
- Preserve current register-once, rebucket-only, withdraw-without-destroy,
Hidden, and retryable logical teardown order.
Gate: projection identity/digest and all existing hydration, rebucket,
visibility, effect, animation, projectile, selection, and teardown tests match.
Source guards reject any App server-GUID authority map.
### J3.4 — canonical object-table ownership
- 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
the same instances.
- Remove the public `ClientObjectTable` owner field from `GameWindow`.
- Route J2 inbound object/property/container updates directly to the Runtime
group.
- Keep UI controllers, item icons, paperdoll, grids, cooldown drawing, and
interaction presentation as App borrowers.
- Preserve synchronous press-time reads and current event ordering; add no
queue or copied inventory model.
Gate: object add/update/remove, placement, container replacement, optimistic
move rollback, stack response, GUID reuse, and session-clear traces match.
### J3.5 — ordered deltas and borrowed views
- Publish one monotonic per-session Runtime entity/object delta stream.
- Make J1 `IGameRuntimeView.Entities` and inventory snapshots read the Runtime
owners directly rather than App adapters.
- Feed App projection mutation from the same committed deltas/acknowledgements;
do not create a second queue or frame delay.
- Remove superseded App entity/object event mirroring and source adapters.
- Assert same-thread graphical input and inbound presentation remain
synchronous.
Gate: direct-host and graphical-host normalized traces are identical, including
sequence, generation, identity, properties, placement, and teardown edges.
### J3.6 — hardening and closeout
- Failure-inject every projection registration/update/withdraw/teardown stage.
- Exercise callback reentrancy, duplicate CreateObject, stale/equal/wrapped
timestamps, delete/recreate with the same GUID, pending-to-loaded movement,
parent events before either object, malformed property packets, and session
reset during callbacks.
- Prove zero leaked canonical records, tombstones, App projections, resources,
container entries, parent candidates, queued deltas, or subscriptions.
- Add a Runtime-only entity/object fixture that performs create, property
updates, parent/container moves, Hidden, delete/recreate, clear, and teardown
without loading presentation/backend assemblies.
- Run Release build, focused suites, complete tests, exact-binary connected
lifecycle/reconnect, and the canonical world route.
- Because J3 changes projection identity plumbing, run the existing automated
render digest/referee at every checkpoint. Pause for user visual verification
only if the referee or connected client exposes a pixel/interaction symptom.
## 6. Commit order
1. `refactor(runtime): move accepted entity wire state`
2. `refactor(runtime): own canonical entity identity and incarnations`
3. `refactor(app): key live projections by runtime identity`
4. `refactor(runtime): own canonical object table`
5. `feat(runtime): publish ordered entity and object deltas`
6. `test(runtime): close J3 entity ownership parity`
Each commit is independently buildable and bisectable. The exact rollback for
each lands in this document before that commit's connected or visual gate.
## 7. Acceptance
J3 is complete only when:
- Runtime owns the only server-GUID/incarnation map and object table;
- App projections are keyed by local identity plus incarnation;
- Runtime has no App/UI/backend dependency;
- App has no authoritative GUID map or accepted timestamp gate;
- no mirrored collection, asynchronous seam, or added input frame exists;
- normalized direct/graphical traces match;
- deterministic render identity/digests match;
- Runtime-only no-window entity/object lifecycle passes;
- Release build and complete tests pass;
- exact-binary connected login/travel/logout/reconnect passes gracefully;
- every teardown owner converges to zero.
## 8. Current rollback chain
```text
git revert 75930787741db40a83eab8663e4464dce8d687ba
git revert 854d9e9cd13092bd5aaa3cf025d73eeb4600e9f8
git revert b632672e5ccabfb44c551e08f1c411ab2669c44a
```
These are J2, J1, and J0 respectively. Do not use them for a J3 sub-slice
failure; record and revert only the exact failing J3 commit.

View file

@ -0,0 +1,97 @@
# Slice J3 entity ownership inventory
## Baseline
Production base: `9496c01b`.
The accepted J2 binary passes 8,428 Release tests with five skips and the
seven-checkpoint connected lifecycle/reconnect gate at
`logs/connected-world-gate-20260725-193936/report.json`.
At this baseline:
- `LiveEntityRuntime.cs` is 2,866 lines and is referenced by roughly 100 App
source/test files;
- it owns both canonical GUID/incarnation/timestamp state and graphical
projection/component state;
- `InboundPhysicsStateController` and `ParentAttachmentState` are already
presentation-free but still reside in App;
- `ClientObjectTable` is a presentation-free Core type, but its production
instance is publicly owned by `GameWindow`;
- the retained render scene is a derived projection and is not a second
gameplay/GUID authority.
## Existing retail oracle
J3 changes ownership, not behavior. It preserves the already cited ports:
- `CObjectMaint::CreateObject`
- `CObjectMaint::DeleteObject @ 0x00508460`
- `CPhysicsObj::set_description @ 0x00514F40`
- `CPhysicsObj::change_cell @ 0x00513390`
- `CPhysicsObj::exit_world @ 0x00514E60`
- `ACCWeenieObject::ServerSaysMoveItem @ 0x0058DBB0`
- the SmartBox CreateObject/event flow in
`docs/research/2026-07-13-retail-projectile-vfx-pseudocode.md`
No new AC algorithm or constant is introduced. Any later behavioral question
returns to named retail before implementation.
## Field ownership
| State | J3 owner | Notes |
|---|---|---|
| Server GUID and `INSTANCE_TS` | Runtime | One current incarnation only |
| Runtime local identity and reverse lookup | Runtime | Issued by Runtime; never inferred by App |
| Latest accepted immutable spawn | Runtime | Includes accepted raw properties and timestamps |
| Position/state/vector/movement/object-description authority versions | Runtime | Reentrant operation validation |
| Full cell and canonical landblock | Runtime | Authoritative object placement, not streaming residency |
| Raw/final physics state and object clock | Runtime | Existing behavior; later physics relocation is J5 |
| Parent candidates and accepted parent relation | Runtime | Exact arrival and generation order |
| Session lifetime version, GUID operation versions, tombstones | Runtime | Retryable replacement/delete/clear |
| `ClientObjectTable` and placement/container indices | Runtime | Existing Core type, same instance |
| `WorldEntity` and mesh/resource registration | App projection | Keyed by local identity + incarnation |
| Loaded/pending bucket and derived visibility | App projection | Streaming fact, not object identity |
| Animation/effect/light/projectile presentation | App projection | No references from Runtime |
| Hydration/recovery/appearance synchronization flags | App projection | Retryable graphical transaction |
| Selection geometry, radar and paperdoll | App projection | Borrow canonical object facts |
`HasPartArray`, `PhysicsBody`, and movement-manager/physics-host fields are
presentation-independent simulation facts. J3 must not push them into App just
to simplify the split. Their behavior and final lifetime-group relocation are
handled in J5.
## Current mixed-owner hazards
1. Local IDs are currently reserved when App materializes a `WorldEntity`.
Runtime must own the allocator while preserving that graphical allocation
order until parity is proven.
2. Logical tombstones currently retain App teardown plans on
`LiveEntityRecord`. J3 replaces this with a typed, exact, retryable
projection acknowledgement.
3. Active animation/remote/projectile worksets currently live beside the GUID
map. They move intact to the App projection store.
4. `GpuWorldState` and several presentation controllers accept server GUID as
their primary key. They must consume Runtime local identity plus incarnation
and treat GUID only as immutable payload metadata.
5. `GameWindow.Objects` makes the graphical shell appear to own the canonical
object table. J3 replaces it with a Runtime lifetime-group owner borrowed by
UI and presentation composition.
## Do not retry
| Dead direction | Why |
|---|---|
| Copy the GUID map into Runtime while keeping App's map | Creates two worlds and ambiguous teardown |
| Let App allocate an independent local ID | Breaks cross-host identity and stale-incarnation rejection |
| Put `WorldEntity` or App component interfaces in Runtime | Makes headless a hidden graphical host |
| Deliver projection updates through a later-frame queue | Changes accepted presentation and input timing |
| Move vitals/spells/combat opportunistically in J3 | Splits the J4/J5 lifetime groups |
| Re-key everything by GUID and call it a projection cache | Leaves App as a second identity authority |
## J3.1 first move
`InboundPhysicsStateController`, `ParentAttachmentState`, their immutable
result types, and dedicated tests move into `AcDream.Runtime.Entities`
unchanged. App temporarily composes these Runtime owners until the canonical
directory and projection store cut over in later J3 sub-slices.