using System.Linq;
using System.Numerics;
using AcDream.App.Rendering;
using AcDream.App.Rendering.Gpu;
using AcDream.App.Rendering.Walk;
using DatReaderWriter;
namespace AcDream.App.Tests.Rendering.Walk;
///
/// S4-c1 fix round 1, F3: the automated depth-event transcript gate.
/// Replays each OH alpha/depth capture's frame 2 through the SAME ported
/// walk ( + ,
/// +
/// — the identical replay harness
/// already uses) with a RECORDING sink/leaf pair that reproduces retail's
/// own PM (mode, counterBefore) and PC (ov, counter, forceClear) sequences,
/// then compares them against 's parse of
/// the real capture, exactly (pointers ignored).
///
/// Why two observation points, not one. Retail's PM line fires
/// at DrawPortalPolyInternal's function ENTRY — before the four-plane
/// boundary guard (S4-c1 F1) has run — so a PM line is emitted for every
/// ATTEMPT, guard-rejected or not. A building punch's guard decision runs
/// INSIDE (a Collect-time
/// hook): a rejected punch never reaches
/// at all, so intercepts
/// OnPunchGeometry ITSELF, at Collect time, before the guard runs —
/// reading there gives the
/// PRE-reset value retail's own punches observe (Collect completes entirely
/// before Replay starts, so reading the counter at Replay time would already
/// see the root's own post-reset value — the wrong number, confirmed against
/// holtburg-doorway-still's own counterBefore=0002 punches immediately
/// followed by counterBefore=0000/0001 seals in the SAME turn). Exit seals
/// have no such Collect-time per-portal hook at all ( takes no cell/portal
/// argument — RetailPViewPassExecutor.DrawPortalDepthWrite owns the
/// real per-portal loop entirely at Replay time), so
/// reproduces that SAME loop (S4-c1 F1's guard, F2's count-before-clip order)
/// directly over the walk's own
/// / / —
/// the identical LOCAL portal-polygon data the production seal path reads,
/// at Replay time, which is also where retail's own exit-seal loop runs
/// (established: punches always precede the single per-root ExitSeals
/// Collect-time event, so simple list concatenation reproduces the exact
/// interleaved PM order without needing a shared Collect/Replay timeline).
///
///
/// Why the counter is SEEDED, not primed by a throwaway pass
/// (S4-c1 fix round 2, R2-2). Retail's portalsDrawnCount is a
/// PERSISTENT session global (wo(008719b4)), unaffected by any reset
/// between captured frames — a running client carries whatever value the
/// counter last settled at into every subsequent capture. Each fixture's
/// own FIRST PM or PC line (wherever it falls — including the "F 1"
/// preamble text a cdb session prints before the first parsed frame
/// marker, which is where terrace-edge's and holtburg-doorway-still's own
/// first samples live) already carries that pre-capture value:
/// cathedral-arrival 0, cathedral-leak 0, foundry-deep 1,
/// holtburg-doorway-still 2, terrace-edge 2, cathedral-stair-arch 8 (see
/// 's own doc comment).
/// Fix round 1 tried to reach a matching steady state by running a
/// throwaway "priming" Collect+Replay pass first and discarding its
/// output — this works only for a fixture whose captured frames themselves
/// contain enough seal activity to climb from a cold
/// PortalsDrawnCount=0 start up to the needed value (holtburg-
/// doorway-still, foundry-deep); terrace-edge's own capture never runs a
/// qualifying interior turn at all (every PC line reads ov=0), so no
/// number of priming passes from a cold start can ever reach its
/// counterBefore=2 — the value has to come from BEFORE the capture,
/// not from replaying the capture against itself. Seeding
/// directly from the
/// fixture's own first sample before a SINGLE Collect+Replay pass fixes
/// this for every pose, terrace-edge included, without changing what a
/// pose with real seal activity computes (a fixture's own first sample
/// IS the steady-state value fix round 1's priming pass converged to,
/// since a steady state is by definition unchanged by one more pass).
///
public sealed partial class WalkTraceConformanceTests
{
/// Collect-time decorator: forwards
/// every hook to the real unchanged (so its
/// own event collection stays complete and correct), while separately
/// recording the two families this gate needs — see this file's own
/// class doc comment for why punches must be captured HERE rather than
/// at Replay.
private sealed class AlphaDepthCollectSink : IWalkEventSink
{
private readonly WalkFrameDriver _driver;
private readonly IWalkEventSink _inner;
public AlphaDepthCollectSink(WalkFrameDriver driver)
{
_driver = driver;
_inner = driver;
}
public readonly List<(int Mode, int CounterBefore)> Punches = new();
public readonly List<(int Ov, int Counter, int ForceClear)> PcEvents = new();
public void Emit(in WalkEvent walkEvent)
{
// PView::DrawCells @0x005a4840's own PC print fires at function
// ENTRY for every call — the root's own turn AND every building
// look-in's re-entrant call alike (oh1-depth-lifecycle.md: the
// breakpoint doesn't distinguish). WalkEventKind.DrawCells's
// Emit call is the walk's own "this DrawCells call happened"
// signal at that SAME entry-order point (its own doc comment:
// "sits at breakpoint-ENTRY order"); forceClear (oh1: no write
// site found anywhere in the binary) is always 0.
if (walkEvent.Kind == WalkEventKind.DrawCells)
PcEvents.Add((walkEvent.OutsideViewCount, _driver.PortalsDrawnCount, 0));
_inner.Emit(walkEvent);
}
public void OnLandscapeViews(WalkPortalView activeViews) =>
_inner.OnLandscapeViews(activeViews);
public void OnLandCellTurn(uint landblockId, int sideCellCount, int cellIndex) =>
_inner.OnLandCellTurn(landblockId, sideCellCount, cellIndex);
public void OnSortCellTurn(uint landblockId, int sideCellCount, int cellIndex) =>
_inner.OnSortCellTurn(landblockId, sideCellCount, cellIndex);
public void OnLandscapeCellTurn(uint cellId) =>
_inner.OnLandscapeCellTurn(cellId);
public void OnLandscapeCellTurn(uint landblockId, int sideCellCount, int cellIndex) =>
_inner.OnLandscapeCellTurn(landblockId, sideCellCount, cellIndex);
public void OnBuildingTurn(WalkBuilding building) =>
_inner.OnBuildingTurn(building);
public void OnBuildingShellTurn(WalkBuilding building) =>
_inner.OnBuildingShellTurn(building);
public void OnPunchGeometry(
WalkBuilding building, WalkPolygon polygon, int activeViewIndex)
{
// Far-Z punches never touch portalsDrawnCount (R2); this reads
// it purely as a snapshot for the transcript, BEFORE the SAME
// interior root's own OnInteriorFloodDrawTurn reset — see class
// doc comment.
Punches.Add((1, _driver.PortalsDrawnCount));
_inner.OnPunchGeometry(building, polygon, activeViewIndex);
}
public void OnInteriorFloodDrawTurn(IReadOnlyList cells, int outsideViewCount) =>
_inner.OnInteriorFloodDrawTurn(cells, outsideViewCount);
public void OnWeatherTurn(uint viewerCellId) =>
_inner.OnWeatherTurn(viewerCellId);
}
/// Replay-time : every
/// member besides is a plain no-op (this
/// gate cares only about the depth/count mechanism, not mesh content).
/// reproduces
/// RetailPViewPassExecutor.DrawPortalDepthWrite's real per-portal
/// loop — S4-c1 F1's guard, F2's count-before-clip order — directly over
/// the walk's own flood/cell data (no fake GPU clip needed: F2
/// established the count happens unconditionally once the guard
/// passes).
private sealed class AlphaDepthLeaf : IWalkFrameLeafRenderer
{
private readonly Dictionary _cells;
/// S4-c2 gate G-c2: the real production queue — this gate's
/// walk carries no live GfxObj/particle mesh content (same design as
/// this file's own PM/PC harness:
/// returns empty everywhere), so stays at
/// ClipCount/AlphaCount == 0 throughout — but the SEQUENCE of (site,
/// threshold) calls the walk drives it through, and each call's
/// real no-op-vs-drain decision under those (always-empty) counts,
/// are both genuine production behavior, not stubbed.
private readonly RetailAlphaQueue _queue;
public AlphaDepthLeaf(Dictionary cells, RetailAlphaQueue queue)
{
_cells = cells;
_queue = queue;
}
/// Set once, immediately after construction — the leaf and
/// its owning are mutually referential
/// (the driver's constructor requires the leaf), so this cannot be a
/// constructor parameter.
public WalkFrameDriver Driver { get; set; } = null!;
public readonly List<(int Mode, int CounterBefore)> Seals = new();
/// Every ACTUAL flush this replay drove the queue through,
/// in order — (site, threshold, clip count observed immediately
/// BEFORE the drain, alpha count observed immediately BEFORE the
/// drain). A no-op call (both counts stay strictly under
/// threshold*3000) is not recorded, matching
/// 's
/// own definition of "a flush" on the capture side.
public readonly List<(RetailAlphaFlushSite Site, float Threshold, int DrainedClip, int DrainedAlpha)>
ActualFlushes = new();
private void RecordFlush(RetailAlphaFlushSite site, float threshold)
{
int clipBefore = _queue.ClipCount;
int alphaBefore = _queue.AlphaCount;
// Same no-op predicate as D3DPolyRender::FlushAlphaList itself
// (RetailAlphaQueue.Flush) — computed here, before the real
// call, purely so this recorder can decide whether THIS call
// counts as "a flush" for the transcript.
bool noOp = clipBefore < threshold * RetailAlphaQueue.ListCapacity
&& alphaBefore < threshold * RetailAlphaQueue.ListCapacity;
_queue.Flush(site, threshold);
if (!noOp)
ActualFlushes.Add((site, threshold, clipBefore, alphaBefore));
}
public void DrawSky() { }
public void DrawLandCellBatch(
IReadOnlyList<(uint LandblockId, int SideCellCount, int CellIndex)> cells) { }
public bool HasRenderableEmittersInCell(uint cellId) => false;
public void DrawCellShell(uint cellId) { }
public ReadOnlySpan PrepareStaticParticles(uint cellId) =>
ReadOnlySpan.Empty;
public ReadOnlySpan PrepareCellParticles(uint cellId) =>
ReadOnlySpan.Empty;
public void ClearInteriorDepth() { }
public void FlushLandscape() => RecordFlush(RetailAlphaFlushSite.LandscapeFlush, 0f);
public void DrawPunchFan(WalkPolygon worldPolygon, int activeViewIndex) { }
public void AlphaBarrier() => RecordFlush(RetailAlphaFlushSite.DrawBuilding, 0f);
public void FlushSortCellExit() => RecordFlush(RetailAlphaFlushSite.SortCellExit, 0.75f);
public int DrawExitSeals()
{
int accepted = 0;
List flood = Driver.InteriorFloodCells;
// Reverse order — the SAME direction DrawWalkExitPortalMasks
// walks driver.InteriorFloodCells in production
// (RetailPViewRenderer.cs).
for (int i = flood.Count - 1; i >= 0; i--)
{
if (!_cells.TryGetValue(flood[i], out WalkCell? cell))
continue;
int sliceCount = Driver.InteriorFloodViewSliceCountAt(i);
for (int slice = 0; slice < sliceCount; slice++)
{
for (int p = 0; p < cell.Portals.Length; p++)
{
WalkCellPortal portal = cell.Portals[p];
// Walk-side exit sentinel is 32-bit 0xFFFFFFFF
// (WalkCellPortal's own doc comment) — the render-
// side CellPortalInfo's 16-bit 0xFFFF is a DIFFERENT
// representation of the same fact.
if (portal.OtherCellId != 0xFFFFFFFFu)
continue;
if ((uint)portal.PolygonIndex >= (uint)cell.PortalPolygons.Length)
continue;
WalkPolygon poly = cell.PortalPolygons[portal.PolygonIndex];
// F2: count happens BEFORE clip (unconditionally,
// once the guard passes) — counterBefore is the
// running total BEFORE this specific portal's own
// decision.
Seals.Add((0, Driver.PortalsDrawnCount + accepted));
if (!WalkVisibilityMath.IsRejectedByPortalPolygonBoundaryGuard(
poly.Vertices))
{
accepted++;
}
}
}
}
return accepted;
}
}
/// Minimal : this gate never
/// inspects mesh content, only the depth/count event sequence, so every
/// query returns the shared empty record.
private sealed class EmptyAlphaDepthWorldData : IWalkFrameWorldData
{
public WalkFrameStaticRecords GetCellObjects(uint cellId) => WalkFrameStaticRecords.Empty;
public WalkFrameStaticRecords GetOutdoorObjects(uint cellId) => WalkFrameStaticRecords.Empty;
public WalkFrameStaticRecords GetCellStatics(uint cellId) => WalkFrameStaticRecords.Empty;
public WalkFrameStaticRecords GetCellDynamics(uint cellId) => WalkFrameStaticRecords.Empty;
public WalkFrameStaticRecords GetOutdoorStatics(uint cellId) => WalkFrameStaticRecords.Empty;
public WalkFrameStaticRecords GetOutdoorDynamics(uint cellId) => WalkFrameStaticRecords.Empty;
public WalkFrameStaticRecords GetBuildingShellStatics(WalkBuilding building) =>
WalkFrameStaticRecords.Empty;
public Matrix4x4 GetBuildingWorldTransform(WalkBuilding building) => Matrix4x4.Identity;
}
/// Seeds from
/// 's own first observed sample (S4-c1 fix
/// round 2, R2-2 — see class doc comment), runs ONE Collect+Replay pass,
/// and asserts BOTH the PM and PC sequences against
/// 's captured frame 2
/// ('s frames[1], matching every
/// other OH-capture row's own frame-2 convention in this file).
/// R2-1 (gate honesty): both diffs are computed BEFORE either is
/// asserted, so a PM mismatch can never prevent the PC comparison from
/// running (or vice versa) — a written claim that one sequence
/// "matches" is only ever one this method actually evaluated.
///
private void RunAlphaDepthTranscriptGate(string fixtureName)
{
IReadOnlyList poseFrames = WalkOracleTrace.Load(OhCaptureRoot, fixtureName);
Assert.NotEmpty(poseFrames);
Assert.True(poseFrames.Count > 1, $"{fixtureName}: need a captured frame 2.");
WalkOracleFrame poseFrame = poseFrames[1];
Assert.NotNull(poseFrame.Pose);
WalkOraclePose pose = poseFrame.Pose!;
IReadOnlyList depthFrames = WalkAlphaDepthTrace.Load(OhCaptureRoot, fixtureName);
Assert.True(depthFrames.Count > 1, $"{fixtureName}: need a captured frame 2's PM/PC content.");
WalkAlphaDepthFrame expected = depthFrames[1];
int initialCounter = WalkAlphaDepthTrace.LoadInitialCounter(OhCaptureRoot, fixtureName);
using DatCollection dats = OpenDats();
WalkLandscapeDatBuilder.BuiltWorld world =
WalkLandscapeDatBuilder.Build(dats, pose.CellId, pose.Origin);
var ctx = new WalkTraceReplayContext(pose, world.Cells) { Buildings = world.Buildings };
WalkCell? camera = (pose.CellId & 0xFFFFu) >= 0x100
? Assert.Contains(pose.CellId, world.Cells)
: null;
using var fx = new WalkFrameDriverTests.DispatcherFixture();
var worldData = new EmptyAlphaDepthWorldData();
// S4-c2: AlphaDepthLeaf now records every flush through a real
// RetailAlphaQueue (used by the AM/FL sibling gate below); this PM/PC
// gate doesn't care about that queue's transcript, but it still must
// be a valid ACTIVE frame or AlphaBarrier/FlushLandscape/
// FlushSortCellExit's real Flush() calls throw.
var alphaQueue = new RetailAlphaQueue();
var leaf = new AlphaDepthLeaf(world.Cells, alphaQueue);
var driver = new WalkFrameDriver(fx.Dispatcher, leaf, worldData);
leaf.Driver = driver;
var sink = new AlphaDepthCollectSink(driver);
var walk = new RetailFrameWalk();
// R2-2: seed the persistent counter from the fixture's own
// pre-capture value instead of priming a throwaway first pass.
driver.PortalsDrawnCount = initialCounter;
alphaQueue.BeginFrame();
using (WalkFrameDriverTests.DrawScope draw = fx.BeginDraw())
{
driver.BeginFrame(ctx, Matrix4x4.Identity, pose.Origin);
walk.WalkFrame(pose.CellId, camera, world.Landscape, ctx, sink);
driver.EndFrame();
driver.Replay(draw.Frame, draw.Pass);
}
// DrawScope.Dispose ends only the pass/publication — the frame
// itself (RecordingGpuDevice's "one open frame" invariant) is
// ended separately, same order WalkOutsideViewReassemblyTests'
// own multi-draw loop uses.
fx.FrameLifetime.EndFrame();
alphaQueue.EndFrame();
var actualPm = new List<(int Mode, int CounterBefore)>(sink.Punches);
actualPm.AddRange(leaf.Seals);
IReadOnlyList<(int Ov, int Counter, int ForceClear)> actualPc = sink.PcEvents;
bool pmMatches = expected.PmEvents.SequenceEqual(actualPm);
bool pcMatches = expected.PcEvents.SequenceEqual(actualPc);
if (!pmMatches || !pcMatches)
{
Assert.Fail(
$"{fixtureName}: PM {(pmMatches ? "matches" : "DIVERGES")} " +
$"— expected {FormatPm(expected.PmEvents)}, actual {FormatPm(actualPm)}; " +
$"PC {(pcMatches ? "matches" : "DIVERGES")} " +
$"— expected {FormatPc(expected.PcEvents)}, actual {FormatPc(actualPc)}");
}
}
///
/// S4-c2 gate G-c2, split by S4-c2 fix round 1 (M5): extends this file's
/// own PM/PC transcript gate from depth events to the AM/FL alpha-queue
/// stream. Reuses the EXACT SAME replay harness
/// (: no live GfxObj/particle mesh
/// content, matching this file's own established PM/PC pattern) with a
/// real wired through
/// 's flush hooks. Returns BOTH the expected
/// and actual transcripts so and
/// can assert their own dimension
/// over the SAME replay logic without duplicating the harness setup.
///
private static (
IReadOnlyList<(RetailAlphaFlushSite Site, float Threshold, int DrainedClip, int DrainedAlpha)> Expected,
IReadOnlyList<(RetailAlphaFlushSite Site, float Threshold, int DrainedClip, int DrainedAlpha)> Actual)
RunAlphaFlushTranscriptReplay(string fixtureName)
{
IReadOnlyList poseFrames = WalkOracleTrace.Load(OhCaptureRoot, fixtureName);
Assert.NotEmpty(poseFrames);
Assert.True(poseFrames.Count > 1, $"{fixtureName}: need a captured frame 2.");
WalkOracleFrame poseFrame = poseFrames[1];
Assert.NotNull(poseFrame.Pose);
WalkOraclePose pose = poseFrame.Pose!;
IReadOnlyList depthFrames = WalkAlphaDepthTrace.Load(OhCaptureRoot, fixtureName);
Assert.True(depthFrames.Count > 1, $"{fixtureName}: need a captured frame 2's AM/FL content.");
WalkAlphaDepthFrame expectedFrame = depthFrames[1];
int initialCounter = WalkAlphaDepthTrace.LoadInitialCounter(OhCaptureRoot, fixtureName);
var expected = WalkAlphaDepthTrace.BuildExpectedFlushTranscript(expectedFrame);
using DatCollection dats = OpenDats();
WalkLandscapeDatBuilder.BuiltWorld world =
WalkLandscapeDatBuilder.Build(dats, pose.CellId, pose.Origin);
var ctx = new WalkTraceReplayContext(pose, world.Cells) { Buildings = world.Buildings };
WalkCell? camera = (pose.CellId & 0xFFFFu) >= 0x100
? Assert.Contains(pose.CellId, world.Cells)
: null;
using var fx = new WalkFrameDriverTests.DispatcherFixture();
var worldData = new EmptyAlphaDepthWorldData();
var queue = new RetailAlphaQueue();
var leaf = new AlphaDepthLeaf(world.Cells, queue);
var driver = new WalkFrameDriver(fx.Dispatcher, leaf, worldData);
leaf.Driver = driver;
var sink = new AlphaDepthCollectSink(driver);
var walk = new RetailFrameWalk();
driver.PortalsDrawnCount = initialCounter;
queue.BeginFrame();
using (WalkFrameDriverTests.DrawScope draw = fx.BeginDraw())
{
driver.BeginFrame(ctx, Matrix4x4.Identity, pose.Origin);
walk.WalkFrame(pose.CellId, camera, world.Landscape, ctx, sink);
driver.EndFrame();
driver.Replay(draw.Frame, draw.Pass);
}
fx.FrameLifetime.EndFrame();
// SmartBox::RenderNormalMode's own top-level FlushAlphaList(0f) —
// RetailAlphaQueue.EndFrame() calls this internally, so it is
// recorded here rather than through a leaf hook (see
// RetailAlphaQueue.EndFrame's own doc comment).
int clipBeforeFinal = queue.ClipCount;
int alphaBeforeFinal = queue.AlphaCount;
queue.EndFrame();
leaf.ActualFlushes.Add(
(RetailAlphaFlushSite.RenderNormalMode, 0f, clipBeforeFinal, alphaBeforeFinal));
return (expected, leaf.ActualFlushes);
}
///
/// S4-c2 fix round 1 (M5): the LIVE half of gate G-c2 — the SEQUENCE of
/// (site, threshold) the walk drives the queue through (which of the
/// four normal-world sites fires, how many times, in what order) is
/// purely structural (a function of cell/building/land-cell traversal,
/// never of mesh content), so it is a real, unconditional pass/fail with
/// no KnownFailure tag — a future site-sequence regression is now
/// visible in the hermetic/InstalledDat lanes instead of being buried
/// under the (expected-red) count dimension.
///
private void RunAlphaFlushSitesGate(string fixtureName)
{
(
IReadOnlyList<(RetailAlphaFlushSite Site, float Threshold, int DrainedClip, int DrainedAlpha)> expected,
IReadOnlyList<(RetailAlphaFlushSite Site, float Threshold, int DrainedClip, int DrainedAlpha)> actual
) = RunAlphaFlushTranscriptReplay(fixtureName);
bool siteSequenceMatches = expected
.Select(e => (e.Site, e.Threshold))
.SequenceEqual(actual.Select(a => (a.Site, a.Threshold)));
Assert.True(
siteSequenceMatches,
$"{fixtureName}: flush SITE sequence diverges (content-independent — this must "
+ $"match unconditionally) — expected {FormatFlushSites(expected)}, "
+ $"actual {FormatFlushSites(actual)}");
}
///
/// S4-c2 fix round 1 (M5): the KnownFailure half of gate G-c2 — the
/// drained entry count PER LIST can never match by construction, for
/// THREE independent reasons (not just "harness content only", the
/// prior write-up's incomplete claim): (1) this harness carries no live
/// GfxObj/particle mesh content at all (),
/// so every 0f-threshold flush observes count (0, 0) — always a real
/// drain (0 is never < 0), matching retail's own always-drains
/// behavior at threshold 0f, but with a different count than the
/// capture's real content volume; (2) AP-238 coalesces transparent
/// EnvCell work to one token per (cell,list), whereas retail
/// appends each contributing subset; (3) AP-239/AP-240 change
/// CLIP-vs-ALPHA membership for the named ordinary-Wb surface cases.
/// Ordinary Wb and particle paths otherwise append per real subset; the
/// former broad "per instance versus per subset" explanation was false.
/// See the S4 packet's §9/§10/§11 subsections for the full per-pose
/// sequences.
///
private void RunAlphaFlushCountsGate(string fixtureName)
{
(
IReadOnlyList<(RetailAlphaFlushSite Site, float Threshold, int DrainedClip, int DrainedAlpha)> expected,
IReadOnlyList<(RetailAlphaFlushSite Site, float Threshold, int DrainedClip, int DrainedAlpha)> actual
) = RunAlphaFlushTranscriptReplay(fixtureName);
// Recorded as a real, evaluated assertion (never weakened), expected
// to diverge on the count dimension for any pose with real content —
// callers gate on Status=KnownFailure, not on this passing.
bool fullMatches = expected.SequenceEqual(actual);
Assert.True(
fullMatches,
$"{fixtureName}: flush transcript diverges on drained COUNT only (the site+threshold "
+ "sequence is separately verified exact by AlphaFlushSites_*) — see this method's "
+ $"own doc comment for the three reasons this can never match — expected "
+ $"{FormatFlushCounts(expected)}, actual {FormatFlushCounts(actual)}");
}
private static string FormatFlushSites(
IReadOnlyList<(RetailAlphaFlushSite Site, float Threshold, int DrainedClip, int DrainedAlpha)> events) =>
"[" + string.Join(", ", events.Select(e => $"({e.Site},thresh={e.Threshold})")) + "]";
private static string FormatFlushCounts(
IReadOnlyList<(RetailAlphaFlushSite Site, float Threshold, int DrainedClip, int DrainedAlpha)> events) =>
"[" + string.Join(
", ",
events.Select(e => $"({e.Site},thresh={e.Threshold},clip={e.DrainedClip},alpha={e.DrainedAlpha})")) + "]";
/// S4-c2 fix round 1 (M5): the live half — see
/// 's own doc comment. No
/// KnownFailure tag: this dimension is content-independent and must
/// match unconditionally.
[Fact]
public void AlphaFlushSites_CathedralArrival_MatchesRetailFrame2()
=> RunAlphaFlushSitesGate("cathedral-arrival.alphadepth");
/// S4-c2 fix round 1 (M5): the KnownFailure half — see
/// 's own doc comment for the three
/// independent reasons the drained COUNT can never match by
/// construction.
[Fact]
[Trait("Status", "KnownFailure")]
public void AlphaFlushCounts_CathedralArrival_MatchesRetailFrame2()
=> RunAlphaFlushCountsGate("cathedral-arrival.alphadepth");
[Fact]
public void AlphaFlushSites_CathedralLeak_MatchesRetailFrame2()
=> RunAlphaFlushSitesGate("cathedral-leak.alphadepth");
[Fact]
[Trait("Status", "KnownFailure")]
public void AlphaFlushCounts_CathedralLeak_MatchesRetailFrame2()
=> RunAlphaFlushCountsGate("cathedral-leak.alphadepth");
[Fact]
public void AlphaFlushSites_CathedralStairArch_MatchesRetailFrame2()
=> RunAlphaFlushSitesGate("cathedral-stair-arch.alphadepth");
[Fact]
[Trait("Status", "KnownFailure")]
public void AlphaFlushCounts_CathedralStairArch_MatchesRetailFrame2()
=> RunAlphaFlushCountsGate("cathedral-stair-arch.alphadepth");
[Fact]
public void AlphaFlushSites_FoundryDeep_MatchesRetailFrame2()
=> RunAlphaFlushSitesGate("foundry-deep.alphadepth");
[Fact]
[Trait("Status", "KnownFailure")]
public void AlphaFlushCounts_FoundryDeep_MatchesRetailFrame2()
=> RunAlphaFlushCountsGate("foundry-deep.alphadepth");
[Fact]
public void AlphaFlushSites_HoltburgDoorwayStill_MatchesRetailFrame2()
=> RunAlphaFlushSitesGate("holtburg-doorway-still.alphadepth");
[Fact]
[Trait("Status", "KnownFailure")]
public void AlphaFlushCounts_HoltburgDoorwayStill_MatchesRetailFrame2()
=> RunAlphaFlushCountsGate("holtburg-doorway-still.alphadepth");
[Fact]
public void AlphaFlushSites_TerraceEdge_MatchesRetailFrame2()
=> RunAlphaFlushSitesGate("terrace-edge.alphadepth");
[Fact]
[Trait("Status", "KnownFailure")]
public void AlphaFlushCounts_TerraceEdge_MatchesRetailFrame2()
=> RunAlphaFlushCountsGate("terrace-edge.alphadepth");
private static string FormatPm(IReadOnlyList<(int Mode, int CounterBefore)> events) =>
"[" + string.Join(", ", events.Select(e => $"(mode={e.Mode},counterBefore={e.CounterBefore})")) + "]";
private static string FormatPc(IReadOnlyList<(int Ov, int Counter, int ForceClear)> events) =>
"[" + string.Join(", ", events.Select(e => $"(ov={e.Ov},counter={e.Counter},fc={e.ForceClear})")) + "]";
[Fact]
public void AlphaDepthTranscript_CathedralArrival_MatchesRetailFrame2()
=> RunAlphaDepthTranscriptGate("cathedral-arrival.alphadepth");
[Fact]
public void AlphaDepthTranscript_FoundryDeep_MatchesRetailFrame2()
=> RunAlphaDepthTranscriptGate("foundry-deep.alphadepth");
[Fact]
public void AlphaDepthTranscript_HoltburgDoorwayStill_MatchesRetailFrame2()
=> RunAlphaDepthTranscriptGate("holtburg-doorway-still.alphadepth");
///
/// S4-c1 fix round 2 (R2-2): seeding
/// from terrace-edge's own first observed sample (counterBefore=2,
/// carried in the file's pre-"F 1" preamble text — see
/// 's own doc
/// comment) closes fix round 1's KnownFailure. Under a cold 0
/// BOTH sequences diverged in the counter field — PM expected
/// (1,2),(1,2) and PC expected (0,2,0),(0,2,0) against a
/// cold (1,0)/(0,0,0) — round 1's write-up wrongly called
/// the PC side a match, and round 2's gate-honesty lens caught the same
/// sentence re-worded here (lead correction at landing). Seeded, both
/// sequences match exactly; this pose never runs a qualifying interior
/// turn, so PC stays ov=0 with the seeded counter.
///
[Fact]
public void AlphaDepthTranscript_TerraceEdge_MatchesRetailFrame2()
=> RunAlphaDepthTranscriptGate("terrace-edge.alphadepth");
///
/// S4-c1 fix round 2, R2-4: the sixth pose (#464 artifact pose, root
/// 0xF4180114, seven outside views). Sixteen building punches —
/// all mode=1, all counterBefore=0008 (far-Z punches never
/// touch the counter) — then eight true-depth exit seals with
/// counterBefore running 0..7 against the root's own
/// ov=7 read-then-zero. This gate compares only the
/// (mode, counterBefore) and (ov, counter, forceClear)
/// tuples: it does NOT observe which of the sixteen punches the ±12
/// boundary guard rejects (the sink records every attempt before the
/// guard runs; the pmverts capture and the DAT, not this Fact,
/// show twelve of them lying on local y=12.000 — S4 packet §9).
/// The seed 8 is the first PM/PC sample
/// finds in the
/// file, which for this fixture lies inside frame 1 (this capture has no
/// pre-"F 1" preamble sample); with the priming pass gone, frame 2's PC
/// counter equals that seed by construction in steady state.
///
[Fact]
public void AlphaDepthTranscript_CathedralStairArch_MatchesRetailFrame2()
=> RunAlphaDepthTranscriptGate("cathedral-stair-arch.alphadepth");
/// Cathedral-leak carries the exact same steady state (counter
/// pinned at 0, every exit portal degenerate-onto-plane rejected) as
/// cathedral-arrival; listed here (S4-c1 fix round 1, F3) because the
/// original spec's own fixture count (four) undercounts the five
/// *.alphadepth.log files that were already present in
/// oh-capture/ at that round — see that round's commit body.
[Fact]
public void AlphaDepthTranscript_CathedralLeak_MatchesRetailFrame2()
=> RunAlphaDepthTranscriptGate("cathedral-leak.alphadepth");
}