mirror of
https://github.com/motajs/template.git
synced 2026-09-24 10:00:17 +08:00
docs(07-12): fold IN-02 test-only contract alignment into plan 07-12
This commit is contained in:
parent
ba60ef99eb
commit
fe63494b12
@ -17,9 +17,9 @@ autonomous: false
|
||||
requirements:
|
||||
- FIX-01
|
||||
estimate:
|
||||
tokens: 58000
|
||||
raw_tokens: 58000
|
||||
tasks: 5
|
||||
tokens: 64000
|
||||
raw_tokens: 64000
|
||||
tasks: 6
|
||||
confidence: low
|
||||
must_haves:
|
||||
truths:
|
||||
@ -27,6 +27,7 @@ must_haves:
|
||||
- "`HeroAttribute.deleteModifierByIndex` 与 `deleteModifier` 的簿记一致:`bindAttribute(null)`、清理 `modifierName`/`modifierNosave`、`markDirty(name)`;被删修饰器可被再次 `addModifier`(不再因残留 `owner` 被码 108 拒绝)(WR-05 关闭)"
|
||||
- "`HeroAttribute.iterateModifiers()` / `getModifierIndex()` 在 `deleteModifierByIndex` 之后不再产出/解析已删除的修饰器;`finalAttribute` 不再停留在陈旧值"
|
||||
- "`HeroEquipment.loadState` 不再经 `equip()` 追加 `Equip` 录像指令:读档期间 `replaySystem.route.add` 不被调用(WR-06 关闭,按 Task 0 Q3 裁决口径)"
|
||||
- "空存档读档(`{ equipments: [] }`)把 `HeroEquipsStore` 的 uid 计数器重置为 0 且不产生码 58,且该重置可观测(随后的 `add(...)` 返回 0)——由测试锚定(IN-02 测试契约对齐 `ba60ef9`;本计划对 IN-02 **只改测试**,不改其生产逻辑)"
|
||||
- "`pnpm test:ci` 全绿,不新增 `it.skip`;`equipment.test.ts:306`(码 147,D-06)保持原样"
|
||||
artifacts:
|
||||
- path: packages-user/data-base/src/hero/equipStore.ts
|
||||
@ -38,7 +39,7 @@ must_haves:
|
||||
- path: packages-user/data-base/src/hero/equipment.ts
|
||||
provides: "`HeroEquipment.loadState` 的无录像副作用还原(按 Q3 裁决:disable/revert 包裹或内部无记录装备助手);按 Q4(ii) 裁决处理 `equipment.test.ts` 相邻缺陷"
|
||||
- path: packages-user/data-base/src/hero/saveLoad.test.ts
|
||||
provides: "WR-04 三档回归(活值往返或契约说明)、WR-06 读档无录像副作用回归、WR-05 相关容器级断言"
|
||||
provides: "WR-04 三档回归(活值往返或契约说明)、WR-06 读档无录像副作用回归、WR-05 相关容器级断言、IN-02 空存档重置 `nextUid` 断言(改写 `:447-457`,把当前 RED 用例按 `ba60ef9` 的新契约转绿)"
|
||||
- path: packages-user/data-base/src/hero/attribute.test.ts
|
||||
provides: "WR-05 回归:`deleteModifierByIndex` 后 iterate/getModifierIndex/final 值/可重新挂载"
|
||||
- path: packages-user/data-base/src/hero/equipment.test.ts
|
||||
@ -50,41 +51,45 @@ must_haves:
|
||||
- "`HeroEquipment.loadState`(`equipment.ts:337-347`)→ `equip()`(`:153-198`)→ `replay.route.add(ReplayCommandCode.Equip, [uid])`(`:195`)——读档经公开入口产生了真实录像副作用"
|
||||
- "`HeroEquipment.equip/unequip` 的 `replay.disable()`/`revert()`(`:169-170`/`:194`/`:211-212`)↔ `ReplaySystem.route`(`data-common/src/replay/system.ts:89-95`,`disable`/`revert` 直接转发到 `ReplayArray`)——Q3 选项 A 依赖该既有通道"
|
||||
- "`compareEquip` 的 `this.attribute.getModifierIndex(modifier)` + `clone.deleteModifierByIndex(name, index)`(`equipment.ts:281-284`)——索引取自原属性却作用于克隆属性(相邻缺陷,按 Q4(ii) 裁决处置)"
|
||||
- "`HeroEquipsStore.loadState`(`equipStore.ts:300-309`,生产修复 `ba60ef9`)↔ `HeroEquipsStore.add`(`:184-192`,`const uid = this.nextUid++`)——空存档 → `nextUid = 0` 的唯一可观测后果是「下一次 `add` 发号 0」,测试必须用 `add` 返回值锚定,而不是只断言「没有码 58」(IN-02)"
|
||||
assumptions:
|
||||
- "A1(D-10 例外,本计划授权):本计划为修复计划。Q1 若选「活修饰器值为存档源」,则 `saveLoad.test.ts:343-380` 中「读档后回到原始定义值」的既有断言必须改为「回到存档点的活值」——该改写是本计划唯一被授权的既有断言变更,必须在 SUMMARY 逐条说明;其余既有断言一字不改"
|
||||
- "A1(D-10 例外,本计划授权):本计划为修复计划。Q1 若选「活修饰器值为存档源」,则 `saveLoad.test.ts:343-380` 中「读档后回到原始定义值」的既有断言必须改为「回到存档点的活值」——该改写是本计划两处被授权的既有断言变更之一(另一处为 A5 的 IN-02 用例改写),必须在 SUMMARY 逐条说明;其余既有断言一字不改"
|
||||
- "A2(项目未发布,与 07-09 的 A2 同口径):`IEquipmentStateSave` 形状可变更,不保留旧形状兼容分支;本计划优先保持形状、只改语义(Q1 选项 A 可在现有 `value`/`percentage` 两个 Map 上表达活值与定义的差异)"
|
||||
- "A3(不新增修饰器 API):`BaseHeroModifier` 已有 `saveState`/`loadState`/`getValue`/`setValue`,本计划**不**修改 `modifier.ts`;若实现中发现必须改修饰器基类,暂停并回到 Task 0 关卡追加裁决,不得自行扩大改动面"
|
||||
- "A4(范围守卫):仅修 WR-04 / WR-05 / WR-06 三条;`IN-02`(空存档不重置 `nextUid`)与 `IN-04`/`IN-05` 不在本轮清单,只登记不修;`ReplayArray.setReplayArray` 的 `disabled = 0` 重置(`array.ts:807`)属 07-10 的文件,本计划不改"
|
||||
- "A4(范围守卫):代码修复面仅 WR-04 / WR-05 / WR-06 三条;`IN-04`/`IN-05` 不在本轮清单,只登记不修;`IN-02`(空存档不重置 `nextUid`)的生产代码已由用户在 `ba60ef9` 修复,本计划以**测试专用**方式纳入(Task 1),不改其生产逻辑;`ReplayArray.setReplayArray` 的 `disabled = 0` 重置(`array.ts:807`)属 07-10 的文件,本计划不改"
|
||||
- "A5(IN-02 测试专用,本计划授权;与 A1 并列的第二处授权断言变更):`saveLoad.test.ts:447-457`(`warns code 58 when the max equipment uid cannot be found`)在 `ba60ef9` 之后**当前为 RED**(`vitest run …/hero/saveLoad.test.ts` → 1 failed / 26 passed),因为空存档已不再发码 58 且会重置 `nextUid`。本计划把该用例改写为新契约(空存档 → `nextUid` 归零、随后的 `add` 返回 0、不发码 58),并在 SUMMARY 登记一条**契约/覆盖变化**:`logger.error(58)` 分支(`equipStore.ts:304-307`)现在只剩「非空但 `maxBy` 仍返回 `undefined`」的畸形数组防御路径可达,码 58 不再由空存档用例覆盖。除该用例与该变化外,其余既有断言一字不改"
|
||||
prohibitions:
|
||||
- "不改 `ReplayArray`/`ReplaySandbox`/`ReplaySystem`(`packages-user/data-common/src/replay/*`)——Q3 的修法只能在 `equipment.ts` 侧实现"
|
||||
- "不改 `modifier.ts` 的 `BaseHeroModifier`(A3);不改 `attribute.ts` 的 `addModifier`/`deleteModifier`(只动 `deleteModifierByIndex` 与其余按 Q2 裁决点名的部分)"
|
||||
- "不改 `HeroState`/`equipStore` 的存读档**实例身份**语义(07-11 已定);本计划不重新引入 `clear()` + 重建"
|
||||
- "不改 `data-state`(`core.ts` / saveables 顺序)、不改渲染端 `@user/client-*` 与 legacy 接线"
|
||||
- "不引入新依赖、不新建源码文件、不新增 `import type`、不使用连续 `as` 断言"
|
||||
- "不新增 `it.skip`;不为了转绿而弱化断言(A1 授权的两处除外)"
|
||||
- "不新增 `it.skip`;不为了转绿而弱化断言(A1、A5 授权的两处除外)"
|
||||
- "IN-02 属**测试专用**:不得为 IN-02 改动 `HeroEquipsStore.loadState` 的 `nextUid` 分支(`equipStore.ts:300-309`)——`ba60ef9` 是用户已完成的生产修复基线,本计划只把测试对齐它;WR-04 对 `equipStore.ts` 存档源的改动不受此禁令限制"
|
||||
---
|
||||
|
||||
<objective>
|
||||
闭合装备/属性子系统的三条存档正确性缺陷:装备数值加成的**存档源**(WR-04)、修饰器删除的**簿记**(WR-05)、读档期的**录像副作用**(WR-06)。
|
||||
闭合装备/属性子系统的三条存档正确性缺陷:装备数值加成的**存档源**(WR-04)、修饰器删除的**簿记**(WR-05)、读档期的**录像副作用**(WR-06);并附一条**测试专用**的契约对齐(IN-02:空存档读档的 uid 计数器契约,生产代码已由用户在 `ba60ef9` 修复,本计划只把测试改写对齐,不重改生产逻辑)。
|
||||
|
||||
现状(逐条锚点):
|
||||
1. **WR-04(warning,`equipStore.ts:34-36, 67-102, 259-288`)** `this.value`/`this.percentage` 只在构造期从 `item.equip` 拷贝、此后只被另一次读档覆盖;活修饰器值(`BaseHeroModifier.setValue`,`modifier.ts:25-28`)从不回流。后果:`saveDiff()`(`:80-102`)与 `item.equip` 比较恒相等 → 低/高压缩档「无差异」→ 装备状态**一个字节都不存**;`saveNoCompression()`(`:67-74`)存的也是 `this.value` 而非修饰器值 → 无压缩档同样丢弃运行时变更。`07-REVIEW.md` 指出 `saveLoad.test.ts:343-380` 恰好把这个错误行为写成了断言(改修饰器到 0.9/99,断言读回 0.5/5)。
|
||||
2. **WR-05(warning,`attribute.ts:230-239`)** `deleteModifierByIndex()` 只 `splice`:不 `bindAttribute(null)`、不清理 `modifierName`/`modifierNosave`、不 `markDirty`。后果:`iterateModifiers()`/`getModifierIndex()` 仍能看到已删除的修饰器;被删修饰器 `owner` 残留 → 再次 `addModifier` 被码 108 拒绝(永久不可用);`finalAttribute[name]` 陈旧。唯一调用点 `compareEquip`(`equipment.ts:281-284`)。
|
||||
3. **WR-06(warning,`equipment.ts:336-346`)** `loadState()` 通过公开 `equip(uid, index)`(`:168-196`,末尾 `:195` 有 `replay.route.add(ReplayCommandCode.Equip, [uid])`)恢复槽位,读档因此向活录像追加 `Equip` 指令。今日只被 `CoreState.loadState` 的 saveable 顺序掩盖(hero 在 replay 之前,随后被覆盖),英雄单独读档或存档缺 `@system/replay` 时污染录像。
|
||||
4. **IN-02(info,`equipStore.ts:282-287` 原文 → 现 `:300-309`)** 原缺陷:空存档时 `maxBy` 返回 `undefined`、发码 58 并提前 `return`,`nextUid` 停在陈旧值。**生产代码已由用户在 `ba60ef9` 修复**:`state.equipments.length === 0` → `this.nextUid = 0`;否则 `maxBy` 取 `maxUid.uid + 1`,取不到才 `logger.error(58)`。但 `saveLoad.test.ts:447-457` 仍按旧契约断言(空存档 → `toContain(58)`),因此该文件**当前为 RED**(`vitest run …/hero/saveLoad.test.ts` → 1 failed / 26 passed);且该用例必须先转绿,否则后续 Task 的 `saveLoad.test.ts` 级 `<verify>` 会被这条**既存失败**阻塞。本计划把该用例改写为新契约(空存档 → `nextUid` 归零、随后的 `add` 返回 0、不发码 58),并登记随之而来的契约/覆盖变化。
|
||||
|
||||
Purpose: 装备是属性加成的来源,装备存档一旦不落地,「读档后数值正确」就只在无压缩档的部分路径上成立;删除簿记不全则修饰器图与访问器视图分叉;读档写录像会让「读档」这个只读语义产生可回放的历史。三者都在存档边界上,属同一主题。
|
||||
Purpose: 装备是属性加成的来源,装备存档一旦不落地,「读档后数值正确」就只在无压缩档的部分路径上成立;删除簿记不全则修饰器图与访问器视图分叉;读档写录像会让「读档」这个只读语义产生可回放的历史。三者都在存档边界上,属同一主题。IN-02 则不同:生产行为已正确,缺的是**测试仍锚定旧契约**——不修则套件持续为红、也失去对 `ba60ef9` 的回归保护。
|
||||
|
||||
Output: `equipStore.ts`(存档源)+ `attribute.ts`(簿记)+ `equipment.ts`(读档无副作用)+ `types.ts`(契约 jsDoc)+ 三份测试文件的回归。
|
||||
Output: `equipStore.ts`(存档源)+ `attribute.ts`(簿记)+ `equipment.ts`(读档无副作用)+ `types.ts`(契约 jsDoc)+ 三份测试文件的回归 + IN-02 测试契约对齐(`saveLoad.test.ts:447-457` 改写,把当前 RED 转绿)。
|
||||
|
||||
**Artifacts this phase produces(本计划产出物)**:修改文件见 frontmatter `files_modified`;回归用例见 `must_haves.artifacts`;无新建源码文件、无新增依赖。
|
||||
**Artifacts this phase produces(本计划产出物)**:修改文件见 frontmatter `files_modified`;回归用例见 `must_haves.artifacts`;IN-02 为**测试专用**条目(不新增文件、不改其生产逻辑);无新建源码文件、无新增依赖。
|
||||
|
||||
**Phase 7 验证已失效**:`07-VERIFICATION.md`(2026-09-16T06:04:11Z)已因 07-09 与本轮 07-10..07-14 失效。本计划执行完毕后**必须重跑 `/gsd-verify-work`**(或 `gsd-verify-work 7`)重新出具验证结论。
|
||||
|
||||
Phase 7 裁决对应(逐条引用):D-01: 本计划是 Phase 7 第 12 个计划,承接 `07-REVIEW.md` 的 WR-04/WR-05/WR-06。D-02: 属 hero 系统(`packages-user/data-base/src/hero`),与 07-04(hero 存读档与属性/槽位)、07-09(属性同引用)、07-11(容器同引用)同系统、按严重度递减排序。D-03/D-04/D-05/D-06: 其它系统契约裁决,不涉及(`HeroEquipment` 的码 146/147 语义不变,147 保持 D-06 的保留码状态)。D-07: path 接线,不涉及。D-08: enemy,不涉及。D-09: 由 Task 0 关卡承载。D-10: 本计划为修复计划,回归用例为必需项(A1)。D-11: 存档源语义变更必须同步 `types.ts` 的 jsDoc。D-12: 沿用 06-CONTEXT D-44 文件级三步门禁。D-13: 按根因原子提交(Task 1 / 2 / 3)。D-14: 三条均无 `WINDOWS.md` 条目,只登记不新建。
|
||||
Phase 7 裁决对应(逐条引用):D-01: 本计划是 Phase 7 第 12 个计划,承接 `07-REVIEW.md` 的 WR-04/WR-05/WR-06。D-02: 属 hero 系统(`packages-user/data-base/src/hero`),与 07-04(hero 存读档与属性/槽位)、07-09(属性同引用)、07-11(容器同引用)同系统、按严重度递减排序。D-03/D-04/D-05/D-06: 其它系统契约裁决,不涉及(`HeroEquipment` 的码 146/147 语义不变,147 保持 D-06 的保留码状态)。D-07: path 接线,不涉及。D-08: enemy,不涉及。D-09: 由 Task 0 关卡承载。D-10: 本计划为修复计划,回归用例为必需项(A1;IN-02 测试对齐见 A5)。D-11: 存档源语义变更必须同步 `types.ts` 的 jsDoc。D-12: 沿用 06-CONTEXT D-44 文件级三步门禁。D-13: 按根因原子提交(Task 2 / 3 / 4 分别对应 WR-04 / WR-05 / WR-06;Task 1 的 IN-02 测试对齐单独一条 `test(07-12): …` 提交,不与其根因混提)。D-14: 三条均无 `WINDOWS.md` 条目,只登记不新建。
|
||||
|
||||
执行序:`depends_on: 07-11`(Wave 12)。除门禁型依赖(D-09 逐计划裁决关卡 + D-12 全量测试基线逐计划重录)外,还有**文件重叠的隐式依赖**:`hero/equipStore.ts`、`hero/types.ts`、`hero/saveLoad.test.ts` 与 07-11 的 `files_modified` 重叠,同波并行会互相覆盖,故必须排在 07-11 之后。
|
||||
|
||||
**基线(2026-09-16 实测)**:`pnpm test:ci` = 66 文件 / 690 通过 / 1 跳过(`equipment.test.ts:306` 码 147,D-06)。执行前重录当日基线并与本行对比。
|
||||
**基线(2026-09-16 实测)**:`pnpm test:ci` = 66 文件 / 690 通过 / 1 跳过(`equipment.test.ts:306` 码 147,D-06)。**自 `ba60ef9` 起**:`saveLoad.test.ts` 的 IN-02 用例转红(单文件 1 failed / 26 passed),故当日全量基线实为「689 通过 / 1 失败(IN-02,`saveLoad.test.ts:447-457`)/ 1 跳过」。执行前重录当日基线并与本行对比;**该 1 条失败是本计划 Task 1 的预期输入**(Task 1 把它转绿),而 Task 5 的全量门禁要求 0 失败、跳过项仍只有 `equipment.test.ts:306` 一条。
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@ -107,6 +112,9 @@ Phase 7 裁决对应(逐条引用):D-01: 本计划是 Phase 7 第 12 个
|
||||
@packages-user/data-base/src/hero/attribute.ts
|
||||
@packages-user/data-base/src/hero/equipment.ts
|
||||
@packages-user/data-base/src/hero/types.ts
|
||||
@packages-user/data-base/src/hero/saveLoad.test.ts
|
||||
|
||||
**执行起点提示(IN-02,测试专用)**:`packages-user/data-base/src/hero/saveLoad.test.ts` **当前为 RED**——`warns code 58 when the max equipment uid cannot be found`(`:447-457`)仍按旧契约断言「空存档 → 出现码 58」,而生产代码已在用户提交 `ba60ef9`(`equipStore.ts:300-309`:空存档 → `nextUid = 0`,不再发码 58)中修复,故 `pnpm exec vitest run …/hero/saveLoad.test.ts` = 1 failed / 26 passed。`ba60ef9` 是 Task 1 测试调整的**基线**:Task 1 改写该用例使其对齐新契约,从而把该文件转绿(并解除对 Task 2 / Task 4 的 `saveLoad.test.ts` 级 `<verify>` 的既存失败阻塞);本计划**不**再改动 IN-02 的生产逻辑。
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
@ -203,7 +211,7 @@ Phase 7 裁决对应(逐条引用):D-01: 本计划是 Phase 7 第 12 个
|
||||
<record>
|
||||
待用户在关卡处逐条填写(Q1..Q4:勾选选项 id 或给出裁决文字;Q4 若选 B/C 请写清具体哪一处修、哪一处登记)。
|
||||
**空的 `<record>` 表示尚未裁决**:执行者必须停在本任务,不得按「建议默认」推进。
|
||||
填写后 Task 1–3 一律按本记录执行;若裁决与 Task 1–3 的分支描述冲突,暂停并请用户澄清。
|
||||
填写后 Task 2–4 一律按本记录执行;若裁决与 Task 2–4 的分支描述冲突,暂停并请用户澄清。
|
||||
</record>
|
||||
<resume-signal>逐条给出 Q1..Q4 的裁决(例如「Q1=A, Q2=A, Q3=A, Q4=B」);若要修订方案,回复修订点,执行者退出并按 D-09 修订本 PLAN.md</resume-signal>
|
||||
<action>向用户汇报上述 4 个契约点与各自的直接后果(尤其 Q1 对 `saveLoad.test.ts:341-380` 断言的影响、Q3 对录像语义的影响)。不得在裁决前修改任何文件。</action>
|
||||
@ -220,7 +228,35 @@ Phase 7 裁决对应(逐条引用):D-01: 本计划是 Phase 7 第 12 个
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: WR-04 —— 装备存档源改为活修饰器值(按 Q1 裁决),三档压缩下运行时变更可往返或契约明写</name>
|
||||
<name>Task 1: IN-02(测试专用)—— 空存档读档重置 `nextUid` 为 0 且不发码 58:改写 `saveLoad.test.ts:447-457` 到 `ba60ef9` 的新契约,把当前 RED 转绿</name>
|
||||
<files>packages-user/data-base/src/hero/saveLoad.test.ts</files>
|
||||
<read_first>
|
||||
- packages-user/data-base/src/hero/equipStore.ts(:267-310 `HeroEquipsStore.loadState`,尤其 :300-309 的 `state.equipments.length === 0 → this.nextUid = 0`、`maxBy(state.equipments, 'uid')` 与 `logger.error(58)` 分支;:184-192 `add` 用 `const uid = this.nextUid++` 发号)
|
||||
- packages-user/data-base/src/hero/saveLoad.test.ts(:406-460 `HeroEquipsStore save and load round trips`::408-423 的夹具与「续接自增 uid」断言风格、:426-444 的码 59 用例(`logger.catch` 用法参考)、:447-457 **当前 RED** 的 `warns code 58 when the max equipment uid cannot be found`)
|
||||
- packages-user/data-base/src/hero/equipStore.test.ts(:140-200 `add` 返回值与 `delete` 用例的断言风格)
|
||||
- .planning/phases/07-data-fixes/07-REVIEW.md(IN-02 原文:`:247-251`;`Fix: set this.nextUid = 0 in the empty branch`)
|
||||
</read_first>
|
||||
<action>
|
||||
**背景与边界**:IN-02 的**生产修复已由用户在 `ba60ef9` 完成**(`equipStore.ts:300-309`:空存档 → `nextUid = 0`;否则 `maxUid.uid + 1`,取不到才 `logger.error(58)`)。本任务**只改测试**:不得为 IN-02 修改 `equipStore.ts` 的任何生产逻辑,也不得顺手改动 `nextUid` 分支的写法。本任务与 Task 0 的 Q1..Q4 裁决无关(生产行为已定),但按文档顺序在 Task 0 关卡之后执行。**先做本任务**:`saveLoad.test.ts` 现有 1 条 RED,若不先转绿,Task 2 / Task 4 对该文件的 `<verify>` 会被这条既存失败阻塞。
|
||||
(1) 改写 `saveLoad.test.ts:447-457` 的用例:夹具改为与 `:408-423` 同口径(先 `registerItem(env, createEquipItem(10, 'sword', [0], [['atk', 5]]))`,让 `add(10)` 可发号),用例名改为反映新契约(如 `resets the uid counter when the save has no equipment`);
|
||||
(2) 用 `logger.catch(() => env.store.loadState({ equipments: [] }, SaveCompression.NoCompression))` 读入空存档后,断言两件事:(a) 计数器重置**可观测**——`expect(env.store.add(10)).toBe(0)`(唯一可观测后果,见 frontmatter `must_haves.key_links`);(b) 该路径**不产生码 58**——`expect(result.info.map(info => info.code)).not.toContain(58)`。只断言 (b) 不足以证明重置发生,两条都必须有;
|
||||
(3) 在该用例内用一行注释登记**契约/覆盖变化**:`logger.error(58)` 现在只剩「非空数组但 `maxBy` 仍返回 `undefined`(畸形/含无效条目)」的防御路径可达,不再是空存档的行为;注释须写明该分支属 `equipStore.ts` 的用户实现、保持原样(防止后续被误当作死代码删除),并在 Task 5 的 SUMMARY 登记段落复述同一结论;
|
||||
(4) 不改动同文件其它用例(尤其 `:408-423` 的 uid 连续性用例、`:426-444` 的码 59 用例);不新增跳过用例;不弱化断言——本用例是新契约的等值/真值断言,不是放宽。
|
||||
</action>
|
||||
<verify>
|
||||
<automated>pnpm exec vitest run packages-user/data-base/src/hero/saveLoad.test.ts</automated>
|
||||
<fails_when>该文件仍有失败用例(含 `:447-457` 改写后的用例);或改写后的用例仍断言出现码 58;或空存档读档后 `env.store.add(10)` 未返回 0(重置不可观测/未发生);或为让该用例通过而改动了 `equipStore.ts` 的生产逻辑 —— 按 D-09 退出并修订计划</fails_when>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- `pnpm exec vitest run packages-user/data-base/src/hero/saveLoad.test.ts` 全绿:改写后的用例断言「空存档 → 随后 `add(10)` 返回 0」且「本次读档不产生码 58」,此前的 RED 已消除
|
||||
- 用例内注释登记了码 58 分支的剩余可达条件(非空畸形数组)与「该分支保持原样」的结论
|
||||
- `equipStore.ts` 的 IN-02 生产逻辑零改动(`ba60ef9` 为基线);未新增跳过用例;同文件其它用例逐字未动
|
||||
</acceptance_criteria>
|
||||
<done>IN-02 的测试契约与 `ba60ef9` 的生产行为一致,`saveLoad.test.ts` 全绿(本计划只改测试);码 58 的覆盖变化已在使用例内注释登记,待 Task 5 汇总进 SUMMARY</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: WR-04 —— 装备存档源改为活修饰器值(按 Q1 裁决),三档压缩下运行时变更可往返或契约明写</name>
|
||||
<files>packages-user/data-base/src/hero/equipStore.ts, packages-user/data-base/src/hero/types.ts, packages-user/data-base/src/hero/saveLoad.test.ts</files>
|
||||
<read_first>
|
||||
- packages-user/data-base/src/hero/equipStore.ts(:15-165 `EquipmentState` 全量:`value`/`percentage` 私有表、`rebuildModifiers`、三档 save/load)
|
||||
@ -254,7 +290,7 @@ Phase 7 裁决对应(逐条引用):D-01: 本计划是 Phase 7 第 12 个
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: WR-05 —— `HeroAttribute.deleteModifierByIndex` 簿记与 `deleteModifier` 对齐(含迭代器/索引/final/可重新挂载回归),按 Q4(i) 裁决处理 `compareEquip`</name>
|
||||
<name>Task 3: WR-05 —— `HeroAttribute.deleteModifierByIndex` 簿记与 `deleteModifier` 对齐(含迭代器/索引/final/可重新挂载回归),按 Q4(i) 裁决处理 `compareEquip`</name>
|
||||
<files>packages-user/data-base/src/hero/attribute.ts, packages-user/data-base/src/hero/attribute.test.ts, packages-user/data-base/src/hero/equipment.ts, packages-user/data-base/src/hero/equipment.test.ts</files>
|
||||
<read_first>
|
||||
- packages-user/data-base/src/hero/attribute.ts(:169-188 `iterateModifiers`/`getModifiers`/`getModifierIndex`、:190-211 `addModifier`(:195-199 码 108 拒绝)、:213-228 `deleteModifier`、:230-239 `deleteModifierByIndex`、:241-249 `markDirty`/`markModifierDirty`)
|
||||
@ -285,7 +321,7 @@ Phase 7 裁决对应(逐条引用):D-01: 本计划是 Phase 7 第 12 个
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: WR-06 —— `HeroEquipment.loadState` 读档不再追加录像指令(按 Q3 裁决)</name>
|
||||
<name>Task 4: WR-06 —— `HeroEquipment.loadState` 读档不再追加录像指令(按 Q3 裁决)</name>
|
||||
<files>packages-user/data-base/src/hero/equipment.ts, packages-user/data-base/src/hero/equipment.test.ts, packages-user/data-base/src/hero/saveLoad.test.ts</files>
|
||||
<read_first>
|
||||
- packages-user/data-base/src/hero/equipment.ts(:153-198 `equip`(:168-170 注释与 disable、:194 revert、:195 route.add)、:200-218 `unequip`(:210-212 route.add)、:330-347 `saveState`/`loadState`)
|
||||
@ -316,18 +352,19 @@ Phase 7 裁决对应(逐条引用):D-01: 本计划是 Phase 7 第 12 个
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 4: D-12/D-44 门禁 + 全量套件 + 缺口登记(WR-04/05/06 + Q4 处置 + IN-02/IN-04/IN-05)与 Phase 7 验证失效说明</name>
|
||||
<name>Task 5: D-12/D-44 门禁 + 全量套件 + 缺口登记(WR-04/05/06 + Q4 处置 + IN-02 已闭合(测试专用) + IN-04/IN-05 保留在册)与 Phase 7 验证失效说明</name>
|
||||
<files>.planning/phases/07-data-fixes/07-12-SUMMARY.md</files>
|
||||
<read_first>
|
||||
- .planning/phases/07-data-fixes/07-REVIEW.md(WR-04/WR-05/WR-06 与 IN-02/IN-04/IN-05 的编号,便于逐条对照闭合/登记)
|
||||
- .planning/phases/07-data-fixes/07-REVIEW.md(WR-04/WR-05/WR-06 与 IN-02/IN-04/IN-05 的编号,便于逐条对照闭合/登记;IN-02 原文在 `:247-251`)
|
||||
- packages-user/data-base/src/hero/saveLoad.test.ts(Task 1 改写后的 IN-02 用例与其中的契约注释 —— SUMMARY 的覆盖变化登记须与之一致)
|
||||
- .planning/phases/07-data-fixes/07-VERIFICATION.md(头部 verified/status)
|
||||
- .planning/ROADMAP.md(Phase 7 段 Plans 与 Wave 列表)
|
||||
- .planning/WINDOWS.md(确认三条无入账 id)
|
||||
</read_first>
|
||||
<action>
|
||||
(1) **门禁(D-12 / D-44 文件级)**:对 `files_modified` 中每个改动的文件执行 `pnpm exec eslint --fix <文件>` 后 `pnpm exec eslint <文件>` 0 错误;**文件级类型门禁按 06-RESEARCH Pitfall 4 的判定方式**:`pnpm exec vue-tsc --noEmit 2>&1 | Select-String -Pattern "<改动文件相对路径>"` 无命中(**不得**用「退出码非 0」判定失败);`pnpm test:ci` 全绿(对比基线 66 文件 / 690 通过 / 1 跳过,跳过项仍只能是 `equipment.test.ts:306` 码 147)。
|
||||
(2) **原子提交(D-13)**:Task 1 / 2 / 3 各自独立提交(`fix(07-12): …`,携带 `WR-04` / `WR-05` / `WR-06` 与必要的 `#06-09-1` 锚点)。
|
||||
(3) **缺口登记**:SUMMARY 的 findings/ledger 段落逐条标注 WR-04/WR-05/WR-06 已闭合(附 commit)、Q4 两处相邻缺陷的处置(已修/已登记)、`IN-02`/`IN-04`/`IN-05` 保留在册;若 A1 授权的断言翻转发生,在此逐条列出「旧断言 → 新断言 → 理由」。`WINDOWS.md` 不新建、不 `fixed`/`waive`。
|
||||
(1) **门禁(D-12 / D-44 文件级)**:对 `files_modified` 中每个改动的文件执行 `pnpm exec eslint --fix <文件>` 后 `pnpm exec eslint <文件>` 0 错误;**文件级类型门禁按 06-RESEARCH Pitfall 4 的判定方式**:`pnpm exec vue-tsc --noEmit 2>&1 | Select-String -Pattern "<改动文件相对路径>"` 无命中(**不得**用「退出码非 0」判定失败);`pnpm test:ci` 全绿(对比基线 66 文件 / 690 通过 / 1 跳过,跳过项仍只能是 `equipment.test.ts:306` 码 147;**执行起点含 `ba60ef9` 引入的 1 条 IN-02 失败,须由 Task 1 消除**——门禁要求 0 失败)。
|
||||
(2) **原子提交(D-13)**:Task 1..4 各自独立提交——Task 1 为 `test(07-12): …`(IN-02 测试契约对齐 `ba60ef9`),Task 2 / 3 / 4 为 `fix(07-12): …`(分别携带 `WR-04` / `WR-05` / `WR-06` 与必要的 `#06-09-1` 锚点);IN-02 的测试调整不得与其根因混入同一条提交。
|
||||
(3) **缺口登记**:SUMMARY 的 findings/ledger 段落逐条标注 WR-04/WR-05/WR-06 已闭合(附 commit)、Q4 两处相邻缺陷的处置(已修/已登记)、**IN-02 已闭合(测试专用:测试契约对齐 `ba60ef9`,附 commit;生产逻辑零改动)并附其契约/覆盖变化——`logger.error(58)` 只剩「非空但 `maxBy` 返回 `undefined`」的畸形数组防御路径可达,码 58 不再由空存档用例覆盖**、`IN-04`/`IN-05` 保留在册;若 A1 授权的断言翻转或 A5 授权的 IN-02 用例改写发生,在此逐条列出「旧断言 → 新断言 → 理由」。`WINDOWS.md` 不新建、不 `fixed`/`waive`。
|
||||
(4) **Phase 7 验证失效**:SUMMARY 明写 `07-VERIFICATION.md` 已因 07-09 + 07-10..07-14 失效,结束后必须重跑 `/gsd-verify-work`(或 `gsd-verify-work 7`);`ROADMAP.md` 的 Phase 7 计划数与 Wave 列表同步。
|
||||
</action>
|
||||
<verify>
|
||||
@ -336,8 +373,8 @@ Phase 7 裁决对应(逐条引用):D-01: 本计划是 Phase 7 第 12 个
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- 改动文件 `eslint` 0 错误、`vue-tsc --noEmit` 该文件 0 类型错误、`pnpm test:ci` 全绿且跳过项仍仅 1 条
|
||||
- Task 1–3 各有独立提交并含对应锚点
|
||||
- SUMMARY 完成缺口登记(WR-04/05/06、Q4 处置、IN-02/IN-04/IN-05、A1 断言翻转清单)与 Phase 7 验证失效说明
|
||||
- Task 1–4 各有独立提交并含对应锚点(Task 1 = IN-02 `test(07-12)`;Task 2–4 = WR-04/05/06 `fix(07-12)`)
|
||||
- SUMMARY 完成缺口登记(WR-04/05/06、Q4 处置、IN-02 已闭合 + 码 58 覆盖变化、IN-04/IN-05 保留在册、A1 断言翻转 / A5 IN-02 用例改写清单)与 Phase 7 验证失效说明
|
||||
- `WINDOWS.md` 未被本计划改动
|
||||
</acceptance_criteria>
|
||||
<done>D-44 三步门禁全绿、原子提交完成、缺口与断言变更逐条登记、Phase 7 验证失效说明写入 SUMMARY</done>
|
||||
@ -353,35 +390,39 @@ Phase 7 裁决对应(逐条引用):D-01: 本计划是 Phase 7 第 12 个
|
||||
| 存档数据 → 装备/属性状态 | 读档决定修饰器的值与成员集合;错误的事实源会让「读档后数值」静默偏离 |
|
||||
| 公开 API 调用方 → 修饰器图 | `deleteModifierByIndex` / `getModifiers()` / `compareEquip` 允许外部改动或误删修饰器,簿记不全即产生与访问器视图分叉的状态 |
|
||||
| 读档 → 录像路由 | `loadState` 经 `equip()` 触发录像记录,把只读语义变成可回放历史 |
|
||||
| 存档快照 → uid 计数器 | 空存档读档决定 `HeroEquipsStore.nextUid` 的复位;测试若锚定旧契约,即失去对已修行为的回归保护(且套件持续为红) |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|
||||
|-----------|----------|-----------|----------|-------------|-----------------|
|
||||
| T-7-31 | Tampering | `EquipmentState.saveDiff` / `saveNoCompression`(`equipStore.ts:67-102`) | medium | mitigate | Task 1:按 Q1 裁决以活修饰器值为存档源(或明写契约),消除「压缩档零持久化」与「运行时变更丢失」 |
|
||||
| T-7-32 | Tampering | `HeroAttribute.deleteModifierByIndex`(`attribute.ts:230-239`) | medium | mitigate | Task 2:与 `deleteModifier` 同构的簿记(解绑 + 清理两表 + `markDirty`),并有四条后果的回归断言 |
|
||||
| T-7-33 | Tampering | `compareEquip` 的索引错配(`equipment.ts:281-284`) | medium | mitigate | Task 2 按 Q4(i) 裁决:在克隆属性上定位修饰器(修)或不修并登记为已知缺陷 |
|
||||
| T-7-34 | Repudiation | `HeroEquipment.loadState` 追加 `Equip` 指令(`equipment.ts:195` 经 `:345`) | medium | mitigate | Task 3:按 Q3 裁决禁用记录或无记录助手;断言 `route.add` 在读档期未被调用,且正常 `equip`/`unequip` 仍记录 |
|
||||
| T-7-31 | Tampering | `EquipmentState.saveDiff` / `saveNoCompression`(`equipStore.ts:67-102`) | medium | mitigate | Task 2:按 Q1 裁决以活修饰器值为存档源(或明写契约),消除「压缩档零持久化」与「运行时变更丢失」 |
|
||||
| T-7-32 | Tampering | `HeroAttribute.deleteModifierByIndex`(`attribute.ts:230-239`) | medium | mitigate | Task 3:与 `deleteModifier` 同构的簿记(解绑 + 清理两表 + `markDirty`),并有四条后果的回归断言 |
|
||||
| T-7-33 | Tampering | `compareEquip` 的索引错配(`equipment.ts:281-284`) | medium | mitigate | Task 3 按 Q4(i) 裁决:在克隆属性上定位修饰器(修)或不修并登记为已知缺陷 |
|
||||
| T-7-34 | Repudiation | `HeroEquipment.loadState` 追加 `Equip` 指令(`equipment.ts:195` 经 `:345`) | medium | mitigate | Task 4:按 Q3 裁决禁用记录或无记录助手;断言 `route.add` 在读档期未被调用,且正常 `equip`/`unequip` 仍记录 |
|
||||
| T-7-35 | Tampering | `EquipmentState.getModifiers()` 返回内部数组(`equipStore.ts:57-61`) | low | mitigate | 按 Q4(ii) 裁决:纳入则返回副本/只读视图,否则登记在册 |
|
||||
| T-7-36 | Denial of Service | `replay.disable()`/`revert()` 配对失衡(Q3 选项 A) | low | mitigate | Task 3:用 `try/finally` 或与 `equip()` 一致的配对路径保证 `revert` 必达;`<fails_when>` 拦截「正常记录被永久关闭」 |
|
||||
| T-7-36 | Denial of Service | `replay.disable()`/`revert()` 配对失衡(Q3 选项 A) | low | mitigate | Task 4:用 `try/finally` 或与 `equip()` 一致的配对路径保证 `revert` 必达;`<fails_when>` 拦截「正常记录被永久关闭」 |
|
||||
| T-7-37 | Repudiation | `HeroEquipsStore.loadState` 空存档的 uid 计数器契约(`equipStore.ts:300-309`,生产修复 `ba60ef9`) | low | mitigate | Task 1:测试锚定「空存档 → 随后 `add` 发号 0」且该路径不产生码 58;并在用例注释与 SUMMARY 登记「码 58 只剩非空畸形数组可达」的覆盖/契约变化(测试专用,生产逻辑零改动) |
|
||||
| T-7-SC | Tampering | npm/pnpm 依赖安装 | high | mitigate | 本计划不新增依赖、不改 `package.json`;出现安装需求即暂停并要求用户确认包合法性 |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
1. `pnpm exec vitest run packages-user/data-base/src/hero/saveLoad.test.ts packages-user/data-base/src/hero/attribute.test.ts packages-user/data-base/src/hero/equipment.test.ts packages-user/data-base/src/hero/equipStore.test.ts` 全绿。
|
||||
2. `pnpm test:ci` 全绿:66 文件 / 690 通过 / 1 跳过(仅 `equipment.test.ts:306` 码 147)。
|
||||
1. `pnpm exec vitest run packages-user/data-base/src/hero/saveLoad.test.ts packages-user/data-base/src/hero/attribute.test.ts packages-user/data-base/src/hero/equipment.test.ts packages-user/data-base/src/hero/equipStore.test.ts` 全绿(`saveLoad.test.ts` 的 IN-02 用例已按 `ba60ef9` 的新契约改写,不再是 1 failed / 26 passed)。
|
||||
2. `pnpm test:ci` 全绿:66 文件 / 690 通过 / 1 跳过(仅 `equipment.test.ts:306` 码 147);执行起点的 IN-02 失败已由 Task 1 消除。
|
||||
3. `pnpm exec eslint <files_modified 中每个改动文件>` 0 错误;`pnpm exec vue-tsc --noEmit` 输出不含这些文件的类型错误。
|
||||
4. Task 0 的 `<record>` 中 Q1..Q4 均有裁决,代码与裁决逐条一致(SUMMARY 对照表);A1 授权的断言翻转已逐条列出。
|
||||
5. 无新增依赖、无新建源码文件、`packages-user/data-common` 下零改动、`WINDOWS.md` 未改动。
|
||||
6. Phase 7 验证失效与重跑要求已在 `07-12-SUMMARY.md` 明写,`ROADMAP.md` 已更新。
|
||||
4. Task 0 的 `<record>` 中 Q1..Q4 均有裁决,代码(Task 2 / 3 / 4)与裁决逐条一致(SUMMARY 对照表);A1 授权的断言翻转与 A5 授权的 IN-02 用例改写均已逐条列出「旧断言 → 新断言 → 理由」。
|
||||
5. IN-02 为测试专用:`equipStore.ts` 的 `nextUid` 分支(`:300-309`)零改动(与 `ba60ef9` 逐字一致);改写后的用例断言「`saved: { equipments: [] }` → 随后 `add(10)` 返回 0」且该路径不产生码 58;码 58 的剩余可达条件(非空但 `maxBy` 返回 `undefined`)已在用例注释与 SUMMARY 登记。
|
||||
6. 无新增依赖、无新建源码文件、`packages-user/data-common` 下零改动、`WINDOWS.md` 未改动。
|
||||
7. Phase 7 验证失效与重跑要求已在 `07-12-SUMMARY.md` 明写,`ROADMAP.md` 已更新。
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- IN-02(测试专用)闭合:`saveLoad.test.ts:447-457` 改写为「空存档 → `nextUid` 归零、`add` 发号 0、不产生码 58」,该文件由 RED(1 failed / 26 passed)转绿;`equipStore.ts` 的 IN-02 生产逻辑零改动(`ba60ef9` 为基线);码 58 的契约/覆盖变化已登记。
|
||||
- WR-04 按 Q1 裁决闭合:装备存档的事实源与裁决一致,三档压缩下有对应断言(或契约明写并有测试锚定)。
|
||||
- WR-05 闭合:`deleteModifierByIndex` 后迭代器/索引/final/重新挂载四条后果全部自洽。
|
||||
- WR-06 按 Q3 裁决闭合:读档不产生录像命令,正常装备/卸下记录能力不变。
|
||||
- Q4 两处相邻缺陷按裁决修或登记,并在 SUMMARY 逐条记录。
|
||||
- `pnpm test:ci` 全绿、无新增 `it.skip`;D-44 三步门禁全绿;A1 授权的断言变更已逐条登记。
|
||||
- `pnpm test:ci` 全绿、无新增跳过用例;D-44 三步门禁全绿;A1 / A5 授权的断言变更已逐条登记。
|
||||
- Phase 7 验证失效与重跑要求已记录。
|
||||
</success_criteria>
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user