Commit graph

1507 commits

Author SHA1 Message Date
Erik
9ad590dcc7 docs(physics): hand off placement continuation executor
Synchronize the architecture doc, milestones, roadmap, and ISSUES with the
continuation-executor behavior commit (5db3de3c): the residence system is
now a complete dormant mechanism, both independent reviews PASS, and the
next boundary is the all-host production cutover. The admission handoff
gains its superseded banner; the successor handoff records the executor's
ownership, the retail anchors proven during review (the wire-contact gate,
queue-by-parent-GUID relation replay, HasAnims semantics), the seven new
register rows, exact test totals, the rollback command, and the cutover
checklist. #275 filed for the post-cutover legacy-Position unification.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 03:52:30 +02:00
Erik
5db3de3c7a feat(runtime): execute initial placement continuations
The admission checkpoint (30012361) sealed accepted updates behind a
pending initial placement; nothing could apply them, so AcknowledgeAdoption
refused any non-empty FIFO and the residence system had no path to
completion. RuntimeInitialCreateContinuationExecutor is that missing
mechanism: a synchronous, retry-idempotent Execute transaction that adopts
the acknowledged initial placement exactly once (consuming the retained
completion so later authored placements for the key can begin), emits the
AfterEnterWorld hook request for the local player, replays deferred
missing-parent raw Creates and queued parent relations by parent GUID
(retail ProcessObjectNetBlobs order: whole-bucket detach, FIFO dispatch,
cancellation-aware restore), and drains the mixed continuation FIFO
strictly by sequence with retail route decisions taken at execution time
via ClassifyAcceptedPosition on live inputs (server-asserted wire contact,
data-driven animation proxy, live distance/options).

Apply bodies are shared with the legacy fused paths through new gate-less
instance seams on InboundPhysicsStateController that keep the one snapshot
store in lockstep; SameIncarnationCreate envelopes apply atomically with
per-stage idempotency and buffered publication after the final stage;
every abandonment path retires the residence through the lifetime choke
point and converges the ownership ledger (executor progress, deferred
buckets, replay windows, placement watches all folded into IsConverged).
Position/placement side effects are exactly-once under retry, external
mutations are detected via a field-masked executor baseline, and
AwaitingContinuationPlacement yields keep the FIFO head retryable.

Production routes are deliberately untouched: graphical and headless
Create still use legacy RegisterEntity, and no host calls Execute. The
cutover is the next checkpoint; AP-1/AD-1 remain open until it lands.
Register rows AD-59/AD-60/AP-130/AP-131/AP-132/TS-62/TS-63 document the
slice's deviations in this commit.

