• R-20.5 四条:
    1. task-planning agent 提进 v1(原计划 v2 人手拆)。产出一律 draft
       清单,人确认编辑后才下达 —— 那一步是唯一一道成本闸门,
       拆出 5 条 task 就是 5 份 DEFAULT_TASK_BUDGET
    2. 并行仍留批 4,v1 只排队(max_parallel_tasks=1,唯一索引不动)
    3. manual 任务不参与自动出队:startRun(manual) 只点亮入口就停下等
       /next,而 run 已经是 running 占着坑,自动拉起会堵死队头
    4. 上游失败 → 下游自动推导为 blocked 并标红,给「重跑上游」和
       「强制放行」两个出路。blocked 每次 tick 重算而不落死,
       上游重跑成功后下游自己回 queued
    
    定案 1 牵出一处新缺陷:task-planning 跑在 goal 层、任何 run 存在
    之前,它烧的 token 没有预算池可扣 —— 正是 defaultPlanWorkGraph
    注释里点名的「隐形的一半花费」在 goal 层的新实例。上一轮刚以
    「零消费方」为由砍掉的 goal 级预算字段,以记账字段 planning_tokens
    的形式回来(闸门仍不做)。
    
    链路照抄 defaultPlanWorkGraph 不另造:调 agent → 校验 → errors
    原样回喂,最多 3 轮;3 轮不过降级成 1 条同描述的 task,绝不能把
    goal 卡在 planning。新本体必须与 _registry.json 登记同 commit,
    否则 template-refs.test.js 当场把 CI 跑红(§八.21 有前科)。
    
    验收 19-24 新增:拆分正常 / 失败降级 / 记账 / 不该花钱的路径不花钱
    / 上游失败下游标红且可恢复。
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse File »
  • 逐条对着代码核初稿,最要命的一条是基础事实搞错了:
    
    - createRun 在建行的事务里就按 readDefGraph 把 graph_run_nodes 全插好了。
      初稿三处基于「queued run 没有节点行、进去就是空指针」的论证全部作废。
      真实情况是 queued run 与 ready run 在数据上只差 run_status 一个字段,
      所以「靠没有节点行自然报错」那道想象中的防线根本不存在,
      唯一的闸门是 advanceOne 里 run_status !== 'running' 那一句,必须加断言钉死。
    
    由此派生出一个既存、但被排队从毫秒窗口放大成必然事件的缺陷:
    节点行在 createRun 时快照,而 getRunState 每次读都取当前定义。
    排队期间点「重新规划组织」→ 新节点没有 run 行 → 出队即卡死。
    补 syncRunNodesToDef,出队前对齐。
    
    其余六处:
    - 出队原语写错:markRunReady 把 from 写死成 planning,复用不了;
      正解是 updateRunStatus(runId,'queued','ready') 这一条条件更新
    - 唯一索引冲突是 throw 不是 changes=0,初稿的错误处理只覆盖一半
    - 触发点枚举本身是漏的:stopRun 与预算耗尽→blocked 两条最常见的
      释放路径都不走 settleRun。改为挂在唯一漏斗 transitionRun 上,
      一个钩子覆盖全部路径(枚举法在这里靠不住,而漏的症状是静默的)
    - report_to 只活在请求闭包里,排队后回调链路静默断掉 → 必须落库
    - okr_node_id 逐 task 下发会反复覆写同一个 OKR 节点,
      done 会被后来的 failed 翻掉 → task 的 run 一律不带 okr_node_id
    - manual 任务占着 running 等人点「下一步」,会永久堵住队头
      → 不参与自动出队
    
    补漏:存量回填脚本(不做的话批 1 反而让历史任务更看不见)、
    任务级 rerun 与节点级 retry 的命名撞车、queuePosition 排序口径
    必须与调度器同源、砍掉没有消费方的 goals.budget_json。
    
    错误结论留档不删(§三 ⚠️ 块 / §五.1 / §九.1 开头)——
    它们是最容易再犯一次的那类。验收从 12 条涨到 19 条。
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse File »
  • 控制台走查暴露的「一个 company 只能有一个任务」,根因不是并发约束,
    是「人类提的需求」和「机器执行的单元」被压在同一条 root graph_run 上。
    
    三个实地核实的前提,让这次不是发明新概念:
    - 27-NAMING §四 早就定义了 goal 1 : task N 的基数
    - graph_runs.goal_id / task_id 两列早就在,company.js 注释也写着
      「三者基数不同不可混用」—— 但全仓库没有任何写入方,goal_id 是悬空外键
    - G- 前缀已被 OKR 正名释放;/api/goals 与 /api/company/:cid/goals 这两个
      OKR 向后兼容别名全仓库零调用方,可以退役后把路径要回来
    
    设计要点:
    - goals / tasks 两张新表 + graph_runs 新增 run_status='queued'
    - queued 的唯一理由是把出队的并发临界区交给既有唯一索引兜底:
      idx_graph_runs_one_active_root 覆盖 ready/running/paused 而不覆盖 queued,
      超发会被索引直接拒绝,不需要应用层的「先查后插」
    - 排队推翻了 P3「拒绝而不排队」的定案,但那条定案的理由(没有出队 worker =
      静默丢任务)今天仍成立,所以调度器的四个触发点是功能主体而非收尾
    - 已下达的描述冻结:入队即写 task-goal.json 并被 describeTask 消费,
      改库不改 artifact 会让两份对不上,两份都改等于篡改审计输入
    - 现有 T-${Date.now().toString(36)} 在「一个 goal 批量建 N 个 task」下
      同毫秒必撞,且 tasks.id 是主键 —— 改用仓库既有约定的 4hex 后缀
    
    R-20 四条定案回记至 27-requirement.md。
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse File »