Skip to content

fix(cli): show the error when a local LoopX service cannot start - #5267

Merged
huangruiteng merged 3 commits into
loopx-project:mainfrom
songoow:codex/cli-chat-start-error-markdown
Sep 29, 2026
Merged

huangruiteng merged 3 commits into
loopx-project:mainfrom
songoow:codex/cli-chat-start-error-markdown

Conversation

@songoow

@songoow songoow commented Sep 28, 2026

Copy link
Copy Markdown
Collaborator

Goal And Delivered Outcome

  • Outcome basis / optional anchor: reproduced defect (no issue).
  • Goal/source and gap: loopx chat, loopx dashboard and loopx serve-status catch a start failure into {ok: false, error, gate?} and print it with render_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 exact npm run build:chat fix, looked like an unexplained broken status.
  • Observable before → after: loopx chat in a checkout without a built Chat bundle printed before a status table of None values; 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: ….
  • Issue/task and intended base: none; base main.

Scope And Continuation

  • Completed scope and remaining work: complete within this scope. One small renderer in support_control.py serves 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.
  • Slice boundary / successor: none.

Validation

  • Tested revision: 095b2ed
  • Run state: finished
  • Input classes: synthetic
Check kind Result Public-safe evidence / limitation
static passed ruff check on the changed files.
unit passed pytest tests/test_dashboard_command.py -k start_failure: parametrized over chat, dashboard, serve-status — the Markdown output starts with the failure heading, contains the error and no status table; chat also names the gate's next action, and --format json keeps loopx_chat_start_v0 with the original error.
regression_parity passed Failing-before check: with the source change reverted, all 4 new tests fail.
real_entrypoint passed python -m loopx.cli … chat --no-open in a checkout without a built Chat bundle, against an isolated synthetic registry: prints the heading, the missing-bundle error with its npm run build:chat fix, and the next action.
integration passed examples/cli-support-control-command-modularization-smoke.py passes.
unit not_run 5 tests in tests/test_dashboard_command.py (test_launch_dashboard_*, test_dashboard_command_in_installed_mode_resolves_packaged_web_bundle) fail identically on a clean main checkout without a built Chat bundle; the failing set is the same with and without this change.
  • Coverage and gaps: all three changed call sites are exercised; none identified.

Frontend / Visual Evidence

  • UI impact: none

Type of Change

  • Bug fix
  • Test update

LoopX Area

  • Host or runtime integration

Technical Direction

  • Direction / acceptance reference, when applicable: Operator surface and IM integration.

Shared-authority RFC fixture impact

N/A

Boundary Checklist

  • Neither the diff nor this PR body/comments/attachments disclose private state, credentials, raw traces or verifier output, internal links, or local machine paths.
  • I did not duplicate maintainer-owned benchmark work unless a maintainer split out a public issue for it.
  • I kept the change scoped to the linked issue/task.
  • I completed the visual evidence section for UI changes, or marked UI impact none.
  • Every commit includes a DCO Signed-off-by trailer (git commit -s).

Behavior change disclosure: CLI Markdown output of a failed chat / dashboard / serve-status start 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 to support_control.py because only these three start branches use it.

`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 huangruiteng left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 和持久化记录未改。

关键代码讲解

  1. _start_failure_markdown:消费已存在的 error、可选 registry 及 gate.next_action,生成短的命令级失败摘要。它只负责输出,不重新分类异常或制造 Goal status。
  2. serve-status 启动 catch:仍由本地服务启动异常进入,payload 和退出码保持原有语义,只把 Markdown renderer 换到失败专用路径。
  3. dashboard 启动 catch:实际隔离命令使用不存在的 assets_dir 后返回 1,显示 bundle 缺失及修复提示;不会再打印健康状态表。
  4. 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;合并就绪需另行满足仓库要求。

@huangruiteng
huangruiteng merged commit 51089ff into loopx-project:main Sep 29, 2026
25 of 31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants