EdgeMemory-MVP 测试与验收计划

EdgeMemory-MVP 分册 05 | 96 条用例 | v1.0(定稿) | 2026-10-26
用例集与放行规则。G7(随机掉电 100 次库 100% 可开)为一票否决项,不通过则整个 MVP 判定失败。
用例编号:TC-C 核心 / TC-F 碎片灌库 / TC-S1/2/3 场景 / TC-N 非功能 / TC-M 网络 / TC-W Web。

1. 判据 → 用例映射

判据内容覆盖用例级别负责人
G1Dev-B ≥12,000 条 / Dev-A ≥2,500 条TC-N-01门槛QA + FW-A
G2P50<10ms / P99<30ms / P99÷P50<4TC-N-02门槛QA
G3PSRAM≤5MB / DRAM≤180KB / malloc=0TC-N-03, TC-N-04门槛FW-A
G4时序→ANN→回写闭环TC-S1-07, TC-C-07门槛FW-A
G5四通道灌库耗时TC-N-05, TC-N-06门槛QA + FW-B
G6断网全功能可用TC-N-07完整性QA
G7随机掉电 100 次库 100% 可开TC-N-08否决QA(台架)
G8emlite .text≤60KB / malloc 0 / abort P99≤2×P50TC-N-09, TC-N-10, TC-N-11完整性FW-A
G9EdgeMesh 三节点接力 + 账本误差 0TC-M-03…TC-M-10完整性FW-B

2. 测试环境与台架

台架构成用途
TS-1 双板台架Dev-A + Dev-B + 交换机 + 双 USB 线 + 2 个 USB-UART 转换器主力。所有板级用例
TS-2 掉电台架程控继电器(或 MOS 开关)+ 脚本,可随机切断供电并记录时刻TC-N-08,100 次。不要手工拔线
TS-3 PC 仿真em_flash_io 文件仿真 + 引擎层单测(host 编译)引擎层全部单测;可精确控制断电字节位置
TS-4 Web 台架Chrome + tools/serve.py 8080 + 板子同网段TC-W-*;CORS 与 mixed content 验证
TS-5 长稳台架7×24 h 连续运行,串口日志落盘TC-N-12
TS-3 是拿到 G7 结论最快的路径。PC 仿真可以在「写到第 N 个字节后中止」的粒度上精确断电,比硬件台架覆盖得更密、更快。硬件台架只做最终确认。

3. TC-C 核心功能(8 条)

ID步骤预期
TC-C-01空库启动 → sys.info → store.map启动 <800 ms;edgehippom 大小 = 11.875 MB(Dev-B)/ 3.875 MB(Dev-A);build_time 为本次构建
TC-C-02mem.put 写入 1 条(含 150 B 摘要)→ mem.get返回 id > 0;取回内容一致;字节占用 ≈ 662 B
TC-C-03mem.put 写入 100 条 → mem.query {k:5}返回 5 条,按 score 降序;matched_by 字段正确标注 ANN / FILTER
TC-C-04混合查询:向量 + filter="dev_id==3"返回结果全部满足过滤条件;ANN 不被过滤条件绕过
TC-C-05写入两条余弦 > 0.95 的条目合并为一条(ADR-007);mem.stat 条数 +1 而非 +2;hit_count 累加
TC-C-06写入语义冲突条目 → mem.query保留 ts_ms 更新者;旧条目 flags 含 CONFLICTED 且 prio 下降
TC-C-07事务:em_tx_begin → put ×50 → em_tx_abort条数不变;库可正常打开;abort 耗时 P99 ≤ 2×P50(G8)
TC-C-08mem.del → mem.stat → edgehippom.compact删除后条数 −1;compact 后 bytes 下降;耗时 <30 s @ 满库
TC-C-09【ADR-014 拒答-低分】写入 100 条与此话题无关的条目 → 查询该话题,设 min_score_q8 高于全库最高分返回 {hits:[], reject: EM_REJECT_LOW};不得返回任何「最接近但不相关」的条目;宿主可据此输出「不在我的知识范围内」
TC-C-10【ADR-014 拒答-红线】写入一条 meta.refusal=1 的条目 → 查询命中该条目返回 {hits:[], reject: EM_REJECT_REDLINE};响应体中不得出现该条目的任何文本片段(连摘要也不能泄漏)
TC-C-11【ADR-014 拒答-过期】写入 ttl_h=1 条目 → 系统时间推进 >1 h → 查询返回 {hits:[], reject: EM_REJECT_EXPIRED},与 LOW / REDLINE / EMPTY 三者可区分
TC-C-12【ADR-014 出处可引用】任意命中查询每条 hit 必带 frag_id / ts_ms / score / src 四字段;缺失任一即判 Fail

