Skip to content

feat(app): 将 Hello Robot 精简为单次开心行为 - #68

Closed
Mr-KID-github wants to merge 4 commits into
mainfrom
codex/hello-robot-behavior-showcase
Closed

feat(app): 将 Hello Robot 精简为单次开心行为#68
Mr-KID-github wants to merge 4 commits into
mainfrom
codex/hello-robot-behavior-showcase

Conversation

@Mr-KID-github

@Mr-KID-github Mr-KID-github commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

背景

新用户通过 watcherobot app init 创建项目后,需要得到最小、可理解、可验证的 Hello World。默认项目不应额外承担随机轮播、待机或睡眠等产品行为。

改动

默认 Hello Robot

  • 连接兼容机器人后只播放一次 happy 行为
  • 通过 Behavior Job 的完成事件等待整个行为自然结束
  • 行为完成后输出成功日志并正常退出
  • 未连接兼容机器人时保留 robot setup 引导并正常结束
  • 不包含随机表情轮播、灯光提示、清醒待机或睡眠逻辑

安装与版本排查

  • 文档增加 python -m pip show watcherobot 和模块来源路径检查
  • 明确 Editable project location 必须指向当前选定的源码 checkout
  • 明确从另一份旧 checkout 执行 pip install -e . 会把环境重新指回旧源码
  • 明确 app init 复制当时模板,SDK 更新不会自动修改已经生成的 app.py
  • 提供新模板的 PowerShell 自检命令

范围边界

  • 只修改 SDK 的项目生成模板、examples/hello_robot、测试和文档
  • 不修改 ESP32 系统行为
  • 不修改 Agent 的闲置或睡眠机制

