From 5a16acc56d572343eaef65367b6b16ca0ff22f79 Mon Sep 17 00:00:00 2001 From: Luis Gonzalez Date: Tue, 7 Jul 2026 21:38:10 -0700 Subject: [PATCH] Docs: attribute-swallow root cause + session log; CLAUDE.md gotcha + 6.5 accuracy fixes (net-zero) Co-Authored-By: Claude Opus 4.8 (1M context) --- CLAUDE.md | 18 +++--- .../06_Roadmap/Iteration_2026-07_CoopHades.md | 21 ++++--- ...Expedition_Enemy_Stuck_Harvest_Feedback.md | 1 + ..._Phase15_NodeFeedback_Attribute_Swallow.md | 62 +++++++++++++++++++ .../_Meta/CLAUDE_Build_Gotchas_Archive.md | 10 +++ 5 files changed, 96 insertions(+), 16 deletions(-) create mode 100644 Docs/Vault/07_Sessions/2026/2026-07-07_Phase15_NodeFeedback_Attribute_Swallow.md diff --git a/CLAUDE.md b/CLAUDE.md index 918935b01..d66bc2cf2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -7,9 +7,9 @@ Multiplayer game on **Unity DOTS (Entities) + Netcode for Entities** — server- **Hard limit: 40 KB (40 960 bytes). This file is context-loaded every session — over-budget gets it truncated. Keep ≥1 KB of headroom below it (target ≤ ~39 KB).** After **any** edit, keep it under budget: - **Size check** — bash: `wc -c CLAUDE.md` · PowerShell: `(Get-Item CLAUDE.md).Length`. Must be `< 40960`. -- **Archive, don't delete.** When trimming, append the verbose / least-hot detail to the obsidian reference note `Docs/Vault/_Meta/CLAUDE_Build_Gotchas_Archive.md` under a **new dated heading** (never overwrite an older snapshot), and leave a one-line pointer + the relevant `[[DR-###]]` link here. Design rationale already lives in the per-milestone DRs (`Docs/Vault/07_Sessions/_Decisions/DR-###`). +- **Archive, don't delete.** When trimming, append the verbose / least-hot detail to the obsidian reference note `Docs/Vault/_Meta/CLAUDE_Build_Gotchas_Archive.md` under a **new dated heading** (never overwrite an older snapshot), and leave a one-line pointer + the relevant `[[DR-###]]` link here. - **Net-zero rule:** every addition is paid for by a condensation elsewhere. Keep only the hottest, highest-recurrence operational rules inline (flag them **★**); depth lives in the archive + DRs. -- Condensation history: 06-04 → 06-17 (M1–END-2 long-form + 6.5 stack swap → archive) · 07-04 (base-mining core-loop bullet + stack-history sentence → archive; DR-044 supersedes) · 07-04b (EB-1/2 · END-1/2 bullets → DR pointers, pays DR-045) · 07-04c (Rukhanka-samples/bake/history/enemy-anim bullets trimmed → archive, pays the DR-046 portal/class/prep additions + Health.Max fix) · 07-06 (Aim-controls + URP-version + VolumeProfile + HDRP-convert → archive; pays the HUMANOID-pipeline correction — the "Generic skeleton" note was WRONG). +- Condensation history (detail → archive dated headings): 06-17 · 07-04 · 07-04b · 07-04c · 07-06 · 07-07b (pays the attribute-swallow gotcha). ## Stack — Unity 6.5.1 (`6000.5.1f1`, stable) as of 2026-06-27 @@ -26,7 +26,7 @@ Multiplayer game on **Unity DOTS (Entities) + Netcode for Entities** — server- | `com.unity.mathematics` | 1.4.0 | (transitive) | | `com.rukhanka.animation` | **2.9.0** | Local pkg (`Packages/com.rukhanka.animation`). ECS skeletal animation (Burst CPU/GPU skinning). Resolves on 6.5.0 via SemVer floor. Netcode replication **OFF** → client-derived. See [[DR-022_Animation_Pipeline_Rukhanka_Synty]]. | -Values match `packages-lock.json` (reconciled 2026-06-17; URP 17.5.0, test-framework 1.7.0, ugui 2.5.0, multiplayer.center 1.0.1; `com.unity.ai.assistant` REMOVED 07-04 — console noise). **History:** 6.4.7→6.5.0 swap validated green; the 6.6.0a6 transport bug misses 6.5-stable — [[DR-002_Unity66_Alpha_Netcode_Transport]] + archive. +Values match `packages-lock.json` (reconciled 2026-06-17; URP 17.5.0, test-framework 1.7.0, ugui 2.5.0, multiplayer.center 1.0.1; `com.unity.ai.assistant` REMOVED 07-04 — console noise). **History:** 6.6.0a6 transport bug — [[DR-002_Unity66_Alpha_Netcode_Transport]] + archive. ## Namespaces & assembly split @@ -56,7 +56,7 @@ Long-form originals + the milestone each came from: `Docs/Vault/_Meta/CLAUDE_Bui - `IInputComponentData` requires implementing **`FixedString512Bytes ToFixedString()`**. ### Burst hazards ★ -- **Cross-assembly generics + enums trip Burst internal compiler errors.** Predicted-spawn classification (`SnapshotDataBufferComponentLookup.TryGetComponentDataFromSnapshotHistory`, takes `ref DynamicBuffer` in 1.13.2) and any enum compared inside a Bursted system are the known offenders. Make such systems **plain non-Burst `ISystem`**, and **store ops/schemes/region ids as `byte`, never `enum`** in anything Bursted or in RPC payloads. +- **Cross-assembly generics + enums trip Burst internal compiler errors.** Predicted-spawn classification (`SnapshotDataBufferComponentLookup.TryGetComponentDataFromSnapshotHistory`) and any enum compared inside a Bursted system are the known offenders. Make such systems **plain non-Burst `ISystem`**, and **store ops/schemes/region ids as `byte`, never `enum`** in anything Bursted or in RPC payloads. - **A Burst ICE corrupts the editor's incremental cache** → afterward, valid `[BurstCompile]` entry points log `"… is not a known Burst entry point"` + run slow managed-fallback. A clean compile + green tests + working runtime confirm the *code* is fine. **Fix = editor restart** (or delete `Library/BurstCache` while closed); a domain reload alone does NOT clear it. - **Editing a Bursted ISystem's SystemAPI query set on an UNFOCUSED editor can leave a STALE binary** → runtime `InvalidOperationException: "required component type was not declared in the EntityQuery"` from an *unrelated* `GetSingleton` (Burst stack reports the OLD line number). Workaround: Burst compilation OFF for the session; permanent fix = restart. Prefer a **focused** editor for Burst-affecting edits. @@ -75,7 +75,7 @@ Long-form originals + the milestone each came from: `Docs/Vault/_Meta/CLAUDE_Bui - **Cooldown/spawn "next tick" sentinels:** route every stored tick through **`TickUtil.NonZero(...)`** (a computed `ServerTick+delay` can wrap to 0, the "ready" sentinel) and compare with `NetworkTick.IsNewerThan` / `.TicksSince`, **never** raw `uint <` / subtraction. - **`GhostRelevancy` for region splits:** use `GhostRelevancyMode.SetIsIrrelevant` (not `SetIsRelevant`) so untagged/global ghosts stay relevant for free — only enumerate cross-region ghosts to hide. `RegionTag{byte Region}` is **server-only, NOT a `[GhostField]`**. **★ A 2nd region sharing an EXISTING tag (`EnemyTag`) → re-audit every query/cull over it: once-safe global despawns/cleared-checks then wipe or block cross-region (DR-031, DR-040).** `RelevantGhostForConnection` = `{int Connection=NetworkId.Value; int Ghost=ghostId}`. See [[DR-013_M6_Aether_Cycle_Region_Split]]. - **Shared GLOBAL state (cycle phase, resource ledger, goal meter) rides an UNTAGGED ghost**, never a region-tagged one (`SetIsIrrelevant` would hide it cross-region). Resolve the ledger via its DISTINCT `ResourceLedger` tag (the multi-`StorageEntry` "multiple instances" rule — EB-2 line). -- **Frontend world lifecycle (menu → on-demand worlds) ★:** `CreateLocalWorld` is `internal` in 1.13.2 — use public `CreateClientWorld`/`CreateServerWorld` (they register the `ServerWorld`/`ClientWorld` statics the UI reads); menu world via `DefaultWorldInitialization.Initialize(name, false)`. **Never dispose/create worlds inside an ECS system** — do it on a frame-boundary coroutine (`SessionRunner`, `DontDestroyOnLoad`). The gameplay subscene streams in ONLY if a netcode world is the `DefaultGameObjectInjectionWorld` at `LoadScene` time. See [[DR-019_Frontend_Menu_Settings_Saves_Build]]. +- **Frontend world lifecycle (menu → on-demand worlds) ★:** use `CreateClientWorld`/`CreateServerWorld` (they register the `ServerWorld`/`ClientWorld` statics the UI reads; `CreateLocalWorld` was internal pre-6.5, PUBLIC on 6.5.0); menu world via `DefaultWorldInitialization.Initialize(name, false)`. **Never dispose/create worlds inside an ECS system** — do it on a frame-boundary coroutine (`SessionRunner`, `DontDestroyOnLoad`). The gameplay subscene streams in ONLY if a netcode world is the `DefaultGameObjectInjectionWorld` at `LoadScene` time. See [[DR-019_Frontend_Menu_Settings_Saves_Build]]. ### Physics & character controller - **Unity Physics 1.x bakes built-in `UnityEngine` colliders + `Rigidbody`** (the Physics-0.x `PhysicsShapeAuthoring`/`PhysicsBodyAuthoring` are gone). Static collider (no Rigidbody) → baked into the subscene PhysicsWorld, deterministic, no replication. `Rigidbody.FreezeRotation` is **NOT** honored by the baker — zero angular velocity + write rotation each tick, or set `PhysicsMass.InverseInertia = float3.zero`. @@ -129,7 +129,7 @@ Full rationale: [[DR-022_Animation_Pipeline_Rukhanka_Synty]] · [[DR-023_Enemy_A ### MCP / editor workflow ★ - **Edit Assets `.cs` ONLY via MCP `apply_text_edits` / `create_script`** (Unity's scripting pipeline) — the raw `Write` tool does NOT reliably trigger a recompile on an unfocused editor → tests/`execute_code` run a **stale assembly**; a raw-`Write`-created NEW `.cs` gets **no `.meta` / no test-discovery** until `refresh_unity scope=all mode=force`. (`Write`/`Edit` are fine for non-asset files: this vault, asmdef JSON, etc.) `script_apply_edits` **`anchor_replace`** (regex) + **`delete_method`** work even on a `struct : ISystem`. -- **`apply_text_edits` with MULTIPLE non-adjacent edits in one call can MISALIGN** — one edit per call (or strict bottom-first), always with `precondition_sha256` (it returns the current SHA on mismatch). **`create_script` won't overwrite**; full-file rewrites = whole-span `apply_text_edits` (its brace-balance validator guards botched spans) or `manage_script delete`+`create_script` (NON-GUID-referenced files only — systems/tests, never authoring MonoBehaviours). `script_apply_edits replace_method` is safe for class methods but **can't target a `struct : ISystem`**. [[DR-017_Persistent_Base_Player_Driven_Pacing]] +- **`apply_text_edits` with MULTIPLE non-adjacent edits in one call can MISALIGN** — one edit per call (or strict bottom-first), always with `precondition_sha256` (it returns the current SHA on mismatch). **★ One edit can SWALLOW an adjacent attribute/comment line** (07-06 `_portalMat` NRE · 07-07 `[RuntimeInitializeOnLoadMethod]` off `WorldFeelConfig.ResetDefaults` → feedback slice silently dead) — re-read neighbors after editing beside attributes; silent presentation slice → probe its config's `Enabled` in-play. **`create_script` won't overwrite**; full-file rewrites = whole-span `apply_text_edits` (its brace-balance validator guards botched spans) or `manage_script delete`+`create_script` (NON-GUID-referenced files only — systems/tests, never authoring MonoBehaviours). `script_apply_edits replace_method` is safe for class methods but **can't target a `struct : ISystem`**. [[DR-017_Persistent_Base_Player_Driven_Pacing]] - **`execute_code` runs as a method body** — no `using` directives (parsed as statements); fully-qualify every type. Identify worlds by `world.Name == "ServerWorld"/"ClientWorld"` (flags overlap a shared `Game` bit). - **`manage_gameobject create` / `manage_prefabs modify_contents` `component_properties` SILENTLY DROP enum + Vector3 fields** — set those via a follow-up `manage_components set_property` and VERIFY through `mcpforunity://scene/gameobject/{id}/component/{Type}` (or read the baked component in `execute_code` after Play). `manage_material set_renderer_color` uses a runtime PropertyBlock that does NOT persist into Play — create + assign a material asset instead. - **New ghost prefab recipe:** `manage_asset duplicate` an existing correctly-configured ghost (e.g. `UpgradePickup.prefab`) → `manage_prefabs modify_contents` to swap the authoring MonoBehaviour (strip MeshFilter+MeshRenderer for an invisible state-holder) — its ownerless/interpolated `GhostAuthoringComponent` + `LinkedEntityGroupAuthoring` come free. **Runtime-spawn shared ghosts** via a one-shot server spawner (dodges the prespawn handshake); wire a baked spawner into the subscene via `manage_scene load additive` → `set_active_scene Gameplay` → create+verify → `save` → `close_scene`. @@ -144,7 +144,7 @@ Full rationale: [[DR-022_Animation_Pipeline_Rukhanka_Synty]] · [[DR-023_Enemy_A ## DOTS / ECS conventions (authoritative summary) -Full rules: `~/.claude/skills/dots-dev/references/dots-conventions.md` (Windows: `%USERPROFILE%\.claude\skills\dots-dev\references\`). These **replace** classic MonoBehaviour/GameObject patterns. +Full rules: `.claude/skills/dots-dev/references/dots-conventions.md` (in-repo; travels with the repo). These **replace** classic MonoBehaviour/GameObject patterns. - **`struct : IComponentData`** is the default (unmanaged, Burst/job-friendly). `class : IComponentData` only for genuine managed refs (main-thread, no Burst). `IBufferElementData` for per-entity arrays. `IEnableableComponent` to toggle state without a structural change. - **Systems:** `ISystem` (struct) + `[BurstCompile]` is the **default**; `SystemBase` only when touching managed objects. `SystemAPI.Query<…>()` to iterate. **Aspects (`IAspect`) are DEPRECATED (Entities 1.4+) — do not author new ones.** `Entities.ForEach` is legacy. @@ -152,12 +152,12 @@ Full rules: `~/.claude/skills/dots-dev/references/dots-conventions.md` (Windows: - **Structural changes** (add/remove component, create/destroy entity) invalidate handles + cause sync points → batch via **`EntityCommandBuffer`** (Begin/End`Simulation`EntityCommandBufferSystem; `.AsParallelWriter()` in parallel jobs). - **Baking:** `…Authoring` MonoBehaviour + `class FooBaker : Baker` → `GetEntity(authoring, TransformUsageFlags.…)` then `AddComponent`. Subscenes stream async — entities aren't present the instant a reference exists. - **Netcode:** ghosts = replicated entities (`GhostAuthoringComponent` + `[GhostField]`); predicted (player-controlled, rolled back) vs interpolated. Core sim runs in `PredictedSimulationSystemGroup` (fixed step, **runs multiple times per frame** on rollback → deterministic/idempotent; filter with `.WithAll()`). **Server-authoritative: clients send input (`IInputComponentData`), not state.** RPCs (`IRpcCommand`) for one-off events. **No wall-clock/`Time.deltaTime`/`System.Random` in predicted sim.** -- **Always verify volatile DOTS/Netcode API shape via context7 at code-time** — do not trust memory. Pinned IDs: Entities → `/websites/unity3d_packages_com_unity_entities_6_5_manual`; Netcode → `/websites/unity3d_packages_com_unity_netcode_1_10_api` (closest published; we run 1.13.2 — re-resolve if a 1.13 set appears); ECS samples → `/unity-technologies/entitycomponentsystemsamples`. +- **Always verify volatile DOTS/Netcode API shape via context7 at code-time** — do not trust memory. Pinned IDs: Entities → `/websites/unity3d_packages_com_unity_entities_6_5_manual`; Netcode → `/websites/unity3d_packages_com_unity_netcode_1_10_api` (closest published as of 07-07, no 6.x set yet — installed is 6.5.0; re-resolve periodically); ECS samples → `/unity-technologies/entitycomponentsystemsamples`. ## Testing - **Default = plain-Entities EditMode test:** create a `World`, register the system in `SimulationSystemGroup`, tick, assert. Public API, version-independent. Example: `Assets/_Project/Tests/EditMode/HeartbeatSystemTests.cs`. Run via `run_tests(mode="EditMode", assembly_names=["ProjectM.Tests.EditMode"])`. -- **`NetCodeTestWorld` is `internal`** in netcode 1.13.2, exposed only to a fixed `[InternalsVisibleTo]` allow-list — to use it, name a test asmdef to match an entry (e.g. `Unity.NetcodeSamples.EditModeTests`) or vendor the test utils. Netcode world boot is covered by the Play Mode check, not a NetCodeTestWorld test. See [[DR-001_Netcode_Test_Harness]]. +- **`NetCodeTestWorld` is `internal`** (6.5.0 re-check: not even loaded outside test asmdefs), exposed only to a fixed `[InternalsVisibleTo]` allow-list — to use it, name a test asmdef to match an entry (e.g. `Unity.NetcodeSamples.EditModeTests`) or vendor the test utils. Netcode world boot is covered by the Play Mode check, not a NetCodeTestWorld test. See [[DR-001_Netcode_Test_Harness]]. - Burst/source-gen errors surface at editor compile, not a plain build — always `read_console` after script changes, and run a play/tick test, not just a compile. **Cover swept hit-detection with a tunnelling regression test** (the point-check tunnel bug doesn't surface in a point-based unit test). ## Guardrails diff --git a/Docs/Vault/06_Roadmap/Iteration_2026-07_CoopHades.md b/Docs/Vault/06_Roadmap/Iteration_2026-07_CoopHades.md index d1caf45e4..d741ba00e 100644 --- a/Docs/Vault/06_Roadmap/Iteration_2026-07_CoopHades.md +++ b/Docs/Vault/06_Roadmap/Iteration_2026-07_CoopHades.md @@ -63,7 +63,8 @@ after a playtest gate). **Blender scope: hero props + decals.** No time estimate > **Gate outcome (2026-07-07 operator playtest):** trunk holds mechanically, but the verdict is *"not cohesive, very > static, visually boring — comparable games' environments are dynamic and interactable"* + ability upgrades boring + -> the 07-07 harvest shrink/pop never showed on-screen (the pre-flagged PostTransformMatrix-on-root risk is real). +> the 07-07 harvest shrink/pop never showed on-screen (root cause later pinned same day: a swallowed +> `[RuntimeInitializeOnLoadMethod]` attribute — NOT the pre-flagged PTM-on-root risk, which live-verified correct). > → **Phase 1.5 (World Alive) inserted below; mechanic-changer boons PULLED FORWARD from Phase 3 (Phase 1.7).** > Explicit operator framing: make the game **look better**, not more "juice" — the code-side hit-feel layer stays as-is. @@ -110,12 +111,18 @@ attacks/animations/effects**. Art direction deliberately UNDECIDED — lighting ### Stabilize first (bugs, do before the pass) -1. **Node harvest feedback fix:** the shrink/pop drives `PostTransformMatrix` on the ghost ROOT, but node meshes are - render (LEG) children → scale the render children instead (the 07-07 session log pre-flagged exactly this). - **Verify ON-SCREEN this time** — the operator's eyes are the test for skinned/scaled visuals. -2. **Collision precision pass:** operator reports collisions imprecise/not working well. Diagnose scope FIRST - (environment collision vs body-blocking vs melee/projectile hit registration — each is a different system) with a - live repro session, then fix. Swept-not-point rules apply to anything hit-reg. +1. ~~Node harvest feedback fix~~ **FIXED (2026-07-07 session B).** Root cause was NOT the pre-flagged + PTM-on-root risk — live inspection proved the Model child properly parented and root-PTM propagation exact + (root PTM 0.783 → child LTW 1.723 = 2.2×0.783). The 07-07 knob edit had **swallowed the + `[RuntimeInitializeOnLoadMethod]` attribute** off `WorldFeelConfig.ResetDefaults` → `Enabled=false` → the WHOLE + feedback slice (shrink/pop + chips + SFX + micro-punch) silently dead. One-line restore; verified on-screen + + numerically on a live server-spawned node; 455/455. Operator still eyeballs a real harvest in the next playtest. +2. **Collision precision pass — SCOPED (2026-07-07 operator): environment collider FIDELITY.** Collision shapes + don't match the visual meshes (cover rocks etc.) — invisible walls / visual clipping. TODO: audit every + Environment-layer collider (boundary rim, landmark rocks, in-room cover props) and fit them to the render mesh + (tightened primitives or convex mesh colliders); verify against the baked subscene PhysicsWorld (that's where + they live — classic-GameObject views lie). Re-validate the enemy stuck-fix backstop after any shape change + (Depenetrate/tangent-slide was tuned on the old shapes). ### Short-term wins (asset/scene/client layer) diff --git a/Docs/Vault/07_Sessions/2026/2026-07-07_Expedition_Enemy_Stuck_Harvest_Feedback.md b/Docs/Vault/07_Sessions/2026/2026-07-07_Expedition_Enemy_Stuck_Harvest_Feedback.md index 20dc1f8a3..9bf7ddaab 100644 --- a/Docs/Vault/07_Sessions/2026/2026-07-07_Expedition_Enemy_Stuck_Harvest_Feedback.md +++ b/Docs/Vault/07_Sessions/2026/2026-07-07_Expedition_Enemy_Stuck_Harvest_Feedback.md @@ -40,6 +40,7 @@ Driving an AFK run headless is flaky: the buffed player's `Health` is **reset to ## Not freeze-framed (operator eyeball on next playtest — trivial with real input) - **Node shrink/pop VISUAL** — `NodeFeedbackSystem` ran clean against the 2 nodes but the run-timing never let me catch the shrink on-screen. **One risk to confirm:** the shrink uses `PostTransformMatrix` on the node **root** — this scales the mesh only if the mesh is on the root or on *parented* children. If a node's mesh is an unparented render child, scale the children instead (cheap fix). + - **RESOLVED same day (session B — hypothesis WRONG):** the child is properly parented and root-PTM propagation is exact; the real bug was this session's own knob edit **swallowing the `[RuntimeInitializeOnLoadMethod]` attribute** off `WorldFeelConfig.ResetDefaults` → `Enabled=false` → the whole feedback slice dead. See [[2026-07-07_Phase15_NodeFeedback_Attribute_Swallow]]. ## Next-session intent Operator playtests the expedition: confirm (1) no stuck stragglers across a few rooms, (2) markers read well / not cluttered, (3) node shrink+pop visible on harvest, (4) tune knobs (`FeelConfig.EnemyMarker*`, `WorldFeelConfig.Node*`/`Chip*`, backstop consts in `EnemyAISystem`). Then bank as a DR if the design sticks. diff --git a/Docs/Vault/07_Sessions/2026/2026-07-07_Phase15_NodeFeedback_Attribute_Swallow.md b/Docs/Vault/07_Sessions/2026/2026-07-07_Phase15_NodeFeedback_Attribute_Swallow.md new file mode 100644 index 000000000..65bcb7c95 --- /dev/null +++ b/Docs/Vault/07_Sessions/2026/2026-07-07_Phase15_NodeFeedback_Attribute_Swallow.md @@ -0,0 +1,62 @@ +--- +title: 2026-07-07_Phase15_NodeFeedback_Attribute_Swallow +type: note +permalink: gamevault/07-sessions/2026/2026-07-07-phase15-nodefeedback-attribute-swallow +--- + +# 2026-07-07 (session B) — Phase 1.5 stabilize: harvest feedback was dead — a swallowed attribute + +**Driver:** `/dots-dev` (Small track — client-only static config, no wire/bake change). Operator playtest reported the +harvest hit-effects "don't seem to be working"; this session also landed the roadmap restructure (Phase 1 gate verdict +→ Phase 1.5 World Alive + boons pulled to 1.7, committed `d966813a3`). + +## Root cause — NOT the pre-flagged PTM risk + +The 07-07 session-A log suspected the `PostTransformMatrix`-on-ghost-ROOT shrink wouldn't reach an unparented render +child. **Live inspection disproved that**: a server-spawned `ResourceNode` ghost (RegionTag overridden to Base so +relevancy kept it near the player) showed the `Model` child properly `Parent`ed (LT.scale 2.2, MMI on child, root +mesh-less), and once enabled the propagation was *exact* — root PTM 0.892 → child LTW 1.962 = 2.2 × 0.892. + +The real bug: **the 07-07 "Node* knobs" edit to `WorldFeelConfig.cs` swallowed the +`[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]` attribute** above +`ResetDefaults()` (the diff in commit `cc2e7ad95` shows the attribute+signature lines replaced by the knob block +ending in a zero-indent bare signature). Nothing else calls `ResetDefaults` → every field stayed C# default: +`Enabled=false`, `ChipBurstCount=0`, SFX 0 → **the entire world-feedback slice silently dead** — node shrink/pop, +chip particles, harvest SFX, camera micro-punch. Matches the operator report exactly ("hit effects + some other +stuff"). Second occurrence of the *edit-swallows-adjacent-line* class in two days (07-06 `_portalMat` +comment-swallow NRE). + +## Diagnosis harness (reusable) + +1. Play; server `execute_code`: find the node prefab (`ResourceNode` + `Prefab`, `IncludePrefab`), `Instantiate`, + `baked.WithPosition(playerPos + offset)`, **`RegionTag{Region=0}`** so region relevancy doesn't cull it at base. +2. Client dump: LEG per-entity `Parent`/`LocalTransform`/`PostTransformMatrix`/`MaterialMeshInfo` + LTW scale. +3. Simulated hits = server-side `Remaining -=` writes (replicates; no player positioning needed); numeric check = + `lerp(NodeMinScale, 1, Remaining/Max)` vs root PTM vs child LTW; screenshot via `ScreenCapture.CaptureScreenshot` + in `execute_code` (async — read the PNG on the next MCP round-trip; never the pausing bridge capture). + +## Fix + validation + +- One edit: restored the attribute on `WorldFeelConfig.ResetDefaults` (`apply_text_edits` + precondition SHA). +- **L1** console clean (baseline warnings only). **L2** EditMode **455/455**. **L3** fresh Play with NO manual poke: + `Enabled=True ChipBurstCount=12 NodeMinScale=0.35` purely from the attribute; end-to-end on a fresh spawned node — + server 30→20, client root PTM **0.783** (= lerp(0.35,1,20/30) exact), child LTW **1.723** (= 2.2×0.783), node + visibly shrunken on-screen (`scratchpad/node_shrink_fixed.png`). + +## Lessons + +- **An MCP text edit can swallow an adjacent attribute/comment line** — after editing next to an attributed method, + re-read the surrounding lines (now a CLAUDE.md gotcha; 2 shipped bugs in 2 days from this class). +- **When a whole presentation slice goes silent, probe its static config's `Enabled` in-play first** — one + `execute_code` read beats structural theorizing (the PTM hypothesis was plausible and wrong). +- The [[mcp-screenshot-step-pause-bug]] + no-sleep-poll rules held: hits were injected server-side and read on + subsequent round-trips; no pauses, no freezes. + +## Next-session intent + +Operator playtest confirms harvest feel on real nodes (knobs: `WorldFeelConfig.Node*`/`Chip*`), then the +**collision-precision live repro** (Phase 1.5 stabilize #2) — scope whether "imprecise collisions" means environment +collision, body-blocking, or melee/projectile hit-reg before touching any system. Then the lighting/atmosphere pass. + +Related: [[2026-07-07_Expedition_Enemy_Stuck_Harvest_Feedback]] · [[Iteration_2026-07_CoopHades]] · +[[DR-044_Expedition_Redesign_Shipped_Demo_Polish]] diff --git a/Docs/Vault/_Meta/CLAUDE_Build_Gotchas_Archive.md b/Docs/Vault/_Meta/CLAUDE_Build_Gotchas_Archive.md index a87a30d7b..80b71514b 100644 --- a/Docs/Vault/_Meta/CLAUDE_Build_Gotchas_Archive.md +++ b/Docs/Vault/_Meta/CLAUDE_Build_Gotchas_Archive.md @@ -437,3 +437,13 @@ from the deleted samples tree) so the deformation-material rule is still self-co ### Synty HUD skin / HudTheme (verbatim from CLAUDE.md) - **Synty HUD skin via a build-safe `HudTheme` ★ (DR-024):** a runtime name-string `Resources.Load` of Synty sprites is **build-stripped** → use a curated `HudTheme : ScriptableObject` of serialized refs (`HudTheme.Get()` null-safe, flat fallback). `unityBackgroundImageTintColor` MULTIPLIES; don't set `unitySlice*` on 9-slice sprites (per-element ERROR); Synty sprites may import as **Multiple** → `LoadAssetAtPath` null. See [[DR-024_HUD_Synty_Skin_Theme]]. + +## 2026-07-07b — condensation-history long form + attribute-swallow incident (Phase 1.5 stabilize) + +**Trimmed from CLAUDE.md this pass** (pays the new apply_text_edits attribute-swallow gotcha): + +- Old condensation-history line, long form: "06-04 → 06-17 (M1–END-2 long-form + 6.5 stack swap → archive) · 07-04 (base-mining core-loop bullet + stack-history sentence → archive; DR-044 supersedes) · 07-04b (EB-1/2 · END-1/2 bullets → DR pointers, pays DR-045) · 07-04c (Rukhanka-samples/bake/history/enemy-anim bullets trimmed → archive, pays the DR-046 portal/class/prep additions + Health.Max fix) · 07-06 (Aim-controls + URP-version + VolumeProfile + HDRP-convert → archive; pays the HUMANOID-pipeline correction — the 'Generic skeleton' note was WRONG)." +- Old stack-history sentence: "6.4.7→6.5.0 swap validated green; the 6.6.0a6 transport bug misses 6.5-stable" (DR-002 holds the full story). +- Old maintenance-section sentence: "Design rationale already lives in the per-milestone DRs (`Docs/Vault/07_Sessions/_Decisions/DR-###`)." + +**The incident behind the new gotcha:** the 2026-07-07 "Node* knobs" edit to `WorldFeelConfig.cs` (commit `cc2e7ad95`) replaced a span that included the `[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]` attribute above `ResetDefaults()`, leaving a bare zero-indent signature. Nothing else calls `ResetDefaults` → all statics stayed C# defaults (`Enabled=false`, `ChipBurstCount=0`) → the ENTIRE harvest/world-feedback slice (node shrink/pop, chip particles, SFX, camera micro-punch) was silently dead; the operator caught it in the Phase 1 gate playtest. Live diagnosis disproved the session-log's own PTM-on-root hypothesis first (the Model child is properly parented; root PTM 0.783 → child LTW 1.723 = 2.2×0.783, exact). Second edit-swallows-adjacent-line bug in two days (07-06 `_portalMat` comment-swallow NRE). Full write-up: `07_Sessions/2026/2026-07-07_Phase15_NodeFeedback_Attribute_Swallow.md`.