4. TC-F 碎片与灌库(8 条)

ID步骤预期
TC-F-01灌入 1,000 条 v2 碎片(带向量)→ frag.list条目数、名称、ver、count 与包内一致;payload_crc32 校验通过
TC-F-02灌入 v1 碎片(无向量)→ 查询成功;dim=0;走文本检索路径(em_kb_search)
TC-F-03v1 固件读 v2 包(跳过多余字节)不崩、不越界;按 rec_len 正确跳到下一条(ADR-010)
TC-F-04灌入错误包(magic 错 / CRC 不符 / 条数不符 / text_len>150)全部被拒绝;返回明确错误码;已有库完好
TC-F-05灌入同名低版本碎片(ver 更小)被拒绝并给出明确错误(OVERWRITE_SAME_NAME 未置位)
TC-F-06灌入同名高版本碎片覆盖成功;旧条目释放;条数正确
TC-F-07灌库中途断电(TS-2,第 40% 处)重启后库可打开;要么完整、要么完全未生效;无半成品
TC-F-08frag.export → 再 frag.import 到另一块板 → 查询往返一致;条数与内容一致;可命中

5. TC-S1 / S2 / S3 三场景(各 8 条,共 24 条)

三场景共用同一套固件、同一份 schema、同一个 .emf 格式——差异只在灌进去的碎片和宿主是谁。这是「做器官」的工程证明,也是测试用例能复用的原因。

5.1 S1 工业振动故障指纹库(时序主导,4 个碎片 2,900 条)

ID步骤预期
TC-S1-01灌入 4 个故障指纹碎片(正常 / 内圈 / 外圈 / 滚动体)2,900 条全部入库;frag.list 显示 4 项
TC-S1-02ts.script {model:"normal", seed:1, dur_s:60}L0 环形缓冲写入正常;无丢样本
TC-S1-03注入内圈故障(BPFI)合成数据 → 窗口特征化特征化耗时 < 单窗口时长;L1 分钟聚合可查
TC-S1-04特征向量 → mem.querytop-1 命中「内圈故障」;score > 阈值
TC-S1-05三种故障 × 20 次(不同 seed)top-1 准确率 ≥ 85%;误报 ≤ 5%(合成数据源口径)
TC-S1-06注入库中没有的新故障类型返回「不知道」,不编造;走 P5 断网话术
TC-S1-07闭环:命中 → 确认 → 回写新指纹 → 再注入同类数据下次可命中刚写入的指纹(G4)
TC-S1-08三种速率数据(4 kHz / 10 Hz / 1 Hz)按时间窗对齐共同时间窗内对齐;dev_id 精确过滤生效(3 台设备)

5.2 S2 野外巡检(断网 + 三种接力,Dev-B 4,100 条 / Dev-A 1,800 条)

ID步骤预期
TC-S2-01Dev-B 灌入 4,100 条基础包;Dev-A 灌入 1,800 条专家包Dev-A 占用约 50%(N8R8 3,700 条档位),仍有余量接纳现场新增
TC-S2-02断开全部网络 → Dev-A 本地检索全部功能可用(G6);响应时间与联网时无显著差异
TC-S2-03现场新增记录(巡检发现)→ Dev-A 本地入库写入成功;可在本地命中
TC-S2-04M1 直连接力:Dev-A 向 Dev-B 请求基础包补充六原语时序跑通;分块 CRC 全过
TC-S2-05M2 Hub 中转:Dev-B 代持 Dev-A 的专家包Dev-A 离线时第三方仍可从 Dev-B 取到
TC-S2-06M3 离线投递:USB MSC 拷入 .emf + .emr≤60 s 完成;可用;回执文件生成正确
TC-S2-07恢复联网 → 账本对账双向误差 = 0(G9);M3 回执按 frag_id+seq 去重
TC-S2-08断网 72 h 连续巡检后回连现场新增记录全部保留;无丢失;对账一致

5.3 S3 私域知识库(纯语义,12,000 条满载,β 首销 SKU)

