diff --git a/.trellis/spec/hyb-oa/deployment-trialrun.md b/.trellis/spec/hyb-oa/deployment-trialrun.md index 9272591a..88192f5b 100644 --- a/.trellis/spec/hyb-oa/deployment-trialrun.md +++ b/.trellis/spec/hyb-oa/deployment-trialrun.md @@ -1302,6 +1302,9 @@ bash scripts/qa/run-automatic-backup-contract.sh - Never derive `REMOTE_DIR` from Compose `com.docker.compose.project.working_dir`; it may name the Compose-file directory rather than the repository root. - The existing-deployment wrapper reads only `REMOTE_DIR`, `REMOTE_ENV_FILE`, `COMPOSE_FILE`, `COMPOSE_PROJECT_NAME`, `POSTGRES_DATA_DIR` and `STORAGE_HOST_DIR`; it does not echo their values. - Before `mkdir` or rsync, existing mode requires repository markers, `PG_VERSION`, storage directory and a regular non-symlink env file when `SYNC_ENV=0`. +- If the frozen unit points to a missing or non-repository root, recover the canonical source tree from an exact published Git commit into a new dedicated root. Do not choose among stale source directories by timestamp, and do not use `--delete` or carry an environment file into the new root during this bootstrap. +- When the frozen `REMOTE_ENV_FILE` is missing, locate candidates from the running Compose configuration and compare each candidate's service/database values with the running containers by per-key digest. Output only key names and PASS/FAIL; never print values or digests. A candidate must pass every container-observable key, including the field-encryption and database identity keys, before root copies it (not moves it) to the canonical root with deployment-user ownership and mode `0600`. If no candidate fully passes, stop and use the environment recovery classification; never use `SYNC_ENV=1` to guess. +- After a successful application switch, re-run the automatic-backup and WARCH3 installers with the newly active full `BUILD_SHA` and matching 12-character frontend prefix. Validate all managed service working directories/env paths/build identities and enabled timers, then run one complete backup plus the freshness watchdog; application `/ready` alone does not prove the backup contract followed the release. - `apk add` and both lockfile `npm ci` steps precede `BUILD_SHA` / `VITE_BUILD_SHA`. Build identity may invalidate application output, never stable dependency installation. - Seed and smoke output reports readiness, counts and booleans; it does not print full login identifiers, contact values, WeCom identifiers, redirect URI or generated credentials. @@ -1309,15 +1312,20 @@ bash scripts/qa/run-automatic-backup-contract.sh - Config directory passed as `REMOTE_DIR` -> preflight fails before rsync. - Remote env missing or symlinked with `SYNC_ENV=0` -> preflight fails before rsync. +- Multiple environment candidates or any per-key mismatch -> stop before copying, syncing or rebuilding containers; do not select the newest-looking file. +- Environment copy digest, owner or mode mismatch -> stop before formal deploy and keep the original candidate unchanged. - Existing PGDATA missing `PG_VERSION` or storage missing -> preflight fails before rsync. - Canonical systemd unit missing an allowlisted value -> wrapper fails without calling `deploy.sh`. +- Post-deploy installer still embeds the previous build SHA, any managed timer is inactive, or the real backup/watchdog fails runtime identity or off-host freshness -> deployment recovery remains incomplete even when `/ready` is green. - Lockfile unchanged but a new SHA reruns dependency installation -> Dockerfile contract test fails. ### 5. Good/Base/Bad Cases - Good: wrapper reads the frozen unit contract, preflight passes, Git archive syncs and unchanged dependency layers are cached. +- Good: a broken-root recovery creates a clean published-commit source tree, proves one environment candidate against running containers without exposing values, copies it with mode `0600`, deploys with `SYNC_ENV=0`, then updates the backup contracts and completes a real off-host backup. - Base: first initialization uses reviewed explicit parameters and `DEPLOY_DATA_MODE=initialize`; it does not pretend an existing unit is available. - Bad: inspect a running container's `working_dir`, append another `deploy/trialrun`, then discover the mistake only after rsync. +- Bad: choose one of several stale `.env` files by path or modification time, use `SYNC_ENV=1`, or stop after `/ready` while the backup unit still expects the previous build. ### 6. Tests Required @@ -1325,6 +1333,7 @@ bash scripts/qa/run-automatic-backup-contract.sh - `node deploy/trialrun/validate-env-contract.js`. - `cd server && npm test -- tests/unit/scripts/deployment-safety-contract.test.js`. - Synthetic fixtures must prove wrong root, missing/symlink env and invalid data fail; static ordering must prove preflight precedes rsync and dependency stages precede build args. +- A controlled recovery must additionally record: exact published source commit, per-key PASS set/date without values, byte-copy/owner/mode PASS, new-SHA service/timer contract PASS, a successful complete backup, and a successful off-host freshness check. Old candidates remain untouched until a separately approved cleanup. ### 7. Wrong vs Correct @@ -1341,6 +1350,11 @@ REMOTE_DIR="$(docker inspect --format '')" \ ```bash DEPLOY_SERVER= \ bash deploy/trialrun/deploy-existing.sh + +# If contract recovery was required, update the backup units with the active +# full SHA after the switch, then prove both creation and freshness. +systemctl start hyb-oa-automatic-backup-daily.service +systemctl start hyb-oa-automatic-backup-watchdog.service ``` ## Scenario: Environment Recovery Classification And Secret Custody diff --git a/.trellis/tasks/08-01-reimbursement-notification-progress-clarity/implement.md b/.trellis/tasks/08-01-reimbursement-notification-progress-clarity/implement.md index 848cea2c..a468d642 100644 --- a/.trellis/tasks/08-01-reimbursement-notification-progress-clarity/implement.md +++ b/.trellis/tasks/08-01-reimbursement-notification-progress-clarity/implement.md @@ -98,7 +98,7 @@ 生产发布(2026-08-02,脱敏):提交 `7c39f8b` 已推送并通过既有安装固化入口部署;同步前远端源码、普通环境文件、PGDATA 和附件目录合同通过,未同步或覆盖 `.env`。63 项 migration 为 up to date,init/server 依赖层命中缓存;`/ready` 构建短标识、external deps smoke、runtime smoke、付款能力 env/flag 常开、公网前端资源扫描和付款懒加载块四项关键文案均通过。当前 shell 未配置 live-smoke 专用凭据,因此没有宣称登录后业务链自动验收;部署未替任何人复核批次、上传回单或登记付款。 -## 9. 付款详情与批次短编号(已实施,未部署) +## 9. 付款详情与批次短编号(已实施并部署) - [x] 将付款详情重排为“批次概要 → 当前处理/下一步 → 报销明细 → 银行回单”,保留现有内联详情和顶部唯一主动作,不新增独立路由。 - [x] 付款页列表、移动卡片、详情总额、批次项、确认弹窗和高风险阈值全部改用共享 `formatMoney()`。 @@ -114,3 +114,12 @@ 实现中发现 Prisma 7.8 driver adapter 无法反序列化 `SELECT pg_advisory_xact_lock(...)` 的 PostgreSQL `void` 列并返回 `P2010`;锁调用改用 `$executeRaw` 后真实 PostgreSQL 并发门禁通过。该陷阱、双 int4 命名空间登记、固定锁顺序和禁止在已 aborted 事务内重试 `P2002` 已写入 backend spec。 只读审查(2026-08-02):未发现权限、maker-checker、付款状态机或历史编号兼容阻断项。新编号分配在既有报销锁定、状态/占用和复核人校验之后、所有批次/批次项/审计/通知写入之前;咨询锁、严格新格式查询和创建保持在同一交互事务。旧长编号不参与日序号,超过 999 时零业务写入;付款页未新增路由或可操作权限,回单缺失时顶部仍只有一个主动作。审查未修改生产数据,也未执行部署。 + +生产发布与部署合同恢复(2026-08-02,脱敏): + +- 提交 `5d274af` 已推送并通过 `deploy-existing.sh` 的既有合同入口部署;六键合同读取、环境合同、远端 preflight、63 项 migration、`/ready` 构建标识、external deps、runtime smoke、付款能力常开和公网资源扫描通过。当前 shell 未配置 live-smoke 专用凭据,因此没有宣称登录后业务链自动验收。 +- 部署前从运行中容器定位权威环境候选并逐键进行 SHA-256 比对:生产环境文件中的 163 个实际赋值键全部 PASS,0 个不可观测键。PASS 键名集合等于提交 `5d274af` 的 `deploy/trialrun/.env.example` 中全部非注释赋值键;唯一未进入生产集合的是注释状态的可选 `SHADOW_DATABASE_URL`。关键不可替代或身份相关键 `FIELD_ENCRYPTION_KEY`、`DATABASE_URL`、`POSTGRES_USER`、`POSTGRES_DB`、`POSTGRES_PASSWORD`、`JWT_SECRET` 均在 PASS 集合内。记录不含任何值、散列值、宿主路径或主机标识。 +- 权威环境文件仅复制到规范部署根,旧文件原地保留;新副本字节 SHA-256 一致、部署用户属主且权限为 `0600`。部署全程保持 `SYNC_ENV=0`,没有从本地同步或覆盖生产环境文件,也没有删除四个旧源码候选目录。 +- 部署后以新完整 SHA 重装 automatic-backup 与 WARCH3 合同;4 个 service 的工作目录、环境文件、构建 SHA 与规范根一致,4 个 timer 均为 enabled/active。 +- 已手工触发一次完整真实自动备份周期:源端成功状态新鲜且构建标识一致,`resolve_active_runtime` 的运行构建、附件挂载和 PGDATA 挂载三项断言通过;异机归档完成,随后 watchdog 证明异机恢复点新鲜。没有输出归档路径、文件名、远端标识或任何密钥。 +- 旧源码候选目录与旧环境文件继续保留,清理不属于本轮授权范围。任务仍保留运行观察项,不因本次部署归档。