docs: S4 contract — AdjustOffset's AD-65/AD-66, snap_to_plane pinned from the binary

snap_to_plane @0x00509c50 semantics extracted at scoping: XY preserved,
Z re-solved so the offset lies in the plane, no-op under the 0.0002
|N.z| epsilon — versus our orthogonal projection, which is exactly the
cos-squared downhill shortfall AD-65 recorded. Branch polarity pinned
from the test ah,0x41 idiom at 0x0050a4fa: into-plane subtracts,
away-from-plane snaps.

AD-66's port carries a mandatory regression scenario: the original
substitution was empirically motivated (uphill contact-flap), so the
contract requires that exact scenario as a test and a full STOP if the
faithful port genuinely reds it — re-filing the divergence as deliberate
is the session lead's call, not the implementer's tune.

Priority note: S4 jumps ahead of S1B in the overnight queue on felt
value — 25-50% downhill XY shortfall is daily-feel, while S1B's
over-inclusion is zero-felt fidelity. The campaign's
membership-before-query ordering is about masking, and an over-inclusive
residual masks nothing downstream.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-07 00:59:09 +02:00
parent 52aea775b9
commit 2edfc70467

View file

@ -0,0 +1,83 @@
# S4 contract (Campaign S) — AdjustOffset's two substitutions: AD-65 + AD-66
**Date:** 2026-08-07 (overnight). **Scoped by:** the session lead.
**Implementer:** one Sonnet agent. **Review:** dual Opus. **This is FEEL
work** — the morning slope-feel gate covers it; nothing here can be
visually accepted overnight.
Both rows live in `Transition.AdjustOffset`
(`src/AcDream.Core/Physics/TransitionTypes.cs`), retail
`CTransition::adjust_offset` @0x0050a370. One function, one slice.
## AD-65 — the away-from-plane arm must SNAP, not project
**Retail, pinned at scoping (pseudo-C 272300272340 + 271852):** in the
non-sliding path (`sliding_normal` not set), retail computes
`arg3 = dot(offset, contact_plane.N)` and branches on it @0x0050a4fa with
`test ah,0x41`:
- `arg3 <= 0` (moving INTO the plane): `offset -= N * arg3` — the
component subtraction (block @0x0050a529).
- `arg3 > 0` (moving AWAY): `Plane::snap_to_plane(&contact_plane, &offset)`
@0x0050a50e. Semantics @0x00509c50, verbatim: if `|N.z| <= 0.000199999995f`
do NOTHING; else `offset.z = -(offset.x*N.x + offset.y*N.y) / N.z`
(the `d` terms cancel algebraically — show this in your pseudocode doc).
**XY is PRESERVED; only Z is re-solved so the offset lies in the plane.**
acdream's `else` arm (the one whose comment already names snap_to_plane
without calling it) does `result -= N * collisionAngle` in BOTH directions —
an orthogonal projection that shrinks XY by the cos²θ factor AD-65 recorded
(25% at 30°, 50% at 45°). Port the branch exactly: subtraction arm for
`<= 0`, snap for `> 0`, including the |N.z| epsilon no-op.
**Watch item carried from #32 (read before coding):** the #32 fix narrowed
last-known-contact-plane validity, and `AdjustOffset` consumers sit in its
blast radius (research doc §3.5). Your tests must cover the case where the
contact plane is the ONLY valid plane and where none is valid.
## AD-66 — the safety push-out uses the BARE radius
Byte evidence already in the register row (do not re-derive, but DO read the
row): retail loads the bare `global_sphere->radius` for BOTH the trigger
comparison (`0050a5c4 fld [ecx+0xc]`, then subtracts F_EPSILON) and the
`zDist` numerator (`0050a5dc fsubr [ecx+0xc]`), dividing by
`contact_plane.N.z` (`0050a5df fdiv [esi+8]`). Neither site multiplies by
N.z. acdream substitutes `naturalRestingDist = radius * ContactPlane.Normal.Z`
in both places, with a long code comment arguing the sphere-origin geometry.
**The comment's argument may be geometrically sincere, but the register's
posture is decided: retail-faithful first.** Port the bare radius in both
sites. THEN — because the original substitution was empirically motivated
("the uncorrected threshold broke ValidateWalkable's contact check on steep
slopes and flickered the Falling animation while running uphill") — your
test set MUST include the scenario the old comment claims regresses: a mover
running uphill on a steep-but-walkable slope, asserting no contact-flap
across ticks. If that test genuinely reds with the faithful port, STOP —
do not tune, do not blend. Report the failing scenario in full; the session
lead decides (that outcome would mean the register row's risk column was
right and the divergence may be re-filed as deliberate rather than fixed).
## Deliverables
1. Pseudocode doc first (`docs/research/2026-08-07-s4-pseudocode.md`) for
`adjust_offset`'s full branch tree — including the sliding-normal arm
@0x0050a42a (cross products + normalize_check_small) which you must VERIFY
against our existing port and report on, but not change unless it
diverges.
2. The two fixes above, minimal diff, comments carrying the addresses.
3. Conformance tests: exact-value tests for snap (XY preserved, Z re-solved,
epsilon no-op), for the into-plane subtraction, for bare-radius trigger +
numerator; the uphill no-flap scenario; sabotage-verify the snap test by
re-instating the projection and watching it redden.
4. Full suite `dotnet test AcDream.slnx -c Release -m:1` green, totals
reported; nothing committed.
## Scope — OUT
Registers/ISSUES (session lead). `calc_friction` (S5, separate). Anything
outside `AdjustOffset` and its tests.
## Process
Only `C:\Users\erikn\source\repos\acdream`, absolute paths, no subagents.
Contradictions between this contract and the source: STOP and report.