Skip to content

停止 run 後,其建立/認領的工作板項目沒有依設計被標記為 paused,仍停留在 in_progress #124

Description

@MarkTsaiCqi

摘要

停止一個正在跑的 run(點擊團隊房間成員面板的「停止」鍵)會正確中斷該 agent 的執行(room trace 正確浮現「任务已被所有者停止」,agent 狀態立刻變回「空闲」,且原本正在跑的 sleep 90 && echo "sleep 90 done" 從未印出完成訊息,證實真的被中斷,不是等它自然跑完),但這個 run 建立/認領的工作板項目(work item)沒有backend/routes/runs.py::_pause_work_items 設計文件所述被標記成 paused——重新整理、直接呼叫 API 複查多次,item 的 status 全程停留在 in_progress

_pause_work_items 的 docstring 明講這正是這個機制存在的核心原因:

Without this the stop is quietly undone: stopping a tree ends its RUNS, but the work board still lists the task as unfinished, so the Leader's next patrol sees "unfinished item, assignee idle", chases it, and starts a fresh run. The owner presses stop and watches new work appear...

也就是說,這次重現的正是這段註解描述的失效模式本身:使用者按下停止,工作板卻完全沒有反映這件事,項目繼續顯示「進行中」,之後 Leader patrol(若開啟)理論上會照常追殺這個「看起來還在跑、其實 run 已死」的項目。

重現步驟

環境:https://dev-agent.narra.nexus,xyzdev01,測試團隊 team_54f412ee0b4c(QA Work Board Test Team,已設 lead_agent_id

  1. 在團隊房間對 qa-tcjob06-testagent_df94a825fc13)下指令:

    請依序做兩件事:1. 呼叫 work_add_item 新增一個工作項,標題「TC-TEAM-BOARD-11 停止run測試」,assignee_id 填你自己的 agent_id(讓它變成 in_progress)。2. 完成後,用 Bash 執行 sleep 90,等它跑完再回覆我。

  2. Agent 依序執行:work_add_item 成功回傳 {"success":true,"item_id":"wi_9a62df9b","status":"in_progress"},接著開始執行 Bash: sleep 90 && echo "sleep 90 done"(透過過程面板即時觀察到工具呼叫)
  3. 確認 GET /api/teams/team_54f412ee0b4c/work-items 回傳 wi_9a62df9bstatus: "in_progress", assignee_id: "agent_df94a825fc13"
  4. sleep 90 執行途中(約第 25~30 秒),展開該成員的過程面板,點擊「停止」按鈕
  5. 觀察到:
    • POST /api/runs/evt_d14e3dc23b8c4c16/cancel 回傳 200
    • 團隊聊天室立刻浮現系統訊息「qa-tcjob06-test 的任务已被所有者停止」
    • 成員狀態面板立刻變回「空闲」,過程面板顯示「该轮次没有过程记录」
    • 從未看到 sleep 90 done 的回覆——證實 run 真的被中斷,不是自然完成
  6. 停止後,重新呼叫 GET /api/teams/team_54f412ee0b4c/work-items 三次(間隔數秒到共 ~40 秒),wi_9a62df9bstatus 全程都是 "in_progress",從未變成 "paused"
  7. UI 上的工作板面板也同步顯示「TC-TEAM-BOARD-11 停止run測試 · qa-tcjob06-test · 进行中」,不是預期的 paused 樣式

程式碼層可能根因(未確認,需 dev 端查證)

  • backend/routes/runs.py::cancel_runroot = row.get("root_run_id") or "",取的是這個 run 自己的 events.root_run_id 欄位;若非空則呼叫 _pause_work_items(db, root)TeamWorkItemRepository(db).pause_by_root(root)
  • pause_by_root docstring 明講「An empty root_run_id is a no-op, NOT a match-all」——如果這個 run 的 events.root_run_id 欄位是空的,_pause_work_items 會靜默 no-op(if not root_run_id: return 0,且外層有 try/except 吞掉例外,只留 logger.warning,前端完全看不到任何錯誤或提示)。
  • run_recorder.py:509"root_run_id": self.inherited_root_run_id or run_id——理論上一個「非串聯」的獨立 run(沒有 inherited_root_run_id)應該會自我指向(root_run_id = run_id),所以照理不該是空的;但這條路徑我沒有 DB 存取權限可以直接驗證,不確定實際落地的值是否真的等於預期。
  • 另一個可能:work_add_item 呼叫 create_item(..., root_run_id=caller_root_run_id()),而 caller_root_run_id() 是從當下 turn 的 ambient headers/bearer identity 讀出來的(src/xyz_agent_context/module/_mcp_identity.py)。如果這個 header 在 Bash 工具呼叫鏈中的傳遞跟 cancel_run 讀到的 events.root_run_id 不是同一個值(例如兩者一個是 run_id 本身、一個是別的東西),pause_by_root(root) 查詢 WHERE root_run_id = %s 就永遠對不上,即使兩邊都非空。
  • 這兩個可能性我都無法從瀏覽器測試本身區分,需要 dev 端直接查 events 表這個 run 的 root_run_id 欄位,以及 team_work_itemswi_9a62df9broot_run_id 欄位,比對兩者是否一致、是否為空。

影響

  • Functional,非安全問題(不涉及跨帳號存取或資料外洩)
  • 但直接命中 _pause_work_items 自己文件宣稱要防範的核心情境:「owner 按停止,工作板卻沒反映,Leader patrol 下一輪又追殺同一個項目,使用者看到停掉的任務又冒出新的 run」
  • Severity:中——功能未達成設計文件描述的行為,且是此功能(work board + leader patrol + stop-run 連動)存在的主要動機之一

測試環境

  • https://dev-agent.narra.nexus
  • 對應測試計畫項目:qa/features/collaboration-marketplace/test-plan-collaboration-marketplace.md TC-TEAM-BOARD-11

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions