• yibo authored
     
    Browse Dir »
  • 39 号文档 P5 落地的是"配了 company.json.target_dir 就生效"这一半,
    "人怎么把这两个字段填进去"此前只能手工编辑 company.json。
    
    复用已存在的 PUT /api/company/:companyId(companyService.update()),
    没有新建端点。新增 target_dir/target_mode 分支,校验直接调
    path-resolver.validateLinkedTarget(39 号文档 P5 抽出来的共享函数,
    和 graph-engine.js 运行时校验同一份判据):合法路径写入成功、敏感
    路径黑名单拒绝、linked-new 非空目录拒绝、linked-existing 不存在拒绝,
    校验失败路由层转 400;传空字符串清空配置;不传 target_mode 默认
    linked-existing,与 graph-engine.js 的 readCompanyTargetConfig 同口径。
    
    前端:CompanyDashboard.vue 概览 tab 加一张配置卡片,本地表单状态
    不直接双向绑定 selectedCompany,保存失败不会冲掉用户刚打的字。
    
    新增测试 8 例,npm run web:build 编译通过。**未做真实浏览器点击
    验证**——服务器上有其它并行会话真实在跑的 agent 进程,这次没有重启
    服务器(42 号文档 §二记过一次"重启杀掉别人真实进程"的教训),文档
    里标记了这个已知缺口。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01BwG4b7Nq1NLR9eeE3M9Us9
    yibo authored
     
    Browse Dir »
  • yibo authored
     
    Browse Dir »
  • 之前几轮全量跑一直有 3 个失败,当时判断是环境耦合/pre-existing、跟本次改动无关就没细查,
    这次逐个真查了一遍,三个都是真 bug:
    
    - system/agents/sdd-planning/SKILL.md:Evolve 训练产出从 train/evolve/SKILL.md 提升到
      生产位置时,"训练依据"引用的相对路径没跟着调整(少了 train/ 前缀),指向了不存在的
      system/agents/hunt/、system/agents/digest/;真实文件其实在同一个 agent 自己的
      train/hunt/、train/digest/ 下,补前缀即可。
    
    - graduation-criteria-reality.test.js:断言死等 code-planner 的状态回到 'active',但
      该本体的 config.json 早已从曾经踩坑的 "training" 改成了真正毕业态的 "enabled"——
      seedAgents() 把 DB 同步成 'enabled' 是设计如此(同为毕业态,不是降级),不该被判失败。
      改成用 isUngraduatedStatus() 判断"没被打回未毕业域",不再钉死具体状态值;顺手把
      graph-seed-service.js 里那句会随源码变化过期的注释也改成不点名具体值。
    
    - graph-engine-extend-budget.test.js:一颗时间炸弹——用硬编码绝对时间戳
      '2026-08-23T05:23:52.886Z' 模拟"卡住很久了",但被测代码真的调 Date.now() 算经过时长,
      写这条用例时"经过时长"确实超过 2h 上限、又低于追加后的 6h 上限;现在真实时间已经过去
      178 小时,连追加后的上限都超了,测试注定要红,跟被测代码毫无关系。改成
      Date.now() - 3小时 相对计算,不再随时间推移漂移。
    
    全量 vitest 131/131 文件、1089/1089 用例通过。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • yibo authored
     
    Browse Dir »
  • 39 号文档 §四.4 拍板"ceo 全权自主 enqueue,但 max_parallel_tasks /
    DEFAULT_TASK_BUDGET / goal 级 org-validator 这几道闸门必须照样拦住
    它",落地时没补这条测试。这次补上,走 ceo 实际会用的通路(draft:true
    + created_by:'ceo' → /enqueue),不抄近道直接摆数据库行:
    
    1. 坑满时 ceo 建的任务排队,不插队。
    2. 预算给 0,checkBudget 照样判耗尽,created_by 不改变结果。
    3. ceo 建的两条 task 挂同一个 goal,全 completed 后 goal 级验收只调一次。
    
    全量测试 1486/1490,零回归。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01BwG4b7Nq1NLR9eeE3M9Us9
    yibo authored
     
    Browse Dir »
  • 冲突在 docs/README.md:并行会话在真实端到端测试里发现 boss agent(39 号文档)还有
    三个单测没覆盖的真问题,把 39 号重新留在 todo/ 并拆出 42 号跟进文档;与我这边把
    39 号挪进 done/ 的判断冲突——采纳对方判断(39 号文档物理路径挪回 todo/system-design/,
    докс/README.md 用对方更新的行 + 补 42 号一行),因为他们是基于更新的真实测试结果。
    
    顺带把 29-dream-design.md 这次会话移到 done/ 后遗漏的引用修掉:
    system/agents/{dream-distiller,dream-screener}/agent.md、
    server/{routes/dream.js,services/dream-service.js}、
    server/tests/unit/dream-routes.test.js 六处 docs/todo/system-design/29-dream-design.md
    路径改成 docs/done/system-design/29-dream-design.md(no-dead-path-references.test.js /
    agent-md-refs.test.js 两条守卫测试之前没扫到这几处,这次真跑测试才暴露)。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • 服务器重启会杀死正在跑的 graph 节点背后的真实进程,但 graph_run_nodes.
    status 永远停在 running(ceo 的两条唤醒路径都只认 blocked,对这个状态
    完全不可见)——39 号文档真跑复盘撞出来的坑,这次补上。
    
    1a:graph-engine.js 新增 sweepOrphanedRunningNodes(cutoffIso),复用
    routeFailure 的 infra_flake 分支(不发明新状态),server/index.js 在
    本次进程启动时刻挂一次开机扫描,排在其余「补一次全量扫描」钩子之前。
    
    1b:新增 forceFailNode() + POST /:id/nodes/:nodeId/force-fail,补上
    retry 端点管不到 running 节点这个缺口——人工/ceo 判定"这个真的救不
    回来了"时的手动出口,转 fail 后交给既有失败路由处理,不发明新路径。
    
    新增测试 12 例(graph-engine-orphan-sweep.test.js 4 例、
    graph-run-force-fail.test.js 8 例),全量测试 1483/1487,零回归。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01BwG4b7Nq1NLR9eeE3M9Us9
    yibo authored
     
    Browse Dir »
  • yibo authored
     
    Browse Dir »
  • 核心修复:indexNodeOutputs() 此前只扫节点私有目录,不扫项目根——但引擎自己的写入边界
    明确允许交付物写项目根,导致内容型交付物节点(产出写进项目根时)永远进不了 R-19 产物
    索引,下游 needs 声明永远查不到实例。新增 readDeclaredProduces():agent 可在
    contract.json 里显式声明 produces:{kind: 绝对路径},校验(绝对路径/落在项目根或私有目录内/
    文件真实存在)通过才登记,不满足就整个 kind 判「没产出」,不静默放过。全局提示词
    (describeArtifactContract)同步更新,所有 org/work 节点自动学到这个新字段。
    
    新增 org-hunt / org-digest / org-evolve 三个 graph 原生 agent(方法论抄
    innovator-hunt/digest/evolver,协议原生兼容 graph 引擎),配套新增 work-graph-templates
    的 deliverable.json(内容型交付物骨架,不引入验证 agent)。11 轮真实试跑挖出并修复 5 个
    独立缺陷(详见 41 号文档问题 7):建公司自动规划与手写图的竞态、work-graph 模板库缺内容型
    交付物骨架、验收节点任务描述范围溢出、两个规划 agent(orggraph-planning/workgraph-planning)
    的路径口径写错、以及上述 R-19 索引缺口。最终产出与原生 FDE 对比:内容骨架可迁移、质量不
    打折,但可靠性差距悬殊。
    
    补充说明:system/agents/{ceo,innovator-evolver,master,org-innovator}/{agent,soul}.md 是
    本会话开始前已存在的 dream 自编辑改动,随本次提交一并收进去,不属于本次改动范围。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • 39 号文档地基 0 当时只改了 company.js(goals/tasks 端点),graph-run.js
    (run 的 start/stop/retry/budget 控制端点)漏了同一处修复,还是纯
    requireAuth。真实端到端测试里 ceo 被唤醒后能读写 goal/task 板,但调
    /api/graph-runs/* 处理卡住的 run 时精确撞上 401——ceo 自己在对话记录里
    诊断出这一点并写出了人工需要跑的 curl 命令,间接验证了这条修复的必要性。
    
    两个挂载点(router + companyRouter)都改成 requireAuthOrInternal,两处
    :companyId/:id 的 param 中间件加了与 company.js 一致的 req.user?.internal
    旁路。route-auth-gate.test.js 同步更新断言。全量测试 1469/1473,零回归。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01BwG4b7Nq1NLR9eeE3M9Us9
    yibo authored
     
    Browse Dir »
  • 排查「前端刷新不及时/没有打字机效果」发现现状已是 SSE,不需要换 WebSocket,
    真正缺的是三件独立的事:
    
    - 打字机平滑:后端把 ACP chunk 按 10 字符切片再发 SSE,前端用定时器队列
      (agentChatEvents.js 的 createTypewriterQueue)匀速吐出,抹平网络突发。
    - 打断并发送:执行中解锁输入框,新增 ⤴ 按钮与 ⏹ Stop 视觉区分;stop 端点
      改为轮询确认 agentChatLocks 真正释放(3 秒预算)再返回 cleaned 字段,
      前端据此决定直接发送还是走"强制发送"兜底。顺手修了一个真实并发漏洞:
      同一 agentId + 同一 convId 的并发请求过去完全没有闸门,只是一直靠前端
      textarea 的 disabled 属性侥幸没被触发。
    - 串台加固:把 handleEvent 分发逻辑抽成不依赖组件实例的纯函数
      (agentChatEvents.js),补上 9b1c3f1 那条不变式的回归测试。
    
    已用真实 Claude CLI 会话(非 mock)在浏览器里验证打字机效果、打断并发送
    全流程、以及纯 Stop 无回归。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01HDtmdnE6pay9BhwZDAKK6M
    yibo authored
     
    Browse Dir »
  • 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 Dir »
  • 按 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 Dir »

  • commit b7b5096 给 agent-chat.js 挂 requireAuthOrInternal 后,只顾了「同进程内
    require 拿 INTERNAL_TOKEN」这一种自环调用(company.js/task-settle.js),漏了
    bin/lemon.js——它跑在 agent 的 claude CLI 子进程里,不共享内存,没法拿到这个
    token,导致所有 agent 的 task report/dispatch 全部 401,没法自行汇报完成状态。
    
    修法:jwt-auth.js 生成 INTERNAL_TOKEN 时顺带写进 process.env.LEMON_INTERNAL_TOKEN
    ——spawn 默认把 process.env 整份传给子进程,免费传给所有后代进程(claude CLI →
    Bash 子进程 → bin/lemon.js)。bin/lemon.js 读到就带上 x-internal-token header。
    顺带把硬编码的 API_BASE 端口改成可用 LEMON_API_BASE 覆盖,方便测试。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »

  • POST /api/open/agents/:agentId/invoke —— 复用现有 company-scoped invoke
    (/api/open/companies/:companyId/agents/:agentId/invoke)那套 jobManager
    异步 job + 轮询/webhook 机制,但不查 company_agents 雇佣关系(全局 agent
    如 master/agent-creator/h3-video-prompt 本来就不挂在任何公司下,套雇佣
    校验只会无条件 404)。只查本体是否存在(system/agents/{id}/config.json)。
    
    两条 invoke 路由共用的执行逻辑(附件处理 + AgentManager 调用 + 写 job
    结果)提到 _executeAgentInvocation,避免整段复制。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • 验证阶段真查出一个 Phase 1/2 都没顾上的真实漏洞:agent-chat.js(/api/agent/*)一直靠
    :agentId 里的 "companyId:role" 命名空间前缀(locator.companyStateDir 解析)区分会话
    属于哪家公司,但这个文件从只挂了 requireAuthOrInternal(登录即可),没有归属校验。
    curl 验证:任意登录用户对 GET /api/agent/{别人的companyId}:{role}/conversations
    依然 200,能跨租户读别家公司的 agent 会话。
    
    修法跟 company.js 同一套思路:router.param('agentId', ...) 解析 : 前的 companyId 段,
    过 canAccessCompany。三个例外:①没有 : 前缀的全局 agent(master/org-innovator 等,
    落 _global)不做归属校验,本来就全系统共享;②内部自环调用(req.user.internal)直接
    放行,招聘/训练自动流水线要能打到任意公司名下;③admin 不受限制。
    
    新增 agent-chat-company-gate.test.js:直接拿 Express 暴露的 router.params.agentId[0]
    单测这个 param handler,覆盖同公司放行/跨公司404/admin不受限/公司不存在404/全局agent
    放行/内部自环放行六种情况。顺带修好一个连带失败的既有测试文件
    (agent-chat-tenant-isolation.test.js,zz-tenant-alpha/beta 同样是从没真正建过的
    虚构公司 id)。
    
    验证:curl 复测跨租户读会话从 200 变 404,owner/admin 依然 200,全局 agent 不受影响;
    真实浏览器用全新账号确认 CompanyDashboard.vue 对"没有可用公司"已有现成空状态,不需要
    新写前端代码。全量 vitest 1433 passed,3 个无关既有失败不受影响。
    
    至此 38 号文档 Phase 0/1/2/3 全部完成。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • company-service.js:company.json 加 ownerId(无 SQL 表,纯 JSON 字段);create() 接收
    ownerId;list({ user }) 按传入用户过滤,不传就不过滤(company-heartbeat-service.js 等
    系统内部巡检要看全部公司);新增 canAccessCompany(company, user) 作为全仓库唯一一份
    归属判据(admin 不过滤,判负一律 404 不是 403,不暴露"这个 id 存在只是不是你的")。
    
    company.js 用 router.param('companyId', ...) 统一收口(约 50 条路由,含 okrPath() 系列
    动态路由)。graph-run.js 的两个挂载点参数形状不一样,设计阶段的描述不准确,实现时才
    发现:companyRouter 唯一路由是 /:companyId/graph-runs 直接查;主 router 全部路由是
    /:id/...,:id 指的是 run 不是公司,改成先查 run 的 company_id 再判断,run 不存在或
    公司不是你的统一回"run 不存在"。devloop.js 用 :pid 反查 company_id;POST /projects
    单独校验 body 里的 company_id(放在基本字段校验之后,避免归属检查打乱输入校验的错误
    优先级);GET /projects 按归属过滤列表。
    
    okr.js 最终没有按原计划接入 companyId 归属校验——实现前查证发现 OkrDashboard.vue
    挂载的 /okr 页面全站没有任何导航入口指向它,是个孤儿页面,给访问不到的页面建"选公司"
    前端交互不划算,改成 requireAdmin(跟 dream.js 同款),先堵住直接拼 URL 看到跨公司
    聚合数据的口子。真正在用的公司级 OKR(company.js 的 /:id/okr)已随 company.js 一起隔离。
    
    存量数据迁移:scripts/backfill-company-owner.js(npm run backfill:company-owner),
    两家存量公司已划给种子账号,幂等验证过。
    
    8 个既有测试文件用的公司 id 都是从没真正建过的字符串标签,挂锁后集体 404——新增共享
    helper server/tests/helpers/ensure-test-company.js 修复;两处原有的"跳过已有
    company.json 的目录,那是真公司"防御逻辑,判据从"有没有 company.json"改成"ownerId
    是不是 -1 这个测试专用标记",否则新出现的测试夹具会被误判成真公司永远不清理。
    
    验证:真实 curl 走过(admin 看到全部存量公司、新用户看到空列表、新用户拼 URL 访问
    别人的公司 404、新建公司 ownerId 精确落在创建者、admin 和创建者都看得到其他人看不到);
    全量 vitest 1427 passed,3 个无关既有失败不受影响。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • 给此前完全零防护的 company.js/okr.js/tasks.js/devloop.js/graph-run.js(两个挂载点)/
    arena.js/config.js/evolution.js/whatsapp-auth.js 挂 requireAuth;dream.js 挂 requireAdmin
    (系统级经验汇流视图,只有 admin 能看)。open-api.js(自己一套 OPEN_API_KEYS)/auth.js
    自身/health 端点按计划排除。这一步只挡"没登录",公司归属过滤留到 Phase 2。
    
    agent-chat.js 不能直接挂 requireAuth:company.js(自动建公司/招聘后自动训练)、
    task-settle.js(goal 汇报回调)三处服务端自环调用会 fetch 自己的 /api/agent/* 端点,
    拿不到任何用户 cookie,直接挂锁会打断这几条核心自动化链路。新增 requireAuthOrInternal +
    jwt-auth.js 的进程内 INTERNAL_TOKEN(启动时随机生成,只活在内存里):自环调用带上
    x-internal-token 头放行,其余情况仍要求真实登录;三处调用点同步改造。
    
    17 个既有测试文件、67+ 处用例直接拿 supertest 打这些路由,从没设过登录 cookie(测的是
    业务逻辑不是鉴权)。没有逐个补登录态,改为 requireAuth/requireAdmin 在 NODE_ENV=test
    时默认放行,除非显式 FORCE_REAL_AUTH=1;新增 route-auth-gate.test.js 在放行关闭状态下
    做结构检查(router.stack 里中间件是否真的排在所有路由前面),确保"锁真的挂上了"这件事
    有测试兜底,不被业务测试的默认放行悄悄掩盖。
    
    验证:真实 curl 走过全部路由(无 cookie 401、admin 登录全通、非 admin 能看公司但
    dream.js 403);手工删过一行 requireAuth 确认守卫测试真的会红;全量 vitest 1427 passed,
    另 3 个失败与本次改动无关(stash 验证过改前改后一致,系仓库并发工作遗留)。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »

  • sweepStuckActiveGoals 候选查询多加了 archived_at IS NULL,把「goal 被归档但 status
    从未跟着收口成终态」这类形状(G-mt1qwgt5-6b77:唯一 task cancelled+archived 时 goal
    的 archived_at 被打上同一时刻,status 却留在 active)永久排除在扫描之外。去掉该过滤:
    settleGoalIfAllTasksDone 对空 task 列表/存在非终态 task 都是 no-op,不会误收口合法归档
    但确实没收敛的 goal。同时手动收口了 e2e-demo 库里唯一命中该形态的 G-mt1qwgt5-6b77。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • settleGoalIfAllTasksDone 用 listGoalTasks 默认口径(includeArchived:
    false)取 task 列表,task 若同时被 cancelled+archived 会被过滤掉,
    tasks.length 提前归零直接 return null,goal 永远收不了口——僵尸扫描等
    于没扫到。实证:G-mt0a6upq-a98e / G-mt0a6upq-52ee 两条 goal 卡在 active
    超 17 小时未被收口,两条 task 均已 cancelled+archived。
    
    改成 includeArchived: true 取全量后再按语义过滤:draft 任务归档(人删
    掉的需求)继续不参与判定,但 cancelled/completed/failed 之后再归档
    (结果已发生只是从界面收起)必须照样计入,否则会反过来破坏"归档掉的
    草稿不参与判定"这条既有行为(拆分产出可删的验收用例钉住的就是这个)。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • resumePendingGoalValidations 只补「收敛钩子跑过、卡在验收/汇报半路」的情形;
    若钩子压根没被触发过(进程重启/异常吞掉/事件丢失),task 早到终态但 goal
    永远停在 active,只能靠人工 OKR 巡检发现(e2e-demo 公司 G-mt2r62ia-73af 卡
    了 48 小时)。新增 sweepStuckActiveGoals 开机全量扫描一次,并启动 5 分钟
    周期性 startZombieGoalSweeper 兜底,把发现窗口从「天」压到「分钟」。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »

  • 两个独立的小修复,都是这次 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 Dir »
  • run_b962ea87f58f 卡在 wallclock_ms 耗尽的锚点未结清问题上,需要人工追加预算
    才能继续。extendBudget 只增不减、只加到已配额的维度上,追加时顺带结清卡死的
    墙钟锚点(否则 startRunSync 一启动就把挂起时长记进新预算里)。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • 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 Dir »
  • POST /:cid/tasks -> enqueueTaskRun 入队时从未写过 assignee_agent_id,
    COO 心跳巡检靠 tasks.assignee_agent_id(the "to" field) 判断任务有没有
    人接手,读到永远的 NULL 就把正常跑图的任务误报成孤儿任务
    (本周已发生两次:T-mt25asz4、T-mt5d3mi9-57cf)。
    
    - enqueueTaskRun 入队时补写占位哨兵 'org-graph'(GRAPH_TAKEOVER_ASSIGNEE)
    - settleTaskFromRun 收敛时用 COALESCE 兜底,只在仍为空时补,不覆盖
      派单式任务(agent-chat dispatch 流程)已经写好的真实 assignee
    - TASK_PATCH_COLUMNS 补上 assigneeAgentId,否则 updateTask 写不进这列
    
    T-mt5dkvjm
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • 新建独立试验节点 prd-methodology-trial,走真实 Hunt→Digest→Evolve 训练 PRD 撰写方法论:
    2 轮 Small Loop 提前收敛(96/97 分,真实基线 50 分),用三个真实历史项目反推验证方法论
    有效,Deploy 范围限定在该独立节点,未触碰生产 prd-planning。结论:FDE 对方法论类角色
    确实管用,但收敛速度/验收严谨度依赖任务书里显式写明的自纠正要求,非免人工黑盒。
    
    过程中连带修复同一个 2026-08-16 改名批次(innovator→org-innovator 等)遗留的 5 处死链:
    company.js 两处硬编码调用、CompanyDashboard.vue「重新训练」按钮,以及四个本体各自
    config.json 内部 id 字段从未随目录改名同步更新,导致 /api/agent/list 展示层用假 id
    显示成死卡片。顺手修了 AgentScaffold 默认 tools.md 模板一处裸相对路径缺陷(对应
    agent-md-refs.test.js 里已登记多时的已知缺陷)。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • system/templates/ 已在 28 号文档的合并里并入 system/agents/,「模板」作为独立
    数据概念已经消失;确认后手工 CRUD 页面(创建/改名/删除骨架、触发生成与训练、
    传 skill 文件、provision)也已经没人用,一并下线:
    
    - 前端:删 AppNav「Templates」菜单项、/templates 路由、TemplateAgents.vue
    - 后端:company.js 删掉只服务于该页面的写接口(PATCH/DELETE/POST /templates、
      generate/train/skills-upload/provision/files 系列),保留 GET /templates
      (CompanyDashboard 招聘弹窗和 CLI `company templates` 命令仍依赖它列出可招角色)
    - CLI:bin/lemon.js 删 `template create`/`template train`(调用的是被删接口)
    - 修正两处过时注释(company-service.js / graph-service.js):不再声称
      buildAgentDefinitionBrief 有两个调用方,/hire 是唯一派单点
    - work-layer-body-paths.test.js 同步更新断言(2 处调用方 → 1 处)
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • 此前只有人类对话路径 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 Dir »
  • 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 Dir »

  • 35 号文档剩下的三个提案,彼此直接相关(C 产出 D 消费的证据,B 展示 D 算出的结果),
    一次做完。
    
    提案 D(goal-contract.js 引擎级证据闸门):
      `runBenchmark` 新增 evidence 检测——`evidence_hint` 命中"浏览器/视口/开发者工具/
      console/network/截图"类关键词的维度,必须在 evidence.<dim> 有真实字符串支撑,
      否则该维度判不过(哪怕分数达标)。证据来源两处都认:`--target` 文件自带的
      `evidence.<dim>`,或同目录姊妹判词文件 `attempt-<n>.json` 的 `dimensions[].evidence`
      ——后者是 benchmark-validator/agent.md 早就在教的既有写法,不用 agent 多写一份。
      新增 `dimensions[].evidence_required` 字段随判决落盘。6 条新单测。
    
    提案 C(可复用视觉验收工具):
      新增 scripts/visual-acceptance-check.mjs,四个子命令(overflow/style/console/
      contrast),封装最常见的几类真实浏览器检查(Playwright),agent 通过 bash 直接
      调用,不用每次自己临时想办法(真实撞见过两个极端:一次真写了 Playwright 认真测,
      一次只写了正则解析文本)。配一份不进 vitest 快速循环的独立冒烟脚本(起真
      Chromium,几秒量级)。benchmark-validator/agent.md Step 2 加了引用说明。
    
    提案 B(控制台证据可见性):
      GET /api/graph-runs/:id 新增 benchmarkVerdicts 字段(只读 goal-contract.js 已落盘
      的判决,不重新计算)。NodeDetailPanel 概览 tab 新增"验收证据"小节:逐维度显示
      分数 + 是否有证据支撑,evidence_required 且缺证据的标 ⚠️。纯展示,不改判定逻辑。
      2 条新单测。
    
    全量单测 1012/1012 通过;控制台功能用 Chrome 实测过(截图确认"验收证据"小节正确
    渲染)。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • 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 Dir »
  • 用户反馈:控制台原来只能逐个节点点「输入输出」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 Dir »
  • settleTaskFromRun 只写 status/finished_at,不清 blocked_reason —— 任务卡住时
    noteTaskBlockedReason 写过的那句「节点 xxx 卡住(...)」会一直挂在 tasks 行上,
    哪怕后来 run 真的恢复跑完转 completed。界面上因此出现「已完成」绿色状态旁边
    还留着一句刺眼的红字,容易被读成还在报错。
    
    真实复现:T-mt2r6fz0-125b 手动推过熔断点后 org run 收敛到 completed,
    任务列表却仍显示 org-coder 卡住的旧归因文本。手动清了这条任务的残留数据,
    代码层面在 settleTaskFromRun 的 UPDATE 里一并置 NULL——task 进终态时
    「为什么卡着」这句话本来就该失效,不管进的是 completed/failed/cancelled 哪一种。
    
    扩展了 goal-task-acceptance.test.js 里"预算耗尽→恢复→跑完"那条既有用例,
    断言 blocked_reason 在完成后确实被清空。全量单测 998/998 通过。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • org-coder-validator 在真实回归任务里被 missing_required_input 反复误判 blocked:
    executor.json/executor-fanout.json 的 test/integration→validator 边声明
    produces:["log"](required 默认 true),但 test 类节点从不写出能被 indexNodeOutputs
    认作 kind=log 的文件(coder.log 实际属于 impl,test 自己的 _plan.md/_research.md
    认不出 kind 也没有一对一无歧义可猜),这个 kind 因此永远不会被索引到,
    requiredInputsMissing 每次都判缺失——不是时序问题,是结构性、每次重试都复现的硬伤。
    
    改成 { kind: 'log', required: false },对齐 validator.json 已有的写法(它的
    needs:artifact 早就标了 required:false)。真正必需的 needs:artifact 不变,
    仍受硬闸门保护。
    
    中途走了一段弯路:最初以为该改 inboundContracts() 按 role 过滤只留 needs,
    改完单测炸了 9 个用例——大多数边只声明 produces(无配对 needs),这条链路
    正是 T-mt25ecbd 产出闸门依赖的数据源,按 role 过滤会静默削弱那次修复。
    已撤回(work-graph-db.js 这次只加注释,没有逻辑改动),细节记在 33 号文档。
    
    新增回归用例 graph-files-index.test.js ⑪:produces required:false 且该 kind
    从未被索引到时不拦下游成功,同边 needs:artifact(required:true) 依旧受保护。
    全量单测 998/998 通过。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »

  • company.js 的 hire 路由原来用一发 fetch(...).catch(...) 回环自己去触发 agent-creator
    填新角色的定义文件——fire-and-forget,.catch 只接网络层错误,agent-creator 撞车时
    (另一次调用正占着它)/api/agent/:agentId/chat 返回 429,被原样当成"成功"吞掉。
    新角色因此永久停在 base 空壳 + status=training,唯一痕迹是一行 console.error(且这行
    在 429 场景下根本不会触发)。
    
    新增两个模块:
    - agent-session-lock.js:从 agent-chat.js 抽出的共享互斥锁存储(纯数据结构,Map +
      超时常量),agent-chat.js 的路由与 hire 路由现在用同一把锁。不能各起一把——
      SessionPool.getOrCreate() 不检查上一次调用有没有跑完,两条路径分开维护锁的话,
      各自都以为自己没有并发,会把第二轮 prompt 发进一个还在处理中的 CLI 会话
      (未定义行为,比现在的"被拒绝丢弃"更危险,见 32 号文档 §二方案 B)。
    - agent-body-filler.js:同进程直接调 AgentManager.callAgent,替掉 HTTP 回环。
      拿到锁才发起调用,锁被占用时等一等重试(最多 3 次,5s/15s 退避),重试耗尽在
      本体目录写一个哨兵文件 .body_fill_failed.json(不动 system_agents.status /
      config.json.status——那两个字段还有 draft→active 毕业逻辑在读,改取值域风险
      大于收益)。
    
    company.js 的 hire 路由第 1 步从 fire-and-forget 换成 await,顺带让第 2/3 步
    (技能注入、5 秒后触发 Innovator 训练)拿到"agent-creator 真的写完了"这个保证——
    第 3 步原来的 setTimeout(5000) 靠的是"假设 5 秒内写完",现在这个假设不再需要,
    但没有顺手删掉那个 setTimeout(不在这次修复范围内,留着无害)。hire 接口本身
    仍是异步的,调用方还是立即拿到 training 状态,没有改成同步等待完成。
    
    agent-chat.js 的锁声明改成从共享模块 require,行为逐字不变——用现有 5 个
    agent-chat 相关测试文件(36 条)验证过零回归。
    
    新增 agent-body-filler.test.js(6 条):首次成功、重试后成功、锁被占用时等待
    重试、重试耗尽写失败标记、成功后清掉上一轮的失败标记、只释放自己抢到的锁
    (不误删第三方在途期间抢到的新锁)。用 vi.useFakeTimers() 控制重试等待,
    不用真等 5s/15s。
    
    全量 npx vitest run:151 文件 / 1384 用例,零回归。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • ## 问题 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 Dir »
  • ## 起因
    
    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 Dir »
  • 新增 graph-engine-billing-gate.test.js(7 条):
    - 未注入 billingGateFn 时零影响
    - advanceOne / advanceBatch 欠费时都正确挂起(reason=billing-arrears,
      block_reason 落库,节点停在 ready 没被 claim)
    - startRunSync「启动」清空 block_reason
    - resumeBillingClearedRuns 正确恢复已清零的 run、放过仍欠费的 run,
      且支持按 companyId 限定扫描范围
    - advanceBatch 排序回归用例:已收敛(零 ready 节点)只是恰好预算/欠费也耗尽的
      run,正确收敛到 completed,不会被误判成挂起
    
    隔离验证:临时 worktree 里单独跑(graph-engine.js/work-graph-db.js 当前正被
    另一个会话同时编辑 fail-fallback-edge 规则,工作区里混着两份改动,跑全量套件
    会被那边未完成的改动带崩;这条测试只依赖已提交的 T3-T5 代码,临时 worktree
    里跑通即可确认与那份改动无关)。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • 先搭桩子,不接真实计费接口(用户明确选择:真实欠费判据依赖计费系统那边确认,
    现在只把可插拔的挂载点和恢复机制先落地,接口确认后再换真实实现)。
    
    新增:
    - 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 Dir »