- drop DirectionMapper; new MapState(state); addLayer(alias)+setEventLayer; remove addHook().load()
- align removed getLayerByAlias/getLayerAlias to layerList/alias and setDynamicDirection to tile.setFaceDirection
- dispose stale codes 62/63/64/84/130 per Q1=A; keep 60/61
- tests: expect default events for transferToDynamic(keepEvent=false); setMapList does not dedup
- getMapData(x,y,w,h): end index lineStart+nw and buffer width*height (keep out-of-range zero padding)
- toStatic(keepEvent=false): reset reused static tile to default events via loadState({})
- cropPointEvents: reindex by new width; resize/resize2 sync indexer width; resize2 clears point events on same-size reset
- eventView.reset(): clear dirty counter so dirty() returns false after restoring reference
EnemyManager tests use a real TileStore with registered id<->num tiles; drop legacy bridge and *ById helpers (getPrefab/createEnemy take number|string); reusePrefab 2-arg; addPrefab dedupes by code only. Widen IEnemyManager.getPrefab signature to number|string (doc+impl). Flag tests align to loadState rebuilding field instances (d74cc35). Focused: 60/60 green.
- clone() now binds each cloned modifier to the clone, records its modifierName, mirrors the source save flag and recomputes the attribute, matching addModifier's bookkeeping
- a cloned attribute can enumerate, index and persist its modifiers, and a cloned modifier's setValue notifies the clone
- add the bookkeeping and save-ability regression cases
- compareEquip clones each compared item's modifier before adding it to the comparison clone and removes the same clone objects afterwards
- the live attribute's already-bound modifiers never enter the clone and are never unbound
- add the equipped-item diff regression case (both directions, no warning 108, live bookkeeping untouched)
- add private clearDynamics() that snapshots the managed dynamic tiles and drops each via the deleteDynamic semantics (syncStaticEvent + removeTile + onDeleteDynamic), without awaiting the hook
- call it as the first line of loadState so all three compression tiers and the missing-dynamicBlocks case start from a clean slate (#06-17-8)
- add regressions: mapLayer hook/instance witness and the three-tier repeated-load non-accumulation case
- measureCase sampling loop now pairs mark(startTag)/mark(endTag) with
measure(measureName, startTag, endTag) and clears marks/measures per sample
- tag names derive from caseName+scale so they never collide across cases
- WARMUP_RUNS/SAMPLE_RUNS/median/min/p95/toFixed(3)/columns unchanged
- HeroAttribute.loadState replaces its private base object; the HeroAttribute wrapper identity is preserved so equipment/combat references stay valid across load (#06-17-1/#06-17-2).
- Clone the incoming base attribute in the constructor so the shared HERO_DEFAULT_ATTRIBUTE is no longer mutated across CoreState instances.
- Use any for the hero position in combat handler/aura/enemy-view generics to clear the 45 new type errors introduced by HeroAttribute implementing ISaveableContent.
- IHeroStateSave.attribute becomes IHeroAttributeSave<THero>; top-level modifiers removed
- HeroState.attribute is readonly and loadState delegates to attribute.loadState (never reassigns)
- delete attachAttribute from IHeroState and HeroState, and its only test case
- thin-delegate registerModifier/createModifier/createAndInsertModifier to the attribute registry
- loadEquipEffect mounts equipment modifiers with save=false so equipment bonuses only return via re-equip
- add #06-17-1 regressions: same attribute instance, bonuses kept, no double-count when type is registered
- D-06: equipment.test.ts code-147 case stays it.skip with a Chinese note that 147 is a reserved code, currently unreachable by design; no production code touched
- WINDOWS.md ids 25/26 marked fixed (open_count 13 -> 11); no new ledger entries
- getCouldEquipSlot tests empty === -1 so the first empty named slot wins instead of always replacing the first match
- un-skip the named-slot empty-preference case; canEquipTo string branch untouched
- recalculateAttribute writes the base value into finalAttribute when no modifier is attached
- un-skip the base-only-change case; catchCalculateProgress is intentionally left untouched
- saveState returns new Map(this.equips) and [...this.slots] so the snapshot is decoupled from the live state
- un-skip the independent-snapshot and container round-trip cases