Both #280 review lenses returned FAIL on the same defect, and both were
right. IsRenderNeighborhoodResident's widened outer arm requires
IsRenderReady out to FarRadius, justified by "a Far-tier landblock
registers with an empty mesh set and is therefore render-ready." That held
only for a landblock that ARRIVED as Far. The second, equally first-class
way to be Far tier is a Near->Far DEMOTE:
DemoteLandblock -> EnqueueNearLayerRetirement
-> LandblockRetirementStage.MeshReferences
-> GpuWorldState.ReleaseLandblockMeshReferences
-> LandblockSpawnAdapter.OnLandblockUnloaded => WantsLoaded = false
while DetachNearLayer deliberately keeps the landblock loaded, terrain-mesh
resident, terrain-collision resident and DRAWN. Nothing re-publishes an
already-loaded landblock, so the demoted member satisfied NEITHER arm of
the gate, permanently: wormhole tunnel plus centered "In Portal Space -
Please Wait..." forever, no recovery short of relog.
Reachable by ordinary play. Two consecutive recalls to the same landblock
with walking in between makes ChangesStreamingCenter false, so there is no
origin recenter and the region recentres through the ordinary demote diff.
Also reachable via a mid-hold quality-preset drop -- ironically the exact
scenario ReconcileDestinationReservationRadius was added to support. The
pre-#280 radius-1 gate never touched that band, because nothing inside the
Near ring can demote.
FIX SHAPE. Make the two routes genuinely equivalent rather than teaching
the predicate to tolerate the difference. ReleaseLandblockMeshReferences
becomes "reconcile the registration to the post-retirement tier": after the
release converges, if the landblock is still loaded AND still Far tier,
re-assert the empty registration -- the identical OnLandblockLoaded(lb,
empty) a PublicationKind.Far activation makes. It is empty by construction:
DetachNearLayer retains only live server projections, which the adapter's
atlas-tier filter skips. A full retirement is unaffected (DetachLandblock
clears both _loaded and _tierByLandblock), and a throwing release still
retries because the re-assert is only reached after the adapter converged.
The alternative -- "|| (IsFarTier && IsLoaded)" at the gate -- was
rejected: it fixes one caller while leaving IsRenderReady meaning two
different things, which is precisely how this defect arose. After this
change the predicate reads "drawable at its current tier" for every caller,
with no knowledge of how the landblock got there.
WHY THE TESTS MISSED IT, fixed here too:
- Proof obligation P2 was discharged against RESIDENCY (the FarRadius+2
eviction threshold) rather than against IsRenderReady, the gate's actual
atom. The contract now carries the correction and the restated
obligation: no transition may REVOKE IsRenderReady from a landblock that
stays inside FarRadius.
- WorldRevealDerivedWindowIntegrationTests advertised itself as end-to-end
against the real GpuWorldState but constructed it with no spawn adapter,
so its IsRenderReady degenerated to IsLoaded via the "?? true". The
single most load-bearing predicate in the change was stubbed out by a
null in the test named after it -- the same shape as C5b's D3 and #276's
three settler tests. Every fixture in that file now owns a real
LandblockSpawnAdapter.
- The P1 test's comment described its subject as "a Near-shaped completion
the streaming window has since DEMOTED to Far". It is not; it is a fresh
PublishAsFar, the case that does hold. Corrected, since a future reader
would have taken it as demote coverage.
Four new regression tests, all driving the real GpuWorldState +
LandblockSpawnAdapter + LandblockPresentationPipeline through an actual
demote, and all sabotage-verified in both directions (fail with the
production change reverted, pass with it):
NearToFarDemote_LeavesTheLandblockRenderReadyThroughTheRealPipeline
NearToFarDemote_LeavesTheLandblockRenderReadyUnderBudgetedRetirement
TieredWindow_StaysResidentAfterAnOuterRingDemote
OutdoorReveal_SurvivesAnOuterRingDemoteDuringTheHold
The budgeted variant exists because production composes
LandblockRetirementCoordinator.CreateBudgeted, whose MeshReferences stage
is a separate call site from the legacy pipeline's.
SECONDARY, same commit:
- R-1: ACDREAM_PROBE_REVEAL_RADIUS=0 was parser-accepted and
Runtime-rejected -- it yields far = 0 for an outdoor destination, which
fails invalid-readiness-shape on every acknowledgement, hanging the very
A/B route the probe exists to measure. Parser floor raised to 1, with a
7-case table test.
- R-2: the composite-warmup TRIGGER had silently moved onto the far
window's critical path. Pre-#280 the gate and the composite domain were
the same radius-1 square; #280 widened the gate without widening the
domain, so every composite upload serialised behind the last outer-ring
landblock for no readiness benefit. Warmup now starts once the NEAR
sub-window is published -- trigger scope == domain scope, as before. The
reveal gate is untouched: Evaluate still requires the full window AND
composite readiness.
- AP-150 filed: acdream's RetailWaitCueDelay = 5 s arming is NOT retail's
trigger, and #280's commit message got this wrong on both clauses. Retail
emits the notice unconditionally per tunnel rotation segment, in the else
arm of the segment-expiry test at 0x004D6FCD; segment duration is
RandDouble(0.6, 1.8) s, byte-decoded at 0x004D6FE6. The 5.0 constant at
VA 0x007991B0 is CellManager::CheckPrefetchStatus's prefetch RETRY
cadence and has nothing to do with the cue. acdream's own 0.6/1.8 segment
constants already match retail exactly; only the arming is wrong.
Adopting retail's unconditional emit is filed as #329 rather than folded
in here -- it is a user-visible presentation change and wants the user's
eyes.
- AP-151 filed: the gate is materially STRICTER than retail on the
mesh-build/GPU-upload axis. Retail's LScape::PreFetchCells blocks on DAT
RESIDENCY only -- no geometry construction, no upload; that work is lazy
at draw. acdream requires a DAT read, terrain mesh build, render-thread
upload, spatial commit, collision admission and spawn-adapter activation
per member of a 625-member window, metered at MaxCompletionsPerFrame.
Nothing bounds the hold. This is the OPPOSITE asymmetry from AP-149; both
are live at once, on different axes.
- AD-2's amendment stated the false Far-tier readiness assumption verbatim;
corrected, along with the same error in
claude-memory/reference_two_tier_streaming.md, which now carries an
explicit DO-NOT-RETRY on the special-case-the-predicate shape.
- AP-115 scope-noted (it covers the cue's presentation, not its arming).
- #326's SmartBox::set_mid_radius citation corrected: the entry is
0x00453180; 0x004531D0 is the mid-function re-arm branch.
Blast radius: GpuWorldState, LandblockSpawnAdapter,
WorldRevealReadinessBarrier and StreamingDiagnostics are all App-internal;
AcDream.Headless and AcDream.Runtime reference none of them outside
comments. Headless tests run green as part of the gate below, per C5b's
lesson about surveys that skip the no-window host.
Gates: Release build 0 errors, 18 pre-existing xUnit analyzer warnings.
Complete suite "dotnet test AcDream.slnx -c Release -m:1" with
ACDREAM_PAK_PATH set: 11,192 passed / 4 skipped / 0 failed, from a clean
rebuild (a prior session's deleted probe file had been compiled into a
stale test DLL). Baseline at fafc0b65 was 11,179 / 4 / 0; the +13 delta
reconciles exactly to this commit's additions -- 3 readiness tests, 1
integration test, 7 parser table cases, 2 warmup-trigger tests. None of the
known flakes #302/#308/#321 surfaced, and none is conflated with the
finding above.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
321 lines
19 KiB
Markdown
321 lines
19 KiB
Markdown
# #280 architecture review — ownership, layering, blast radius, test quality
|
||
|
||
- **Commit under review:** `3aab05b0` — *fix(streaming): derive the portal
|
||
reveal window from the live streaming radii (#280)*
|
||
- **Branch / HEAD:** `claude/acdream-physics-divergence-5aa784` @ `fafc0b65`
|
||
- **Worktree:** `.claude/worktrees/peaceful-visvesvaraya-e0a196`
|
||
- **Scope:** ownership, layering, blast radius, test quality. Retail fidelity
|
||
is a separate reviewer's.
|
||
- **Mode:** read-only. Production edits were made only as sabotage probes and
|
||
reverted; the tree is clean at `fafc0b65` with no working-tree changes.
|
||
|
||
## Verdict: **FAIL**
|
||
|
||
One confirmed defect, reproduced end-to-end through production code paths.
|
||
The predicate D2 widened from a 3×3 always-Near neighbourhood to the full Far
|
||
window now demands `IsRenderReady` from landblocks that the streaming system
|
||
**permanently** takes out of render-readiness while leaving them loaded. There
|
||
is no code path that restores them. Under two ordinary player flows — one of
|
||
which is the exact mid-hold radius change this commit added
|
||
`ReconcileDestinationReservationRadius` to support — the reveal gate becomes
|
||
unsatisfiable and the client stays in "In Portal Space - Please Wait…"
|
||
forever.
|
||
|
||
Everything else checked out. Layering held, the no-window host is genuinely
|
||
untouched, the allocation fix is real, and the tests are unusually well
|
||
disciplined about not re-encoding the constant under test. The failure is a
|
||
single missed state transition, not sloppy work — but it is the failure mode
|
||
the change's own proof obligation P2 was written to exclude, discharged
|
||
against the wrong predicate.
|
||
|
||
---
|
||
|
||
## Defects
|
||
|
||
### D-1 — A demoted-but-loaded landblock is permanently not `IsRenderReady`, so the widened gate can never open (**CONFIRMED, reproduced**)
|
||
|
||
**Severity: defect. Blocks the fix.**
|
||
|
||
`src/AcDream.App/Streaming/StreamingController.cs:270-278` — the new outer arm
|
||
requires `_state.IsRenderReady(canonical)` of every in-bounds member out to
|
||
`farRadius`.
|
||
|
||
`IsRenderReady` is not a property of "loaded":
|
||
|
||
- `src/AcDream.App/Streaming/GpuWorldState.cs:179-181`
|
||
`IsRenderReady = _loaded.ContainsKey(id) && _wbSpawnAdapter.IsLandblockRenderReady(id)`
|
||
- `src/AcDream.App/Rendering/Wb/LandblockSpawnAdapter.cs:135-139`
|
||
returns **false** when the landblock has no registration, or a registration
|
||
with `WantsLoaded == false`.
|
||
|
||
A Near→Far **demote** destroys exactly that registration while keeping the
|
||
landblock loaded:
|
||
|
||
- `src/AcDream.App/Streaming/StreamingController.cs:555-559`
|
||
`DemoteLandblock` → `LandblockPresentationPipeline.EnqueueNearLayerRetirement`
|
||
- `src/AcDream.App/Streaming/LandblockRetirementCoordinator.cs:309-312`
|
||
a `NearLayer` ticket still runs `CoreStages`, which includes
|
||
`LandblockRetirementStage.MeshReferences`
|
||
- `src/AcDream.App/Streaming/LandblockRetirementCoordinator.cs:738-740`
|
||
→ `GpuWorldState.ReleaseLandblockMeshReferences`
|
||
- `src/AcDream.App/Streaming/GpuWorldState.cs:1559-1560`
|
||
→ `LandblockSpawnAdapter.OnLandblockUnloaded` → `WantsLoaded = false`,
|
||
registration dropped
|
||
- `src/AcDream.App/Streaming/GpuWorldState.cs:1800-1835`
|
||
meanwhile `DetachNearLayer` **keeps** `_loaded[canonical]` and flips
|
||
`_tierByLandblock[canonical] = Far`
|
||
|
||
Nothing re-publishes an already-loaded landblock. `RecenterTo`
|
||
(`StreamingRegion.cs:232-244`) emits `ToLoadFar` only for ids absent from
|
||
`_tierResidence`, and a demoted block is still present as `Far`. Only a
|
||
**promote** back into the Near ring (`StreamingRegion.cs:239-244` →
|
||
`PublicationKind.PromoteExisting` → `ActivateLandblockPresentation` →
|
||
`OnLandblockLoaded`) restores the registration. Members of the band
|
||
`NearRadius+1 … FarRadius` are never promoted, so they stay dead for the rest
|
||
of the window's life.
|
||
|
||
**Evidence (temporary probes, run then removed):**
|
||
|
||
| Probe | Result |
|
||
|---|---|
|
||
| `GpuWorldState.IsRenderReady` after `LandblockPresentationPipeline.BeginNearLayerRetirement` (the production demote entry point) | **false** |
|
||
| `GpuWorldState.IsRenderReady` after `GpuWorldState.RemoveEntitiesFromLandblock` (same `DetachNearLayer` + `ReleaseLandblockMeshReferences` pair) | **false** |
|
||
| Full Near 5×5 window; `controller.IsRenderNeighborhoodResident(dest, 1, 2)` before one outer-ring demote | **true** |
|
||
| …the same call after that single demote | **false**, and never recovers |
|
||
|
||
All three probes used a real `GpuWorldState` + real `LandblockSpawnAdapter` +
|
||
real `LandblockPresentationPipeline`.
|
||
|
||
**Reachable failure scenarios**
|
||
|
||
1. **Mid-hold quality change (the one this commit explicitly added code for).**
|
||
`StreamingController.ReconfigureRadii` at `:484-488` emits
|
||
`DemoteLandblock` for every currently-Near landblock that the new preset
|
||
puts in the Far band. Presets are High `(4,12)` → Low `(2,5)`
|
||
(`src/AcDream.UI.Abstractions/Settings/QualityPreset.cs:31-34`), so a
|
||
High→Low switch demotes the Chebyshev 3–5 band, which is *inside* the new
|
||
Far window. The gate then requires `IsRenderReady` from all of them.
|
||
Result: the hold never ends, and
|
||
`WorldRevealCoordinator.ReconcileDestinationReservationRadius`
|
||
(`:521-543`) cheerfully re-opens a reservation on a square that can no
|
||
longer converge. Note also that `NearRadius`/`FarRadius` only publish at
|
||
transaction convergence (`StreamingController.cs:886-888`) while the
|
||
demote mutations run earlier (`:860-876`), so for several frames the
|
||
barrier measures the OLD wide window against blocks already demoted for
|
||
the NEW narrow one.
|
||
|
||
2. **A reveal that does not recenter the origin.**
|
||
`LocalPlayerTeleportController.cs:934` only calls `_streaming.BeginRecenter`
|
||
when `transition.ChangesStreamingCenter`, i.e. when the destination
|
||
landblock differs from the **world-origin** landblock. Origin recenter is
|
||
the only thing that clears the window (`GpuWorldState.cs:1481`
|
||
`_loaded.Clear()`). Walking ≳ `NearRadius + 2` landblocks from the last
|
||
origin (`StreamingRegion.cs:263-270`) demotes blocks into the Far band;
|
||
a subsequent teleport back to the origin landblock (lifestone recall →
|
||
travel → lifestone recall) takes the no-recenter path and inherits them.
|
||
Blocks that land back inside the Near ring are promoted and recover;
|
||
blocks in `near+1 … far` do not.
|
||
|
||
**Why the old gate was safe:** at `OutdoorNeighborhoodRadius = 1` the required
|
||
set was the destination's own 3×3, which during a hold is pinned to the
|
||
streaming centre (`StreamingFrameController.SelectObserver:194-200` freezes the
|
||
observer at `_origin` while `PlayerState.PortalSpace`) and therefore always
|
||
Near tier. Demotion inside the Near ring cannot happen. The defect is
|
||
introduced by this commit, not exposed by it.
|
||
|
||
**Why P2 missed it.** `docs/research/2026-08-05-280-contract.md:876-884` states
|
||
P2 as "an outer-ring member cannot be **evicted** while it is inside
|
||
`FarRadius`" and cites the `FarRadius + 2` unload threshold. That is true and
|
||
irrelevant: the gate's atom is `IsRenderReady`, not residency. P2 was
|
||
discharged against the wrong predicate. Every remaining sentence of the
|
||
static proof (hysteresis, recenter, dungeon collapse) is correct.
|
||
|
||
**Also note:** the P1 test's own comment
|
||
(`tests/AcDream.App.Tests/Streaming/StreamingControllerReadinessTests.cs:567-570`)
|
||
describes its subject as *"A Near-shaped completion that the streaming window
|
||
has since demoted to Far"*. It is not — it is a fresh `PublishAsFar` of a
|
||
landblock that was never loaded, which is the case that **does** hold. The
|
||
comment names the one case that is false. A future reader will take it as
|
||
coverage of the demote path.
|
||
|
||
---
|
||
|
||
## Latent risks
|
||
|
||
### R-1 — `ACDREAM_PROBE_REVEAL_RADIUS=0` is accepted by the parser and rejected by Runtime
|
||
|
||
`src/AcDream.App/Streaming/StreamingDiagnostics.cs:54-55` accepts any
|
||
`value >= 0`. `ApplyRevealRadiusOverride` then yields
|
||
`StreamingRevealWindow(0, 0)`, so `RequiredWindow` returns far = 0 for an
|
||
**outdoor** destination, and
|
||
`src/AcDream.Runtime/World/RuntimeWorldTransitState.cs:585-597` fails
|
||
`invalid-readiness-shape` on every acknowledgement (outdoor ⇒ `>= 1`). The
|
||
A/B probe would hang the route it is meant to measure. The documented value is
|
||
`1`; the parser should refuse `0` rather than let the two halves of the same
|
||
commit disagree. One line.
|
||
|
||
### R-2 — the composite-warmup **trigger** moved onto the far window's critical path, undocumented
|
||
|
||
`WorldRevealReadinessBarrier.Prepare:120-133` starts composite preparation only
|
||
once `_isRenderNeighborhoodReady(dest, near, far)` is true. Before this commit
|
||
that predicate was the destination's 3×3; it is now the entire 25×25. The
|
||
commit message and D3 justify keeping the composite **radius** at `NearRadius`
|
||
(correct — Far builds carry no entities), but say nothing about the trigger.
|
||
Net effect: composite upload can no longer overlap far-ring streaming; the
|
||
hold is longer than the streaming work alone requires by the whole warmup
|
||
duration. This is a real serialisation the "expect longer holds" paragraph
|
||
does not account for, and it is separable — warming composites at Near-ready
|
||
would restore the overlap without weakening the gate.
|
||
|
||
### R-3 — two different windows describe "destination publication incomplete"
|
||
|
||
`StreamingController.cs:719-724` computes the destination-lane preference with
|
||
`(Math.Min(NearRadius, DestinationRadius), DestinationRadius)` — live
|
||
`NearRadius` against the *reservation's* radius — while the gate uses
|
||
`RequiredWindow`'s `(clamp(NearRadius, 0, FarRadius), FarRadius)`. Between a
|
||
preset change and reservation reconciliation these name different squares, so
|
||
the work-lane prioritisation can consider the destination complete on a
|
||
square the gate still rejects. Cosmetic today; it becomes a real starvation
|
||
question once R-1/D-1 are fixed and holds get long.
|
||
|
||
### R-4 — Runtime lost its only cross-host consistency check
|
||
|
||
The `indoor ? 0 : 1` equality became `indoor ⇒ 0`, `outdoor ⇒ ≥ 1`
|
||
(`RuntimeWorldTransitState.cs:585-597`). The layering argument is right —
|
||
Runtime cannot learn the graphical host's streaming configuration, and
|
||
plumbing App radii in would be exactly the C5b failure. The cost is that
|
||
Runtime can no longer detect an App regression to a hardcoded radius: a
|
||
future revert of D1 would emit `1` and pass. Accepted, but worth recording
|
||
that the invariant is now shape-only and the only remaining guard on the
|
||
value is the App-side tests.
|
||
|
||
---
|
||
|
||
## What was checked and is sound
|
||
|
||
**Layering / ownership (attack 6) — held.**
|
||
`WorldRevealCoordinator` and `WorldRevealReadinessBarrier` are `internal` to
|
||
`AcDream.App`. `src/AcDream.Headless/AcDream.Headless.csproj` references only
|
||
`AcDream.Runtime`. Both non-graphical producers
|
||
(`HeadlessSessionWorldProjection.cs:979`,
|
||
`RuntimeLiveEntitySessionController.cs:782`) keep emitting their own
|
||
centre-ring token and are legal under the loosened shape by construction, and
|
||
both were annotated in place explaining why they must **not** track the
|
||
graphical radius. No App type crossed into Runtime. `AcDream.Headless.Tests`
|
||
passes 89/89 including its dependency/loaded-assembly guards. C5b's lesson was
|
||
applied, not repeated.
|
||
|
||
**P1 (attack 2) — independently verified TRUE, by reading rather than by
|
||
reading the test.** `PublishAsFar` (`LandblockPresentationPipeline.cs:432-522`)
|
||
constructs a `LoadedLandblock` with `Array.Empty<WorldEntity>()` and
|
||
`PhysicsDatBundle.Empty`; the spatial commit
|
||
(`LandblockPresentationPipeline.cs:900-916`) routes `PublicationKind.Far`
|
||
through `CommitLandblockSpatial`, which returns a publication with
|
||
`RequiresActivation` defaulted true (`GpuWorldState.cs:24`), so
|
||
`ActivateLandblockPresentation` (`:1009-1035`) calls `OnLandblockLoaded` with
|
||
an empty entity list and empty `AdditionalRenderIds`. The registration is
|
||
created with `WantsLoaded = true` and both reference dictionaries empty, so
|
||
`IsLandblockRenderReady` returns true with no `IWbMeshAdapter` upload. The one
|
||
early-out (`GpuWorldState.cs:913-923`, a stale Far completion over a live Near
|
||
tier) returns `RequiresActivation: false`, but that case is already
|
||
registered. **P1 holds. The fix shape does not collapse.** Its failure is D-1,
|
||
a different transition.
|
||
|
||
Same check for the collision arm, which is equally load-bearing and was not
|
||
called out as a proof obligation: `LandblockPhysicsPublisher.AdvanceBeginOne`
|
||
(`:390-435`) builds the terrain surface from the heightmap with no dat bundle
|
||
and stages it, so a Far publication does register terrain collision;
|
||
`AdvanceDemotion` (`:675-683`) calls `DemoteCollisionToTerrain`, which
|
||
**retains** terrain. `IsNeighborhoodTerrainResident` therefore stays satisfiable
|
||
across a demote. The render arm is the only one that breaks.
|
||
|
||
**Reveal-hang sweep (attack 1) — all other configurations converge.**
|
||
|
||
| Configuration | Result |
|
||
|---|---|
|
||
| Map edge (coords clamped 0/254) | Safe. Gate skips `> 254` (`StreamingController.cs:265-266`); `StreamingRegion` loads up to `0xFF` (`:80`). The loaded set is a strict superset of the required set, and `PhysicsEngine.cs:157` uses the identical bound. |
|
||
| Dungeon → outdoor | Safe. `TryCommitOriginRecenterCore:1235-1251` clears `_collapsed` for a non-dungeon destination and nulls `_region` so the next tick bootstraps the full window. |
|
||
| Indoor destination | Safe. `RequiredWindow` returns `(0,0)` before touching the live window (`WorldRevealReadinessBarrier.cs:206-215`), so no streaming state can gate it. |
|
||
| Recenter in flight | Safe. `Tick` is blocked while `_originRecenterRetirement` is open, radii requests defer, and `_loaded.Clear()` (`GpuWorldState.cs:1481`) guarantees every member of the rebuilt window gets a fresh publication and registration. |
|
||
| Destination/observer landblock offset | Safe **during a hold**: `StreamingFrameController.SelectObserver:194-200` pins the observer to `_origin` while `PlayerState.PortalSpace`, so the gate's inner ring and the streaming Near ring are the same square. Worth noting the new inner arm has **zero** margin here where the old one had `NearRadius − 1`; the pin is now load-bearing. |
|
||
| `FarRadius == 0` on an outdoor destination | Not reachable from presets (min far = 5); reachable only via R-1. |
|
||
|
||
**D6 allocation fix — real, and the sabotage number is honest.**
|
||
`PhysicsEngine.cs:49-52,146-149`. Single production call site
|
||
(`SessionPlayerComposition.cs:384`), main thread, leaf method — the
|
||
instance-owned scratch is safe, and it is cleared at entry so no stale state
|
||
can leak across sessions.
|
||
|
||
**Test discipline — good, with two gaps.** No test re-encodes the constant
|
||
under test: every radius assertion references the fake window's own input
|
||
(`WorldRevealReadinessBarrierTests`, `WorldRevealDerivedWindowIntegrationTests`).
|
||
No new `Skip=`. No test deleted — one was renamed
|
||
(`OutdoorReveal_JoinsNearRenderTexturesAndTerrain` →
|
||
`RequiredWindow_IsRereadOnEveryEvaluationWithoutReconstruction`) with its
|
||
original assertions carried into a new
|
||
`OutdoorReveal_JoinsRenderTexturesAndTerrainOverTheDerivedWindow`; coverage is
|
||
preserved and the +36 arithmetic holds.
|
||
|
||
Gaps:
|
||
- No test exercises a demote. That is D-1.
|
||
- `WorldRevealDerivedWindowIntegrationTests` advertises itself as end-to-end
|
||
against "the REAL `StreamingController`, `GpuWorldState`, and
|
||
`PhysicsEngine`", but constructs `new GpuWorldState()` with no spawn
|
||
adapter, so `IsRenderReady` degenerates to `IsLoaded` via the
|
||
`?? true` at `GpuWorldState.cs:181`. The single most load-bearing predicate
|
||
in the whole change is stubbed out by a null in the test named after it.
|
||
It also builds its own `revealWindow` lambda rather than the production one
|
||
in `SessionPlayerComposition.cs:373-379`, so the
|
||
`ApplyRevealRadiusOverride` wrapper is never exercised in composition.
|
||
|
||
**Sabotage spot-checks — 4 of 9 families reproduced (asked for ≥ 3).**
|
||
|
||
| Sabotage | Named test(s) | Result |
|
||
|---|---|---|
|
||
| `PhysicsEngine`: scratch → `new HashSet<uint>()` | `WarmedNeighborhoodQuery_AtTheFarRadius_AllocatesNothing` | FAIL, **"allocated 27,712,000 bytes"** — S5's claimed figure reproduces to the byte |
|
||
| `StreamingController`: revert tier split to `!IsNearTier \|\| !IsRenderReady` | `TieredWindow_OuterRingFarTierMemberIsResident`, `TieredWindow_AbsentOuterRingMemberIsNotResident`, `TieredWindow_MapCornerDestinationConvergesAtAWideRadius`, `OutdoorReveal_HoldsUntilTheWholeDerivedWindowIsPublished` ×2, `LoginReveal_UsesTheSameWidenedGateAsPortalArrival` | 6 FAIL |
|
||
| `RuntimeWorldTransitState`: shape → `== (isIndoor ? 0 : 1)` | `OutdoorReadinessShape_AcceptsAnyDerivedStreamingRadius` | 6 FAIL (all inline radii) |
|
||
| `WorldRevealCoordinator`: neuter `ReconcileDestinationReservationRadius` | `MidHoldRadiusChange_ReopensTheReservationOnTheSameGeneration` | 1 FAIL |
|
||
|
||
All four reverted; `git status` clean afterwards. No sign of the C5b-D3 class
|
||
(a test that passes with its own change reverted) in the families checked.
|
||
|
||
**Suite at HEAD `fafc0b65`, Release, verified by running it:**
|
||
`11,179 passed / 4 skipped / 0 failed` — exactly the expected
|
||
11,178 + 1 Core settler test. Per project: UI 546, Content 124, Runtime 1217,
|
||
Cli 4, Bake 15, App 4157/3 skips, Headless 89, Core.Net 764, Core 4263/1 skip.
|
||
None of #302/#308/#321 surfaced in this run and none are conflated with the
|
||
finding above.
|
||
|
||
**Process rules — clean.** No suppression flag, grace period, retry loop, or
|
||
symptom guard was introduced. `ACDREAM_PROBE_REVEAL_RADIUS` lives in a
|
||
diagnostic owner per Code Structure Rule 5 and is correctly argued as a
|
||
measurement probe rather than a shipped knob. `AD-2` was amended and `AP-149`
|
||
filed in the same commit, satisfying the divergence-register rule. The
|
||
`ACDREAM_STREAM_RADIUS` CLAUDE.md correction is accurate against
|
||
`SessionPlayerComposition.ComposeCore:249-257`.
|
||
|
||
---
|
||
|
||
## Recommended disposition
|
||
|
||
Do not ship the gate widening until D-1 is closed. The two shapes worth
|
||
considering, in preference order:
|
||
|
||
1. **Make a demote re-register the Far tier.** The demote already leaves a
|
||
fully valid Far-tier landblock behind; the registration it drops is the
|
||
*Near* mesh set. `LandblockRetirementStage.MeshReferences` on a `NearLayer`
|
||
ticket should release the Near references and then re-assert an empty
|
||
Far registration, exactly as `PublishAsFar` does — i.e. the same
|
||
`OnLandblockLoaded(landblock, empty)` call the Far publication makes.
|
||
This makes `IsRenderReady` mean "drawable at its current tier", which is
|
||
what both the render path and the gate already assume it means.
|
||
2. **Re-publish demoted members inside a destination reservation.** Weaker:
|
||
it fixes the gate without fixing the predicate, and leaves the next caller
|
||
of `IsRenderReady` holding the same trap.
|
||
|
||
Whichever is chosen, it needs a test at the demote transition —
|
||
`Near publish → demote → IsRenderNeighborhoodResident(dest, near, far)` — and
|
||
the `WorldRevealDerivedWindowIntegrationTests` fixture should be given a real
|
||
`LandblockSpawnAdapter` so its `IsRenderReady` stops being a tautology.
|
||
R-1 and R-2 are one-line and one-decision respectively and can ride along.
|