• ## 上一版漏了什么
    
    预算行挂在 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 »