-
先搭桩子,不接真实计费接口(用户明确选择:真实欠费判据依赖计费系统那边确认, 现在只把可插拔的挂载点和恢复机制先落地,接口确认后再换真实实现)。 新增: - 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>
-
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>
-
## 病症(真跑实测) 2026-08-20 那条 run 的 timeline 里,**每个节点**的产物索引写入都失败: node.output | 产物索引写入失败 contract.json: UNIQUE constraint failed: index 'idx_graph_files_path' node.output | 产物索引写入失败 sdd.md: UNIQUE constraint failed: index 'idx_graph_files_path' 19 次 agent 调用、每次两三个文件,无一成功。 ## 根因:加了新索引、没删旧索引 产物索引加「版本」之前,唯一索引是无条件的 `UNIQUE(company_id, COALESCE(task_id,''), rel_path)`。加版本之后建表块换成了 带谓词的 idx_graph_files_current(WHERE superseded_by IS NULL),**但没人删旧的那条** —— `CREATE ... IF NOT EXISTS` 只管不重复建,不会删已有的。 于是 recordFile 那套「先把旧行 superseded_by 指向新行、再插新行」的版本化写法 在存量库上整个失效:旧索引不认 superseded_by,同路径第二行直接撞。 而重试恰恰会让同一条 rel_path 被产出第二次。 ## 失效方向:静默 writeFileIndex 把 recordFile 的抛降级成一条 timeline,**节点照样 success**。 后果是产物索引在任何有重试的 run 上都不全 —— 而下游节点正是靠这份索引 按 kind 找上游产物。不报错、不影响测试、git status 干净。 ## 为什么没有任何测试照到 新建库压根没有那条索引(建表块里早就只剩 current 那条),**只有存量库会踩**。 这是同一类问题的**第三次**(tasks 加列的索引顺序、墙钟锚点迁移、这次)。 判据固定成一句话:**凡是碰 schema,「存量库重启」那条路必须单独走一遍。** ## 修 + 验 迁移里一句 DROP INDEX IF EXISTS idx_graph_files_path。 守卫 graph-files-stale-index.test.js 走存量库重启那条路,并验版本化语义没被削弱 (同路径两行、当前版本恰好一条、旧行指向新行)。反向验过:不删就红。 生产库已实跑验证:删索引后同路径连产两次不再抛,graph_files 24 行未受影响 (备份 /tmp/graph.sqlite.pre-idxdrop)。 全量:141 文件 / 1300 测试全绿。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> -
## 上一版漏了什么 预算行挂在 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>
-
**本文件除了 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>
-
agent 之间派单(POST /api/agent/task/dispatch)与「跑一张 org graph」是 两种执行载体,硬说成一回事是骗自己:前者直接起一次 agent 对话,后者走 调度器出队 + 图执行。所以显式建模而不是让调度器去猜 —— executor(graph|agent)、assignee_agent_id、parent_task_id(派单链, 与 depends_on 的「同级先后」不是一回事)。 迁移的落点对:旧行 executor 缺省 'graph'(批 1-4 建的全是图任务), 另两列 NULL。 ## 加这一步时踩到并修掉的一个会让存量库开不了的 bug 第一版把 CREATE INDEX ... ON tasks(assignee_agent_id) 写进了建表块。 建表块整体先于迁移块执行,而存量库上 CREATE TABLE IF NOT EXISTS tasks 是 no-op —— 表还是批 4 的形态、没有那一列,于是这句索引直接抛 「no such column」,getDb() 起不来 = **所有存量库一启动就炸**。 而新建库(单测默认走的那条路)完全正常,只测新建库根本看不见这个洞。 已把索引挪进迁移块、排在 ALTER 之后。 守卫:server/tests/unit/tasks-executor-migration.test.js —— 真造一个 批 4 形态的库(删索引 + 删三列)再重开,断言三列与索引都补回、 executor 默认值是 'graph'。 另记一处自己踩的:SQL 模板字符串里的注释用了反引号,把 db.exec 的模板 提前闭合了。DDL 注释里一律不用反引号,已写进那段注释。 已知不对称:ALTER TABLE ADD COLUMN 加不了 CHECK 也加不了外键,所以存量库 上 executor 的取值域只由应用层守,与新建库的 CHECK 不同源。写在注释里, 免得下一个人以为库层拦得住。 全量:129 文件 / 1245 测试全绿(+2 新守卫)。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
-
## 批 4:解除「一家公司同时只有一个 active root run」 - DROP idx_graph_runs_one_active_root(建表块删掉 + 迁移块显式 DROP, CREATE ... IF NOT EXISTS 对存量库不生效) - max_parallel_tasks 从 company.json 调用时读(不是模块加载时快照, 否则改配置要重启才生效且不报错);缺省 1、非法回落 1 并留声、硬上限 20 - 准入判据「从活跃集外进入活跃集」按额度计数 **生死线用 8 个真进程并发抢坑实测**(不是纸面论证):max=1/2/3/5 四组, 每组恰好抢到 max 个、库里活跃数 = max,落选进程各吃 40 次 RunSlotBusyError, 零 other 错误。唯一索引没了之后,准入事务是真守卫。 ## 批 3 对抗复核的四条遗留 - N1 🔴 cancelQueuedRun 的前提被批 3 自己的 requeueBlockedRootRun 改没了: queued 的 run 现在可以是跑过一半的。探针实测取消后子 run 残留 paused/stop_requested=0 —— 那条 running 的子 run 继续调 agent 烧 token。 改为无条件走 engine.stopRun - L1 ⚠️ 恢复路径对 manual 只兑现一半:注释写着「T1 永远回不来这个后果因此 消失」,但调度器故意不碰 manual → 退回队列后没人接。定案:「manual 不自动 出队」约束的是首次起跑的授权,已被人亲手起过的不算新授权。 判据用 attempt>0 而不是 started_at —— 后者会被 requeue 清成 NULL, 恰好在需要它的那一刻是空的(实测踩过) - N2 🟠「退回队列」对用户完全不可见:200 + ok:true + 按钮全灰 + 图消失 + 一句「节点状态一律 pending」的假提示。加 notice 明说 - N3 🟠 isSlotRaceError 把 InvalidTransitionError 当竞态吞掉,而探针证伪了 那条注释:被推走时条件更新返回 0 不抛,真正抛它的只有「调用方声明了非法 转换」。删掉该分支,让它吵起来 四条里三条是同一个模式:注释承诺的前提被本轮自己的改动改没了,注释还留着。 文档 §十三.3 / §十四.4 已就这个模式立过两次案,这是第三、四、五次。 ## 文档 §十五 实施记录,含 §15.4 一次流程事故:87b776c 由第三方会话在对抗复核完成 前误提,提交说明的「1201 用例全绿」压在一份尚未通过复核的代码上。 全量:128 文件 / 1240 测试全绿,web:build 通过。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
-
队列与调度(§五.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>
-
goal 从「人写一句话」变成「agent 拆成一张 task DAG、人确认后下达」, 收敛时由 org-validator 在 goal 层再验一次。 ## 交付 - task-planning 本体(毕业判据实测通过:agent.md + soul.md 一次写完、 不带 _base 哨兵、路径按 cwd 深度 3 实算核对) - 拆分链路:3 轮 errors 原样回喂 → 不过就降级成 1 条同描述 task, goal 绝不卡在 planning;token 记 goals.planning_tokens - goal 级 org-validator 验收:≥2 条 task 全 completed 才调,单 task 跳过, 有 failed 直接 failed 不调(省一次真花钱的调用) - 控制台四栏 + GoalTaskPane 两级树 + 两步表单(拆分中给逃生口) - 中断恢复:启动时补跑验收与汇报 ## 三条跨进程竞态,全部用真探针实跑复现后才判定修好 1. runGoalSplit 无条件把 goal 写回 draft,抹掉逃生口刚下达的 active —— 而触发它的正是表单自己推荐的那个逃生口。后果是一个正在烧钱的 goal 显示成「草稿」,且从此永不收敛(OKR 不写、上级不唤醒、无日志) 2. CAS 把最终态提前写进库,验收窗口内重启即丢,且 verdict=NULL 与 「单 task 合法跳过」形态相同,查不出来 → 占位判词 + 启动重推 3. 认领语句 SET verdict=RESUMING WHERE verdict IN (PENDING, RESUMING) —— 写进去的值就在 WHERE 里,changes 恒为 1。而函数注释白纸黑字 写着「认领写的是另一个占位值」。注释是对的,代码没兑现。 实测:org-validator 调 2 次 / OKR 写 2 条 / token 双记 → claim 带唯一 token + 时间戳租约,回写条件更新决胜,主路径同闸门 ## 与设计正文的偏离(已记进文档 §十四) 拆分后 goal 落 draft 而非 active(active 的语义已被 markGoalDispatched 钉死为「已下达」);mode 三级继承对显式 tasks[] 同样生效;占位判词不出 API;新增 planning_error / reported_at 两列,后者迁移带回填(否则升级后 第一次启动会把历史终态 goal 全部重报一遍)。 ## 过程 四轮 26 个 agent,复核报出 22 条缺陷,其中 6 条是修复自己引入的。 第四轮修完挂了 3 条测试,全是测试在钉住缺陷本身 —— 其中一条的注释 写着「模拟进程死后重启」,构造的却是新鲜占位值:注释描述 A 场景、 代码构造 B 场景、断言按 A 场景写。 全量:118 文件 / 1112 测试全绿,web:build 通过。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
-
把「人类提的一个需求」与「机器执行的一件活」拆成两级。这不是新概念: 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>
-
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> -
第四次真跑的断点: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>
-
界面走查(真 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> -
按 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>