上真实台架:从用例脑图到台架上的结果
状态(2026-10-23 晚,设计稿;第 6 节的 12 问已由决定 35 答复)。依据:决定 26(生成在客户端,Hub 只核验)、决定 27(用所有者指定的 SyntheticReference 测试环境,先只读核对,再执行,不重启)、决定 35 的“上真实台架”一组;理论文档第 6 节“执行不越权或重复”、第 7 节“持久执行”;模型 node tools/show-element.ts D02 D03 D04 D07 B05 B06 B07 V03 W05 P05。实现的任务见第 8 节。真实台架上的动作只由集成者做。
原则
- Circle 用任务包带的确定性 TypeScript 脚本在本地生成工作簿并上传。Hub 回读解析、逐项核对,再用同一个生成器从槽位重算比对,记下摘要 S;只有 S 那份字节会被派发。Hub 不生成内容,不调用模型。
- 运行器和网关的具体内容(对象名、方法名、表头、标记、工具名、状态词)只在部署数据里;地址、凭据、用例内容不进 Git。
- 派发、受理、结果、对账分开记录。回执丢了就是未知,只能对账,不能重发。
- 封闭集合只有三个来源:词表生成物(Hub 的概念);发布的合约和运行环境配置(运行器的对象、方法、签名和本轮允许的组合);包的格式表(外部格式自己的取值,不认识就整份拒绝)。不登记临时基线,不加例外表;新代码让词表扫描有一处命中就失败。
1. 端到端路径
指定脑图里 auto 是 YES 的 5 个用例全编全上(第 1 问)。用例一次一个:前一个的作业有了终态,才开下一个;前一个留下未知(目标冻结)就停,先对账,后面的不派发。
| 步 | 谁 | 做什么 | 留下什么 |
|---|---|---|---|
| 0 | 集成者(管理员) | 登记网关执行器,密钥经标准输入进密钥库(hub-variables 第一步部署以后:定义里写 credential 引用 GATEWAY_RUN_KEY,钥匙经标准输入交给 circle-hub secret set --name GATEWAY_RUN_KEY;先登记,后交);把 case-compile、test-bed 分配给所有者的 Circle 用户;把包里的客户端脚本装进上机用的 Circle 主目录(集成者另备,钩子放行技能工具和这些脚本),按 pack.json 的摘要核对;device-run.ts 核对执行器定义的摘要 |
执行器定义 |
| 1 | 集成者 | circle-hub job precheck:取得网关租约,环境核对(构建、设备数、可达、规则),读缓存拓扑(不刷新),逐台只读探测,释放 |
原始回答存成观察,另存一份到暂存目录 |
| 2 | 集成者 | 第 2 节的转换;circle-hub doc put 发布三份文档:bed-topology、runner-contract、runtime-profile |
三份命名文档 |
| 3 | Circle | 每个用例开头:test-bed acquire 占住整个台架(清单里的每台设备),不写时长 |
Hub 租约,由这个用例的谱系持有 |
| 4 | Circle(模型) | case-compile start_run(用例、三份文档;参数 autoid;前驱写占台架的那次运行),按受限结构填槽,submit_result,按修正说明修到计划的要求项全部满足 |
计划运行 |
| 5 | Circle(脚本) | 从满足的槽位、合约身份、配置和条目字段生成工作簿,upload_file;以第 4 步为前驱再开一次运行,多绑定工作簿,原样再交一次槽位 |
src:S |
| 6 | Hub(包的检查) | 第 3 节的全部检查,含重算比对 | 全部必需要求项满足才可派发 |
| 7 | 集成者触发,Hub 连接器执行 | device-run.ts 停下等集成者输入这个用例的核验运行号,再跑 circle-hub job dispatch,按第 4 节;派发前在同一个网关租约里再做一次第 1 步的核对 |
每个阶段一条动作记录 |
| 8 | Hub | 逐用例结论(带限定)存成运行用户可读的源文件;Circle 用 get_nodes 按引用读,集成者报告所有者 |
结果 |
| 9 | Circle、集成者 | 作业有终态以后,Circle 经 test-bed release 释放台架;有未知时,集成者从目标自己的审计只读取证,circle-hub job dispose |
释放、处置记录 |
- 第 4、5 步分成两次运行:运行的输入在开始时冻结,修复会改槽位,工作簿必须从最后一版槽位生成。第二次运行没有判断,由脚本完成。第一次运行里工作簿那一项记过渡的未知(缺输入),写明下一步是生成并上传工作簿、再开核验运行;它不能派发。
device-run.ts只跑 Hub 的管理命令,不写任何槽位内容,也不调 MCP 工具;第 4 到 6 步由 Circle 会话做。- 第一个用例跑完先停:集成者据它的结果认证“结果按派发身份归属”(
gateway-request-ids.md第 12 条),更新执行器定义、重做第 0 步的摘要核对以后,再跑后四个。
2. 从 SyntheticReference 那套环境取什么、怎么转
决定 27 只放开“读这套环境的接入方式和这个用例,作为部署数据用”。转换脚本由集成者在本机对只读副本跑,只打印计数和摘要,输出写到暂存目录,经 doc put 进 Hub,不进 Git,不进日志。
- 函数合约:不换格式,原样发布运行器认的那一份。 转成包自己的合约形状会丢掉身份、禁用原因、派发方式和仅位置参数;参数类型、地址角色、命令标志原合约里本来没有,补上是判断,不是转换。脚本核两件事,包在每次运行时再核一遍:
- 形状:schema 和 complete;对象唯一、(对象,方法)唯一;禁用对象下没有启用条目;状态、派发方式、参数种类只认格式表里的取值,不认识就整份拒绝。
- 身份:按运行器的规则重算合约摘要(键排序、紧凑分隔、UTF-8 不转义、去掉摘要本身),等于声明值,也等于配置里的期望值;有非整数的数字就整份拒绝。镜像目录里旁挂的那份旧副本身份不同,会被拒。
- 台架清单:从网关缓存的拓扑机械转换(第 1 步存下的回答;不刷新,不用 SyntheticReference 目录里的旧文件)。
- 名字转小写,撞名拒绝;类型标签映射到 test-bed-manifest@1 的五类,不认识的拒绝;只留“地址/前缀”,没有前缀或掩码是假定的条目拒绝(或按显式参数列出后丢弃)。
- 服务取拓扑回答里的
services,协议照一张对照表换:http、https记tcp,dns记udp;表外的协议整份停下,写明是哪个服务、哪个字段。 - 在用的地址就是拓扑里每台主机和设备的接口地址。保留段取拓扑观察里的未登记邻居(
observation.unregistered_neighbours,形状{<跳板机接口>: [<IPv4 文字>…]},只来自 ARP,只有 IPv4,是采集那一刻的快照),每个写成一个单地址的保留段;不在本台架任何设备接口网段里的邻居不是台架地址,不进保留段。拓扑里没有的不猜、不编。 - test-bed 和 case-compile 两个包的清单解析都要通过。
- 执行器的只读核对比的是声明的 JSON 指针下内容的规范摘要,指针同时覆盖拓扑、观察和服务,派发前比对;保留段和服务变了也能发现。
- 运行环境配置(新文档,形状 runtime-profile@1,由 case-compile 包定义):期望身份(运行时版本、合约摘要:取网关钉住的那份);工作簿格式(执行表名、九列表头、身份标记、范围名、收尾行、用例号形状);座位(运行器的设备对象 ↔ 清单里的设备 ↔ 网关配置里的序号);主机方法 ↔ 清单里的设备;允许的(对象,方法)和参数角色(设备命令行、主机 shell、字面断言、等待);设备和主机上拒绝的命令。
- 合约启用的(对象,方法)全开,只要每个参数都能落到这四种角色之一;有参数落不进的不开,单列成“未开”,写明缺的事实和下一步。允许的必须是启用组合的子集,启用的每一个要么允许、要么未开,不重不漏。
- 派发方式能配哪些角色照决定 40:转发命令文字的只配设备命令行和主机 shell;检查点只配字面断言;内建的等待只配等待;直接调组合方法的,方法本身和零参数的方法一样,由集成者只读看过以后逐个显式开;动作注册表和三参数检查点这一轮不开。
- 集成者按只读读到的运行器行为和第 1 步的回答起草,
--check用 case-compile 的配置解析器核,发布不再等所有者确认(第 2 问)。
- 逐台只读探测(第 6 问):只在执行器定义里声明,由
hub/dispatch发。每个座位两项:version、interfaces,各是一条命令原文加一条只有一个命名分组的正则;网关收的命令形状也是执行器定义里的字段。version取出的值要等于锁定构建的某个名字(基础构建名或知识库记的别名),interfaces取出的地址集合要等于清单里这台设备的地址集合;不符就“未发出”,写明哪台哪一项。 - 用例:所有者指定的脑图和 autoid。敏捷平台上有文档号就绑
agile:mindmap:<id>,由 Hub 自己读原件;只有本地文件时,Circle 脚本做公开的规整(去掉文件头的两个 U+FFFF,单元素数组改成{root}),上传名改成中性的,原文件摘要写进上传说明,结论带“客户端规整”。用例内容会发给 Circle 所选模型的服务商(决定 7、27)。 - 网关接入:https 端点、固定的实验室 CA、只有运行权限的那把密钥,登记成执行器(第 11 问)。集成者 10-23 晚只读查过:环境配置、Hub 持久部署的密钥库、所有者 Circle 的密钥和 MCP 配置里都没有这把密钥,所以上台架前由所有者经标准输入交给 Hub(hub-variables 第一步部署以后:定义里写
credential引用GATEWAY_RUN_KEY,钥匙经标准输入交给circle-hub secret set --name GATEWAY_RUN_KEY;先登记,后交)。执行器定义里期望的清单摘要可以写成${var:BED_MANIFEST_SHA256},重发台架清单以后执行var set,不用executor update(第 44 项)。不读 SyntheticReference 的 environment 文件,不走 SSH(那条路不核主机密钥,密码是明文)。 - 不取:SyntheticReference 的代码、模板字节、凭据;不把网关代码里钉住的合约摘要抄进 Hub 的代码。
3. Hub 对上传工作簿的核验
两层读取:核心加一个通用的 xlsx@1 解析器(单元格、定义名、隐藏行、公式、最后一行,没有运行器的字);包里的读取器按运行器的写法解释单元格。读取器和生成器是两份独立的代码(P05)。
- 文件:zip 上限(字节、成员数、解压量和解压比、路径、加密);只认生成器写的那个子集:一张表,没有隐藏的表和行,内联字符串,没有公式,没有以
=开头的值。凭据扫描命中的工作簿只保留,这一轮不做受理,所以绑定不上,也不会派发。 - 身份:恰好一行身份标记,在表头上方;运行时版本和合约摘要等于发布合约重算出的值和配置的期望值;认证请求那两格必须空(工作簿不能给禁用条目授权)。
- 结构:恰好一个九列表头;范围名是工作簿级的,正好指向表头那一行;表名照配置。
- 每一行:表头以下凡是 E–I 有值的都是执行行,说明行和收尾行同样要查。(E,F)在合约启用集合里,也在本轮允许里;G 按运行器的解析规则读回来,与槽位逐个相等,个数、关键字、必填、仅位置参数与签名一致;单行,没有
{}、{0},首尾没有空白;H、I 本轮为空。 - 命令:设备命令行要在锁定构建的命令目录里解析得出(catalog@1),并且不在配置的设备拒绝表里;主机 shell 不在主机拒绝表里(重启、关机、断电、切运行级、格式化、整盘写、删根目录这一类)。
- 地址(第 5 问):每个字面地址都在台架上;设备命令行里可以出现本台架其他设备的地址(例如路由下一跳),不许出现保留段里的地址;用例原文里的台架外地址只能经槽位里的替换表换成台架地址,一一对应,替换表写进结论给人看。
- 用例:只有一个用例;autoid 是文本,等于条目的 autoid,也符合网关认的位数;autoid 那一行 E 不空(否则运行器整例静默跳过);每条期望一行字面断言,紧跟一个返回文本的步骤(等待不返回文本);断言内容是期望引文套用替换表后的片段;至少一条断言(运行器要至少一次成功才判通过);收尾行是最后一个有单元格的行。
- 座位:用到的座位序号小于网关报告的设备数(派发前再核一次)。
- 重算:用本运行的包里同一个生成器,从本次提交的槽位、合约身份、配置和条目字段重算,字节必须完全相同。支持结论写出 S 和生成器版本,不带工作簿内容。
结论都是事实或未知,都写“不证明在真实设备上跑得通”;配置声明是合成的时另写“按合成合约核对”。
4. 派发协议
连接器是新的核心包 hub/dispatch(模型 D07 的执行器资源):通用的作业生命周期;网关的工具名、字段位置和对方的状态词写在执行器定义里,映射到词表的码,核心代码里没有网关或产品的字。效果类“作业”,重试安全类“不安全”。网关改过以后(第 7、8 问,gateway-request-ids.md)上机前先记请求 id、按它去重并提供只读查询,拒绝时写明阶段和是否已上机;丢了回执就按请求记录核对,不重发。每个阶段一条 B05 动作记录,原始回答先存成源文件再解释。
- 前提:核验运行全部必需要求项满足;本血缘持有台架上每台设备的租约,还没释放(租约没有时长限额,第 15 项;有到期时刻的,到期晚于“现在 + 最长运行时间”);目标键没有冻结。
- 网关租约:取得(操作者写作业号),心跳间隔不超过期限的三分之一,整个作业期间持有。
- 只读核对(读,可重试,从不记未知):构建等于锁定构建,设备数够,设备可达,危险命令规则和凭据字面量都在;缓存拓扑、观察和服务的摘要等于清单的来源;逐台探测的版本和接口地址相符(第 2 节)。不满足就记“未发出”,写明哪一项,释放网关租约。
- 意图:先写盘再调用。意图键 K = 哈希(运行,条目,S,Hub 围栏令牌,网关租约号和令牌);记下动作摘要和目标键。写不进去就不调用。
- 提交:带 S 的字节和请求 id 调用一次
case_submit;超时长于网关提交前检查的最坏耗时;进程退出不中止这次调用;不盲目重发,丢了回执先按请求 id 查网关的请求记录。 - 回执:成功、有 task_id、回执摘要等于 S、用例号等于核验得出的集合,才记“已受理”,存下远端 id。
- 状态(读,可重试):进行中就等;终态记退出码。
- 结果(读):task_id 和摘要都对上才采信;逐用例按映射给出结论;日志和会话原文只存不解释。
- 释放:有终态之后才释放网关租约;台架租约由这个用例的谱系在作业有终态以后释放,下一个用例再占。
| 情形 | 记成 | 之后 |
|---|---|---|
| 网关租约取不到 | 未发出 | 可以再试(没碰设备) |
| 只读核对不过 | 未发出,写明原因 | 释放网关租约 |
| 意图没写进去 | 不调用 | — |
| 提交超时、连接在发出后断开、进程在提交中退出 | 先按请求 id 查请求记录:查到就照它接着走;查不到、又证明不了没执行,才记未知 | 冻结,不重发,等对账 |
| 网关拒绝,写明没上机(D04 R[4]) | 目标拒绝,写明阶段 | 不冻结,照拒绝原因改了再派发 |
| 网关拒绝,已上机或阶段不明 | 未知,拒绝原文存下 | 冻结,管理员对账 |
| 受理了,但摘要或用例号对不上 | 未知,内容不符 | 冻结,管理员处置 |
| 状态是 lost 或不认识的词 | 未知 | 不照网关的提示重交 |
| 终态,但结果不完整 | 缺的用例记无判定 | 照实报告 |
| 结果里的摘要不是 S | 整份不采信,未知 | 管理员处置 |
| 心跳失败 | 记“网关租约中断” | 照常读状态,不重发 |
| 进程重启 | 有意图没回执的记未知;已受理没终态的接着读状态 | 不重发 |
- 冻结由“没有处置的未知派发”推出,不另开表;经 Hub 对冻结目标键的派发一律拒绝。
- 对账(B07 的处置):管理员从网关自己的审计里找出这次提交的 task_id(只读),
circle-hub job dispose --reconcile <task_id>;Hub 用它读状态和结果,结果的摘要等于 S、投递时刻在派发窗口内才算对上,然后接着走第 7、8 步。“查无记录 = 没执行”只在网关先记请求 id、拒收过期请求、按记录作答时成立(D04 R[6]):同一份请求记录(journal_id 和派发前读到的一样)、同一个主体、网关时间晚于原来的期限、期限又晚于记录的回收下限,才算证明没执行;否则只能“保持未知”或“标记失败”,原结局不改,冻结由管理员的处置解除。 - 结论的写法(D02):每个用例的判决作为“目标对本次派发的报告”交付;“结果按派发身份归属”认证之前,动态层记无判定,认证以后照逐用例的结果给通过和失败;所有观察带“未排除本地操作”,不通知别人(第 10 问)。派发是服务账号代表发起用户(D03 F[8])。运行器在 Hub 之外的动作照常接受,作为观察记进结果(第 9 问)。
5. 安全限制
- 合约禁用的组合(重启、升级、设备级生命周期方法、不存在的座位、临时夹具)照拒。合约启用的组合全开,只要每个参数都能落到四种角色之一(第 4 问);落不进的不开,单列成“未开”。拒绝表照旧:设备上拒绝重启、清配置、升级、HA 这类整机命令,主机上拒绝重启、关机、格式化这类命令。认证请求永不经工作簿。
- 不重启:合约禁用,拒绝表拦截,网关没有重启工具;清空并重建基线的管理工具 Hub 永不调用,Hub 只持有运行权限的密钥;拓扑不刷新(刷新会登录每台主机)。
- 先只读核对:第 1 步一次,派发前在同一个网关租约里再一次,都含逐台探测。
- 一份工作簿一个用例:一份工作簿只有一个用例和收尾行;次数只来自冻结的重复策略和效果表的次数预算;case-compile 派发那一行写了自己的次数的,以写的为准。五个用例一次一个。
- 占住台架:Hub 租约(台架上每台设备,由这个用例的谱系持有,没有时长限额)加网关租约(整个作业)。网关租约的期限比运行的最长时间短,必须心跳。
- 运行器在 Hub 之外做的事,Hub 看不见也拦不住,结果里照写:预检之前就会登录前两台设备、写结果库;用到的每台被测设备先下一遍初始化配置;管理口 ping 不通时,框架会经控制台清掉全部配置;最后一个用例跑完不清理,配置留在设备上;运行器读的是跳板机上实时的框架文件和合约,核验之后有人改了,Hub 发现不了(网关不报告合约摘要)。
- 网关的危险命令表和框架凭据字面量 Hub 看不见:漏掉的会被网关拒,按未知处理。
- 本地入口:SyntheticReference 自己的 SSH 入口和人工登录不受这两层租约约束。
- 数据:工作簿、回执、日志、会话(文件名里有设备管理地址)都是部署数据,只在 Hub 记录里(运行用户和管理员可读),不进 Git,不发外部服务;测试和样例只用 192.0.2.0/24、2001:db8::/32 和编的名字。
6. 所有者的答复(决定 35)
原来的 12 问已由决定 35 的“上真实台架”一组答复,全文在 docs/hub-r4-progress.md;各条落到上面哪里:
- 指定脑图里 5 个 auto:YES 的用例全编全上,有文档号就按号绑定:第 1 节。
- 合约、台架清单、运行环境配置都算接入方式,由转换脚本生成、管理员发布:第 1 节第 2 步、第 2 节。
- 合约身份取网关钉住的那份:第 2 节。
- 合约启用的(对象,方法)全开,拒绝表照旧:第 2 节、第 5 节。
- 同台架设备的地址可以出现在设备命令行里,在用和保留的地址段从网关拓扑只读取得:第 2 节、第 3 节第 6 条。
- 逐台只读探测版本和接口地址:第 2 节、第 4 节第 3 步。
7、8. 改网关:请求 id、去重和只读查询、拒绝带阶段;结果按派发身份归属并认证:第 4 节,
gateway-request-ids.md。 - 运行器在 Hub 之外的动作照常接受,作为观察记进结果:第 4 节。
- 不通知别人,结论带“未排除本地操作”:第 4 节。
- TLS 加固定的实验室 CA,沿用只有运行权限的那把密钥;集成者没找到,由所有者经标准输入给:第 2 节。
- 这一轮由集成者把脚本放进上机用的 Circle 主目录并核对摘要,Hub 的通用下发也做(
script-distribution.md):第 1 节第 0 步。
7. 与模型的对应和这一轮的偏离
- 对应:D03(作业、不安全、超时不当未执行);D04(一个物理目标一个租约、至多一次、丢回执冻结、只认绑定原操作的回读);B05(每个阶段一条动作记录,原文先存);D02(前提默认为假);V03(动态层);W05、P05(受限结构;生成器和读取器分开)。
- 偏离:
- B06 效果记录和 B07 处置:早期写在 action@1 的 detail 里,现已切换为 effect@1 和请求记录(
hub/dispatch、hub/requests);切换前的旧记录不改。 - D07 R[4]:设备凭据在网关的框架配置里,不由 Hub 按租约注入;网关收的是字节而不是引用,回执摘要必须等于 S,效果上等同冻结的引用。
- 派发由管理员命令触发,模型定的“submit_result 受理后由 Hub 派发”等引擎的主人接线;作业在命令行进程里跑,进程停了可以接着读状态,提交中途停了就是未知。
- 动态层结论写在派发的结果里,不写回运行的条目(运行状态归引擎)。
- B06 效果记录和 B07 处置:早期写在 action@1 的 detail 里,现已切换为 effect@1 和请求记录(
8. 任务
状态(2026-10-24):任务 1–6 已落地(hub/dispatch、hub/refs/parsers/xlsx.ts、hub/server/cli-job.ts 已在主线;编译包版本以 packs/workflow/case-compile/pack.json 为准);各项的最终状态以进度页为准,下表保留当时的计划原文。
| 次序 | 任务 | 分支 | 范围 | 依赖 |
|---|---|---|---|---|
| 1 | 编译包 2.0:按运行器合约和运行环境配置核对计划 | hub-case-runtime |
packs/workflow/case-compile/** |
— |
| 2 | 客户端生成工作簿,Hub 回读核验 | hub-case-workbook |
packs/workflow/case-compile/**;hub/refs/parsers/xlsx.ts(新)和注册;hub/server/upload.ts 的扩展名表 |
1 |
| 3 | 作业派发连接器 | hub-job-dispatch |
hub/dispatch/**(新);hub/server/cli-job.ts(新);hub/server/cli-admin.ts 命令表和装配的几行 |
无,可与 1 并行 |
| 4 | 转换脚本、合成全链路验收、真机运行脚本 | hub-device-run |
scripts/real/convert/**;scripts/real/accept/device-run.ts;tests/acceptance/ 的一个新文件 |
转换脚本在 1 之后;验收在 2、3 之后 |
| 5 | 网关:请求 id、去重和只读查询、拒绝带阶段、结果按投递归属 | 网关仓库 request-ids |
网关仓库,gateway-request-ids.md 第一步 |
— |
| 6 | Hub 一侧:按请求 id 核对、前提认证、逐台探测、结果归属 | dispatch-gateway |
hub/dispatch/**、hub/server/cli-job.ts |
3、5 |
| 7 | 上机和技能用的 Circle 主目录 | circle-home-skills |
scripts/real/circle-home.ts |
— |
| 8 | 效果类和写声明、效果前边界 | d03-p11-boundary |
见那一页 | 3、6 等 |
| 9 | 重复策略 | v07-repetition |
见那一页 | 6、8 等 |
- 2 和 3 之间的接口:核验运行里工作簿那一项的支持结论,
data写{sha256, autoids, bed, build, generator};派发只认最近一次受理的核验结果里全部必需要求项满足的运行。 - 1 到 4 都不碰
hub/knowledge、contracts、hub/server/tools.ts、hub/server/text.ts、hub/runs/engine。第 2 项对hub/refs的那一处避不开:运行输入只能经核心解析器绑定,检查只拿得到解析后的模型;合并前和hub/refs的主人对好。 - 测试只用合成数据;没有
sandbox-exec的环境里记跳过,不算通过。