Record the canonical entity/object delta cutover, full Release and exact-binary connected evidence, rollback, and the adversarial zero-owner J3.6 execution contract. Synchronize architecture, roadmap, milestones, and agent guidance. Co-authored-by: Codex <codex@openai.com>
13 KiB
Modern runtime J3.5 — committed entity/object deltas and borrowed views
Status: COMPLETE — ce3ac310d92722ffb637e81cb1957458874dd220
Parent: 2026-07-25-modern-runtime-slice-j3.md
Required production base: 5ef8b5371d8990f0380acd939e71cd711289d429
Prior closeout: docs/research/2026-07-26-slice-j3-4-canonical-object-lifetime.md
Objective
Publish one synchronous, monotonic, generation-stamped stream from the canonical Runtime entity/object lifetime and make both graphical and no-window/direct hosts observe those same committed facts.
IGameRuntimeView.Entities and IGameRuntimeView.Inventory must read the
Runtime-owned RuntimeEntityDirectory and ClientObjectTable directly.
CurrentGameRuntimeEventAdapter must stop reconstructing entity changes from
App projection visibility and inventory changes from an App-owned event seam.
This slice adds no later-frame queue, copied world, inventory mirror, input delay, renderer dependency, or gameplay behavior change.
Current seam at the production base
RuntimeEntityObjectLifetimeowns the exact directory and object table.CurrentGameRuntimeViewAdapter.EntityViewstill reads AppLiveEntityRuntime, so a direct host cannot enumerate canonical entities without constructing the graphical projection host.- Its inventory view reads the right table but asks
LiveEntityRuntimeto recover incarnation metadata. CurrentGameRuntimeEventAdapterowns a separate sequencer and reconstructs:- entity events only from
ProjectionVisibilityChanged; and - inventory events by subscribing directly to object-table callbacks.
- entity events only from
- Registered, refreshed, parented, picked-up, state-updated, cellless, and authoritative-delete canonical edges therefore do not have one complete Runtime stream.
- Runtime local IDs are still claimed at App materialization rather than at canonical registration, even though direct and graphical hosts require one stable cross-host identity.
The J1 adapter was intentionally temporary. J3.5 deletes these superseded entity/object adapter responsibilities.
Fixed contracts
One canonical stream owner
Add one instance-scoped stream under AcDream.Runtime.Entities, owned by
RuntimeEntityObjectLifetime.
It exposes a narrow subscription equivalent to:
public interface IRuntimeEntityObjectObserver
{
void OnEntity(in RuntimeEntityDelta delta);
void OnInventory(in RuntimeInventoryDelta delta);
}
public interface IRuntimeEntityObjectEventSource
{
IDisposable Subscribe(IRuntimeEntityObjectObserver observer);
}
Entity and inventory deltas share one sequence owner. There is no entity queue
plus inventory queue and no App repeater. Every stamp carries the exact
RuntimeGenerationToken and current Runtime frame number.
Sequence rules:
- sequence starts at one for each live session generation;
- entity and inventory commits consume the same monotonic sequence;
- rejected/stale packets consume no sequence;
- callback reentrancy appends later sequence values in actual commit order;
- session reset publishes its committed withdrawals/removals/clear edge, then the retired generation is sealed;
- a replacement generation cannot publish through the retired stream context.
Canonical entity identity and snapshot
Runtime claims a local ID when it creates the canonical active record, before any graphical projection callback. The ID remains stable for that incarnation until canonical teardown; graphical hydration failure must not release a still-active Runtime identity.
The public entity view is canonical and presentation-free:
- server GUID;
- Runtime local ID;
- exact
INSTANCE_TSincarnation; - full cell;
- final physics state;
- accepted position/snapshot facts.
Graphical hydration, loaded/pending visibility, and render-resource state do not become gameplay truth. If retained diagnostic contracts still need attached-host counts, those are supplied by a separate projection acknowledgement/diagnostic seam and are not mixed into canonical entity identity.
Commit ordering
An entity delta is created only after the exact Runtime record mutation commits. The graphical projection host receives that same immutable committed delta synchronously and returns an exact acknowledgement; public observers then observe the same sequence. There is no second conversion step and no frame boundary.
Required entity edges:
Registeredafter active-record/local-ID commit;Updatedafter same-incarnation CreateObject, ObjDesc, vector, movement, accepted position, parent, or equivalent canonical refresh;Rebucketedafter a canonical full-cell change;Hiddenafter a committed Hidden state transition;Withdrawnafter pickup/accepted parent/cellless exit-world while the incarnation remains logically alive;Deletedafter authoritative identity retirement and retained-object removal acceptance;- generation replacement orders old
Deleted/withdrawal before newRegistered.
Loaded-to-pending renderer visibility is not a canonical withdrawal and emits no gameplay entity delta.
Required inventory edges remain the exact object-table callback order:
Added;Updated;Moved;Removedwith exact removed generation;Cleared.
Unknown-object move notices retain the object ID and placement facts rather
than disappearing because no ClientObject instance exists.
Borrowed views
The Runtime lifetime group exposes concrete allocation-free
IRuntimeEntityView and IRuntimeInventoryView implementations:
- entity enumeration walks
RuntimeEntityDirectory.ActiveRecords; - entity lookup resolves directly through the directory;
- inventory enumeration/lookup walks
ClientObjectTable; - inventory incarnation comes from the exact Runtime directory or the object's retained generation, never an App sidecar;
- visitors receive value snapshots and may not retain mutable owners.
CurrentGameRuntimeViewAdapter borrows those view objects. It retains only
the still-unmoved J4+ chat/movement/portal/lifecycle composition.
Execution
1. Pin the pre-cutover normalized oracle
Add a focused deterministic fixture covering:
- create, same-incarnation refresh, and generation replacement;
- ObjDesc/property and accepted state/vector/movement/position updates;
- parent, pickup, cellless withdrawal, and re-entry;
- object add/update/move/remove/clear;
- optimistic move, confirm, rejection rollback, and container replacement;
- authoritative delete and delete/recreate with the same GUID;
- reset and next-generation reuse;
- callback reentrancy.
Record both the canonical final state and exact event order. Preserve existing retail-facing object-table callback order; add missing canonical entity edges without treating projection visibility as gameplay truth.
2. Add the Runtime entity/object stream
- Add the stream owner and narrow observer/source contracts in Runtime.
- Give it one per-generation sequencer and explicit session-generation/frame context.
- Use copy-on-write subscriber storage so steady dispatch allocates no observer list and unsubscribe is exact.
- Reject duplicate subscriptions, cross-owner subscriptions, stale generations, and dispatch after disposal.
- Keep callback execution on the existing update/network-drain thread.
- Revalidate generation/incarnation/operation version after any callback that can re-enter Runtime.
3. Move identity allocation to canonical registration
- Claim the Runtime local ID in the same transaction that installs an active canonical record.
- Preserve current allocation range and wrap/exhaustion behavior.
- Stop releasing the ID when only App materialization rolls back.
- Release it only when the canonical incarnation reaches terminal teardown.
- Prove pending/unhydrated and direct-host entities still have exact keys.
- Prove GUID reuse gets a new key and stale keys never resolve.
4. Publish canonical entity commits
- Centralize accepted canonical mutations behind
RuntimeEntityObjectLifetimetransaction methods. - Publish only after state commits and only while the exact operation remains current.
- Carry the complete immutable accepted snapshot required by a graphical or direct projection host.
- Replace App's projection-visibility-derived entity event reconstruction.
- Preserve synchronous graphical projection application and exact retryable teardown acknowledgements.
- Do not emit on renderer rebucket, loaded/pending visibility, hydration retries, or other presentation-only changes.
5. Publish exact object-table commits
- Bind the stream to its owned
ClientObjectTable. - Convert callbacks to immutable
RuntimeInventoryItemSnapshotvalues with generation metadata resolved by Runtime. - Preserve add/update/move/remove/clear order and callback reentrancy.
- Keep optimistic movement and rollback synchronous.
- Ensure an authoritative delete produces the exact entity/object ordering pinned by J3.4.
- Do not add a second retained inventory collection.
6. Replace App entity/object view and event adapters
- Make
CurrentGameRuntimeViewAdapter.Entitiesand.Inventoryreturn the Runtime group's borrowed views. - Remove its
LiveEntityRuntimedependency for canonical entity/inventory reads. - Make
CurrentGameRuntimeEventAdapterforward the Runtime stream directly. - Remove its
ProjectionVisibilityChangedand object-table subscriptions. - Retain command, lifecycle, chat, movement, and portal adapter work only until their scheduled J4–J6 owner moves.
- Add source guards preventing a new App entity/object mirror, sequencer, or owner-event reconstruction.
7. Direct/graphical parity and teardown
- Build one no-App Runtime fixture and one graphical-host fixture over the same input script.
- Compare normalized generation, sequence, identity, cell/state, properties, placement, and terminal edges exactly.
- Verify subscriber removal and Runtime disposal leave zero observers.
- Verify reset seals the old generation and a new session begins at sequence one without stale delivery.
- Verify graphical callbacks remain same-thread and press-time object reads remain immediate.
Adversarial cases
- Observer adds/removes an observer during dispatch.
- Observer creates, deletes, or replaces the same GUID reentrantly.
- Observer clears the session during create or object-table callbacks.
- Graphical projection callback throws after canonical commit.
- Same-incarnation CreateObject arrives while a prior projection teardown is retained.
- Authoritative delete races a dormant exact-incarnation record.
- Equal, stale, and wrapped timestamps.
- Local-ID wrap and namespace exhaustion.
- Graphical hydration fails before and after resource registration.
- Unknown-object move and removal.
- Container replacement mutates contents from inside an event callback.
- Multiple Runtime groups use identical GUIDs and session-generation values.
- Subscription disposal is replayed and Runtime disposal occurs during a callback.
Mandatory gates
Run in this order:
- Runtime stream, borrowed-view, sequence, reentrancy, and identity tests.
- Core
ClientObjectTable*andObjectTableWiring*tests. - Focused App Runtime-adapter, hydration, update, rebucket, deletion, reset, inventory, paperdoll, selection, interaction, and cooldown tests.
- Direct-host versus graphical-host normalized parity fixture.
- All Runtime tests.
- All App tests.
dotnet build AcDream.slnx -c Release.dotnet test AcDream.slnx -c Release --no-build.- Exact-binary connected lifecycle/reconnect gate.
- At every connected checkpoint: zero render-shadow mismatches, zero pending deltas, graceful process exit, and exact canonical/projection teardown convergence.
A user visual pause is required only if an automated or connected gate exposes a visible inventory, selection, radar, paperdoll, or world-object symptom.
Acceptance
- Runtime owns one per-session ordered entity/object stream.
- Entity and inventory deltas share one generation/sequence order.
- Runtime views read the directory and object table directly.
- Every active canonical entity has a Runtime key before App projection.
- App reconstructs neither entity nor inventory events.
- Projection visibility is not emitted as canonical gameplay withdrawal.
- Graphical and no-window normalized traces are identical.
- No copied collection, queue, extra input frame, or backend dependency exists.
- Reentrant callbacks cannot resurrect or mutate a superseded incarnation.
- Reset/disposal leaves zero observers, queued deltas, and retained stream state.
- Complete Release and exact connected gates pass on one committed binary.
Rollback
J3.5 is independently reversible:
git revert ce3ac310d92722ffb637e81cb1957458874dd220
Do not revert J3.4 as a shortcut for a J3.5 failure. Automated and connected
evidence is recorded in
../research/2026-07-26-slice-j3-5-canonical-delta-stream.md.
Next boundary
J3.6 executes from
2026-07-26-modern-runtime-slice-j3-hardening.md.
It failure-injects the complete canonical entity/object lifecycle and proves
zero records, tombstones, projections, resources, container entries, parent
candidates, subscriptions, or pending deltas after reset and disposal. That
closeout is required before J4 moves chat, inventory transactions, vitals,
enchantments, spells/components, cooldowns, and ViewModel-facing revisions.