Reviewed: retail-conformance PASS + architecture/adversarial PASS after
five implementation rounds (wire-contact source, snapshot lockstep,
WeenieDescription merge, abandonment convergence, reentrant retirement
windows, acknowledged-completion leak, baseline precision, replay
containment/restore, queue-by-parent-GUID relation deferral all fixed at
root cause). Runtime tests 903/903; complete Release solution 10,696
passed / 4 intentional skips; focused executor gate 161/161.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 03:49:56 +02:00
Erik
4a8f74dc72 docs(physics): hand off initial placement admission 2026-08-01 21:03:51 +02:00
Erik
9d601817b8 docs(physics): hand off initial create residence 2026-08-01 19:42:19 +02:00
Erik
5785a07b3e feat(runtime): commit dormant SetPosition activation 2026-08-01 14:25:02 +02:00
Erik
99f867f053 feat(runtime): seal dormant SetPosition evaluations 2026-08-01 11:31:58 +02:00
Erik
22651c823d feat(runtime): publish dormant local physics ownership 2026-08-01 10:01:30 +02:00
Erik
442cb8f97b feat(runtime): prepare authored SetPosition movers 2026-08-01 09:16:09 +02:00
Erik
237d1184d2 feat(runtime): own SetPosition collision reports 2026-08-01 00:15:11 +02:00
Erik
ec627c13a2 docs(physics): hand off remaining divergence campaign 2026-07-31 23:13:46 +02:00
Erik
270f5154b9 feat(runtime): expose dormant placement receipts 2026-07-31 23:11:44 +02:00
Erik
4c02ac4259 feat(runtime): own deferred set-position residence 2026-07-31 22:32:49 +02:00
Erik
e84a388e6f feat(physics): port canonical retail set-position core 2026-07-31 20:44:03 +02:00
Erik
6b28ff999c fix(physics): make collision activation starvation-free 2026-07-31 18:34:46 +02:00
Erik
d94145e6b8 fix(physics): seal collision generations before activation 2026-07-31 15:53:05 +02:00
Erik
be94bc9b06 fix(physics): activate collision generations atomically 2026-07-31 15:19:25 +02:00
Erik
3e0f3b6206 fix(physics): validate retail cell containment roots 2026-07-31 14:48:26 +02:00
Erik
7716c2ee89 fix(physics): restore retail cell availability semantics 2026-07-31 14:22:45 +02:00
Erik
d3c0d9ec0e test(physics): harden TS-4 production chronology 2026-07-31 14:08:51 +02:00
Erik
75b6f6b6c9 fix(physics): restore retail path-6 collision response 2026-07-31 13:44:55 +02:00
Erik
acec33eca8 fix(physics): preserve retail step-down probe state 2026-07-31 13:24:25 +02:00
Erik
1fd5da67b4 fix(physics): restore retail step-down placement validation 2026-07-31 13:13:09 +02:00
Erik
4fbd93ecdb fix(physics): preserve retail edge-slide stop semantics 2026-07-31 13:03:46 +02:00
Erik
c559c48d80 fix(physics): restore retail edge-response ordering 2026-07-31 12:55:49 +02:00
Erik
c24bc571cf fix(physics): enforce retail step-down support radius (#273) 2026-07-31 12:10:03 +02:00
Erik
4dd40ad8fe docs(physics): close Campaign P visual matrix 2026-07-31 10:20:48 +02:00
Erik
d6e8b60303 fix(movement): invalidate burden on enchantment changes 2026-07-31 10:16:27 +02:00
Erik
2b9dfec9d7 docs(physics): close stat-chain live gate 2026-07-31 09:31:47 +02:00
Erik
2dcb4f1d94 fix(physics): port retail stair edge backprobe 2026-07-31 09:26:28 +02:00
Erik
5a0f9868a6 fix(physics): port retail slope landing stop 2026-07-31 09:10:53 +02:00
Erik
461a1fb7b4 feat(player): port retail augmentation stat chain 2026-07-31 08:08:23 +02:00
Erik
bb1640f777 fix #270 closeout: strip investigation probes; close the issue
User-verified: casting fixed (exhaustion-edge gate) and monster attack
animations restored (spawn settle placement + lost-cell retry). Final
session evidence: 14/15 spawn settles grounded; Falling-refusal spam
collapsed 2,954 -> 15 transient pre-settle lines.

Strips the [UM-ACT]/[MT-FAIL]/[SPAWN-PLACE]/[remote-edge] probes, the
MotionInterpreter.DiagnosticGuid plumbing, and the two throwaway probe
tests (motion-table attack sweep, vitae color dump - both findings are
recorded in ISSUES/research). Complete Release suite: 10,030 passed /
5 skips / 0 failures.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 07:26:57 +02:00
Erik
4da25a442b fix #270: run retail spawn placement at remote-body creation - standing monsters' attack animations restored
The [MT-FAIL] probe caught combat-stance monsters constantly failing to
dispatch 0x40000015 (Falling): their bodies were airborne-flagged while
standing. contact_allows_move (0x00528dd0) requires Contact+OnWalkable
and silently refuses every action animation for an airborne mover - a
spawned-standing monster's swings never played until it first moved.

Retail never has this state: CreateObject spawns run the placement
transition (CPhysicsObj::SetPosition -> SetPositionInternal 0x00515330),
which establishes CONTACT/ON_WALKABLE from the floor at spawn. Our
remote creation seeded a raw position with no placement.

SeedRemoteSpawnPlacement mirrors RemoteTeleportPlacement: engine
placement resolve (Setup-derived cylinder, TS-46) + the verbatim
CommitSetPositionTransition, wired at BOTH RemoteMotion creation sites
(UM-triggered creation - so a first-ever-UM attack animates in the same
packet - and ordinary first-UP creation). Unplaceable results leave the
body airborne exactly like a failed retail placement.

Also adds the [UM-ACT] (wire action items + stamp-gate verdict) and
[MT-FAIL] (refused animation dispatches) probes, riding
ACDREAM_DUMP_MOTION=1, which are what convicted the body state.

Complete Release suite: 10,032 passed / 5 skips / 0 failures.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 23:28:57 +02:00
Erik
1390add140 docs: #270 retest - casting fixed (user-confirmed); attack misses narrowed to link-less cycle hard-swap
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 23:03:34 +02:00
Erik
a46c8e65b2 fix #270: fire ReportExhaustion on the stamina-exhaustion edge only, not every stats tick
Retail calls CPhysicsObj::report_exhaustion from exactly one site -
CommandInterpreter::HandleExhaustion (0x006b3c70), a notification handler
for the stamina-exhaustion EVENT. Campaign P P1 wired it to every
movement-stats application instead (every stamina regen/drain tick), and
each call re-dispatches the current movement state through the animation
sink - truncating any in-flight action animation. The diagnostic session
log shows 490 spurious casting-stance re-queues in one short session:
'sometimes stuck in spell animations' was every stamina tick that
collided with a cast gesture's play window.

The re-apply now fires only when the exhausted state (stamina == 0)
transitions, matching retail's event semantics. Stats still reach
PlayerWeenie immediately via RuntimeMovementSkillProjection.ApplyTo.

Also adds the [remote-edge] probe (rides ACDREAM_DUMP_MOTION=1): one
line per remote HitGround/LeaveGround - each such edge drains the
mover's pending action animations (retail HandleEnterWorld), the
working theory for intermittently missing monster attack swings.

Complete Release suite: 10,026 passed / 5 skips / 0 failures.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 22:11:20 +02:00
Erik
5788fdaa02 docs: Campaign P session-2 wrap-up - speed + bounce family accepted; matrix state; next = #268+TS-8 stat-chain package
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 20:47:49 +02:00
Erik
fd5d11df37 docs: file #269 - slope-stop slide residual; byte-verify friction + jump chain (refutes jump-height hypothesis)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 20:33:06 +02:00
Erik
2d611b2b01 fix(physics): #265 landing-bounce family - retail check_contact seed + velocity-free landing commit
Retail jump landings BOUNCE: the floor touch records both a contact plane
(grounding) AND a collision normal (collided_with_environment), and
handle_all_collisions reflects the unmodified impact velocity off it at
5% elasticity (v += -(v.n)(elasticity+1).n, DEFAULT_ELASTICITY 0.05
@0x007c6a7c). Our transition already recorded both facts; the bounce was
suppressed by the AD-25 adaptation stack in the per-tick commit: a
Velocity.Z<=0 landing gate (needed because the resolver glued ascending
movers to the ground) plus a landing Velocity.Z=0 hand-zero whose stated
purpose was making the reflect a no-op. Downhill glided instead of
bouncing, flat-ground landings had no pop, and uphill jumps flapped
between grounded/airborne against the animation machine.

Three retail mechanisms replace the stack:
- check_contact (0x0050f5b0) seeding in ResolveWithTransition: a body in
  CONTACT seeds the transition's contact only while v.contactPlane.N <=
  0.0002; moving away seeds the last-known plane alone (get_object_info
  0x00511cc0). Ascending jumps therefore run contact-free (ballistic, no
  glue) - the gate's reason-for-being is gone. The plane requirement is
  strict: Contact-without-plane is unrepresentable in retail.
- SetPositionInternal-shaped commit (0x00515330, byte-read end-to-end,
  velocity-sign-FREE): contact purely from the transition's contact
  plane, HitGround on the airborne->walkable edge, HandleAllCollisions
  with unmodified impact velocity. Whole commit gated on Ok &&
  candidateMoved (retail pc:283657 skips SetPositionInternal entirely
  when the candidate did not move) - a standing body's contact state is
  never re-derived, which is what keeps rest bit-stable (AD-41 updated).
- Byte decodes: gate override state&0x800000=Sledding, zero branch
  state&0x20000=Inelastic, reflect strictly dot<0 - our port already had
  all three correct.

Settle: real landings (>=0.25 m/s) bounce and decay geometrically;
smaller impacts are consumed by retail's unconditional small-velocity
zero, so standing never micro-bounces. Re-baselines documented in place:
landing-survival pin measures decay post-settle; LiveCompare_Tick0/376
pin the new IsOnGround=false on zero-move ticks (captured true was the
retired seed echo; tick 376's captured body carries an 11.8 m/s grounded
velocity from the deleted get_state_velocity-overwrite era); de-overlap
fixture now carries the plane real grounded bodies always have. New
pins: LandingBounceSeedingTests (ascent no-seed, rest keeps contact,
strict plane, slope 5% reversal + tangential preservation, Sledding
override).

Investigation + implementation record:
docs/research/2026-07-30-landing-bounce-family.md. Complete Release
suite: 10,031 passed / 5 skips / 0 failures.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 20:12:06 +02:00
Erik
7fcc7db1d1 docs: #265/#166 - ledger updates and capture-bisect as-fixed addendum
docs/ISSUES.md: #265 and #166 updated with the root cause and fix from
the prior two commits; closure of both pends the user's visual-gate
acceptance. #265 also records the confirmed-separate uphill-bounce
finding (AD-25, byte-exact retail, out of scope). #166 records that the
Campaign P visual-matrix recheck it was waiting on DID happen and found
the glide/bounce still missing even with AD-25/AP-7/AD-55/TS-4 all
landed - that negative result is what triggered the #265 capture bisect
and this fix.

docs/architecture/retail-divergence-register.md: AP-7's retirement note
corrected. The row's original claim ("no horizontal velocity to hammer")
undersold the gap - calc_friction was structurally unreachable with
meaningful data on any grounded path, not just inert on the root-motion
path. No new row filed: this change ports retail's mechanism faithfully
and does not introduce a new deviation.

docs/research/2026-07-30-265-capture-bisect.md: full "as-fixed" addendum
(new section 9) recording the implementation - the fix mechanism, fixture
results (freeze reproduced under the old model, slide+decay proven under
the new one), the downhill-direction derivation for the synthetic decay
case, the two separate mechanisms found while building the Runtime tests
(LeaveGround's edge-timing recompute, AP-77's no-sink fallback), the
uphill-bounce orthogonality proof, and final test totals.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 19:28:28 +02:00
Erik
61e959169b fix #266: retail run-rate 800 branch is exact-equality sentinel, not a cap
Raw byte decode of MovementSystem::GetRunRate (0x006b0950, PDB-paired
binary): fild skill; fcom [800f]; fnstsw; test ah, 0x44; jp general —
the C2/C3 parity idiom whose 18/4 fall-through executes ONLY at
skill == 800 exactly. ACE read this as >= 800 ('max run speed?') and
Campaign P P1 inherited that misread when BN dropped the arithmetic,
flat-lining every maxed character at 4.5 (retail-true ~3.70, +21%) and
erasing the vitae differential (both 10200 and 15225 sat above 800).

The [stat-chain] live capture proved the enchant chain correct end to
end (vitae 0.67 -> eff run 10200 -> controller), isolating the formula.
General path byte-verified: (loadMod*(skill/(skill+200)*11)+4)/scaling/4.
InqMaxRunRate's skill=9999 probe gets ~3.6961, not 4.5.

Golden tests pin the 799/800/801 straddle and the maxed-skill vitae
differential; pseudocode doc §6 carries the decode plus a do-not-
reimport-ACE warning. Complete Release suite: 10,025 passed / 5 skips.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 19:22:41 +02:00
Erik
bd3ade625f docs: file #268 - panel vitae color, buff coloring, augmentation bonuses (promotes AP-127)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 18:32:10 +02:00
Erik
2493f24c63 merge: #267 vitae character-panel display (attributes vitae-immune per retail; skill dual parentheticals)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 18:14:48 +02:00
Erik
cf2605fa4a fix(ui): #267 character panel reflects vitae/buffed skills and attributes
Retail CACQualities::EnchantAttribute (0x00594570), EnchantAttribute2nd
(0x00594670, already ported for #6), and EnchantSkill (0x005947b0) are the
three enchantment-composition functions the Character window's Attributes
and Skills tabs depend on. Primary attributes never reference the vitae
singleton in retail (only Attribute2nd/Skill do) — confirmed directly from
the decompiled function bodies, not assumed.

EnchantmentMath.GetMod gains requiredType/includeVitae parameters (default
to the prior behavior) so a numeric StatMod key collision across domains
(e.g. key=1 is both Strength and MaxHealth) can't leak a buff into the
wrong computation. Spellbook.GetAttributeMod/GetSkillMod and
LocalPlayerState.GetEffectiveAttribute/GetEffectiveSkill/
GetSkillVitaeModifier wire the retail chain through to the panel.
CharacterSheetProvider now reports the effective value as the main number
and CharacterSkill.CurrentLevel is no longer an alias of BaseLevel (this
also activates the previously-dead SkillValueColor buffed/debuffed row
coloring). CharacterStatController's footer-title parenthetical is cited
from gmAttributeUI::DisplaySelectionFooter_Attribute (0x0049d280) and
gmSkillUI::DisplaySelectionFooter_Trained (0x0049b860) +
SkillInfoRegion::GetVitaeModifier (0x004f0fa0): skills show up to two
segments (vitae's own contribution, then the buff-only residual), while
vitae-immune attributes show at most one; no parenthetical when the delta
is zero. The panel now refreshes on Spellbook.EnchantmentsChanged, not only
raw property/attribute updates.

Core goldens cover the user-reported 33% vitae example (303->203, "(-100)"
exactly), buff+vitae composition, and the attribute vitae-immunity finding.
Provider/controller tests cover the full row-click -> footer-title path and
live refresh. Full solution suite passes with zero failures.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 18:08:55 +02:00
Erik
96a62a191b docs(research): #265 capture bisect - S1 and S2 both cleared, real culprit is R6 grounded-animation-zero (S3, pre-Campaign-P)
Root-caused via segment mining + a real-trajectory replay harness
(previous commit). Mined two dramatic real "velocity annihilation +
permanent freeze" events from the live capture (a high-speed fall landing
on a moderate, walkable-by-threshold roof slope, then a full velocity
zero + frozen position for the rest of the capture - 12,292 ticks for the
worse of the two).

A/B verdict: S1 (db2889af, #116 shape-1's Path-6 hasSphere1 change) is
provably UNREACHED for the mined trajectory - hit1 never fires once across
the 80-tick replay, and diagnostic instrumentation shows the landing
actually goes through the still-unchanged sphere0 (foot) branch. Reverting
S1 locally produced byte-identical replay output, confirming this
mechanically rather than by inference. S2 (calc_friction's AP-7 threshold)
has zero production call sites (grep-confirmed) - it is dead code and
cannot affect any live behavior in either direction.

The real mechanism, hand-traced against both mined events exactly: the R6
"grounded movement is animation-root-motion-owned" architecture
(PlayerMovementController.cs:1868-1882, landed 2026-07-20 via f961d700,
ten days before Campaign P) unconditionally zeros horizontal Velocity
every tick once OnWalkable is true. With no held movement key at the
instant of landing, the mover never advances again - a frozen-phase design
predating Campaign P entirely, not a regression from S1/S2.

Recommended direction: do not revert S1 (a real, narrow, retail-faithful
fix unrelated to these two events); do not touch S2 until it's actually
wired into a live path; the real target is #166 (downhill sled) plus the
grounded-movement architecture, which needs a brainstorming pass before
any implementation, not a quick S1/S2 revert.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 17:59:57 +02:00
Erik
4880d7d9cf docs: #267 scoping - character sheet has no vitae path; fix shape recorded
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 17:06:59 +02:00
Erik
355c13c273 docs: matrix session 1 results - file #265/#266/#267; TS-4 removal reverted on live evidence
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 17:04:34 +02:00
Erik
a8a7d64b47 Revert "fix(physics): TS-4 retired — Path-6 steep-poly shortcut deleted"
This reverts commit 5e2be19b4e.
2026-07-30 17:02:22 +02:00
Erik
c0afcacbb2 fix(physics): movement-parity fixes - adjusted catch-up cap, autorun retail semantics, AP-30 retired
Ports CMotionInterp::get_adjusted_max_speed (0x00527D00, byte-decoded:
bare rate unless RunForward; forward_speed x 4.0 when running;
current_speed_factor proven a ctor-constant 1.0 at 0x00528C34) and swaps
all five interpolation catch-up call sites to it - retail's
fUseAdjustedSpeed_ static (.data 0x0081F418 = 1) makes this the live
branch, so standing/walking remotes now catch up at ~2x runRate instead
of 4x too fast (the #41/#165 presentation family). Autorun now hard-
forces Run for its duration and cancels on every fresh forward press
(CommandInterpreter::HandleNewForwardMovement 0x006b3d60 is literally
SetAutoRun(0,1)); the old test pin codified the divergence. AP-30
retired: retail Frame::is_equal genuinely uses the 0.0002 epsilon - the
row recorded a non-divergence. Three catch-up test pins re-baselined to
retail semantics with citations. Full Release suite 9,983/0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 15:04:17 +02:00
Erik
2123b44e8c docs(research): movement parity audit - catch-up cap 4x gap (HIGH), autorun divergences, AP-30 stale
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 14:54:32 +02:00
Erik
4031a01881 docs(research): animation parity audit - 103/103 methods accounted, 7/7 flows parity, 5 ranked gaps
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 14:46:31 +02:00