修复自动备份运行身份漂移(07-28-automatic-backup-runtime-identity-failure)
这个提交包含在:
@@ -1005,6 +1005,13 @@ test "$(printf '%s\n' "$actual" | LC_ALL=C sort)" = \
|
|||||||
Compose-project-mismatched state. A fresh recovery point with a different
|
Compose-project-mismatched state. A fresh recovery point with a different
|
||||||
build SHA is `PASS_WITH_FINDINGS`, because deploy-time code identity and
|
build SHA is `PASS_WITH_FINDINGS`, because deploy-time code identity and
|
||||||
recoverable data freshness are separate facts.
|
recoverable data freshness are separate facts.
|
||||||
|
- Automatic-backup units freeze the active Compose project, build, PGDATA and
|
||||||
|
storage mount when installed. After any deployment, rollback or direct
|
||||||
|
container rebuild that changes the active container identity or host mounts,
|
||||||
|
compare all frozen values with the unique running server/database pair. If
|
||||||
|
the units are already installed and any value differs, rerun the existing
|
||||||
|
installer with the complete current contract and recheck all identities
|
||||||
|
before declaring the deployment complete; do not patch one unit in place.
|
||||||
- Installer `install` is disabled-by-default; `enable`, real transfer, real
|
- Installer `install` is disabled-by-default; `enable`, real transfer, real
|
||||||
WeCom alert and all server/account/key changes remain `EXTERNAL_GATED`.
|
WeCom alert and all server/account/key changes remain `EXTERNAL_GATED`.
|
||||||
|
|
||||||
@@ -1027,6 +1034,10 @@ test "$(printf '%s\n' "$actual" | LC_ALL=C sort)" = \
|
|||||||
authority -> preserve all objects and fail.
|
authority -> preserve all objects and fail.
|
||||||
- Missing, corrupt, stale, future or project-mismatched success record -> hard
|
- Missing, corrupt, stale, future or project-mismatched success record -> hard
|
||||||
watchdog failure. Fresh build mismatch -> one finding without a failing exit.
|
watchdog failure. Fresh build mismatch -> one finding without a failing exit.
|
||||||
|
- `phase_stale_check_failed` can also mean the watchdog failed its runtime
|
||||||
|
identity precheck before reading the success record. Diagnose the nested safe
|
||||||
|
category and active-unit identity first; do not infer that the last archive is
|
||||||
|
stale or corrupt from this outer phase alone.
|
||||||
- Missing/non-regular/symlink/non-`0600` Webhook file -> fail closed without
|
- Missing/non-regular/symlink/non-`0600` Webhook file -> fail closed without
|
||||||
printing the file value. Identical alert state -> update/evaluate without send.
|
printing the file value. Identical alert state -> update/evaluate without send.
|
||||||
- Ordinary shell, rsync sender/read/delete or archive overwrite -> receiver
|
- Ordinary shell, rsync sender/read/delete or archive overwrite -> receiver
|
||||||
@@ -1040,12 +1051,16 @@ test "$(printf '%s\n' "$actual" | LC_ALL=C sort)" = \
|
|||||||
resumes inside inbox, ARM finalize verifies and archives it, then production
|
resumes inside inbox, ARM finalize verifies and archives it, then production
|
||||||
writes success and prunes old automatic source points. If production later
|
writes success and prunes old automatic source points. If production later
|
||||||
stops, ARM independently raises one dead-man alert and one recovery notice.
|
stops, ARM independently raises one dead-man alert and one recovery notice.
|
||||||
|
- Good: a deployment rebuilds the server with a different storage mount, then
|
||||||
|
refreshes the installed backup units through the installer and proves every
|
||||||
|
frozen identity matches before closing the deployment.
|
||||||
- Base: local implementation and synthetic contracts pass while both server
|
- Base: local implementation and synthetic contracts pass while both server
|
||||||
installers remain disabled and explicitly externally gated.
|
installers remain disabled and explicitly externally gated.
|
||||||
- Bad: send through `compose exec server`, hard-code a personal employee number,
|
- Bad: send through `compose exec server`, hard-code a personal employee number,
|
||||||
persist a robot URL in `.env`/unit/repository, give production a normal ARM
|
persist a robot URL in `.env`/unit/repository, give production a normal ARM
|
||||||
login key, put archive below inbox, accept a new host fingerprint automatically,
|
login key, put archive below inbox, accept a new host fingerprint automatically,
|
||||||
or prune local points before target-side finalize.
|
prune local points before target-side finalize, or leave installed backup
|
||||||
|
units bound to a pre-deployment mount until the next scheduled alert.
|
||||||
|
|
||||||
### 6. Tests Required
|
### 6. Tests Required
|
||||||
|
|
||||||
@@ -1068,6 +1083,10 @@ test "$(printf '%s\n' "$actual" | LC_ALL=C sort)" = \
|
|||||||
timers, ARM dead-man timer, explicit project/data/build/SSH/Webhook-file
|
timers, ARM dead-man timer, explicit project/data/build/SSH/Webhook-file
|
||||||
binding, disabled-by-default installation and absence of a literal complete
|
binding, disabled-by-default installation and absence of a literal complete
|
||||||
robot URL in tracked or untracked repository files.
|
robot URL in tracked or untracked repository files.
|
||||||
|
- Deployment and rebuild review must assert that installed production units
|
||||||
|
still match the unique active project, build, PGDATA and storage mount. A
|
||||||
|
changed mount with otherwise healthy `/ready` is not a completed deployment
|
||||||
|
until installer refresh, timer state and runtime identity all pass.
|
||||||
- Real ARM setup, first paired batch, deliberate source-timer stop or transfer
|
- Real ARM setup, first paired batch, deliberate source-timer stop or transfer
|
||||||
failure, dead-man first/repeated/recovery behavior and quarterly shadow restore
|
failure, dead-man first/repeated/recovery behavior and quarterly shadow restore
|
||||||
are separately approved operational evidence. A direct robot send test alone
|
are separately approved operational evidence. A direct robot send test alone
|
||||||
@@ -1078,6 +1097,10 @@ test "$(printf '%s\n' "$actual" | LC_ALL=C sort)" = \
|
|||||||
#### Wrong
|
#### Wrong
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# Rebuild the application, observe /ready, but leave installed backup units
|
||||||
|
# frozen to the pre-deployment runtime identity.
|
||||||
|
docker-compose <approved-compose-arguments> up -d --build server
|
||||||
|
|
||||||
compose exec server send-alert --recipient=<personal-employee-number>
|
compose exec server send-alert --recipient=<personal-employee-number>
|
||||||
rsync -a <batch>/ backup-host:<archive>/
|
rsync -a <batch>/ backup-host:<archive>/
|
||||||
docker exec -e PGPASSWORD="$POSTGRES_PASSWORD" db pg_dump ...
|
docker exec -e PGPASSWORD="$POSTGRES_PASSWORD" db pg_dump ...
|
||||||
@@ -1086,6 +1109,12 @@ docker exec -e PGPASSWORD="$POSTGRES_PASSWORD" db pg_dump ...
|
|||||||
#### Correct
|
#### Correct
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# After a deployment/rebuild, compare the installed unit contract with the
|
||||||
|
# unique active server/database pair. If any frozen identity changed, refresh
|
||||||
|
# all units through the installer before verifying timers and identity again.
|
||||||
|
<complete-current-contract-env> \
|
||||||
|
bash deploy/trialrun/install-automatic-backup-timer.sh enable
|
||||||
|
|
||||||
# Install only with explicit active identity and a root-only Webhook file path;
|
# Install only with explicit active identity and a root-only Webhook file path;
|
||||||
# default remains disabled.
|
# default remains disabled.
|
||||||
<explicit-project-data-build-ssh-and-webhook-file-env> \
|
<explicit-project-data-build-ssh-and-webhook-file-env> \
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# 自动备份运行身份失败修复设计
|
||||||
|
|
||||||
|
## 1. Root Cause
|
||||||
|
|
||||||
|
自动备份安装时把附件宿主目录冻结进 systemd unit。后续 server 容器重建使用了另一实际宿主目录,但 unit 未同步刷新。runner 在 `runtime_identity` 阶段比较 Docker 实际 mount source 与 unit 的 `STORAGE_HOST_DIR`,发现不一致后在 DB dump、附件归档和传输前失败关闭。
|
||||||
|
|
||||||
|
严格检查本身正确:两个目录均存在、非符号链接、realpath 和 inode 不同,因此不能通过路径规范化或放宽校验修复。
|
||||||
|
|
||||||
|
## 2. Safe Refresh
|
||||||
|
|
||||||
|
从 daily unit 读取现有环境合同,从唯一运行 server 容器解析实际附件 mount source,仅在内存中替换 `STORAGE_HOST_DIR`。验证所有路径、权限、build 对应关系后,调用仓库安装器 `enable` 原子重写三个 service/timer 文件并 daemon-reload;写入前保存三个 services/timers 的 root-only 临时副本用于失败回滚,成功验证后删除副本。
|
||||||
|
|
||||||
|
不在命令输出、task 或文档中记录实际路径、容器 ID、连接串或 webhook。
|
||||||
|
|
||||||
|
## 3. Validation Sequence
|
||||||
|
|
||||||
|
```text
|
||||||
|
local 73-check contract
|
||||||
|
-> production read-only recheck
|
||||||
|
-> snapshot six unit files
|
||||||
|
-> installer enable with actual storage mount
|
||||||
|
-> six-dimension runtime identity check
|
||||||
|
-> manual daily service start
|
||||||
|
-> verify new success state + ARM finalize + recovery alert state
|
||||||
|
-> verify timers and unrelated monitors
|
||||||
|
-> remove temporary unit snapshot
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. Rollback
|
||||||
|
|
||||||
|
- 安装器或 unit 验证失败:恢复六个 unit 快照、daemon-reload、恢复原 timer 状态;不运行备份。
|
||||||
|
- unit 正确但补跑备份后续阶段失败:不恢复旧 `STORAGE_HOST_DIR`,因为旧值已证实错误;保留新配置、失败摘要和本地诊断,按具体 phase 另行修复。
|
||||||
|
- 新批次只有在 ARM finalize 后才更新 success state 和触发本地保留策略,失败不会删除现有最后成功恢复点。
|
||||||
|
|
||||||
|
## 5. Prevention
|
||||||
|
|
||||||
|
把“部署后 active mount 与自动备份 unit 合同一致”写入部署/试运行规约和 checklist。涉及 data path 的部署不能只验证应用 `/ready`;若自动备份 units 已安装,必须在交付结束前核对或刷新冻结的 project/build/PGDATA/storage 身份。
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# 自动备份运行身份失败实施清单
|
||||||
|
|
||||||
|
## 1. Pre-fix
|
||||||
|
|
||||||
|
- [x] 确认 daily service 02:30 非零退出且告警类别为 `phase_runtime_identity_failed`。
|
||||||
|
- [x] 按 runner 的 Compose 选择逻辑确认唯一根因为附件宿主挂载不匹配。
|
||||||
|
- [x] 确认不是 symlink/realpath/inode 等价,其他五项 runtime identity 通过。
|
||||||
|
- [x] 确认最近成功恢复点约 11 小时,尚未超过 watchdog/dead-man 窗口。
|
||||||
|
- [x] 确认 07:10 `phase_stale_check_failed` 由 watchdog 复用同一 runtime identity 检查触发,不是独立的归档损坏结论。
|
||||||
|
- [x] 运行本地 `bash scripts/qa/run-automatic-backup-contract.sh`(73 项通过)。
|
||||||
|
|
||||||
|
## 2. Unit Refresh
|
||||||
|
|
||||||
|
- [x] 重新只读解析当前实际附件 mount source并验证安全边界。
|
||||||
|
- [x] 保存六个自动备份 unit 的 root-only 临时快照。
|
||||||
|
- [x] 沿用现有 unit 合同,仅替换实际附件宿主目录,调用安装器 `enable`。
|
||||||
|
- [x] 验证 daily/watchdog/drill timers enabled/active 和六项 runtime identity 全部匹配。
|
||||||
|
|
||||||
|
## 3. Recovery Backup
|
||||||
|
|
||||||
|
- [x] 手工启动 daily service;记录稳定 phase/result,不输出原始日志。
|
||||||
|
- [x] 验证新的 last-success 时间、项目/build 身份和 manifest 哈希格式。
|
||||||
|
- [x] 通过仅在 ARM finalize 成功后写入的 success state 验证新归档闭环;普通 ARM 账号无 archive 读取权限,未扩大权限取证。
|
||||||
|
- [x] 验证 alert state 为 `PASS_AUTOMATIC_BACKUP_COMPLETE`。
|
||||||
|
- [x] 用户确认群内只收到一条 `PASS_AUTOMATIC_BACKUP_COMPLETE / healthy` 恢复通知。
|
||||||
|
- [x] 验证应用健康 timer、备份 watchdog/drill 和 ARM dead-man/receiver 不受影响。
|
||||||
|
|
||||||
|
## 4. Cleanup and Governance
|
||||||
|
|
||||||
|
- [x] 删除临时 unit 快照;不删除旧目录、旧批次或失败证据。
|
||||||
|
- [x] 更新部署规约/checklist 的自动备份 unit 漂移门禁。
|
||||||
|
- [x] 运行 task validate、governance、sensitive、docs audit 和 `git diff --check`;全部无错误,docs audit 仅保留既有 22 项未登记文档 warning。
|
||||||
|
|
||||||
|
## 5. Rollback
|
||||||
|
|
||||||
|
- unit 刷新或 identity 复检失败:恢复六个 unit 快照并 daemon-reload,不补跑备份。
|
||||||
|
- 补跑在 paired/offhost phase 失败:保留已纠正 unit,依据安全 phase 诊断继续处理,不回退旧附件目录。
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
# 自动备份运行身份失败
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
恢复每日自动成对备份,使生产 unit 冻结的存储路径重新与当前运行容器一致,补跑并完成一次数据库、附件和 ARM 异机归档闭环,同时保留失败告警的脱敏证据。
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
- 只读确认 02:30 `phase_runtime_identity_failed` 的精确失败维度和最近成功恢复点。
|
||||||
|
- 使用当前运行 server 容器的实际附件挂载源刷新自动备份三个 systemd services/timers;其他已验证环境值沿用现有 unit,禁止打印路径或秘密。
|
||||||
|
- 刷新前后验证 active server/db 数量、运行状态、构建 SHA、附件挂载、PGDATA 和数据库身份。
|
||||||
|
- 手工运行一次 daily backup,要求本地完整批次、ARM finalize、success state 和一条恢复通知全部成功。
|
||||||
|
- 保持应用健康监控、备份 receiver、ARM dead-man 和业务服务不变。
|
||||||
|
|
||||||
|
## Confirmed Facts
|
||||||
|
|
||||||
|
- 2026-07-28 02:30 daily service 以非零状态失败,脱敏状态为 `FAIL_AUTOMATIC_BACKUP / phase_runtime_identity_failed`。
|
||||||
|
- 生产机使用 standalone `docker-compose`;最初诊断命令误用 `docker compose`,已按 runner 相同选择逻辑更正,未把诊断工具错误当作根因。
|
||||||
|
- 当前 server/db 各唯一且运行中;unit 冻结的构建 SHA、PGDATA、应用/数据库身份均与运行容器匹配。
|
||||||
|
- 唯一不匹配项是 `STORAGE_HOST_DIR`:unit 配置目录和当前容器实际挂载源都存在,但 realpath/inode 均不同,不是符号链接或路径规范化差异。
|
||||||
|
- unit 早于当前 server 容器启动,说明部署/重建后容器使用了新附件目录,而自动备份 unit 仍保留旧目录。
|
||||||
|
- 最近一次成功异机恢复点约 11 小时前,项目和构建身份均匹配;当前未超过 24/26 小时窗口,但本次 02:30 没有产生新恢复点。
|
||||||
|
- runtime identity 阶段不写 `last-error.log`,因此没有原始诊断残留;脚本按设计在任何备份写入前失败关闭。
|
||||||
|
- 2026-07-28 07:10 watchdog 又发送 `phase_stale_check_failed`。`check-stale` 在读取 success state 前同样调用 `resolve_active_runtime`,因此该消息由同一个附件挂载漂移触发;它不是第二个独立故障,也不证明最后成功批次本身损坏或过期。
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- R1:刷新前再次从运行容器解析唯一实际附件挂载源,要求安全绝对目录、存在、非符号链接,且不得输出路径。
|
||||||
|
- R2:不得直接手改 unit 单行;使用已提交安装器以完整合同重写 daily/watchdog/drill units,并保持三个 timers enabled/active。
|
||||||
|
- R3:除 `STORAGE_HOST_DIR` 改为当前实际挂载源外,Compose project、PGDATA、build、远端目标、SSH、webhook 和备份目录等合同必须与现有 unit 一致。
|
||||||
|
- R4:刷新后先只读复跑六项 runtime identity;任何一项不匹配均不得补跑备份。
|
||||||
|
- R5:手工补跑必须使用现有 runner,完成数据库 dump、附件归档、manifest、受限传输和 ARM finalize 后才写 success state。
|
||||||
|
- R6:成功后应把当前告警状态(最新类别为 `phase_stale_check_failed`)切换为 `PASS_AUTOMATIC_BACKUP_COMPLETE` 并发送一次恢复通知;相同健康状态不得重复发送。
|
||||||
|
- R7:不得删除旧成功批次、旧目录或失败状态;正常保留策略只在新批次已由 ARM finalize 后运行。
|
||||||
|
- R8:如果 unit 刷新失败,恢复原 unit 文件并 daemon-reload;如果补跑失败,保留刷新后的正确 identity 配置和脱敏失败状态,不回退到旧附件目录。
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [x] AC1:根因证据明确为非规范化的附件宿主目录漂移,其他 runtime identity 维度全部匹配。
|
||||||
|
- [x] AC2:三个自动备份 timer 仍为 enabled/active,刷新后的 daily unit 与当前附件挂载、PGDATA、build、项目和数据库身份全部匹配。
|
||||||
|
- [x] AC3:手工 daily backup 成功生成并在 ARM finalize 一个新的完整批次,生产 success state 时间更新。
|
||||||
|
- [x] AC4:群内只收到一次自动备份恢复通知,状态为成功;无秘密、路径或原始日志进入消息/任务证据。
|
||||||
|
- [x] AC5:应用健康 timer、ARM backup dead-man 和备份 receiver 未受影响。
|
||||||
|
- [x] AC6:补充防复发合同:部署后若容器宿主挂载变化,必须刷新/核对自动备份 unit,不能等次日告警发现。
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- 修改数据库、附件内容、OA/Caddy、备份保留数量或 ARM 归档结构。
|
||||||
|
- 删除旧附件目录或判断其业务处置。
|
||||||
|
- 重置数据库、恢复数据、改 webhook、SSH key 或真实业务数据。
|
||||||
|
- 将路径、容器 ID、数据库身份、连接串或原始日志写入文档/聊天。
|
||||||
|
|
||||||
|
## Test Plan
|
||||||
|
|
||||||
|
- 刷新前后运行脱敏 runtime identity 矩阵。
|
||||||
|
- 运行本地自动备份 73 项合同,确保安装器/helper/runner 行为无回归。
|
||||||
|
- 生产补跑后检查 service result、last-success 新鲜度、ARM 新批次 finalize、alert state 和三个 timers。
|
||||||
|
- 运行 task validate、Trellis governance、敏感内容扫描、文档审计和 `git diff --check`。
|
||||||
|
|
||||||
|
## Key Decision
|
||||||
|
|
||||||
|
立即修复 unit 的附件路径并补跑备份,不修改严格相等检查。两个目录不是同一 inode,放宽为 realpath 或“目录存在即可”会掩盖真实数据源漂移。
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"id": "automatic-backup-runtime-identity-failure",
|
||||||
|
"name": "automatic-backup-runtime-identity-failure",
|
||||||
|
"title": "自动备份运行身份失败",
|
||||||
|
"description": "诊断 02:30 自动备份 phase_runtime_identity_failed 告警,确认影响范围、根因与安全修复路径。",
|
||||||
|
"status": "in_progress",
|
||||||
|
"dev_type": "operations",
|
||||||
|
"scope": "deployment",
|
||||||
|
"package": "hyb-oa",
|
||||||
|
"priority": "P2",
|
||||||
|
"creator": "shunli",
|
||||||
|
"assignee": "shunli",
|
||||||
|
"createdAt": "2026-07-28",
|
||||||
|
"completedAt": null,
|
||||||
|
"branch": null,
|
||||||
|
"base_branch": "main",
|
||||||
|
"worktree_path": null,
|
||||||
|
"commit": null,
|
||||||
|
"pr_url": null,
|
||||||
|
"subtasks": [],
|
||||||
|
"children": [],
|
||||||
|
"parent": null,
|
||||||
|
"relatedFiles": [],
|
||||||
|
"notes": "02:30 daily 与 07:10 watchdog 均由自动备份 unit 的旧附件宿主目录漂移触发,其他运行身份正常。已用现有安装器刷新 units,三个 timer 与全部运行身份通过;手工补跑、ARM finalize、成功状态、单条群恢复通知和关联监控复核通过,临时快照已删除。73 项合同及治理、敏感、流程、文档和格式门禁无错误,全部验收项闭环。",
|
||||||
|
"meta": {}
|
||||||
|
}
|
||||||
@@ -93,6 +93,7 @@
|
|||||||
- [ ] 公网 live smoke(只读,不做写操作):`bash scripts/qa/run-web-gates.sh smoke-live`
|
- [ ] 公网 live smoke(只读,不做写操作):`bash scripts/qa/run-web-gates.sh smoke-live`
|
||||||
- [ ] 若本地已配置 `LIVE_SMOKE_*`,确认 `bash deploy/trialrun/deploy.sh` 输出中包含 live smoke 结果
|
- [ ] 若本地已配置 `LIVE_SMOKE_*`,确认 `bash deploy/trialrun/deploy.sh` 输出中包含 live smoke 结果
|
||||||
- [ ] 已确认 release gate / live smoke 结果为通过;关键链路不再作为每次发布的手工必做项
|
- [ ] 已确认 release gate / live smoke 结果为通过;关键链路不再作为每次发布的手工必做项
|
||||||
|
- [ ] 若自动备份 units 已安装,已脱敏核对其冻结的 Compose project、build、PGDATA、附件宿主挂载与当前唯一活动 server/db;任一项变化均已用现有安装器按完整合同刷新,三个 timer 仍为 enabled/active,未直接手改 unit
|
||||||
- [ ] 查看 server 日志(无异常错误风暴):
|
- [ ] 查看 server 日志(无异常错误风暴):
|
||||||
- [ ] `docker-compose -p "$COMPOSE_PROJECT_NAME" -f deploy/trialrun/docker-compose.yml --env-file deploy/trialrun/.env logs --tail=200 server`
|
- [ ] `docker-compose -p "$COMPOSE_PROJECT_NAME" -f deploy/trialrun/docker-compose.yml --env-file deploy/trialrun/.env logs --tail=200 server`
|
||||||
- [ ] 已确认 smoke 账号为独立账号(`smoke-admin` / `smoke-employee`),且不参与真实业务
|
- [ ] 已确认 smoke 账号为独立账号(`smoke-admin` / `smoke-employee`),且不参与真实业务
|
||||||
|
|||||||
@@ -236,6 +236,8 @@ manifest 必须至少记录一个真实来源标识:活动 `/ready` 有构建
|
|||||||
- 生产与 ARM 都直接使用独立企微运维群机器人,不依赖 OA server、数据库或个人工号。Webhook 正文只由用户手工写入两台主机的 root-only `0600` 文件;仓库、普通 `.env`、systemd unit、日志和聊天只能出现受控文件路径,不能出现 URL 或 key。
|
- 生产与 ARM 都直接使用独立企微运维群机器人,不依赖 OA server、数据库或个人工号。Webhook 正文只由用户手工写入两台主机的 root-only `0600` 文件;仓库、普通 `.env`、systemd unit、日志和聊天只能出现受控文件路径,不能出现 URL 或 key。
|
||||||
- 失败告警只包含阶段、状态、finding 数和类别建议;原始命令诊断只保留在源端状态目录的 `0600` 最近失败日志。同一失败降噪;首次失败、状态恶化、新 finding、恢复通知和每季度首次恢复提醒才发送。新部署构建与最近恢复点 build SHA 不同只记 finding,缺失、损坏、超龄、未来时间或 Compose project 不同仍硬失败。
|
- 失败告警只包含阶段、状态、finding 数和类别建议;原始命令诊断只保留在源端状态目录的 `0600` 最近失败日志。同一失败降噪;首次失败、状态恶化、新 finding、恢复通知和每季度首次恢复提醒才发送。新部署构建与最近恢复点 build SHA 不同只记 finding,缺失、损坏、超龄、未来时间或 Compose project 不同仍硬失败。
|
||||||
|
|
||||||
|
自动备份 unit 会冻结安装时的 Compose project、build、PGDATA 和附件宿主挂载。每次发布、回滚或直接重建容器后,若这些 units 已安装,必须把 unit 中的四项身份与唯一活动 server/db 逐项脱敏核对;任一项变化都要使用现有安装器和完整当前合同刷新 units,再复核三个 timer 与全部运行身份,不能直接手改单个 unit,也不能等次日定时告警后再处理。`phase_stale_check_failed` 是 watchdog 外层阶段,它也可能表示读取成功状态前的运行身份检查失败;应先看安全子类别和 unit/活动容器身份,不能仅凭该阶段认定归档已经超龄或损坏。
|
||||||
|
|
||||||
创建机器人后,由用户分别登录两台主机,在 root shell 中用静默输入把完整 Webhook 写入受控路径;不要把值作为命令参数、环境文件内容或聊天消息交给 Codex:
|
创建机器人后,由用户分别登录两台主机,在 root shell 中用静默输入把完整 Webhook 写入受控路径;不要把值作为命令参数、环境文件内容或聊天消息交给 Codex:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -431,6 +433,7 @@ curl -fsS "http://127.0.0.1:${SERVER_HOST_PORT##*:}/ready"
|
|||||||
重建后必须补做:
|
重建后必须补做:
|
||||||
- `smoke-external-deps.js`
|
- `smoke-external-deps.js`
|
||||||
- `smoke-trialrun-runtime.js`
|
- `smoke-trialrun-runtime.js`
|
||||||
|
- 若自动备份 units 已安装,核对其 Compose project、build、PGDATA、附件宿主挂载与当前唯一活动 server/db;不一致时用安装器完整刷新并复核三个 timer
|
||||||
- 公网页面视觉检查:至少覆盖 `/invoice-pool`、`/reimbursements`、`/mobile/capture` 的 390px 视口
|
- 公网页面视觉检查:至少覆盖 `/invoice-pool`、`/reimbursements`、`/mobile/capture` 的 390px 视口
|
||||||
|
|
||||||
发布/回滚 Checklist:
|
发布/回滚 Checklist:
|
||||||
|
|||||||
在新工单中引用
屏蔽一个用户