跳转至

22 · 一次任务的端到端源码追踪

综合案例真实调用链证据链故障分叉复用清单

一句话结论: 一次看似简单的“让 Agent 修改项目并提交产物”,实际穿过 Company/Issue、授权、预算、wakeup、checkout、workspace lease、adapter session、tool gateway、cost、live event、finalize 和 recovery 十多个状态边界。

1. 场景设定

假设 Board 在 Company PAP 下创建 Issue:

PAP-123 为产品项目补充一个 API 错误处理页面
assignee: CTO/Engineer Agent
project: web-app
workspace preference: isolated
tool profile: GitHub read + approved write
budget: company + agent monthly window

目标不是复述某次线上运行,而是沿当前源码快照还原一条合法执行路径。

2. 创建 Issue

sequenceDiagram
  participant U as Board UI
  participant R as issues route
  participant A as Authorization
  participant I as Issue Service
  participant DB as PostgreSQL
  participant E as Activity/Live

  U->>R: POST /companies/:companyId/issues
  R->>R: validate createIssueSchema
  R->>A: actor can create in Company/project?
  A-->>R: allow
  R->>I: create issue + identifier
  I->>DB: transaction / prefix counter / relations
  I->>E: activity.logged
  I-->>R: Issue DTO
  R-->>U: 201 + PAP-123

identifier 来自 Company prefix/counter,origin 若为手工则保持 manual。UI mutation 成功后更新/失效 Issue list。

3. 创建时的隐藏约束

服务可能同时校验:

  • project/goal 同 Company;
  • parent Issue 同 Company/project policy;
  • assignee Agent 可 invokable;
  • responsible user 有资源权限;
  • workspace preference 被 project policy 允许;
  • source trust/low-trust settings;
  • labels/attachments/document refs;
  • create idempotency key(若 client 提供)。

所以“POST 成功”已经是一次跨域 transaction,而不是单表 insert。

4. Assignment 触发 Wakeup

Board 指派 Agent 或 Agent 通过上级委派时,Issue assignment service 可能发布 assignment wakeup:

Issue mutation
  → assignment activity
  → responsible user resolution
  → enqueueWakeup(source=assignment)

如果 Agent paused、budget blocked、Company hold 或无 actionable work,request 仍可能记录为 skipped/deferred,并带原因。

5. Wakeup 入队

flowchart TD
  W["assignment wake"] --> N["canonical Issue + Actor + responsible user"]
  N --> V["invokability + budget + pause hold"]
  V -->|fail| SK["agent_wakeup_requests=skipped"]
  V -->|pass| TX["transaction lock Issue / coalesce"]
  TX --> Q["wakeup request=queued"]
  Q --> RUN["heartbeat_run=queued"]

如果已有同一 Issue 的 active execution,可能进入 deferred_issue_execution 或 coalesced。队列不是内存数组,重启后仍可读取。

6. Claim Heartbeat Run

Agent queue 选择该 run,执行 CAS:

queued → running

此时注册 active execution,后续 stop/recovery 可以定位它。另一个 worker 同时 claim 时只有一个成功。

7. 自动 Checkout Issue

assignment wake 允许且 Issue 仍 todo/可认领时,Heartbeat 调用 Issue service checkout:

  • 验证 Agent assignable;
  • 检查 tree hold/blocker/workspace finalize;
  • 条件更新 assigneeAgentId + checkoutRunId + executionRunId;
  • 失败后区分幂等、stale adoption、409。
Issue todo
  → assignee=Engineer
  → checkoutRunId=RUN-1
  → executionRunId=RUN-1
  → status=in_progress

此刻任务获得的是数据库所有权,不是 Agent Prompt 中的“请你负责”。

8. 解析运行上下文

Heartbeat 读取并合并:

Company mission / goal ancestry
Project + project workspace policy
Issue + parent + comments + blocker
Agent identity + org instructions
Wake reason + current run
Responsible user + trust preset
Adapter config + model/profile
Skill selection + tool profile

不可信 Issue/comment 内容被标注/清理;Paperclip runtime protocol 和 governance constraints 由 Server 生成,而不是让 Issue 文本覆盖。

