Both AD-10 review lenses PASS; the deletion stands. These are the findings they raised. One production file touched, comment-only. AD-65 WAS UNDERSTATED BY HALF, and it is the finding that matters. The row states the factor as cos^2(theta) and then quantified 1-cos(theta): "13% at 30 degrees, 29% at 45". The correct figures are 25% and 50%. This is not algebra alone — #331's probe in the same push measures 0.0735 m travelled for a 0.1 m request at 30.96 degrees, i.e. 26.5% short, which is exactly cos^2(30.96). AD-65 is a LEAD for #269's slope-slide residual; at the understated magnitude it reads as marginal and could have been dismissed. At 50% short at 45 degrees it is a serious candidate. I repeated the wrong figure in conversation before the review caught it. "VERBATIM/FAITHFUL PORT" of Transition.AdjustOffset was asserted in five places and was false as of the very next commit, which filed AD-65 and AD-66 against that same function. Corrected to "structurally exact, with exactly two filed divergences" in the register row and the production doc comment. RECORDED, and it favours the change: the redundancy measurement is CONTINGENT on AD-65 — the two mechanisms agree today partly because both under-travel downhill. That makes this deletion a PREREQUISITE for fixing AD-65 rather than merely compatible with it; had the projection survived, correcting AdjustOffset would have re-introduced a disagreement between two live projections. The record claimed no such thing and should have. UNTESTED AXIS recorded: the contract's T2 — its mandatory wrong-plane-versus- right-plane discriminator — was dropped without record, breaching the contract's own clause requiring exactly that to be written down. The consequence is precise: the deletion is measured, but the change's only claimed BENEFIT (a walkable non-terrain surface now gets the committed contact plane instead of terrain far below) has zero automated coverage and rests on source reasoning. Stated in the row rather than left implied. #331 SEVERITY RAISED from UNKNOWN — the discriminator is known and it is not the fixture. With `body: null` the same uphill sweep climbs (ok=True, moved (0, -0.0999, +0.060)); with a body supplied it returns ok=False and zero movement, under a call profile identical to the local player's (IsPlayer|EdgeSlide + the human two-sphere Setup). A diagonal request keeps cross-slope X and zeroes only up-slope Y, and it fires on a 1.1 degree ramp. So "confined to the synthetic fixture" is no longer the comfortable default: the failing call shape is the shape production uses. Nothing in the suite asserts uphill progress on a walkable slope, which is why it was invisible — the test that found it passed vacuously, because the body never moved. Also: malformed XML doc on ComposeOffset (duplicate </summary> swallowed the retirement note from tooling) fixed; the placement-cutover plan's item 5 and its stale "After C5" line now record AP-22 and AD-10 as retired. Core builds clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
19 KiB
AD-10 retirement-by-deletion — architecture review
Reviewer scope: completeness, blast radius, test quality. Retail fidelity is
a separate reviewer's report (2026-08-06-ad10-review-retail.md).
Range: ef976c6d..2223ed17 on claude/acdream-physics-divergence-5aa784
(fe6ee877, 886333a2, fb454b74, 2223ed17).
Date: 2026-08-06.
Verdict: PASS, with one process defect and one under-scoped issue
The Stage 0 measurement is sound and I believe it — I re-derived both reported numbers independently and added four cases the implementer did not report. The deletion is complete, the blast radius claim is correct, all three reported sabotages reproduce with matching numerals, and the two deleted tests were genuinely obsolete.
Nothing here is a workaround, a suppression flag, a grace period, a symptom guard, a new skip, or a weakened test. No known flake was conflated.
The defects are: one mandatory contract test silently dropped (D1), a stale roadmap entry (D2), and a malformed doc comment (D3). None justifies blocking the change; D1 should be settled before the visual gate is called done.
Part 1 — Is the Stage 0 measurement sound? (priority 1)
Method
I did not take the implementer's word for it. I deleted all 44 bin/obj
directories, built Release clean (0 errors), byte-scanned
AcDream.Core.dll to confirm the string SampleTerrainNormal is absent from
the compiled metadata (0 occurrences; SampleTerrainWalkable still present at
2, proving the scan works), then drove RemoteRampHarness through the
production RuntimeRemotePhysicsUpdater.Tick for 30 ticks and dumped every
position as raw IEEE-754 bits.
I then restored the pre-deletion production files verbatim
(git checkout ef976c6d -- the three src/ files — those files changed only
in 886333a2, so this is an exact revert) and re-ran the identical dump.
Result — reproduced, and extended
| Case | gradient | root motion / tick | pre-deletion vs HEAD |
|---|---|---|---|
| downhill 31° | 0.6 | (0, +0.10, 0) | bit-identical, all 30 ticks |
| downhill 8.4° | 0.1477 | (0, +0.10, 0) | X, Y bit-identical; Z differs by 2.9e-5 m at tick 30 |
| cross-slope 31° | 0.6 | (+0.10, 0, 0) | bit-identical |
| diagonal 31° | 0.6 | (+0.0707, +0.0707, 0) | bit-identical |
| flat | 0.0 | (0, +0.10, 0) | bit-identical |
| uphill 31° | 0.6 | (0, −0.10, 0) | bit-identical — but vacuous, see L1 |
Both reported numbers land exactly. The last three rows are mine; they close the obvious "only one direction was measured" objection.
Is "bit-identical" vacuous — did neither path do anything?
No, for the downhill case. Over 30 ticks the body advances Y by 2.206 m and
descends 1.324 m. Those are not noise: they are exactly the analytically
predicted projected step. For N = (0, 0.5145, 0.8575) and a requested
(0, 0.1, 0), the projection v − N·(v·N) gives (0, 0.07353, −0.04412);
the measured per-tick advance is 0.07353 / −0.04412. The sweep alone
reproduces the deleted projection to the last digit.
The idempotency argument the commit message gives also holds in the code, not
just on paper: Transition.AdjustOffset
(src/AcDream.Core/Physics/TransitionTypes.cs:5246-5258) subtracts the full
normal component in both its collisionAngle <= 0 and > 0 arms, so a
vector already on the plane is returned unchanged. That is why the composition
collapses.
Yes, for the uphill case — and the implementer said so and refused to ship the test. That refusal was correct (see L1).
Does the fixture make it vacuous?
The concern is real and worth naming: the ramp is one constant-gradient plane
over the whole landblock, so SampleTerrainNormal(x, y) equals
Body.ContactPlane.Normal everywhere. That is precisely the case where
idempotency guarantees no change — the friendliest possible geometry for the
"redundant" claim.
But it is also the right geometry for the question actually being asked. Deletion can only regress where the two planes agree (there the old layer was doing correct work that must now come from somewhere else); where they disagree the old layer was applying a wrong plane, so removing it cannot be a regression. The measurement covers the regression-capable case exactly, and my cross-slope/diagonal/flat additions cover it in three more directions.
Verdict on Stage 0: I believe it. The redundancy claim is established for terrain. What is not established is the claimed improvement — see D1.
Part 2 — Findings
Defects
D1 — a mandatory contract test was dropped with no record (process)
docs/research/2026-08-06-ad10-contract.md:546-577 specifies T2, "the
discriminating test", on a fixture where the committed contact plane differs
from the terrain sample at the same XY. Construction (a), a two-gradient
terrain crest, is marked "Mandatory." Construction (b), an off-terrain
walkable surface, is "Required, with a documented fallback: if the fixture
cannot be built in reasonable time, say so explicitly in the closeout, ship on
(a), and record (b) as an untested axis rather than silently dropping it."
Line 690 of the same contract puts T2–T5 in the deletion commit.
Neither exists. grep -rn -i "crest|ridge|two-gradient" tests/AcDream.Runtime.Tests/
returns nothing, and no commit message, register row, or doc in the range
records the disposition of T2.
T3, T4 and T5 are defensibly moot under deletion (T3 and T4 test guards that no longer exist; T5's intent is superseded by the stronger compile-error guarantee). T2 is not moot. Under deletion it becomes the test for the one behavioural benefit the change claims — and that claim is stated as fact in two places without evidence:
docs/architecture/retail-divergence-register.md:122(the retired AD-10 row);886333a2commit message: "What deletion does improve is the case #32 never covered: a remote on a WALKABLE non-terrain surface … That surface now gets the body's own committed contact plane."
Concrete failure scenario. A remote runs along a sloped wooden bridge or a
dock over sloped terrain. After deletion the only projection is the sweep's,
which runs only when ci.ContactPlaneValid (TransitionTypes.cs:5202-5224
takes the no-contact-plane branch otherwise). If check_contact ever fails to
seed a contact plane on a tick where the body is OnWalkable, the offset is
not projected at all, where before the terrain sample supplied one. The body
holds Z between server updates — the exact ~5 Hz staircase the projection
existed to remove — and nothing in CI goes red, because every automated
assertion about this behaviour lives on a pure-terrain fixture.
I judge the residual risk low (on flat terrain under a bridge the deleted projection was a near-no-op anyway), but "low risk" is a different statement from "measured", and the contract explicitly forbade making it silently.
Ask: either build T2(a) — the harness already supports it, it is a
two-gradient heightmap in RemoteRampHarness.Ramp — or add one paragraph to
the register row recording T2 as a deliberately untested axis, per the
contract's own fallback clause.
D2 — stale roadmap entry (docs)
docs/plans/2026-04-11-roadmap.md:108 still lists
AP-22 authored object shapes, and AD-10 remote contact-plane projection.
as open campaign work. AD-10 is retired. CLAUDE.md's roadmap discipline rule 3
requires the roadmap update in the same commit as, or immediately after, the
work. The register row, both downstream docs/ISSUES.md cross-references
(:10513-10515, :10672-10676) and the historical record were all swept
correctly — the roadmap was the one miss.
D3 — malformed XML doc comment (style)
src/AcDream.Core/Physics/RemoteMotionCombiner.cs:38-53. The AD-10 retirement
rationale is written as a <para> block placed after the closing
</summary> on line 38, and terminated by a second </summary> on line 52:
38 /// </summary>
39 ///
40 /// <para><b>AD-10, retired 2026-08-06.</b> …
52 /// …unchanged.</para>
53 /// </summary> <- unmatched close tag
Two closes, one open, one orphaned <para>. The build is green only because
this project does not generate documentation files; the rationale will not
render in IntelliSense, and the file would emit CS1570 the moment
GenerateDocumentationFile is turned on. Same paragraph reads fine — just move
it inside the first </summary>.
Latent risks
L1 — #331 is real, is under-scoped, and one cheap probe moves it a long way
The issue as filed (docs/ISSUES.md:75-150) is honest and unusually thorough:
it rules out gradient, step size, cell boundaries, Z seating, and axis, and it
names the vacuous test that found it. I reproduced every one of those
exclusions. But the "fixture-versus-production" question it leaves open is
answerable more cheaply than it says, and the answer points away from the lead
the issue names.
What I measured, calling PhysicsEngine.ResolveWithTransition directly
on the harness's engine (no remote tick involved):
| call | result |
|---|---|
uphill, body: rm.Body, isOnGround: true |
ok=False, moved (0,0,0) |
uphill, body: rm.Body, isOnGround: false |
ok=False, moved (0,0,0) |
uphill, body: null |
ok=True, moved (0, −0.0999, +0.060) |
uphill, body: null, lifted 0.5 m |
ok=True, moved (0, −0.0999, 0) |
uphill, with IsPlayer|EdgeSlide + the human two-sphere Setup list |
ok=False |
uphill diagonal (0.1, −0.1, 0), body: supplied |
ok=True, moved (0.1, 0, 0) |
downhill / cross-slope / straight-up / straight-down, body: supplied |
ok=True, all correct |
Three things follow that the issue does not yet carry:
- The discriminator is the
body:parameter, i.e. the seeded contact-plane / retained-walkable-polygon path (retailcheck_contact). Without a body the same uphill sweep climbs; with one it refuses. Production always passes a body — the local player atsrc/AcDream.Runtime/Gameplay/PlayerMovementController.cs:2646, the remote atsrc/AcDream.Runtime/Physics/RuntimeRemotePhysicsUpdater.cs:424. - It is not remote-specific and not sphere-list-specific. It reproduces
under a call profile identical to the local player's, including
IsPlayer | EdgeSlideand the human Setup's two-sphere list. The issue's stated "first thing to check" — the terrain publication path (AddLandblockvsLandblockPhysicsContentBuilder.PublishStaticCollision) — is now the less likely lead of the two. - The up-slope component is clipped, not the whole step. The diagonal case
returns
ok=Trueand keeps its cross-slope X while zeroing its up-slope Y. A walkable 31° ramp is behaving like a wall for up-slope motion, and a 1.1° ramp (gradient 0.02) does the same.
Nothing in the repository proves walkable uphill progress works anywhere.
RetailEdgeResponseOrderingTests.cs:277-312 runs a downhill / uphill /
tangential theory and asserts horizontal progress for downhill and tangential
only — the uphill arm is routed to a no-bounce assertion instead. That is
correct for its unwalkable steep-roof fixture, but it means the suite has no
uphill-progress assertion to lean on.
This is not a regression from AD-10 — I confirmed the uphill trajectory is bit-identical before and after the deletion, so nothing here blocks this change. But #331's severity should not stay UNKNOWN: the trigger is now known to be a production-shaped call, and the remaining unknown is narrow enough to settle in one probe.
L2 — the surviving tests cannot see the projection, only the Z seating
Verified, and quantified. With Transition.AdjustOffset short-circuited to
return offset;:
RuntimeRemoteSlopeProjectionTests— all green, confirming the implementer's rejection of the contract's proposed T1 sabotage;- the downhill Y advance goes from 0.0735 m/tick to 0.1000 m/tick, i.e. the remote runs 36% faster downhill, and the tests do not notice;
- Z still tracks the surface exactly, because
ValidateWalkable's push-out (TransitionTypes.cs:3445-3452, reached fromFindEnvCollisions) re-seats the sphere at its natural resting distance every sub-step. The implementer's stated third mechanism is real, both structurally and empirically.
This is disclosed, at length and unprompted, in the test's own doc comment
(RuntimeRemoteSlopeProjectionTests.cs:97-113), including the instruction that
it "must not be cited as" a unit test of adjust_offset. That is the correct
handling of a non-discriminating test.
It is also not a coverage hole overall: running the full suite under that same
sabotage produces 15 failures — 14 in AcDream.Core.Tests plus
RuntimeRemoteSteepContactSlideTests.SteepContactKeepsTheBodySlidingDownhill
("expected a slide, body moved 0.0000 m"), which is the Runtime remote path's
own guard. No action required; recorded so nobody later mistakes the new file
for adjust_offset coverage.
L3 — "bit-identical" is a property of this fixture, not of the change
The 8.4° ramp differs by 2.9e-5 m in Z because dot(v', N) is not exactly
zero in float after the first projection. The 31° case lands bit-identical; a
third gradient might not. The commit message already reports both numbers and
calls the difference float ordering noise, which is the honest framing. Nobody
should generalise "bit-identical" into a guarantee.
Part 3 — What I checked and found clean (so the PASS is auditable)
Deletion completeness (priority 2). Three independent sweeps:
SampleTerrainNormal (zero code references — only comments and docs),
terrainNormal case-insensitive (same), and the formula shape
* Vector3.Dot( / -= N * across all of src/ (only an unrelated
Vfx/ParticleSystem.cs:1245 axis projection). No fourth copy, no dead
diagnostic, no test reference. Both fork branches carried the block verbatim
pre-deletion (:272-282 and :317-325) and both are gone — the AP-22 shape was
handled. SampleTerrainWalkable, the internal the deleted wrapper called, is
not orphaned: it retains live callers at TransitionTypes.cs:2909 and
:3432. ComputeOffset confirmed production-dead — the only callers are in
tests/AcDream.Core.Tests/. Removing the parameter rather than passing null
does make a one-site reintroduction a compile error, as claimed.
Sabotage reproduction (priority 3). All three reproduce, from clean builds:
| sabotage | reported | reproduced |
|---|---|---|
rm.Body.Position = postIntegratePos (:457) |
RED at tick 1, 0.05999 m off surface | RED at tick 1, "body root sits 0.05999 m above the terrain under it" |
| flatten ramp to gradient 0 | RED on the anti-vacuity guard, dz = 0.0000 |
RED, "fixture is not exercising slope descent: dz = 0.0000 m" |
short-circuit Transition.AdjustOffset |
GREEN | GREEN (see L2) |
The anti-vacuity guard is real and load-bearing: note that the other fixture
test, TheFixtureRampIsWalkableAndItsPlaneIsTheGeometricOne, passes happily on
a flattened ramp (RampNormal(0) is (0,0,1) and so is the flat contact
plane), so the guard is the only thing standing between this file and a
silently-flattened fixture.
Deleted tests (priority 4). Both were genuinely obsolete.
..._SlopedTerrainNormal_ProjectsZOntoSlope asserted only the deleted formula,
against expected values re-derived from that same formula in a comment — it
could catch a wrong multiply but never a wrong plane, exactly as the commit
message says. ..._FlatTerrainNormal_NoZChange is a literal duplicate of the
surviving ComputeOffset_AnimationOnly_Forward_BodyAdvances
(RemoteMotionCombinerTests.cs:51-68): same dt: 0.1, same
rootMotionLocalDelta: (0, 0.4, 0), same identity orientation, same asserted
(0, 0.4, 0). Nothing was asserted by either test that is not still asserted.
The tombstone comment left in their place names both and points at the
replacement.
Harness extraction (fe6ee877). Filtering the diff of
RuntimeRemoteSteepContactSlideTests.cs to Assert / [Fact] / [Theory]
lines yields zero added or removed assertions — the extraction moved the
private nested Harness out verbatim and nothing else. Its start position
constants are unchanged (96, 96). All ten Bug B tests still pass.
Blast radius (priority 6). Confirmed exactly as stated.
new RuntimeRemotePhysicsUpdater( has one production site,
src/AcDream.App/Physics/RemotePhysicsUpdater.cs:46; every other match is a
test fixture. src/AcDream.Headless/ contains no reference to
RuntimeRemotePhysicsUpdater, RemotePhysicsUpdater, RemoteMotion or
RemoteMotionCombiner. The class is internal to AcDream.Runtime and reaches
production only via InternalsVisibleTo into AcDream.App
(AcDream.Runtime.csproj:12). RemoteMotionCombiner lives in AcDream.Core
and is instantiated in RemoteMotion.cs:242, but the only things that drive
it are the three ComposeOffset sites in RuntimeRemotePhysicsUpdater. The
inverse of C5b's lesson does apply here and the implementer got it right:
assembly placement is not reachability, and a green headless gate would have
been vacuous evidence. #332 records that reasoning correctly.
TickHidden (priority 7). Correct to leave untouched. I byte-compared the
call at RuntimeRemotePhysicsUpdater.cs:940-948 against ef976c6d: it is
character-for-character identical, and it used the named argument
inContact: while skipping the optional terrainNormal, so the hidden-remote
path always received null. Deleting the parameter is a no-op there by
construction.
Gates. From a state with all 44 bin/obj directories deleted:
Release build 0 errors; full solution suite 11,196 passed / 4 skipped /
0 failed, matching the claim exactly and reconciling with the stated
11,195 / 4 / 0 baseline as +3 new Runtime [Fact]s and −2 deleted Core tests.
Run twice — once before my sabotage experiments and once after restoring the
tree — with identical totals. Skip count unchanged at 4 (3 App, 1 Core); no
skip was added.
Tree state. git status --porcelain and git diff HEAD are clean. The one
untracked file, docs/research/2026-08-06-ad10-review-retail.md, belongs to the
concurrent retail-fidelity reviewer and was not touched. Every temporary
sabotage and probe file I introduced was reverted or deleted and verified gone.
Rule compliance. No workaround, no suppression flag, no grace period, no
if (badState) return, no swallowed exception, no widened tolerance, no new
skip. The 5 mm tolerance in the new tracking test is justified in its own doc
comment against both the failure it must catch (~1.8 m) and the noise it must
tolerate (<1e-4 m), and I confirmed the ratio by measurement. Known flakes
#302 / #308 / #321 were not touched or conflated. AD-65 and AD-66 were filed as
separate register rows rather than folded into this change, which is the right
call — both change local-player feel and need their own gate.