fix(cli): show the error when a local LoopX service cannot start - #5267
huangruiteng merged 3 commits into
Conversation
`loopx chat`, `loopx dashboard` and `loopx serve-status` rendered a start failure with the status renderer. A failed start has no status projection, so the default Markdown output was an empty status table (goals: None, runs: None, ...) and dropped the error, for example the missing Chat bundle and the npm command that builds it. Render start failures as a short failure summary with the error, the registry when known and the gate's next action. JSON output is unchanged. Signed-off-by: song <liusongstep@gmail.com>
Signed-off-by: song <liusongstep@gmail.com>
…error-markdown Signed-off-by: song <22676124+songoow@users.noreply.github.com>
huangruiteng
left a comment
There was a problem hiding this comment.
English verdict: APPROVE — the local startup failure is now actionable in Markdown, with the existing JSON and exit contract preserved.
Reviewed exact head: 92367f8128c569df0d417fb6e929810af8ed4d6c.
动机
本 PR 解决的是一个很具体的本地操作问题:loopx chat、dashboard 或 serve-status 启动失败时,异常已经放进 payload,但普通 Markdown 输出走了健康状态渲染器,用户看到空的 Goal/status 信息却看不到失败原因。用户实际需要的是知道哪项本机能力或 bundle 缺失、修好后重试;这与改变 Goal 状态或做服务端恢复无关。当前改动在这条入口上交付了完整有用的结果。
改动思路
PR 复用现有三个 try/catch、原始异常字符串、print_payload 的格式选择和既有非零退出码。新增一个仅供启动失败使用的窄 Markdown renderer,而不是让健康状态 renderer 猜测错误 payload 的形状,也没有新增并行的状态源。人读的输出包含命令与 error;已有 registry 时可显示路径,Chat 原有 gate 的 next action 也能显示。JSON 分支继续使用原 payload,不因 renderer 被替换而换 schema。
具体改动
完整 PR 是两文件 +70/-4:生产代码约 24 行在现有 CLI dispatcher,另有 46 行焦点测试。它改变的是三条本地命令的失败呈现;成功路径、服务端行为、权限、scheduler 和持久化记录未改。
关键代码讲解
- _start_failure_markdown:消费已存在的 error、可选 registry 及 gate.next_action,生成短的命令级失败摘要。它只负责输出,不重新分类异常或制造 Goal status。
- serve-status 启动 catch:仍由本地服务启动异常进入,payload 和退出码保持原有语义,只把 Markdown renderer 换到失败专用路径。
- dashboard 启动 catch:实际隔离命令使用不存在的 assets_dir 后返回 1,显示 bundle 缺失及修复提示;不会再打印健康状态表。
- chat 启动 catch:实际隔离命令同样返回 1,显示 assets 不可用与原有 next action;对应测试还验证 JSON 的
loopx_chat_start_v0和 error 未变。
对主干的风险
未发现阻塞项。这个 renderer 不会启动额外服务、写 Goal,也不改变 JSON;风险主要是某些异常文本本身不够具体,但那是异常来源的诊断质量,不能用一个新协议来弥补。失败恢复仍是用户按提示补齐本机依赖后重试。相邻 future-facing pass 看过健康状态和失败 payload 的 owner:保留两种小 renderer 比让状态视图接受不存在的 projection 更易维护;没有相关的待清理并行状态。
本地验证必须分清:head 的 4 项新增启动失败测试全部通过,真实隔离的 Chat/Dashboard CLI 缺 bundle 均按预期显示 error 并返回 1;完整 tests/test_dashboard_command.py 在 base 为 5 failed/32 passed,在 head 为相同 5 failed/36 passed。五项同名失败均因两个干净检出都没有生成的 Chat bundle manifest,失效代码与断言不在本 PR 改动内,不是这个 PR 造成的红灯。若合并门槛要求整套绿,请先单独构建 bundle 或按仓库流程处理就绪度;不应因此要求本 PR 修复别的代码。没有查询或等待远端 CI。
我的整体评价
long_horizon 保持原有失败后重试路径,user_experience 则由无意义的空状态表变成可操作的本地错误。按相同 base/head 与实际命令核对,JSON schema、错误内容和 exit1 不变;默认 Markdown 行为变化已由新增用例及 PR 说明披露。没有 default-off 能力、actor 生命周期或共享控制面语义扩张,语义对齐属于局部呈现。改动与问题成比例,风险集中在 bundle 尚未构建的独立验证条件。基于已覆盖的改变不变量,我给出 APPROVE;合并就绪需另行满足仓库要求。
Goal And Delivered Outcome
loopx chat,loopx dashboardandloopx serve-statuscatch a start failure into{ok: false, error, gate?}and print it withrender_status_markdown. That renderer expects a status projection, so the default Markdown output was an empty status table (ok: False,registry: None,goals: None, …) and the error never appeared. A missing Chat bundle, whose error names the exactnpm run build:chatfix, looked like an unexplained broken status.loopx chatin a checkout without a built Chat bundle printed before a status table ofNonevalues; after it prints# LoopX chat could not start,- error: LoopX Chat bundle is missing, stale or invalid: … Run npm ci and npm run build:chat …and- next action: ….main.Scope And Continuation
support_control.pyserves the three start-failure branches (error, registry when known, gate next action). JSON output is unchanged (same payload and schema versions); only the default Markdown rendering of a failed start changes.Validation
staticpassedruff checkon the changed files.unitpassedpytest tests/test_dashboard_command.py -k start_failure: parametrized overchat,dashboard,serve-status— the Markdown output starts with the failure heading, contains the error and no status table;chatalso names the gate's next action, and--format jsonkeepsloopx_chat_start_v0with the original error.regression_paritypassedreal_entrypointpassedpython -m loopx.cli … chat --no-openin a checkout without a built Chat bundle, against an isolated synthetic registry: prints the heading, the missing-bundle error with itsnpm run build:chatfix, and the next action.integrationpassedexamples/cli-support-control-command-modularization-smoke.pypasses.unitnot_runtests/test_dashboard_command.py(test_launch_dashboard_*,test_dashboard_command_in_installed_mode_resolves_packaged_web_bundle) fail identically on a cleanmaincheckout without a built Chat bundle; the failing set is the same with and without this change.Frontend / Visual Evidence
Type of Change
LoopX Area
Technical Direction
Shared-authority RFC fixture impact
N/A
Boundary Checklist
none.Signed-off-bytrailer (git commit -s).Behavior change disclosure: CLI Markdown output of a failed
chat/dashboard/serve-statusstart changes from an (empty) status table to a failure summary. JSON output and exit codes are unchanged.Future-facing refactor pass: considered a shared failure renderer in
loopx/presentation/renderers/; kept it private tosupport_control.pybecause only these three start branches use it.