ID步骤预期
TC-S3-01灌入 3 个碎片共 12,000 条成功且库可打开(G1 目标值);占用 ≈9.0 MB
TC-S3-02用 P4 的 20 条验收用例自检(frag.verify)≥18/20 通过
TC-S3-03用别名提问(术语表未覆盖的同义表述)命中标准词条目;不因表述差异漏检
TC-S3-04提问红线内容必须拒答;话术符合 P1(≤40 字,承认不知道)
TC-S3-05提问库中故意没有的知识(2 条用例)返回「不知道」,不编造
TC-S3-06提问已过期知识(ttl_h 已过)提示已过期 + 「联网后可更新」;走 P5 第三类模板
TC-S3-07满库状态下再灌入 500 条触发容量压力策略:按 prio+last_hit_ms 淘汰至 75%;PINNED 条目不被淘汰
TC-S3-08满库下 mem.query × 1,000 次P50 <10 ms,P99 <30 ms,P99/P50 <4(G2)

6. TC-N 非功能(12 条)

6.0 断电强度分级(G7 的验收口径,2026-10-26 定案)

[重要] 不同强度的断电注入不可互相替代,对外 G7 只认 L2 与 L3。
此前固件侧报告「100 次真断电零失败」,经其本人纠正:该注入为进程内仿真(把介质 IO 置失效后重新 open 模拟上电,进程并未真死),属 L1。它仍是有效且有价值的回归证据(编译过、普通单测过的偏移口径类缺陷只有它能逼出),但不能作为 G7 的对外验收证据。
本分册今后凡出现「真断电」一词,一律指 L2 / L3;L1 一律表述为「断电注入(进程内仿真)」。
级别机制进程真死提供者定位
L1进程内注入仿真:IO 置失效后重新 open 模拟上电否固件侧 tests/unit/开发期回归与精确定位(能指出断在事务哪一步)。不出对外数字
L2真进程中止:子进程写到第 N 个字节后 abort(),父进程 reopen 验收是QA tests/accept/PC 侧对外口径。字节粒度,可精确复现
L3硬件掉电:程控继电器 / 程控电源随机切断供电并记录时刻是板端台架 TS-2板端对外口径。含真实 flash 时序与电源跌落行为

★ 判据缺陷登记(2026-10-28,TS-2 台架设计时发现):「随机掉电 100 次」的抽样方式本身会让 G7 被误杀。

G7 原文只写「随机掉电 100 次」,未规定断电时刻的抽样分布。但一次 em.put 的提交耗时 远小于两次 put 之间的间隔 ⇒ 纯随机时刻落在提交窗口内的概率很低 ⇒ 越"真随机",saw_visible(防空通过计数)越容易掉到下限 10 以下, 于是 G7 会因为抽样方式、而不是固件缺陷被判失败。L3 的时刻抖动比 L2 大得多,这个偏差只会更明显。

裁定:L3 的 100 次必须分层抽样,不许纯随机。三组各约 1/3:

  • 钉在 put 之后:断电紧跟一次已返回 ack 的 put —— 制造"提交刚完成即断电"
  • 钉在 put 之间:断电落在两次 put 之间 —— 制造"写入进行中断电",这是 saw_visible 的主要来源
  • 纯随机 idle:覆盖空闲态

★ 判据缺陷登记 · 增补(2026-10-28 晚,零采购裁定之后):上述「分层抽样」在人工拔插方案下有 2/3 层做不到,必须说明用什么替代。

用户裁定「先别买硬件,用别的办法顶」后,L3 的执行机构临时改为人工拔插 USB-C(真实拔掉 VBUS,属真实断电)。 结论是「部分接受」,论证全文见 07-实测手册/TS-2掉电台架设计_v0.3_20261028.md §7。分层抽样的三层在这一方案下的处境:

层人工方案下能否做到替代办法
钉在 put 之后(after_put)做不到。人的延迟 200–500 ms,而该窗口是毫秒级,差一个数量级以上,练不出来并入「写入占空比」方案,事后归因
钉在 put 之间(in_put)做不到(同上)并入「写入占空比」方案,事后归因
纯随机 idle做得到。脚本保证窗口内无写操作 ⇒ 人延迟几百毫秒仍在窗口内无需替代,可继续执行

替代办法(两条补丁,缺一不成立): ① 写入占空比:让 DUT 背靠背持续灌库,人何时拔都大概率落在写里 —— 这使 saw_visible 天然高位, 恰好规避了本红框担心的「因抽样方式被误杀」,且断点更多落在写里 ⇒ 通过更难 ⇒ 判据偏严,不是偏松。 ② 事后归因:host 侧记录每条 put 的发送/ack 时间戳,在检测到端口消失的瞬间打快照, 把每轮事后归类为 in_put/after_put/idle。配额不可保证,但「实际命中分布」因此成为实测数字而非推断。

