Repository navigation
fix(coordination): resume interrupted canonical Goal creation - #7381
Conversation
The canonical creation retry passes the registered Goal with its state_file rewritten as an absolute path, while the typed source owner required the exact registry observation. An interrupted bootstrap therefore failed every retry with source_registry_changed_retry and left the project registered with a half-created Goal whose status reported canonical authority required. Compare the resolved state route, as the typed owner already does, so the same route resumes the original creation operation while a real route change still fails closed. Fixes loopx-project#7379 Signed-off-by: JasonBuildAI <jasonbuildai@gmail.com>
|
Validation note at
|
|
Hosted CI note at |
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: model_agent; model=gpt-6.1-sol; provider=OpenAI; runtime_reported; reasoning_effort=max.
动机
APPROVE,限 5018930b23cacad722639b90a5beccc43fe92cdb;未发现阻断问题。 本次独立核验的是中断创建后的恢复和有用后续工作,保留合并授权与安装资格的独立边界。
新建 Goal 时遭遇中断、随后重试 bootstrap 的用户或 Agent。
在注册发布后、权威初始化前中断:基线重试报 source_registry_changed_retry;当前头沿原操作和存储目标恢复,并能新增任务、再次重试且保留该任务。
同一次创建恢复后可继续做原任务,设备默认值变化不改写已冻结的目标,错误路由和强制重建仍被拒绝。
本次不重建丢失的已完成权威存储,不改变权限、默认 provider、Todo 生命周期、安装状态或首 Host 的执行资格。
注册条目存在并不表示 Goal 已经可用。原失败会让每次重试在到达原生恢复 owner 之前退出,用户只能反复收到同一诊断。当前修复消除的是同一来源路径的表示差异,没有把真正的操作或存储变化当作可恢复输入。
改动思路
路径解析属于既有 Python 来源观察适配层;复用 resolve_state_file 后只归并同一路由,冻结目标、空来源、writer fence、CAS 和回执仍由原 TypeScript 创建 owner 判断。
当前边界:恢复既有 bootstrap 的同操作重试;一个本地比较函数加现有回归即可,不增加配置、状态副本、恢复协议或新的用户确认。
先比较除 state_file 之外的完整注册,再用已有 resolver 解析两份状态路径;只有指向同一路由才继续从当前注册内容构造来源快照。后续仍带注册字节 witness,并由 TypeScript 检查 operation、target、state route、空来源与原始 fence/receipt。这样修改的是表示归一化,不把 Python helper 变成第二个创建决策者。
| 输入 | 基线 | 本 head |
|---|---|---|
| 原创建、相对/绝对同一路径 | source_registry_changed_retry | 沿原 operation 恢复 |
| 恢复前修改设备默认 provider | 仍不能恢复 | 冻结的原 provider 不变 |
| 已新增 Todo 后再次 bootstrap | 原 completed-receipt 路径 | 原回执恢复,Todo 完整保留 |
| 改路由、改 operation 或强制重建 | 拒绝 | 继续拒绝 |
最强反对理由是放松注册比较会掩盖真实变更;检查完整其它字段、来源 witness、下游冻结目标及真实拒绝测试,比另建重试状态或人工修注册更直接,也不会多加一次确认。
具体改动
评审前读取的接受依据是 https://github.com/loopx-project/loopx/blob/f794ee4642353b70cde185e2774fa0059c467d6a/docs/reference/local-authority-provider-selection.md,不可变版本 f794ee4642353b70cde185e2774fa0059c467d6a,尤其 New Goal authority 与原操作恢复/备份边界,并读取 issue #7379 和全部当前讨论。下列 id 是原条款的审计映射标签:
frozen-operation-and-target— implemented:After interruption rerun the same bootstrap; changed defaults cannot retarget its original operation and workspace.replay-preserves-later-work— implemented:The original receipt survives later native writes; replay cannot erase or repeat Todos.source-and-backup-boundary— implemented:Incomplete creation validates original source; lost completed authority requires backup recovery and forced bootstrap cannot rebuild it.
关键代码讲解
_same_registry_observation只将 state_file 放入单独比较,其余完整字段仍需一致;两份路径复用resolve_state_file并解析为实际路由。缺失值不会制造一个新的来源。build_runtime_shadow_source_snapshot在既有 registry source witness 下读取当前记录。比较失败仍返回原 source_registry_changed_retry;成功后传入 current,保持快照来自当前权威观察。initialize_goal_storage_target是原有调用方:先向 typed owner 请求原回执,只有尚未完成创建才捕获来源。修复使 CLI 预先解析过的 state_file 能进入这条已有路径;已完成回执仍不要求重读缺失的 Markdown 展示。
差异为2文件、+79/-1;生产部分 +22/-1,其余是现有配置测试文件里的确定性中断回归。没有新模块、配置、持久化词汇、请求版本、provider 选择或安装步骤。调用搜索覆盖 bootstrap、App storage API、migration/inspection 和 unchanged typed source owner,没有重复业务决策或闲置抽象。
对主干的风险
独立完整 test_new_goal_storage_defaults.py 68项通过,test_shadow_registry_source.py 26项通过,原生 TypeScript 创建 suite 7项通过;同一普通创建/显式关闭/既有拒绝选择在不可变基线和 head 各9项通过。它们覆盖 File/SQLite、changed operation/source、缺失已完成 store、来源竞争、App HTTP 路径、禁用配置及正常目标冻结;不是只看新测试自己通过。
额外用同一独立 harness 在基线 f794ee4642353b70cde185e2774fa0059c467d6a 与当前头制造注册发布后的中断。只有中断点注入异常,后续均为真实 CLI:两种 provider 的基线重试都以 source_registry_changed_retry 退出1;head 都退出0,state_action 为 kept-existing、authority_initialized 为 true,并沿原 operation/provider 恢复。改变设备默认值不改写目标;恢复后实际新增 Todo、重试、再独立读取该 Todo,完整记录相同。错误路由与 force 分别退出1,未产生替代状态文件。
scoped Ruff、Python 语法和差异/双语检查通过。标准 premerge 的6项直接检查、13项选中检查通过,无人工留置;精确范围质量收据 cqr_d0a2bf8963d0ce479efb 为 PASS / valid,safe-fix 未使用。所有这些信息在首次完整评审前取得。原生 CI 未查询、轮询或等待;作者在讨论中的其它 CI 陈述没有被当作本次独立通过证据。
语义与 CI 对齐
该变更复用既有 original-operation/source-identity 契约,没有新共享词汇。原来把同一路由误拒绝的行为有意改变,其它字段比较、typed witness 和原创建目标保持;原始 force、缺失 store、changed operation/source 的拒绝不变。错误文本或 Agent 指导没有被压缩、替换或改成新的结算义务。回滚仅需撤回本地比较,不迁移权威数据、不清理 writer fence 或原回执。
我的整体评价
APPROVE,限 5018930b23cacad722639b90a5beccc43fe92cdb。 当前头完成了中断后重试并继续原任务这一有用修复。长期工作与使用体验维度为 improved,证据限于已验证的恢复、少一次手工修注册、以及后续 Todo 的持续读回;没有据此声称整体 Goal 完成或长期因果净收益。
检查的是现有 CLI 和规范来源捕获路径,没有页面、布局、组件或图片变更;视觉构图检查不适用。
未独立复验原生 Windows 的杀进程窗口、已安装 App 或首 Host 启动;中断发生点由受控故障注入,后续恢复与有用任务读回使用真实 CLI 和 File/SQLite。
这个局部边界适当:来源适配层归一化已知路径,现有 typed owner 保留执行决定,原注册和回执不会因设备默认值改变而漂移。有界简化是复用 resolver 与完整字段比较;无需新的状态机、版本或人工步骤。发布审批不清除其它人的未解决意见,也不授予合并、安装或生产操作权限。
English verdict: APPROVE at 5018930. The existing source adapter now recognizes equivalent state routes while the typed creation owner retains operation, target, witness and replay authority. Independent real File/SQLite CLI recovery preserves later work;94 Python tests,7 native TS tests,9 normal/refusal controls per arm and standard premerge passed. Exact-scope quality is PASS; native Windows kill timing and installed Host execution remain unqualified here.
Goal And Delivered Outcome
An interrupted
loopx bootstrapcurrently bricks the Goal id and leaves the entire project'sstatusfailing; this change lets the next bootstrap resume the registered creation instead of rejecting it as a changed registration.f794ee464; original baseline93bb1ed27)loopx bootstrappublishes the Goal to.loopx/registry.jsonand writes its projection before the canonical authority exists. Any interruption after that point (Ctrl-C during the cold TypeScript authority start, a host failure, or a concurrent creator) leaves a registered Goal with no authority. Every retry then exits 1 withsource_registry_changed_retry,--forcerefuses with "Canonical creation cannot rebuild existing state", andloopx statusexits 1 with "canonical Todo contract unavailable: Canonical authority is required". The documented recovery ("retry its original operation") never runs.source_registry_changed_retry/statusok=false→ retry exit 0 /state_action=kept-existing/authority_initialized=true/statusexit 0ok=true. The focused regression fails on the parent owner withShadowManagementError: source_registry_changed_retryand passes at this head.Closes #7379; baseloopx-project/loopx:main.Author Declaration
Implemented against
f794ee464— the supplied Goal must bind to one registry observation, and an unfinished canonical creation resumes through its originalcreation_operation_idwithout replacing a provider._same_registry_observationinloopx/control_plane/coordination/authority_source_capture.pytest_interrupted_cli_creation_resumes_the_registered_operationtest_cli_creation_recovery_cannot_recreate_or_adopt_authority[changed_source-file|sqlite](unchanged, passes)bootstrap_projectretry pathScope And Continuation
creation_operation_id, or for an explicit route change.Validation
5018930b23cacad722639b90a5beccc43fe92cdbunitpassedpytest -q tests/control_plane/test_new_goal_storage_defaults.py -k "interrupted_cli_creation or cli_creation_recovery": 7 passed, 61 deselected at the tested revision. The new case fails at the parent owner withShadowManagementError: source_registry_changed_retry.real_entrypointpassedbootstrap(Goal observed in the project registry, process killed) → retry exit 0 withstate_action=kept-existingandstorage_selection.authority_initialized=true;statusexit 0ok=true;todo addexit 0.staticpassedruff check loopx/control_plane/coordination/authority_source_capture.py tests/control_plane/test_new_goal_storage_defaults.py;git diff --check.Type of Change
LoopX Area
Boundary Checklist
none(none).Signed-off-bytrailer.AI Disclosure
AI disclosure: this change was drafted with AI assistance at my request, under my direction and review.