验证

  • 全量 pytest 通过(本机访问 localhost 时显式设置 NO_PROXY=127.0.0.1,localhost
  • 相关定向测试 43 项通过
  • mypy src 通过
  • git diff --check 通过
  • 使用 conda watcherobot 环境从当前 editable 源码实际生成 D:\Project\hello_robot_verified
  • 生成的 app.py 仅包含一次 app.robot.behavior.play("happy", repeat=1)job.wait(20.0)

默认项目先播放 happy 行为并在支持时闪烁灯光,随后持续洗牌轮播固件明确支持的行为状态,直到用户按 Ctrl+C 停止。随机轮播会避免相邻轮次重复首尾行为,并对单个行为不可用的情况进行提示后继续运行。

同时为 app run 增加 Runtime、机器人连接及 Application 启动的彩色状态提示,更新中英文安装与 CLI 文档、示例项目,并补充项目生成、随机行为和在线/离线运行状态测试。
@github-actions

Copy link
Copy Markdown

🤖 Luxiao PR 审查报告

🤖 PR 审查报告

PR: feat(app): 优化 Hello Robot 的持续随机行为体验
变更: 14 个文件,+251 / -17


维度一:代码质量

🏗️ 架构视角 — ⚠️

整体方向符合现有架构:

  • 默认应用继续通过 ApplicationContext 使用 Runtime 注入的机器人能力。
  • CLI 状态提示仍位于 watcherobot.cli,没有把展示逻辑侵入 Runtime。
  • 灯光能力使用 supports("light") 做能力检测,兼容无灯光设备。
  • 行为轮播被拆分为 _shuffled_demo_behaviors()_flash_success()_showcase_behaviors(),职责基本清晰。

但存在两个架构问题:

  1. 没有正确管理 Behavior Job 生命周期

    现有 happy 行为明确采用:

    job = await asyncio.to_thread(app.robot.behavior.play, ...)
    await asyncio.to_thread(job.wait, 20.0)

    新增轮播却只调用 play(),没有等待返回的 job:

    await asyncio.to_thread(
        app.robot.behavior.play,
        behavior_id,
        repeat=1,
    )
    await asyncio.sleep(DEMO_BEHAVIOR_SECONDS)

    这会带来以下风险:

    • 行为超过 4 秒时,下一个行为可能与前一个重叠或抢占。
    • play() 成功但 job 后续失败时,异常无法被当前 try/except 捕获。
    • 无法确认行为真实完成,日志中的“Playing”仅代表请求已提交。
    • Ctrl+C 时可能遗留仍在执行的行为 job,具体取决于 Runtime 清理机制。

    当前实现将“行为完成”错误地等同于“等待固定 4 秒”,与项目已有 Job API 使用方式不一致。

  2. 模板与示例出现实现分叉

    src/watcherobot/application/project.py 中的生成模板具备:

    • 灯光反馈
    • 单行为异常处理
    • 跳过后的退避
    • 成功状态日志

    examples/hello_robot/app.py 却没有这些能力,甚至没有捕获单个行为失败。PR 描述称“同步更新 examples/hello_robot”,但实际只同步了持续轮播的部分逻辑。

    两份近似实现已经发生行为差异,后续容易继续漂移。至少应通过共享模板源、golden fixture 或行为级测试确保一致性。


🎨 产品视角 — 🔴

持续 Hello Robot 演示对新用户体验有明显改善:

  • 机器人不会播放一次 happy 后立即停住。
  • 蓝色闪灯能提供直接的连接成功反馈。
  • 随机轮播比固定重复更自然。
  • 单个行为失败时继续演示的产品策略合理。
  • 离线模式继续提供 setuppair 引导,没有阻断纯软件开发。

但目前有三处会直接误导用户:

  1. “Application is running”并不代表 Application 已运行

    CLI 在启动 POST 请求返回后立即输出:

    print(_styled("✓ Application is running.", "green"))

    此时没有查询 Application 的实际状态。POST 成功最多只能证明启动请求被 Runtime 接受,不能证明进程已经进入 running 状态。

    新测试甚至让 /daemon/status 返回:

    {"application": {"state": "ended"}}

    同时仍断言输出:

    ✓ Application is running.
    

    这证明当前测试固化了误报:Application 已结束时仍显示绿色“正在运行”。

    建议二选一:

    • 查询 /daemon/status,确认状态确实为 running 后再输出该提示;
    • 如果只验证了请求成功,文案改为“Application start requested”或“Application launch accepted”。

    当前英文和中文文档所称“确认真实的 Application 状态”与实现不符。

  2. 离线 Hello World 不再输出成功日志

    原来的首条日志是:

    "Hello, WatcheRobot! Your first Application worked."

    现在改为:

    "Hello, WatcheRobot! Starting your first Application."

    真正的成功日志位于机器人行为执行之后:

    "✓ Your first WatcheRobot Application is running successfully!"

    当没有兼容机器人时,代码会进入原有的离线分支并提前返回,因此生成应用不再满足 README 和 CLI 文档中的承诺:

    The generated app.py always logs a Hello World success.

    这也是离线开发体验的回退。应在 ApplicationContext 成功建立后输出明确的 Application 成功日志,把“应用成功运行”和“机器人演示成功”分开表达。

  3. “行为不可用时警告并继续”没有被完整实现

    当前异常处理只覆盖 behavior.play() 的同步提交阶段。若行为请求成功创建 job,但固件之后报告失败,由于没有调用 job.wait(),用户既看不到警告,也无法知道行为没有实际执行。

    因而 PR 描述中的容错承诺尚未真正成立。

此外,将默认应用改为无限运行属于有意设计,但仍是明显的生命周期语义变化。文档已说明 Ctrl+C,这一点可以接受;建议补充自动化场景下的退出方式或明确默认示例是交互式应用。


📏 规范视角 — ⚠️

做得好的部分:

  • 行为状态 ID 有明确注释,避免与表情或动画资源 ID 混淆。
  • 函数命名和类型标注清楚。
  • 中英文文档、安装文档和 CLI 参考同步范围较完整。
  • if __name__ == "__main__" 使生成脚本可以通过 runpy 安全加载测试。
  • 洗牌逻辑能够避免跨轮边界的直接重复。
  • 灯光失败不会导致整个示例退出,符合演示应用的容错要求。

需要改进:

  1. 异常捕获过宽

    except Exception as exc:

    对默认生成模板而言,宽泛捕获可以理解,但最好捕获 SDK 定义的设备、能力或 Job 执行异常。否则编程错误也可能被包装成“行为不可用”,掩盖真实缺陷。

  2. 返回值无实际用途

    _print_application_robot_guidance()None 改为 bool,但调用方没有使用返回值:

    _print_application_robot_guidance(state.control_url)

    这增加了无效接口语义。若后续不根据在线状态调整输出,应继续返回 None

  3. 测试以源码字符串断言为主

    新增测试大量使用:

    assert "while True:" in source
    assert "random.shuffle(behaviors)" in source

    这些测试只能证明源码中存在字符串,不能证明:

    • 行为 job 被正确等待;
    • 单个行为失败后会继续;
    • 灯光不支持时不会调用灯光 API;
    • 灯光失败时仅告警;
    • 跨轮不会重复;
    • Ctrl+C 或任务取消能正确清理;
    • 示例与生成模板行为一致。

    _shuffled_demo_behaviors() 的测试有一定行为覆盖,但核心异步流程仍未被执行测试覆盖。

  4. 示例与功能描述不一致

    examples/hello_robot/app.py 没有:

    • 灯光成功反馈;
    • 单行为失败告警;
    • 行为失败后继续的显式处理。

    如果示例有意保持精简,应在 PR 描述和文档中明确;否则应与生成模板保持一致。


⚠️ 损伤视角 — 🔴

当前存在以下潜在回归:

  1. 行为可能重叠或被意外抢占

    固定 sleep(4) 不能代替 job.wait()。具体表现取决于固件行为时长和调度策略。

  2. 异步行为失败被静默遗漏

    只捕获 play(),不等待 Job,无法发现设备侧执行失败。

  3. CLI 输出虚假的运行成功状态

    启动请求返回并不等于 Application 已运行。应用立即崩溃或已经结束时,用户仍会看到绿色成功提示。

  4. 离线成功语义回退

    无机器人时,生成应用不再输出文档承诺的 Hello World 成功日志。

  5. 默认命令由短任务变为永久任务

    这是产品预期,但可能使原有教程脚本、冒烟测试或调用 watcherobot app run 并等待自然退出的自动化流程挂起。当前 Diff 没有看到对这一兼容性变化的专门测试或迁移说明。

  6. 测试未覆盖真实风险路径

    “全量 pytest 通过”只能说明现有断言通过;新增断言本身允许 /daemon/statusended 时仍输出“Application is running”,因此测试通过不能消除状态误报风险。


维度二:意图分析

🎯 意图提炼

该 PR 希望把默认 Hello Robot 从“一次性动作示例”升级为持续、可感知、具备容错能力的机器人演示,同时通过 CLI 的绿色状态提示改善 Runtime、机器人连接和 Application 启动过程的可理解性。

🔀 偏离检测

意图与项目方向一致,没有明显功能蔓延。默认项目应该让新用户快速看到机器人持续响应,CLI 也应该明确反馈当前阶段状态。

实现总体围绕该目标展开,但有三处偏离原始意图:

  • “持续演示”实现成了固定间隔提交行为,而不是可靠地顺序执行行为。
  • “单行为不可用时告警”只覆盖请求提交异常,没有覆盖 Job 执行异常。
  • “确认 Application 运行状态”实际只确认了启动请求成功,且测试接受 Application 已结束时仍显示运行成功。

因此问题不在功能方向,而在成功状态和异步生命周期的定义不够严谨。


Merge 建议

🔴 不建议合并

建议至少完成以下修改后再合并:

  1. 保存每次 behavior.play() 返回的 Job,并通过 job.wait() 等待完成或超时。
  2. 对 Job 执行失败输出行为不可用警告,并继续下一项,确保容错承诺真实成立。
  3. 明确超时后的处理策略,避免前一个行为仍在运行时直接启动下一个行为。
  4. 只有确认 Application 状态为 running 后,才显示 ✓ Application is running.;否则将文案改为“启动请求已接受”。
  5. 修复离线分支,确保没有机器人时仍输出明确的 Hello World/Application 启动成功日志。
  6. examples/hello_robot 与生成模板保持一致,或者明确说明示例为何有意精简。
  7. 增加行为级异步测试,至少覆盖:
    • Job 正常完成;
    • play() 失败;
    • job.wait() 失败或超时;
    • 失败后继续执行下一行为;
    • 无灯光能力;
    • 灯光执行失败;
    • Application 启动后立即 ended/failed;
    • 离线模式仍输出应用成功状态。
  8. 删除未使用的 _print_application_robot_guidance() 布尔返回值,或真正使用它。

总结: 产品方向正确,文档覆盖也较完整,但当前实现对 Behavior Job 和 Application 状态的判断不可靠,可能造成行为重叠、失败静默以及绿色状态误报;这些属于默认新手体验中的核心路径问题,修复前不建议合并。

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 58d3eb0d0e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread examples/hello_robot/app.py Outdated
"speechless",
"concentration",
"get",
"query",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Remove the unsupported query behavior

query is absent from the repository's verified ESP32-S3 v0.3.4 behavior-state catalog, whose documentation states that unknown IDs are rejected with not_found. Once every shuffled cycle reaches this entry, behavior.play() raises and this example has no exception handling, so the advertised continuous showcase terminates. Remove or replace this ID here and in the matching generated-project template.

Useful? React with 👍 / 👎.

async def main() -> None:
async with ApplicationContext.from_environment() as app:
app.logger.info("Hello, WatcheRobot! Your first Application worked.")
app.logger.info("Hello, WatcheRobot! Starting your first Application.")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Restore the unconditional Hello World success

When no robot is connected, supports("behavior") is false and the generated application now logs only that it is starting before returning. This removes the offline success message even though the generated README and CLI documentation still promise that the Hello World application always logs a success, undermining the first-run path before robot setup. Keep an unconditional success message before the capability check and report hardware-specific success separately.

Useful? React with 👍 / 👎.

Comment thread src/watcherobot/application/project.py Outdated
async def _showcase_behaviors(app: ApplicationContext) -> None:
previous_behavior = None
app.logger.info("Press Ctrl+C to stop the behavior showcase.")
while True:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Stop the active behavior when the showcase is terminated

Most entries in this loop are firmware states that run until explicitly replaced or stopped, but stopping watcherobot app run terminates the child process without sending behavior.stop(), and ApplicationContext.close() also only closes the transport. Consequently, pressing Ctrl+C as instructed can leave the robot indefinitely displaying the last looping behavior after the Application has stopped. Add shutdown cleanup coordinated with the Runtime, or avoid persistent states that cannot be reset when the process is terminated.

Useful? React with 👍 / 👎.

Comment thread src/watcherobot/cli.py
Comment on lines 1283 to +1286
method="POST",
timeout=APPLICATION_START_TIMEOUT_SECONDS,
)
print(_styled("✓ Application is running.", "green"))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Check the start response before reporting success