9. 选择并准备 Workspace

本例 Issue 指定 isolated,Server 根据 Project primary workspace 和 Environment:

  1. 找/建 execution workspace;
  2. 记录 strategy/base ref;
  3. acquire environment lease;
  4. local 创建 worktree 或 derived dir,远端使用 provider realization;
  5. 写 workspace prepare operation;
  6. 运行 setup;
  7. materialize instructions/skills;
  8. 配置 runtime services 和 tool gateway。

任何一步失败都会让 Run 有明确 setup failure,不能直接让 Agent 在未知 cwd 启动。

10. 解析 Secrets 与 Tools

Adapter 获得的不是长期 GitHub token,而是:

  • run-scoped Paperclip JWT;
  • named gateway URL/token;
  • tool profile 允许的 catalog;
  • secret refs 在运行时投影的 header/env;
  • issue/project/responsible-user attribution。

Agent 请求 create_pull_request 时,仍需经过 Gateway policy/approval;Prompt 中写“请提交 PR”不具备权限。

11. Adapter 启动

Registry 解析 codex_local 或 claude_local module,adapter:

  • 读取 config schema;
  • 解码该 task session;
  • 检查 workspace/cwd compatibility;
  • 建立 CLI args/env/instructions;
  • 尽早上报 PID/process group;
  • 开始发送 stdout/structured event。

Server 将事件写入 run log/event,并通过 Company live WebSocket 发布。

12. UI 如何看到过程

sequenceDiagram
  participant S as Server
  participant WS as Live WebSocket
  participant P as LiveUpdatesProvider
  participant Q as Query Cache
  participant V as Issue Detail

  S->>WS: heartbeat.run.status(running)
  WS->>P: JSON LiveEvent
  P->>Q: patch active/live run
  P->>Q: batch invalidate heartbeat/issue queries
  Q-->>V: render running + current tool
  S->>WS: heartbeat.run.progress
  WS->>P: progress event
  P->>Q: immediate snippet/tool patch

页面只开一个 Company socket;事件丢失时,重连会 refetch live runs/详情。

13. Agent 调用 MCP Tool

sequenceDiagram
  participant A as Agent Runtime
  participant G as Tool Gateway
  participant P as Profile/Policy
  participant H as Human
  participant U as GitHub MCP

  A->>G: create_pull_request(params)
  G->>P: catalog + profile + policy
  P-->>G: require_approval
  G-->>A: 409 + actionRequestId
  G->>H: issue thread confirmation / attention
  H->>G: approve
  A->>G: retry + approvedActionRequestId
  G->>P: hash/schema/scope revalidation
  G->>U: call with projected credential
  U-->>G: PR result
  G-->>A: guarded result

Approval 的参数 hash 防止 Agent 把批准的安全调用替换成另一个 destructive 参数。

14. Agent 产出与 liveness

假设 Agent 返回:

  • 修改了代码;
  • 创建了 draft PR;
  • 写了 Issue comment;
  • 还有一个待 reviewer 的下一步。

Run 可能是 succeeded,但 liveness 为 needs_followup 或 advanced,Issue 进入 in_review,并创建/唤醒 reviewer path。不能因为 process exit 0 就自动 done。

如果 Agent 只写了“我完成了”却没有代码/PR/测试证据,liveness/issue policy/watchdog 可以把它标成 empty/plan_only 或要求继续。

15. Cost 与 Budget

Adapter result 归一化 token/provider/biller/cents,Server:

  1. append cost event;
  2. 更新 spend read model;
  3. evaluate policy window;
  4. 生成 warning/hard incident;
  5. 如跨 hard limit,暂停 Agent、阻断未来 wake;
  6. publish activity/live update。

Run 已完成的成本不会因 budget 后续暂停而被删除。

16. Finalization 顺序

adapter result
→ run terminal status + liveness + usage/session
→ issue continuation/review/lock decision
→ workspace finalize operation
→ sync/export work product refs
→ release environment lease
→ stop/reuse runtime services
→ clear active execution registry
→ start next queued run

