Files
getDomain/docs/schemas/ops_job_contract.md
2026-04-18 23:52:51 +08:00

14 KiB
Raw Blame History

domainCheck Ops Job Contract

1. 目标

这份文档用于冻结 ops job 的正式 contract。

它是整套海外单脑控制面里最核心的执行对象。

正式定义:

ops job 是一条可审计、可编排、可派发、可回执、可取消的标准运维任务记录。

它不是:

  • 一条 shell 命令
  • 一个页面按钮点击结果
  • 一个仅供 agent 消费的内部对象

它应该同时服务于:

  • 页面按钮
  • CLI
  • 海外 Codex 驾驶员
  • Node Agent
  • Release / Rollout
  • Playbook Run

也就是说:

  • playbook run
    • 是编排聚合对象
  • rollout
    • 是发布推进对象
  • ops job
    • 是真正的执行颗粒度对象

2. 入口接口

当前已落地的主入口:

  • GET /api/v1/ops/jobs
  • GET /api/v1/ops/jobs/{job_id}
  • GET /api/v1/ops/jobs/{job_id}/events
  • POST /api/v1/ops/jobs
  • POST /api/v1/ops/jobs/batch
  • POST /api/v1/ops/policy/preview
  • POST /api/v1/ops/jobs/{job_id}/approve
  • POST /api/v1/ops/jobs/{job_id}/cancel
  • POST /api/v1/ops/jobs/{job_id}/dispatch

配套对象来源:

  • ops_jobs
  • ops_job_steps
  • ops_job_events

正式约束:

  • 页面、CLI、Codex 不能各自定义第 2 套任务模型
  • playbook run 只能聚合 ops job
  • rollout 只能批量生成 ops job
  • Node Agent 只能拉取和回执 ops job

3. 当前实现里的 Job Object

来源:

  • GET /api/v1/ops/jobs
  • GET /api/v1/ops/jobs/{job_id}
  • 创建 / 审批 / 取消 / 派发后的返回体中的 job

最小字段:

{
  "id": 101,
  "job_code": "ops-20260418064500-a1b2c3",
  "action": "logs.collect",
  "target_type": "node",
  "target_node_code": "mainland-worker-01",
  "target_node_codes": [
    "mainland-worker-01"
  ],
  "status": "queued",
  "status_label": "排队中",
  "execution_mode": "remote-agent",
  "execution_mode_label": "Node Agent",
  "requested_by": "web-ui/ops-center",
  "payload": {},
  "metadata": {},
  "result": {},
  "summary": "目标节点 mainland-worker-01 / 发起 web-ui/ops-center / 方式 Node Agent",
  "summary_text": "目标节点 mainland-worker-01 / 发起 web-ui/ops-center / 方式 Node Agent",
  "error_message": "",
  "created_at": "2026-04-18 06:45:00",
  "started_at": "",
  "finished_at": "",
  "updated_at": "2026-04-18 06:45:00",
  "risk_level": "medium",
  "approval_required": false,
  "approval_status": "approved",
  "approval_status_label": "已审批",
  "approved_by": "",
  "approved_at": "",
  "blocked_reason": "",
  "cancellation_reason": "",
  "dispatched_at": "",
  "target_selector": {},
  "policy": {},
  "rollout_id": 0,
  "steps_total": 0,
  "steps_running": 0,
  "steps_success": 0,
  "steps_failed": 0,
  "steps_terminal": 0,
  "steps_loaded": false,
  "focus_ref": {
    "kind": "ops_job",
    "job_id": 101,
    "job_code": "ops-20260418064500-a1b2c3",
    "action": "logs.collect",
    "target_node_code": "mainland-worker-01"
  },
  "steps": []
}

