Merge branch 'claude/quirky-payne-46a2e6' into claude/latest-commits-cb0c8f
This commit is contained in:
commit
629d83411d
5 changed files with 274 additions and 32 deletions
|
|
@ -209,7 +209,8 @@ reached — no `seal-refused` spam, no crash from the original bug) and the
|
|||
session exits gracefully every time (`[session] graceful logout confirmed`,
|
||||
zero leases at disposal). Full `airborne-transition True` confirmation is
|
||||
blocked by a SEPARATE, newly-discovered, pre-existing defect — see #368 below
|
||||
— not by anything in this issue's scope. A diagnostic-only run with #368's
|
||||
— not by anything in this issue's scope. (2026-08-10 update: #368 is CLOSED
|
||||
at `b7f59923`; the airborne residual survived that fix and is now #370.) A diagnostic-only run with #368's
|
||||
guard temporarily neutralized (never shipped, reverted before commit)
|
||||
confirmed the #365 fix produces the correct behavior once past that unrelated
|
||||
blocker: full hydration, the jump-probe policy running to completion, exit
|
||||
|
|
@ -241,9 +242,33 @@ Environment). `HeadlessDiagnosticWriter.Failure` now emits `errorDetail`
|
|||
|
||||
## #368 — Headless scheduler's async tick loop can run collision-generation calls on different threads, tripping `EnsureCollisionMutationThread`
|
||||
|
||||
**Status:** OPEN — filed 2026-08-10 during #365's end-to-end verification.
|
||||
Explicitly OUT OF SCOPE for #365 — orthogonal mechanism, not mentioned
|
||||
anywhere in that diagnosis.
|
||||
**Status:** CLOSED 2026-08-10 — fixed in `b7f59923`. One dedicated update
|
||||
thread (`acdream-headless-update`, spawned by `HeadlessProcessHost.RunAsync`)
|
||||
now owns the whole session-side lifecycle: `Start()` (the live connect
|
||||
transaction, where the first collision-mutating call can already happen),
|
||||
every scheduler turn, and the post-loop resource captures.
|
||||
`HeadlessProcessScheduler.Run(CancellationToken)` replaced `RunAsync` — the
|
||||
same deadline math and counters, but fully synchronous on the calling
|
||||
thread, with waits going through one rearmed `TimeProvider` timer
|
||||
signalling an event instead of `await Task.Delay`, so the loop never leaves
|
||||
its thread. `EnsureCollisionMutationThread` is untouched — the invariant it
|
||||
guards is real, and the headless host now satisfies it the same way the
|
||||
graphical host's game-loop thread does. Zero shared Runtime changes, so the
|
||||
graphical host is unaffected by construction. Evidence: new
|
||||
`ProcessHostRunsStartAndEveryTickOnOneDedicatedUpdateThread` test (RED
|
||||
pre-fix — Start ran on the caller's thread, ticks migrated to pool
|
||||
workers), Headless suite 97/97, full Release suite 12,554 / 4 skips / 0
|
||||
failures, and three live jump-probe runs against local ACE that each
|
||||
crossed `[wake] begin gen=1` — the exact point all three pre-fix runs
|
||||
quarantined — with zero faults, 204–205 hydrated entities, policy
|
||||
completion, ACE-confirmed graceful logout, converged disposed samples, and
|
||||
exit 0. The jump-airborne timeout this issue carried as an open question
|
||||
persists 3/3 on the fixed tree — the threading-artifact hypothesis is
|
||||
refuted; split off as #370.
|
||||
|
||||
Filed 2026-08-10 during #365's end-to-end verification. Explicitly OUT OF
|
||||
SCOPE for #365 — orthogonal mechanism, not mentioned anywhere in that
|
||||
diagnosis.
|
||||
|
||||
**Symptom:** a real headless run against live ACE (`jump-probe` policy,
|
||||
`ACDREAM_PROBE_PARK=1`) that survives long enough for the local player's own
|
||||
|
|
@ -292,21 +317,62 @@ directly, never through the real `HeadlessProcessScheduler.RunAsync` await
|
|||
loop — so none of them exercise this path either. A genuine coverage gap,
|
||||
not a regression from a specific commit.
|
||||
|
||||
**NOT fixed here.** A proper fix needs its own investigation (e.g. a
|
||||
dedicated single update thread for the scheduler's tick loop, or redesigning
|
||||
the thread-affinity invariant for verified multi-thread-safe callers) and
|
||||
must be verified against the GRAPHICAL host too — out of reach for the
|
||||
session that found this (constrained not to launch the graphical client).
|
||||
Do not "fix" this by loosening or removing `EnsureCollisionMutationThread`'s
|
||||
check — it guards a real invariant elsewhere in the physics/collision
|
||||
system, and a workaround shape here is exactly what CLAUDE.md's
|
||||
no-workarounds rule forbids without explicit approval.
|
||||
**Why the dedicated-thread shape (not an invariant redesign):** the guard
|
||||
is only the ENFORCER — Runtime's single-update-thread contract is
|
||||
documented all over (`RuntimeEntityDirectory` "single update-thread
|
||||
authority", `RuntimeLocalPlayerPhysicsPublicationState` "this single
|
||||
Runtime update thread", `RuntimePlacementProjectionChannel`), mostly
|
||||
without enforcement. An async tick loop violates the whole contract, not
|
||||
one check; weakening the check would have silenced the one place that
|
||||
noticed while leaving every unenforced assumption exposed, and would have
|
||||
degraded the guard for the graphical host where migration is always a bug.
|
||||
The dedicated thread fixes the entire class. Start() had to move onto the
|
||||
same thread too: the first collision-mutating call can land during
|
||||
connect, and a caller-thread Start would bind the guard there and trip the
|
||||
very first dedicated tick. Disposal legitimately stays on the lifecycle
|
||||
thread — `ResetSessionPhysics`'s doc comment designs for exactly that, and
|
||||
every prior graceful-teardown run (including the quarantined ones)
|
||||
exercised it.
|
||||
|
||||
**Repro:** run the `jump-probe` policy against local ACE with
|
||||
**Repro (historical):** run the `jump-probe` policy against local ACE with
|
||||
`ACDREAM_PROBE_PARK=1` for long enough that the local player's own landblock
|
||||
collision generation spans more than a couple of scheduler ticks (the
|
||||
default case against a real DAT-loaded landblock).
|
||||
|
||||
## #370 — Headless jump-probe: the released jump never registers as airborne (proven NOT a threading artifact)
|
||||
|
||||
**Status:** OPEN — filed 2026-08-10 during #368's fix verification.
|
||||
|
||||
**Symptom:** with #368 fixed (one dedicated update thread, thread
|
||||
migration provably gone — the new affinity test pins it), the `jump-probe`
|
||||
policy reaches `[jump-probe] releasing jump (fire)` and then reports
|
||||
`TIMEOUT waiting for airborne after jump fire -- the released jump never
|
||||
registered as airborne.` Reproduced 3/3 on the fixed tree at
|
||||
`lb=0x1134FFFF`; the #365 session saw the identical timeout in its
|
||||
guard-neutralized diagnostic run at `lb=0x0904FFFF`
|
||||
(`docs/research/2026-08-10-365-headless-hydration-diagnosis.md` §8), so it
|
||||
is location-independent and pre-existing. The run still exits 0 with
|
||||
ACE-confirmed graceful logout — the probe treats its timeout as
|
||||
completion, so nothing quarantines.
|
||||
|
||||
**What this refutes:** the #365 OUTCOME's hypothesis that the timeout was
|
||||
"plausibly a downstream artifact of the same unsynchronized-thread
|
||||
condition" — threads are now single and the timeout persists unchanged.
|
||||
This is a distinct defect (or probe-expectation gap) in the headless jump
|
||||
path: either the policy's charge/release never becomes a real jump on the
|
||||
wire/local motion, or the airborne signal the policy polls is never set on
|
||||
the headless projection. Everything before the fire provably works
|
||||
(hydration 204–205 entities, `local player present`, movement owner
|
||||
publication per #365's fix).
|
||||
|
||||
**Where to start:** `JumpProbeHeadlessBotPolicy` (what it reads as
|
||||
"airborne"), the J5.4 `RuntimeLocalPlayerMovementState` jump intent/outbound
|
||||
cadence path, and whether the graphical host's airborne transition has a
|
||||
presentation-side dependency the headless projection lacks.
|
||||
|
||||
**Repro:** #368's recipe (jump-probe vs local ACE, `ACDREAM_PROBE_PARK=1`);
|
||||
the timeout fires seconds after `releasing jump (fire)`.
|
||||
|
||||
## #369 — Unconfirmed whether retail's floating chat windows share the main window's currently-selected talk-focus channel
|
||||
|
||||
**Status:** OPEN — filed 2026-08-10, Campaign CH slice CH6b (register row
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue