# acdream — project instructions for agents ## Goal Build **acdream**, a modern open-source C# .NET 10 Asheron's Call client. A faithful port of the retail AC client's behavior to modern C# + Silk.NET, with a plugin API the original never had. **The code is modern. The behavior is retail.** Every AC-specific algorithm is ported from the **named retail decomp** at `docs/research/named-retail/` — Sept 2013 EoR build PDB (18,366 named functions, 5,371 named struct/class types) + Binary Ninja pseudo-C with 99.6% function-name recovery + verbatim retail header struct definitions. The older Ghidra `FUN_xxx` chunks under `docs/research/decompiled/` (22,225 functions, 688K lines) remain a fallback for chunk-by-chunk address-range navigation. **Grep `named-retail/acclient_2013_pseudo_c.txt` by `class::method` BEFORE decompiling fresh.** The code around those algorithms is modern C# with clean architecture. The plugin API exposes game state through well-defined interfaces. **Architecture:** `docs/architecture/acdream-architecture.md` is the single source of truth for how the client is structured. All work must align with this document. When the architecture doc and reality diverge, update one or the other — never leave them out of sync. **WorldBuilder code lives in our tree.** Phase O extracted ~33 WB files (~7.7K LOC) into our own namespaces and dropped the two external project references. `DatCollection` is the **only** dat reader in process — `DefaultDatReaderWriter` is gone. `references/WorldBuilder/` remains in-tree as a read-reference (MIT-licensed; grep it freely), but nothing in `src/AcDream.*` references it as a project dependency. **Where the extracted code lives (post-Phase O):** - `src/AcDream.Core/Rendering/Wb/` — pure dat/mesh helpers (5 files, ~782 LOC): `TerrainUtils`, `TerrainEntry`, `RegionInfo`, `SceneryHelpers`, `TextureHelpers`. No GL dependency; safe to use from Core. - `src/AcDream.App/Rendering/Wb/` — GL infrastructure + mesh pipeline (~27 files, ~7K LOC): `ObjectMeshManager`, `WbMeshAdapter`, `WbDrawDispatcher`, `LandblockSpawnAdapter`, `EntitySpawnAdapter`, `TextureCache`, `GlobalMeshBuffer`, shader infrastructure, and the EnvCell/portal/scenery/terrain-blending pipeline classes. **Modern rendering path is MANDATORY** as of the N.5 ship amendment. `WbFoundationFlag`, `InstancedMeshRenderer`, and `StaticMeshRenderer` are deleted. Missing `GL_ARB_bindless_texture` or `GL_ARB_shader_draw_parameters` throws `NotSupportedException` at startup. There is no legacy fallback. Engineering cribs (WbMeshAdapter seams, N.5 SSBO layout, translucency model, gotchas) live in `memory/reference_modern_rendering_pipeline.md`. Before re-implementing any AC-specific rendering or dat-handling algorithm, **read `docs/architecture/worldbuilder-inventory.md` FIRST**. The inventory describes what we extracted (now in our tree) and what we still write ourselves. Re-porting from retail decomp when we already have a tested port is how subtle bugs (the scenery edge-vertex bug, the triangle-Z bug) keep slipping in. Retail decomp remains the oracle for network, physics, animation, movement, UI, plugin, audio, chat — see the inventory doc's 🔴 list. **Execution model:** the active source of truth is the **milestones doc** (`docs/plans/2026-05-12-milestones.md`) for "what are we building right now" and the **strategic roadmap** (`docs/plans/2026-04-11-roadmap.md`) for the per-phase ledger of what's shipped, what's in flight, and what comes next. **Ignore the old "R1→R8" sequence** — it was an early refactor sketch that no longer matches reality (see the "Roadmap Model" section in `docs/architecture/acdream-architecture.md`). Per-phase detailed specs live under `docs/superpowers/specs/`. The codebase is organized by layer (see architecture doc + the **Code Structure Rules** section below). Plans live in `docs/plans/`, research in `docs/research/`, persistent project memory in `memory/` and `~/.claude/projects/.../memory/` (the latter is browsable in Obsidian via the `claude-memory/` junction in the repo root; see `memory/reference_obsidian_vault.md`). **UI strategy:** two coexisting presentation stacks over shared state, ViewModels, events, and commands. ImGui.NET + `Silk.NET.OpenGL.Extensions.ImGui` is the permanent `ACDREAM_DEVTOOLS=1` developer stack using `IPanel`/`IPanelRenderer`. Retail gameplay UI is the independent retained `UiHost`/`UiRoot` tree in `AcDream.App/UI`, imported from LayoutDesc/DAT assets and bound by focused controllers. The stable cross-stack seam is ViewModels/commands, not a backend swap. `TextRenderer` + `BitmapFont` also serve D.6 world-space HUD elements where ImGui cannot reach the 3D scene. Plugin gameplay UI uses the BCL-only `AcDream.Plugin.Abstractions.IUiRegistry.AddMarkupPanel` contract; plugins never import App or ImGui namespaces. Full design: [`docs/plans/2026-04-24-ui-framework.md`](docs/plans/2026-04-24-ui-framework.md). Memory cribs: `claude-memory/project_chat_pipeline.md` (chat pipeline as of Phase I), `claude-memory/project_input_pipeline.md` (input pipeline as of Phase K). **Input pipeline:** `src/AcDream.UI.Abstractions/Input/` (action enum, `KeyChord`, `KeyBindings`, multicast `InputDispatcher` with scope stack + modal capture for rebind UX) + `src/AcDream.App/Input/` (Silk.NET adapters). Retail-default keymap loaded from `%LOCALAPPDATA%\acdream\keybinds.json` at startup (falls back to `KeyBindings.RetailDefaults()` matching `docs/research/named-retail/retail-default.keymap.txt`). The Settings panel (F11 / View → Settings) lets users remap any action via click-to-rebind. As of Phase K, ALL keyboard / mouse input flows through the dispatcher — no IsKeyPressed polling outside the per-frame movement queries. ## Current state **M3 — Cast a spell LANDED 2026-07-21. M4 — Live in the world ACTIVE.** Retail casting, spellbook/component-book UI, active effects, recall/portal-space presentation, the R6 locomotion/collision/projectile/teleport/radar rebaseline, and the final two-client portal-out/materialization observer flow are user-gated. Deterministic world-lifecycle automation protects fresh login, outdoor/world-edge/dungeon travel, same-location revisit, exact graceful disconnect, and uncapped fresh-process reconnect with JSON/PNG artifacts. Release builds; the current performance-program checkpoint passes 3,836 App tests / 3 skips and 8,413 complete-solution tests / 5 skips. **M4 feature work order (paused while the authorized performance program executes):** the six-slice pre-M4 world-interaction completion program in `docs/plans/2026-07-23-world-interaction-completion.md`: favorite spell-bar overflow, status Use/Assess, assessment information, equipped-child picking, vendor browsing, and authoritative vendor transactions. Slices 1 and 2 plus the adjacent shared-cooldown presentation are user-accepted. Slice 3's first connected gate exposed and corrected the monster-response flag mismatch, non-retail shared-panel mounting, and missing inscription transaction. The creature and common-item report corrections are user-accepted. Favorite-spell press-time selection and right-click local examination are implemented; the current visual gate is its corrected modern scarab/prismatic formula, DAT component icons, and authored 310 x 400 extent. **Current active engineering work:** complete Slices D–L of `docs/plans/2026-07-24-modern-runtime-architecture.md` under the recorded approval interlocks. Slices A–H completed by 2026-07-25. Production world-mesh streaming now consumes the validated machine-local `acdream.pak` through `IPreparedAssetSource`; live DAT extraction remains explicit bake/equivalence/UI-Studio tooling. Capped, uncapped, dense-Arwic, lifetime, full-suite, and user-visual gates passed. Process allocation fell 41.8–61.9%, GC pause time fell 48.4–54.1%, uncapped CPU/GPU p99 improved 17.0%/20.6%, and invalid Setup-probe exceptions fell to zero. Slice D unified typed residency policy/accounting and bounded retained caches. Slice E now enforce typed completion admission, generation-scoped old-world quiescence, metered full-window/recenter retirement, and retained cursor-budgeted render/physics/static/building/EnvCell publication. The canonical reveal generation now owns one exact destination reservation across every typed budget dimension; incomplete portal destinations stay in the authored tunnel and receive retail's centered wait cue instead of forced early reveal. E6 deterministic/resource suites, Release build, complete tests, capped/uncapped nine-stop routes, and pinned dense Arwic pass on exact binary `91e82c3c`. Every checkpoint converged without pending publication, retirement, upload, warmup, or class backlog. The user explicitly approved Slices F–L, including the F/G ECS and Slice J gameplay-owner gates, on 2026-07-24. G0–G5 are complete. G4's 46,599-comparison automated gate has zero mismatches, the user accepted the physical-display visual matrix, and G5 removed the production `InteriorEntityPartition`. The ordinary production profile sustains 519.7 FPS with CPU/GPU p50 1.869/1.096 ms and 652.1/928.3 MiB working/private memory. The G5 profile's 22.34 KiB/frame remainder led into Slice I1. That slice is now complete: player, remote, projectile, camera, and grounded walkable-publication profiles each measure 0 B/resolve, with bit-identical fresh-vs-reused results and complete reset/reentrancy gates. Slice I2 is also complete: immutable physics/containment BSP, polygon, Setup, bounds, and EnvCell-topology records preserve source float bits and ordering across synthetic corruption gates and representative installed DATs without changing production traversal. Slice I3 is complete: bake-tool 4 appends typed GfxObj, Setup, CellStruct, and EnvCell-topology collision payloads; the collision interface shares the render package's single mmap. Two complete 29,908,271,024-byte bakes produced 2,232,170 typed keys, zero failures, and the same SHA-256 across 16 and 7 workers. Corruption, cancellation, installed package, Release build, and 8,371-test / 5-skip gates pass. Slice I4 is complete: every current containment, overlap, walkable, and six-path moving-collision entry point has an integer-indexed flat shadow port. The exact referee passes 13 focused tests, 7,500 installed-DAT comparisons, 1,200 complete resolver frames, and 10,000 warmed iterations at zero managed bytes with no mismatch. Release build and 8,384 solution tests / 5 skips pass. Slice I5 is complete: immutable near-build collision closures and strict live collision publication install graph and prepared-flat views under canonical ownership. Cancellation, demotion, rehydration, revisit, removal, and reset gates pass. The lifecycle/reconnect and nine-stop routes sampled 14,064 exact queries with zero mismatch, fault, or artifact; graph/flat cell residency matched at every stable checkpoint and teardown converged. Release build and 8,396 solution tests / 5 skips pass. Slice I is complete. I6 made gameplay collision flat-authoritative at `068a0651`; its strict dense-Arwic gate matched 46,309/46,309 queries and the physical nine-stop soak converged. After user collision acceptance, I7 removed every production parsed collision graph at `82f8d4f8`. Release build and 8,413 tests / 5 skips pass; both exact-binary connected routes report `0/0/0` parsed graph residency at every stable checkpoint with graceful teardown. The cutover rollback is `git revert 068a06518dd710ca5e7f166754cbb7789ca1ed0f`; graph-removal rollback is `git revert 82f8d4f82e24ad85042ac3e25c4f6464bebce758`. Slice J0 is complete at `b632672e`: `AcDream.Runtime` now provides the enforced presentation-independent assembly boundary without moving gameplay behavior. Four dependency guards pass. J1 is complete at `854d9e9c`: borrowed runtime views, typed synchronous commands, ordered deltas, the instance clock, generation/lifecycle contracts, and teardown acknowledgements project the same canonical App owners without a mirror or extra frame. Runtime tests pass 13/13, App tests pass 3,838/3 skips, the complete Release suite passes 8,424/5 skips, and the exact-binary seven-checkpoint connected lifecycle/reconnect gate passes with graceful exits. J2 is complete at `75930787`: the canonical `WorldSession` generation, connect/enter/tick/replace transaction, ordered inbound route, and retryable teardown acknowledgement now live in Runtime. App retains only option conversion, graphical callbacks, and one borrowed inertable UI command projection. Runtime tests pass 79/79, App tests pass 3,776/3 skips, the complete Release suite passes 8,428/5 skips, and the exact- binary seven-checkpoint gate passes with graceful exits and zero render-shadow mismatches or pending deltas. J3.1 moved accepted physics timestamp/snapshot and parent-relation owners into Runtime at `f7442d13`. J3.2 is complete at `f46ddb5c`: `RuntimeEntityDirectory` now owns the only active GUID/incarnation, teardown tombstone, local-ID/reverse, accepted-wire, parent, and session- operation maps; `RuntimeEntityRecord` is presentation-free and App records are graphical sidecars. Runtime tests pass 110/110, App tests pass 3,758/3 skips, the complete Release suite passes 8,441/5 skips, and the exact-binary seven- checkpoint lifecycle/reconnect gate passes with graceful exits, zero render- shadow mismatches, and zero pending deltas. J3.3 is complete at `420e5eea` plus exact-spatial correction `e937cc36`: `LiveEntityProjectionStore` and every materialized presentation/spatial owner now use exact `RuntimeEntityKey`; the temporary GUID-shaped compatibility view is deleted. App tests pass 3,761/3 skips, the complete Release suite passes 8,444/5 skips, and the final exact- binary seven-checkpoint gate passes with two graceful exits, zero failures, zero render-shadow mismatches, and zero pending deltas. J3.4 is complete at `5ef8b537`: `RuntimeEntityObjectLifetime` owns the sole entity directory and live `ClientObjectTable`; App, routing, interaction, and UI borrow the exact instances. Runtime tests pass 118/118, App tests pass 3,762/3 skips, the complete Release suite passes 8,453/5 skips, and the exact-binary seven-checkpoint gate passes with graceful exits, zero failures, zero render-shadow mismatches, and zero pending deltas. J3.5 is complete at `ce3ac310`: Runtime issues identity before App hydration, publishes entity and inventory commits through one per-generation sequence, and supplies the same direct borrowed views to graphical and no-window hosts. Runtime tests pass 134/134, App tests pass 3,765/3 skips, the complete Release suite passes 8,472/5 skips, and the exact-binary seven-checkpoint gate passes with graceful exits, zero failures, zero render-shadow mismatches, and zero pending deltas. J3.6 is complete at `119b7c11`: exact teardown receipts precede fallible callbacks, re-entrant commits retain one monotonic synchronous stream, accepted-channel versions reject displaced callbacks, and reset/direct disposal converge the complete ownership ledger to zero. Runtime tests pass 146/146, App tests pass 3,765/3 skips, the complete Release suite passes 8,484/5 skips, and both the exact lifecycle/reconnect and canonical nine-stop routes pass with graceful zero-code exits and no failures. J3 is closed; J4 gameplay-state lifetime groups are active. J4.1 is complete at `c9d25ade`: `RuntimeCommunicationState` owns the exact chat transcript, reply/retell targets, negotiated rooms, friends, and squelch state; graphical and no-window consumers borrow them. Runtime tests pass 152/152, App tests pass 3,765/3 skips, the complete Release suite passes 8,494/5 skips, and the exact-binary seven-checkpoint lifecycle/reconnect gate passes with graceful exits and zero failures. J4.2 inventory transaction ownership is active. J0's rollback is `git revert b632672e5ccabfb44c551e08f1c411ab2669c44a`. J1's rollback is `git revert 854d9e9cd13092bd5aaa3cf025d73eeb4600e9f8`. J2's rollback is `git revert 75930787741db40a83eab8663e4464dce8d687ba`. J3.2's rollback is `git revert f46ddb5cdb1e145752bea49aeb1d62bfe71284d3`. J3.3's rollback is, newest first, `git revert e937cc36df39cf6ea1eaba2f9c0243d1929da702` then `git revert 420e5eea70fd2c29cf9c8614e6298a1f84d64045`. J3.4's rollback is `git revert 5ef8b5371d8990f0380acd939e71cd711289d429`. J3.5's rollback is `git revert ce3ac310d92722ffb637e81cb1957458874dd220`. J3.6's rollback is `git revert 119b7c115107245546180b2f5cb3cb44c7d5476a`. J4.1's rollback is `git revert c9d25ade50c0a5c4f7db5b6cca680e01e35cc18e`. Slice H's retained UI/frame, bounded-light, and pooled/borrowed/direct network work is complete; the exact seven-checkpoint connected lifecycle/reconnect gate passes. Slice I is closed in `docs/plans/2026-07-25-modern-runtime-slice-i.md`; the coherent Slice J campaign executes from `docs/plans/2026-07-25-modern-runtime-slice-j.md`; J3.6 closeout is `docs/research/2026-07-26-slice-j3-6-lifetime-closeout.md`; J4 executes from `docs/plans/2026-07-26-modern-runtime-slice-j4.md`; J4.1 closeout is `docs/research/2026-07-26-slice-j4-1-communication-state.md`. The exact G4 visual rollback is `git revert ef1d263337997bb030eadb7b8e71d73dc659907a`; do not revert G3 or the portal-warmup corrections. Evidence: `docs/research/2026-07-25-slice-h-closeout.md` and `docs/research/2026-07-25-slice-g5-production-profile.md` and `docs/research/2026-07-25-slice-i1-transition-scratch.md` and `docs/research/2026-07-25-slice-i2-flat-collision-schema.md` and `docs/research/2026-07-25-slice-i3-prepared-collision-package.md` and `docs/research/2026-07-25-slice-i4-flat-bsp-differential.md` and `docs/research/2026-07-25-slice-i5-dual-collision-shadow.md` and `docs/research/2026-07-25-slice-i6-flat-production-cutover.md` and `docs/research/2026-07-25-slice-i7-closeout.md` and `docs/research/2026-07-25-slice-j1-runtime-contract-closeout.md` and `docs/research/2026-07-25-slice-j2-session-lifetime-closeout.md` and `docs/research/2026-07-25-slice-j3-2-canonical-entity-directory-closeout.md` and `docs/research/2026-07-25-slice-j3-3-exact-projection-store.md` and `docs/research/2026-07-26-slice-j3-4-canonical-object-lifetime.md` and `docs/research/2026-07-26-slice-j3-5-canonical-delta-stream.md` and `docs/research/2026-07-26-slice-j3-6-lifetime-closeout.md`. **Structural prerequisite before new M4 subsystem work:** all eight behavior-preserving `GameWindow` decomposition slices and the automated closeout landed 2026-07-22. The class is now a 1,622-line native composition/callback shell, down 14,101 lines (89.7%) from baseline. Ordered startup, update/render frame graphs, native/input callbacks, settings, session/reset wiring, canonical checkpoint capture, and retryable shutdown are owned by focused typed collaborators without a service locator or stored window back-reference. All 293 focused tests, App Release 3,462/3, the complete Release suite 7,835/5, connected lifecycle/reconnect, two fresh-process canonical nine-stop soaks, three corrected-diff reviews, and the exact Slice-7 framebuffer comparison pass. Issue #232 is closed without loosening process limits. The user's connected visual matrix passed 2026-07-23, including the post-closeout radar correction; the structural campaign is complete and M4 feature bodies may resume after any active regression gate. The capped/RDP jump-presentation cadence alias is deferred as issue #235: uncapped Release presentation is smooth, while physics, collision, and wire truth remain correct. See `docs/plans/2026-07-22-gamewindow-slice-8-composition-lifecycle.md` and `docs/architecture/code-structure.md`. **Carried:** #153, #116, remaining R6 ownership cleanup, TS-50/TS-51/TS-53, Modern Runtime Slice J+, and #225's lifestone/particle alpha visual gate. Start structural work at `memory/project_gamewindow_decomposition.md` and `docs/architecture/code-structure.md`; start magic follow-up at `claude-memory/project_magic_ui_and_casting.md`; start render/streaming work at `claude-memory/project_render_pipeline_digest.md`. Documentation entry point: [`docs/README.md`](docs/README.md). For canonical state, read in this order: - [`docs/plans/2026-05-12-milestones.md`](docs/plans/2026-05-12-milestones.md) — milestone targets + freeze list per milestone - [`docs/plans/2026-04-11-roadmap.md`](docs/plans/2026-04-11-roadmap.md) — what's shipped, what's in flight, what's next - [`docs/ISSUES.md`](docs/ISSUES.md) — open + recently closed bugs (tactical) - [`docs/architecture/retail-divergence-register.md`](docs/architecture/retail-divergence-register.md) — every known acdream-vs-retail deviation (see the register rules in the workflow section) **Domain entry points (START HERE before domain work):** - `claude-memory/project_render_pipeline_digest.md` — indoor render / doorway-FLAP / portal flood SSOT, with the DO-NOT-RETRY table - `claude-memory/project_physics_collision_digest.md` — physics / collision / cell-membership SSOT, with the DO-NOT-RETRY table For engineering reference (read on demand, not at session start): - `memory/reference_modern_rendering_pipeline.md` — N.4/N.5 bindless+MDI dispatch, WbMeshAdapter/WbDrawDispatcher, translucency model - `memory/reference_two_tier_streaming.md` — Phase A.5 streaming architecture - `memory/reference_indoor_cell_tracking.md` — Phase 2 + A4 portal-based cell tracking + multi-cell BSP iteration - `memory/reference_obsidian_vault.md` — Obsidian-as-memory-viewer setup + the memory-handling protocol - `memory/reference_ghidra_projects.md` — Ghidra + GhidraMCP for acclient RE - `memory/reference_repos.md` — what each `references/*` repo is for The memory dir also holds `feedback_*.md` lessons-learned (cross-cutting patterns the project has agreed on) and `project_*.md` per-subsystem cribs (chat pipeline, input pipeline, interaction pipeline, etc.). See `claude-memory/MEMORY.md` for the index. ## Code Structure Rules These are the structural rules the project commits to. They are **process rules** (where code goes, what depends on what), not style rules (formatting / naming). They exist to keep the layer split honest and to stop `GameWindow.cs` from continuing to grow into a 10k-line god object. The full rationale + the extraction sequence we're pursuing live in [`docs/architecture/code-structure.md`](docs/architecture/code-structure.md). 1. **No new substantial feature bodies in `GameWindow.cs`.** It is now the roughly 1,600-line native composition/callback shell. New runtime work goes into a dedicated controller / sink / orchestrator class in `src/AcDream.App/` (or deeper in `AcDream.Core` when it's pure logic). Adding a handful of fields and a one-paragraph method to wire an extracted class in is fine; adding a new ~200-line feature directly is not. When in doubt, file a small follow-up extraction as part of the change. 2. **`AcDream.Core` must not depend on the window / GL / backend projects, except via documented interop seams.** As of Phase O, the only allowed seams are the extracted helpers in `src/AcDream.Core/Rendering/Wb/` (`TerrainUtils`, `TerrainEntry`, `RegionInfo`, `SceneryHelpers`, `TextureHelpers` — stateless, no GL). The former `WorldBuilder.Shared` and `Chorizite.OpenGLSDLBackend.Lib` project references are gone; their code now lives in our tree at those paths. New Core code that needs a GL surface must define an interface in Core and let `AcDream.App` implement it — never the reverse. If you need to add a project reference to Core, the change must come with an inventory-doc update explaining why. 3. **UI panels target `AcDream.UI.Abstractions` only.** No panel may import `AcDream.UI.ImGui` or any backend namespace. ViewModels, commands, and the `IPanel` / `IPanelRenderer` contract are the surface; everything else is backend. This is what lets us swap D.2a (ImGui) for D.2b (retail-look) later without rewriting panels. 4. **Startup environment variables enter through a typed options object.** `AcDream.App.RuntimeOptions` is the single source of truth for startup configuration. `Program.cs` reads the environment once into `RuntimeOptions` and passes it into `GameWindow`. Don't sprinkle `Environment.GetEnvironmentVariable` reads through new code paths; add a field to `RuntimeOptions` and pipe it through. 5. **Runtime probes (diagnostic toggles) belong in diagnostic owner classes.** Today `AcDream.Core.Physics.PhysicsDiagnostics` owns the `ACDREAM_PROBE_*` family. The pattern: one static class per subsystem, exposing typed bool/int properties read from env vars once at startup (with optional runtime-toggleable counterparts for the DebugPanel). Per-call-site `Environment.GetEnvironmentVariable` reads in new code are a process smell — if a flag survives one phase, promote it to a diagnostic owner. The dozens of existing `ACDREAM_DUMP_*` reads scattered through `GameWindow` are tech debt; do not add more. 6. **Tests live in the project matching the layer under test.** Core tests in `tests/AcDream.Core.Tests/`, UI tests in `tests/AcDream.UI.Abstractions.Tests/`, network tests in `tests/AcDream.Core.Net.Tests/`. App-layer tests (RuntimeOptions parsing, etc.) belong in `tests/AcDream.App.Tests/`. When adding a new test project, register it in `AcDream.slnx`. ## How to operate **Memory — read the digests before domain work.** Durable project knowledge lives in `claude-memory/` (the auto-loaded index is `MEMORY.md`). Before starting work in a domain that has a **digest**, read it first: `project_render_pipeline_digest.md` (indoor render / doorway FLAP) and `project_physics_collision_digest.md` (physics / collision / membership). Each digest is current-truth-on-top plus a DO-NOT-RETRY table — it supersedes dated banners. The memory-handling protocol (distill-don't-journal, the digest pattern, tags, recall + capture) is in `reference_obsidian_vault.md`. **Do NOT add new dated status banners to this file — update the relevant digest (or the Current state pointer list) instead.** Obsidian (auto-started in the main repo via a SessionStart hook) is the live search / graph / tag lens over `claude-memory/` through the `mcp__obsidian__*` tools when it's running; the files read and write the same with it closed. **You are the lead engineer AND architect on this project at all times.** You own the architecture (`docs/architecture/acdream-architecture.md`), the execution plan (milestones doc + strategic roadmap), the development workflow, and all technical decisions. Stop as little as possible. Drive work autonomously and continuously through full phases and across commit boundaries. Do not stop mid-phase for routine progress check-ins, permission asks on low-stakes design calls, or "should I continue?" confirmations. The user has repeatedly authorized direct-to-main commits, multi-commit sessions, and cross-phase jumps when the work is sequenced in the roadmap. The only thing that genuinely requires stopping is **visual confirmation** — the user needs to look at the running client and tell you whether it matches retail. Everything else is your call. **No workarounds without explicit approval.** When you spot a bug or encounter a behavioral mismatch, fix the underlying cause — do not ship a band-aid, suppression flag, grace period, retry loop, or any other "make the symptom go away" shortcut, unless the user has explicitly approved that shape OR you are building a NEW feature with a different design. This rule exists because every workaround creates architectural debt that masks the real issue, makes future refactors harder, and erodes the codebase's retail-faithfulness. Examples of disallowed shortcuts: an `if (problematicState) return early` guard at the symptom site instead of investigating why the state happened; a timer-based "settle period" to hide a race; a flag like `_suppressXDuringY` to mask a wire-level mistake; a `try/catch` swallowing an exception that signals a real problem. If you notice a fix is starting to look like a workaround mid-implementation, stop, file the proper investigation as an issue with full reproduction notes, and either (a) ask the user before shipping the workaround, or (b) invest the time to fix the root cause. The user has explicitly authorized "spend more time, get it right" over "ship a shortcut and file the cleanup." Quote them: "we should have no workarounds unless I say so or we want a different feature." **Only stop and wait for the user when:** - Visual verification is the acceptance test ("does the drudge look right now?") - The roadmap and the observed bug disagree and you need to brainstorm a new phase or sub-step (use `superpowers:brainstorming`, not a freeform chat) - A genuinely destructive or hard-to-reverse action is on the table outside the normal commit workflow (force push, history rewrite, deleting memory files, reverting multiple commits) - Memory or committed history shows a clear user preference you're about to diverge from - **The request is an investigation, audit, analysis, review, or "report-only"** — no edits, no writes, no diagnostic code drops until you've delivered the report and the user explicitly approves a fix. Use the `/investigate` skill (`.claude/skills/investigate/SKILL.md`) to enter this mode cleanly. - **A referenced commit, file path, branch, or doc doesn't exist** where the user said it would. Ask one short question; don't go hunting across branches or worktrees. A 1-line clarification beats 30 minutes of wrong-branch exploration. **Things you should just do without asking:** - Continue to the next planned sub-step of a phase after the previous one lands clean — including immediately starting work on the next phase if the current one is done. **You pick what comes next** per the Milestone discipline section — never present the user a menu like "should we do X or Y?" or ask "what next?". Just choose and announce the choice in one sentence. Work-order selection is the agent's job, not the user's. - Pick between two roughly equivalent implementations; justify the choice in the commit message - Refactor small amounts of surrounding code when genuinely needed to land a change cleanly (but not "while I'm here" scope creep) - Run the test suite, build the project, commit to main with co-author attribution - Add diagnostic logging when you need evidence, then strip it when the evidence is in hand - Spawn subagents for bounded implementation chunks (see Subagent policy) Before claiming a phase or sub-step is done: run `dotnet build` and `dotnet test` green, commit with a message that explains the "why", update memory if there's a durable lesson, update the roadmap's "shipped" table if a phase just landed, and move to the next todo item. **If you catch yourself about to ask "should I continue?", the answer is always yes — keep going.** The single exception is visual verification; otherwise, act. ## Communication style The user is a strong systems / C# / network programmer but **less practiced at 3D math, physics, graphics, and animation**. They want to learn — they're not asking for dumbed-down content, but for explanations that build understanding alongside the work. When discussing 3D / physics / graphics / animation / dat-format / protocol-internals topics: - **Name the concept in plain language first**, then introduce the term of art. "The angle of a slope (we call its straight-up component `Normal.Z`)" rather than dropping `normal.Z = cos θ` with no anchor. - **Give units**: degrees, meters, cm — NOT raw floats. "FloorZ ≈ 0.66 means slopes up to about 49° are walkable" rather than "FloorZ = 0.66417414f". Floats are for the code; English is for the conversation. - **Use analogies for spatial concepts** when they fit. A BSP tree is "a way of slicing space into nested rooms"; a contact plane is "the imaginary floor under the player's feet"; a sphere sweep is "rolling a ball forward through space and stopping it on contact"; a cross product is "the direction perpendicular to two arrows"; a dot product is "how aligned two arrows are (1 = same, 0 = perpendicular, -1 = opposite)". - **Don't pile on multiple new concepts in one paragraph.** If a problem touches step-up AND step-down AND edge-slide AND walkable-polygon tracking, walk through them one at a time, each with what it does and why it exists. - **Show the math when it matters, but explain it.** Don't just drop a formula and move on; tag it with "what this means geometrically". - **Use frame-by-frame walk-throughs** for control-flow-heavy physics: "frame N: player here, lands. Frame N+1: state checks…" beats a function-call trace for understanding what's happening in motion. - **Flag terms of art** the first time they appear in a session, even if they're sprinkled through code comments. "Broadphase", "BSP", "step-up", "ContactPlane", "ValidateWalkable" — they earn their meaning the first time you spell it out. The goal is collaborative learning. Don't simplify the content; just make sure every term and number is grounded so the user can keep up and build intuition over time. ## Development workflow: grep named → decompile → verify → port **This is the mandatory workflow for implementing ANY AC-specific behavior.** The triangle-boundary Z bug cost 5 failed fix attempts from guessing. The animation frame-swap bug cost 4 failed attempts. Every time we checked the decompiled code first, we got it right on the first try. **Now we have named retail symbols too — Step 0 cuts most lookups from 30 minutes to 5 seconds. When "what does retail actually DO at runtime?" is the question and decomp alone isn't enough, attach cdb to a live retail client (Step -1).** ### For each new feature or bug fix: -1. **ATTACH cdb TO RETAIL (when behavior is the question, not code).** For "what does retail actually DO frame-by-frame?" questions — wedges, weird animation flicker, geometry-specific bugs, anything where the decomp is correct but it's not clear how it produces the visible behavior — **don't guess; attach the Windows debugger to a live retail client and trace it.** See "Retail debugger toolchain" below for setup. We discovered the steep-roof wedge had a 30Hz physics-tick cause this way; would have taken weeks of guessing without the trace. 0. **GREP NAMED FIRST.** Before any decompilation work, search `docs/research/named-retail/acclient_2013_pseudo_c.txt` by `class::method` name. 99.6% of functions have real names from the Sept 2013 EoR build PDB. `docs/research/named-retail/acclient.h` has every retail struct verbatim. `docs/research/named-retail/symbols.json` is greppable by name or address (regenerate via `py tools/pdb-extract/pdb_extract.py refs/acclient.pdb`). Only fall back to Step 1 below if the named pseudo-C lacks a function (rare — covers only the obfuscated/packed minority). 1. **DECOMPILE FIRST (fallback).** Only when grep-named-first returned nothing. Find the matching function in the older Ghidra chunks at `docs/research/decompiled/` or decompile a new region using `tools/decompile_acclient.py`. Use the function map at `docs/research/acclient_function_map.md` (cross-port index) + `docs/research/named-retail/symbols.json` (raw PDB names) to find known functions. If the function isn't mapped yet, search by characteristic constants (motion commands, magic numbers, string literals). 2. **CROSS-REFERENCE.** Check the decompiled code against ACE's C# port (`references/ACE/Source/ACE.Server/Physics/`) and ACME's `ClientReference.cs`. The decompiled code is ground truth; ACE and ACME are interpretation aids. If they disagree, the decompiled code wins. 3. **WRITE PSEUDOCODE.** Translate the decompiled C into readable pseudocode before porting to C#. Save it in `docs/research/*_pseudocode.md` for future reference. This step catches misinterpretations before they become bugs. 4. **PORT FAITHFULLY.** Translate the pseudocode to C# line-by-line. Use the same variable names, the same control flow, the same boundary conditions. Do not "improve" or "simplify" the algorithm — the retail client's code works; our job is to match it. 5. **CONFORMANCE TEST.** Write tests that verify our port matches the decompiled behavior. Use golden values from the decompiled code or from ACME's conformance tests. If the function touches terrain, port the 4M-cell sweep from `TerrainConformanceTests.cs`. 6. **INTEGRATE SURGICALLY.** When wiring ported code into the renderer or game loop, change the MINIMUM necessary. Keep existing working transform pipelines, only replace the specific computation. The animation sequencer integration proved this: replacing the slerp source was safe; replacing the entire transform composition broke everything. ### The divergence register (mandatory bookkeeping) [`docs/architecture/retail-divergence-register.md`](docs/architecture/retail-divergence-register.md) is the single auditable list of every KNOWN place acdream's runtime behavior can deviate from retail (108 rows at creation, 2026-06-12: intentional architecture / adaptation / approximation / stopgap / unclear). Two rules, both binding on subagents too: 1. **Any commit that introduces a deviation** (an adaptation, an approximation, a stopgap, a "retail does X but we...") **adds its register row IN THE SAME COMMIT.** Any commit that ports the retail mechanism deletes the row in the same commit. A deviation found without a row is a bug twice over. 2. **Any unexplained visual/physics symptom → scan the register BEFORE instrumenting.** The "Risk if assumption breaks" column is written as the symptom you'd observe (the #119 vanishing staircase, the #112 transparent cottage, and the knife-edge flap all lived in rows of this register's scope before they had names). The register holds one-line rows and pointers — detail lives at the cited `file:line` and in the digests, never in the register itself. ### What NOT to do: - **Do not guess** at AC-specific algorithms, formulas, constants, wire formats, or coordinate conventions. Ever. **The named retail decomp has the answer for almost everything; guessing is no longer a recoverable error, it's negligence.** If you can't find it in `docs/research/named-retail/`, file a research note and ASK before writing. - **Do not "fix" the decompiled code.** If the retail client does something that looks wrong, it's probably right. Verify before changing. - **Do not skip the pseudocode step.** The frame-swap bug was caused by misreading the decompiled C directly into C# without an intermediate translation. - **Do not integrate via subagent** unless the subagent has the full context of the existing code it's modifying. The first animation sequencer integration was done by a subagent that didn't understand the transform pipeline — it broke everything. - **Do not replace working retail-faithful logic with a modern redesign** without explicit user approval. Two campaigns (267 min remote-entity prediction+rubber-band replacing hard-snap; speculative shader edits in the sky-fog work) had to be reverted in full because the redesign regressed behavior the original port had right. When you see "I could simplify this with X" on a retail-port, flag the tradeoff and ask before deleting the existing path. Retail-faithful first; "cleaner" second. ### Phase completion checklist: Before marking any phase as done: - [ ] Every AC-specific algorithm has a decompiled reference cited in comments (named symbol + address from `named-retail/symbols.json`, OR function address + chunk file from older `decompiled/` chunks) - [ ] Every retail deviation this phase introduced has a row in `docs/architecture/retail-divergence-register.md` (and every deviation it retired had its row deleted) - [ ] Conformance tests exist for the critical paths - [ ] The code was cross-referenced against at least 2 reference repos - [ ] `dotnet build` green, `dotnet test` green - [ ] Visual verification by the user (if applicable) - [ ] Roadmap updated - [ ] Memory updated if there's a durable lesson ## Retail debugger toolchain (live runtime trace) **When the question is "what does retail actually DO frame-by-frame?"** the decomp alone is often not enough — code paths interact with state (LastKnownContactPlane, transient flags, accumulated counters) in ways that aren't obvious from reading. We have a working toolchain to attach Windows' console debugger (cdb.exe) to a live retail acclient.exe with full PDB symbols and capture state at any breakpoint. **Use this when guessing has failed twice in a row.** ### What we have - **Matching binary**: `C:\Users\erikn\Downloads\acclient.exe` v11.4186 (linker timestamp `2013-09-06 00:17:56 UTC`, CodeView GUID `9e847e2f-777c-4bd9-886c-22256bb87f32`). Pairs exactly with our `refs/acclient.pdb`. The current `C:\Turbine\Asheron's Call\acclient.exe` is a 2015 build with GUID `08e25c14-e2a1-46d5-b056-92b2e43a7234` and must not be used for this PDB/address map. - **Debugger**: `cdb.exe` (console WinDbg) at `C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\cdb.exe`. Install via Microsoft Store WinDbg (~50 MB). 32-bit version is required for acclient.exe. - **PDB**: `refs/acclient.pdb` (29 MB, Sept 2013 EoR build). 18,366 named functions + 5,371 named struct types resolve. - **Symbol verifier**: `tools/pdb-extract/check_exe_pdb.py ` reads any acclient.exe and prints whether it pairs with our PDB (`MATCH` / `MISMATCH (expected GUID = ...)`). Always run this on a candidate binary BEFORE attaching. - **PDB metadata dumper**: `tools/pdb-extract/dump_pdb_info.py refs/acclient.pdb` prints the PDB's expected timestamp + GUID + age. Use to figure out which build to look for if the chain ever breaks. ### Workflow 1. **Verify the binary matches the PDB:** ```bash py tools/pdb-extract/check_exe_pdb.py "C:/Users/erikn/Downloads/acclient.exe" ``` Expect: `=== MATCH: this exe pairs with our acclient.pdb ===` 2. **Have the user launch retail client** and connect to local ACE. Retail must already be in-world before attaching. 3. **Write a `.cdb` script** that arms breakpoints with non-blocking actions (count + log + `gc`). Pattern: ``` .logopen .sympath C:\Users\erikn\source\repos\acdream\refs .symopt+ 0x40 .reload /f acclient.exe r $t0 = 0 bp acclient!CTransition::transitional_insert "r $t0 = @$t0 + 1; .if (@$t0 % 5000 == 0) { .printf \"...\" }; .if (@$t0 >= 30000) { qd } .else { gc }" bp acclient!OBJECTINFO::kill_velocity "r $t1 = @$t1 + 1; gc" ... g ``` `gc` = "go conditional" (continue without breaking). Auto-detach via `qd` after a hit-count threshold to avoid manual cleanup. 4. **Launch cdb in the background** via a PowerShell wrapper: ```powershell & "C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\cdb.exe" ` -pn acclient.exe -cf