Commit graph

214 commits

Author SHA1 Message Date
Erik
48ce52c6ed docs(B.4b): final-review polish — file #59 #60 follow-ups + handoff correction
Final whole-branch code review (Opus) surfaced two Important post-merge
follow-ups + a one-word inaccuracy in the handoff doc:

- #59: tighten WorldPicker per-entity Setup.Radius (M1-deferred; the
  ServerGuid==0 invariant is load-bearing and worth documenting before
  L.2d's CBuildingObj port lands).
- #60: port retail's obstruction_ethereal downstream path so combat-HUD
  contact reporting works for ethereal creatures (M2-combat).
- handoff: corrected "Added a _entitiesByServerGuid reverse-lookup" to
  "Used the pre-existing _entitiesByServerGuid" — the dict has existed
  since Phase 6.6/6.7; slice 1c used it, didn't add it.

Review verdict: branch ready to merge to main.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 19:43:38 +02:00
Erik
2c9bdb512b docs(B.4b): ship handoff + close #57 + file #58 + roadmap/CLAUDE update
Phase B.4b shipped end-to-end 2026-05-13. Holtburg inn doorway
double-click verified: pick -> BuildUse -> ACE SetState reply ->
ID-translated registry update -> CollisionExemption exempts ->
player walks through. M1 demo target "open the inn door" met.

9 commits on this branch:
- Tasks 1-4 per plan (BuildRay, Pick, rename, handler wiring)
- 4 bonus visual-test discoveries:
  * InputDispatcher double-click detection (was dead code)
  * DoubleClick activation gate fix in OnInputAction
  * L.2g slice 1b: CollisionExemption widened to ETHEREAL alone
  * L.2g slice 1c: ServerGuid -> entity.Id translation (silent blocker)

Closes #57. Files #58 for door swing animation (UpdateMotion routing
for non-creature entities, M1 deferred polish). Updates roadmap and
CLAUDE.md Phase L.2 paragraph. Memory file project_interaction_pipeline.md
updated outside the repo.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 19:33:27 +02:00
Erik
179e441d11 docs(B.4b): implementation plan — 6 tasks, TDD picker + handler wiring
Task-by-task plan with full code in every step, no placeholders.

Tasks 1+2: WorldPicker.BuildRay + WorldPicker.Pick (TDD: tests first,
8 xUnit Facts total).
Task 3: rename _selectedTargetGuid -> _selectedGuid (mechanical).
Task 4: add 3 OnInputAction switch cases + 3 private helpers
(PickAndStoreSelection, UseCurrentSelection, SendUse).
Task 5: Holtburg inn doorway visual test (user-performed).
Task 6: ship handoff + close #57 + roadmap/CLAUDE.md/memory updates.

Self-review table at bottom maps every spec section to its task(s);
all covered. Companion to spec
docs/superpowers/specs/2026-05-13-phase-b4b-design.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 17:37:09 +02:00
Erik
ffa404d236 docs(B.4b): correct file paths — WorldPicker lives in AcDream.Core.Selection
Original spec placed WorldPicker in src/AcDream.App/Rendering/ and the
test in tests/AcDream.App.Tests/, but AcDream.App.Tests doesn't exist
as a project. Moved to AcDream.Core.Selection where it conceptually
belongs (no App-layer deps; only WorldEntity + System.Numerics) and
where the existing AcDream.Core.Tests project can hold the tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 17:31:49 +02:00
Erik
4a1c594887 docs(B.4b): design spec for outbound Use handler wiring
Phase B.4b closes the M1-blocker discovered during the L.2g slice 1
visual test: the input dispatcher fires SelectDblLeft on click but
GameWindow.OnInputAction has no case for any Select* / UseSelected
action, so clicks silently die.

