15-task TDD plan covering the three pieces of A6.P1:
Phase A — Build the [push-back] acdream probe (Tasks 1-9):
toggle + 3 helpers + 3 emission sites in BSPQuery/Transition,
DebugVM mirror, CLAUDE.md env-var docs.
Phase B — Build the cdb infrastructure (Tasks 10-12):
7-BP cdb script, PowerShell runner, README.
Phase C — Execute 9 captures + findings stub (Tasks 13-15):
PDB-match verify, capture dir + findings stub, scenario captures.
API surface verified against current code: ResolvedPolygon has no
Id property (probe omits poly attribution; cross-ref via time-
adjacent [push-back-cell] line). CheckOtherCells locals are
sp.CheckCellId + cellId + result (verified at TransitionTypes.cs
lines 1418-1473). SpherePath has Collide/InsertType/WalkInterp,
ObjectInfo has State (verified).
Spec: docs/superpowers/specs/2026-05-21-phase-a6-indoor-physics-fidelity-design.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1339 lines
49 KiB
Markdown
1339 lines
49 KiB
Markdown
# Phase A6.P1 — cdb Probe Spike Implementation Plan
|
||
|
||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||
|
||
**Goal:** Build the `[push-back]` acdream probe + the 7-breakpoint cdb script, then capture paired retail vs acdream traces at 9 indoor scenarios so A6.P2 can quantify the BSP collision response divergence.
|
||
|
||
**Architecture:** Three layers — (1) a new `ProbePushBackEnabled` toggle in `PhysicsDiagnostics` with 3 helper methods + 3 emission sites in `BSPQuery` / `Transition`; (2) a cdb breakpoint script + PowerShell wrapper that attaches to live retail; (3) a per-scenario capture protocol producing `{retail,acdream}.log` pairs.
|
||
|
||
**Tech Stack:** C# / .NET 10, xUnit, `Console.WriteLine` for probe emission (existing pattern), `cdb.exe` (Windows debugger), PowerShell.
|
||
|
||
**Spec:** [`docs/superpowers/specs/2026-05-21-phase-a6-indoor-physics-fidelity-design.md`](../specs/2026-05-21-phase-a6-indoor-physics-fidelity-design.md).
|
||
|
||
---
|
||
|
||
## File Structure
|
||
|
||
**Files to create:**
|
||
- `tools/cdb/a6-probe.cdb` — cdb breakpoint script (~80 lines)
|
||
- `tools/cdb/a6-probe-runner.ps1` — PowerShell wrapper to attach cdb (~40 lines)
|
||
- `tools/cdb/README-a6-probe.md` — usage documentation (~60 lines)
|
||
- `docs/research/2026-05-21-a6-captures/.gitkeep` — directory marker
|
||
- `docs/research/2026-05-21-a6-captures/scenN-<scenario>/{retail,acdream}.log` — capture pairs (filed by Task 15)
|
||
- `docs/research/2026-05-21-a6-cdb-capture-findings.md` — findings doc stub (Task 14)
|
||
|
||
**Files to modify:**
|
||
- `src/AcDream.Core/Physics/PhysicsDiagnostics.cs` — add `ProbePushBackEnabled` toggle + 3 helpers
|
||
- `src/AcDream.Core/Physics/BSPQuery.cs` — 2 emission sites (lines 332, 1550)
|
||
- `src/AcDream.Core/Physics/TransitionTypes.cs` — 1 emission site in `CheckOtherCells` (line ~1472)
|
||
- `src/AcDream.UI.Abstractions/Panels/Debug/DebugVM.cs` — add `ProbePushBack` mirror
|
||
- `tests/AcDream.Core.Tests/Physics/PhysicsDiagnosticsTests.cs` — add toggle roundtrip test
|
||
- `CLAUDE.md` — add `ACDREAM_PROBE_PUSH_BACK=1` to "Diagnostic env vars" section
|
||
|
||
---
|
||
|
||
## Phase A — Build the `[push-back]` probe (TDD)
|
||
|
||
### Task 1: Add `ProbePushBackEnabled` toggle to `PhysicsDiagnostics`
|
||
|
||
**Files:**
|
||
- Modify: `src/AcDream.Core/Physics/PhysicsDiagnostics.cs` (append after `ProbeWalkMissEnabled` at line ~277)
|
||
- Test: `tests/AcDream.Core.Tests/Physics/PhysicsDiagnosticsTests.cs`
|
||
|
||
- [ ] **Step 1: Write the failing test**
|
||
|
||
Append to `tests/AcDream.Core.Tests/Physics/PhysicsDiagnosticsTests.cs` (before the final closing `}` of the class):
|
||
|
||
```csharp
|
||
// -----------------------------------------------------------------------
|
||
// ProbePushBackEnabled — flag gates the [push-back] emission path.
|
||
// A6.P1 (2026-05-21).
|
||
// -----------------------------------------------------------------------
|
||
|
||
[Fact]
|
||
public void ProbePushBack_StaticApi_Roundtrip()
|
||
{
|
||
bool initial = PhysicsDiagnostics.ProbePushBackEnabled;
|
||
try
|
||
{
|
||
PhysicsDiagnostics.ProbePushBackEnabled = true;
|
||
Assert.True(PhysicsDiagnostics.ProbePushBackEnabled);
|
||
|
||
PhysicsDiagnostics.ProbePushBackEnabled = false;
|
||
Assert.False(PhysicsDiagnostics.ProbePushBackEnabled);
|
||
}
|
||
finally
|
||
{
|
||
PhysicsDiagnostics.ProbePushBackEnabled = initial;
|
||
}
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 2: Run test to verify it fails**
|
||
|
||
Run: `dotnet test tests/AcDream.Core.Tests/AcDream.Core.Tests.csproj --filter FullyQualifiedName~ProbePushBack_StaticApi_Roundtrip --no-build`
|
||
|
||
Expected: COMPILE ERROR — `'PhysicsDiagnostics' does not contain a definition for 'ProbePushBackEnabled'`.
|
||
|
||
- [ ] **Step 3: Implement the toggle**
|
||
|
||
Append to `src/AcDream.Core/Physics/PhysicsDiagnostics.cs` immediately before the `LogCpBoolWrite` method (around line 279):
|
||
|
||
```csharp
|
||
/// <summary>
|
||
/// Phase A6.P1 cdb probe spike (2026-05-21). When true, every BSP
|
||
/// collision response site emits a structured <c>[push-back]</c> line:
|
||
/// input/output sphere center, plane geometry, push-back delta, walk
|
||
/// interp, and the dispatcher's selected path. Direct comparison to
|
||
/// retail's cdb breakpoint set documented at
|
||
/// <c>tools/cdb/a6-probe.cdb</c>.
|
||
///
|
||
/// <para>
|
||
/// Three emission sites: <see cref="BSPQuery.AdjustSphereToPlane"/>
|
||
/// (the suspected over-correction site), <see cref="BSPQuery.FindCollisions"/>
|
||
/// (the 6-path dispatcher), and <see cref="Transition.CheckOtherCells"/>
|
||
/// (multi-cell BSP iteration outcomes). All three are zero-cost when
|
||
/// off — checked via early-out at each site.
|
||
/// </para>
|
||
///
|
||
/// <para>
|
||
/// Initial state from <c>ACDREAM_PROBE_PUSH_BACK=1</c>.
|
||
/// Runtime-toggleable via DebugVM mirror.
|
||
/// </para>
|
||
///
|
||
/// <para>
|
||
/// Spec: <c>docs/superpowers/specs/2026-05-21-phase-a6-indoor-physics-fidelity-design.md</c>.
|
||
/// </para>
|
||
/// </summary>
|
||
public static bool ProbePushBackEnabled { get; set; } =
|
||
Environment.GetEnvironmentVariable("ACDREAM_PROBE_PUSH_BACK") == "1";
|
||
|
||
```
|
||
|
||
- [ ] **Step 4: Run test to verify it passes**
|
||
|
||
Run: `dotnet build src/AcDream.Core/AcDream.Core.csproj -c Debug --nologo && dotnet test tests/AcDream.Core.Tests/AcDream.Core.Tests.csproj --filter FullyQualifiedName~ProbePushBack_StaticApi_Roundtrip --no-build`
|
||
|
||
Expected: PASS.
|
||
|
||
- [ ] **Step 5: Commit**
|
||
|
||
```bash
|
||
git add src/AcDream.Core/Physics/PhysicsDiagnostics.cs tests/AcDream.Core.Tests/Physics/PhysicsDiagnosticsTests.cs
|
||
git commit -m "$(cat <<'EOF'
|
||
feat(physics): A6.P1 — add ProbePushBackEnabled toggle
|
||
|
||
New PhysicsDiagnostics flag gates the [push-back] probe shipping
|
||
in subsequent tasks. Env-var ACDREAM_PROBE_PUSH_BACK=1 + DebugVM
|
||
mirror, matching the existing probe-toggle pattern.
|
||
|
||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
EOF
|
||
)"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 2: Add `LogPushBackAdjust` helper method
|
||
|
||
**Files:**
|
||
- Modify: `src/AcDream.Core/Physics/PhysicsDiagnostics.cs`
|
||
|
||
- [ ] **Step 1: Add the helper method**
|
||
|
||
Append immediately after the `ProbePushBackEnabled` property (just added) in `src/AcDream.Core/Physics/PhysicsDiagnostics.cs`:
|
||
|
||
```csharp
|
||
/// <summary>
|
||
/// A6.P1 emission helper for the <c>AdjustSphereToPlane</c> site.
|
||
/// One line per call: input sphere center, plane geometry, push-back
|
||
/// delta, walk-interp before/after, and whether the adjust applied.
|
||
/// Direct paired comparison to retail's cdb breakpoint on
|
||
/// <c>CPolygon::adjust_sphere_to_plane</c>.
|
||
///
|
||
/// <para>
|
||
/// Caller MUST guard with <c>if (!ProbePushBackEnabled) return;</c>
|
||
/// before computing the delta arguments — this method assumes the
|
||
/// caller paid that price already.
|
||
/// </para>
|
||
/// </summary>
|
||
public static void LogPushBackAdjust(
|
||
Vector3 inputCenter,
|
||
Vector3 outputCenter,
|
||
Plane plane,
|
||
float radius,
|
||
float walkInterpBefore,
|
||
float walkInterpAfter,
|
||
float dpPos,
|
||
float dpMove,
|
||
float iDist,
|
||
bool applied)
|
||
{
|
||
var delta = outputCenter - inputCenter;
|
||
float deltaMag = delta.Length();
|
||
Console.WriteLine(System.FormattableString.Invariant(
|
||
$"[push-back] site=adjust_sphere " +
|
||
$"in=({inputCenter.X:F4},{inputCenter.Y:F4},{inputCenter.Z:F4}) " +
|
||
$"out=({outputCenter.X:F4},{outputCenter.Y:F4},{outputCenter.Z:F4}) " +
|
||
$"delta=({delta.X:F4},{delta.Y:F4},{delta.Z:F4}) deltaMag={deltaMag:F4} " +
|
||
$"n=({plane.Normal.X:F4},{plane.Normal.Y:F4},{plane.Normal.Z:F4}) d={plane.D:F4} " +
|
||
$"r={radius:F4} winterp={walkInterpBefore:F4}->{walkInterpAfter:F4} " +
|
||
$"dpPos={dpPos:F4} dpMove={dpMove:F4} iDist={iDist:F4} applied={applied}"));
|
||
}
|
||
|
||
// Note: cellId/polyId omitted — ResolvedPolygon has no Id property and
|
||
// AdjustSphereToPlane is called from inside BSP traversal without cell
|
||
// attribution. Cross-reference with [push-back-cell] (same trace, time-
|
||
// adjacent) for cell attribution; polyId pursued only if A6.P2 needs it
|
||
// (would require plumbing the ushort dict-key through BSP traversal).
|
||
|
||
```
|
||
|
||
- [ ] **Step 2: Verify it compiles**
|
||
|
||
Run: `dotnet build src/AcDream.Core/AcDream.Core.csproj -c Debug --nologo`
|
||
|
||
Expected: BUILD SUCCEEDED with 0 errors.
|
||
|
||
- [ ] **Step 3: Commit**
|
||
|
||
```bash
|
||
git add src/AcDream.Core/Physics/PhysicsDiagnostics.cs
|
||
git commit -m "$(cat <<'EOF'
|
||
feat(physics): A6.P1 — add LogPushBackAdjust helper
|
||
|
||
One-line per-call emission helper for the AdjustSphereToPlane
|
||
instrumentation site. Direct field-for-field paired comparison to
|
||
retail's CPolygon::adjust_sphere_to_plane breakpoint.
|
||
|
||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
EOF
|
||
)"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 3: Wire `LogPushBackAdjust` into `BSPQuery.AdjustSphereToPlane`
|
||
|
||
**Files:**
|
||
- Modify: `src/AcDream.Core/Physics/BSPQuery.cs:332-362` (the `AdjustSphereToPlane` method body)
|
||
|
||
- [ ] **Step 1: Read the current method body**
|
||
|
||
Read `src/AcDream.Core/Physics/BSPQuery.cs` lines 320-365 to confirm the current shape — `AdjustSphereToPlane` returns bool, modifies `validPos.Center` and `path.WalkInterp` in-place.
|
||
|
||
- [ ] **Step 2: Replace the method body with the instrumented version**
|
||
|
||
Replace the body of `AdjustSphereToPlane` in `src/AcDream.Core/Physics/BSPQuery.cs` (lines 332-362) with:
|
||
|
||
```csharp
|
||
private static bool AdjustSphereToPlane(
|
||
ResolvedPolygon poly,
|
||
SpherePath path,
|
||
CollisionSphere validPos,
|
||
Vector3 movement)
|
||
{
|
||
// A6.P1: snapshot inputs for the [push-back] probe (cheap copy of
|
||
// Vector3 + 2 floats). Only the LogPushBackAdjust call below pays
|
||
// the Console.WriteLine cost, and only when the probe is on.
|
||
var inputCenter = validPos.Center;
|
||
float walkInterpBefore = path.WalkInterp;
|
||
|
||
float dpPos = Vector3.Dot(validPos.Center, poly.Plane.Normal) + poly.Plane.D;
|
||
float dpMove = Vector3.Dot(movement, poly.Plane.Normal);
|
||
float dist;
|
||
|
||
if (dpMove <= PhysicsGlobals.EPSILON)
|
||
{
|
||
if (dpMove >= -PhysicsGlobals.EPSILON)
|
||
{
|
||
if (PhysicsDiagnostics.ProbePushBackEnabled)
|
||
{
|
||
PhysicsDiagnostics.LogPushBackAdjust(
|
||
inputCenter, validPos.Center, poly.Plane, validPos.Radius,
|
||
walkInterpBefore, path.WalkInterp,
|
||
dpPos, dpMove, 0f,
|
||
applied: false);
|
||
}
|
||
return false;
|
||
}
|
||
dist = dpPos - validPos.Radius;
|
||
}
|
||
else
|
||
{
|
||
dist = -validPos.Radius - dpPos;
|
||
}
|
||
|
||
float iDist = dist / dpMove;
|
||
float interp = (1f - iDist) * path.WalkInterp;
|
||
|
||
if (interp >= path.WalkInterp || interp < -0.5f)
|
||
{
|
||
if (PhysicsDiagnostics.ProbePushBackEnabled)
|
||
{
|
||
PhysicsDiagnostics.LogPushBackAdjust(
|
||
inputCenter, validPos.Center, poly.Plane, validPos.Radius,
|
||
walkInterpBefore, path.WalkInterp,
|
||
dpPos, dpMove, iDist,
|
||
applied: false);
|
||
}
|
||
return false;
|
||
}
|
||
|
||
validPos.Center -= movement * iDist;
|
||
path.WalkInterp = interp;
|
||
|
||
if (PhysicsDiagnostics.ProbePushBackEnabled)
|
||
{
|
||
PhysicsDiagnostics.LogPushBackAdjust(
|
||
inputCenter, validPos.Center, poly.Plane, validPos.Radius,
|
||
walkInterpBefore, path.WalkInterp,
|
||
dpPos, dpMove, iDist,
|
||
applied: true);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
```
|
||
|
||
**Note:** Cell + poly attribution intentionally omitted — `ResolvedPolygon`
|
||
has no `Id` property and `AdjustSphereToPlane` is private inside BSP
|
||
traversal without cell scope. A6.P2 cross-references with the time-adjacent
|
||
`[push-back-cell]` line for cell attribution. If `polyId` becomes
|
||
necessary, A6.P3 plumbs the dictionary key through (mechanical change).
|
||
|
||
- [ ] **Step 3: Verify build is green**
|
||
|
||
Run: `dotnet build src/AcDream.Core/AcDream.Core.csproj -c Debug --nologo`
|
||
|
||
Expected: BUILD SUCCEEDED with 0 errors. The probe is now wired but inactive unless `ACDREAM_PROBE_PUSH_BACK=1`.
|
||
|
||
- [ ] **Step 4: Quick smoke run to confirm the probe emits**
|
||
|
||
Set the env var and run any test that calls AdjustSphereToPlane (existing BSPQuery tests touch this path):
|
||
|
||
```powershell
|
||
$env:ACDREAM_PROBE_PUSH_BACK = "1"
|
||
dotnet test tests/AcDream.Core.Tests/AcDream.Core.Tests.csproj --filter "FullyQualifiedName~BSPQuery" --no-build --logger "console;verbosity=detailed" 2>&1 | Select-String "\[push-back\]" | Select-Object -First 3
|
||
Remove-Item Env:\ACDREAM_PROBE_PUSH_BACK
|
||
```
|
||
|
||
Expected: at least 1 `[push-back] site=adjust_sphere ...` line in the output. If zero lines, the probe didn't fire — investigate.
|
||
|
||
- [ ] **Step 5: Commit**
|
||
|
||
```bash
|
||
git add src/AcDream.Core/Physics/BSPQuery.cs
|
||
git commit -m "$(cat <<'EOF'
|
||
feat(physics): A6.P1 — instrument AdjustSphereToPlane with [push-back]
|
||
|
||
Wires the LogPushBackAdjust helper into all three return paths
|
||
of AdjustSphereToPlane (early-return on no-movement, early-return
|
||
on interp out-of-window, and the applied path). Probe is gated by
|
||
ProbePushBackEnabled so it's zero-cost when off.
|
||
|
||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
EOF
|
||
)"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 4: Add `LogPushBackDispatch` helper method
|
||
|
||
**Files:**
|
||
- Modify: `src/AcDream.Core/Physics/PhysicsDiagnostics.cs`
|
||
|
||
- [ ] **Step 1: Add the helper method**
|
||
|
||
Append immediately after the `LogPushBackAdjust` method (just added) in `src/AcDream.Core/Physics/PhysicsDiagnostics.cs`:
|
||
|
||
```csharp
|
||
/// <summary>
|
||
/// A6.P1 emission helper for the <c>FindCollisions</c> dispatcher
|
||
/// site. One line per call: input sphere center, movement vector,
|
||
/// path-selection state flags (collide / insertType / objState),
|
||
/// walk-interp at entry, and the return state. Direct paired
|
||
/// comparison to retail's cdb breakpoint on
|
||
/// <c>BSPTREE::find_collisions</c>.
|
||
///
|
||
/// <para>
|
||
/// Caller MUST guard with <c>if (!ProbePushBackEnabled) return;</c>
|
||
/// before calling.
|
||
/// </para>
|
||
/// </summary>
|
||
public static void LogPushBackDispatch(
|
||
Vector3 sphereCenter,
|
||
Vector3 movement,
|
||
bool collide,
|
||
int insertType,
|
||
int objState,
|
||
float walkInterpEntry,
|
||
int returnState)
|
||
{
|
||
Console.WriteLine(System.FormattableString.Invariant(
|
||
$"[push-back-disp] site=dispatch " +
|
||
$"center=({sphereCenter.X:F4},{sphereCenter.Y:F4},{sphereCenter.Z:F4}) " +
|
||
$"mvmt=({movement.X:F4},{movement.Y:F4},{movement.Z:F4}) " +
|
||
$"collide={collide} insertType={insertType} objState=0x{objState:X} " +
|
||
$"winterp={walkInterpEntry:F4} return={returnState}"));
|
||
}
|
||
|
||
```
|
||
|
||
- [ ] **Step 2: Verify it compiles**
|
||
|
||
Run: `dotnet build src/AcDream.Core/AcDream.Core.csproj -c Debug --nologo`
|
||
|
||
Expected: BUILD SUCCEEDED.
|
||
|
||
- [ ] **Step 3: Commit**
|
||
|
||
```bash
|
||
git add src/AcDream.Core/Physics/PhysicsDiagnostics.cs
|
||
git commit -m "$(cat <<'EOF'
|
||
feat(physics): A6.P1 — add LogPushBackDispatch helper
|
||
|
||
One-line per-call emission helper for the FindCollisions dispatcher
|
||
instrumentation site. Captures path-selection state (collide flag,
|
||
insertType, objState) + walk-interp + return state for direct
|
||
comparison to retail's BSPTREE::find_collisions breakpoint.
|
||
|
||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
EOF
|
||
)"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 5: Wire `LogPushBackDispatch` into `BSPQuery.FindCollisions` (modern overload only)
|
||
|
||
**Files:**
|
||
- Modify: `src/AcDream.Core/Physics/BSPQuery.cs:1550-1576` (the modern `FindCollisions` overload entry)
|
||
|
||
**Note:** The legacy overload at `:1895` delegates to the modern overload — instrumenting only the modern one covers all dispatches.
|
||
|
||
- [ ] **Step 1: Locate the dispatcher's entry block**
|
||
|
||
Read `src/AcDream.Core/Physics/BSPQuery.cs` lines 1550-1580 to confirm the entry shape (we want to emit AFTER the path/collisions/obj locals are computed but BEFORE the actual dispatch begins).
|
||
|
||
- [ ] **Step 2: Insert the probe emission after `movement` is computed**
|
||
|
||
After line 1576 (`var movement = sphere0.Center - localCurrCenter;`) and BEFORE the next significant statement in `BSPQuery.cs`'s `FindCollisions`, add:
|
||
|
||
```csharp
|
||
// A6.P1: snapshot dispatcher entry for the [push-back-disp] probe.
|
||
// Emitted before path selection so the captured state reflects
|
||
// the inputs the dispatcher routes on. Return value is captured
|
||
// by wrapping the actual dispatch — see end of method.
|
||
if (PhysicsDiagnostics.ProbePushBackEnabled)
|
||
{
|
||
PhysicsDiagnostics.LogPushBackDispatch(
|
||
sphereCenter: sphere0.Center,
|
||
movement: movement,
|
||
collide: path.Collide,
|
||
insertType: (int)path.InsertType,
|
||
objState: (int)obj.State,
|
||
walkInterpEntry: path.WalkInterp,
|
||
returnState: -1); // Sentinel; full return state captured separately.
|
||
}
|
||
|
||
```
|
||
|
||
**Note:** The `returnState=-1` sentinel marks "entry log" — A6.P2 analysis can pair entry/exit by call order in the trace. If a per-call exit log is needed, A6.P1.5 adds a wrapping try/finally; for now entry-only suffices since the dispatcher's path can be inferred from subsequent `[push-back]` adjust-sphere lines and the eventual return.
|
||
|
||
- [ ] **Step 3: Verify build is green**
|
||
|
||
Run: `dotnet build src/AcDream.Core/AcDream.Core.csproj -c Debug --nologo`
|
||
|
||
Expected: BUILD SUCCEEDED.
|
||
|
||
- [ ] **Step 4: Quick smoke run**
|
||
|
||
```powershell
|
||
$env:ACDREAM_PROBE_PUSH_BACK = "1"
|
||
dotnet test tests/AcDream.Core.Tests/AcDream.Core.Tests.csproj --filter "FullyQualifiedName~FindCollisions" --no-build --logger "console;verbosity=detailed" 2>&1 | Select-String "\[push-back-disp\]" | Select-Object -First 3
|
||
Remove-Item Env:\ACDREAM_PROBE_PUSH_BACK
|
||
```
|
||
|
||
Expected: at least 1 `[push-back-disp] site=dispatch ...` line.
|
||
|
||
- [ ] **Step 5: Commit**
|
||
|
||
```bash
|
||
git add src/AcDream.Core/Physics/BSPQuery.cs
|
||
git commit -m "$(cat <<'EOF'
|
||
feat(physics): A6.P1 — instrument FindCollisions with [push-back-disp]
|
||
|
||
Wires LogPushBackDispatch into the modern FindCollisions overload
|
||
at the entry block (after path/collisions/obj locals + movement
|
||
computed). Legacy overload delegates to modern, so single
|
||
instrumentation site covers all dispatches.
|
||
|
||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
EOF
|
||
)"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 6: Add `LogPushBackCellTransit` helper method
|
||
|
||
**Files:**
|
||
- Modify: `src/AcDream.Core/Physics/PhysicsDiagnostics.cs`
|
||
|
||
- [ ] **Step 1: Add the helper method**
|
||
|
||
Append immediately after `LogPushBackDispatch` in `src/AcDream.Core/Physics/PhysicsDiagnostics.cs`:
|
||
|
||
```csharp
|
||
/// <summary>
|
||
/// A6.P1 emission helper for the <c>CheckOtherCells</c> multi-cell
|
||
/// BSP iteration site. One line per off-cell hit: from-cell, to-cell,
|
||
/// BSP result (Ok / Adjusted / Slid / Collided), and the iteration
|
||
/// outcome. Direct paired comparison to retail's
|
||
/// <c>CTransition::check_other_cells</c> loop at decomp line
|
||
/// 272717. Augments the existing A4 multi-cell BSP instrumentation
|
||
/// with explicit per-iteration outcome telemetry.
|
||
///
|
||
/// <para>
|
||
/// Caller MUST guard with <c>if (!ProbePushBackEnabled) return;</c>
|
||
/// before calling.
|
||
/// </para>
|
||
/// </summary>
|
||
public static void LogPushBackCellTransit(
|
||
uint primaryCellId,
|
||
uint otherCellId,
|
||
int bspResult,
|
||
bool halted)
|
||
{
|
||
Console.WriteLine(System.FormattableString.Invariant(
|
||
$"[push-back-cell] site=other_cell " +
|
||
$"primary=0x{primaryCellId:X8} other=0x{otherCellId:X8} " +
|
||
$"bspResult={bspResult} halted={halted}"));
|
||
}
|
||
|
||
```
|
||
|
||
- [ ] **Step 2: Verify it compiles**
|
||
|
||
Run: `dotnet build src/AcDream.Core/AcDream.Core.csproj -c Debug --nologo`
|
||
|
||
Expected: BUILD SUCCEEDED.
|
||
|
||
- [ ] **Step 3: Commit**
|
||
|
||
```bash
|
||
git add src/AcDream.Core/Physics/PhysicsDiagnostics.cs
|
||
git commit -m "$(cat <<'EOF'
|
||
feat(physics): A6.P1 — add LogPushBackCellTransit helper
|
||
|
||
One-line per-iteration emission helper for the CheckOtherCells
|
||
multi-cell BSP loop. Captures primary/other cell ids, BSP result,
|
||
and halted flag for direct comparison to retail's
|
||
CTransition::check_other_cells loop.
|
||
|
||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
EOF
|
||
)"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 7: Wire `LogPushBackCellTransit` into `Transition.CheckOtherCells`
|
||
|
||
**Files:**
|
||
- Modify: `src/AcDream.Core/Physics/TransitionTypes.cs:1404-1480` (the `CheckOtherCells` loop)
|
||
|
||
- [ ] **Step 1: Locate the insertion point**
|
||
|
||
In `src/AcDream.Core/Physics/TransitionTypes.cs`, find the line block at
|
||
~1466-1473:
|
||
|
||
```csharp
|
||
if (PhysicsDiagnostics.ProbeIndoorBspEnabled)
|
||
{
|
||
Console.WriteLine(System.FormattableString.Invariant(
|
||
$"[other-cells] primary=0x{sp.CheckCellId:X8} iter=0x{cellId:X8} result={result}"));
|
||
}
|
||
|
||
if (ApplyOtherCellResult(result, out var halted))
|
||
return halted;
|
||
```
|
||
|
||
We add the new `[push-back-cell]` emission AFTER the existing `[other-cells]`
|
||
block and BEFORE the `ApplyOtherCellResult` call.
|
||
|
||
- [ ] **Step 2: Insert the new emission**
|
||
|
||
In `src/AcDream.Core/Physics/TransitionTypes.cs`, modify the line block at
|
||
~1466-1473 by inserting a new `if` block between the existing
|
||
`[other-cells]` probe and the `ApplyOtherCellResult` call. The locals in
|
||
scope at this point are `sp.CheckCellId` (the primary), `cellId` (the
|
||
loop variable for the candidate cell), and `result` (the BSP query
|
||
return state). After the edit it reads:
|
||
|
||
```csharp
|
||
if (PhysicsDiagnostics.ProbeIndoorBspEnabled)
|
||
{
|
||
Console.WriteLine(System.FormattableString.Invariant(
|
||
$"[other-cells] primary=0x{sp.CheckCellId:X8} iter=0x{cellId:X8} result={result}"));
|
||
}
|
||
|
||
if (PhysicsDiagnostics.ProbePushBackEnabled)
|
||
{
|
||
PhysicsDiagnostics.LogPushBackCellTransit(
|
||
primaryCellId: sp.CheckCellId,
|
||
otherCellId: cellId,
|
||
bspResult: (int)result,
|
||
halted: false); // Sentinel; A6.P2 infers halt by checking if the iter ended here.
|
||
}
|
||
|
||
if (ApplyOtherCellResult(result, out var halted))
|
||
return halted;
|
||
```
|
||
|
||
- [ ] **Step 3: Verify build is green**
|
||
|
||
Run: `dotnet build src/AcDream.Core/AcDream.Core.csproj -c Debug --nologo`
|
||
|
||
Expected: BUILD SUCCEEDED with 0 errors.
|
||
|
||
- [ ] **Step 4: Quick smoke run**
|
||
|
||
```powershell
|
||
$env:ACDREAM_PROBE_PUSH_BACK = "1"
|
||
dotnet test tests/AcDream.Core.Tests/AcDream.Core.Tests.csproj --filter "FullyQualifiedName~CheckOtherCells" --no-build --logger "console;verbosity=detailed" 2>&1 | Select-String "\[push-back-cell\]" | Select-Object -First 3
|
||
Remove-Item Env:\ACDREAM_PROBE_PUSH_BACK
|
||
```
|
||
|
||
Expected: at least 1 `[push-back-cell] site=other_cell ...` line.
|
||
|
||
- [ ] **Step 5: Commit**
|
||
|
||
```bash
|
||
git add src/AcDream.Core/Physics/TransitionTypes.cs
|
||
git commit -m "$(cat <<'EOF'
|
||
feat(physics): A6.P1 — instrument CheckOtherCells with [push-back-cell]
|
||
|
||
Wires LogPushBackCellTransit into the multi-cell BSP iteration loop
|
||
just before ApplyOtherCellResult halts. Captures primary/other
|
||
cell ids + BSP result for direct comparison to retail's
|
||
CTransition::check_other_cells loop (already ported as A4).
|
||
|
||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
EOF
|
||
)"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 8: Add `ProbePushBack` mirror to `DebugVM`
|
||
|
||
**Files:**
|
||
- Modify: `src/AcDream.UI.Abstractions/Panels/Debug/DebugVM.cs` (append after `ProbeIndoorBsp` at line ~360)
|
||
|
||
- [ ] **Step 1: Add the mirror property**
|
||
|
||
After the `ProbeIndoorBsp` property in `src/AcDream.UI.Abstractions/Panels/Debug/DebugVM.cs` (closing `}` around line 360), insert:
|
||
|
||
```csharp
|
||
|
||
/// <summary>
|
||
/// Phase A6.P1 cdb probe spike (2026-05-21). Runtime mirror of
|
||
/// <c>PhysicsDiagnostics.ProbePushBackEnabled</c> (env var
|
||
/// <c>ACDREAM_PROBE_PUSH_BACK</c>). Toggling here flips the three
|
||
/// <c>[push-back]</c> emission sites live — no relaunch required.
|
||
/// </summary>
|
||
public bool ProbePushBack
|
||
{
|
||
get => PhysicsDiagnostics.ProbePushBackEnabled;
|
||
set => PhysicsDiagnostics.ProbePushBackEnabled = value;
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 2: Verify build is green**
|
||
|
||
Run: `dotnet build src/AcDream.UI.Abstractions/AcDream.UI.Abstractions.csproj -c Debug --nologo`
|
||
|
||
Expected: BUILD SUCCEEDED.
|
||
|
||
- [ ] **Step 3: Commit**
|
||
|
||
```bash
|
||
git add src/AcDream.UI.Abstractions/Panels/Debug/DebugVM.cs
|
||
git commit -m "$(cat <<'EOF'
|
||
feat(ui): A6.P1 — add ProbePushBack mirror to DebugVM
|
||
|
||
Runtime checkbox mirror for ProbePushBackEnabled. Toggling in
|
||
DebugPanel (under ACDREAM_DEVTOOLS=1) flips all three [push-back]
|
||
emission sites live without relaunch.
|
||
|
||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
EOF
|
||
)"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 9: Update CLAUDE.md "Diagnostic env vars" section
|
||
|
||
**Files:**
|
||
- Modify: `CLAUDE.md` ("Diagnostic env vars" subsection)
|
||
|
||
- [ ] **Step 1: Locate the section**
|
||
|
||
Run: `grep -n "ACDREAM_PROBE_RESOLVE\|Diagnostic env vars" CLAUDE.md | head -5`
|
||
|
||
Identify the line where the existing `ACDREAM_PROBE_*` env var documentation ends.
|
||
|
||
- [ ] **Step 2: Add the new env var entry**
|
||
|
||
Append the new entry immediately after the existing `ACDREAM_PROBE_WALK_MISS` entry (look for the `[walk-miss]` documentation line). Add:
|
||
|
||
```markdown
|
||
- `ACDREAM_PROBE_PUSH_BACK=1` — A6.P1 cdb probe spike (2026-05-21).
|
||
Emits three line types per physics tick: `[push-back]` (per
|
||
`BSPQuery.AdjustSphereToPlane` call), `[push-back-disp]` (per
|
||
`BSPQuery.FindCollisions` dispatch), `[push-back-cell]` (per
|
||
`Transition.CheckOtherCells` off-cell hit). Heavy under motion
|
||
(~100–500 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.
|
||
```
|
||
|
||
- [ ] **Step 3: Commit**
|
||
|
||
```bash
|
||
git add CLAUDE.md
|
||
git commit -m "$(cat <<'EOF'
|
||
docs(CLAUDE): A6.P1 — document ACDREAM_PROBE_PUSH_BACK env var
|
||
|
||
Adds the new probe to the Diagnostic env vars list with hit-rate
|
||
estimate and cross-reference to the cdb probe script.
|
||
|
||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
EOF
|
||
)"
|
||
```
|
||
|
||
---
|
||
|
||
## Phase B — Build the cdb script + infrastructure
|
||
|
||
### Task 10: Write `tools/cdb/a6-probe.cdb`
|
||
|
||
**Files:**
|
||
- Create: `tools/cdb/a6-probe.cdb`
|
||
|
||
- [ ] **Step 1: Verify the parent directory exists**
|
||
|
||
```bash
|
||
ls tools/ 2>&1; mkdir -p tools/cdb
|
||
```
|
||
|
||
- [ ] **Step 2: Write the cdb script**
|
||
|
||
Create `tools/cdb/a6-probe.cdb`:
|
||
|
||
```text
|
||
$$
|
||
$$ Phase A6.P1 cdb probe spike — 2026-05-21
|
||
$$
|
||
$$ 7 breakpoints on retail's BSP collision response sites. Each BP fires
|
||
$$ a non-blocking action (printf + gc), incrementing a per-BP counter.
|
||
$$ Auto-detaches via 'qd' after 50,000 total hits to avoid retail lag.
|
||
$$
|
||
$$ Symbol lookup convention per CLAUDE.md "Retail debugger toolchain":
|
||
$$ - snake_case for BSPTREE, CTransition, OBJECTINFO, COLLISIONINFO, SPHEREPATH
|
||
$$ - PascalCase for CPhysicsObj
|
||
$$ - Always 'x' first to confirm the actual name
|
||
$$
|
||
|
||
.logopen /t a6-probe-${ARG_LOG_TAG}.log
|
||
.sympath C:\Users\erikn\source\repos\acdream\refs
|
||
.symopt+ 0x40
|
||
.reload /f acclient.exe
|
||
|
||
$$ Counters: $t0 = total hits, $t1..$t7 = per-BP hits
|
||
r $t0 = 0
|
||
r $t1 = 0
|
||
r $t2 = 0
|
||
r $t3 = 0
|
||
r $t4 = 0
|
||
r $t5 = 0
|
||
r $t6 = 0
|
||
r $t7 = 0
|
||
|
||
$$ BP1: CTransition::transitional_insert
|
||
bp acclient!CTransition::transitional_insert "r $t1 = @$t1 + 1; r $t0 = @$t0 + 1; .printf /D \"[BP1] transitional_insert hit#%d eax_2=%d insertType=%d\\n\", @$t1, poi(@ecx+0x18), poi(@ecx+0x1c); .if (@$t0 >= 50000) { qd } .else { gc }"
|
||
|
||
$$ BP2: CTransition::step_up
|
||
bp acclient!CTransition::step_up "r $t2 = @$t2 + 1; r $t0 = @$t0 + 1; .printf /D \"[BP2] step_up hit#%d walkable_allowance=%f\\n\", @$t2, poi(@ecx+0x60); .if (@$t0 >= 50000) { qd } .else { gc }"
|
||
|
||
$$ BP3: SPHEREPATH::set_collide
|
||
bp acclient!SPHEREPATH::set_collide "r $t3 = @$t3 + 1; r $t0 = @$t0 + 1; .printf /D \"[BP3] set_collide hit#%d normalZ=%f\\n\", @$t3, dwo(@edx+0x8); .if (@$t0 >= 50000) { qd } .else { gc }"
|
||
|
||
$$ BP4: BSPTREE::find_collisions
|
||
bp acclient!BSPTREE::find_collisions "r $t4 = @$t4 + 1; r $t0 = @$t0 + 1; .printf /D \"[BP4] find_collisions hit#%d collide=%d insertType=%d\\n\", @$t4, poi(@ecx+0x40), poi(@ecx+0x1c); .if (@$t0 >= 50000) { qd } .else { gc }"
|
||
|
||
$$ BP5: CPolygon::adjust_sphere_to_plane (the over-correction suspect)
|
||
bp acclient!CPolygon::adjust_sphere_to_plane "r $t5 = @$t5 + 1; r $t0 = @$t0 + 1; .printf /D \"[BP5] adjust_sphere hit#%d Nx=%f Ny=%f Nz=%f d=%f cx=%f cy=%f cz=%f r=%f winterp=%f\\n\", @$t5, dwo(@ecx+0x0), dwo(@ecx+0x4), dwo(@ecx+0x8), dwo(@ecx+0xc), dwo(@edx+0x0), dwo(@edx+0x4), dwo(@edx+0x8), dwo(@edx+0xc), dwo(@edi+0x40); .if (@$t0 >= 50000) { qd } .else { gc }"
|
||
|
||
$$ BP6: CTransition::validate_walkable
|
||
bp acclient!CTransition::validate_walkable "r $t6 = @$t6 + 1; r $t0 = @$t0 + 1; .printf /D \"[BP6] validate_walkable hit#%d arg2=%f\\n\", @$t6, dwo(@esp+0x4); .if (@$t0 >= 50000) { qd } .else { gc }"
|
||
|
||
$$ BP7: COLLISIONINFO::set_contact_plane
|
||
bp acclient!COLLISIONINFO::set_contact_plane "r $t7 = @$t7 + 1; r $t0 = @$t0 + 1; .printf /D \"[BP7] set_contact_plane hit#%d isWater=%d\\n\", @$t7, dwo(@esp+0x8); .if (@$t0 >= 50000) { qd } .else { gc }"
|
||
|
||
.printf \"a6-probe armed: BPs 1-7 set, threshold=50000 total hits, qd on threshold\\n\"
|
||
|
||
$$ Continue execution
|
||
g
|
||
```
|
||
|
||
**Note:** The `${ARG_LOG_TAG}` placeholder is substituted by the PowerShell runner (Task 11) per scenario. The field offsets (`+0x18`, `+0x1c`, etc.) are based on the named-retail struct layouts in `docs/research/named-retail/acclient.h`; if a BP fires with garbage values, verify the offset against the struct definition.
|
||
|
||
- [ ] **Step 3: Commit**
|
||
|
||
```bash
|
||
git add tools/cdb/a6-probe.cdb
|
||
git commit -m "$(cat <<'EOF'
|
||
feat(cdb): A6.P1 — 7-BP probe script for retail BSP collision response
|
||
|
||
Sets non-blocking breakpoints on transitional_insert, step_up,
|
||
set_collide, find_collisions, adjust_sphere_to_plane,
|
||
validate_walkable, set_contact_plane. Each BP increments a counter
|
||
and emits a single printf line. Auto-detach via qd at 50K total
|
||
hits to avoid retail lag (CLAUDE.md gotcha — high BP rates trigger
|
||
ACE timeout).
|
||
|
||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
EOF
|
||
)"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 11: Write `tools/cdb/a6-probe-runner.ps1`
|
||
|
||
**Files:**
|
||
- Create: `tools/cdb/a6-probe-runner.ps1`
|
||
|
||
- [ ] **Step 1: Write the wrapper**
|
||
|
||
Create `tools/cdb/a6-probe-runner.ps1`:
|
||
|
||
```powershell
|
||
# Phase A6.P1 cdb probe runner — 2026-05-21
|
||
#
|
||
# Attaches cdb to a live retail acclient.exe with the a6-probe.cdb script.
|
||
# Per-scenario usage:
|
||
# .\tools\cdb\a6-probe-runner.ps1 -ScenarioTag "scen1_inn_doorway"
|
||
#
|
||
# Prerequisites (verify before invoking):
|
||
# 1. Retail acclient.exe v11.4186 running and in-world (matches refs/acclient.pdb).
|
||
# Verify with: py tools\pdb-extract\check_exe_pdb.py "C:\Turbine\Asheron's Call\acclient.exe"
|
||
# 2. ACE running locally on 127.0.0.1:9000.
|
||
# 3. Retail character at the scenario start position.
|
||
#
|
||
# Output:
|
||
# docs\research\2026-05-21-a6-captures\<ScenarioTag>\retail.log
|
||
|
||
param(
|
||
[Parameter(Mandatory=$true)]
|
||
[string]$ScenarioTag
|
||
)
|
||
|
||
$cdbExe = "C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\cdb.exe"
|
||
if (-not (Test-Path $cdbExe)) {
|
||
Write-Error "cdb.exe not found at $cdbExe. Install Microsoft Store WinDbg (~50 MB)."
|
||
exit 1
|
||
}
|
||
|
||
$scriptPath = Join-Path $PSScriptRoot "a6-probe.cdb"
|
||
if (-not (Test-Path $scriptPath)) {
|
||
Write-Error "a6-probe.cdb not found at $scriptPath."
|
||
exit 1
|
||
}
|
||
|
||
$captureDir = Join-Path $PSScriptRoot "..\..\docs\research\2026-05-21-a6-captures\$ScenarioTag"
|
||
if (-not (Test-Path $captureDir)) {
|
||
New-Item -ItemType Directory -Path $captureDir | Out-Null
|
||
}
|
||
|
||
$logPath = Join-Path $captureDir "retail.log"
|
||
|
||
# Patch the .cdb script with the scenario-tagged log path (in-place substitution).
|
||
$scriptContent = Get-Content $scriptPath -Raw
|
||
$patchedScript = $scriptContent -replace '\$\{ARG_LOG_TAG\}', $ScenarioTag
|
||
|
||
$tempScript = Join-Path $env:TEMP "a6-probe-$ScenarioTag.cdb"
|
||
Set-Content -Path $tempScript -Value $patchedScript -Encoding ASCII
|
||
|
||
Write-Host "Attaching cdb to acclient.exe with scenario tag '$ScenarioTag'..."
|
||
Write-Host "Log: $logPath"
|
||
Write-Host "(cdb auto-detaches at 50K total hits; or press Ctrl-Break to interrupt.)"
|
||
|
||
& $cdbExe -pn acclient.exe -cf $tempScript 2>&1 | Tee-Object -FilePath $logPath
|
||
|
||
Remove-Item $tempScript -ErrorAction SilentlyContinue
|
||
|
||
Write-Host ""
|
||
Write-Host "Capture complete. Log saved to $logPath"
|
||
```
|
||
|
||
- [ ] **Step 2: Commit**
|
||
|
||
```bash
|
||
git add tools/cdb/a6-probe-runner.ps1
|
||
git commit -m "$(cat <<'EOF'
|
||
feat(cdb): A6.P1 — PowerShell runner for a6-probe.cdb
|
||
|
||
Wrapper that attaches cdb to a live retail acclient.exe with a
|
||
scenario-tagged log path. Per-scenario invocation:
|
||
.\tools\cdb\a6-probe-runner.ps1 -ScenarioTag "scen1_inn_doorway"
|
||
Output: docs\research\2026-05-21-a6-captures\<ScenarioTag>\retail.log
|
||
|
||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
EOF
|
||
)"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 12: Write `tools/cdb/README-a6-probe.md`
|
||
|
||
**Files:**
|
||
- Create: `tools/cdb/README-a6-probe.md`
|
||
|
||
- [ ] **Step 1: Write the README**
|
||
|
||
Create `tools/cdb/README-a6-probe.md`:
|
||
|
||
```markdown
|
||
# A6.P1 cdb probe — usage
|
||
|
||
Phase A6.P1 cdb probe spike (2026-05-21). Captures retail's per-tick BSP
|
||
collision response state for paired comparison against acdream.
|
||
|
||
## Files
|
||
|
||
- `a6-probe.cdb` — the 7-breakpoint cdb script (see comments inside).
|
||
- `a6-probe-runner.ps1` — PowerShell wrapper that attaches cdb to live retail.
|
||
|
||
## Prerequisites
|
||
|
||
1. **Retail binary matches our PDB.** Verify with:
|
||
```powershell
|
||
py tools\pdb-extract\check_exe_pdb.py "C:\Turbine\Asheron's Call\acclient.exe"
|
||
```
|
||
Expected: `=== MATCH: this exe pairs with our acclient.pdb ===`
|
||
|
||
2. **cdb installed.** Microsoft Store WinDbg installs cdb at
|
||
`C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\cdb.exe`. 32-bit
|
||
required for acclient.exe.
|
||
|
||
3. **Retail running and in-world.** Launch retail, log into your character,
|
||
navigate to the scenario start point.
|
||
|
||
4. **ACE running locally on 127.0.0.1:9000.**
|
||
|
||
## Usage
|
||
|
||
For each of the 9 A6.P1 scenarios:
|
||
|
||
```powershell
|
||
# 1. Position retail character at scenario start (e.g., outside Holtburg inn).
|
||
# 2. Launch the probe with a scenario tag:
|
||
.\tools\cdb\a6-probe-runner.ps1 -ScenarioTag "scen1_inn_doorway"
|
||
|
||
# 3. cdb attaches; "a6-probe armed: BPs 1-7 set" appears in console.
|
||
# 4. In the retail client window, perform the scripted walk for this scenario.
|
||
# 5. cdb auto-detaches at 50K total hits (cleanly via qd; retail keeps running).
|
||
# 6. Log filed at docs\research\2026-05-21-a6-captures\scen1_inn_doorway\retail.log
|
||
```
|
||
|
||
## The 9 scenarios
|
||
|
||
Per the A6 design spec, in capture order:
|
||
|
||
| # | Tag | Walk script |
|
||
|---|---|---|
|
||
| 1 | `scen1_inn_doorway` | Walk forward through door, stop just inside |
|
||
| 2 | `scen2_inn_stairs` | Walk up 4 steps, stop on landing |
|
||
| 3 | `scen3_inn_2nd_floor` | Walk forward 3 m, sidestep 1 m, walk back |
|
||
| 4 | `scen4_cottage_cellar` | Walk to cellar opening, descend 2 steps |
|
||
| 5 | `scen5_sewer_entry` | Walk into portal, then walk 2 m forward inside |
|
||
| 6 | `scen6_sewer_first_stair` | Walk down full stair flight |
|
||
| 7 | `scen7_sewer_inter_room` | Walk through portal, stop 1 m past |
|
||
| 8 | `scen8_sewer_chamber` | Walk in, traverse center, walk out other side |
|
||
| 9 | `scen9_sewer_corridor` | Walk full length end-to-end |
|
||
|
||
## Pairing with acdream
|
||
|
||
For each scenario, run acdream IN PARALLEL with the same scripted walk:
|
||
|
||
```powershell
|
||
$env:ACDREAM_DAT_DIR = "$env:USERPROFILE\Documents\Asheron's Call"
|
||
$env:ACDREAM_LIVE = "1"
|
||
$env:ACDREAM_TEST_HOST = "127.0.0.1"
|
||
$env:ACDREAM_TEST_PORT = "9000"
|
||
$env:ACDREAM_TEST_USER = "testaccount"
|
||
$env:ACDREAM_TEST_PASS = "testpassword"
|
||
$env:ACDREAM_DEVTOOLS = "1"
|
||
$env:ACDREAM_PROBE_PUSH_BACK = "1"
|
||
$env:ACDREAM_PROBE_INDOOR_BSP = "1"
|
||
$env:ACDREAM_PROBE_CELL = "1"
|
||
$env:ACDREAM_PROBE_CELL_CACHE = "1"
|
||
$env:ACDREAM_PROBE_CONTACT_PLANE = "1"
|
||
|
||
dotnet run --project src\AcDream.App\AcDream.App.csproj --no-build -c Debug 2>&1 |
|
||
Tee-Object -FilePath "docs\research\2026-05-21-a6-captures\scen1_inn_doorway\acdream.log"
|
||
```
|
||
|
||
The `[push-back]`, `[push-back-disp]`, `[push-back-cell]` lines in
|
||
`acdream.log` pair with the `[BP1]..[BP7]` lines in `retail.log` via
|
||
scenario tag.
|
||
|
||
## Known watchouts
|
||
|
||
Per [`CLAUDE.md`](../../CLAUDE.md#known-watchouts):
|
||
|
||
- **High BP hit rates cause retail lag.** 50K threshold is the calibrated
|
||
ceiling that keeps retail responsive without triggering ACE timeout.
|
||
- **`qd` (quit detached) is forbidden in BP actions** per CLAUDE.md cdb
|
||
watchout. The a6-probe.cdb script puts `qd` ONLY in the threshold
|
||
branch, NOT directly in a BP action's primary command list. Verify this
|
||
is preserved if editing.
|
||
- **Killing cdb kills retail.** `qd` detaches cleanly; `Stop-Process` does
|
||
not. If you must interrupt mid-capture, press Ctrl-Break in the cdb
|
||
console.
|
||
- **acclient.exe is 32-bit + uses thiscall.** `this` is in `ecx`. Struct
|
||
field offsets in the script come from `docs/research/named-retail/acclient.h`.
|
||
```
|
||
|
||
- [ ] **Step 2: Commit**
|
||
|
||
```bash
|
||
git add tools/cdb/README-a6-probe.md
|
||
git commit -m "$(cat <<'EOF'
|
||
docs(cdb): A6.P1 — README for the cdb probe + runner
|
||
|
||
Documents prerequisites (PDB match, cdb install, retail+ACE
|
||
running), per-scenario invocation, the 9-scenario tag table, and
|
||
the parallel acdream capture command. Includes the CLAUDE.md cdb
|
||
watchouts inline so probe operators don't have to chase them.
|
||
|
||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
EOF
|
||
)"
|
||
```
|
||
|
||
---
|
||
|
||
## Phase C — Execute 9 captures + file findings stub
|
||
|
||
### Task 13: Verify retail binary matches PDB
|
||
|
||
**Files:** None modified — verification step.
|
||
|
||
- [ ] **Step 1: Run the PDB matcher**
|
||
|
||
```powershell
|
||
py tools\pdb-extract\check_exe_pdb.py "C:\Turbine\Asheron's Call\acclient.exe"
|
||
```
|
||
|
||
Expected: `=== MATCH: this exe pairs with our acclient.pdb ===`
|
||
|
||
If MISMATCH: stop. Investigate which retail build is installed; either re-install the matching build or update `refs/acclient.pdb` to match. Don't proceed with captures using a mismatched PDB — symbol resolution will fail and all BP actions will dump garbage.
|
||
|
||
- [ ] **Step 2: Document the result in the capture directory**
|
||
|
||
After confirming MATCH, capture the verification output to file. This is the audit trail for the capture session:
|
||
|
||
```powershell
|
||
mkdir -p docs\research\2026-05-21-a6-captures
|
||
py tools\pdb-extract\check_exe_pdb.py "C:\Turbine\Asheron's Call\acclient.exe" > docs\research\2026-05-21-a6-captures\pdb-match-verification.txt 2>&1
|
||
```
|
||
|
||
- [ ] **Step 3: Commit the verification artifact**
|
||
|
||
```bash
|
||
git add docs/research/2026-05-21-a6-captures/pdb-match-verification.txt
|
||
git commit -m "$(cat <<'EOF'
|
||
docs(research): A6.P1 — record retail-PDB match verification
|
||
|
||
Audit trail for the A6.P1 capture session: confirms the live retail
|
||
acclient.exe matches refs/acclient.pdb before any BP-driven
|
||
captures fire.
|
||
|
||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
EOF
|
||
)"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 14: Create capture directory structure + findings doc stub
|
||
|
||
**Files:**
|
||
- Create: `docs/research/2026-05-21-a6-captures/scen{1..9}_<name>/.gitkeep` (9 stub dirs)
|
||
- Create: `docs/research/2026-05-21-a6-cdb-capture-findings.md` (findings doc stub)
|
||
|
||
- [ ] **Step 1: Create the per-scenario directory stubs**
|
||
|
||
```powershell
|
||
$scenarios = @(
|
||
"scen1_inn_doorway",
|
||
"scen2_inn_stairs",
|
||
"scen3_inn_2nd_floor",
|
||
"scen4_cottage_cellar",
|
||
"scen5_sewer_entry",
|
||
"scen6_sewer_first_stair",
|
||
"scen7_sewer_inter_room",
|
||
"scen8_sewer_chamber",
|
||
"scen9_sewer_corridor"
|
||
)
|
||
foreach ($s in $scenarios) {
|
||
$dir = "docs\research\2026-05-21-a6-captures\$s"
|
||
if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir | Out-Null }
|
||
New-Item -ItemType File -Path "$dir\.gitkeep" -Force | Out-Null
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 2: Create the findings doc stub**
|
||
|
||
Create `docs/research/2026-05-21-a6-cdb-capture-findings.md`:
|
||
|
||
```markdown
|
||
# A6.P1 cdb capture findings — 2026-05-21
|
||
|
||
**Status:** STUB — captures in progress. Tables filled by A6.P2 analysis.
|
||
|
||
**Spec:** [`docs/superpowers/specs/2026-05-21-phase-a6-indoor-physics-fidelity-design.md`](../superpowers/specs/2026-05-21-phase-a6-indoor-physics-fidelity-design.md).
|
||
|
||
**PDB match verification:** [`pdb-match-verification.txt`](2026-05-21-a6-captures/pdb-match-verification.txt).
|
||
|
||
## Capture inventory
|
||
|
||
| Scenario | Tag | Retail log | acdream log | Status |
|
||
|---|---|---|---|---|
|
||
| 1 | scen1_inn_doorway | [retail.log](2026-05-21-a6-captures/scen1_inn_doorway/retail.log) | [acdream.log](2026-05-21-a6-captures/scen1_inn_doorway/acdream.log) | pending |
|
||
| 2 | scen2_inn_stairs | [retail.log](2026-05-21-a6-captures/scen2_inn_stairs/retail.log) | [acdream.log](2026-05-21-a6-captures/scen2_inn_stairs/acdream.log) | pending |
|
||
| 3 | scen3_inn_2nd_floor | [retail.log](2026-05-21-a6-captures/scen3_inn_2nd_floor/retail.log) | [acdream.log](2026-05-21-a6-captures/scen3_inn_2nd_floor/acdream.log) | pending |
|
||
| 4 | scen4_cottage_cellar | [retail.log](2026-05-21-a6-captures/scen4_cottage_cellar/retail.log) | [acdream.log](2026-05-21-a6-captures/scen4_cottage_cellar/acdream.log) | pending |
|
||
| 5 | scen5_sewer_entry | [retail.log](2026-05-21-a6-captures/scen5_sewer_entry/retail.log) | [acdream.log](2026-05-21-a6-captures/scen5_sewer_entry/acdream.log) | pending |
|
||
| 6 | scen6_sewer_first_stair | [retail.log](2026-05-21-a6-captures/scen6_sewer_first_stair/retail.log) | [acdream.log](2026-05-21-a6-captures/scen6_sewer_first_stair/acdream.log) | pending |
|
||
| 7 | scen7_sewer_inter_room | [retail.log](2026-05-21-a6-captures/scen7_sewer_inter_room/retail.log) | [acdream.log](2026-05-21-a6-captures/scen7_sewer_inter_room/acdream.log) | pending |
|
||
| 8 | scen8_sewer_chamber | [retail.log](2026-05-21-a6-captures/scen8_sewer_chamber/retail.log) | [acdream.log](2026-05-21-a6-captures/scen8_sewer_chamber/acdream.log) | pending |
|
||
| 9 | scen9_sewer_corridor | [retail.log](2026-05-21-a6-captures/scen9_sewer_corridor/retail.log) | [acdream.log](2026-05-21-a6-captures/scen9_sewer_corridor/acdream.log) | pending |
|
||
|
||
## Analysis tables (filled by A6.P2)
|
||
|
||
### Table 1 — Per-site push-back delta
|
||
|
||
*Placeholder — filled by A6.P2 analysis. Rows = (site × scenario) cross-product.
|
||
Delta computed as `‖output_center − input_center‖` per call. Bug candidate
|
||
threshold: ratio > 3× retail.*
|
||
|
||
| Site | Scenario | Retail mean delta (mm) | Retail p99 (mm) | acdream mean (mm) | acdream p99 (mm) | Ratio |
|
||
|---|---|---|---|---|---|---|
|
||
|
||
### Table 2 — Path-frequency diff
|
||
|
||
*Placeholder — filled by A6.P2 analysis. Paths labeled 1-7 per the
|
||
find_collisions dispatcher.*
|
||
|
||
| Scenario | Path | Retail count | acdream count | Diff % |
|
||
|---|---|---|---|---|
|
||
|
||
### Table 3 — ContactPlane lifecycle diff
|
||
|
||
*Placeholder — filled by A6.P2 analysis.*
|
||
|
||
| Scenario | Retail CP writes/sec | acdream CP writes/sec | Retail CP-restore-from-LKCP/sec | acdream CP-restore/sec |
|
||
|---|---|---|---|---|
|
||
|
||
### Table 4 — Sub-step state mutations
|
||
|
||
*Placeholder — filled by A6.P2 analysis. Fields: cell_array_valid,
|
||
hits_interior_cell, walk_interp, walkable, collide.*
|
||
|
||
| Scenario | Field | Retail mutations/sec | acdream mutations/sec |
|
||
|---|---|---|---|
|
||
|
||
## Per-scenario narrative (filled by A6.P2)
|
||
|
||
### Scenario 1 — Inn doorway entry
|
||
|
||
*Placeholder.*
|
||
|
||
### Scenario 2 — Inn stairs ascent
|
||
|
||
*Placeholder.*
|
||
|
||
*(etc. — 3 through 9)*
|
||
|
||
## Findings (filled by A6.P2)
|
||
|
||
*Numbered bug candidates. Each entry contains: title, retail decomp
|
||
anchor (line in acclient_2013_pseudo_c.txt), our suspect code site
|
||
(file + line), divergence quantified, proposed fix sketch, scenarios
|
||
affected.*
|
||
```
|
||
|
||
- [ ] **Step 3: Commit the stub structure**
|
||
|
||
```bash
|
||
git add docs/research/2026-05-21-a6-captures/ docs/research/2026-05-21-a6-cdb-capture-findings.md
|
||
git commit -m "$(cat <<'EOF'
|
||
docs(research): A6.P1 — capture directory structure + findings stub
|
||
|
||
Creates the 9 per-scenario capture directories (gitkeep stubs) and
|
||
the findings doc stub at docs/research/2026-05-21-a6-cdb-capture-findings.md.
|
||
A6.P1 fills the capture log slots; A6.P2 fills the analysis tables
|
||
and findings section.
|
||
|
||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
EOF
|
||
)"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 15: Execute the 9 capture scenarios (USER-DRIVEN)
|
||
|
||
**Files:**
|
||
- Modify (filled by capture runs): `docs/research/2026-05-21-a6-captures/scen{1..9}_<name>/{retail,acdream}.log`
|
||
- Modify: `docs/research/2026-05-21-a6-cdb-capture-findings.md` (update "Status" column from `pending` → `captured`)
|
||
|
||
**Operator role:** This task is USER-DRIVEN — the user runs retail + acdream
|
||
clients, executes the scripted walks, and confirms each scenario's
|
||
capture pair is filed. Each scenario is ~5 min of user time at the
|
||
keyboard. Captures can be split across days; each scenario's pair is
|
||
self-contained.
|
||
|
||
For EACH of the 9 scenarios:
|
||
|
||
- [ ] **Step 1: Position retail at scenario start**
|
||
|
||
Open retail client, navigate to the scenario start position. For
|
||
scenario 1: outside the Holtburg inn front door, facing the doorway.
|
||
Stop. (Don't move while the cdb attaches.)
|
||
|
||
- [ ] **Step 2: Attach cdb with the scenario tag**
|
||
|
||
```powershell
|
||
.\tools\cdb\a6-probe-runner.ps1 -ScenarioTag "<scenN_name>"
|
||
```
|
||
|
||
Wait for `a6-probe armed: BPs 1-7 set, threshold=50000 total hits, qd on threshold`
|
||
in the cdb console.
|
||
|
||
- [ ] **Step 3: Launch acdream with all probes enabled**
|
||
|
||
In a SEPARATE PowerShell window:
|
||
|
||
```powershell
|
||
$env:ACDREAM_DAT_DIR = "$env:USERPROFILE\Documents\Asheron's Call"
|
||
$env:ACDREAM_LIVE = "1"
|
||
$env:ACDREAM_TEST_HOST = "127.0.0.1"
|
||
$env:ACDREAM_TEST_PORT = "9000"
|
||
$env:ACDREAM_TEST_USER = "testaccount"
|
||
$env:ACDREAM_TEST_PASS = "testpassword"
|
||
$env:ACDREAM_DEVTOOLS = "1"
|
||
$env:ACDREAM_PROBE_PUSH_BACK = "1"
|
||
$env:ACDREAM_PROBE_INDOOR_BSP = "1"
|
||
$env:ACDREAM_PROBE_CELL = "1"
|
||
$env:ACDREAM_PROBE_CELL_CACHE = "1"
|
||
$env:ACDREAM_PROBE_CONTACT_PLANE = "1"
|
||
|
||
dotnet build -c Debug
|
||
dotnet run --project src\AcDream.App\AcDream.App.csproj --no-build -c Debug 2>&1 |
|
||
Tee-Object -FilePath "docs\research\2026-05-21-a6-captures\<scenN_name>\acdream.log"
|
||
```
|
||
|
||
Navigate `+Acdream` to the same scenario start position. Stop.
|
||
|
||
- [ ] **Step 4: Perform the scripted walk in BOTH clients**
|
||
|
||
Per the README table:
|
||
|
||
| Scenario | Walk script |
|
||
|---|---|
|
||
| 1 inn doorway | Walk forward through door, stop just inside |
|
||
| 2 inn stairs | Walk up 4 steps, stop on landing |
|
||
| 3 inn 2nd floor | Walk forward 3 m, sidestep 1 m, walk back |
|
||
| 4 cottage cellar | Walk to cellar opening, descend 2 steps |
|
||
| 5 sewer entry | Walk into portal, then walk 2 m forward inside |
|
||
| 6 sewer first stair | Walk down full stair flight |
|
||
| 7 sewer inter-room | Walk through portal, stop 1 m past |
|
||
| 8 sewer chamber | Walk in, traverse center, walk out other side |
|
||
| 9 sewer corridor | Walk full length end-to-end |
|
||
|
||
Perform the SAME walk in retail and in acdream. cdb fires BPs; acdream
|
||
emits `[push-back]` lines.
|
||
|
||
- [ ] **Step 5: Teardown**
|
||
|
||
cdb auto-detaches at 50K hits. If it doesn't fire within ~30 sec of the
|
||
walk completing, press Ctrl-Break in the cdb console.
|
||
|
||
Close acdream by gracefully closing the window (NOT Stop-Process, per
|
||
CLAUDE.md logout-before-reconnect rules).
|
||
|
||
- [ ] **Step 6: Verify capture files are non-empty**
|
||
|
||
```powershell
|
||
$dir = "docs\research\2026-05-21-a6-captures\<scenN_name>"
|
||
Get-Item "$dir\retail.log", "$dir\acdream.log" | Format-Table Name, Length
|
||
```
|
||
|
||
Expected: both files > 1 KB. If retail.log is < 1 KB, BPs didn't fire —
|
||
investigate (probably PDB load failed or retail wasn't in-world).
|
||
If acdream.log doesn't contain `[push-back]` lines, the env var didn't
|
||
take effect — re-launch.
|
||
|
||
- [ ] **Step 7: Mark scenario captured + commit**
|
||
|
||
Update `docs/research/2026-05-21-a6-cdb-capture-findings.md`: change
|
||
the scenario's row "Status" from `pending` to `captured`.
|
||
|
||
```bash
|
||
git add docs/research/2026-05-21-a6-captures/<scenN_name>/ docs/research/2026-05-21-a6-cdb-capture-findings.md
|
||
git commit -m "$(cat <<'EOF'
|
||
capture(research): A6.P1 — scenario <N> <name>
|
||
|
||
Paired retail + acdream traces filed at
|
||
docs/research/2026-05-21-a6-captures/<scenN_name>/.
|
||
|
||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
EOF
|
||
)"
|
||
```
|
||
|
||
**Repeat steps 1–7 for all 9 scenarios.** Total user time: ~45 min
|
||
(5 min per scenario) + ~30 min capture-setup overhead = ~75 min single
|
||
session. Splittable across days; each scenario's commit is self-contained.
|
||
|
||
---
|
||
|
||
## A6.P1 acceptance
|
||
|
||
Phase A6.P1 is complete when:
|
||
|
||
1. All 9 scenarios have `{retail,acdream}.log` pairs filed under
|
||
`docs/research/2026-05-21-a6-captures/scenN_<name>/`.
|
||
2. Each log pair is non-empty (retail > 1 KB, acdream > 1 KB).
|
||
3. Each scenario's row in `docs/research/2026-05-21-a6-cdb-capture-findings.md`
|
||
shows Status = `captured`.
|
||
4. `pdb-match-verification.txt` is filed as audit trail.
|
||
5. The `[push-back]`, `[push-back-disp]`, `[push-back-cell]` probes ship
|
||
on main (Tasks 1–9 committed).
|
||
6. `dotnet build` green, `dotnet test` baseline unchanged (no test
|
||
regressions from the probe instrumentation — probe is zero-cost when
|
||
off).
|
||
|
||
A6.P2 takes the captures + findings doc stub and produces the
|
||
quantitative analysis tables + bug candidates list.
|