Skip to content

fix: prevent premature completion notifications and Windows console popups - #183

Merged
Kuddev merged 6 commits into
mainfrom
review/bugfix-merge-20260917
Sep 17, 2026
Merged

Kuddev merged 6 commits into
mainfrom
review/bugfix-merge-20260917

Conversation

@Kuddev

@Kuddev Kuddev commented Sep 17, 2026

Copy link
Copy Markdown
Owner

Result / 用户结果

修复现有通知误报和 Windows 后台命令弹窗,保留原作者提交与 PR 历史:

Design / 设计边界

只处理 bug 修复。#130#131#133#144#145 及其功能和翻译扩展均不纳入。没有新增设置、工作流、依赖或持久化格式。

合并冲突保留 main 的 SSH 显式配置路径与现有 HTTP 更新客户端。屏幕状态自愈、无 hook 会话的通知回退和需要交互的进程保持现有语义。源码注释已纠正:看门狗连续识别 Idle 界面,并非仅检测屏幕静默。

采用 merge commit 保留 #142/#143 的原始 head 为祖先,合并后核验原 PR 的远端 merged 状态。

Evidence / 验证依据

  • 以 main 的 c48dffd 为基准运行 architecture checker:通过;51 项架构检查器测试通过。
  • Windows 完整 GPUI 产品测试:1604 通过、0 失败、12 项按既有条件忽略。另跑定向回归 144 项全部通过(hook、屏幕通知、隐藏控制台、runtime exec、Git/worktree、SSH/代理、冲突编辑器与忽略规则)。共享设置 59 项、行数合同 2 项、独立 i18n 20 项通过(1 项既有手工基准忽略);cargo fmt --all -- --check 通过。远端 Full native tests workflow 为 completed/success,Windows x64/ARM64、macOS ARM/Intel 检查成功。Linux 检查摘要仍显示 in_progress,但其全部步骤含 Complete job 已成功;已下载完整日志核实产品测试 1497 通过、0 失败,工作区测试和 release check 全部通过。必需 architecture-contracts 为 SUCCESS。
  • 新增回归覆盖 pending、缺失/未知状态、嵌套后台任务,以及真实 Windows PowerShell 子进程的 GetConsoleWindow、stdin/stdout/stderr 和退出码。
  • 未采集本次真实 Claude Stop 载荷;provider 兼容性基于源码与测试载荷。无新 UI 布局,不以自动测试替代实际桌面视觉验收。

Required Review / 必须确认

  • 已按贡献指南、架构与工程约束审查。
  • 共享规则保持单一实现;未提高预算、移除测试或扩大排除范围。
  • 架构检查以实际 main 基准运行通过。
  • 测试覆盖活动/结束任务、通知回退和 Windows 子进程 I/O。
  • 无新增 UI 文案、治理规则或依赖。

AnxForever and others added 6 commits September 14, 2026 22:54
`Stop` fires at the end of every turn, not at task completion, so the guard
that suppresses the completion toast while work is still in flight is what
keeps that notification honest. It reads `background_tasks`, but the counter
only accepted entries whose type was `subagent` — and Claude Code reports a
backgrounded Bash command as `local_bash`. The most common shape (start a
long command, turn ends, command still running) therefore counted zero: the
guard stayed off and the toast claimed the turn was done while the command
was still running.

Count every entry the field carries. It is documented as in-flight work
("Empty array when nothing is in flight"), so the filter belongs on the
status, not on the type. Idle `in_process_teammate` entries stay excluded —
they keep reporting `status: "running"` after they finish
(anthropics/claude-code#85955) — matching Claude Code's own
`status === "running" && !isIdle` predicate.

Tests: three cases pin the counting (every in-flight type, an empty array
leaving the turn done, an idle teammate not holding it open). The first one
counted zero before this change.
Pebrel is a `windows_subsystem = "windows"` process, so it has no console to
hand to a child, and Windows allocates a new one for every console program we
spawn. When the default terminal application is Windows Terminal that is not
a hidden flash — it is a whole window on screen. Measured on the test binary,
which has no console for the same reason: one full `cargo test` run popped 86
windows, and the `runtime_api` module alone popped 7.

Most spawn sites already carried `CREATE_NO_WINDOW`, but each spelled it
itself — `0x0800_0000` in some places, the named constant in others, a
private `hidden_command` in one module. Three sites had no flag at all:
`pane.exec` (`runtime_exec::configure_process_group` was a no-op on Windows),
the WSL path of drag-and-drop (`path_drop::wsl_paths`), and the `ssh -G`
probe behind the `pebrel ssh` entry point.

Route every console child through one helper, `platform::process`:

- `hidden_command` for the common case;
- `hidden_command_with` when the caller needs extra creation flags (the
  daemon's `CREATE_NEW_PROCESS_GROUP`) — `creation_flags` replaces rather
  than ORs, so the helper owns the `CREATE_NO_WINDOW` bit;
- a `CREATE_NO_WINDOW` constant for `Command` types that are not
  `std::process::Command` (the tokio proxy command). No raw literals left.

Test helpers are included: the test binary is console-less too, so its git
calls popped windows as well (7 during one `runtime_api` run, 0 after).

Deliberately left alone, and now documented as such: the interactive
`pebrel ssh <host>` session (it inherits the parent console through
`AttachConsole`), the update installer wizard, and `notepad`/`explorer`/
`open`, which exist to be visible.
The screen watchdog downgrades `Working -> Done` after the pane's screen has
been static for 5 ticks (it samples at 1 Hz), and that transition used to
notify unconditionally. But a static screen is not evidence that the agent
stopped: Claude Code prints nothing while it thinks, and a long command such
as a full build writes nothing for minutes. The result was a "回合完成" toast
while the task was still running.

The screen is only a fallback for when the hook never arrives. When hooks are
live, the agent CLI's own `TurnDone` is the authoritative terminal state, so
keep the screen-derived transition (the sidebar dot still updates) but stay
silent about it. Clients without hooks keep the old behaviour, where the
screen really is the only evidence.

This is the notification half of the problem. The other half is that
`active_background_tasks` never fires for Claude Code — its `Stop` payload
carries no `background_tasks` field — so the guard at the top of the
`TurnDone` arm is dead code for that CLI. Left as-is for now.
@Kuddev
Kuddev merged commit c9cbfa6 into main Sep 17, 2026
9 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