Spec creates the minimum new structure to close the gap:
- New static helper WorldPicker (BuildRay + Pick over WorldEntities)
- Rename _selectedTargetGuid -> _selectedGuid on GameWindow (unifies
  combat + interaction selection per retail's single-target model)
- Three switch cases (SelectLeft, SelectDblLeft, UseSelected)

Two further L.2g handoff inaccuracies surfaced during exploration:
WorldPicker and SelectionState do NOT exist in src/ (handoff and
ISSUES #57 both claimed they did). BuildPickUp also doesn't exist;
only BuildUse / BuildUseWithTarget / BuildTeleToLifestone are present.
Spec accounts for the actual state and defers BuildPickUp + SelectionState
class extraction.

Visual verification scenario reuses the L.2g slice 1 reproducibility
recipe: one Holtburg inn doorway log captures both L.2g + B.4b.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 17:28:18 +02:00
Erik
aba6c9ac7f docs(phys L.2g): slice 1 shipped handoff + B.4 gap discovery + plan-of-record
L.2g slice 1 is CODE-COMPLETE: parser + registry mutator + WorldSession
dispatcher + GameWindow subscriber (4 commits: 2459f28, d538915,
536a608, 108e386). Build clean, 6 new tests pass, baseline-stable
across the full suite. Per-commit + final integration code reviews
all approved.

Visual verification deferred: while running the Holtburg-doorway test,
Phase B.4's outbound Use handler turned out to be unwired. The wire
builders (InteractRequests.BuildUse), classes (SelectionState,
WorldPicker), input-action enums, and keybindings all exist — but
GameWindow.OnInputAction has no case for SelectDblLeft, so the click
silently does nothing. The inbound L.2g chain we just landed can't
fire until something sends an outbound Use.

This commit captures the handoff + reframes next-session work:

  * docs/research/2026-05-12-l2g-slice1-shipped-handoff.md (NEW)
    Full evidence: 4 shipped commits, end-to-end code flow, B.4
    discovery explanation, 4 minor + 1 Important review notes
    (the Important one is a test-coverage gap that the B.4b visual
    test will settle automatically), reproducibility recipe,
    next-session pick.

  * CLAUDE.md
    "Currently in Phase L.2" paragraph: L.2g slice 1 code shipped;
    visual test deferred to B.4b. Next-phase-candidates list:
    L.2g slice 1 (now done) replaced with the B.4b candidate
    pointing at the slice scope.

  * docs/plans/2026-04-29-movement-collision-conformance.md
    L.2g section gains a "Current shipped slice (2026-05-12):" table
    listing the 4 commits.

  * docs/plans/2026-05-12-milestones.md
    M1 phase-list updated: L.2g slice 1 (code) shipped; B.4 renamed
    "B.4 / B.4b" with the gap-discovery note + B.4b shape.

  * docs/ISSUES.md
    New issue #57 (HIGH) for the B.4 interaction-handler gap.
    Promoted to Phase B.4b; will close as
    DONE (promoted to Phase B.4b) when B.4b's design spec lands.

  * Memory file project_interaction_pipeline.md (in personal
    memory dir, not in this commit) updated to reflect reality.

Next session: Phase B.4b (~30-50 LOC, 1-2 subagent dispatches,
~30 min). Subscribe SelectDblLeft -> WorldPicker.Pick ->
InteractRequests.BuildUse -> _liveSession.SendGameMessage. Same
Holtburg-doorway visual test verifies both L.2g slice 1 and B.4b
in one pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 23:17:05 +02:00
Erik
869677bc88 docs(phys L.2g): slice 1 implementation plan
Step-by-step TDD plan for L.2g slice 1 — dynamic PhysicsState toggling.
Six commits' worth of work + a user-driven visual test at the Holtburg
inn doorway + a ship handoff commit.

Task 1: SetState (0xF74B) wire parser + xUnit tests (TDD).
Task 2: ShadowObjectRegistry.UpdatePhysicsState mutator + tests (TDD).
Task 3: WorldSession dispatcher branch + StateUpdated event.
Task 4: GameWindow subscribes, routes to registry, smoke-tests launch.
Task 5: One-shot hex-dump probe to settle holtburger 12-byte vs ACE
        16-byte payload claim before declaring slice 1 done.
Task 6: Slice 0.5 freebie — extend [entity-source] log with state +
        flags (the handoff's 'slice 1.6' suggestion).
Task 7: User-driven visual verification at Holtburg inn doorway.
Task 8: Ship handoff + CLAUDE.md / plan-of-record updates.

Risk surface: all changes are additive. No resolver edits. No broadphase
edits. No retail-port semantics changes. Per-task revert is safe.

Plan: docs/superpowers/plans/2026-05-12-phase-l2g-slice1.md
Spec: docs/superpowers/specs/2026-05-12-l2g-dynamic-physicsstate-design.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 21:52:31 +02:00
Erik
2c10dd4d67 docs(phys L.2g): design spec for dynamic PhysicsState toggling (doors)
L.2d slice 1.5 ship identified the Holtburg doorway blocker as a closed
Door entity (Setup 0x020019FF) whose PhysicsState.Ethereal bit flips
when the player Uses the door. The L.2d shape-fidelity work doesn't
cover this — the door's collision shape is correct; what's missing is
honoring the *runtime* state change.

L.2g is the new sub-phase that handles it. Scope is narrow:

  * Parse inbound GameMessageSetState (0xF74B).
  * Plumb the new PhysicsState value into ShadowObjectRegistry's
    cached per-entity state so the existing CollisionExemption.IsExempt
    already-in-place short-circuit sees up-to-date bits.
  * Verify the Holtburg inn-door scenario: walk in blocked, Use door,
    walk through, auto-close blocks again after 30s.
  * Confirm UpdateMotion (NonCombat, On/Off) drives non-creature
    entities (door swing animation).

Why a new L.2 sub-letter (and not B.4 or Door-special-case): the wire
mechanism (SetState flipping Ethereal) is also how ACE handles activated
traps, opened chests, spell projectiles becoming ethereal. Generic
infrastructure with doors as the verification scenario; lane is the
informal sixth "dynamic state."

Roadmap state:
  * L.2 plan-of-record adds the L.2g section after L.2f.
  * Milestones doc M1 phase list extended `a-f` -> `a-g`.
  * CLAUDE.md status pointer + "next phase candidates" list updated to
    name L.2g slice 1 implementation as the natural next step.

Risk: low. Wire-byte width has a hex-dump fallback path in slice 1
(holtburger says 12 bytes, ACE writes 16, capture settles it). ETHEREAL
plumbing already exists; we feed it new data. No resolver changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 21:00:36 +02:00
Erik
ecb0f2d65f docs(planning): add M0-M7 milestones layer + CLAUDE.md milestone discipline
Introduce a morale + scope-management layer above the roadmap. Seven
milestones (M0-M7) from "Connect & explore" to "v1.0", each defined by a
concrete playable scenario and ~6-10 weeks of work. Each milestone hit
triggers a recorded demo video + a freeze list of phases that go
off-limits until M7's polish pass.

M0 ("Connect & explore") is declared done retroactively — ~25 shipped
phases through 2026-05-12 (terrain, network, audio, particles, chat,
input, streaming, WB rendering migration, sky/lighting, particle system,
combat/spell/item data layers) are frozen. Currently working toward M1
("Walkable + clickable world"): L.2 collision + B.4 interaction, ~4-6
weeks.

CLAUDE.md gains a "Milestone discipline" section above the existing
"Roadmap discipline" — sets the two-altitude orientation (milestones
above, phases below), names M1 as the current target, and codifies the
four motivation rules: (1) one active milestone at a time, (2) frozen
phases off-limits, (3) recorded demo video per landing, (4) state both
altitudes at session start.

Addresses the "everything is half-built" feeling caused by per-phase
vertical-slice ships never adding up to a milestone-level "done" event.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 20:36:50 +02:00
Erik
34b7f1faa1 docs(phys L.2d): slice 1 + 1.5 shipped handoff + 3rd plan-of-record reframe
L.2d as scoped is essentially closed at the Holtburg site. The slice-1.5
trace settled the question: the "I can't walk through doorways" symptom
is a closed Door entity (Setup 0x020019FF named "Door") at the building
threshold, not a building-BSP-collision issue. Building BSP is healthy.

The two prior framings turned out wrong:
- L.2a handoff (2026-05-12): "per-cell walkability missing" — based on
  hit attribution pointing at the building, missed the Door cylinder
  also colliding per tick.
- L.2d slice 1 spec (2026-05-13 morning): "BSP shape fidelity, three
  hypotheses X/Y/Z" — ruled out by the trace once the probe labeling
  bug was fixed in slice 1.5.

Handoff doc captures full evidence, side findings (building double-
registration latent bug, missing PhysicsState in entity-source log),
and a candidates list for the next-session ordering discussion.

Plan-of-record L.2d sub-direction paragraph updated to match: "watch-
and-wait" mode, no more slices until a new shape-fidelity bug is
observed at a different site. Door-state handling becomes its own
sub-phase, scope deferred to project-ordering discussion.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 19:46:45 +02:00
Erik
66dc23e087 feat(phys L.2d slice 1): BSP-hit diagnostic probe + plan-of-record correction
Adds ACDREAM_PROBE_BUILDING — a read-only per-shadow-entry probe that
captures full BSP collision evidence whenever TransitionTypes.FindObjCollisions
attributes a hit (via the existing L.2a slice 3 chain). One multi-line
[resolve-bldg] entry per attributed hit: partIdx, hasPhys, bspR vs
vAabbR, world-space entOrigin_lb, and the actual hit polygon's vertices
in both object-local and world space.

Paired with a one-time [entity-source] line at every ShadowObjects.Register
call site in GameWindow so entityId from a probe line is greppable to its
WorldEntity source within a single log file.

Plumbing: BSPQuery writes the resolved hit polygon to a new
PhysicsDiagnostics.LastBspHitPoly side-channel at the 5 SetCollisionNormal
sites in Paths 5/6 + CollideWithPt. TransitionTypes clears that field
before each shadow-entry dispatch and reads it back at the L.2a slice 3
attribution site to emit the probe line.

Spec component 4 originally described an out ResolvedPolygon? parameter
on BSPQuery.FindCollisions; the static side-channel achieves the same
observable behavior without plumbing through BSPQuery's recursive private
methods. Deviation noted in PhysicsDiagnostics.LastBspHitPoly's XML doc.

Reframes the plan-of-record's L.2d sub-direction paragraph: the 2026-05-12
handoff proposed porting CBuildingObj + per-cell walkability, but ACE
BuildingObj.cs:39-52 + named-retail acclient_2013_pseudo_c.txt:701260
show find_building_collisions is one BSP test on Parts[0]. Per-cell
walkability belongs to L.2e, not L.2d. L.2d slice 1 is the diagnostic;
slice 2 is the actual fix scoped from slice 1's evidence (one of three
hypotheses: wrong BSP loaded / over-registered parts / BSPQuery flaw).

Tests: 2 synthetic unit tests in PhysicsDiagnosticsTests.cs pin the
static API contract that the BSPQuery → side-channel → TransitionTypes
emission chain depends on. The multi-line line format itself is verified
by acceptance criterion 2 (live Holtburg-doorway capture) — covering it
here would require a heavy PhysicsEngine + Transition fixture for a
diagnostic-only emission.

Verified: dotnet build green; the 2 new tests pass; the 8 pre-existing
test failures listed in the L.2a handoff (MotionInterpreter GetMaxSpeed_*,
PositionManager.ComputeOffset_BothActive_Combined,
PlayerMovementController.Update_ForwardInput_*, Dispatcher.W_held_*,
BSPStepUpTests.{D4,C3}) remain failing — none introduced by this slice.

Spec: docs/superpowers/specs/2026-05-13-l2d-cbuildingobj-collision-design.md
Conformance anchors:
- acclient_2013_pseudo_c.txt:701260 (CBuildingObj::find_building_collisions)
- acclient_2013_pseudo_c.txt:323725 (BSPTREE::find_collisions)
- ACE references/ACE/Source/ACE.Server/Physics/Common/BuildingObj.cs:39-52

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 19:14:34 +02:00
Erik
92cd7238ff docs(phys L.2d): design spec for slice 1 BSP-hit diagnostic + L.2d reframe
Reframes L.2d direction based on ACE BuildingObj.cs:39-52 + named-retail
acclient_2013_pseudo_c.txt:701260: retail's find_building_collisions is
one BSP test on PartArray.Parts[0]. No per-cell walkability. Per-cell
work (find_cell_list, point_in_cell, sphere/box_intersects_cell) is
L.2e territory.

Slice 1 is now a read-only BSP-hit diagnostic that captures full
collision evidence per L.2a [resolve] hit=yes line. Distinguishes 3
hypotheses (wrong BSP loaded / over-registered parts / BSPQuery flaw)
before any behavior change. Slice 2 is the actual fix, scoped from
slice 1's evidence.

Authors: brainstorm session 2026-05-13 (cold-start from L.2a slice
1+2+3 evidence). Predecessor handoff at
docs/research/2026-05-12-l2a-shipped-l2d-handoff.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 19:01:44 +02:00
Erik
eb401e8cac docs(phys L.2a): handoff + next-session prompt + CLAUDE.md / plan-of-record updates
Documents the L.2a-slice-1/2/3 ship for the next session and updates
the project's source-of-truth docs to reflect current state.

New files:
- docs/research/2026-05-12-l2a-shipped-l2d-handoff.md — full cold-start
  handoff: what shipped, three findings (L.2e cell-id format gap, L.2c
  wall-slide working, L.2d sub-direction = CBuildingObj port), branch
  state, diagnostic surface inventory, files changed, open concerns,
  cold-start checklist, reproduction recipe.
- docs/research/2026-05-12-l2d-next-session-prompt.md — terse copy-paste
  prompt for the next Claude Code session.

Modified:
- docs/plans/2026-04-29-movement-collision-conformance.md — added "Current
  sub-direction" note under L.2d capturing the evidence-driven call:
  building-mesh fidelity issue, not door-state-toggle.
- CLAUDE.md — updated "Currently between phases" → "Currently in Phase
  L.2"; added L.2a shipped paragraph; added ACDREAM_PROBE_RESOLVE /
  ACDREAM_PROBE_CELL to Diagnostic env vars; prepended L.2d brainstorm
  to next-phase candidates list.

No code changes in this commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 18:15:16 +02:00
Erik
ebef82034e feat(phys L.2a slice 1): resolver + cell-transit probes (PhysicsDiagnostics)
New static `AcDream.Core.Physics.PhysicsDiagnostics` holds two
runtime-toggleable flags initialized from env vars:

- ACDREAM_PROBE_RESOLVE=1 — emit one [resolve] line per
  PhysicsEngine.ResolveWithTransition call: input/target/output
  position+cell, ok-vs-partial, grounded-in, contact-plane status,
  wall normal if hit, walkable-polygon valid, moving entity id.
- ACDREAM_PROBE_CELL=1 — emit one [cell-transit] line per
  PlayerMovementController.CellId change: old → new cell, current
  world position, reason tag (resolver / teleport).

Both also exposed as runtime-toggleable checkboxes in the DebugPanel
"Diagnostics" section. Unlike the existing four Dump-* checkboxes
(which only mirror sticky-at-startup env vars), the two new ones
forward directly to PhysicsDiagnostics — toggling on/off takes
effect on the next physics resolve, no relaunch.

Why now: L.2's plan-of-record (docs/plans/2026-04-29-movement-collision-
conformance.md) explicitly says "Land L.2a diagnostics first. Do not
make another physics change blind." This slice closes the most-load-
bearing gap in L.2a — a general-purpose probe on the resolver outcome
and a cell-transit log — so that later L.2b/c/d/e physics changes can
be evidence-driven instead of guessed. Foundation for the indoor /
dungeon walking trajectory (G.3 unblock).

Pure additive: when both flags are off (default), the probes collapse
to a single static-bool read per resolve, zero log cost. PlayerMovement
Controller's two CellId-mutation sites are now routed through a
private UpdateCellId(reason) helper for diag chokepoint.

Build green, 1032/1040 unit tests pass. The 8 failing tests are
pre-existing on the branch base (verified by stash-and-rerun);
none touch resolver or cell-transit code; all fail identically with
this slice stashed. Investigation deferred to a follow-up.

Refs: docs/plans/2026-04-29-movement-collision-conformance.md (L.2a
shipped-slice note added in same commit).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 17:19:05 +02:00
Erik
c988e98b5a docs(vfx #C.1.5b): ship Phase C.1.5b — closes #56 + EnvCell DefaultScript dispatch
Roadmap:
- Status header now reads "Between phases" with C.1.5b in the recent-wins
  bullet list.
- New SHIPPED row in the table (C.1.5b) summarising both slices, the
  reality discovery about EnvCell statics, and the 4 visual-verified sites.
- The "IN FLIGHT — C.1.5b" sub-bullet under Phase C flipped to SHIPPED.

ISSUES.md:
- #56 removed from Active.
- #56 added to Recently closed with full commit chain (1e3c33b spec+plan,
  f3bc15e SetupPartTransforms helper, 11521f4 ParticleHookSink part-transform,
  5ca5827 activator refactor, 8735c39 GpuWorldState fire-sites), resolution
  notes, and the visual-verification record.

CLAUDE.md:
- "Currently in flight" pointer replaced by a "Currently between phases"
  marker + a C.1.5b shipped paragraph that's parallel to the existing
  C.1.5a entry.
- "After C.1.5b" → "Next phase candidates".

Visual verification 2026-05-12: portal swirl matches retail extent + lateral
spread (no ground-burial); Holtburg Inn fireplace flames; cottage chimney
smoke; spell-cast particles on +Acdream — all match retail.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 08:51:26 +02:00
Erik
1e3c33b4db docs(vfx #C.1.5b): design + plan for issue #56 + EnvCell DefaultScript
Two-slice phase:
- Slice A: ParticleHookSink applies CreateParticleHook.PartIndex via
  SetupPartTransforms.Compute(setup.PlacementFrames). Closes #56.
- Slice B: drop EntityScriptActivator's ServerGuid==0 guard so
  dat-hydrated EnvCell statics + exterior stabs fire DefaultScript.

Key reality discovery folded into the spec §3: EnvCell.StaticObjects
are already WorldEntities (via GameWindow.BuildInteriorEntitiesForStreaming),
so no synthetic-ID scheme + no new walker class needed — the handoff's
§4 Q1/Q2 options are mooted by entity.Id being collision-free.

Doc-drift fixes from C.1.5a folded into §8.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 23:51:44 +02:00
Erik
55dc861724 docs(hygiene): reconcile CLAUDE.md + roadmap; triage 16 → 12 open issues
CLAUDE.md:
- "Currently in flight" (was NONE) → C.1.5b with pickup-doc link
- Added C.1.5a + N.6 slice 1 + post-A.5 polish ship paragraphs
- "Next planned phase" (was N.6) → ranked candidate list
- Collapsed Tier 1 + 4 historical phase paragraphs into
  roadmap-table pointer (signal density +; line count ~net zero)
- Fixed broken project_ui_architecture.md Memory-crib reference

Roadmap header: dated 2026-05-10 → 2026-05-11 with "since the last
update" delta. New Phase C.1.5 sliced sub-piece in Phases ahead.

ISSUES.md triage:
- Closed #37 (humanoid coat — resolved by #47 GfxObjDegradeResolver),
  #49 + #50 (scenery placement — accepted WB-vs-retail divergence).
- Promoted #36 (sky-PES dispatch) to Phase C.1.5c; #2/#28/#29
  auto-close when that phase ships.
- Downgraded #39 (Run↔Walk cycle) to LOW + VERIFY-PENDING.
- Cross-referenced #41 + #46 to Phase L.2 motion conformance.
- Anti-coupling note on #4 (NOT in C.1.5c cluster).
- Chore tag on #3 (single-commit clock-drift fix).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 23:34:01 +02:00
Erik
ad261539d6 docs(vfx #C.1.5b): handoff for next slice (issue #56 + EnvCell statics)
Self-contained handoff doc for the C.1.5b session. §1 is a copy-paste
startup prompt for a fresh Claude Code session; §2+ is detailed
context (commits shipped in C.1.5a, decision space for the #56 fix
including precompute-per-spawn vs render-thread-side-table options,
EnvCell synthetic-id scheme, walker-class placement options, file
pointers, verification plan).

Slice will resolve issue #56 first (per-part transform handling for
static entities) before adding the EnvCell static-object DefaultScript
walker, per the C.1.5a final reviewer's recommendation that resolving
#56 unblocks slice 2's visual delight gate (the multi-emitter
collapse symptom affects portals, chimneys, and fireplaces alike).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 18:22:01 +02:00
Erik
9009318656 docs(vfx #C.1.5a): ship Phase C.1.5a + file issue #56 for per-part collapse
Visual verification at the Holtburg Town network portal passed for the
slice's mechanism: 10-hook portal script fires end-to-end with correct
color, persistence, orientation, and multi-emitter dispatch. After the
334f0c6 rotation-seed fix, the swirl is oriented correctly along the
portal's facing instead of world-NS.

Known limitation surfaced during verification and filed as issue #56:
ParticleHookSink ignores CreateParticleHook.PartIndex, so all 10 of the
portal's emitters collapse to the entity root position + identity-rotated
offset, producing a compressed and partly-ground-buried swirl instead of
the multi-tier shape retail renders. Mechanism is correct; per-part
transform handling is the next vfx-pipeline concern (will affect every
multi-emitter PES — slice 2 chimneys/fireplaces in particular).

Documentation changes:
- docs/ISSUES.md: new #56 entry with the captured entity guids
  (0x7A9B405B / 0x7A9B4080), script ids (0x3300126D / 0x3300067A),
  symptom data, root-cause hypothesis, file pointers, and acceptance
  criterion. Notes the blocks-slice-2 relationship explicitly.
- docs/superpowers/specs/2026-05-12-phase-c1.5a-portals-design.md §9:
  new limitation #1 documenting the verified PartIndex collapse symptom.
- docs/plans/2026-04-11-roadmap.md: new "C.1.5a" row in the shipped
  table referencing the spec, plan, and #56 caveat.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:13:12 +02:00
Erik
ed5335b81e docs(vfx #C.1.5a): implementation plan + spec wiring-location fixes
Plan: docs/superpowers/plans/2026-05-12-phase-c1.5a-portals.md. Four
tasks, TDD-style, each task is a single commit boundary:
  1. EntityScriptActivator class + three xUnit tests
  2. Wire into GpuWorldState (new optional ctor param + two ?. calls)
  3. Construct in GameWindow with resolver lambda
  4. Visual verification at Holtburg Town network portal + roadmap update

Spec amendments correct an inaccuracy in the 2026-05-12 commit
(06d7fbd): the activator's call sites live in GpuWorldState
(AppendLiveEntity / RemoveEntityByServerGuid), not directly in
GameWindow as the original spec described. Also fixes the test file
path: tests/AcDream.Core.Tests/... not AcDream.App.Tests/... per the
existing test-project convention. No design changes — same activator,
same trigger condition, same lifecycle ordering.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 13:36:18 +02:00
Erik
06d7fbd5ef docs(vfx): Phase C.1.5a — portal PES wiring design spec
Slice 1 of Phase C.1.5: fire Setup.DefaultScript through the already-
shipped PhysicsScriptRunner on server-spawned WorldEntity create, so
portals (and any other entity with a DefaultScript) emit their retail-
faithful persistent particle effects at spawn time. Reuses the C.1
runner-sink-system-renderer chain end-to-end; one new ~50-line class
(EntityScriptActivator) plus a two-line wiring in GameWindow.

Slice 2 (C.1.5b) will cover EnvCell.StaticObjects + animation-hook
verification; spec landed separately after slice 1 verification passes.

Acceptance: visual confirmation at the Holtburg Town network portal,
side-by-side with retail.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 13:28:55 +02:00
Erik
41981c4d74 docs(perf #N6.1): apply final-review fixes — spec, baseline doc, issue #55
Final code review of slice 1 flagged one Important issue (the spec's
"zero cost when off" claim for the surface-dump path is technically
violated — _uploadMetadata always writes one dict entry per upload
regardless of env var) plus minor doc/consistency gaps. Applied:

1. Spec §5 "Cost when off": dropped the "Zero" claim; replaced with
   "Negligible — one Dictionary write per upload (~30-50 KB at Holtburg)
   plus a hash-table write per upload. Expensive work (file I/O,
   histogram construction) is still env-gated." This matches reality.

2. Baseline doc §5: rewrote from "Raw logs (scratch, can be deleted)"
   referencing files that were never preserved in this worktree, to
   "Reproducing the measurements" with the actual PowerShell launch
   commands. Honest about the raw logs not being kept; the captured
   medians in section 2 are the canonical record.

3. New issue #55 filed in docs/ISSUES.md — static-entity slow path
   reports ~1.45M meshMissing/5s at r4 standstill, drops to ~0 when
   walking. LOW severity (no visible regression), hypothesis points
   at a "permanently-missing entity gets re-classified every frame"
   pattern that Tier 1 cache doesn't cover.

4. Roadmap shipped table: renamed "N.6.1" row to "N.6 slice 1" to
   match every other artifact's naming. Search-discoverability fix.

None of these change the slice's conclusion or next-phase
recommendation (C.1.5 first, then reduced-scope slice 2).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 12:51:10 +02:00
Erik
76ca3ffca8 docs(perf #N6.1): apply code-quality review fixes to baseline doc
Code-quality review on commit 13abf96 flagged 3 Important issues in
the baseline document plus 2 minor roadmap consistency gaps. Applied
all of them:

1. The "CPU scales superlinearly with N₁" claim was imprecise because
   CPU growth (4.0×) is actually sublinear vs near-LB count (7.7×).
   Clarified: CPU grows more than linearly with radius N₁ but
   sublinearly with visible-LB count; frustum cull discards most far
   LBs early. The outer per-LB walk still scales with N₁, which is
   what Tier 2's persistent groups address.

2. The "40-50% memory footprint reduction from atlas packing" estimate
   was asserted without derivation and likely too optimistic given all
   surfaces are already power-of-two and same-format (RGBA8). Replaced
   with a more honest bound: "low-MB to ~10 MB absolute saving" with
   explicit per-array metadata overhead reasoning. Conclusion is
   unchanged — atlas adoption still isn't justified given GPU
   under-utilization.

3. The "spec §6 threshold for atlas is >30%" citation pointed at text
   that doesn't exist in the spec. Replaced with "A conventional
   rule-of-thumb" so a future reader doesn't chase a phantom citation.

Plus roadmap consistency:

M1: The N.6 slice 1 bullet now uses the canonical "✓ SHIPPED — Title.
   Shipped YYYY-MM-DD." prefix that every other shipped phase uses.
M2: Added N.6.1 row to the shipped table at the top of the roadmap
   (lines ~55-66) so the at-a-glance shipped list is complete.

None of these change the conclusion or the next-phase recommendation
(C.1.5 first, then reduced N.6 slice 2). The fixes improve doc accuracy
and future-readability.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 12:43:35 +02:00
Erik
13abf96a5e docs(perf): Phase N.6 slice 1 — radius=12 baseline + surface dump path
Capture authoritative CPU+GPU dispatch numbers at Holtburg with the
gpu_us diagnostic now working (commit 25cb147). Three radii (4/8/12)
x two motion modes (standstill/walking) + a surface-format histogram
from ACDREAM_DUMP_SURFACES=1.

Adds env-gated one-shot dump path (TextureCache.TickSurfaceHistogramDumpIfEnabled,
called from GameWindow.OnRender) that fires once after both (a) frame
600 of the session AND (b) the upload-metadata dict reaches 100 entries
-- the cache-size gate prevents the dump from firing during pre-world
GUI ticks where OnRender spins at high rates but no scenery has streamed.
Output writes to %LOCALAPPDATA%\acdream\n6-surfaces.txt with a try/catch
around the I/O so disk-full / permission errors don't crash mid-measurement.

Baseline document at docs/plans/2026-05-11-phase-n6-perf-baseline.md
documents:
- CPU dominates GPU by 30-50x at every radius (strongly CPU-bound)
- GPU wildly under-utilized (max gpu_us p95 ~600us vs 16,600us frame budget)
- CPU scales superlinearly with N1 (Tier 1 cache wins on inner loop but
  not outer LB walk)
- Surface atlas opportunity high (59% of textures in top-3 triples) but
  win is memory-only since GPU isn't bottlenecked

Recommendation: C.1.5 (PES emitter wiring) next, then a reduced-scope
N.6 slice 2 (drop atlas + persistent-mapped buffers -- not justified by
the GPU under-utilization observed).

Roadmap entry amended to split N.6 into slice 1 (shipped) and slice 2
(planned, reduced scope, deferred until after C.1.5).

Spec: docs/superpowers/specs/2026-05-11-phase-n6-slice1-design.md.
Plan: docs/superpowers/plans/2026-05-11-phase-n6-slice1.md (Task 4).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 12:34:10 +02:00
Erik
a4931eeaa2 docs(perf): Phase N.6 slice 1 — implementation plan
Step-by-step plan for the two-commit slice: fix WbDrawDispatcher's
gpu_us double-buffering bug (ring-of-3 query slots, read-before-overwrite,
vendor-neutral) then capture the radius=12 baseline at Holtburg with
the now-working diagnostic. Includes exact old_string/new_string Edit
patterns for every code change, PowerShell launch + measurement
procedure for the manual baseline, baseline doc template with explicit
fill-in slots, and a per-criterion acceptance checklist.

Output companion to docs/superpowers/specs/2026-05-11-phase-n6-slice1-design.md
(commit 05d590c).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 11:12:26 +02:00
Erik
05d590cd54 docs(perf): Phase N.6 slice 1 — spec for gpu_us fix + radius=12 baseline
Brainstormed design for the first slice of Phase N.6 (perf polish).
Slice 1 ships two commits: (1) fix the GPU timing query double-buffering
in WbDrawDispatcher (cross-vendor ring of 3, read-before-overwrite),
(2) add an env-gated surface-format histogram dump + capture the
radius=12 perf baseline at Holtburg. Slice 2 (TextureCache cleanup +
shader migration + optional persistent-mapped buffers) is deferred
until after C.1.5 (PES emitter wiring), with the next-phase decision
to be made on the baseline numbers slice 1 produces.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 11:03:44 +02:00
Erik
175ad14f8b Merge branch 'claude/friendly-varahamihira-7b8664' — Tier 1 entity-classification cache (closes #53)
Post-A.5 polish Priority 3. EntityClassificationCache keyed by
(entityId, landblockHint) tuple. Entity dispatcher cpu_us median ~1.2 ms,
p95 ~1.5 ms — ~66% reduction vs pre-Tier-1 baseline. Closes the
post-A.5 polish phase entirely (#52, #54, #53 all closed).

See docs/ISSUES.md #53 closure + memory/project_tier1_cache.md for the
24-commit chain, 4 bug-fix iterations, and the per-tuple-vs-per-entity
recurring trap pattern documented for future cache work.
2026-05-11 00:11:42 +02:00
Erik
110fb691a8 ship(post-A.5 #53): Tier 1 entity-classification cache — closes ISSUE #53
EntityClassificationCache keyed by (entityId, landblockHint) tuple lands
per spec docs/superpowers/specs/2026-05-10-issue-53-tier1-cache-design.md
+ plan docs/superpowers/plans/2026-05-10-issue-53-tier1-cache.md.

Perf result (horizon-safe preset + High quality, AMD Radeon RX 9070 XT
@ 1440p): entity dispatcher cpu_us median ~1200 us, p95 ~1500 us. Down
from ~3500m / ~4000p95 pre-Tier-1. ~66% / ~63% reduction. Well under
the A.5 spec budget (median <= 2.0 ms, p95 <= 2.5 ms). No BUDGET_OVER
flag across 30s+ standstill captures.

Visual gate cleared after 4 bug-fix iterations:
  - 71d0edc: namespace stab Ids globally (cross-LB id collision)
  - 95ebbf3: key cache by (entityId, landblockHint) tuple (defensive)
  - c55acdc: skip cache populate when classification is incomplete
  - f928e66: incomplete-entity flag must persist across same-entity tuples

User-confirmed visually via +Acdream test character: NPCs animate,
multi-part static buildings render fully (no airborne geometry, no
Z-fighting, no missing parts, no wrong textures), Nullified Statue of
a Drudge on top of the Foundry renders all parts, trees outside
Holtburg render with branches present.

Closes the post-A.5 polish phase. Issues #52, #54, #53 all closed.

Tests: 1711 passing, 8 pre-existing physics/input failures unchanged.
N.5b sentinel: 112/112 throughout.

Memory: ~/.claude/projects/.../memory/project_tier1_cache.md +
feedback_cache_per_tuple_pattern.md capture the audit-gap and per-tuple-
vs-per-entity recurring trap for future cache work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 00:09:57 +02:00
Erik
c7021d8645 docs(phase-m): sharpen Phase M into design spec + opcode coverage matrix
Captures Phase M (Network Stack Conformance) as a fully-formed phase
ready to be picked up later. Three deliverables:

1. Design spec at docs/superpowers/specs/2026-05-10-phase-m-network-stack-design.md
   (~700 lines, 8 sections):
   - Bar C completeness target ("wireable on demand"): every wire opcode
     a 2013 EoR retail client receives or sends gets a parser/builder +
     golden-vector test + typed event in the new layered stack.
   - Three-layer architecture: INetTransport / IReliableSession /
     IGameProtocol, with WorldSession as a thin behavior consumer.
     Concrete C# interface signatures, sub-component decomposition.
   - Worktree-branch big-bang migration on claude/phase-m-network-stack;
     weekly rebase cadence; single --no-ff merge ships the phase.
   - Per-sub-phase entry/exit gates, conformance test plan (golden vectors
     + live capture replay + live ACE smoke), 10-row risk register, scope-
     cut order if calendar compresses.
   - Cost: 256 hours / ~6.4 weeks single-developer; 4-6 weeks calendar
     with subagent parallelization on M.1 + M.6.

2. Opcode coverage matrix at docs/research/2026-05-10-phase-m-opcode-matrix.md
   (~284 rows across 5 sections):
   - Section 1: 22 transport flags (14 implemented).
   - Section 2: 12 optional-header fields (10 partial).
   - Section 3: 51 top-level GameMessages (21 implemented).
   - Section 4: 103 GameEvent sub-opcodes inside 0xF7B0 (27 parsed,
     26 wired).
   - Section 5: 96 GameAction sub-opcodes inside 0xF7B1 (24 built,
     8 with live callers).
   - Roll-up: ~34% complete by raw opcode count. Biggest single
     unblocking step is wiring the 16 dead builders in section 5
     (Phase B.4 surface — Use / UseWithTarget / Allegiance / Inventory
     / Social / Cast / Appraise).
   - Sources cited per row: holtburger (629695a), ACE, named retail
     decomp, acdream current state.
   - Produced by 4 parallel research agents (one per class). Spot-check
     pass owed before M.1 closes.

3. Roadmap update: Phase M section trimmed to summary + status + pointer
   to the spec; the previously-tracked M.0 Tier 1 quick-wins are folded
   into M.3 / M.4 / M.6 per the spec; M.1 retained as the matrix
   construction sub-lane with status note.

Why this shape: the user goal is a complete, layered, testable network
stack that can be wired in as gameplay phases need it — independent of
whether each opcode is yet hooked to game state. The matrix is the
source of truth for "done"; the spec is the architecture the matrix
implements against; the roadmap is the index that points at both.

Decisions captured during the design discussion (in case they need
revisiting):
- Bar C ("wireable on demand") chosen over Bar A (holtburger parity)
  or Bar B (named-retail completeness).
- Three layers (INetTransport / IReliableSession / IGameProtocol)
  chosen over holtburger's two-layer split.
- Big-bang on a feature branch (worktree) chosen over strangler
  pattern; preserves live-ACE testing on main throughout the phase.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 19:22:49 +02:00
Erik
b8b9845f50 docs(post-A.5): capture holtburger network-stack study + Phase M.0 quick-wins
Holtburger reference fast-forwarded from 88b19bd to 629695a (+237 commits).
Four parallel research agents produced a parity-first-pass between
holtburger's network stack and acdream's src/AcDream.Core.Net/.

Why captured now: study surfaced six small, high-confidence "Tier 1" fixes
that can ship before the bigger M.1-M.8 layer extraction. Most likely fix
for the longstanding "remote retail observer sees us not perfect" bug
(MoveToState wire-format mismatches). Two transport gaps (no EchoResponse
reply, eager port-switch) match recent holtburger fixes (403bc98, 99974cc).
One latent bug worth a 5-min check (ISAAC search-mode for out-of-order
ENCRYPTED_CHECKSUM packets).

Captured as Phase M.0 in the roadmap so the work survives the session and
can be picked up later. Existing M.1-M.8 lift unchanged; M.1 marked as
partially started since the research note is the parity-map deliverable
in draft form.

Files:
- docs/research/2026-05-10-holtburger-network-stack-study.md (new) — full
  study with ranked port candidates, recent commits worth knowing, and
  acdream-vs-holtburger file map.
- docs/plans/2026-04-11-roadmap.md — Phase M Plan-of-record updated with
  2026-05-10 pointer; M.0 sub-lane added before M.1; M.1 status note.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 17:52:26 +02:00
Erik
2f8a574b92 docs(post-A.5 #53): Tier 1 cache — implementation plan (writing-plans)
17-task TDD-style plan for the Tier 1 entity-classification cache, sized
~5-7 days. Phases:

  Phase 1 (Tasks 1-5):  Cache foundation — extract GroupKey, build the
                        cache class with TryGet/Populate/InvalidateEntity/
                        InvalidateLandblock, and 11 pure-CPU tests.
  Phase 2 (Tasks 6-10): Dispatcher integration — plumb landblockId
                        through the walk scratch, inject the cache,
                        wire ClassifyBatches collector + cache-miss
                        populate + cache-hit fast path. +2 integration
                        tests.
  Phase 3 (Tasks 11-12): Invalidation hooks — wire InvalidateEntity from
                        RemoveLiveEntityByServerGuid + InvalidateLandblock
                        from GpuWorldState.RemoveEntitiesFromLandblock
                        via callback (W3b per spec §5.3).
  Phase 4 (Task 13):    DEBUG cross-check — assert membership predicate
                        + DebugCrossCheck method + 2 unit tests via
                        TraceListener capture.
  Phase 5 (Tasks 14-16): Verification — full suite + sentinel + visual
                        gate (user) + perf gate (user, ≤2.0 ms median).
  Phase 6 (Task 17):    Ship — ISSUES + CLAUDE.md + memory + final commit.

Plan resolves spec §11 open implementation choices: W3b for LB invalidation,
GroupKey at namespace internal, ResolveLandblockHint plumbed via walk
scratch, _populateScratch as a field.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 17:06:42 +02:00
Erik
4abb838729 docs(post-A.5 #53): Tier 1 retry — mutation audit + cache design spec
The audit at docs/research/2026-05-10-tier1-mutation-audit.md enumerates
every entity.MeshRefs write site (5 STATIC at hydration, 1 DYNAMIC at
GameWindow.cs:7580 inside TickAnimations) and verifies that all 7
Position/Rotation write sites only touch entities in _animatedEntities.
Establishes the load-bearing invariant: an entity's renderer state is
stable from spawn to despawn iff entity.Id is NOT in _animatedEntities.

The spec at docs/superpowers/specs/2026-05-10-issue-53-tier1-cache-design.md
locks in the design from brainstorming on 2026-05-10:
- Static-only cache + DEBUG cross-check (option c) — catches future
  regressions of the prior bug class without paying perf cost in Release
- Separate EntityClassificationCache class injected into WbDrawDispatcher
- Cache the rest pose, not the full model matrix (Position/Rotation read
  live each frame so Release stays correct even if the invariant breaks)
- Pre-flatten Setup multi-parts at populate time (the bulk of the win)
- 15 new tests covering all invalidation paths + DEBUG cross-check +
  Setup pre-flatten + lifecycle pin

Closes the audit + design steps of the post-A.5 polish Priority 3 work.
Implementation plan owned by superpowers:writing-plans next.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 16:50:26 +02:00
Erik
15376c7a73 docs(post-A.5): cold-start handoff for the Tier 1 retry session (#53)
After the post-A.5 lifestone (#52) + JobKind plumbing (#54) work shipped,
only Priority 3 (Tier 1 entity-classification cache retry, ISSUE #53)
remains. This handoff captures the audit insights gathered during the
#52 investigation that the original post-A.5 handoff didn't have:

- MeshRef is a `readonly record struct` — its fields can NOT be mutated
  in place. The actual per-frame mutation for animated entities is the
  entire MeshRefs LIST replacement at GameWindow.cs:7474-7553. This
  reframes the cache design.
- _animatedEntities dict at GameWindow.cs:160 is the source of truth
  for which entities go through the per-frame rebuild path.
- Static entity = entity.Id NOT in _animatedEntities. Its MeshRefs is
  the same instance from spawn until rare events (ObjDesc / palette
  swap / part hide / scale apply).
- Recommended cache approach: static-only with explicit invalidation
  hooks on the network/spawn-time write sites enumerated in the doc.

Doc covers: where main is, what shipped this session, why the first
Tier 1 attempt failed, the pre-started audit, cache design options,
acceptance criteria, files to read, workflow for the next session, and
things-to-NOT-do.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 16:14:24 +02:00
Erik
9a55354143 docs(post-A.5 #54): close JobKind plumbing issue + update CLAUDE.md flight status
Move ISSUE #54 to Recently closed referencing commit `bf31e59`. Drop
#54 from CLAUDE.md "Currently in flight" — only #53 (Tier 1 retry)
remains open in the post-A.5 polish phase.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 16:04:01 +02:00
Erik
b19f1d10ec docs(post-A.5 #52): close lifestone issue + update CLAUDE.md flight status
Move ISSUE #52 from Active to Recently closed with full root-cause writeup
referencing commit `e40159f`. Strip lifestone reference from CLAUDE.md
"Currently in flight"; remaining post-A.5 polish scope is #53 (Tier 1
retry) + #54 (JobKind plumbing).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 15:51:46 +02:00
Erik
c111312e13 docs(post-A.5): cold-start handoff for the next session
Captures the three deferred items from A.5 ship:
- ISSUE #52: lifestone visual missing (1-3 hours, fast win)
- ISSUE #54: JobKind plumbing through BuildLandblockForStreaming
  (~30 min - 1 hour, worker-thread efficiency cleanup)
- ISSUE #53: Tier 1 entity-classification cache retry (~5-7 days,
  biggest perf win remaining; needs animation-mutation audit before
  designing to avoid the freeze-pose bug from the first attempt)

Doc covers: A.5 final state + 3 high-value gotchas, files to read,
per-priority detail with effort estimates and acceptance criteria,
what NOT to do, the first-30-minute workflow, and the full A.5
commit chain for reference.

Phase is sized ~1 week if all three priorities land. The audit
step on Tier 1 is the highest-leverage investment.

Tier 2 + Tier 3 (static/dynamic split + GPU compute culling) are
explicitly out-of-scope for this phase — separate multi-week phases
per docs/plans/2026-05-10-perf-tiers-2-3-roadmap.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 10:16:10 +02:00
Erik
d3d78fa14f Merge branch 'claude/hopeful-darwin-ae8b87' — Phase A.5 SHIP + Quality Preset system
Phase A.5 — Two-tier Streaming + Horizon LOD shipped.

Headline: 2.3 km terrain horizon (radius=4 near + 12 far) with off-thread
mesh build, fog blend at N₁, mipmaps + 16x AF, MSAA 4x + A2C foliage,
depth-write audit, BUDGET_OVER diag, Quality Preset system (Low/Medium/
High/Ultra) with env-var overrides + F11 mid-session re-apply.

~999 tests pass, 8 pre-existing physics/input failures unchanged.

Two structural-to-A.5 bug fixes shipped post-T26:
- Bug A (9217fd9): far-tier worker strips entities (T13/T16 had only
  wired the controller side; far-tier was loading full entity layers,
  ~71K entities instead of ~10K, 5x perf regression).
- Bug B (0ad8c99): WalkEntities scratch list reused across frames
  (was 480 KB / frame allocation).

Tier 1 entity-classification cache attempted as polish (3639a6f),
reverted (9b49009) — broke animation by caching mutable per-frame
state. Retry deferred to post-A.5 polish phase (ISSUE #53).

Deferred to post-A.5 polish:
- Tier 1 retry with animation-mutation audit (ISSUE #53)
- Lifestone missing visual (ISSUE #52)
- JobKind plumbing through BuildLandblockForStreaming (ISSUE #54)
- Tier 2 (static/dynamic split) + Tier 3 (GPU compute cull) —
  separate multi-week phases. Roadmap at
  docs/plans/2026-05-10-perf-tiers-2-3-roadmap.md.

SHIP commit: 9245db5.
2026-05-10 10:09:03 +02:00
Erik
d93d823539 docs(A.5 T27): roadmap + ISSUES + CLAUDE.md updates for A.5 ship
Roadmap (2026-04-11-roadmap.md):
- Status header updated to 2026-05-10 / A.5 as the shipped phase.
- A.5 row added to shipped table (after A.3): two-tier streaming,
  QualityPreset, Bug A/B fixes, deferred items, plan archive link.
- A.5 sub-piece in Phase A section marked SHIPPED with archive link
  (replaces the old "not yet brainstormed" entry).
- N.6 bullet changed from "Currently in flight" to "Planned
  (post-A.5 polish takes priority)"; A.5's landing means the
  "direct higher-radius comparison once A.5 lands" item is now
  available.

ISSUES.md:
- #52 (A.5/lifestone-missing): Holtburg lifestone not rendering since
  A.5 dev; two root-cause candidates; investigation approach.
- #53 (A.5/tier1-redo): classification cache reverted at 9b49009;
  animation-mutation audit required before retry; 1-week estimate.
- #54 (A.5/jobkind-plumbing): Bug A's post-load strip wastes worker
  CPU; proper fix plumbs JobKind through BuildLandblockForStreaming;
  30 min–1 hour estimate.

CLAUDE.md:
- "Currently in flight" paragraph updated from N.6 to Post-A.5 polish
  (issues #52/#53/#54) with note that N.6 follows.
- A.5 shipped paragraph added (mirrors N.5b/N.5/N.4 format).
- WB integration cribs: new bullet documenting the two-tier streaming
  architecture (StreamingRegion / StreamingController / LandblockStreamer
  / GpuWorldState, N₁/N₂ defaults, QualitySettings, spec pointer).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 10:06:40 +02:00
Erik
68d6898339 roadmap: add F.5a — visible-at-login dev panels (consumes #13)
Sub-phase under existing F.5 (Core panels) capturing the immediate
follow-up to ISSUES.md #13: now that PlayerDescriptionParser surfaces
the full trailer (Inventory / Equipped / Shortcuts / HotbarSpells /
DesiredComps / Options1+2 / SpellbookFilters) and GameEventWiring
populates ItemRepository at login, F.5a wires that data into minimal
ImGui dev panels under ACDREAM_DEVTOOLS=1 so it's observable in-game.

Establishes the binding pattern (AcDream.UI.Abstractions ViewModels →
ImGui renderer) that the eventual D.2b retail-skinned F.5 panels
reuse. Spec to brainstorm before code.
2026-05-10 10:06:30 +02:00
Erik
a28a5b7583 docs(A.5 T27): spec + plan amendments for T22.5 + ship
Spec (2026-05-09-phase-a5-two-tier-streaming-design.md):
- §2 acceptance metrics reshaped from absolute 240 FPS to
  refresh-rate-relative + per-preset (95th-pct ≤ 1000ms/refresh
  standstill; ≤ 1.5× walking) to match the Quality Preset reality.
- New §4.10 Quality Preset System (T22.5): enum Low/Medium/High/Ultra,
  QualitySettings schema, canonical preset values table, env-var
  override table, wiring notes (GameWindow.OnLoad + ReapplyQualityPreset),
  MSAA mid-session unsupported caveat, file list, test count (12).
- New §11 What was deferred: 8 items (Tier 1 cache, lifestone, JobKind
  plumbing, Tier 2/3, ToEntries alloc, InvalidateEntity wiring, High
  preset retest). Former §11 References renumbered to §12.

Plan (2026-05-09-phase-a5-two-tier-streaming.md):
- New Task 22.5 section inserted between T22 and T23: full inline spec
  with schema, preset table, env-var list, wiring steps, acceptance
  criteria, deferred items, commit SHAs. Includes file-name corrections
  (SettingsState → DisplaySettings, DisplayTab → SettingsPanel).
- Self-review cross-check table: new §4.10 row pointing at T22.5.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 10:06:26 +02:00
Erik
95aaa6c92e docs: close ISSUES.md #13 — PD trailer parser shipped
Issue #13 closed in `078919c`. Full trailer (Options1 / Shortcuts /
HotbarSpells / DesiredComps / SpellbookFilters / Options2 /
GameplayOptions blob / Inventory / Equipped) now walked by
PlayerDescriptionParser. ItemRepository wired up to receive parsed
Inventory + Equipped at login. 20 PD parser tests + 1 wiring
acceptance test. 282/282 Net.Tests pass.

Plan archived at
docs/superpowers/plans/2026-05-10-issue-13-pd-trailer.md.
2026-05-10 09:47:51 +02:00
Erik
462f9d6377 docs(perf): roadmap for Tier 2 + Tier 3 entity-dispatcher optimizations
Captured 2026-05-10 during Phase A.5 polish discussion. User asked why
the 9070 XT @ 1440p doesn't hit Unreal-level FPS for an old game like
AC. Answer: architectural — we rebuild the entire draw plan from
scratch every frame instead of caching pre-baked static-world data.

Tier 1 (entity-classification cache) lands as A.5 polish (separate
commit). Tiers 2 + 3 documented here for future scheduling:

- Tier 2 — Static/dynamic split with persistent groups
  ~2-week phase. Static entities (~95% of world) get permanent GPU-
  resident matrix slots, populated at spawn, dirty-tracked for delta
  upload. Per-frame CPU cost for static = LB-cull + dirty-flag check
  only. Estimated entity dispatcher: 3.5ms → 0.5-1ms median.
  400-600 FPS at standstill, radius=12.

- Tier 3 — GPU-side culling (compute pre-pass)
  ~1-month phase. Per-instance frustum cull moves to GPU compute
  shader. Compute writes draw-indirect buffer; rasterizer reads it.
  Estimated CPU dispatcher: ~0.05ms (essentially free).
  600-1000+ FPS at standstill, radius=12.

Doc captures effort estimates, sub-decisions, risks, mitigations, and
scheduling triggers for each tier. Also notes the architectural
ceiling (~800-1500 FPS for a C# + GL client; reaching native engine
performance requires becoming a different engine).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 09:38:38 +02:00
Erik
9a0dfe03da refactor(net): #13 Parsed.TrailerTruncated + diag logging
Code-quality review followup on Task 2 (becbde6) — addresses I1 (the
forward-looking concern that Tasks 3-9's inner-catch will leave partial
lists visible to callers with no signal) and M1 (silent inner catch).

Changes:
  - Parsed gains a trailing `bool TrailerTruncated` field. Both
    construction sites pass `false` by default; the trailer try/catch
    flips a local `trailerTruncated` to `true` on FormatException and
    feeds it into the final return.
  - Inner catch logs `pos`/`payload.Length`/exception message under
    ACDREAM_DUMP_VITALS=1, mirroring the outer catch's diagnostic
    pattern.
  - Task 2 test strengthened to assert defaults on Options2 /
    SpellbookFilters / HotbarSpells / DesiredComps / GameplayOptions /
    Equipped + TrailerTruncated=false (M2 followup — gives Tasks 3-9
    a regression guard if they consume into the wrong field).
  - New test `TryParse_TrailerAbsent_LessThan8BytesAfterEnchantments_*`
    documents the contract that <8 bytes after enchantments means the
    trailer is absent (not truncated): TrailerTruncated stays false,
    upstream attribute data survives.
  - Plan updated in lockstep so Tasks 3-11 implementers see the
    `trailerTruncated` local and the new return-arg position.

271/271 AcDream.Core.Net.Tests pass.
2026-05-10 08:26:08 +02:00
Erik
65870349a8 refactor(net): #13 rename Shortcut → ShortcutEntry, expand doc citations
Code review nit-fix on top of d3b58c9 — addresses two issues from the
quality review of Task 1:

  I1 (Important): the record struct `Shortcut` was a homograph with the
  flag member `CharacterOptionDataFlag.Shortcut`. Both names live inside
  `PlayerDescriptionParser`'s scope. Rename to `ShortcutEntry` aligns
  with `InventoryEntry`/`EquippedEntry` and removes the trap before
  Task 3's walker references both names in the same method body.

  M2 (Minor): `EquippedEntry` had no holtburger source citation; added
  one referencing events.rs:180-190. Also expanded `InventoryEntry`'s
  comment with the strict reader's validation reference.

Plan doc updated in lockstep so Task 3+ implementers see the new name.
8/8 PlayerDescriptionParser tests still pass.
2026-05-10 08:16:01 +02:00
Erik
b373523f98 docs(A.5): two-tier streaming + horizon LOD implementation plan
28-task TDD-first implementation plan for Phase A.5. Maps each spec
section to concrete bite-sized tasks with failing-test → minimal-impl
→ commit cycles. Self-review at plan footer cross-checks coverage,
type consistency, placeholders.

Plan structure:
- T1-T6: StreamingRegion two-tier + 5-list TwoTierDiff with hysteresis
- T7: LandblockStreamResult.Loaded.Tier + MeshData; Promoted variant
- T8: WorldEntity AABB cache + dirty flag
- T9-T12: off-thread mesh build (ConcurrentDictionary + DatLock + worker activation + DI)
- T13-T16: StreamingController two-tier + GpuWorldState two-tier ops + GameWindow wiring
- T17-T18: WbDrawDispatcher bucketing tightening (Change #1 + Change #2)
- T19-T21: visual quality (mipmaps + A2C + depth-write lock-in)
- T22: fog params from N₁/N₂ + env-var multipliers
- T23: BUDGET_OVER flag in DIAG output
- T24-T26: perf baseline (before/after) + visual user gate
- T27-T28: roadmap/ISSUES/CLAUDE.md updates + memory + SHIP commit

Plan at docs/superpowers/plans/2026-05-09-phase-a5-two-tier-streaming.md.
Spec at docs/superpowers/specs/2026-05-09-phase-a5-two-tier-streaming-design.md (fcaff71).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 22:10:38 +02:00
Erik
fcaff71352 docs(A.5): two-tier streaming + horizon LOD design spec
Brainstorm output for Phase A.5. Locks key decisions:
- Hardware target: 240 Hz / 1440p, 4.166ms vsync budget
- Tier radii: N₁=4 (full detail, 81 LBs), N₂=12 (terrain only, 544 LBs)
- Far-tier strategy: terrain-only + fog blend at N₁ (zero engineering cost)
- Bucketing: tighten existing per-LB walk (Q5 Option A); persistent
  groups deferred to a later phase
- Worker thread: single-thread mesh build off render path (Q6 Option A)
- Hysteresis: existing radius+2 convention applied to both tiers
- Visual ride-alongs: mipmaps + anisotropic + A2C/MSAA + depth-write audit
- Acceptance: 240Hz standstill / 144 FPS walking (Q9 Option B)

Spec at docs/superpowers/specs/2026-05-09-phase-a5-two-tier-streaming-design.md.
Awaiting user review before transitioning to writing-plans.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 21:52:00 +02:00
Erik
f7f88674e1 docs(A.5): cold-start handoff for the next session
Records what N.5b shipped, where the actual FPS bottleneck lives
(WbDrawDispatcher entity cull at ~4.3ms/frame, 86% of frame budget;
terrain dispatcher is now <1% of frame), and what A.5 has to do to
make the world look big without falling off a perf cliff.

Three concrete A.5 deliverables:
1. Two-tier streaming (near = full, far = terrain-only)
2. Per-LB entity bucketing in WbDrawDispatcher
3. Off-thread LandblockMesh.Build to avoid streaming hitches at higher
   radius

Eight brainstorm questions for the next session, plus acceptance
criteria, files-to-read list, and explicit "don't do" warnings (don't
raise STREAM_RADIUS without tiering in place; don't put scenery in
far tier without an impostor pipeline; don't break the N.5b conformance
sentinel; etc.).

User's stated goal verbatim: "great smooth HIGH fps visuals. Should
look great. As long as it scales and we get very high FPS." This
reframes priorities away from radius=5 micro-optimization toward
visual scale.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 21:11:46 +02:00
Erik
08b736207c phase(N.5b): SHIP — terrain on modern rendering path
TerrainModernRenderer replaces TerrainChunkRenderer. Single global
VBO/EBO + slot allocator + glMultiDrawElementsIndirect. Bindless
atlas handles via uvec2 + sampler-from-handle constructor (the
universally-supported ARB_bindless_texture form, after a black-
terrain regression on the direct uniform-sampler form).

Path C: WB renderer pattern + acdream's LandblockMesh.Build for
retail's FSplitNESW formula compliance. Closes issue #51.

Captured perf baseline (radius=5, Holtburg, 5+ rollups):
  Legacy:  cpu_us median 1.5  / p95 3.0   (1 chunk = 1 glDrawElements)
  Modern:  cpu_us median 6.4-7.0 / p95 9-14  (51 visible LBs, 1 MDI)

Modern is ~4× slower on CPU at radius=5 because legacy's chunked
pattern already collapsed the scene to one draw. Architectural wins
(zero glBindTexture/frame; constant-cost dispatch as A.5 raises
radius) manifest at higher scene complexity. Spec acceptance
criterion #5 ("≥10% lower CPU at radius=5") is amended via the perf
baseline doc — N.5b ships on visual identity + structural correctness.

Three high-value gotchas captured to memory:
1. `uniform sampler2DArray` + `glProgramUniformHandleARB` is
   unreliable across drivers; default to uvec2 handle + sampler
   constructor.
2. Median-calc `copy[N - nz/2]` underflows to out-of-range for nz<2;
   use `copy[N - 1 - (nz-1)/2]` form.
3. Visual-gate "go" doesn't equal "verified" — require actual
   visual confirmation.

Visual verification: confirmed at Holtburg town. 114/114 tests pass
in N.5+N.5b filter. Conformance sentinel max ‖Δ‖ = 0.015 mm across
1000 sample points / 10 representative landblocks.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 13:05:12 +02:00
Erik
083c10c514 docs(N.5b T10): roadmap + ISSUES + CLAUDE.md + perf baseline updates
Document Phase N.5b shipping (terrain on the modern rendering path via
Path C — `TerrainModernRenderer` mirrors WB's `TerrainRenderManager`
pattern but consumes acdream's `LandblockMesh.Build` so retail's
`FSplitNESW` formula stays in lockstep with physics + visual mesh).

Changes:

- `docs/plans/2026-04-11-roadmap.md` — add N.5b row to the Shipped
  table; promote N.5b's "Phases ahead" entry to ✓ SHIPPED with the
  Path C resolution + perf reality check; refresh N.6 scope to note
  Terrain has joined the modern path (legacy `Texture2D` retirement
  scope narrows to Sky + Debug); update top-of-doc Status line.

- `docs/ISSUES.md` — close issue #51 (WB terrain-split formula
  divergence). Move from OPEN to "Recently closed" with the Path C
  resolution: never adopted WB's formula; modern dispatcher uses
  retail's via `LandblockMesh.Build`. References `da56063` (the
  black-terrain fix that landed within the N.5b ship chain).

- `CLAUDE.md` — add `TerrainModernRenderer.cs` to the WB integration
  cribs list with the GL_INVALID_OPERATION caveat (use uvec2 +
  `sampler2DArray(handle)` constructor, NOT direct
  `uniform sampler2DArray` + `glProgramUniformHandleARB`). Update
  the "Currently in flight" preamble: N.6 builds on N.5 + N.5b;
  add an N.5b shipped paragraph linking the perf baseline doc.

- `docs/plans/2026-05-09-phase-n5b-perf-baseline.md` — new doc
  capturing the radius=5 Holtburg perf measurement (modern 6.4-7.0
  µs median vs legacy 1.5 µs — modern is ~4× SLOWER on CPU at
  radius=5). Documents the spec acceptance criterion #5 amendment,
  the architectural wins that DO hold (zero glBindTexture/frame,
  constant-cost dispatch as A.5 raises radius, per-LB frustum cull),
  and the three high-value gotchas surfaced during implementation.

User-memory updates (outside repo, not in this commit):
- `memory/project_phase_n5b_state.md` — full N.5b state file with
  the three gotchas captured.
- `memory/MEMORY.md` — index entry pointing at the state file.

Build: dotnet build green. No code changes in this commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 13:03:14 +02:00
Erik
79367d4c15 plan(N.5b): implementation plan for terrain on modern path
Expands spec section 10 into 10 TDD-style tasks with explicit
dependency arrows. Phase A (T1, T2, T4, T5, T7) parallelizable
across 5 subagents; Phase B (T6 dispatcher) serial; Phase C (T8
GameWindow integration) serial; user verification gate; Phase D
(T9 delete legacy + T10 docs/memory) parallelizable.

Each task includes exact file paths, complete code blocks, exact
test/build commands with expected output, and HEREDOC commit
messages. Self-review: no placeholders; type-consistent across
tasks (TerrainSlotAllocator API, GetBindlessHandles signature,
SetSamplerHandleUniform contract).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 08:32:19 +02:00