acdream/docs/research/2026-08-07-s4b-tangent-rest-contract.md
Erik e1beb7d31f docs: S4b contract — port the retail resting pair (tangent walkable rest + bare push-out + AD-69)
The suspect line is pinned at scoping: our ValidateWalkable measures the
sphere's VERTICAL bottom against the plane (planted rest, perp = r*N.z)
while our AdjustSphereToPlane is already a faithful tangent port — we
mix the two geometries today and the planted one wins on terrain. D0
byte-pins retail's validate_walkable distance basis with an explicit
STOP if it refutes the premise; D2 retests the #341 harness flip ten
ways under the mechanism's stability prediction; the gate teaches the
user that slightly hovering feet on steep slopes is the CORRECT retail
look. Queued behind S1B and S2 — one physics slice in flight at a time.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 07:21:07 +02:00

5.3 KiB
Raw Permalink Blame History

S4b contract (Campaign S) — port the retail resting pair: tangent walkable rest + bare-radius push-out

Date: 2026-08-07. User decision, same day: "port the retail pair." Implementer: one Sonnet agent, AFTER S1B and S2 have landed (one physics slice in flight at a time; build slots). Review: dual Opus. Gate: the user's eyes on a slope — feet are EXPECTED to hover slightly (authentic retail): ~3 cm at 30°, up to ~20 cm near the walkable limit.

The mechanism, settled by measurement + two decompilers (#341)

Retail's walkable placement (CPolygon::adjust_sphere_to_plane @0x00538210, BN pseudo-C 322032 + Ghidra cross-confirm) solves t = (dist ∓ r) / dot(N, stepDir) — the sphere rests TANGENT to the slope, perpendicular distance = radius. Its adjust_offset push-out uses the BARE radius (AD-66's byte evidence) and is therefore structurally inert at rest. acdream rests PLANTED (perp = r·N.z) and its substituted r·N.z trigger is inert here by the same algebra. The user's live capture: bare trigger vs our rest would fire on 84% of grounded slope ticks (2,471/2,955), lifts 288 mm. The pair ports together or not at all.

The suspect line, pinned at scoping

Transition.ValidateWalkable (TransitionTypes.cs ~3652) computes lowPoint = sphereCenter (0,0,radius) — the sphere's VERTICAL bottom — and dist = dot(lowPoint, N) + D + waterDepth, pushing up by dist/N.z until the FEET clear the plane. Resting identity: dot(center,N)+D = r·N.z. Meanwhile our AdjustSphereToPlane (BSPQuery.cs:364 and its FlatBspQuery twin) is ALREADY a faithful tangent port of @0x00538210 — so acdream today mixes tangent (BSP walk solve) with planted (ValidateWalkable) and the planted one wins the resting height on terrain.

Deliverables, in order

D0 — byte-pin retail's OBJECTINFO::validate_walkable FIRST. Grep the named pseudo-C; disassemble the distance basis from the PDB-paired binary if BN is ambiguous (the flag-idiom artifact class). The ONE question: does retail measure the perpendicular sphere clearance (dot(center,N)+D radius — tangent semantics) or a vertical foot point? Cross-check ACE's ObjectInfo.ValidateWalkable. Write the pseudocode doc. If retail's validate_walkable turns out to ALSO use a vertical foot point, STOP — the whole slice premise changes and the session lead re-decides. The waterDepth term's exact placement in retail's expression must be pinned too (AP-10's sink-in behaviour must survive byte-for-byte).

D1 — port the pair, one commit:

  1. ValidateWalkable's distance basis → retail's (per D0's pin).
  2. AdjustOffset's push-out → bare radius in trigger AND numerator (the exact diff that was withheld — its two [Skip]-ed conformance tests in S4AdjustOffsetConformanceTests un-skip and must pass unchanged).
  3. AD-69 rides along: the same block's dist gains retail's LandDefs::get_block_offset cell-relative correction (the plane's cell frame vs the mover's), per that row's citations.

D2 — the #341 harness anomaly retest. With the pair in, run RuntimeRemoteUphillProgressTests TEN times (mixed single/class filters, one full bin/obj clean-room among them) and report every outcome. The mechanism predicts the absorbed-tick latch becomes STABLE at the tangent rest (no lift — the settle leaves perp = r, bare trigger inert). If the flip persists, capture per-tick positions inside the test (temporary prints) and report the matrix — do NOT recalibrate goldens on a flipping measurement.

D3 — conformance + regression:

  • Exact-value tests for the new resting identity on a tilted plane (perp = r), the flat-ground case (unchanged: N.z=1 makes planted and tangent identical — this is why the suite never saw the difference), and the water sink-in cases (AP-10's 0.1/0.45/0.9 depths, unchanged behaviourally per D0's waterDepth pin).
  • The uphill no-flap guard (Uphill_NoContactFlapAcrossTicks) must stay green — the mechanism predicts it does (tangent rest + bare trigger = retail's own stable pair).
  • Sabotage: revert ONLY the ValidateWalkable half — the un-skipped AD-66 exact-value tests stay green but the new resting-identity test reds AND a re-run of the user's capture analysis script over a synthetic planted rest shows the 84% fire pattern returning. Report verbatim.

D4 — bookkeeping (session lead does the register/ISSUES edits): AD-66 retires with the pair; AD-69 retires; #341 closes; the AD-65 register retirement gains a sibling note.

Scope — OUT

The sliding/crease arm, the #331 absorb semantics (crease arm — untouched by this pair), any scaling change (AP-156's question is separate), indoor EnvCell walkable paths beyond what D0 proves shares the same validate_walkable, S1B's files.

Acceptance

Full suite clean-room green (all bin/obj deleted first — this family has burned three verdicts on stale artifacts); D2's ten-run matrix reported; nothing committed. Absolute paths, no subagents, contradictions → STOP.

The gate (user, ~3 min)

Same slope run as this morning's G1, plus: stand still on a steep slope and LOOK DOWN — feet slightly above the surface is CORRECT (retail's look). Downhill speed must remain the post-AD-65 feel. Any per-tick jitter, bouncing, or Falling-animation flicker on slopes = FAIL, revert the slice.