test(phys): A6.P3 #98 — live ResolveWithTransition capture apparatus

Apparatus only — no fix attempt. Per the systematic-debugging skill's
"3+ failures = question architecture" rule, the 6 hypotheses we
tested speculatively on the harness's airborne-at-tick-1 bug all
failed because we kept guessing what state the harness lacks. This
commit ships the evidence-driven path: capture the EXACT player
ResolveWithTransition call (every input + body-before + body-after +
result) into a JSON Lines fixture, then a comparison test loads the
fixture and replays it against the test engine. The first per-field
divergence pinpoints the missing apparatus state — no more guessing.

Adds:
- src/AcDream.Core/Physics/PhysicsResolveCapture.cs — new static module
  with CapturePath (env var ACDREAM_CAPTURE_RESOLVE), PhysicsBodySnapshot
  record, JSON Lines writer (thread-safe, flushes per record), process-
  exit hook for clean shutdown.

- PhysicsEngine.ResolveWithTransition probe wiring: snapshot body at
  method entry, snapshot again before return, refactor the two returns
  into one path so the capture call site is single. Filtered to
  IsPlayer mover flag so NPC/remote DR calls don't pollute.

- CellarUpTrajectoryReplayTests.cs:
  • Capture_WritesJsonLinesRecordsWhenIsPlayerAndEnabled — drives 3
    ticks with capture on, reads file back, verifies round-trip of
    inputs + body-before/after snapshots.
  • Capture_SkipsNonPlayerCalls — drives 3 NPC-style ticks (no
    IsPlayer flag), confirms the file is not created.

Off by default. Set ACDREAM_CAPTURE_RESOLVE=<path> to a writable file
path; capture starts on the next player ResolveWithTransition call.

Test baseline: 1172 + 8 pre-existing failures + 2 new smoke tests
that pass = 1174 + 8. Verified by stashed-baseline comparison.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-23 19:41:11 +02:00
parent ec47159a2e
commit fb5fba6229
4 changed files with 461 additions and 16 deletions

View file

@ -1314,6 +1314,16 @@ via `PlayerMovementController.ApplyServerRunRate`) or from
(~100500 lines/sec). Pair with retail's cdb breakpoint set at
`tools/cdb/a6-probe.cdb` for the A6.P1 capture protocol.
Runtime-toggleable via the DebugPanel "Diagnostics" section.
- `ACDREAM_CAPTURE_RESOLVE=<path>` — A6.P3 #98 live capture of every
player-side `PhysicsEngine.ResolveWithTransition` call (2026-05-23 PM
apparatus). Each call appends one JSON Lines record with full inputs,
PhysicsBody snapshot before AND after, plus the `ResolveResult`.
Filtered to `IsPlayer` mover flag — NPC / remote DR calls don't
pollute. Pairs with the trajectory replay harness comparison test
(`CellarUpTrajectoryReplayTests.Capture_*`) to diff captured vs harness
state per field — the first divergence pinpoints missing apparatus
state. Capture is OFF when the env var is unset (one null-check
cost per call).
- *(retired 2026-05-05 by L.3 M2/M3)* `ACDREAM_INTERP_MANAGER` was an
env-var gate on an experimental per-tick remote motion path. L.3 M2
(commit 40d88b9) replaced both gates (`OnLivePositionUpdated` +