Files

1018 lines
157 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Dada P0-A 测试驱动开发计划
| 文档项 | 内容 |
| --- | --- |
| 文档版本 | 1.4 |
| 编写日期 | 2026-07-27 |
| 适用产品版本 | PRD v1.10,P0-A 本机测试需求冻结稿 |
| 功能索引版本 | FeatureSummary 1.1 |
| 工程计划版本 | DevelopmentPlan 1.1 |
| UI 设计版本 | UIDesign 1.0 |
| 当前交付 | 仅 P0-A Windows 本机测试版 |
| 产品需求唯一权威 | `PRD.md` |
| 文档职责 | 固定测试驱动节奏、测试层级、fixture、工作包门禁、AC 追踪、证据和发布验收方法 |
| 当前状态 | 可用于开发的候选测试基线;`RELEASE.json` 尚未生成,不能据此宣告发布验收完成 |
## 1. 文档定位与冻结输入
### 1.1 权威顺序
1. `PRD.md` v1.10 是产品范围、状态、权限、业务规则、数据语义和验收结果的唯一权威。
2. `FeatureSummary.md` 1.1 只用于定位十三个功能模块、关键状态、十九个数据契约和 AC,不产生测试新需求。
3. `DevelopmentPlan.md` 1.1 固定技术栈、OpenAPI、SQLite、事务、存储、进程、工作包及测试层级;本文件不得改写这些工程决策。
4. `UIDesign.md` 1.0 与低保真资产用于页面、控件、状态、文案、焦点、布局和界面验收;它们不得覆盖 PRD 行为或 DevelopmentPlan 接口。
5. 本文件只定义如何先写失败测试、如何得到最小通过实现、如何回归和留证;不修改产品 API、Schema、状态、稳定错误码或 `message_key`
若本文件与上游冲突,停止受影响测试和实现,以 PRD 为产品权威重新判断,并按依赖顺序同步 DevelopmentPlan、UIDesign 和本文件。不得只替换哈希后沿用旧结论。
### 1.2 输入哈希与失效作用域
#### 1.2.1 全局冻结来源
| 输入 | SHA-256 |
| --- | --- |
| `PRD.md` | `31F93674DF1A90B557FEE3AA9E74FB084E246CA8FE09F6BD4B1DC9D56D606565` |
| `FeatureSummary.md` | `6F80E272AAB08A5525B54501D83F16A4F6A7A170596947BBC25F1DA54F2FE844` |
| `DevelopmentPlan.md` | `7C579E9DF8D00DA9333F587D05FC966123437EE68575D87D73C903C1487FA9D6` |
| `UIDesign.md` | `0C449DF833E5471843E17EB74F5B470EBC955F117CAF1D79AC9C0EF90DE763F0` |
上述四份 Markdown 是所有测试层的全局来源基线。每次测试运行先校验四项哈希;任一不一致时:
1. 整次运行标记为 `source_changed`,不得继续生成通过证据。
2. 完整重读发生变化的权威来源及全部受影响下游来源。
3. 更新需求、UI、fixture、测试和 AC 映射后重新建立失败基线。
4. 不把变更前的截图、性能数据、外部契约记录或人工确认复用为变更后的通过证据。
#### 1.2.2 UI 参考资产
| 输入 | SHA-256 |
| --- | --- |
| `Dada-P0A-LowFi.pen` | `A352C8F205E9399EB2E6306176253AF6FF65DDDBB9FDCEA2E2FE13E3C3459975` |
低保真预览图清单:
| 文件 | SHA-256 |
| --- | --- |
| `Dada-LowFi-Preview/awt96.png` | `9E0392DBEA2407BC0602E62EDEE755DF85BE085B0A6CD328C69DA8C79832AB2C` |
| `Dada-LowFi-Preview/bHQnJ.png` | `FA9F1CFC664F90BB8CD5DCFE985DA9A1280B3F3EF3C971F9480CB226A6D6DC88` |
| `Dada-LowFi-Preview/d99Lo.png` | `2ADC0DE944290E2BDFFF91083F0A3AB5BBEB0C3E294E0DC39CAE55763EFCF0C0` |
| `Dada-LowFi-Preview/DVPM8.png` | `9B1E7237857089CB3185EC4D3BBC2B3C9293267D7026A4F411A2131175FDEE90` |
| `Dada-LowFi-Preview/e4e2Kz.png` | `9D528A72A4902934B77421FB53EF35457B62AEA0477155BA8664B38DF0833232` |
| `Dada-LowFi-Preview/WbDYT.png` | `703A3D371F716C808CA158700D380B7CAFA25D89A7672B7199362A8A65E8EB76` |
| `Dada-LowFi-Preview/Z0pf8.png` | `ED213DFD7022BC355C7E243CD27484BFB25791DF2C38ED1A498175C563E17B9B` |
| `Dada-LowFi-Preview/Z8p3I.png` | `4714CA2C6D4CC00395B81B175389CF04DDE9EB0C71876BDBE72060DC741A1035` |
`.pen` 和预览 PNG 不是产品需求来源,只约束第 11 节映射到的 UI、视觉和 E2E 证据。相关 job 运行前校验这些哈希;某张 PNG 变化时只把对应页面/状态的 UI、视觉和 E2E case 标记为 `source_changed``.pen` 变化时先比较受影响 frame,只失效对应页面证据;若无法确定受影响 frame,则失效全部 UI、视觉和 E2E 证据。UNIT、DB、API、WRK、EXT-MOCK、EXT-REAL 和不消费布局的 PKG-SEC 证据不得仅因低保真资产变化而失效。所有失效项都必须重新读取 UIDesign 与变更资产、更新映射并重建视觉基线,不能只替换哈希。
### 1.3 当前范围
P0-A 只在项目方当前 Windows 电脑上运行。Fastify 只监听 `127.0.0.1:43121`,浏览器只从 localhost 访问本地后端,业务数据只写入 `LocalDataRoot`,约 3.57 GB 规范素材从安全配置引用的原目录只读使用。本地后端访问 AI 网关、Resend 和高德时仍使用外网 HTTPS。
当前测试不得实现或暗示手机、局域网、远程访问、跨设备继续处理、公司服务器、对象存储、正式域名、产品 HTTPS、云备份、项目归档或 P0-A 数据迁移。AC-08、AC-26、AC-37、AC-54 是未来 P0-B 门槛,不进入当前完成统计;AC-15、AC-23、AC-36、AC-38 只执行 PRD 明确的 P0-A 子集。
当前 AC 总数固定为 52
- P0-A 必验 48 项:AC-01 至 AC-07、AC-09 至 AC-14、AC-16 至 AC-22、AC-24 至 AC-25、AC-27 至 AC-35、AC-39 至 AC-53、AC-55 至 AC-56。
- P0-A 子集 4 项:AC-15、AC-23、AC-36、AC-38。
- 未来且不计入当前完成 4 项:AC-08、AC-26、AC-37、AC-54。
## 2. TDD 执行协议
### 2.1 Red-Green-Refactor
每个能力和缺陷都按以下顺序执行,不能先实现后补测试:
1. **Red**:先写一个只因目标行为尚未实现而失败的最小测试。记录测试 ID、权威需求、fixture、预期失败原因和所有禁止副作用。
2. **确认 Red**:在隔离环境运行该测试,确认失败信息指向目标缺口,而不是环境、随机数据、未安装依赖或其他已知失败。
3. **Green**:只实现使该测试通过的最小产品行为;不得借机增加 PRD 未定义的入口、状态、回退或配置。
4. **回归 Green**:运行同层测试、所属工作包测试和跨工作包依赖测试。金融、文件、权限、审计和外部调用必须同时断言状态与无副作用。
5. **Refactor**:在测试持续通过的前提下消除重复、改善结构;不得改变 OpenAPI、Schema、事务边界和用户行为。
6. **Evidence**:保存脱敏证据清单。没有 Red 证据、最终 Green 结果或必要人工证据的场景不能标记完成。
测试状态只允许:
| 状态 | 含义 |
| --- | --- |
| `not_started` | 尚未建立失败测试 |
| `red` | 已确认测试因目标缺口失败 |
| `green` | 自动断言通过,但尚可能等待工作包或发布门禁 |
| `externally_blocked` | 本地模拟通过,但等待真实 AI 网关、Resend、高德、浏览器或发布环境证据 |
| `passed` | 自动、人工、外部及证据门槛全部满足 |
| `source_changed` | 全局来源或作用域内 UI 参考变化,受影响结果按第 1.2 节失效 |
不设置 `skipped_as_passed``expected_failure_as_passed` 或无依据豁免。临时跳过只能保持未完成并写明原因。
### 2.2 测试 ID 与用例记录
测试族 ID 使用 `TDD-WP<0-7>-<AREA>-<NNN>`,例如 `TDD-WP0-STO-002`。一个测试族可以包含参数化子用例,子用例追加稳定后缀,例如 `TDD-WP0-STO-002-equal-limit`
每个可执行用例必须记录:
| 字段 | 要求 |
| --- | --- |
| `test_id` | 全局唯一且提交后不复用 |
| `work_package` | 唯一主责 WP;交叉依赖另列 supporting WP |
| `requirements` | 一个或多个 PRD 需求 ID、数据契约或 NFR |
| `acceptance_criteria` | AC 编号及 P0-A 子集标记 |
| `layer` | 本文第 3 节固定层级 |
| `fixture_ids` | 本文第 5 节稳定 fixture |
| `red_reason` | 实现前预期失败的唯一原因 |
| `green_assertions` | 状态、响应、持久化、UI 和禁止副作用 |
| `automation` | 按第 2.2.1 节生成的非空有序数组,元素只允许 `automated``controlled_real``manual_review` |
| `evidence_refs` | 机器可读结果、截图、性能、日志或人工记录 |
| `release_gate` | 按第 2.2.1 节生成的非空门禁数组,明确阻止的工作包和 P0-A 发布 |
#### 2.2.1 测试族执行属性
本文实际定义 89 个测试族;AREA 允许大写字母和数字,因此 `TDD-WP4-A11Y-001` 必须计入。`pnpm validate:tdd-trace` 生成 `artifacts/tdd/<run_id>/family-manifest.json`,逐族按下列规则确定执行属性,不能依赖人工补默认值:
| 字段 | 确定规则 |
| --- | --- |
| `red_reason` | 精确继承父测试族“首个 Red”;case 可追加更窄触发条件,但不得删除或弱化父断言 |
| `automation` | 非空有序数组:父层级含 UNIT/DB/API/WRK/EXT-MOCK/E2E/PKG-SEC/VIS-PERF 时含 `automated`;含 EXT-REAL 时含 `controlled_real`;含 MANUAL 时含 `manual_review`;固定按此顺序去重 |
| `release_gate` | WP-0 至 WP-6 固定为 `["work_package:WP-n","release:P0-A"]`WP-7 固定为 `["release:P0-A"]` |
| `work_package` | 从测试族 ID 的 `WP<n>` 唯一解析;case 不得覆盖 |
| `layer` | 精确继承父测试族“层级”列;case 结果必须逐层列出执行状态,不得以 E2E 代替 DB/API 或以 mock 代替真实门禁 |
因此第 2.2 节 `automation``release_gate``result.json` 中使用上述数组值。任一父族缺“首个 Red”或“层级”、属性数组为空、case 指向未知父族或人工改写派生值时,追踪校验失败。
### 2.3 测试脚本接口
后续工程必须提供以下仓库级脚本;脚本名称属于开发接口,不是产品 API:
| 命令 | 作用 |
| --- | --- |
| `pnpm test:unit` | TypeBox、纯函数、状态转换和前端域控制器 |
| `pnpm test:integration` | SQLite、临时 LocalDataRoot、文件一致性和服务事务 |
| `pnpm test:api` | Fastify inject、OpenAPI、鉴权、错误信封和资源路由 |
| `pnpm test:worker` | 队列、租约、崩溃、结算、清理和重计量 |
| `pnpm test:e2e` | Playwright Chrome/Edge 独立 profile 的产品流程 |
| `pnpm test:visual` | 固定 Canvas 与页面截图、Chrome/Edge 差异和人工复核包 |
| `pnpm test:performance` | 50 元素、自动保存、打开编辑器和导出预算 |
| `pnpm test:security` | 凭据、日志、制品、路径、缓存和私有内容泄漏扫描 |
| `pnpm test:package` | 便携包、随包 Node/native 模块、Supervisor 和固定端口边界 |
| `pnpm validate:tdd-trace` | 校验当前 AC 唯一主责、P0-B 排除项、PRD 需求 ID、UIDesign 页面/状态覆盖和 case 必填字段,防止追踪矩阵手工漂移 |
| `pnpm test:all` | 除真实外部调用外的全部阻断测试 |
| `pnpm validate:external -- --service <service> [--model <id>]` | 显式受控真实验证;普通 CI 禁止运行 |
任一脚本失败必须返回非零退出码。测试脚本不得读取真实 P0-A `LocalDataRoot`、日常浏览器 profile 或未显式授权的 Windows 凭据。
### 2.4 证据清单
每次运行写入 CI 或验收工作目录下的 `artifacts/tdd/<run_id>/evidence.json`。该目录不是产品数据目录,不提交真实私有内容。清单至少包含:
```json
{
"schema_version": 1,
"run_id": "opaque-run-id",
"commit": "git-commit",
"source_hashes": {},
"ui_reference_hashes": {},
"environment": {},
"release_record_hash": null,
"tests": [
{
"test_id": "TDD-WP0-BRW-001-real-supported",
"parent_family": "TDD-WP0-BRW-001",
"work_package": "WP-0",
"requirements": ["NFR-01"],
"acceptance_criteria": ["AC-24"],
"layer": ["E2E", "MANUAL"],
"fixture_ids": ["final-release-browser-fixture"],
"red_reason": "真实支持 Chrome/Edge 无法通过门禁",
"green_assertions": ["release-recorded full version passes"],
"automation": ["automated", "manual_review"],
"release_gate": ["work_package:WP-0", "release:P0-A"],
"status": "green",
"started_at": "ISO-8601",
"finished_at": "ISO-8601",
"evidence_refs": []
}
],
"redaction_scan": "passed"
}
```
`evidence.json`、截图、trace、日志、OpenAPI 示例和 fixture 均不得包含真实密钥、验证码、会话令牌、管理员真实邮箱、完整私有提示词、真实用户图片、Windows 用户名、绝对本机路径或供应商原始错误正文。扫描失败时整次运行失败。
## 3. 固定测试层级
| 层级 | 标识 | 基础设施 | 主要职责 |
| --- | --- | --- | --- |
| Schema/单元 | `UNIT` | Vitest、TypeBox fixture、可控时钟 | 枚举、Schema、格式化、版本、状态转换、路径、容量计算、色卡确定性 |
| SQLite/文件集成 | `DB` | 每测试临时 SQLite、临时 LocalDataRoot、受控文件驱动 | 事务、索引、幂等、审计、引用、预留、物理清理和恢复 |
| API 契约 | `API` | Fastify inject、OpenAPI snapshot、原始 HTTP 客户端 | 路由、错误信封、门禁、鉴权、CSRF、If-Match、multipart、资源隔离 |
| Worker 恢复 | `WRK` | 模拟网关、进程终止 harness、可控租约时钟 | 领取、心跳、恢复、结算一次、成本核对、清理重放 |
| 外部模拟 | `EXT-MOCK` | 三模型、Resend、高德的确定 fixture | 错误、配额、同步/异步和恢复状态;普通 CI 不访问外网 |
| 外部真实 | `EXT-REAL` | 显式命令、Windows 凭据管理器、受控账号 | 三模型独立契约、邮件送达、高德接口和发布门槛 |
| 浏览器流程 | `E2E` | Playwright Chrome/Edge channel、独立 user-data-dir | 认证、项目、编辑、导出、多标签、后台、缓存、权限和门禁 |
| 安全/打包 | `PKG-SEC` | .NET/Node harness、进程检查、ZIP/日志/环境扫描 | 凭据传递、固定端口、便携包、诊断脱敏和零泄漏 |
| 视觉/性能 | `VIS-PERF` | 固定 Canvas、截图 diff、PerformanceObserver | NFR-02、NFR-03、1080p、200% 缩放、50 元素和导出 |
| 人工发布 | `MANUAL` | 真实便携包、真实外部依赖、固定验收账号 | 自动化不能替代的视觉、送达、替代资源和发布确认 |
低层测试发现的数据、状态和权限错误不能只由 E2E 覆盖;E2E 负责证明用户流程接线正确。真实外部测试不能替代模拟失败分支,模拟测试也不能替代真实发布门槛。
### 3.1 OpenAPI 与事件分组覆盖
| 分组 | 主测试族 | 必验契约 |
| --- | --- | --- |
| 浏览器支持 | `TDD-WP0-BRW-001``TDD-WP0-BRW-002` | support check、426、最小字段、产品路由前置门禁 |
| Bootstrap | `TDD-WP0-API-001``TDD-WP3-MDL-003` | app、公开功能、模型配置/runtime/推荐和非敏感依赖摘要 |
| 普通认证 | `TDD-WP1-AUTH-*` | 注册/登录分离、挑战、会话、限流和稳定错误 |
| 管理员认证 | `TDD-WP1-ADM-001``TDD-WP1-CFG-001` | 白名单前置校验、独立 audience、告知版本摘要 |
| 账号 | `TDD-WP1-DEL-001``TDD-WP2-CRED-*` | 资料、点数、流水和不可逆注销 |
| 模型 | `TDD-WP3-MDL-*` | 配置集合、版本、runtime availability 和派生推荐 |
| 生成 | `TDD-WP2-GEN-*``TDD-WP2-REF-001` | multipart、配置复核、单任务、参考图和任务真相 |
| 项目 | `TDD-WP2-PROJ-*` | 所有者、If-Match、完整 JSON、回收站和 purge |
| 历史与成品 | `TDD-WP2-EXP-*``TDD-WP4-EXP-001` | 原图、历史、每格式 latest 与客户端下载分离 |
| 三类素材 | `TDD-WP5-RES-001``TDD-WP5-PREV-001` | public/preview/private 分路由、鉴权和缓存 |
| 贴纸历史清理 | `TDD-WP5-CLN-001` | 候选、intent、确认、整批复核、异步结果 |
| 私有告知与访问 | `TDD-WP6-PRIV-*` | 身份+版本 ack、服务端区域门禁、先审计后读取 |
| 管理后台 | `TDD-WP6-ADM-001``TDD-WP6-AUD-001` | active super_admin、事务内审计和安全摘要 |
| 状态事件 | `TDD-WP0-EVT-001` | 仅提示 ID/类型/版本/时间;断线或缺号后 REST refetch |
所有 `/api/v1` JSON、multipart、SSE 和错误响应都由 TypeBox/OpenAPI 3.1 的同一 Schema 来源验证。前端生成客户端 snapshot 漂移、未声明枚举、错误 HTTP 状态或错误信封字段时,API 契约测试失败。
## 4. 候选环境与发布冻结
### 4.1 当前候选基线
| 项目 | 候选值 | 用途 |
| --- | --- | --- |
| 操作系统 | Microsoft Windows 11 家庭版 中文版,25H2`10.0.26200`build `26200` | 开发视觉、性能、打包和辅助界面候选基线 |
| Chrome | `150.0.7871.184`major `150` | 当前真实 Chrome channel 候选基线 |
| Edge | `150.0.4078.99`major `150` | 当前真实 Edge channel 候选基线 |
| 产品视口 | `1920 x 1080` CSS px | UIDesign 首要桌面验收视口 |
| 截图 DPR | `1` | Playwright 确定性页面与 Canvas 基线 |
| 默认缩放 | `100%` | 正常布局、视觉和性能 |
| 可访问性缩放 | `200%` | 文字覆盖、弹层、主动作和滚动可达性 |
| 语言 | `zh-CN` | 固定简体中文文案和排序 |
| 时区 | `Asia/Shanghai` | 界面时间显示;持久化仍为 UTC 毫秒 |
上述浏览器必须使用本机真实 Chrome/Edge channel。Playwright 自带 Chromium 只能用于开发诊断,不能作为 AC-24、AC-32 或发布通过证据。伪造 User-Agent 或 UA-CH 可以证明拒绝分支,但不能证明支持分支。
本节具体版本号只用于当前候选视觉、性能和打包基线,不是产品支持范围本身,也不得写死进产品逻辑;产品运行支持口径以最终 `RELEASE.json` 和浏览器门禁实现为准。
### 4.2 `RELEASE.json` 缺失门禁
当前目录没有 `RELEASE.json`,因此本节环境只能标为 candidate。WP-7 必须:
1. 从实际发布包记录 Windows build、Chrome/Edge 品牌、major、full version 和记录时间。
2. 在记录的两个真实 full version 上重新运行 AC-24、AC-32、全部 E2E、视觉、性能、下载、文件选择和 200% 缩放。
3. 对比本节候选值。任何不同都使候选截图和性能结果失去发布证据资格,但不自动否定与浏览器无关的单元/DB 测试。
4.`RELEASE.json` SHA-256 写入最终 `evidence.json`;缺失、版本不一致或哈希不一致时 WP-7 失败。
5. 运行时只接受 `RELEASE.json` 记录的 major,不联网查询“当前版本”,也不接受前一 major。
## 5. 固定 fixture
### 5.1 主体与认证
全部自动化邮箱使用 `.invalid`,不得使用项目方真实邮箱。
| fixture | 内容 |
| --- | --- |
| `FX-ACTOR-USER-A` | active 普通用户,10 available、0 reserved,拥有项目 A |
| `FX-ACTOR-USER-B` | 通过正常邀请、验证码和注册链临时创建的第二普通用户 |
| `FX-ACTOR-SUSPENDED` | suspended 普通用户,仍占普通账号名额 |
| `FX-ACTOR-DELETED` | 已注销主体,只保留允许的匿名事件 |
| `FX-ACTOR-ADMIN-A` | active super_admin,已在安全白名单 |
| `FX-ACTOR-ADMIN-B` | 第二个独立 active super_admin |
| `FX-ACTOR-DEPLOYER` | 无 UserProfile、无会话、无产品角色的配置维护主体 |
| `FX-INVITE-VALID` | 未过期、启用且有剩余次数 |
| `FX-INVITE-EXPIRED` | 已过期 |
| `FX-INVITE-DISABLED` | 已停用 |
| `FX-INVITE-EXHAUSTED` | 已达最大使用次数 |
| `FX-CHALLENGE` | 10 分钟一次性验证码、60 秒重发限制、带 pepper 摘要 |
跨普通用户隔离的发布验收不得直接插入 `FX-ACTOR-USER-B`。它必须在独立 Chrome/Edge profile 中使用真实可验收的邀请和验证码链创建,完成越权检查后正常注销,并等待内容进入规定清理路径。直接 DB fixture 只用于低层事务测试。
### 5.2 模型、任务与点数
| fixture | 内容 |
| --- | --- |
| `FX-MODEL-SEED` | 三模型 configured enabledFlash 唯一默认 priority 1Pro priority 2GPT priority 3;初始 contract unverified、runtime unavailable |
| `FX-MODEL-VERIFIED` | 三模型分别具有独立 verified 证据和可用 runtime 状态 |
| `FX-MODEL-NO-RUNTIME` | 配置不变,三个模型 runtime 均不可用于新任务 |
| `FX-MODEL-CONFLICT` | 过期 config_set_version、非法默认替代或重复优先级候选集合 |
| `FX-CREDIT-ENOUGH` | available 10、reserved 0、模型成本 1 |
| `FX-CREDIT-INSUFFICIENT` | available 0、reserved 0、模型成本 1 |
| `FX-CREDIT-NEGATIVE` | 仅由 admin_adjustment 产生 available -5reserved 保持不变 |
| `FX-JOB-ACTIVE` | 一个 queued 或 running 任务及一份 generation_reserve |
| `FX-JOB-TERMINAL` | succeeded、failed、rejected 的不可逆终态组合 |
| `FX-GATEWAY-SYNC` | 同步完成模型响应 |
| `FX-GATEWAY-ASYNC` | start 返回上游引用、poll 返回终态 |
| `FX-GATEWAY-UNKNOWN` | 已提交但无可安全查询引用;`GenerationJob.status=failed``error_category=unknown_retryable``upstream_cost_reconciliation=pending_manual_review``upstream_outcome_known=false`,冻结点释放一次 |
### 5.3 项目、参考图与画布
| fixture | 内容 |
| --- | --- |
| `FX-PROJECT-EMPTY` | failed_empty 草稿,无成功图 |
| `FX-PROJECT-HISTORY-9` | 9 张成功图,可继续生成 |
| `FX-PROJECT-HISTORY-10` | 10 张成功图,阻止追加 |
| `FX-PROJECT-ACTIVE-19` | 19 个 active 项目,可再创建一个 |
| `FX-PROJECT-ACTIVE-20` | 20 个 active 项目,阻止创建和恢复 |
| `FX-PROJECT-CONFLICT` | 两个标签基于同一 state_version 的不同内存状态 |
| `FX-REF-VALID` | 可解码的 JPG、PNG、WebP,分别满足模型限制 |
| `FX-REF-MALFORMED` | 扩展名与内容不符、损坏解码和错误 MIME |
| `FX-REF-OVERSIZE` | 超单文件、总大小或数量限制 |
| `FX-REF-NONSAFETY-INVALID` | 供应商明确判定不可用但不属于安全拒绝的参考图 |
| `FX-REF-SAFETY-REJECTED` | 供应商明确安全拒绝证据;不得与前一 fixture 共用模拟错误 |
| `FX-CANVAS-50` | 12 个文字、24 个普通贴纸、4 个色卡、10 个动态贴纸,共 50 个覆盖元素 |
| `FX-CANVAS-RATIOS` | 3:4、1:1、4:3、9:16 四个固定画布和标准像素 |
### 5.4 时间、位置与视觉
- 固定后端时钟为 `2026-07-27T04:00:00.000Z`,界面期望为 Asia/Shanghai 的 `2026-07-27 12:00:00`
- DYN004 使用确定测试坐标与固定地理编码响应;拒绝 Dada 自有提示时定位 API 调用次数必须为 0。
- 色卡背景使用固定解码像素和 `mmcq-v1`;测试裁剪、调色、滤镜不改变五色,更换原始底图会改变五色。
- 字体测试等待 `FontFace` ready;字体缺失使模板 unavailable。DYN012 只使用 FONT081 Lexend Deca,并始终标记替代字体。
- 动态值、动画时刻、随机 ID 生成器、列表排序和图片解码均固定 seed;视觉基线不得依赖当前墙钟或网络返回顺序。
### 5.5 存储边界
容量测试通过受控文件驱动、数据库计数和预留模拟精确字节,不实际创建 5 GB 文件。必须参数化以下值:
| 字节值 | 期望 |
| ---: | --- |
| `4,294,967,295` | normal |
| `4,294,967,296` | warning 起点 |
| `4,831,838,207` | warning 末值 |
| `4,831,838,208` | critical 起点 |
| `5,368,709,119` | critical 末值 |
| `5,368,709,120` | full;若由本次写入恰好达到,写入成功后进入 full |
| `5,368,709,121` | 超限请求拒绝;不得创建持久副作用 |
计量分类至少区分 reference、generated、export、derived、`sticker_original``sticker_thumbnail`。只读规范素材、应用代码、数据库、日志、审计、浏览器公开缓存和下载目录均不得计入。
## 6. 工作包 TDD 门禁
### 6.1 WP-0 基础工程与本机边界
| 测试族 | 首个 Red | Green 断言 | 层级 |
| --- | --- | --- | --- |
| `TDD-WP0-DEP-001` | 冻结依赖任一无法安装、加载或最小运行 | 精确版本、TypeScript、React/Fabric、Fastify、better-sqlite3 native 和随包 Node 全通过;不私自替换技术栈 | UNIT、PKG-SEC |
| `TDD-WP0-API-001` | TypeBox、OpenAPI、生成客户端或错误信封漂移 | OpenAPI 3.1 snapshot 与运行验证一致;响应有 correlation ID`details` 只含白名单字段且不泄露内部信息 | UNIT、API |
| `TDD-WP0-BND-001` | API 接受非回环绑定、错误 Host/Origin 或 CORS | 只绑定 `127.0.0.1:43121`;拒绝局域网、其他 Host/Origin,不创建防火墙入站规则 | API、PKG-SEC |
| `TDD-WP0-BRW-001` | 真实支持 Chrome/Edge 无法通过门禁 | `RELEASE.json` 记录的两个真实 full version 通过并建立短期支持 Cookie,产品 API 每次复核品牌/major | E2E、MANUAL |
| `TDD-WP0-BRW-002` | 其他品牌、错误 major、UA-CH 缺失/冲突或直调 API 可进入产品 | 全部返回 426 `BROWSER_UNSUPPORTED`,只有最小引导,无登录、资源、DB 业务读取或外部调用,无绕过令牌 | API、E2E |
| `TDD-WP0-SUP-001` | 第二实例、端口占用、API/Worker 崩溃或退出留下残留进程 | mutex、固定端口、有限重启、degraded 状态、15 秒优雅退出和托盘允许动作符合 DevelopmentPlan | PKG-SEC、MANUAL |
| `TDD-WP0-DATA-001` | LocalDataRoot 位于仓库/项目/下载/素材根,或丢失后自动重建 | 路径校验拒绝违规目录;已初始化根缺失进入 data_missing;浏览器只见资源 ID | DB、API、PKG-SEC |
| `TDD-WP0-DATA-002` | 产品出现自动备份、项目包、业务导入或 P0-B 迁移 | 只允许下载原始生成图和 JPG/PNG;删除根或重装不可恢复;固定本机风险文案持续显示 | API、E2E、MANUAL |
| `TDD-WP0-STO-001` | 任一阈值使用模糊比例或边界错一字节 | 第 5.5 节七个精确字节值产生固定 notice/status | UNIT、DB |
| `TDD-WP0-STO-002` | 写入预计量用 `>=` 拒绝等号,或超限后仍有副作用 | `>` 才拒绝;等于上限提交成功后 full;超限不创建任务、冻结、文件关系或上游调用 | UNIT、DB、API |
| `TDD-WP0-STO-003` | full/unavailable 动作混淆或清理排队即减字节 | full 允许 JSON 保存与客户端仅下载;unavailable 拒绝新增后端写;物理删除和重测前计量不变 | DB、API、WRK、E2E |
| `TDD-WP0-FILE-001` | 文件流、数据库事务或进程崩溃产生可读半文件、孤儿引用或计量漂移 | 预检、staging 流式哈希/MIME、同卷原子改名、SQLite 提交、补偿队列和启动对账全覆盖;路径规范化拒绝 `..`、符号链接逃逸和 manifest 外对象 | DB、API、WRK |
| `TDD-WP0-CACHE-001` | 应用缓存超过 157,286,400 字节或含私有/内部内容 | LRU 在写前淘汰;只含 allowlist 公开资源;逐场景枚举 SW、Cache Storage、IndexedDB | UNIT、E2E |
| `TDD-WP0-EVT-001` | SSE 携带私有正文、被客户端当作业务真相或事件缺号后不恢复 | 事件只含固定非敏感字段;客户端收到后 REST refetch;断线/缺号 bootstrap;配置和 runtime 版本分离 | API、E2E |
| `TDD-WP0-SEC-001` | 凭据进入参数、环境、文件、日志或错误 | Supervisor 通过继承匿名管道最小传递;API/Worker 分离;缺失、清除、重启和扫描全通过 | PKG-SEC |
| `TDD-WP0-LOG-001` | 日志无限增长、含敏感数据或目录不可写后仍调用 AI | 10 MiB/10 文件/30 天轮转;白名单字段;写失败进入 unavailable 并阻断 AI | DB、WRK、PKG-SEC |
| `TDD-WP0-PKG-001` | ZIP 缺组件、包含真实配置或动态换端口 | 便携布局、SHA-256、START-HERE、RELEASE 记录和固定端口全部正确,制品零凭据 | PKG-SEC |
WP-0 Green 不等于 AC-24 发布通过;真实浏览器记录必须在 WP-7 重新冻结。
### 6.2 WP-1 认证、权限、数据模型与审计基础
| 测试族 | 首个 Red | Green 断言 | 层级 |
| --- | --- | --- | --- |
| `TDD-WP1-AUTH-001` | 有效邀请注册任一步部分成功 | 发码前和建号前复核;账号、初始 10 点、registration_grant、告知版本和邀请码次数同事务一次提交 | DB、API、E2E |
| `TDD-WP1-AUTH-002` | 无效/过期/停用/耗尽邀请码仍发码或占用次数 | 不发码、不建号、不赠点,返回稳定原因且无副作用 | DB、API、E2E |
| `TDD-WP1-AUTH-003` | 注册/登录对未注册、已注册、已注销、suspended 邮箱静默切换流程 | 六种状态严格按 PRD 分离入口;已注销邮箱只能新邀请重建且旧数据不恢复 | API、E2E |
| `TDD-WP1-AUTH-004` | 验证码重用、过期仍有效或会话未及时撤销 | 10 分钟一次性、60 秒重发、30 天会话;退出/暂停/注销/白名单移除立即撤销 | DB、API |
| `TDD-WP1-NOTICE-001` | 未勾选、版本缺失或告知遗漏第三方/本机数据处理仍可注册 | 告知含版本和生效日期,覆盖 AI 网关、Resend、高德、本机存储与无备份、管理员访问、DYN004、注销和 180 天规则;未同意不建号,成功时记录版本/时间 | UNIT、API、E2E、MANUAL |
| `TDD-WP1-SLOT-001` | suspended 不占名额、super_admin 占名额或第 11 人可注册 | active+suspended 普通账号最多 10deleted 释放;super_admin 不占且不用邀请码 | DB、API、E2E |
| `TDD-WP1-ADM-001` | 白名单外发码、普通账号提升或全部管理员停用后自助恢复 | 后台独立 audience;多管理员可用;恢复仅安全配置;部署人员无产品身份 | DB、API、E2E |
| `TDD-WP1-CFG-001` | 安全配置修订部分生效或泄露白名单/HMAC | 完整验证后单一事务生效、撤销会话、system 审计和 revision 推进;失败整体回滚 | DB、PKG-SEC |
| `TDD-WP1-DEL-001` | 注销后仍可访问账号、项目、资源、坐标或会话 | 注销事务立即撤权和断链,物理内容异步清理,邮箱可作为全新账号重新注册 | DB、API、WRK、E2E |
| `TDD-WP1-DEL-002` | 匿名事件可回溯或注销延长访问审计期限 | 只保留允许字段和随机主体;匿名事件不晚于注销后 180 天;访问审计沿原 expires_at | DB、WRK |
| `TDD-WP1-AUD-001` | 普通连接可改删两类审计 | trigger 阻断 UPDATE/DELETE;仅保留期/隐私清理作用域可执行规定删除 | DB |
真实 Resend 认证在 WP-7 才能把相关 AC 从 green 提升为 passed;开发预置账号永远不能成为正式认证证据。
### 6.3 WP-2 项目、点数与 GenerationJob
| 测试族 | 首个 Red | Green 断言 | 层级 |
| --- | --- | --- | --- |
| `TDD-WP2-PROJ-001` | 新建/继续生成/历史上限混淆项目数量或比例 | 每次“新建创作”新项目;继续生成只追加;比例固定;10 张历史阻断追加但不阻断新项目 | DB、API、E2E |
| `TDD-WP2-PROJ-002` | 连续编辑并发保存、失败显示已保存或刷新恢复私有草稿 | 1 秒 debounce、单请求在途、最新快照合并;失败重试;强制退出只恢复 saved,撤销栈为空 | UNIT、API、E2E |
| `TDD-WP2-PROJ-003` | 回收站计时、20 项目恢复或 purge 可恢复边界错误 | purge_at 精确 +720 小时;满 20 保持 trashed;purged 立即不可访问并异步清理 | DB、API、WRK、E2E |
| `TDD-WP2-PROJ-004` | 旧标签覆盖、另存副本或多次冲突导出 | CAS 返回 412;旧页只读;仅一次本地导出且不写 latest;刷新后端最新状态 | API、E2E |
| `TDD-WP2-PROJ-005` | 重试新建项目或成功项目混入 failed_empty 批量删除 | 同一草稿每次重试只新增任务;仅无成功图草稿可批量入回收站 | DB、API、E2E |
| `TDD-WP2-CRED-001` | 点数不足、重复结算或生成流程制造负数 | 不足不建任务/流水/上游;reserve/commit/release 各一次;生成流程 available 不为负 | DB、API、WRK |
| `TDD-WP2-CRED-002` | 管理员调点修改 reserved 或缺原因/审计 | 只改 available,可为负;原因、流水和 AdminOperationLog 同事务 | DB、API、E2E |
| `TDD-WP2-GEN-001` | 并发提交创建两个任务、项目、参考关系或冻结 | `BEGIN IMMEDIATE`+部分唯一索引只保留一个 queued/running;后续返回当前任务 | DB、API |
| `TDD-WP2-GEN-002` | 成功/失败/拒绝与点数、项目历史或重试不一致 | 成功一图并 commit;失败/rejected 无输出并 release;重试同项目新任务 | DB、API、WRK、E2E |
| `TDD-WP2-GEN-003` | 旧 model_config_version 静默按新成本或限制提交 | 返回 412、最新摘要;不建任务、不上传引用、不冻结、不调用上游;重新确认后才提交 | API、E2E |
| `TDD-WP2-REF-001` | 未提交参考图被持久化或重试覆盖旧关系 | 提交前只在内存;每个任务保存独立关系快照;purged 后立即不可读并异步删除 | DB、API、E2E |
| `TDD-WP2-EXP-001` | 项目/成品保存失败撤回下载或覆盖旧 latest | 同一次 Blob 用于下载和上传;下载可成功、latest 保持旧值;刷新只恢复 saved | API、E2E |
| `TDD-WP2-EXP-002` | 从未导出项目自动生成默认成品 | 显示“暂无导出成品”,只提供进入编辑导出和下载原图 | API、E2E |
| `TDD-WP2-BAL-001` | 模型/账户/未知余额影响改写配置或重复释放 | 按影响范围只改 runtime;只 release 一次;未确认恢复前不可恢复 | DB、API、WRK |
| `TDD-WP2-ERR-001` | 九类错误被新增、归并、泄露原文或错误处理点数 | 三模型统一固定 category;提交前无任务,提交后只 release 一次;状态和用户动作符合第 9 节 | UNIT、API、WRK、E2E |
### 6.4 WP-3 三模型契约验证与适配
| 测试族 | 首个 Red | Green 断言 | 层级 |
| --- | --- | --- | --- |
| `TDD-WP3-MDL-001` | 初始集合零/多默认、优先级非法或 runtime 假装 verified | 三模型固定 seed;一个 enabled defaultpriority 1/2/3contract unverified 时 runtime 不可用 | DB、API |
| `TDD-WP3-MDL-002` | 停用默认、重复优先级或 CAS 冲突产生部分配置 | 完整三模型集合原子校验和切换;失败原集合、runtime 和审计结果符合协议 | UNIT、DB、API、E2E |
| `TDD-WP3-MDL-003` | runtime 降级改变 enabled/default/priority 或推荐为空时写回配置 | 配置字段不变;推荐只从可用候选按 priority 派生,可为 null | UNIT、DB、API、E2E |
| `TDD-WP3-ADP-001` | 一个模型 fixture 被复用于另两个模型或静默接受多图 | 三个独立 Adapter 分别验证纯文字、参考图、四比例、同步/异步、响应和一图约束 | EXT-MOCK、EXT-REAL |
| `TDD-WP3-ADP-002` | 契约字段变化后仍为 verified | 端点、字段、参考图、响应、异步查询或错误协议任一变化立即 unverified,完整重验后才恢复 | DB、EXT-MOCK、EXT-REAL |
| `TDD-WP3-WRK-001` | Worker 崩溃后重新 submit、重复结算或丢任务 | 租约/心跳;有引用只 poll;无安全引用则 unknown_retryable+release+pending_manual_review | DB、WRK |
| `TDD-WP3-BAL-001` | 余额恢复自动执行或成本核对补扣用户点数 | model/account/unknown 影响正确;人工确认恢复并审计;成本核对不改任务终态或用户点数 | DB、WRK、E2E |
三个模型任一缺少最后一次配置下的真实证据时,WP-3 保持 externally_blocked。
### 6.5 WP-4 桌面编辑器与导出
| 测试族 | 首个 Red | Green 断言 | 层级 |
| --- | --- | --- | --- |
| `TDD-WP4-BG-001` | 换底图丢覆盖层、保留旧调色或色卡不刷新 | 覆盖层位置/尺寸保留;底图参数重置;色卡重提;一次撤销和保存 | UNIT、E2E |
| `TDD-WP4-BG-002` | 填充/适配/裁剪/缩放/定位或亮度、对比度、饱和度、色温、锐度、预设滤镜、恢复原图未保存、不可撤销或未进入导出 | 每项参数可预览、提交、撤销、自动保存并在重开与导出中一致;恢复原图只重置底图处理,不改变覆盖元素 | UNIT、E2E、VIS-PERF |
| `TDD-WP4-CAN-001` | 选择循环、多选、层级、吸附或第 51 元素错误 | 50 元素可编辑保存导出;第 51 个阻断;无图层面板/隐藏/锁定/编组 | UNIT、E2E、VIS-PERF |
| `TDD-WP4-TXT-001` | 多行丢换行、空内容提交或切模板改变变换 | 内容/换行持久;模板切换重置默认样式但保留位置/旋转/整体尺寸,可撤销 | UNIT、E2E、VIS-PERF |
| `TDD-WP4-TXT-002` | 字体静默回退、字号与控制柄不同步、文字裁切、范围步长错误或背景透明度影响文字 | 真实归档字体;字号与缩放同步且内容重新测量不裁切;行距/字距/描边等精确范围;完成为一次撤销;重新打开和导出一致 | UNIT、E2E、VIS-PERF |
| `TDD-WP4-TXT-003` | 未选模板即可添加普通文字,或模板分类、显示名称搜索、catalog 顺序、账号最近使用漂移 | 只能从模板添加;四类、显示名称搜索、稳定顺序和账号级最近使用符合 PRD;普通贴纸不复用该搜索 | UNIT、E2E |
| `TDD-WP4-STK-001` | 普通贴纸变换不进入保存/撤销/导出 | 移动、缩放、旋转、翻转、透明度、复制、层级和多选一致 | UNIT、E2E |
| `TDD-WP4-COL-001` | 色卡随机、读取处理后底图或额外上传图片 | 同图 `mmcq-v1` 五色确定;裁剪/调色/滤镜不刷新;换底图刷新;网络上传为 0 | UNIT、E2E、VIS-PERF |
| `TDD-WP4-DYN-001` | 时间贴纸重开后变成新时间 | 使用插入/确认值并持久化,导出静态不可点击内容 | UNIT、E2E |
| `TDD-WP4-DYN-002` | DYN004 提示前调用定位、拒绝后插入或删除后留坐标 | 同意后才定位并保存坐标;拒绝调用为 0;手动地点回退;删除/注销清理 | UNIT、E2E |
| `TDD-WP4-DYN-003` | 身份贴纸重复 `@@`、覆盖修改账号或 DYN012 静默换字体 | 前缀一个;实例覆盖独立;DYN012 固定 FONT081 且证据标记替代 | UNIT、E2E、MANUAL |
| `TDD-WP4-EXP-001` | 四比例像素、JPG/PNG、未提交修改或同 Blob 规则错误 | 精确像素、sRGB、无水印、PNG 无损、JPG 80-100 默认 92;未提交文字或样式经确认后,提交形成且仅形成一次撤销记录并进入正常自动保存,再用提交后的同一画布状态合成;真正的导出动作不额外创建撤销记录 | UNIT、E2E、VIS-PERF |
| `TDD-WP4-A11Y-001` | 关键动作不可键盘到达、焦点逃逸或状态只靠颜色 | 焦点顺序、ARIA、live region、弹层返回、等价画布工具、axe critical/serious 为 0 | E2E、VIS-PERF |
| `TDD-WP4-VIS-001` | Chrome/Edge 白名单结构或导出漂移 | 固定环境按第 10 节阈值比较;已知替代项人工复核 | VIS-PERF、MANUAL |
| `TDD-WP4-PERF-001` | 50 元素、自动保存、资源面板或导出超过预算 | 第 10.2 节全部预算通过,失败不破坏项目或 latest export | VIS-PERF |
### 6.6 WP-5 素材转换、发布与 P0-A 白名单
| 测试族 | 首个 Red | Green 断言 | 层级 |
| --- | --- | --- | --- |
| `TDD-WP5-CAT-001` | 计数、part、重复项、顺序或懒加载错误 | part1-25 共 1,407 PNG;3 组重复保留;虚拟列表节点不线性增长,原图按需加载 | UNIT、E2E、MANUAL |
| `TDD-WP5-MAN-001` | 编译器读取证据目录、执行 Lua/Prefab、复制全库或改写源 | 只读规范入口;声明式转换;源前后哈希/时间不变;无等价全库副本 | UNIT、PKG-SEC、MANUAL |
| `TDD-WP5-WHITE-001` | 普通用户发现白名单外复杂素材或缺少规定白名单 | 精确 32 文字、实际字体+FONT081、4 色卡、10 动态、1,407 普通贴纸 | API、E2E |
| `TDD-WP5-COL-001` | P0-A 启用非四种色卡或四种布局不消费同一五色 | 只启用 COLOR001/002/008/016,统一五色,刷新规则与 WP-4 一致 | UNIT、E2E、VIS-PERF |
| `TDD-WP5-RES-001` | 公开 manifest 暴露 internal/private、路径可推导或缓存策略混用 | 三类路由、鉴权和 cache header 分离;浏览器永不获得绝对路径 | API、E2E、PKG-SEC |
| `TDD-WP5-UPL-001` | 后台上传未流式校验、原图/缩略图漏计量或 full 仍写入 | PNG/WebP、稳定 ID、part、顺序和 MIME 校验;两类文件分别预留并计量 | DB、API、E2E |
| `TDD-WP5-CLN-001` | 自动清理、有引用仍删除、整批部分排队或排队即减容量 | 候选、同管理员确认、BEGIN IMMEDIATE 复核、整批拒绝/排队、物理删除、重测和全阶段审计 | DB、API、WRK、E2E |
| `TDD-WP5-PREV-001` | 授权改变角色、公开 manifest 泄漏或撤销后仍可取资源 | 每次校验 grant;no-store;撤销立即阻断新访问;已有项目按版本渲染 | DB、API、E2E |
| `TDD-WP5-CACHE-001` | SW 拦截 internal/private 或 IndexedDB 保存私有字段 | 仅公开 allowlist;每场景实际枚举 registration、Cache keys 和 IndexedDB 记录 | E2E |
真实 3.57 GB 归档只在受控 release job 只读校验;日常 CI 使用小型合成 fixture。
### 6.7 WP-6 运营后台与内部运营控制
| 测试族 | 首个 Red | Green 断言 | 层级 |
| --- | --- | --- | --- |
| `TDD-WP6-ADM-001` | 普通/预览账号进入后台、后台显示私有正文或敏感配置 | active super_admin 服务端授权;默认仅安全摘要;角色和会话域严格分离 | API、E2E |
| `TDD-WP6-PRIV-001` | 告知确认只存在 Cookie/session/localStorage 或同版本重复 | UserProfile 按 admin+version 持久;跨标签、浏览器/API 重启、重登和 Chrome/Edge不重复;升版重阻断 | DB、API、E2E |
| `TDD-WP6-PRIV-002` | hover/预取打开内容、确认替代逐次审计、要求填写访问目的/原因或审计失败仍返回内容 | 每个入口原样显示“打开并记录审计”;每次点击先提交独立 PrivateContentAccessLogUI 和 API 均不要求逐次填写访问目的、原因或二次确认;审计只记录 PRD/DevelopmentPlan 允许的对象、管理员、时间、内容类型等非正文信息;失败不返回;列表/HTML/缓存无正文 | DB、API、E2E |
| `TDD-WP6-SVC-001` | Resend 发第 81 封/超月限、高德发第 1001 次或暂停后伪造成功 | 配额领取事务化;paused_quota/provider;已有会话继续;DYN004 停而手动地点继续 | DB、API、EXT-MOCK、E2E |
| `TDD-WP6-SVC-002` | 后台提高上限、启用收费或自动恢复 | 只允许不扩大能力的下调并审计;提高/付费/自动扩容被拒绝;恢复需前置确认 | DB、API、E2E |
| `TDD-WP6-AUD-001` | 敏感操作无日志、两类日志混用、含正文或 180 天内可删 | 全部规定操作同事务审计;贴纸清理全阶段;两类表分离;到期 Worker 清理 | DB、API、WRK、E2E |
| `TDD-WP6-STATE-001` | 后台把 configured default、runtime availability 和 recommended 合并 | 三列独立;临时异常不改配置;恢复只重新派生推荐 | API、E2E |
| `TDD-WP6-DIAG-001` | 总览/服务/存储/诊断暴露路径、密钥或正文 | 只显示固定非敏感字段、计量和状态;复制诊断通过同一脱敏扫描 | API、E2E、PKG-SEC |
### 6.8 WP-7 P0-A 验收准备与发布
| 测试族 | 首个 Red | Green 断言 | 层级 |
| --- | --- | --- | --- |
| `TDD-WP7-REL-001` | `RELEASE.json` 缺失/漂移、ZIP 哈希或真实浏览器未验收 | 发布记录、制品、START-HERE、Chrome/Edge full version、固定 major 和 Windows build 一致 | PKG-SEC、E2E、MANUAL |
| `TDD-WP7-EXT-001` | 任一模型缺独立真实契约证据 | 三模型各自完成纯文字、参考图、四比例、同步/异步、响应、九类错误和结算证据 | EXT-REAL、MANUAL |
| `TDD-WP7-EXT-002` | Resend 域名/免费规则/送达门槛未证实 | 依据 PRD 第 23.2 节、AC-41、AC-47 与 DevelopmentPlan 第 1.4、11.1 节:SPF/DKIMQQ、163、企业邮箱各 20 封至少 19 封在 2 分钟内送达;正式账号真实认证 | EXT-REAL、MANUAL |
| `TDD-WP7-EXT-003` | 高德接口、绑定、安全限制或 1,000 次硬上限未证实 | 定位/逆地理、QPS、白名单、安全和应用硬停止实测通过 | EXT-REAL、MANUAL |
| `TDD-WP7-UI-001` | UIDesign 页面/入口或适用状态无实现落点,或 1080p/200% 出现遮挡 | 第 11.1 节页面 × 状态矩阵全部适用单元格有证据;Pencil 仅作布局参考,Canvas Index 不计产品实现;固定文案、焦点、状态、控件和无重叠检查通过 | E2E、VIS-PERF、MANUAL |
| `TDD-WP7-SEC-001` | 制品、日志、DB 配置、manifest 或文档出现真实敏感值/用户路径 | 全制品扫描零命中;凭据只在 Windows Credential Manager;诊断脱敏 | PKG-SEC |
| `TDD-WP7-AC-001` | 52 项 AC、89 个测试族或 117 个规范 case 缺失,重复主责、孤儿 case 或用 P0-B 结果冒充 | 第 12 节矩阵每项一个主责且证据齐全;89/89 父族有 case 且派生属性完整;未来四项不计入;四份全局来源哈希未变,UI 参考变化按第 1.2.2 节局部重验 | UNIT、MANUAL |
任一真实外部门槛失败时不得开始 P0-A 实际验收,也不得把 WP-7 标记 passed。
### 6.9 高风险可执行 case
第 6.1 至 6.8 节的 89 个测试族是覆盖导航,不代替逐场景用例。第 6.9.2 至 6.9.9 节给出重点能力的细分 case,第 6.9.10 至 6.9.17 节为其余父族给出至少一个规范 case;合计 117 个 case,保证 89/89 父测试族均有执行入口。同一测试族的参数分支仍按第 2.2 节展开。实现不得删除 case、合并掉禁止副作用断言,或用一条端到端 happy path 代替 DB/API 分层断言。
#### 6.9.1 通用执行约定
- API case 默认使用已通过浏览器门禁的请求上下文、`Host: 127.0.0.1:43121`、同源 Origin、正确 audience 的隔离 session、页面加载取得的 CSRF token;所有创建型、金融或文件副作用请求使用稳定的独立 `Idempotency-Key`。case 明确测试门禁或鉴权失败时才替换这些默认值。
- `REGISTER_SEND``REGISTER_COMPLETE``LOGIN_SEND``LOGIN_COMPLETE``PRIVATE_OPEN``EXPORT_SAVE` 是测试别名,分别在生成的 OpenAPI 中解析到 `/api/v1/auth/register/*` 发码/完成、`/api/v1/auth/login/*` 发码/完成、`/api/v1/admin/private-content/*` 单对象打开和 `/api/v1/projects/{id}/exports` latest 成品保存 operation。别名解析不到唯一 operation、请求或响应不符合 TypeBox/OpenAPI 时先使 `TDD-WP0-API-001` 失败;不得为满足用例新增产品路由。
- 表中 `case_id` 就是第 2.2 节的可执行 `test_id`。父测试族必须先与第 6.1 至 6.8 节的已知父族集合匹配,再用正则 `^(TDD-WP[0-7]-[A-Z0-9]+-[0-9]{3})-` 解析,且捕获组 1 必须是该已知父族;不得通过删除最后一个命名段或按最后一个连字符截断。`result.json` 必须逐项记录第 2.2 节全部字段,包括父测试族继承的 WP、层级、自动/人工属性和发布门禁;不能只记录测试族汇总结果。
- 每个 case 在临时 SQLite、临时 LocalDataRoot、可控 UTC 毫秒时钟和独立浏览器 profile 中运行。步骤 0 固定保存脱敏 DB/文件基线;最后一步固定复查余额不变量、活动任务唯一索引、审计不可变约束和证据脱敏。
- 证据根固定为 `artifacts/tdd/<run_id>/cases/<case_id>/`。每个目录必须有 `result.json`API case 增加 `request.json``response.json`DB case 增加 `db-diff.json`,文件 case 增加 `fs-before.json``fs-after.json`Worker case 增加 `worker-events.json`E2E case 增加 `trace.zip``screenshots/`。文件只保留 Schema 白名单字段、哈希和逻辑资源 ID,不保留正文、图片、邮箱、验证码、令牌或绝对路径。
- 表中的“响应”同时是 HTTP 与 UI 断言:HTTP 状态、错误信封和字段以冻结 OpenAPI 为准;上游只冻结语义而未冻结具体成功状态码/字段名时,case 断言生成 Schema 所定义的成功响应及列出的业务结果,不在本文件发明字段。
#### 6.9.2 认证
| case_id | 需求 / AC | fixture 与前置 | 编号操作步骤 | 预期响应 / UI | DB / 文件变化 | 禁止副作用 | 证据文件 |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `TDD-WP1-AUTH-001-valid-register` | AUTH-01 至 04、AC-01 | `FX-INVITE-VALID``FX-CHALLENGE`;占名额普通账号为 9;未注册 `.invalid` 邮箱 | 1. 调 `REGISTER_SEND`;2. 读取模拟 Resend 验证码;3. 以署名、社交 ID、当前告知 version/同意和同一 registration_id 调 `REGISTER_COMPLETE`4. 调普通 session | 两次均符合成功 Schema;注册后得到普通 audience 的 30 天会话并进入创作,点数显示 available 10/reserved 0 | `users/user_profiles/credit_accounts/privacy_consents` 各新增正确关系;`invite_codes.used_count +1`;challenge 只消费一次;新增 session 只存 token 摘要;初始点数流水一次 | 不创建 super_admin;不保存验证码明文;不重复赠点或核销邀请码;不产生第二会话/第二注册实体 | `request.json``response.json``db-diff.json``trace.zip``screenshots/registered.png` |
| `TDD-WP1-AUTH-002-invalid-invite` | AUTH-01、02、AC-02 | 参数化 `FX-INVITE-EXPIRED/DISABLED/EXHAUSTED` 和不存在的邀请码 | 1. 对每个参数调 `REGISTER_SEND`2. 检查 Resend mock 调用;3. 查询认证相关表 | 返回冻结的非成功错误语义并在邀请码字段显示原因,第二阶段不展开 | `users/user_profiles/credit_accounts/privacy_consents/email_challenges/sessions` 无新增;邀请码计数不变 | Resend 调用为 0;不建号、不赠点、不占名额、不写成功审计或成功提示 | 每参数 `response.json``db-diff.json``external-calls.json``screenshots/error.png` |
| `TDD-WP1-AUTH-001-final-recheck-race` | AUTH-01、NFR-04、AC-01、02、45 | `FX-INVITE-VALID` 已成功发码;随后参数化为邀请码耗尽/停用/过期或第 10 个名额被另一事务占用 | 1. 完成前改变可变条件;2. 调 `REGISTER_COMPLETE`3. 用相同 Idempotency-Key 重试 | 返回对应冻结错误;不建立 session,不进入创作;相同重试返回同一失败结果 | `BEGIN IMMEDIATE` 后无新 user/profile/credit/consent/session;邀请码次数不增加;名额统计不变 | 不出现账号已建但邀请码未核销、邀请码已核销但账号未建或初始点数孤儿流水 | `response.json``db-diff.json``transaction-trace.json` |
| `TDD-WP1-AUTH-003-entry-state-matrix` | AUTH-01、02、05、06、AC-33 | 参数化未注册、active 已注册、deleted、suspended 邮箱;注册和登录两个入口 | 1. 每种邮箱分别调注册发码与登录发码;2. 对允许分支完成验证码;3. 检查页面不自动切入口 | 行为严格匹配 PRD AUTH-02 状态矩阵;deleted 仅可新邀请注册;suspended 均提示联系管理员 | 只允许的分支创建 challenge/session/new user;重复注册不核销邀请码;deleted 新建主体与旧主体无关联 | 不静默注册/登录转换;不恢复 deleted 数据;不因 suspended 释放名额;错误不泄漏后台白名单 | 每参数 `response.json``db-diff.json``trace.zip``screenshots/entry-state.png` |
| `TDD-WP1-AUTH-004-challenge-guards` | AUTH-02、04、AC-33 | `FX-CHALLENGE`、可控时钟、active 普通账号 | 1. 成功消费验证码;2. 重放同码;3. 新建 challenge 后前进超过 10 分钟再提交;4. 发码后 60 秒内再发;5. 连续错误触发限流 | 仅首次有效提交成功;重放/过期/错误使用稳定错误;60 秒重发和高频为 429;页面不显示假成功 | 首次 challenge `consumed_at` 只写一次;失败次数/限流记录按规则变化;失败分支不新增 session | 不记录验证码明文;不延长原 challenge;限流失败不调用 Resend、不创建会话 | 每参数 `response.json``db-diff.json``external-calls.json` |
| `TDD-WP1-AUTH-004-session-revocation` | AUTH-04 至 07、AC-22、33、49 | 每个参数使用独立库:active 普通用户或 active super_admin 各有 2 个 session | 1. 验证正常 session2. 分别触发普通用户 logout/暂停/注销或管理员 logout/停用/移出白名单;3. 再调受保护 API | 撤销前成功;每种撤销后均为 401 或相应身份入口,已打开页面退出旧数据;两种 audience 不能互用 | 相应主体全部 session `revoked_at` 在同一事务写入;账号/白名单状态按触发操作变化 | 不只撤销当前浏览器;不把普通 session 提升为后台 session;不保留可交互私有响应 | 每参数 `response.json``db-diff.json``trace.zip``screenshots/revoked.png` |
#### 6.9.3 点数
| case_id | 需求 / AC | fixture 与前置 | 编号操作步骤 | 预期响应 / UI | DB / 文件变化 | 禁止副作用 | 证据文件 |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `TDD-WP2-CRED-001-reserve` | CREDIT-01 至 05、AC-03 | `FX-CREDIT-ENOUGH``FX-MODEL-VERIFIED`,无活动任务 | 1. 记录余额;2. 以成本 1 调 `POST /api/v1/generations`3. 查询 `/me/credits` 和流水 | 创建任务成功;UI 显示 available 9/reserved 1 和 queued;流水显示 generation_reserve | `credit_accounts` 原子 10/0→9/1;一条 reserve ledger、一个 queued job 和 outbox 同事务 | 不提前生成 commit;不使总点数改变;不出现任务已建但未冻结或冻结无任务 | `request.json``response.json``db-diff.json` |
| `TDD-WP2-CRED-001-insufficient` | CREDIT-04、AC-28 | `FX-CREDIT-INSUFFICIENT`,成本 1 | 1. UI 检查生成按钮;2. 直接调生成 API 防绕过;3. 查询 DB/网关 mock | UI 禁用并显示 available/reserved/required/联系管理员;API 返回冻结错误语义 | 余额、ledger、job、project、reference、outbox 均无变化 | 上游调用 0;无 storage reservation/staging 文件;不产生负余额 | `response.json``db-diff.json``external-calls.json``screenshots/insufficient.png` |
| `TDD-WP2-GEN-001-concurrent-reserve` | CREDIT-03、GEN-07、AC-05、29 | `FX-CREDIT-ENOUGH`,两个不同 submission/idempotency key 同时开始 | 1. 用 barrier 并发提交两次;2. 等待响应;3. 查询 current job、余额和流水 | 一个响应创建任务,另一个 200 返回同一现有任务引用;UI 提供返回当前任务 | 仅一个 queued/running job、一个 projectnew_project 模式)、一条 reserve ledger;余额只转移一次 | 不创建第二项目/参考关系/outbox/upstream callavailable 不为负 | 两份 `response.json``db-diff.json``concurrency-trace.json` |
| `TDD-WP2-CRED-002-admin-adjustment` | CREDIT-05、06、ADMIN-01、09、AC-29、50 | `FX-JOB-ACTIVE`available 0/reserved 1`FX-ACTOR-ADMIN-A` | 1. 管理员填写原因扣减 52. 用同 adjustment_id 重试;3. 用户读点数 | 首次成功,重试返回同一结果;用户显示 available -5/reserved 1;无充值入口 | available 0→-5reserved 保持 1;一条 admin_adjustment ledger 和一条 AdminOperationLog 同事务 | 不改活动任务/冻结流水;不重复调点;不允许空原因;不使生成流程自身制造负数 | `response.json``db-diff.json``screenshots/negative-balance.png` |
| `TDD-WP2-CRED-001-finalize-once` | CREDIT-03、05、GEN-09、10、AC-03、04、29 | 参数化成功、技术失败和安全拒绝的 `FX-JOB-ACTIVE` | 1. Worker 提交终态;2. 重复 callback/poll3. 模拟 Worker 重启再处理 | 成功为 succeeded;技术失败为 failed;安全拒绝为 rejected;用户余额/动作匹配固定类别 | 每任务恰一条 commit 或 releasereserved 恰减一次;成功才建 output/history,失败/拒绝 available 恢复 | 不双重 commit/release;不在失败建 output;终态不可逆;不保存供应商原文 | `worker-events.json``db-diff.json``fs-before.json``fs-after.json` |
#### 6.9.4 GenerationJob
| case_id | 需求 / AC | fixture 与前置 | 编号操作步骤 | 预期响应 / UI | DB / 文件变化 | 禁止副作用 | 证据文件 |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `TDD-WP2-GEN-001-submit-snapshot` | GEN-01 至 08、PROJECT-01、AC-03、34、44 | `FX-MODEL-VERIFIED``FX-CREDIT-ENOUGH``FX-REF-VALID`new_project | 1. multipart 提交 prompt/model/ratio/config version/cost/reference2. 查询任务;3. 检查 staging/正式文件 | 成功响应为 queued,返回同一任务真相;UI 不显示假百分比 | 新 project、job、reference snapshot、reserve ledger、outbox 同事务;提交后 reference 从 staging 进入私有 managed file | 不接受未冻结高级字段;不让 Worker 领取未完整提交任务;不暴露路径;失败补偿不留下可读半文件 | `request.json``response.json``db-diff.json``fs-before.json``fs-after.json` |
| `TDD-WP2-GEN-002-success` | GEN-06、08、11、CREDIT-03、AC-03 | 上一 case 的 queued job + `FX-GATEWAY-SYNC` | 1. Worker 领取;2. 调上游并验证单图;3. 落盘;4. 查询任务/项目 | queued→running→succeeded;项目可进入编辑且历史新增一图 | lease/heartbeat、output managed file、output_asset、generation_commit、project history/draft_state 一次提交;容量增加实测字节 | 不在文件安全落盘前 succeeded;不保存多图;不重复扣点;不改变项目比例 | `worker-events.json``db-diff.json``fs-after.json``screenshots/succeeded.png` |
| `TDD-WP2-ERR-001-upstream-timeout` | GEN-09、16、17.18、AC-04、53 | 独立 `upstream_timeout` fixture;已提交任务超时 | 1. 触发超时;2. 检查唯一下一步为“使用原输入重试”;3. 点击该动作;4. 查询原任务、新任务和项目 | 原任务 failed/category=`upstream_timeout`;只提供“使用原输入重试”;动作后同项目新增一个 GenerationJob | 原任务 release 一次且无 output;新任务独立 reserve 并保存相同输入关系快照 | 不改成“稍后重试”;不要求修改输入/选择模型/联系管理员;不复用原任务 ID 或创建第二项目 | `response.json``worker-events.json``db-diff.json``screenshots/upstream-timeout.png` |
| `TDD-WP2-ERR-001-upstream-failed` | GEN-09、16、17.18、AC-04、53 | 独立 `upstream_failed` fixture;已提交任务临时失败 | 1. 触发临时失败;2. 检查唯一下一步为“稍后重试”;3. 在受控条件恢复后点击;4. 查询原任务、新任务和项目 | 原任务 failed/category=`upstream_failed`;只提供“稍后重试”;动作后同项目新增一个 GenerationJob | 原任务 release 一次且无 output;新任务独立 reserve 和关系快照 | 不显示“使用原输入重试”或输入/模型修改动作;不自动重试、不创建第二项目 | `response.json``worker-events.json``db-diff.json``screenshots/upstream-failed.png` |
| `TDD-WP2-ERR-001-safety-rejected` | GEN-10、16、17.18、AC-04、53 | 供应商明确安全拒绝证据;与 reference fixture 完全分离 | 1. 触发安全拒绝;2. 检查唯一下一步为“修改提示词或参考图”;3. 不修改输入尝试继续;4. 修改提示词或参考图后提交 | 原任务 rejected/category=`safety_rejected`;未修改时不能新提交;修改后同项目新增 GenerationJob | 原任务 release 一次、无 output;修改前无新 job/reserve;修改后新任务保存修正快照 | 不提供原样/稍后重试、选择模型或联系管理员;不保存违规输出;不映射为 reference_invalid | `response.json``worker-events.json``db-diff.json``screenshots/safety-rejected.png` |
| `TDD-WP2-ERR-001-model-disabled` | GEN-07、16、17.18、AC-30、53 | `model_disabled` 创建前 fixture;参数化存在/不存在其他 enabled 模型 | 1. 触发创建前阻断;2. 检查下一步仅“选择其他模型或等待”;3. 有可用模型时显式改选并确认;4. 无可用模型时保持页面等待 | 返回 category=`model_disabled` 且不创建任务;改选后才能提交;无模型时保持禁用 | 阻断分支无 job/project/reference/reserve/outbox;改选分支按新模型快照创建 | 不提供联系管理员、原输入重试或稍后重试;不自动切推荐或把 runtime 状态写回配置 | `response.json``db-diff.json``external-calls.json``screenshots/model-disabled.png` |
| `TDD-WP2-ERR-001-gateway-balance` | GEN-15、16、17.17、17.18、AC-40、51、53 | `gateway_balance_insufficient` 创建前/后 fixture;明确未受影响模型集合 | 1. 触发余额不足;2. 检查下一步仅“选择未受影响模型或联系管理员”;3. 显式选择未受影响模型并重新确认;4. 无候选时检查联系管理员 | 返回/保存 category=`gateway_balance_insufficient`;已提交任务 failed;只有改选未受影响模型后才能新提交 | 创建前无 reserve/job;创建后 release 一次;新任务使用改选模型快照;runtime 影响范围按 GEN-15 更新 | 不提供等待、原输入/稍后重试或任意“其他模型”;不自动充值、不用 Dada 点数恢复、不回写 ModelConfig | `response.json``db-diff.json``external-calls.json``screenshots/gateway-balance.png` |
| `TDD-WP2-ERR-001-gateway-contract` | GEN-13、16、17.18、AC-40、53 | `gateway_contract_invalid` 创建前/后 fixture;另一个 contract verified 模型 | 1. 触发契约未验证/失效;2. 检查下一步仅“选择其他模型或联系管理员”;3. 显式改选已验证模型并重新确认;4. 无候选时检查联系管理员 | 返回/保存 category=`gateway_contract_invalid`;已提交任务 failed;改选 verified 模型后才能新提交 | 创建前无 reserve/job;创建后 release 一次;失效模型 contract 状态按契约更新;新任务保存新模型/版本 | 不提供等待、原输入/稍后重试;不让未验证模型提交;不自动切推荐或部分改写配置 | `response.json``db-diff.json``external-calls.json``screenshots/gateway-contract.png` |
| `TDD-WP2-ERR-001-reference-invalid` | GEN-05、16、17.18、AC-04、44、53 | 格式/大小/数量和非安全型内容不可用四组 fixture;不得使用安全拒绝响应 | 1. 分别触发创建前/后无效;2. 检查唯一下一步为“更换或移除参考图”;3. 不改参考图尝试继续;4. 更换/移除后提交 | category=`reference_invalid`;未修改时不创建新任务;更换/移除后同项目新增 GenerationJob | 创建前无 job/reserve;创建后 failed+release 一次;修正后保存新的 reference snapshot | 不提供修改提示词、重试、等待、选模型或联系管理员;不映射为 safety_rejected | 每 fixture `response.json``worker-events.json``db-diff.json``screenshots/reference-invalid.png` |
| `TDD-WP2-ERR-001-unknown-retryable` | GEN-09、16、17.18、AC-04、53 | 独立 `unknown_retryable` fixture;已提交任务 | 1. 触发未知临时错误;2. 检查唯一下一步为“稍后重试”;3. 在受控条件恢复后点击;4. 查询任务和脱敏诊断 | 原任务 failed/category=`unknown_retryable`;只提供“稍后重试”;动作后同项目新增一个 GenerationJob | 原任务 release 一次、无 output;新任务独立 reserve;只保留非敏感诊断 | 不显示“使用原输入重试”、修改输入、选模型或联系管理员;不泄漏原始错误或自动重试 | `response.json``worker-events.json``db-diff.json``screenshots/unknown-retryable.png` |
| `TDD-WP2-ERR-001-unknown-non-retryable` | GEN-09、16、17.18、AC-04、53 | 独立 `unknown_non_retryable` fixture;已提交任务 | 1. 触发未知不可重试错误;2. 检查唯一下一步为“联系管理员”;3. 尝试寻找或调用重复提交动作;4. 查询任务 | 原任务 failed/category=`unknown_non_retryable`;只提供“联系管理员”,无重试或修改入口 | release 一次;无 output;原草稿保留但本 case 不创建新 job | 不自动重试、不循环轮询制造新任务、不降级为 unknown_retryable、不提供其他类别动作 | `response.json``worker-events.json``db-diff.json``screenshots/unknown-non-retryable.png` |
| `TDD-WP2-GEN-003-stale-config` | GEN-15、ADMIN-03、AC-30 | 用户已确认旧 `model_config_version/cost`;管理员原子发布新集合 | 1. 旧页面直接提交;2. 读取 412 摘要;3. 重新 GET models 并明确确认;4. 再提交 | 第一次 412 且要求重新确认;第二次才可创建任务 | 第一次 job/project/reference/reserve/outbox 全无变化;第二次按新快照创建 | 第一次不上游、不持久化参考图、不冻结;不自动替用户切推荐模型 | `response.json``db-diff.json``external-calls.json``trace.zip` |
| `TDD-WP3-WRK-001-recovery` | GEN-08、09、CREDIT-03、AC-04、51 | 参数化 `FX-GATEWAY-ASYNC` 有引用和 `FX-GATEWAY-UNKNOWN` 无安全引用;在 submit 后终止 Worker | 1. 使租约过期;2. 重启 Worker;3. 有引用分支 poll;4. 无引用分支终结;5. 再重启复查 | 有引用不重新 submit,只 poll 到真实终态;无引用为 failed/unknown_retryable,后台显示成本核对待人工 | 租约重新领取;无引用 `upstream_outcome_known=false``upstream_cost_reconciliation=pending_manual_review`release 一次 | 不把 pending_manual_review 写入 job status;不补扣用户点数;不重复上游 submit/结算 | `worker-events.json``db-diff.json``external-calls.json` |
#### 6.9.5 容量
| case_id | 需求 / AC | fixture 与前置 | 编号操作步骤 | 预期响应 / UI | DB / 文件变化 | 禁止副作用 | 证据文件 |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `TDD-WP0-STO-002-equal-limit` | 17.19、NFR-05、AC-55 | 已计量+活动预留=`5,368,709,119`,预计写 1 字节,可写目录 | 1. 调受管二进制写入预检;2. 提交文件/DB3. 重读 storage state | 不得返回 507;写入成功后 notice/status 为 full | 建立并消费一份 reservationmanaged bytes 精确为 `5,368,709,120`managed file 已提交;last_measured_at 更新 | 不用 `>=` 拒绝;不回滚已成功等号写入;不调用自动清理 | `response.json``db-diff.json``fs-before.json``fs-after.json` |
| `TDD-WP0-STO-002-over-limit` | 17.19、NFR-05、AC-55 | 已计量+活动预留=`5,368,709,119`,预计写 2 字节 | 1. 分别尝试生成/参考图/export/贴纸原图/缩略图;2. 查询所有副作用 | 每次 507 `STORAGE_CAPACITY_EXCEEDED`,返回非敏感状态和 remaining bytes | storage state 保持由实际量决定;无新 reservation/managed file/latest export/job/ledger/outbox | 上游调用 0;无 staging 残留;不把单个超大请求伪造成持久 full(若现有承诺仍低于上限) | 每路由 `response.json``db-diff.json``fs-after.json``external-calls.json` |
| `TDD-WP0-STO-003-full-matrix` | 17.19、PROJECT-04、EXPORT-06、AC-55 | managed bytes 恰为上限,status fullSQLite 可写 | 1. 保存纯 JSON project state2. 读取/下载已有文件;3. 客户端合成并仅下载;4. 尝试 latest 保存和新生成;5. 永久删除/显式清理 | JSON 保存、读取、下载、仅下载、删除/清理入口可用;latest/新生成/其他二进制写为 507 | JSON state_version 可增加;禁止项不新增 managed file/job/ledger;删除/清理只先逻辑提交/排队 | 不自动删除;客户端下载不更新 latest_exports;排队不提前减少 managed bytes | `response.json``db-diff.json``fs-before.json``fs-after.json``trace.zip` |
| `TDD-WP0-STO-003-unavailable-matrix` | 17.19、NFR-05、AC-35、55 | LocalDataRoot 不可写,再参数化 SQLite 仍可写/不可写 | 1. 尝试 project JSON 保存、生成和二进制写;2. 读取/下载/仅下载;3. 尝试删除/清理 | 新增后端写全部失败且 UI 为 failed/unavailable;读取、下载和可行的仅下载保留;SQLite 不可写时删除/排队明确未提交 | 不更新 state_version/managed bytes/latest/job/ledgerSQLite 可写时允许规定逻辑删除/队列,反之无 DB 变化 | 不显示已保存;不上游;不把内存草稿写浏览器持久存储;不伪造清理完成 | `response.json``db-diff.json``trace.zip``screenshots/unavailable.png` |
| `TDD-WP0-STO-003-cleanup-remeasure` | ADMIN-06、17.19、AC-31、55 | full;查询时全部为无引用、可清理的历史贴纸文件 | 1. 查询候选并创建 intent;2. 参数 A 在确认前给其中一项新增 active/trashed 项目、已发布版本或待发布引用,再确认;3. 参数 B 保持全部无引用并确认;4. 读取排队容量;5. Worker 物理删除并重计量 | A 返回 `ASSET_HISTORY_REFERENCE_CONFLICT` 且整批不排队;B 整批排队,排队阶段仍 full,物理删除并重计量后才按真实值恢复 | A 只追加 `result=denied` 审计且 managed bytes 不变;B scheduled 前后 bytes 不变,物理成功后 files/queue/bytes/status/last_measured_at 同事务更新 | 不让查询时已有引用文件进入候选;不部分排队冲突批次;不在物理删除前减量;不自动创建 intent | 每参数 `response.json``db-diff.json``worker-events.json``fs-before.json``fs-after.json` |
#### 6.9.6 私有内容访问
| case_id | 需求 / AC | fixture 与前置 | 编号操作步骤 | 预期响应 / UI | DB / 文件变化 | 禁止副作用 | 证据文件 |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `TDD-WP6-PRIV-001-ack` | ADMIN-05、17.1、AC-25 | `FX-ACTOR-ADMIN-A` 未确认 current notice v1 | 1. 进入私有区域;2. 得到 4283. 以 expected v1 调 notice ack4. 重试区域摘要 | 首次 428 `PRIVATE_CONTENT_NOTICE_ACK_REQUIRED` 只含 version/message_keyack 成功后区域仅显示安全摘要 | 只更新该 admin 的 notice version/ack time;幂等重试不新增语义变化 | ack 不创建 PrivateContentAccessLog;不返回正文/图片;不写 Cookie/localStorage 作为真相 | `response.json``db-diff.json``trace.zip``screenshots/notice.png` |
| `TDD-WP6-PRIV-001-persistence` | ADMIN-05、AC-25 | admin A 已确认 v1 | 1. 新标签;2. API 重启;3. 浏览器重启/重登;4. 在真实 Chrome 与 Edge 登录同一 admin | 同版本均不重复提示,session/bootstrap 计算 `notice_acknowledged=true` | UserProfile ack 字段不因浏览器/会话改变;每次 session 独立但 ack 不重复写 | Cookie/session/localStorage/IndexedDB 清空不使服务端遗忘;跨管理员不共享 ack | 每环境 `response.json``db-diff.json``trace.zip``screenshots/ack-persisted.png` |
| `TDD-WP6-PRIV-001-version-bump` | ADMIN-05、AC-25 | admin A/B 已确认 v1,应用 current notice 切到 v2 | 1. 两个 admin 分别进入区域;2. 尝试用 expected v1 ack3. 读取 v2 并确认 | 两者重新 428;旧 expected version 失败且要求重读;各自确认 v2 后可进入摘要 | 旧 ack 保留历史语义但当前字段仅在正确 v2 ack 后更新;A/B 独立 | 不以旧确认放行;不因 A 确认替 B 确认;不返回私有正文 | `response.json``db-diff.json``trace.zip` |
| `TDD-WP6-PRIV-002-open-after-audit` | ADMIN-05、17.10、AC-25、50 | active admin A 已确认 current;目标含图片和完整提示词 | 1. 列表确认无正文;2. 不带 purpose/reason 调 `PRIVATE_OPEN`;3. 在提交钩子确认审计落库后才释放受控读取;4. 对同目标再打开 | 每个入口原样“打开并记录审计”;每次打开成功且各生成一条访问审计 | 每次先 append 一条含 admin/目标/时间/content_type/expires_at 的 PrivateContentAccessLog;内容本身不入审计 | UI/API 不要求目的、原因或二次确认;不预取/hover 打开;不把正文放列表、HTML、缓存或证据 | `response.json``db-diff.json``ordering-trace.json``trace.zip``screenshots/open-entry.png` |
| `TDD-WP6-PRIV-002-deny-without-audit` | ADMIN-05、17.10、AC-25 | 参数化未确认、普通用户/预览账号、停用 admin、审计 insert 失败 | 1. 调区域/`PRIVATE_OPEN`;2. 模拟审计约束失败;3. 枚举页面、SW、Cache、IndexedDB | 未确认 428;其他未授权 401/403/404 按冻结协议;审计失败返回非成功且无内容字节 | 未授权无访问审计;审计失败事务不留下伪成功记录,也不发放读取 | 不返回正文/图片/可推导 URL;不写 AdminOperationLog 替代;不因 notice ack 省略逐次审计 | 每参数 `response.json``db-diff.json``cache-enumeration.json``trace.zip` |
#### 6.9.7 模型配置
| case_id | 需求 / AC | fixture 与前置 | 编号操作步骤 | 预期响应 / UI | DB / 文件变化 | 禁止副作用 | 证据文件 |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `TDD-WP3-MDL-001-seed-read` | GEN-01、ADMIN-03、17.2、AC-30 | 全新迁移后的 `FX-MODEL-SEED` | 1. 启动迁移;2. 调 `GET /api/v1/models`;3. 查集合约束 | 返回固定三模型;Flash 唯一 configured defaultpriority 1/2/3recommended null;三者 contract unverified/runtime unavailable | 一个 current set,三个 immutable member/version,独立 runtime 行 | 不把 unverified 标为 available;不生成第二默认;不把 recommended 写配置 | `response.json``db-diff.json` |
| `TDD-WP3-MDL-002-replace-default` | ADMIN-03、17.2、AC-30 | `FX-MODEL-VERIFIED`current set version N | 1. 完整提交三模型集合:停用当前默认并指定另一个 enabled default2. 带 expected N/Idempotency-Key3. GET models | 保存成功,读取始终恰一个 enabled defaultconfig_set_version=N+1 | 新 immutable set/必要 config versions、AdminOperationLog/outbox 写入,最后原子切 current pointer | 不出现可见零默认/双默认中间态;不逐行部分保存;不改无关 runtime | `request.json``response.json``db-diff.json``transaction-trace.json` |
| `TDD-WP3-MDL-002-invalid-set` | ADMIN-03、17.2、AC-30 | 参数化缺替代、非法替代、priority 缺失/浮点/0/负数/重复、缺模型/未知模型 | 1. 分别 PUT 完整候选;2. 读取稳定错误;3. GET 当前集合 | 返回对应 400/409 稳定 code;UI 聚焦修正字段;当前集合仍为 N | current pointer、versions/runtime/审计成功记录/outbox 均无变化 | 不生成孤儿候选 set;不部分改变 enabled/default/priority;不新增错误分类 | 每参数 `response.json``db-diff.json` |
| `TDD-WP3-MDL-002-cas-conflict` | ADMIN-03、AC-30 | 两个 admin 基于 expected N 编辑不同完整集合 | 1. A 保存为 N+1;2. B 仍以 N 保存;3. B 刷新 | A 成功;B 返回 412 `MODEL_CONFIG_VERSION_CONFLICT` 和最新安全摘要 | 仅 A 的 immutable set、pointer、审计/outbox 存在;B 零写入 | B 不覆盖 A、不自动 merge、不复用过期配置继续提交 | 两份 `response.json``db-diff.json``trace.zip` |
| `TDD-WP3-MDL-002-contract-change` | GEN-13、15、ADMIN-03、AC-30、40 | verified 模型集合 | 1. 修改端点/字段/参考图/响应/异步查询/error mapping 任一契约字段;2. 保存;3. GET models;4. 尝试生成 | 保存成功但对应模型 contract unverified、runtime reason contract_unverified;生成被阻断直到独立重验 | 新 config_version/set;对应 runtime 独立置 false;审计/outbox 同事务 | 不沿用旧 verified 证据;不暗改 enabled/default/priority;不调用真实上游 | 每参数 `response.json``db-diff.json``external-calls.json` |
| `TDD-WP3-MDL-003-runtime-recommendation` | GEN-15、ADMIN-03、17.2、AC-30、51 | 三模型 verified/available,配置快照和哈希已记录 | 1. 依次模拟默认模型不可用、全部不可用、一个原因恢复但另一原因仍在、全部恢复;2. 每步 GET models | configured default 始终原 IDrecommended 按可用模型 priority 派生或 nullreason 使用固定优先级 | 只更新 `model_runtime_availability`/GatewayBalanceState/runtime versionconfig set/version/hash 不变 | 不写 enabled/is_default/priority;不自动恢复;不把推荐变成第二默认或用户隐式选择 | 每步 `response.json``db-diff.json``screenshots/model-states.png` |
#### 6.9.8 自动保存与冲突
| case_id | 需求 / AC | fixture 与前置 | 编号操作步骤 | 预期响应 / UI | DB / 文件变化 | 禁止副作用 | 证据文件 |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `TDD-WP2-PROJ-002-debounce` | PROJECT-04、06、AC-20 | saved project version N、可控前端时钟 | 1. 1 秒内完成多次已提交编辑;2. 在首个保存请求在途时继续编辑;3. 释放响应并推进时钟;4. 撤销/重做 | 同时最多一请求;第一份后下一请求为合并后的最新完整状态;状态 saving→saved | 每次成功 state_version +1;最终 project_states 为最新已提交 JSON;撤销/重做也正常保存 | 不逐操作并发;不保存 Fabric 私有字段;不把未提交编辑或撤销栈写浏览器持久存储 | `network-timeline.json``response.json``db-diff.json``trace.zip` |
| `TDD-WP2-PROJ-002-cas-success` | PROJECT-04、17.3、AC-20 | owner project version N,完整合法 Canvas JSON | 1. PUT state with `If-Match: N`2. GET project;3. 用相同幂等请求重放 | 首次成功并返回 saved/N+1;重放返回第一次结果 | `UPDATE ... state_version=N` 命中一行;project_states 新快照,state_version 仅 +1 | 不隐式上传/复制/派生资源;不泄漏或接受他人资源引用;不因重放 +2 | `request.json``response.json``db-diff.json` |
| `TDD-WP2-PROJ-004-stale-tab` | PROJECT-07、AC-36 P0-A 子集 | `FX-PROJECT-CONFLICT`,标签 A/B 均基于 N | 1. A 保存到 N+12. B 以 N 保存;3. B 尝试编辑/覆盖/另存副本 | B 返回 412 和最新安全版本摘要;立即只读 conflicted,只显示一次本地导出与刷新 | 仅 A 状态写入;B 的 state_version/save_status/latest_exports/project 数量不变 | B 不自动重试覆盖、不 merge、不另存项目、不恢复自动保存 | 两标签 `response.json``db-diff.json``trace.zip``screenshots/conflicted.png` |
| `TDD-WP2-PROJ-002-save-failure-recovery` | PROJECT-04、05、EXPORT-06、AC-20、35 | saved N;页面创建 dirty;参数化网络失败、storage unavailable、强制刷新/崩溃 | 1. 触发保存失败;2. 检查 failed/离开弹层;3. 页面保持时恢复并重试;4. 另一路强制退出后重开;5. 枚举浏览器存储 | 失败不显示 saved;正常恢复可保存;强制退出只恢复后端 N 且撤销栈为空 | 失败期间 DB 不变;成功重试才 N+1;浏览器持久存储无草稿/提示词/私有引用 | 不把失败当成功;不在关闭后恢复内存草稿;不让 SSE 代替 REST 真相 | `response.json``db-diff.json``cache-enumeration.json``trace.zip``screenshots/save-failed.png` |
#### 6.9.9 导出
| case_id | 需求 / AC | fixture 与前置 | 编号操作步骤 | 预期响应 / UI | DB / 文件变化 | 禁止副作用 | 证据文件 |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `TDD-WP4-EXP-001-confirm-pending-edit` | PROJECT-04、06、EXPORT-03、AC-23 | saved 画布、一次未提交文字/样式编辑、撤销栈深度 U | 1. 打开导出;2. 看到“导出前需要提交当前修改”;3. 确认;4. 等待画布稳定并启动正常自动保存;5. 合成导出;6. 执行一次撤销 | 导出使用确认后的状态;确认使撤销栈 U→U+1 且撤销可回到确认前;导出动作不再增加撤销项 | 确认后的完整状态进入正常保存队列;保存成功按 PROJECT-04 增加 state_versionlatest 是否更新由后续保存结果决定 | 不在确认时产生多条撤销;不以导出额外编辑画布;不导出确认前状态;不跳过自动保存流程 | `undo-trace.json``network-timeline.json``export-hash.json``trace.zip``screenshots/pending-confirm.png` |
| `TDD-WP4-EXP-001-cancel-pending-edit` | EXPORT-03、AC-23 | 与上一 case 相同 | 1. 打开弹层;2. 取消未提交修改确认;3. 检查画布/撤销/网络 | 弹层关闭或停留在未导出状态;无下载、无成功提示 | state_version、latest_exports、撤销栈、保存队列均不因取消改变 | 不提交当前编辑、不合成、不下载、不调 `EXPORT_SAVE` | `network-timeline.json``db-diff.json``trace.zip` |
| `TDD-WP4-EXP-001-format-ratio` | EXPORT-01 至 03、AC-23 | `FX-CANVAS-RATIOS`JPG quality 参数化 80/92/100PNG | 1. 对四比例分别导出 JPG/PNG2. 解码像素/ICC3. 与 Canvas fixture 比较 | 尺寸精确为 1080×1440、1080×1080、1440×1080、1080×1920sRGB、无水印、PNG 无损、JPG 范围/默认正确 | 纯客户端合成本身无 DB 变化;后续保存只处理同一 Blob | 不缩放到其他尺寸;PNG 时质量控件不可聚焦;不因导出改变画布/撤销栈 | 每组合 `result.json``image-metadata.json``pixel-diff.json``export-hash.json` |
| `TDD-WP2-EXP-001-same-blob-success` | EXPORT-04、AC-23、35 | saved project,已有旧 JPG latest;新客户端 Blob B | 1. 记录 B SHA-2562. 用 B 触发浏览器下载;3. 同一 B 调 `EXPORT_SAVE`4. 重读 latest 并下载 | UI 显示“已下载并保存为最新成品”;两个通道 hash 相同 | 原子替换该格式 latest_exports/managed file;旧 JPG 进入规定清理;PNG latest 不变;容量按真实文件更新 | 不重新编码第二份 Blob;不保留完整导出历史;不影响另一格式;幂等重试不重复计量 | `download.json``request.json``response.json``db-diff.json``fs-before.json``fs-after.json` |
| `TDD-WP2-EXP-001-result-matrix` | EXPORT-05、06、AC-35、55 | 参数化:合成失败、下载失败、project 保存失败、latest 持久化失败、storage full/unavailable | 1. 在 UIDesign 四结果组合及存储状态下导出;2. 记录下载/上传调用顺序;3. 重读项目/latest | 分别显示冻结结果文案;可合成时允许规定本地下载;下载失败可复用同一次合成;后端失败明确 latest 未更新 | 失败分支保留旧 latest;project 保存失败不写新 statelatest 失败不写 latest/managed file;成功分支才更新 | 不撤回已下载文件;不以本地下载覆盖后端记录;不显示双成功假状态;不把 Blob 存浏览器持久缓存 | 每参数 `network-timeline.json``response.json``db-diff.json``trace.zip``screenshots/result.png` |
| `TDD-WP2-PROJ-004-conflict-export-once` | PROJECT-07、EXPORT-05、AC-36 P0-A 子集 | conflicted 标签页,`conflict_export_used=false` | 1. 本地导出一次(成功和失败各参数);2. 再次尝试;3. 刷新最新版本 | 第一次结束后立即禁用再次导出;结果明确“仅下载本页版本,未写入项目”;刷新到后端最新状态 | 所有步骤不调用 latest APIprojects/project_states/latest_exports 无变化;flag 只在页面内存 | 不因第一次失败允许第二次;不覆盖后端、不另存副本、不把 flag 持久化 | `network-timeline.json``db-diff.json``trace.zip``screenshots/conflict-export.png` |
#### 6.9.10 WP-0 其余测试族规范 case
| case_id | 需求 / AC | fixture 与前置 | 编号操作步骤 | 预期响应 / UI | DB / 文件变化 | 禁止副作用 | 证据文件 |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `TDD-WP0-DEP-001-frozen-toolchain` | NFR-09、DevelopmentPlan 2.1、13.1 | 冻结 lockfile、干净离线依赖缓存、win-x64 runner | 1. 严格按 lockfile 安装;2. 编译 TypeScript3. 最小加载 React/Fabric/Fastify/better-sqlite34. 用随包 Node 运行 native smoke | 所有命令退出 0,版本与 lockfile/DevelopmentPlan 一致 | 只生成声明的构建缓存与报告;业务 DB/LocalDataRoot 无变化 | 不升级/替换依赖、技术栈或 native 模块;不联网漂移解析版本 | `commands.json``dependency-tree.json``native-smoke.json` |
| `TDD-WP0-API-001-schema-envelope` | NFR-05、DevelopmentPlan 6.1、6.6 | 生成 TypeBox/OpenAPI/客户端 snapshot;合法与非法 fixture | 1. 生成 OpenAPI 3.12. 比较 snapshot3. 对每个 operation 注入合法/非法请求;4. 校验错误信封和 correlation ID | Schema/运行/客户端一致;非法响应使用冻结 HTTP/code/message_key/details 白名单 | 只读场景 DB 无变化;写场景失败按各 operation 事务回滚 | 不出现未声明字段/枚举、供应商原文、堆栈、路径或缺失 correlation ID | `openapi.json``snapshot-diff.json``response.json``redaction.json` |
| `TDD-WP0-BND-001-loopback-origin` | NFR-09、AC-24 | API 未启动;回环、局域网、错误 Host/Origin 和预检请求参数 | 1. 启动产品包;2. 检查监听地址;3. 从允许/拒绝地址请求;4. 发送跨源/CORS 预检 | 只接受 `127.0.0.1:43121` 与同源;其他路径连接失败或按门禁拒绝 | 无业务表、会话、外部用量变化 | 不监听 `0.0.0.0`/IPv6 wildcard,不建防火墙入站规则,不返回 CORS 放行头 | `socket-listeners.json``response.json``firewall-diff.json` |
| `TDD-WP0-BRW-001-real-supported` | NFR-01、AC-24、41 | 最终 `RELEASE.json`;真实 Chrome/Edge 独立 profile | 1. 分别启动记录的 full version2. 完成 UA-CH support check3. 进入 bootstrap/login4. 复查产品 API | 两浏览器均通过并建立短期支持 cookie,产品 API 每次复核品牌/major | 仅门禁所需非敏感记录/cookie;不建用户 session | 不用 Playwright Chromium、伪 UA 或前一 major 代替;不联网查询当前版本 | 每浏览器 `environment.json``response.json``trace.zip``screenshots/supported.png` |
| `TDD-WP0-BRW-002-hard-block` | NFR-01、AC-24 | 参数化其他品牌、错误 major、非 Windows、UA-CH 缺失/冲突/解析失败和原始 API 客户端 | 1. 请求 support check2. 请求 HTML/bundle/API3. 尝试登录/项目/后台深链;4. 查 DB/外部调用 | 全部 426 `BROWSER_UNSUPPORTED`,reason 仅四类,页面只有重新打开说明且无绕过 | 业务 DB 读取/写入计数为 0,session/验证码/资源读取均无变化 | 不返回继续令牌/参数,不发 Resend/AI/高德,不泄露产品数据 | 每参数 `response.json``db-access.json``external-calls.json``screenshots/blocked.png` |
| `TDD-WP0-SUP-001-lifecycle` | NFR-09、AC-24、41 | 真实便携包;参数化第二实例、43121 占用、API/Worker 崩溃、正常退出 | 1. 双击启动;2. 注入各故障;3. 观察托盘/重启次数;4. 退出并等待 15 秒 | 单实例;端口占用明确失败且不换端口;有限重启/degraded/允许动作符合契约;退出后无残留 | 仅规定日志/状态记录;正常退出 flush WAL/队列;不改业务真相 | 不无限重启、不杀无关进程、不弹隐藏控制台、不留下 API/Worker/端口 | `process-tree.json``port.json``supervisor-events.json``screenshots/system-ui.png` |
| `TDD-WP0-DATA-001-root-validation` | NFR-09、17.19、AC-41、55、56 | 参数化 repo/project/download/只读素材根、合规外部目录、已初始化后缺失目录 | 1. 用离线配置设置各路径;2. 启动;3. 删除已初始化根后重启;4. 调资源 API | 违规路径拒绝;合规路径可写;缺失根进入 data_missing 且不自动重建;浏览器只见逻辑资源 ID | 仅合规首次初始化创建固定目录/DB;失败不创建散落目录 | 不硬编码用户名、不回显绝对路径、不在源码/下载/素材根写业务数据 | `config-result.json``fs-before.json``fs-after.json``response.json` |
| `TDD-WP0-DATA-002-no-backup-migration` | AUTH-06、NFR-09、AC-24、56 | 有账号/项目/下载成品的 LocalDataRoot | 1. 搜索 UI/API/包功能;2. 尝试导出项目包/DB/账号/点数;3. 删除根/重装;4. 启动模拟 P0-B 导入 | 仅原图/JPG/PNG 下载可用;固定本机提示持续显示;删除/重装不可恢复;无迁移入口 | 下载目录文件保留;业务数据删除后不被重建或导入 | 不自动备份、不生成迁移包、不把下载图识别为正式迁移数据 | `route-inventory.json``package-scan.json``trace.zip``screenshots/fixed-copy.png` |
| `TDD-WP0-STO-001-byte-thresholds` | 17.19、AC-55 | 第 5.5 节七个精确字节 fixture,可写目录 | 1. 对每个值重计量;2. 读取 storage state/API/UI3. 重复计算 | normal/warning/critical/full 边界逐字节正确,重复结果确定 | 只更新 measured bytes/notice/status/last_measured_at;无 managed file 变化 | 不用浮点比例导致错一字节,不自动删除,不把 warning/critical 当阻断 | 每值 `result.json``db-diff.json``screenshots/capacity-bar.png` |
| `TDD-WP0-FILE-001-atomic-commit` | NFR-05、17.19、AC-44、55 | 受控文件驱动;合法/错误 MIME、hash、`..`、符号链接逃逸;各崩溃点 | 1. 流式写 staging2. 校验/预留;3. 在改名/DB 提交前后终止;4. 重启对账 | 非法输入拒绝;成功对象完整可读;崩溃后恢复为已提交或补偿清理,不出现半对象 | reservation、managed_files、staging/正式文件按提交点一致;启动对账修复计量 | 不接受扩展名猜 MIME、不越根、不产生可读半文件/孤儿引用/重复计量 | `response.json``crash-points.json``db-diff.json``fs-before.json``fs-after.json` |
| `TDD-WP0-CACHE-001-public-lru` | NFR-07、17.14、AC-46、48 | 公开资源超过 157,286,400 字节;internal/private/project/提示词 fixture | 1. 按访问顺序填充 public cache2. 触发写前 LRU;3. 尝试缓存私有类型;4. 枚举 SW/Cache/IndexedDB | 总量不超上限,淘汰最久未用且当前资源可重取;私有类型均不缓存 | `public_asset_lru` 只含公开元数据;业务 DB 无变化 | 不缓存 internal preview、私有内容、项目、验证码、session、未保存编辑;不计用户下载 | `cache-enumeration.json``lru-trace.json``trace.zip` |
| `TDD-WP0-EVT-001-rest-refetch` | PROJECT-04、GEN-14、NFR-05、DevelopmentPlan 6.2、AC-20、30 | 可控 SSE 序列:正常、断线、event_id 缺号、runtime/config 变化 | 1. 订阅 `/api/v1/events`;2. 注入事件;3. 断线/缺号;4. 观察 REST 请求和 UI | 事件只提示 ID/type/entity/version/time;每次由 REST 取真相;断线/缺号 bootstrap/refetch | SSE 不直接写业务表;REST 读取后前端状态更新 | 不携带正文/图片/凭据,不把 runtime 事件写 ModelConfig,不抢焦点 | `sse-events.json``network-timeline.json``trace.zip` |
| `TDD-WP0-SEC-001-credential-channel` | PRIV-01、AC-41 | Windows Credential Manager 测试条目;API/Worker 各需不同凭据;缺失/清除参数 | 1. Supervisor 读取;2. 通过对应匿名管道启动子进程;3. 检查命令行/环境/句柄;4. 清除并重启 | 仅目标子进程能在内存读取一次;缺失时服务明确不可用/启动失败 | 不写业务 DB;只写非敏感状态与日志 | 凭据不进参数、环境、文件、日志、崩溃诊断;API 不获网关密钥,Worker 不获 Resend/高德密钥 | `process-env-scan.json``pipe-acl.json``redaction.json` |
| `TDD-WP0-LOG-001-rotation-redaction` | PRIV-01、02、NFR-09、AC-41、56 | 生成超过 10 MiB/10 文件/30 天日志;注入密钥、邮箱、提示词、路径和写失败 | 1. 写边界日志;2. 触发轮转/保留清理;3. 复制诊断;4. 使目录不可写并尝试 AI | 轮转严格为 10 MiB/10 文件/30 天;诊断只含白名单;写失败进入 unavailable 并阻断 AI | 只更新日志文件和非敏感状态;不改业务对象 | 不记录敏感正文/绝对路径/供应商原文,不在日志失败时继续 AI | `log-manifest.json``redaction.json``response.json``external-calls.json` |
| `TDD-WP0-PKG-001-portable-zip` | NFR-01、09、AC-24、41、56 | release build、空白 Windows 用户目录 | 1. 生成 win-x64 ZIP/哈希/START-HERE/RELEASE2. 解压非仓库目录;3. 离线启动;4. 扫描制品和端口 | 包含 Dada.exe、随包 Node/native 依赖并固定 43121;说明与 RELEASE 一致 | 仅合规 LocalDataRoot 生成运行数据;制品本身不可变 | 不含真实配置/凭据/用户名/LocalDataRoot,不动态换端口,不要求 Docker/安装器 | `package-manifest.json``sha256.txt``package-scan.json``process-tree.json` |
#### 6.9.11 WP-1 其余测试族规范 case
| case_id | 需求 / AC | fixture 与前置 | 编号操作步骤 | 预期响应 / UI | DB / 文件变化 | 禁止副作用 | 证据文件 |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `TDD-WP1-NOTICE-001-registration-consent` | AUTH-03、PRIV-04、06、AC-01、41、56 | 当前告知 version/effective_at/hash;缺字段、未勾选和完整参数 | 1. 展开注册第二阶段;2. 检查全部处理方/用途/本机风险;3. 未同意提交;4. 同意当前 version 注册 | 未同意/版本错误不能注册;完整告知原样可达;成功进入创作 | 失败无 user/consent/invite/credit;成功记录 consent version/time 与账号同事务 | 不省略 AI/Resend/高德、DYN004、管理员访问、注销、180 天、无备份/迁移;不预勾选 | `response.json``db-diff.json``screenshots/notice-expanded.png` |
| `TDD-WP1-SLOT-001-stage-limit` | AUTH-01、NFR-04、AC-32、45 | 参数化 9/10 个 active+suspended 普通账号、deleted、多个 super_admin | 1. 发码前尝试第 10/11 人;2. 在发码后并发占满名额再完成;3. 删除普通账号再注册 | 第 10 人可成功,第 11 人发码/完成均阻断;deleted 释放;super_admin 不占名额 | 成功时计数/账号/邀请原子;失败零变化;累计统计保留 deleted | 不把 suspended/deleted/super_admin 计错,不通过并发超 10,不让失败占邀请码 | `response.json``db-diff.json``concurrency-trace.json` |
| `TDD-WP1-ADM-001-admin-auth-boundary` | AUTH-07、AC-49 | 白名单/非白名单邮箱、已存在普通账号冲突、两个 admin、全部停用状态 | 1. 后台发码;2. 首次验证/后续登录;3. 尝试普通 session 访问后台;4. 停用全部并尝试自助恢复 | 非白名单发码前拒绝;白名单首次建 super_admin;audience 严格分离;无最后管理员保护且无自助恢复 | 允许分支建 admin/session/审计;拒绝分支不建 challenge/session;停用撤销 session | 不泄漏白名单、不使用邀请码/普通名额、不原地提升普通账号、不创建部署人员角色 | 每参数 `response.json``db-diff.json``external-calls.json``trace.zip` |
| `TDD-WP1-CFG-001-secure-revision` | AUTH-07、ADMIN-09、AC-49、50 | current applied revision N;参数化完整 N+1、HMAC/凭据缺失、身份冲突 | 1. 离线写候选安全配置;2. API 启动应用;3. 查询 apply state/session4. 注入失败重试 | 完整配置原子生效并推进 revision;失败保持 N 并阻止受影响入口 | 成功同事务更新 apply state、撤销受影响 session、写 system/backend_secure_config 审计;失败全回滚 | 不写白名单/HMAC/pepper/人员身份/凭据,不出现半生效配置 | `config-result.json``db-diff.json``redaction.json` |
| `TDD-WP1-DEL-001-account-delete` | AUTH-06、AC-22、39、56 | 用户拥有项目、参考/生成/导出、坐标、点数、多个 session | 1. 确认注销;2. 并发调用受保护 API;3. 尝试旧资源 ID/恢复;4. 用新邀请重新注册同邮箱 | 注销成功后所有旧访问立即 401/404;不可恢复;新注册是全新账号 | 同事务撤销 sessions、删除身份/业务关系/未用点数并创建允许匿名事件;物理文件排队 | 不延迟撤权到物理清理,不恢复旧 ID/点数/资料,不保留邮箱映射到匿名主体 | `response.json``db-diff.json``worker-events.json``trace.zip` |
| `TDD-WP1-DEL-002-anonymous-retention` | AUTH-06、17.10、17.11、AC-22、39 | 注销前有生成/点数事件和私有访问审计;可控 180 天时钟 | 1. 注销;2. 检查匿名字段;3. 在到期前尝试管理删除;4. 到期运行 Worker | 匿名事件只含允许统计;访问审计目标不可反推;到期前不可删,到期后系统清理 | 原身份关联移除;随机匿名主体无映射;expires_at 不因注销延长;到期行删除 | 不保留邮箱/IP/提示词/图片/项目/job 原 ID,不让管理员手删或延长期限 | `db-diff.json``retention-events.json``redaction.json` |
| `TDD-WP1-AUD-001-immutable-triggers` | ADMIN-05、09、17.10、17.16、AC-50 | 各一条 AdminOperationLog/PrivateContentAccessLog;普通、retention、privacy purge 连接 | 1. 普通连接 UPDATE/DELETE2. 伪造 purge 函数;3. 合法作用域在到期/注销执行 | 普通和伪造操作被 trigger 阻断;仅注册作用域执行规定清理 | 非法尝试零变化;合法作用域只删除/匿名化允许行并留非敏感结果 | 不允许后台/API 任意修改,不混用两类日志,不把正文写审计 | `sql-results.json``db-diff.json``trigger-definition.json` |
#### 6.9.12 WP-2 其余测试族规范 case
| case_id | 需求 / AC | fixture 与前置 | 编号操作步骤 | 预期响应 / UI | DB / 文件变化 | 禁止副作用 | 证据文件 |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `TDD-WP2-PROJ-001-new-versus-continue` | PROJECT-01 至 03、GEN-04、11、AC-03、06、34 | `FX-PROJECT-ACTIVE-19`、一个 9 图固定比例项目 | 1. 工作台新建两次;2. 在第一个项目继续生成;3. 尝试改比例;4. 补到 10 图再继续 | 两次新建为两个 project;继续只新增 job/history且沿用比例;10 图阻断追加但可另建 | projects +2;继续分支 project 数不变;成功 history 正确关联 | 不默认追加最近项目、不创建第三项目、不改变既有项目比例、不让历史上限阻断其他新建 | `response.json``db-diff.json``trace.zip` |
| `TDD-WP2-PROJ-003-trash-restore-purge` | PROJECT-03、08、AC-21 | active project、`FX-PROJECT-ACTIVE-20`、可控 720 小时时钟 | 1. 移入回收站;2. 满 20 时恢复;3. 释放名额再恢复;4. 主动永久删除;5. 另一项目推进到 purge_at | 满额恢复阻断且时间不变;释放后可恢复;purge 后立即不可见/不可恢复 | status/deleted_at/purge_at 精确;purged 撤销访问关系并排物理清理 | 不提前物理结果、不因恢复重算 purge_at、不让 purged 资源可读 | `response.json``db-diff.json``worker-events.json` |
| `TDD-WP2-PROJ-005-failed-draft-retry` | PROJECT-01、GEN-09、AC-04、43 | `FX-PROJECT-EMPTY` 和含成功图项目 | 1. 失败页重试两次;2. 返回工作台主动新建;3. 多选批量删除 failed_empty 与成功项目 | 重试只在原 project 新增 job;主动新建才新 project;批量只接受无成功图草稿 | 原草稿保留 prompt/configjob 数递增;批量合格项 trashed,成功项目不变 | 不把重试变新项目、不把成功项目纳入快捷批量、不丢草稿输入 | `response.json``db-diff.json``trace.zip` |
| `TDD-WP2-REF-001-reference-lifecycle` | GEN-05、PROJECT-09、AC-44、48 | 新上传、同项目已有 reference、他人/跨项目/撤销/已 purge reference | 1. 提交前在页面选择/移除;2. multipart 新上传;3. 同项目复用;4. 尝试越权/失效;5. 删除项目 | 提交前仅内存;每 job 独立关系快照;仅同 owner+project 可复用;purge 后立即不可读 | 成功建立 job reference/managed file;移除前无文件;purge 撤销关系并排清理 | 不修改旧任务关系、不跨用户/项目复用、不缓存预提交图片、不暴露路径 | 每参数 `response.json``db-diff.json``fs-after.json``cache-enumeration.json` |
| `TDD-WP2-EXP-002-empty-latest` | EXPORT-04、AC-23、38 P0-A 子集 | 从未导出但有成功原图的项目 | 1. 打开项目详情/latest 区;2. 请求 JPG/PNG latest3. 使用允许动作 | 显示“暂无导出成品”,只提供进入编辑并导出和下载原始生成图 | latest_exports 无行且不因读取创建;原图读取只读 | 不自动合成/保存默认成品,不伪造跨设备下载,不改变项目 | `response.json``db-diff.json``trace.zip``screenshots/no-export.png` |
| `TDD-WP2-BAL-001-balance-impact` | GEN-14、15、17.17、AC-04、51 | 参数化 model/account/unknown 影响;活动任务与空闲模型 | 1. 注入余额不足;2. 结算已提交任务;3. GET models/后台;4. 重复事件;5. 未确认恢复前尝试启用 | 影响范围按规则;任务 failed+release 一次;runtime 不可用;恢复前操作阻断 | GatewayBalanceState/affected models/runtime/审计更新;ModelConfig hash 不变 | 不自动充值、不用 Dada 点数继续调用、不重复 release、不自动恢复或改默认/priority | `response.json``db-diff.json``worker-events.json``external-calls.json` |
#### 6.9.13 WP-3 其余测试族规范 case
| case_id | 需求 / AC | fixture 与前置 | 编号操作步骤 | 预期响应 / UI | DB / 文件变化 | 禁止副作用 | 证据文件 |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `TDD-WP3-ADP-001-per-model-contract` | GEN-01、02、05、06、13、16、AC-40 | 三个模型各自独立 mock/真实受控账号和脱敏 fixture | 1. 每模型分别执行纯文字、合法参考图、四比例;2. 覆盖同步/异步与查询;3. 验证 MIME/尺寸/单图;4. 注入九类映射 | 每模型只按自身已证实协议成功;结果恰一图;错误映射到固定 category | 真实验证只写对应 model/config_version 的 evidence 状态;任务/点数按独立用例结算 | 不从一个模型推断另两个、不共享 verified 标记、不接受多图/未知字段、不记录原始响应 | 每模型 `contract-matrix.json``response.json``external-calls.json``redaction.json` |
| `TDD-WP3-ADP-002-invalidate-revalidate` | GEN-13、15、ADMIN-03、AC-30、40 | 三模型 verified;参数化端点/字段/参考图/响应/异步/查询/error protocol 变化 | 1. 改一个契约字段并保存;2. 尝试生成;3. 仅为该模型重跑完整矩阵;4. 再尝试 | 变化后立即 unverified/不可新建;完整独立重验后才恢复 verified | 新 config_version;对应 runtime/evidence 版本更新;其他模型证据不变 | 不沿用旧证据、不局部 smoke 即 verified、不暗改 enabled/default/priority | 每字段 `db-diff.json``contract-matrix.json``response.json` |
| `TDD-WP3-BAL-001-confirmed-recovery` | GEN-14、15、ADMIN-09、AC-51 | 余额导致 model/account/unknown runtime unavailable;无/有真实恢复确认 | 1. 未确认时点击恢复;2. 执行非敏感健康检查;3. admin 明确确认;4. 重新计算所有阻断原因 | 无确认失败;确认真实恢复后只解除已满足原因,仍有其他原因则继续 unavailable | recovery check、GatewayBalanceState/runtime/AdminOperationLog 同事务更新;config set 不变 | 不自动恢复/充值,不补扣用户点数,不清除仍存在的 contract/Worker 阻断 | `response.json``db-diff.json``external-calls.json``screenshots/recovery.png` |
#### 6.9.14 WP-4 其余测试族规范 case
| case_id | 需求 / AC | fixture 与前置 | 编号操作步骤 | 预期响应 / UI | DB / 文件变化 | 禁止副作用 | 证据文件 |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `TDD-WP4-BG-001-switch-background` | GEN-12、EDITOR-01、PROJECT-04、AC-07 | 画布含文字/贴纸/色卡/动态元素和已调底图,项目有第二历史图 | 1. 记录覆盖元素;2. 确认换底图;3. 检查参数/色卡;4. 撤销/重做;5. 重开 | 覆盖层位置尺寸保留;底图处理重置;五色重提;一次撤销且重开一致 | 完整 Canvas state 自动保存并 state_version 递增 | 不删除/缩放覆盖层,不保留旧调色,不产生多次撤销或改项目比例 | `canvas-before.json``canvas-after.json``db-diff.json``trace.zip``pixel-diff.json` |
| `TDD-WP4-BG-002-processing-controls` | EDITOR-01 至 03、PROJECT-04、EXPORT-03、AC-07、20、23 | 固定底图和像素基线 | 1. 参数化填充/适配/裁剪/缩放/定位及亮度、对比度、饱和度、色温、锐度、预设滤镜;2. 完成;3. 撤销;4. 恢复原图;5. 导出/重开 | 每项预览与提交值一致;完成一次撤销;导出/重开一致;恢复原图只清底图处理 | 每次提交进入保存的 Canvas JSON;取消无 DB 变化 | 不影响覆盖元素,不持久化未提交预览,不用静默超范围值 | 每参数 `canvas-state.json``db-diff.json``pixel-diff.json``trace.zip` |
| `TDD-WP4-CAN-001-fifty-elements` | EDITOR-04 至 09、17.5、AC-27 | `FX-CANVAS-50`,稳定选择重叠 fixture | 1. 添加到 50;2. 循环选择/长按候选/框选/多选移动;3. 层级/复制/删除;4. 保存/导出;5. 添加第 51 | 前 50 可编辑保存导出;选择顺序确定;第 51 明确阻断且布局不跳 | Canvas 最多 50,成功操作各一次撤销/保存;第 51 无 state change | 不出现图层面板/隐藏/锁定/编组,不因阻断损坏已有元素 | `canvas-state.json``db-diff.json``trace.zip``performance.json` |
| `TDD-WP4-TXT-001-multiline-template-switch` | TEXT-01 至 06、PROJECT-04、AC-09、10 | 多行文字模板实例、固定变换 | 1. 输入含换行文字并完成;2. 切换另一模板;3. 撤销/重做;4. 重开/导出 | 换行持久;空内容按规则拒绝;切模板重置默认样式但保留位置/旋转/整体尺寸 | 每次完成一条撤销并保存最新 element snapshot | 不把切模板重置变换、不裁切内容、不保存取消编辑 | `canvas-state.json``db-diff.json``pixel-diff.json``trace.zip` |
| `TDD-WP4-TXT-002-font-metrics-ranges` | TEXT-07 至 14、NFR-02、AC-11、12 | 真实归档字体/缺失字体、最长文字、属性边界/步长 | 1. 等待 FontFace ready2. 参数化字号/缩放/行距/字距/描边/颜色/背景透明度;3. 完成;4. 缺字体加载;5. 重开/导出 | 字号与控制柄同步,重新测量不裁切;范围步长精确;缺字体模板 unavailable;背景透明度不影响文字 | 完成一次撤销/保存;缺字体分支无 Canvas 新元素 | 不静默系统字体回退、不把背景透明度应用文字、不超范围取值 | `font-load.json``canvas-state.json``pixel-diff.json``trace.zip` |
| `TDD-WP4-TXT-003-catalog-search-recent` | TEXT-01、17.6、AC-09、32 | P0-A 32 文字模板、四分类、稳定 catalog、两个用户 | 1. 未选模板尝试加文字;2. 按显示名称搜索;3. 检查分类/顺序;4. 使用模板;5. 切换账号读最近使用 | 只能从模板添加;四类/显示名搜索/稳定顺序正确;最近使用按账号隔离 | 只在成功使用后更新 `recent_assets`;未选/搜索不写项目 | 不提供独立普通文字,不复用普通贴纸搜索,不跨账号共享最近项 | `catalog.json``response.json``db-diff.json``trace.zip` |
| `TDD-WP4-STK-001-transform-sticker` | EDITOR-04 至 09、PROJECT-04、AC-13、27 | 普通贴纸元素和重叠选择 fixture | 1. 移动/缩放/旋转/翻转/透明度;2. 复制/层级/多选;3. 撤销重做;4. 保存/导出重开 | 画布、属性控件、导出和重开状态一致;每次连续手势是一项撤销 | Canvas element snapshot/state_version 按提交更新 | 不改变 stable ID/resource_version,不引入隐藏/锁定/编组,不丢选择 | `canvas-state.json``db-diff.json``pixel-diff.json``trace.zip` |
| `TDD-WP4-COL-001-deterministic-palette` | COL-01 至 05、AC-15 P0-A 子集、16 | 固定解码底图、`mmcq-v1`、四个 P0-A 色卡 | 1. 重复提取五色;2. 调裁剪/调色/滤镜;3. 换原始底图;4. 渲染四布局;5. 监控网络 | 同图五色/顺序确定;处理不刷新;换底图刷新;四色卡消费同一五色 | 五色快照只在底图更换提交时保存;无上传文件 | 不读取处理后像素/额外图片,不联网提色,不启用白名单外色卡 | `palette.json``network-timeline.json``pixel-diff.json``db-diff.json` |
| `TDD-WP4-DYN-001-fixed-time` | DYN-01 至 03、AC-14 | 可控时钟、DYN001-003/007/008/011 fixture | 1. 插入/确认动态值;2. 推进墙钟;3. 重开;4. 导出 | 使用插入/确认时快照,重开/导出不随当前时间变化;结果静态不可点击 | 动态实例只保存允许字段和值快照 | 不保存定时器/外部链接,不在重开重新计算,不输出 GIF/视频 | `canvas-state.json``clock-trace.json``pixel-diff.json` |
| `TDD-WP4-DYN-002-location-consent` | DYN-04、PRIV-03、AC-17、47 | DYN004 固定坐标/高德 mock;同意、拒绝、服务暂停参数 | 1. 插入前检查 Dada 提示;2. 拒绝;3. 同意并定位/逆地理;4. 手动地点回退;5. 删除元素/注销 | 拒绝调用 0 且不插入坐标;同意后正确显示;暂停时自动定位禁用但手动地点可用 | 只在同意后把坐标/文本存实例;删除/注销按规则清理 | 前端不持高德密钥,不把坐标用于其他元素/用户,不在提示前调用 | `external-calls.json``canvas-state.json``db-diff.json``trace.zip` |
| `TDD-WP4-DYN-003-identity-font` | DYN-05 至 09、AC-18、19 | 用户社交 ID 含/不含 `@`;实例覆盖;DYN012/FONT081 | 1. 插入身份贴纸;2. 修改实例覆盖;3. 检查账号资料;4. 插入 DYN012;5. 重开/导出 | 最多一个前导 @;实例覆盖独立且不改账号;DYN012 使用 FONT081 并明确标注替代 | 只保存实例 snapshotUserProfile 不变 | 不声称 DIN 字体、不静默回退、不执行 Lua/Prefab、不把覆盖写账号 | `canvas-state.json``db-diff.json``font-load.json``pixel-diff.json` |
| `TDD-WP4-A11Y-001-keyboard-focus` | NFR-06、AC-32 | 第 11.1 节全部 page_id,最长文案、弹层和 200% 缩放 | 1. 仅键盘走主流程;2. 检查 skip link/focus order/弹层返回;3. 触发 live region4. 运行 axe;5. 测画布等价控件 | 所有动作可达,焦点不逃逸/不被 SSE 抢走;critical/serious 为 0;状态非仅颜色 | 无非用户触发业务写;可访问性扫描只产证据 | 不用正 tabindex、不隐藏主动作、不以缩小字体规避 200% 问题 | 每页 `axe.json``focus-trace.json``trace.zip``screenshots/200pct.png` |
| `TDD-WP4-VIS-001-browser-diff` | NFR-02、AC-19、23、32 | 最终 RELEASE Chrome/Edge、固定 Canvas/字体/动态值/DPR | 1. 等待资源稳定;2. 两浏览器截图/导出;3. 计算元素边界/显著像素;4. 人工复核已知替代 | 结构、字体、换行、颜色、描边、装饰在第 10.1 阈值内;替代项明确标记 | 只生成视觉证据;业务状态使用同一 fixture | 不用低保真 PNG 作像素基准,不用阈值掩盖缺字体/空画布,不冒充全 P0-B | 每场景 `pixel-diff.json``layout-boxes.json``manual-review.json`、截图 |
| `TDD-WP4-PERF-001-budget` | NFR-03、AC-27、32 | `FX-CANVAS-50`、1,407 贴纸目录、四比例导出、最终浏览器 | 1. 预热一次;2. 每预算运行五次;3. 记录 p50/p95/max/memory/DOM4. 注入导出失败 | 第 10.2 节所有预算通过;失败不破坏 project/latest;资源面板节点不线性增长 | 只生成性能数据;失败分支 DB/latest 不变 | 不提高超时、减少元素/素材或降低尺寸掩盖失败,不用 Chromium 替代真实浏览器 | `performance.json``memory.json``dom-count.json``environment.json` |
#### 6.9.15 WP-5 全部测试族规范 case
| case_id | 需求 / AC | fixture 与前置 | 编号操作步骤 | 预期响应 / UI | DB / 文件变化 | 禁止副作用 | 证据文件 |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `TDD-WP5-CAT-001-catalog-1407` | STATIC-01 至 04、AC-13、32 | 规范 catalog/metadata,含 3 组重复图和 part1-25 | 1. 编译/加载目录;2. 校验 ID/part/顺序/计数;3. 滚动全目录;4. 观察原图请求/DOM | 恰 1,407 项,重复图片保留不同稳定 ID;虚拟列表稳定,原图按需加载 | 只读 catalog;不复制或改写源;浏览器仅缓存允许公开项 | 不去重业务 ID、不一次下载全量、不暴露绝对路径、不线性增长 DOM | `catalog-validation.json``network-timeline.json``dom-count.json``source-hashes.json` |
| `TDD-WP5-MAN-001-readonly-compiler` | TEXT-01 至 TEXT-14、COL-01 至 COL-05、DYN-01 至 DYN-09、STATIC-01 至 STATIC-04、AC-32、42 | 只读规范入口与独立输出目录;Lua/Prefab/证据目录 trap | 1. 记录源 hash/mtime;2. 运行声明式转换;3. 注入尝试执行 Lua/Prefab/读取证据目录;4. 复核输出 | 只消费允许 catalog/metadata/资源;非法入口失败;输出 manifest 可重现 | 只写版本化派生输出;源 hash/mtime 零变化 | 不执行 Android runtime、不扫描证据目录、不复制约 3.57 GB 全库、不覆盖源 | `compiler-report.json``source-before.json``source-after.json``output-manifest.json` |
| `TDD-WP5-WHITE-001-p0a-allowlist` | TEXT-01、TEXT-04、COL-01、DYN-01、DYN-08、STATIC-01、ADMIN-07、NFR-02、NFR-07、AC-32、42、48 | 普通用户无 grant、预览用户有单批 grant、注册全量模板 | 1. 分别读取公开/预览 manifest;2. 搜索白名单外 ID;3. 浏览所有面板 | 普通用户仅 32 文字、实际字体+FONT081、4 色卡、10 动态、1,407 贴纸;预览只多授权批次 | 只读;授权不改变 role | 不向普通用户暴露白名单外 passed/internal,不提前开放 P0-B,不遗漏白名单;AC-54 为未来 P0-B,不进入本 case 的当前验收或 passed 关联 | `manifest.json``response.json``trace.zip``screenshots/allowlist.png` |
| `TDD-WP5-COL-001-four-layouts` | COL-01 至 05、AC-15 P0-A 子集、16、32 | COLOR001/002/008/016 与同一五色 fixture | 1. 渲染四卡;2. 改裁剪/滤镜;3. 换底图;4. 尝试访问其他色卡 | 四布局消费完全相同五色;刷新规则一致;其他色卡不可发现 | 只保存稳定 ID/version/五色 snapshot | 不启用其余 12 色卡、不各自重新提色、不联网读取图片 | `palette.json``pixel-diff.json``manifest.json` |
| `TDD-WP5-RES-001-three-access-classes` | PRIV-02、05、17.12、13、AC-42、48 | public、internal_preview、private 资源及不同主体/grant | 1. 请求三类 manifest/资源;2. 推导 stable ID/路径;3. 撤销 grant 后重取;4. 检查 headers/缓存 | public 可匿名于产品授权范围读取;preview 每次验 grant/no-storeprivate 仅所有者/受控 admin;越权 403/404 | 读取无业务写,访问审计仅私有完整内容规定入口产生 | 不混路由/cache header,不返回路径/internal manifest,不用公开 ID 推导私有对象 | 每主体 `response.json``headers.json``cache-enumeration.json` |
| `TDD-WP5-UPL-001-upload-metering` | ADMIN-06、17.7、17.19、AC-31、55 | PNG/WebP、坏 MIME/解码、稳定 ID 冲突;active/full/unavailable;原图+缩略图字节 | 1. multipart 上传并指定 part/order;2. 生成缩略图;3. 注入非法/冲突/容量状态;4. 重计量 | 合法上传成功并可上架;非法/冲突明确失败;full/unavailable 阻断写但已有资源可读 | 原图/缩略图分别 reservation/managed_file 并计量;失败零文件/目录项 | 不用扩展名替代解码、不漏计缩略、不部分覆盖稳定 ID、不在 full 写入 | `response.json``db-diff.json``fs-before.json``fs-after.json` |
| `TDD-WP5-CLN-001-reference-race` | ADMIN-06、09、17.7、17.19、AC-31、50、55 | 查询时整批文件全部无任何发布/项目/待发布引用 | 1. 查询候选并由 admin A 创建 intent;2. 参数化确认前新增各类引用/快照过期/admin 变化;3. A 确认;4. 无竞态分支确认并运行 Worker | 任一新增引用整批 409 conflict+denied 审计;stale 重新查询;无竞态才整批排队/物理删除/重测 | 冲突 bytes/状态/queue 不变;成功排队不减量,物理后同事务更新并记录全阶段审计 | 不让查询时有引用项入候选、不由 admin B 确认、不部分排队、不自动清理或提前减量 | 每参数 `response.json``db-diff.json``worker-events.json``fs-after.json` |
| `TDD-WP5-PREV-001-grant-lifecycle` | ADMIN-07、17.13、AC-42、48 | 普通账号,active/revoked/expired grant,已有项目引用预览版本 | 1. admin 授权批次;2. 用户读取/添加;3. 撤销/到期;4. 再读取/添加;5. 重开已有项目 | active 时只见授权项;撤销/到期立即阻断新读取/添加;已有项目按保存版本渲染 | grant 状态/审计更新;UserProfile.role 不变;项目引用版本不改写 | 不出现后台权限、不把 preview 写 public manifest/cache、不因撤销破坏既有项目 | `response.json``db-diff.json``cache-enumeration.json``trace.zip` |
| `TDD-WP5-CACHE-001-no-private-client-state` | NFR-07、17.14、AC-46、48 | 已访问 public/preview/private、提示词和项目;注册 SW fixture | 1. 完成各访问;2. 离线/刷新;3. 枚举 SW registrations、Cache keys/body、IndexedDB stores/records | 仅 public allowlist 元数据/资源存在;SW 不拦 preview/private;离线不能恢复私有正文/草稿 | 只允许 `public_asset_lru` 记录;业务 DB 无变化 | 不仅凭响应头判断,不缓存 no-store 内容、项目/提示词/session/验证码/未保存编辑 | `cache-enumeration.json``service-worker.json``trace.zip` |
#### 6.9.16 WP-6 其余测试族规范 case
| case_id | 需求 / AC | fixture 与前置 | 编号操作步骤 | 预期响应 / UI | DB / 文件变化 | 禁止副作用 | 证据文件 |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `TDD-WP6-ADM-001-role-and-summary` | ADMIN-01、02、04、08、AC-25、49 | active/suspended super_admin、普通/预览账号,后台安全摘要 fixture | 1. 各主体请求后台页/API;2. 停用 admin 后复用旧页面;3. 浏览列表/详情;4. 检查私有默认 | 仅 active super_admin 可进入;停用立即退出;默认只见脱敏元数据,普通/预览无后台入口 | 读取无业务变化;拒绝分支无审计/私有读取;规定登录审计独立存在 | 不共享普通/后台 session,不因预览 grant 获后台,不默认返回提示词/图片/密钥 | 每主体 `response.json``db-access.json``trace.zip``screenshots/admin-denied.png` |
| `TDD-WP6-SVC-001-hard-quota` | ADMIN-05、08、NFR-08、17.15、AC-47 | Resend 日 79/80、月 2399/2400;高德 999/1000provider 异常;已有 session | 1. 并发领取最后额度;2. 尝试下一次;3. 注入 provider 故障;4. 检查登录/注册/DYN004/现有 session | 最后一份已领取调用可结束,后续不调用;状态 paused_quota/provider;已有 session 继续;手动地点继续 | `external_service_usage` 事务计数/status/reason 更新;失败不创建验证码假成功 | 不发送第 81/超月/第 1001 次,不影响已有 session,不切付费/备用,不建运行时邮件质量状态 | `response.json``db-diff.json``external-calls.json``concurrency-trace.json` |
| `TDD-WP6-SVC-002-limit-recovery` | ADMIN-08、09、NFR-08、AC-47、52 | paused 服务;参数化下调 hard_limit、提高/付费/自动扩容、新周期/供应商恢复 | 1. 尝试各配置;2. 无确认恢复;3. 建立非敏感恢复检查;4. admin 明确确认 | 只允许不扩大能力的下调并审计;提高/付费/自动扩容拒绝;满足条件+确认后恢复 | 成功下调/恢复与 AdminOperationLog 同事务;非法请求零变化 | 不自动恢复、不保存凭据/供应商原文、不把暂停伪装成功或提高上限 | 每参数 `response.json``db-diff.json``screenshots/service-state.png` |
| `TDD-WP6-AUD-001-sensitive-operations` | ADMIN-09、17.16、AC-50 | 每类规定敏感操作 fixture,含贴纸清理各阶段和 system 配置生效 | 1. 逐类执行成功/拒绝;2. 查询 AdminOperationLog3. 查询 PrivateContentAccessLog4. 尝试改删/越权查看 | 每次操作同事务形成不可变、非敏感 before/after/result;两类审计分离;只有 admin 查看 | AdminOperationLog 条数/actor_type/ref/expires_at 正确;私有打开只写专表 | 不漏失败审计、不重复保存私有正文、不记录白名单/密钥/验证码/路径、不允许人工修改删除 | `operation-matrix.json``db-diff.json``redaction.json``trace.zip` |
| `TDD-WP6-STATE-001-three-model-states` | GEN-15、ADMIN-03、08、AC-30、51 | default 不可用、其他推荐、全不可用、恢复参数 | 1. 打开总览/模型页;2. 注入 runtime 变化;3. SSE 后 REST refetch4. 恢复一个原因 | configured default、逐模型 runtime、recommended 三列独立;推荐可 null;UI 不暗换用户选择 | 只改 runtime/versionModelConfig/current set 无变化 | 不合并三种状态、不把 SSE 当真相、不回写默认/enabled/priority | `response.json``db-diff.json``screenshots/model-columns.png``trace.zip` |
| `TDD-WP6-DIAG-001-redacted-diagnostics` | PRIV-01、02、ADMIN-08、NFR-09、AC-41、55 | 总览/服务/存储/诊断包含所有状态;注入密钥、邮箱、路径、正文 trap | 1. 请求各摘要;2. 复制诊断;3. 检查托盘诊断;4. 跑脱敏扫描 | 仅显示固定非敏感字段、逻辑位置、计量和状态;复制内容一致 | 只读;可记录非敏感诊断访问/时间,不改变服务状态 | 不含密钥/HMAC/邮箱/提示词/图片/绝对路径/供应商正文,不提供隐藏修复写入口 | `response.json``diagnostic.txt``redaction.json``screenshots/diagnostics.png` |
#### 6.9.17 WP-7 全部测试族规范 case
| case_id | 需求 / AC | fixture 与前置 | 编号操作步骤 | 预期响应 / UI | DB / 文件变化 | 禁止副作用 | 证据文件 |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `TDD-WP7-REL-001-final-release-record` | NFR-01、09、AC-24、41 | 最终便携 ZIP、START-HERE、SHA-256、RELEASE,真实验收电脑 | 1. 校验制品 hash2. 导入 Windows build/Chrome/Edge 品牌、major/full version3. 两浏览器跑全部发布 E2E/视觉/性能;4. 比较候选基线 | RELEASE/制品/诊断/门禁值一致;任一漂移使旧视觉/性能失去发布资格并重跑 | 只生成发布记录/证据;业务 fixture 在隔离验收根 | 不缺 RELEASE、不手填假版本、不接受前一 major/Chromium、不动态换端口 | `RELEASE.json``environment.json``package-manifest.json``evidence.json` |
| `TDD-WP7-EXT-001-three-real-models` | GEN-13、AC-40、41 | 三模型各自真实凭据/配置,显式执行确认 | 1. 显示模型/次数/额度;2. 分别执行纯文字、参考图、四比例、sync/async/查询/响应/错误;3. 脱敏;4. 绑定 config_version | 三模型独立完整矩阵通过才解除;任一缺项保持 externally_blocked | 只写对应模型验证证据/runtime 状态和允许审计;测试任务/点数按隔离规则 | 不共享证据、不自动充值、不保留真实输入/图片/原始响应、不在普通 CI 调用 | 每模型 `contract-matrix.json``external-calls.json``redaction.json``manual-review.json` |
| `TDD-WP7-EXT-002-real-resend` | AUTH-01、02、07、AC-01、33、41、47、49 | 已验证发信域名、正式受控普通/admin 邮箱、免费规则记录 | 1. 核 SPF/DKIM/免费规则;2. QQ/163/至少一种企业邮箱各发 20 封;3. 统计 2 分钟送达;4. 真实完成普通/admin 认证 | 每类至少 19/20 在 2 分钟内且两类认证成功;否则阻止发布但不改 runtime service_status | 只保存人工汇总/证据 hash;不建逐封 EmailVerificationEvent | 不用预置账号/mock 冒充,不保存验证码/真实邮箱进证据,不切付费备用 | `delivery-summary.json``domain-check.json``auth-result.json``redaction.json` |
| `TDD-WP7-EXT-003-real-amap` | DYN-04、PRIV-03、AC-17、41、47 | 个人认证高德 Web 服务 Key、安全绑定和显式确认 | 1. 核接口/QPS/白名单/安全/免费规则;2. 执行定位/逆地理;3. 验证 1,000 硬限模拟/受控测试;4. 脱敏坐标证据 | 接口、绑定和应用硬停止通过才解除;失败保持 externally_blocked | 只写非敏感用量/状态/证据;测试坐标不关联真实项目 | 不暴露 Key/真实用户坐标,不发第 1001 次,不启用付费或自动扩容 | `amap-contract.json``external-calls.json``redaction.json``manual-review.json` |
| `TDD-WP7-UI-001-page-state-evidence` | UIDesign 全页、NFR-02、06、AC-24、25、32、41、55、56 | 第 11.1 节 22 page_id 与适用状态、最终 Chrome/Edge、100%/200% | 1. 逐非 N/A 单元运行 case2. 截图/键盘/axe;3. 验证全局容量条;4. 对照 Pencil 相关 frame5. 人工复核 | 所有 UIDesign 页面/状态有实现证据且无重叠;Canvas Index 不实现;无 frame 页面不漏测 | 只生成 UI 证据;交互 DB 变化由所属业务 case 证明 | 不用 PNG OCR 推文案、不把 frame 当页面全集、不以 N/A 逃避容量条/错误态 | 每单元 `trace.zip``axe.json``screenshots/100pct.png``screenshots/200pct.png``manual-review.json` |
| `TDD-WP7-SEC-001-artifact-leak-scan` | PRIV-01、02、NFR-09、AC-41、48、56 | ZIP、源码、Git、DB 配置、manifest、日志、截图、trace/evidence 全集合;秘密 trap | 1. 解包并扫描文本/二进制/元数据;2. 检查 Windows 用户名/绝对路径;3. 检查缓存/凭据位置;4. 人工复核命中 | 真实敏感值和硬编码用户路径零命中;凭据仅 Credential Manager;诊断通过同一扫描 | 只生成扫描报告;不修改被扫描制品 | 不把 allowlist/HMAC/验证码/session/提示词/图片/原始错误写证据,不用删除报告掩盖命中 | `scan-report.json``allowlist.json``manual-review.json` |
| `TDD-WP7-AC-001-trace-structure` | NFR-05、52 当前 AC、109 需求 ID、13 模块、19 契约、9 错误 | 冻结 allowlist、89 family 定义、117 case 定义、页面矩阵和全部 evidence manifest | 1. 运行 `pnpm validate:tdd-trace`2. 注入缺 AC/重复主责/P0-B passed/未知需求/孤儿 family/case 缺字段/错误父族解析;3. 恢复后重跑 | 每种破坏均非零且定位;正确状态返回 0:52 AC、109 唯一 ID、89/89 family 有 case、117 case 字段齐全 | 只生成 family/trace validation manifest;不改业务 DB | 不信任手写合计、不让 AC-08/26/37/54 计 passed、不允许未知父族、删除末段式解析或空 automation/release_gate | `family-manifest.json``trace-validation.json``negative-fixtures.json` |
## 7. 十九个产品数据契约覆盖
| 产品契约 | 主测试族 | 核心不变量 |
| --- | --- | --- |
| 17.1 UserProfile | `TDD-WP1-AUTH-*``TDD-WP6-PRIV-001` | 角色/状态/名额、点数和告知版本字段 |
| 17.2 ModelConfig | `TDD-WP3-MDL-*` | 唯一默认、唯一正整数优先级、配置与 runtime 分离 |
| 17.3 Project | `TDD-WP2-PROJ-*` | 固定比例、版本、上限、草稿、回收站 |
| 17.4 GenerationJob | `TDD-WP2-GEN-*``TDD-WP3-WRK-001` | 单活动任务、快照、不可逆终态、一次结算 |
| 17.5 CanvasElement | `TDD-WP4-CAN-001` | 四类型、最多 50、无 Fabric 私有字段 |
| 17.6 TemplateRegistry | `TDD-WP5-MAN-001``TDD-WP5-WHITE-001` | 稳定 ID/版本/状态/白名单与 full_p0 边界 |
| 17.7 StaticStickerCatalog | `TDD-WP5-CAT-001``TDD-WP5-CLN-001` | part/顺序/重复、来源、文件关系和引用保护 |
| 17.8 DynamicStickerData | `TDD-WP4-DYN-*` | 只提供消费字段、确认值和 DYN004 坐标 |
| 17.9 CreditAccount/CreditLedger | `TDD-WP2-CRED-*` | available/reserved、不可变流水和唯一操作键 |
| 17.10 PrivateContentAccessLog | `TDD-WP6-PRIV-002``TDD-WP6-AUD-001` | 每次打开独立记录、先审计后读取、180 天 |
| 17.11 AnonymousRetainedEvent | `TDD-WP1-DEL-002` | 随机断链主体、允许字段、最迟 180 天 |
| 17.12 AssetReleaseManifest | `TDD-WP5-RES-001` | immutable 版本、哈希、access_class、公开过滤 |
| 17.13 AssetPreviewGrant | `TDD-WP5-PREV-001` | 不改角色、每次校验、撤销/到期立即生效 |
| 17.14 ClientCachePolicy | `TDD-WP0-CACHE-001``TDD-WP5-CACHE-001` | 150 MB LRU、公开 allowlist、私有禁存 |
| 17.15 ExternalServiceUsage | `TDD-WP6-SVC-*` | 四状态、硬上限、人工恢复、零自动费用 |
| 17.16 AdminOperationLog | `TDD-WP6-AUD-001` | 两种 actor、非敏感摘要、180 天不可改删 |
| 17.17 GatewayBalanceState | `TDD-WP2-BAL-001``TDD-WP3-BAL-001` | model/account/unknown 影响、确认恢复、不回写配置 |
| 17.18 GenerationErrorCategory | `TDD-WP2-ERR-001` | 九项只读注册表、稳定动作与点数口径 |
| 17.19 LocalBackendStorageState | `TDD-WP0-STO-*` | 精确阈值、`>` 准入、file kind 计量、物理后重测 |
## 8. 十三个功能模块覆盖
| FeatureSummary 模块 | 主测试族 | 主责 WP |
| --- | --- | --- |
| 邀请注册与邮箱登录 | `TDD-WP1-AUTH-*``TDD-WP7-EXT-002` | WP-1 |
| 点数与原子结算 | `TDD-WP2-CRED-*``TDD-WP2-GEN-*` | WP-2 |
| 三模型 AI 生图 | `TDD-WP2-GEN-*``TDD-WP3-ADP-*` | WP-2 / WP-3 |
| 项目、历史、保存与冲突 | `TDD-WP2-PROJ-*` | WP-2 |
| 电脑画布与底图编辑 | `TDD-WP4-BG-001``TDD-WP4-CAN-001` | WP-4 |
| 文字模板与字体 | `TDD-WP4-TXT-*``TDD-WP5-WHITE-001` | WP-4 / WP-5 |
| 25 个普通贴纸 part | `TDD-WP5-CAT-001``TDD-WP5-UPL-001``TDD-WP5-CLN-001` | WP-5 |
| 色卡与动态贴纸 | `TDD-WP4-COL-001``TDD-WP4-DYN-*``TDD-WP5-COL-001` | WP-4 / WP-5 |
| 导出 | `TDD-WP2-EXP-*``TDD-WP4-EXP-001` | WP-4 |
| 运营后台 | `TDD-WP6-*` | WP-6 |
| 素材发布与内部预览 | `TDD-WP5-MAN-001``TDD-WP5-RES-001``TDD-WP5-PREV-001` | WP-5 / WP-6 |
| 本机数据与资源访问 | `TDD-WP0-*` | WP-0 / WP-2 |
| 隐私、审计与免费服务边界 | `TDD-WP1-DEL-*``TDD-WP6-PRIV-*``TDD-WP6-SVC-*` | WP-1 / WP-6 |
## 9. 九类生成错误测试矩阵
### 9.1 GenerationErrorCategory
`reference_invalid``safety_rejected` 必须使用不同 fixture、不同模拟响应和不同断言。供应商安全拒绝才能映射为 `safety_rejected`;格式、大小、数量或非安全型内容不可用映射为 `reference_invalid`
| category | 创建前断言 | 创建后断言 | 用户动作 | 点数 |
| --- | --- | --- | --- | --- |
| `upstream_timeout` | 不适用 | failed、无输出、保存 category | 使用原输入重试 | release 一次 |
| `upstream_failed` | 不适用 | failed、无输出、保存 category | 稍后重试 | release 一次 |
| `safety_rejected` | 不适用 | rejected、无输出、使用明确安全证据 | 修改提示词或参考图 | release 一次 |
| `model_disabled` | 不创建任务 | 不适用 | 选择其他模型或等待 | 不 reserve |
| `gateway_balance_insufficient` | 可在创建前阻断 | 已创建则 failed,并更新独立 runtime 影响 | 选择未受影响模型或联系管理员 | 无 reserve 或 release 一次 |
| `gateway_contract_invalid` | contract 非 verified 时阻断 | 已创建后契约失效则 failed | 选择其他模型或联系管理员 | 无 reserve 或 release 一次 |
| `reference_invalid` | 格式/大小/数量可在创建前阻断 | 供应商非安全型不可用时 failed | 更换或移除参考图 | 无 reserve 或 release 一次 |
| `unknown_retryable` | 不适用 | failed,保留非敏感诊断 | 稍后重试 | release 一次 |
| `unknown_non_retryable` | 不适用 | failed,不显示重复重试入口 | 联系管理员 | release 一次 |
对每类都必须额外断言:前端、GenerationJob、后台生成记录和统计使用同一 category ID;原始错误码、正文、堆栈和内部地址不进入响应或 UI;queued、running、succeeded 的 `error_category` 为空;后台不能新增、重命名或删除分类。
### 9.2 稳定工程错误码
这些错误不扩充 GenerationErrorCategory。每项都必须验证 HTTP 状态、`error.code`、稳定 `message_key`、correlation ID、允许的 `details`、UI 动作和零敏感泄漏。
| code | HTTP | 主测试族 | 关键断言 |
| --- | ---: | --- | --- |
| `BROWSER_UNSUPPORTED` | 426 | `TDD-WP0-BRW-002` | 四个固定 reason;只有阻断引导,无绕过或业务副作用 |
| `MODEL_CONFIG_VERSION_CONFLICT` | 412 | `TDD-WP3-MDL-002` | 返回最新安全版本摘要,整套配置不写入,UI 刷新后重编 |
| `MODEL_DEFAULT_REPLACEMENT_REQUIRED` | 409 | `TDD-WP3-MDL-002` | 停用当前默认却未指定替代时整次失败 |
| `MODEL_DEFAULT_REPLACEMENT_INVALID` | 409 | `TDD-WP3-MDL-002` | 替代不存在/未启用/产生非唯一默认时整次失败 |
| `MODEL_RECOMMENDATION_PRIORITY_INVALID` | 400 | `TDD-WP3-MDL-002` | 缺失、浮点、零或负数定位到字段且不部分写入 |
| `MODEL_RECOMMENDATION_PRIORITY_CONFLICT` | 409 | `TDD-WP3-MDL-002` | 重复优先级标出冲突模型且不部分写入 |
| `PRIVATE_CONTENT_NOTICE_ACK_REQUIRED` | 428 | `TDD-WP6-PRIV-001` | 只返回当前告知版本/message_key,不返回区域数据 |
| `ASSET_HISTORY_REFERENCE_CONFLICT` | 409 | `TDD-WP5-CLN-001` | 任一引用使整批不排队,容量不变并记录 denied 审计 |
| `ASSET_CLEANUP_CANDIDATE_STALE` | 409 | `TDD-WP5-CLN-001` | 旧候选失效,要求重新查询和确认,不复用令牌 |
| `STORAGE_CAPACITY_EXCEEDED` | 507 | `TDD-WP0-STO-002` | full/unavailable 或预计值 `>` 上限才拒绝;等于上限不返回 507 |
未知 `message_key` 使用 UIDesign 的安全兜底“操作未完成”、correlation ID 和接口允许的重试/返回/诊断动作;不得被映射成成功、已释放点数或允许覆盖。
## 10. 视觉、性能与可访问性
### 10.1 视觉基线
- 页面结构验收使用 `1920 x 1080`、DPR 1、100% 缩放;低保真画板只验证区域、控件、状态、固定文案、焦点和交互,不做像素级 gold image。
- 200% 浏览器缩放逐页验证无文字覆盖、不可关闭弹层、不可达主动作或不连贯重叠;允许滚动,不缩小字号规避。
- Canvas 视觉基线固定 OS、浏览器 full version、视口、DPR、字体 ready、动画时刻、背景图和动态数据。
- Chrome/Edge 元素边界相对基准偏差不超过 2 px。
- 任一颜色通道绝对差异超过 16/255 的像素视为显著差异,显著像素不超过画布总像素 1%。
- 任一阈值超限必须人工复核,不能自动判定通过。
- P0-A 中 COLOR002、COLOR008、COLOR016、DYN012 及其他已知替代/近似项无论自动差异是否通过均人工复核;DYN012 报告必须写明 FONT081 Lexend Deca 替代。
### 10.2 性能预算
在候选或最终冻结的 Windows、Chrome/Edge、1920 x 1080、DPR 1、50 个混合覆盖元素下:
| 指标 | 门槛 |
| --- | --- |
| 连续拖动/缩放/旋转 10 秒的指针到下一画面更新 | p95 不超过 50 ms |
| 画布交互帧耗时 | p95 不超过 33 ms |
| 单个主线程长任务 | 不超过 200 ms |
| 连续无响应 | 不得达到 500 ms |
| 自动保存序列化主线程阻塞 | p95 不超过 50 ms |
| 已缓存白名单资源的编辑器重新打开到可操作 | 不超过 3 秒 |
| 1080 x 1920、50 元素导出 | 不超过 10 秒 |
| 导出页面峰值额外内存 | 不超过 1 GiB |
| 普通贴纸面板 DOM | 仅视口和两屏缓冲,不随 1,407 总数线性增长 |
每项至少运行一次预热和五次测量,报告单次值、p50、p95、最大值、浏览器 full version 和 fixture hash。性能失败不得通过提高超时、减少元素或降低最终导出尺寸掩盖。
### 10.3 可访问性
- 关键页面运行 axecritical 和 serious 问题均为 0。
- 页面第一个 Tab 为“跳到主要内容”;视觉顺序与焦点顺序一致,不使用正 tabindex。
- 按钮、图标、状态和表单具有可读名称;状态不只依赖颜色。
- 弹层焦点被限制,关闭后回到触发点;破坏性弹层请求期间不能关闭或重复提交。
- saving、queued、running、重计量使用 polite live region;保存失败、冲突、浏览器阻断、full/unavailable 使用 assertive。
- 画布外提供所有关键命令的可聚焦等价控件;焦点或 SSE 更新不得抢走用户当前焦点。
## 11. 页面、状态与低保真映射
### 11.1 UIDesign 页面 × 适用状态矩阵
UIDesign 第“页面与入口地图”及各页面章节是产品页面覆盖全集;Pencil 不定义页面全集。下表的 22 个 coverage unit 包含产品页、身份变体和 Windows 辅助界面。每个非 `N/A` 单元格必须至少关联一个自动或人工 case,并把 `1920 x 1080`、DPR 1、100% 与 200% 缩放截图写入 `artifacts/tdd/<run_id>/ui/<page_id>/<state>/``N/A` 必须保留下表理由,不能用缺实现代替。内部预览账号沿用普通页面,只作为授权状态变体,不创建新角色或后台页面。
| page_id / 页面或入口 | 主体与入口 | 正常 | 空 / 加载 | 失败 / 禁用 | 冲突 / 容量 / 特殊状态 | 主测试落点 |
| --- | --- | --- | --- | --- | --- | --- |
| `support-gate` 浏览器不受支持引导 | 所有主体,产品门禁前 | 四种阻断 reason、支持品牌/major、无继续入口 | N/A:引导无业务空态;身份不可判定属于阻断 | bundle/API 均无法绕过;诊断仅最小字段 | N/A:不读取业务状态或容量 | `TDD-WP0-BRW-002``TDD-WP7-UI-001` |
| `user-auth` 登录 / 注册 | 未登录普通入口 | 登录、分阶段注册、管理员文字入口、固定本机提示 | 字段初始空;发码/验证/注册加载 | Resend、验证码、邀请、名额、告知未完成;按钮禁用 | N/A:认证不消费存储容量 | `TDD-WP1-AUTH-*`、第 6.9.2 节 |
| `workspace` 创作工作台 | active 普通/内部预览账号 | 生成表单、模型/点数/比例、最近项目 | 无项目艺术带;模型/项目加载 | 服务/参考图/生成错误;模型、点数、单任务、历史/项目上限禁用 | model config 变化重确认;warning/critical 提示;full/unavailable 阻断新增 | `TDD-WP2-GEN-*``TDD-WP0-STO-*` |
| `current-task` 当前生成任务 | active 普通/内部预览账号 | 真实状态、结果入口 | 无活动任务;queued/running 无假百分比 | failed/rejected 与九类固定动作;第二任务禁用 | storage 错误按任务真相;pending_manual_review 不作为用户任务状态;继承普通端全局容量条 | `TDD-WP2-GEN-*``TDD-WP3-WRK-001` |
| `projects` 项目列表与回收站 | active 普通/内部预览账号 | active/trashed 列表、筛选、多选 failed_empty | 无项目/筛选空;稳定卡片骨架 | 列表失败保留旧数据;20 active、恢复/批量条件禁用 | 多标签版本提示;删除排队不乐观减容量;继承普通端全局容量条 | `TDD-WP2-PROJ-001``TDD-WP2-PROJ-003``TDD-WP2-PROJ-005` |
| `project-detail` 项目详情 | 项目所有者 | 历史、固定比例、继续生成、编辑、原图/latest 下载 | 无成功图草稿;历史/成品加载 | 10 图上限、当前底图不可删、无 latest、资源读取失败 | 比例不可改;清理/容量状态不伪造资源可用性;继承普通端全局容量条 | `TDD-WP2-PROJ-*``TDD-WP2-EXP-002` |
| `editor` 编辑器 | 有成功底图的项目所有者 | 三栏、保存状态、画布、属性与素材 | 画布/素材加载;无成功图不能正常进入 | 保存/素材失败;第 51 元素、不可用模板禁用 | dirty/saving/saved/failedconflicted 只读;full 可存 JSONunavailable 保存失败 | `TDD-WP4-*`、第 6.9.8 节 |
| `export` 导出弹层 | 编辑器内项目所有者 | JPG/PNG、质量、尺寸、下载/latest 分结果 | 合成/持久化加载;无业务空态 | 合成、下载、项目保存、latest 保存分别失败;无画布/冲突次数用尽禁用 | 未提交确认;conflicted 仅一次本地导出;full/unavailable 后端失败不撤回下载 | `TDD-WP4-EXP-001`、第 6.9.9 节 |
| `credits` 点数明细 | active 普通/内部预览账号 | available/reserved、流水、关联与时间 | 无流水;分页加载 | 加载失败保留安全余额摘要 | 负 available 如实显示;无购买/充值入口;页面操作不消费容量但仍继承普通端全局容量条 | `TDD-WP2-CRED-*`、第 6.9.3 节 |
| `settings` 设置与账号注销 | active 普通/内部预览账号 | 资料、本机数据、容量摘要、固定文案、注销 | 资料/状态加载 | 保存失败;注销确认/处理中禁用重复提交 | full/unavailable 状态与允许动作;注销后返回登录 | `TDD-WP1-DEL-*``TDD-WP0-DATA-002` |
| `preview-user-variant` 内部预览账号普通页变体 | 有有效/撤销/过期 AssetPreviewGrant 的普通账号 | 授权批次素材显示“预览”,其余页面同普通用户 | 无授权/批次空;manifest 加载 | 撤销/过期立即阻断新读取 | 不出现后台入口、调点、邀请码、模型或私有内容;继承普通端全局容量条 | `TDD-WP5-PREV-001``TDD-WP5-RES-001` |
| `admin-auth` super_admin 登录 | 未登录后台入口 | 白名单邮箱验证码、独立 audience、返回普通登录 | 字段空;发码/登录中 | 非白名单发码前阻断、停用/移除白名单、Resend 错误 | N/A:不显示普通创作或容量业务 | `TDD-WP1-ADM-001``TDD-WP1-CFG-001` |
| `admin-overview` 后台总览 | active super_admin | 名额、任务、模型、点数、服务、容量、审计摘要 | 无近期事件;指标骨架 | 聚合失败保留旧值和时间;敏感动作按状态禁用 | default/runtime/recommended 分列;pending_manual_review 为成本核对;容量持续条 | `TDD-WP6-ADM-001``TDD-WP6-STATE-001``TDD-WP6-DIAG-001` |
| `admin-users` 用户管理 | active super_admin | 脱敏列表/详情、暂停恢复、调点 | 无用户/筛选空;表格骨架 | 操作失败、原因缺失、状态前置禁用 | 调点可使 available 为负但 reserved 不变;页面操作不消费容量但仍继承后台全局容量条 | `TDD-WP6-ADM-001``TDD-WP2-CRED-002` |
| `admin-invites` 邀请码 | active super_admin | 创建、停用、期限/次数、脱敏复制 | 无邀请码;表格骨架 | 名额满、停用/过期/耗尽不可当可用;请求失败 | CAS/幂等结果;页面操作不消费容量但仍继承后台全局容量条 | `TDD-WP1-AUTH-*``TDD-WP1-SLOT-001``TDD-WP6-AUD-001` |
| `admin-models` 模型 | active super_admin | 完整三模型集合、default/runtime/recommended 三层 | 初始加载骨架;无 recommended 是合法状态 | 合法替代/priority 前置禁用;契约未验证/运行时不可用 | config_set CAS 冲突、完整集合原子回滚;仍继承后台全局容量条 | `TDD-WP3-MDL-*`、第 6.9.7 节 |
| `admin-assets` 素材 | active super_admin | 普通贴纸、资源版本/发布、历史清理 | 无上传素材/无候选;列表/处理加载 | 格式/编译失败;权限、文件状态禁用 | stable ID/version 冲突;full/unavailable 阻断上传;引用冲突/排队/重计量 | `TDD-WP5-UPL-001``TDD-WP5-CLN-001``TDD-WP5-MAN-001` |
| `admin-preview` 内部预览 | active super_admin | 批次、模板状态、账号授权/撤销/到期 | 无批次/授权;表格骨架 | 非法状态转换、过期、发布校验失败 | 授权不改变角色;P0-B 全量开放不可在 P0-A 出现;继承后台全局容量条 | `TDD-WP5-PREV-001``TDD-WP5-WHITE-001` |
| `admin-generations` 生成记录与私有内容 | active super_admin | 默认安全摘要、告知、逐次打开 | 无记录;列表/审计写入加载 | 未确认 428、审计失败不打开、权限禁用 | 告知升版重阻断;每次打开先独立审计;页面读取不消费容量但仍继承后台全局容量条 | `TDD-WP6-PRIV-*`、第 6.9.6 节 |
| `admin-services-storage` 服务与存储 | active super_admin | Resend/高德/网关/Worker/LocalDataRoot 状态与允许动作 | 指标加载;无告警 | paused/degraded/unavailable;不合法恢复/提高上限禁用 | full/unavailable、清理排队和物理重计量;不显示绝对路径 | `TDD-WP6-SVC-001``TDD-WP6-SVC-002``TDD-WP6-DIAG-001``TDD-WP0-STO-*` |
| `admin-audit` 审计 | active super_admin | 后台操作与私有访问两类独立页/标签 | 无记录;游标加载 | 查询失败保留旧结果;无修改/人工删除动作 | 180 天保留;正文、图片、密钥和路径不出现;页面读取不消费容量但仍继承后台全局容量条 | `TDD-WP6-AUD-001``TDD-WP6-PRIV-002` |
| `system-ui` Windows 辅助系统界面 | 本机托盘/Supervisor,无产品角色 | 托盘、运行状态、打开 Dada、诊断、退出 | 启动中;N/A 业务空态 | 启动失败、端口占用、API/Worker degraded,动作严格受限 | full/unavailable 只作脱敏告警;固定端口不动态切换 | `TDD-WP0-SUP-001``TDD-WP0-PKG-001``TDD-WP0-LOG-001``TDD-WP7-REL-001` |
`pnpm validate:tdd-trace` 必须把 UIDesign 页面/入口 allowlist 与上述 `page_id` 对照:缺少 UIDesign 页面、重复 page_id、把内部预览误建角色/后台、把 Canvas Index 计为产品页、适用状态无 case/evidence 或 `N/A` 无理由时返回非零退出码。
### 11.2 Pencil 布局参考映射
`.pen` 中共有 19 个顶层画板。画板尺寸 1600 x 900,索引画板为 1440 x 420;预览 PNG 外边框使图像为 1602 x 902 或 1442 x 422。实现验收仍以 UIDesign 的 1920 x 1080 为主,不把预览尺寸改成产品视口。
画板中的版本号、账号、点数、容量和时间均是低保真示例数据。例如辅助界面预览里的 Chrome/Edge 版本不能作为支持记录;自动化必须从当前测试 fixture 读取状态,发布验收必须从 `RELEASE.json` 读取真实版本。若 `.pen` 或预览 PNG 中的示例文案、账号、容量、时间与 UIDesign 固定文案冲突,以 UIDesign 为界面验收依据;自动化不得通过 PNG OCR 反推验收文案。
| frame ID | 画板 | 预览 | 主要测试落点 |
| --- | --- | --- | --- |
| `Z8p3I` | 00 Canvas Index | `Z8p3I.png` | 非产品索引,仅校验设计资产清单;不要求实现,不计页面完成率 |
| `Z0pf8` | 01A 普通用户登录 | `Z0pf8.png` | 登录字段、标签、管理员入口、固定本机提示、焦点 |
| `DVPM8` | 01B 注册展开态 | `DVPM8.png` | 邀请/邮箱只读摘要、资料、告知、注册主动作 |
| `WbDYT` | 02A 空工作台 | `WbDYT.png` | 艺术带、生成表单、推荐模型、比例和空任务 |
| `d99Lo` | 02B 生成中工作台 | `d99Lo.png` | 当前任务、冻结点数、critical 容量条和禁用提交 |
| `zXyG9` | 03A 项目列表 | 无独立 PNG | 项目/回收站、上限、failed_empty、稳定卡片 |
| `v2Qbu` | 03B 项目详情 | 无独立 PNG | 历史、固定比例、编辑/生成/下载、无成品状态 |
| `bHQnJ` | 04A 三栏编辑器 | `bHQnJ.png` | 三栏、保存状态、画布、对象属性、固定底栏 |
| `i9upg7` | 04B 导出弹层 | 无独立 PNG | JPG/PNG、质量、未提交确认、下载/持久化分结果 |
| `YlKzE` | 05A 点数明细 | 无独立 PNG | available/reserved、流水、无购买入口 |
| `Ws5E0` | 05B 设置与本机数据 | 无独立 PNG | 固定文案、容量、文件权限风险、注销 |
| `e4e2Kz` | 06A 后台总览 | `e4e2Kz.png` | 名额、任务、成本核对、服务/容量、最近审计 |
| `YVfVG` | 06B 模型配置 | 无独立 PNG | 默认/推荐/runtime 三列、完整集合和冲突 |
| `A28V3c` | 06C 素材与历史清理 | 无独立 PNG | 上传计量、候选/确认/冲突/排队/重计量 |
| `OnsiT` | 06D 生成记录与私有告知 | 无独立 PNG | 默认隐藏正文、身份+版本告知、逐次入口 |
| `L3lde2` | 06E 服务与存储 | 无独立 PNG | Resend/高德/网关/Worker/storage 状态与动作 |
| `DlCpN` | 06F 私有内容访问审计 | 无独立 PNG | 两类审计分离、不可编辑删除、无正文 |
| `u1adni` | 07A 浏览器硬阻断 | 无独立 PNG | 426、四原因、支持 major、无继续入口 |
| `awt96` | 07B Windows 辅助系统界面 | `awt96.png` | 托盘、启动失败、端口占用、degraded、诊断脱敏 |
`Z8p3I` Canvas Index 外,Pencil 产品 frame 只为其映射到的第 11.1 节页面/状态提供布局参考;是否需要截图及状态数量由第 11.1 节决定,不以“18 个产品 frame”替代完整页面覆盖。UIDesign 中没有独立 Pencil frame 的后台用户、邀请码、内部预览、管理员登录等页面仍必须具备完整 UI 证据。预览缺失不阻止测试,`.pen` 不是实现范围清单;不得自行补画或改写设计文件作为测试通过手段。
## 12. 当前 AC 唯一主责矩阵
每项 AC 只有一个主责 WP。支持测试可以跨 WP,但只能由主责 WP 汇总完成,WP-7 只汇总发布证据而不夺取产品需求主责。
`pnpm validate:tdd-trace` 必须从冻结的 PRD 需求/AC allowlist 校验本节、第 13 节和第 6.9 节,失败时返回非零退出码:当前 AC 集合必须恰为 52 项;每项恰有一个唯一主责;AC-08、AC-26、AC-37、AC-54 不得进入当前矩阵或 `passed` 计数;第 13 节范围展开后必须恰为 109 个互不重复的已知 PRD 需求 ID,不得遗漏、重复或新增未知 ID;测试族必须恰为 89 个且 AREA 正则允许数字;117 个规范 case 必须全部按 `^(TDD-WP[0-7]-[A-Z0-9]+-[0-9]{3})-` 捕获已知父族,每个父族至少一个 case;每个 case 的 fixture、步骤、响应、DB/文件变化、禁止副作用、证据文件和第 2.2.1 节派生属性均非空。检查必须读取结构化追踪数据或确定性解析本文,不得信任手写合计值。
| AC | 当前范围 | 唯一主责 | 主测试族 | 必要支持 |
| --- | --- | --- | --- | --- |
| AC-01 | 全部 | WP-1 | `TDD-WP1-AUTH-001` | `TDD-WP1-NOTICE-001``TDD-WP7-EXT-002` |
| AC-02 | 全部 | WP-1 | `TDD-WP1-AUTH-002` | 无 |
| AC-03 | 全部 | WP-2 | `TDD-WP2-GEN-002` | `TDD-WP3-ADP-001` |
| AC-04 | 全部 | WP-2 | `TDD-WP2-GEN-002` | `TDD-WP2-ERR-001` |
| AC-05 | 全部 | WP-2 | `TDD-WP2-GEN-001` | `TDD-WP2-CRED-001` |
| AC-06 | 全部 | WP-2 | `TDD-WP2-PROJ-001` | 无 |
| AC-07 | 全部 | WP-4 | `TDD-WP4-BG-001` | `TDD-WP4-BG-002``TDD-WP4-COL-001` |
| AC-09 | 全部 | WP-4 | `TDD-WP4-TXT-001` | `TDD-WP4-EXP-001` |
| AC-10 | 全部 | WP-4 | `TDD-WP4-TXT-001` | 无 |
| AC-11 | P0-A 字体子范围 | WP-4 | `TDD-WP4-TXT-002` | `TDD-WP5-WHITE-001` |
| AC-12 | 全部 | WP-4 | `TDD-WP4-TXT-002` | `TDD-WP4-EXP-001` |
| AC-13 | 全部 | WP-5 | `TDD-WP5-CAT-001` | `TDD-WP5-CACHE-001` |
| AC-14 | 全部 | WP-4 | `TDD-WP4-STK-001` | 无 |
| AC-15 | 仅四种 P0-A 色卡 | WP-5 | `TDD-WP5-COL-001` | `TDD-WP4-COL-001` |
| AC-16 | 全部 | WP-4 | `TDD-WP4-DYN-001` | 无 |
| AC-17 | 全部 | WP-4 | `TDD-WP4-DYN-002` | `TDD-WP7-EXT-003` |
| AC-18 | 全部 | WP-4 | `TDD-WP4-DYN-003` | 无 |
| AC-19 | 全部 | WP-4 | `TDD-WP4-DYN-003` | `TDD-WP4-VIS-001` |
| AC-20 | 全部 | WP-2 | `TDD-WP2-PROJ-002` | `TDD-WP0-CACHE-001` |
| AC-21 | 全部 | WP-2 | `TDD-WP2-PROJ-003` | `TDD-WP0-STO-003` |
| AC-22 | 全部 | WP-1 | `TDD-WP1-DEL-001` | `TDD-WP1-DEL-002` |
| AC-23 | 仅 P0-A 四比例与同机下载 | WP-4 | `TDD-WP4-EXP-001` | `TDD-WP2-EXP-001` |
| AC-24 | 全部 | WP-0 | `TDD-WP0-BRW-001``TDD-WP0-BRW-002` | `TDD-WP0-BND-001` |
| AC-25 | 全部 | WP-6 | `TDD-WP6-PRIV-001``TDD-WP6-PRIV-002` | `TDD-WP6-AUD-001` |
| AC-27 | 全部 | WP-4 | `TDD-WP4-CAN-001` | `TDD-WP4-PERF-001` |
| AC-28 | 全部 | WP-2 | `TDD-WP2-CRED-001` | `TDD-WP2-GEN-003` |
| AC-29 | 全部 | WP-2 | `TDD-WP2-CRED-002` | `TDD-WP2-GEN-001` |
| AC-30 | 全部 | WP-3 | `TDD-WP3-MDL-002``TDD-WP3-MDL-003` | `TDD-WP2-GEN-003` |
| AC-31 | 全部 | WP-5 | `TDD-WP5-UPL-001``TDD-WP5-CLN-001` | `TDD-WP0-STO-003` |
| AC-32 | 全部 P0-A 白名单 | WP-4 | `TDD-WP4-VIS-001` | `TDD-WP4-TXT-003``TDD-WP5-WHITE-001``TDD-WP7-REL-001` |
| AC-33 | 全部 | WP-1 | `TDD-WP1-AUTH-003` | `TDD-WP7-EXT-002` |
| AC-34 | 全部 | WP-2 | `TDD-WP2-PROJ-001` | 无 |
| AC-35 | 全部 | WP-2 | `TDD-WP2-EXP-001` | `TDD-WP2-PROJ-002` |
| AC-36 | 仅同机多标签 | WP-2 | `TDD-WP2-PROJ-004` | `TDD-WP2-EXP-001` |
| AC-38 | 仅 P0-A 本机成品空状态 | WP-2 | `TDD-WP2-EXP-002` | 无 |
| AC-39 | 全部 | WP-1 | `TDD-WP1-DEL-002` | `TDD-WP6-AUD-001` |
| AC-40 | 全部 | WP-3 | `TDD-WP3-ADP-001``TDD-WP3-ADP-002` | `TDD-WP7-EXT-001` |
| AC-41 | 全部 | WP-7 | `TDD-WP7-REL-001``TDD-WP7-EXT-*` | WP-0 至 WP-6 全部 Green |
| AC-42 | 全部 | WP-5 | `TDD-WP5-PREV-001` | `TDD-WP5-RES-001` |
| AC-43 | 全部 | WP-2 | `TDD-WP2-PROJ-005` | `TDD-WP2-GEN-002` |
| AC-44 | 全部 | WP-2 | `TDD-WP2-REF-001` | `TDD-WP0-CACHE-001` |
| AC-45 | 全部 | WP-1 | `TDD-WP1-SLOT-001` | `TDD-WP5-PREV-001` |
| AC-46 | 全部 | WP-0 | `TDD-WP0-CACHE-001` | `TDD-WP5-CACHE-001` |
| AC-47 | 全部 | WP-6 | `TDD-WP6-SVC-001` | `TDD-WP7-EXT-002``TDD-WP7-EXT-003` |
| AC-48 | 全部 | WP-5 | `TDD-WP5-RES-001` | `TDD-WP5-PREV-001``TDD-WP0-SEC-001` |
| AC-49 | 全部 | WP-1 | `TDD-WP1-ADM-001``TDD-WP1-CFG-001` | `TDD-WP7-EXT-002` |
| AC-50 | 全部 | WP-6 | `TDD-WP6-AUD-001` | `TDD-WP5-CLN-001``TDD-WP6-PRIV-002` |
| AC-51 | 全部 | WP-2 | `TDD-WP2-BAL-001` | `TDD-WP3-BAL-001` |
| AC-52 | 全部 | WP-6 | `TDD-WP6-SVC-002` | `TDD-WP6-AUD-001` |
| AC-53 | 全部 | WP-2 | `TDD-WP2-ERR-001` | `TDD-WP3-ADP-001` |
| AC-55 | 全部 | WP-0 | `TDD-WP0-STO-001``TDD-WP0-STO-002``TDD-WP0-STO-003` | `TDD-WP5-UPL-001``TDD-WP5-CLN-001` |
| AC-56 | 全部 | WP-0 | `TDD-WP0-DATA-002` | `TDD-WP7-UI-001` |
未来 AC-08、AC-26、AC-37、AC-54 不得出现在当前 `passed` 计数。允许编译器或内部预览为未来素材生成非发布证据,但不得扩大普通用户可见范围或阻止 P0-A 完成。
## 13. 109 个 PRD 需求 ID 覆盖索引
| 主责 WP | 需求 ID | 数量 |
| --- | --- | ---: |
| WP-0 | PRIV-01、PRIV-02、NFR-01、NFR-07、NFR-09 | 5 |
| WP-1 | AUTH-01 至 AUTH-07、PRIV-03、PRIV-04、PRIV-06、NFR-04 | 11 |
| WP-2 | CREDIT-01 至 CREDIT-06、PROJECT-01 至 PROJECT-09、GEN-03 至 GEN-06、GEN-11、GEN-12、GEN-14、GEN-16、NFR-05 | 24 |
| WP-3 | GEN-01、GEN-02、GEN-07 至 GEN-10、GEN-13、GEN-15、ADMIN-03 | 9 |
| WP-4 | EDITOR-01 至 EDITOR-09、TEXT-01 至 TEXT-14、COL-01 至 COL-05、DYN-01 至 DYN-09、EXPORT-01 至 EXPORT-06、NFR-03、NFR-06 | 45 |
| WP-5 | STATIC-01 至 STATIC-04、ADMIN-06、ADMIN-07、PRIV-05、NFR-02 | 8 |
| WP-6 | ADMIN-01、ADMIN-02、ADMIN-04、ADMIN-05、ADMIN-08、ADMIN-09、NFR-08 | 7 |
| 合计 | 不重复的当前 PRD 需求 ID | 109 |
每个具体测试文件必须从本表选择需求 ID,不得创造未经 PRD 定义的新 ID。跨包依赖可以重复引用需求 ID,但唯一主责不变。
## 14. CI、隔离与外部验证
### 14.1 普通 CI
- Gitea Actions 使用冻结 lockfile,全部外部服务为 mock,仅使用脱敏 fixture 和 `.invalid` 邮箱。
- 每个 DB/文件测试使用系统临时目录和独立 SQLite;完成后关闭句柄并删除测试目录。
- 浏览器与打包 job 并发数为 1,使用 `%RUNNER_TEMP%` 下的独立 user-data-dir,不读取或关闭真实 Dada 实例。
- 普通 CI 使用系统分配临时回环端口;临时端口仅限测试 harness。打包、Supervisor、启动入口、诊断和发布验收必须证明产品包固定使用 `127.0.0.1:43121` 且端口占用时失败,不会动态换端口。
- 普通 CI 不读取 Windows Credential Manager 中真实条目,不访问项目方真实 LocalDataRoot,不扫描 3.57 GB 归档。
- flaky 重跑不能把失败改成通过。任何自动重跑都保留首轮失败,并在证据中记录;连续不稳定测试阻止所属 WP 完成。
### 14.2 受控真实验证
- `pnpm validate:external` 只能由项目方在 P0-A 验收电脑显式运行;命令从 Windows Credential Manager 按最小范围读取凭据,不接收命令行密钥。
- 每次真实调用先显示服务、模型、预计调用数量和是否可能消耗既有额度;执行者明确确认后才开始。
- 真实响应立即脱敏,仅保留协议字段、状态类别、耗时、MIME、尺寸、非敏感用量、证据哈希和时间。
- AI 网关三个模型分别生成证据,不能共享“已验证”标记。
- Resend 送达记录只保存人工统计,不建立运行时 EmailVerificationEvent19/20、2 分钟的阻断门槛直接来自 PRD 第 23.2 节、AC-41、AC-47 和 DevelopmentPlan 第 1.4、11.1 节,不是 TDD 新增指标。
- 高德真实测试不把测试坐标关联到真实用户项目。
- 外部验证失败保持 externally_blocked,不自动切换付费、备用供应商、自动充值或自动扩容。
## 15. 工作包顺序与完成门禁
| WP | 依赖 | 自动完成门禁 | 仍需真实/人工门禁 |
| --- | --- | --- | --- |
| WP-0 | 无 | 单元、API、存储、Supervisor、安全、打包 Green | RELEASE 浏览器记录在 WP-7 冻结 |
| WP-1 | WP-0 | 认证状态机、事务、会话、名额、注销、审计 Green | Resend 正式认证 |
| WP-2 | WP-0、WP-1 | 项目、点数、任务、参考图、冲突、错误 Green | 真实三模型完成态依赖 WP-3 |
| WP-3 | WP-2 | 模型集合、runtime、Adapter mock、Worker 恢复 Green | 三模型独立真实契约 |
| WP-4 | WP-2 | 编辑器、导出、a11y、性能 harness Green | 最终视觉依赖 WP-5 真实白名单 |
| WP-5 | WP-0;与 WP-4 前半可并行 | 编译、目录、隔离、缓存、上传、清理 Green | 真实归档只读校验和人工视觉 |
| WP-6 | WP-1、WP-2、WP-5;模型页依赖 WP-3 | 后台、告知、审计、服务与存储 Green | 真实服务恢复和正式身份流程 |
| WP-7 | WP-0 至 WP-6 | `pnpm test:all`、52 AC 结构检查、制品扫描 Green | RELEASE、Chrome/Edge、AI、Resend、高德及人工验收全部通过 |
任何工作包的“不能提前宣告”条件沿用 DevelopmentPlan。依赖工作包未 Green 时,下游可以编写 Red 测试和 mock 接线,但不能标记完成。
## 16. P0-A 测试完成定义
只有以下条件全部满足,`tdd.md` 所定义的 P0-A 测试才完成:
1. 四份 Markdown 全局来源哈希与第 1.2.1 节一致;第 1.2.2 节 UI 参考资产已匹配当前哈希,或受影响页面已完成局部重读、映射更新和视觉重建,不存在未处理的 `source_changed` case。
2. WP-0 至 WP-7 全部达到第 15 节门禁,第 6.9 节 117 个规范 case 及其参数分支均有可复现 Red 和最终 Green/Passed 证据。
3. `pnpm validate:tdd-trace` 通过;第 12 节 52 项当前 AC 全部 passed,每项恰有一个唯一主责;AC-08、26、37、54 未混入当前完成统计;89/89 测试族至少有一个 case,117 个 case 的必填字段、派生执行属性和证据目录完整。
4. 第 13 节 109 个互不重复且无未知项的需求 ID、十三个功能模块、十九个数据契约和九类错误均有可执行测试落点。
5. `reference_invalid``safety_rejected` 由不同证据证明;九类错误的任务、点数、用户动作和脱敏断言全部通过。
6. `managed_content_bytes` 的阈值、等号写入、超限无副作用、原图/缩略图计量、full/unavailable 动作和物理后重测全部通过。
7. ModelConfig 始终一个 enabled default、唯一正整数 priorityruntime 降级不回写配置;recommended 可重新派生或为空。
8. 私有告知按 super_admin 身份+版本跨标签、重启、重登和两浏览器持久;每次图片/完整提示词读取仍先写独立访问审计。
9. Playwright 已实际枚举 Service Worker、Cache Storage 和 IndexedDB,确认没有 internal preview、私有内容、项目、提示词或未保存草稿。
10. 跨用户隔离使用正常邀请/验证码/普通权限创建临时第二账号,完成后正常注销并进入规定清理路径。
11. 第 11.1 节 UIDesign 页面 × 状态矩阵的全部适用单元格均有实现和证据;Canvas Index 未计入产品页,UIDesign 中没有独立 Pencil frame 的页面没有漏测;1920 x 1080、200% 缩放、焦点、最长文案、弹层和多状态叠加无不可用重叠。
12. 视觉差异、50 元素性能、四比例导出、Chrome/Edge 下载与文件选择均在最终 RELEASE 版本通过。
13. 三模型、Resend、高德真实门槛通过;未配置自动充值、收费备用或自动扩容。
14. `RELEASE.json`、便携 ZIP、SHA-256、START-HERE 和固定端口边界一致;局域网、其他设备和不支持浏览器无法进入产品。
15. 制品、代码、日志、DB 配置、manifest、截图和证据中不存在真实凭据、验证码、会话令牌、管理员真实邮箱、完整私有内容或硬编码用户绝对路径。
16. 普通用户主框架和设置区持续原样显示:“测试数据仅保存在本机,不自动备份,也不会迁移到正式系统。”
缺少任一项时保持各 case 的实际测试状态,不得标记 P0-A 测试整体完成或发布完成。