文件
hyb-oa/.trellis/tasks/08-30-reimbursement-expense-policy-invoice-guard-deploy/prd.md
T
wangsli faf1a0cff9
ci-e2e / e2e (push) Canceled after 0s
ci-minimal / check-minimal (push) Canceled after 0s
规划报销费用门禁生产部署(08-30-reimbursement-expense-policy-invoice-guard-deploy)
2026-08-30 00:38:46 +08:00

13 KiB
原始文件 Blame 文件历史

报销费用政策与票据门禁生产部署

Goal

把已完成并复核的报销费用政策与票据门禁安全发布到生产:先以 B2 有效关闭的状态完成 B1 migration、主数据和 owner 基线验收,再在隔离部门内验证 B2,最后全局启用并让财务统一退回 4 张真实审批中单据。全过程不得拿真人单据做灰度试验,不得因应用回滚误删 migration 或已建立的票据—明细关系。

Background and Confirmed Facts

  • 生产当前运行候选为 59dc5f6;本地 main 比 origin/main 领先 4 个提交,B1/B2 业务实现为 6e999d5,尚未推送、部署或应用生产 migration。
  • 待应用 migration 固定为 20260829120000_reimbursement_expense_policy_invoice_guard,只新增 expense_types.requires_invoice(非空、默认 true)、reimbursement_items.asset_prior_approval_basis(可空),并把两项补助精确回填为免票。
  • init 会运行 migration、seed:config 与 seed:trialrun;费用类型改名、停用和新建由 seed 落地,不能只核 migration 表。
  • B2 是 env hard gate、代码白名单和 DB flag 三件套。生产部署使用 SYNC_ENV=0,不会自动把新增 env 写入生产;env 未开启时 DB force_on 无效。
  • B2 提交门禁只检查:已绑定发票是否关联明细、需票明细是否有关联票面金额、申报金额是否高于关联票面合计。它不自动校验“同一费用类型 + 同一发生日期”的明细粒度。
  • 当前 4 张审批中单据均需退回重做:0014 票据关联/金额完整但 5 个日期被压成 1 条明细,B2 无法拦截;0016、0017、0018 可被关联或金额门禁拦住,但日期/粒度仍需人工说明和复核。
  • 生产现有 admin/employee smoke 可登录,旧 finance smoke 身份已不在 OA 用户表;employee smoke 当前没有自有报销。现有 3 个部门中有 1 个无成员,可在明确授权后作为隔离灰度范围;employee smoke 当前所在部门有 5 个用户,不能直接作为灰度范围。
  • 材料合并预览会创建短期进程内预览包并写预览审计记录;这是设计内审计写入,必须纳入验收授权,而不是伪装成零写入检查。

Scope

  • 创建生产验收条件:把现有空部门固定为常驻 smoke 部门,新建专用 OA-only manager/finance smoke 身份,把 employee smoke 迁入并长期保留,建立专用合成草稿和合成票据;凭据只通过私有 env 注入。
  • 冻结、推送并严格验证包含 B1/B2 实现与本部署合同的最终候选。
  • 使用 existing 部署入口完成 migration、seed、应用切换、构建标识、runtime/external smoke、备份合同刷新和角色化公网验收。
  • B1 验收通过后,在 DB 继续 force_off 时显式启用生产 env hard gate 并重启同一候选;随后仅对隔离部门开启 B2,验证通过后再全局开启。
  • B2 全局开启后,由真实财务角色统一退回 BX-20260828-0014/0016/0017/0018;员工按新规则修正并重提,财务人工复核日期粒度。
  • 已付款 BX-20260730-0008 仅留历史关联缺口记录,不退回、不改付款数据;空草稿 BX-20260805-0013 不处理。

Requirements

R1 授权与候选冻结

  • 规划批准、生产验收准备写入批准、push 批准、生产配置批准、部署批准、B2 灰度批准、B2 全局启用批准和真实单据退回批准互不推定。
  • 本次规划文档提交完成后,以最终干净 HEAD 冻结候选;具体完整 SHA 和 12 位 bundle marker 记在外部审批记录,不写回仓库。冻结后任何提交、tracked/untracked 变化或 HEAD 漂移都使审批失效。
  • push 后必须确认候选是 upstream 祖先,严格 provenance 为 PASS,published=true、remoteVerified=true、overrideUsed=false;禁止 unpublished override。
  • 固定应用回滚基线为当前生产候选 59dc5f6。不得临场选择其他提交。

