diff --git a/docs/research/2026-08-08-345-mechanism-contract.md b/docs/research/2026-08-08-345-mechanism-contract.md new file mode 100644 index 00000000..69bd359f --- /dev/null +++ b/docs/research/2026-08-08-345-mechanism-contract.md @@ -0,0 +1,60 @@ +# #345 mechanism-session contract — who eats angled input on the steep-slope approach? + +**Date:** 2026-08-08. **Order:** after #344 lands. **This is an +instrumentation session, not a fix session** — the fix contract gets written +from the capture, not before. + +## Evidence in hand (do not re-derive) + +- `uphill-45deg-capture.jsonl`: 838 uniform stuck ticks — request ~0.23 m/tick + at 45° to the face, **result byte-identical to input position**, + `collisionNormalValid=True` with **`collN=(0,0,1)`** (straight up), + `slidingNormal=(0,0,0)`, carried contact = the ~6.8° approach terrain + `(-0.083,0.083,0.993)`, `transient=0x3` (Contact|OnWalkable). +- `uphill-slide-capture.log`: the steep face itself was reached only 18 + times; every reach took branch2 cliff-slide with a healthy applied + constraint. The eater is UPSTREAM of the face. +- `uphill-AB-precampaign.jsonl`: identical 0.0%-yield signature on the + pre-campaign binary — **pre-existing**, Campaign S exonerated. +- Ruled out by measurement: the #331 sliding absorb (no latch), cliff-slide + degeneracy (#32 fixed, visibly healthy), all Campaign S landings. + +## The one probe this session builds + +A per-tick TRANSITION PHASE TRACE, `ACDREAM_DUMP_TRANSIT_FAIL=1`, firing ONLY +when a resolve returns zero XY displacement against a nonzero XY request +(the stuck-tick predicate — self-selecting, so a normal session prints +nothing). For each such tick, one line each for: +1. every `TransitionalInsert` attempt: attempt #, phase reached + (env/building/objects), each phase's TransitionState, and on Collided the + colliding polygon's plane normal + which BSP/terrain source produced it; +2. every step-up entry and its step-down verdict (reuse/extend + `ACDREAM_DUMP_STEPUP`'s existing lines — they already print the input + normal and the landing verdict); +3. every `ValidateWalkable` outcome on the tick: dist, waterDepth, which + branch (resting / below-push / CheckWalkable-fail), and BOTH flag guards + (`oi.Contact`, `sp.StepDown`) at its `SetCollisionNormal` site — the + `collN=(0,0,1)` fingerprint most plausibly comes from a + `SetCollisionNormal(groundNormal)` *somewhere*, and this names which. +4. the final `AdjustOffset` input/output pair for the tick. + +House rules: lives in `PhysicsDiagnostics` (rule 5), self-selecting trigger, +zero cost when off, mover id on every line +(`feedback_probe_identity_attribution`). + +## The session + +Build with the probe → the user repeats the exact 45° protocol (~1 min) → +read the trace → the failing phase + normal source IS the mechanism → write +the fix contract from it, citing the retail counterpart +(grep-named-first: `CTransition::transitional_insert` family, the step-down +refusal path, `validate_walkable`'s collision-normal writes — all already +mapped in this repo's pseudocode docs). + +## Known trap inventory for the eventual fix (from this week's do-not-retry ledger) + +The #331 absorb is retail-faithful — whatever the fix is, that +characterization pin must stay green. The #32 setter split must not be +re-latched. AD-65's snap arm is byte-anchored. The step-up chain's +`stepDownHeight = oi.StepUpHeight` oddity is retail-verbatim (#338's +research). Do not touch any of these on a theory.