From 2edfc704674d6ac0df6866f343d5d18b9141cb8e Mon Sep 17 00:00:00 2001 From: Erik Date: Fri, 7 Aug 2026 00:59:09 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20S4=20contract=20=E2=80=94=20AdjustOffse?= =?UTF-8?q?t's=20AD-65/AD-66,=20snap=5Fto=5Fplane=20pinned=20from=20the=20?= =?UTF-8?q?binary?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../2026-08-07-s4-adjustoffset-contract.md | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 docs/research/2026-08-07-s4-adjustoffset-contract.md diff --git a/docs/research/2026-08-07-s4-adjustoffset-contract.md b/docs/research/2026-08-07-s4-adjustoffset-contract.md new file mode 100644 index 00000000..8b9c8783 --- /dev/null +++ b/docs/research/2026-08-07-s4-adjustoffset-contract.md @@ -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 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.