19-agent verified audit of how retail decides which objects collide vs
acdream's per-channel filters. Confirms the user's "1 fix for all collision"
intuition: acdream already has retail's two-layer shape (per-cell shadow
registration + query-time exemption); the divergences are now narrow and
enumerable, not a scattered filter mess.
Audit (docs/research/2026-06-24-collision-inclusion-audit.md): 6 confirmed
deviations (D1 mesh-AABB phantom HIGH, D2 ETHEREAL-alone, D3 no sphere
primitive, D4 entry-restrictions, D5 obstruction_ethereal absent, D8 cell-
transform stale cache) + 2 refuted by the adversarial pass (D6 placement-
insert present in BSPQuery; D7 terrain pass-through is the #135/#138
streaming-gap, retail does it too). Loader verified faithful: CacheGfxObj
reads PhysicsBSP gated on HasPhysics; the mesh-AABB is a pure additive
non-faithful layer.
Design (docs/superpowers/specs/2026-06-24-unified-collision-inclusion-design.md):
"1 fix" = one DAT-only shape authority (delete mesh-AABB), one query
predicate, four faithful channels kept distinct (retail keeps find_env/
find_building/find_obj separate), one per-apply rebase invariant. 5
independently-gated slices. Retires register rows AP-2 + AD-7.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Live profiling found the dense-town (Arwic 29fps) bottleneck: EnvCellRenderer
.Render called ~94x/frame (per-cell x opaque+transparent) = 24.75ms = 75% of
the GPU frame. Render is a heavy per-frame method (state reset + SSBO upload +
MDI) invoked per-cell for far->near transparency order. Eliminated, with
evidence, every other suspect incl. the handoff's distance-degrade theory
(entities 0.22ms; resolution-independent => not fill; update 0.1ms).
Spec: batch the shell draws into one Render per pass. Opaque needs no order
(z-buffer) + lighting is per-instance (CellId-keyed SSBO) => safe to batch.
Transparent: skip opaque-only cells, preserve order for the rest.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5-task TDD plan: PhysicsDatBundle on LoadedLandblock; worker pre-reads the
apply's six Get<T> sites into it; ApplyLoadedTerrainLocked reads from the
bundle; drop the apply's lock(_datLock); verify lockwait->0; strip probes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The 30↔200 FPS swing is _datLock contention: the streaming worker holds
the global dat lock for the full per-landblock build (lockwait measured
24ms median / 88ms p95), stalling the update thread's ApplyLoadedTerrain.
Fix (A1): the worker pre-reads the apply's six Get<T> sites into a physics
dat bundle so ApplyLoadedTerrainLocked makes zero DatCollection calls and
its lock(_datLock) is removed. Approved design; implementation next.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Live tp-probe capture refuted the handoff's _datLock-starvation hypothesis:
worker BUILD is fast + uncontended (waited=0ms); the 10-14s 'long transition'
is render-thread APPLY latency, and 'dropped at wrong position' is the
per-frame resolve corrupting the outbound cell frame (lbX zeroed) while the
player sits on an empty world. Design: priority-apply the player's dest
landblock + hold-until-resident behind a retail fade cover (reuse the dormant
TeleportAnimSequencer) + cell-march landblock-id hardening. Foundation-first,
not a hold over slow streaming. Flood-timeslicing + 3D swirl deferred.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bite-sized TDD plan for the retail teleport flow. Slice 1: pure
TeleportAnimSequencer (7-state TAS) + golden-timing tests. Slice 2: the
#145 readiness-gate fix (IsLandblockLoaded + Decide outdoorReady axis +
apparatus probe) — ships independently of the visuals. Slice 3:
TeleportFlowController (delegate-injected, unit-tested) + TeleportFadeOverlay
+ portal wiring (PlaceTeleportArrival split: place vs InWorld so the input
lock persists the whole animation). Slice 4: one yaw-freeze + portal sounds
via the EnumIDMap chain. Slice 5: de-dup login readiness onto Decide, route
login/death through the controller, logout (Shift+Esc) + 0xF653 + disconnect,
remove dead _teleportArrival plumbing. Slice 6 (literal 3D swirl) is a
follow-up plan gated on a cdb asset trace.
Drafted via two research + drafting workflows; slices 3-5 redrafted as one
cohesive unit against a pinned controller API after the first parallel pass
produced cross-slice inconsistencies (missing controller task, triplicated
yaw-freeze, a fabricated PlayerMovementController.Update signature). All
load-bearing signatures personally verified against the tree.
Plan: docs/superpowers/plans/2026-06-21-retail-teleport-flow.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The new value type collided with DatReaderWriter.Types.Frame (used in
physics-adjacent code like ShadowShapeBuilder), which the structural fix
(per-file using-aliases across 6 files) would have re-incurred in every
later physics slice. Renamed the TYPE to CellFrame; the Position.Frame
MEMBER keeps retail's name. Restored the 5 alias-only files to their
pre-Slice-1 state; synced spec + plan. Core 1522 passed / 0 failed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Approved design for the cell-relative physics frame port: retire
_liveCenter from physics (render-only), carry Position{ObjCellId,
Frame{local in [0,192), quat}} as the source of truth, port
get_block_offset (verified 0x0043e630 nets to delta-landblock * 192 m),
translate inter-tick collision state per-cell like retail (0x0050a592).
7-slice parallel-frame migration; conformance via transform-on-read (no
fixture re-capture).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Decomp + in-game probe (agent-arcanum-probe.log) show the ambient regime
is already retail-faithful (CellManager::ChangePosition 0x004559B0); only
the sun is wrong — acdream gates it per-FRAME, retail per-STAGE
(useSunlight 0x0054d450). Fix = a per-instance indoor flag (reusing
IndoorObjectReceivesTorches, the AP-43 predicate) gating the sun off for
indoor mode-0 objects. No second ambient, UpdateSunFromSky + EnvCellRenderer
unchanged. User pre-approved.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Integrates main's 19 commits (A7 outdoor/indoor torch lighting Fix A/B/C/D,
GlobalLightPacker, shader updates, UN-7) under the D.5 toolbar/item-model stack
(D.5.1/D.5.2/D.5.4/D.5.3a). Auto-merged cleanly except docs/ISSUES.md.
Conflict resolved: both lineages used #140 for different issues. Kept main's
#140 = "A7 Fix D" (resolved); renumbered the toolbar/selected-object issue to
#141 (note added; this branch's commits/spec still reference #140 — immutable).
The register auto-merged (AP-46 cites file:line, not #140; UN-7 keeps #140=Fix D).
Build + full suite green on the merged tree (2,713 passed / 4 skipped).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resolves the divergence-register conflict: kept the accurate per-VERTEX AP-35
(Fix A shipped per-vertex; main's row was the stale pre-Fix-A per-pixel text),
kept main's UI rows AP-37..AP-42, and renumbered this branch's torch-gate row
AP-37 -> AP-43 (AP-37 was taken by main's LayoutDesc row). AP count 41 -> 42.
Retargeted the AP-37 references in WbDrawDispatcher + the CHECKPOINT to AP-43.
Marked ISSUES #140 RESOLVED (b7d655b) with the corrected root cause.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Port of gmToolbarUI::HandleSelectionChanged (acclient_2013_pseudo_c.txt:198635).
When the player selects a world object the action bar's bottom strip shows the
object name + (for player/pet/attackable targets) a live Health meter; deselect
clears it. Mana (#140) + stack slider deferred.
- SelectedObjectController (new): clear-then-populate on selection change; sets
name (UiText child, VitalsController pattern), overlay state (ObjectSelected /
StackedItemSelected via UiDatElement.ActiveState), shows the health meter and
sends QueryHealth for health targets. Subscribes via a delegate seam (no
GameWindow coupling).
- GameWindow: _selectedGuid field -> SelectedGuid property + SelectionChanged
event (fires on actual change only); 3 write sites converted, reads untouched.
All selection-write paths (LMB pick, Tab/Q, despawn-clear via Tick()) run on
the render thread, so the event-driven UI mutation is single-threaded.
- WorldSession.SendQueryHealth (0x01BF) — wraps SocialActions.BuildQueryHealth.
- DatWidgetFactory.BuildMeter: handle the single-image toolbar meter shape
(back-track on the element's own DirectState, fill on one Type-3 child). The
sprites go in the TILE slot (DrawMode=Normal tiles to full bar geometry per
UIElement_Meter::DrawChildren) — a left-cap assignment would gap/clamp a
sub-140px sprite. Vitals 3-slice path unchanged.
- ToolbarController.HiddenIds: A1 (health) now owned by SelectedObjectController;
A2 (mana) + A4 (stack) stay hidden (deferred) so their dat back-tracks don't
render as stray empty bars.
Adversarial Opus review found + fixed: the mana-meter orphan (A2 left unhidden)
and the meter tile-vs-cap render bug (C1). Divergence rows AP-46 (health gate
approximation: IsLiveCreatureTarget vs IsPlayer||pet||attackable) + AP-47
(meter shown on select vs on UpdateHealth reply). Spec §5 corrected.
Build + full test suite green (2,684 passed / 4 skipped). Health meter render
fidelity (full-width fill + fraction mapping) pending the user's visual gate.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Brainstormed design for the action bar's bottom strip: name + Health meter
on selection (mana deferred #140). Decisions: SelectionChanged via property
setter; send QueryHealth(0x01BF) on select. Grounded in retail
gmToolbarUI::HandleSelectionChanged (acclient_2013_pseudo_c.txt:198635) —
clear-then-populate, overlay state 0x1000000b, health gate
IsPlayer||pet||attackable. Render-bug fix is BuildMeter-only (single-image
back+fill meter; UiMeter already renders it).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Task-by-task TDD plan: (1) extract GlobalLightPacker (Core, pure) + test + refactor
WbDrawDispatcher; (2) lock the bake contract via LightBake conformance test on the
captured golden torches; (3) D-1 clamp the point-light sum on its own in
mesh_modern.vert; (4) D-2 EnvCellRenderer binds its own per-cell light set (SSBO 4+5)
via SelectForObject over cell bounds; (5) correct register AP-35 + reconcile Fix B.
Concrete code + exact insertion points; visual verification is the acceptance gate.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resolve the Fix D contradiction with decomp (workflow wf_f660eb88 + adversarial
verify) + 4 live cdb captures. The D3D-FF model was the WRONG oracle: retail has
TWO light systems — STATIC torches BAKE into wall vertices (calc_point_light,
triple-clamped: range gate + per-channel min(scale*color,color) + per-vertex
[0,1] from black), DYNAMIC lights go D3D hardware. The captured intensity=100 is
the purple PORTAL (magenta, dynamic), not a wall torch. Ground truth: 38 static
warm torches (orange (1,0.588,0.314)/cream, intensity=100, falloff 3-5) + 2 dynamic.
acdream over-brightness = two confirmed bugs: D-1 mesh_modern.vert folds
ambient+sun+torches into one UNCLAMPED accumulator (single frag clamp) -> warm
blowout; D-2 EnvCellRenderer never binds SSBO 4/5 so the cell shell reads a leaked
light set. Spec: D-1 in-shader clamp-split (clamp the torch sum on its own before
ambient/sun); D-2 bind the shell's own per-cell light set (mirror WbDrawDispatcher);
LightBake.cs is the C# conformance oracle. Adds the 4 reusable cdb capture scripts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Code-review follow-up from Task 2: align StackSizeMax with the other quantity
fields (int?, ACE PropertyInt convention) in Tasks 3/4/5; drop the (int) cast.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two guid-keyed tables (retail shape), CreateObject = canonical merge-upsert
for the data table (ACCWeenieObject-equivalent holding ALL server objects),
container membership index, retire _liveEntityInfoByGuid + EnrichItem. Settles
the handoff crux against the named decomp: retail is TWO tables, not one, so
acdream's WorldEntity + item-table split is already faithful — fix ingestion,
don't unify.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bite-sized TDD plan for the stateful item-icon system. Corrects spec 5.8:
the live 0x02CE event binds in GameWindow (next to VitalUpdated), not
GameEventWiring (which only handles the 0xF7B0 GameEvent dispatcher).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Research basis (clean Ghidra decompile via MCP + live-dat probe + ACE oracle)
overturns two handoff hypotheses:
- Appraise carries NO icon/UiEffects data (Icon/IconOverlay/IconUnderlay +
PropertyInt.UiEffects all lack [AssessmentProperty]); every icon input is
CreateObject-only. The "wire appraise -> enrichment" item is a no-op.
- The effect overlay (enum 0x10000005) is a ReplaceColor tint SOURCE, not a
blit layer (RenderIcons 0x0058d180 + ReplaceColor 0x00441530); effect tiles
are 32x32 fully-opaque colored squares.
Design (user-approved): capture UiEffects (weenieFlags 0x80, currently discarded)
-> ItemInstance.Effects; faithful 2-stage IconComposer recolor (white pixels ->
effect hue); live PublicUpdatePropertyInt(0x02CE) wire-up so the icon updates as
state changes ("item with mana vs out of mana"). Drops the appraise no-op.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
First D.5 sub-phase: ship gmToolbarUI as the first data-driven game panel (18 slots from LayoutDesc 0x21000016, populated from the persisted PlayerDescription shortcuts, real composited icons, click-to-use). Minimal scope; faithful CPU icon pre-composite (IconData::RenderIcons port). Five bounded units: UiItemSlot, UiItemList, IconComposer, CreateObject IconId extension, ToolbarController. Roadmap registration of D.5.1 is plan step 0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Record the two execution-time corrections to the design's registration
assumptions: the editable input resolves to Type 12 (Variant B, controller-placed
UiField), and Type 3 is NOT factory-registered (acdream's Type-3 elements are
chrome/containers, kept on the UiDatElement fallback).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Capture the 'why beyond chat' the user articulated: chat is the proving ground;
the real payoff is inventory/spell-bar/vendor/character-sheet/trade becoming
data-driven assembly + thin controller. Notes what carries forward (the generic
widget toolkit + the find-by-id controller pattern) vs what those windows still
need (ListBox/Panel + Field drag-drop, the window-manager half of Plan 2, and
per-domain item/container data).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Design for refactoring the hand-named chat widgets + Send/MaxMin click-wiring
into generic, Type-registered widgets built by DatWidgetFactory, collapsing
ChatWindowController (and, gated-last, VitalsController) to a thin retail
gm*UI::PostInit-style find-by-id binder.
Key finding that reframes the pass: the importer's base-chain Type resolution
is already retail-faithful, and Type 12 is UIElement_Text (a real behavioral
class), not a style prototype to skip — verified against
acclient_2013_pseudo_c.txt:115655. The generalization is therefore a
registration task (register Types 1/3/6/11/12 -> generic widgets, delete the
Type-12 skip), not a new mechanism.
Approved scope: full registry (bounded to the Types chat+vitals use; rest stays
UiDatElement fallback), chat-first, vitals rewire as the final separately-gated
step. 7-step one-widget-per-commit migration; new chat_21000006.json golden
fixture; vitals fixture stays frozen through steps 1-6.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
docs(D.2b): chat-window re-drive design spec + list-ui-layouts research tool
Plan-2 chat piece of the LayoutDesc importer. Identifies the chat window as
LayoutDesc 0x21000006 (gmMainChatUI, element class 0x10000041) and grounds a
faithful, data-driven re-drive in the named retail decomp (ChatInterface +
gmMainChatUI + UIElement_Text/_Scrollable/_Scrollbar/_Menu) plus a user-provided
retail screenshot.
Design (full-faithful scope, user-approved):
- transcript = UIElement_Text 0x10000011 (dat font, bottom-pinned, 10k behead cap,
pixel scroll, 1 line/wheel-notch)
- scrollbar = right-side track 0x10000012 + thumb 0x1000048c + up/down
- input = editable UIElement_Text 0x10000016 (caret, 100-entry history, Enter/Send)
- channel menu = UIElement_Menu 0x10000014 ("Chat" selector -> active channel)
- shared ChatCommandRouter extracted from ChatPanel
- screenshot correction: the four 0x10000522-525 left-edge elements are the
numbered CHAT TABS (1-4), not scroll buttons (a research-agent inference the
retail screenshot refutes)
- deferred (need non-UI plumbing, each gets a divergence row): tab switching/
filtering, squelch, clickable name-tags, in-element word-wrap, styled runs,
font config, opacity transition
Tooling: AcDream.Cli `list-ui-layouts <datdir> [0xRootType]` — read-only index of
every UI LayoutDesc by root element class + size + element-Type histogram; how the
chat layout was located (root type 0x10000041). Reusable for future panel re-drives.
Spec: docs/superpowers/specs/2026-06-15-chat-window-redrive-design.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@
The earlier 'not resizable / fixed-size' note was wrong (inverted edge-flag
reading). Resize shipped: dat edge-anchors reflow per UIElement::UpdateForParentSizeChange.
Noted the two number-render fixes (submission-order + glyph pixel-snap).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Roadmap: update D.2b LayoutDesc importer entry to record that the default
flip shipped 2026-06-15 (bf77a23) — importer is the default at
ACDREAM_RETAIL_UI=1; vitals.xml + ACDREAM_RETAIL_UI_IMPORTER flag retired;
window movable, resize deferred to Plan 2 (WindowManager).
Plan: update "After Plan 1" to mark the flip DONE, clean up the Plan 2
description now that vitals.xml is gone.
Register:
- AP-37 "Why" cell: replace "Gated opt-in (ACDREAM_RETAIL_UI_IMPORTER)"
with "Now the default vitals path (the hand-authored markup vitals was
retired)" — the flag is gone.
- IA-15: add row (was missing from this branch) — D.2b retail UI design
stance, updated to note that the vitals window is now rendered by the
LayoutDesc importer (dat chrome elements), not UiNineSlicePanel;
UiNineSlicePanel/RetailChromeSprites now back only chat window + plugin
panels. IA count header 14 → 15.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
main was 65 commits ahead of this branch's fork point. Only conflict was the
divergence register: both sides appended an 'AP-32' row. Resolved by keeping
main's AP-32..AP-36 (cell-shell lift, look-in cells, alpha deferral, dungeon
streaming, point lights) and renumbering the importer's row to AP-37; AP header
count -> 37. GameWindow.cs auto-merged cleanly. Verified: AcDream.App builds
0/0; AcDream.App.Tests 354 passed / 1 skipped / 0 failed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wires the dormant AcDream.App/UI retained-mode tree into GameWindow under
ACDREAM_RETAIL_UI=1: an 8-piece dat-sprite UiNineSlicePanel framing three
UiMeter vital bars bound to the existing VitalsVM. Render-only (UiHost input not
yet bridged to the InputDispatcher — next sub-phase). Coexists with the ImGui
devtools path; no regression there.
Visually verified against a live retail client: the bars match retail's vitals
structure (three stacked horizontal bars, current/max numbers centered) — so the
earlier "orbs" assumption was wrong (retail vitals ARE bars), and stamina is GOLD
not cyan (the #10F0F0 research note was wrong). UiMeter gains a centered numeric
Label (stub debug font for now). Spec §8 + the markup example corrected to match.
Bookkeeping: retired divergence row TS-30 (flat-rect panels -> real dat chrome)
and added IA-15 (our UiHost/markup engine vs keystone.dll's LayoutDesc tree).
Remaining polish (filed, §15): glassy gradient bar fill sprite + the retail dat
font for the numbers.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
9-task TDD plan against the re-grounded spec, building on the existing
AcDream.App/UI scaffold: RuntimeOptions toggles, textured-sprite path in
TextRenderer (+ frag uUseTexture=2, + TextureCache size overload), Step-0 chrome
prove-out, UiNineSlicePanel + UiMeter widgets, wire UiHost + live Vitals
(render-only) retiring TS-30, controls.ini loader, MarkupDocument (XML ->
UiElement tree), and the IUiRegistry plugin surface. Exact code per step; pure
parsers TDD'd in AcDream.App.Tests, GL/visual bits user-verified.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A direct read of src/AcDream.App/UI/ found a complete (dormant) retained-mode
toolkit the grounding workflow missed: UiRoot (input routing, focus, capture,
drag-drop, tooltip, click detection, world fall-through), UiElement,
UiPanel/UiLabel/UiButton, UiHost (Tick/Draw + WireMouse/WireKeyboard),
UiRenderContext, retail-faithful UiEvent codes. It's never wired into GameWindow,
and UiPanel.cs is the exact file divergence row TS-30 cites.
So the retail UI is this existing UiRoot tree — NOT an IPanelHost/IPanelRenderer
backend. Rewrote the architecture sections: Spec 1 now WIRES the dormant UiHost
and adds only the gaps (DrawSprite + frag uUseTexture=2, UiNineSlicePanel,
UiMeter, MarkupDocument that builds a UiElement subtree, ControlsIni). Input
machinery already exists in UiRoot; deferring it is now about integrating two
input consumers, not a missing contract. Plugin contract becomes a UiElement/
markup subtree added to UiRoot (IUiRegistry on IPluginHost), not IPanel.
Net: strictly less new code, more faithful, retires TS-30 by subclassing the
file it cites. Added §0 documenting the correction + the process lesson
(subsystem-discovery must glob by directory, not by the parent's framing).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Brainstormed design for the D.2b retail-look UI backend: our own KSML-style
markup + controls.ini stylesheet + retained-mode toolkit on Silk.NET (no
embedded browser, zero external deps — Approach C, chosen over Ultralight/CEF
and RmlUi for memory/dep-weight/faithfulness).
Spec 1 scope: an 8-piece dat-sprite window frame + live Vitals bars bound to
the existing VitalsVM, gated behind ACDREAM_RETAIL_UI=1, rendered via a reused
TextRenderer batch. Render-only (input/hit-test, AcFont glyphs, anchor solver,
LayoutDesc importer all deferred).
Grounded by a read-only research workflow (7 readers + gap-critic). The critic
corrected several stale memory/plan-doc facts now baked into the spec's
do-not-trust list: VitalsVM is a sealed class (not the old record); chrome
sprite IDs are unverified (Step-0 dat prove-out resolves them empirically);
controls.ini exists and #FFDBD6A8 is editbox text not a bg; DatCollection reads
are thread-safe; KSML is rich-text not the layout language (we mirror
ElementDesc).
Phase D.2b / Milestone M5 (parallelizable with M3/M4 — opened as a parallel
track while M1.5 stays the active critical-path milestone). Retires divergence
row TS-30 + adds one IA row when the chrome ships.
Also gitignores the /.superpowers/ visual-companion scratch dir.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The G.3a visual gate ran a real PlayerTeleport into the 0x0007 dungeon. The core
hold+place worked (grounded on the dungeon floor, no ocean) and Bug A (landblock-
prefix mis-stamp) is fixed (2ce5e5c). But the gate proved #95 (portal-graph
visibility blowup, ~9.1M instances/frame) is LIVE under the current pipeline — my
plan's "likely superseded / conditional G.3b" premise was wrong. Spec §2.5/§3.2 +
ISSUES #133/#95 updated: G.3b (grab_visible_cells stab_list bounding) is REQUIRED,
needs its own grounding/brainstorm. Also noted: the render-only hydration decouple
was reverted (e7058ca) for making the player invisible at Holtburg.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
TDD plan for the gated G.3a core: a pure TeleportArrivalController state machine
(hold-until-hydration + force-snap on impossible/timeout) + its GameWindow wiring
(replace the unconditional arrival snap with recenter + deferred BeginArrival;
per-frame Tick; readiness predicate reusing the #107 login triplet) + the EnvCell
physics/visibility hydration decouple + the visual acceptance gate. G.3b/c/d get
their own plans after the gate.
Also syncs the spec: the readiness predicate reuses SampleTerrainZ + IsSpawnCellReady
+ IsSpawnClaimUnhydratable (the validated #107 login gate) rather than a new
IsLandblockApplied query — strictly more faithful, less new surface.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Brainstorm outcome for #133/G.3. Grounds the corrected root cause (dungeon
landblock = flat terrain + EnvCells, streams via the existing pipeline; the
blocker is the teleport-arrival snap firing BEFORE the dest landblock hydrates)
against the current code (5 verified seams) and lays out Approach C:
G.3a core teleport-into-dungeon: hold-until-hydration on the arrival path
(reuse #107 IsSpawnCellReady + IsSpawnClaimUnhydratable) + #111
validated-claim EnvCell placement + dest-ready streaming query +
dest-coord validation + timeout safety + decouple EnvCell
physics/visibility hydration from the render-mesh guard. -> VISUAL GATE
G.3b #95 stab_list bounding — CONDITIONAL on the gate showing the blowup
(its repro is stale, from the T4-deleted WB path; the current flood is
landblock-confined + enqueue-once, so #95 is likely superseded).
G.3c faithful TeleportAnimState portal-tunnel FSM (decomp 004d6300 /
219405-219774); the TAS_TUNNEL hold-exit gates on G.3a's same readiness
predicate (the tunnel IS the hold's visual form).
G.3d recall game-actions (/ls etc.) — same arrival flow; doubles as the test
lever.
Supersedes the §12 port-plan of r09 (most of it already shipped); r09 stays the
wire/format/recall contract reference. Resolves the handoff's 4 open questions.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>