Campaign OVERHAUL S3 chunk 1 (docs/research/2026-09-01-overhaul/s3-walk-ownership-map.md
§11): the transcript-kinds/fixtures/print-only-emitter half of the walk work,
built AFTER chunk 3 landed LC/SC (the per-land-cell interleave).
B1 — the emitter (print-only, never gates admission/depth/order):
- ACDREAM_DUMP_WALK_TRANSCRIPT=1 is read once into RuntimeOptions.DumpWalkTranscript
(rule 4) and handed to RenderingDiagnostics.DumpWalkTranscriptEnabled (rule 5,
a settable static, not a second env read) once at GameWindow construction — the
deep walk call sites have no reachable RuntimeOptions reference.
- WalkTranscriptDump (new) prints the OH line kinds — F/P/LS/LC/SC/BLD/DI/DC/EC/OC —
to Console at the exact points retail's cdb breakpoints sit
(tools/walk-oracle/oh/oh-capture-walk.cdb.template), gated internally so every
method bails out before any string work when the flag is off.
- Every call site lives in WalkFrameDriver.cs, at the point the driver already
processes that turn: Collect (F/P, after BeginFrame), Emit's DI/LS/DC/BLD cases,
OnLandCellTurn/OnLandscapeCellTurn (LC/SC, at LOD resolution via the new
WalkTranscriptDump.LodCellId helper, before the 8x8-bucket expansion), and the
EmitFloodTurns/EmitCellContentsTurn loops (EC/OC — both UNCONDITIONAL per flood
visit, matching the OH captures' always-equal EC/OC counts; retail's own
DrawEnvCell stamp dedupe sits past the breakpoint, inside the function).
- DC's "pv=" field encodes interior(0)/outdoor(1) as an 8-hex-digit 0/1 so it
satisfies the same pv=[0-9a-f]{8} regex real captures use; derived from
_currentDcStage at the DC event (CellStatic = interior pview, else outdoor).
- The frame-root pose (origin, quaternion) is a reasonable orthonormal basis built
from the walk's own CyPlane.Normal forward vector and WalkLandscape's own
ViewerWorldOriginX/Y block origin — self-consistent for the round-trip parser,
not a byte-exact reproduction of retail's Frame (B4's diff never compares P).
B2 — WalkOracleTrace learns EC/OC event kinds (LC/SC already existed from chunk 3).
New WalkOraclePartsTrace.cs holds two small read-only parsers for the parts log
(PD/DM) and the alpha-depth log (AM/FL/PM/PC) — records only, no validator, no
canonical JSONL, no new tool.
B3 — fixtures: the five OH walk captures join WalkOracleTraceTests.AllFixtures
(now (root, name) pairs — FW0's own root plus the OH capture directory) for
parse + complete-frame pins. The four kit poses join WalkTraceConformanceTests'
still-fixture coverage as NEW rows (the OH cathedral-arrival root is f4180108,
not FW0's f4180106): terrace-edge/cathedral-arrival extend the existing theory
(now (root, fixture) parameterized); holtburg-doorway-still and foundry-deep get
dedicated tests mirroring their FW0 siblings' own structure. Finding: the OH
foundry-deep capture's own retail transcript draws 12 real town buildings through
its exit chain (unlike the FW0 capture, which apparently reached none at that
pose) — the FW0 test's stub 1x1 landscape undershoots it (first divergence:
nothing after "LS" vs retail's real BLD content); fixed by building the full
landscape/building world via WalkLandscapeDatBuilder.Build, matching the shared
theory's own approach, not by skipping or weakening the row.
WalkTraceReplayContext.Signature(WalkOracleFrame) now filters to the DI/DC/BLD/LS
kinds (LC/SC/EC/OC never had a WalkEvent analogue in RetailFrameWalk's own
four-kind vocabulary) instead of mapping them to a "?" placeholder, so the still-
fixture comparison stays apples-to-apples on both sides.
B4 — WalkTranscriptSignatureDiff (test-side only, no runner/tool): diffs two
transcripts (raw lines or parsed frames) at the full DI/DC/BLD/LS/LC/SC/EC/OC
level, reporting the first divergent event and position per frame. Proven over a
synthetic self-vs-self-minus-one-event pair (SignatureDiff_ReportsTheExactRemovedEvent).
Tests: T1 (flag off) is split into a unit-level zero-allocation/zero-output check
on WalkTranscriptDump itself (the walk's pre-existing allocation, e.g.
RetailFrameWalk.EmitDrawCells's per-call array, is untouched by this chunk and not
independently zero-alloc) and an integration-level Collect() check; both assert via
WalkOracleTrace.Parse returning zero frames rather than raw string equality, which
is robust to unrelated Console.WriteLine noise from other test classes running in
parallel (a real, observed hazard — WalkFrameDriverTests joins
CameraDiagnosticsCollection for the same reason CornerFloodReplayTests/
Issue181WallPressEquilibriumTests already do, issue #251). T2 proves the
emitter/parser round trip on a synthetic interior frame. T3's InstalledDat rows all
pass. T4: LaunchOptionsDocumentationTests green with the ACDREAM_DUMP_WALK_TRANSCRIPT
row (both directions).
Gates: hermetic lane 6,814/0 (was 6,795 baseline + new tests), three consecutive
clean runs; InstalledDat lane 245/3 known-failures (the two pre-existing #383
layout tests + TowerAscent) unchanged from baseline.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
331 lines
15 KiB
C#
331 lines
15 KiB
C#
using AcDream.App.Tests.Rendering;
|
||
using AcDream.App.Rendering.Walk;
|
||
using DatReaderWriter;
|
||
using DatReaderWriter.Options;
|
||
|
||
namespace AcDream.App.Tests.Rendering.Walk;
|
||
|
||
/// <summary>
|
||
/// FW1's conformance gate, first slice: replay pose-stamped oracle
|
||
/// fixtures through the ported walk and require the identical event
|
||
/// sequence. The projection-light interior fixtures gate first
|
||
/// (docs/research/2026-08-30-fw-walk-oracle/README.md, posed round);
|
||
/// the outdoor/landscape fixtures join as the landscape world build
|
||
/// lands.
|
||
/// </summary>
|
||
[Trait("Lane", "InstalledDat")]
|
||
public sealed class WalkTraceConformanceTests
|
||
{
|
||
private sealed class Recorder : IWalkEventSink
|
||
{
|
||
public readonly List<WalkEvent> Events = new();
|
||
public void Emit(in WalkEvent walkEvent) => Events.Add(walkEvent);
|
||
}
|
||
|
||
private static DatCollection OpenDats()
|
||
{
|
||
string? datDir = CornerFloodReplayTests.ResolveDatDir();
|
||
if (datDir is null)
|
||
{
|
||
Assert.Fail("Lane=InstalledDat requires an installed retail DAT directory; see docs/release-gate.md.");
|
||
}
|
||
return new DatCollection(datDir!, DatAccessType.Read);
|
||
}
|
||
|
||
[Fact]
|
||
public void Street_outdoor_first_frame_diff()
|
||
{
|
||
// The first landscape-involving conformance case: diff-first (the
|
||
// assert prints both signatures on mismatch for adjudication).
|
||
IReadOnlyList<WalkOracleFrame> frames =
|
||
WalkOracleTrace.Load("posed/holtburg-street-outdoor");
|
||
Assert.NotEmpty(frames);
|
||
using DatCollection dats = OpenDats();
|
||
WalkOracleFrame frame = frames[1]; // frame 2: pose settled (marker timing)
|
||
Assert.NotNull(frame.Pose);
|
||
WalkLandscapeDatBuilder.BuiltWorld world =
|
||
WalkLandscapeDatBuilder.Build(dats, frame.Pose!.CellId, frame.Pose.Origin);
|
||
var ctx = new WalkTraceReplayContext(frame.Pose, world.Cells)
|
||
{
|
||
Buildings = world.Buildings,
|
||
};
|
||
var walk = new RetailFrameWalk();
|
||
var recorder = new Recorder();
|
||
|
||
walk.WalkFrame(frame.Pose.CellId, null, world.Landscape, ctx, recorder);
|
||
|
||
string expected = WalkTraceReplayContext.Signature(frame);
|
||
string actual = WalkTraceReplayContext.Signature(recorder.Events);
|
||
Assert.True(
|
||
expected == actual,
|
||
$"walk diverged from retail\nEXPECTED: {expected}\nACTUAL: {actual}");
|
||
}
|
||
|
||
[Fact]
|
||
public void Doorway_still_first_frame_diff()
|
||
{
|
||
// Interior flood adjudication: DI + DC(ov=2, n=3) + the landscape
|
||
// through two exit views — tests the flood depth in isolation from
|
||
// the building look-in machinery.
|
||
IReadOnlyList<WalkOracleFrame> frames =
|
||
WalkOracleTrace.Load("posed/holtburg-doorway-still");
|
||
Assert.NotEmpty(frames);
|
||
using DatCollection dats = OpenDats();
|
||
WalkOracleFrame frame = frames[1];
|
||
Assert.NotNull(frame.Pose);
|
||
WalkLandscapeDatBuilder.BuiltWorld world =
|
||
WalkLandscapeDatBuilder.Build(dats, frame.Pose!.CellId, frame.Pose.Origin);
|
||
var ctx = new WalkTraceReplayContext(frame.Pose, world.Cells)
|
||
{
|
||
Buildings = world.Buildings,
|
||
};
|
||
WalkCell camera = Assert.Contains(frame.Pose.CellId, world.Cells);
|
||
// Render::deg_mul is DYNAMIC (auto-tuned by frame load), and this
|
||
// capture ran right after the heavy terrace-edge capture with the
|
||
// multiplier depressed: at mul ≤ 0 the degrade thresholds sit at or
|
||
// below each level's ideal, so 001e (eff 29.3), 0026 (27.1), and
|
||
// 002f (43.4) all select the portless level 1 — retail's zero
|
||
// look-in floods. Every other fixture pins ≈ +0.99 (thresholds at
|
||
// max). The recon session's live dump read −0.99 under the same cdb
|
||
// load. Re-dump deg_mul per capture at the next retail session.
|
||
var walk = new RetailFrameWalk { DegradeMultiplier = 0f };
|
||
var recorder = new Recorder();
|
||
|
||
walk.WalkFrame(frame.Pose.CellId, camera, world.Landscape, ctx, recorder);
|
||
|
||
string expected = WalkTraceReplayContext.Signature(frame);
|
||
string actual = WalkTraceReplayContext.Signature(recorder.Events);
|
||
Assert.True(
|
||
expected == actual,
|
||
$"walk diverged from retail\nEXPECTED: {expected}\nACTUAL: {actual}");
|
||
}
|
||
|
||
[Fact]
|
||
public void Oh_doorway_still_first_frame_diff()
|
||
{
|
||
// S3 chunk 1 (§11.2 B3): the OH kit pose's own doorway-still capture
|
||
// — DI f4180108... no, a9b4013f, DC(ov=2, n=3) per §6b. Mirrors
|
||
// Doorway_still_first_frame_diff's own structure (this pose needs
|
||
// the interior camera cell, unlike the plain outdoor/theory rows).
|
||
IReadOnlyList<WalkOracleFrame> frames = WalkOracleTrace.Load(
|
||
"docs/research/2026-09-01-overhaul/oh-capture", "holtburg-doorway-still.walk");
|
||
Assert.NotEmpty(frames);
|
||
using DatCollection dats = OpenDats();
|
||
WalkOracleFrame frame = frames[1];
|
||
Assert.NotNull(frame.Pose);
|
||
WalkLandscapeDatBuilder.BuiltWorld world =
|
||
WalkLandscapeDatBuilder.Build(dats, frame.Pose!.CellId, frame.Pose.Origin);
|
||
var ctx = new WalkTraceReplayContext(frame.Pose, world.Cells)
|
||
{
|
||
Buildings = world.Buildings,
|
||
};
|
||
WalkCell camera = Assert.Contains(frame.Pose.CellId, world.Cells);
|
||
var walk = new RetailFrameWalk();
|
||
var recorder = new Recorder();
|
||
|
||
walk.WalkFrame(frame.Pose.CellId, camera, world.Landscape, ctx, recorder);
|
||
|
||
string expected = WalkTraceReplayContext.Signature(frame);
|
||
string actual = WalkTraceReplayContext.Signature(recorder.Events);
|
||
Assert.True(
|
||
expected == actual,
|
||
$"walk diverged from retail\nEXPECTED: {expected}\nACTUAL: {actual}");
|
||
}
|
||
|
||
private const string FwOracleRoot = "docs/research/2026-08-30-fw-walk-oracle";
|
||
|
||
/// <summary>S3 chunk 1 (§11.2 B3): the OH capture directory — its OWN
|
||
/// pose-stamped kit-pose captures, a DIFFERENT root than the FW0 still
|
||
/// fixtures above.</summary>
|
||
private const string OhCaptureRoot = "docs/research/2026-09-01-overhaul/oh-capture";
|
||
|
||
[Theory]
|
||
[InlineData(FwOracleRoot, "posed/terrace-center")]
|
||
[InlineData(FwOracleRoot, "posed/terrace-edge")]
|
||
[InlineData(FwOracleRoot, "posed/cathedral-arrival")]
|
||
// S3 chunk 1 (§11.2 B3): the OH kit poses, as NEW rows — the OH
|
||
// cathedral-arrival root is f4180108, NOT FW0's f4180106 (a new pose,
|
||
// not a replacement of the FW0 row above).
|
||
[InlineData(OhCaptureRoot, "terrace-edge.walk")]
|
||
[InlineData(OhCaptureRoot, "cathedral-arrival.walk")]
|
||
public void Still_fixture_first_frame_reproduces_exactly(string root, string fixture)
|
||
{
|
||
IReadOnlyList<WalkOracleFrame> frames = WalkOracleTrace.Load(root, fixture);
|
||
Assert.NotEmpty(frames);
|
||
using DatCollection dats = OpenDats();
|
||
WalkOracleFrame frame = frames[1];
|
||
Assert.NotNull(frame.Pose);
|
||
WalkLandscapeDatBuilder.BuiltWorld world =
|
||
WalkLandscapeDatBuilder.Build(dats, frame.Pose!.CellId, frame.Pose.Origin);
|
||
var ctx = new WalkTraceReplayContext(frame.Pose, world.Cells)
|
||
{
|
||
Buildings = world.Buildings,
|
||
};
|
||
WalkCell? camera = (frame.Pose.CellId & 0xFFFFu) >= 0x100
|
||
? Assert.Contains(frame.Pose.CellId, world.Cells)
|
||
: null;
|
||
var walk = new RetailFrameWalk();
|
||
var recorder = new Recorder();
|
||
|
||
walk.WalkFrame(frame.Pose.CellId, camera, world.Landscape, ctx, recorder);
|
||
|
||
string expected = WalkTraceReplayContext.Signature(frame);
|
||
string actual = WalkTraceReplayContext.Signature(recorder.Events);
|
||
Assert.True(
|
||
expected == actual,
|
||
$"walk diverged from retail ({fixture})\nEXPECTED: {expected}\nACTUAL: {actual}");
|
||
}
|
||
|
||
[Fact]
|
||
public void Foundry_entry_reproduces_every_frame_before_the_f67_order_segment()
|
||
{
|
||
// F67–F79 (the fixture's terminal standing segment) diverge in ONE
|
||
// aspect: the flood SET through building a9b40036 is exact, but
|
||
// retail orders its DCs 116,118,11d where the replay orders
|
||
// 11d,116,118. The microscope isolates the flip to the ROOT plane
|
||
// of 0036's drawing BSP (N=(0,0,−1) D=2.8): the replay's
|
||
// building-local eye z is 2.33 (d=+0.47 → NEG-subtree first), while
|
||
// retail behaves as d<0 (POS-first) — a structural ~0.5 m frame
|
||
// question (positionPush(2) / part-scale semantics), only
|
||
// adjudicable by a live cdb dump of FrameCurrent->viewer.viewpoint
|
||
// during 0036's build_draw_portals_only (script:
|
||
// tools/walk-oracle/fw1-f67-viewpoint-probe.cdb). Frames 1–66 must
|
||
// reproduce exactly.
|
||
MovingFixtureReplay("posed/foundry-entry", stopBeforeFrame: 67);
|
||
}
|
||
|
||
[Theory]
|
||
[InlineData("posed/holtburg-walkout")]
|
||
[InlineData("posed/holtburg-transitions")]
|
||
[InlineData("posed/holtburg-walkabout")]
|
||
public void Moving_fixture_reproduces_every_pairable_frame(string fixture)
|
||
=> MovingFixtureReplay(fixture);
|
||
|
||
private void MovingFixtureReplay(string fixture, int stopBeforeFrame = int.MaxValue)
|
||
{
|
||
// Marker timing: the pose stamped at frame N+1 is the camera state
|
||
// frame N drew with (fixture README) — pair events(N) with
|
||
// pose(N+1). One walk + one landscape carry state across frames,
|
||
// reproducing retail's cross-frame caches (the stale cell-order
|
||
// quirk included).
|
||
IReadOnlyList<WalkOracleFrame> frames = WalkOracleTrace.Load(fixture);
|
||
Assert.True(frames.Count >= 3);
|
||
using DatCollection dats = OpenDats();
|
||
WalkOraclePose anchor = frames[1].Pose!;
|
||
WalkLandscapeDatBuilder.BuiltWorld world =
|
||
WalkLandscapeDatBuilder.Build(dats, anchor.CellId, anchor.Origin);
|
||
var walk = new RetailFrameWalk();
|
||
|
||
for (int n = 1; n < frames.Count - 1; n++)
|
||
{
|
||
WalkOracleFrame frame = frames[n];
|
||
if (frame.Number >= stopBeforeFrame) break;
|
||
string expected = WalkTraceReplayContext.Signature(frame);
|
||
// The marker dumps the PREVIOUS frame's camera, so frame N's
|
||
// true camera state lies between pose(N) and pose(N+1) — a
|
||
// capture artifact, not a port ambiguity. A frame passes when
|
||
// the replay matches under either adjacent pose.
|
||
string? firstActual = null;
|
||
bool matched = false;
|
||
foreach (WalkOraclePose pose in new[] { frames[n + 1].Pose!, frame.Pose! })
|
||
{
|
||
Assert.NotNull(pose);
|
||
WalkLandscapeDatBuilder.SetViewer(world.Landscape, pose.CellId, pose.Origin);
|
||
var ctx = new WalkTraceReplayContext(pose, world.Cells)
|
||
{
|
||
Buildings = world.Buildings,
|
||
};
|
||
WalkCell? camera = null;
|
||
if ((pose.CellId & 0xFFFFu) >= 0x100)
|
||
{
|
||
Assert.True(
|
||
world.Cells.TryGetValue(pose.CellId, out camera),
|
||
$"frame {frame.Number}: interior camera cell {pose.CellId:x8} not loaded");
|
||
}
|
||
var recorder = new Recorder();
|
||
walk.WalkFrame(pose.CellId, camera, world.Landscape, ctx, recorder);
|
||
string actual = WalkTraceReplayContext.Signature(recorder.Events);
|
||
firstActual ??= actual;
|
||
if (actual == expected)
|
||
{
|
||
matched = true;
|
||
break;
|
||
}
|
||
}
|
||
Assert.True(
|
||
matched,
|
||
$"frame {frame.Number} diverged under both adjacent poses ({fixture})\n"
|
||
+ $"EXPECTED: {expected}\nACTUAL: {firstActual}");
|
||
}
|
||
}
|
||
|
||
[Fact]
|
||
public void Foundry_deep_reproduces_every_complete_frame_exactly()
|
||
{
|
||
IReadOnlyList<WalkOracleFrame> frames = WalkOracleTrace.Load("posed/foundry-deep");
|
||
Assert.NotEmpty(frames);
|
||
using DatCollection dats = OpenDats();
|
||
Dictionary<uint, WalkCell> cells =
|
||
WalkWorldDatAdapter.BuildInteriorCells(dats, 0xA9B40000u);
|
||
var landscape = new WalkLandscape { MidWidth = 1, Blocks = new WalkLandBlock?[1] };
|
||
|
||
foreach (WalkOracleFrame frame in frames)
|
||
{
|
||
Assert.NotNull(frame.Pose);
|
||
WalkCell camera = Assert.Contains(frame.Pose!.CellId, cells);
|
||
var ctx = new WalkTraceReplayContext(frame.Pose, cells);
|
||
var walk = new RetailFrameWalk();
|
||
var recorder = new Recorder();
|
||
|
||
walk.WalkFrame(frame.Pose.CellId, camera, landscape, ctx, recorder);
|
||
|
||
Assert.Equal(
|
||
WalkTraceReplayContext.Signature(frame),
|
||
WalkTraceReplayContext.Signature(recorder.Events));
|
||
}
|
||
}
|
||
|
||
[Fact]
|
||
public void Oh_foundry_deep_reproduces_every_complete_frame_exactly()
|
||
{
|
||
// S3 chunk 1 (§11.2 B3): the OH kit pose's own foundry-deep capture
|
||
// — DI a9b40176, DC(ov=1, n=2), 12 town buildings drawn through the
|
||
// surviving exit chain (§6b). UNLIKE the FW0 sibling above, this
|
||
// capture's own retail transcript shows real BLD content at that
|
||
// depth, so the stub 1x1 landscape (which has no blocks/buildings to
|
||
// walk at all) undershoots it — first divergence, run without the
|
||
// fix below: "DI:a9b40176|DC:ov=1:a9b40176,a9b40177|LS" (nothing
|
||
// after LS) vs retail's "…|LS|BLD:a9b40031|BLD:a9b…" (12 real
|
||
// buildings). The full landscape/building assembler
|
||
// (WalkLandscapeDatBuilder.Build — the SAME one the shared theory
|
||
// and Oh_doorway_still_first_frame_diff use) reproduces them; the
|
||
// camera is stationary across this still pose, so one build serves
|
||
// every frame in the loop, matching the FW0 sibling's "build once"
|
||
// shape.
|
||
IReadOnlyList<WalkOracleFrame> frames = WalkOracleTrace.Load(
|
||
"docs/research/2026-09-01-overhaul/oh-capture", "foundry-deep.walk");
|
||
Assert.NotEmpty(frames);
|
||
using DatCollection dats = OpenDats();
|
||
Assert.NotNull(frames[0].Pose);
|
||
WalkLandscapeDatBuilder.BuiltWorld world = WalkLandscapeDatBuilder.Build(
|
||
dats, frames[0].Pose!.CellId, frames[0].Pose!.Origin);
|
||
|
||
foreach (WalkOracleFrame frame in frames)
|
||
{
|
||
Assert.NotNull(frame.Pose);
|
||
WalkCell camera = Assert.Contains(frame.Pose!.CellId, world.Cells);
|
||
var ctx = new WalkTraceReplayContext(frame.Pose, world.Cells)
|
||
{
|
||
Buildings = world.Buildings,
|
||
};
|
||
var walk = new RetailFrameWalk();
|
||
var recorder = new Recorder();
|
||
|
||
walk.WalkFrame(frame.Pose.CellId, camera, world.Landscape, ctx, recorder);
|
||
|
||
Assert.Equal(
|
||
WalkTraceReplayContext.Signature(frame),
|
||
WalkTraceReplayContext.Signature(recorder.Events));
|
||
}
|
||
}
|
||
}
|