[ZCode 主仓 bug] 后台智能体运行期间编辑消息重发会永久丢失完成通知(stale-branch fencing 误伤),任务 UI 永久运行中——已定位到源码丢弃点
I maintainer di solito rispondono entro 2 giorni
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 52/100
- Tipo di issue
- Bug
- Chiarezza
- Abbastanza chiara
- Stato di attività
- Attiva
- Stack tecnologico
- typescript
Direzione di ricerca
Start with core/src/runtime-task/registry.ts and trace branchGeneration updates in core/src/runtime/methods/rewind-message.ts. Then inspect core/src/runtime/methods/background-notifications.ts and core/src/runtime/methods/subagent.ts, using the described edit-and-retry scenario to follow notification and terminal-event fencing. Done means a still-running task remains deliverable after an edit, stale abandoned-branch events remain fenced, and the UI reaches a terminal state.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
借道说明同 #60:ZCode 主仓 issues 关闭、外部账号 PR 被权限拦截,只能在本仓反馈。本 issue 已完成日志证据与开源源码(main@29628c9)双侧定位,丢弃点、日志级别、设计意图注释均可直接对应到行。
[Bug] 后台智能体运行期间"编辑消息重发"会永久丢失该智能体的完成通知,任务在 UI 永久显示运行中
一句话
用户在后台子智能体运行期间编辑一条已发送消息并重发(editUserQuery → conversation rewind → 会话分支代数 branchGeneration 推进),该智能体完成后其通知被"迟到围栏"(stale-branch fencing)以 debug 级日志静默丢弃——生产日志不可见、无报错、无补偿;UI 因终态事件被同一围栏拦截而永久显示"运行中",取消时报 background_task_not_running,形成死循环。
复现步骤
- 在会话中派一个后台子智能体(
Agent工具run_in_background=true),任务需运行数分钟; - 智能体运行期间,编辑一条已发送的消息并重发(触发
editUserQuery→ rewind); - 等智能体完成。
结果:~/.zcode/cli/agents/<会话>/<智能体id>/metadata.json 为 status: completed(执行侧干净结束),但父会话永远收不到完成通知;UI 任务永久"运行中";尝试取消报 background work <id> was not cancelled: background_task_not_running。
实测证据链(2026-10-04,zcode-2026-10-04.jsonl + model-io 转录,时间为本地)
| 时间 | 事件 | 证据 |
|---|---|---|
| 10:38:22 | round2 评审智能体后台启动 | tracking.started,taskId=agent_0003c234 |
| 10:45:07 / 10:45:29 / 10:45:40 | 用户三次编辑重发消息(根因窗口) | 3 次 conversation.command.edit_user_query;伴随 2 次 stopActiveForegroundExecution + 1 次 sendQueuedNow preempts active turn;回合计数 165→32(回卷重建痕迹) |
| 11:03:53.281 | 智能体完成,通知"封存" | runtime.background_task_notifications.sealed, reason: subagent_terminal |
| 11:03:53.313 | 通知"入队"但随即被丢弃 | subagent.background.notification.enqueued(info)之后全天无该智能体的 background_task.notification.runtime_enqueued——实际在 background-notifications.ts:33 被 debug 级 stale_branch_dropped 丢弃(见下方源码;当天日志级别分布 info 9332 / warn 65 / error 14 / debug 0,故不可见) |
| — | 通知从未注入模型 | 父会话转录中 <task-id>agent_d2419519(对照组 round1)通知块 23 个;<task-id>agent_0003c234 0 个(会话全天共 265 个通知块) |
| 11:21-11:44 | 桥本身正常 | 回卷之后启动的任务分别获得 runtime_command_52/55/57 并正常送达——坏的只有"回卷前注册"的那一个 |
| 11:44:32-11:47:31 | 取消失败 ×5 | background_task_not_running;UI 仍显示运行中(终态事件 emit 被同一围栏拦截,状态永不更新) |
对照:round1(agent_d2419519)10:20:34 完成瞬间为完整链 runtime_enqueued(runtime_command_36) → enqueued → batch_started → 注入;全天仅有的 3 次 edit_user_query 恰好全部落在 round2 运行窗口内。
源码因果链(四步闭合,apps/zcode-cli)
-
注册打戳:
core/src/runtime-task/registry.ts:119-122——任务注册时盖branchGeneration戳,接口注释明说意图:"task 注册时所属 active conversation branch;用于迟到 completion fencing"。 -
编辑→回卷→代数推进:
bootstrap/src/zcode-protocol-v4/commands/handlers/fork-edit-retry.ts:4("editUserQuery = 换文本的 retryTurn:rewind 截断该 turn");core/src/runtime/methods/rewind-message.ts:580-581——this.branchGeneration = branchGeneration; this.runtimeTaskRegistry.setActiveBranchGeneration(...),只推进新注册指针,存量运行中任务的旧代数戳原样保留。 -
丢弃点:
core/src/runtime/methods/background-notifications.ts:31-43——通知到达时查注册表,task.branchGeneration !== this.branchGeneration→logger.debug("Dropped stale-branch background task notification")+ return。debug 级 + 生产日志无 debug = 静默。 -
误导性"成功"痕迹:
core/src/subagent/runner.ts:1860-1896——enqueueParentTaskNotification为 void 调用、不检查结果,之后无条件打 info 级subagent.background.notification.enqueued。日志里"已入队"不代表进了投递队列(真正的入队日志在runtime-command-queue.ts:23-35的runtime_enqueued)。
补充:终态事件路径 core/src/runtime/methods/subagent.ts:76(isStaleBranchRuntimeTaskEvent)被同一围栏拦截,导致 UI 永久"运行中";回卷后新任务通知正常(其代数戳为新代数),且不在注册表的 taskId 会走 ?? this.branchGeneration 回退而恰好放行——两个现象都与源码一致。
设计缺陷本质
围栏无法区分两种情形:任务属于被放弃的旧分支(该拦:防止迟到通知污染回卷后的新对话)vs 任务仍在运行、用户只是编辑了一条消息(不该拦:智能体还在为新分支工作,其完成通知对新分支完全有效)。后者的通知被拦后,任务在 UI 悬死且无任何可见痕迹。
修复建议(按侵入性排序)
- 语义修复:回卷时(或通知到达时)对注册表中非终态的运行中任务重刷
branchGeneration为新代数——仍存活的任务,其通知对新分支有效;只有终态/已通知条目保留旧代数参与围栏。 - 可观测性修复:
stale_branch_dropped从 debug 提升到 warn,并在 runner 侧检查投递结果,消除误导性的 info 级 "enqueued" 痕迹。 - 状态收口:终态事件(emitParentEvent 路径)的 fencing 应与通知 fencing 区分处理,至少保证 UI 侧任务状态能收口,避免"永久运行中 + 取消报 not_running"的死循环。
规避(用户侧,已验证)
后台智能体运行期间不要"编辑消息重发",需补充内容新发一条;若已触发,以磁盘产物为准:读 ~/.zcode/cli/agents/<会话>/<智能体id>/metadata.json 的 status,最终回复在同目录 task.output。
- Lingua principale
- Python
- Stelle
- 86
- Fork
- 51
- Metriche di merge delle PR
- Nessuna PR unita negli ultimi 30g
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di zai-org/zcode-plugins
-
video-agent-kit (0.4.3): hooks invoke python3, which doesn't exist on Windows — SessionStart/UserPromptSubmit/Stop hooks fail on every triggerForse già presa @dvd233 l’ha presa 12 giorni fa. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 85/100
zai-org/zcode-plugins#14 ·
I maintainer di solito rispondono entro 2 giorni
-
Difficoltà 5/5 Più di una settimana Idoneità per principianti 25/100
zai-org/zcode-plugins#63 ·
I maintainer di solito rispondono entro 2 giorni
-
Difficoltà 3/5 1-2 giorni Idoneità per principianti 25/100
zai-org/zcode-plugins#60 ·
I maintainer di solito rispondono entro 2 giorni
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 38/100
zai-org/zcode-plugins#59 ·
I maintainer di solito rispondono entro 2 giorni
-
Difficoltà 5/5 Più di una settimana Idoneità per principianti 45/100
zai-org/zcode-plugins#58 ·
I maintainer di solito rispondono entro 2 giorni
Tutte le issue di zai-org/zcode-plugins
Issue simili
-
Device Details tables: FS/SF columns contradict each other (nfet_01v8 Vt row, pfet_01v8 Idsat row)Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
google/skywater-pdk#450 ·
-
Drained trajectory arrays are overwritten when the sequence buffer is reusedForse già presa @sylvesterkaczmarek l’ha presa oggi. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
google-deepmind/bsuite#56 ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
LearningCircuit/local-deep-research#7206 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
chingu-voyages/V62-tier3-team-33#285 ·
I maintainer di solito rispondono entro 1 giorno
-
Proxy drops log notifications from backends that don't send FastMCP's msg/extra dictForse già presa @asasemahmed l’ha presa oggi. Apertabug server
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
I maintainer di solito rispondono entro 1 giorno