• ## 第 6 步:调度器只管图任务(一行判据,但漏了是静默的)
    listQueue / deriveQueueBlocks / sweep 三处都加 executor='graph'。
    派单任务虽然也进 tasks 表,但它的执行载体是「直接起一次 agent 对话」,
    由 /task/dispatch 自己触发。漏这一条的后果:调度器拿一条根本没有 run 行的
    task 去出队 → syncRunNodesToDef 判空 → abandonRun → **把一条正在被 agent
    执行的派单收成 cancelled**。守卫用例把它摆成「最像可出队」的样子
    (queued + auto + 无依赖),只有 executor 这一维拦得住它。
    
    ## 第 5 步:GET /api/tasks 改读表,删掉 122 行的会话反推
    原来的 getAggregatedTasks 是启发式:扫全部 agent 的 conversations,按会话 id
    里的 T-xxx 归组,再用三条策略(meta 的 parentTaskId / from agent 的活跃会话
    时间包含 / master conv 的标题相似)**猜**父子关系。猜出来的树没法验证对错,
    而且每次请求都要遍历所有 agent 的会话目录 —— route 那 10 秒缓存就是为它加的。
    
    现在父子关系是 tasks.parent_task_id,派单时就显式写下来了,不用猜。
    task-service.js 391 行 → 236 行;_inferParentChild / _loadTaskMetas /
    TASK_META_DIR 一并删除(改完就是零调用方)。
    
    **getTaskTimeline 保留**,但来源拆清楚:任务树从表来(parent_task_id,
    可验证),时间线正文仍从 agent 的 conversations/ 扫 —— 那是一手记录,
    没有替代品。删掉的只是「从会话反推任务树」那一半,不是会话本身。
    
    两处口径变化(写在代码注释里):
    - agents:原来是「会话里出现过的 agent 集合」,现在是「谁派的 + 派给谁」。
      对派单任务这两者本来就是同一件事
    - conversationCount:原来是扫出来的文件数,现在是「有没有起过执行会话」(0/1)。
      为一个列表字段遍历所有 agent 的会话目录,正是这次要去掉的那笔开销;
      真要看内容走 /api/tasks/:taskId/timeline,那条仍读一手记录
    
    ## 漏改一个消费方,真打接口才发现
    routes/tasks.js 自己还在算 t.conversations.length,而 conversations 是会话
    反推那套的产物、已随之删除 → 500「Cannot read properties of undefined」。
    测试没覆盖这条路(它只在真跑接口时才走到)。已改成用 t.conversationCount。
    
    真打验证:GET /api/tasks 返回 17 条、12 个字段齐全 —— 条数比改前多,
    因为测试跑出来的派单现在也进表了,这本身就是「存储真的换了」的证据。
    
    全量:132 文件 / 1257 测试全绿。
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse File »
  • **本文件除了 saveTaskMeta / loadTaskMeta 的内脏和两处扫目录,其余一行没动。**
    /task/dispatch 与 /task/report 的 URL、请求体、响应体形态全部原样 —— 它们
    印在 packages/agent-core/base/common/tools-base.md 里,是所有 agent 的工具
    说明,改一个字等于同时改所有 agent 的提示词。
    
    ## 换掉文件存储的三个理由
    - 去重要「readdirSync 整个 tasklog + 逐个 JSON.parse」,任务一多就是 O(n) 次
      磁盘读,且没有索引可用
    - 派单任务在库里看不见 —— 控制台任务列表读 tasks 表,派单那套是平行世界
    - 同一个 T- 前缀两套 id 体系,读日志时分不清
    
    ## 落点
    - saveTaskMeta / loadTaskMeta 变成 tasks 表的适配器(upsert + 反向映射),
      meta 的形状一个字段不少:调用方按字段名取值,形状变了就是契约变了
    - 去重与父任务推断改成带索引的 SQL(idx_tasks_assignee),**判据一个字没改**,
      换的只是从哪儿把候选捞出来。查不动时留声不静默 —— 静默跳过等于去重形同虚设
    - 加 summary / execution_conv_id 两列(用户选的方案 A)
    - sourceConvId **删掉**:全仓零读取方,正是本仓库反复立案的「实现在、零调用方」,
      不往新表里再搬一份
    - objectiveId 不单独存:它恒等于 okr node id 的首段(OkrService.parseNodeId 与
      reportRunOutcome 同一口径),且两处读取方都是 `if (objId && meta.nodeId)`
    
    ## 真打过一遍,不只是测试绿
    照 tools-base.md 印的请求体原样 POST:200 + 老形态响应;再打一次 → 409 且
    existingTaskId 正确;落库 executor='agent' / assignee=qa-judge / status=running,
    自动建的 goal 标题「来自 ceo 的派单」、okr_node_id 落对;/task/report 之后
    status→completed、summary 与 finished_at 都在;loadTaskMeta 往返出来的形状与
    老 JSON 逐字一致,objectiveId 从 nodeId 首段推出 O-001;system/tasklog/ 不再
    产生新文件(仍是回填前那 4 个)。
    
    守卫:server/tests/unit/task-dispatch-store.test.js(4 例,盯的是「形状没变」)
    
    全量:132 文件 / 1255 测试全绿。
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse File »