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