R2 验收身份与合成数据

  • 不恢复已从用户表消失的旧 manager/finance smoke;新建角色纯净的 OA-only manager smoke(manager + employee)和 finance smoke(finance + employee),不绑定或重置真实员工身份。
  • 把已确认无成员的部门固定为常驻 smoke 部门;employee smoke 迁入后不再迁回,manager smoke 设置为其直属 manager,三类 smoke 身份留在该部门供后续隔离灰度复用。迁移前再次确认部门仍无真人成员。
  • 普通 employee 缺直属 manager 时,报销提交会因 blockOnUnresolvedAssignee=true 和 no_manager_found 返回 409;owner_self_fallback_role=finance 只处理 owner 本人持有 CEO 角色的特殊情形,不得把 finance fallback 当作普通 smoke 的兜底方案。
  • 合成草稿、明细、票据和附件必须使用明显测试标识与非真实材料;不得复制真人票据、金额、说明或附件。
  • 所有成功提交的合成单据在验证后按合法 owner 流程撤回并删除;失败场景草稿直接删除。审计/工作流历史按系统合同保留,不做数据库硬删。
  • 材料预览允许产生 REIMBURSEMENT_MATERIAL_PREVIEW_BUNDLE_ISSUED / REIMBURSEMENT_MATERIAL_PREVIEWED 审计记录;证据只保留状态、页数、页序、比例与构建标识。

R3 B1 部署

  • 部署前必须显式记录 migration status,期望 pending migration 只能包含 20260829120000_reimbursement_expense_policy_invoice_guard;多、少或漂移均停止。
  • 使用 deploy/trialrun/deploy-existing.sh、existing 数据模式和 SYNC_ENV=0。不 reset 数据库,不覆盖环境、DB 或附件目录。
  • init 后再次显式记录 migration status,并核对:新增列存在、两项补助 requires_invoice=false、其他启用类型为 true、费用类型改名/停用/新建准确、account_code_default 未被 seed 清空。
  • 若正式科目表仍未提供,account_code_default 保持 null;责任角色为财务负责人,原目标回填日期 2026-09-05 保留在父 task,不由开发猜测编码,也不以此阻断本轮材料门禁上线。
  • B1 首次切换后 B2 DB 记录必须由 seed 显式创建且为 force_off,不得以 missing_flag_record 代替关闭意图;B2 必须保持 effective off。owner 普通草稿维护、assets 事前批准、2,000 元提示、无票补助和非参与人只读投影均须通过。

R4 B2 hard gate 与隔离灰度

  • B1 验收通过后,先确认 DB 为 force_off,再经单独生产配置批准把 REIMBURSEMENT_INVOICE_GUARD_ENABLED=true 写入受控生产 env,并重启同一候选。重启后应显示 env enabled 但 effective off。
  • 隔离灰度只能将 DB flag 设置为 force_on + department_allowlist + 隔离部门 ID;不得使用 employee smoke 原部门,不得把 4 张真人单据作为灰度样本。
  • 每个状态必须记录精确 reason:env 未开为 env_hard_off;env on + DB off 为 flag_force_off;灰度名单外部门为 department_not_allowed;隔离部门在本轮 force_on 模式下为 flag_force_on_department_allowed;全局开启为 flag_force_on_global。不得只凭提交成功/失败推断门禁已运行。
  • 普通 feature flag 列表使用 strictDepartment=false,在部门 allowlist 状态只能给出延后判断,不能证明具体 actor 命中。灰度证据必须运行受控容器内只读 actor/department 诊断,并与报销详情 DTO 的 invoiceGuardEnabled 交叉验证;诊断不输出部门 ID、账号或原始 payload。
  • 隔离灰度必须验证:owner 普通提交成功;两项无票补助不被阻断;多票显式关联成功;超票面准确阻断;旧草稿补齐后可提交;非参与人零写入副作用;切回 force_off 后旧提交路径恢复且既有关联数据不删除、不解绑。
  • B2 不校验日期粒度的事实必须在验收结论和财务退回说明中保留;不得把 flag PASS 描述为“系统已自动保证明细粒度”。
  • 灰度失败立即把 DB 恢复为调用前完整快照,最迟先落 force_off;关闭 flag 只回退门禁,不回滚关联数据。

