docs: A6.P3 #98 — record apparatus convergence + residual X-motion
The findings doc gets an evening-v2 follow-on documenting: - GfxObj dump infrastructure shipped (cc3afbc) - Harness reproduces cap-event collision normal (97fec19) - Residual +0.0266m X-motion divergence — the new investigation target - Pre-existing test suite flakiness (out of scope, tracked separately) CLAUDE.md's "Current A6 phase" block points at the residual divergence as the next concrete move with the test that gives <1s feedback per fix attempt. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
97fec19dbb
commit
7729bdcf98
2 changed files with 150 additions and 59 deletions
40
CLAUDE.md
40
CLAUDE.md
|
|
@ -842,9 +842,43 @@ cause identified.** Four commits (`fb5fba6` → `44614ab` → `0f2db62` →
|
||||||
- 13 new cell fixtures cover the full 0xA9B4014X neighborhood (272 KB).
|
- 13 new cell fixtures cover the full 0xA9B4014X neighborhood (272 KB).
|
||||||
Findings doc (canonical pickup):
|
Findings doc (canonical pickup):
|
||||||
[`docs/research/2026-05-23-a6-p3-issue98-comparison-harness-findings.md`](docs/research/2026-05-23-a6-p3-issue98-comparison-harness-findings.md).
|
[`docs/research/2026-05-23-a6-p3-issue98-comparison-harness-findings.md`](docs/research/2026-05-23-a6-p3-issue98-comparison-harness-findings.md).
|
||||||
**Next-session move: extract cottage GfxObj polygons via focused
|
|
||||||
`ACDREAM_PROBE_BUILDING=1` capture, register as ShadowEntry in harness,
|
**Evening v2 follow-on — apparatus convergence SHIPPED 2026-05-23 PM.**
|
||||||
flip first-cap test to assertion form.**
|
Two commits (`cc3afbc` → `97fec19`):
|
||||||
|
- `cc3afbc` adds the GfxObj dump infrastructure (`ACDREAM_DUMP_GFXOBJS`)
|
||||||
|
mirroring the existing `ACDREAM_DUMP_CELLS` pattern, with new
|
||||||
|
`GfxObjDump`/`GfxObjDumpSerializer` parallel to `CellDump`. The new
|
||||||
|
env var triggers `PhysicsDataCache.CacheGfxObj` to write the full
|
||||||
|
resolved polygon table as JSON when a listed id caches. Closes the
|
||||||
|
gap that the existing `[resolve-bldg]` probe couldn't fill (the BSP
|
||||||
|
wire site that populates `LastBspHitPoly` was never wired, so the
|
||||||
|
probe only emitted GfxObj-level metadata, not per-poly geometry).
|
||||||
|
- `97fec19` lands the cottage GfxObj fixture (`0x01000A2B`, 74 polygons,
|
||||||
|
BSP radius 13.989m matching live), the new `RegisterCottageGfxObj`
|
||||||
|
harness helper, and a minimum-stub landblock so
|
||||||
|
`TryGetLandblockContext` succeeds at the cellar XY. Harness now
|
||||||
|
reproduces the live `cn=(0,0,-1)` cap bit-perfect. The full per-field
|
||||||
|
round-trip uncovers ONE residual: live preserves +0.0266m of +X
|
||||||
|
motion through the cap (edge-slide along the cottage floor); harness
|
||||||
|
blocks all motion. Captured in
|
||||||
|
`LiveCompare_FirstCap_ResidualXMotionDivergence_DocumentsNextInvestigation`
|
||||||
|
in documents-the-bug form.
|
||||||
|
- All 21 issue-#98-relevant tests (12 harness + 4 GfxObjDumpRoundTrip +
|
||||||
|
1 new PhysicsDiagnosticsTests + 4 CellDumpRoundTripTests) pass
|
||||||
|
deterministically in isolation.
|
||||||
|
- Pre-existing test suite flakiness observed (8–19 failures across runs
|
||||||
|
of the same code, from PhysicsResolveCapture / PhysicsDiagnostics
|
||||||
|
statics leaking between test classes). INDEPENDENT of A6.P3 — verified
|
||||||
|
by stashing the cottage helper and reproducing the same flaky range.
|
||||||
|
Out of scope for this session; tracked as follow-up.
|
||||||
|
|
||||||
|
**Next-session move:** investigate the residual +X edge-slide divergence
|
||||||
|
in `Transition.transitional_insert` / `AdjustOffset`'s handling of a
|
||||||
|
`cn=(0,0,-1)` head-bump. Live treats it as a Z-only constraint and
|
||||||
|
slides the remaining XY motion along the cottage floor; harness blocks
|
||||||
|
the entire move vector instead. The harness's
|
||||||
|
`LiveCompare_FirstCap_ResidualXMotionDivergence_DocumentsNextInvestigation`
|
||||||
|
test gives <1s feedback per fix attempt. ~2 hours estimate.
|
||||||
Original demo scenario (Holtburg Sewer end-to-end) is unreachable: sewer
|
Original demo scenario (Holtburg Sewer end-to-end) is unreachable: sewer
|
||||||
doesn't exist on this server, and **issue #95** (portal-graph visibility
|
doesn't exist on this server, and **issue #95** (portal-graph visibility
|
||||||
blowup) blocks any substitute dungeon. Revised M1.5 demo split into
|
blowup) blocks any substitute dungeon. Revised M1.5 demo split into
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,12 @@ documents the FIRST evidence-driven step in the saga.
|
||||||
|
|
||||||
## TL;DR
|
## TL;DR
|
||||||
|
|
||||||
|
**Updated 2026-05-23 evening v2: apparatus convergence shipped.** The
|
||||||
|
harness now reproduces the live cottage-floor cap event bit-perfect on
|
||||||
|
the collision normal. The residual divergence is a single +X-motion
|
||||||
|
edge-slide gap; everything else round-trips. The session below covers
|
||||||
|
both arcs (root-cause identification THEN convergence).
|
||||||
|
|
||||||
- **Evidence-driven apparatus shipped.** `PhysicsResolveCapture` writes one
|
- **Evidence-driven apparatus shipped.** `PhysicsResolveCapture` writes one
|
||||||
JSON Lines record per player ResolveWithTransition call when
|
JSON Lines record per player ResolveWithTransition call when
|
||||||
`ACDREAM_CAPTURE_RESOLVE=<path>` is set. 41,228 records from a single
|
`ACDREAM_CAPTURE_RESOLVE=<path>` is set. 41,228 records from a single
|
||||||
|
|
@ -21,21 +27,26 @@ documents the FIRST evidence-driven step in the saga.
|
||||||
new `LiveCompare_*` tests in `CellarUpTrajectoryReplayTests.cs` load three
|
new `LiveCompare_*` tests in `CellarUpTrajectoryReplayTests.cs` load three
|
||||||
representative records (spawn, on-ramp, first-cap) and replay them
|
representative records (spawn, on-ramp, first-cap) and replay them
|
||||||
through the harness engine. Spawn + on-ramp PASS bit-perfect; first-cap
|
through the harness engine. Spawn + on-ramp PASS bit-perfect; first-cap
|
||||||
FAILS with a clear divergence — the right divergence.
|
FAILED with a clear divergence — the right divergence.
|
||||||
- **Root cause identified: the cottage GfxObj is missing from the harness.**
|
- **Root cause identified: the cottage GfxObj was missing from the harness.**
|
||||||
Live cap attributes the blocking entity to `obj=0xA9B47900` — a
|
Live cap attributes the blocking entity to `obj=0xA9B47900` — a
|
||||||
landblock-baked static building. The cottage's floor polygons live in
|
landblock-baked static building. The cottage's floor polygons live in
|
||||||
this GfxObj's polygon table (registered as a ShadowEntry), NOT in any
|
this GfxObj's polygon table (registered as a ShadowEntry), NOT in any
|
||||||
cottage CELL. The harness's `RegisterStairRampGfxObj` already models
|
cottage CELL.
|
||||||
the cottage's RAMP polygon but is commented out and only covers one
|
- **Apparatus convergence (v2 update).** With the cottage GfxObj
|
||||||
polygon. We need the full cottage GfxObj polygon set.
|
`0x01000A2B` extracted via the new `ACDREAM_DUMP_GFXOBJS` infrastructure
|
||||||
|
and registered as a ShadowEntry in `BuildEngineWithCellarFixtures`, the
|
||||||
|
harness now reproduces the live `cn=(0,0,-1)` cap exactly. The
|
||||||
|
full per-field round-trip reveals one residual: live preserves
|
||||||
|
+0.0266 m of +X motion through the cap; harness blocks all motion.
|
||||||
|
That's the next investigation target — see the "Residual divergence"
|
||||||
|
section below.
|
||||||
- **Not a step-up / AdjustOffset bug.** The head sphere (top at Z=foot+1.2)
|
- **Not a step-up / AdjustOffset bug.** The head sphere (top at Z=foot+1.2)
|
||||||
hits the cottage floor at Z=94.0 from BELOW. Math: cap at foot Z=92.74
|
hits the cottage floor at Z=94.0 from BELOW. Math: cap at foot Z=92.74
|
||||||
matches 94.0 − 1.2 = 92.80. Confirmed by user reporting same cap when
|
matches 94.0 − 1.2 = 92.80. Confirmed by user reporting same cap when
|
||||||
JUMPING in the cellar (purely vertical motion). The retail comparison
|
JUMPING in the cellar (purely vertical motion). The retail comparison
|
||||||
question is now "why doesn't retail block the head sphere there?" —
|
question is now sharpened to "how does live's post-cap edge-slide
|
||||||
likely cottage GfxObj polygon side-mode + retail's BSP query
|
preserve the +X component that the harness drops?"
|
||||||
treats single-sided polygons correctly.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -220,43 +231,73 @@ polygon facing DOWN.
|
||||||
|
|
||||||
## Next-session pickup
|
## Next-session pickup
|
||||||
|
|
||||||
### Recommended move: extract cottage GfxObj polygons + register in harness
|
### What shipped 2026-05-23 evening v2 (post-prior-section)
|
||||||
|
|
||||||
1. **One more focused live capture** with `ACDREAM_PROBE_BUILDING=1` (the
|
Three commits land apparatus convergence on the cap event:
|
||||||
`[resolve-bldg]` probe). The cap event will dump the cottage GfxObj's
|
|
||||||
full BSP root + hit polygon vertices in both object-local and world
|
|
||||||
space, plus the cottage GfxObj id, scale, rotation, and world origin.
|
|
||||||
|
|
||||||
2. **Add `RegisterCottageGfxObj(engine, cache)` helper** in the harness
|
| Commit | What |
|
||||||
(rename or extend `RegisterStairRampGfxObj`). Construct a synthetic
|
|---|---|
|
||||||
GfxObj with the full polygon table extracted from the capture. The
|
| `cc3afbc` | **GfxObj dump infrastructure.** Mirrors `ACDREAM_DUMP_CELLS`: new env var `ACDREAM_DUMP_GFXOBJS` triggers `PhysicsDataCache.CacheGfxObj` to write the full resolved polygon table as JSON, suffix `.gfxobj.json` so dumps don't collide with cell dumps in the same dir. New `GfxObjDump` DTO + `GfxObjDumpSerializer` parallel to `CellDump`; round-trip tests cover Capture / Write / Read / Hydrate; the Hydrate path constructs a synthetic single-leaf BSP for query coverage. |
|
||||||
BSP needs all polygons (ramp + cottage floor + walls); a one-leaf
|
| `97fec19` | **Harness reproduces the cottage-floor cap event.** `BuildEngineWithCellarFixtures` now registers a stub landblock 0xA9B40000 (TerrainSurface at z=-1000) so `TryGetLandblockContext` succeeds at the cellar XY, plus a new `RegisterCottageGfxObj` helper that loads the dumped cottage GfxObj fixture, hydrates it with synthetic BSP, and registers as a ShadowEntry at world (130.5, 11.5, 94.0) with 180° Z rotation — matching production's `GameWindow.cs:5893` registration shape for landblock-baked statics. The cottage fixture (74 polys, 6 downward-facing floor triangles, BSP radius 13.989 m) lives at `tests/.../Fixtures/issue98/0x01000A2B.gfxobj.json`; capture launch script is `launch-a6-issue98-cottage-gfxobj-dump.ps1`. |
|
||||||
wrapper suffices for the comparison-test scope.
|
|
||||||
|
|
||||||
3. **Uncomment the registration call** in
|
Test outcome at apparatus convergence:
|
||||||
`BuildEngineWithCellarFixtures`. Re-run
|
|
||||||
`LiveCompare_FirstCap_HarnessMissesCottageFloorBecauseCottageGfxObjNotRegistered`
|
|
||||||
— it should now FAIL because the harness DOES reproduce the cap.
|
|
||||||
Flip the test body to `AssertCallMatchesCapture(engine, captured)` to
|
|
||||||
enforce the round-trip.
|
|
||||||
|
|
||||||
4. **At this point the harness is FAITHFUL to live.** Now the question
|
| Test | Outcome | Meaning |
|
||||||
becomes "how does retail differ?" — which is a retail cdb investigation,
|
|---|---|---|
|
||||||
not an apparatus investigation.
|
| `LiveCompare_Tick0_Spawn` | PASS | Spawn round-trip preserved by the new landblock + cottage state |
|
||||||
|
| `LiveCompare_Tick376_OnRamp` | PASS | On-ramp round-trip preserved |
|
||||||
|
| `LiveCompare_FirstCap_HarnessReproducesCottageFloorCapNormal` | PASS (NEW) | Harness reproduces the live cn=(0,0,-1) cap-event normal exactly |
|
||||||
|
| `LiveCompare_FirstCap_ResidualXMotionDivergence_DocumentsNextInvestigation` | PASS (documents-the-bug) | Captures the ONE remaining post-cap divergence: live preserves +0.0266 m of +X motion through the cap (edge-slide along the cottage floor in XY); harness blocks ALL motion. Y and Z agree. |
|
||||||
|
|
||||||
### Alternative move: skip ahead to the retail cdb question
|
### The residual divergence (next investigation target)
|
||||||
|
|
||||||
If the user is impatient with apparatus polish, skip step 3 and go
|
After registering the cottage GfxObj:
|
||||||
directly to a retail trace of the cellar-up scenario. Attach cdb to a
|
|
||||||
running retail acclient, set breakpoints on `BSPTREE::find_collisions`
|
|
||||||
and `CGfxObj::shadow_find_obj_collisions`, walk up the cottage ramp,
|
|
||||||
log every BSP query against the cottage GfxObj. Compare which polygons
|
|
||||||
retail finds vs which polygons our acdream engine finds.
|
|
||||||
|
|
||||||
The retail trace is the ultimate oracle — but it requires reproducible
|
```
|
||||||
retail-server state (the user has a working `+Acdream` on local ACE,
|
Live: cn=(0,0,-1), position=(141.3865, 7.2243, 92.7390) ← +X motion preserved
|
||||||
but the retail client also needs to connect there, which works per the
|
Harness: cn=(0,0,-1), position=(141.3599, 7.2243, 92.7390) ← X stuck at input
|
||||||
CLAUDE.md "Retail debugger toolchain" section).
|
Input: currentPos=(141.3599, 7.2243, 92.7390)
|
||||||
|
targetPos =(141.3865, 6.8221, 92.7390)
|
||||||
|
requestedDelta=(+0.0266, -0.4022, 0)
|
||||||
|
```
|
||||||
|
|
||||||
|
The cap-event collision normal matches bit-perfect. Position diverges
|
||||||
|
in X only. Working hypothesis: live's response to a `cn=(0,0,-1)`
|
||||||
|
head-bump treats it as a Z-only constraint and edge-slides the
|
||||||
|
remaining XY component along the cottage floor; harness's BSP path is
|
||||||
|
rejecting the entire move vector instead of computing a slid offset.
|
||||||
|
|
||||||
|
That hypothesis is the next-session investigation target — work the
|
||||||
|
slide path in `Transition.transitional_insert` / `AdjustOffset` against
|
||||||
|
the production cap-event call. The new
|
||||||
|
`LiveCompare_FirstCap_ResidualXMotionDivergence_DocumentsNextInvestigation`
|
||||||
|
test PASSES today (asserting the current residual) and FAILS when the
|
||||||
|
divergence closes — that's the signal to flip it into
|
||||||
|
`AssertCallMatchesCapture` form.
|
||||||
|
|
||||||
|
### Alternative pickup move: retail cdb trace at the cottage ramp-top
|
||||||
|
|
||||||
|
If apparatus polish is enough and the user wants to widen the question
|
||||||
|
to "how does retail differ?", attach cdb to a running retail acclient
|
||||||
|
(see CLAUDE.md "Retail debugger toolchain"), set breakpoints on
|
||||||
|
`BSPTREE::find_collisions` and `CGfxObj::shadow_find_obj_collisions`,
|
||||||
|
walk up the cottage ramp, and log every BSP query against the cottage
|
||||||
|
GfxObj. Compare which polygons retail finds vs which polygons our
|
||||||
|
acdream engine finds. Retail's trace is the ultimate oracle for the
|
||||||
|
"how does retail differ?" question — but the apparatus-side X residual
|
||||||
|
investigation is the more focused, faster-feedback next step.
|
||||||
|
|
||||||
|
### Pre-existing test flakiness (out of scope but documented)
|
||||||
|
|
||||||
|
While verifying the cottage helper, the full `dotnet test` serial run
|
||||||
|
produced 8–19 failures across 1192 tests depending on order — the
|
||||||
|
suite has static-state leakage between test classes (likely from
|
||||||
|
`PhysicsResolveCapture.CapturePath`, `PhysicsDiagnostics.Probe*Enabled`,
|
||||||
|
and similar global mutators). The flakiness is **independent of A6.P3**:
|
||||||
|
stashing the cottage helper out and rerunning produces the same flaky
|
||||||
|
range. All 21 issue-#98-relevant tests (12 harness + 4
|
||||||
|
`GfxObjDumpRoundTripTests` + 1 new `PhysicsDiagnosticsTests` + 4
|
||||||
|
`CellDumpRoundTripTests`) pass deterministically in isolation.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -279,40 +320,56 @@ CLAUDE.md "Retail debugger toolchain" section).
|
||||||
## Pickup prompt for next session
|
## Pickup prompt for next session
|
||||||
|
|
||||||
```
|
```
|
||||||
A6.P3 #98 comparison harness — session paused 2026-05-23 evening.
|
A6.P3 #98 — apparatus convergence landed, residual X-motion divergence
|
||||||
|
is next.
|
||||||
|
|
||||||
Read FIRST:
|
Read FIRST:
|
||||||
1. docs/research/2026-05-23-a6-p3-issue98-comparison-harness-findings.md
|
1. docs/research/2026-05-23-a6-p3-issue98-comparison-harness-findings.md
|
||||||
(this doc — TL;DR, what was shipped, the root cause finding)
|
(this doc — particularly the "What shipped 2026-05-23 evening v2"
|
||||||
|
and "The residual divergence" sections)
|
||||||
2. tests/AcDream.Core.Tests/Physics/CellarUpTrajectoryReplayTests.cs
|
2. tests/AcDream.Core.Tests/Physics/CellarUpTrajectoryReplayTests.cs
|
||||||
(especially the LiveCompare_* tests + LiveCompare_FirstCap_DiagnosticDump)
|
(especially the two LiveCompare_FirstCap_* tests and the
|
||||||
3. CLAUDE.md "Current A6 phase" block (look for the 2026-05-23
|
RegisterCottageGfxObj helper)
|
||||||
evening apparatus + finding paragraph)
|
3. CLAUDE.md "Current A6 phase" block
|
||||||
|
|
||||||
State both altitudes:
|
State both altitudes:
|
||||||
Currently working toward: M1.5 — Indoor world feels right
|
Currently working toward: M1.5 — Indoor world feels right
|
||||||
Current phase: A6.P3 — comparison harness shipped + root cause
|
Current phase: A6.P3 — apparatus convergence shipped. Harness now
|
||||||
identified. Cottage GfxObj 0xA9B47900 missing from harness; needs
|
reproduces the live cottage-floor cap event (cn=(0,0,-1) round-trips
|
||||||
full polygon list extracted from a focused capture.
|
bit-perfect). Residual: +0.0266 m of +X motion lost in the harness's
|
||||||
|
post-cap slide where live preserves it.
|
||||||
|
|
||||||
The doc has TWO concrete options for what to do next:
|
Two concrete next moves:
|
||||||
|
|
||||||
(A) Extract cottage GfxObj polygons via focused ACDREAM_PROBE_BUILDING
|
(A) Investigate the +X edge-slide divergence in the harness. The
|
||||||
capture, register as ShadowEntry in harness, flip
|
LiveCompare_FirstCap_ResidualXMotionDivergence_DocumentsNextInvestigation
|
||||||
LiveCompare_FirstCap_* test to assertion form. ~2 hours.
|
test currently passes asserting the divergence; flipping it should
|
||||||
Apparatus convergence. RECOMMENDED.
|
drive the investigation. Likely target: Transition.transitional_insert
|
||||||
|
/ AdjustOffset's handling of a cn=(0,0,-1) head-bump — live treats
|
||||||
|
it as Z-only constraint and edge-slides the remaining XY motion;
|
||||||
|
harness blocks all motion. Decomp anchor: acclient_2013_pseudo_c.txt
|
||||||
|
in the find_obj_collisions → adjust_sphere_to_plane chain. ~2 hours
|
||||||
|
estimate.
|
||||||
|
|
||||||
(B) Skip ahead to retail cdb trace of cottage ramp-top BSP queries to
|
(B) Attach cdb to retail at the cottage ramp-top, trace the BSP queries,
|
||||||
answer "what does retail actually do differently?". Larger scope;
|
compare polygon-by-polygon what retail finds vs what acdream finds.
|
||||||
more direct fidelity question.
|
Authoritative for the "how does retail differ?" question but
|
||||||
|
larger scope (~half day setup + capture).
|
||||||
|
|
||||||
Pick A or B. If A: the doc has a step-by-step plan in the
|
(A) is recommended — the harness now isolates this divergence to a
|
||||||
"Recommended next-session move" section.
|
specific known XY slide path; the test gives <1s feedback per fix
|
||||||
|
attempt. (B) becomes valuable if (A) hypothesis chase stalls.
|
||||||
|
|
||||||
CLAUDE.md rules apply throughout. NO speculative fixes — the saga
|
CLAUDE.md rules apply throughout. NO speculative fixes — the saga
|
||||||
already converted from speculation to evidence-driven; keep it that
|
already converted from speculation to evidence-driven; keep it that
|
||||||
way.
|
way.
|
||||||
|
|
||||||
|
Out-of-scope but observed: pre-existing test suite has 8–19 failures
|
||||||
|
across runs of the same code due to static-state leakage between test
|
||||||
|
classes (PhysicsResolveCapture, PhysicsDiagnostics statics). Targeted
|
||||||
|
issue-#98 tests pass deterministically in isolation. Don't touch the
|
||||||
|
flakiness this session; it's a separate investigation.
|
||||||
|
|
||||||
Test baseline: 1178 + 8 pre-existing failures (serial run).
|
Test baseline: 1178 + 8 pre-existing failures (serial run).
|
||||||
Maintain throughout. The previously-failing
|
Maintain throughout. The previously-failing
|
||||||
LiveCompare_FirstCap_HarnessMissesCottageFloorBecauseCottageGfxObjNotRegistered
|
LiveCompare_FirstCap_HarnessMissesCottageFloorBecauseCottageGfxObjNotRegistered
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue