# domainCheck Release Hub Contract ## 1. 目标 这份文档用于冻结 Release / Rollout / Launchpad / Gate 的正式 contract。 核心原则: - 先有 Release,再有 Rollout - Release 是不可变版本对象 - Rollout 是分批推进对象 - Gate 是是否可发的统一门禁对象 - Launchpad 是给页面与 Codex 消费的发布驾驶舱对象 建议与 [ops_job_contract.md](/www/wwwroot/getDomain/docs/schemas/ops_job_contract.md:1) 配套阅读,因为 Rollout 的真正执行颗粒度最终仍然是 `ops job`。 --- ## 2. Release Object 来源: - `POST /api/v1/ops/releases` - `GET /api/v1/ops/releases` - `GET /api/v1/ops/releases/latest` - `GET /api/v1/ops/releases/{id}` - `POST /api/v1/ops/releases/{id}/activate` 最小字段: ```json { "id": 1, "release_version": "2026.04.18+bd6bcb2", "channel": "stable", "commit_sha": "bd6bcb2", "status": "ready", "status_label": "已就绪", "artifact_url": "https://release.example.com/domaincheck-2026.04.18.tgz", "checksum": "sha256:...", "notes": "", "metadata": {}, "created_by": "www", "created_at": "2026-04-18 05:30:00", "activated_at": "2026-04-18 05:35:00", "summary": "2026.04.18+bd6bcb2 已就绪,制品与校验信息齐备,可进入 Rollout。", "summary_text": "2026.04.18+bd6bcb2 已就绪,制品与校验信息齐备,可进入 Rollout。", "focus_ref": { "kind": "release_hub", "release_id": 1, "release_version": "2026.04.18+bd6bcb2", "channel": "stable", "rollout_id": 0, "rollout_code": "", "section": "release_detail" } } ``` 约束: - 同一 `release_version` 只对应一份发布物 - Release 创建后不应重新指向另一份 artifact - `artifact_url + checksum` 是发布可验证性的最小基线 推荐状态: - `draft` - `ready` - `active` - `archived` --- ## 3. Rollout Object 来源: - `POST /api/v1/ops/releases/{id}/rollouts` - `GET /api/v1/ops/rollouts/{id}` - `GET /api/v1/ops/rollouts/{id}/jobs` - `POST /api/v1/ops/rollouts/{id}/advance` 最小字段: ```json { "id": 11, "release_id": 1, "rollout_code": "rollout-20260418-001", "target_selector": { "region": "mainland", "role": "worker" }, "target_nodes": [ "mainland-worker-01", "mainland-controller-01" ], "policy": { "batch_size": 1, "require_approval_between_batches": true, "rollback_on_failure": true }, "status": "running", "status_label": "执行中", "batch_cursor": 1, "batches_total": 2, "jobs_total": 2, "jobs_created": 1, "result_summary": {}, "target_node_codes": [ "mainland-worker-01", "mainland-controller-01" ], "summary": "当前已覆盖 1/2 台节点,仍有 1 条任务执行中。", "summary_text": "当前已覆盖 1/2 台节点,仍有 1 条任务执行中。", "focus_ref": { "kind": "release_rollout", "rollout_id": 11, "rollout_code": "rollout-20260418-001", "release_id": 1, "section": "rollout_jobs" } } ``` 正式定义: > 某个 Release 在某批目标节点上的一次分批推进计划 推荐状态: - `planned` - `running` - `awaiting_approval` - `ready_for_next_batch` - `halted` - `completed` - `completed_with_issues` --- ## 4. Default Rollout Gate 来源: - `GET /api/v1/ops/overview` - `overview.release_hub.default_rollout_gate` 这层是 Release Hub 最关键的后端 gate contract。 最小字段: ```json { "status": "attention", "status_label": "待处理", "status_type": "warning", "summary": "当前已有 Release,但默认目标节点中仍有未接管或未通过巡检的节点。", "summary_text": "当前已有 Release,但默认目标节点中仍有未接管或未通过巡检的节点。", "release": { "id": 1, "release_version": "2026.04.18+bd6bcb2", "status": "ready", "status_label": "已就绪" }, "execution_mode": "remote-agent", "execution_mode_label": "Node Agent", "target_node_codes": [ "mainland-worker-01" ], "default_target_node_codes": [ "mainland-worker-01" ], "blocking_reasons": [], "warning_reasons": [ "存在未通过标准巡检的节点" ], "recommendations": [ "先执行标准巡检,再进入 Rollout" ], "operational_readiness": { "summary": "remote-agent 就绪 1/2,标准巡检通过 1/2", "rows": [] }, "focus_ref": { "kind": "release_hub", "release_id": 1, "release_version": "2026.04.18+bd6bcb2", "channel": "stable", "rollout_id": 0, "rollout_code": "", "section": "default_rollout_gate" } } ``` 推荐状态: - `missing_release` - `release_not_ready` - `artifact_missing` - `no_targets` - `blocked` - `attention` - `ready` 要求: - 首页驾驶建议 - Release 页面门禁 - Rollout 预检 - Codex 自动驾驶 必须共用这同一份 Gate。 --- ## 5. Default Rollout Execution 来源: - `overview.release_hub.default_rollout_execution` - `overview.release_hub.default_rollout_gate_options` 作用: - 告诉页面 / Codex 这轮默认应使用什么执行方式 - 给出其他可选 gate / mode 最小字段: ```json { "recommended_mode": "remote-agent", "recommended_mode_label": "Node Agent", "recommended_gate": {}, "gates": { "remote-agent": {}, "ssh": {} } } ``` 推荐执行模式: - `remote-agent` - `ssh` - `control-plane` 说明: - 正式发布优先 `remote-agent` - `ssh` 只作为首发接管与紧急救援兜底 - `control-plane` 不应用来承载大规模 Worker 发布 --- ## 6. Release Launchpad 来源: - `overview.release_hub.launchpad` 作用: - 给页面与 Codex 提供更偏“驾驶舱”的发布聚合视角 最小字段: ```json { "latest_package": {}, "latest_release": {}, "worker_rollout_preview": {}, "control_rollout_preview": {}, "launchpad_status": { "status": "attention", "status_label": "待处理", "summary": "建议先完成 Worker 点状灰度,再决定是否推进控制面。", "summary_text": "建议先完成 Worker 点状灰度,再决定是否推进控制面。", "recommended_action_code": "open_release_deploy_worker", "recommended_target_node_code": "mainland-worker-01", "recommended_recovery_label": "签发接入工单", "recommended_recovery_summary": "当前节点仍处于接入阶段,建议先签发 onboarding.bootstrap。", "onboarding_bootstrap_pending_nodes": 1, "onboarding_acceptance_ready_nodes": 0, "recommended_mode": "remote-agent", "recommended_mode_label": "Node Agent", "focus_ref": { "kind": "release_hub", "release_id": 1, "release_version": "2026.04.18+bd6bcb2", "channel": "stable", "rollout_id": 0, "rollout_code": "", "section": "release_launchpad", "mode": "worker" } } } ``` `launchpad_status` 至少应回答: - 当前先补什么 - 推荐动作码是什么 - 当前首个缺口节点是谁 - 当前恢复动作的人类标签是什么 - 当前缺口更偏“待接入”还是“待验收” - 推荐执行方式是什么 - 先看 Worker 预案还是先看 Control 预案 正式约束: - `recommended_target_node_code` - 一旦 launchpad 已经收敛出首个缺口节点,就应直接返回 - 页面、CLI、Codex 不应再分别从 gap rows 中自行挑第一台 - `recommended_recovery_label` - 是给人看的动作标题,例如: - `签发接入工单` - `补执行面接管` - `执行接管验收` - `recommended_recovery_summary` - 是解释“为什么当前默认不是发版,而是先补接管”的正式摘要 - `onboarding_bootstrap_pending_nodes / onboarding_acceptance_ready_nodes` - 是 launchpad 汇总层面的正式计数 - 允许被 `go-live-summary`、CLI 摘要、Codex brief 直接复用 --- ## 7. 发布动作 contract 当前发布相关动作不应再退回“通用发任务”语义,而应正式区分: - `deploy.release.control` - `deploy.release.worker` - `deploy.release.custom` 这三个动作必须共用: - 同一份 Gate 判断 - 同一份 Release 版本对象 - 同一份 Rollout 编排结果 --- ## 8. Artifact 不可变要求 上线前建议把 Release 供应链固定为: 1. 构建产物 2. 生成 `checksum` 3. 生成 `manifest` 4. 上传 artifact 5. 创建 Release 6. 激活 channel 7. 创建 Rollout Node Agent 侧部署链路要求: 1. 下载 artifact 2. 校验 checksum 3. 解压到 `releases/` 4. 切换 `current` 5. 重启服务 6. 健康检查 7. 失败回滚 --- ## 9. Rollout 与 Ops Job 的关系 约束: - Rollout 是分批推进对象 - Ops Job 是单次动作对象 - Rollout 通过一批或多批 `deploy.release.*` Job 落地 控制面责任: - 决定下一批是否生成 Job - 汇总 Job 结果 - 反推 Rollout 状态 - 决定是否暂停、继续或回滚 Node Agent 不负责: - 推进下一批 - 修改 Rollout 策略 - 修改 Release 状态 --- ## 10. 页面 / Codex 共同消费约束 以下对象必须作为稳定后端 contract 存在: - `default_rollout_gate` - `default_rollout_execution` - `launchpad_status` - `worker_rollout_preview` - `control_rollout_preview` 页面只负责展示和少量交互兜底。 Codex 只负责消费后端给出的: - 当前是否可发 - 建议先发哪一侧 - 应该使用什么 mode - 下一步推荐动作是什么 --- ## 11. Artifact Manifest Contract Release Hub 不能只停在: - `artifact_url` - `checksum` 这只能证明“有个包”,还不能证明“这个包会把哪些服务改成什么样”。 建议把 `release.metadata.manifest` 固定为正式对象。 最小结构: ```json { "artifact_kind": "tarball", "package_name": "domaincheck-2026.04.18+bd6bcb2.tgz", "package_size_bytes": 186542331, "checksum": "sha256:...", "build_id": "build-20260418-001", "commit_sha": "bd6bcb2", "built_at": "2026-04-18 05:20:00", "included_services": [ "domaincheck-api", "domaincheck-worker", "domaincheck-sync-agent", "domaincheck-node-agent" ], "required_env_keys": [ "NODE_CODE", "NODE_REGION", "NODE_ROLE" ], "health_checks": [ { "name": "api_health", "type": "http", "url": "http://127.0.0.1:8100/health" } ], "rollback_hint": "切回上一个 current 并重启对应服务" } ``` 正式要求: - Release 创建后,`manifest` 不应再被覆盖成另一份语义不同的发布物 - Node Agent、页面、Codex 必须共用这同一份 manifest - 是否可发、怎么回滚、要验哪些服务,不应再散落在: - shell 脚本 - 页面表单 - 人工脑补 --- ## 12. Operational Readiness Row Contract `default_rollout_gate.operational_readiness.rows[]` 不应只是“几行提醒文案”,而应成为逐节点门禁对象。 最小结构: ```json { "node_code": "mainland-worker-01", "region": "mainland", "role": "worker", "agent_managed": true, "execution_mode_ready": true, "execution_mode": "remote-agent", "execution_mode_label": "Node Agent", "inspection_status": "healthy", "inspection_status_label": "已通过", "current_release_version": "2026.04.11+1921318", "desired_release_version": "2026.04.18+bd6bcb2", "risk_level": "low", "blocking_reasons": [], "warning_reasons": [], "recommended_action_code": "", "focus_ref": { "kind": "release_hub", "release_id": 1, "release_version": "2026.04.18+bd6bcb2", "channel": "stable", "rollout_id": 0, "rollout_code": "", "section": "default_rollout_gate" } } ``` 正式要求: - `rows[]` 应能直接回答: - 这台节点能不能进下一轮 rollout - 不能的话卡在哪 - 只是 warning 还是 hard block - 页面、CLI、Codex 不应再从: - inspection - runtime - nodes 三处数据手工拼门禁结论 推荐风险分级: - `low` - `medium` - `high` - `blocked` --- ## 13. Rollout Preview Contract 真正创建 Rollout 之前,还应保留一层正式 preview,而不是由页面自己预估批次和风险。 建议入口: - `POST /api/v1/ops/releases/{id}/rollouts/preview` 最小请求体: ```json { "release_id": 1, "target_node_codes": [ "mainland-worker-01", "mainland-controller-01" ], "policy": { "batch_size": 1, "require_approval_between_batches": true, "rollback_on_failure": true }, "execution_mode": "remote-agent" } ``` 最小返回体: ```json { "release": {}, "execution_mode": "remote-agent", "execution_mode_label": "Node Agent", "target_nodes_total": 2, "target_node_codes": [ "mainland-worker-01", "mainland-controller-01" ], "expected_batches_total": 2, "expected_jobs_total": 2, "gate": {}, "batch_plan": [ { "batch_no": 1, "node_codes": [ "mainland-worker-01" ], "risk_level": "low" }, { "batch_no": 2, "node_codes": [ "mainland-controller-01" ], "risk_level": "medium" } ], "summary": "建议先灰度 1 台 Worker,再推进 Controller。", "summary_text": "建议先灰度 1 台 Worker,再推进 Controller。", "focus_ref": { "kind": "release_hub", "release_id": 1, "release_version": "2026.04.18+bd6bcb2", "channel": "stable", "rollout_id": 0, "rollout_code": "", "section": "rollout_preview" } } ``` 正式要求: - preview 是: - 页面发 rollout 前的统一预检 - Codex 自动驾驶发 rollout 前的统一预检 - CLI 批量发布前的统一预检 - 不允许前端重新自己算: - 该分几批 - 哪台应该先发 - 哪台属于高风险 只有这样,Release Hub 才是真正的发布控制面,而不是“能看版本的页面”。