归档日常发布与首次seed初始化分离任务 [09-09-existing-deploy-seed-separation]

全部 AC 完成:部署脚本与 Compose init 消除无条件 seed 旁路,existing 对
已修改配置不重写、重复执行稳定,缺项检查不误判、切应用前停止不自动
seed;数据库生命周期隔离回归不退步。2026-09-09 已完成 b87cc55 真实发布
(本轮回填补齐了完整执行记录:845 镜像清理、备份验真、发布后复核)。

Co-Authored-By: Claude Sonnet 5 <[email protected]>
这个提交包含在:
2026-09-25 20:52:12 +08:00
共同撰写人 Claude Sonnet 5
父节点 89fad4857f
当前提交 b9415330e5
共修改 6 个文件,包含 3 行新增和 3 行删除
@@ -0,0 +1,67 @@
# 已批准实施方案:初始化与日常升级分离
## 已核事实
- 当前本地基线 b918530。deploy/trialrun/deploy.sh 的 run-init 无条件执行配置预检、migrate deploy、seed:config、seed:trialrun。
- deploy/trialrun/docker-compose.yml 的 init 默认命令也执行 migrate 与两类 seed;只改 deploy.sh 不能覆盖默认 init 使用路径。
- seed-config 包括工作流取消激活后 upsert、假期条目删除重建等写入;seed-trialrun 对既有开关强制更新。不能用“本次值与默认相同”替代发布模式隔离。
- 原生命周期隔离 task 已约定 existing 不操作 db 生命周期、init 和应用采用依赖隔离;本轮保留这些合同。
## 拟采用路径
使用既有 existing/initialize 模式,不新增第三个部署模式。拟在 server/src/scripts 增加一个小型 init 命令,由 server/package.json 暴露统一入口,供 deploy.sh 和 Compose init 默认 command 共用;不复制两套分支。当前 Dockerfile 已 COPY server,无需为入口修改构建拓扑。模式必须显式传入,空值或非法值在迁移/seed 前失败;不默认 initialize。deploy.sh 用容器环境参数显式传递已验证的模式,Compose 直接调用则要求调用者提供模式。保持 --no-deps;受支持的直接调用文档同样明确依赖隔离,不改变 Compose 服务拓扑。
顺序:配置预检 → migrate deploy → initialize 才执行 seed:config 和 seed:trialrun → 两种模式均运行只读基础配置检查 → 成功才进入应用构建/切换。迁移和 seed 子命令非零即停,不重试、不自动改模式。未明确模式的旧直接 init 调用将失败,这是刻意的安全兼容变化,需在交审及操作手册写明。
只读配置检查是本项目报销主链路的最低配置门禁,不声称这些都是服务启动必需项。使用短只读事务和错误即停,只输出检查代码/数量及维护入口,不输出记录内容。拟阻断条件限定为以下三类完全缺失:至少一个 enabled expenseType、至少一个启用的 approvalRule、至少一个 entity_type=reimbursement 且 is_active 的 workflowDefinition(与当前提交入口选择条件一致)。不要求固定名称、固定 v1、固定角色、固定金额阈值或内容等于 seed 默认值。
这只是“非空”最低门禁,不证明金额区间无空洞、审批人都可解析或全部单据可提交。空库冒充 existing 因缺三类基础配置失败,不自动初始化。管理员故意关闭全部报销基础配置的环境也会停止发布,而不是被强制恢复;这是本方案新增的明确兼容约束,须用户批准。
确需新增配置的数据升级另设可审查的版本化变更;本轮不借默认 seed 自动补齐。发现缺项则报告维护路径,由负责人另批初始化或数据修复,不能在 existing 中静默写入。
## 风险及回滚
停止 seed 不等于停止 migrate,也不保证旧镜像能撤销数据变化。应用回滚、迁移兼容、数据库恢复分开说明。初始化 seed 仍有写入风险,非法模式和 init 失败必须停止切换。最终实施须在独立工作树进行,避免与尚未提交的 Trellis 修复混合。
## 已核源码与配置裁决
| 对象 | 证据 | 方案裁决 |
| --- | --- | --- |
| 部署入口 | deploy-existing.sh 显式设置 existing;deploy.sh 本地及远端都校验模式,run-init 覆盖 Compose 默认 command | 两条 init 路径统一入口,模式传入容器;保留 V1/V2 Compose 支持 |
| 报销审批规则 | reimbursements.js 的 loadApprovalRuleForAmount 无匹配时 409 | 仅查启用规则非空;不宣称覆盖全部金额 |
| 报销工作流 | reimbursements.js 提交读取 active reimbursement definition;缺失时报业务错误 | 查 active 定义存在,不改审批链,不强制 v1 |
| 费用类型 | reimbursements.js 的 resolveEnabledExpenseTypesByIds 只接收 enabled 类型 | 查至少一个 enabled,不重建默认类型,不验证每种差旅费用 |
| 工作区 | workspaceSettings.js 的 mapWorkspaceSetting(null) 返回 buildDefaultWorkspaceSetting | 非发布硬阻断,不创建记录 |
| 编号 | documentSequence.js 的 resolveDocumentSequenceConfig 及 issueDocumentNumber 有解析兜底和业务时 upsert | 本检查不调用发号函数,不要求初始化序列存在,不更改业务发号写入 |
| 开关 | featureFlags/index.js 结合环境门禁与 DB 记录评估 | 不强制 force_on 或默认 rollout,不把管理员关闭视为缺配置 |
| 日历、预算、部门、用户/主体/银行卡及其他模块 | 属于各自业务就绪与配置范围 | 不扩为全站发布门禁,不读账号/银行资料,不在发布时自动维护 |
维护提示沿用费用类型、审批规则、流程定义的既有管理入口;若初始化数据根本未建,由运维另审 initialize 或版本化数据修复,不建议在 existing 重跑整套 seed。
## 实施文件与验证
预计涉及 deploy/trialrun/deploy.sh、docker-compose.yml、server/package.json、新的小型 init/只读检查脚本及定向测试,原 deployment spec/手册和本 task。复用 server/tests/unit/scripts/deployment-db-lifecycle.test.js、deployment-safety-contract.test.js;新测试覆盖真实统一入口执行顺序,不只匹配脚本文本。
矩阵:existing/initialize × 成功/非法或缺模式/配置预检失败/迁移失败/seed失败/只读缺项或查询失败。existing 中 seed 调用次数恒为零且成功路径必须真正到达应用步骤;initialize 的两个 seed 必须按序执行,失败不得到达应用步骤。直接 Compose 默认 command 使用同一入口,已有依赖隔离的变异测试继续生效。
合成库测试与生产完全隔离:验证 initialize 空库建立配置;人为修改已有开关、工作流、日历及其他 seed 管理表后运行 existing 两次,比较配置字段、版本/时间戳不变,迁移记录单列。只用测试身份/临时数据库,不能以无 Docker 或初始化失败冒充通过。环境前置不足先报告,不借用 ARM 正式归档或生产库。
## 审批边界
### 新候选发布准备草案(提交后,未授权部署)
- 候选固定为 b87cc55aca0003130cf8a5bbe38d0dd15528389c,已推远端。执行来源为干净的 seed-preflight-integration 副本,不使用仍在旧基线且有其他治理差异的主工作树。此前候选和例外不自动转移。
- 本地已有证据:已审两包、顺序补丁集成与 110/110 定向回归。尚无完整镜像构建或完整候选门禁通过证据;不将定向回归冒充普通发布资格,不自动启动暂停的 R1、阶段 B 或修复 58 条红项。
- 下一步申请本候选的一次生产只读预检:运行版本/镜像身份、候选迁移集合与数据库迁移状态、三类 enabled/active 最低配置计数、磁盘余量、最近备份时效及已有回滚制品元数据。只输出脱敏摘要;不读取个人访问历史、单据明细或银行信息,不执行 migrate/seed,不写配置,不切容器。现有审批记录不替代本轮明确边界。
- 预检必须执行已修的状态解析逻辑;如远端仍为旧脚本,应先设计获准的调用方式,不以“只读”名义自行上传或安装文件。读取受限则标未验证,不绕权限。
- 回滚目标以本轮确认的生产运行版本为准,不默认仍是历史 f223bfb。旧镜像必须保持独立可用;备份完整性与恢复能力分别记录。已知 DB 最小恢复成功不代表附件或应用恢复成功,也不证明新备份已恢复演练;不沿用历史“从未成功”的过期结论。
- 若最低配置缺失,报告管理员,不自动 seed/启用配置。existing 仍会执行迁移,配置检查失败可能发生在迁移落库之后,镜像回滚不能撤销数据变化;任何数据恢复还涉及恢复点之后业务数据的处置,另行审批。
- 汇总本轮证据后再提交发布决定:普通路径需要完整候选验收;若仍未具备,明确列出缺口与回滚验收,用户对本 SHA 单独批准例外和部署。未获批准不执行真实发布,不拿旧例外填空。
- 部署获批后按既有 existing 路径执行,检查日志无 seed 阶段、版本/健康一致;AP 验收覆盖仅 ceo/manager 拒绝、finance/finance_mgr 可读、兼财务多角色可读、审批/复核入口保留。真人制单/付款流程由授权人员验收,下载不等于付款;不代操作真实银行业务。
- 失败停止并报告,不自动扩大写入或恢复范围;成功也不删除独立回滚制品。磁盘治理、恢复附件/应用验证、统一待办和错误豁免期限沿原任务处理,不混进本次发布写入。
用户已同意本 task 规划方向;以上统一入口、直接调用显式模式和三类非空硬门禁为本轮收敛的待审实施方案。获批前保持 planning;不把此前“好的”解释成批准尚未呈现的兼容变化。实施只在独立工作树,不叠加未提交的治理修复。提交、推送、生产部署另批。
后续授权与实施:用户对收敛方案明确回复“是”,已进入独立工作树实施。共享入口为 server/src/scripts/deploy-init.js。只读检查通过 Prisma repeatable-read 事务显式 SET TRANSACTION READ ONLY;费用类型没有专用管理页面,改为提示管理员核对既有 /api/expense-types,缺失数据另批修复,不新增 UI。
环境契约同步限定 init 可注入且必须声明 DEPLOY_DATA_MODE,并要求 Compose 缺省展开为空,拒绝默认 initialize;server/worker 仍不允许该 override。此为已批准的模式传递配套,不扩大业务环境变量或放宽校验器。
@@ -0,0 +1,160 @@
# 规划与执行清单
- [x] 由用户采纳的队列进入本 task,官方 create 激活 planning;未运行 start。
- [x] 确认两个无条件 seed 入口,建立需求/草案;关联既有数据库生命周期隔离合同。
- [x] 核对仓库支持的部署/init 调用路径、最低配置与现有测试,形成待审方案和最小写入文件清单;不声称核对了仓库外手工命令。
- [x] 用户明确批准方案;独立执行工作树基于 b918530,官方 start 并回读当前任务。
- [x] 实施、定向测试及主会话只读差异复核;独立 reviewer 受 inline 限制未执行,保留缺口。
- [x] 更新既有部署规约和手册;提交、推送、部署未授权、未执行。
## 当前状态
本地实现和验证完成,未发布。执行工作树为 artifacts/isolated-worktrees/20260909-seed-separation,detached b918530;主工作树仅保留本任务合同与状态,不集成产品差异,既有 Trellis 治理差异不混入本轮实施。错误豁免原任务未延期、未在本轮迁移原因码。不需要用户再操作 ARM。
## 本阶段检查
`task.py current --source`、`get_context.py --mode phase --step 1.1 --platform codex`、本 task 的 `task.py validate`、Trellis governance 和 `git diff --check`。这些只验证规划结构,不代表发布或数据语义验证。
## 实施与检查证据(2026-09-09)
- 新增 deploy-init 共享入口、模式分支与三类只读配置检查;两个实际部署入口共用。迁移先于配置检查,所以检查失败不能声称零数据库写入,只能保证不自动 seed、不进入后续应用切换。
- 生命周期 fake 保留外部命令 fail-closed,更新为精确核对共享入口和显式模式,不因删除旧 seed 字符串跳过 init。既有 db/依赖隔离变异仍必须被发现。
- 定向命令:server 下运行 Vitest 的 deploy-init.test.js、deployment-db-lifecycle.test.js、deployment-safety-contract.test.js。初轮 90/90 通过;后补 init 环境契约四场景,deploy-init 单独 21/21 通过,完整最新矩阵另记录最终输出。
- 合成数据库使用本机 Unix Docker socket、既存 PG16 alpine 镜像、本轮随机名和回环临时端口;不读生产 env 或正式备份,不用 ARM。测试环境仅显式构造的合成配置。容器与其卷逐轮精确清理,回执均为 removed=true。
- 集成测试 deploy-init.integration.test.js:空数据库真正执行迁移和 initialize;修改开关为 force_off/department_allowlist、版本 77,修改工作流版本 42 和假期名称;existing 连续两次后 17 类配置全字段(含版本、时间戳)的排序摘要相同。逐类禁用最低配置后 existing 非零且配置摘要不变,迁移名称集合单列核对相同。最终集成运行通过,不将可选测试的默认 skip 写作通过。
- 首轮集成在初始化成功后因测试快照假设员工角色表有单列 id 而失败;改用全行序列化排序摘要,复验通过。未为迎合测试修改 schema 或业务数据模型。
- 部署环境校验最初拒绝 init 新 override;按方案只给 init 增加允许/必需项及缺省空值约束。正例、缺模式、默认 initialize、注入错误服务四种契约测试通过;没有放宽其他服务。
- 已通过:node --check、Bash syntax、Prisma validate、validate-env-contract、task validate、Trellis governance、process contract、git diff --check。docs registry errors=0、warnings=1(22 个未注册文档),本轮未新建 docs 文件,不扩大范围治理该警告。
- 主会话只读复核覆盖模式先验证、共享入口、只读事务、阶段非零停止、原依赖隔离、维护提示无虚构页面及错误脱敏。inline 不允许派 implement/check 子代理,独立 reviewer 未运行,不宣称独立复核通过。
- 未执行完整候选发布门禁、镜像构建/生产部署或真人业务验收;集成使用本地 test 环境,不能代替生产配置检查。本轮不修改 seed 内容、不启用 R1/阶段 B、不触及六个 view_all。
| 代码 | 发布 | 验收 | 等谁/什么 |
| --- | --- | --- | --- |
| 独立工作树本地实现完成,未提交/未集成 | 未部署 | 新增及合成库验证通过,完整安全回归有一次待核失败 | 预检回归待核、独立复核/交审;提交推送及生产例外/部署另批 |
### 最终矩阵与保留 finding
- 最新完整三文件矩阵:94 项中 93 通过、1 失败。失败为既有 deployment-safety-contract 的 unknown migration 场景:模拟 Prisma status 输出未知待执行迁移,但预检错误返回 PASS。不能将初轮 90/90 或分组通过覆盖本次失败。
- 两个直接相关文件 production-preflight-remote.sh 与 deployment-safety-contract.test.js 经 cmp 与主工作树基线一致,本轮未改;原主工作树基线该用例连续三次定向复验均通过(每次 1 passed/23 skipped)。因此只能记为一次间歇性回归失败、原因未明,不能说已在基线重现或已修复。
- 新增 deploy-init 单测最终 21/21;合成 PG 集成 1/1;既有生命周期矩阵通过。集成最终再跑与当时 17 项单测共 18/18,后补四项环境契约测试另跑通过。
- 此 finding 不触发自动修改旧预检、删除断言或生产验证。保留为交审和发布前必须核清的项;本 task 不宣称完整回归全绿或具备部署资格。
### 继续核查:已复现管道误判,由既有门禁任务承接
用户“继续推进”后仅进行了本地合成核查,未改 production-preflight-remote.sh 或原安全测试,未访问生产。两种证据分开记录:
1. 直接执行原始短状态文本的 printf | grep -Eiq 管道,在 pipefail 下实际捕获三次 producer=141、consumer=0。这证明 grep 已匹配,但生产者 SIGPIPE 使整个条件返回失败;不是根据三次基线定向通过推断安全。
2. 从现有源码提取 status_pending_ids 解析和 unknown pending 拒绝分支,在内存构建合成执行片段。用只对相应 printf 返回 141 的函数注入上述已观测状态:原片段返回 ACCEPTED/exit=0;仅将该匹配改为 here-string 的候选片段返回 PROD_MIGRATION_STATUS_UNKNOWN_PENDING/exit=1。此为确定性分支复现,不冒充真实远端完整预检。
3. 候选 here-string 匹配对同一短文本连续 2000 次,falseNegatives=0。未修改产品文件,不能因此宣布修复已交付。
根因:grep -q 提前退出 + 上游 printf + pipefail,使“有待执行迁移”被误判为不存在,跳过 unknown pending 拒绝。此类组合在同一状态解析区共有四处。它是现有发布预检可靠性缺陷,不是 seed 分支或数据库 schema 问题;此前“原因未明”已由上述证据更新,不删除历史失败记录。
归属更正(用户已同意):撤回“必须独立新建 task”的建议。预检修复由既有 09-06-release-gate-integrity 的“迁移状态误判修复”阶段承接,具体修复范围与回归清单只在该处维护。本 task 保留上述复现证据和发布前置依赖,不混入预检产品差异;任务复用不等于混合工作树或提交,旧冻结门禁包不变。当前仅更新归属,预检尚未修复。
当前结论:seed 本地实现与新增测试成果保留,发布前置缺陷已查明但未修复。整体不得宣布全绿或可部署。
### 门禁关联更新:本地预检修复验证完成
09-06-release-gate-integrity 已在独立 preflight-fix 副本完成四处状态谓词修复,主会话复核与确定性回归通过。以 b918530 为共同基线临时组合本 seed 代码和预检修复,109/109 回归通过、临时验证副本已清理;输入指纹与具体证据只在门禁 task 同日结果维护。
此前 93/94 异常的根因与本地修复已闭合,不再把它记为原因未明;未覆盖历史失败。seed 运行时代码没有追加更改,原真实合成 PG 验证沿用。两个产品副本未合并到主工作树、未提交/推送/部署;独立复核及完整候选发布门禁仍未完成,生产状态未重新查询。下一步是按现有 task 分别交审改动,不扩展新功能。
### 交审前入口补验(2026-09-09)
交审前发现 Compose init 原 folded scalar 配合 entrypoint ["sh", "-c"] 会拆分 shell 参数。Docker Compose v2.24.7 本地无网络合成容器实际复现:原格式未到达标记且退出 2;改为单元素 command 数组后到达标记并原样传播退出 17。config 渲染及 YAML 字符串断言不足以证明命令执行。
本次仅修 init command 数组并补 shell 执行回归,仍归本 task,不新建任务。单测 22/22;更新后的 seed + 预检临时组合三文件 110/110 通过,11 个输入文件摘要 a3f91e3d1fcdbc522347725659e3b8b5a0438df62a7eea52b03f05fb3ba6d70a。旧 109/109 结果只对应旧输入,不代替本次证据。临时组合 worktree 和合成容器已清理;共享 JS 数据库逻辑未改,原 PG 集成证据沿用,不声称本次重跑数据库集成。
交审包:artifacts/qa-flow/20260909-seed-preflight-review-v1/seed/。包含基线差异、新文件、文件摘要与验证边界;不含依赖、运行时会话及其他 task。与 preflight 包都修改部署 spec,未来集成需保留两个增量,禁止整文件相互覆盖。
当前:本地实现及主会话检查完成;独立 reviewer 待复核,未提交、未推送、未部署,完整候选门禁未运行。此段为最新状态,前述失败和阶段记录保留用于追溯。
## 2026-09-09 独立复核接受与集成准备
用户回传独立审查:seed 通过(3 条非阻断备注),preflight 通过(无 finding)。审查者核验冻结 diff 并阅读合同与代码,另做本地 bash SIGPIPE 合成复现;未重跑项目测试、未审完整历史叙述、未构建镜像或访问生产/ARM,未验证后续集成。不得把该结论扩大为独立执行了 110 项测试或批准部署。
集成副本:artifacts/isolated-worktrees/20260909-seed-preflight-integration,基线 b918530,归当前门禁 task 协调整合;两个原 task 归属不变。按共同祖先合并部署 spec,零冲突,不用整文件覆盖。原两份冻结包和执行副本保留未改,主工作树不混入产品实现。
备注处理:
- F1:集成副本的既有运维手册明确补充,有意停用全部费用类型、审批规则或报销流程也会挡住下次发布;不得自动补齐或改用 initialize 绕过。
- F2:保留并强化“迁移先执行,配置检查失败时迁移可能已经落库”,不简化成零写入。
- F3:deploy.sh 的 sh -lc 与 Compose 的 sh -c 环境加载不同,记录为待环境验收关注项;本轮不改 shell 行为。
- Compose folded scalar 是基线已有问题,本 task 因承诺两入口真实到达而一并修复,不是本次引入。
- 预检缺陷定性是“该拦时可能拦不住”,未证明生产曾误放行。独立审查在其环境较大输出触发 141,但约 64KB 不是通用安全阈值;此前已有短输出捕获 producer=141/consumer=0 的记录,调度和管道实现都会影响触发,不采纳“日常绝不会触发”的推断。
两笔提交准备顺序:先 preflight(门禁 task 三文档、预检脚本、测试及 spec 对应增量),再 seed(原 17 文件范围,包含 F1/F2 手册补充和 seed task 最新状态;spec 只取 seed 增量)。共享 spec 第二笔以第一笔结果为基线。提交、推送、完整候选门禁与部署均未因此授权;当前只做隔离集成和定向验证。
## 本轮提交推送回执
用户明确授权按清单两笔提交并推送,不含部署。已在隔离集成副本完成:
- 81102c2:迁移预检修复,6 文件,归 09-06-release-gate-integrity。
- b87cc55aca0003130cf8a5bbe38d0dd15528389c:日常发布与首次初始化分离,17 文件,归 09-09-existing-deploy-seed-separation。
逐笔暂存内容按已批准补丁、路径集合和文件摘要核验;没有纳入其他治理改动。推送后 fetch 回读:集成副本 HEAD = origin/main = b87cc55,副本工作树干净。原冻结包保留;未新建分支、未归档 task、未部署或查询生产。
主工作树仍在 b918530,保留原有未提交治理差异及任务状态补记,没有强行快进覆盖它们。后续候选检查必须在隔离集成副本或新建干净候选副本执行,不能用主工作树 HEAD 冒充新候选。
验证沿用同字节输入的集成 110/110;源码包独立复核通过,集成由主会话验证,完整候选门禁尚未执行。当前段落为提交后的本地回执,不倒改提交前冻结材料;不为状态补记另造第三笔提交。下一步按 seed design 的新候选发布准备草案核对前置,生产只读预检与部署不在本次 Git 授权内。
## 新候选生产只读预检回执(2026-09-09)
用户在提交推送后批准本候选只读预检。本次在内存经 SSH stdin 执行已提交的新预检脚本,没有上传安装、修改远端文件、执行 migrate/seed、切换容器或业务写入。执行脚本摘要 2386ac545d8d12595c61fbcdb5e807746965a36ce539170cc6118f3eb6f15d2e,与 b87cc55 候选一致;原始身份字段只在内存传递,输出为既有脱敏摘要。
观测时间 2026-09-09 12:54 CST:
- 候选 b87cc55;生产运行 build f223bfb72fb2baef51199702c2def84c41af7aef,容器运行中且检查前后身份/启动时间不变。Docker 未提供 Health 状态,未据此声明公网健康通过。
- 目标/数据库身份及迁移对账 PASS,无待执行迁移。脚本 rollbackWindow=OPEN 仅是既有 terminated 状态计数检查,不是备份可恢复或完整回滚演练结论。
- 三类最低配置在明确 READ ONLY 事务内读取:启用费用类型 39、启用审批规则 3、活动报销流程 1,全部满足本候选最低非空门禁;没有读取员工或业务明细。
- 数据/附件/回滚制品所在文件系统可用 8.32 GiB、使用率 85.9%。不能据此单独保证镜像构建和备份峰值空间够用,未清理任何文件。
- 最新备份状态文件 ACCESS_DENIED,不能确认其完成时间、制品身份或摘要。备份 service 最后退出时间为本日 02:30:53 CST、Result=success/exit=0;这只是服务元数据,不能替代备份验真,不绕权限升级读取。
- 独立旧镜像文件仍存在,匹配当前生产 build/image,大小 1117595648 字节与旧 receipt 一致;只核元数据,未重算 archive SHA-256、未 docker load 或恢复测试,不把旧 receipt 中的摘要冒充本轮实测。
证据入口:artifacts/qa-flow/20260909-b87cc55-readonly/,含只读脚本、preflight-result.json、metadata-result.json 与脱敏 migration 输出。候选副本仍干净,未提交额外代码、未推送、未部署。当前仅完成只读预检,不产生发布资格。
下一步:先通过获准管理员读取路径补最新备份的时效/manifest/数据库包和附件包完整性验证;在发布前另外核算构建峰值空间、确认回滚制品验收范围。读权限缺口不以自动 sudo、切 root 或宣称服务成功代替。材料齐后对本 SHA 明列完整候选门禁未通过等缺口,再请用户决定例外及部署;当前不建议直接发布。不重复要求已完成的数据库最小恢复演练,也不把它扩为附件/应用恢复成功。
## 获准 root 通道备份验真与空间盘点(2026-09-09)
用户明确允许 hyinda-root 执行只读备份验真,并询问发布空间及旧部署内容清理。本轮经该既有 SSH 身份内存执行检查;未自动 sudo、上传安装、恢复数据、删除文件或部署。已有管理员备份校验脚本沿用严格的 f223bfb/build/image 约束,未放宽失败条件。
最新备份 PASS_INTEGRITY_ONLY:完成于 2026-09-09 02:30:53 CST,检查时约 10.48 小时;manifest 摘要、数据库 dump 和附件归档大小/摘要全部一致。验证的是当前备份完整性,本轮未恢复;此前 DB 最小恢复成功不因此扩为附件或应用恢复成功。
磁盘约 58.94 GiB,可用 8.32 GiB,使用率 85.9%,可用 inode 1400857。Docker system df:Images 39.54GB、标称可回收 39.24GB;停止容器标称约 190MB;构建缓存约 268MB。目录 allocated 统计:日志约 1.019 GiB、独立回滚制品约 1.041 GiB、自动备份 0.132 GiB、数据库 0.092 GiB、附件 0.018 GiB、部署仓库 0.028 GiB;目录可能重叠,不相加。Docker store 的 du 超时记 NOT_MEASURED,不当作零占用。image ls -a 含中间镜像,与 system df 汇总计数口径不同,不相加或冒充同一分母。
已生成只读清理候选清单:845 个创建至少 7 天、无 tag/digest、未被任何运行或停止容器引用且排除已固定生产回滚镜像的镜像。清单 SHA256=30d78bac23658f70453e5cc8a6d2e9c943c32c0ab48227e605efa3abb8e6438e。共享层与中间镜像依赖使实际可释放字节未知,不能承诺释放 Docker 标称全部 39.24GB。这是候选清单,不是用户已授权删除。
建议先做这批精确清单的受控清理,不直接部署:8.32 GiB 不能证明构建峰值够用,候选 server/package.json 变化会失效 server_deps 的依赖安装缓存。若用户另批清理,每项删除前必须重新核对年龄/标签/digest/容器引用/受保护镜像;引用变化、存在后继依赖或条件不符则跳过报告,不强制删除。不执行 system prune -a/--volumes,不删除任何容器、卷、数据库、附件、备份、独立回滚制品或带标签镜像。清理前后核对运行镜像/启动时间及磁盘,剩余空间再用于发布预算判断;未达余量不扩大清理范围。
证据位于 artifacts/qa-flow/20260909-b87cc55-readonly/:backup-integrity-result.json、disk-inventory-result.json、image-cleanup-candidates-result.json 及本轮只读脚本。下一步请用户批准上述精确范围的删除;本轮只读授权不包含清理或部署。原备份 ACCESS_DENIED 已由本次获准 root 完整性检查补齐,不能继续写作验不了。
## 已授权镜像清理结果(2026-09-09)
用户对“仅清理已列明 845 项、逐项复核、不强删、不动容器/卷/业务数据/备份/回滚制品”回复“是”,本轮据此执行。没有将权限扩大为全局 prune、删除带标签镜像或部署。
通过获准 hyinda-root 的 SSH stdin 执行本地检查脚本,未远端安装脚本。每项操作前复核全部容器 ID/镜像/状态/启动时间不变,重新检查镜像无 tag/digest、年龄至少七天、与冻结创建时间一致,排除所有容器引用和已固定回滚镜像。唯一删除命令为 docker image rm --no-prune <完整批准ID>,不用 force;--no-prune 防止隐式继续删除清单外父镜像。Docker 拒绝的依赖项保留,不改依赖关系、不重新扩大范围。
2026-09-09 13:11 CST 结束:
- 845 个批准 ID 全部逐项处理,811 删除成功,34 SKIPPED_DOCKER_REFUSED;本地逐事件核对 ID 集合与批准清单完全相等,无重复或额外项。
- 可用空间 8.32 → 46.43 GiB,净增加 38.11 GiB。此为文件系统实测净变化,不是把镜像虚拟大小相加。空间薄余量问题本次已解除,不继续清理备份或回滚制品来凑空间;具体构建峰值仍未实测,不承诺任意后续构建永不耗尽。
- 全部运行/停止容器的 ID、镜像、状态、启动时间在清理前后保持一致;Docker volume 名单不变,没有执行容器/卷删除或业务数据写入。
- 独立回滚归档在清理前后完整 SHA256 都与原保存回执匹配,本轮已重新验摘要;没有 docker load/恢复演练,不能把此写成恢复成功。
- 真实生产版本未切换,未部署、未提交额外代码、未推送,未删除任何备份或审计日志。
证据:artifacts/qa-flow/20260909-b87cc55-readonly/cleanup-result.json、cleanup-execution-events.jsonl、cleanup-approved-images.py。脚本 SHA256=748c2d11e92ed5373969418410bfd83a5cc86fe16f3099e683879b75311a6895;原批准清单 SHA256=30d78bac23658f70453e5cc8a6d2e9c943c32c0ab48227e605efa3abb8e6438e。34 项保留不是重新索要强删授权的理由。
### 下一步:新候选发布审批,不再追加空间清理
候选仍是 b87cc55。已有:独立源码复核、集成 110/110、本候选生产身份/迁移只读预检(无 pending)、39/3/1 最低配置、最新备份 manifest/数据库/附件完整性、独立旧镜像完整性及 46.43 GiB 余量。生产仍处于旧候选,Git 推送和清理授权均不是部署授权。
尚缺完整候选门禁通过;真实候选镜像尚未构建,附件/应用恢复未演练。原数据库最小恢复已成功,不能继续表述为“从未成功恢复”;镜像回滚不能撤销迁移等数据库写入,恢复备份会影响恢复点后的数据。应据这些明确缺口对本 SHA 提交发布例外及部署审批,获批后才执行 existing 发布与版本/健康/AP权限验收;不沿用旧 AP 候选的例外授权。直到该批准到来,保留原回滚制品、34 个被拒绝镜像及业务数据不动。
## 本候选例外与部署授权
用户对明确列出的 b87cc55 发布例外、existing 部署及必要的旧版本应用回滚回复“是”。接受完整候选验收未通过/真实镜像尚未构建/附件与应用恢复未演练;不授权自动数据库恢复或删除回滚制品。本次使用远端 main 的干净克隆作为发布源(原集成副本 detached 不满足 upstream 门禁,不设置未发布 override);固定 b87cc55,来源检查 published/remoteVerified=true。Trellis start 写入本克隆 task branch 元数据后,已只恢复该自动改动到候选原字节,保留会话指针;未改变候选代码或新建功能分支。
执行证据归 artifacts/qa-flow/20260909-release-b87cc55/。沿既有 deploy-existing.sh 调用链,固定 existing/SYNC_ENV=0,使用原受控 refresh 身份,不运行初始化 seed;任何失败停止,应用是否已切换以阶段与运行版本实测为准。准备阶段尚不代表部署成功。
## 已按本候选例外完成部署(2026-09-09)
用户明确批准 b87cc55 的例外、existing 发布及必要旧应用回滚,不授权自动数据库恢复。14:07 CST 既有 deploy-existing.sh 返回 0:build-init、run-init、build-server、build-switch-server 均 PASS;随后迁移对账、就绪/依赖/runtime smoke、公网构建标识及前端 bundle 检查和受控备份合同 refresh 完成。没有使用未发布 override,没有执行回滚。独立 clone main=origin/main=b87cc55 是本次发布来源,主工作树仍保留原未提交治理改动,不据主工作树旧 HEAD 判断生产版本。
部署后复核:
- 正式运行 build=b87cc55aca0003130cf8a5bbe38d0dd15528389c;14:08 CST 单独公网 /ready 与首页 HTTP 200,/ready buildSha=b87cc55aca00。
- init 日志的阶段精确为 validate_config → migrate → check_required_configuration,INIT_PASS/existing,无 seed 阶段;发布前后迁移对账无 pending。
- 运行容器内权限常量、AP 路由、workspaceSettings、auth 插件及 deploy-init 五文件摘要与候选完全一致。直接调用运行镜像中的权限解析器:manager/ceo 不获 finance.ledger.view,finance/finance_mgr 及兼财务角色可读,admin 通配保留;manager/ceo 的 expense.approve 和 payment.batch.review 保留。
- 上一项是部署制品字节核验和运行时代码策略执行,不是使用真人会话请求 AP 接口或实际点击菜单。本轮没有伪造真人 token、登录真实账户或读取台账明细;authenticated account route test / real-user page acceptance 均 NOT_RUN,不冒充四项真人验收全通过。
- 系统已发布且公网可测试,但经理/CEO 仍可经既有报销列表查看全部报销的开口保留,本轮仅收窄 AP,不宣称“经理只能看所有相关单据”整体实现。
- 发布后可用空间 45.50 GiB;旧备份、独立旧镜像制品及 34 个 Docker 拒绝删除项仍保留,未追加清理。backup unit BUILD_SHA 已刷新为新版本;最新已验备份仍是发布前 f223bfb,不把合同刷新写作新版本备份已产出。
- 例外缺口仍保留:完整候选验收没有通过证据;本次真实镜像构建现已通过,不能继续列为未构建;附件与应用恢复尚未演练,镜像回滚不撤销数据库写入。数据库最小恢复历史成功不等于任意当前备份均已演练恢复。
证据归 artifacts/qa-flow/20260909-release-b87cc55/:deployment-result.json、postcheck-result.json、public-check.json、脱敏迁移回执;完整发布输出为受控 0600 日志,不回传原始环境/业务内容。主会话执行发布及验证,不伪称独立 reviewer 验证部署集成。此次状态补记未另行提交或推送,不归档整个门禁 task,不启动 R1/阶段 B 或修 58 条。
下一步回到业务验收:majianyong 本人确认 AP 台账入口不可用、原审批/付款复核入口可用;财务本人确认 AP 可读和已复核批次可下载银行制单。仅让用户验证自己的授权界面,不代付、不代批、不创建额外测试付款。实际付款仍是复核 → 制单 → 网银付款 → 回单登记,下载成功不等于付款成功。取得真人交接证据后按已排队的统一待办任务收敛下一阶段方案,不让等待验收自动变成另起一笔未批准功能改动。
@@ -0,0 +1,39 @@
# 日常发布与首次 seed 初始化分离
## Goal
规划 existing 发布不自动执行 seed,保留 initialize 初始化能力、缺配置停止和数据库生命周期隔离
## Requirements
- 日常 existing 发布不再自动运行 seed:config / seed:trialrun,不重写管理员维护的开关、工作流、日历等配置。
- initialize 保留首次建库和初始化能力;非法模式失败关闭,不以补配置为由自动切入初始化模式。
- 保留既有迁移门禁、数据库生命周期隔离、健康等待、日志及应用切换门禁。
- 缺少必需配置时,在应用切换前停止并给出脱敏缺项类别与维护路径;不自动补齐生产数据。
- 未来必要的数据升级采用明确版本化步骤,不把 seed 当作通用升级器;本轮不搭建新的迁移平台。
## Scope
先核对 deploy.sh、Compose init 默认命令及受支持的调用入口,设计初始化/升级分流和最小只读配置前置检查;实施候选为部署入口、必要的配置检查脚本、定向回归、原部署规约与本 task。
## Out of Scope
用户已批准独立工作树实施与本地验证;不部署,不改业务权限、真实账号、银行卡、审批链,不回填生产数据,不运行生产 seed/migrate,不启用 R1/阶段 B,不重构全部 seed 函数或安装新运行器。真实 PostgreSQL 合成空库允许用于本地验证,不使用生产备份。
## Acceptance Criteria
- [x] 已批准的最终方案覆盖部署脚本与 Compose init 默认路径,不留无条件 seed 旁路。
- [x] initialize 在合成空库初始化成功;existing 对刻意修改过的关键配置不重写、不递增 seed 版本;重复执行稳定。
- [x] 只读缺项检查不把“与默认值不同”误判为缺配置;缺项/查询失败在切应用前停止,不自动 seed。
- [x] 原数据库生命周期隔离回归不退步,existing 不启动或重建数据库。
- [x] 测试覆盖非法模式、初始化失败、迁移失败、配置缺失及直接 init 入口;不能因提前退出得到零写入假通过。
- [x] 独立复核能力、剩余发布风险和生产授权分别标明;本地通过不表示公网已生效。
## Test Plan
先复用现有本地合成部署测试,截获外部命令;配置检查增加定向单测及隔离合成库测试。空库验证 initialize,预置自定义开关/工作流/日历的已有库验证 existing 保持原值和版本,migration 对比单列,不将“无 seed”说成“无数据库写入”。具体必需配置清单和测试入口在方案收敛前完成源码核实。
## Notes
- 用户在明确列明统一入口、显式模式及三类非空硬门禁后回复“是”,批准独立工作树实施和本地验证;已通过官方 start 激活。提交、推送和生产部署不在授权内。
- 关联 09-05-existing-deploy-db-lifecycle-isolation,其“不改 seed 内容和顺序”是旧任务范围约束,不在旧任务内偷偷扩大改动。本任务单独定义新的分流合同。
@@ -0,0 +1,26 @@
{
"id": "existing-deploy-seed-separation",
"name": "existing-deploy-seed-separation",
"title": "日常发布与首次 seed 初始化分离",
"description": "规划 existing 发布不自动执行 seed,保留 initialize 初始化能力、缺配置停止和数据库生命周期隔离",
"status": "completed",
"dev_type": null,
"scope": "deployment-seed",
"package": null,
"priority": "P2",
"creator": "shunli",
"assignee": "shunli",
"createdAt": "2026-09-09",
"completedAt": "2026-09-25",
"branch": null,
"base_branch": "main",
"worktree_path": null,
"commit": null,
"pr_url": null,
"subtasks": [],
"children": [],
"parent": null,
"relatedFiles": [],
"notes": "",
"meta": {}
}