fix(diagnose): surface orphaned Goal state - #4912
huangruiteng merged 7 commits into
Conversation
Signed-off-by: duanjialing.777 <duanjialing.777@bytedance.com>
Signed-off-by: duanjialing.777 <duanjialing.777@bytedance.com>
|
Exact-head CI triage for
This branch intentionally does not duplicate #4906. I will merge current |
Signed-off-by: duanjialing.777 <duanjialing.777@bytedance.com>
Signed-off-by: duanjialing.777 <duanjialing.777@bytedance.com>
…ed-goal-state Signed-off-by: duanjialing.777 <duanjialing.777@bytedance.com>
…ed-goal-state Signed-off-by: duanjialing.777 <duanjialing.777@bytedance.com>
…ed-goal-state Signed-off-by: duanjialing.777 <duanjialing.777@bytedance.com>
huangruiteng
left a comment
There was a problem hiding this comment.
评审 exact head:68ae3cef27eb422e09937c88cf3b60d4fa9543d8。结论:APPROVE(仅覆盖本 PR 的只读诊断切片,不代表 #4801 的恢复/身份问题已完成)。
动机
#4801 要求在 Goal 注册项消失但项目内状态尚存时,diagnose 不能报告普通“未连接”。我用同一合成输入在合并基线和此 head 的真实 CLI 重放:基线退出 0、ok=true、仍给 quota should-run;此 head 退出 1,明确给 orphaned_goal_state,只留下只读检查与备份预览。这个切片完成了 #4801 的 diagnose 可见性要求;项目本地 archive/adopt 和 Goal lifetime identity 仍属于后续里程碑。
改动思路
入口仍是 diagnose --goal-id。代码先从 status 快照确认该 id 的 registry_member=false,再复用 #4808 已交付的四根目录孤儿状态探测与 resolution gate;项目 registry 仍是注册权威,状态文件只提供冲突事实。诊断包改为 blocked,不创建状态也不提供 bootstrap、quota 或 --execute 续行。无孤儿文件时保留原普通未连接路径;这比另写一套扫描/恢复机制更合适。
具体改动
loopx/diagnose.py增加冲突探测、将既有 gate 映射到 JSON packet,并在 Markdown 展示原因、相对状态路径和禁止操作;顶层ok/退出码与所选 Goal 的阻断状态一致。- 新增
tests/control_plane/test_diagnose_orphaned_goal_state.py,覆盖真实 CLI 的孤儿 JSON、无状态对照和 Markdown 安全命令。没有持久化 schema、前端或 Lark 改动。
关键代码讲解
_orphaned_goal_state_for_diagnosis(line 125):仅在 status 明确给registry_member=false时调用既有投影,避免将已注册 Goal 误判为孤儿。_apply_orphaned_goal_state_diagnosis(line 494):复用安全 gate 并替换不适用的 quota/agent 命令;此处只构造诊断证据,不授予恢复权限。collect_diagnosis(line 539)与render_diagnosis_markdown(line 652):将该状态传播到机器可读退出结果和人工可读视图。
对主干的风险
最强反例是遗漏 .loopx、.codex、.claude、.local 中任一状态根,或把普通未连接误阻断。我用真实 CLI 对四根目录逐一验证:均退出 1、should_run=false、只有两条非执行命令;无状态对照仍退出 0。基线同输入复现旧误判。focused pytest 3/3、agent-diagnose 与 bootstrap smokes、Ruff、diff check 均通过。风险 canary 首次因隔离 worktree 缺 Node parser 依赖而失败;执行 npm ci --ignore-scripts 后原命令重跑 2/2 通过、无 warning。按 Goal wait_for_ci=false 未查询远端 CI。
语义与 CI 对齐
此 PR 复用现有 orphaned_goal_state 语义而非新增并行分类;diagnose 的机器阻断不等于已经实现 archive/adopt 或 Goal instance fence。后者必须继续按 #4914 RFC 和 #4801 验证。这里无阻断性发现。
我的整体评价
2 文件改动把已有安全事实接入独立诊断入口,范围与风险相称,未引入第二个权威或破坏正常缺席分支;相关小型未来改进已体现在复用现有 gate,而不宜在此 PR 扩为恢复事务。批准此 exact head。合并权限、远端检查和 #4801 剩余验收仍是独立门禁。
English verdict: APPROVE - At 68ae3cef27eb422e09937c88cf3b60d4fa9543d8, real CLI baseline/head and four-root negative checks confirm the read-only orphan diagnosis; focused tests and risk canary pass. Recovery and lifetime fencing remain separate work.
Goal And Delivered Outcome
diagnose --goal-idstill treated the same state as ordinary unregistered absence and recommended connect/sync.ok=true,machine_signal=not_connected_or_not_projected, and a quota drill-down before this change. It now returns exit 1,diagnosis_blocked_by=orphaned_goal_state, the existing orphan gate and only its read-only status/backup-preview commands.main.Scope And Continuation
diagnosesurfacing item from [Bug]: Guard orphaned Goal state before guided bootstrap or host activation #4801 for an explicitly requested Goal whose status snapshot confirms it is absent from the registry. It reuses the existing four-root orphan detector and resolution gate. Project-local archive/adopt and opaque Goal instance identity remain open in [Bug]: Guard orphaned Goal state before guided bootstrap or host activation #4801.Validation
a595fe5fb96aa0b4a46df224ff4a31db0928b1e3regression_paritypasseddiagnoseexited 0; it passes on this head with a blocking orphan signal and no quota or--executecommand.unitpassedreal_entrypointpassedloopx diagnose --goal-id reset-goalreturnedok=false, preservedstatus_ok=true, reported the relative surviving-state route, and exposed only status plus backup preview. Default Markdown renders the same gate.integrationpassedagent-diagnose-packet-smoke.py,bootstrap-command-pack-smoke.py, andstatus-neutral-run-window-smoke.pypassed.staticpassedgit diff --check, the 14-test maintainability ratchet, and the public-boundary scan passed. The exact-head semantic vocabulary and frozen incident-retrodiction checks pass after keeping the diagnostic action out of the typed quota-action field. A standalone mypy run reports the same 10 pre-existingloopx/diagnose.pyerrors on base and head..loopx,.codex,.claude, and.localGoal-state roots. This change tests the public JSON and Markdown diagnose surfaces plus a no-state negative twin.diagnosewithout an explicit Goal id does not scan arbitrary orphan directories; [Bug]: Guard orphaned Goal state before guided bootstrap or host activation #4801 is scoped to the requested id. Invalid registry JSON remains a distinct fail-closed collection error rather than being reclassified here.See validation disclosure guidance.
Frontend / Visual Evidence
Type of Change
LoopX Area
Technical Direction
Shared-authority RFC fixture impact
Boundary Checklist
none.Signed-off-bytrailer (git commit -s).