# 生产部署验证与迁移门禁 ## Goal 在不写入真实业务数据的前提下,将包含合同 `terminated` 三列迁移的当前发布提交安全部署到预期生产环境,并用可追溯、脱敏的证据证明生产迁移状态、运行版本、只读权限边界和回滚窗口边界。 ## Background and Confirmed Facts - 当前既有生产发布入口是 `deploy/trialrun/deploy-existing.sh`;它会先调用 `check-existing-deployment.sh` 的 13 项聚合体检,再调用 `deploy.sh`,默认 `SYNC_ENV=0`。 - 当前 `deploy.sh` 在远端先执行 `prisma migrate deploy`(4.4),之后才执行 `prisma migrate status`(4.6)。这只能证明写入后的状态,不能作为本轮要求的部署前迁移门禁。 - 当前发布提交包含合同迁移 `20260807193000_contract_registry_termination_workspace`:三个可空列、`ON DELETE RESTRICT` 外键和索引;本轮不应执行任何真实 `terminated` 业务写入。 - 既有部署合同从受控 systemd unit 读取六个 allowlist 参数,13 项聚合体检覆盖部署身份、仓库、环境文件、数据目录、运行实例、挂载、build 和 refresh 身份;不得从 Compose 标签猜路径或选择 SSH 身份。 - 合同设计规定:生产出现第一条已提交 `terminated` 记录的瞬间关闭应用回滚窗口;此后不得回退到不识别该状态的旧应用,也不自动 drop 三列,只能 roll-forward。 - 本任务当前尚未执行生产部署、生产 migration、真实合同写入、企业微信写入或付款/审批操作。 ## Scope - 为既有生产部署入口增加/启用生产目标识别与迁移 preflight,使 `migrate status` 成为任何 schema 写入前的硬门禁。 - 在获批窗口内部署包含合同 `terminated` 三列迁移的明确候选 commit,并完成迁移、运行版本、只读权限和公网 readiness 验证。 - 记录首条 `terminated` 记录前后的回滚窗口判据、脱敏证据和失败分流;不主动制造任何真实终止记录。 ## Requirements ### R1 任务来源与部署身份 - 发布候选必须是干净、已提交且已进入上游的明确 commit;来源校验和 13 项既有部署体检通过后才可继续。 - 本任务的 `deploy-existing.sh` 拒绝 `DEPLOY_ALLOW_UNPUBLISHED_COMMIT`/`DEPLOY_UNPUBLISHED_REASON` override;离线灾备或远端故障的未发布提交必须走独立、另行批准的恢复流程。 - 生产目标必须通过独立 target gate:执行时显式提供 `EXPECTED_PRODUCTION_ORIGIN` 与 `EXPECTED_PRODUCTION_DB_NAME`(仅进程环境,不写入仓库、日志或报告),与远端 `.env` 的 `APP_ORIGIN`、数据库身份逐项比对。 - target gate 必须确认 `NODE_ENV=production`、`COOKIE_SECURE=true`、公网 origin 为 HTTPS 且与预期值一致;拒绝回环地址、本地数据库(含 `hyb_oa_regression`)、占位符或 staging/trialrun 身份标识。不要把本地任务中“必须是回环地址”的 R2 复用于生产。 - 目标、账号、路径、数据库名和环境值不得回显;只记录 PASS/FAIL 及脱敏原因。 ### R2 部署前迁移只读门禁 - 在任何 `migrate deploy`、seed、应用切换或真实业务写入之前,针对已确认身份的预期生产数据库执行只读 migration status 检查;原始 stdout/stderr 只留在受控执行现场,任务 artifacts 仅保存脱敏副本供人工核对。 - 预检必须明确区分 `applied`、pending、failed、rolled back、diverged;失败/回滚/分叉或无法人工解释的输出立即 `PAUSED`。 - 候选迁移目录与生产 `_prisma_migrations` 的已应用集合必须可对账;pending 集合只能是执行时显式批准的 allowlist(本轮预期最多为合同迁移),出现未知 pending 必须停止。 - 禁止 `prisma migrate dev`、`migrate reset`、`db push`、`migrate resolve`、手工 DDL、修改 `_prisma_migrations` 或以退出码代替原始输出核对。 ### R3 受控发布 - 仅使用既有 `deploy-existing.sh`,保持 `SYNC_ENV=0`、`DEPLOY_DATA_MODE=existing` 和既有 PGDATA/附件目录;不得覆盖生产 `.env`。 - 预检通过后才允许部署脚本执行已批准的 `migrate deploy`、构建、服务切换、运行时 smoke 和备份合同 refresh。 - 本任务不创建、编辑、终止、归档或恢复真实合同,不写报销、付款、审批、银行、ERP 或企业微信数据。 ### R4 发布后迁移与运行验证 - 部署后再次保存并人工核对 `migrate status` 的脱敏证据副本,必须显示所有候选迁移已 `applied`/数据库 schema up to date;原始 stdout/stderr 仍只留在受控执行现场,不能只看命令退出码。 - `/ready`、运行时完整 build SHA、公开短 SHA、基础 smoke、external deps、runtime smoke 和公开 bundle 扫描必须与发布 commit 一致。 - 所有权限负向路径、详情只读检查和迁移状态复核必须排在任何真实 `terminated` 写入之前;本任务不执行真实终止写入。缺少专用凭据时记录 `PAUSED/SKIP`,不伪装成通过。 ### R5 回滚窗口与结果证据 - 部署前只读查询 `terminated` 行数量并记录窗口状态:数量为 0 记为 `OPEN`,大于 0 记为 `CLOSED`;窗口按首条已提交记录的产生时刻关闭,不按时间计算。 - 在窗口 `OPEN` 时,应用健康失败可按既有发布合同回退到兼容版本,但不 drop 新列;窗口一旦 `CLOSED`,任何失败只允许 roll-forward 或兼容 hotfix,不回退到旧状态机。 - 结果记录发布 commit、target gate、pre/post migration 原始证据引用、`/ready`/smoke、权限负向路径、窗口状态、可解释 SKIP/PAUSED 和是否发生真实写入;不记录凭据、原始环境或业务正文。 ## Acceptance Criteria - [ ] AC1:候选 commit 干净、已进入上游,来源校验与 13 项部署体检通过。 - [ ] AC2:生产 target gate 证明目标是预期生产 origin/数据库,明确排除本地、回环和 staging/trialrun 身份,且没有泄露环境值。 - [ ] AC3:任何 schema 写入前完成 pre-migration status;受控现场原始输出与 artifacts 脱敏副本均可核对,failed/rolled-back/diverged 为 0,`_prisma_migrations` 的 pending 集合与批准 allowlist 完全一致,status 文案中出现的 pending ID 不得超出候选 manifest。 - [ ] AC4:`deploy-existing.sh` 仅在 AC1–AC3 通过后执行,保持 `SYNC_ENV=0`/existing 数据合同,未执行 reset、db push、resolve、手工 DDL 或真实业务写入。 - [ ] AC5:部署后 migration status 脱敏证据副本显示全部候选迁移 applied/schema up to date;`/ready`、build SHA、运行时 smoke、external deps 和公开 bundle 与候选 commit 一致。 - [ ] AC6:权限负向路径和只读合同详情检查在任何真实终止写入前完成;本任务无真实 `terminated` 写入,缺凭据项明确记录 PAUSED/SKIP。 - [ ] AC7:以首条已提交 `terminated` 记录定义回滚窗口 OPEN/CLOSED,并记录兼容回滚或 roll-forward 判据;不自动 drop 三列。 - [ ] AC8:任务证据、Trellis/process/docs/sensitive/diff 门禁通过;只有部署、公网和必要真机检查全部获批并通过后,才可告知用户可以测试。 ## Out of Scope - 真实 `terminated` 写入、合同业务编辑/归档/恢复、报销/付款/审批/银行/ERP/企业微信写入。 - 生产数据库 reset、db push、resolve、手工 DDL、删除或修改 `_prisma_migrations`。 - 修改合同业务代码、权限模型、状态机或生产环境变量;如部署 preflight 需要工具加固,只改部署/QA 脚本与其测试。 - 以本地或试运行数据库代替预期生产目标;以临时 runner、删除断言或未授权凭据冒充公网通过。 ## Accepted Risk and Deferred Items - 登录态 smoke、企业微信真机和真实 OCR 样本可能因专用凭据/样本缺失而 `PAUSED/SKIP`;这不等同于生产部署或业务功能通过。 - 首条 `terminated` 写入后的旧版本回退风险由产品与合同 task 已接受;本任务只验证窗口判据,不主动关闭窗口。 - preflight 到 `deploy.sh` 的受控窗口内,远端 `.env`、Compose project 和数据库连接目标必须保持不变;并发改动无法由 post-status 追溯性补救。若无法取得该变更冻结,任务应在写入前 `PAUSED`,不得把 post gate 当作事后纠错。 ## Operational Inputs (not committed) - `EXPECTED_PRODUCTION_ORIGIN` - `EXPECTED_PRODUCTION_DB_NAME` - `EXPECTED_PENDING_MIGRATIONS`(本轮候选迁移 allowlist) - 受控 `DEPLOY_SERVER`、`DEPLOY_REFRESH_SERVER` 与 live smoke 凭据(如获批提供) ## Open Questions - 无;目标识别、迁移门禁顺序、回滚窗口定义和真实写入边界均已确认。 ## Test Plan - 本地静态:`node deploy/trialrun/validate-env-contract.js`、shell syntax、相关部署安全测试、task validate、process contract、docs registry、Trellis governance、sensitive scan、`git diff --check`。 - 部署前:生产 target gate、`check-existing-deployment.sh` 13 项体检、candidate migration manifest 对账、pre-status 原始输出(仅受控现场)及其脱敏副本和 `terminated` 计数。 - 受控发布:`DEPLOY_SERVER=<...> DEPLOY_REFRESH_SERVER=<...> bash deploy/trialrun/deploy-existing.sh`;仅在前置门禁全 PASS 后执行。 - 部署后:post-status 原始输出(仅受控现场)及脱敏副本、`/ready`/build SHA、`scripts/qa/run-live-smoke-readiness.sh`、external/runtime smoke、公开 bundle 扫描和权限负向路径。 - 失败分流:target/migration/preflight 失败不触碰远端应用;应用发布后 refresh 失败不重复发布;首条 `terminated` 出现后只允许 roll-forward。 ## Evidence Notes - 规划阶段未执行生产 SSH、migration、部署、真实数据写入或 live smoke。