fix(runtime): same-value LocalEntityId re-assertion is a no-op; file #365 headless hydration regression
The Campaign CH jump-probe headless reproduction quarantined on its first advance tick: AdvanceBeforeNetwork re-asserts the resolved local entity id every tick, which the C3c configuration seal treats as a mutation on a dormant controller. A same-value write is now a no-op; a DIFFERENT id while sealed still throws. One layer deeper the probe exposed #365: the headless world never hydrates (entities stay 0, the movement controller never publishes), so headless bots cannot move at head — filed with the full evidence chain. HeadlessDiagnosticWriter.Failure now emits the full exception detail (type-only cost a whole diagnosis round-trip). Runtime tests 1,322/0, Headless tests 89/0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
c1f1582576
commit
ab82347d42
3 changed files with 56 additions and 0 deletions
|
|
@ -121,6 +121,51 @@ than a REJECT-review fix batch; CH5-or-later.
|
||||||
**Campaign:** `docs/plans/2026-08-09-chat-parity-campaign.md` (Campaign CH,
|
**Campaign:** `docs/plans/2026-08-09-chat-parity-campaign.md` (Campaign CH,
|
||||||
CH4 REJECT-review).
|
CH4 REJECT-review).
|
||||||
|
|
||||||
|
## #365 — Headless host cannot move at head: session quarantines on the first advance tick; world never hydrates
|
||||||
|
|
||||||
|
**Status:** OPEN — filed 2026-08-10 during the Campaign CH jump-probe
|
||||||
|
reproduction. Distinct from #330 (no collision) and #332 (no remote DR):
|
||||||
|
this is the LOCAL player.
|
||||||
|
|
||||||
|
**Symptom:** a headless session (`jump-probe` policy, real ACE connect,
|
||||||
|
`testaccount`/`+Acdream`) reaches `InWorld`, `live: in world — CreateObject
|
||||||
|
stream active` prints, but `entityCount` stays **0** for the whole run and
|
||||||
|
the local movement controller is never published. The first
|
||||||
|
`SetIntent(Jump: true)` → next `AdvanceBeforeNetwork` tick quarantined the
|
||||||
|
session.
|
||||||
|
|
||||||
|
**Evidence chain (2026-08-10):**
|
||||||
|
1. First quarantine: `RuntimeLocalPlayerFrameController.AdvanceBeforeNetwork:93`
|
||||||
|
unconditionally re-assigned `controller.LocalEntityId` — a sealed
|
||||||
|
configuration property — on a still-dormant controller. FIXED in the
|
||||||
|
probing session: a same-value re-assertion is now a no-op (a different
|
||||||
|
id while sealed still throws).
|
||||||
|
2. Second quarantine (one layer deeper): the frame controller takes the
|
||||||
|
`ObjectClockDisposition == Suspend` branch and calls
|
||||||
|
`SuspendObjectUpdate` → `EnsurePublishedForRuntimeOperation` throws —
|
||||||
|
`_host.CanAdvancePlayer` is true while the controller is unpublished.
|
||||||
|
3. Root condition: the session's world never hydrates (`entities: 0`
|
||||||
|
despite the active CreateObject stream), so dormant local activation
|
||||||
|
never completes and the controller never publishes. K3/K4 ran full
|
||||||
|
connected multi-session gates with movement — this regressed somewhere
|
||||||
|
in the many Runtime/placement/CH landings since 2026-07-27.
|
||||||
|
|
||||||
|
**Also observed in the same run:** `[weenie-error] unmapped code=0x051D` —
|
||||||
|
an ACE-only id outside retail's 344-case `HandleFailureEvent` switch;
|
||||||
|
CH2's silent-toward-player + diagnostics-line fallback handled it as
|
||||||
|
designed (no action needed, noted for completeness).
|
||||||
|
|
||||||
|
**Repro:** `dotnet run --project src/AcDream.Headless -c Release -- run
|
||||||
|
--config <cfg>` with a `jump-probe` policy session against local ACE
|
||||||
|
(config shape: version 1, endpoint 127.0.0.1:9000, credential provider
|
||||||
|
Environment). `HeadlessDiagnosticWriter.Failure` now emits `errorDetail`
|
||||||
|
(full exception) — added during this diagnosis.
|
||||||
|
|
||||||
|
**Next step:** bisect the headless hydration path (why zero entities admit
|
||||||
|
headless when the graphical host hydrates fine) BEFORE touching
|
||||||
|
`CanAdvancePlayer` — the #357 closeout warns that dormant-activation
|
||||||
|
classification changes have a strict test matrix.
|
||||||
|
|
||||||
## #364 — Three `/help` group topics still partial: HelpStupidChannelHack unresolved
|
## #364 — Three `/help` group topics still partial: HelpStupidChannelHack unresolved
|
||||||
|
|
||||||
**Status:** OPEN — filed 2026-08-09, Campaign CH user-gate round 2, item 3.
|
**Status:** OPEN — filed 2026-08-09, Campaign CH user-gate round 2, item 3.
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,9 @@ internal sealed class HeadlessDiagnosticWriter
|
||||||
sessionId,
|
sessionId,
|
||||||
phase,
|
phase,
|
||||||
errorType = error.GetType().FullName,
|
errorType = error.GetType().FullName,
|
||||||
|
// Full detail: a quarantine that reports only the exception TYPE
|
||||||
|
// costs a whole diagnosis round-trip (jump-probe, 2026-08-10).
|
||||||
|
errorDetail = error.ToString(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -381,6 +381,14 @@ public sealed class PlayerMovementController
|
||||||
get => _localEntityId;
|
get => _localEntityId;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
// A same-value re-assertion is not a mutation:
|
||||||
|
// RuntimeLocalPlayerFrameController.AdvanceBeforeNetwork
|
||||||
|
// re-asserts the resolved id every advance tick, which on the
|
||||||
|
// headless host reached a still-dormant/sealed controller and
|
||||||
|
// quarantined the whole session (jump-probe repro, 2026-08-10).
|
||||||
|
// A DIFFERENT id while sealed remains the error it always was.
|
||||||
|
if (value == _localEntityId)
|
||||||
|
return;
|
||||||
EnsureConfigurationMutable();
|
EnsureConfigurationMutable();
|
||||||
_localEntityId = value;
|
_localEntityId = value;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue