From 5edaea1f2108fcf63a58f425dec3fdd5f3578286 Mon Sep 17 00:00:00 2001 From: unanmed <1319491857@qq.com> Date: Tue, 15 Sep 2026 11:02:50 +0800 Subject: [PATCH] docs(06): add gap-fill plan 06-14 (G-06-04-C replay read-stream) --- .planning/ROADMAP.md | 6 +- .planning/phases/06-unit-tests/06-14-PLAN.md | 247 +++++++++++++++++++ 2 files changed, 252 insertions(+), 1 deletion(-) create mode 100644 .planning/phases/06-unit-tests/06-14-PLAN.md diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 84640cd..3844cc0 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -210,7 +210,7 @@ Plans: 3. 测试在本地可运行且全部通过 4. 测试由 AI 编写并运行,通过验证后可提交 -**Plans**: 9/9 原计划 executed replanned (D-28;旧 06-01/06-02 执行结果标记 superseded,按同号重跑;数据端切片,非数据 render/legacy 覆盖延后) + gap-fill 06-10..06-13 (D-46;人工评审缺口补测,只补测试不改生产代码;10/13 executed) +**Plans**: 9/9 原计划 executed replanned (D-28;旧 06-01/06-02 执行结果标记 superseded,按同号重跑;数据端切片,非数据 render/legacy 覆盖延后) + gap-fill 06-10..06-14 (D-46;人工评审缺口补测,只补测试不改生产代码;10/14 executed) Plans: @@ -246,6 +246,10 @@ Plans: - [x] 06-12-PLAN.md — replay/enemy gaps: G-06-04-B (runnable) + G-06-04-A/G-06-03-A (correct-expectation skip) - [ ] 06-13-PLAN.md — save/load gaps: G-06-09-A/B (only plan testing saveState/loadState, D-32) +**Wave 4** *(G-06-04-C follow-up gap-fill, D-46; serialized after 06-12 because it shares `array.test.ts`)* + +- [ ] 06-14-PLAN.md — replay read-stream gaps: G-06-04-C/A (stream-only complex route + middle start index) + G-06-04-C/B (per-param typed assertions, index progression, expired-after-mutation); blocked `#06-04-3`/`#06-04-4` as correct-expectation `it.skip` + ## Progress **Execution Order:** diff --git a/.planning/phases/06-unit-tests/06-14-PLAN.md b/.planning/phases/06-unit-tests/06-14-PLAN.md new file mode 100644 index 0000000..8f427c5 --- /dev/null +++ b/.planning/phases/06-unit-tests/06-14-PLAN.md @@ -0,0 +1,247 @@ +--- +phase: 06-unit-tests +plan: 14 +type: execute +wave: 4 +depends_on: [06-04, 06-12] +files_modified: + - packages-user/data-common/src/replay/array.test.ts + - .planning/phases/06-unit-tests/06-COVERAGE-MAP.md +autonomous: true +requirements: [TEST-01] +estimate: + tokens: 50000 + raw_tokens: 50000 + tasks: 3 + confidence: low +must_haves: + truths: + - "TEST-01 由数据端行为单测满足,门禁为固定非 watch 命令 `pnpm test:ci` 全绿(D-08)。" + - "G-06-04-C / A(可跑绿):新增一条**仅经 `createReadStream`**(用例体内不调用 `array.get`)的复杂序列验证——5+ 条命令、参数个数(1/2/0/1/4/2/3)与类型各不相同,混合 boolean / int8 边界值 / int32 / float / 单字节 bigint / 短 string(内联 token)/ 长 string(type 7)/ 空 string;逐条 `stream.read()` 断言 `command` 与**每个参数的 JS 类型(`typeof`)及值严格一致**(bigint 保持 bigint、boolean 保持 boolean、string 保持 string、number 保持 number),读流 `index` 逐次递进 `1..N`(`index = position + 1`),末尾 `read()` 返回 `null`;并覆盖同一复杂序列的**中间起始索引**读取(`createReadStream(3)` → 索引 4..7)。" + - "G-06-04-C / B(可跑绿):强化既有流断言——新增文件内 helper `expectParamTyped`/`expectStepTyped`,使被 3 条既有用例复用的 `expectHeterogeneousRead` 逐参数断言类型+值、逐条断言读流 `index` 递进(`i + 1`)、循环后断言 `stream.index === expectedSteps.length` 且 `read()` 为 `null`;`reads a sequence of steps through a read stream`、`expands buffers and still reads every step back` 同样补每参数 `typeof` 与 `stream.index` 递进;并新增一条用例断言**复杂序列经 `add` 变更后既有读流 `expired === true`**(读过期流触发告警码 155),随后新建的读流按新次序逐条类型化读回一致。" + - "索引语义区分(务必成立,否则断言会误失败):`createReadStream(startIndex).read()` 返回的 `index === position + 1`(起始 `stream.index === startIndex`),而 `get(i).index === i`;因此 `read()` 与 `get(i)` 之间只按 `command`/`params` 对应,不对整体对象做相等。" + - "过期语义:`add`/`insert`/`delete`/`set`/`setCommandWidth` 任一调用后,此前创建的读流 `expired === true`;对复杂(异质)序列同样成立。" + - "受阻塞缺口以**正确预期 + `it.skip`** 登记(D-05/D-46):`#06-04-3`(delete 中间步)、`#06-04-4`(insert 后新次序)本批次新增异质序列流读次序的正确预期 `it.skip`;既有 `#06-04-1`(int64 超 int32)、`#06-04-2`(多字节 bigint)的 2 条 skip 与既有 `#06-04-3`/`#06-04-4` 共 5 条 skip 全部**保持不变**,不得取消、不得改写为可通过断言。" + - "受阻塞范围不得被『绕过』:A/B 的复杂序列一律使用当前可正确编解码的参数(bigint 限单字节且落在 0..127 的低位区间、整数限 int32 范围、无多字节 bigint、无超 int32 的 int64),否则会把受阻塞缺口伪装成失败用例。" + - "接口覆盖:只经既有公开接口驱动(`ReplayArray` 的 add/insert/delete/set/get/createReadStream/rebuildIndexArray/getCommandArray/getParamArray/setCommandWidth/length),名称含 legacy 的接口/方法排除(D-30)。" + - "code 覆盖(模块可达):本计划**不引入新码**;复用并再确认既有可达码 148/149/150/151/152/153/154/155 的覆盖不被削弱(D-31),其中 155 为「变更后读过期流」的告警断言。" + - "测试形态(D-01):纯数据模块单测(显式依赖注入、无 mock 数据层接口);fixture 与 helper 文件内联,不建立跨文件共享 fixture/factory(D-02/D-03);不使用 fake timers、不使用真实计时器(本计划全同步)(D-04)。" + - "阶段化(D-43):计划按 构件→组合→完整 三阶段组织,取消 tracer-first;每阶段跑绿后才进入下一阶段;某阶段发现阻断性 bug 时暂停并按 D-05/D-07 向用户汇报、等确认,不得跳过或弱化。" + - "质量门禁(D-44):每个计划执行后、提交前,改动文件 `eslint --fix` 后 `eslint <改动文件>` 0 错误;本计划改动的测试文件在 `pnpm exec vue-tsc --noEmit` 输出中按文件路径过滤 0 类型错误(全局既有无关错误不在范围);`pnpm test:ci` 全绿;任一不通过不得提交。" + - "执行节奏(D-33):本计划执行前先向用户确认;执行完成后暂停,用中文分条简要汇报验证结果,详细结果写入共用的 `06-TEST-FINDINGS.md`。" + - "增量补测(D-46):只新增/扩展 `*.test.ts` 与规划产物,不改动已执行的 06-01..06-13 计划与任何生产/核心源码;不测 `saveState`/`loadState`(D-32);不安装覆盖率工具(D-09)。" + artifacts: + - path: packages-user/data-common/src/replay/array.test.ts + provides: G-06-04-C/A 仅经读取流的复杂序列类型化验证(含中间起始索引)与 G-06-04-C/B 既有流断言强化(每参数类型、index 递进、变更后 expired)+ 2 条增删次序的正确预期 skip + - path: .planning/phases/06-unit-tests/06-COVERAGE-MAP.md + provides: 阶段级 code → 模块 → 用例 映射表(本计划 create-or-append 06-14 小节) + key_links: + - "复用文件内既有 `createArray(overrides)`、`heterogeneousSteps`、`createHeterogeneousArray()`、`expectHeterogeneousRead()`、`firstParamToken()` 与 `ReplayCommandWidth`/`ReplayParamValue` 导入;新增 helper `expectParamTyped`/`expectStepTyped` 仅在本文件内定义与使用(D-03)" + - "读取流语义来自 `array.ts`:`createReadStream(startIndex)` 初始 `index = startIndex`、`read()` 每次先判 `index >= length` 返回 `null`、命中后 `index++` 并以自增后的值作为返回对象的 `index`(即 `position + 1`);`expireStreams()` 在 add/insert/delete/set/setCommandWidth 内被调用并置 `expired = true`" + - "既有 `it.skip`(`deletes a middle step and shifts later param indexes` 锚定 `#06-04-3`、`reads the new order after inserting a step` 锚定 `#06-04-4`、`round-trips a multi-byte bigint` 锚定 `#06-04-2`、`round-trips int64 values above the int32 range` 锚定 `#06-04-1`、`round-trips a heterogeneous step mixing a multi-byte bigint and an int64 value` 锚定 `#06-04-1`/`#06-04-2`)保持不变;本计划新增 2 条异质序列增删 skip,复用同一组锚点,不新建 finding 条目" + - "`06-COVERAGE-MAP.md` 为共享 create-or-append 文件(幂等、顺序无关):写 06-14 小节前先检查表头(`| code | 模块 | 用例 | 计划 |`)是否存在,不存在则先创建,再只追加/更新本小节,绝不重写他节" + prohibitions: + - "不创建或修改任何生产/核心源码,只新增/扩展 `*.test.ts` 与规划产物" + - "不改动 06-01..06-13 的 PLAN.md(含待执行的 06-13)" + - "不取消、不改写任何既有 `it.skip`;受阻塞缺口不得弱化为通过(D-05/D-46)" + - "不测试 `saveState`/`loadState`(集中到 06-13)(D-32)" + - "不在 A 用例体内调用 `array.get(...)`(A 必须仅经读取流验证)" + - "不使用受 `#06-04-1`/`#06-04-2` 阻塞的参数形态(多字节 bigint / 高位字节 bigint / 超 int32 的 int64)编写可跑绿用例" + - "不安装覆盖率工具(D-09)" + - "不建立跨文件共享 fixture/factory helper,fixture/helper 全部文件内联(D-02/D-03)" + - "不使用 fake timers(D-04)" +--- + + +按 D-43 阶段化(构件 → 组合 → 完整)补齐 06-04(录像)人工评审新记录的覆盖缺口 **G-06-04-C:录像读取流验证不够专项与严格**(D-46),只改 `packages-user/data-common/src/replay/array.test.ts`(测试)与 `06-COVERAGE-MAP.md`(覆盖表),不修任何生产代码: + +- **A —— 仅经读取流的复杂序列专项验证**:新增用例,驱动 5+ 条异质命令(不同参数个数与类型),**全程只用 `createReadStream`**(不搭配 `get`),逐条断言 `stream.read()` 的 `command` 与**每个参数的精确类型(`typeof`)与值**、`index` 逐次递进 `1..N`、末尾 `null`;并覆盖同一复杂序列的**中间起始索引**读取。 +- **B —— 强化既有流断言**:补每参数类型、`index` 递进、以及**序列变更后 `expired === true`**(例如一次 `add` 之后),落在既有的 `expectHeterogeneousRead`、`reads a sequence of steps through a read stream`、`expands buffers and still reads every step back` 上。 +- **附带**:`insert`/`delete` 后的异质序列流读次序在修复 `#06-04-3`/`#06-04-4` 前仍为**正确预期 `it.skip`**(本批次只登记、不修复)。 + +Purpose: 现有异质序列用例虽读了流,但(1)流与 `get` 混用、(2)`toMatchObject` 只保证值相等与数组长度、未逐参数断言类型、(3)流 `index` 只断言首条、(4)未验证变更后 `expired` 与复杂序列的中间起始索引、(5)增删后的流读次序被 `it.skip` 且只在同质序列上。用户已明确「录像系统应**重点验证读取流**」,本计划把「读取流」提升为一等验证对象,同时不触碰受代码缺陷阻塞的行为。 + +Output: `array.test.ts` 的流验证强化(新增 A 用例 2 条、B 新增 1 条 + 强化既有 3 处、新增增删 skip 2 条)与 `06-COVERAGE-MAP.md` 的 06-14 小节。 + +阶段结构(D-43):阶段 1 构件级(A:仅流读的复杂序列 + 中间起始索引)→ 阶段 2 组合(B:既有流断言强化 + 变更后 `expired`)→ 阶段 3 完整/收口(增删后异质序列流读次序的正确预期 skip + 06-14 覆盖表小节)。每阶段跑绿后再进入下一阶段。 + + + +@C:/Users/book/.config/opencode/gsd-core/workflows/execute-plan.md +@C:/Users/book/.config/opencode/gsd-core/templates/summary.md + + + +@.planning/phases/06-unit-tests/06-CONTEXT.md +@.planning/phases/06-unit-tests/06-COVERAGE-GAPS.md +@.planning/phases/06-unit-tests/06-PATTERNS.md +@.planning/phases/06-unit-tests/06-TEST-FINDINGS.md +@.planning/codebase/TESTING.md +@dev.md +@packages-user/data-common/src/replay/array.ts +@packages-user/data-common/src/replay/types.ts +@packages-user/data-common/src/replay/array.test.ts +@packages/common/src/logger.json + + + +- 只写测试,绝不修改 `packages-user/*/src` 下的非 `*.test.ts` 文件。 +- 每个 `it(...)` / `it.skip(...)` 之前必须有一行中文注释说明覆盖内容(dev.md 注释规范),测试范围变化时同步更新。 +- fixture 与 helper 內联且最小(D-02/D-03),沿用目标文件既有 `createArray`/`heterogeneousSteps`/`createHeterogeneousArray`/`expectHeterogeneousRead`,不新造跨文件 helper。 +- 类型化断言统一走新增的文件内 helper `expectParamTyped`/`expectStepTyped`(`typeof` + `toBe` 双重断言),不要只靠 `toMatchObject`/`toEqual` 的宽松比较。 +- 索引语义必须分清:`read()` 的 `index = position + 1`;`get(i)` 的 `index = i`;两者之间只按 `command`/`params` 对应。 +- 可跑绿用例只使用当前可正确编解码的参数:bigint 限单字节且 0..127、整数限 int32 范围;多字节 bigint 与超 int32 的 int64 一律 `it.skip`(D-05,锚定既有 `#06-04-1`/`#06-04-2`)。 +- 受阻塞的增删次序按正确预期编写、标记 `it.skip`、中文注释锚定既有 `#06-04-3`/`#06-04-4`;`pnpm test:ci` 必须保持全绿(D-05)。 +- 本计划不测 `saveState`/`loadState`(D-32)。 +- `06-COVERAGE-MAP.md` 为共享 create-or-append 文件(幂等、顺序无关):写入前先检查表头(`| code | 模块 | 用例 | 计划 |`)是否存在,不存在则先创建,再只追加/更新本计划小节,绝不重写他节。 +- **执行协议(D-33):执行本计划前必须先向用户确认;执行完成后暂停,用中文分条简要汇报本计划验证结果;详细结果写入共用的 `06-TEST-FINDINGS.md`。** +- **阶段门禁(D-43):本计划按 构件 → 组合 → 完整 三阶段顺序执行,每阶段跑绿后才进入下一阶段;若某阶段发现阻断性 bug 使后续阶段无法运行,暂停并按 D-05/D-07 向用户汇报、等确认,不得跳过或弱化。** +- **质量门禁(D-44,每个计划执行后、提交前必过)**:对改动文件运行 `eslint --fix`,并确保 `eslint <改动文件>` **0 错误**;对**本计划改动的测试文件**运行 `pnpm exec vue-tsc --noEmit`,并按输出文件路径过滤,确保这些文件路径下 **0 类型错误**(全局既有无关错误,如 `client-modules`/`legacy-plugin-data`,不在本门禁范围);`pnpm test:ci` **全绿**。任一不通过则**不得提交**。 + + + + + + 阶段 1(构件级):G-06-04-C/A 仅经读取流的复杂序列逐条类型/值/索引验证(含中间起始索引) + packages-user/data-common/src/replay/array.test.ts + + packages-user/data-common/src/replay/array.ts(`createReadStream` 的 `index` 语义与 `read()` 的 `null`/自增行为、`expireStreams` 触发点、`normalizeParam` 的类型 token 判定,**只读参考**) + packages-user/data-common/src/replay/types.ts(`IReplayReadStream` 的 `index`/`length`/`expired`/`read`/`destroy`、`ReplayParamValue`、`ReplayCommandWidth`) + packages-user/data-common/src/replay/array.test.ts(既有 `createArray`/`heterogeneousSteps`/`createHeterogeneousArray`/`expectHeterogeneousRead` 与「stream and buffer combination」分区,避免重复已有用例) + .planning/phases/06-unit-tests/06-COVERAGE-GAPS.md(G-06-04-C 的 A 期望) + .planning/phases/06-unit-tests/06-PATTERNS.md(B 节 array + 共享样板 + 中文注释规范) + + + 在 `array.test.ts` 顶部 helper 区新增两个文件内 helper(D-03): + 1) `expectParamTyped(actual: ReplayParamValue, expected: ReplayParamValue): void` —— 先 `expect(typeof actual).toBe(typeof expected)`,再 `expect(actual).toBe(expected)`,使 bigint 100n 与 number 100、`true` 与 `1` 不会被宽松比较混淆。 + 2) `expectStepTyped(step: { command: number; params: readonly ReplayParamValue[] }, command: number, params: readonly ReplayParamValue[], index: number): void` —— 断言 `step.command === command`、`step.params.length === params.length`、逐个 `expectParamTyped(step.params[i], params[i])`、`step.index === index`(用 `expect(step.params.length)` 而非 `toHaveLength`,避免类型摩擦;不新增导入)。 + 在「stream and buffer combination」分区新增 2 条可跑绿用例(每条前一行中文注释): + A1 `reads a heterogeneous route exclusively through a read stream`:內联构造 7 条命令(≥5、参数个数 1/2/0/1/4/2/3、类型各异,全部落在当前可正确编解码的范围):`add(1, [10])`(int8)、`add(2, [true, 'x'])`(boolean + 短 string,内联 token)、`add(3, [])`(无参数)、`add(4, [100n])`(单字节低位 bigint)、`add(5, [1, -32769, 3, 1.5])`(int8/int32/int8/float)、`add(6, ['a'.repeat(300), false])`(长 string 走 type 7 + boolean)、`add(7, ['', -1, 127])`(空 string 走 type 7 + int8 边界值)。**本用例体内不得出现 `array.get(`**——只用 `createReadStream(0)`:先断言 `stream.length === 7` 且 `stream.index === 0`;随后逐条 `stream.read()` 用 `expectStepTyped(step!, command, params, position + 1)` 断言(索引 1..7 递进),并在每次读取后断言 `stream.index === position + 1`;最后断言 `stream.read()` 为 `null` 且 `stream.index === 7`(末尾后不再递进)。 + A2 `starts a complex route read stream at a middle index`:用同一份 A1 的 7 命令构造(提取为一个文件内 `const` 数组或本地 `createComplexRoute()` helper,避免重复字面量),`createReadStream(3)`:断言初始 `stream.index === 3`;逐条读取得到原第 4..7 步(`command` 4..7)且 `expectStepTyped` 的索引为 4..7;末尾 `null` 且 `stream.index === 7`。同样**不得调用 `get`**。若需更强的「中间起始」证据,可再补一次 `createReadStream(6)`(只剩最后一步,读回索引 7 后 `null`)。 + 约束:不要使用多字节 bigint、高位字节 bigint(>127)或超 int32 的 int64 作为可跑绿期望(受 `#06-04-1`/`#06-04-2` 阻塞);不要改动既有 `it.skip`;不要重写既有用例的断言目标。若 A 用例跑红,先判断是否为阻塞缺口(是则按 D-05 写正确预期 + `it.skip` 并登记 `06-TEST-FINDINGS.md` 的 `#06-14-N`,同时暂停按 D-07 汇报),不得弱化断言使其通过。 + + + pnpm exec vitest run packages-user/data-common/src/replay/array.test.ts + 该文件运行失败(非 skip),或 A 用例体内出现 `array.get(` 调用,或未逐参数断言 `typeof` 与值(只用 `toMatchObject`/`toEqual`),或未断言读流 `index` 递进 `1..N` 与末尾 `null`,或未覆盖复杂序列的中间起始索引,或可跑绿用例使用了受 `#06-04-1`/`#06-04-2` 阻塞的参数形态,或既有 148/149/150/151/152/153/154/155 用例回归失败 + + + - `array.test.ts` 零非跳过失败;`pnpm exec vitest run packages-user/data-common/src/replay/array.test.ts` 通过。 + - G-06-04-C/A:存在 2 条新增可跑绿用例(复杂序列仅流读 + 中间起始索引),命令 ≥5 条、参数个数与类型各异,逐条断言 `command`、每参数类型与值、`index = position + 1`(1..N)、末尾 `null`;用例体内无 `array.get(`。 + - 新增 helper `expectParamTyped`/`expectStepTyped` 为文件内定义(无跨文件共享,D-03)。 + - 既有 5 条 `it.skip` 的数量与锚点不变(D-05)。 + - 每个新增 `it` 前有单行中文注释。 + - D-44 门禁通过:改动文件 `eslint --fix` 后 `eslint <改动文件>` 0 错误;本计划改动的测试文件在 `pnpm exec vue-tsc --noEmit` 输出中按文件路径过滤 0 类型错误;`pnpm test:ci` 全绿。 + + 阶段 1 的 G-06-04-C/A 全部跑绿后才进入阶段 2。若发现阻断性 bug(含 A 用例无法在正确预期下通过),暂停并按 D-05/D-07 向用户汇报、等确认。 + + + + 阶段 2(组合):G-06-04-C/B 强化既有流断言(每参数类型、index 递进、变更后 expired) + packages-user/data-common/src/replay/array.test.ts + + packages-user/data-common/src/replay/array.test.ts(既有 `expectHeterogeneousRead`(被 3 条用例复用)、`reads a sequence of steps through a read stream`、`warns code 155 when reading an expired stream`、`expands buffers and still reads every step back`) + packages-user/data-common/src/replay/array.ts(`expireStreams()` 在 `add` 等变更方法内的调用点;`read()` 对 `expired` 的处理是「告警后继续读」而非返回 `null`,**只读参考**) + .planning/phases/06-unit-tests/06-COVERAGE-GAPS.md(G-06-04-C 的 B 期望) + .planning/phases/06-unit-tests/06-TEST-FINDINGS.md(`#06-04-3`、`#06-04-4`,确认本计划不新建条目) + + + 在不取消任何既有用例、不改变其覆盖目标的前提下,把「流断言」从「值相等」提升到「类型 + 值 + 索引递进 + 过期」: + 1) 重写文件内 `expectHeterogeneousRead(array, expectedSteps)`(3 条既有用例共享受益):循环内改为 `const step = stream.read()!; expectStepTyped(step, command, params, i + 1)`(每参数类型+值 + 流索引 `position + 1`);对 `get` 的对照改为 `expect(array.get(i).command).toBe(command)` 并对每个参数用 `expectParamTyped`(`get(i)` 的 `index` 语义保持 `i`,如需断言则用 `expect(array.get(i).index).toBe(i)`,**不要**对 `read()` 与 `get(i)` 整体做 `toEqual`);循环结束后断言 `stream.index === expectedSteps.length` 且 `stream.read()` 为 `null`。函数注释补充「流索引 = position + 1、get 索引 = i」的语义说明。 + 2) `reads a sequence of steps through a read stream`:在既有 `toEqual` 之外,补每参数 `typeof`(`expect(typeof step.params[0]).toBe('number')`)与逐次读取后的 `expect(stream.index).toBe(i + 1)`;保留末尾 `null` 断言。 + 3) `expands buffers and still reads every step back`:15 步循环内同样补 `expect(typeof step.params[0]).toBe('number')`、`expect(step.params[0]).toBe(i)`、`expect(stream.index).toBe(i + 1)`;保留扩容后逐条读回与末尾 `null`。 + 4) 新增 1 条可跑绿用例(前一行中文注释)`expires a read stream after the heterogeneous route is mutated`:`const array = createHeterogeneousArray()`;`const stream = array.createReadStream(0)`;先读 2 步并用 `expectStepTyped` 断言(索引 1、2);随后 `array.add(7, ['z'])`(变更触发 `expireStreams`)→ 断言 `expect(stream.expired).toBe(true)`;`const { info } = logger.catch(() => stream.read())` → 断言 `info.map(v => v.code)` 含 **155**(复用既有告警码语义,不引入新码,D-31);**不要**断言过期流本次 `read()` 的返回值(过期后读取非契约行为);最后用 `array.createReadStream(0)` 新建读流,按 6 条原步骤 + 新 `add` 步骤共 7 步逐条 `expectStepTyped`(索引 1..7)读回一致,末尾 `null`。 + 约束:不测 `saveState`/`loadState`(D-32);不使用 fake timers(本计划全同步,D-04);不新增跨文件 helper(D-03);若强化后某既有用例断言失败,先判断是被测行为缺陷还是断言过强——属缺陷则按 D-05 写正确预期 + `it.skip` 并登记 `#06-14-N`(若已有同锚点条目则复用),不得为跑绿而回退类型化断言。 + + + pnpm exec vitest run packages-user/data-common/src/replay/array.test.ts + 该文件运行失败(非 skip),或 `expectHeterogeneousRead` 未逐参数断言类型+值 / 未断言流索引递进 / 未断言末尾 `null` 与 `stream.index === length`,或既有 3 条复用用例未受益于强化,或新增用例缺少「`add` 变更后 `expired === true` + 告警码 155 + 新流按新次序类型化读回」,或既有 155 用例回归失败 + + + - `array.test.ts` 零非跳过失败。 + - G-06-04-C/B:`expectHeterogeneousRead` 逐参数断言类型与值、逐条断言流索引 `i + 1`、末尾断言 `stream.index === expectedSteps.length` 与 `null`;`reads a sequence of steps through a read stream` 与 `expands buffers and still reads every step back` 补上每参数类型与 `stream.index` 递进。 + - 新增用例覆盖「复杂序列 `add` 变更后既有读流 `expired === true`」与告警码 155,并验证新建读流按新次序类型化读回(7 步、索引 1..7、末尾 `null`)。 + - 不新增告警/错误码;既有 148/149/150/151/152/153/154/155 覆盖不削弱(D-31)。 + - 每个新增 `it` 前有单行中文注释;既有 5 条 `it.skip` 不变。 + - D-44 门禁通过:改动文件 `eslint --fix` 后 `eslint <改动文件>` 0 错误;本计划改动的测试文件在 `pnpm exec vue-tsc --noEmit` 输出中按文件路径过滤 0 类型错误;`pnpm test:ci` 全绿。 + + 阶段 2 的 G-06-04-C/B 在阶段 1 跑绿基础上保持全绿后才进入阶段 3。若发现阻断性 bug,暂停并按 D-05/D-07 向用户汇报、等确认。 + + + + 阶段 3(完整/收口):增删后异质序列流读次序的正确预期 skip + 06-14 覆盖表小节 + packages-user/data-common/src/replay/array.test.ts, .planning/phases/06-unit-tests/06-COVERAGE-MAP.md + + packages-user/data-common/src/replay/array.test.ts(既有 `deletes a middle step and shifts later param indexes`(`#06-04-3`)、`reads the new order after inserting a step`(`#06-04-4`)两条 skip 的注释与锚点写法) + packages-user/data-common/src/replay/array.ts(`delete`/`insert` 的索引数组推进逻辑,**只读参考**,用于写「正确预期」) + .planning/phases/06-unit-tests/06-TEST-FINDINGS.md(`#06-04-3`、`#06-04-4` 的现象描述与关联用例) + .planning/phases/06-unit-tests/06-COVERAGE-MAP.md(既有 06-12 小节形状:小节标题 + 表头 `| 缺口 | 用例 | 文件 | 计划 |` + 收尾说明) + + + 1) 在「stream and buffer combination」分区新增 2 条**正确预期 `it.skip`**(前一行中文注释,锚定既有 finding 条目,不新建 `#06-14-N`): + - `reads the new order after deleting a middle step from a heterogeneous route`(锚定 `#06-04-3`):用 6 条异质序列(`createHeterogeneousArray()`)执行 `delete(1)`;正确预期:`array.length === 5`,经 `createReadStream(0)` 只用流读出剩余 5 步(原第 1、3、4、5、6 步)且每步 `command`/每参数类型与值精确一致、索引 1..5 递进、末尾 `null`;中文注释注明「delete 中间步后索引数组未按删除位置回退,详见 `06-TEST-FINDINGS.md` `#06-04-3`,修复后取消 skip」。 + - `reads the new order after inserting a step into a heterogeneous route`(锚定 `#06-04-4`):用同一 6 条异质序列执行 `array.insert(2, 9, [true, 5])`;正确预期:`array.length === 7`,经 `createReadStream(0)` 只用流读出**新次序** 7 步(原第 1、2 步 → 新插入步(`command 9`、`[true, 5]`)→ 原第 3..6 步)且每步类型/值与索引 1..7 递进、末尾 `null`;中文注释注明「insert 的参数缓冲区位移方向相反,详见 `06-TEST-FINDINGS.md` `#06-04-4`,修复后取消 skip」。 + 两条 skip 均只影响 skip 计数,不影响 `pnpm test:ci` 绿;**不得**取消或改写既有 5 条 skip(含 `#06-04-1`/`#06-04-2` 的两条编解码 skip)。若执行时发现 `#06-04-3`/`#06-04-4` 已被后续修复批次解决,则取消对应 skip 转为可跑绿回归用例,并在 `06-TEST-FINDINGS.md` 更新状态;否则保持 skip 不变。 + 2) 以 create-or-append 方式写入 `06-COVERAGE-MAP.md` 的 **06-14** 小节:先检查表头 `| code | 模块 | 用例 | 计划 |` 是否存在(不存在则先创建文件与表头),只追加/更新本小节,绝不重写他节。小节内容:标题 `## 06-14 录像读取流专项验证补齐(D-46)`;说明本计划为 **G-06-04-C(A + B)** 的补测、**不引入新码**(复用 148..155 既有可达码,其中 155 为「变更后读过期流」断言)、阶段结构为 构件(仅流读复杂序列)→ 组合(既有流断言强化 + 变更后 expired)→ 完整(增删后流读次序正确预期 skip);表格 `| 缺口 | 用例 | 文件 | 计划 |` 列出:G-06-04-C/A → `reads a heterogeneous route exclusively through a read stream`、`starts a complex route read stream at a middle index`;G-06-04-C/B → `expectHeterogeneousRead`(helper 强化,服务 3 条既有用例)、`reads a sequence of steps through a read stream`、`expands buffers and still reads every step back`、`expires a read stream after the heterogeneous route is mutated`;G-06-04-C(附带)→ `reads the new order after deleting a middle step from a heterogeneous route`(`it.skip`,`#06-04-3`)、`reads the new order after inserting a step into a heterogeneous route`(`it.skip`,`#06-04-4`);收尾注明「本计划无新增码;两条为受阻塞的正确预期 `it.skip`(锚定既有 `#06-04-3`/`#06-04-4`,并确认既有 `#06-04-1`/`#06-04-2` 两条 skip 不变);不削弱也不删除任何既有用例或跳过」。 + 3) `06-TEST-FINDINGS.md`:本计划预期**不新增**条目(阻断项复用既有锚点);仅在阶段 1/2/3 真的发现新缺陷时按 D-05 追加 `#06-14-N` 并同步到计划汇报中。 + + + pnpm exec vitest run packages-user/data-common/src/replay/array.test.ts + 该文件运行失败(非 skip),或新增的 2 条增删后流读次序用例不是 `it.skip` 或缺少指向 `#06-04-3`/`#06-04-4` 的中文注释锚点,或既有 5 条 skip 被取消/改写,或 `06-COVERAGE-MAP.md` 缺少 06-14 小节(或该小节重写了他节内容),或覆盖表未列出 G-06-04-C/A、B 与两条新增 skip 的用例映射 + + + - `array.test.ts` 零非跳过失败;`it.skip` 由 5 条变为 7 条(新增 2 条增删次序 skip),既有 5 条的数量/锚点/正确预期均不变(D-05)。 + - 新增 2 条 skip 覆盖「异质序列 `delete` 中间步后的流读次序」与「异质序列 `insert` 后的流读新次序」,断言按正确预期(每步 `command`/参数类型与值/索引递进/末尾 `null`)。 + - `06-COVERAGE-MAP.md` 存在 06-14 小节(create-or-append,未重写他节),列出 G-06-04-C/A、B 及两条 skip 的 `缺口 → 用例 → 文件 → 计划` 映射,并注明本计划无新增码。 + - 每个新增 `it.skip` 前有单行中文注释并锚定 `06-TEST-FINDINGS.md` 条目。 + - D-44 门禁通过:改动文件 `eslint --fix` 后 `eslint <改动文件>` 0 错误;本计划改动的测试文件在 `pnpm exec vue-tsc --noEmit` 输出中按文件路径过滤 0 类型错误;`pnpm test:ci` 全绿。 + + 阶段 3 的 2 条正确预期 skip 在阶段 1/2 跑绿基础上保持全绿,且 06-14 小节已写入 `06-COVERAGE-MAP.md`。完成后暂停并用中文分条汇报;详细结果写入 `06-TEST-FINDINGS.md`。 + + + + + +## Trust Boundaries + +| Boundary | Description | +|----------|-------------| +| none | 仅新增/扩展仓库内 Node Vitest 测试;无运行时输入面、网络、DOM 或文件写入。 | + +## STRIDE Threat Register + +| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan | +|-----------|----------|-----------|----------|-------------|-----------------| +| T-06-14-01 | Tampering | `data-common/src/replay/array.test.ts` 与规划产物 | low | mitigate | 只导入已跟踪模块、內联合成 fixture;不改核心源码;门禁 `pnpm test:ci` + `git status` 复核仅 `*.test.ts` 与 `06-COVERAGE-MAP.md` 变更。 | +| T-06-14-02 | Information disclosure | 合成录像序列 | low | accept | 合成命令与参数(无真实录像/存档数据),无密钥或个人信息(D-02)。 | +| T-06-14-03 | Repudiation | 受阻塞行为被误改为「通过」 | low | mitigate | `#06-04-1`/`#06-04-2`/`#06-04-3`/`#06-04-4` 一律保留正确预期 `it.skip` 与中文锚点;可跑绿用例限定在可正确编解码的参数范围内;`pnpm test:ci` 全绿即证明未弱化。 | +| T-06-14-04 | Tampering | 断言宽松化(只用 `toMatchObject`)导致类型混淆漏检 | low | mitigate | 统一经 `expectParamTyped`/`expectStepTyped` 做 `typeof` + `toBe` 双重断言,并逐条断言流 `index` 递进。 | +| T-06-14-SC | Tampering | 包安装 | high | mitigate | 明确不引入任何新依赖(D-09);若确需安装,停止并先走包合法性 checkpoint。 | + + + +- 聚焦运行 `pnpm exec vitest run packages-user/data-common/src/replay/array.test.ts` 通过;`pnpm test:ci` 全量绿。 +- `git status` 仅显示 `packages-user/data-common/src/replay/array.test.ts` 与 `06-COVERAGE-MAP.md` 的变更;无核心文件改动,无 06-01..06-13 计划改动。 +- G-06-04-C/A(仅流读复杂序列 + 中间起始索引)与 B(每参数类型、索引递进、变更后 `expired`)均为可跑绿;增删后流读次序为 2 条正确预期 `it.skip`,且既有 5 条 skip 不变。 +- 断言强度复核:新增/强化处均出现 `typeof` 与流 `index` 递进断言;A 用例内无 `array.get(`。 +- 以 create-or-append 方式写入 `06-COVERAGE-MAP.md` 06-14 小节;表头不存在则先创建;幂等、顺序无关,不重写他节。 +- **执行协议**:执行前必须先向用户确认;执行后暂停并用中文分条汇报。 + + + +- G-06-04-C/A:reads 复杂异质序列**仅经读取流**逐条读回,参数类型/值与 `index` 递进(`1..N`)均被显式断言,含中间起始索引。 +- G-06-04-C/B:既有流用例的断言强度提升到「类型 + 值 + 索引递进」,并覆盖变更后 `expired === true` 与告警码 155。 +- 受阻塞的增删次序(`#06-04-3`/`#06-04-4`)以正确预期 `it.skip` 登记;既有 5 条 skip 不变;`pnpm test:ci` 保持全绿。 +- 无生产代码改动;`06-COVERAGE-MAP.md` 06-14 小节落地。 + + + +Test files (extended): +- `packages-user/data-common/src/replay/array.test.ts`(新增 G-06-04-C/A 2 条可跑绿用例 + B 1 条可跑绿用例;强化 `expectHeterogeneousRead` 与 2 条既有流用例;新增 `expectParamTyped`/`expectStepTyped` 文件内 helper;新增 2 条受阻塞的正确预期 `it.skip`) + +Planning artifacts: +- `06-COVERAGE-MAP.md` create-or-append 06-14 小节 + + + +完成时创建 `.planning/phases/06-unit-tests/06-14-SUMMARY.md`,以 create-or-append 方式写入 `06-COVERAGE-MAP.md` 06-14 小节;疑似 bug 追加到 `06-TEST-FINDINGS.md` 的 `#06-14-N` 小节(仅在确有新缺陷时;受阻塞项复用既有 `#06-04-1`/`#06-04-2`/`#06-04-3`/`#06-04-4` 锚点,不新建)。执行完成后暂停并用中文分条汇报。 +