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>
83 lines
4.3 KiB
Markdown
83 lines
4.3 KiB
Markdown
# 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 272300–272340 + 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.
|