🔴 由此新增的判据红线:若 100 轮事后归因中 in_put 类为 0, 则本批不得宣称覆盖了提交中断,只能宣称「多次真实断电后可开」。不加这条,占空比补偿就是循环论证。

表述纪律(违反即假绿):本方案的结果只能写作「人工时刻掉电 100 次」, 不得写成「随机掉电 100 次」—— 时刻是人选的,既非 PRNG 也非均匀分布,且与被测系统可见行为存在相关性。 同时必须随结论发布效力边界:① 覆盖范围只能事后报告、不能事前保证;② 不得与 L2 的 100/100 合并成一个数字; ③ pass + fail + not_verified == rounds 必须恒等,NOT-VERIFIED 轮次不得计入通过、也不得挑着补跑。 完整表述模板见 TS-2 v0.3 §7.5,六条假绿风险自查见 TS-2 v0.3 §7.8。

在按此口径重跑之前,任何「L3 随机掉电 N 次」的数字都不得与 L2 的 100/100 混为一谈, 也不得用它去推翻或支撑 G7 结论。另:TS-2 设计阶段还查出 nonmono 与 tx2 两项判据 在 L3 结构上不可判定(详见 07-实测手册/TS-2掉电台架设计_v0.1_20261028.md §5), 其板端口径分别退化为「前缀完整性」与「上界约束 visible ≤ acked + in_flight」,与 L2 同名判据不等价。

放行要求:G7 的对外结论须同时具备 L2(PC 侧 100 次) 与 L3(板端 100 次),二者不可互相替代——L2 能定位到字节但缺少真实 flash 时序,L3 真实但无法复现到字节。
交叉验证(推荐做法):固件侧用 QA 的 L2 注入器与监管层,把自己那套 100-seed 场景再跑一遍。结果一致 → 证据同时具备"进程内"与"真死亡"两个角度;不一致 → 以 L2 为准,且差异本身就是必须查清的信号。

6.0.1 G7 实测结果(2026-10-26,L2 已通过)

G7 在 L2(真实引擎 + 真进程 abort)已通过;L3(板端硬件掉电)尚未验证,需台架 TS-2。
[G7 真数字] 100 次随机硬掉电(真实进程 abort):
  open_fail=0 half=0 tx2=0 nonmono=0 lost=0 mismatch=0 corrupt=0 noreject=0 notfired=0
  saw_visible=98 首见字节=722
[PASS] TC-N-08  IMPL  随机掉电 100 次,库 100% 可开        43427.6 ms
[PASS] TC-N-11  IMPL  断电扫描 96 点:全部中断点可无损重启 46935.1 ms
放行标准实测结论
open_fail = 00[OK]
partial = 0(half / tx2 / nonmono 均 0)0[OK]
lost = 00[OK]
saw_visible ≥ 10(防空通过)98 / 100[OK] 远超下限,证明断点确实落在提交之后
open_inval = 0(第 5 条,后加)0[OK] 配置类 open 失败不计入 G7,但须单独断言为 0
为什么必须把 open_inval 从 open_fail 里分出来:二者原本共用一个计数器,一个配置 bug 就能把 G7 的失败计数顶爆——这是判据被无关因素污染的典型。G7 测的是「掉电后库开不开」,不该被配置错误干扰。现在 open_fail 只统计 IO / CORRUPT / 超时(计入 G7);open_inval(EM_E_INVAL)不计入,但单独断言必须为 0,否则说明测试环境本身有配置错。

三个前提同时成立(缺一则该数字不成立):① emstub_impl.c 已删,13 个引擎 .c + em_blk_reg.c 原样入链,后端自报 emlite 且 em_open 真跑通;② 断电是真进程 abort,父进程靠「异常终止 + POWERCUT marker + 无 DONE」三重判定;③ 非空通过,98/100 断点落在提交之后。

两个未覆盖项(如实登记,不粉饰):
① L3 未做。本结果仅代表 L2。按 6.0 的放行要求,对外完整的 G7 结论还需要板端台架 TS-2 的 100 次硬件掉电。在 L3 完成前,对外表述必须是「G7 的 L2 已通过」,不得简化为「G7 已通过」。
② 业务状态覆盖 3/5。已覆盖 open / import / commit;compact 与 relay 无实现可注入。在 compact 实现并覆盖前,不得对外宣称「G7 五状态全过」。
【纪律 · 2026-10-26 事故留痕】凡本分册与 SPEC 中的「已实测」类结论,必须能指到一条「会随结论失效而变红」的用例。
只有「纯枚举」型不算——它不碰介质,注入器坏了照样绿。文档里写「实证」之前,先确认地基会在失效时红。

