From 2223ed174589f18739cd6db1c38ef5d0e0481f8c Mon Sep 17 00:00:00 2001 From: Erik Date: Thu, 6 Aug 2026 09:37:47 +0200 Subject: [PATCH] docs: file the uphill-resolve blockage (#331) and the headless remote-DR gap (#332) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two findings from the AD-10 work that are out of its scope and are filed rather than absorbed. Neither is caused by AD-10 and neither is affected by its deletion. #331 — PhysicsEngine.ResolveWithTransition returns ok=False and the unchanged input position for EVERY uphill step on the synthetic constant-gradient terrain ramp, while the identical downhill step succeeds and produces a correct slope-following result. Probed and ruled out: gradient (fails at 2.9 degrees as at 31), step size, cell boundaries (five start positions with the cell id recomputed), and Z seating. It is not an axis bug either — inverting the ramp so it rises along +Y makes +Y the failing direction, so the failure tracks the slope. Filed with severity UNKNOWN on purpose. Players demonstrably walk uphill in acdream and the local player runs the same call, so either production terrain differs from what the fixture publishes (AddLandblock only, no flat-collision statics) or something in the live arguments does. That was not traced, and guessing which would be exactly the kind of inference this campaign keeps getting burned by. The issue names the one probe that decides it. It surfaced because an uphill counterpart to the AD-10 tracking test was written, PASSED, and was then found vacuous — the body never moved, so it "stayed on the surface" by standing still. That test was dropped rather than shipped. Any future uphill assertion against that harness is vacuous the same way until this is resolved, which is reason enough to record it even if production is fine. #332 — Headless bots appear to have no remote dead-reckoning at all. RuntimeRemotePhysicsUpdater has exactly one production instantiation, AcDream.App/Physics/RemotePhysicsUpdater.cs:46, and src/AcDream.Headless/ never names it or RemoteMotion. Remote entities on that host would move only at UpdatePosition cadence. Filed as an observation for the headless owner to judge, adjacent to #330 but a separate mechanism. #332 also records the reasoning trap it exposes, because it inverts the C5b lesson rather than repeating it: RemoteMotionCombiner is in Core and RuntimeRemotePhysicsUpdater is in Runtime, so "therefore headless runs it" is the natural correction to C5b's graphical-only survey — and it is wrong. Assembly placement is not reachability; the instantiation census is. AD-10 designed no headless gate for exactly this reason, and a passing one would have been vacuous evidence. Co-Authored-By: Claude Opus 4.8 --- docs/ISSUES.md | 125 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/docs/ISSUES.md b/docs/ISSUES.md index 345e0987..55e0b5bd 100644 --- a/docs/ISSUES.md +++ b/docs/ISSUES.md @@ -24,6 +24,131 @@ What does NOT go here: - Every session: scan OPEN issues at start; promote/close anything we touched during the session before ending. - Promoting to a Phase: mark as `DONE (promoted to Phase X)` + commit SHA where the Phase entry landed. +## #332 — Headless bots appear to have no remote dead-reckoning at all + +**Status:** OPEN (observation, not yet established as a defect) +**Severity:** for the headless owner to judge — it depends entirely on what +headless bots are for. +**Filed:** 2026-08-06, from the AD-10 blast-radius census. +**Adjacent to #330** (headless registers no live-entity collision), but a +separate mechanism. + +### The evidence + +`RuntimeRemotePhysicsUpdater` — the per-tick owner that advances every remote +entity between server `UpdatePosition` bursts (interpolation catch-up, root +motion, gravity, the collision sweep) — has exactly ONE production +instantiation: + +``` +$ grep -rn "new RuntimeRemotePhysicsUpdater" --include=*.cs src/ tests/ +src/AcDream.App/Physics/RemotePhysicsUpdater.cs:46 <- only production site +tests/AcDream.Runtime.Tests/Physics/... <- fixtures only +``` + +`src/AcDream.Headless/` contains no reference to `RemoteMotion`, +`RemotePhysicsUpdater`, or `OrdinaryPhysicsUpdater`. The class is `internal` to +`AcDream.Runtime` and reaches production only through `InternalsVisibleTo` into +`AcDream.App`. So on the headless host remote entities would move only at +`UpdatePosition` cadence — roughly 5 Hz teleport-stride — with no interpolation +between bursts. + +### Why it is filed rather than fixed + +Whether this matters depends on what headless bots need to observe. A bot that +only reads positions from the wire may not care; one that makes decisions from +observed remote motion, or that is used as a second client in a two-client +visual gate, would. + +### The reasoning trap it exposes, worth recording on its own + +`RemoteMotionCombiner` is in `AcDream.Core` and `RuntimeRemotePhysicsUpdater` is +in `AcDream.Runtime`, so "therefore headless runs it too" is the available +inference — and it is false. This is the C5b lesson running in the opposite +direction: C5b's survey missed `AcDream.Headless` by only walking the graphical +host's call graph, and the natural correction ("check Core and Runtime, those +are shared") produces the wrong answer here. **Assembly placement is not +reachability; the instantiation census is.** AD-10's closeout deliberately +designed NO headless gate for that reason — a passing headless run would have +been vacuous evidence. + +--- + +## #331 — `ResolveWithTransition` refuses ALL uphill motion on a constant-gradient terrain ramp (fixture-or-production unresolved) + +**Status:** OPEN +**Severity:** UNKNOWN until the fixture-versus-production question below is +settled. If it reproduces on DAT terrain it is severe and affects the local +player as much as remotes; if it is confined to the synthetic fixture it is a +test-infrastructure defect that silently voids any uphill assertion written +against that fixture — which is how it was found. +**Filed:** 2026-08-06, while measuring AD-10 (commits `fe6ee877`, `886333a2`). +**Not caused by AD-10, and unaffected by its deletion** — the behaviour is +identical with the pre-sweep slope projection enabled and disabled. + +### What was measured + +Against `tests/AcDream.Runtime.Tests/Physics/RemoteRampHarness.cs` — a single +synthetic landblock whose terrain is one constant-gradient plane, published via +`PhysicsEngine.AddLandblock` with empty `CellSurface[]` and `PortalPlane[]` — +`PhysicsEngine.ResolveWithTransition` returns `ok=False` and the UNCHANGED input +position for every uphill step, while the identical downhill step returns +`ok=True` with a correct slope-following result. + +``` +from=<96, 96.11029, 57.53382> to=<96, 96.01029, 57.53382> (uphill) + => pos=<96, 96.11029, 57.53382> ok=False onWalkable=False +from=<96, 96.11029, 57.53382> to=<96, 96.21029, 57.53382> (downhill) + => pos=<96, 96.18382, 57.489704> ok=True onWalkable=True +``` + +The downhill answer is right in detail: the XY advance is 0.0735 m for a 0.1 m +request, exactly the `cos^2(theta)` shortening `Transition.AdjustOffset`'s +away-plane arm produces at this gradient (see AD-65). So terrain IS being seen +and the sweep IS projecting; only the uphill direction fails. + +Ruled out by probe, all with `sphereRadius: 0.48`, `stepUpHeight: 0.4`, +`body:` supplied and a valid walkable contact plane at tick start: + +- **Not gradient-dependent.** Fails at 0.05 (2.9 degrees) exactly as at 0.6 + (31 degrees). A 2.9-degree slope is ordinary terrain. +- **Not step-size-dependent.** Fails for a 0.1 m and a 0.5 m request. +- **Not a cell-boundary artifact.** Fails from (96,96) — which is exactly on + both cell boundaries — and equally from (100,100), (100,96), (96,100) and + (110,110) with the cell id recomputed for each. +- **Not a Z-seating artifact.** Fails with the body lifted 0.05 m off the + surface as well as seated on it. +- **Not an axis bug — it follows UPHILL.** With the ramp inverted so it rises + along +Y instead of descending, it is +Y that returns `ok=False` and -Y that + succeeds. The failing direction tracks the slope, not the coordinate. + +### Why this is not obviously a production defect + +Players demonstrably walk uphill in acdream, and the local player runs the same +`ResolveWithTransition`. So either production terrain differs from what this +fixture publishes in some way that matters, or something in the live call +arguments does. The fixture publishes terrain through `AddLandblock` only and +registers no flat-collision statics; production goes through +`LandblockPhysicsContentBuilder.PublishStaticCollision`. That difference was +NOT traced end to end and is the first thing to check. + +### How it was found, and why it matters regardless + +An uphill counterpart to the AD-10 surface-tracking test was written and +PASSED — vacuously. The body never moved, so it "stayed on the surface" by +standing still. The test was dropped rather than shipped. Any future assertion +about uphill movement written against this harness will be vacuous in the same +way until this is resolved, which is reason enough to fix or document it even +if production turns out to be fine. + +### Next step + +Re-run the same two-position probe against a real DAT-published landblock (the +bake/pak path, or a live capture with `ACDREAM_PROBE_RESOLVE=1` while walking +uphill) and compare `ok`. That single comparison decides severity. + +--- + ## #330 — The headless host registers no live-entity collision at all: a bot walks through every NPC and every server-spawned object **Status:** OPEN