POST /daemon/application/start returns the current Application status, but this call discards it and unconditionally prints that the Application is running. A short-lived Application can satisfy startup readiness and then exit or fail before the REST response is serialized—in which case the response already says ended or error, yet the CLI prints a green running confirmation immediately before reporting the terminal state. Inspect the returned application.state before emitting this success message.

Useful? React with 👍 / 👎.

Comment thread src/watcherobot/cli.py
print(f"Running Application: {application_path}")
print("Press Ctrl+C to stop.")
state, _reused = ensure_runtime()
print(_styled("✓ Runtime is ready.", "green"))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep status output encodable on supported Windows consoles

On supported Windows Python 3.10–3.12 installations, redirected or piped stdout commonly uses a legacy encoding such as cp1252 or cp936, neither of which can encode . In that context this first new status print raises UnicodeEncodeError, so watcherobot app run > log.txt aborts before selecting or starting the Application; just_fix_windows_console() does not change the encoding of redirected streams. Use an ASCII status marker or an encoding-safe output strategy for these newly added messages.

Useful? React with 👍 / 👎.

CLI 复用 SDK-owned Runtime 前同时校验控制协议和 SDK 版本;发现旧版本或缺失身份时,按对应 state root 安全停止旧进程,再从当前环境启动匹配 Daemon,避免新版 Application 清单被旧运行时拒绝。

后台进程显式继承当前 CLI 所加载的 SDK 导入根,确保源码开发、editable 安装和测试环境不会意外加载同一解释器中的旧包。同步补充版本复用、自动重启、源码一致性测试,以及中英文安装、CLI 与控制协议文档。
@github-actions

Copy link
Copy Markdown

🤖 Luxiao PR 审查报告

🤖 PR 审查报告

PR: feat(app): 优化 Hello Robot 的持续随机行为体验
变更: 18 个文件,+470 / -28


维度一:代码质量

🏗️ 架构视角 — ⚠️

Hello Robot 模板、示例、CLI 输出和中英文文档同步更新,整体仍沿用现有 ApplicationContext → robot capability API 的调用路径,没有引入新的业务层耦合。将控制协议版本提取到独立的 control/protocol.py,也比从 REST 实现模块反向引用常量更合理。

但 Runtime 版本恢复逻辑存在明显的生命周期边界问题:

if _runtime_matches_current_sdk(status):
    return existing, True
stop_runtime(state_root=resolved_state_root)

这里只要 SDK 版本不一致就会停止现有 Runtime,没有验证:

  • Runtime 是否确实由当前 SDK CLI 启动;
  • 是否由 Desktop 或其他客户端管理;
  • 是否正在运行其他 Application;
  • 当前 CLI 是否比 Runtime 更新。

文档将目标限定为“旧版 SDK-owned Daemon”,但代码中没有对应的 owner 身份字段或所有权判断。旧版本 CLI 连接到较新的 Daemon 时,同样会将其停止并降级为旧版本,可能影响 Desktop、其他终端或正在运行的 Application。

这项 Runtime 生命周期改动也超出了“持续随机行为和运行反馈”的主要范围,属于高风险的附带改动。建议单独拆分 PR,或至少补齐明确的 Runtime ownership 合同。


🎨 产品视角 — ⚠️

正向改进:

  • 默认项目不再播放一次 happy 后立即退出,首次体验更持续、直观。
  • 只在 online=True 时显示“机器人已连接”,避免离线运行时误报。
  • 保留 setuppair 恢复指引,未阻断离线 Application。
  • 灯光能力通过 supports("light") 检测,能力降级路径合理。
  • 明确提示 Ctrl+C,持续运行行为对用户可见。
  • 单个演示行为设计为失败后继续,符合 Hello World 示例的容错目标。

但行为轮播的实现并未真正保证这些产品语义。behavior.play() 从同一文件中的 happy 调用可以看出会返回一个 job:

job = await asyncio.to_thread(
    app.robot.behavior.play,
    "happy",
    repeat=1,
)
await asyncio.to_thread(job.wait, 20.0)

而轮播中只调用了 play(),没有等待 job:

await asyncio.to_thread(
    app.robot.behavior.play,
    behavior_id,
    repeat=1,
)
await asyncio.sleep(DEMO_BEHAVIOR_SECONDS)

这会带来三个问题:

  1. 四秒后可能在上一行为尚未完成时启动下一行为,产生重叠、抢占或命令积压。
  2. 如果行为不可用的错误发生在 job 执行阶段而不是 play() 提交阶段,当前 try/except 捕获不到。
  3. 日志在任务提交后立即输出“Playing”,不能证明行为实际执行成功。

因此,“单个行为不可用时警告并继续”和“持续轮播行为”目前只在同步提交失败的情况下成立,不能覆盖异步 job 失败。

此外,绿色提示:

✓ Application is running.

实际只表示 /daemon/application/start 请求成功。虽然与 PR 描述一致,但从用户语义看仍偏强;Application 可能启动后立即异常退出。更准确的措辞可以是“Application started”,或者等待 Runtime 状态进入明确的 running 状态后再输出“is running”。


📏 规范视角 — ⚠️

