Repository navigation
fix(collaboration): recheck source grants at worker execution - #7016
loopx-agent wants to merge 1 commit into
Conversation
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: model_agent; model=gpt-6.1-sol; provider=OpenAI; declaration_source=runtime_reported; reasoning_effort=max
Request changes conclusion (author-owned PR; GitHub blocks formal self-review)
代码结论:REQUEST_CHANGES,精确 head 0aa4cab18f83b3c833351fee01593080db94ef51,不可变基线 3e166772b6b3b7e00b7d44ae9fcddb9cfba09c68。
[P2] pre-launch 来源拒绝后,恢复原操作必须保留交接输入。 Delegations._execute 新增的 running 分支检查拒绝后,finally 会删除 DELEGATION.json。恢复授权再续接同一 running 操作时只重新检查执行权,没有重新准备 bootstrap,实际 Host 随后因该文件缺失而失败。真实 File/SQLite 原样恢复均失败;仅为同一操作重新准备 bootstrap 的两个对照都 accepted、Host 计数1。需在原 owner 补齐该恢复,不应绕过授权或创建替代任务。
动机
使用已经绑定任务的 Chat 交接、等待另一个 Agent 完成工作的使用者。来源授权在任务取得执行权后、Host 启动前被撤销:新版正确挡住启动,但恢复授权并续接原操作时,交接文件已经被清除,Host 失败,任务无法完成。
已独立确认排队后的撤权能够阻止 Host;另外发现恢复缺口,两个后端原样续接均失败,仅恢复同一操作的交接文件后均完成且 Host 只执行一次。普通无预建任务的闲时唤醒、正式 App/Lark、真实模型采用和长期成本本轮仍未资格化。
改动思路
保留既有 TypeScript 来源授权 owner;Python 只重新观察来源、原 Inbox 与对话证据,并在原 worker 的检查点调用同一精确授权规则。
本 PR 的当前边界是已有任务交接的 worker 来源撤权;需要一并恢复该检查点拒绝后的原操作续接,不要求实现整个通用唤醒系统。维持来源 grant 与原 binding 两层授权,不能把收到上下文当成启动许可。比新建 scheduler、任务或权限存储更合适的修复,是复用现有授权判断并补齐同一操作的恢复输入。
| 触发点 | 当前精确 head 的观测 |
|---|---|
| 排队后撤权、来源缺失或不匹配 | 拒绝原操作,不启动 Host;历史仍可读 |
| Host 已返回后撤权,再恢复授权 | 原公开用例可沿原 Turn 结算,不重复 Host |
| 取得执行权后、第一次 Host 前撤权,再恢复 | 原样续接因交接文件缺失失败;最小 bootstrap 对照恢复 |
表格说明源码边界与测试观测,不把流程展示本身当执行证据。
具体改动
先读规范 docs/reference/local-delegation.md,修订 3e166772b6b3b7e00b7d44ae9fcddb9cfba09c68 的 Activate、Original task revalidation、Source use、原操作恢复与验收/返回条款。此次18行双语说明作为待审修改,未用它自己证明实现。
完整 PR 为6文件、+279/-32:三个既有产品文件、双语说明和175行来源/真实 worker 回归测试。executionBinding 是现有校验的同域提取,没有另造授权 owner;整体机制与问题规模相称,但恢复结果仍需修正。
关键代码讲解
Delegations._bound:822在 active binding 复核中调用新观察适配器;显式 operator/MCP 无 Chat source 的路径仍保留原 binding 权限。require_delegation_source_execution:176锁定当前 policy,重新读取原 Inbox 与 roundtrip,匹配原身份并只读观察固定 Chat Session/Turn,避免为了观察而恢复或修改原 Chat store。来源正文、sender、recipient 与 Goal 实例仍由原 typed owner 核对。sourceExecutionBindings复用四字段精确 tuple;新的required_binding必须在当前允许集合中。sender、注册、blocked target 和原 requester 分别校验,旧接口不带该参数时仍返回原集合。_execute:1741在 running 后、Host 前再次检查来源;这是必要的安全检查,但它的异常会经过 bootstrap 清理。现有 running 恢复路径不重建文件,形成上述 P2。来源恢复并不授权跳过原任务、lease、Stop 或验收。
规范映射:LD-source-grant 已实现,原 exact grant 的撤销实际阻止启动;LD-original-recovery not_met,需要保留或恢复原操作执行输入;LD-independent-return 的规范完成、接收方采纳与返回 owners 保留,并有原公开路径验证。未将本修复扩张为完整闲时唤醒或安装态承诺。
191项相邻 Python 测试、15项 TS 来源合同、control-plane typecheck 和标准 premerge6直接+14选中检查通过。30项公开来源用例在非 editable wheel 全过,实际加载331个产品模块、零源码逃逸,三个改动产品 blob 匹配准确 head;实际夹具 Host 也从安装包导入。不可变基线的两个排队撤权反例仍启动 Host,证明原安全缺口真实存在。
对主干的风险
新反例在真实 _acquire_delegation_lease 返回后撤销原 source policy,不伪造 operation 或直接改状态。第一次 execute 后状态 running,Host/Turn 均未创建,原 Task 未完成;恢复同一 policy 再执行,Host 进入后观测到 DELEGATION.json 缺失并返回 host_failure,最终 rejected。只补同一 operation 的 bootstrap 就能完成,两个后端都是相同结果。隔离 wheel 中再执行同四个恢复对照也为2败2过,产品导入全部来自安装包。原公开测试覆盖 capture 内撤权与 Host 后恢复,没有覆盖这段中间窗口。
最小修复:在既有 worker owner 中补齐 running 且尚无原 Turn 的恢复输入,或采用等价的原操作恢复;保留来源/binding/Stop/lease 检查和清理规则,不能重建任务或绕过撤权。新增回归应从拒绝到恢复完整走通,保持原 operation/Turn 身份及 Host 计数1,并再重放确认无重复执行。优先使用既有 _write_delegation_bootstrap,不需要新状态或第二个 executor。
宽范围 mypy 在不可变 base/head 均为5065错误、588文件,完整诊断多重集仅按源码字节相同的行映射后完全一致;保留原失败,不记为通过。初次打包遇到陈旧 frontend bundle,正常重建后成功;私有 wheel 夹具初次缺 test extras、再遇相对 TS 初始化路径,修复测试准备后原30例全过。初次错误测试路径也保留为未执行观察。这些不用于冲销当前已复现的恢复回归。
语义与 CI 对齐
撤权是机器强制条件;新双语文本只是披露该条件,不能将“拒绝记录可读”等同恢复成功。共享 ID/状态词汇沿用原 owner,无基于正文的权限分类器;未配置 Chat source 的 operator/MCP 路径通过原规则。当前问题违反原操作续接的实际用户路径,应修正在既有边界。按当前策略未查询或等待 CI,premerge通过也不能消除该反例。
我的整体评价
problem_context=not_yet_proven,long_horizon=regression、user_experience=regression:安全拒绝本身成立,但恢复原授权后仍不能完成已经持有的工作,造成无效人工重试。这里采用当前 packet 的经验建议,实际检查拒绝后的有用结果;没有从上下文送达或测试数量推断长期收益。
架构复用和当前比例判断可以保留,具体恢复必须在本 PR 修复。结论 REQUEST_CHANGES;新 head 复评需运行上述两后端完整拒绝/恢复/重放回归,并重新核对整个授权与结算路径。GitHub同账号以本标题 COMMENTED 记录代码拒绝结论,不将其称为正式 self-request-changes 或合并授权。
English verdict: REQUEST_CHANGES — 0aa4cab18f83b3c833351fee01593080db94ef51, immutable base 3e166772b6b3b7e00b7d44ae9fcddb9cfba09c68. P2: revocation after original execution acquisition but before Host leaves a running operation whose bootstrap is cleared; restoring authority then resuming fails the real Host. Both File/SQLite fail, while bootstrap-only controls accept once. Retain the source fence and repair original-operation recovery. TS15, typecheck, public wheel30 and native premerge6+14 pass; matched broad mypy failure remains recorded. No merge authority or live-model qualification is implied.
| self._acquire_delegation_lease(path, row, binding) | ||
| if row["status"] == "running": | ||
| self._raise_if_stop_requested(path) | ||
| self._bound(row, require_active=True) |
There was a problem hiding this comment.
[P2] Restore the original bootstrap after a pre-launch source refusal
If the original source is revoked immediately after _acquire_delegation_lease returns, this new check correctly prevents Host launch but leaves the operation running. The finally block then removes DELEGATION.json. Restoring the same policy and resuming re-enters the running branch, which does not recreate that file, so the real Host fails and the operation becomes rejected. File and SQLite both reproduce this in source and an isolated wheel; only re-preparing the same original bootstrap makes both controls accept with one Host invocation. Restore the needed original-operation input on this prelaunch recovery path while retaining the source/Stop/binding/lease fences, and add the full refusal -> restore -> accepted -> no-op replay regression.
A Chat handoff could pass its source grant check, persist a delegation, and still launch the worker after that exact source grant was revoked. The worker now reobserves the original Inbox request and frozen Chat provenance, then asks the existing TypeScript source-grant owner to require the original Goal/Agent/requester/binding tuple at execution checkpoints.
Queued refusals remain readable on the original operation before Host or Turn creation. Work already executed stays in its original journal; restoring authority permits settlement without another Host invocation. A completed caller Chat Turn still permits separately authorized work. The existing operator/MCP path retains its binding authority.
Validation for
0aa4cab18f83b3c833351fee01593080db94ef51against3e166772:The initial premerge attempt placed temporary fixtures beneath a Git checkout, violating the canary fixture's no-Git premise. After verifying that distinction, the focused canary and full gate passed with an isolated temporary directory outside Git; the failed observation is retained.
Mypy remains failed with the same 5065 errors and 156 notes on immutable base/head workloads. The complete diagnostic multiset agrees after witnessed source offsets; one unchanged diagnostic moves in report order. Optional baseline attribution retains that failure as a risk. Required CI and independent exact-head review remain separate.
This proposal covers the existing task-bound worker. General Inbox idle/busy wake admission and an installed App/live-provider round trip still require their own qualification. Related #7015 repairs prepared-operation recovery; overlapping worker changes require integration revalidation.