事故经过:位级撕裂注入器 EM_PCUT_BIT_AT 第一版是坏的——em_flash_write 按 EM_FLASH_CHUNK=256 整块推进,触发条件 done == bit_at 而 done 只取 0/256/512…,bit_idx=7 被整块跨过,注入静默退化成"什么都没发生"。
失效形态是最坏的一种:不红,反绿。字节级用例照常通过,"位级验过了"这个结论就成立在纸面上,骗过三个人两轮评审——因为结论写在注释和文档里,而注释不会被执行。

结论本身没错(mark 判据确实完备),修复后两条独立证据逐位对上(纯枚举 128/127、真实介质 128/127),只是依据现在才补齐。

配套三条规矩(已入 SPEC §4.5):① 注入器必须有自身回归——不得只靠"用它跑出来的用例绿了"间接证明它可用;② 不写死介质操作序号(提交期顺序会随实现变,用「命中三条件」扫,扫不到就红);③ 回读必须在前滚之前(引擎在 powered==false 下返回 EM_E_POWER,需直接读仿真介质文件;否则读到的是前滚补完的行,测不到撕裂态)。
【实测性质 · 影响判据的表述】实测操作序:WAL 增量×2 → COMMIT → 数据 8 B → mark 4 B → 超级块擦/体/魔法。COMMIT 先于数据页落盘 → 「数据页写一半」这类撕裂在下次开库时被幂等前滚修好、不残留(实测:撕裂后重开有效样本=4,均值=20.00)。
因此 L1 行尾 mark 是纵深防御——它兜的是"前滚本身再被断电打断"的中间态,不是兜住每一次半写。两个极端都不要写:既不要写成"判据兜住每一次半写",也不要反过来据此说"判据用不上"。
ID判据步骤预期
TC-N-01G1向 Dev-B 持续灌入直到拒绝为止,记录条数≥12,000 条(可行性门槛 8,000);Dev-A ≥2,500 条;库可正常打开并检索
TC-N-02G212,000 条库,mem.query × 1,000 次,记录板侧耗时P50<10 ms,P99<30 ms,P99/P50<4
TC-N-03G3满库稳态下读水位PSRAM ≤5 MB,DRAM ≤180 KB
TC-N-04G3包装 malloc/calloc/realloc,进入 RUNNING 后计数计数 = 0(全预分配池)
TC-N-05G512,000 条 .emf 经 HTTP 灌入,计时≤ 60 s
TC-N-06G5同一包分别经 USB bulk / 串口 921600 / MSC 灌入USB ≤90 s;串口 ≤240 s;MSC ≤60 s。四通道分别计,不合并
TC-N-07G6拔网线 / 关 Wi-Fi,重跑 TC-C / TC-F / TC-S3 各核心用例全部通过;本地灌库、检索、回写、接力均可用;联网后复测全过
TC-N-08G7TS-2 台架:随机掉电 100 次(覆盖:空闲 / 灌库中 / 提交中 / compact 中 / 接力中 五种状态各 20 次)100% 库可打开且索引与数据一致 —— 一票否决。任一次失败即判定 MVP 失败,回退评估
TC-N-09G8解析 .map,统计 libedgehippom.a 的 .text≤ 60 KB
TC-N-10G81,000 次「begin → put×N → abort」,记录耗时abort P99 ≤ 2 × P50
TC-N-11G8PC 仿真(TS-3):在写入第 N 字节后精确中断,遍历 N所有中断点恢复后数据一致;无半成品记录
TC-N-12长稳7×24 h 连续运行(采集 + 入库 + 查询并发)无自发重启;无内存泄漏;heap 水位无单调下降。若重启先查栈溢出(抓 Rebooting 前 30 行)
TC-N-08 是全部 96 条用例里唯一的一票否决项。
它同时是本产品否决所有竞品的唯一硬指标——云端记忆引擎(红熊笔记熊、mem0、Zep、Letta)根本无法讨论「掉电后库是否可开」,因为它们的库不在设备上。

7. TC-M 网络(12 条)

