# 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.