做得较好的部分:

  • DEMO_BEHAVIORSDEMO_BEHAVIOR_SECONDS 命名清晰。
  • 注释明确区分 behavior-state ID 与裸表情/动画资源 ID,避免 API 概念混淆。
  • 新函数均有完整类型标注。
  • 中英文 CLI、安装和 Application 文档基本同步。
  • if __name__ == "__main__" 使生成脚本可通过 runpy 导入测试,优于模块导入即执行。
  • DAEMON_CONTROL_PROTOCOL_VERSION 独立为协议常量,模块职责更清楚。

需要改进:

1. 模板与示例实现不一致

生成模板包含:

  • 灯光成功反馈;
  • 单行为异常捕获;
  • Ctrl+C 日志;
  • 成功运行日志。

examples/hello_robot/app.py 却没有这些逻辑,也没有等待行为 job。既然 PR 声明“同步更新 examples/hello_robot”,示例应尽量复用或完整体现模板行为,否则文档、模板和示例将形成三套事实。

2. except Exception 范围过宽

Hello World 模板强调不中断体验可以理解,但直接捕获所有 Exception 可能隐藏编程错误、参数合同变化及内部 SDK 缺陷。应优先捕获机器人能力或 job 执行相关的稳定异常类型;如果 SDK 暂无合适异常基类,至少应在注释中说明这里为何需要宽捕获。

3. 测试偏向源码字符串断言

新增测试大量检查:

assert "while True:" in app_source
assert "random.shuffle(behaviors)" in app_source
assert "app.robot.lights.play_effect" in app_source

这些测试只能证明文本存在,无法证明:

  • behavior job 会被等待;
  • 一个行为结束后才开始下一个;
  • job 异步失败会被捕获并继续;
  • 灯光失败不会中断;
  • Ctrl+C 或取消能正常退出;
  • Runtime 不会错误停止其他 owner 的进程。

当前测试数量较多,但关键运行语义覆盖不足。


⚠️ 损伤视角 — 🔴

存在两个阻塞合并的问题。

阻塞问题 1:轮播行为没有等待 job 完成

生成模板和示例都没有调用返回 job 的 wait()。这可能导致行为重叠、积压以及错误无法被捕获,直接违背 PR 描述中的容错和持续轮播语义。

建议使用类似逻辑:

job = await asyncio.to_thread(
    app.robot.behavior.play,
    behavior_id,
    repeat=1,
)
await asyncio.to_thread(job.wait, BEHAVIOR_TIMEOUT_SECONDS)

如果产品需要行为间隔,应在 job 完成后再 sleep,而不是使用固定 sleep 代替完成确认。同时要对 job 超时和设备断连定义明确策略。

阻塞问题 2:版本不匹配时无条件停止现有 Runtime

ensure_runtime() 没有所有权或占用状态判断,可能停止:

  • Desktop 启动的 Runtime;
  • 另一终端启动的 Runtime;
  • 较新版本 SDK 启动的 Runtime;
  • 正在承载其他 Application 的 Runtime。

这属于用户可感知的破坏性生命周期操作。不能仅依赖版本不一致就自动停止进程。

至少需要满足以下一种方案:

  1. /daemon/status 返回明确的 owner、launcher identity 或 lifecycle policy,只自动替换当前 CLI 所拥有的 Runtime;
  2. 确认 Runtime 空闲且属于 SDK CLI 后再停止;
  3. 对未知 owner、较新版本或正在运行 Application 的 Runtime 拒绝自动停止,并给出人工处理提示;
  4. 将该改动拆分为独立 PR,对 Desktop/CLI 共享 Runtime 的生命周期合同单独评审。

其他风险

/daemon/status 使用固定 0.5 秒超时且没有捕获 CliError

status = _request_json(
    existing.control_url,
    "/daemon/status",
    timeout=0.5,
)

Daemon 在高负载、冷启动或短暂阻塞时,app run 可能直接失败,既不会复用也不会进入恢复路径。建议有限重试,并区分“身份不匹配”和“暂时无法获取状态”,不能将瞬时通信问题等同于版本不匹配。


维度二:意图分析

🎯 意图提炼

该 PR 的核心意图是让新用户创建的 Hello Robot Application 提供持续、明显且可容错的机器人反馈,并让 app run 更准确地展示 Runtime、机器人连接和 Application 启动状态。

同时,PR 额外引入了 CLI 与后台 Daemon 的 SDK 版本一致性检查及自动重启机制,试图解决升级后新旧 Application 清单合同不兼容的问题。

🔀 偏离检测

Hello Robot 持续行为、灯光反馈、在线状态提示与 PR 标题和背景高度一致,属于合理的首次使用体验优化。

Daemon 版本身份校验和自动停止/重启则存在明显的功能扩张:

  • PR 标题没有体现 Runtime 生命周期策略变化;
  • 风险远高于 UI 输出和模板更新;
  • 涉及 Desktop、SDK CLI、后台 Application 的共享进程所有权;
  • 当前实现没有完成文档所声称的“仅替换旧版 SDK-owned Daemon”约束。

建议将 Runtime 自动恢复拆为独立 PR。若必须保留在本 PR 中,至少应更新标题和描述,明确这是一次 Runtime 生命周期合同变更,并补齐所有权、占用状态、升级/降级方向及兼容性测试。


Merge 建议

🔴 不建议当前状态直接合并

合并前至少需要完成:

  1. 轮播中保存 behavior.play() 返回的 job,并等待 job 完成或超时。
  2. 确保 job 执行阶段的“行为不可用”错误能够被捕获、记录并继续下一项。
  3. 明确固定四秒是行为完成后的间隔,还是允许抢占的产品策略;不能用 sleep 隐式代替 job 完成。
  4. 为 Runtime 自动重启增加明确的 ownership 和 Application 占用保护,防止停止 Desktop、其他 CLI 或较新版本管理的 Runtime。
  5. /daemon/status 超时增加有限重试或稳定的错误处理。
  6. 增加行为级测试,而不只是源码字符串断言,至少覆盖:
    • job 被等待;
    • job 失败后继续;
    • 行为不会并发重叠;
    • 灯光不支持及灯光失败;
    • 循环取消;
    • Runtime owner 不匹配;
    • Runtime 正在运行 Application;
    • 当前 CLI 版本低于 Runtime;
    • 状态接口暂时超时。
  7. 统一 examples/hello_robot 与生成模板的灯光、容错和 job 等待语义。

总结: 产品方向正确,文档和运行反馈也有明显提升,但行为 job 未等待以及 Runtime 无条件停止属于实质性损伤风险;修复这两个阻塞问题后再考虑合并。