正式要求:

  • job_code
    • 是跨页面、CLI、事件流的稳定任务标识
  • action
    • 是执行语义主键,不应用 title 或自然语言代替
  • target_type
    • 定义任务作用范围,不应只靠是否传了 target_node_code 来猜
  • execution_mode
    • 是正式执行方式,不是附属备注
  • policy
    • 是创建时冻结的策略快照,不应由页面二次推导
  • status_label / approval_status_label
    • 是给页面、CLI、Codex 直接消费的人类可读标签
  • summary_text
    • 是任务摘要 contract不要求调用方再用状态和节点自己拼一句中文
  • steps_total / steps_running / steps_success / steps_failed / steps_terminal
    • 是任务步骤聚合统计,避免前端和 CLI 再自行遍历 steps 统计
  • focus_ref
    • 是统一定位对象后续页面、CLI、Codex 应优先消费它,而不是自己重组跳转参数

4. Job 状态机

4.1 当前实现中已使用的状态

当前后端已实际使用:

  • queued
  • awaiting_approval
  • blocked
  • running
  • success
  • failed
  • cancelled

这些状态已经是正式 contract 的最小闭环。

4.2 推荐终局扩展状态

终局可继续扩展为:

  • draft
  • approved
  • queued
  • dispatching
  • running
  • verifying
  • partially_succeeded
  • rollback_running
  • rolled_back
  • success
  • failed
  • cancelled
  • timed_out

但要明确区分:

  • 当前实际 API 已稳定承诺的,是最小状态集
  • 终局扩展状态不能破坏当前状态语义

5. Execution Mode Contract

当前实现里正式出现的执行方式:

  • remote-agent
  • local-runtime
  • control-plane
  • ssh

语义必须固定:

5.1 remote-agent

  • 默认正式执行方式
  • 由目标节点 Node Agent 拉取并执行

5.2 local-runtime

  • 仅用于当前节点本机即时执行
  • 主要用于本机 runtime 动作

5.3 control-plane

  • 由海外控制面直接执行
  • 适用于控制面内生动作

5.4 ssh

  • 只作为救援和过渡执行器
  • 不能成为大规模正式发布的默认方式

正式约束:

  • 发布默认优先 remote-agent
  • ssh 只应是兜底链路
  • 页面、CLI、Codex 不应再自行创造第 5 种 execution mode

6. Target Type Contract

当前 contract 至少应允许:

  • node
  • batch
  • nodes
  • rollout

语义建议固定为:

6.1 node

  • 单节点任务

6.2 batch / nodes

  • 多节点批量任务
  • 当前通常通过 /ops/jobs/batch 创建

6.3 rollout

  • 该任务属于某轮 release rollout 推进的一部分

正式要求:

  • 如果任务来自 rollout必须能通过 rollout_id 回溯
  • 如果任务来自 playbook应通过 metadata 标出 run 归属

7. Job Step Contract

来源:

  • GET /api/v1/ops/jobs/{job_id}

最小字段:

{
  "id": 201,
  "step_key": "dispatch",
  "title": "调度动作",
  "node_code": "mainland-worker-01",
  "status": "queued",
  "stdout": "",
  "stderr": "",
  "result": {},
  "created_at": "2026-04-18 06:45:00",
  "started_at": "",
  "finished_at": "",
  "updated_at": "2026-04-18 06:45:00"
}

当前实现说明:

  • 现阶段每条 ops job 至少会先生成一个 dispatch step
  • 以后如果引入真正 DAG / 多步骤执行,也应继续沿用 ops_job_steps

正式约束:

  • step_key
    • 是步骤主键,不应用标题代替
  • status
    • 必须与 job 状态联动,但不要求完全相同
  • stdout / stderr
    • 是步骤级输出,不应与整条 job 的 result 混成一层

8. Job Event Contract

来源:

  • GET /api/v1/ops/jobs/{job_id}/events

最小字段建议:

{
  "id": 301,
  "event_key": "job-event:301",
  "job_id": 101,
  "step_id": 201,
  "node_code": "mainland-worker-01",
  "client_event_id": "evt-001",
  "event_type": "job_dispatch_requested",
  "level": "info",
  "level_label": "信息",
  "message": "任务等待 node-agent 拉取: 101",
  "summary": "任务等待 node-agent 拉取: 101",
  "payload": {},
  "has_payload": false,
  "occurred_at": "2026-04-18 06:45:03",
  "created_at": "2026-04-18 06:45:03"
}

