15 KiB
15 KiB
Implementation Plan:错误可解释性与付款就绪治理
0. 开工门
- 用户复核并批准 PRD、design、implement。
- 批准后运行
task.py start;inline 模式实施前加载trellis-before-dev,不派 implement/check sub-agent。 - 记录开工基线、工作区状态和当前 20 处旧字符串测试命中清单。
1. 原因码合同与 parity
- 新增原因码注册表和 reason-aware validation helper;保留顶层
VALID_001与 legacy details。 - 新增客户端 reasonCode 映射与未知错误安全兜底,接入
ApiError.requestId。 - 建立 AST/结构化 parity 检查和版本化遗留指纹:基于规范化 AST 节点形状而非源码文本,纯格式化不得改变指纹;注册唯一性、服务端使用、客户端覆盖、孤儿映射、新增遗留、过期例外全部 fail closed。
- self-test 覆盖同 key 跨域、未注册、拼写、缺映射、一增一删、结构性指纹变化、纯格式化不变、到期和主链路试图加例外。
- 接入既有治理入口;报告只输出相对文件、原因码/域、计数和稳定 finding。
2. 首批主链路迁移
- 迁移报销提交/编辑/票据关联的用户可达校验。
- 迁移付款创建/复核/回单/登记的用户可达校验。
- 迁移请假提交、考勤修正提交的用户可达校验。
- 主链路达到零例外;其他存量按域记录 owner/reason/expiresOn,不使用永久目录豁免。
- 逐项处置 20 处旧字符串:
- 客户端 4 个 mock 改为 reasonCode,并断言具体提示/安全兜底;
- 主链路服务测试断言 exact reasonCode、status 和兼容 details;
- workflow utility、ERP procurement、budget 等逐项迁移或登记有效遗留;
failure_message、批处理 reason 等持久化/内部摘要单独验证兼容,不与 UI 文案混同。
3. 付款 readiness
- 抽取共享 resolver,复用快照、部门 gate、账户、占用和 reviewer 事实;搬移 create 条件而非复制。
- 候选批量计算单条 readiness,避免 N+1;异常候选仍返回但不可选择。
- 增加多选组合只读 readiness API;严格输入,响应仅含安全 blocker。
- create 在锁和事务内复用 resolver;失败零 batch/item/成功 audit/notification 副作用。
- 覆盖资料或账户变化后的即时重算,不新增持久字段或长期缓存。
4. 付款页面
- 扩展 API types;候选行显示就近 blocker,正常行无解释噪音。
- 异常候选不可选择并具备可访问原因;有维护权限才显示维护入口。
- 多选变化查询组合 readiness;提示紧贴主按钮,按钮禁用、handler 和 create 二次拦截消费同一原因映射。
- 并发失败留在动作附近;toast 为补充;未知原因使用 request ID 安全兜底。
- PC 覆盖主路径;移动端保持现有能力边界且不显示技术错误。
5. 验证
node scripts/qa/check-error-reason-contract.mjs --self-test与 strict 正常扫描。- Server:registry/helper、付款 readiness/route、报销/B2、请假、考勤 focused Vitest;涉及 workflow/状态机时做只读 review。
- Client:error mapping 测试;付款 Playwright 覆盖正常、三类 0014 缺项、非建行、跨公司、占用、无 reviewer、并发、未知 request ID fallback、键盘/ARIA。
cd server && npm run prisma:validate;触达 JS 运行node --check。cd client && npm run typecheck && npm run build。- UI strict/self-test、Trellis governance、敏感信息扫描、
git diff --check。 - 核对 schema/migration/seed/flag/routing 和生产数据修改为零。
6. Spec、提交与部署
- 实现验证稳定后,把“原因码 + 遗留指纹 + 主链路零例外”和“共享 readiness 多消费者”沉淀到现有 spec,不新建泛化设计文档。
- spec 明确迁移一条遗留原因码的完整合同:服务端注册、客户端映射、具体内容断言三件缺一不可。
- 提交建议:A 原因码/parity;B 首批迁移;C 付款 readiness/UI;task 元数据另行回填。
- commit message 使用中文并引用
[09-05-error-explainability-payment-readiness]。 - commit、push、provenance、部署分别批准;切换前再次运行 UI strict。
- 部署保持
SYNC_ENV=0、existing 数据合同,无 migration/schema/seed/flag/routing/资料补录。 - 部署后核版本、公网文案、合成 readiness/错误投影、B2、maker-checker 和真人单据状态。
- 不自动创建 0014 批次、不复核、不登记付款;真实业务由授权人员完成。
- 回滚无需数据库补偿;新客户端遇旧服务端未知码仍须显示可行动兜底。
7. 开工基线与 20 处旧断言处置
2026-09-05 恢复实施;开工 HEAD 为 ab82fe3,仅当前 task 目录未跟踪并已保留。
依据交接中的明确批准幂等执行 task.py start;本会话主会话实施/检查,没有派 implement/check agent。
原生 update_plan 未在工具集中提供,未创建替代 plan;继续以原已批准清单执行。
下表行号是开工前定位,不以当前字符串总数作为完成证据。兼容响应的原始 message
仍可以出现在测试 fixture;用户展示断言必须具体,不能用宽泛正则掩盖。
延期域均由 shunli 负责,遗留 AST 基线到期日为 2026-10-05(UTC);未增添主链路例外。
| 编号 | 开工前测试定位 | 逐项处置 |
|---|---|---|
| T01 | client/tests/e2e/r103-cross-page-ui-consistency.spec.ts:544 |
采购附件 mock 增加 purchase_attachment.unsupported_type;对应服务端分支、固定中文断言和安全上传浏览器断言一并保留。 |
| T02 | client/tests/e2e/r111-invoice-reimbursement-guard.spec.ts:448 |
提交 mock 改为 reimbursement.items_invoice_item_guard_failed;保留四类精确数量解释、异常字段安全兜底与双端就近提示断言。 |
| T03 | client/tests/e2e/r111-invoice-reimbursement-guard.spec.ts:481 |
明细关联 mock 改为 reimbursement.invoice_invalid_unlinked_item_state;保留准确中文和原子关联断言。 |
| T04 | client/tests/e2e/pc-finance-admin-visual-wave4.spec.ts:83 |
核验 mock 分别登记频控/次数上限原因码;服务端注册、客户端映射、固定内容和 route details 精确断言齐全;旧 VALID_001: UI 前缀断言改为无技术前缀的准确中文,不用正则。 |
| T05 | server/tests/unit/utils/workflow.test.js:64 |
workflow 配置工具延期;保留原准确消息断言,对应 utils/workflow.js 到期指纹。 |
| T06 | server/tests/unit/utils/workflow.test.js:65 |
workflow 空步骤配置延期;保留原断言,对应同文件独立调用指纹。 |
| T07 | server/tests/unit/utils/workflow.test.js:87 |
approval-rule 配置工具延期;保留原断言及到期指纹。 |
| T08 | server/tests/unit/utils/workflow.test.js:94 |
approval-rule 无效配置分支延期;保留原断言及到期指纹。 |
| T09 | server/tests/unit/utils/workflow.test.js:112 |
approval-rule 结构配置分支延期;保留原断言及到期指纹。 |
| T10 | server/tests/unit/utils/workflow.test.js:331 |
缺少 assignee rule 配置延期;保留原断言及到期指纹。 |
| T11 | server/tests/unit/utils/workflow.test.js:332 |
未知 assignee rule 类型延期;保留原断言及到期指纹。 |
| T12 | server/tests/unit/services/leave.test.js:50 |
改为精确 VALID_001、422、leave.leave_invalid_range 与兼容 leave: invalid_range。 |
| T13 | server/tests/unit/services/leave.test.js:98 |
改为精确 VALID_001、422、leave.leave_non_working_range 与兼容 leave: non_working_range。 |
| T14 | server/tests/unit/routes/integrations.erp.procurement.test.js:49 |
ERP procurement 非首批域延期;保留原消息断言与 integrations 模块到期指纹。 |
| T15 | server/tests/unit/routes/integrations.erp.procurement.test.js:57 |
ERP procurement 另一资格分支延期;保留原断言,不扩展集成能力。 |
| T16 | server/tests/unit/routes/paymentBatches.reimbursement.test.js:2341 |
内部持久化 failure_code/failure_message 及 API 字段兼容断言保留;执行风险既有 JSON 仅附加注册原因码、刷新安全说明,不改风险状态;明细/风险卡经专用 helper 展示,历史与未知原因安全兜底,不直接展示原摘要。 |
| T17 | server/tests/unit/routes/workflow.batch.test.js:377 |
内部批处理 reason 兼容断言原样保留;不把批处理摘要冒充用户恢复文案。 |
| T18 | server/tests/unit/routes/workflow.permissions.test.js:172 |
workflow 管理查询域延期;保留准确消息与 routes/workflow.js 到期指纹。 |
| T19 | server/tests/unit/budgetPool.service.test.js:28 |
budget scope 配置域延期;保留原断言与 services/budgetPool.js 到期指纹。 |
| T20 | server/tests/unit/budgetPool.service.test.js:75 |
budget pool 候选配置域延期;保留原断言及到期指纹,不变更预算规则。 |
8. 实施与只读复核记录
- 原因码合同接入既有
run-release-gate.shstatic 阶段;主链路包含 invoice bind/unbind 与共享 item resolver,不借整目录永久豁免绕过。 - 候选、组合预检和创建事务复用
loadPaymentCreationFacts与resolvePaymentBatchCreationReadiness;候选关系、占用、公司账户、复核人批量读取。 部门 gate 仅在单次事实加载内复用底层 flag 查询,仍调用原判定实现;没有修改 flag。 - 只读 reviewer 首轮发现两项 P2:未选中时无法切换付款渠道、失效已选项无法取消。 已分别移出渠道选择器的选中条件、允许失效已选项取消,并补浏览器回归。 第二轮只读 review 确认这两项闭环及请求内 gate 查询复用;无新增高置信问题。
- 系统 Python 缺
xlwt导致既有银行模板导出测试失败;使用现有隔离测试环境提供的 Python、通过PAYMENT_ORDER_PYTHON_BIN运行后恢复通过。未改依赖清单或生产配置。 - 首轮扩大浏览器运行另包含一个合同只读用例超时,未完成该项归因,未改合同产品逻辑或该断言; 付款、B2、采购与本轮核验反馈改为明确 focused 集重跑,不把无关失败标成通过。
- 已更新现有 backend/frontend spec;根因归类为跨层合同和传播缺失,而非真实资料补录。
- 本轮不提交、不 push、不部署、不归档;真实资料和 0014 的任何业务写入均未执行。 部署后 AC12 仍须独立批准与公网证据,不能用本地合成结果替代。
- 最终检查进一步发现两处付款明细直出旧失败摘要,已按 T16 补齐安全展示转换; 新原因只保存在既有执行风险 JSON 元数据中,未新增 schema、readiness 持久字段或日志机制。 追加只读 review 确认历史兼容、事务和风险状态边界,无新增高置信问题。
9. 本地验证结果(2026-09-05)
| 验证 | 结果与边界 |
|---|---|
| AST strict | PASS;142 注册原因、199 已迁移使用点、695 遗留调用、主链路例外 0、finding 0。遗留身份同时包含相对文件、operation 和规范化 AST 形状,不能靠移动操作或一增一删抵消。 |
| AST self-test | 20 项 PASS;正常跨域与纯格式化退出 0,其余拒绝场景退出 1,包括六类必查失败、主链路加例外、同数替换、别名、具体内容断言缺失、operation 移动、条件式通用码和不安全文案。 |
| 扩展服务端 focused | 23 文件 / 407 用例 PASS:报销、票据、请假、考勤、付款、readiness、registry/helper 和客户端投影。 |
| 后续兼容/新增边界 focused | 8 文件 / 309 用例 PASS:包含当轮 75 个付款 route 用例、149 个具体内容/安全投影用例、采购附件和延期域/内部摘要兼容测试;与上一行有交集,不合并虚报数量。 |
| 最终摘要投影 focused | 4 文件 / 244 用例 PASS:76 付款 route、150 内容/投影、16 readiness、2 helper;新增既有风险元数据更新与历史摘要保护,和前述测试有交集。 |
| Playwright focused | 18 个不同用例 PASS(原 17 项 + 最后追加的失败摘要保护 1 项):银行正常提交、现金渠道、三类缺项、非建行、跨公司、占用、无 reviewer、创建并发、移动未知原因排障编号、键盘/ARIA、渠道可达与失效项取消、B2 双端、采购附件、核验错误、PC/移动视口历史失败明细及风险保护。仅本地全合成 API mock。 |
| 客户端工程 | npm run typecheck、npm run build PASS;未部署构建产物。 |
| 服务端工程 | Prisma validate、触达 JS/MJS node --check PASS;没有执行 migration 或数据库重置。 |
| 治理 | UI strict/self-test、process baseline、docs registry、Trellis validate/governance、敏感扫描、git diff --check PASS。 |
| 只读 review | 首轮两项 P2 已修复;第二轮确认闭环;追加摘要投影与风险元数据复核无新增高置信问题。Reviewer 未写文件、未操作真实数据。 |
| 边界核对 | schema/migration/seed/flag 实现、前端 routing、真实资料与真实付款写入均为 0;0014 未操作。 |
复跑入口:node scripts/qa/check-error-reason-contract.mjs --strict 与 --self-test;
服务端测试中的银行模板导出需把 PAYMENT_ORDER_PYTHON_BIN 指向满足现有锁定依赖的隔离测试 Python。
浏览器 focused 文件为 payment-reviewer-permissions.spec.ts、r111-invoice-reimbursement-guard.spec.ts、
r103-cross-page-ui-consistency.spec.ts 和 pc-finance-admin-visual-wave4.spec.ts,筛选本表相关用例;
不得对公网或真实账户套用这些 mock 场景。
10. 独立复核、遗留验证事项与发布授权(2026-09-05)
- 用户独立复核 PASS:真实调用点注入未注册原因码时 strict 退出 1;向主链路新增例外时 strict 退出 1;确认 readiness 三处共用、20 项 self-test 及格式化指纹稳定。用户确认探针全部还原,基线校验与备份一致。
- 两个 task 已按完整文件物理分离;本目录仅保留错误可解释性与付款 readiness,Trellis 升级在独立 worktree 待单独审查。原治理文件均与开工 HEAD 一致,分离后原因码及治理门禁通过。
- 未解决验证事项:首轮扩大浏览器运行的合同只读用例超时,未归因、不计入通过结果,不是已确认的产品缺陷或可永久忽略的 flaky。后续合同域验证前必须单独复现,补充精确用例、失败步骤及环境证据后归类;本任务不得通过放宽断言、重试至绿或混入 focused PASS 关闭它。
- 用户明确批准本任务提交、push、部署;部署后核验
/ready与 bundle marker、文案扫描零 findings、0014 候选禁用及缺项原因、创建接口 fail-closed、B2 保持 force_on + global。 - 生产核验只使用最小脱敏结果,不读取或修改真实银行卡资料;不补录 0014,不执行真实付款。若前置条件变化可能导致创建成功,停止强行创建探针。
- Task 保持
in_progress;独立复核通过不等于公网 AC12 完成。发布和公网证据待实际执行后回填,不提前标记 PASS,不归档 Trellis 升级任务。