ID步骤预期
TC-M-01两板上电,观察 link.scan30 s 内互相可见;能力通告含 frags 摘要与 bearers
TC-M-02关闭一台设备,等待120 s 后从邻居表消失(TTL 生效)
TC-M-03M1 完整六原语时序:query→bid→request→result×N→award跑通;碎片入库;账本双方各记一条
TC-M-04大碎片分块传输(≥100 块)每块 CRC32 校验通过;无错位
TC-M-05传输到第 50 块时中断 → 重连 → 续传从第 50 块继续,不从头;最终完整
TC-M-06M2:源设备离线,Hub 代持其碎片第三方可从 Hub 取得;Hub 账本记双份(给 owner + 给 Hub)
TC-M-07M2 下 owner 重新上线三方账本对账一致;无重复记账
TC-M-08M3:USB MSC 拷入 .emf + .emr≤60 s;可用;回执字段完整
TC-M-09M3 后联网对账账本并集去重,误差 = 0
TC-M-10mesh.ledger 双方导出比对双向条目一致,误差 = 0(G9)
TC-M-11接力中途失败(断链 / CRC 错 ×3 / 配额不足)abort 且已有库完好如初;返回明确错误码
TC-M-12配额耗尽后继续请求返回 ERR_QUOTA_EXCEEDED;不得降级为「先给数据后补账」

8. TC-W Web 控制台(28 条)

8.1 连接与降级(TC-W-01…08)