当前代码里事件来源包括:

  • job created
  • job blocked
  • job awaiting approval
  • job approved
  • job cancelled
  • job dispatch requested
  • job executed locally / over ssh / on control plane
  • agent start / complete / custom events

正式要求:

  • events 是任务时间线,不是调试附属品
  • 页面、CLI、Codex 都应从这里读“任务做到哪一步了”
  • 不应重新从 stdout/stderr 推断关键状态

补充约束:

  • event_key
    • 是事件行的稳定前端 / CLI 标识
  • summary
    • 是给页面、CLI、Codex 直接消费的短摘要,不要求调用方再拼接 message
  • level_label
    • 是面向人看的等级标签
  • occurred_at
    • 是统一观察面时间字段,允许与 created_at 同值

8.1 Event List Summary Contract

当前 GET /api/v1/ops/jobs/{job_id}/events 建议同时返回:

{
  "summary": {
    "job_id": 101,
    "returned_total": 12,
    "level_counts": {
      "info": 8,
      "warning": 3,
      "error": 1
    },
    "event_type_counts": {
      "job_created": 1,
      "job_dispatch_requested": 1,
      "executor_received": 1
    },
    "node_counts": {
      "mainland-worker-01": 12
    },
    "latest_at": "2026-04-18 06:45:08",
    "filters": {
      "limit": 50
    }
  }
}

正式要求:

  • 事件接口不只给一串数组
  • 至少要给:
    • 当前返回了多少条
    • 等级分布
    • 事件类型分布
    • 最近时间
  • 这样页面、CLI、Codex 才能先判断“有没有异常事件”再决定是否展开明细

9. List Contract 与 Compact 模式

来源:

  • GET /api/v1/ops/jobs

当前实现特点:

  • 默认 list_ops_jobs(..., compact=True)
  • 返回 compact job
  • steps 被置空
  • payload / metadata / result 会被压缩

因此建议把列表 contract 明确成:

{
  "jobs": [
    {
      "id": 101,
      "job_code": "ops-20260418064500-a1b2c3",
      "action": "logs.collect",
      "status": "queued",
      "execution_mode": "remote-agent",
      "requested_by": "web-ui/ops-center",
      "payload": {
        "tail_lines": 120
      },
      "metadata": {},
      "result": {},
      "result_summary": {},
      "is_compact": true,
      "steps": []
    }
  ]
}

正式要求:

  • 列表页默认只承诺 compact 视图
  • 详情页再承诺 full job + steps
  • 页面不应假设列表结果天然带完整 steps

10. Create Contract

来源:

  • POST /api/v1/ops/jobs

最小请求体:

{
  "action": "logs.collect",
  "target_type": "node",
  "target_node_code": "mainland-worker-01",
  "execution_mode": "remote-agent",
  "requested_by": "web-ui/ops-center",
  "payload": {
    "tail_lines": 120
  },
  "metadata": {
    "source": "ops-center"
  },
  "auto_approve": false,
  "run_now": true
}

当前实现还支持:

  • target_selector
  • rollout_id

最小返回体:

{
  "job": {},
  "executed_immediately": false
}

关键语义:

  • executed_immediately=true
    • 说明任务已被控制面即时执行,而不是停留在队列
  • executed_immediately=false
    • 说明任务进入了:
      • queued
      • awaiting_approval
      • blocked

正式要求:

  • 创建接口不能只回答“建成功了”
  • 必须明确回答:
    • 当前状态是什么
    • 是否立即执行
    • 如果没执行,是在等审批、等 agent、还是被阻断

11. Batch Create Contract

来源:

  • POST /api/v1/ops/jobs/batch

最小请求体建议:

