mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
feat(ql3): operationalize manual primary canary
This commit is contained in:
@@ -170,10 +170,13 @@ owner 在接受触发时写入执行上下文,并贯穿日志、指标和回
|
||||
- `defaultMode` 固定为 off,当前只允许 `origins.manual`;Primary 不得通过环境变量、通配 origin 或隐式默认值开启。
|
||||
- enabled manifest 必须记录 revision、approvedBy、approvedAtMs、expiresAtMs 和 rollbackPlanRef;审批窗口最长 30 天。
|
||||
- `durableCancellation`、`startupReconciliation`、`atomicLegacyProjection`、`rollbackDrill`、`edgeBudget` 必须全部为 passed。
|
||||
- ADR-0453 后 enabled manifest 还必须绑定可由 loader 独立重算的 manual capture/terminal/resource Primary gate bundle;ADR-0454 的一次性目标实例仪式进一步绑定
|
||||
exact Profile/admission 计划、原始文件摘要、短期审批、selection receipt 与 rollback intent/completion。配置选择只表示 `primary_selected`,不能冒充当前 worker
|
||||
已经完成 `selected → reconciled → activated`。
|
||||
- 审计只包含 source path/hash、revision、时间和稳定判定。接受审计先于 router 安装;安装后审计失败立即调用 disposer,恢复原 owner。
|
||||
- edge 不启动 watcher;当前 bootstrap 只在 HTTP worker 启动时读取一次,未来显式 reload 必须复用同一校验和审计边界。
|
||||
|
||||
bootstrap 接入不代表 Primary 已默认开放。文件缺失、disabled、rejected 或 `manual` 非 primary 时保持 Legacy,且不加载完整 Runtime stack、不创建 router 或 timer。只有 accepted 且全部 gate 通过的 manifest 才按 startup reconciliation、cancel lifecycle、router 的顺序激活;任一步失败都会撤销 router 并停止 lifecycle。ADR-0007 定义的 completion/log supervisor、固定 edge 基准、部署配置写入与用户可见状态、操作回滚演练仍需再次评审。
|
||||
bootstrap 接入不代表 Primary 已默认开放。文件缺失、disabled、rejected 或 `manual` 非 primary 时保持 Legacy,且不加载完整 Runtime stack、不创建 router 或 timer。只有 accepted 且全部 gate 通过的 manifest 才按 startup reconciliation、cancel lifecycle、router 的顺序激活;任一步失败都会撤销 router 并停止 lifecycle。ADR-0454 已提供部署配置写入、只读选择状态与操作回滚仪式,但首次真实目标实例执行、运行态 durable activation receipt、固定 edge 基准和 ADR-0007 的完整实机恢复仍需继续评审。
|
||||
|
||||
## 8. Legacy 到 Run 的身份映射
|
||||
|
||||
|
||||
@@ -95,6 +95,7 @@ fail-closed 还会改变 Legacy 可用性。
|
||||
|
||||
## 后续
|
||||
|
||||
正式启用 manual Primary 前,维护者必须在目标 edge/standalone 实例完成一次真实、干净关闭的 manual canary,等待 settling 后运行 terminal audit,组合与同版本
|
||||
resource report 生成 bundle,再由 v2 loader 重放。固定物理路由、flash 写放大、断电、非干净退出和 config-root 签名/备份仍是独立发布证据;其他 origin 必须分别建立
|
||||
自己的 admission authority、样本预算和 rollback gate,不能复用 manual receipt。
|
||||
ADR-0454 已把目标实例的 prepare、observe、resource、qualify、显式短期 approve、只读 audit 与 crash-replay rollback 固化为一次性状态机,并明确
|
||||
`primary_selected` 不等于运行态 activated。正式启用 manual Primary 前,维护者仍必须在目标 edge/standalone 实例实际执行该仪式并保留 bootstrap activated audit;
|
||||
仓库内 synthetic fixture 或 compiled resource child 不能替代真实产品入口的八/三十二条 admission。固定物理路由、flash 写放大、断电、非干净退出和 config-root
|
||||
签名/备份仍是独立发布证据;其他 origin 必须分别建立自己的 admission authority、样本预算和 rollback gate,不能复用 manual receipt。
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
# ADR-0454:目标实例 Manual Primary Canary 与显式回滚仪式
|
||||
|
||||
- 状态:Accepted(仪式实现完成;首次真实用户目标实例执行仍待运维)
|
||||
- 日期:2026-08-19
|
||||
- 关联 RFC:QL-RFC-0001 D-361、PR-4、PR-5
|
||||
- 关联 ADR:ADR-0002、ADR-0449、ADR-0450、ADR-0451、ADR-0453
|
||||
- Amends:ADR-0453 的首次目标实例 canary 操作缺口
|
||||
|
||||
## 上下文
|
||||
|
||||
ADR-0453 已提供 process-epoch capture authority、clean-shutdown evidence、closed terminal audit、compiled-backend rollback/resource report、Primary gate bundle 与
|
||||
rollout manifest v2 loader,但维护者仍需手工拼接多个命令、文件名、时间窗口和摘要。只提供底层 gate 会留下四类运维缺口:
|
||||
|
||||
1. canary 开始前没有不可变计划绑定 Profile、精确样本数和当时的 live rollout 基线;
|
||||
2. terminal/resource/gate 输出容易经 shell 重定向得到宽权限、部分写入或互相不属于同一 session 的文件;
|
||||
3. qualification 与写入 live manifest 之间没有显式人工边界,配置选择又容易被误报成“当前进程已经激活”;
|
||||
4. rollback 没有绑定被替换 manifest 的摘要,也没有 crash-replay intent/completion 链。
|
||||
|
||||
Edge 可能是 128 MiB 路由设备,不能为 canary 新增 daemon、watcher、遥测栈或第二数据库;Standalone 可能运行在集群节点上,但本仪式仍只裁决本机
|
||||
`edge|standalone` Profile,不得借宿主机形态伪装成 `cluster-control|worker` Primary 证据。
|
||||
|
||||
## 决策
|
||||
|
||||
1. 新增 `qinglong/manual-primary-canary-plan@v1`。`prepare` 只接受受控 session ID、`edge|standalone`、Edge 精确 8 条或 Standalone 32–128 条中由
|
||||
operator 选定的精确 admission 数;它记录当前 `qinglong3-rollout.json` 为 absent 或 disabled+SHA-256。已有 enabled manifest 时拒绝准备。
|
||||
2. plan 固定派生全部 basename,不接受调用方提供输出路径。config root 必须是当前 UID 拥有、非 symlink、group/world 不可写的真实目录;所有 artifact 均以
|
||||
`0600`、同目录临时 inode、file fsync、hard-link no-replace 与 directory fsync 发布。重复调用只接受逐字节相同文件。
|
||||
3. `prepare` 只输出三项部署环境:实际 Profile、`QL3_SHADOW_ORIGINS=manual` 与唯一 capture basename;它不写 enabled manifest、不启动任务、不重启服务。
|
||||
operator 必须在隔离窗口中通过既有 Legacy 产品入口精确执行计划数量的 manual admission,并干净关闭同一 worker;任何额外 manual execution 都使样本不相等。
|
||||
4. `observe` 只有在 capture qualified、单一 manual origin、计数精确且窗口结束至少五分钟后,才以固定参数启动 Node 24 terminal auditor。数据库必须为非 symlink、
|
||||
group/world 不可写的普通文件;auditor 只读打开 SQLite。调用方不能注入 origin、window、settling、child script 或 shell。
|
||||
5. `resource` 用固定 `full + require-compiled + 8 samples` 启动 ADR-0451 runner。它使用自身临时 SQLite,不接触生产数据库;低配设备必须在应用停止后运行,不能把
|
||||
与常驻 workload 争用内存后的失败解释为产品回归。
|
||||
6. `qualify` 复用 ADR-0453 gate,但额外绑定 plan 文件摘要、三份 source 的原始文件摘要、三份 canonical JSON 摘要、精确 admission target 与 gate 文件摘要。
|
||||
现有 gate、partial qualification 或 response loss 只能通过原文件重放;source 在 qualification 后被替换时,mutating CLI 与独立 auditor 都失败关闭。
|
||||
7. `approve` 是唯一写 live manifest 的操作。它要求显式 `approvedBy`,审批最短一分钟、最长 24 小时;先复核 plan 时的 absent/disabled 基线,disabled 基线先
|
||||
no-replace 归档,再以同目录 fsync 临时文件和最终摘要复核发布 schema v2 manifest。结果状态固定为 `activation_approved`/`primary_selected`,并明确
|
||||
`requiresRestart=true`、`runtimeActivationObserved=false`;配置选择不得冒充 HTTP worker 已完成 startup reconciliation 和 router activation。
|
||||
8. 应用重启后,实际运行态仍由现有 bootstrap 的 `selected → reconciled → activated` audit 证明。独立 `audit:manual-primary-canary:ql3` 只读复核 plan、source、gate、
|
||||
qualification、selection receipt 与 live manifest,可要求 `prepared|qualified|selected|off|rolled-back`;`off` 只证明 loader 当前关闭,`rolled-back` 还要求本 session 的
|
||||
intent/completion 摘要链完整。auditor 刻意不提供 `primary_active` 结论。
|
||||
9. `rollback` 只接受四个固定 reason 与有界 operator。它先 no-replace 发布 intent,绑定当前 enabled manifest SHA-256 和目标 disabled SHA-256,再复核当前摘要并原子替换
|
||||
live manifest,最后发布 completion;intent 或 completion response loss 可用相同参数重放。disabled 已有效但不同于本 session 的目标时拒绝覆盖。
|
||||
10. 审批过期后,既有 loader 必须立即 fail-closed 为 off;状态/auditor 报 `approvalExpired=true` 和 `rolloutMode=off`。operator 仍应执行显式 rollback,把磁盘事实
|
||||
收敛为 schema v2 disabled manifest,然后重启应用;过期不能被当作自动续期或自动删除 authority。
|
||||
|
||||
## 被拒绝的替代方案
|
||||
|
||||
### 常驻 watcher 自动检测 capture 并启用 Primary
|
||||
|
||||
拒绝。它把一次性证据变成后台控制面,增加路由设备 timer/文件监控成本,并绕过独立人工审批。
|
||||
|
||||
### Canary CLI 自动触发八个用户任务
|
||||
|
||||
拒绝。工具没有用户会话、Task Policy、脚本内容或副作用 authority;synthetic spawn 不能证明真实 Legacy 产品入口的 admission capture。
|
||||
|
||||
### 用 enabled manifest 存在表示 Primary 已运行
|
||||
|
||||
拒绝。manifest 只在下次 bootstrap 被读取;进程可能尚未重启、startup reconciliation 可能失败或审批已经过期。selection 与 runtime activation 必须分开陈述。
|
||||
|
||||
### 原地覆盖证据和 rollout 文件以方便重试
|
||||
|
||||
拒绝。覆盖会丢失冲突和 crash window。证据采用 no-replace;live rollout 只在摘要复核后原子替换,并由 previous/intent/completion 文件保留恢复链。
|
||||
|
||||
## 资源、安全与部署影响
|
||||
|
||||
- 正常 QingLong runtime 不新增 import、timer、watcher、listener、数据库连接、schema、migration、package 或生产依赖。全部动作由 operator 一次性调用。
|
||||
- plan/qualification/selection/rollback 报告不包含数据库路径、命令、Task、Run、Cron、PID、日志、用户输出、错误正文或 Secret;stdout 只返回 stage、Profile、样本数和布尔状态。
|
||||
- `observe` 只读生产 SQLite;`resource` 只使用 runner 的临时 SQLite。Edge 应在应用停止后运行 resource,Standalone 也不得与同机生产 canary 并发争用。
|
||||
- POSIX owner 是本机信任根。摘要复核能防止误覆盖和非协作漂移,但不能防御同一 UID 在最后复核与 rename 之间的恶意并发;共享 config root 或远程 delegation 需要
|
||||
另行引入签名/锁服务,不得把本仪式描述成多写者共识。
|
||||
- 本仪式不适用于 `cluster-control|worker`,也不证明物理路由、flash wear、断电、跨主机签名或真实生产任务安全。
|
||||
|
||||
## 验证
|
||||
|
||||
- domain/CLI 聚焦矩阵覆盖 Edge/Standalone 样本预算、exact shape、短期审批、prepare 重放、unsafe root、disabled baseline drift、source replacement、partial
|
||||
qualification、selection receipt response loss、审批过期、rollback intent/completion response loss及独立审计。
|
||||
- 真实 compiled backend 已通过 D361 `prepare → resource`:Edge plan 保持 automatic activation false;resource 阶段产生 qualified full rollback evidence,
|
||||
enabled/off 进程 peak RSS 分别为 `109,953,024 / 102,383,616` bytes,真实 Legacy child 均 exit 0,Shadow Run delta 从 1 收敛为 0。
|
||||
- 阶段门已重跑:聚焦 `24/24`、`build:back`、完整 backend `1,481 pass / 0 fail / 2 conditional skip`、18-package clean build/test、四项架构审计与
|
||||
`14/14` artifact audit 全部通过;产物字节与 D-360 一致。隔离 frozen-lockfile Linux arm64 Docker 的 128 MiB router/256 MiB Edge release peak 分别为
|
||||
`81,887,232 / 148,840,448` bytes,`memory.events max/oom/oom_kill` 增量均为 0。
|
||||
- D361 不修改 PostgreSQL schema/migration、依赖树或 Kubernetes 拓扑,因此不重跑 PostgreSQL HA;任何仓库 synthetic fixture、compiled resource child 或 Docker
|
||||
arm64 结果都不沿用、也不冒充尚未执行的真实目标实例 manual capture/activation。
|
||||
|
||||
## 后续
|
||||
|
||||
维护者仍需在一台真实目标 Edge 或 Standalone 实例执行 [Manual Primary Canary 操作手册](../operations/ql3-manual-primary-canary.md),并保留 capture、terminal、resource、
|
||||
gate、qualification、selection、bootstrap activated audit 与 rollback completion。其他 origin 必须建立自己的 admission authority 与 gate;固定物理路由/flash/断电、
|
||||
运行态 durable activation receipt 和多写者 config authority 仍是独立后续工作。
|
||||
@@ -457,6 +457,7 @@
|
||||
| [ADR-0451](./ADR-0451-profile-bounded-legacy-shadow-resource-and-off-rollback-evidence.md) | 按 Profile 有界的 Legacy Shadow 资源与关闭回滚证据 | Accepted |
|
||||
| [ADR-0452](./ADR-0452-atomic-flattened-backend-build-publication.md) | 原子且扁平兼容的 Backend 构建发布 | Accepted |
|
||||
| [ADR-0453](./ADR-0453-origin-scoped-legacy-shadow-capture-authority-and-primary-gate.md) | Origin-scoped Legacy Shadow 捕获权威与 Primary 门禁 | Accepted(首次真实目标实例 manual canary 待执行) |
|
||||
| [ADR-0454](./ADR-0454-target-instance-manual-primary-canary-ceremony.md) | 目标实例 Manual Primary Canary 与显式回滚仪式 | Accepted(首次真实用户目标实例执行待运维) |
|
||||
|
||||
## 规则
|
||||
|
||||
|
||||
Reference in New Issue
Block a user