• company.json 新增 target_dir/target_mode,graph-engine 的项目根分配
    读到配置后不再逐 run 分配新 sandbox,同公司所有 run 解析到同一个固定
    路径(复用 path-resolver 的 linked-new/linked-existing 校验逻辑,
    抽出 validateLinkedTarget/resolveFixedTarget 共享);未配置时零回归。
    配了 target_dir 的公司 max_parallel_tasks 强制钉 1,避免多任务并发
    写同一个仓库。按 39 号设计文档 §二 P5 落地。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01BwG4b7Nq1NLR9eeE3M9Us9
    yibo authored
     
    Browse File »
  • 按 39 号设计文档落地 ceo 常驻监督:company.js 的 goals/tasks 端点
    放行内部令牌自环调用,POST /tasks 支持 draft 模式(agent 自主提任务
    不再立刻烧预算),ceo 补雇进公司创建流程;task-settle 新增事件驱动
    唤醒 + 30 分钟兜底轮询;graph-engine 的回退熔断收敛时顺手回写一条
    draft task,形成"卡住 → 自动/ceo 判断 → 重新规划"闭环。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01BwG4b7Nq1NLR9eeE3M9Us9
    yibo authored
     
    Browse File »

  • 两个独立的小修复,都是这次 e2e 回归 + 用户讨论"workgraph-planning 该怎么训练"时发现的:
    
    1. workgraph-plan-<taskId>.md 撞名:一个任务里 org-planner/org-coder/org-validator
       各自调 workgraph-planning 一次、各自的规划记录却共用同一个文件名,落在同一个项目根下,
       后写的原样覆盖先写的——真实撞见过(org-planner 当时的规划记录被 org-validator 覆盖,
       事后一个字都找不回来)。改成 workgraph-plan-<taskId>-<宿主orgNode节点名>.md。
    
    2. subgraph 节点结算 → workgraph-planning 自己的 memory.md 桥接:阶段一的
       recordNodeOutcomeMemory 明确排除了 subgraph 节点(那是给 agent_call 记的),导致
       workgraph-planning 每次规划出的图最终跑得好不好,完全没有可积累的历史——这正是它
       "允许裁剪却没有具体判据"这个问题一直无法通过真实反馈自我改进的原因之一。新增
       recordWorkgraphPlanningMemory,在 subgraph 节点结算时把结果记进 workgraph-planning
       (全局唯一规划本体,不是宿主 org node 自己的身份)的 memory.md,只在真实规划
       (planWorkGraph===defaultPlanWorkGraph)时记,避免测试夹具污染真实目录。
    
    新增 3 条回归测试,graph-engine 相关测试文件全绿。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse File »
  • run_b962ea87f58f 卡在 wallclock_ms 耗尽的锚点未结清问题上,需要人工追加预算
    才能继续。extendBudget 只增不减、只加到已配额的维度上,追加时顺带结清卡死的
    墙钟锚点(否则 startRunSync 一启动就把挂起时长记进新预算里)。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse File »
  • 30 号文档记录的真实事故:org-coder-test 被上游 missing_required_input 打回后,
    产出的是「建议重跑 org-coder-validator」的诊断文件而不是正常测试结果,引擎把诊断文件里
    的 infra_flake 标记当成了这个节点自己的执行结果——infra_flake 原地重跑故意不烧
    attempt/backward_count("这不是节点的错,不该烧它的重试额度"),代价是它是唯一没有
    熔断线的失败路径,真撞上了一次每轮产出雷同、永不停止的死循环,眼看着把任务预算耗光。
    
    新增 graph_run_nodes.infra_flake_count 列,只在 infra_flake 原地重跑时累加,真正发起
    新一轮真实派发(bumpNodeAttempt)时清零;连续判 infra_flake 超过硬顶(3,与
    LOOP_HARD_CAP_EDGE 同量级)就不再原地重跑,改判 blocked 交给人工——不猜该走哪条
    fail 边(infra_flake 这个取值域通常没人给它连边)。新增回归测试锁死"永不恢复的
    infra_flake 在硬顶后停止重跑"这条行为,graph-engine 相关 10 个测试文件 94/94 通过。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse File »
  • 此前只有人类对话路径 agent-chat.js 会写 memory.md,图执行的验收反馈从不进
    memory.md,Dream 天天扫全是空转。runClaimedNode 结算后新增 recordNodeOutcomeMemory
    桥接点,只在真派发(isRealDispatch)且 systemAgentId 对应本体已雇佣(bodyDir 存在)
    时才记一行 success/fail 摘要,避免测试夹具/假 executor 用例把假地址写进真实
    system/agents/ 目录。memory-service.appendToMemoryMd 补 mkdirSync,因为这条新调用方
    不像既有的 saveSessionSummary 那样先建过 memory/ 目录。
    
    从原本混在墙钟看门狗改动里的工作区 diff 中拆出,两个特性互不相关。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse File »
  • manual 模式没人点「下一步」、auto 驱动循环随进程重启丢失时,run_status='running'
    的 run 再也不会触发 checkBudget,wallclock_ms 超时无人发现(实例:T-msx6toej 卡
    135 小时才被人工揪出)。新增 sweepWallclockBudgets + 60s 兜底定时器
    startWallclockWatchdog,复用既有 checkBudget/suspendRunTree 路径,接入点在
    index.js DISABLE_BACKGROUND_AGENTS 早退之前。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse File »

  • runClaimedNode 派发真实调用之前,先查一次 agentLocator.missingBodyDefinition
    (systemAgentId)——此前这个判据只在招聘/建公司路径上被查过,图执行的核心路径
    从没查过:本体因任何原因(装配没跑完、手工改坏、迁移遗留)仍是空骨架时,引擎
    会原样把它当正常本体派发一次真实调用,静默产出一份没有角色指导支撑的结果
    (真实样本见 34 号文档 §五:unit-tester 顶着 _base 骨架跑完了一整条真实任务)。
    
    本体不完整时:新 fail_reason='body_not_ready'(与招聘路径的同名状态同义),
    不扣预算(没有调用发生,"一次调用一定发生了"这条记账理由不成立)、不起边界
    守卫、走正常的选边/兜底/熔断路径(不发明第二套重试语义)。
    
    只在 agentExecutor === defaultAgentExecutor 时生效(与 boundaryGuardApplicable
    同一判据)——第一版忘了这条,把全仓库几百个用假 systemAgentId + 注入假 executor
    的用例全部判成 body_not_ready,跑单测直接炸穿 75 个用例;加上这条判据后收窄回来,
    只在真会起 CLI 的路径上生效。
    
    只查 agent_call 节点:subgraph 节点的 systemAgentId 不是即将被直接调用的本体,
    它派发的是 workgraph-planning 去规划子图。
    
    新增 graph-engine-body-not-ready.test.js(4 例)+ 更新
    graph-engine-fail-reason.test.js 的取值域断言(五个→六个)。全量单测
    1004/1004 通过。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse File »
  • 用户反馈:控制台原来只能逐个节点点「输入输出」tab 看产物,缺一个全局入口。
    新增 TaskFilesPanel.vue,挂在页头,跨节点/跨 run 一次性列出这个任务的全部产物。
    
    做的时候发现分两类、且第二类之前完全没覆盖:
      · 节点产出索引(graph_files,按 company+task 检索)——规划笔记/日志/契约快照
      · 项目根(W2 ensureProjectRoot,data/companies/{cid}/projects/{pid}/)——
        真实交付物代码写的地方,跨节点共享,indexNodeOutputs 从不扫它
    
    后端补 readProjectRootRecord 导出 + GET /api/graph-runs/:id 挂 projectRoot 字段;
    项目根的文件列表复用已有的 GET /:companyId/files(此前零调用方),不新开列目录端点。
    新增 graph-run-route-io.test.js 两例(分配前 null、分配后原样透传 + 幂等)。
    
    顺带订正 34 号文档:当时断言"index.html 从未被写出来"是错的——真实网站文件
    其实在项目根里,只是没查那个目录。34 号文档 §零 记录了订正过程,问题收窄成
    "validator 有没有真的做过浏览器级检查"(仍未解决,留待下次)。
    
    全量单测 1000/1000 通过。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse File »

  • ## 问题 1:work 节点只有 agent.md 进提示词
    
    graph-engine 驱动 work 节点走 PromptBuilder.build(),只读 protocol + agent.md +
    companyContextPath + user.md/memory.md/近期记忆。交互式的 agent-chat.js
    (buildSystemPrompt)另有一条独立实现,会读 soul.md/identity.md/tools.md/
    heartbeat.md/OKR 目标/hippocampus 检索/main_skill/SKILL.md/其它 skills——两条链路
    从未同步过。静默到什么程度:agent 照样跑、照样成功,只是少了人格/工具约束/长期
    记忆检索,没有任何报错能看出原因。
    
    新增 identity-context.js,抽出 agent-chat 那份逻辑里「读哪些文件」的等价实现(不
    读 agent.md/user.md/memory.md/近期记忆——那五样已经被 PromptBuilder 覆盖,避免
    重复),接进 PromptBuilder.build() 的非 SKILL.md 分支(scoped skill 那类不是完整
    身份,不适用)。用户明确选择「全量对齐」:soul/identity/tools/heartbeat/OKR/
    hippocampus/skills 一次性全补,不做裁剪版。
    
    ponytail:没有反过来改 agent-chat.js 去调用这份共享实现——两处现在是「同构但
    独立」的两份代码。agent-chat.js 是长期稳定的高频路径,为这次修复去动它风险和
    收益不成比例,见 identity-context.js 头注的升级路径。
    
    ## 问题 2:org node 角色定义截断到 2000 字,切没切完全不可见
    
    describeRoleDefinition 给 workgraph-planning 的角色定义摘录一直硬切前 2000 字
    (有意为之,规划要的是「职能」不是全文)。但切没切、切掉了多少,规划 agent
    一无所知——agent.md 写得长一点、职能说明排在后半段,这句关键描述可能根本没
    进提示词,且没有任何信号。截断上限不变,改成截断时附一句 ⚠️ 提示 + 完整路径,
    给它一条自己去读全文的退路。
    
    ## 测试
    
    identity-context.test.js(10 条):文件读取的每个条件分支、OKR/hippocampus
    只在有 agentId 时才查、OKR 抛错不拖累其它内容、skills 目录扫描排除 main_skill。
    两个依赖(OkrService/retrieveRelevantPlans)用直接换 CJS module.exports 属性
    的手法隔离——本仓库服务层是纯 CommonJS,vi.mock 对嵌套 require() 链路不生效
    (实测踩过),且这两个依赖没有 env 覆盖点,真实路径不能进单测。
    
    describe-role-definition-truncation.test.js(3 条):截断/不截断/本体不存在
    三种情况,用真实 system/agents/ 下的 zz- 前缀临时本体(locator.bodyDir 没有
    env 覆盖点,只能顺着真实路径走),afterEach 清理不留痕迹。
    
    全量 npx vitest run:150 文件 / 1378 用例,零回归。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse File »
  • ## 起因
    
    e2e-demo 真实任务卡住:org-planner-sdd 自己判 input_underspecified
    并点名 org-planner-prd,但这张图里 sdd 没有任何 fail 出边——精确匹配、
    兜底边两条路都接不住,节点直接 blocked,靠人工点了「重试」才续上。
    
    ## 根因
    
    fail-fallback-edge 校验(27-P3/P4 §三.7 第五项)只在节点已经画了别的
    fail 边时才检查兜底边齐不齐。一条 fail 边都没有的节点完全不受约束——
    workgraph-planning/agent.md 原文明说这是「合法设计」。但系统里所有本体
    共用 _base 的四段式协议,VERIFY 阶段任何 agent_call 节点都可能自判
    失败,不止专职判词节点。四个骨架模板审计后发现 6 个中间节点存在同一个
    漏洞(sdd 只是撞上的那一个)。
    
    ## 决策
    
    除图的唯一入口节点外,每个节点必须留一条兜底边(判据是 forward 入度,
    不是节点名字模式)。org 层暂不动——node 全是 subgraph 类型,失败来源
    机制不同,且套用同一条规则会让三个 org 模板立刻不合法,属于 27-p3b
    「验收9」还没收口的链路,留给那边一起做。
    
    中途发现无条件收紧会炸 46 个用例/13 个文件(createWorkGraph 被大量
    测试当成建最小图的通用工具)。改法是加 strict 开关,默认关闭(行为
    不变),只在 workgraph-planning 真实提交图落库的那个唯一入口
    (createWorkGraphSafely)打开。
    
    ## 改动
    
    - work-graph-db.js / graph-engine.js:新规则 + strict 开关
    - planner.json / executor-fanout.json:补 6 条兜底边
    - workgraph-planning/agent.md:约束三等四处措辞同步
    - 新增单测:strict 模式 7 例 + 真实链路回归用例(真实引擎代码+真实
      DB+只换最外层 CLI 子进程,复现 sdd 自判失败后引擎自动路由到 prd,
      全程零 retryNode 调用)——比重跑一次真实任务更严格,可回归、进 CI
    
    978/978 单测通过。需求与决策记录见
    docs/todo/system-design/31-work-graph-fail-fallback-coverage.md。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse File »
  • 先搭桩子,不接真实计费接口(用户明确选择:真实欠费判据依赖计费系统那边确认,
    现在只把可插拔的挂载点和恢复机制先落地,接口确认后再换真实实现)。
    
    新增:
    - setBillingGate(fn):与 setAgentExecutor/setPlanWorkGraph 同款注入模式,未注入
      时 checkBilling 恒返回「未欠费」,对现有行为零影响。
    - checkBilling 挂进 advanceOne 与 advanceBatch,紧跟 checkBudget 之后——两者是
      不同维度的门(任务级预算池 vs 账户级欠费),不合并成一个判据。
    - 欠费挂起复用现成的 suspendRunTree(预算耗尽走的同一条路),只是 reason 换成
      'billing-arrears'。suspendRunTree 加 reason/summary 参数,并把 reason 写进新增的
      graph_runs.block_reason 列(欠费清零后的自动恢复扫描要按它过滤,不用解析 timeline
      文本);startRunSync「启动」时清空这一列。
    - resumeBillingClearedRuns():欠费清零后的自动恢复触发器——今天从 blocked 恢复靠
      人手点「启动」,欠费清零不会有人去点,这个函数补上「扫一遍 block_reason=
      billing-arrears 的 blocked root run,欠费真清了就代它点启动」。调度触发源
      (webhook/轮询)留给计费接口接通之后再定。
    
    顺手修了一个 advanceBatch 的排序 bug:budget/billing 门必须在「确认真有 ready
    候选」之后才查,跟 advanceOne 的既有顺序一致——反过来会把一个已经收敛、只是
    恰好预算也花完了的 run 误判成「挂起」而不是正确收敛到 completed。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse File »
  • 自动模式的驱动原语 advanceTree 里 advanceOne 换成 advanceBatch:树上「最深
    active run」的选取逻辑不变,但推进这一步现在能一次并发 claim 一批 ready 节点,
    而不是每 tick 只推一个。核实过 advanceTree/runAuto 的返回值全仓库只被读 .ok,
    不依赖 .node/.results 的具体形状,不需要兼容层。
    
    顺带抓到并修了一个真 bug:advanceBatch 的 no-ready-node / taken-by-other 分支
    原来写的是 ok:true,跟 advanceOne 用 result() helper 的惯例(这两种情形一律
    ok:false)不一致。不修的话接进 runAuto 后,一个已经收敛的 run 会被自动循环
    一路空转到 maxSteps 才停——旧测试从没对已收敛的 run 连续调 advanceBatch,
    所以这条一直没被撞出来。
    
    新增两条测试:ok:false 语义与 advanceOne 对齐(直接绕开引擎构造边界状态);
    runAuto/advanceTree 端到端确实吃到并行(用真实 setTimeout 延迟证明并发窗口
    重叠,不是同步假象)。全量 vitest 147 文件 / 1350 用例,零回归。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse File »
  • planner 骨架(brd→prd→sdd→goal-benchmark→validator)复现:三个 planning 节点
    声明了 produces 却没真落盘,executor 没抛异常就被引擎判 success;goal-benchmark
    的入边 needs:[brd,prd,sdd] 只用来拼提示词里的 ⚠️ 提示,agent 视而不见照样产出
    合法 acceptance。下游 validator 永远读不到三份文档,backward 重试撞循环硬顶后
    只能人工兜底。
    
    settleNode 判 success 之前新增两道闸门(均只对 agent_call 节点生效,subgraph/org
    节点的契约由子 work run 自己的节点过闸):
    - unsatisfiedRequiredProduces:声明的 required produces 一个候选文件都没落盘 →
      fail_reason=produces_missing,不再放行
    - requiredInputsMissing:声明的 required needs 在 graph_files 里查不到实例 →
      fail_reason=missing_required_input,不采信 agent 自报的 success
    
    两者都只判「有没有」,不判「分类对不对」——ambiguous 归类留给下游 ⚠️ 提示词处理,
    避免把「写了东西但引擎猜不出对应哪个 kind」误判成没执行。
    
    包含此前已完成但未提交的 27-P4 T3(graph_runs.max_concurrency 落库)及配套
    advance-batch 测试。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse File »

  • ## 一、W7「零边图」不是待办 —— 它早修了,是注释在骗人
    
    `company-service.js` 的判据注释仍写着「判据用**有没有边**」,并留着一段
    「已知残留:单 org node 的最小图没有边,判据③不生效……真跑到那一步再看」。
    
    **那个残留在 `58a2865`(08-17)就修掉了**:`orgGraphMembership` 现在的主判据是
    `authored_by='planner'` 的节点存在(「orggraph-planning 提交过一张图」的直接证据,
    零边也认),边数降为存量行的回落判据;守卫 `company-hire-org-layer.test.js` 的
    `CO_ZEROEDGE` + `W7-fix` 两条盯着。
    
    注释没跟着改,于是它把读的人**引向一个已经修好的缺陷**去重修一遍 —— 我就是这么被
    带偏的:读到那段注释就没再往 `orgGraphMembership` 里跟,还据此往待办清单里写了一条。
    这是「指路牌失效」那一族,和 CLAUDE.md 死链同型,只是方向相反(那边指向不存在的东西,
    这边描述已不存在的洞)。
    
    判据的措辞也改了:**「成型」的判据在 graph-service.orgGraphMembership(),不在这里**,
    免得下一个人照注释理解。P3B 文档与 docs/README.md 的对应条目一并更正。
    
    ## 二、还上「真实路径 smoke 进 CI」那笔欠账的一半(27-P3/P4 §十 硬规矩 2)
    
    原话是「凡是靠注入点验的能力,都要有一条真实路径的 smoke 进 CI 才算验收完成 ——
    这条欠账仍未还」。此前唯一走真实路径的 `scripts/real-pm-smoke.js` 要真 API 额度、
    人工跑、而且验的是 DevLoop 不是图执行。
    
    做法是把「真 LLM」和「真代码路径」拆开:**只把最外层的 CLI 子进程换成 fixture 回放**,
    setAgentExecutor / setPlanWorkGraph 一个都不调。链路完整走
    advanceOne → defaultAgentExecutor → AgentManager → ClaudeCliProtocol
    → onAcpEvent → 落盘 → readNodeProduct → settleNode → spendBudget → 边界守卫。
    
    三条断言各盯一处**假 executor 天然绕开**的接线:
    · ① mock/files 真的落到节点产物目录(假 executor 直接 return,这一环整个不存在)
    · ② token 用量真的从 session/result.usage 数进预算 —— **就是 §六 验收 10 卡了两轮的那一维**
    · ③ boundaryGuardApplicable 在这条路径上为真(注入假 executor 时它恒为假,
         那条唯一有实际后果的分支实际零覆盖);带反向对照,注入后必须变假
    
    反向验过:断掉①的落盘、断掉②的累加,对应用例分别变红。
    
    为此改了一处 mock:session/result 的 usage 原来硬编码 {input:0,output:0},
    现在 fixture 可指定(缺省仍是 0,既有 fixture 行为一个字不变)。
    **不改这一处,token 那一维在 CI 里永远产不出数** —— 验收 10 当初就是卡在这儿。
    
    **没还清的部分写在文档里,别当成还清了**:真 LLM 仍进不了 CI;
    subgraph 那一支(defaultPlanWorkGraph)仍只有注入式覆盖;
    多节点 / 回退边 / 熔断没进这条 smoke,它只推一个节点。
    
    afterAll 清掉真实链路建出的公司态目录(gitignore 挡着不进库,但留着会让别的
    用例遍历 system/agents/ 时多扫一份)。
    
    全量:139 文件 / 1295 测试全绿。
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse File »
  • ## 上一版漏了什么
    
    预算行挂在 root run,我就只在 root 的转换上结算。而**活动发生在树里任何一层**:
    人工重试一个 work 层节点动的是子 run(blocked → running),root 全程 blocked。
    
    后果:子 run 跑整场、墙钟一次都没开始计,旧锚点还留着 —— 第一次 checkBudget
    立刻判耗尽。单测当时全绿,因为它们只喂了 root。
    
    **是真起服务、真点重试才撞出来的**:那条卡住的 run 重试后子 run 起来了,
    root 的 started_at 仍是 08-18。
    
    ## 改
    
    · 任何一层进 running 都开始计时(预算行仍写 root,readBudget 自动上溯)。
    · 离开 running 时先查树里还有没有 running 的 run —— 有就不结算,
      否则一层停了会把整段计时提前掐断。
    · 树里已经有 run 在跑时,另一条进 running 不重锚,否则前一段被抹掉。
    
    ## 存量重锚必须在开库时做
    
    钩子只在「进 / 出 running」时触发,而升级那一刻正停在 running 的 run
    不会再进一次 running —— 旧锚点一直挂着,下一次 checkBudget 直接判耗尽。
    
    ⚠️ 第一版把这段迁移写进了 graph-db.js 的 migrate(),而 graph_runs 是
    work-graph-db 建的表、graph-db 的迁移块跑在它之前 —— 新库上直接
    `no such table: graph_runs`,getDb() 起不来。已挪到建这张表的模块里。
    和 §16.2 那次「加索引写进建表块」是同一类顺序问题,这是第二次。
    
    守卫 budget-wallclock-migration.test.js 走的是**存量库重启**那条路
    (照抄 tasks-executor-migration 的 freshDb 写法):老锚点归零、running 的续上、
    其余断开、没配 wallclock_ms 的不许被塞字段、坏 json 不许把开库卡死、幂等。
    
    ## 真跑验证
    
    重启服务 → 迁移在真库上跑到(run_9168bd24f46c 的 started_at 归 null、active_ms=0)
    → 重试 org-planner-sdd → POST /next → **节点 success,attempt 2**,
    预算 8/100 次调用、78,858/5,000,000 tokens,org-planner-benchmark 已转 ready。
    
    这条 run 从 2026-08-18 16:19 卡到现在,是 27-p3c-batch3-handover §五
    「还没解决的原始问题」。
    
    全量:138 文件 / 1289 测试全绿。
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse File »
  • ## 病症:blocked 和墙钟预算互相拆台
    
    墙钟按 `now - spent.started_at` 算,而 started_at 是「第一次花钱的绝对时刻」。
    blocked 的定义恰恰是**挂起等人工** —— 预算耗尽、节点判罚都会把 run 打到 blocked。
    于是:人隔夜来处置 → 等待那段被算成「已用」→ 一起跑就报 wallclock_ms 耗尽
    → 再 blocked → 再等人。**一条过夜的 run 从此永远醒不过来。**
    
    而且报出来的原因具有误导性:写着 wallclock_ms(跑太久),实际是等人等太久。
    
    真事:run_9168bd24f46c(e2e-demo 做官网)08-18 15:48 起跑、16:19 判 blocked,
    只花了 7/100 次调用、69,961/5,000,000 tokens —— **预算用掉不到 2%,两天没能重跑**。
    它正是 27-p3c-batch3-handover §五 那条「还没解决的原始问题」。
    
    ## 修法
    
    spent 里改记 `active_ms`(累计活跃时长),started_at 退化成「当前这一段的起点」,
    不在 running 时为 null。已用 = active_ms + 当前段。
    
    起止结算挂在 graph-run-sse.afterRunTransition 这个**单一漏斗**上,不去枚举
    「哪些地方会让 run 停下来」—— 枚举漏一条就是静默少算。
    
    ⚠️ 钩子必须放在那个 early return **之前**:进 running 既不是收敛、也不空坑,
    两个条件都不满足,放后面就变成「只结算停下来、从不开始计时」,
    症状是 active_ms 恒 0、墙钟形同虚设,而且照样全绿。守卫里专门有一条钉这个位置
    (反向验过:把钩子注释掉,那一条立刻红)。
    
    spendBudget 不再兼职锚墙钟 —— 一个事实一处持有者。
    
    ## 存量行
    
    active_ms 缺失的行:旧锚点里混着等待时间,真实活跃时长复原不出来,
    按 0 起算、一笔勾销。有意为之 —— 另外两维(调用次数 / tokens)是精确的且没受影响,
    真正兜住花钱的是那两维,墙钟只是跑飞保护。所以那条卡住的 run 不需要动库,
    重试进 running 时自动重新锚定。
    
    ## ponytail
    
    只认 running 一个态。manual 模式下两次点击之间 run 也停在 running,
    那段人类思考时间仍会计入 —— 上限已知,比原来跨天累计小几个数量级。
    要更准得按 agent 调用的起止配对计时,那是另一套账。
    
    ## 顺带
    
    goal-task-acceptance 两条用例原先 deepEqual 整个 budget 对象,现在剥掉 spent
    只比申报的额度 —— 它守的是「用户填的额度没被静默换成默认值」,
    跟运行期账本长什么样无关;账本另有断言(起跑那一刻 agent_calls/tokens 仍是 0)。
    
    全量:137 文件 / 1284 测试全绿。
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse File »
  • ## ① system/tasklog/ 改名保留(§十二.5)
    派单存储统一进 tasks 表后,旧目录改名为 system/tasklog.bak-unify/ 而不是
    直接删 —— 跑顺一个版本再清。回填脚本改成**两处都认**(先原位置再备份位置,
    顺序不能反:万一有人从备份恢复出一份新的 tasklog,那份才是要回填的)。
    
    ## ② agent 提示词里的端口 3000 → 3010
    packages/agent-core/base/ 下那几份是**所有 agent 的工具说明**,印着
    curl http://localhost:3000/... 而实际端口是 3010。agent 照着打拿到的是
    连接被拒、不是「端口错了」,它多半会去猜别的原因。
    
    这不是假想:批 5 回填出来的历史派单正文里,有人手写了「(端口 3010,
    不是 3000)」在纠正它 —— 靠人肉在每条任务书里打补丁。
    
    守卫 agent-docs-port.test.js:扫 base 模板与 agent 本体,禁 localhost:3000。
    判据刻意宽松(不校验「必须等于 config.port」—— 端口可被 PORT 覆盖,写死会在
    部署环境误报)。带「样本非空」前置断言。第一版扫出 5 条误报,全是某 agent
    目录下 Python venv 里 playwright 的 ThirdPartyNotices —— 那是第三方文件不是
    提示词,已把 venv/site-packages/workspace/conversations/train 一并跳过。
    
    ## ③ 边界守卫不再把并行会话的改动算到 agent 头上(§15.5 第 1 条)
    守卫的前提是「窗口内新增的仓库改动 = 这个节点写的」。多会话共用 worktree 时
    这个前提是假的 —— git status 的差集里没有「谁写的」这一维。实测已发生:
    批 3 的开发改动把一条 run 判成 boundary_escape 卡住。
    
    可检测的信号:节点**开跑那一刻**仓库就已经有源码目录的未提交改动 → 有人正在
    这个 worktree 上开发 → 窗口内新增的源码改动无法归属 → **降级为只记证据、
    不判罚**(与既有的 unattributable 桶同一语义),而不是关掉守卫:证据照留,
    人一眼看得出「这次没判罚,因为当时仓库是脏的」。
    
    BOUNDARY_GUARD=strict 强制判罚(CI / 干净环境),=record 一律只记。
    守卫 8 例,含关键对照:仓库脏时不判、干净时同样一处越界仍判 fail ——
    守卫没被削弱,只是不再乱扣帽子。
    
    ## ④ TaskTimeline 的标签不再撒谎
    conversationCount 在批 5 之后只会是 0/1(「有没有起过执行会话」),而界面
    写的是 "Convs: N" —— 读的人会以为这条任务只有一次对话。改成有/无的表达。
    agents 的语义变化在注释里说明。
    
    注:innovator-hunt/todo.md 与 reddit-scraper/todo.md 里的端口也改了,
    但它们是 gitignored 的运行态文件,不入库。
    
    全量:134 文件 / 1268 测试全绿,web:build 通过。
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse File »

  • 队列与调度(§五.1):
    - 下达任务不再因「公司已有 active root run」吃 409,改为建 queued run + queued task
      排队;出队走 updateRunStatus 的 CAS + BEGIN IMMEDIATE 准入事务,触发挂在
      transitionRun 这个唯一漏斗上,60s sweep 兜底。
    - 依赖已不可能满足的排队任务就地推导成 blocked,且每次 tick 重算不落死,上游重跑
      成功后能原路解开。
    
    恢复路径(批 3 复核 F1/F2):三条恢复转换的 from 全在活跃集之外,撞坑满时退回队列而不是
    永远躺在 blocked;退不回去的裸 run 干净拒绝,不留半截状态。
    
    控制台三处可见性修复 —— 症状全是纯视觉、不报错、git status 干净:
    1. 顶栏墙钟恒 `now - started_at`,没有终点。一条昨晚就 blocked 的 run 第二天还在涨到
       「16h 58min」,而那是页面上唯一的时间信号 —— 「已经停了等人处置」被显示成了
       「还在跑再等等」,这两件事需要的动作相反。终点改取 run.updated_at,并标「已停」。
    2. 越界日志只报「留下 N 处改动」,文件清单躺在 timeline.ndjson 里翻不到。判罚就是按
       这份清单下的,而人要判断「这真是 agent 写的吗」唯一的依据就是路径(并行的开发会话
       改宿主仓库会被算到当时在跑的节点头上,那是误判,看一眼就知道)。改为列出 entries。
    3. run 收敛成 blocked 时只把 task 写成 blocked、不写原因,任务列表那一行只剩一个橙色
       的「挂起」,必须逐层点进 org 图 → work 图 → 节点详情才知道发生了什么。改为把
       「卡在哪个节点 + 什么 fail_reason + 判词摘要」写进 tasks.blocked_reason。
       原因绝不带 UPSTREAM_BLOCK_PREFIX —— 带了会被调度器当成依赖推导出的 blocked 自动
       推回 queued,而这类 blocked 本来就等着人处置(守卫在 task-blocked-reason.test.js)。
    
    测试:126 文件 / 1201 用例全绿。
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse File »

  • 把「人类提的一个需求」与「机器执行的一件活」拆成两级。这不是新概念:
    27-NAMING §四 早就定义了 goal 1 : task N,graph_runs.goal_id / task_id
    两列从 P3 起就一直在写,却没有任何表接得住 —— 本次补上那两张缺席的表。
    
    ## 交付
    - goals / tasks 两张表 + CRUD + depends_on 环检测 + ID 生成器
      T-{base36}-{4hex}:原格式同毫秒必撞,而 tasks.id 是主键,
      「一个 goal 拆 N 条 task」是循环建行,撞了就是 500
    - goal / task 全套端点;POST /:cid/tasks 向后兼容(不带 goal_id 自动
      建 goal);退役 /api/goals 与 /:cid/goals 两个 OKR 别名(零调用方)
    - 终态回写 + goal 收敛 + OKR 写一次:钩子挂在唯一漏斗
      graph-run-sse.transitionRun 上,而不是枚举路径 —— stopRun 与
      「预算耗尽 → blocked」两条最常见的释放路径都不走 settleRun,
      枚举一次漏一次。goal 收敛用 CAS 闸门,并行完成的 N 个 task 只有
      一个赢家做不可重放的副作用(写 OKR / 唤醒上级)
    - blocked 在 task/goal 两层都不是终态:它是「挂起等人介入」,
      与 completed/failed/cancelled 不同类。否则预算耗尽后加预算恢复、
      跑完,两次条件更新静默命中 0 行,最终 run=completed 而
      task=blocked/goal=failed/OKR=failed 永久对不上(实跑复现过)
    - goal 级是唯一汇报出口:删掉 run 级 reportRunOutcome(零其它调用方),
      1 goal:1 task 形态下它会把上级 agent 就同一件事唤醒两次
    - dispatch 挑第一条可跑的 draft,而不是恒取 seq 最小那条 ——
      否则「第 1 条依赖第 2 条」这种合法输入会让 goal 永远下达不了
    - 路径缺陷:writeTaskGoalArtifact 不认 GRAPH_RUNS_DIR 而读侧认,
      设了 env 就写进 A 处、从 B 处读,describeTask 拿到 null 后静默退回
      兜底文案「执行图节点任务」;三处模块加载时快照改为调用时读
    - 存量回填脚本(幂等 + --dry-run):不做的话批 1 反而让历史任务
      更看不见 —— 正是本批要修的问题的反向放大
    
    ## 过程
    三轮多 agent 编排 + 每轮对抗复核,共报出 19 条缺陷(10 条首轮实现的、
    4 条第二轮修复引入的、5 条判定为非问题)。全部已修并有用例覆盖。
    新增测试 4 个文件,含不变式测试 runs-root-single-holder(凡是声明了
    GRAPH_RUNS_DIR 覆盖的模块,该覆盖必须真的被用到)。
    
    全量:116 文件 / 1057 测试全绿。
    
    ## 未做(属批 2-5)
    task-planning 与 goal 级 org-validator 验收(批 2)、调度器与
    run_status='queued'(批 3)、并行(批 4)、两套任务体系统一(批 5)。
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse File »
  • 设计见 docs/done/system-design/28-agent-registry-unification.md。
    
    改造前 Agent 本体散在三处(system/agents/ 11 个、system/templates/ 50 个、
    data/companies/{id}/agents/ 17 个),同一个 AgentScaffold 被三个 baseDir 实例化。
    后果是同一角色在 N 家公司下有 N 份互不相干的副本,模板更新不会同步;
    org-coder / org-planner 这类走 hireFromBaseMetaAgent 造出来的本体
    legacy_path 指回自己公司目录,永远无法被第二家公司复用。
    
    本次:
    - G1 单一注册表:system/templates/ 整体并入 system/agents/,base → _base。
      7 个同名 id 逐个裁决,被丢弃的一份归档到 docs/_archive/ 而非删除。
      source_kind(template/global_agent/company_hired) → origin(builtin/created)。
    - G2 运行态下沉:CLI 的 cwd 从本体根改为 system/agents/{aid}/{cid}/{taskId}/。
      memory / conversations 落公司态 {aid}/{cid}/,按公司隔离不串。
      sessionPool 的 key 含 (companyId, taskId),两家公司调同一 agent 不复用会话。
    - G2b 产物不下沉:交付物仍落 data/companies/{cid}/,system/agents/ 只留运行痕迹。
      写入边界按链路分叉——graph 节点走节点私有目录(被 indexNodeOutputs 索引),
      chat 走公司 workspace,同一提示词里不再出现两条互斥指令。
    - G3 公司只存引用:data/companies/{id}/agents/ 删除,新增 company_agents 关联表。
      不复用 org_graph_nodes——后者是执行流图,work 层本体被雇时不写它,天生覆盖不全。
    - G4 缺则创建:本体缺失时从 _base 复制骨架进 system/agents/{id}/,
      agent-creator 填 agent.md/soul.md 后才从 draft 转 active(哨兵判据,
      骨架未填时哨兵仍在,不许被 hire)。产物即刻是全系统可复用的本体。
    
    新增 server/services/agent-locator.js 收编原先散在 6 处的寻址逻辑。
    
    边界守卫(graph-engine)的豁免从「本体根上五个文件名」放宽为整棵 system/agents/:
    新本体的骨架本来就要进 git,gitignore 挡不住 ?? system/agents/{newId}/,
    旧判据会把一个真正成功的招聘节点判成 boundary_escape fail。
    
    存量迁移见 scripts/migrate-agent-registry.js(幂等,备份到 data/_migration-backup-*)。
    
    真 Claude CLI E2E 已验证:新建公司 → orggraph-planning 自动规划 →
    自动招聘 org-planner/org-coder/org-validator 三节点流水线;
    data/companies/{cid}/agents/ 不存在,company_agents 4 行,
    system_agents 56→56(零新建私有本体,纯复用),git status 零增量。
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse File »

  • 第六次真 CLI 冒烟 gapClosed=true:org 层契约落库 4 行(此前恒 0),
    且那段文字真的进了 workgraph-planning 收到的提示词(52677 字节,第 746 行起)。
    
    ## workflow 四路并行的产出
    
    - **org 层两半一起接**(关键顺序):先接读回方 buildPlanningPrompt 的 ③bis 段
      (org 边入边 → 上游绝对路径;出边 produces → 「你规划的子图必须有节点产出这些」),
      再接声明方 orggraph-planning 模板。只补模板的话就是又一次「声明了没人读」——
      org 图节点全是 subgraph,而 describeInputs 对 subgraph 直接返回空。
      resolveInboundLines 从 describeInputs 抽出共用,两侧口径物理上不可能漂移。
    - **清掉 404 死路径**:模板三处教的 POST /api/company/:id/work-graphs 从未注册。
      真跑复验:agent 侧零调用,改为按新 Step 7 写项目根绝对路径,还自己 require
      validateWorkGraph 预检了一遍才交回,round=1 一次过。
    - **四张骨架补 produces/needs**:从「靠模板文字施压」变成结构保证,每张过规则 6。
      真跑里 agent 照抄了 planner.json 并跟着任务删掉 sdd 节点——是改编不是复制。
    - **graph-seed 顺序依赖**:靠生产代码里已有的 GRAPH_SEED_ROOT 缝隙指向冻结快照。
      不是「跑几次都绿」结案:构造了原先必红的 churn,修前 12 次 3 红、修后 15 次 0 红。
    
    ## 我自己收的 4 条 major + 1 条 minor(都在 graph-engine.js)
    
    复验挖出「引擎自己拼的提示词在教 agent 做模板明令禁止的事」,同一条提示词里对立:
    1. 「已自行落库成功时回一行 workGraphId: wg_xxx」vs 模板「你永远拿不到真的 wg_ id,
       永远别写」。agent 没有任何受支持的落库途径,写出来必是编的,而 extractPlannedGraph
       优先匹配那个形状、查库查不到就整轮作废。删掉邀请,保留引擎解析真实 wg_ 的能力。
    2. 内联图示例的边一个 produces 都没有——agent 唯一见过的「边长什么样」是零声明的。
    3. 「端点尚未上线」暗示将来会上线,于是它还想去试一次。改成「不存在、也不会上线」。
    4. indexNodeOutputs 收集 declared 不按 role 过滤,把出边的 needs 当成本节点的产出。
       Chrome 走查实测:产品需求文档.md 落成 artifact,下游 prd 输入变 ⚠️。
       更坏的一面是错标(brd_notes.md 命中中继 needs[brd] → 归因给错的产出方)。
       1ea9efa 加的 role 列,这里正是它存在的理由。
    5. (minor) contract.prev.json(retryNode 的契约快照)被索引成 artifact 噪声。
    
    新增 guard:引擎提示词不许与模板对立(静态读源码,剔掉注释行——
    第一版把「解释为什么不许说某句话」的注释也扫进去了,量错了对象)。
    
    测试 850 全绿(83 文件),既有断言未改。5 处变异精确变红后逐字还原。
    Chrome 走查确认控制台正常分支(此前只验过降级分支):契约药丸、绝对路径、
    needs 中继说明、tab 上的 ⚠️ 计数全部渲染,零 console 报错。
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse File »
  • 第五次真 CLI 冒烟(gapClosed=true)暴露的两条,都是「这轮成立是侥幸」型。
    
    1) indexNodeOutputs 靠文件名定 kind
       真跑成立纯属 brd 节点恰好把文件命名成 `brd.md`。换写 `业务需求文档.md`
       就落成 kind=artifact → 下游入边声明的 brd 查不到实例 → 提示词退化成
       「⚠️ 索引里查不到实例」,即刚关掉的断点换个形式重现。agent 给产物起中文名
       是完全正常的行为,不该由它决定索引对不对。
       改成两遍:先按文件名认领,再在**一对一无歧义**时(还差一个 kind 没人产、
       正好还剩一个认不出的文件)按声明认领。有歧义不猜 —— 猜错比留 artifact 更坏,
       错的 kind 让下游读到一份不是它要的文件,artifact 只是查不到、会如实报 ⚠️。
    
    2) describeInputs 任务级共享不去重
       真跑里 brd 与 prd 两个节点各写了一份 contract.json,都是 kind=contract,
       于是往后每个节点的提示词里「contract(任务级共享)」逐个累加。节点一多是噪声,
       且给 agent 两个同类路径等于把一个我们自己都没定的选择推给它。
       按 kind 保留最新那份,与沿边那一半的 byKind.set 同口径。
       ponytail 标了根因(节点私有目录的文件也会按文件名落成 task-shared 的 kind)
       与真正的修法(任务级产物只认 tasks/{taskId}/),后者要动 R-9.3 现有语义,
       归到那条线一起定。
    
    测试 829 全绿(+3),既有断言未改。4 处变异精确变红后逐字还原。
    含 verify agent 回填的 §4.6「第五次真 CLI 冒烟」小节。
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse File »
  • R-19.A 把 declareEdgeContract 的调用方补上之后,inboundContracts 终于非空,
    「沿边传递」这类数据流在真跑里生效了(真实规划 agent 自己在边上写出了
    produces/needs 声明)。本次收掉它留下的三个缺口。
    
    1) role 落库(第 14 次「四环漏一环」)
       normalizeContracts 产出 role,declareEdgeContract 入参没有它、表也没这列 ——
       产生 → 传递(丢) → 落库 → 读回。后果不止是控制台不精确:规则 6(data-contract)
       靠「有人 needs 但没人 produces」判错,只存 kind 就没法从库里重算,而
       patchGraph 第 5 步的重新校验读的正是库 —— 增量 PATCH 又恰恰是
       「此前满足的 needs 失去产出方」的唯一发生地,校验偏偏在最该生效的通道上失效。
       - graph_edge_contracts 加 role 列(CHECK + 老库 ALTER 后回填 produces:
         NULL 在规则 6 里既不算产出也不算需求,会误拒一张合法的图;闸门宁可漏判不可错拦)
       - 两层调用点透传 role;readGraph 把 contracts 读回来并递进 validateGraph
    
    2) 控制台三字段(前端在、后端没喂)
       GET /api/graph-runs/:id 补 edges[].contracts / companyRoot / taskFiles,
       否则 R19-C 那三块界面整块降级:声明和索引都在库里,界面上看不见。
       contracts 只在非空时出现,与 failReason 同一个约定。
    
    3) 收拢 companyRootDir/taskDirRel 镜像
       goal-contract.js 里那份镜像靠注释约定「与 graph-engine 逐字同构」维持,
       控制台路由成为第三个调用方时这个约定就不成立了。抽到 company-paths.js,
       GRAPH_RUNS_DIR 改为调用时读(原先两个模块各自在加载时快照,
       加载顺序不同会算出不同的根)。goal-contract 那条「镜像漂了就报警」的用例
       按其自身预告改成比函数返回值。
    
    测试 826 全绿(+11:role 8 / 路由 3),既有断言未改。
    11 处变异逐一验证精确变红后逐字还原;其中一条断言初版在 fixture 上永远自洽
    (图里没有无声明的边),补了一条无声明的边才真正有判别力。
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse File »
  • 第四次真跑的断点:org-planner-prd 失败,它自己的归因原话是
    「不是上游没产出,是它的产出**结构上到不了我手上**」。核实属实 ——
    上游 brd.md / contract.json 落在**节点私有目录**,prd 的提示词只有它自己的私有目录
    + 只读 artifacts/,而 brd--success-->prd 这条边**不携带任何文件引用**。
    边一直只承载控制流(condition/edge_kind/fail_reason),R-4 要的「边的输入输出」
    被落成了从 output_ref **推断**的 UI 展示、没有实体,而推断的那一端下游读不到。
    
    ## 定案(27-REQ R-19)
    
    - 建 sqlite 表而不是 files.json:JSON 会重演 _registry.json 的并发重写与 git 冲突
      (本轮刚被它咬过,W13 只能把 usedCount 挪去另一个被 ignore 的文件)
    - **两类数据流都要**:纯边绑定表达不了 R-9.3 的「planner 生成、validator 执行」——
      org graph 是 planner→coder→validator,acceptance 跳过 coder,这两点之间没有边
    - workspace 根**代码推导不入库**:存绝对路径会烂,W13 清的那 10 条 legacy_path
      悬空行就是这个病。表里只存相对公司根的路径
    - 路径走 tasks/{taskId}/,不走 workspace/:后者语义是「Agent 自治领地、框架不干预」
      (06-沙盒自治),与「框架管着、可检索的交付物区」是两种所有权
    - 契约(kind)与实例(path)分规划时/运行时两阶段 → 引擎可在节点跑之前校验
      「你声明要吃 brd,但上游没人产 brd」,这类图结构错误此前只能等跑到那步才发现
    
    ## 实现
    
    work-graph-db:graph_files(实例)+ graph_edge_contracts(契约)两张表。
    graph_files 的当前版本走**部分唯一索引** WHERE superseded_by IS NULL ——
    与 idx_org_graph_nodes_company_role_active 同一模式:要「同键只有一个活的」
    又要留历史,条件得写进索引而不是靠删行。API:recordFile / findFiles /
    declareEdgeContract / edgeContracts / inboundContracts / rebuildFilesFromNodes。
    
    graph-engine 两侧都接:
    - 输入侧 describeInputs:按**入边声明的 kind** + **本 task 共享产物** 查实例,
      把绝对路径拼进提示词。声明了却查不到的**明说缺哪个 kind**,不静默省略。
      刻意不是「注入一句『索引在某文件,自己去检索』」—— 那仍靠 agent 自觉拼路径,
      真跑已证明它拼不出来;引擎直接递到手上,不给它猜的机会
    - 产出侧 indexNodeOutputs:节点 success 时登记落盘文件,kind 先看出边声明、
      再按文件名兜底;attempt-*.json 是引擎自读的归因文件,不当产物登记
    
    文档:27-REQ 新增 R-19(四条,含两段原话);27-P4 §4.6 按定案重写
    (原「公司根一个 files.json」作废)。
    
    测试 788/788 全绿。后续(下一轮):规划侧声明契约、goal-contract 路径归并、
    前端「输入输出」tab 展示解析结果、真 CLI 复验 prd 能拿到输入。
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse File »
  • 测试 786/786 全绿(76 文件,起点 760/72),未改任何既有断言。
    
    ## 本轮三件新东西,全部经真 CLI 验证
    
    W12 补完 · 失败原因四环打通
    - 引擎自判的失败(executor 抛异常、边界越界、子 run 无归因)现在也落
      graph_run_nodes.fail_reason,不再只有 agent 归因产出的才有值
    - 概览 tab 展示「[引擎判定] boundary_escape」+ 终端样式 excerpt,
      取不到 fail_reason 时回落 output_excerpt,并标明来源
    - 真跑实查:两行 fail_reason='boundary_escape' 都在库里,
      产生→传递→落库→读回四环在真 CLI 下走通
    
    W13 · 运行态文件根治(守卫误判的根因)
    - 17 个 todo.md / plan.md / scheduled_tasks.json git rm --cached + gitignore;
      源码(agent.md/config.json/soul.md/tools.md)一个未碰,模板本体仍跟踪
    - _registry.json 的 usedCount 挪进已忽略的 data/template-usage.json
    - 真跑验收:整轮(agent-creator、Master 各一次真 CLI、3 次 hire)结束时
      worktree git status 与基线逐字相同,_registry.json 零 diff
    
    单步进度感 · 真 CLI 一个节点 3-7 分钟
    - 画布卡片秒表 + 详情面板常驻条「运行中 2m40s · 最近事件 …(2m40s 前)」
    - 不做假进度条:引擎不知道 agent 还要多久
    - 顺手修 NodeDetailPanel 不跟 SSE 走(否则秒表也不会动)
    
    W9 失败上下文进提示词、W10 跨层地址两半、W11 Hot Swap 显式告警同批完成。
    
    ## 真跑结论:链路更长,但「work 节点 → success」仍未跑到
    
    走到:orggraph-planning 落图自雇 → agent-creator 补定义 → workgraph-planning
    真跑规划 + **先雇后提交**(W5/W6 再次成立)→ 子 run 起来 → work 节点 RUNNING
    → 7m19s 后 fail。两层原因叠加,都与 W1-W14 无关:
    
    1. **新缺陷**:agent-manager.js:324 把 idleTimeoutMs 硬编码 120s,
       readme-writer 通读源码静默超 2 分钟被掐死(动笔前就死了)。
       同进程 ClaudeCliProtocol 早把自己的 idle 上限放到 24h,
       stream-executor 的 JSDoc 默认值写的是 30 分钟 —— 这里把两者都压掉一个数量级;
       且 ...options 透传的 idleTimeoutMs 会被下一行覆盖,调用方无法覆写。
    2. **并行 agent 污染**:retrain-watchdog 每 5 分钟唤起的 Master 在同一工作树里
       改了两个源文件,被守卫 git status 差集算到该节点头上 → 额外署 boundary_escape。
       这是 W3「拦不住」第 5 条第一次真实发生。
    
    顺带记一笔:这一轮任务是 **LemonBoss 自己的 Master 自主下的单**,
    走的正是 POST /api/company/:id/tasks,它还自行诊断出了上面那个 idle timeout
    并动手改(引用了未定义的常量,是坏的)。
    
    ## 已知未修(下一轮)
    
    - idleTimeoutMs 硬编码 120s —— 「work 节点 → success」跑不通的唯一技术原因
    - W7 在**零边最小图**上失效:判据③要求存在 active org_graph_edges,
      零边图上三条判据全落空,work 层本体被写进 org_graph_nodes。
      服务层那 9 例测试造的都是有边的成型图,正好绕开这个形状
    - 前端 ACTIVE_RUN 含 blocked、后端 ACTIVE_ROOT_RUN_STATUS 不含:
      一家公司只要失败过一次,控制台上就再也下不了第二个任务(后端其实放行)
    - 真跑期间工作树里不能有第二个 agent 活动,否则守卫判罚不可信 —— 应写进真跑规程
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse File »
  • ## 1. 子 run 转终态不回写父节点 → 任务永久卡死(界面走查发现)
    
    复现:org 节点的子 work run 第一次失败 → 父节点判 fail → 入口节点没有 fail 出边
    → blocked → 用户在中栏把子 run 修好跑到 completed → **父节点仍钉在 blocked,
    root run 也钉住,org 层永远推不下去**。唯一人工出路是对 org 节点点「重试」,
    而那会把整张子图重置到下一个 attempt,刚跑完的成果全丢。
    
    根因:propagateToParent 的幂等守卫写的是 `parentNode.status !== 'running' → return`,
    判据用错了维度——它想防「同一个结果被结算两次」,实际拦掉的是「子 run 换了个结果」。
    subgraph 节点的状态本来就是从子 run 派生的,子 run 换了终态就该重新派生。
    
    修法:新增 reviveParentFromChild,只认「转好」(fail/blocked → success),
    且要求 parentNode.child_run_id 正是这个子 run。恢复走 **blocked → ready → running
    → success 三步合法转换**,而不是给全局放宽 NODE_TRANSITIONS——转换表是给所有调用方的
    安全网,不该为一条恢复路径把 blocked → success 对外开放;三条 node.status 事件
    也让「这个节点是被救回来的」在 timeline 上看得见。
    
    补测试时才暴露的第二半:父 run 多半也钉在 blocked,而 RUN_TRANSITIONS 没有
    blocked → completed,settleRun 在转换非法时是**静默跳过**的。所以复活时要先把
    父 run 从 blocked 拉回 running(这条合法),否则节点复活了 run 还是卡着。
    
    ## 2. 边界守卫把并行 agent 的写算到当前节点头上(真 CLI 冒烟的断点)
    
    真跑实测:招聘模板库里没有的角色走 hired_from_base,自动流水线起 agent-creator,
    而它的 CLI workDir 就是 system/agents/agent-creator/——一个被 git 跟踪的目录。
    它在里面重写自己的 todo.md、删一条 scheduled_tasks,被整仓 git diff 扫到,
    归属算到当时正在跑的 work 节点,一个**真正成功**的节点被判 fail。
    只要这些运行态文件还被 git 跟踪,每次含 hired_from_base 的招聘都稳定复现。
    
    修法:splitUnattributable 把 agent 运行态文件(todo.md / plan.md / memory.md /
    _lessons.md / .claude/scheduled_tasks.json)从越界证据里摘出去,单独记一条
    unattributable,不参与判罚。摘除范围刻意窄——同目录下的 agent.md / config.json
    等源码仍然算越界。
    
    路径提取按「首个空白之前」切而不是固定偏移:diffRepoStatus 已去掉前导空格,
    porcelain 状态位长度不定(修改类 "M path" 1 字符、未跟踪类 "?? path" 2 字符),
    固定 slice(3) 恰好对**修改类**失效——而 agent-creator 改自己 todo.md 正是修改类。
    这条单独立了一个回归用例。
    
    ponytail: 这是缓解不是根治。根治要把运行态文件从 git 移出去(27-P3B W13),
    本次只保证守卫不再冤枉节点;同一次招聘里 agent 若写了运行态文件之外的源码,
    仍会算到当前节点头上。
    
    测试 760/760 全绿(72 文件,起点 753/71):新增 graph-engine-revive-parent.test.js
    4 条 + graph-boundary.test.js 3 条。
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse File »
  • 真 CLI 冒烟(worktree 隔离,PORT 3010)九轮来首次把 work 节点跑起来:
    orggraph-planning 真跑 3 分钟自主裁成单 org node 零边图 → workgraph-planning
    第 1 轮产出合法图 → **自己按 Step 6 把 brd-planning / planner-validator 雇进公司**
    (W5/W6 核心验收成立,且 W7 判据生效、两者都没写进 org_graph_nodes)→
    第一个 work 节点 brd-planning 真起 CLI 子进程,产出 brd.md / contract.json /
    attempt-0.json 全部落在 run 目录内。图内预算仅 2 次调用 / 13998 tokens(上限 15)。
    
    W5/W6 · workgraph-planning 自雇:撤掉「你不招人也不辞人」,Step 6 加招聘环节,
      写死「先雇、再提交图」的顺序,给出失败分级与幂等要求
    W7 · work 层招聘不写 org_graph_nodes:按 type 与 work graph 骨架点名两条判据区分层级,
      P2 「hire org 角色 → org graph 出现该节点」的语义保持不变
    W8 · 控制台「规划组织结构」按钮(抽成 ReplanOrgGraphButton.vue,带过渡态与秒表),
      接 POST /:companyId/org-graph/replan(该端点此前无任何前端调用方);
      新增 fixtures/devloop-mock/orggraph-planning.json
    W12 · 节点失败留痕:node.output 带 output_excerpt、新增 node.error 事件。
      真跑当场救场——守卫误判那条归因正是靠它才查得出来,
      换成之前看到的只有「节点 fail、无原因、无痕迹」
    
    测试 753/753 全绿(71 文件,起点 736/68)。
    
    已知未修(下一轮):子 run 转终态不回写父 node_run 导致任务永久卡死;
    applyBoundaryGuard 用整仓 git diff 判归属会把并行 agent 的写算到当前节点头上;
    W12 的 fail_reason 未落库(概览 tab 仍为空);POST /:cid/tasks 无前端调用方。
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse File »

  • 测试 736/736 全绿(68 文件,起点 719/66,净增 17 条,未改任何既有断言)。
    
    W1 · seed 接进启动流程 + 悬空行回收
    - server/index.js 与 agent-server.js 均在 listen() 前调 runStartupSeedSafely()
      (幂等、失败降级记日志、不挡服务启动)
    - seedTemplates() 此前**没有任何运行时调用方**,只有测试与手动脚本引用,
      于是 system/templates/ 新增本体永远进不了 system_agents,而
      work_graph_nodes.system_agent_id 是指向该表的外键
    - 占位 id 回收:legacy_path 已失效的 company_hired 行改为可被 template 就地转正,
      保留行 id 以保外键。此前 seedTemplates 遇占用即 skip,导致污染不可逆:
      lookupSharedAgentMd 恒 null → hire 静默退回全量拷贝 → 模板改动对已雇公司不生效
    - 生产库实查:company_hired 0 行(原 10 条悬空全清)、template 45、
      verifyCounts().ok=true、foreign_key_check 空
    
    W2 · 项目根分配(四环闭合)
    - ensureProjectRoot 复用 path-resolver.allocateProjectRoot(sandbox 模式)
    - 分配 → 进 agent_call 与 planning 两处提示词 + callAgent metadata
      → 落 artifacts/project-root.json(wx 独占)→ readProjectRootRecord 读回
    - 子 run 经 createRun 继承 root_run_id,跨层共享同一项目根
    - 核实 data/companies/ 在 .gitignore 内,故项目根内的合法写入不会被 W3 守卫误报
    
    W3 · 产出边界守卫(部分,文档如实标注)
    - 真 CLI 是子进程直接写盘,引擎拦不住,守卫只能拒收结果、不能回滚
    - 7 条「拦不住」的情形逐条列明;文档自述「在 cwd 隔离做掉之前,
      真跑仍应在可丢弃的工作副本里做」
    
    W4 · 修验收 fail-open(性质严重)
    - benchmark-validator / testdata-validator 的 Step 表教 agent 写 verdict:"fail",
      而引擎 FAILING_VERDICTS={attributed,inconclusive} 不含它 → 契约非法的产物
      被判 success 放行。失效方向是放行不是拦截
    - org-validator 的审计字段 verdict → audit_verdict:它与 work 层判词写同一路径
      (nodes/<nodeRunId>/attempt-<n>.json),此前只靠「该节点恰好是 subgraph」
      才没被 readNodeProduct 当判词读,属隐患藏在巧合里
    - 新增防线 template-verdicts.test.js,从 graph-engine.js 源码取
      FAILING_VERDICTS / ATTRIBUTION_REASONS 扫全部模板;经三次变异验证有效
      (改回 "fail" / 改回 verdict / 造 "ok" 均变红并报精确行号)
    
    真 CLI 冒烟未执行,两条理由(终验 agent 自行判断,符合任务给的门槛):
    ① W3 按其自身文档明确「不是沙箱」且要求真跑在可丢弃副本里做,而当前是主仓库、
       持有未提交交付物、且有并行 agent 在写,W3 自述的误判项必然触发;
    ② 断点已零成本静态证明:getAgentConfig 解析裸名只查 system/agents/、
       从不查 system/templates/,resolveAgentName 不做按需 hire,实测 prd-planning /
       sdd-planning / benchmark-validator / testdata-validator / validator-triager /
       org-validator 全部 throw「Agent not found」。花 $3-8 复现已知断点无意义。
    故「做完这四件就能真跑」不成立,真跑挪到 W5/W6 之后。
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse File »
  • 界面走查(真 server + HTTP + 真 AgentManager,底层 mock 协议)已把
    manual 模式全流程点通:org 节点起子图 → 下钻 work graph → 单步 →
    第 3 步触发回退(徽标 0/3→1/3、run 级 1/5 双层计数同时可见)→
    归因记录与 remediation_note 完整送达 → 收敛后父节点结算、左栏前移。
    
    mock 侧补两处缺失接线(都是「四环漏一环」,非新功能)
    - ClaudeCliProtocol._sendMockPrompt 按 agent 类型查 fixture:图执行侧的
      agent 名带公司前缀(resolveAgentName 返回 {cid}:{id}),fixture 不该
      按公司复制一份。DevLoop 侧名字不含冒号,行为不变
    - graph-engine.defaultAgentExecutor 接住 mock/files 事件:此前只有
      DevLoop 的 loop-controller 接,图执行侧没接,于是 mock 下
      readNodeProduct 永远找不到 attempt-{n}.json,validator 系节点恒 blocked。
      写入限制在节点产物目录内,越界丢弃并留痕
    
    新增 5 份 P3 fixture(workgraph-planning / prd-planning / sdd-planning /
    coder-validator / validator-triager),coder-validator 用 sequence 演示
    一次回退。图必须放 chunks 不能只放 result——callAgent 的 output 来自
    流式 chunks,session/result 的文本不进 output。
    
    控制台入口:CompanyDashboard 加「🕸️ 执行控制台」按钮(路由早已注册,
    此前无任何入口,只能手敲 URL)。
    
    scripts/p3-demo-setup.js:手工测试准备脚本(雇人+写图+起 run),头部
    写明它是两个已知缺口的绕行,缺口修好后第 1 步即可删。
    
    _registry.json 补齐 6 条本体登记(brd/prd/sdd-planning、goal-benchmark、
    benchmark/testdata-validator)——同轮新增的 template-refs 防线测试正是
    因为缺这 6 条而变红,防线抓到了「交付与报告不符」。
    
    文档
    - 27-REQ R-18:招聘归 workgraph-planning(方案 a)、停用 mock 改真 CLI
    - 27-P3B 真实运行前置(新增):11 个工作项分三批,含现状核实 10 条证据
      (seedTemplates 无运行时调用方、10 条悬空 company_hired 行、
       FAILING_VERDICTS 不含 "fail" 导致验收假放行等)
    
    测试 719/719 全绿(66 文件)。
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse File »
  • 按 27-p3-p4-work-graph-console.md v0.3 实现,三轮开发+校验后测试全绿
    (693/693,P2 26/26、DevLoop 230/230 未被打破,web:build 干净)。
    
    服务层
    - work-graph-db.js  定义层(work_graph_*) + 运行时层(graph_runs/graph_run_nodes/
      graph_run_loop_records) 五张表;状态转换表照 unit-registry 模式;
      复用 org-graph-service 四项校验 + 新增第五项 fail-fallback-edge
    - graph-engine.js   advanceOne 幂等原语;agent_call / subgraph 两种 executor;
      手动与自动共用同一原语;双层熔断(单边 loop_count + 按 root_run_id 共池的
      backward_count);任务级预算挂 root run;三层暂停/停止级联;
      重试按 contract_hash unchanged 判据
    - graph-run-sse.js  按 run_id 广播,状态转换+timeline+SSE 三者原子化
    - goal-contract.js  验收契约格式与执行器(R-15.6 验收先行)
    
    引擎泛化(R-15.4):org graph 与 work graph 走同一套 advanceOne,
    定义层两套、运行时层一套,三层下钻用 parent_run_id 链。
    
    R-16 失败归因:validator-reflection 专职归因节点;fail_reason 多边路由
    (精确匹配→兜底边→死路);子 run 收敛透传 success/fail + fail_reason +
    remediation_note 三值;infra_flake 不烧任何计数。
    
    路由 / 前端 / 模板
    - routes/graph-run.js 八个端点;company.js 任务入口
    - WorkGraphCanvas.vue 扩展 UnitsDAG(subgraph 角标、backward 虚线曲线、
      N/3 循环徽标、异常流程视觉降权);GraphConsole.vue 三栏 + 四 tab 详情
    - 五个新 agent 模板:validator-reflection / org-validator / planner-validator /
      coder-validator / workgraph-planning
    
    命名统一(R-15.3 + NAMING §三):org 节点改为 org-{职能},
    三个 org-graph 模板末节点收敛到 org-validator;org_graph_edges 加 fail_reason
    列并把唯一索引补上该列(避免精确边与兜底边互相吞掉)。
    
    三轮校验修掉的主要缺陷:subgraph 重跑不起新子 run 导致永久死锁;
    SSE/timeline 零调用方;org 侧 fail_reason 写入被丢弃;fail-fallback-edge
    只做了一层;backward_count 跨 run 树重置架空熔断外层;
    seedEntryNodes 误点亮只有 fail 入边的节点。
    
    已知缺口见后续提交:remediation_note 仅在 backward 边落库、
    total_tokens 预算维度无生产方。
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse File »