21 KiB
Implementation Plan:错误可解释性与付款就绪治理
2026-09-11 到期治理接续(不统一续期)
本次只读复核现有 AST 基线为 673 条记录,到期仍为 2026-10-05,距 09-11 为 24 天;不是 673 个已证实产品故障。本轮主线修复不改到期日,不重复建立豁免台账。
先按员工首批业务路径排序既有延期域:付款账户维护(paymentAccounts)、发票池读取/识别/核验(invoicePool 系列)、登录/个人资料(auth/me)、审批动作错误(workflow)。这些是下一阶段候选模块名单,不批准整文件替换泛化错误或解冻配置/合同功能;实现时沿注册原因码、客户端映射、具体文本断言三件套按操作迁移。
09-18 验证首批操作迁移;09-25 按模块复审剩余记录;09-30 前逐模块写明未改原因、负责人与新验收点;10-02 查漏。只有有理由的单项/模块延期可交审,禁止到期前统一延长。当前仍全部保留原日期,本轮没有声称已完成余下错误迁移。
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 升级任务。
11. 发布与公网验收结果(2026-09-05)
- 产品提交:
a83791263833547fdab7ba6910f190fcccd5e34b,已 push;本任务 42 个文件,未包含 Trellis 升级改动。当前节及验收状态属于后续证据提交,不改变线上产品版本。 - 按既有入口
production-identity-bootstrap.mjs先执行只读预检再部署,两次退出均为 0。生产身份验证通过,迁移 reconciliation 前后均pending=none,回滚窗口 OPEN;应用发布、运行时 smoke 及备份合同刷新通过。未修改部署脚本、schema、migration、seed 或 flag 实现;使用既有部署 init,不将其描述为完全无运维写入。 /ready:HTTP 200,buildSha=a83791263833,与产品提交前缀一致。公网 bundle 扫描:scanned=106 focused=2 buildMarker=PASS findings=0。- 0014 候选 API:
ready=false,原因码精确包含payment_batch.legal_entity_required、payment_batch.payee_bank_required、payment_batch.payee_account_required。 - 公网真实浏览器:候选行可见,复选框 disabled;所属公司及银行资料缺项说明就近可见,
aria-describedby正确关联。不是本地 mock 或仅根据 bundle 推断 UI。 - 创建接口探针:先重新请求组合 readiness,确认三类缺项仍存在且
ready=false后,才发出用户批准的创建请求;HTTP 409、VALID_001、details.reasonCode=payment_batch.legal_entity_required。未补录资料、未创建成功批次、未执行付款。 - B2:受控认证只读查询确认
reimbursement_invoice_guard_v1仍为force_on + global;未调用 flag 修改接口。 - 验证账号边界:受控财务测试账号登录 HTTP 403,未重置、恢复或更改权限;上述页面与 API 验收改用可正常登录且有既有权限的受控管理员账号完成。不将本次结果表述为该财务账号登录恢复;该账号可用性待管理员单独核对。
- 所有公网探针仅输出状态码、原因码和布尔断言;未保存 token、真实银行资料、原始业务响应或包含真实业务内容的截图。临时浏览器均已关闭。
- AC12 已凭上述真实负向证据勾选。0014 仍需业务人员补所属公司、确认真实开户行并录入真实资料;非建行停止,不得伪填。合同只读超时仍为未归因事项,由
shunli在下次合同域验证前安排独立复现并归类,不计入本轮通过,不以本次发布关闭。 - 本 task 发布验收完成,未获归档授权,保留
in_progress和现有目录;Trellis 升级继续留在独立 worktree,未审、未提交、未部署。
12. 用户复核后的交接与待规划事项(2026-09-05)
- 用户确认部署后复核 PASS:线上
/ready=a83791263833,复核时工作区干净且 HEAD 与 origin/main 一致。 - 公司使用事实:用户明确本系统仅供本公司内部使用。付款主体字段用于确定对公付款账户归属,不等同于为外部公司提供多租户服务;是否存在多个付款主体不能仅由“内部使用”推导,后续方案以用户确认的单主体业务事实及有效配置为准。
- 待规划建议(未批准实施):单公司模式下,在主体归属唯一且有效时自动采用默认主体,保留现有账户归属结构;没有有效主体、多主体或员工已有显式归属冲突时不得猜测、覆盖或按首条记录取值。员工创建时赋值与付款时解析默认值是不同的数据合同,须先选定方案;付款事实加载、账户查询、候选/组合/事务须保持一致,不能只在页面隐藏缺项或只消除 blocker。当前付款路径仍读取员工的主体归属,尚未实现此建议;未全面审计所有员工写入入口,不宣称“全系统没有自动赋值”。
- 该建议可与单主体体验盘点一起收集,但不作为统一待办实现的前置条件或顺带改动;成本中心、单据分类仅列为待核查项,未确认其冗余,也不删除或擅自隐藏。
- 0014 当前仍由授权业务人员按顺序补所属公司、向本人确认真实开户行及卡号并直接录入系统;资料不发聊天,非建行停止,不伪填。完成后由业务人员走创建并提交复核、经理/CEO 复核、财务登记付款。本会话不执行这些真实业务动作。
- 下一项统一待办需求仅以该真实流程的三个交接观察为输入:交给财务组批、交给经理/CEO 复核、交回财务登记付款;每处记录谁交给谁、接收人通过什么现有入口或提醒知道轮到自己、是否需要口头催办。尚未发生的交接不预填结论,不记录真实银行资料。
- 独立待办仍保留:财务测试账号 403 需要单独解决并补财务身份真实验收(管理员代验不等于该角色验收);合同只读超时需单独复现归因,不计入通过;“三张单的答复”仅作为用户提醒保留,未明确具体单据和答复内容,不自行推断或代答。
- Trellis 升级另行审查:逐条核对 workflow.md 与 project-governance.md 删除或弱化的规则及理由,独立检查 hooks 行为变化;不以模型升级为整批删除保障的理由。本次仅记录后续事项,不启动新产品任务、不修改配置或真实资料、不自动提交、部署或归档。