{
  "template_key": "diagnostics.collect",
  "target_node_codes": [
    "mainland-worker-01",
    "mainland-worker-02"
  ],
  "execution_mode": "remote-agent",
  "requested_by": "web-ui/ops-center",
  "payload": {},
  "metadata": {},
  "auto_approve": true
}

最小返回体建议:

{
  "jobs": [],
  "results": []
}

正式要求:

  • batch 只是创建方式,不是新的执行对象
  • batch 最终仍要展开成多条 ops job
  • 单条失败不能让结果完全丢失,必须保留逐项结果

12. Policy Preview Contract

来源:

  • POST /api/v1/ops/policy/preview
  • ops_jobs.policy

这层是创建前的正式门禁对象。

当前创建任务时,后端已先做:

  • 风险等级判断
  • 阻断判断
  • 审批要求判断

建议最小返回字段:

{
  "blocked": false,
  "blocking_reasons": [],
  "approval_required": true,
  "approval_reasons": [
    "当前目标包含 control 节点"
  ],
  "warnings": [],
  "recommendations": [],
  "risk_level": "high",
  "target_summary": {},
  "batch_plan": {},
  "cluster_guardrails": {},
  "operational_readiness": {}
}

正式要求:

  • 页面、CLI、Codex 都应复用同一份 policy preview
  • 不能各自再写一套“能不能执行”的判断逻辑

13. Approval / Cancel / Dispatch Contract

13.1 Approve

来源:

  • POST /api/v1/ops/jobs/{job_id}/approve

最小请求体:

{
  "approved_by": "admin"
}

语义:

  • awaiting_approval 可审批
  • 审批后回到 queued

13.2 Cancel

来源:

  • POST /api/v1/ops/jobs/{job_id}/cancel

最小请求体:

{
  "cancelled_by": "admin",
  "reason": "本轮灰度暂停"
}

语义:

  • success / failed / cancelled 不可取消
  • 取消后 job 进入 cancelled
  • steps 同步进入 cancelled 或保持终态

13.3 Dispatch

来源:

  • POST /api/v1/ops/jobs/{job_id}/dispatch

语义:

  • queued 才可派发
  • control-plane
    • 立即由控制面执行
  • local-runtime
    • 当前节点本机即时执行
  • ssh
    • 由 SSH 执行器执行
  • remote-agent
    • 保持 queued,等待对应 agent 拉取

这点必须明确:

dispatch 不等于一定会立刻变成 running。

对于 remote-agent,它可能只是:

  • 写入事件
  • 保持排队
  • 等节点稍后 pull

14. 与 Playbook / Rollout / Agent 的边界

14.1 与 Playbook

  • playbook run
    • 负责展开、聚合、重跑、取消整轮编排
  • ops job
    • 负责单条执行记录

14.2 与 Rollout

  • rollout
    • 负责发布批次推进
  • ops job
    • 负责每个节点每步的实际任务

14.3 与 Node Agent

  • 控制面创建 ops job
  • agent 拉取 ops job
  • agent 回写 ops_job_events
  • agent 推进 job / step 状态

14.4 与 Observability

  • ops_observability_contract
    • 看现场事实
  • ops_job_contract
    • 看执行动作本身

这两层不能混:

  • 观察面告诉你“谁在跑、谁异常”
  • 任务面告诉你“这一条任务如何被创建、审批、派发、执行、取消”

15. 终局约束

后续不应再回退到下面几种模式:

1. 页面点按钮直接跑 shell

错误。

正确方式:

  • 页面创建 ops job

2. Codex 直接 SSH 改现场

错误。

正确方式:

  • Codex 创建 ops job / playbook run

3. 发布系统不经过任务层

错误。

正确方式:

  • rollout 批量生成 ops job

4. 死信治理不留痕

错误。

正确方式:

  • flush / replay / discard 都生成正式 ops job

所以可以把这份文档看成整套系统的“执行订单母本”。

后续只要这份 contract 稳住页面、CLI、Codex、Agent、Release、Playbook 就都能围绕同一个中心对象协作。