任务:台架归登记它的用户;预订没有时长限额,由运行持有到释放
依据:
- 决定 35 第 14 项(所有者 10-23 晚:谁的环境就是谁登记的):设备归登记它的用户。设备的主人看得到谁占着,也能释放;别的用户看得到“被占用”,看不到是谁。
- 第 15 项(所有者 10-23 晚,更正以前的默认值):不要预约时长的限额;租约只在运行期间持有;用户读得到哪套台架正被哪个运行占用。
- 第 14 项原话里的“占到几点”,到第 15 项以后没有固定的时刻。显示的是:从几点起、被哪个运行占着,到它的谱系释放为止。
- 第 13、16 项:D04 R[0] 的修订(预订类租约按血缘持有)确认。
- 决定 35 上机第 2 问:台架清单是管理员发布的部署数据(
circle-hub doc put bed-topology)。所以这里的“登记”只记归属:用户把清单里自己的台架登记到自己名下,清单的内容照旧由管理员发布。用户自己上传清单不在这一件里。 hub-device-run.md第 1 条:每个用例自己一条谱系,作业有终态以后释放,下一个用例再占,脚本里不写任何时长上限。这一件给它提供核心的做法。- 现在的代码:
- 租约表一行一个键,记持有的用户和运行;
holders(keys)给出持有人,view(prefix)不给(hub/leases/service.ts,README 的 Limits 一节)。 - 运行上下文的租约视图只有
key、until、mine,说明里写着可见性未定(contracts/src/pack.ts的RunContext.leases)。视图冻结进RunRecord.lease_context:contracts/src/run.ts;hub/runs/engine/start.ts约 466–480 行;evaluate.ts的contextLeasesOf。 - 时长:
- 工作流声明
leases.max_ttl_seconds(contracts/src/pack.ts:单次请求和累计续期的上限);start.ts第 140 行一带按它判。 - 台架包声明 86400;
functions/conclude.ts有MAX_HOURS = 8、DEFAULT_HOURS = 2和hours参数,extend动作续期。
- 工作流声明
- 强制释放只有管理员的
circle-hub lease release <键> --reason <理由>(hub/server/cli-admin.ts);续期和释放只认血缘(start.ts的grantLeases)。 - hub-job-dispatch 加了只读的持有人快照
holderSnapshot:带围栏令牌,只给派发这类可信代码用,不进客户端和模型。强制释放会让令牌加一,原持有人之后的派发会被围栏拒绝。 - 台架包 1.1.0(core-fixes 上是 1.1.1)的动作:search、facts、acquire、extend、release。
packs/workflow/test-bed/README.md的“核心缺口”一节第一条,就是持有人进不了运行上下文。
- 租约表一行一个键,记持有的用户和运行;
-
模型元素:D04、D01、R01、R02、B05。
node tools/show-element.ts D04 D01 R01 R02 B05。 - 分工:模型文字由集成者先改,再开这一件:
- D04 R[0]:
- “时长上限取自允许的效果表”改成:声明了时长上限的租约照旧到期;预订类租约没有时长限额,由预订它的谱系持有,到谱系释放、或主人和管理员强制释放为止。
- 加:预订类目标有主人,是登记它的用户;登记和注销各记一条租约动作记录;登记的前缀互不包含;主人和管理员看得到持有人,所有人看得到占着它的运行;主人和管理员能强制释放(带理由);管理员能改登记。
- D01:部署实例归管理员管的规定加一句:用户可以把清单里的台架登记到自己名下,只记归属。
- R01 R[0]、R[1] 和 R02 照上面写。
- 词表
lease_op加register(登记)、unregister(注销)。
- D04 R[0]:
- 分支和工作树:
test-bed-ownership,目录circle-hub-test-bed-ownership。- 等下面这些都合并以后,由集成者从 hub-redesign 开出并通知:core-fixes、hub-job-dispatch、kb-candidates、kb-links、views-and-notes、plan-evidence、hub-device-run,和集成者的模型提交。
- 原因:
hub/leases、start.ts、contracts/src/pack.ts、run.ts、hub/records/migrations.ts、cli-admin.ts和台架包都在这些分支上改着。
- 可以改的路径:
hub/leases/**;hub/records/migrations.ts:只在末尾加一个新迁移,已应用的不改;迁移快照测试跟着改;hub/runs/engine/start.ts(租约时长、租约视图的冻结、grantLeases)、evaluate.ts(contextLeasesOf)、deps.ts(第 3 条的端口)、README 和对应测试;hub/server/assemble/hub.ts:只接第 3 条的端口;contracts/src/pack.ts(leases声明、RunContext.leases、投影的lease_requests)、contracts/src/run.ts(leases、lease_context)、contracts/src/records.ts(登记动作的形状)、contracts/src/leases.test.ts,照生成器重新生成contracts/generated/**;hub/server/cli-admin.ts的 lease 命令和hub/server/assemble/cli-admin.test.ts;packs/workflow/test-bed/**;tests/acceptance/24-lease-start-run.test.ts、25-test-bed.test.ts;hub/dispatch/原范围只加一个测试文件;凌晨补充授权的只读公共查询及导出见本页“集成者答复”,不改派发行为。
别的路径不动。还有几处重叠:
- hub-pack-findings 第 9 条也改台架包,集成者让两件先后做。
- 同一批写的几页也碰到这里的文件,段落不同:kb-unversioned 改
start.ts锁根的一段和contracts/src/run.ts;script-distribution 改contracts/src/run.ts的 PackSummary;gateway-request-ids 第二步改hub/dispatch/**。集成者排先后,后合并的照合并后的契约重新生成contracts/generated/**。 - hub-variables 改
start.ts开头读变量的一段和参数的凭据扫描、hub/server/cli-admin.ts(var、secret、resource 的命令)、hub/dispatch/**(执行器的钥匙和变量)。段落不同,集成者排先后,后合并的照合并后的契约重新生成contracts/generated/**。
- 规则:同前(不推送、不建 PR、不发布、不部署;不碰 7420、钥匙串、环境配置文件和任何真实系统;测试只用合成数据,版本线 10.6 以后,地址只用 192.0.2.0/24 和 2001:db8::/32;
git commit -s)。所有者的规矩:- 核心里不出现设备、台架这类词:核心只认“声明的前缀下面,一个键前缀有主人”;
- 令牌不进运行上下文、运行记录、工具回答和控制台;
- 占用的结论照旧写“Hub 记录里…(未排除本地操作)”;
- 未知只能是过渡状态,写明缺的事实和下一步动作;
- 枚举只来自生成的词表。
- 验证:
npx tsc --noEmit;node tools/gen-contracts.ts --check;node --test --test-concurrency=1对这些:contracts/**、hub/leases/**、hub/records/**、hub/runs/engine/**、hub/dispatch/**、hub/server/assemble/cli-admin.test.ts、packs/workflow/test-bed/**、packs/workflow/case-compile/**、tests/acceptance/24-lease-start-run.test.ts、tests/acceptance/25-test-bed.test.ts;node packs/workflow/test-bed/pack-files.ts --check。
要做的
- 没有时长限额的租约(核心):
- 工作流的
leases.max_ttl_seconds改成可选。- 写了的,照现在到期、续期。
- 没写的,租约没有到期时刻(
until为 null),到下面三种情况之一为止:持有它的谱系释放;主人或管理员强制释放(第 4 条);管理员lease release。 - 没有到期时刻的租约不能续期,续期请求整次不受理,修正说明写“这个前缀的租约不到期,不用续”。
- 谱系照 D04 R[0] 和
grantLeases现在的写法:以预订运行为前驱的运行沿用同一份租约。 - 测试:
- 不到期的租约过了很久还在;续期被拒;
- 谱系里后一个运行释放成功,别的谱系释放被拒;
- 写了上限的旧行为照旧,现有测试照样通过。
- 工作流的
- 登记和注销(
hub/leases):- 用户可以把工作流声明的前缀下面的一个键前缀登记到自己名下。台架包用
bed/<台架>/:台架的设备键和地址键都在它下面。 - 登记的前缀以
/结尾,严格在声明的前缀下面,不能就是声明的前缀;和已有的登记互不包含(bed/a/和bed/a/x/不能同时有)。不合这几条的拒绝,码是LEASE_UNAVAILABLE,消息只说哪一条不合,不说别人的名字。 - 先登记的人是主人,这是所有者的原话:谁登记就是谁的。冒领时由管理员纠正:
- 管理员可以
circle-hub lease register <前缀> --owner <用户> --reason <理由>,把前缀登记给指定的用户,已有主人时直接改给他; - 管理员也可以
lease unregister。 - 两条都记动作记录。
- 管理员可以
- 用户自己登记时,前缀已经有主人就拒绝,消息只说“已经有主人”,不说是谁。两条连接同时登记同一前缀,恰好一个成功,照现有的 50 轮并发测试写。
- 主人或管理员可以注销。台架上还有别人没释放的租约时不准注销,修正说明写“先强制释放,或等它释放”。
- 当前主人存在一张按前缀唯一的表里(新迁移)。每次变更在同一事务里追加一条租约动作记录(op
register、unregister,记主人、前缀、理由,管理员改登记时还记原来的主人)。表能由动作记录重建,回放测试照现有写法。 - 测试:
- 登记;重复登记;并发登记;
- 嵌套的、等于声明前缀的、不以
/结尾的前缀,各一例被拒; - 主人、管理员、别人各注销一次;有租约时注销被拒;
- 管理员改登记以后,原主人失去主人的视角;
- 回放。
- 用户可以把工作流声明的前缀下面的一个键前缀登记到自己名下。台架包用
- 运行上下文里谁看得到什么(
start.ts、evaluate.ts、contracts/src/pack.ts、run.ts):- 冻结的租约视图每行有这几项:
key、since(取得时刻);until:没有到期时刻的是 null;run:占着它的运行号,所有人都有;mine;- 持有人(用户编号和名字):只在这次运行的用户是这一行所在前缀的主人、或是管理员时才有。持有人自己照旧看
mine。
- 另加一张登记表:声明前缀下每个已登记的前缀,加“是不是我的”。别人的登记不带名字,管理员的运行带。
- 两张都冻结进运行记录;用记录里的上下文重跑投影,得到同样的请求。
- 每行还要有一项:这些键上有没有还没终态的派发作业,有的话是哪个。这项经
deps.ts里一个只读端口,向 hub-job-dispatch 已有的作业记录查(作业按围栏令牌记着租约键),在hub/server/assemble/hub.ts里接上。没有现成的查询时,按本页“集成者答复”的授权补只读公共查询,不改派发行为。 RunContext.leases的说明去掉“可见性未定”,写成现在的规则。- 测试:主人、持有人、第三个人、管理员四个视角各开一个运行,断言冻结的行。第三个人的行里有运行号,没有持有人的编号和名字。
- 冻结的租约视图每行有这几项:
- 主人强制释放(
start.ts的grantLeases、contracts/src/pack.ts):- 投影的
lease_requests加{ op: "force_release", keys, reason }。Hub 先核对下面几条,有一条不对就整次不受理,什么都不动:- 每个键都在这次运行的用户登记的前缀下,或者这个用户是管理员;
- 键上有没释放的行;
- 理由不空;
- 键上没有还没终态的派发作业。有的话,修正说明写:作业 <号> 还没有终态,本地释放停不了网关上的作业;等它结束,或者请管理员用 `circle-hub job status` 看、用 `job dispose` 处置。号>
- 管理员的
lease release照同一条核对作业。 - 核对通过以后,用租约服务的强制释放,
by是这个用户,理由进动作记录;运行记录的leases写下释放了哪些键,以及原来的运行。 - 原持有人的运行记录不变;它的谱系之后的派发被围栏拒绝。
- 测试:
- 主人释放成功;非主人被拒(
LEASE_UNAVAILABLE);管理员成功; - 有没终态作业时,主人和管理员都被拒,修正说明是上面那句;
hub/dispatch/下加一条:主人释放以后,原持有人的派发因为令牌变了被拒。照 hub-job-dispatch 现有的合成执行器写。
- 主人释放成功;非主人被拒(
- 投影的
- 台架包(
packs/workflow/test-bed,升到 1.2.0):- 时长:
workflow.json去掉max_ttl_seconds;conclude.ts去掉MAX_HOURS、DEFAULT_HOURS和hours参数;- 去掉
extend动作。旧的hours、extend请求如实拒绝,修正说明写“台架预订不限时长,用完由运行释放”。
- 新动作:
register:参数bed,清单里的台架名;unregister:参数bed、reason;force_release:参数bed,可选devices,必填reason(照用户的原话写)。- 投影出对应的
lease_requests;检查照旧按冻结内容重算(functions/check-bed.ts);每个动作一对正反样例。
- 投影如实拒绝(包码写进 codes):
register的台架不在当前清单里,或者已经有主人;force_release的台架不是自己登记的;没写理由。 - search 和 facts 的结论:
- 主人和管理员看到:“<设备> 被 <名字> 的运行 <号> 占用,自 <时刻> 起”。时刻>号>名字>设备>
- 别人看到:“被运行 <号> 占用,自 <时刻> 起”,不带名字。时刻>号>
- 有没终态作业的,加一句:“作业 <号> 还没有终态”。号>
- 已登记的台架写“有主人”,自己的写“你登记的”。主人的名字对别人不显示(决定没说,先按不显示做,README 写明)。
- 原来那句“Hub 记录里…(未排除本地操作)”照留。
- 主人 search 不写 need,就能看到自己每台设备谁占着、从几点起。
- SKILL.md 写三个新动作:
- 登记前先确认是用户自己的环境;
- 强制释放前先问用户,理由照原话写;
- 结论原样转述;
- 另外写明台架预订不限时长,用例的作业有终态以后释放。
- README 改“核心缺口”第一条,记下第 13–16 项已定。第 15 项写“没有时长限额,租约由运行持有到释放”,不写小时数。
- 测试:
- 每个动作的投影和检查;
- 两个用户各 search 一次,断言别人那份里有运行号,没有持有人的名字;
- 旧的
hours和extend被拒; node packs/workflow/test-bed/pack-files.ts --write重签。
- 时长:
- 管理员命令(
cli-admin.ts):lease list加主人、持有人、运行三列,照旧不显示令牌和租约号;- 加
lease register <前缀> --owner <用户> --reason <理由>和lease unregister <前缀> --reason <理由>; - 测试:
hub/server/assemble/cli-admin.test.ts。
- 验收(
tests/acceptance/25-test-bed.test.ts加一段):- A 登记台架;B 预订,不写时长;
- A 搜索,看到 B 的名字和运行号;C 搜索,只看到运行号和起始时刻;
- C 强制释放被拒;A 强制释放成功,理由进记录;B 的谱系之后的派发被拒;
- C 登记 A 的台架被拒;管理员把它改登记给 C 以后,C 成了主人,A 不再是;
- 管理员
lease list看到主人、持有人和运行。 - case-compile 也读租约视图,它的测试照样要过。
集成者答复(2026-10-24 凌晨)
- 05:22,答 codex-d,可改路径补充:可在
hub/dispatch/**加按租约键查询未终态作业的只读公共方法、API 导出及测试;只读既有记录,不改派发行为。expires_at为 null 判为不到期,不得因没有时间值判成已过期。 - 05:24:用
start.ts的LeaseRequest替换projection.ts的手写类型,可改这一处及对应测试。 - 05:37:旧
hours、extend只保留拒绝入口,给出确定的修正说明;不保留旧的有时长预订或续期功能。 - 05:56,可改路径补充:
packs/workflow/case-compile/functions/bed.ts、project.ts及测试适配until: null,包升补丁号并重签;对应 README 和版本断言同步。这里不改其他编译规则。 contracts/src/pack.ts与 hub-pack-findings 的动态读取声明有重叠,后合并方解冲突,保留双方的可选契约与测试。
完成后在终端里回报:提交号、测试数量、没做完或拿不准的地方。