Run terminal 早于 lease release,防止下一个 run 复用环境时 recovery 仍把上一 run 看成 live。

17. 成功路径后的证据

Board 最终可以看到:

  • Issue status/assignee/checkout run;
  • run transcript/log/progress/usage/cost;
  • Activity timeline;
  • document revision;
  • PR/preview/artifact work product;
  • workspace/branch/provider reference;
  • approval/tool audit;
  • follow-up/reviewer path。

这些是不同读模型,不能只用一个“完成截图”代替。

18. 故障分叉 A:Adapter 启动前失败

例如 binary 缺失:

Run claim 成功
→ workspace prepare 可能成功
→ adapter test/execute 失败
→ run=failed(setup class)
→ release lease/cleanup workspace
→ Issue 保留 owner 或转 recovery
→ activity + actionable notice

是否 retry 取决于配置是否可修复,不能无限重试同一缺失 binary。

19. 故障分叉 B:进程被杀/Server 重启

旧 Run running + PID/process metadata
→ startup hot-restart reconcile
→ process/provider identity check
→ adopted / finalized_while_down / lost
→ terminalize or retry/recovery
→ clear/retain Issue lock with evidence

UI 重连后通过 REST 对账,不能只依赖旧 WebSocket event。

20. 故障分叉 C:workspace finalize 失败

即使 adapter result succeeded:

  • Run 可以是 succeeded;
  • workspace operation 是 failed/pending;
  • Work Product 可能标记 incomplete/unhealthy;
  • 下游 blocker 不应立即满足;
  • recovery/retry 处理 sync/cleanup;
  • Board 看到明确 notice。

这正是执行成功、交付成功、依赖满足三者分离的价值。

21. 故障分叉 D:预算越界

多个 run 并发时可能都在阈值前启动。成本落账后:

  • 第一条 crossing event 创建 incident;
  • policy/Agent 持久化 paused/block;
  • queued wake 被 skipped;
  • 可取消的 run 进入 interrupt;
  • Board 可选择 keep paused 或 raise budget/resume。

系统不承诺对已经消耗的 provider 成本回滚,只保证未来执行不再无界增长。

22. 最终状态的判定

一个可靠的“完成”至少要满足:

Issue transition allowed
  + required review/approval satisfied
  + execution evidence present
  + workspace finalize settled
  + expected work product/document/artifact recorded
  + no unresolved blocker
  + run/lease/lock coherent

不同 work mode 可调整证据要求,但不能绕过 Company scope、authorization、budget 和 audit。

23. 这条链暴露的架构原则

  1. 状态机优先:每一步都可被数据库、事件和 recovery 解释。
  2. 控制面/执行面分离:模型 runtime 可换,治理事实不换。
  3. 证据优先:完成是可验证产物,不是文本宣称。
  4. 失败可继续:每个中断点都有 retry、recovery、review 或人工路径。
  5. 租户与责任贯穿:Company、Agent、Run、responsible user 在每一层被携带。

24. 复用这条模式时的最小清单

[ ] tenant/company scope
[ ] separate intent, attempt, session
[ ] atomic ownership CAS
[ ] durable queue + idempotency
[ ] runtime adapter capability contract
[ ] workspace/environment/lease lifecycle
[ ] short-lived run credentials
[ ] preflight auth/budget/approval
[ ] terminal-before-resource-release
[ ] liveness + recovery scanner
[ ] activity/cost/output evidence
[ ] REST truth + realtime hints

这也是本文全部章节的压缩版。

源码入口

  • Issue:server/src/routes/issues.ts、server/src/services/issues.ts
  • Wake/Run:server/src/services/heartbeat.ts
  • Adapter:packages/adapter-utils/、packages/adapters/
  • Workspace:server/src/services/execution-workspaces.ts、workspace-realization.ts
  • Auth:server/src/middleware/auth.ts、services/authorization.ts
  • Governance:services/budgets.ts、approvals.ts、tool-gateway.ts
  • UI:ui/src/context/LiveUpdatesProvider.tsx
  • Recovery:server/src/services/recovery/

至此主线结束。可继续用源码导航图、数据表图谱和状态速查按主题回查。