默认项目在完整播放 happy 后,改用 animation 域随机播放机器人实际公布的一次性静默表情,并等待当前 Job 的完成事件后再开始下一项,移除固定 4 秒切换造成的待机空档和动画截断。

候选资源会按 available_ids 过滤、提前预取并避免洗牌边界立即重复;没有兼容表情时由示例 Application 主动保持 awake_idle。该改动只作用于生成的 Hello Robot 和配套示例,不修改 ESP32 系统待机或 Agent 睡眠机制。同步更新中英文文档及生成项目、示例回归测试。
@Mr-KID-github Mr-KID-github changed the title feat(app): 优化 Hello Robot 的持续随机行为体验 feat(app): 优化 Hello Robot 的静默表情连续播放体验 Aug 18, 2026
@github-actions

Copy link
Copy Markdown

🤖 Luxiao PR 审查报告

🤖 PR 审查报告

PR: feat(app): 优化 Hello Robot 的持续随机行为体验
变更: 18 个文件,+631 / -28


维度一:代码质量

🏗️ 架构视角 — ⚠️

整体分层基本合理:

  • Hello Robot 模板继续放在 application/project.py,没有把演示逻辑侵入核心机器人 API。
  • Daemon 协议版本提取到独立的 control/protocol.py,避免 CLI 从 REST 实现层导入常量,方向正确。
  • stop_runtime(state_root=...) 保留自定义 Runtime state root,修复了隔离 Runtime 可能误操作默认目录的问题。
  • 通过子进程 PYTHONPATH 固定 SDK 导入根目录,能够解决 editable 安装场景下 CLI 与后台加载不同包的问题。

但存在一个需要合并前处理的生命周期风险:

1. SDK 版本不一致时直接停止现有 Runtime,缺少安全条件

ensure_runtime() 当前逻辑是:

if _runtime_matches_current_sdk(status):
    return existing, True
stop_runtime(state_root=resolved_state_root)

这里只根据 control_protocolsdk_version 判断,随后直接调用 /daemon/stop。代码没有验证:

  • 当前 Daemon 是否确实属于本 CLI 可管理的 SDK-owned Runtime;
  • 是否有 Application 正在运行;
  • 是否存在活跃机器人会话、配对流程或其他客户端;
  • 版本不一致是否真的意味着合同不兼容。

因此,一次普通的 watcherobot app run 可能静默终止已有 Application 或机器人连接。PR 文档反复使用“旧 SDK-owned Daemon”作为安全前提,但 Diff 中没有看到与该 ownership 判断对应的代码。

建议:

  • 至少在重启前检查 /daemon/status 中 Application/session 状态;
  • 若 Runtime 正忙,明确报错并提示用户执行 watcherobot daemon stop,不要自动终止;
  • 如果系统已有 Daemon owner/launcher identity,应同时验证 owner;
  • 增加“版本不匹配但 Runtime 正忙时不自动停止”的测试。

2. 将完整 SDK 版本作为 Runtime 兼容合同,策略偏重

当前要求:

runtime.get("control_protocol") == DAEMON_CONTROL_PROTOCOL_VERSION
and runtime.get("sdk_version") == __version__

这意味着任何版本字符串差异,包括预发布版本、本地版本标记或仅文档级发布差异,都会强制重启 Runtime。它解决了本次 manifest 新旧不兼容,但把“身份一致”与“协议兼容”绑定在了一起。

短期可以接受,但长期建议为 Application manifest/runtime contract 单独维护兼容版本,而不是永久依赖 SDK 完整版本严格相等。


🎨 产品视角 — ⚠️

产品目标合理:默认项目不再“一闪而过”,Runtime、机器人和 Application 状态也更直观,明显改善首次使用体验。

做得较好的部分:

  • 离线运行时不再误报机器人已连接;
  • Runtime、机器人和 Application 分别输出成功状态,用户能理解当前进度;
  • 没有机器人时继续保留 setuppair 引导;
  • 使用能力检测和 available_ids,比直接假定所有机器人都支持固定资源更可靠;
  • 洗牌边界避免连续重复;
  • 灯光属于可选增强,失败不会阻断主流程;
  • 通过 Ctrl+C 结束持续演示,符合 CLI 示例应用的直觉。

但产品定义与实现之间存在明显不一致:

1. PR 描述、标题和实际实现不是同一套行为模型

PR 描述称:

  • 持续轮播“行为状态”;
  • 行为池为 smileshocksunglassesspeechlessconcentrationgetqueryfondle_love
  • 注释应明确它们是行为状态 ID,而不是裸表情或动画资源 ID。

实际代码则使用:

SILENT_EXPRESSIONS = (
    "fondle_love",
    "speaking_blink",
    "speaking_eye",
    "click_eye",
    "query",
)

并通过:

app.robot.animation.available_ids
app.robot.animation.prefetch(...)
app.robot.animation.play(...)

进行裸动画播放。源码注释也明确写的是 “animation IDs”。

这不是命名小问题,而是产品行为变化:

  • 行为状态可能包含完整的状态机、声音或身体动作;
  • animation 只是资源播放;
  • PR 描述中列出的八个 ID 与实际五个 ID 不一致;
  • 标题和背景强调“持续随机行为”,实现与文档却改成了“静默表情展示”。

需要在合并前确定唯一产品定义:究竟是行为状态轮播,还是裸动画轮播。随后同步修改 PR 描述、源码注释、常量名称、文档和测试。

2. 示例项目与生成模板体验不一致

src/watcherobot/application/project.py 中的生成模板包含:

  • 蓝灯闪烁;
  • animation capability 检查;
  • play/wait 异常处理;
  • 超时取消;
  • awake_idle 失败降级;
  • 单个表情失败后继续。

examples/hello_robot/app.py

  • 没有成功灯光;
  • animation.play()job.wait() 出错会终止整个示例;
  • awake_idle 不可用时会直接退出;
  • 没有 TimeoutError 后的 cancel;
  • 与“单个行为不可用时警告并继续”的 PR 承诺不一致。

既然 PR 明确声称“同步更新 examples/hello_robot”,示例应与生成模板共享相同的错误恢复语义。否则用户复制官方 example,得到的体验反而比 app init 生成项目差。

3. “Application is running”只代表启动请求成功

CLI 在启动请求返回后立即输出:

print(_styled("✓ Application is running.", "green"))

这可以理解为“启动请求已被接受”,但尚未验证 Application 是否进入 running 状态。若 Daemon 异步启动后立即崩溃,用户仍会先看到绿色“正在运行”。

建议二选一:

  • 文案改为 ✓ Application start request accepted.
  • 或短暂查询 /daemon/status,确认状态进入 running 后再显示当前文案。

📏 规范视角 — ⚠️

