User decision 2026-07-05: the FPS/ms wild-fluctuation complaint is
resolved (MP0 profiler + MP1a Content extraction + MP-Alloc safe batch,
all shipped + merged to local main, user-confirmed steady FPS). ECS
(MP3) and the rest are deferred to the future - ECS is the throughput
lever (higher avg FPS), a different goal from the steadiness we fixed;
revisit only if a higher raw FPS number is specifically wanted. Rust
rejected. Rationale captured in memory project_mp_track_findings.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Dense-town before/after (frame profiler + user eyes): frame-time max
20-87ms -> 6-10ms, single-frame alloc spikes (30-75MB) eliminated,
gen2 GC 5-11/window -> ~0, user confirms the FPS counter now holds
steady instead of swinging. The residual steady ~1.6MB/frame (from the
deferred harder sites: EnvCell rebuild gate + physics Transition
pooling) is small gen0-only churn that no longer causes swings; those
are now an OPTIONAL follow-up that would also lower the median.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
54-site adversarial allocation audit: town GC churn (1.5-3 MB/frame) is ~90-95 pct OUTSIDE the MP3 draw-submission surface (visibility compute, EnvCell rebuild, particle draw-lists, per-entity anim pose) and independent of the pak. So MP3 will NOT fix the steady-state stutter, and the triage is separable + mostly easy faithfulness-neutral buffer reuse. New MP-Alloc phase pulled ahead of MP1c (load-time smoothness is the rarer hitch). Also flips MP1b to blocked-on-dedup and records the pview-runs-outdoors scenario. Findings in project_mp_track_findings.md.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Session handoff for the next pickup. #176 (purple seam flash) + #177
(stair-room light pop-in) are ONE bug, root-caused to the camera-nearest
MaxGlobalLights=128 snapshot cap evicting in-range lights of visible
cells (Hub = 366 fixtures). Fix deferred to A7 because uncapping exposes
unported per-cell light-reach (through-floor light) + the fixture
falloff-curve misassignment + an unattributed striped-floor artifact.
- New handoff: docs/research/2026-07-06-176-177-handoff-A7-lighting.md
(root cause, why-deferred, the A7 fix order, tooling inventory).
- Roadmap A7 updated: now owns #176/#177; the 'light visibility culling'
hypothesis layer is CONFIRMED (no per-cell insert_light registration);
fixture-curve layer added. Analysis pre-paid.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Launch smoke passed: world renders identically (user-confirmed), zero
dat-layer tripwires in the session log, frame profile identical to the
pre-extraction baseline (town p50 2.1-2.2ms, same GC cadence) - the
move was perf-neutral as a pure relocation should be. Roadmap Track MP
table updated: MP1 split into shipped MP1a and upcoming MP1b (pak +
bake tool + reader) / MP1c (streaming cutover + hitch gate).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Full-route capture (Holtburg -> dungeon 0x0007 -> town 0xCE94 -> dungeon
-> Holtburg, 4 teleports) plus the supplemental town run. Verdict: the
render-side-CPU split is confirmed (GPU <= ~2.7ms, upd/upl/imgui ~= 0),
steady medians beat the spec assumption (worst town p50 3.6ms), and the
smoothness gap is GC: 1.5-3 MB allocated per frame drives gen2
collections 1-2/s and every town p99/max violation, while dungeon
windows (zero gen1/gen2) are spike-free at ~2000 fps. Teleport hitch
quantified at 211ms worst frame / 75.7MB single-frame allocation.
Gate decision: PROCEED to MP1 unchanged, with one recorded amendment -
a bounded post-MP1 allocation-triage session for churn sites outside
the MP3 rewrite surface. Fort Tethana axiom view still to be
re-measured at the MP2/MP3 gates.
MP0 complete: profiler shipped (7d74c68c..4b44a152) + baseline + gate.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Roadmap gains the Track MP phase table (MP0 in progress, MP1-MP5 ahead);
milestones doc records the side track + the explicit user-authorized
freeze exception (MP may reopen frozen streaming/WB-rendering subsystems
under its own gated phases only); CLAUDE.md Current state gets the
one-line pointer.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Discharges the 2026-06-22 banner-reconcile note: M1.5 stays the single
active milestone (remaining: A7 dungeon lighting, #137 dungeon collision,
#138 teleport-OUT). The D.2b retail-UI track and the R5 movement-manager
arc are recorded in the milestones doc as user-report-driven,
issue-level parity tracks subordinate to M1.5 — not milestones. Banner
rewritten within the 6-line budget; R5-V3/#170/#171 closes and the
R5-V4 pickup pointer included.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Mark ISSUES #159 done (2de5a011) and record the key finding for #170: the
CombatAnimationPlanner numbering fix is latent (the planner isn't wired into
the live dispatch) and the Mite Scamp's 0x62-0x64 attacks were always in the
correct block — so #170's visible symptom is sub-bug 2 (the pending_motions
MOTIONDONE loop), not command misclassification. Roadmap L.1b note updated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Register:
- Verified retired: AD-8/AD-9/AP-8/AP-9 (V4), TS-36/AD-26 (V5) - all gone.
- AD-34 widened: MoveToManager.pending_actions joins the managed-LinkedList
row, with the MoveToNode rename note (retail MoveToManager::MovementNode,
renamed to avoid colliding with R2's MotionNode) - V2 owed this note.
- NEW TS-39: MoveToManager.StickTo/Unstick are unbound no-op seams - a
sticky MoveTo (wire bit 0x80) completes-and-stops instead of sticking;
PositionManager/StickyManager bodies are R5 scope (call shapes only in
the R4 extraction). Retires with R5.
- Re-anchored after the V5 controller deletion shifted lines: AD-25
(:1212->:874), AP-24 (:170->:176), AP-30 (:1503->:1110), TS-21
(:362->:311, stale-comment clause updated - V5 fixed the comment).
- AP-79 (widened in V5's commit) and the PlanFromVelocity row (V4)
verified present.
Roadmap: Phase R entry - R4 SHIPPED 2026-07-03 (V0-V6 trail), R2+R3+R4
share the ONE pending user visual pass; next code stage R5 (MovementManager
facade + Sticky/Constraint/TargetManager).
Plan of record: R4 stage entry filled in with the full V0-V6 commit trail
+ expected-diffs for the visual pass (retail cylinder-distance stop, real
turn cycles during corrections, CanCharge walk/run legs).
Handoff doc: postscript - R4 complete, V4 smoke log analyzed clean and
deleted; memory index updated (animation deep-dive map: MoveToManager gap
CLOSED, the "three approximations" pattern retired).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
AP-78 retired (its condition — App seam bindings + K-fix18 deletion —
landed in W4); TS-21/TS-23/AP-30 anchors re-pinned to the post-W6
controller lines; roadmap + plan record the full W0-W7 trail; the
memory index notes the 2026-06-04 sequencer-deep-dive divergence map
CLOSED by Phase R1-R3. R2+R3 share ONE pending user visual pass; R4
(MoveToManager) is next.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Advance(dt, Frame? rootMotionFrame) overload: retail's actual root-
motion contract (CSequence::update(quantum, Frame*) 0x00525b80) —
every crossed frame's pos_frame combines into the caller's Frame plus
the sequence velocity/omega via apply_physics. This is the seam R6's
retail per-tick order (CPartArray.Update -> adjust_offset ->
Frame.combine) consumes.
- DELETED: ConsumeRootMotionDelta + the dead adapter accumulator fields
(zero external callers; gap-map API-migration table).
- Root-motion test now asserts REAL accumulation through the wired
Frame path (replaces the P5 inert-stub pin).
- Phase R plan: R1 stage marked SHIPPED with its commit trail.
Full suite green (3346). R1 done: P0 research/pins -> P1 node -> P2
container -> P3 physics -> P4 advance core -> P5 adapter cutover ->
P6 wiring. Next: R2 (GetObjectSequence + MotionTableManager; extraction
workflow already running).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
User mandate 2026-07-02: complete new movement + animation system,
verbatim retail equivalent, all entity classes, inbound + outbound, no
frozen code, no bandaids. Executed as a staged verbatim reconstruction
(R1 CSequence -> R8 cutover audit), each stage harness-gated with the
proven cdb-golden technique; legacy paths DELETED at each cutover.
Supersedes L.2g S3-S6; absorbs shipped S1/S2/S5 as components.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
/investigate deliverable for the inbound (remote-entity) animation+position
retail-parity effort. 10 deviations (DEV-1..10) mapped and adversarially
verified against the named retail decomp + ACE port + current code (9
confirmed, 1 refuted-and-corrected).
Headline: the #39-era UP-pace->cycle inference layer's premise ('wire goes
silent on Shift toggle') is refuted at both oracles — retail sends a fresh
MoveToState on HoldRun toggle while moving (0x006b37a8) and ACE rebroadcasts
every MoveToState unconditionally (GameActionMoveToState.cs:36); retail has
NO pace->animation adaptation anywhere (position error is absorbed solely by
the InterpolationManager chase, already ported verbatim in L.3).
Registers sub-lane L.2g in the roadmap: port the CMotionInterp inbound funnel
verbatim for all remote entity classes, slices S0-S6.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The outbound MoveToState now sends the RAW forward_speed 1.0 (omitted by
default-difference packing) instead of the pre-computed runRate, matching what
retail's client sends.
Settled the open question with a live echo-test: acdream sends forward_speed=1.0,
ACE broadcasts back RunForward @ runRate (not 1.0), and a retail observer saw
+Acdream run at full pace. So ACE RECOMPUTES the broadcast run speed from the
character's run skill and auto-upgrades WalkForward+HoldKey.Run -> RunForward for
observers. The earlier PlayerMovementController comment citing ACE
MovementData.cs ("ACE relays the speed, so the wire must send run_rate") was
wrong; corrected.
Changes:
- PlayerMovementController section 6: forward wire command is WalkForward @ 1.0
(+ HoldKey.Run when running); LocalAnimationCommand still carries RunForward for
the local cycle. Backward already 1.0. The MotionStateChanged detection now
keys the walk<->run toggle off HoldKey + LocalAnimationCommand (forward_speed is
constant 1.0); the forward_speed comparison is retained but never fires.
- GameWindow: reverted the D6.2b echo-test one-liner; the wire RawMotionState
takes forward_speed from result (now 1.0).
The wire and the D6.2a local velocity are now both raw-1.0. Threading them onto
one shared RawMotionState (removing section-6's duplicate build) is a
behavior-neutral cleanup follow-up.
Full suite green (3255). Docs: roadmap + pseudocode doc updated with the
echo-test finding.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Strafe-left moves + symmetric, backward outpaces strafe (retail-faithful at
high run skill), jump travels lateral, turn feel unchanged, no crash/rejection.
Closes the visual-verification acceptance for the D6.2a velocity/turn/jump
unification.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ports retail's raw->interpreted motion normalization and routes the local
player's velocity through it, so backward/strafe come from the retail source
instead of hand-mirrored controller code (retires register TS-22 + UN-5).
MotionInterpreter (decomp-verbatim, Ghidra/ACE-cross-checked):
- adjust_motion (0x00528010): WalkBackward->WalkForward (x-0.649999976),
TurnLeft->TurnRight (x-1), SideStepLeft->SideStepRight (x-1), sidestep scale
0.5*(WalkAnimSpeed/SidestepAnimSpeed), RunForward early-return, per-channel
holdkey fallback to CurrentHoldKey.
- apply_run_to_command (0x00527be0): WalkForward->RunForward when speed>0 +
UNCONDITIONAL *runRate; TurnRight *1.5; SideStepRight *runRate clamp +/-3.0.
- apply_raw_movement (0x005287e0): copy raw->interpreted, adjust the 3 channels
with per-channel hold keys.
- WalkAnimSpeed unified to the retail-exact 3.11999989f (was 3.12f).
PlayerMovementController: build ONE RawMotionState from MovementInput at
forward_speed=1.0 (apply_run_to_command applies the run rate — a pre-scaled
speed would double-scale), run apply_raw_movement, then take grounded + jump
velocity straight from get_state_velocity (now correct for all directions) and
drive the keyboard turn omega from the interpreted turn_speed (BaseTurnRate x
turn_speed = the same pi/2 formula the remote path uses; numerically identical
to the old TurnRateFor). Deleted the hand-mirrored backward(x-0.65) /
strafe(x+-1.25) formulas in both the grounded and jump blocks. Mouse turn and
the auto-walk path are untouched.
Retail-faithful behavior changes (confirm in smoke): strafe is now
1.25 * ~1.248 * runRate = ~1.56*runRate, clamped via SideStepSpeed<=3.0 so
|v.X|<=3.75 (was 1.25*runRate, no scale/clamp); backward is unchanged
(3.12*0.65*runRate); backward/strafe-left velocity now comes from the pipeline
instead of returning zero. Forward run pace unchanged (4.0*runRate).
Tests: MotionNormalizationTests (17, adjust_motion/apply_run_to_command) +
MotionVelocityPipelineTests (10, apply_raw_movement->get_state_velocity golden
velocities incl. the strafe clamp + turn speeds). Full suite green (3255).
Register: TS-22 + UN-5 deleted (retired); TS-34 added (adjust_motion creature
guard is a no-op — IWeenieObject has no IsCreature; correct for the only caller,
the player). Pseudocode: docs/research/2026-07-01-d6-motion-interp-pseudocode.md.
NEXT D6.2b: the echo-gated wire forward_speed=1.0 question (evidence suggests
ACE relays rather than recomputes; verified via the smoke echo).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Login/run/turn/RunLock accepted by ACE (player motion echoed, no rejection),
NPCs animate including inbound MoveTo type-7 and TurnToHeading type-9 without
crash, graceful exit. Closes the visual-verification acceptance item for the
L.2b outbound wire-parity slice (D1/D3/D4).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ports the three retail outbound-movement packers verbatim (decomp-derived
golden bytes, confirmed via the Ghidra bridge, cross-checked vs holtburger):
- D1 — RawMotionState::Pack (0x0051ed10): new AcDream.Core.Physics.RawMotionState
data type (11 fields + actions, retail defaults) + RawMotionStatePacker that
sets a flag bit only when the field DIFFERS from its default. MoveToState.Build
now takes a RawMotionState instead of presence-based nullable params, so the
over-sent forwardSpeed=1.0 / currentHoldKey=None / default per-axis holdkeys are
no longer emitted. num_actions packs into bits 11-15 (not "bits 11-31").
- D3 — MoveToStatePack::Pack (0x005168f0) trailing byte =
(standingLongjump ? 0x02 : 0) | (contact ? 0x01 : 0); explicit contact/
standingLongjump params (standingLongjump=false honestly until the feature lands).
- D4 — JumpAction rewritten to retail JumpPack::Pack (0x00516d10): extent,
velocity, full Position, four u16 timestamps, align. Removed the spurious
objectGuid/spellId u32s; Position is now packed (it was absent). Body 56 bytes.
- Position::Pack (0x005a9640) / Frame::Pack (0x00535130) verified already-correct
(cellId, origin xyz, quaternion wxyz); locked with a golden test, no change.
GameWindow callers adapted minimally: build the RawMotionState from the existing
MovementResult values (behavior preserved except the intended D1 omissions) and
pass cellId/position/rotation to the Jump send. Pre-existing MotionInterpreter
placeholder struct RawMotionState renamed LegacyRawMotionState (D6/Phase-2 scope,
pure rename) to free the name for the retail-faithful type.
D5 audit: confirmed a real divergence — retail SendMovementEvent (0x006b4680)
stamps only last_sent_position_time after an MTS while SendPositionEvent
(0x006b4770) stamps all three; acdream's NotePositionSent stamps all three on
both paths. Left unchanged (comments added at both call sites), recorded as
register TS-33, deferred to a dedicated cadence-port slice.
Tests: RawMotionStatePackTests / MoveToStateGoldenTests / JumpActionTests /
PositionPackTests + updated MoveToStateTests / AutonomousPositionTests. Full
suite green (Core.Net.Tests 372, full solution 3228 passed / 4 pre-existing skips).
Register: TS-24/TS-25 refreshed (packer now supports actions/style; runtime
emission still deferred), TS-33 added. Roadmap L.2b shipped note added.
Spec: docs/superpowers/specs/2026-06-30-movement-wire-parity-design.md (2-6)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
UI Studio (preview panels through the production renderer), importer dat-fidelity (Fix A/B/C/4/5:
the importer carries dat font/justification/color; boundary look=importer / state=runtime), and the
Character window Attributes tab (reads as retail). 3062 tests green.
Handoff: docs/research/2026-06-26-mockup-stage-handoff.md.
# Conflicts:
# docs/ISSUES.md
# docs/architecture/retail-divergence-register.md
Roadmap: extend D.5-remaining ledger with three new shipped entries (UI Studio,
importer Fix A/B/C, Character window 0x2100002E). Milestone M5: update D.2b
status paragraph to name all shipped sub-phases including the studio, importer
boundary discovery (look=importer / state=runtime), and Character window with
deferred polish pointer (#151).
ISSUES: add #151 (Character window deferred polish — LOW, filed 2026-06-26;
user accepted Attributes tab as good-enough, polish items to be enumerated later).
Issues #149 (studio inventory all-black) and #150 (GameWindow font resolver) were
already present.
Divergence register: add AP-69 (per-element dat-font resolver is studio-only;
GameWindow passes null to the four Import sites until #150 lands). No row for
Fix-4 UIState-group activation (runtime-faithful, not a deviation) or for the
tab-sprite injection / footer-state hiding (same).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- #145: the original fix was incomplete (mounted panels inherited ZLevel 1000 →
behind the backdrop → washed out); continuation 417b137 closes it. Updated status.
- D.2b-B SHIPPED entry: visually confirmed; the two render bugs (backdrop wash-out
+ captions host-direct) documented.
- New OPEN issue: contents grid overflows (needs the gutter scrollbar wired; scroll
was out-of-scope for B-Controller per the spec).
- Roadmap ledger: B-Controller visually confirmed + the render fix noted.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Roadmap: refresh the D.5.2 entry to its final shipped state (per-pixel gradient
surface overload 0x004415b0; AP-43/AP-44 retired by visual verification; range
419c3ac..fb288ad). Add an explicit D.5 sub-phase ledger: D.5.4 client object/item
data model (foundation, NEXT) -> D.5.3 selected-object + spell shortcuts -> window
manager -> D.5.5+ core panels. Handoff doc gains a paste-ready new-session prompt.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extensive handoff for the next session to build the full stateful item-icon system (the 5-layer IconData::RenderIcons composite + effect layer 0x10000005 + overlay ReplaceColor tint + appraise-driven enrichment/re-composition). D.5.1 toolbar flipped to SHIPPED; D.5.2 (icon system) + D.5.3 (toolbar interactivity / selected-object display) registered as next.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Record the D.2b widget-generalization landing: generic Type-registered widgets
built by DatWidgetFactory, thin find-by-id controllers, the ConsumesDatChildren
leaf rule, Type-3-not-registered decision, and the centered-UiText vitals numbers.
Both visual gates user-confirmed; 404 tests green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the hand-authored chat block (UiNineSlicePanel + inline UiChatView
+ local BuildRetailChatLines/RetailChatColor statics) with
ChatWindowController.Bind(LayoutDesc 0x21000006) — the same LayoutImporter
path as the vitals window. The controller places UiChatView (transcript) +
UiChatInput (text entry, on-submit) + UiChatScrollbar + UiChannelMenu inside
the dat-authored chrome. The dead local statics are deleted.
Wired to _commandBus (same LiveCommandBus as the ImGui ChatPanel) so
type+Enter dispatches SendChatCmd server-ward. Transcript keyboard set from
_uiHost.Keyboard (set by WireKeyboard above the chat block) for Ctrl+C/Ctrl+A.
Divergence register: added AD-28 (two-widget split vs UIElement_Text),
AP-38 (no in-element word-wrap), AP-39 (per-line colour vs per-glyph runs),
AP-40 (no opacity fade / shared vitalsDatFont), TS-30 (tab buttons no-op),
TS-31 (no squelch); updated IA-15 to cover both vitals + chat importer paths.
Build: 0 errors/warnings. Tests: 392 passed, 1 skipped (expected).
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>
Roadmap: D.2b (custom retail-look backend) and D.4 (dat sprites + 9-slice +
DrawSprite) both shipped this session via the Spec-1 work — the UiHost-based
markup engine (MarkupDocument + ControlsIni + IUiRegistry) rendering a
markup-driven retail Vitals panel (8-piece dat chrome + red/gold/blue bars).
Records the direct-RenderSurface decode finding + the confirmed chrome sprite
ids. Remaining D.2b polish (gradient bar sprite, AcFont/D.3, input integration,
LayoutDesc importer, D.5 panels) noted inline.
Full suite green (2413 passed / 0 failed / 3 pre-existing skips).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Correction to 1bf037a. The user reverted the M1.5 landing: the indoor
world isn't done while dungeons are completely broken. Attempting the
dungeon demo revealed it's not a single bug (#133) but a whole-feature
gap — terrain-less indoor-only dungeon landblocks aren't supported
anywhere in the streaming/load/render/physics pipeline:
- LandblockLoader.Load returns null when there's no LandBlock terrain
record (dungeons have none) -> the dungeon never loads.
- LandblockStreamer fails when the terrain mesh build returns null
(dungeons have no terrain mesh).
- The teleport-arrival snap Resolves before the dungeon hydrates ->
places the player in the old Holtburg frame over ocean.
The user chose the FULL Phase G.3 scope (dungeon streaming + portal-space
loading screen + multi-landblock LOD + PlayerTeleport handling) and
pulled it into M1.5. M1.5 lands only when BOTH the building/cellar demo
(done) and the dungeon demo (enter via portal, navigate 3-5 rooms, walls
block, smooth transitions) pass. M2 (CombatMath) re-deferred.
Currently brainstorming the dungeon-support design (spec ->
docs/superpowers/specs/). Docs corrected: milestones (M1.5 ACTIVE +
extended, M2 DEFERRED, currently-working-toward -> M1.5), CLAUDE.md
current-state, ISSUES.md #133 (G.3 pulled into M1.5).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
M1.5 "Indoor world feels right" lands on its primary building/cellar
demo, user-gated across the 2026-06 sessions: multi-floor inn navigation
without sling-out/wall-clip, cottage cellar descend+ascend without
falling through, walls block everywhere, smooth cell transitions. The
holistic Option-A render port (one DrawInside(viewer_cell), BR-2..BR-7 /
T1..T6) and the A6.P4 per-cell shadow physics shipped and were gated; the
doorway-flap family is closed (#119/#128, #112, #113, #124,
#129/#130/#131/#132, #108-residual, #127) and the #90/synthesis
workarounds removed.
The dungeon half is the one piece NOT landed: attempting the dungeon demo
(meeting-hall portal) surfaced issue #133 - teleport-into-a-dungeon snaps
the player BEFORE the dungeon landblock streams in (GameWindow.cs:4928
Resolve falls back to the resident Holtburg landblocks -> snaps to an
outdoor cell over ocean). That is Phase G.3 (dungeon streaming +
PlayerTeleport handling, M4), not a render bug (#95 died with the Option
A rewrite). Per the milestones doc's pre-flagged choice, the dungeon demo
is promoted to G.3 and M1.5 lands on the building/cellar demo (user
decision 2026-06-13).
Start M2 "Kill a drudge" - first port target CombatMath.ComputeDamage
(port-ready per the combat-math research memo; ACE oracle). Drudges spawn
outdoors for the demo, so M2 does not depend on #133/G.3.
Files: milestones doc (M1.5 -> LANDED, M2 -> ACTIVE, currently-working-
toward flipped), CLAUDE.md current-state -> M2, ISSUES.md #133 filed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Doc-and-reality sync before the session handoff: the plan's phase
sections read future-tense while T1-T4 landed them. The render digest
carries the authoritative ledger; this stamp points there and names the
4 pre-existing #99-era Core failures as BR-7's built-in acceptance
signal.
'I don't care if it is non-playable... I want everything ported, then we
test.' Per-phase playability + per-phase visual gates DROPPED. BR-2..BR-6
execute as ONE continuous port with build+tests green per commit and a
single comprehensive visual pass at the end (T5). Replaces the
playability rule with: every installment must be a COMPLETE retail
behavior, never half of one (the BR-2 punch-without-ordering lesson,
88be519).
The plan's BR-1 ('implement the skipNoTexture draw-time surface gate')
died on its pre-check: acdream ALREADY suppresses every portal fill.
ReplicateProductionEmission_OnPortalFills replicates the exact emission
conditions of the production extractors on the hall/cottage fills:
pos=False neg=False for every one (Stippling.NoPos skips the positive
side at ObjectMeshManager.PrepareGfxObjMeshData:1046,
PrepareCellStructMeshData:1394, CellMesh.Build:44, GfxObjMesh.Build:71;
the fills have no negative surface). There is nothing to gate.
What ships instead: StipplingSurfaceEquivalenceTests - 2,607 polys across
13 building models + 13 environments, ZERO violations both directions:
NoPos <=> untextured-surface. Our build-time skip is proven equivalent to
retail's draw-time skipNoTexture rule (Ghidra 0x0059d4a4, default on
@0x00820e30) on this content. The pin fails loudly if future content
breaks the invariant - the cue to implement the draw-time gate then.
Corrections folded into the plan + comparison docs:
- The #113 phantom residual CANNOT be GfxObj fills (they never reach a
vertex buffer). Plausible true sites are cell-side: flood-admitted
cells drawn with the pass-all NoClipSlice when slot-less
(RetailPViewRenderer.cs:71), and/or cell statics drawn unclipped +
un-viewcone'd (object-lists-skip-portal-view-gate, confirmed).
BR-2 opens with the probe that pins which.
- The e46d3d9 user-gate observations (filter removed phantom/doors) were
confounded - the filter was a provable mesh no-op on shells AND doors.
- Ledger rows solid-surface-skip-missing + the acdream half of
portal-polys-baked-unconditional re-marked REFUTED-for-fills; the
retail mechanism descriptions and the un-consumed PortalIndex->
CBldPortal pairing (BR-4) stand.
Suites: Core 1398 green (1392 baseline + 6 new facts) + the 4 pre-existing
#99-era failures + 1 skip. No production code.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>