docs: hand off placement campaign finish

This commit is contained in:
Erik 2026-08-03 12:54:47 +02:00
parent 175ad6b0d0
commit 205f3fea6f
8 changed files with 441 additions and 176 deletions

View file

@ -1,155 +1,285 @@
# Task: diagnose and fix the acdream collision/placement regressions, then COMMIT the fixes
# Next-agent prompt — finish the retail placement/collision campaign
Work EXCLUSIVELY in `C:\Users\erikn\.codex\worktrees\af5e\acdream`, branch
`codex/port-claude-agents`, HEAD `71604331`. Read `CLAUDE.md` at the repo
root FIRST — its rules bind you (grep-named-retail-first workflow, no
workarounds, divergence-register bookkeeping, connected gates, launch
instructions). The user's in-game observations quoted below are AXIOMS —
they override any green gate number.
Continue acdream from the 2026-08-03 stabilization checkpoint. The code is
modern; behavior must remain retail-faithful. The campaign is playable again,
but it is **not closed**.
## Standing constraints
## Start here
- NEVER `git add -A`, `git add .`, `git reset --hard`, or
Use the merged local `main` worktree:
```text
C:\Users\erikn\source\repos\acdream
```
The completed fixes originated on `codex/port-claude-agents` and are merged
into local `main`. Confirm the exact starting commit with `git rev-parse HEAD`
and read the operator's handoff message for the merge SHA. Do not reset,
clean, or overwrite the main worktree's untracked research/reference files.
The original feature worktree remains at:
```text
C:\Users\erikn\.codex\worktrees\af5e\acdream
```
That feature worktree contains protected user-local modifications and is not
the preferred continuation workspace.
Read these files in order before editing:
1. `CLAUDE.md` and `AGENTS.md`.
2. `docs/plans/2026-08-02-placement-cutover.md` — canonical placement-cutover
plan and the 2026-08-03 checkpoint.
3. This prompt.
4. `docs/research/2026-08-02-collision-throughput-handoff/implementer-progress.md`
— especially `## P1` and the final stabilization checkpoint.
5. `docs/research/2026-08-02-collision-throughput-handoff/p1-retirement-receipt-loop.md`.
6. `docs/research/2026-08-02-c3c-cutover-closeout.md`.
7. `docs/ISSUES.md` #269 and #276#280.
8. `docs/architecture/retail-divergence-register.md` rows AP-1, AP-22,
AP-131, AD-1, AD-10, AD-60, and TS-28.
`docs-drafts.md` is now explicitly historical. Do **not** apply it wholesale:
it predates the final fixes and incorrectly tries to reuse issue number #280.
## Binding rules
- Grep `docs/research/named-retail/acclient_2013_pseudo_c.txt` by named
`class::method` before fresh decompilation. Retail behavior is the oracle.
- Preserve the modern Runtime-owned, presentation-independent architecture.
Graphical and headless hosts must use the same canonical gameplay owners.
- Root causes only. Do not add timeouts, grace periods, suppression flags,
catch-and-swallow paths, duplicated placement writers, or compatibility
bypasses to make a test green.
- The user's connected observations are acceptance facts. A green automated
test cannot overrule a live regression.
- Never use `git add -A`, `git add .`, `git reset --hard`, or
`git checkout -- <path>`. Stage exact paths only.
- NEVER stage, revert, or normalize these user-local files: `AGENTS.md`,
`src/AcDream.App/Interaction/PlayerInteractionMovementSink.cs`,
`src/AcDream.App/Rendering/LiveAnimationPresentationContext.cs`,
`src/AcDream.Runtime/Physics/RuntimeRemotePhysicsUpdater.cs`,
`tests/AcDream.Core.Tests/Physics/CellTransitTests.cs`,
`tests/AcDream.Core.Tests/Physics/Issue133DungeonTeleportPrefixTests.cs`,
`tools/A8CellAudit/A8CellAudit.csproj`, the root
`implementer-progress.md`, and any `launch-*.log`.
- No workarounds without explicit user approval: root causes only — no
rate knobs, grace periods, suppression flags, or catch-and-swallow.
- Do not push. Commit locally to `codex/port-claude-agents` only.
- Connected gates need the user's ACE server at `127.0.0.1:9000`
(normally up). Follow CLAUDE.md's "Logout-before-reconnect" graceful-
close discipline between client launches. If ACE is unreachable, STOP
and report — never fake or skip a connected gate.
- Do not delete or normalize unrelated/untracked worktree content.
- Each independent fix must be a bisectable commit whose message records the
root cause and evidence.
- Update the divergence register and issues in the same commit that changes
their truth. Retire a row only when its exact legacy mechanism is gone.
- Do not push unless the user explicitly asks.
- Connected tests require the user's ACE server at `127.0.0.1:9000`. Close
the client gracefully before reconnecting so ACE releases the session.
## Where everything is
## What has been completed
- **This directory** (`docs/research/2026-08-02-collision-throughput-handoff/`):
`design-note.md` (the approved D2 delta-commit design),
`implementer-progress.md` (the complete campaign evidence trail — read
the `## Collision-clone O1/O2/O3` and `## C3c-F4` sections first),
`docs-drafts.md` (register/digest drafts NOT yet applied),
`user-observations-*.md` (the axioms).
- Campaign plan: `docs/plans/2026-08-02-placement-cutover.md`. C3c
closeout: `docs/research/2026-08-02-c3c-cutover-closeout.md`. Issues:
`docs/ISSUES.md` #276#279.
- Recent commits: `529e0e9d` (C3c production placement cutover — REVIEWED
and accepted), `f4ef2b2a`/`c52ce14a` (docs), `71604331` (the O1O3
collision delta-commit — **WIP, ON HOLD, NO review passed it**; its
commit message summarizes state).
- Logs (untracked): `launch-feeltest-oclone.log` (the FAILING user feel
test on the O-tree), `logs/connected-r6-soak-20260802-194423.*`
(current soak: Passed=false, 4 failures),
`logs/connected-r6-soak-20260802-143157.*` (pre-O soak: 37 failures),
`logs/connected-r6-soak-20260727-004942.artifacts` (last fully-PASSING
baseline), `logs/connected-world-gate-20260802-193029` (lifecycle PASS
on the O-tree).
### C3c and collision-publication checkpoint
## Problems to diagnose and fix (priority order)
- `529e0e9d` — C3c production first-entry cutover for graphical and headless
hosts.
- `71604331` — O(changed) per-landblock collision publication checkpoint.
Its original commit was deliberately marked WIP after the first feel test;
do not treat that old label as the current product status. The following
fixes addressed the observed failures.
**P1 — the retirement-receipt exception loop (smoking gun).**
`launch-feeltest-oclone.log` contains **243** occurrences of
`streaming: origin-recenter preparation will resume:
System.InvalidOperationException: Landblock <id> already has a full
retirement receipt.` — a continuous catch-retry loop during origin
recenter. Find the receipt-lifecycle break introduced (or exposed) by
the O1O3 diff (`git show 71604331`): the retirement machinery is
`RuntimePhysicsState.AdvanceCollisionRetirementMutation` (~:1880-1990),
`StreamingController.cs` `AdvanceRetirements` call sites (:648/:670/:689/:736),
recenter adoption at `LandblockPresentationPipeline.cs:322-345`. The
catch-and-resume site that swallows this exception may itself violate
the no-silent-catch rule — evaluate it too.
### Stabilization fixes, all user-verified where visual behavior applies
**P2 — user-visible placement failures on the O-tree (the axioms).**
Quote: "Monsters pop into existence as I run by them. ran to a different
place, spawned in the air long in front of me, stabs are placed
incorrectly. This is not how retail worked. I could see monsters way in
front of me." Suspects, in order: (a) P1's stalled/looping recenter
preparation starving placement; (b) the commit-time reflood diverging
from the deleted staged-clone world in live conditions
(`PhysicsEngine.CommitLandblockReplacement` +
`RefloodPrefixOwnersAfterReplacement`; the oracle test
`CommitTimeRefloodMatchesPrecomputedReflood` covers only 3 landblock
archetypes and may under-cover); (c) siblings of the staged-owner
admission gap the implementer already fixed once (ShadowObjectRegistry
phase 2); (d) ordering between the reflood and admission
commit/RetryDeferred for operations parked on the republished prefix.
Note: under C3c rules entities WAIT for collision before placing — so
"spawned mid-air" means placement completed against wrong/incomplete
collision, not merely late.
1. `01f4791e` — **retirement-receipt replay loop fixed.**
- Root cause: a pending-only live-projection bucket was promoted to a
second full landblock cleanup receipt after the first detach had already
committed. The duplicate guard threw; a broad resumable path replayed
the detach 243 times.
- Fix: retain pending identities without manufacturing another receipt;
post-commit receipt invariants are terminal, not resumable.
- Evidence: focused recenter tests; complete Release suite 10,815 passed /
4 skipped; lifecycle report
`logs/connected-world-gate-20260802-203751/report.json`; nine-stop report
`logs/connected-r6-soak-20260802-204309.report.json` with `Passed=true`,
nine checkpoints, zero failures, zero wait cues, zero pending
retirements, and no recurrence of the 243x exception.
**P3 — the soak's 4 remaining failures.**
`connected-r6-soak-20260802-194423.report.json`:
`pendingLandblockRetirements`=131/122 at sawato-baseline/plateau,
`waitCueShown=true` at holtburg, cpuUs p99 over-limit at the six
non-Caul stops. The WIP's "exposed pre-existing" classification
(reasoning in `implementer-progress.md ## Collision-clone`) was made
BEFORE P1 was discovered — re-judge it against the exception loop.
2. `670f307c` — **remote placement and targeting share one world frame.**
- Root cause: CreateObject positions are landblock-local, but Runtime
submitted remote first-entry placement with zero world offset; the local
physics host also published a landblock-local origin to targeting.
- Fix: Runtime owns the accepted local-player world center, converts remote
Create placement before SetPosition, and publishes the local body's world
position.
- User gate: monsters and statics place correctly; monsters chase and hit
the visible player instead of attacking another coordinate.
**P4 — door/use approach regression (in COMMITTED C3c code, `529e0e9d`).**
Quote: "I dont approach doors when I use them." Using a door no longer
walks the character to it first. Prime suspect:
`src/AcDream.App/Input/PlayerModeController.cs`'s conditional MoveTo
bind (`if (controller.MoveTo is { } moveTo)`) — the legacy path CREATED
the MoveToManager at attach via factory; the flip only binds approach
callbacks IF Runtime's publication chain already ran `MakeMoveToManager`
(`RuntimeLocalPlayerPhysicsPublicationState`). Establish when (or
whether) Runtime creates it relative to player-mode attach; the
interaction flow is `SelectionInteractionController`
approach-MoveTo → use (see `claude-memory/project_interaction_pipeline.md`
and register row AD-27).
3. `1fc529cd` — **distant Use/approach restored.**
- Root cause: Runtime object lookup is intentionally non-constructing, so a
static door/corpse could enter MoveTo without a physics host and its
target snapshot expired at the origin. Startup placement could also
leave an impossible pre-PartArray motion suffix ahead of later actions.
- Fix: ensure the canonical minimal static host before routing MoveTo and
reconcile the startup suffix exactly at presentation attach.
- User gate: near and distant object use works, including approach, turn,
and use after arrival.
**P5 — intermittent spell particle loss (#279, C3c-era).**
One-shot VFX/scripts arriving while their entity is in C3c's
suppressed-until-receipt window are dropped instead of deferred; retail
queues scripts for not-yet-in-world objects (grep
`docs/research/named-retail/acclient_2013_pseudo_c.txt` for the
PlayScript/pending-script handling around `HandleCreateObject`
0x00454C80 / `set_hidden` 0x00514C60). Route the deferral through the
presentation-binding moment
(`TryApplyInitialCreateCompletionPresentation` in
`src/AcDream.App/World/LiveEntityRuntime.cs` and the equivalent headless
sink).
4. `f24532ad` — **spell, recall, projectile, and static VFX binding fixed.**
- Root cause: C3c could create effect/projectile/static-animation sidecars
before first SetPosition had bound the entity's mesh, pose, cell, and
visibility. One-shot F754/F755 packets were lost and projectiles could
inherit a cell-less body.
- Fix: exact-incarnation presentation barrier and FIFO replay; retry
projectile/static binding on the committed visibility edge; synchronize
effect cells on rebucket.
- User gate: buffs/protections, recall effects, arrows, combat spell
projectiles, portals, and static animation all work.
**P6 — verify after P1P3 land:** purple materialization haze re-firing
while standing still (#278a — believed the same root as the visibility
edges), the monster pop-in re-check, slope-glide vs open #269.
5. `175ad6b0` — **login materialization acknowledgement fixed.**
- Root cause: ACE creates the local player Hidden and releases that state on
LoginComplete. Sending LoginComplete from raw F746 receipt raced first
canonical placement and left the purple haze over the character.
- Fix: one completion callback from Runtime's local first-entry terminal
edge; content-less headless retains its only truthful accepted-Create
edge.
- User gate: ordinary login no longer leaves the purple haze; recall still
has the intended materialization presentation.
## Required process per fix
### Latest focused verification
1. Root cause with file:line evidence BEFORE writing the fix; for any
AC-specific behavior, cite the named-retail decomp.
2. Tests that fail pre-fix and pass post-fix.
3. Any new retail deviation gets its register row
(`docs/architecture/retail-divergence-register.md`) in the SAME
commit; when the collision work lands for real, APPLY the prepared
`docs-drafts.md` entries (AD-6 successor note, AD-62, digest entries,
#280) rather than re-deriving them.
4. Gates before each commit: `dotnet build AcDream.slnx -c Release`;
`dotnet test AcDream.slnx -c Release --no-build --no-restore -m:1`
with `ACDREAM_PAK_PATH=C:\Users\erikn\Documents\Asheron's Call\acdream.pak`
(baseline: 10,812 passed / 0 failed / 4 skipped);
`tools/run-connected-world-lifecycle-gate.ps1` must PASS; then
`tools/run-connected-r6-soak.ps1` — the FINAL acceptance:
`report.json` `Passed=true`, zero failures, `waitCueShown=false` at
all nine stops, per-checkpoint cpuUs p99 within +10% of the
20260727-004942 baseline. READ `report.json`, never the markers log.
A failure whose only signature is `activeTeleportCount=1` at a stable
checkpoint is user interference — one clean re-run allowed; any other
failure is real: fix it, don't re-run past it.
5. COMMIT each completed fix as its own bisectable commit on
`codex/port-claude-agents` with a message explaining the why.
6. Manual verification launch (for the user): the PowerShell launch in
CLAUDE.md "Running the client against the live server", plus
`ACDREAM_RETAIL_UI=1`.
After the final fix, these passed:
## Final deliverable
- 90 focused App effect/projectile/static-animation scheduler tests.
- Two focused Runtime login-completion tests.
- The exact live-entity cell-tracking regression.
- All 79 Headless tests.
- `dotnet build AcDream.slnx -c Release --no-restore` with zero errors
(existing warnings remain).
Per-problem disposition (root cause → fix → evidence), the commit SHAs,
full gate numbers including the soak report values, and an explicit list
of anything left open.
The long complete suite and connected nine-stop soak were **not rerun after
the final four stabilization commits**. The P1 soak proves P1's binary, not
the final campaign binary.
## What remains — execute in this order
### 1. Reconcile the six selected-fixture failures
A broad selected run after cleanup exposed:
- five failures in `LiveEntityRuntimeTests`, associated with the still-open
placement/cell cutover;
- one old `RuntimeLiveEntitySessionControllerTests` remote-first-entry fixture
that provides an empty collision source while the production contract now
requires truthful collision admission.
Re-run these two classes first and record the exact test names and assertions.
Classify each as either a real product failure or a stale fixture. If stale,
update the fixture to provide the same valid prepared collision neighborhood
as production; never weaken the product contract or merely change expected
values. If real, fix the owning production mechanism and add a smaller
regression test.
Suggested first commands:
```powershell
$env:ACDREAM_PAK_PATH='C:\Users\erikn\Documents\Asheron''s Call\acdream.pak'
dotnet test tests/AcDream.App.Tests/AcDream.App.Tests.csproj -c Release --no-restore --filter FullyQualifiedName~LiveEntityRuntimeTests -m:1
dotnet test tests/AcDream.Runtime.Tests/AcDream.Runtime.Tests.csproj -c Release --no-restore --filter FullyQualifiedName~RuntimeLiveEntitySessionControllerTests -m:1
```
### 2. Finish C4: remaining authoritative placement routes
The plan still marks routes 27 open:
- route 2: ForcePosition;
- route 3: portal placement through `RuntimeWorldTransitState` and
`RuntimePortalPlacementAuthority`;
- route 4: remote Create/Position, deleting the remaining
`RemoteTeleportController`/inline MoveOrTeleport duplicate;
- route 5: authoritative projectile correction;
- route 6: drops and split-recovery marking;
- route 7: pickup, parent-detach, and delete/recreate residue.
Inventory every current writer before editing. For each route, prove:
- one Runtime SetPosition transaction owns accepted frame, exact cell,
collision result, shadow/workset membership, and deferred-cell lifetime;
- App only projects the committed result;
- graphical and headless hosts use the same command and state path;
- stale sequences, delete/GUID reuse, missing cells, portal generations, and
replacement collision generations cannot commit old state;
- no route reconstructs from a stale spawn or uses the legacy outdoor demote/
terrain-Z lift.
Resolve #276 when the spawn settler's resolved `CellId` becomes authoritative.
Resolve #277 with a real service-window/celless lifecycle instead of relying
on ACE's current broadcast radius.
### 3. Fix #280: destination prefetch before portal reveal
Current behavior waits only a hard-coded radius-one (3x3) outdoor
neighborhood, while the visible configured world extends farther. The user
can see distant terrain continue building after portal exit.
Port the retail mechanism, not a larger magic number:
- `CellManager::PreFetchCells @ 0x00455820`;
- `LScape::PreFetchCells @ 0x00505660`;
- `CLandBlock::PreFetchCells` and `CLandBlockInfo::PreFetchCells`;
- `SmartBox::UseTime @ 0x00455410` while `blocking_for_cells`;
- the `TAS_TUNNEL_CONTINUE` resume/reveal order.
Use the quality/view-distance configured destination window. Hold one
generation-scoped reservation across terrain, buildings/statics, EnvCells,
render publication, composite textures, and collision. Keep portal UI and
wait cue responsive. Never reveal early because of a timeout, and do not wait
for an impossible terminal marker for all dynamic ACE objects.
Acceptance: repeated login, `/ls`, spell recall, and portals at every quality
setting reveal no constructing terrain, missing nearby statics/buildings,
unready interiors, missing composites, or absent nearby collision. Dynamic
monsters/items may still arrive later from ACE.
### 4. C5 closeout and live gates
After steps 13:
1. Delete every superseded placement writer and compatibility projection.
2. Run focused Runtime/Core/App tests for every route.
3. Run the complete Release solution suite with the installed pak.
4. Run the exact lifecycle/reconnect route.
5. Run the canonical nine-stop soak on the **final binary**. Read
`report.json`, not marker output. Required: `Passed=true`, zero failures,
every canonical checkpoint, `waitCueShown=false`, zero pending
publication/retirement/reveal debt, graceful exit, and no render-shadow
mismatch. Diagnose any real failure; do not rerun past it.
6. Perform two-client observation for remote creation, chase/attack, doors,
drops/pickups, portal departure/arrival, arrows, and spells.
7. Ask the user for the remaining #269/#278 slope-glide comparison at the
known impassable slope.
Only then retire AP-1, AD-1, AP-131, and the legacy half of AD-60 and close
the corresponding placement issues.
### 5. Finish the original physics-divergence campaign
After placement C5 is green:
- **AP-22:** make `ShadowShapeBuilder` the sole authority for authored Setup
collision shapes. Preserve cylinder order; use authored spheres when there
are no cylinders; cylinder-first for mixed data; truly shapeless means no
shadow. Remove invented `Setup.Radius` cylinders, `Radius * 2` heights, and
sphere-to-cylinder coercion across graphical, headless, static, and live
paths.
- **AD-10:** prove remote motion uses the full transition sweep, remove
terrain-normal preprojection, and let `CTransition::adjust_offset` project
against the actual retained contact plane. Preserve interpolation,
correction replacement, Hidden state, and network cadence.
- Run the final movement/collision matrix and update the divergence ledger,
architecture, roadmap, milestones, memory, `CLAUDE.md`, and `AGENTS.md`.
Resume vendor Slice 5 only after this campaign is genuinely closed.
## Required deliverable
For every remaining item report:
- observed failure and deterministic reproduction;
- retail/reference evidence with named functions and addresses;
- root cause in plain language plus file/line evidence;
- exact fix and why it preserves Runtime ownership;
- tests added or corrected;
- commit SHA;
- complete build/test/connected-gate numbers;
- user visual result where required;
- divergence/issue rows retired, narrowed, or left open.
Finish with an explicit list of anything still open. Do not describe the
campaign as complete while any C4 route, #280, final-binary soak, AP-22,
AD-10, or required user visual gate remains.

View file

@ -1,5 +1,14 @@
# Docs-commit drafts — collision publication-throughput fix (O1/O2/O3)
> **HISTORICAL DRAFT — DO NOT APPLY WHOLESALE (2026-08-03).** The O1/O2/O3
> implementation landed in `71604331` and the user-visible stabilization
> fixes continued through `175ad6b0`. This draft predates that work, assigns
> issue number #280 to the collision clone even though #280 now canonically
> tracks incomplete portal-destination prefetch, and names ledger edits that
> must be re-audited against the final production tree. It remains only as
> research evidence. Use `NEXT-AGENT-PROMPT.md`, the campaign plan, and the
> live divergence register for current work.
Drafted per contract; NOT applied to the repo. Apply in the docs commit after
code review. Register judgment executed as pinned: AD-6 stays retired with a
successor note; the residual timing/order compression gets a NEW row (AD-62).

View file

@ -4547,3 +4547,41 @@ _entities.AdvanceCreateAuthority in the executor, inside
ApplyWeenieDescriptionAction; registration advance still residence-gated).
Focused files 82/82; Runtime 1,003/1,003; App 4,039/3 skips of 4,042;
git diff --check exit 0. No staging/commits.
## 2026-08-03 stabilization checkpoint and handoff
Five separately bisectable root-cause fixes followed the O1/O2/O3 checkpoint:
1. `01f4791e` — pending-only live projection buckets no longer manufacture a
second origin-recenter retirement receipt. Complete Release, lifecycle,
and canonical nine-stop soak passed on this binary; the soak had nine
checkpoints, zero failures, zero wait cues, and zero pending retirements.
2. `670f307c` — Runtime converts remote Create frames through the accepted
local-player world center and publishes the local physics body's world
position. The user accepted monster/static placement, chase, and attacks.
3. `1fc529cd` — distant Use materializes the canonical minimal static physics
host before MoveTo, and presentation attach reconciles the pre-PartArray
startup motion suffix. The user accepted near and distant object use.
4. `f24532ad` — exact-incarnation effect packets wait for canonical
presentation binding; projectile and static-animation sidecars retry on the
committed visibility edge; effect cells follow rebuckets. The user accepted
buffs, recalls, arrows, combat spell projectiles, portals, and statics.
5. `175ad6b0` — LoginComplete is emitted from the local first-placement
terminal edge instead of raw PlayerCreate receipt. The user accepted login
materialization haze behavior.
Post-fix focused evidence: 90 App effect/projectile/static-scheduler tests,
two Runtime login tests, the exact live-entity cell tracking test, all 79
Headless tests, and a Release solution build with zero errors passed. The
complete suite and connected nine-stop soak were not rerun after the final
four stabilization commits. A broader selected fixture run exposed five
still-open `LiveEntityRuntimeTests` failures associated with the placement
cutover and one old remote-first-entry fixture that supplies an empty
collision source. These are campaign work, not evidence to weaken the new
production contracts.
Open campaign finish: classify/fix those six fixtures; finish placement routes
27 and delete legacy writers; resolve #276/#277 and portal-prefetch #280; run
the final-binary complete suite/lifecycle/nine-stop/two-client gates; perform
the #269 slope-glide visual check; retire AP-1/AD-1/AP-131/AD-60 only when the
legacy paths are gone; then land AP-22 and AD-10 and close the ledger.