任务:作业派发连接器:只读核对、至多一次提交、回执、结果、冻结与对账

依据:docs/hub-r4-device-run.md(上真实台架的设计,第 1–5 节和第 8 节的分工),决定 26、27(docs/hub-r4-progress.md),理论文档第 6、7 节;模型用 node tools/show-element.ts D02 D03 D04 D07 B05 B06 B07 V03 W05 P05 读。

  • 分支和工作树:hub-job-dispatch,目录 circle-hub-hub-job-dispatch(依赖已装好)
  • 可以改的路径:hub/dispatch/**(新包);hub/server/cli-job.ts(新);hub/server/cli-admin.ts 命令表加 executor 和 job 两行;hub/server/assemble/ 里装配 dispatch 的几行。只经 api 使用 hub/records、hub/leases 和密钥库,不改它们;不碰 contracts(executor@1 的形状先放在 hub/dispatch 里,README 记为偏离)。
  • 规则:同前(不推送、不建 PR、不发布、不部署;不碰 7420、钥匙串、环境配置文件和任何真实系统;测试只用合成数据,地址只用 192.0.2.0/24 和 2001:db8::/32;git commit -s)。所有者的规矩:未知只能是过渡状态,写明缺的事实和下一步动作,流程自动走这一步;Hub 只核验,不生成内容、不调用模型;枚举只来自 contracts/generated(运行时用按包生成的快照)。
  • 依赖:无,可以马上开始

要做的

  1. 执行器登记(D07):circle-hub executor add update list disable;密钥经标准输入存进密钥库(executor..bearer),不回显。(hub-variables 第一步以后的做法,这一件照现在的写:执行器定义的 `credential` 写 `${secret:名字}`,钥匙用 `circle-hub secret set --name` 写;`executor add` 不读标准输入,`update --secret` 和 `setSecret` 去掉;没有 `credential` 的旧定义 precheck 和派发都拒绝,`executor update` 加上 `credential` 时,`executor..bearer` 里的钥匙迁移过去;定义里只有期望值能写成 `${var:…}`;作业钉住解析后的定义。)定义:台架名;端点(https,明文只许回环);固定 CA;每类调用的超时(提交默认 300 秒);生命周期各角色的工具名和参数名(取得、心跳、释放租约,只读核对,提交,状态,结果);回执、状态、结果的 JSON 指针;对方状态词和结果词到词表码的映射(只许 action_result、acceptance、effect_outcome、dynamic_terminal 的生成值,没映射的词一律未知);只读核对的条件(JSON 指针等于、不小于、摘要等于,值可引用运行的构建和座位数);允许调用的工具清单;最长运行时间。定义存成规范 JSON,按摘要经 resource_config 授权生效,照 connectors 的资源做法。
  2. 客户端:MCP JSON-RPC over HTTPS。每个作业先 tools/list,与允许清单逐个比对,缺了就停,多出来的从不调用;固定 CA,不跟重定向,有字节上限;密钥只在请求头,日志和错误都过脱敏;操作者头写作业号。
  3. 状态机:每个阶段一条 action@1(读取用 read,提交用 dispatch,对账回读用 read_back),原始回答先存成源文件再解释(B05 R[2]);B06 的意图键、请求 id、动作摘要、目标键、效果类、重试安全类和结局写在 detail,取值来自词表生成物。次序:前提(核验运行全部必需要求项满足,取出 S、台架、构建、用例号和 plan_run,plan_run 必须等于核验运行自己的 predecessor;血缘持有台架上每台设备的租约,到期晚于现在加最长运行时间;目标键没有冻结)→ 网关租约(心跳不超过期限的三分之一)→ 只读核对 → 意图写盘 → 提交一次(不随进程退出中止,绝不重试)→ 回执 → 状态 → 结果 → 释放。每种情形的记法照设计第 4 节的表。
  4. 冻结由没有处置的未知派发推出,不另开表;同一目标键的新派发一律拒绝。重启恢复:有意图没回执的记未知;已受理没终态的接着读状态;从不重发。
  5. 处置(B07,这一轮用核对、保持未知、标记失败):job dispose <作业> --reconcile 读状态和结果,摘要等于 S 且投递时刻在派发窗口内才算对上,然后接着走状态和结果;--keep-unknown;--mark-failed --reason。只记选择和选择人,原结局不改,冻结由处置解除;“查无记录”从不当作没执行。
  6. 结果:逐用例给动态层终态,带“目标对本次派发的报告”和“未排除本地操作”;结果通道不完整的用例记无判定;结果存成运行用户和管理员可读的源文件,命令打印它的引用。
  7. 命令:job precheck –executor [--booking <运行>] [--save <暂存目录>](只做网关租约、只读核对和释放);job dispatch --run <核验运行> --executor ;job status|resume <作业>。输出只有阶段、码、摘要和计数,不打印对方回答的原文。
  8. README:与 D02、D03、D04、D07、B05、B06、B07 的对应;偏离(B06、B07 先写在 action@1 的 detail;设备凭据在网关;网关收字节);Needs(effect@1 和 request@1 进 contracts;submit_result 受理后触发派发;作业移到服务进程里跑)。

必须有的测试

  • 回环网关桩(工具名和字段都是编的,记录每次调用),临时 PostgreSQL,固定时钟;全部合成数据。
  • 正常路径:租约、核对、意图、提交、受理、进行中两次、完成、结果、释放;记录的次序和内容;原始回答先于解释入库(包装存储断言)。
  • 网关租约忙:未发出,没有意图,桩没收到提交。只读核对不过(构建不符、设备数不够、拓扑摘要不符):未发出,网关租约已释放,没有意图。
  • 意图写盘失败(注入):桩收到 0 次提交。
  • 提交超时、请求发出后断开、提交途中进程退出再重启:未知、冻结;桩只收到 1 次提交,重启后不重发。
  • 对方拒绝(ok:false):未知、冻结,拒绝原文存下。受理但摘要不符或用例号不符:未知、内容不符。
  • 状态 lost 或不认识的词:未知,不重发。结果不完整、某个用例为空、结果摘要不符:对应用例无判定,或整份不采信。
  • 冻结:同一目标键再派发被拒;处置以后放开;对账给的 task_id 摘要不符被拒;“查无记录”不解除冻结。
  • 租约:血缘不持有、到期太早、派发后围栏令牌变了(管理员强制释放),分别拒绝或记漂移。
  • tools/list 缺工具就停;桩多出来的管理工具从不被调用。心跳按期限的三分之一发;心跳失败记“网关租约中断”,照常读状态。
  • 密钥不出现在任何日志、记录、源文件和命令输出里(扫描全部产出);npx tsc –noEmit;全仓词表扫描零命中,不加例外。

完成后在终端里回报:提交号、测试数量、没做完或拿不准的地方。真实台架上的任何动作都由集成者做,并且要等所有者答复设计第 6 节的问题。