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:
parent
e937cc36df
commit
d9bf4c4960
15 changed files with 757 additions and 114 deletions
237
docs/plans/2026-07-25-modern-runtime-slice-j3-object-table.md
Normal file
237
docs/plans/2026-07-25-modern-runtime-slice-j3-object-table.md
Normal 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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue