补齐验收记录并归档非参与人报销只读投影任务 [08-28-reimbursement-nonparticipant-readonly-projection]
ci-e2e / e2e (push) Canceled after 0s
ci-minimal / check-minimal (push) Canceled after 0s

发现并修正文档滞后:业务提交 e8d6766 已确认合入并随后续发布上线(是当前
生产 b1d25d3c 的祖先提交),但 PRD 的 9 条 AC 一直保持未勾选、task.json
备注仍写"未推送、未部署"。implement.md 的服务端/前端验证清单与独立只读
复核记录逐条对应 PRD 各项,现据实补勾 AC 并更正 notes,再归档。

Co-Authored-By: Claude Sonnet 5 <[email protected]>
这个提交包含在:
2026-09-25 20:52:12 +08:00
共同撰写人 Claude Sonnet 5
父节点 b87b594e62
当前提交 7ad022ce4b
共修改 6 个文件,包含 15 行新增和 13 行删除
@@ -0,0 +1,74 @@
# 设计:非参与人报销详情只读投影修复
## 设计边界
本任务修正“访问资格”和“维护资格”被混用的问题,不新建报销详情页面、不重构审批工作台,也不改变角色/权限码。详情读取继续支持 owner/submitter、当前审批职责和 `expense.view_all`;报销人自助写入必须额外通过对象参与资格。
## 资格模型
```text
可查看详情
= owner/submitter 的 expense.view_own
OR 当前审批/职责访问
OR expense.view_all
可维护报销
= 当前用户是 owner/submitter
AND 当前状态允许该动作
AND 具备对应自助权限码
职责动作
= workflow 审批/退回、财务付款、付款批次等专用接口
!= 报销人自助维护
```
- 前端 PC 与移动端都从同一参与资格推导 `canMaintainReimbursement`,所有新增、编辑、删除、绑定、解绑、提交、撤回、催办和项目维护入口使用该事实。
- 只读视图不渲染禁用的维护按钮;它保留可访问的事实信息和材料查看动作,并用“只读查看”标明当前上下文。
- owner 与 submitter 都是参与人,支持代填/代提交的现有合同;其他角色不因 admin 或全量查看权限获得自助维护资格。
## 服务端数据流
```text
请求权限码通过
-> 锁定/读取目标报销
-> 校验 actor.id == owner_id OR submitter_id
-> 校验动作状态
-> 执行业务写入与副作用
```
- `reimbursements.js` 将 `ensureApplicantOrAdmin` 收紧为无 admin 豁免的参与人检查,统一用于报销头、删除、附件、明细和提交;`link-project` 补 `expense.edit_own`,从可查看检查改为参与人写检查,并增加 `draft/rejected` 状态门。
- `invoicePool.js` 的 reimbursement bind/unbind 分支移除 `expense.view_all` 写入豁免,只保留 owner/submitter。travel order 分支继续按 `travel.payment.manage`,不受本任务影响。
- 对象资格失败统一返回 `403 PERM_001`,并尽量使用稳定的 `only_owner_or_submitter` 详情码;测试断言事务内没有 update/create/delete、文件落盘、审计、workflow 或通知副作用。
- `ensureOwnOrCanViewAll` 继续用于详情、附件原件和材料预览等读取接口,并补到会返回预算、费用标准和风险结果的 precheck;这些读取接口不改为参与人专属。
- `expense.finance.pay` 和 workflow 路由是显式职责动作,保持原授权和状态机。
## 前端角色化呈现
### 参与人维护视图
- draft/rejected:保留报销头、明细、项目、发票、附件、提交和删除等现有动作。
- reviewing:保留撤回和催办;不恢复草稿编辑动作。
- 其他状态:沿用现有只读和付款结果呈现。
### 非参与人只读视图
- 概况区标明“只读查看”,下一步只描述状态事实,不给报销人操作指令。
- 明细、材料对应情况、处理进度、风险、原件和合并预览继续可见。
- 发票卡显示实际关联事实:`关联明细:...`、`已关联`;没有 `linkedItemId` 时明确为 `未关联`,不推断自动生成或匹配结果。`已关联` 适用于所有当前不可维护视角,不只限于非参与人。
- 不渲染任何维护控件,避免出现按钮后再依赖 403 纠错。
- PC 与移动端使用同一文案和资格含义;交互继续使用现有语义化 Button/Link、焦点样式和移动触控尺寸,不引入新的视觉系统。
## 兼容性与回滚
- 无 schema、migration、API payload 或角色定义变化;读取 DTO 不变。
- 权限收紧会移除管理员/财务通过通用详情维护他人报销的旧行为,这是本任务的预期破坏性修正。历史数据不迁移,已经发生的审计记录不改写。
- 回滚仅恢复本任务前后端代码;回滚会重新开放已确认的不安全资格,因此只有新版本造成更高等级阻断且获得明确回滚批准时才执行。
- 本任务提交后仍不部署;部署子任务必须冻结新的候选 SHA,不能继续使用 `174cb7a`。
## 关键权衡
- 复用同一详情页面但切换角色化模式,避免复制 PC/移动两套详情和数据查询;资格合同通过共享事实和回归保证,不用 URL 区分安全边界。
- 只隐藏动作不足以修复问题,因此同步收紧服务端对象资格。
- 不给 admin 保留隐式后门。若未来确有代维护需要,应新建设计明确、带原因与审计的管理动作,而不是复用员工自助接口。
- admin 豁免关闭后,离职/长期缺席员工的卡住草稿暂不提供代维护路径;真实需求由 Task B 的专用纠错动作或独立运营修复任务承接。
- 本轮保留票据和明细事实展示,为财务核验提供上下文;一页式审批的信息结构和金额门禁留给 Task B。
@@ -0,0 +1,54 @@
# 实施清单:非参与人报销详情只读投影修复
## 1. 实施前门禁
- [x] 用户已审查并明确批准本 PRD、设计和实施清单;运行 `task.py start` 后再修改业务代码。
- [x] 加载 `trellis-before-dev`,重读 frontend/backend/approval/reimbursement 规约和当前任务材料。
- [x] 冻结工作区与基线;确认部署任务仍为 planning,未推送、未部署、未访问或写入生产。
## 2. 服务端对象资格
- [x] 将报销头、删除、附件、明细和提交路由的 `ensureApplicantOrAdmin` 收紧为 owner/submitter 参与人检查,保留原状态与权限码门禁。
- [x] 为项目关联补 `expense.edit_own`,从 `expense.view_all` 可写收紧为 owner/submitter 可写,并增加 `draft/rejected` 状态门;不改变现有 feature flag 和 payload。
- [x] 为 precheck 补 owner/submitter 或 `expense.view_all` 对象级读守卫;普通非参与人 403 且 precheck 服务零调用,合法查看者保持成功。
- [x] 将 reimbursement 发票 bind/unbind 分支从 `isOwn || expense.view_all` 收紧为 owner/submitter;保持 travel order 分支不变。
- [x] 统一 403 详情码并检查失败顺序,确保资格失败发生在数据、文件、审计、workflow 和通知副作用之前。
## 3. PC 与移动端动作投影
- [x] 建立统一的参与人/可维护资格事实,PC 和移动端都使用“参与人 AND 状态”控制维护动作。
- [x] PC 收紧主要动作、明细增改删、项目选择、票据添加/移除、附件上传/删除、提交、删除草稿、撤回和催办。
- [x] 移动端收紧 sticky submit、明细增改删、票据添加/移除、附件上传/删除、删除草稿、撤回和催办。
- [x] 保留 owner/submitter 既有动作、财务/审批专用流程、材料查看和打印。
## 4. 只读文案与信息
- [x] 非参与人显示“只读查看”上下文,并把 draft/rejected 的下一步改为等待报销人处理的事实文案。
- [x] `linkedItemId` 为空改为“关联明细:未关联”,无法解析改为“关联明细:无法识别”;移除“自动生成或匹配明细”。
- [x] 当前不可维护视角的 bound 发票显示“已关联”,不渲染“草稿或驳回状态可移除”;参与人可维护视角保留必要操作说明。
- [x] 检查 PC/移动无禁用的伪操作、无内部权限码或实现术语,继续使用语义化按钮/链接和现有焦点/触控合同。
## 5. 回归测试
- [x] 服务端合成矩阵按 S1/S2/S3 分档覆盖:admin、finance/`expense.view_all` 非参与人对 form/items/attachments/submit/project/invoice bind-unbind 的 403 与零副作用;approved/paid 项目关联为 409 且零写入。
- [x] 服务端保留 owner/submitter 成功、GET/material preview 读取、workflow/finance pay 和 travel invoice 分支基线。
- [x] Playwright 覆盖 PC/移动 nonparticipant draft 的无维护动作和文案,并保留 participant 主流程;rejected/reviewing 由相同资格投影与既有撤回/催办测试覆盖。
- [x] 运行前端 typecheck/build、后端 node check/Prisma/聚焦测试及相关报销回归。
## 6. 复核、规约与提交
- [x] 按 `trellis-check` 复核跨层资格流,修复发现后重跑聚焦与全局门禁。
- [x] 因涉及权限模型,实施完成后启动独立只读 review,不让 reviewer 修改代码;结论为 PASS,无权限阻断。
- [x] 将“全量查看不等于对象维护、非参与人报销详情默认只读”的稳定合同更新到报销规约,避免再次 fail-open。
- [x] 运行 task validate、process contract、Trellis governance、敏感检查和 `git diff --check`。
- [x] 用户批准提交后,使用中文并引用 `08-28-reimbursement-nonparticipant-readonly-projection`;业务提交为 `e8d6766`,未推送、未部署、未归档。
- [x] 提交完成后更新部署子任务候选依赖;完整 PRD 已明确固定回滚、迁移状态和公网角色检查,继续等待用户审查部署方案。
## 7. 实施发现与验证记录
- `link-project` 原实现调用未定义的 `parseBody`,feature flag 打开后会在写入前返回 500,因此其错误的 `expense.view_all` 写授权历史上不可实际利用。本轮在同一路由范围内改为专用 Zod 解析函数,测试覆盖 draft 参与人 200、非参与人 403、approved/paid 409。
- r102 完整文件仍为 5/6:唯一失败保持在 `spec.ts:412`,根因仍是 travel category 夹具使 `showDescription=false`,与本轮 diff 无关;本轮新增的管理员 PC/移动只读投影用例通过。
- 后端全量 Vitest 为 1303/1306;3 个失败均在既有付款单 BIFF8 生成链路,错误为 `payment_order_generator_unavailable`,本轮 diff 未触及付款批次或生成器。当前任务相关的聚焦与扩展套件均通过。
- 独立只读权限复核结论 PASS:非参与人写入守卫位于副作用前,读取、workflow、财务付款和差旅专用分支未收紧。非阻断建议是后续补附件 DELETE 单独用例及 rejected/reviewing UI 状态矩阵。
- 提交前复核发现 precheck 缺少对象级读授权;本轮决定直接修复。路由现在先读取报销并复用 `ensureOwnOrCanViewAll`,普通非参与人 403 且 precheck 服务零调用,owner/submitter 与 `expense.view_all` 保持成功。父 task `task.json` 末字节已再次确认为 `0a`。
- 本轮没有 schema 或 migration 变化,没有访问生产、推送、部署或归档;部署任务继续处于 planning 并阻塞于本任务复核与提交决定。
@@ -0,0 +1,102 @@
# 非参与人报销详情只读投影修复
## Goal
把报销详情按对象参与资格收敛为两种明确模式:owner/submitter 在允许编辑的状态维护自己的报销;财务、审批人、管理员和其他因职责或 `expense.view_all` 获得访问权的非参与人只查看、核验材料和执行职责内的独立业务动作,不得复用报销人的维护入口或直接变更他人报销数据。
## Background
- `ReimbursementDetailPage.tsx:725` 和 `MobileReimbursementDetailPage.tsx:206` 只按 `draft/rejected` 判断可编辑状态。PC 的新增/编辑/删除明细、项目关联、附件上传/删除和主要提交动作,以及移动端对应动作,没有全部叠加 owner/submitter 资格。
- PC 与移动端的 `canUnbindInvoices` 都把 `expense.view_all` 当成他人报销的解绑资格;`invoicePool.js:1082` 的服务端解绑合同也允许该权限直接解除他人报销与发票的关联,因此不是单纯的前端误显。
- `ensureApplicantOrAdmin` 允许管理员修改、删除、提交他人草稿,新增/修改/删除他人明细和附件;`link-project` 与发票 bind 也把全量查看权限当成写入资格。全量查看、系统管理或当前审批职责不等于报销人自助维护资格。
- 发票卡片在所有视角共用 `resolveInvoiceNextStepText`,向非参与人显示“草稿或驳回状态可移除”;`linkedItemId` 为空时又显示技术性兜底“自动生成或匹配明细”。这些文案不是只读核验事实。
- 现有事实源要求页面按当前任务角色收敛,审批人和财务只看到职责内动作;报销材料仍允许授权查看原件和合并预览。Task A 已修复审批中撤回入口,但没有覆盖更广的草稿/驳回维护动作和服务端对象资格。
## Defect Severity
- **S1 真实写入能力**:reimbursement 发票 unbind/bind 把 `expense.view_all` 当成对象写资格;`ensureApplicantOrAdmin` 允许 admin 修改、删除、提交他人报销头、明细和附件。`link-project` 的授权设计同样错误(仅认证、复用读守卫且没有状态门),但原实现还调用了未定义的 `parseBody`,因此 feature flag 打开后会在写入前 500,历史上该写入洞不可实际利用;本轮仍须同时修复可用性和授权边界。测试必须断言 403/409 及零副作用,不能只检查按钮不可见。
- **S2 仅显示或前后端不一致**:PC 多处明细/附件动作和移动端对应动作只按 editable status 渲染,普通财务可能看到按钮但被部分服务端路由拒绝。测试必须断言 PC/移动 DOM 中不存在无资格动作,并保留直接 API 负向回归。
- **S3 已有正确边界**:PC/移动报销头编辑器已经同时检查 editable status 和参与人资格;实施只统一资格事实并证明基线,不重复改写已经正确的组件合同。
## Scope
- 统一 PC、移动端的报销参与人和可维护资格计算,修正所有报销人自助维护动作的投影。
- 收紧报销与发票路由中把 admin 或 `expense.view_all` 当成他人报销维护资格的服务端分支。
- 为报销 precheck 补对象级读取授权,避免普通员工横向读取他人预算、费用标准和风险结果。
- 为非参与人提供明确的只读查看语义和事实型票据文案,同时保留授权材料查看、打印、审批和财务专属流程。
- 使用合成身份和数据覆盖 owner/submitter、财务、管理员及普通非参与查看者,不读取或修改真实报销。
## Requirements
### R1 服务端对象资格必须 fail closed
- 报销 owner 或 submitter 统称参与人。只有参与人可通过报销人自助接口修改报销头、删除草稿、上传/删除附件、新增/修改/删除明细、提交报销、关联项目,以及为该报销绑定或解绑发票;状态和对应权限码仍需同时满足。
- `POST /reimbursements/:id/link-project` 必须补 `expense.edit_own` 权限门,只允许参与人在 `draft/rejected` 状态写入;非参与人返回 403,已通过、已支付等非编辑状态返回 409。不得继续使用 `ensureOwnOrCanViewAll` 读守卫授权写入。
- admin、财务、审批人及任何仅持 `expense.view_all` 的非参与人调用上述接口时必须稳定返回 `403 PERM_001`,并在读取目标对象后、任何业务写入、文件持久化、审计或通知副作用前失败。
- `expense.view_all` 继续允许授权查看报销详情、发票/附件原件和材料合并预览;不得把读权限降级为无权查看。
- `POST /reimbursements/:id/precheck` 虽不写业务数据,但会返回预算、费用标准、审批规则和风险结果,必须先读取目标报销并通过 owner/submitter 或 `expense.view_all` 对象级读守卫;普通非参与人返回 403,且不得调用 precheck 服务。审批页既有 `standardViolations` 快照继续保留。
- 显式职责动作保持独立:workflow 审批/退回、财务付款及付款批次接口不改;差旅订单 bind/unbind 分支不改。若未来需要财务纠正票据归属,必须在 Task B 设计专用、可审计的业务动作,不能继续借用报销人解绑接口。
- 不改变状态机、权限码集合、角色定义、数据库 schema 或迁移。
### R2 PC 与移动端统一维护资格
- 页面使用“参与人 AND 状态允许编辑”计算统一的 `canMaintainReimbursement`,不得再把 `draft/rejected` 或 `expense.view_all` 单独当成维护资格。
- 非参与人不得看到或触发:新增/编辑/删除明细、修改报销日期/事由/差旅摘要、关联项目下拉框、添加/移除发票、上传/删除附件、提交报销、删除草稿、撤回、催办等报销人自助动作。
- owner/submitter 在 draft/rejected 状态继续看到并可使用既有维护动作;owner/submitter 在 reviewing 状态继续使用 Task A 已修复的撤回和催办合同,其他状态行为不变。
- 只读查看者可继续查看报销概要、明细事实、处理进度、材料原件/合并预览、风险与材料对应情况,以及权限允许的打印入口;页面不通过禁用按钮暗示不可用动作,而是完全不渲染无资格动作。
- PC 和移动端必须使用同一资格语义,不能只修截图对应的 PC 页面。
### R3 非参与人使用事实型查看文案
- 同一详情路由可以复用,但非参与人应显示明确的“只读查看”上下文,不伪装成报销人维护页,也不新建一套重复详情页面。
- 非参与人的“下一步”按当前业务状态表达观察事实:草稿为“等待报销人完善”,驳回为“等待报销人修改后重提”,审批中显示当前处理事实,已通过为“等待财务处理”,已支付为“查看付款结果”。不得提示其新增明细、添加发票或提交审批。
- 发票存在真实 `linkedItemId` 时显示 `关联明细:<费用类型 · 明细说明>`;为空时显示 `关联明细:未关联`,关联对象无法解析时显示 `关联明细:无法识别`。不得再显示“自动生成或匹配明细”。
- bound 发票在当前不可维护视角(包括非参与人,以及 owner/submitter 查看 approved/paid 等非编辑状态)使用事实状态“已关联”,且不显示“草稿或驳回状态可移除”等操作提示。只有参与人的可维护视角可保留必要的移除说明。
- 材料对应、票面金额、日期和风险提示继续作为财务/审批核验信息展示;本轮不重构为 Task B 的一页式审批工作台。
### R4 回归与可部署基线
- 前端回归至少覆盖 PC 和移动端的非参与管理员/财务查看他人 draft、rejected、reviewing 报销,断言全部维护动作不存在、只读文案正确、原件和合并预览入口仍可用。
- owner/submitter 合成场景继续覆盖 draft/rejected 维护动作和 reviewing 撤回,防止把页面整体改成只读。
- 服务端对每类维护接口至少覆盖一个非参与高权限身份的 `403` 和零副作用,并保留参与人成功基线;发票 bind/unbind 同时保留差旅分支现有合同。
- 权限与审批规则改动实施后必须完成独立只读复核;未经复核和新工作提交,不更新部署候选、不推送、不部署。
## Acceptance Criteria
2026-09-25 核对:本节 9 条勾选此前未随实施同步更新,任务文档一直显示未完成;业务提交 `e8d6766` 已确认合入并随后续发布上线(是当前生产 `b1d25d3c` 的祖先提交),implement.md 的服务端/前端验证清单与独立只读复核记录逐条对应下列各项,现据实补勾。
- [x] 非参与管理员或财务查看他人 draft/rejected 报销时,PC 和移动端不存在明细、项目、票据、附件、提交、删除、撤回和催办等报销人维护动作。
- [x] 同一非参与人仍能查看报销概要、明细事实、处理进度、材料原件和合并预览;reviewing 审批/财务职责入口仍在原工作流页面正常使用。
- [x] 非参与人看到“只读查看”上下文;未关联票据显示“关联明细:未关联”,bound 状态显示“已关联”,页面不存在“自动生成或匹配明细”和“草稿或驳回状态可移除”。
- [x] 非参与人直接调用报销头修改/删除、附件增删、明细增改删、提交、项目关联、发票 bind/unbind 均返回 `403 PERM_001`,且对应数据、文件、审计、workflow 和通知零副作用。
- [x] 普通非参与人调用 precheck 返回 `403 PERM_001` 且 precheck 服务零调用;owner/submitter 与 `expense.view_all` 查看者保持成功读取基线。
- [x] owner/submitter 在 approved/paid 等非编辑状态调用 `link-project` 返回 409 且项目归属不变;draft/rejected 且具备 `expense.edit_own` 时保持成功基线。
- [x] owner/submitter 在既有允许状态仍可维护、提交、撤回和催办自己的报销;现有状态校验、材料查看、打印、财务付款、workflow 审批和差旅票据分支不变。
- [x] 无 schema、迁移、权限码或角色定义变更;前后端聚焦测试、前端 typecheck/build、相关 Playwright、Trellis 治理、敏感检查和 `git diff --check` 通过。
- [x] 只读权限复核结论为 PASS 后才形成新工作提交;部署子任务随后重新冻结包含 Task A 与本任务的候选 SHA,原 `174cb7a` 不再作为最终部署基线。
## Out of Scope
- Task B 的财务一页式审批、费用口径、`requires_invoice`、票据—明细强制关联、金额差异门禁和多票拆分。
- 为财务或管理员新增“代员工修改”“纠正票据归属”或其他管理修复入口。
- 修改 workflow 审批/退回、付款批次、财务付款、材料查看授权、打印权限、发票 OCR 或材料合并算法。
- 处理 `BX-20260828-0017` 的真实单据、票面差额、退回、付款或附件重传。
- 修复 r102 差旅夹具测试债;该项仍按独立任务处理。
- 推送、部署、公网验证、生产配置、生产数据写入和任务归档。
## Risks and Constraints
- 服务端移除 admin/`expense.view_all` 写入豁免属于真实权限收紧,必须用直接接口测试证明读取能力仍在、写入统一拒绝。
- 不能只隐藏前端动作;否则旧客户端或直接 API 仍可变更他人报销。
- 不能把所有非参与人操作一刀切:workflow 审批、财务付款和授权材料查看是职责动作,必须与报销人维护动作区分。
- 移除 admin 通用写入豁免后,员工离职或长期缺席造成的卡住草稿暂时没有代维护入口;若真实发生,应在 Task B 的专用可审计纠错动作或独立运营修复任务中重新规划,本任务不以保留通用后门兜底。
- 同一页面角色化比复制新页面更低风险,但 PC/移动端必须共享资格事实和文案合同,避免再次漂移。
## Test Plan
- 服务端语法与基础:`cd server && node --check src/routes/reimbursements.js && node --check src/routes/invoicePool.js && npm run prisma:validate`。
- 服务端聚焦:新增或扩展报销参与人资格矩阵测试,覆盖 form/items/attachments/submit/project/invoice bind-unbind 的非参与高权限 403、零副作用和参与人成功基线。
- 前端:`cd client && npm run typecheck && npm run build`。
- Playwright:扩展 `r102-reimbursement-main-flow.spec.ts` 或新增聚焦 spec,使用阻断 Service Worker 的合成 PC/移动身份覆盖 participant 与 nonparticipant 的 draft/rejected/reviewing 投影及只读票据文案。
- 全局回归:运行相关报销、材料预览和系统动作确认用例;完整 r102 的既有差旅夹具失败必须如实单列,不得把它当成本任务新失败或默认绿色基线。
- 治理:`node scripts/qa/check-process-contract.mjs --baseline --json`、`node scripts/qa/check-trellis-governance.mjs`、`node scripts/qa/check-sensitive-content.mjs`、`git diff --check`。
@@ -0,0 +1,32 @@
{
"id": "reimbursement-nonparticipant-readonly-projection",
"name": "reimbursement-nonparticipant-readonly-projection",
"title": "非参与人报销详情只读投影修复",
"description": "收紧非 owner/submitter 查看报销详情时的 PC、移动端操作投影与服务端变更资格,并改为面向审批/浏览者的只读票据文案。",
"status": "completed",
"dev_type": null,
"scope": "PC/mobile reimbursement read-only projection; reimbursement and invoice-pool object authorization; role-aware invoice copy",
"package": null,
"priority": "P0",
"creator": "shunli",
"assignee": "shunli",
"createdAt": "2026-08-28",
"completedAt": "2026-09-25",
"branch": null,
"base_branch": "main",
"worktree_path": null,
"commit": "e8d6766",
"pr_url": null,
"subtasks": [],
"children": [],
"parent": "08-28-reimbursement-detail-material-preview-fixes",
"relatedFiles": [
"client/src/pages/ReimbursementDetailPage.tsx",
"client/src/pages/MobileReimbursementDetailPage.tsx",
"client/src/lib/invoiceStatus.ts",
"server/src/routes/reimbursements.js",
"server/src/routes/invoicePool.js"
],
"notes": "实施与独立只读权限复核 PASS,业务提交为 e8d6766;同时补齐 precheck 对象级读授权。财务/审批详情中的行内费用标准提示当前不主动请求 precheck,由 Task B 的 B3 一页式审批恢复,现有 budgetHint、riskFlags 和审批页 standardViolations 快照不受影响。2026-09-25 复核:e8d6766 已确认合入并随后续发布上线(当前生产 b1d25d3c 的祖先提交),此前\"未推送、未部署\"的记录已过期;PRD 9 条 AC 据实补勾,予以归档。",
"meta": {}
}