- review mapping: CR-01 to Q1, WR-01 to Q2, WR-02 to Q3, WR-03 to Q4, each with its fix commit and closure state
- gate results, per-defect reverse-verification evidence and the re-recorded test:ci baseline
- escalations: the user-owned equipment.test.ts replay-stub drift blocks the CR-01 witness; WINDOWS.md intentionally untouched; ROADMAP.md/STATE.md left to the orchestrator
- 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
- both multiplier checks tighten from < 1 to <= 1, matching warning 149's own text (must be greater than 1)
- an exact multiplier of 1 now warns 149 and falls back to the existing factor 2, so buffer expansion always terminates
- add the multiplier-of-one regression case (two code 149 warnings, 15 appended steps all read back in order)
- normalizeParam returns null for an unencodable param value, matching its own jsDoc and the documented discard semantics of normalizeParamList
- the command param count, the bytes actually written and the param cursor stay consistent so later commands keep their correct byte offsets
- add the discard-alignment regression case (count 0, later command bytes intact, read stream aligned)
- 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)
- Q1..Q4 all 不改动: legacy data-fallback compatibility layer is untouched
- Verbatim user answer recorded in 07-14-PLAN.md Task 0 <record>
- Disposition: #06-17-7 / audit item C = wontfix (经用户裁定不修复)
- Legitimate Phase 7 criterion-1 disposition: 修复或经用户裁定不修复
- Consequence: zero code changes, zero tests, zero new dependencies
- 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
- deleteEnemy removes the deleted view from point.affectedBy and drops its point.damages entry so lazy refreshIndex never revisits a dead view (IN-01)
- deleteMapDamage removes the sourcelessDamage IPointInfo once both damages and affectedBy are empty, keeping markDirtyIndex
- add IN-01 regressions: dead-view witness (getDamageWithoutCheck not called) and the two arms of the sourceless empty-point cleanup
removeEnemyAffecting collects removed coordinate indexes and markDirtyIndex each; refreshEnemyAndClearCache/refreshEnemy register enemyStore.set(view, set) before the empty-set return so later markEnemyDirty uses local refresh instead of refreshAll(). Adds a Chinese comment before each new it.