优点:

  • 新增函数均有清晰职责,类型标注基本完整;
  • DAEMON_CONTROL_PROTOCOL_VERSION 从 REST 模块提取后依赖方向更清楚;
  • 中英文文档覆盖较完整;
  • _daemon_subprocess_environment() 对路径进行规范化去重,并保留已有 PYTHONPATH
  • 对非关键能力采用 warning + continue,符合示例应用的容错定位;
  • if __name__ == "__main__" 避免测试加载模板时直接执行应用。

需要改进:

1. 测试主要在检查源码字符串,而不是验证运行行为

例如多处测试依赖:

assert "while True:" in app_source
assert "random.shuffle(expressions)" in app_source
assert "app.robot.animation.play" in app_source

这类测试只能证明模板里存在某段文字,无法证明:

  • 表情超时后确实调用 cancel;
  • 单个表情异常后确实继续下一个;
  • 灯光异常不会影响演示;
  • 没有 animation 能力时正确进入 fallback;
  • 只有一个可用表情时不会错误交换;
  • Runtime 忙时是否被版本恢复逻辑终止;
  • /daemon/status 超时或返回异常结构时如何处理。

而且正常的代码重构、格式调整也可能导致测试失败。

建议通过 fake ApplicationContext、fake robot/job 和受控退出条件,直接调用模板中的辅助协程,验证可观察行为。字符串断言只保留用于确认模板确实包含必要入口即可。

2. 新增 Runtime 恢复路径的异常场景覆盖不足

当前覆盖了:

  • 相同版本复用;
  • 旧版本重启;
  • PYTHONPATH 包含当前 SDK 根目录。

还缺少:

  • control protocol 不匹配;
  • /daemon/status 超时、连接失败或返回非字典结构;
  • runtime 字段缺失;
  • stop_runtime() 超时;
  • 自定义 state root 从停止到重新启动的完整路径;
  • Runtime 正在执行 Application 时的处理;
  • 重启后的 Daemon 仍然报告错误版本时的处理。

3. 模板与 example 出现大段重复实现

目前两个文件已经产生行为漂移。后续修改 bug 时很容易只修一处。

如果不适合抽成公共运行模块,至少应:

  • 让 example 与 _APP_TEMPLATE 保持逐项一致;
  • 增加一个同步测试,验证两者的行为池和关键恢复逻辑一致;
  • 或由同一个模板源生成 example,降低维护成本。

⚠️ 损伤视角 — 🔴

当前最主要的回归风险是 Runtime 自动恢复策略可能造成未提示的破坏性操作。

阻断问题:版本不匹配可能终止正在使用的 Runtime

这个问题影响的不只是当前 app run

  • 用户已经运行的 Application 可能被停止;
  • 机器人连接和应用会话可能需要重新配对;
  • Desktop 与 SDK CLI 如果共享 Runtime state root,可能互相重启;
  • 版本字符串的轻微差异也会触发停止;
  • 用户看到的是“自动恢复”,但实际发生了运行中任务中断。

PR 描述中的实机验证已经表明 Daemon 重启后需要机器人重新进入 Python SDK 应用完成配对,这进一步说明该操作对用户状态有明显影响,不能无条件静默执行。

此外还有以下中等风险:

  1. examples/hello_robot 中单个动画异常会终止整个持续演示,与承诺不符。
  2. 表情超时取消后没有等待取消完成;如果底层 cancel 是异步生效,下一表情可能与前一表情短暂重叠。
  3. available_ids 只在进入演示时读取一次;连接状态或资源列表变化后不会刷新。
  4. /daemon/status 请求使用 0.5 秒超时,慢机器上可能直接让 app run 失败,而不是进入可解释的恢复路径。
  5. 无限运行是明确的产品变更,但会改变脚本、教程或自动化中依赖默认项目正常退出的既有行为;文档已说明,仍建议在 release note 中突出标注。

维度二:意图分析

🎯 意图提炼

该 PR 实际包含两个目标:

  1. 将默认 Hello Robot 从一次性 happy 演示升级为持续运行、具备能力检测和错误降级的随机静默表情 showcase。
  2. 修复 CLI 与后台 Daemon 加载不同 SDK 版本导致的 Application manifest 合同不兼容,并增强 app run 的状态反馈。

🔀 偏离检测

方向与项目的新用户 onboarding 和本地 Runtime 稳定性目标一致,但存在一定功能蔓延。

“Hello Robot 持续演示”和“Daemon 版本身份、自动停止与重启策略”是两个独立风险域:

  • 前者属于示例和产品体验;
  • 后者属于 Runtime 生命周期与兼容性合同。

当前 PR 把大范围文档、模板行为、CLI 输出和具有破坏性的 Daemon 生命周期变更放在一起,导致审查和回归定位困难。尤其 Daemon 自动恢复的风险明显高于 Hello Robot 体验优化。

同时,PR 描述仍以“behavior state”表述,而实现已经转向 animation,说明意图在开发过程中发生过变化但没有完整收敛。

理想情况下应拆为两个 PR;如果不拆,至少必须先统一行为模型,并补齐 Runtime 安全重启条件。


Merge 建议

⚠️ 有条件合并

当前不建议直接合并。满足以下条件后可以合并:

  1. 阻断:限制 Runtime 自动重启的破坏范围

    • 验证 Daemon ownership;
    • 检查是否存在运行中的 Application/session;
    • Runtime 正忙时不得静默停止,应报错并提供显式恢复命令;
    • 增加对应测试。
  2. 阻断:统一 PR 产品定义

    • 明确最终使用 behavior state 还是 animation ID;
    • 统一 PR 描述、标题、行为池、源码注释、文档和测试;
    • 如果最终选择 animation,应删除“这些是行为状态 ID”的错误承诺。
  3. 阻断:同步 examples/hello_robot 的容错语义

    • 单个 animation 不可用、等待超时或 fallback 失败时不能直接终止整个演示;
    • 与生成模板保持一致,或者明确说明 example 为简化版本。
  4. 建议:补充行为测试

    • 用 fake robot/job 验证异常继续、超时取消、灯光降级、无资源 fallback 和洗牌边界;
    • 减少只检查源码字符串的测试。
  5. 建议:校准 CLI 成功文案

    • 确认 running 状态后再显示“Application is running”,或将文案改为“启动请求已接受”。

总结: Hello Robot 持续反馈的产品方向正确,代码也具备较好的能力检测和降级意识;但 Runtime 版本不匹配时无条件停止现有 Daemon 存在真实的会话中断风险,同时 PR 描述、生成模板与官方 example 尚未形成一致合同,因此应在完成上述阻断项后再合并。

将 app init 生成的默认 Application 收敛为最小 Hello World:连接机器人后仅播放一次 happy 行为,等待完成事件后正常退出,不再附带随机表情轮播、灯光提示或待机逻辑。

