Fix: dynamics' ATTACHED emitters (portal swirls on server-spawned portal
entities, creature effects) fell through EVERY particle filter under the
unified pview path - the landscape slice filter carries outdoor statics
(+ the #118 outside-stage dynamics), the per-cell callback carries cell
statics, and T4 deleted the clipRoot==null global pass from normal
frames. T5 never checked portals; the user's re-gate caught it ("all
portals that were previously showing are now gone"). DrawDynamicsLast
now hands its cone-surviving dynamics (minus outside-stage entities,
whose emitters already drew in the landscape slice - alpha particles
must not double-draw) to a new DrawDynamicsParticles callback;
GameWindow draws Scene-pass emitters filtered to those owner ids,
mirroring DrawRetailPViewCellParticles. Retail shape: emitters draw
with their owner object.
Re-gate ledger (user verdicts are axioms):
- #117 CLOSED ("Yes solved"), #118 CLOSED ("Yes solved" + NPC-through-
door "Yes fixed").
- #108 REOPENED narrowed: cellar-ascent eye-below-grade window only
(grass covers the exit door until the head pops over ground level);
fix belongs on the membership/viewer side - the depth-gated punch
stays (DO-NOT-RETRY).
- #119 user split: phantom walkable stairs at the hill cottage (#113
family), tower missing stairs + barrel (#119 proper), hill-house
transparent-on-entry (#112 - re-check after the #120 fix; the
ping-pong fired at exactly A9B3 0103/010F).
- #120 FIXED pending re-gate (dede7e4).
- NEW #122 window oscillation on entry (re-check after #120 first),
NEW #123 buildings transiently disappear running close past,
NEW #124 far-building back walls missing through openings (lead:
per-building look-in floods run only for outdoor roots -
NearbyBuildingCells is null for interior roots; retail runs the
look-in inside LScape::draw for ANY root).
Suites: App 236, Core 1419+2skip, UI 420, Net 294.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Issue119UpNullGfxObjDumpTests pins the dat truth: 0x010002B4 = 9 polys,
ALL NoPos, all surfaces Base1Solid; 0x010008A8 = 1 poly, NoPos,
Base1Solid|Translucent. Retail's skipNoTexture never draws either model
(the BR-1 build-time-skip <=> draw-time-skip equivalence), so
ObjectMeshManager's empty render-data cache is the CORRECT terminal state
- the only defect was the alarming "permanently invisible" log line,
reworded into an honest tripwire pointing at the dump test.
Second fact, same test (ShellModel_NoTexturedPolyIsDropped): on the
hall/tower shell 0x010014C3, ZERO textured polys are dropped by the
extraction gates (137/149 draw; the 12 dropped are the known #113
no-draw orphans) - the per-poly GfxObj extraction is exonerated for
building shells, kept green as a regression pin.
Net for #119: the missing tower-stair parts are NOT the up-null pair and
NOT a per-poly extraction drop. Remaining hypothesis space (interior
stair-cell flood admission, or a different model than assumed) needs the
re-gate to identify the exact tower; then the cell set + flood replay
headlessly like #118. ISSUES.md updated.
Suites: App 232, Core 1419+2skip (1416+3 new), UI 420, Net 294.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Root cause (pinned by the new deterministic exit-walk harness, NOT guessed):
under an interior render root, the exit-portal SEAL stamps the door fan at
TRUE depth after the gated full depth clear, and T1's "ALL dynamics last"
pass then drew the outdoor-classified player depth-tested - every fragment
beyond the door plane z-failed against the seal across the whole aperture.
Harness measured the full window: from the moment the sphere center crosses
the plane until the eye follows (~2.6 m of camera lag, ~2.2 s at walk speed)
the player is invisible; while straddling, the beyond-plane body half clips
at the plane. The handoff's three cone-level candidates are all EXONERATED:
the cone walk passes every step; (eye, ViewerCellId) come from the same
SweepEye call with camera-update-before-visibility-read in the same frame;
the side-test window is sub-epsilon under healthy resolution.
Retail oracle (grep-named-first): PView::DrawCells 0x005a4840 runs
LScape::draw FIRST (pc:432719), then the gated depth clear (pc:432731-32)
and the exit-portal seals (pc:432785-86); outdoor cell objects draw inside
the landscape stage (DrawBlock 0x005a17c0 -> DrawSortCell pc:430124), and
an object draws once per overlapped shadow cell (pc:430056-64) - the
straddling body composes from both stages, neither half clips.
Fix: RetailPViewRenderer assigns dynamics to the OUTSIDE stage under an
interior root when outdoor-classified OR sphere-straddling an exit-portal
plane of their flood-visible cell (DynamicDrawsInOutsideStage - pure, the
harness drives it as the ordering contract); they ride the landscape slice
draw (pre-clear, seal-protected) with the same per-slice cone test as
outdoor statics. Indoor dynamics keep the last pass (retail loop C);
straddlers draw in both (retail shadow dual-draw). Outdoor roots keep
all-dynamics-last - the BR-2 punch-after-dynamics lesson (88be519) stands.
Apparatus: HouseExitWalkReplayTests - dat-backed corner-building exit walk
driving the production stack headlessly (RetailChaseCamera damping ->
healthy-sweep viewer resolution -> PortalVisibilityBuilder.Build ->
ClipFrameAssembler -> ViewconeCuller -> the DrawDynamicsLast predicate +
a CPU seal-depth model). 5 tests: cone pin, seal-depth pin, straddle
dual-draw pin, per-step table, stale-root window quantifier (#118 cand 2).
Suites: App 232 (227+5), Core 1416+2skip, UI 420, Net 294.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Read-level exonerations: the local player routes to Dynamics correctly
(ServerGuid set), and its entity ParentCellId syncs per tick from the
controller - neither is the vanish mechanism. Live candidates are the
doorway-crossing decision stack: (a) eye/cell incoherence under camera
damping (the verified #115/BR-8a divergence - we damp from our own
damped eye while the root comes from the swept cell), (b) the
exit-portal side test culling the OutsideView when the eye is
epsilon-outside while the root is still interior (retail's
AdjustPosition demotes the viewer cell the same moment), (c) the
aperture-cone tightness for an outdoor player with an indoor viewer.
Next step is the deterministic exit-walk harness (all-CPU drive of the
production decision stack over the corner-building cells); designed in
the issue entry, queued for a focused session.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Investigation: retail's growth propagation RECURSES natively too
(AddViewToPortals -> FixCellList -> AdjustCellView -> AddViewToPortals,
Ghidra 0x005a52d0/0x005a5250/0x005a5770, no depth guard) - the in-place
recursion shape is faithful; retail's safety is fast convergence. Our
depth-128 firing means slow/non-saturating growth (each lap of a portal
cycle nests one recursion level), not necessarily a true infinite loop.
Two dat-backed sweeps over the corner-building cell set could NOT
reproduce the T5 firing:
- PortalPlaneCrossings_InPlacePropagationConverges: +/-6cm eye sweep
across every portal plane, seeded from both sides.
- InCellDirectionSweep_InPlacePropagationConverges: 3024 builds, in-cell
eye grid x 8 yaw x 3 pitch (the walking-and-turning regime).
Both pass with 0 firings -> production-only ingredients suspected (full
lookup graph - one T5 firing was 0x0162, another building - and/or the
real camera path).
Armed: PortalVisibilityBuilder.ConvergenceTripwireCount (test
observable, both Build + look-in sites) + DumpPropagationChain - on the
next firing the log carries root cell, eye, per-cell frequency summary,
and the 24-entry chain tail, so the cycle's structure (A<->B ping-pong
vs 3-cycle laps) reads directly off the output. Both sweeps stay as
regression pins.
App tests: 227 green (was 225; +2 pins).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The A6.P4 port, fused into one installment per the BR-2 half-port lesson
(registration and query are co-dependent: flood-registering shells under
the old radial query would re-open #98 through the vestibule).
REGISTRATION (ShadowObjectRegistry rewritten):
- Register/RegisterMultiPart/UpdatePosition compute the cell set via
CellTransit.BuildShadowCellSet (the C2 find_cell_list flood) seeded by
the entity's m_position cell id; the private 24m XY-grid rectangle and
its single-landblock clamp are deleted. Flood spheres follow retail's
CylSphere rule (base point + cyl radius, cap 10; BSP bounding-sphere
fallback - Ghidra 0x0052b9f0). Statics flood with the do_not_load
prune; dynamics (server spawns, isStatic:false) without.
- Keep-when-empty (SetPositionInternal num_cells gate, pc:283540): a
failed flood leaves the previous registration in place.
- RefloodLandblock: streaming-race hook re-runs the flood when a
landblock's cells hydrate (retail init_objects -> recalc_cross_cells,
Ghidra 0x0052b420/0x00515a30); wired at GameWindow's hydration tail.
- GameWindow sites pass the server position's full cell id as the seed
(spawn + UpdatePosition); the five static sites pass ParentCellId.
BUILDING CHANNEL (CSortCell.building shape):
- Building SHELLS are not shadow objects in retail (only caller of
find_building_collisions is CSortCell::find_collisions 0x005340aa;
one building per origin landcell, init_buildings 0x0052fd80 verified
verbatim + ACE cross-ref). IsBuildingShell entities skip the registry;
Transition.FindBuildingCollisions runs the shell part-0 BSP off
cache.GetBuilding(cellId) with bldg_check set around it
(find_building_collisions 0x006b5300), CollidedWithEnvironment on
non-Contact non-OK. BuildingPhysics.ModelId = pre-resolved part-0
GfxObj (0x02 Setups resolved at the CacheBuilding site).
- Placement/ethereal weakening: BSPQuery Path 1 passes center_solid=0
when BldgCheck && HitsInteriorCell (BSPTREE::find_collisions 0x0053a82e
+ placement_insert 0x005399d8) so doorway crossings don't hard-fail
against shell solids. SpherePath gains both retail fields;
HitsInteriorCell is rebuilt at every cell-array build
(build_cell_array reset 0x00509ef2 + find_cell_list/check_building_
transit set sites).
QUERY (retail per-cell order, transitional_insert 0x0050b6f0):
- TransitionalInsert per attempt: env -> building (LandCell only) ->
objects on the PRIMARY cell, then on OK the check_other_cells pass
(env -> building -> objects per OTHER overlapped cell) + the
carried-cell advance - the advance now happens AFTER all per-cell
object passes (the WF1 ordering divergence), with Adjusted/Slid
feeding the retry exactly like retail's OK_TS case.
- FindObjCollisionsInCell = CObjCell::find_obj_collisions (0x0052b750):
iterate ONLY the asked cell's list. DELETED: the radial 9-landblock
sweep, the +5m query pad, the b3ce505 indoor-primary gate, and the
isViewer exemption (the camera is bounded by interior cell-BSP env
collision - retail's own channel; CameraCornerSealReplayTests pins it
against real dat, and the new building-channel camera test pins the
outdoor stop).
TESTS: Core 1416/0/2 (was 1398 + 4 pre-existing #99-era fails + 1 skip),
App 225, UI 420, Net 294 - all green.
- 3 of the 4 #99-era reds flipped green as designed: the door apparatus
(Apparatus_Grounded_50cmOffCenter_FrontApproach_Blocks) and tick-13558
(indoor walkthrough) now assert the door BLOCKS; tick-22760 pins the
outdoor blocking invariant.
- The 4th (BSPStepUp D4) + 22760's lateral-slide delta are NOT cell-set
problems (probes prove the door is found + BSP-only dispatched;
BR-7 left both byte-identical) - filed as issue #116 (slide-response
family), D4 skipped with the issue reference.
- FindEnvCollisionsMultiCellTests migrated to the public entry (the A4
multi-cell halt now lives at the retail call site).
- New registry pins: per-cell query surface, outdoor-footprint-never-
indoor (#98 architectural), door-outdoor-cell membership, reflood.
- CameraCollisionIndoorTests rewritten against the building channel
(the isViewer-exemption pins died with the exemption).
Closes#99 (doors block both ways via registration-time cell membership
+ the straddle-spanning player cell array). #97 likely closed (the +5m
radial pad that produced phantom-collision candidates is gone) - verify
at T5. #98 stays closed ARCHITECTURALLY (outdoor footprints structurally
cannot reach interior cells; the cellar harness stays green).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The BR-2 punch/seal gate proved #108 (cellar grass-sweep) is a membership
flip (player classified outdoor mid-cellar), not a render depth bug. The
punch only masked it on outdoor-root frames. Move #108 to the membership
track; the interior depth seal is a separate mechanism that does not fix it.
User mandate: stop bug-by-bug; map acdream-vs-retail for building draw,
interiors, interior collision, dynamics, clipping, culling; plan the port
of retails drawing discipline once and for all. The handoff carries the
branch state (124c6cb, nothing on main), the full evidence inventory from
this session (orphan no-draw polys, door-vanish mystery, draw-side clip
status, straddle gate), the gap map, tooling (Ghidra MCP 8081 correct
PDB, live cdb protocol, dat dump + flood harnesses), the investigation
charter (workflow fan-out per subsystem, adversarial verification), and
the paste-ready new-session prompt. #113 marked REOPENED and folded in.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
#113 moved to Recently closed: the phantom staircase was the Holtburg
meeting hall (AAB3, not A9B3) interior stair cells drawn unclipped from
outside - the PView shell clip was routed but never GL-enabled (927fd8f).
Misplaced-cell hypothesis refuted with dat evidence. #112 residual
paragraph updated: retail straddle gate live-binary verified + ported
(414c3de); at-doorway demote is retail-faithful, deep gaps now keep-curr.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The 137b4f2 payload, re-landed now that #110 is resolved: the missing-indoor-
textures correlation was the pre-existing #105 staged-texture-flush drop
(fixed in c787201), not a near-plane mechanism. znear=0.1 merely raised #105's
trigger probability — a closer near plane makes close-up geometry newly
visible, inflating per-frame prepare/upload pressure indoors and growing the
never-flushed tail. Exactly the handoff's only-credible-link hypothesis,
verified instead of assumed.
Retail: Render::SetFOVRad sets znear=0.1 flat (decomp :342173, initializer
:1101867). 0.1 < the 0.3m camera-collision sphere, so a wall the collided eye
presses against no longer falls inside the near plane — the §4 corner
see-through-wall closes.
Verification on the 0.1 arm (the arm that struck 2-of-3 on 2026-06-10):
nearplane-reland-1.log — [tex-flush] after=0 on all 45 lines, 68,291 [shell]
lines with zero zh>0 batches, all four dat tripwires silent, no [wb-error].
ISSUES.md: #105 + #110 moved to Recently closed with root cause + evidence.
Pending user re-gate: corner press (wall stays solid) + distance scan for
z-shimmer (none expected; retail ships 0.1 with D24).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bisect (user-gated): two consecutive runs on 0.1 lost indoor textures; the 1.0 bisect run rendered clean. #105 tripwires silent on the bad runs (GL-side). No known mechanism links the near plane to texturing - #110 filed to investigate (RenderDoc / flip-testing) before re-landing retail's znear=0.1, which the corner see-through fix depends on. Comments on all four cameras point at #110 so the retail value is not re-landed blind.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The collided camera eye sits 0.3m from walls (viewer_sphere radius); a 1.0m near plane clipped the wall face away, so pressing the camera into a corner showed the clear color through the wall (gate result: unchanged by the flood fix - it was never a flood bug). Retail sets Render::znear = 0.1 flat in SetFOVRad (decomp :342173, initializer :1101867). All four cameras aligned. Also files #107 (indoor spawn wedge, 3-for-3), #108 (cellar-up terrain sweep across door opening), #109 (exit-door texture/background oscillation) from the 2026-06-10 visual gate; gate confirms the dac8f6a flood fix: room-room + indoor-outdoor transitions clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Move #106 to Recently closed (user-verified collision + solid walls;
probe-verified 49 clean transitions incl. south A9B4->A9B3 at y=-0.19,
east A9B3->AAB3 at x=192.2, and room-by-room tracking through the
originally-failing A9B3 cottage). Records the three adjacent
pre-existing bugs the gate runs surfaced and fixed (legacy Resolve bare
ids, bogus-indoor-claim recovery, entry-hold streaming deadlock).
Correct the capture doc's attribution: the outdoor running distortion
was NOT fully the stale anchor — gate 4 shows residual background-color
screen artifacts persist with a correctly-following anchor. The
residual is the render §4 flap family (render digest), not membership.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The #106 live gate run was sabotaged by a pre-existing bug the corrective
ACE teleport exposed: PhysicsEngine.Resolve (the legacy Phase-D resolve,
still used by the teleport-arrival snap at GameWindow.cs:4869 and the
player-mode-entry snap at :11295) returned BARE low-word cell ids on
every computed exit (ComputeOutdoorCellId, bestCell.CellId & 0xFFFF,
nextCellIndex, enterCellIndex). The teleport committed 0x0000013F into
PlayerMovementController.CellId, and a bare indoor id wedges the entire
membership chain:
- GetCellStruct(0x0000013F) misses (cells are keyed full-prefix) -> no
indoor wall BSP -> walk through walls;
- the b3ce505#98 gate reads "indoor primary" -> outdoor object radial
sweep skipped -> NO object collision anywhere in the world;
- BuildCellSetAndPickContaining early-returns an unresolvable id forever
(block 0x0000 is a real far-NW map block) -> membership frozen;
- render root never resolves -> interiors draw empty when stepping in.
Probe evidence: probe-cell-106-gate.log has exactly 2 [cell-transit]
lines for the whole session, both reason=teleport — the second one
(0xA9B3003C -> 0x0000013F) is the wedge. This is the L.2e "player CellId
tracked as bare low byte" finding (2026-05-12) finally biting; prefix
survival until now was a race artifact — Resolve only preserved the full
id when the landblock had not streamed in yet (passthrough exit), which
is why login snaps usually came out prefixed.
Fix: capture the matched landblock's key in Resolve's containment loop
and return lbPrefix | (targetCellId & 0xFFFF) on the computed exit —
the same full-32-bit convention Resolve's own doc comment states for
its inputs, and what both production callers (player snaps) require.
The passthrough exits (no landblock / step-reject) still return the
caller's id unchanged.
Tests: Resolve_IndoorStay_ReturnsFullPrefixedCellId (the teleport
shape, red pre-fix) + Resolve_OutdoorStay_ReturnsFullPrefixedCellId;
Resolve_LeaveIndoorCell_TransitionsToOutdoor's unmasked
`CellId < 0x100` assertion codified the bare behavior — now masked +
asserts the prefix. Full suite: 294+218+420 green; Core 1371 green +
the same 4 pre-existing door/#99-era failures + 1 skip.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The player's outdoor cell froze at the last in-block cell the moment they
walked over a landblock boundary (10,449-frame playerCell freeze in the
2026-06-09 capture; whole neighbouring-block interiors unenterable, plus
the running-distortion from the stale render anchor). Root cause: the
add_all_outside_cells port clamped BOTH the candidate proposal and the
find_cell_list containing-cell pick to the current landblock's 8x8 grid,
in a frame that silently assumed the current block sits at world origin.
One step over the line -> zero candidates -> FindCellSet returns
currentCellId forever.
Retail has no such clamp. Its cell math runs in a GLOBAL landcell grid
(lcoord 0..2039 spanning the map): get_outside_lcoord = blockid_to_lcoord
+ floor(blockLocalPos/24) with no bounds besides the map edge, and
lcoord_to_gid re-derives the landblock id from the lcoord's upper bits —
crossings are inherent, never special-cased.
The fix, decomp-cited throughout:
- New AcDream.Core.Physics.LandDefs: in_bounds (pc:68509),
blockid_to_lcoord (pc:68520), inbound_valid_cellid (pc:163438),
gid_to_lcoord (pc:163500), lcoord_to_gid (pc:171859),
get_outside_lcoord (pc:438690), adjust_to_outside (pc:438719).
Cross-checked against ACE LandDefs.cs; three artifacts documented and
avoided: BN's int8_t mis-render of block_y, BN's dropped 192f
BlockLength constant, and ACE add_cell_block's "FIXME!" same-block
guard (an ACE divergence, not retail).
- CellTransit.AddAllOutsideCells rewritten as the faithful sphere
variant (pc:317499 @0x00533630): adjust_to_outside re-seats the
(cell, position) pair cross-block, check_add_cell_boundary (pc:317229)
adds up to 3 neighbours by global lcoord, add_outside_cell (pc:317056)
has no same-block filter. adjust_to_outside failure breaks the sphere
loop (pc:533699 verbatim).
- BuildCellSetAndPickContaining: the outdoor containing-cell pick is now
the global XY-column under the sphere centre (AdjustToOutside), not
the [0,8)-clamped current-prefix reconstruction. Interior-wins order
and current-cell-first hysteresis unchanged.
- World->block-local frame conversion via the landblock origin already
registered in CellGraph (new TryGetTerrainOrigin); Zero fallback
preserves the legacy anchor-block assumption for unregistered terrain.
- Cross-landblock building entry comes free: the candidate snapshot now
contains neighbour-block landcells, so GetBuilding/CheckBuildingTransit
fire for cottages across the line (the capture's one failing entry).
Investigated FIRST per the pickup brief: the b3ce505#98 stopgap gate is
definitively exonerated — it is a collision-object query gate that fires
only for indoor primary cells; no membership path touches
ShadowObjectRegistry.
Tests: 31 new (25 LandDefs conformance incl. capture-geometry goldens
0xA9B40031 -> 0xA9B30038/0xA9B30034 and the northbound return; 4
AddAllOutsideCells cross-block; 3 FindCellSet membership goldens incl.
the non-anchor-frame origin conversion). Full suite: 294+218+420 green;
Core 1369 green + the 4 pre-existing door/#99-era failures + 1 skip
(unchanged from baseline).
Pseudocode + artifact notes:
docs/research/2026-06-09-landdefs-outside-cells-pseudocode.md.
Remaining acceptance: live boundary walk with ACDREAM_PROBE_CELL=1
(ISSUES.md #106).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The 53MB flap-probe capture (user live-reproduced the broken-house entry)
plus 3-agent analysis settles the day: playerCell froze at 0xA9B40031 for
10,449 frames spanning ~130m of outdoor walking into landblock A9B3 and a
stand INSIDE an A9B3 cottage. Within-landblock outdoor flips are 96/96
clean; all 10 successful indoor entries were same-landblock buildings; the
single cross-landblock entry failed. The render flood independently drew the
A9B3 interior cells the whole time — rendering is downstream and healthy;
membership is the broken layer (feedback_render_downstream_of_membership,
proven again). The stale render anchor also explains the outdoor running
distortion; the capture refutes flood-level causes outdoors (26,960/26,960
outdoor frames rigid at outPolys=1 vis=1).
Files #106 (HIGH, physics/membership) with fix pointers: ResolveCellId /
AddAllOutsideCells cross-landblock proposal, the b3ce505 outdoor-sweep gate
(possible stopgap fallout, like #99), retail find_cell_list :308742 +
LandDefs.get_outside_lcoord. Reframes #105: largely superseded by #106;
residual (single wall missing while membership indoor-correct) stays open
with all tripwires armed. Handoff:
docs/research/2026-06-09-105-capture-analysis-membership-landblock-pin.md
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Records the 2026-06-09 dat-reader thread-safety investigation: concurrent
READS on Chorizite.DatReaderWriter 2.1.7 exonerated (source audit + 1.1M-read
hammer, b3920d8); the real crash was dispose-during-read at teardown (fixed,
8fadf77); the white-walls mechanism remains open as #105 with every silent
dat-miss exit tripwired (7433b70) so the next occurrence self-attributes.
Also corrects project lore: the A.1-era rule that all dat reads must stay on
one thread does not hold for the 2.1.7 read path, and both investigation
subagents'' claimed ReadBlock instance-field race does not exist in the
shipped source — verify agent claims against source before acting on them.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Canonical handoff: docs/research/2026-06-03-membership-and-bluehole-shipped-handoff.md
(what shipped: membership Stage 1 ordered-CELLARRAY port + the blue-hole render-root
clobbering fix; the full remaining-issues list — A camera-collision, B R1b particles,
C R2 outside-looking-in, Stage 2 membership, #7 stairs, the 5-test baseline; KEEP/
DON'T-REDO; key files + decomp anchors; copy-paste pickup prompt for next session).
- ISSUES.md: recorded the cottage doorway flap DONE (both causes) in Recently closed.
- render design spec §7: R1 + flap marked DONE; A/B/C mapped to the next render phases.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Phase W indoor seal did NOT land. The 2026-06-02 visual gate proved the interior render is fundamentally broken (#78: transparent walls, outdoor terrain + scenery entities bleeding in, grey floors, no outside-looking-in). Stage 4 (sky-through-door clip) was real but a top layer on a base that never sealed.
DECISIVE EVIDENCE (committed in the handoff): the PVS computes correctly AND the cell shells render correctly (opaque, textured, complete — the [shell] probe shows zero NOSNAP / zero missing-texture). The failure is the SEAL + three inconsistent gates — concretely the WbDrawDispatcher.cs:1756 ParentCellId==null -> return true bypass draws outdoor scenery indoors, and the indoor path draws the outdoor world then gates it instead of running ONLY DrawInside. Retail, when inside, runs ONE PView flood: visibility IS the cull; the landscape enters only through clipped exit portals + a conditional depth-only clear.
Dossier (per the user's mandate: NO shortcuts/bandaids, port from retail, redesign the whole pipeline if needed, brainstorm first):
- Master handoff (root cause + retail target + reusable-vs-redesign + apparatus + do-not-repeat + copy-paste pickup prompt).
- Huge staged redesign plan R0(brainstorm)->R1(one visibility authority, kill the bleed)->R2(indoor=DrawInside-only)->R3(the seal, DrawCells port)->R4(per-cell object/particle clip)->R5(outside-looking-in)->R6(dungeons)->R7(polish/conformance). Each ends at a user visual gate.
- 3 research docs: full retail render pipeline reference (705 lines, decomp-verified), acdream pipeline inventory + failure map, reference cross-check (WB two-pipe is the wrong model).
#78 promoted to the redesign. The 5 remaining Core test failures are pre-existing physics/collision bugs, none render-related.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
GetMaxSpeed deliberately does NOT branch on ForwardCommand — it returns RunAnimSpeed x run-rate as the InterpolationManager.AdjustOffset catch-up speed (doc comment + ACE MotionInterp.cs:670-678, retail-verified; the slow catch-up fixed the 1-Hz remote-blip). The 3 failing tests (WalkForward/WalkBackward/Idle) asserted a REMOVED command-branching design. Consolidated into one [Theory] pinning the no-branch contract across commands.
Also files #104 (LOW): scene VFX particles not clipped to the PView visible cell set — deferred out of the Phase W seal (entity bleed already gated by Stage 5; scene particles depth-tested; sky particles scissored). Needs OwnerCellId plumbing (~6-8 files).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The architecture and ISSUES edits in the prior commit (0013819) failed silently because
they were anchored on the session-reminder's rendering of the files, not the real text.
Redone against actual content:
- architecture doc: new 'Render Pipeline (SSOT)' section — the 3-gate patchwork vs the
unified-PView target + the one rule (compute visibility once, enforce it once).
- ISSUES #78: promoted to the render-architecture-reset target; points to the canonical
handoff + the architecture section.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Canonical handoff (research note) for the U.4c flap fix + the three residuals the
visual gate revealed (#78 terrain-not-gated-inside, camera-collision need, U.5).
Records the full hypothesis journey (H1/H2 both evidence-disproven) so the next
session doesn't re-walk them. ISSUES.md: flap recorded in Recently-closed; #78
annotated (more visible post-fix). CLAUDE.md: U-phase orientation updated with the
flap-fixed status + the canonical handoff pointer + camera-collision-next.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Code review caught a CRITICAL under-inclusion: ApplyReciprocalClip scanned for the
first OtherCellId match, so a cell with two portals to the same neighbour clipped both
near-side openings against the FIRST reciprocal polygon — hiding geometry through the
second opening (real on Holtburg cellar cells 0x148<->0x149). Plumb the dat's
OtherPortalId back-link through CellPortalInfo + BuildLoadedCell and index the reciprocal
directly (retail arg2->other_portal_id, 433557). Skip (degrade to over-include) when the
index is unresolvable — never clip against a guessed polygon. Adds a disjoint two-back-
portal regression test.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Clip the portal opening against the neighbour's matching back-portal polygon
before propagating, so a cell's clip region is the intersection of the opening
seen from both sides. Closes the M-4 stub in ISSUES #102. Can only tighten,
never under-include; degrades to prior behavior when no back-portal is found.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Code-review minor follow-ups: correct the CellTodoList comments (ties are LIFO,
not FIFO — an equal-distance newcomer lands at the tail and pops first, matching
retail's break-on-first-not-greater + pop-from-tail). Update ISSUES #102 to record
that U.2a closes I-1/I-2 (under-count + duplicate accumulation) via the enqueue-once
gate, narrowing the residual to diamond-topology clip-completeness (AddToCell onward
re-propagation, tracked under U.6).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Decision (2026-05-30, with user): the WB-inherited two-pipe (inside/outside) render
split is the root cause of the indoor seam bugs (flap, missing/transparent walls,
terrain bleed) and cannot be seamless. Abandon A8/A8.F (#103); build ONE unified
pipeline driven by retail's PView portal visibility — seamless by construction. The
2026-05-30 camera-collision + physics viewer-cap work is kept (retail-faithful, but a
detour from the seam fix). New Phase U scoped; #103 superseded; CLAUDE.md / roadmap /
milestones updated; full decision + scope + next-session pickup prompt in
docs/research/2026-05-30-unified-render-pipeline-decision-and-handoff.md.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Lands the working A8 indoor-rendering and streaming fixes accumulated this
session. User has verified these visually to some degree (e.g. lifestone /
translucent meshes confirmed fine under the FrontFace flip; bridge / wall /
collision regressions confirmed fixed after travel); not every path has been
exhaustively gated. The cellar-flap defect remains OPEN and will be solved
the retail-faithful way via a dedicated brainstorm (see handoff docs).
Rendering core (reviewed, high confidence):
- EnvCellRenderer SSBO stride fix: upload packed Matrix4x4[] (64B) instead of
the 80B CPU InstanceData struct the shader never expected — fixes the
transform/texture "explosion" for any draw with >1 instance (cells that
dedupe to a shared cellGeomId). Real root cause.
- WB-style global FrontFace(CW) + per-batch CullMode carried through the MDI
layout (GroupKey + BuildIndirectArrays + DrawIndirectRange split into
same-cull runs with absolute uDrawIDOffset per run).
- EntitySet partitioning (IndoorPass / OutdoorScenery / LiveDynamic) +
WorldEntity.BuildingShellAnchorCellId so building shells scope to their
dat-derived building cell instead of rendering everywhere.
- RenderOutsideInAcdream (look into buildings from outside) +
CollectVisiblePortalBuildings frustum cull of portal bounds.
- Sky-when-inside-building + per-cell audit probe + GL-state probe.
Streaming / perf (test-covered; not independently code-reviewed this session):
- Near/far priority queues so near work wins over far; PromoteToNear carries
full landblock + mesh data; LandblockEntriesWithoutAnimatedIndex avoids
rebuilding the animated-lookup dict in the hot draw path. Fixes the
bridge-not-appearing / missing-walls / broken-collision-after-travel
regressions and improves post-transition FPS.
Tooling + docs:
- tools/A8CellAudit: offline dat cell/portal/building dumper (portals +
buildings modes) — reproduces the cellar-flap investigation with no launch.
- docs/research cellar-flap root-cause + option-2 handoff (the didInsideStencil
double-duty finding + the WB-recursive design decision + brainstorm prompt),
entity-taxonomy, replan, issue-78 visibility investigation.
Diagnostics retained on purpose: ACDREAM_A8_DIAG_* gates, portal_stencil.vert
provisional pos.w clamp, and the probe families are kept (env-var gated, zero
cost when off) because the pending option-2 cellar-flap brainstorm needs them.
Strip in the option-2 ship commit.
Indoor branch stays behind ACDREAM_A8_INDOOR_BRANCH=1 (default off = pre-A8
visual). Build green; App tests + Core (streaming/dispatcher/loader) tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Session-end documentation for the issue #100 ship and the visibility-
culling investigation handoff for the next session.
Three documents land together:
- docs/superpowers/plans/2026-05-25-issue-100-terrain-cutout.md
(the 3-task plan that drove this session's f48c74a / a64e6f2 /
84e3b72 — never committed by Tasks 1-2)
- docs/research/2026-05-25-issue-100-terrain-cutout-handoff.md
(the predecessor session's smoking-gun research that drove the
#100 fix — never committed by the prior session)
- docs/research/2026-05-25-issue-100-shipped-and-culling-handoff.md
(THIS session's handoff: what shipped, what visual-verification
surfaced, the issue family map for #78 + #95 + the new cellar-
stairs finding, root-cause hypothesis, retail anchors, WB
references, do-not-retry list, and pickup prompt for the next
session's investigation + plan + implementation)
Plus two updates to existing files:
- CLAUDE.md — adds a ship paragraph for #100 to the M1.5 progress
block. References the new handoff doc as the next-session pickup
point.
- docs/ISSUES.md #78 — broadens scope from "outdoor stabs visible
through floor" to "outdoor stabs + terrain mesh visible inside
EnvCells". Adds the 2026-05-25 cellar-stairs evidence (per user
direction: not filed as new issue; treated as evidence
reinforcing #78's hypothesis #2). Promotes hypothesis #2 to
"high confidence as of 2026-05-25" and adds the retail anchor
(acclient_2013_pseudo_c.txt:311397 CEnvCell::find_visible_child_cell).
Acceptance criteria broadened to include the cellar-stairs case.
Next session: pickup prompt at the bottom of the new handoff doc
drives a /investigate → writing-plans → subagent-driven-development
pass on indoor-cell visibility culling — the work that closes#78
+ cellar-stairs together, and possibly #95 if the infrastructure
overlaps.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Task 2's commit landed at 64518d59, then an amend (to fix the original
placeholder SHA in the same file) produced the new HEAD a64e6f2 with
identical content. The in-file SHA still pointed at the pre-amend
64518d59 — reachable today only via reflog, unreachable after the next
git gc. Switch to a64e6f2 which is on the branch and survives gc.
This is a follow-up commit (not an amend) so the canonical SHA is
itself stable on the branch from this commit forward.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Retired in favour of Task 1's retail-faithful terrain shader Z nudge.
Pure removal — ~50 LOC of dead surface area across:
- src/AcDream.Core/Terrain/LandblockMesh.cs (drop parameter +
cell-collapse block)
- src/AcDream.Core/World/LoadedLandblock.cs (drop field)
- src/AcDream.Core/World/LandblockLoader.cs (drop method + call)
- src/AcDream.App/Rendering/GameWindow.cs (3 sites)
- src/AcDream.App/Streaming/GpuWorldState.cs (6 ctor sites)
- src/AcDream.App/Streaming/LandblockStreamer.cs (1 ctor site)
- tests/AcDream.Core.Tests/World/LandblockLoaderTests.cs (drop test)
- tests/AcDream.Core.Tests/Terrain/LandblockMeshTests.cs (drop test)
No retail anchor — the deleted mechanism never had one; this commit
rolls our code back to the actual retail behaviour established in
the prior commit's shader nudge.
ISSUES.md #100 moved to Recently closed.
Cross-ref:
docs/research/2026-05-25-issue-100-terrain-cutout-handoff.md
docs/superpowers/plans/2026-05-25-issue-100-terrain-cutout.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Code-review found four Important findings in the ISSUES.md entry
that landed in 381561f:
- broken relative link to the plan (../superpowers/... vs
superpowers/...)
- wrong test class name (PhysicsDataCacheTests, the actual
class is PhysicsDataCachePhantomSourceTests)
- wrong predicate description (referenced PhysicsRadius and
vAabbR; the predicate only checks the high byte and the
cached BSP root)
- fabricated method names (GameWindow.RegisterGfxObjShadow and
ShadowShapeBuilder.FromGfxObj — neither exists)
This commit corrects all four. The verification evidence in
the entry was accurate and is preserved.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Records the A6.P8 mesh-aabb-fallback suppression ship outcome
in CLAUDE.md and moves issue #101 to Recently closed. Visual
verification confirmed end-to-end ramp climb on GfxObj 0x01000C16's
BSP (walkable inclined polygon, Normal.Z=0.717).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Visual verification of A6.P7 at Holtburg cottage door passed cleanly
(1187 [cyl-skip-bsp] guard fires, 0 [cyl-test] on doors, 30/30
axis-aligned hits, smooth NE/SE slide along door face). While
exploring post-verification, the user discovered a different
staircase in cells 0xA9B40159 + 0xA9B4015A where the sphere cannot
climb at all.
Captured working baseline (stairs-working.jsonl, cottage cellar
stairs in cells 0xA9B40143/146/147 — clean ↔ Z=90.95-94.00 traversal)
and broken scenario (stairs-broken.jsonl, Z stays at 94.00 the entire
4216-record capture).
Root cause is NOT a regression of A6.P7. It's a different bug shape:
the staircase is built as a multi-part EnvCell entity (entityId
0x0040B500, ~150 parts), with 10 of those parts being 0.80m-radius
cylinders forming the steps. Each cyl carries state=0x00000000 — no
HAS_PHYSICS_BSP_PS — so A6.P7's BspOnlyDispatch guard correctly
doesn't fire. Cyl height 0.80m exceeds A6.P6's step-up budget 0.60m
so grounded step-over fails. Falls through to wall-slide which
produces the same diagonal radial phantom A6.P7 closed for the door.
The [resolve-bldg] lines reveal gfxObj=0x0100081A hasPhys=False
bspR=0.00 vAabbR=0.82 — the underlying GfxObj has NO physics BSP;
we appear to be synthesizing a cyl from the visual AABB radius. That
synthesis path is the suspected misregistration.
Filed as issue #101 with severity HIGH. Investigation handoff written
covering 4 retail-research questions (cdb on retail at this stair
location, Setup trace via entity-source probe, ShadowShapeBuilder
vAabbR fallback audit, cell BSP poly dump), do-not-retry list, and 3
candidate fix shapes (don't synthesize cyl from vAabbR / cell BSP for
stairs / cyl-height-tolerant step-over). The handoff explicitly
defers implementation to a later session pending retail evidence.
Files:
- docs/research/2026-05-25-stairs-cyl-investigation-handoff.md (new)
- docs/ISSUES.md — added #101 entry
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Knowledge-preservation pass after the issue #98 cellar-up fix shipped
(`b3ce505`). Closes the saga's documentation loop and plans the next
phase.
Changes:
- docs/research/2026-05-23-a6-p3-issue98-comparison-harness-findings.md
Appended "Resolution 2026-05-24" section: v3 hypothesis falsified,
actual mechanism (head-bump cottage GfxObj floor poly from below)
confirmed, b3ce505 fix shipped, known door regression flagged.
Memory artifacts cross-referenced.
- docs/ISSUES.md
#98 moved to DONE with full resolution writeup + decomp anchors.
#99 filed: door regression at building thresholds (caused by
b3ce505's indoor-primary gate). Closes via A6.P4.
#100 filed: transparent rectangular patches around houses
(terrain rendering). Bisect found commit 35b37df introduced the
hiddenTerrainCells mechanism that collapses 24m outdoor cells
when buildings sit in them; cottage building only fills part of
its cell so the rest of the 24m cell shows the sky-bleeding gap.
Three fix-path options documented.
- docs/superpowers/specs/2026-05-24-phase-a6-p4-retail-shadow-architecture.md
Full A6.P4 design doc. Three-slice plan: (1) query-side portal
expansion to close#99 while preserving #98 fix, (2) port retail's
BuildShadowCellSet at registration time so per-cell semantics match
`CObjCell::find_cell_list`, (3) remove b3ce505 stopgap entirely.
Decomp anchors, file-by-file plan, risk inventory, open questions.
Memory entries written separately (out-of-tree at
~/.claude/projects/.../memory/):
- feedback_retail_per_cell_shadow_list.md
The architectural lesson: retail uses per-cell shadow_object_list
with portal-aware registration; our landblock-wide spatial
registry diverges at indoor/outdoor seams.
- feedback_apparatus_for_physics_bugs.md
The apparatus-first pattern that cracked the saga: live capture +
fixture dump + replay harness. Template for future physics bugs.
Quote rule: "when a physics bug is resisting and you catch
yourself about to ship 'fix attempt N+1 with no new evidence,'
STOP. Build the apparatus first."
- MEMORY.md index updated with both new entries.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the canonical pickup document
docs/research/2026-05-23-a6-p3-issue98-harness-handoff.md with:
- TL;DR + session arc (10 commits chronological)
- What the trajectory replay harness IS (committed apparatus)
- Bug 1 status: #98 cellar-up freeze (unfixed, 6 fix shapes failed)
- Bug 2 status: airborne-at-tick-1 (new, 6 hypotheses tested, root
cause not isolated)
- Exclusion list: DO NOT retry any of the 6+6 dead ends
- Apparatus inventory: probes, tests, fixtures, cdb captures
- Recommended next move: side-by-side comparison harness against
live PlayerMovementController state (evidence-first instead of
speculation-first)
- Alternative moves: pivot to other M1.5 issues or M2 prep
- Self-contained pickup prompt at the bottom of the handoff doc
Updates CLAUDE.md's "Current A6 phase" block to point at the new
handoff doc as the canonical resume artifact.
Updates ISSUES.md's #98 entry with the late-day extension findings,
the 6-hypothesis exclusion list, and a pointer to the handoff doc.
Test baseline maintained at 1172 + 8 pre-existing failures.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Updates ISSUES.md and CLAUDE.md to reflect the actual state of #98
after two days of work:
- The new [step-walk-adjust] probe (8a232a3) + capture + findings
(8daf7e7) prove AdjustOffset's slope projection is CORRECT. Sphere
Z climbs monotonically 90.95 -> 92.80 across the ramp at +0.045 m
mean zGain per call. The earlier "Fix targets 1-4" priority list
is OBSOLETE — AdjustOffset is not the bug.
- The climb caps at world Z ~= 92.80 because step-up's downward
step-down probe finds no walkable within stepDownHeight=0.6 m
below the proposed position. Cottage floor at Z=94 is ABOVE, not
below. 101 stepdown-reject hits in the capture vs 1 acceptance.
- Shape 1 fix attempted (0cb4c59): gated BSPQuery.AdjustSphereToPlane's
two SetContactPlane call sites by Normal.Z >= 0.99 to match retail's
cdb-observed flat-CP-only pattern. Reverted (402ec10) — gate broke
OnWalkable tracking. 74% of new capture in falling state. User
report: "can't get up the first step, jumped, stuck in falling
animation." Either retail synthesizes a flat CP from sloped
contacts (step_sphere_down:321203 path, unclear from BN decomp)
or our OnWalkable tracking is over-coupled to ContactPlaneValid.
Apparatus state: probe, findings, replay harness, plan, retail
cdb capture all committed and ready for next session.
Honest next-session moves (in order):
1. Build deterministic trajectory replay harness — 200ms inner loop
instead of 5-minute live test. Issue98 cell fixtures are half of
this already.
2. Pivot to less-coupled M1.5 issue while #98 awaits the harness.
3. Deeper named-decomp research on CEnvCell::find_env_collisions ->
BSPTREE::find_collisions indoor CP-setting chain. Prior passes
worked on the outdoor CLandCell path; indoor was never traced.
NO further #98 fix attempts until apparatus or research has
converged. Five+ failed attempts in the saga is the signal.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Final step of the apparatus plan. Updates ISSUES.md issue #98 and
CLAUDE.md's M1.5 status to reflect:
- The apparatus completed (Steps 1-5 land in commits 35b37df →
28c282a).
- The real divergence: retail's sphere is at world Z ≈ 94.48 (resting
on cottage floor) when find_walkable accepts; acdream's failing-
frame sphere is 2.47m lower at world Z ≈ 92.01.
- The four fix targets, in priority order. Fix plan is the NEXT plan,
scoped to Target 1 (step-up + ramp climb Z gain) or Target 2
(cottage-cell sphere reference).
- The replay harness (Issue98CellarUpReplayTests) is the test loop —
any fix that doesn't change the failing assertions is not the fix.
Today's commit graph on top of slice 5 (cf3deff):
35b37df triage — revert neg-poly + bldg-check experiments
f62a873 Step 2 — cell-dump probe + roundtrip test
3f56915 Step 2 capture — 3 real-geometry cell fixtures
856aa78 Step 3 — deterministic replay harness (7 tests)
6f666c1 Step 4 — retail cdb find_walkable capture script
28c282a Step 5 — replay vs retail divergence comparison
(this) Step 6 — ISSUES.md + CLAUDE.md handoff
Test baseline: 1167 + 8 (8 pre-existing failures, +19 new passing
tests across the apparatus). Build green throughout.
A6.P3 #98 is now in evidence-driven mode. Fix plan starts from the
divergence doc at
docs/research/2026-05-23-a6-p3-issue98-replay-comparison.md.
Pickup prompt for the fix-plan session is in §"Pickup prompt for the
fix plan" of that doc.
Add ACDREAM_PROBE_PLACEMENT_FAIL gate + LogPlacementFail emitter +
side-channel polygon attribution in PhysicsDiagnostics. Wire into
BSPQuery.FindCollisions Path 1 (Placement/Ethereal) on Collided
returns; wire into Transition.DoStepDown after the placement_insert
TransitionalInsert(1) call; wire into Transition.FindObjCollisions
to emit per-static-object [place-fail-obj] lines.
Run scen4 cellar-up with the probe → 168 [place-fail] events. 80 of
81 BSPQuery Path 1 placement rejections cite polygon 0x0020 in
cellar cell 0xA9B40147's BSP: n=(0,0,-1) d=-0.2, world Z=93.82 —
the cellar ceiling (underside of cottage main floor thickness layer).
0 [place-fail-obj] lines, confirming the failure source is the cell
BSP not a static object.
The probe-driven evidence INVALIDATES the 2026-05-22 morning
handoff's "Path 5 vs Path 6 in BSPQuery.FindCollisions" diagnosis.
Retail's BP4 trace shows every find_collisions hit has collide=0 —
retail enters the same Contact branch we do, no outer-dispatcher
divergence. Retail's BP5 fires 17+ times on the cellar ramp polygon,
not "30 hits all on flat planes" as morning claimed.
The actual divergence is downstream in cell-promotion: retail's
check_cell transitions to cottage cell 0xA9B40146 during the ascent
(BP7 sets ContactPlane to the cottage main floor poly, which lives
in cottage cell's BSP not cellar's). Ours stays at cellar 0xA9B40147,
where the ceiling poly 0x0020 correctly rejects the lifted sphere.
No fix attempted this session per CLAUDE.md discipline check
(3+ failed fixes = handoff). Full slice 5 evidence + concrete
next-session pickup steps at docs/research/2026-05-22-a6-p3-slice5-handoff.md.
ISSUES.md #98 updated with the corrected diagnosis.
Test baseline: 1148 + 8 pre-existing fail. Maintained.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>