ID步骤预期
TC-W-01页面(http://localhost:8080)fetch 板子 /api/status成功(CORS + OPTIONS 预检生效)。这是地基,不通则后面全不成立
TC-W-02https 页面请求 http 板子复现 mixed content 拦截 → 文档与启动脚本须明确禁止
TC-W-03WebUSB 连接 Dev-B枚举成功;设备卡片显示 ID / 形态(form)/ 容量 / 固件版本(不出现角色字段,ADR-018)
TC-W-04WebUSB 不可用 → 自动降级 Web Serial降级提示清晰;用户可手动选端口
TC-W-05Wi-Fi 通道连接同网段板子成功;/api/store/map 数据正确渲染
TC-W-06连接中拔掉 USB / 断开 Wi-Fi进入 LOST;明确区分「设备掉线」与「灌库失败」
TC-W-07恢复连接自动或一键重连成功;状态回到 READY
TC-W-08设备变砖 → esptool-js 救砖可进入 ROM bootloader 并刷回。救砖通道必须可用

8.2 向导与 Prompt(TC-W-09…16)

ID步骤预期
TC-W-09五步向导逐步填写状态机正确;不可跳步;六项产出收集齐才可进第 5 步
TC-W-10任一必填项留空 → 下一步被拦截并给出明确提示
TC-W-11P0 变量替换({{cap_kb}} 等)全部替换;无残留 {{ }}
TC-W-12P0 输出 JSON 解析六个键齐全;容量约束被遵守
TC-W-13P2 生成条目的 text 字节数全部 ≤150 字节;超长条目被重新生成而非截断
TC-W-14P3 去重判据余弦 >0.95 → MERGE;<0.85 → KEEP_BOTH;中间档给出理由
TC-W-15API Key:留空提交 / 输入新值 / 输入 __clear__分别:保留旧值 / 更新 / 清除;全程不回显明文
TC-W-16P5 断网话术生成三类模板齐全;中文 ≤40 字;承认不知道

8.3 .emf 打包(TC-W-17…22)

ID步骤预期
TC-W-17打包 1,000 条 → 检查 32 B 头magic="EMKBIMG1";version / vec_dim / rec_count / payload_len 正确
TC-W-18校验 payload_crc32与独立计算一致
TC-W-19向量是否在记录末尾解析记录:text→meta→vec;vec 必须是最后一段(ADR-010)
TC-W-20P4 的 20 条验收用例是否入包存在特殊 frag_id=0xFFFFFFFF 元数据块;frag.verify 可读取
TC-W-21M3 回执 .emr 生成字段完整(from/frag_id/bytes/quota/crc32)
TC-W-22embedding 调用是否经过我方服务器抓包验证:直连厂商 API,无任何我方域名(ADR-008)

8.4 烧录(TC-W-23…28)

ID步骤预期
TC-W-23HTTP 烧录 12,000 条≤60 s(G5);进度条连续
TC-W-24USB bulk 烧录同一包≤90 s
TC-W-25串口 921600 烧录同一包≤240 s;进度条必须清晰(这是串口通路被保留的前提)
TC-W-26烧录到 50% 时拔线 → 重连 → 续传从断点继续;最终完整;已有库在失败期间完好
TC-W-27灌入错误包被拒绝;已有库完好如初;UI 明确提示「已自动回滚,您的记忆不会丢」
TC-W-28固件 OTA 后检查知识库OTA 只写 app,已灌记忆完好;版本号 sys.info 已更新

9. 自动化与 CI

层次范围触发说明
L1 PC 单测引擎层全部(em_flash_io 文件仿真)每次 commit最快反馈。含 TC-N-11 精确断电遍历
L2 构建门禁两板构建 + 槽位硬门槛(W0-06)+ .text 体积检查每次 commit占用 >90% 构建失败
L3 板级冒烟TC-C-01…08 + TC-F-01/04每日需 TS-1 台架在线
L4 全量回归96 条中可自动化的部分(约 70%)每周 + 里程碑前掉电/长稳/断网需人工台架
L5 人工TC-N-08 掉电、TC-N-12 长稳、TC-W 主观体验里程碑—

不可自动化的 22 条:TC-N-08(需物理台架)、TC-N-12(7×24 h)、TC-S2-*(断网操作)、TC-W 主观体验部分。

10. 缺陷分级与放行规则

级别定义响应放行
S1
致命
库损坏 / 掉电不可恢复 / 数据静默丢失 / 合规越线立即停线0 容忍
S2
严重
判据未达标(G1–G6、G8、G9)/ 主要功能不可用 / 接力失败破坏已有库24 h 内修复可放行但必须记录并给出时间表
S3
一般
次要功能异常、性能略低于目标但不影响可用性排入迭代可放行
S4
提示
文案、样式、体验建议择机可放行

10.1 里程碑放行规则

  1. S1 缺陷 = 0;S2 缺陷 ≤ 3 且均有明确修复时间表
  2. G1–G5 全部达标(可行性门槛)
  3. G7 必须 100% 通过(一票否决)
  4. G6 / G8 / G9 若有未达标项,须在验收报告中明确标注「已知限制」,且不得进入对外材料
  5. 对外材料中的任何容量 / 延迟数字,必须来自本轮实测报告,不得使用推算值

11. 验收报告模板

EdgeMemory-MVP 验收报告  [里程碑 M_]

一、结论
   □ Go —— 进入下一阶段
   □ No-Go —— 回退评估(说明触发的判据)
   □ 有条件 Go —— 列出挂起项与时间表

二、判据达成情况
   判据   目标值        实测值        用例        结论
   G1     ≥12,000 条    _______      TC-N-01     □达标 □未达
   G2     P50<10/P99<30 _______      TC-N-02     □达标 □未达
   G3     ≤5MB/≤180KB   _______      TC-N-03/04  □达标 □未达
   G4     闭环可命中     _______      TC-S1-07    □达标 □未达
   G5     四通道耗时     _______      TC-N-05/06  □达标 □未达
   G6     断网全通过     _______      TC-N-07     □达标 □未达
   G7     掉电 100 次    _______/100  TC-N-08     □达标 □未达  ← 一票否决
   G8     emlite 三项    _______      TC-N-09/10  □达标 □未达
   G9     账本误差 0     _______      TC-M-10     □达标 □未达

三、用例统计
   总数 92|通过 __|失败 __|阻塞 __|未执行 __
   S1 缺陷 __(必须为 0)|S2 __|S3 __|S4 __

四、实测 vs 推算对照(用于更新文档)
   项目            推算值        实测值        是否需修订文档
   单条有效成本     930 B        ______       □是 □否
   Dev-B 容量       12,900       ______       □是 □否
   ...

五、已知限制(不得进入对外材料)
   1. …
   2. …

六、签字
   PM ____  FW-A ____  FW-B ____  TOOL ____  WEB ____  QA ____

12. 定向回归 · W0-05 通用层改动(状态:全部「待跑」)

为什么单独立这一节。 2026-10-28 的 α 分支改动(`main/Kconfig.projbuild` 的 `EMGW_FORM_ALPHA_MODULE` 第三选项、 新增 `partitions_emgw_4m_single.csv`、`emgw.h` / `led_mgr.c` 的 `#elif` 分支)经审查是干净的: 不选中即为死代码,对 β/γ 既有路径零影响。
但同一时间窗里混进了三项与 α 无关、且不在任何 `#ifdef` 保护下的通用层改动(见下表)。 它们属于这一轮 EdgeMemory 引擎接线的正常工作,不是 α 的账; 但按项目铁律「通用层改动必须两板回归」,必须补测。

⚠ 本节 3 条用例一条都还没跑过,状态一律「待跑」。 谁跑完谁改状态(team-lead 代改亦可);交出来时不得写「已验证」。
编号关联判据被测改动平台状态
TC-R-01G3 内存app_main.c 新增 emlite_init_task(栈 8192 B)+ 双模 BLE + telem_startDev-A 优先,Dev-B 同测待跑
TC-R-02启动时序同上;Dev-A 冷启动改走 BLE autoconnect + 15 s 遥测Dev-A 优先,Dev-B 同测待跑
TC-R-03kb 数据契约link_mgr.c 落盘 tags src= 由写死 edge 改为 emgw_form_str()Dev-A + Dev-B 互联待跑

TC-R-01 · G3 内存余量(新增 emlite 开库任务后)

项内容
目的确认 W0-05 后新增的 emlite 开库任务与遥测任务,未把峰值内存推过 G3 门槛
改动来源app_main.c:xTaskCreate(emlite_init_task, "emlite_init", 8192, NULL, 5, NULL);link_mgr 双模 BLE;telem_start()(周期 15 s)
步骤① 刷入当前 xiao(gamma-card)固件,整片擦除后冷启动;② 等日志出现「emlite 引擎就绪」;③ 读取 sys.info / store.map,记录 PSRAM 占用与 DRAM 峰值;④ 重复 3 次取最大值
期望PSRAM ≤ 5 MB;内部 DRAM 峰值 ≤ 180 KB;运行期 malloc = 0
关联G3;07-实测手册 §6 硬指标
状态待跑 —— 未执行,无任何证据

TC-R-02 · 启动时序(Dev-A 冷启动 3/3)

项内容
目的确认 Dev-A 新增的 BLE 自动重连与 15 s 遥测上报,未拖垮冷启动成功率;且引擎开库不阻塞其它模块
★ precondition
(必须显式记录)
刷了新固件的板,NVS 键由 "role" 改为 "form",旧值不再被读取 ⇒ 形态一定取编译期默认值(EMGW_FORM_DEFAULT_STR)。若需覆盖,必须显式执行 sys.set_form。 据此,本用例执行前必须先确认被测板的 sys.form 实际返回值,否则后续结论不成立。
步骤① 记录当前 sys.form;② 连续冷启动 3 次;③ 每次记录 NVS / transport / store / wifi / cli / ble / link / telem / emlite 各模块是否全部成功;④ 记录从复位到「emlite 引擎就绪」的耗时
期望冷启动成功率 3/3;emlite 首次开库(含整片擦除)不阻塞日志/网络/互联起来(这是把它独立成任务的初衷)
状态待跑 —— 未执行,无任何证据

TC-R-03 · kb 落盘 `src=` 标签一致性

项内容
目的确认落盘 tags 的 src= 字段与全仓断言一致,避免静默失配 → 假绿
改动来源link_mgr.c L773-777:em_kb_add(rec_id, "tier=dynamic;src=edge;type=telemetry", ...) → snprintf(src_tag, "tier=dynamic;src=%s;type=telemetry", emgw_form_str())
⚠ 风险说明查 src=edge 的断言在改成 src=gamma-card / src=usb-dongle 之后会变成「查不到」, 而「查不到」在不少判据里被当作「拒答正确」→ 假绿。这比直接报错危险得多,因此单独立用例。
步骤① Dev-A 与 Dev-B 配对并互联;② 触发一次遥测上报并落库;③ 执行 em.kb.list;④ 检查返回的 tags 中 src= 取值
期望Dev-A 落盘记录 tags 含 src=gamma-card;Dev-B 落盘记录 tags 含 src=usb-dongle;不再出现 src=edge
状态待跑 —— 未执行,无任何证据
口径纪律(凡本节及全仓引用条数处,必须同时满足):
  1. 凡出现条数,必须同时标出记录尺寸 s。s=44 与 s=644 相差 14.6 倍,脱离 s 的条数是无效数字。
  2. 权威取值(几何推算,★未经实测★):C = rec_pages × 4080 B/页; Dev-B(16MB / 3,040 页)rec_pages = 2,683 → C = 10,946,640 B,s=644 稳态约 14,448 条; Dev-A(8MB / 992 页)rec_pages = 635 → C = 2,590,800 B,s=644 稳态约 3,419 条。
  3. 前提必须一起写:实际烧录的分区表 == 仓库 partitions_emgw.csv;分区表一换,C 必须重算。
  4. 权威值待补:需「清库 → 固定尺寸包灌满 → 清库」的独占时段实测。在此之前任何条数必须带「推算」字样,禁止写成实测值。