From caa5eb8b2bf03a45a5735fb14c2e8bfdcec41624 Mon Sep 17 00:00:00 2001 From: Erik Date: Tue, 18 Aug 2026 15:31:57 +0200 Subject: [PATCH] test: replace input and physics source freezes --- .../2026-08-18-r3-test-truth-ledger.md | 59 ++++++++ .../Architecture/CompiledCallGraph.cs | 65 ++++++++ .../Input/C3cF2AutoEntryWiringTests.cs | 102 +++++++------ .../Physics/C3cF1ProductionWiringTests.cs | 47 ++---- .../Physics/Issue270ProductionWiringTests.cs | 136 ++++++++++------- .../LiveEntityNetworkBranchRoutingTests.cs | 142 +++++------------- 6 files changed, 313 insertions(+), 238 deletions(-) diff --git a/docs/reviews/2026-08-18-r3-test-truth-ledger.md b/docs/reviews/2026-08-18-r3-test-truth-ledger.md index 16c1e4b0..95b3ec66 100644 --- a/docs/reviews/2026-08-18-r3-test-truth-ledger.md +++ b/docs/reviews/2026-08-18-r3-test-truth-ledger.md @@ -1149,3 +1149,62 @@ Verification: attributed methods, and reduces direct/total source readers from 63/98 to 57/92. The remaining 92 reconcile to 22 approved retained policies/contracts and 70 staged replacements. + +## Batch Y input, physics, and inbound-network source-freeze replacement + +Batch Y converts the nine approved source-text readers in the input/physics/ +network wiring group. Seven remain as compiled-metadata architecture checks; +two redundant source assertions are removed. No product source changes. + +The exact decisions are: + +- the ForcePosition generic-tail suite now inspects the built + `TryApplyGenericRemoteRenderPose` body for its one `WorldEntity.SetPosition` + edge and proves the committed/deferred local-position observer call is + immediately followed by a compiled return. The negative assertion that the + already-deleted `LocalForcePositionTransaction` class name was absent is + removed: the Runtime-owned transaction and its displaced-authority behavior + are already exercised by + `RuntimeAcceptedPositionDriveControllerTests`, while an absent identifier is + not behavior; +- the contact-versus-walkability source assertion is removed. The dedicated + `SteepContactBody_InterpolatesInsteadOfSnapping` and + `FreeFlightBodyWithNoContact_StillSnaps` behavioral tests exercise the one + discriminating state and the opposite boundary through the real routing + method, so retaining a string check would add brittleness rather than + coverage. The AP-140 retail rationale remains beside those behavioral tests; +- local inbound SetState routing now verifies the compiled controller graph has + exactly one `PlayerMovementController.ApplyServerPhysicsState` edge and no + direct `ApplyPhysicsState` edge; +- the #270 movement-stats contract reflects the single + `StaminaExhaustionEdgeTracker` owner and follows the compiled Observe, + ReportExhaustion, Reset, and two factory Apply edges. Existing tracker and + Runtime movement-state behavior suites remain the behavioral oracle; +- remote spawn settling now identifies both compiled call sites + (`DispatchRemoteInboundMotion` and `OnPosition`), verifies the repeated + inbound route checks `PhysicsBody.InContact` before reseeding, and verifies + the shared seed helper calls `SpawnPlacementSettler.TrySettle`; +- production auto-entry readiness now follows compiled calls to the published + controller, live-record lookup, and physics-host view and verifies the exact + `EntityPhysicsHost` type operand; and +- player presentation attachment now verifies compiled call order from the + animation sink assignment through matched animation drain to the unmatched + interpreter suffix drain. + +`CompiledCallGraph` gained reusable instruction and type-reference readers. +They allow exact return and `isinst`/cast boundaries to be checked without +making comments, whitespace, local names, or source paths part of the oracle. + +Verification: + +- the directly affected and adjacent behavioral App suites pass 31/31, and + the two Runtime owner suites pass 52/52; +- the complete locked Release build covers all 44 projects with zero warnings + and zero errors; +- the no-retry complete hermetic Release gate passes 14,347/14,347 with zero + skips or failures across all 12 test assemblies. The exact two-case + reduction is the two redundant source assertions above; and +- the regenerated 1,254-file inventory parses every file, reports 11,415 + attributed methods, and reduces total direct/helper source readers from 92 + to 83 while direct readers remain 57. The remaining 83 reconcile to the 22 + approved retained policies/contracts and 61 staged replacements. diff --git a/tests/AcDream.App.Tests/Architecture/CompiledCallGraph.cs b/tests/AcDream.App.Tests/Architecture/CompiledCallGraph.cs index d97e5cad..b82a3025 100644 --- a/tests/AcDream.App.Tests/Architecture/CompiledCallGraph.cs +++ b/tests/AcDream.App.Tests/Architecture/CompiledCallGraph.cs @@ -4,6 +4,7 @@ using System.Reflection.Emit; namespace AcDream.App.Tests.Architecture; internal readonly record struct CompiledCall(int Offset, MethodBase Target); +internal readonly record struct CompiledInstruction(int Offset, OpCode OpCode); /// /// Reads compiled call/new-object edges from a method body. Architecture tests @@ -70,6 +71,70 @@ internal static class CompiledCallGraph return literals; } + /// + /// Reads the compiled instruction sequence without exposing operands. This + /// is sufficient for architecture tests that need to distinguish an + /// immediate return from fall-through without freezing source formatting. + /// + public static IReadOnlyList ReadInstructions( + MethodBase method) + { + ArgumentNullException.ThrowIfNull(method); + byte[] il = method.GetMethodBody()?.GetILAsByteArray() + ?? throw new InvalidOperationException( + $"{method.DeclaringType?.FullName}.{method.Name} has no compiled body."); + var instructions = new List(); + + for (int cursor = 0; cursor < il.Length;) + { + int instructionOffset = cursor; + OpCode opCode = ReadOpCode(il, ref cursor); + instructions.Add(new CompiledInstruction(instructionOffset, opCode)); + cursor += OperandSize(opCode.OperandType, il, cursor); + } + + return instructions; + } + + /// + /// Reads types named by compiled type operands such as casts, boxing, and + /// isinst. This lets tests retain an exact type boundary without + /// depending on the source expression used to spell it. + /// + public static IReadOnlyList ReadTypeReferences(MethodBase method) + { + ArgumentNullException.ThrowIfNull(method); + byte[] il = method.GetMethodBody()?.GetILAsByteArray() + ?? throw new InvalidOperationException( + $"{method.DeclaringType?.FullName}.{method.Name} has no compiled body."); + Type[]? declaringArguments = method.DeclaringType?.IsGenericType == true + ? method.DeclaringType.GetGenericArguments() + : null; + Type[]? methodArguments = method.IsGenericMethod + ? method.GetGenericArguments() + : null; + var types = new List(); + + for (int cursor = 0; cursor < il.Length;) + { + OpCode opCode = ReadOpCode(il, ref cursor); + if (opCode.OperandType == OperandType.InlineType) + { + int token = BitConverter.ToInt32(il, cursor); + Type? type = method.Module.ResolveType( + token, + declaringArguments, + methodArguments); + if (type is not null) + types.Add(type); + } + + cursor += OperandSize(opCode.OperandType, il, cursor); + } + + return types; + } + public static int IndexOf( IReadOnlyList calls, Type declaringType, diff --git a/tests/AcDream.App.Tests/Input/C3cF2AutoEntryWiringTests.cs b/tests/AcDream.App.Tests/Input/C3cF2AutoEntryWiringTests.cs index 6c4659da..62aa723e 100644 --- a/tests/AcDream.App.Tests/Input/C3cF2AutoEntryWiringTests.cs +++ b/tests/AcDream.App.Tests/Input/C3cF2AutoEntryWiringTests.cs @@ -1,10 +1,18 @@ +using System.Reflection; +using AcDream.App.Input; +using AcDream.App.Tests.Architecture; +using AcDream.App.World; +using AcDream.Core.Physics; +using AcDream.Core.Physics.Motion; + namespace AcDream.App.Tests.Input; /// -/// C3c-F2 (2026-08-02): source pin for the production player-mode auto-entry -/// precondition. PlayerModeAutoEntry is a ONE-SHOT that disarms before -/// invoking its callback, and the production callback completes the world -/// reveal, so an attempt made before the Runtime first-entry conductor has +/// C3c-F2 (2026-08-02): compiled architecture check for the production +/// player-mode auto-entry precondition. PlayerModeAutoEntry is a +/// ONE-SHOT that disarms before invoking its callback, and the production +/// callback completes the world reveal, so an attempt made before the Runtime +/// first-entry conductor has /// committed permanently seals the reveal with the player never in world — /// the second link of the connected-gate login wedge /// (logs/connected-world-gate-20260802-130455: one "not committed yet" line, @@ -14,45 +22,61 @@ namespace AcDream.App.Tests.Input; /// The guard's own one-shot/latch behavior is covered behaviorally by /// AcDream.Core.Tests.Input.AutoEnterPlayerModeTests; only the production /// context's dependency graph (a ~15-dependency PlayerModeController) has no -/// focused harness, hence the source pin. +/// focused harness, hence the metadata check. /// public sealed class C3cF2AutoEntryWiringTests { [Fact] public void ProductionAutoEntryRequiresTheRuntimePublishedController() { - string source = ReadSource("Input", "PlayerModeAutoEntry.cs"); + MethodInfo readiness = typeof(LivePlayerModeAutoEntryContext) + .GetProperty( + "IsPlayerControllerReady", + BindingFlags.Instance | BindingFlags.Public)! + .GetMethod!; + IReadOnlyList calls = CompiledCallGraph.Read(readiness); - Assert.DoesNotContain( - "public bool IsPlayerControllerReady => true;", - source, - StringComparison.Ordinal); Assert.Contains( - "IsRuntimePublished: true", - source, - StringComparison.Ordinal); + calls, + call => call.Target.DeclaringType == typeof(PlayerMovementController) + && call.Target.Name == "get_IsRuntimePublished"); Assert.Contains( - "record.PhysicsHost is EntityPhysicsHost", - source, - StringComparison.Ordinal); + calls, + call => call.Target.DeclaringType == typeof(LiveEntityRuntime) + && call.Target.Name == nameof(LiveEntityRuntime.TryGetRecord)); + Assert.Contains( + calls, + call => call.Target.DeclaringType == typeof(LiveEntityRecord) + && call.Target.Name == "get_PhysicsHost"); + Assert.Contains( + typeof(EntityPhysicsHost), + CompiledCallGraph.ReadTypeReferences(readiness)); } [Fact] public void PlayerPresentationAttach_DrainsMatchedAnimationsBeforeStartupMotionSuffix() { - string source = ReadSource("Input", "PlayerModeController.cs"); - - int attach = source.IndexOf( - "controller.Motion.DefaultSink =", - StringComparison.Ordinal); - int animationDrain = source.IndexOf( - "sequencer.Manager.HandleEnterWorld();", - attach, - StringComparison.Ordinal); - int unmatchedMotionDrain = source.IndexOf( - "controller.Motion.HandleExitWorld();", - animationDrain, - StringComparison.Ordinal); + MethodInfo enter = typeof(PlayerModeController).GetMethod( + "BuildControllerAndCamera", + BindingFlags.Instance | BindingFlags.NonPublic) + ?? throw new MissingMethodException( + typeof(PlayerModeController).FullName, + "BuildControllerAndCamera"); + IReadOnlyList calls = CompiledCallGraph.Read(enter); + int attach = CompiledCallGraph.IndexOf( + calls, + typeof(MotionInterpreter), + "set_DefaultSink"); + int animationDrain = CompiledCallGraph.IndexOf( + calls, + typeof(MotionTableManager), + nameof(MotionTableManager.HandleEnterWorld), + attach + 1); + int unmatchedMotionDrain = CompiledCallGraph.IndexOf( + calls, + typeof(MotionInterpreter), + nameof(MotionInterpreter.HandleExitWorld), + animationDrain + 1); Assert.True(attach >= 0, "The player animation sink was not attached."); Assert.True( @@ -62,24 +86,4 @@ public sealed class C3cF2AutoEntryWiringTests unmatchedMotionDrain > animationDrain, "Only the unmatched pre-attach interpreter suffix may drain last."); } - - private static string ReadSource(params string[] relativePath) - { - DirectoryInfo? directory = new(AppContext.BaseDirectory); - while (directory is not null) - { - if (File.Exists(Path.Combine(directory.FullName, "AcDream.slnx"))) - { - return File.ReadAllText(Path.Combine( - directory.FullName, - "src", - "AcDream.App", - Path.Combine(relativePath))); - } - - directory = directory.Parent; - } - - throw new DirectoryNotFoundException("Could not find AcDream.slnx."); - } } diff --git a/tests/AcDream.App.Tests/Physics/C3cF1ProductionWiringTests.cs b/tests/AcDream.App.Tests/Physics/C3cF1ProductionWiringTests.cs index 967c15e4..d09c89cb 100644 --- a/tests/AcDream.App.Tests/Physics/C3cF1ProductionWiringTests.cs +++ b/tests/AcDream.App.Tests/Physics/C3cF1ProductionWiringTests.cs @@ -1,13 +1,15 @@ -using System.Text.RegularExpressions; +using AcDream.App.Tests.Architecture; +using AcDream.App.Physics; +using AcDream.Runtime.Gameplay; namespace AcDream.App.Tests.Physics; /// -/// C3c-F1 (2026-08-02): source pins for the App-side halves of the -/// movement-owner application seams whose production graphs have no +/// C3c-F1 (2026-08-02): compiled architecture checks for the App-side halves +/// of the movement-owner application seams whose production graphs have no /// focused harness (the inbound network-update controller's dependency /// set is composition-only). The Runtime lifecycle matrices carry the -/// behavioral coverage; these pins keep the App wiring routed through the +/// behavioral coverage; these checks keep the App wiring routed through the /// owner's typed entries instead of the throwing direct mutations that /// crashed the connected lifecycle gate twice /// (logs/connected-world-gate-20260802-122749 — SetCharacterSkills; @@ -18,36 +20,17 @@ public sealed class C3cF1ProductionWiringTests [Fact] public void LocalPlayerInboundSetState_RoutesThroughTheTypedOwnerEntry() { - string source = ReadSource( - "Physics", - "LiveEntityNetworkUpdateController.cs"); + IReadOnlyList calls = CompiledCallGraph.ReadDeclared( + typeof(LiveEntityNetworkUpdateController)); Assert.Single( - Regex.Matches(source, @"ApplyServerPhysicsState\(") - .Cast()); + calls, + call => + call.Target.DeclaringType == typeof(PlayerMovementController) + && call.Target.Name == "ApplyServerPhysicsState"); Assert.DoesNotContain( - ".ApplyPhysicsState(", - source, - StringComparison.Ordinal); - } - - private static string ReadSource(params string[] relativePath) - { - DirectoryInfo? directory = new(AppContext.BaseDirectory); - while (directory is not null) - { - if (File.Exists(Path.Combine(directory.FullName, "AcDream.slnx"))) - { - return File.ReadAllText(Path.Combine( - directory.FullName, - "src", - "AcDream.App", - Path.Combine(relativePath))); - } - - directory = directory.Parent; - } - - throw new DirectoryNotFoundException("Could not find AcDream.slnx."); + calls, + call => call.Target.DeclaringType == typeof(PlayerMovementController) + && call.Target.Name == nameof(PlayerMovementController.ApplyPhysicsState)); } } diff --git a/tests/AcDream.App.Tests/Physics/Issue270ProductionWiringTests.cs b/tests/AcDream.App.Tests/Physics/Issue270ProductionWiringTests.cs index b83fa9d1..2dc7ec95 100644 --- a/tests/AcDream.App.Tests/Physics/Issue270ProductionWiringTests.cs +++ b/tests/AcDream.App.Tests/Physics/Issue270ProductionWiringTests.cs @@ -1,4 +1,9 @@ -using System.Text.RegularExpressions; +using System.Reflection; +using AcDream.App.Net; +using AcDream.App.Physics; +using AcDream.App.Tests.Architecture; +using AcDream.Core.Physics; +using AcDream.Runtime.Gameplay; namespace AcDream.App.Tests.Physics; @@ -13,77 +18,98 @@ public sealed class Issue270ProductionWiringTests // the factory's deleted ApplyMovementStats body into // LiveMovementStatsApplier, which routes through the Runtime // movement owner's typed seam instead of touching the controller. - string applier = ReadSource("Net", "LiveMovementStatsApplier.cs"); - string factory = ReadSource("Net", "LiveSessionRuntimeFactory.cs"); + _ = Assert.Single( + typeof(LiveMovementStatsApplier).GetFields( + BindingFlags.Instance | BindingFlags.NonPublic), + field => field.FieldType == typeof(StaminaExhaustionEdgeTracker)); - Assert.Contains( - "_staminaExhaustion.Observe(snapshot.CurrentStamina)", - applier, - StringComparison.Ordinal); + IReadOnlyList applierCalls = + CompiledCallGraph.ReadDeclared(typeof(LiveMovementStatsApplier)); Assert.Single( - Regex.Matches( - applier, - @"_movement\.ReportExhaustion\(\);") - .Cast()); - Assert.Contains( - "_staminaExhaustion.Reset();", - applier, - StringComparison.Ordinal); - Assert.Contains( - "_movementStats.Reset();", - factory, - StringComparison.Ordinal); + applierCalls, + call => + call.Target.DeclaringType == typeof(StaminaExhaustionEdgeTracker) + && call.Target.Name == nameof(StaminaExhaustionEdgeTracker.Observe)); + Assert.Single( + applierCalls, + call => + call.Target.DeclaringType + == typeof(RuntimeLocalPlayerMovementState) + && call.Target.Name == nameof( + RuntimeLocalPlayerMovementState.ReportExhaustion)); + Assert.Single( + applierCalls, + call => + call.Target.DeclaringType == typeof(StaminaExhaustionEdgeTracker) + && call.Target.Name == nameof(StaminaExhaustionEdgeTracker.Reset)); + + IReadOnlyList factoryCalls = + CompiledCallGraph.ReadDeclared(typeof(LiveSessionRuntimeFactory)); + Assert.Single( + factoryCalls, + call => + call.Target.DeclaringType == typeof(LiveMovementStatsApplier) + && call.Target.Name == nameof(LiveMovementStatsApplier.Reset)); // The factory keeps zero direct controller mutations: both stat // callbacks route through the applier's seam. Assert.Equal( 2, - Regex.Matches(factory, @"_movementStats\.Apply\(").Count); + factoryCalls.Count(call => + call.Target.DeclaringType == typeof(LiveMovementStatsApplier) + && call.Target.Name == nameof(LiveMovementStatsApplier.Apply))); Assert.DoesNotContain( - "Motion.ReportExhaustion", - factory, - StringComparison.Ordinal); + factoryCalls, + call => call.Target.DeclaringType == typeof(MotionInterpreter) + && call.Target.Name == nameof(MotionInterpreter.ReportExhaustion)); } [Fact] public void RemoteSpawnSettle_IsRetriedAndCoversBothCreationRoutes() { - string source = ReadSource( - "Physics", - "LiveEntityNetworkUpdateController.cs"); + const BindingFlags flags = BindingFlags.Instance + | BindingFlags.Static + | BindingFlags.Public + | BindingFlags.NonPublic + | BindingFlags.DeclaredOnly; + MethodInfo[] callers = typeof(LiveEntityNetworkUpdateController) + .GetMethods(flags) + .Where(method => method.GetMethodBody() is not null) + .Where(method => CompiledCallGraph.Read(method).Any(call => + call.Target.DeclaringType + == typeof(LiveEntityNetworkUpdateController) + && call.Target.Name == "SeedRemoteSpawnPlacement")) + .ToArray(); - Assert.Contains( - "if (!remote.Body.InContact)", - source, - StringComparison.Ordinal); + Assert.Equal(2, callers.Length); Assert.Equal( - 3, - Regex.Matches(source, @"SeedRemoteSpawnPlacement\(").Count); + ["DispatchRemoteInboundMotion", "OnPosition"], + callers.Select(method => method.Name).Order().ToArray()); + MethodInfo retryingInboundRoute = Assert.Single( + callers, + method => method.Name == "DispatchRemoteInboundMotion"); + IReadOnlyList retryCalls = + CompiledCallGraph.Read(retryingInboundRoute); + int contact = CompiledCallGraph.IndexOf( + retryCalls, + typeof(PhysicsBody), + "get_InContact"); + int settle = CompiledCallGraph.IndexOf( + retryCalls, + typeof(LiveEntityNetworkUpdateController), + "SeedRemoteSpawnPlacement"); + Assert.True(contact >= 0 && settle > contact); // C3c-F5: the settle helper moved to Core (SpawnPlacementSettler) so // the local player's Runtime first-entry activation shares the same // tested compressed-first-gravity-frame sweep. + MethodInfo seed = typeof(LiveEntityNetworkUpdateController).GetMethod( + "SeedRemoteSpawnPlacement", + flags) + ?? throw new MissingMethodException( + typeof(LiveEntityNetworkUpdateController).FullName, + "SeedRemoteSpawnPlacement"); Assert.Contains( - "SpawnPlacementSettler.TrySettle(", - source, - StringComparison.Ordinal); - } - - private static string ReadSource(params string[] relativePath) - { - DirectoryInfo? directory = new(AppContext.BaseDirectory); - while (directory is not null) - { - if (File.Exists(Path.Combine(directory.FullName, "AcDream.slnx"))) - { - return File.ReadAllText(Path.Combine( - directory.FullName, - "src", - "AcDream.App", - Path.Combine(relativePath))); - } - - directory = directory.Parent; - } - - throw new DirectoryNotFoundException("Could not find AcDream.slnx."); + CompiledCallGraph.Read(seed), + call => call.Target.DeclaringType == typeof(SpawnPlacementSettler) + && call.Target.Name == nameof(SpawnPlacementSettler.TrySettle)); } } diff --git a/tests/AcDream.App.Tests/Physics/LiveEntityNetworkBranchRoutingTests.cs b/tests/AcDream.App.Tests/Physics/LiveEntityNetworkBranchRoutingTests.cs index 9d235319..29dfa9c5 100644 --- a/tests/AcDream.App.Tests/Physics/LiveEntityNetworkBranchRoutingTests.cs +++ b/tests/AcDream.App.Tests/Physics/LiveEntityNetworkBranchRoutingTests.cs @@ -1,5 +1,9 @@ -using System.Text.RegularExpressions; +using System.Reflection; +using System.Reflection.Emit; +using AcDream.App.Tests.Architecture; using AcDream.App.Physics; +using AcDream.Core.World; +using AcDream.Runtime.Session; namespace AcDream.App.Tests.Physics; @@ -57,14 +61,14 @@ public sealed class LiveEntityNetworkBranchRoutingTests // there: tests/AcDream.Runtime.Tests/Session/RuntimeAcceptedPositionDriveControllerTests.cs. /// - /// R8 review fix (2026-08-03): source pins for the generic-tail + /// R8 review fix (2026-08-03): assembly checks for the generic-tail /// double-write guard in LiveEntityNetworkUpdateController.OnPosition. /// A full behavioral fixture is impractical here for the SAME reason /// C3cF1ProductionWiringTests gives — the controller's dependency /// set is composition-only (67+ collaborators wired only by /// SessionPlayerComposition) — so this follows that file's exact - /// established pattern: assert the STRUCTURE of the production source - /// rather than construct the class. The Runtime-level behavioral + /// established pattern: assert the compiled call/return structure rather + /// than construct the class. The Runtime-level behavioral /// coverage for the seam itself lives in /// RuntimeAcceptedPositionDriveControllerTests; these pins are what stop /// this App-layer call site from silently reintroducing the retired @@ -74,122 +78,56 @@ public sealed class LiveEntityNetworkBranchRoutingTests /// public sealed class LiveEntityNetworkUpdateControllerForcePositionWiringTests { - [Fact] - public void LocalForcePositionTransactionIsNeverCalledFromThisFile() - { - string source = ReadSource("LiveEntityNetworkUpdateController.cs"); - - // The name may still appear in a comment explaining what - // replaced it (contract §"the deleted LocalForcePositionTransaction"); - // what must be gone is any actual call into it. - Assert.DoesNotContain( - "LocalForcePositionTransaction.Apply(", - source, - StringComparison.Ordinal); - } - [Fact] public void GenericTailWriteIsNeverDuplicatedForTheLocalForcePositionPath() { - string source = ReadSource("LiveEntityNetworkUpdateController.cs"); - // The generic render-tail's WorldEntity write is the ONE // remaining writer of an accepted Position — it must serve // remotes only, never a second local-player write alongside the // Runtime-committed one. + MethodInfo genericTail = typeof(LiveEntityNetworkUpdateController) + .GetMethod( + "TryApplyGenericRemoteRenderPose", + BindingFlags.Static | BindingFlags.NonPublic) + ?? throw new MissingMethodException( + typeof(LiveEntityNetworkUpdateController).FullName, + "TryApplyGenericRemoteRenderPose"); Assert.Single( - Regex.Matches(source, @"entity\.SetPosition\(worldPos\);") - .Cast()); + CompiledCallGraph.Read(genericTail), + call => call.Target.DeclaringType == typeof(WorldEntity) + && call.Target.Name == nameof(WorldEntity.SetPosition)); } [Fact] public void CommittedOrDeferredCellReturnsBeforeReachingTheGenericTail() { - string source = ReadSource("LiveEntityNetworkUpdateController.cs"); - // The Committed/DeferredCell branch must still return // immediately after its two preserved side effects — a missing // `return` here would fall through into the generic tail below // and resurrect the double-write. - Assert.Matches( - new Regex( - @"ObserveAcceptedLocalPosition\(\s*" - + @"update\.Position\.LandblockId\);\s*return;", - RegexOptions.Singleline), - source); - } + MethodInfo onPosition = typeof(LiveEntityNetworkUpdateController) + .GetMethod( + "OnPosition", + BindingFlags.Instance | BindingFlags.Public) + ?? throw new MissingMethodException( + typeof(LiveEntityNetworkUpdateController).FullName, + "OnPosition"); + IReadOnlyList calls = CompiledCallGraph.Read(onPosition); + CompiledCall drive = Assert.Single(calls, call => + call.Target.DeclaringType + == typeof(RuntimeAcceptedPositionDriveController) + && call.Target.Name == "TryExecuteAcceptedLocalPosition"); + CompiledCall observe = calls.First(call => + call.Offset > drive.Offset + && call.Target.Name == "ObserveAcceptedLocalPosition"); + IReadOnlyList instructions = + CompiledCallGraph.ReadInstructions(onPosition); + int observeInstruction = instructions + .Select((instruction, index) => (instruction, index)) + .Single(pair => pair.instruction.Offset == observe.Offset) + .index; - /// - /// AP-140 (retired 2026-08-04): the accepted-Position routing gate - /// for the free-flight/landing decision must select the hard snap on - /// retail's CONTACT predicate - /// (InterpolationManager::adjust_offset @0x00555D30 gates its - /// whole body on transient_state & 1 @0x00555D52, and bit 0 - /// is CONTACT_TS), not on the client Airborne flag, - /// which is !Body.OnWalkable — WALKABILITY, a strictly wider - /// set that also captures a remote sliding on a steep face. - /// - /// - /// C4 route 4b-3's OnPosition collapse (2026-08-04) dissolved - /// OnPosition's former standalone player-remote LANDING - /// TRANSITION block — which used to carry its own - /// if (!rmState.Body.InContact) copy of this gate — into - /// ApplyRemoteContactRouting's free-flight carve-out, now the - /// ONE site (for every guid) that decides this. The gate itself did - /// not move in spirit, only in address: this pin follows it there. - /// - /// - /// - /// A source pin rather than a behavioural fixture for the reason this - /// class already documents: the controller's dependency set is - /// composition-only. The gate inside ApplyRemoteContactRouting - /// — a static method, so reachable — IS covered behaviourally, in - /// LiveEntityNetworkRemoteSteadyStateIntegrationTests. Restore - /// if (rmState.Airborne) or if (remote.Airborne) here - /// and this test fails. - /// - /// - [Fact] - public void PlayerRemoteLandingSnapSelectsOnContactNotOnWalkability() - { - string source = ReadSource("LiveEntityNetworkUpdateController.cs"); - - Assert.Contains( - "if (!remote.Body.InContact)", - source, - StringComparison.Ordinal); - // `rmState.Airborne` survives as a WRITE target and in prose (the - // block's own comment explains why it is deliberately not cleared - // there); what must never come back is reading it as the gate. - Assert.DoesNotContain( - "if (rmState.Airborne)", - source, - StringComparison.Ordinal); - Assert.DoesNotContain( - "if (remote.Airborne)", - source, - StringComparison.Ordinal); - } - - private static string ReadSource(string fileName) - { - DirectoryInfo? directory = new(AppContext.BaseDirectory); - while (directory is not null) - { - if (File.Exists(Path.Combine(directory.FullName, "AcDream.slnx"))) - { - return File.ReadAllText(Path.Combine( - directory.FullName, - "src", - "AcDream.App", - "Physics", - fileName)); - } - - directory = directory.Parent; - } - - throw new DirectoryNotFoundException("Could not find AcDream.slnx."); + Assert.Equal(OpCodes.Ret, instructions[observeInstruction + 1].OpCode); } } }