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>
318 lines
13 KiB
Markdown
318 lines
13 KiB
Markdown
# 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
|
||
|
||
- `RuntimeEntityObjectLifetime` owns the exact directory and object table.
|
||
- `CurrentGameRuntimeViewAdapter.EntityView` still reads App
|
||
`LiveEntityRuntime`, so a direct host cannot enumerate canonical entities
|
||
without constructing the graphical projection host.
|
||
- Its inventory view reads the right table but asks `LiveEntityRuntime` to
|
||
recover incarnation metadata.
|
||
- `CurrentGameRuntimeEventAdapter` owns a separate sequencer and reconstructs:
|
||
- entity events only from `ProjectionVisibilityChanged`; and
|
||
- inventory events by subscribing directly to object-table callbacks.
|
||
- 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:
|
||
|
||
```csharp
|
||
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_TS` incarnation;
|
||
- 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:
|
||
|
||
- `Registered` after active-record/local-ID commit;
|
||
- `Updated` after same-incarnation CreateObject, ObjDesc, vector, movement,
|
||
accepted position, parent, or equivalent canonical refresh;
|
||
- `Rebucketed` after a canonical full-cell change;
|
||
- `Hidden` after a committed Hidden state transition;
|
||
- `Withdrawn` after pickup/accepted parent/cellless exit-world while the
|
||
incarnation remains logically alive;
|
||
- `Deleted` after authoritative identity retirement and retained-object
|
||
removal acceptance;
|
||
- generation replacement orders old `Deleted`/withdrawal before new
|
||
`Registered`.
|
||
|
||
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`;
|
||
- `Removed` with 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
|
||
`RuntimeEntityObjectLifetime` transaction 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 `RuntimeInventoryItemSnapshot` values 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.Entities` and `.Inventory` return the
|
||
Runtime group's borrowed views.
|
||
- Remove its `LiveEntityRuntime` dependency for canonical entity/inventory
|
||
reads.
|
||
- Make `CurrentGameRuntimeEventAdapter` forward the Runtime stream directly.
|
||
- Remove its `ProjectionVisibilityChanged` and 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:
|
||
|
||
1. Runtime stream, borrowed-view, sequence, reentrancy, and identity tests.
|
||
2. Core `ClientObjectTable*` and `ObjectTableWiring*` tests.
|
||
3. Focused App Runtime-adapter, hydration, update, rebucket, deletion, reset,
|
||
inventory, paperdoll, selection, interaction, and cooldown tests.
|
||
4. Direct-host versus graphical-host normalized parity fixture.
|
||
5. All Runtime tests.
|
||
6. All App tests.
|
||
7. `dotnet build AcDream.slnx -c Release`.
|
||
8. `dotnet test AcDream.slnx -c Release --no-build`.
|
||
9. Exact-binary connected lifecycle/reconnect gate.
|
||
10. 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:
|
||
|
||
```text
|
||
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`](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.
|