# 实施清单:B2 提交错误可解释性与 OCR 降级合同 ## 1. 规划与证据冻结 - [x] 记录生产 `OCR_PROVIDER=tencent`,确认不回退 B2 flag。 - [x] 用生产只读数据复现 0014 当前三类 B2 判断与时间线,记录历史失败 details 不可恢复的证据边界。 - [x] PRD、design、implement 经用户审查并明确批准;批准前不运行 `task.py start`、不改业务代码。 ## 2. 服务端门禁与 DTO - [x] 抽取并独立测试只接收最小字段集的金额证据纯函数:verified / manual_review / ocr_failed;三个分支各含边界用例,不依赖 Prisma 记录形状。 - [x] 提交查询补齐最小 OCR 执行字段,将“无关联 PDF”“金额识别失败”“降级人工核对”“完整金额超额”分支按设计顺序实现。 - [x] 金额完整时继续用 `Prisma.Decimal`;混合降级票据时不做不完整金额比较,并把人工核对摘要返回成功响应。 - [x] 保持 `invoice_item_guard_failed`、既有字段与 flag off 语义,新增 `unlinkedInvoiceIds`、`amountUnavailableItems` 等定位字段。 - [x] 详情 DTO 增加发票金额证据状态和整单人工核对投影,不返回 OCR 原始结果或敏感票面字段。 ## 3. 未归属发票恢复接口 - [x] 新增 bound 未归属发票关联本单明细的单事务接口;锁发票/报销、复用 `ensureReimbursementParticipant` 校验 owner/submitter,并校验状态和目标明细归属。 - [x] 只更新 `linked_item_id` 并留审计,不执行 unbind/bind、不短暂释放发票、不创建明细。 - [x] 覆盖不存在、非 bound、已有归属、跨单明细、非参与人、非草稿/驳回及并发状态变化,失败保持零写入。 ## 4. PC 与移动端 - [x] 增加共享的 guard details 窄化解析器和安全摘要;未知 details 回退通用中文,不展示 UUID/原始 payload。 - [x] PC/移动提交失败同时显示可访问摘要和明细/票据就近错误;toast 仅作补充。 - [x] 未归属票据卡片显示显著状态、明细选择和原子“确认关联”动作;成功刷新详情,失败错误留在卡片。 - [x] 金额识别失败与降级人工核对使用不同文案;后者在提交前/成功后及审批只读详情持续可见,不宣称自动校验通过。 - [x] 不调整报销事由编辑、处理进度、关联项目顺序、自动生成明细或 PC sticky 操作布局。 ## 5. 文档与规约 - [x] 更新 `docs/报销发票附件规则.md`:明确关联 PDF 与金额证据分层、off 人工核对、腾讯金额失败和混合票据行为。 - [x] 更新 `.trellis/spec/hyb-oa/reimbursement-invoice-ocr.md` 的提交合同、错误矩阵、测试矩阵和“取消/增加校验需核对降级合同”通用规则。 - [x] 不新建文档,不改员工手册中与本轮无关的填报粒度和日期口径。 ## 6. 测试矩阵 - [x] 后端:未归属发票返回可定位 ID;需票明细无 bound PDF;Tencent/已尝试 OCR 但金额为空;完整金额超额;正常金额通过。 - [x] 后端:off/未执行 OCR 且金额为空允许提交并标人工核对;有金额 + 降级无金额进入人工核对;有金额 + 识别失败阻断;免票补助无票通过。 - [x] 后端:0014 类 5 明细/5 金额有效发票逐条关联时正常提交;B2 flag off 兼容路径不变。 - [x] 后端:原子重新归属接口的成功、权限、状态、跨单、已有归属和并发零写入矩阵。 - [x] 前端单元/组件:四类中文摘要、畸形 details 回退、ID 到明细/发票可读标签映射、无内部 ID 泄露、ARIA 告警语义。 - [x] PC/移动 E2E:错误就近呈现、未归属卡片原子选明细、人工核对提示、正常提交;使用合成数据,不保存真实票据截图。 - [x] 运行 `cd server && npm run prisma:validate`、聚焦 Vitest,必要时全量相关路由回归。 - [x] 运行 `cd client && npm run typecheck && npm run build` 及聚焦 Playwright。 - [x] 运行 `node scripts/qa/check-process-contract.mjs --baseline --json`、`node scripts/qa/audit-docs.mjs --check-registry`、`node scripts/qa/check-trellis-governance.mjs` 和 `git diff --check`。 - [x] 部署失败热修:运行 `node scripts/qa/check-ui-design-rules.mjs --strict`,确保四个普通用户禁用术语(`绑定整单`、`不绑定明细`、`发票池`、`OCR`)在报销 PC/移动错误态均无命中;strict 扫描 148 个文件为 0 findings,self-test PASS,该门禁已在重新提交和部署前通过。 - [x] 第二次部署失败热修:PC/移动提交 mutation 在 B2 `onError` 中关闭 `warn` / `manual_review` 确认弹窗,待模态层卸载后聚焦首个问题;双端回归分别走完两类预检的弹窗确认、B2 阻断、遮罩消失、就地关联和再次提交成功。 ## 7. 只读复核、提交与部署门 - [x] 实施后执行只读专项 review,重点检查权限/状态机、金额证据三态、混合票据、API 兼容和 PC/移动数据流;reviewer 不改代码。 - [x] 用户明确批准本轮无障碍机械修正后直接提交并回填元数据;业务提交使用中文并引用 `08-30-b2-submit-error-ocr-fallback`。新候选须经用户最终 diff 复核后才可 push,部署仍需独立授权。 - [x] 部署前视觉门:确认 `frontend-design` 当前不可用后,经用户批准以 `ui-ux-pro-max` + Playwright 真实渲染替代。PC/390px 手机/667px 横屏无横向溢出,移动选择框与确认按钮均为 44px,sticky 底栏贴合视口且不遮挡问题卡片,提交失败后焦点落到首个具体问题;检查发现 `red-600` / `red-50` 对比度约 4.42:1,已将 PC 与移动报销详情页内所有同类错误块统一为 `red-700` / `red-50`(约 5.92:1),并为共享操作错误补齐 `role=alert` / `aria-live=assertive`,复跑通过。该替代门不冒充 `frontend-design`。全局错误语义色 token 化与视觉裁决 skill 引用修订作为后续治理债处理。部署后仍须用合成草稿完成公网验收,确认 `/ready`、bundle marker、B2 `force_on + global` 和真人单据状态;验收前不通知员工可以测试。 ## 8. 回滚 - [x] 两次公网验收失败均停止员工测试并回滚到 `0f3ecfa`;本轮无 migration/seed/flag 写入,真人报销和票据关系未修改,合成草稿已删除、合成票据已归档。