同步精简 hello_robot 示例和初始化测试,补充源码可编辑安装路径与旧项目模板不会自动更新的中英文说明,帮助开发者识别多份源码 checkout 导致的版本混淆。
@Mr-KID-github Mr-KID-github changed the title feat(app): 优化 Hello Robot 的静默表情连续播放体验 feat(app): 将 Hello Robot 精简为单次开心行为 Aug 18, 2026
@github-actions

Copy link
Copy Markdown

🤖 Luxiao PR 审查报告

🤖 PR 审查报告

PR: feat(app): 优化 Hello Robot 的静默表情连续播放体验
变更: 19 个文件,+372 / -32


核心阻塞问题

1. 🔴 实际代码与 PR 标题、描述完全相反

PR 描述的核心目标是:

  • happy 完成后持续随机播放静默表情
  • 根据 available_ids 过滤资源
  • 等待动画真实完成事件
  • 预取当前及下一表情
  • 避免洗牌边界重复
  • 无兼容表情时保持 awake_idle
  • 持续运行到用户按 Ctrl+C
  • 支持蓝色灯光反馈

但本次 Diff 实际明确实现并通过测试锁定的是:

  • 只播放一次 happy
  • 等待最多 20 秒
  • 随后正常退出
  • 不允许出现随机播放
  • 不允许出现 while True
  • 不允许调用 animation.play
  • 不允许调用灯光接口

证据包括:

assert "random" not in app_source
assert "while True:" not in app_source
assert "app.robot.animation.play" not in app_source
assert "app.robot.lights.play_effect" not in app_source

文档也明确写成:

不包含随机轮播、灯光或待机逻辑。

因此当前提交不是“静默表情连续播放体验优化”,而是“将 Hello Robot 收敛为播放一次 happy 后退出”。这不是实现细节偏差,而是交付目标完全不同。

2. 🔴 Runtime 自动替换无法可靠识别 editable 源码旧进程

当前身份判断只比较:

runtime.get("control_protocol") == DAEMON_CONTROL_PROTOCOL_VERSION
and runtime.get("sdk_version") == __version__

但安装文档自己已经说明:

未发布 checkout 可能仍报告上一已发布版本。

这意味着两个不同 commit、不同 checkout,甚至包含不同 Application Manifest 合同的 editable 源码,很可能具有相同的 __version__。此时旧 Daemon 会被错误复用,PR 声称解决的:

editable 源码测试仍连接旧进程

仍然可能发生。

_daemon_subprocess_environment() 只能保证新启动的 Daemon 优先导入当前源码,无法识别已经运行、版本号相同但代码不同的旧 Daemon。

建议至少选择一种可靠身份:

  • 构建或源码 revision;
  • 包路径/安装根目录的稳定哈希;
  • manifest contract version;
  • 独立递增的 runtime compatibility version。

如果不准备支持同版本不同 checkout 的识别,则必须收窄文档和 PR 描述,不能承诺自动解决该场景。

3. 🔴 Daemon 自动停止的所有权边界不清晰

ensure_runtime() 在身份不匹配时直接执行:

stop_runtime(state_root=resolved_state_root)

但代码没有验证该进程是否真的是当前 CLI 可以管理的 “SDK-owned Daemon”。当前判断仅证明状态目录里存在一个活跃 Runtime,并未证明:

  • 它由当前 CLI 启动;
  • 它不是 Desktop 管理的 Daemon;
  • 当前调用方拥有替换该进程的生命周期权限。

文档声称只替换 “SDK-owned Daemon”,代码却没有对应的 ownership 检查。若 Desktop 和 CLI 可能共享状态目录,该逻辑可能导致 CLI 因 SDK 版本不同直接停止 Desktop 正在使用的 Runtime。

合并前需要明确并测试:

  • Runtime 所有权如何记录;
  • 哪些 owner 允许 CLI 自动停止;
  • 非 CLI-owned Runtime 遇到版本不匹配时应报错、复用其他状态目录,还是交给上层处理。

4. ⚠️ 旧 Runtime 状态查询失败时缺少恢复路径

新增逻辑直接调用:

status = _request_json(
    existing.control_url,
    "/daemon/status",
    timeout=0.5,
)

如果现有 Daemon:

  • /daemon/status 不兼容;
  • 暂时响应超过 0.5 秒;
  • 返回非预期响应;
  • 控制端口处于启动或退出过渡状态;

_request_json() 很可能直接抛出 CliError,导致 ensure_runtime() 终止,而不是执行文档承诺的自动替换。

当前测试只覆盖:

  • 状态正常且版本匹配;
  • 状态正常且版本不匹配。

没有覆盖状态端点超时、协议错误或 malformed payload。这里需要区分:

  • 确认存活但身份不匹配:按所有权规则替换;
  • 状态请求暂时失败:重试或给出明确错误;
  • 状态文件残留、进程不可用:清理残留状态后重启。

不能简单吞掉所有错误并停止进程,但也不能让旧 Daemon 的不兼容响应直接阻断恢复。


维度一:代码质量

🏗️ 架构视角 — 🔴

正面部分:

  • DAEMON_CONTROL_PROTOCOL_VERSION 从 REST 实现模块拆到独立 control/protocol.py,减少 CLI 对 REST 层实现细节的依赖,方向合理。
  • stop_runtime() 增加 state_root 参数,使自定义状态目录的生命周期操作更一致。
  • Daemon 启动环境封装在 _daemon_subprocess_environment(),职责相对清晰。

主要问题:

  1. Hello Robot 产品改动与 Runtime 身份、Daemon 自动替换混在同一个 PR 中,属于明显的跨域改动。
  2. Runtime 身份模型只使用 SDK 公开版本,不能表达同版本不同源码 checkout。
  3. 文档提出了 “SDK-owned Daemon” 生命周期边界,但实现中没有 owner 概念。
  4. PR 的核心连续动画架构完全不存在,无法审查其资源过滤、预取、Job 完成事件和待机回退设计。

建议将改动拆为至少两个 PR:

  • Hello Robot 行为及生成模板;
  • Runtime 身份握手和旧 Daemon 恢复。

当前结构增加了回归定位和独立回滚难度。

🎨 产品视角 — 🔴

用户预期与实现严重不一致。

按标题和描述,新用户运行 Hello Robot 后应该持续获得机器人反馈,直到 Ctrl+C;按实际代码,机器人播放一次 happy 后 Application 直接结束。这正是描述中希望解决的“默认 Application 无法持续提供反馈”问题,而不是解决方案。