R5 全局启用与真实单据处置

  • 隔离灰度全部通过并清理合成单据后,必须再次获得全局启用批准,才可把 rollout 从部门 allowlist 改为 global;更新使用版本并发控制并产生 feature flag 审计。
  • 全局启用后先验证 effective global on、owner 基线、财务/管理员只读投影、错误提示和运行指标,再安排真实财务账号退回 4 张单据。
  • 0014 退回说明必须写死“按 5 个发票日期拆成 5 条明细”,重提后由财务人工确认;B2 不会自动拦截原样重提。
  • 0016 至少按 3 个发票日期拆分并逐票关联;0017 按 2 个发票日期重做、上传正式 PDF、逐票关联并解释 1,949.81 元差额;0018 若同日同类可保留一条,但须改正日期并关联全部 5 张票。
  • 退回必须由真实 finance 审批主体执行。admin、smoke admin 或脚本不得代行财务职责;Codex 不保存真实财务凭据。

R6 失败与回滚

  • 切换前失败只停止,不执行应用回滚。migration/seed 或应用切换开始后任一必需门禁失败,先确保 B2 DB force_off,再按同一 existing 入口回滚应用到 59dc5f6。
  • 应用回滚不逆向 migration、不删新增列、不删除票据—明细关联。新增列对旧应用为向前兼容。
  • init 已改变费用类型主数据,应用回滚不会自动恢复旧名称/启用状态。部署前必须保存受影响类型的脱敏主数据快照;只有 B1 主数据本身被判定错误且取得单独补偿批准时,才按快照执行精确补偿,不运行整库回滚或重放猜测 SQL。
  • 回滚后必须核对 /ready、bundle marker、migration status、runtime/external smoke、DB/附件挂载和备份合同;若 env hard gate 已写入,可保持 true,但 DB 必须 force_off。若回滚原因涉及 flag 配置或新代码误触发,则同时恢复 env hard-off 并重启旧应用。

Acceptance Criteria

  • 常驻 smoke 部门、专用 manager/finance smoke、employee smoke 迁入和直属 manager 关系经单独生产写入批准;准备完成后不涉及任何真实员工身份或材料。
  • 最终候选冻结、单独 push、upstream 祖先关系和严格 provenance 全部通过。
  • 用户在 provenance 通过后另行批准 B1 生产部署;pre/post migration status 均明确记录,唯一 migration 正确应用。
  • B1 schema、费用主数据、requires_invoice、assets 批准依据、2,000 元提示和 account code 保留检查通过;B2 DB 记录明确存在且为 force_off,effective off reason 不是 missing record。
  • /ready、12 位 bundle marker、runtime/external smoke、备份合同刷新和角色化公网检查通过。
  • owner 合成草稿维护正常;finance/admin 非参与人无维护入口;材料合并预览成功并只产生获批的设计内审计。
  • env hard gate 在 DB force_off 状态下单独启用并重启,确认 env on / effective off 后才进入隔离灰度。
  • B2 隔离灰度七项合同全部通过;失败时 DB 快照恢复,既有关联数据未回滚。
  • 用户单独批准后 B2 全局 effective on;合成数据已按合法流程清理,employee/manager/finance smoke 常驻隔离部门且无真人混入。
  • 真实财务按明确说明退回 0014/0016/0017/0018,重提后人工复核日期粒度;0008 与 0013 未被误改。
  • 任一失败按固定回滚/补偿边界处理,无 reset、无逆 migration、无批量伪造批准依据或票据关联。
  • 所有公网门禁完成前不通知普通用户测试、不归档父任务。

Out of Scope

  • 修改 B1/B2 业务代码、把日期粒度新增为服务端阻断、补数量/单价模型或提前实施 B3 一页式。
  • 反向修改已付款 0008、替真人财务审批、保存真实凭据、复制真实票据作为测试材料。
  • 在本轮编造会计科目编码、重置数据库、回退 migration、批量写入事前批准依据或清空审计记录。
  • 把合成灰度批准推定为真实单据退回批准,或把部门灰度 PASS 推定为全局启用批准。

Test Plan

  • 来源:干净工作区、候选 SHA、独立 push、upstream 祖先关系、严格 provenance。
  • 生产 preflight:existing 部署合同、身份、数据挂载、唯一 pending migration、受影响主数据快照、B2 env/DB 初始状态。
  • B1:init 前后 migration status、schema/seed/account code 断言、build marker、runtime/external smoke、备份合同和角色化页面。
  • B2:env on + DB off、隔离部门 allowlist、七项合成业务合同、flag 快照恢复、关联数据保留、全局启用后复核。
  • 真实业务:四张单据退回说明逐单核对,重提后财务人工核验日期粒度;0008/0013 零改动。
  • 治理:task validate、Trellis governance、process contract、docs registry audit、敏感信息扫描和 git diff --check。