# Campaign P — Physics Retail-Feel Parity **Status:** CLOSED 2026-07-31 — final user matrix accepted; tight-gap clearance issue #273 and deferred restricted-house gate #274 are explicitly carried follow-ups. **Filed:** 2026-07-29. **Directed by the user** as a pre-vendor-management detour after the same-day physics/collision retail-fidelity audit. The world-interaction program (Slice 5, vendor browsing) resumes when this campaign closes. **Execution model:** Claude drives autonomously slice-to-slice. Sonnet subagents implement bounded chunks against this plan's specs; an Opus review subagent gates every slice boundary. The only user stops are (a) the final batched connected visual matrix, (b) a DO-NOT-RETRY conflict, (c) anything destructive. All the CLAUDE.md workflow rules apply: grep-named-first → pseudocode → port → conformance test; register moves in the same commit; no workarounds. --- ## The parity goal (the autonomy contract) **"Retail Movement Parity v1"** — the campaign is DONE when all of the following are auditable-true: 1. **Zero physics-scope temporary stopgaps.** TS-1, TS-4, TS-5, TS-23, TS-46 retired by porting the retail mechanism. TS-24, TS-35, TS-40 either retired or re-classified (IA/AD) with a recorded justification. 2. **No feel-affecting approximations left unargued.** AP-7 resolved by decoding retail's friction state gate; AP-10 restored to retail's 0.1 m water sink-in; AP-25 replaced by the retail effective-skill chain (vitae/enchantment-aware); AP-71 (`check_entry_restrictions`) ported. 3. **Issue ledger:** #262, #165, #166, #116, #167, #72, #153 closed; the stale "pending visual gate" statuses on #172/#173/#174/#175/#41 reconciled (folded into the final matrix below). Explicitly excluded: #235 (user-deferred 2026-07-27) and #256/#257 (lifecycle/memory, not physics feel — separate track). 4. **Verification:** each port carries decomp citations + conformance tests; `dotnet build` + full Release suite green at every slice commit; one batched connected visual matrix (below) passes at the end, run by the user. Anything not in this list is out of scope for the campaign — file it, don't chase it. --- ## Slices ### P1 — Stat-coupled movement (burden / stamina / vitae) — retires TS-5, AP-25, the burden gap **Status (2026-07-30): COMPLETE.** Landed at `9355ddce`; Opus review APPROVE at `001e466d` (which also retired UN-8 — the CanJump polarity is byte-PROVEN `load < 2.0` from the PDB-paired binary — and recorded the PK-timer jump-cost decode for P3). Full Release suite 9,880/0/5 at the slice gate. Retail's vitae/enchant chain reuses the M3 bucket-4 representation; `JumpStaminaCost` never refuses (weak-jump only) — the plan's formula shorthand had the `+0.5` operand wrong and the implementation follows the decomp's `(load+0.5)*power*8+2`. AP-127 was filed for the then-bounded bonus properties and retired by #268 on 2026-07-31. Today `PlayerWeenie.SetBurden` has zero callers, `CanJump` is always true, `JumpStaminaCost` is 0, and pushed run/jump skill is attributeBonus + init + ranks only. Retail modulates movement by character state continuously. **Retail anchors (named decomp, verified 2026-07-29):** - `ACCWeenieObject::{CanJump 0x0058c400, JumpStaminaCost 0x0058c440, InqJumpVelocity 0x0058c520, InqRunRate 0x0058c560, InqMaxRunRate 0x0058c5a0}` — thin delegations to the qualities DB (pseudo-C ~406512). - `CACQualities::{InqMaxRunRate 0x00591b20, CanJump 0x00591b50, JumpStaminaCost 0x00591b90, InqRunRate 0x00592800, InqJumpVelocity 0x00592980, InqLoad 0x0058f130}` (pseudo-C ~412901–413975, ~409756) — the real load/skill/vitae composition. `InqJumpVelocity` ends in `sqrt(GetJumpHeight(...) * 19.6)` (pc 413975). - `MovementSystem::{GetRunRate 0x006b0950, GetJumpHeight 0x006b09b0, JumpStaminaCost 0x006b0a40}` (pseudo-C ~695958) — GetJumpHeight readable: `LoadMod(load) * (skill/(skill+1300) * 22.2 + 0.05) * power / scaling`, floor 0.35; JumpStaminaCost readable: `ceil(((power + 0.5) * load) * 8 + 2)` on the arg3==0 branch. - `EncumbranceSystem::{EncumbranceCapacity 0x004fcc00, Load 0x004fcc40, LoadMod 0x004fcc70}` (pseudo-C ~256393). - Cross-refs: ACE `MovementSystem`/`EncumbranceSystem` C# ports; holtburger if it models load. **Work:** 1. Port the full chain into Core (`EncumbranceSystem` + `MovementSystem` statics; `PlayerWeenie` becomes the CACQualities-shaped composition). Where BN x87 mush blocks a branch (GetRunRate body), use Ghidra MCP or the ACE port as the tiebreaker and cite which. 2. Determine, from `CACQualities::InqRunRate`'s own body, exactly which skill level retail feeds (base vs enchantment/vitae-adjusted) and port THAT — scoped to the run/jump query path only, reading vitae + relevant skill enchantments from the M3 active-effect state. Do not build a general effective-skill engine. 3. Plumb the inputs from Runtime: burden (EncumbranceVal/capacity from PlayerDescription + property updates), current stamina (vitals), vitae. Extend `RuntimeMovementSkillState` (J4.4 seam) so updates flow mid-session, same as run/jump skill today. 4. Wire the existing `jump_is_allowed` stamina-refusal branch and the `ReportExhaustion` dual-dispatch gate (R3-W4 seam) to a real consumer, matching retail's refusal/weak-jump behavior. 5. Conformance tests: formula tables (golden values incl. 800-skill cap, load-mod knees at 100%/200%, stamina cost ceil), gating tests (no-stamina jump refusal), plumbing tests (burden/stamina/vitae changes move the produced rate). Register: delete TS-5 + AP-25 rows, note the retirement in the same commit. ### P2 — Response-layer edge family — retires TS-1, TS-4, AP-7; closes #166, #116 **Status (2026-07-31, FINAL):** TS-1 and AP-7 retired as originally recorded. TS-4's first 2026-07-30 removal was accepted by an incomplete resolver-only fixture, failed the live matrix with a roof wedge/uphill-bounce regression, and was reverted. The 2026-07-31 closure began from a fresh `BSPTREE::find_collisions` read and ports the exact asymmetric Path-6 split: primary/foot hits use `SetCollide` + `LandingZ` + `Adjusted`, while secondary/head hits use `CollisionNormal` + `Collided`; neither writes a sliding normal. Both graph and prepared-flat implementations match that oracle. The corrective acceptance no longer calls the old horizontal-input fixture "production-shaped." `Ts4ProductionQuantumConformanceTests` executes the already-airborne, zero-root-motion 30 Hz Core collision tail — acceleration, body integration, transition resolve, exact body/cell commit, then `handle_all_collisions` — and retains its behavior-bearing cell, contact, sliding, stationary-fall, and velocity state for 90 ticks. Graph and flat match by raw bits for vertical/inward/tangential/downhill cases and a genuine positive-Z uphill jump; exact terminal state, non-penetration, no fixed point, and no second launch are pinned. No further product-code correction was needed after that test became faithful, and there is no active AD-56 row. The older resolver-only wedge test remains only as a historical three-second signature control. #116 shape-2 remains closed; shape-1 remains narrowed as recorded in its issue history. AD-55 remains retired by the raw-byte `cos(10°)` proof. The collision *response* layer (what happens after a hit): ground friction, cliff edges, downhill landings, near-perpendicular wall slides. One oracle-driven pass; the physics digest's DO-NOT-RETRY table binds every subagent here. **Work (research doc FIRST, then port):** 1. **AP-7:** decode the state gate on retail's friction block (`calc_friction` region, pseudo-C ~276702-276705) that lets retail use threshold 0.25 without hammering normal locomotion (the reverted L.3c attempt). Ghidra MCP for the x87 branch if BN is garbled. 2. **TS-1:** port the `EdgeSlide → PrecipiceSlide / CliffSlide` chain (precipice context, steep-plane bookkeeping) replacing our stop-at-edge. 3. **#166:** port the landing "sled" (Sledding state set/clear sites; the sled friction constants already sit in `calc_friction`). 4. **TS-4:** remove the Path-6 steep-poly shortcuts and port retail's exact sphere split: primary/foot uses `SetCollide` + `LandingZ` + `Adjusted`; secondary/head uses `CollisionNormal` + `Collided`. Remove every BSP-layer `SetSlidingNormal` write (retail's only in-transition writer is `validate_transition`). 5. **#116:** the near-perpendicular lateral-slide loss + first-airborne- frame divergence, driven by the existing tick-22760 replay and D4 pins. 6. Apparatus: extend the trajectory-replay tests; capture fixtures before changing behavior. Register: delete TS-1/TS-4/AP-7 rows. ### P3 — Remote-object residuals — retires TS-46, TS-23; closes #165; narrows/retires AD-25 1. **TS-46:** pass the Setup's verbatim sphere LIST into the transition (`CPhysicsObj::transition 0x00512dc0 → init_sphere`) instead of the two-scalar reconstruction, for local player and remotes; derive remote step-up/step-down from the Setup instead of the pinned 0.4 m. Captured-fixture replays must stay green or be re-baselined with evidence. 2. **AD-25:** align the remote post-resolve with the ported `handle_all_collisions` (grounded-bounce rule) as the player half already did in #182. 3. **#165:** remotes visibly penetrate walls before stopping — diagnose against the (now Setup-true) sweep; suspect list starts at the catch-up step length vs sweep sub-steps. 4. **TS-23:** parse PlayerKillerStatus from PlayerDescription/property updates and plumb PK/PKLite/Impenetrable onto local + remote player movers (`OBJECTINFO::init 0x0050cf30` state bits). Non-PK ACE behavior must be provably unchanged. ### P4 — World specials — retires AP-71, AP-10 **Status (2026-07-30): COMPLETE**, including a same-day Opus review fix. AP-71 landed at `d6c3f865` (20 new conformance tests); AP-10 landed at `cc8d57a2` (12 new conformance tests). Complete solution suite at that gate: 9,946 total across 9 test projects, 9,941 passed, 5 skipped, 0 failed on a clean run. One run in the same session saw a single unrelated flake (`AcDream.Content.Tests.Vfx.RetailDatLoaderTests .AnimationCache_CoalescesSameDidAndAllowsUnrelatedReadsInParallel`, a parallel-cache-coalescing timing test untouched by either commit) that passed 3/3 in isolation and on the immediate re-run — full-suite parallel contention, not a regression (independently fixed afterward at `dc0468cc`). **P4 review verdict: FIX-FIRST (2026-07-30).** `RestrictionObjPrevalenceInspectionTests` (`3b5e0992`) measured the installed cell DAT: 103,766 of 729,888 EnvCells across 1,293 landblocks — the entire housing estate, `restrictionObj` GUIDs `0x70xxxxxx` — carry a baked `RestrictionObj`. AP-71's fail-closed default (landed with `CanMoveInto` deliberately unmodeled, per the original AP-129 row) would have locked every apartment/cottage/villa interior for every player, including its own owner — a live regression, not the "inert in dev content" the row assumed. Fixed at `7a0f836a`: `ACCWeenieObject::CanMoveInto` (0x0058da40) and `RestrictionDB::IsAllowedIn` (0x005ae8f0) are now ported verbatim, fed end-to-end from CreateObject's HouseOwner/HouseRestrictions/ Monarch PWD-tail fields (previously parsed-and-discarded) plus a new live `House_UpdateRestrictions (0x0248)` parser, and resolved through a new `PhysicsEngine.Objects` property wired to the canonical `ClientObjectTable` in `RuntimeEntityObjectLifetime` (production fix, not just gate logic — an unwired table still fails closed). AP-129 is narrowed (not retired) to the genuine residual: no sequence-based staleness rejection for `House_UpdateRestrictions` (low-probability, self-correcting), and the outdoor `CLandCell` restriction path (a separate DAT structure) remains unported, unaffected by this fix. Gate: `AcDream.Core.Tests` 4,049 passed / 2 skipped / 0 failed; `AcDream.Core.Net.Tests` 761 passed / 0 skipped / 0 failed; complete solution suite 9,961 total, 9,956 passed, 5 skipped, 0 failed. 1. **AP-71:** port the `CObjCell::check_entry_restrictions` gate at the head of `find_env_collisions` (pc:309576) — barred house cells block at the threshold client-side. Landed: the gate is wired at the top of the indoor branch of `Transition.FindEnvCollisions`; `CellPhysics .RestrictionObj` is fed from the DAT-baked `EnvCell.RestrictionObj` field (§4.3's open question resolved via ACE's DatLoader + an independent `Chorizite.DatReaderWriter` reflection probe — it's a plain per-cell DAT field, not a live wire override) in both the dev and production caching paths, at zero bake-format cost. The mover's `CanBypassMoveRestrictions` (BF_ADMIN & BF_IMMUNE_CELL_RESTRICTIONS) is decoded via the TS-23 PWD-bitfield pipeline. The original landing deliberately left `CanMoveInto` unmodeled (fail-closed default, filed as AP-129) — the P4 review found this fails closed for the ENTIRE housing estate and required the fix-first pass described above. 2. **AP-10:** restore retail's 0.1 m water sink-in; while there, verify the water-contact step behavior (`WATER_CONTACT_TS` consumers) against retail and file anything found. Landed: the dry-corner constant is restored (full suite green — the sticky Contact/OnWalkable bit argument held); `WaterContact` is now produced at every `Contact`/`OnWalkable` commit site. No confirmed retail consumer of `WATER_CONTACT_TS` was found this pass; filed as #264 along with two other explicitly-unverified water items (the `ENTIRELY_WATER` ethereal/swim terrain-collision exemption, and jump/swim movement-effects) — none block this port. **P4 review addendum (2026-07-30): APPROVED after one FIX-FIRST round.** The initial AP-71 landing failed closed with `CanMoveInto` unmodeled; the prevalence inspection (`3b5e0992`) proved that locks all 103,766 housing EnvCells. `7a0f836a` ports `CanMoveInto`/`IsAllowedIn` verbatim (owner/self/null-db admit; unresolved object blocks), captures the previously-discarded HouseOwner/Monarch PWD fields, parses `House_UpdateRestrictions 0x0248` live, and wires the canonical object table into the gate. AP-129 narrowed to the sequence-byte and outdoor RestrictionTables residuals. Suite 9,956/0/5. ### P5 — Deferred fidelity — closes #167, #153, #72 **Status (2026-07-30): item 1 (#167) COMPLETE.** Both blockers resolved without Ghidra/cdb — the two x87-elided constants were byte-decoded straight from the matching binary's raw machine code (`docs/research/2026-07-30-constraint-leash-constants.md`). The leash is now armed at every current acdream inbound-position acceptance seam (`ConstraintDistance`, `LiveEntityNetworkUpdateController`, `PlayerMovementController.SetPosition`/`BlipPosition`), the per-tick `PhysicsBody.IsFullyConstrained` push replaces the always-false stub, and register row TS-35 is deleted. Full Core/Runtime/App suites pass with new conformance tests (leash-armed jump refusal, teleport-vs-blip anchor/teardown, taper reduction, remote-tick push). Items 2 (#153) and 3 (#72) remain open. 1. **#167:** decode the two unknown x87 ConstraintManager constants (Ghidra) and port leash arming. 2. **#153:** the far-teleport arrival onto an unstreamed landblock near a 192 m edge — apparatus first (the issue's own trigger table), then the streaming-gap hold shape ALREADY sketched there (freeze the per-tick resolve until the landblock loads — the async equivalent of retail's synchronous load; this is an AD row, not a workaround, and gets filed as one). 3. **#72:** close on the R6 evidence (DAT-authored omega ±1.5 rad/s is live; the cdb confirmation ask is obsolete). ### P6 — #262 login run-on-the-spot (live defect) Probe-instrumented fresh-process login repros (`ACDREAM_PROBE_RESOLVE=1` + net probes) against local ACE; the issue's hypothesis list is the script. Root cause, fix, regression test. No workarounds (no auto-recall, no synthetic position kick). Runs serialized (owns the build tree + client). ### P7 — Ledger + camera feel 1. Retire the stale TS-25 row (outbound stance ships via `RawState.CurrentStyle` since #219) and refresh TS-24/TS-35/TS-40 classifications. 2. #115 camera-drag: investigation-only against `CameraManager` constants (AD-37's vector-nlerp vs retail quaternion-slerp is the prime suspect); fix if a concrete divergence falls out, otherwise re-classify with evidence. 3. Reconcile #172/#173/#174/#175/#41 statuses via the final matrix. --- ## Implementation-phase closeout (2026-07-30) — awaiting the matrix Every implementation slice is COMPLETE and Opus-reviewed; the campaign now waits on the single user gate below. **Register scorecard:** goal-enumerated physics stopgaps at ZERO — TS-1, TS-4 (+ its FlatBspQuery twin), TS-5, TS-23, TS-35, TS-46 retired by ports; TS-25 retired on #219 evidence; TS-24→AD-57, TS-40→AD-58 re-argued. AP-7, AP-10, AP-25, AP-71 retired; UN-8 and AD-55 retired by raw-byte proof; AD-25 retired. AP-127 was subsequently retired by #268; the remaining new argued rows are AP-128/129, AD-53/54/55(retired)/56/57/58. **Issues:** #72, #153, #167, #255 closed; #116 shape-2 closed /shape-1 narrowed to a probable harness artifact (response layer byte-verified); #165 diagnosed to the render-lag candidate (matrix scenario 8a decides); #166 all four composite deviations landed (scenario 5 decides); #262 apparatus permanently live + 3/3 clean probe logins (scenario 11 decides). Notable finds along the way: ACE's inverted leash-start mapping, ACE's radians/degrees sled-constant bug (cos 10°), the housing-lockout prevalence catch (103,766 restricted cells), and the HouseOwner/Monarch PWD fields that were parsed-and-discarded. **Verification:** every slice gated on the complete Release suite; final state 9,977 passed / 0 failed / 4 skipped (the D4 un-skip retired one permanent skip). Suite grew from 8,826 to 9,977 tests over the campaign (+1,151, all conformance/golden/pin coverage). ## Final batched connected visual matrix (the ONE user gate) 1. Burden >100%: run slows, jump shrinks; ~200%: barely moves/jumps. 2. Repeated jumps drain stamina; low stamina → weak/refused jump; exhaustion behavior matches retail. 3. Fresh vitae: movement penalty present. 4. Walk off a cliff/roof edge: slides over like retail, no dead stop. 5. Downhill jump landing: sled glide + bounce. 6. Shallow-angle wall graze: lateral slide preserved. 7. Packed crowd: spacing + shuffle-out unchanged (regression). 8. Two-client: remote stops at walls without visible penetration; remote ceiling-jump bounces down immediately (#173); Holtburg portal platform step-up (#172); door Use after jumping (#174); closed-door collision matches the visual door (#175); observed-player blips gone (#41). 9. Locked/barred house: blocked at the threshold. 10. Wading: slight retail sink-in. 11. ~20 fresh logins: no run-on-the-spot. 12. Regression sweep: walk/run/strafe/turn/jump/stairs/doors/water edges feel unchanged from the accepted R6 baseline. ## Risk notes - P2 and P3 touch the frozen-adjacent transition internals — every subagent prompt must carry the digest's DO-NOT-RETRY table and the no-workarounds rule; 3 failed attempts on any item = stop and build apparatus, per [[feedback_apparatus_for_physics_bugs]]. - TS-46 changes the collision capsule of every mover; the captured replay fixtures pin behavior — re-baseline only with a recorded retail argument. - P1's enchantment-aware skill read is the scope-creep risk; it is bounded to the run/jump query path by this plan. ## Live-gate session 2 (2026-07-30) — speed + bounce family landed The matrix's first live rows surfaced three defects; all three are root-caused, retail-ported, and user-accepted in the same session: - **#266 CLOSED** — run speed: retail `MovementSystem::GetRunRate` (0x006b0950) treats 800 as an EXACT-EQUALITY sentinel; ACE's `>= 800` reading is a misread of the same x87 mush that our P1 port inherited, flat-lining every maxed character at 4.5 (retail-true ~3.70, +21%, vitae-independent). Byte-decoded, fixed at `61e95916`; the [stat-chain] live capture proved the vitae→skill chain correct end-to-end. Side-by- side pace vs a retail client accepted by the user. - **#265/#166 landing-momentum + bounce family** — two stacked fixes: (1) `c60f6e5d` stopped hand-zeroing grounded residual velocity and wired the never-written `GroundNormal` (roof slides restored); (2) `2d611b2b` replaced the AD-25 landing adaptation with the retail mechanism: `check_contact` (0x0050f5b0) transition seeding, the velocity-free `SetPositionInternal` commit (0x00515330), and the live 5%-elasticity landing reflect (DEFAULT_ELASTICITY 0.05 @0x007c6a7c). Downhill bounce chain, flat-ground pop, and clean uphill landings all user-accepted ("almost pass with merits"). Investigation + byte-decode record: `docs/research/2026-07-30-landing-bounce-family.md`. - **#267 shipped** (vitae/buff panel values; attributes vitae-immune). **#268 closed 2026-07-31**: the complete augmentation chain is shared by panel and Runtime movement; AP-127 is retired. Attributes, secondary attributes, and skills use retail's vitae-excluded green/red comparison. The selected-skill footer now renders per-fragment colors through the shared retained text primitive, using the authored 0x1B palette exactly: #7FFFFF vitae, #00FF00 buff, #FF0000 debuff. TS-8 is also retired: a real live 0x02C2 payload carries its full StatMod through dispatch and changes the effective skill immediately. The user accepted the live colors, values, footer, and immediate row refresh. - **#269 closed 2026-07-31** — the live 2,184-quantum trace proved the landing reflect and friction math were correct. ACDream omitted retail's `OBJECTINFO::kill_velocity` before restoring a remembered contact plane in `CTransition::validate_transition @ 0x0050AA70`, retaining full downhill velocity while repeatedly re-grounding the mover. The exact non-OK-only restore/kill order and final last-known validity overwrite are now ported, focused/full gates pass, and the user accepted repeated slope jumps. Evidence: `docs/research/2026-07-31-269-slope-stop-capture.md`. - **#271 closed 2026-07-31** — a bounded stair-side trace proved ACDream could bypass retail's current-position edge back-probe by promoting a stale `LastWalkable` tread. That made PrecipiceSlide reverse an uphill tangent and rapidly carry the player down the stairs. The two stale-history substitutions are removed; current-walkable, back-probe, and no-walkable outcomes now follow `CTransition::edge_slide @ 0x0050B3D0`. The exact captured frame is pinned in the existing installed-stair fixture and the complete Release suite passes 10,062 tests / 5 skips. The user accepted repeated uphill runs while pressing into the stair sides. Evidence: `docs/research/2026-07-31-271-stair-side-slide-capture.md`. - **#272 complete and user-accepted 2026-07-31** — `CACQualities::InqLoad` consumes enchantment-adjusted Strength through `InqAttribute`, but Runtime movement and both retained burden displays read raw Strength and did not share the enchantment invalidation edge. They now consume `GetEffectiveAttribute(Strength)` and `Spellbook.EnchantmentsChanged`, so buff, dispel, expiration, and death purge recompute the same burden state immediately. Focused and full Runtime/App tests pass. Matrix rows accepted so far: speed parity, roof slide, downhill bounce, flat pop, uphill landing, and #269's slope-stop feel (rows 3/4/5-partial/12-partial). The 2026-07-31 final session accepted burdened movement, exhausted jumping, wall/corner response, crowded-monster movement, two-client remote/door/portal behavior, and shallow water. The user waived the general sweep, deferred restricted-house validation as #274, and retained the separate tight-gap clearance mismatch as #273. Automated scenario 11 remains 20/20 passing. The #269 checkpoint passes 4,107 Core tests / 2 skips and 439 Runtime tests / 0 skips; the complete Release suite passes 10,061 tests / 5 skips / 0 failures.