此外:

  • 蓝色灯光成功反馈未实现;
  • 静默表情未实现;
  • available_ids 过滤未实现;
  • 连续播放未实现;
  • awake_idle 回退未实现;
  • 预取和避免边界重复未实现。

CLI 状态提示的改动本身有产品价值:

  • Runtime 就绪后显示成功状态;
  • 只在 online=True 时显示机器人已连接;
  • Application 启动请求成功后显示运行状态;
  • 离线恢复引导得以保留。

但“Application is running”是在启动请求返回后立即打印的。它只能证明启动请求被接受,不能证明 Application 已稳定进入运行态;如果进程立即崩溃,该提示可能短暂误导用户。建议文案改为 “Application started”,或查询状态确认进入 running 后再输出 “is running”。

📏 规范视角 — ⚠️

优点:

  • 新增函数具备类型标注和简短说明。
  • 中英文 CLI、安装、Application 文档有同步更新。
  • 测试覆盖了在线与离线输出、版本匹配和 PYTHONPATH 组装。
  • 示例增加了标准的 if __name__ == "__main__" 入口保护。

问题:

  1. README 出现明显语义错误:

    If a compatible robot is connected, it plays... If no robot is connected, it plays...

    第二个条件应当是后续离线说明的一部分,当前文本自相矛盾。

  2. PR 描述、标题、代码、文档之间严重失配,属于发布规范上的阻塞问题。

  3. 测试大量通过源码字符串断言行为:

    assert "random" not in app_source
    assert "while True:" not in app_source
    assert "app.robot.animation.play" not in app_source

    这类测试脆弱,而且测试的是实现文本,不是运行语义。合法的重构、别名或注释都可能造成误判。

  4. examples/hello_robot 只测试文件包含若干字符串,没有模拟 Job 并验证调用顺序、超时和退出行为。

  5. tests/application/test_project_init.py 中新增了多余空行,虽然不影响功能,但与“whitespace 检查通过”的陈述不完全协调。

  6. PR 描述声称覆盖“资源过滤、随机边界、完成事件等待”,实际测试只明确证明这些连续播放能力不存在。

⚠️ 损伤视角 — 🔴

存在以下回归风险:

  1. 默认生成项目从预期的持续体验退化为一次播放后退出。
  2. SDK 版本不同可能触发自动停止正在工作的 Runtime。
  3. 同版本旧源码 Daemon 又不会被替换,形成“该停的不一定安全、不该复用的仍可能复用”的双向风险。
  4. /daemon/status 的 0.5 秒查询成为 Runtime 复用的新硬依赖,缺少失败恢复测试。
  5. 现有 PYTHONPATH 被保留在当前源码路径之后,虽然当前源码会优先导入,但其余继承路径仍可能给 Daemon 带来额外模块污染。
  6. CLI 提示 Application 正在运行,但未确认实际运行状态。
  7. 文档对自动恢复能力作了强保证,实际实现无法覆盖相同版本号的 editable checkout。

PR 声称全量测试、mypy 和 pip check 通过,这些结果无法从提供的 Diff 独立验证;即使结果属实,也不能覆盖上述产品目标和生命周期边界问题。


维度二:意图分析

🎯 意图提炼

从 PR 描述看,目标是让默认 Hello Robot 在完成欢迎动作后,基于设备真实能力持续播放静默表情,并改善 CLI 的在线状态反馈和源码升级后的 Runtime 一致性。

从实际代码看,提交真正实现的是:将 Hello Robot 固定为只播放一次 happy 后退出,同时加入 CLI 状态提示和基于 SDK 版本号的 Daemon 自动重启。

🔀 偏离检测 — 🔴 严重偏离

偏离不是局部遗漏,而是核心意图被反向实现:

PR 声明 实际实现
持续随机播放静默表情 禁止 randomanimation.play
持续到 Ctrl+C 禁止 while True,完成后退出
蓝色闪烁两次 禁止灯光调用
available_ids 过滤 无相关实现
预取当前及下一表情 无相关实现
避免洗牌边界重复 无相关实现
无兼容表情保持 awake_idle 无相关实现
覆盖资源过滤和随机边界测试 测试反而断言随机逻辑不存在

同时,Daemon 自动替换属于独立基础设施行为,明显扩大了 Hello Robot 体验 PR 的风险范围。即使这部分修改有必要,也应单独提交和评审。


Merge 建议

🔴 不建议合并

当前版本不具备可合并条件。

合并前至少需要完成:

  1. 明确本 PR 的真实目标:

    • 如果要实现静默表情连续播放,补齐 PR 描述中的全部核心行为和运行级测试;
    • 如果真实目标是“只播放一次 happy 后退出”,则必须重写标题、描述和验证声明,删除所有连续播放相关表述。
  2. 将 Runtime/Daemon 自动替换从 Hello Robot 产品改动中拆分为独立 PR。

  3. 为 Runtime 自动替换建立明确的所有权模型,禁止仅凭版本不匹配停止未知 owner 的进程。

  4. 解决相同 __version__、不同 editable checkout 无法识别的问题,或明确取消对此场景的支持承诺。

  5. 增加状态查询超时、不兼容响应、缺少字段、协议不匹配和停止失败的测试。

  6. 修正 README 中机器人连接条件自相矛盾的文案。

  7. 使用行为测试替代关键路径上的源码字符串断言。

  8. 如果保留 “Application is running” 提示,应确认真实运行状态;否则改成只表达启动请求成功的文案。


总结: 当前 Diff 与 PR 声明的核心功能完全相反,同时引入了缺少所有权保护且身份识别不充分的 Daemon 自动替换逻辑;建议拒绝合并,先拆分范围并纠正实现与意图。

@Mr-KID-github

Copy link
Copy Markdown
Contributor Author

本 PR 不再合并,按当前产品与架构边界关闭。

原因:

  • SDK main 已经具备最小 Hello Robot:生成的 Application 只播放一次 happy,等待 Behavior Job 完成后退出。
  • happy 完成后错误进入 standby_loop 的根因属于 ESP32 全局行为回退,已由 fix: 移除开心行为完成后的全局睡眠回退 WatcheRobot_esp32#168 独立修复。
  • 本 PR 仍混入 Runtime 自动停止/替换 Daemon 的独立改动;该能力需要先明确进程所有权、同版本不同 editable checkout 的身份识别,以及状态查询失败时的恢复策略,不适合随 Hello Robot 一起合入。

后续如仍需要 CLI 彩色状态提示和安装排查文档,应拆成聚焦的小 PR;Runtime 身份与生命周期机制另行设计和评审。本次保留远端分支,不删除代码,方便后续按需拆分复用。

@Mr-KID-github
Mr-KID-github deleted the codex/hello-robot-behavior-showcase branch August 18, 2026 16:40
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.

1 participant