• tasks 表新增 subtask_kind('acceptance_item'|'claimable_work',区分 43 号
    文档的验收条目物化和 Phase 3 的可认领工作项物化,共用一张表不新开)+
    claimed_at 列(批 6 迁移,ALTER ADD COLUMN,纯增量向后兼容)。
    
    - work-graph-db.js:createTask 新增 subtaskKind/status 参数 + 校验;
      claimSubtask/unclaimSubtask 是全仓库第一个真正的"认领(pull)"CAS 写入
      (之前 assignee_agent_id 全部写入点都是"派发 push",没有先例可抄,照抄
      claimQueuedTask 的 changes 判据);materializeClaimableSubtasks 把
      subtask-planning 的 steps[] 物化成可认领子任务,两阶段建法处理
      depends_on_hint 的前向引用,幂等,与 acceptance_item 物化互不干扰。
    - graph-engine.js:settleNode 的 SUBTASKS_INCOMPLETE 硬闸门收窄成只查
      subtask_kind='acceptance_item'——这是必须同步改的一步,不收窄的话新物化
      的 claimable_work 行会让所有开了 subtask-planning 的 task 验收判定失控
      (已用回归测试固定住这个场景)。maybeMaterializeItemizedSubtasks 的
      createTask 调用同步打上 subtask_kind='acceptance_item'。
    - subtask-planning.js:triggerSubtaskPlanning 写完 artifact 后立刻物化
      claimable_work 子任务,不等 org-planner 跑完。
    - company.js:taskView 补 subtaskKind/claimedAt;GET /tasks 加 subtaskKind
      过滤(?subtaskKind=claimable_work&status=claimable 就是可认领池查询);
      新增 POST .../claim、POST .../unclaim 两个端点,角色白名单先写死
      ['org-coder'](不做能力匹配,系统里没有结构化角色能力字段)。认领成功
      同时把 executor 改成 'agent',复用既有的 executor='agent' 派单闭环
      (/task/report 上报生命周期),不需要碰 graph-engine 的调度模型。
    
    新增 task-pool-claim.test.js(23 条:DB 层校验/CAS/物化 + 路由层白名单/
    并发/放弃);task-decomposition-validator-gate.test.js 补一条回归测试
    (claimable_work 子任务不影响 acceptance_item 的验收判定)+ 更新既有用例
    显式打 subtaskKind;task-decomposition-materialize.test.js 补
    subtask_kind 落库断言。全量单测 1202 条,1200 passed(唯二失败与本次改动
    无关、改动前即存在)。
    
    本轮明确不做:依赖顺序强制、能力匹配、graph-engine 调度模型改动、
    Phase 4 的独立开发验证循环、Phase 5 看板 UI。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01H85SS5hVC4u1PbVhjmrhH8
    yibo authored
     
    Browse File »
  • 47号:itemized 子任务物化在当前 workgraph-planning 常见子图形状下因 acceptance.md
    永远产不出来而形同虚设;maybeMaterializeItemizedSubtasks 新增兜底,缺失时改读
    已被 44 号文档证实稳定产出的 goal-benchmark.json 的 dimensions[]。
    
    46号 Phase 1:新 agent subtask-planning,钩在 enqueueTaskRun(显式 opt-in,只在
    goal 批量下达/单条 task 下达两处触发,保住 quick task 零 LLM 调用的既有保证);
    产物写共享 artifacts/subtask-plan.json,复用 R-19.3 任务级共享索引对接下游。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse File »

  • 实现路径:task-planning 判断是否 itemized(硬规则 5 开例外让 LLM 写这
    一个字段)→ org-planner 收尾把 acceptance.md 物化成子任务 → coder-validator
    用新工具 task subtask-result 逐条上报,settleNode() 新增闸门强制核对
    子任务全部 completed 才认这一轮 success。
    
    - work-graph-db.js: tasks 加 acceptance_mode 列,createTask 支持
      parentTaskId(复用既有列),新增 settleSubtaskResult 条件更新
    - graph-engine.js: maybeMaterializeItemizedSubtasks(org-planner 结算
      success 后物化子任务,agent_call/subgraph 两种形态都认,subgraph 场景
      扫子 run 全节点找 acceptance.md,找不到优雅降级不阻塞主产出);
      coder-validator 判 success 前新增子任务完整性闸门
      (ENGINE_FAIL_REASONS.SUBTASKS_INCOMPLETE)
    - task-planning.js / company.js: acceptance_mode 字段透传链路
    - bin/lemon.js: 新增 task subtasks / task subtask-result 两个子命令
    - coder-validator/agent.md、task-planning/agent.md: 对应提示词更新
    
    真实回归验证过(新建公司走完整链路),过程中发现两个跟本次改动无关的
    预存在问题:一次瞬时的 produces_missing 误判(重试即愈)、一个结构性的
    writesArtifacts 产出闸门不匹配 bug(另立 44 号文档记录,未修复)。
    
    全量单测 1120/1123 通过,3 个失败与本次改动无关(git stash 验证过)。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01U7xYyvv3pzuV9diMVUH5QL
    yibo authored
     
    Browse File »
  • 原来的 /task-board 挂的是 GoalTaskPane(折叠式目标→任务列表),字段少、
    没有搜索筛选排序、点任务是整页跳转。GoalTaskPane 同时被 GraphConsole
    左栏复用,原地重写会连带改掉那边,所以新做 TaskTable + TaskDetailModal
    两个组件只给 TaskBoard 用。
    
    - TaskTable:表格列(状态/描述/所属目标/负责Agent/优先级/创建·开始·结束
      时间),搜索框 + 状态/目标/负责人筛选 + 点表头排序
    - TaskDetailModal:点一行弹出详情,可编辑 + 增删改查(复用既有
      taskActions()/PATCH/POST/DELETE 端点,没加新接口)
    - taskView() 补上 assigneeAgentId 字段(DB 列早就有,只是没读侧暴露)
    
    已用真实登录态跑通端到端验证(建临时测试公司+任务,搜索/筛选/排序/
    增删改查全走了一遍,控制台零报错),确认 GraphConsole 侧栏未受影响。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01XUvQ2xSFk7YoavE5SQENdw
    yibo authored
     
    Browse File »

  • 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 File »
  • 按 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 File »

  • 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 File »
  • 给此前完全零防护的 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 File »

  • 招聘自动训练与「重新训练」按钮都是给 org-innovator 发消息、由它去执行训练,
    所以对话记录一直落在 org-innovator 的会话库里,而不是被训练的那个 agent 自己——
    conversationId 里带目标 agent 的名字只是命名巧合,容易让人在错的 agent 页面下找不到记录
    (今天实测:手工模板训练页删除前产生的一条记录就复现过这个问题)。
    
    - company.js /hire:训练 conversationId 提前算好并通过 res.json 带回给前端
      (不再等 setTimeout 回调里才生成,那时响应早发完了)
    - CompanyDashboard.vue 重新训练按钮:发完请求立即 window.open 到正确的
      /agent/org-innovator/{convId}
    - CompanyDashboard.vue 招聘弹窗:雇佣触发自动训练时,弹窗给出可复制的正确链接
      (不自动开新标签页——这条训练要等 agent-creator 先写完定义,立刻打开大概率是空的)
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse File »
  • 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 File »
  • 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 File »

  • 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 File »

  • ## 批 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>
    yibo authored
     
    Browse File »
  • 队列与调度(§五.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>
    yibo authored
     
    Browse File »

  • 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>
    yibo authored
     
    Browse File »
  • 把「人类提的一个需求」与「机器执行的一件活」拆成两级。这不是新概念:
    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>
    yibo authored
     
    Browse File »
  • 28 号把本体根从 system/templates/ 搬到 system/agents/,agent-locator 就是
    为了当这个路径的唯一持有者而建的。但搬完之后仓库里仍有 13 处自己拼这个路径,
    等于 locator 只是多了一份副本、没有真正收敛:
    
      server/routes/company.js            9 处(6 个 TEMPLATES_DIR + 3 个 DATA_DIR,
                                              同一文件里只有 1 处用了 locator)
      server/services/task-service.js     1 处
      server/src/core/skill-config-loader.js  1 处
      server/src/core/devloop/loop-controller.js  1 处
      scripts/extract-agent-prompts.js    1 处
    
    危害不是「不好看」,而是下一次再搬时改 12 处、漏 1 处:漏掉的那处指向一个
    不存在的目录,而本仓库读这类路径几乎都包在 fs.existsSync() 守卫里 ——
    目录没了不报错,只是静默跳过。这个形状刚在 scripts/demo-harness-company.js
    上实测过(base 本体没挂上,demo 跑出来的 agent 没有四段式骨架,脚本照常打印成功)。
    
    局部变量名保留不动(TEMPLATES_DIR / DATA_DIR / SYSTEM_AGENTS_DIR / agentsRoot /
    OUT_DIR),只把取值改成 locator.AGENTS_ROOT —— 名字是局部别名,无害;
    「路径这个事实」则收敛成一处,下次再搬只改 agent-locator.js。
    
    新增 server/tests/unit/agents-root-single-holder.test.js 锁住这条不变式:
    扫 server/ packages/ scripts/ 下所有源码,禁止 path.resolve/join 自己拼
    「以 ../ 开头、以 system/agents 结尾」的字面量。带「样本非空」前置断言,
    防止扫描路径写错时退化成永远通过。改坏一处实测能精确点名到文件行号。
    
    线索来自并行会话(它在准备改 company.js 时发现同一文件里 7 份定义)。
    实际比它看到的更多:它只查了 TEMPLATES_DIR,同文件另有 3 个 DATA_DIR、
    另外 3 个文件各 1 处。
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse File »
  • 设计见 docs/done/system-design/28-agent-registry-unification.md。
    
    改造前 Agent 本体散在三处(system/agents/ 11 个、system/templates/ 50 个、
    data/companies/{id}/agents/ 17 个),同一个 AgentScaffold 被三个 baseDir 实例化。
    后果是同一角色在 N 家公司下有 N 份互不相干的副本,模板更新不会同步;
    org-coder / org-planner 这类走 hireFromBaseMetaAgent 造出来的本体
    legacy_path 指回自己公司目录,永远无法被第二家公司复用。
    
    本次:
    - G1 单一注册表:system/templates/ 整体并入 system/agents/,base → _base。
      7 个同名 id 逐个裁决,被丢弃的一份归档到 docs/_archive/ 而非删除。
      source_kind(template/global_agent/company_hired) → origin(builtin/created)。
    - G2 运行态下沉:CLI 的 cwd 从本体根改为 system/agents/{aid}/{cid}/{taskId}/。
      memory / conversations 落公司态 {aid}/{cid}/,按公司隔离不串。
      sessionPool 的 key 含 (companyId, taskId),两家公司调同一 agent 不复用会话。
    - G2b 产物不下沉:交付物仍落 data/companies/{cid}/,system/agents/ 只留运行痕迹。
      写入边界按链路分叉——graph 节点走节点私有目录(被 indexNodeOutputs 索引),
      chat 走公司 workspace,同一提示词里不再出现两条互斥指令。
    - G3 公司只存引用:data/companies/{id}/agents/ 删除,新增 company_agents 关联表。
      不复用 org_graph_nodes——后者是执行流图,work 层本体被雇时不写它,天生覆盖不全。
    - G4 缺则创建:本体缺失时从 _base 复制骨架进 system/agents/{id}/,
      agent-creator 填 agent.md/soul.md 后才从 draft 转 active(哨兵判据,
      骨架未填时哨兵仍在,不许被 hire)。产物即刻是全系统可复用的本体。
    
    新增 server/services/agent-locator.js 收编原先散在 6 处的寻址逻辑。
    
    边界守卫(graph-engine)的豁免从「本体根上五个文件名」放宽为整棵 system/agents/:
    新本体的骨架本来就要进 git,gitignore 挡不住 ?? system/agents/{newId}/,
    旧判据会把一个真正成功的招聘节点判成 boundary_escape fail。
    
    存量迁移见 scripts/migrate-agent-registry.js(幂等,备份到 data/_migration-backup-*)。
    
    真 Claude CLI E2E 已验证:新建公司 → orggraph-planning 自动规划 →
    自动招聘 org-planner/org-coder/org-validator 三节点流水线;
    data/companies/{cid}/agents/ 不存在,company_agents 4 行,
    system_agents 56→56(零新建私有本体,纯复用),git status 零增量。
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse File »

  • ## 为什么加
    
    「输入输出」tab 把产物的绝对路径摆出来了,但那是服务器本地路径 —— 浏览器里点不开,
    走查时只能靠肉眼确认「路径长得对」,而真正要确认的是「这份 brd.md 里写了什么、
    下游读到的是不是它」。
    
    ## 前端
    
    新增 web/src/components/graph-console/FilePreviewModal.vue,输入/输出两处路径
    改成可点按钮。渲染三档:.md 走 marked(已是既有依赖,没新增包)、.json 重新缩进、
    其它原文。两处刻意的选择:
    - .json 解析失败时**退回原文**并说明,不替它藏起来 —— 产物 json 写坏了本身就是要看的信息
    - 超过 512KB 时后端回 truncated,界面如实说「不是完整内容」,不装作读全了
    output_ref 那一行不做成可点:它是目录不是文件。
    
    后端没有新造端点:复用现成的 GET /api/company/:cid/files/content,它的根目录正是
    data/companies/<cid>,而 graph_files.rel_path 本来就是相对公司根的,正好对上。
    
    ## 修掉六处路径越界(真漏洞)
    
    company.js 里 3 个 GET + 3 个 PUT(templates / company / workspace)用的是同一个写法:
        if (!absPath.startsWith(safeBoundary)) → 403
    「一个目录名是另一个的前缀」时它会放行:
        cid = co-a,path = ../co-a-evil/company.json
        → abs = .../data/companies/co-a-evil/company.json
        → startsWith('.../data/companies/co-a') === true → 放行
    于是公司 co-a 能读 co-a-evil 的文件;PUT 那三处是同一缝隙上的跨公司**写**。
    抽 resolveInside() 用 path.relative 判断,六处共用一份(复制六份判据迟早会漂)。
    新增 7 条用例走真 HTTP 验,PUT 那条断言的是**文件没被改**而不只是响应码。
    
    ## 顺带修掉一个被你的实际使用触发的 bug
    
    跑全套时 graph-seed-service 的对账用例红了,报
    `org_graph_nodes WHERE company_id='mycompany' expected 10 / actual 9`。
    不是并发偶发 —— 单跑也红。原因:seedCompanyAgents 对 type='meta-bootstrap' 的角色
    是 continue(既不落 org_graph_nodes 也不落 system_agents),而 verifyCounts
    复算期望值时把 agents[] 全算进去。mycompany 的 10 个角色里 orggraph-planning
    正是 meta-bootstrap → 差 1。
    
    也就是说:**界面上点一遍新建公司就会踩到**。这条缺陷上一轮就被预告过
    (当时那个 agent 说「不在我的独占文件里」没修),今天被真实使用兑现了。
    修法是让 verifyCounts 复用同一个 isMetaBootstrap 判据,不复制那个 type 判断。
    新增独立测试文件(不并进 graph-seed-service.test.js —— 往它的 hermetic 夹具里
    加角色会连带改既有断言),用 GRAPH_SEED_ROOT 自建最小夹具。
    
    那个文件里第二条用例是刻意的:只断言「对账干净」时,seed 一个都没种也会“通过”,
    两种原因症状相同。第一版就是这么错的 —— 我误以为 runStartupSeed 会种公司角色
    (它只做 reconcile + 全局 agent + 模板),是第二条断言把「夹具没种」和
    「过滤没生效」分开的。
    
    ## 验证
    
    871 全绿(87 文件)。3 处变异精确变红后逐字还原
    (startsWith 回退 → 精确打红跨公司读/写两条;verifyCounts 去掉过滤 → 打红对账条)。
    拿运行中的实例 + 用户真实公司 mycompany 走查过:读到真实 brd.md 内容,
    `../idearun/company.json` 与 `/etc/hosts` 均 403。web:build 通过。
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse File »

  • 命名轴是「节点在哪一层图里」,把散落的老 agent 归到它实际所处的层级:
    
      innovator → org-innovator      type='cxo',是 org graph 层的节点
      hunter    → innovator-hunt     innovator work graph 第 1 阶段
      digester  → innovator-digest   第 2 阶段(输入是 hunt 产出)
      evolver   → innovator-evolver  第 3 阶段
    
    依据:retrain-pipeline.js 的 stepConfigs 就是按 hunter → digester → evolver 顺序
    跑的,行 231 明写 steps: ['re_hunt','re_digest','evolve'] —— 这三个本来就是
    innovator 的 work graph 三阶段,只是名字没体现出来。
    
    ■ 迁移(scripts/migrate-agent-naming.js,--dry-run + 幂等可重跑)
    - 8 个目录(system/agents/ + system/templates/),用 git mv 保留历史
    - system_agents 表 4 行的 id 与 legacy_path
    - 公司实例目录与 company.json.agents[](本次无公司雇过这四个,实际 0 条)
    
    ■ 代码引用点(5 处,手工改)
    - retrain-pipeline.js: stepConfigs 的 agent 字段 ×3、agentToStep 映射的 key ×3、
      call_sub_agent 的 evolver 判断
    - agent-chat.js: agent 列表过滤里的 innovator
    - company.js: 模板训练触发的 agentId
    
    ■ 刻意不动的三类同形异义(改了会坏)
    1. skill 内部子目录名 {skillDir}/hunter/knowhow.md —— 那是 hunt/digest/evolve
       三阶段的产物目录约定,与 agent 名同形但不同义。涉及 open-api.js:402、
       retrain-pipeline.js:148/374、evolution-loop.js:178、sync-skills.js 的 INTERNAL_DIRS
    2. skill 命名空间白名单 —— skill-dir-manager.js 的 CORE_SKILLS、
       migrate-trained-skills.js 的 SYSTEM_SKILLS,那里的 'innovator' 指同名 skill
    3. conversations/ 与 system/tasklog/ 下的历史记录 —— 运行态数据,不改写历史
    
    evolver-youtube-crawler 按 §3.4 列为待定,迁移脚本按精确目录名匹配,未误伤。
    
    这四个重命名顺带解决了 27 号文档 §七 挂了很久的 6 个命名冲突 id —— 重命名后
    system/agents/ 与 system/templates/ 下不再有同名目录(只剩 agent-creator 与
    evolver-youtube-crawler 两个,前者是基础设施角色按规则不改名)。
    
    验证:npm run test:unit 693 全绿(64 files);test-company-e2e 114/114;
    迁移脚本二次执行 dirs=0 dbRows=0 skipped=8(幂等)。
    
    未触碰另一进程正在进行的 P3 文件(graph-engine.js / graph-run-sse.js /
    work-graph-db.js / 27-p3-p4-work-graph-console.md / 27-requirement.md)。
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse File »
  • 按 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>
    yibo authored
     
    Browse File »

  • 真实建公司验证时发现的两个串联问题——只有起真服务才暴露得出来,
    工作流的集成 agent 全程没起过服务,测试也覆盖不到。
    
    1. 地址错误:orggraph-planning 是被 hire 进公司的,住在
       data/companies/{id}/agents/ 下,寻址必须是 `{companyId}:orggraph-planning`。
       原代码请求裸名 /api/agent/orggraph-planning/chat → 404 Agent not found,
       agent 从未被触发,公司永远停在空壳状态。
    
    2. 失败被静默吞掉:fetch 只在网络层出错时 reject,4xx/5xx 不会 reject,
       所以原来的 .catch() 从头到尾没打印过任何东西。补 res.ok 检查 + 成功日志,
       这类"地址写错"的问题以后能第一时间看见。
    
    修复后端到端验证通过(临时公司 p2-verify,验证完已清理):
    - POST /create → company.goal 落盘、orggraph-planning 被招聘且因 meta-bootstrap
      被正确排除在 org graph 外
    - 日志出现 "✅ 已触发 p2-verify:orggraph-planning" + ACP 会话创建
    - 30 秒内 agent 产出 4 节点 5 边的图并完成 4 个角色招聘
    - agent 选中 dev.json 且**扩充**出模板没有的 code-tester(goal 要求带单测),
      并在规划记录里明确排除了 content.json 的歧义("Markdown/HTML 是被处理的
      数据格式,不是要产出的内容作品")——模板作为"起点集合"的设计意图成立
    - 产出的图实跑 validateGraph 通过四项校验
    - 四个角色 config.json 均为 inheritedFrom=base(元 agent 继承路径生效),
      system_agents 里 scope='company' + owner_company_id 正确
    - orggraph-planning 收尾后 heartbeat.enabled=false,正确休眠
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse File »
  • 用 9 个并行 agent 按「文件独占」划分实现 P2 的 T1-T9(T0 已于 2255f09 完成)。
    详见 docs/todo/system-design/27-p2-org-graph.md §五。
    
    阶段 A · 数据层
    - graph-db.js: org_graph_nodes 加 status/reports_to_node_id(PRAGMA 探测后 ALTER,
      幂等);全量唯一索引换成 WHERE status='active' 的部分索引,使「fire 后重新 hire
      同角色」可行;org_graph_edges 重建为执行流边(condition/edge_kind/status)
    - graph-service.js: ensureOrgGraphNodeIndex 同步改建部分索引,且 upsertOrgGraphNode
      的 ON CONFLICT 必须写成 ON CONFLICT(company_id, role_name) WHERE status='active'
      —— SQLite 的冲突目标要与部分索引谓词逐字对应,否则整条 INSERT 直接失效
    - org-graph-service.js(新): validateGraph 四项校验 + writeGraph/patchGraph/
      getGraph/removeNode,事务化,节点软删除级联失效相关边
    - routes/company.js: 四个端点 POST/PATCH/GET /org-graph + POST /org-graph/replan;
      /create 接入自动招聘 orggraph-planning + 异步触发(失败降级为空壳公司);
      /fire 接入节点软删除
    
    阶段 B · 自动生成
    - system/org-graph-templates/{dev,content,ops}.json + README:三张三段式骨架
      (规划→执行→验收,验收失败回退到执行节点),角色名遵循 {domain}-{职能}
    - system/templates/orggraph-planning/:新 system agent(type='meta-bootstrap'),
      含 §4.1 八步工作流
    - company-service.js: 模板不存在时从 system/templates/base/ 元 agent 继承骨架
      (自动带 RESEARCH→PLAN→EXECUTE→VERIFY 四段式)+ agent-creator 填充,
      新建 agent scope='company'。这是 27-E2E §八bis 元 agent 继承的落地点
    
    阶段 C · 展示
    - CompanyDashboard.vue: 组织流程拓扑链渲染 + 空态降级 + 环深度保护
    
    并行编排中发现并修复的三处(均不在原任务划分内):
    - AppNav.vue 建公司表单缺 goal 输入框 —— T0 只改了后端,前端表单没同步,
      company-goal 实际传不进来。这是上一轮 T0 的遗漏
    - graph-seed-service.js 会把 type='meta-bootstrap' 的 orggraph-planning 塞回
      org graph,与 hire() 的 guard 不一致
    - graph-agent-p1-acceptance.test.js: hire 未知角色的返回从 needs_training 改为
      hired_from_base(T5b 的行为变更),断言同步
    
    验证(独立复核,非 agent 自述):
    - npm run test:unit 676 passed / 63 files(基线 652,+24 为新增的 org-graph 用例)
    - npm run web:build 通过
    - 三个模板 JSON 实跑 validateGraph 全部通过;四类非法图各自被对应 rule 拒绝;
      writeGraph→getGraph→removeNode 级联验证通过(删中间节点后 3 条边全部失效)
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse File »
  • 起因:逐条比对 27-REQ 的原话与 P2 v0.4,发现四处偏离,其中两处是设计悄悄
    改变了原意。本次修正并落定。
    
    代码(P2 §五 T0):
    - company.json 新增 goal 字段(company-goal),create({id,name,goal,description})
    - POST /api/company/create 与 update() 同步支持
    - 修的是一个实打实的遗漏:R-6 原话是"写入 company 的目标和描述"(两个字段),
      而 create() 一直只有 description。orggraph-planning 规划流水线的主依据本该是
      company-goal,此前根本拿不到。
    
    文档定案(R-13):
    - R-13.1 goal 三层递归:company-goal / task-goal / subagent-goal,与"验收逐层
      递归"对应。澄清 R-12 说的"唯一的那个 goal"其实是 task-goal;三个验收 agent
      保持通用不按层级拆分。记录一处待定:subagent-goal 按执行者命名,与另两个的
      作用域命名轴不一致(按轴应为 subtask-goal)。
    - R-13.2 公司类型完全由 orggraph-planning 推断 —— 这条**推翻了我自己记的一个
      缺陷**:审计时我把"不加 type 枚举字段"记为擅自偏离原话,实际那正是所需,
      当前设计无需改动。
    - R-13.3 新 agent 从 system/templates/base 元 agent 继承骨架(自动带四段式
      RESEARCH→PLAN→EXECUTE→VERIFY)+ agent-creator 填充,而非直接走 needs_training
      全量培养;新建一律 scope='company';innovator 训练是可选后置步骤。
      这是 R-9.2"所有 node agent 由元 agent 继承生成"在 P2 的落地点(新增 T5b)。
    - R-13.4 org graph 强制单入口,原 open question 关闭。
    
    顺带修掉 P2 的一处内部不一致:§六 承诺"提供手动触发 orggraph-planning 的端点"
    给存量公司补图,但 §四.3 API 表和 §五 任务表都没有。已补 POST …/org-graph/replan。
    
    另更正 R-11 中"代码层未执行"的过期表述——OKR 正名已于 74ea1ff 实施完毕。
    
    回归:npm run test:unit 652 例全绿。
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse File »
  • 代码层:
    - server/services/goals.js → okr-service.js;GoalsService → OkrService
      · this.goalsDir → okrDir,目录 goals/ → okr/
      · ID 前缀 G- → O-;落盘文件 goal.json → objective.json
      · updateGoal → updateObjective,_nextGoalId/_saveGoal 等同步改名
      · parseNodeId 层级名 ['Goal','Objective','KeyResult','SubTask']
        → ['Objective','KeyResult','SubTask','SubTask'](OKR 无 Goal 这一层)
    - server/routes/goals.js → okr.js;:goalId → :objectiveId
    - 7 处调用点同步:index.js / agent-server.js / agent-chat.js / company.js /
      company-heartbeat-service.js / prompt-builder.js / test-company-e2e.js
    - 前端 OkrDashboard.vue / CompanyDashboard.vue 切到 /api/okr(前后端命名终于一致)
    - bin/lemon.js 新增 okr create/add-node/list/status
    
    向后兼容(不破坏任何现有调用方):
    - /api/goals/* 与 /api/company/:cid/goals/* 全部保留为别名,与新路径同时挂载
    - task dispatch/report 同时接受 objectiveId(新) 与 goalId(旧),写入时两字段都留
    - bin/lemon.js 保留 goal * 命令别名
    
    迁移脚本 scripts/migrate-goals-to-okr.js(npm run okr:migrate):
    - 目录 data/{,companies/*/}goals/ → okr/;条目 G-xxx → O-xxx
    - 改写 objective.json 的 id / nodes 的 key / children 数组、_index.json、
      progress.json 的 goalId、history.log 文本、system/tasklog/*.json 的 goalId+nodeId
    - --dry-run 预演;幂等;迁移前自动备份为 *.bak-goals(已加入 .gitignore)
    - 处理一个反直觉边界:OkrService 构造函数会自动 mkdir 目标目录,"okr/ 已存在"
      不等于"已迁移过"——只要服务被 require 过一次就留下空壳。用 isEmptyOkrDir()
      区分空壳与真实数据,空壳移除后继续迁移。这个 bug 是在 fixture 上验证时发现的。
    - 同样在 fixture 上发现并修掉:nodeId 的值漏改写、tasklog 迁移不幂等
    
    验证:新增 server/tests/unit/okr-service.test.js(7 例,覆盖新命名工作正常 +
    迁移纯函数的 ID 改写);全量 npm run test:unit 652 例全绿。
    
    范围说明:NAMING §五 的 benchmark 三分(skill-/agent-/goal-)本轮未动代码——
    该节自身的建议就是存量保持现状、只对新增代码强制前缀,因为 benchmark_runs 等表
    服务于线上 Arena 排行榜,改表名/路由是另一次独立迁移。
    §二-四 的新 agent id 尚不存在实体,待 P2/P3 实现时按规范创建。
    
    已知遗留(非本次引入):server/scripts/test-company-e2e.js 早已失效——它按
    data/{id}/ 找公司,而 company-service 用的是 data/companies/{id}/,在 HEAD 版本
    同样全部断言失败。本次只同步了其中的 OKR 命名,未修这个路径问题。
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse File »
  • 四处写 _registry.json 的地方都用 JSON.stringify(registry, null, 2) 直接落盘,
    不带末尾换行。文件在仓库里是带换行的,导致任何一次 hire()/depositTemplate()/
    模板增删改之后,git diff 都会多出一行 "\ No newline at end of file" 的噪音,
    把真实的数据变更淹掉。
    
    写入点:
    - company-service.js:374  depositTemplate()
    - company-service.js:824  _updateTemplateUsedCount()
    - routes/company.js:86    PATCH /templates/:templateId
    - routes/company.js:120   DELETE /templates/:templateId
    
    同时提交一次真实的运行时数据变更:qa-judge usedCount 7 → 8。
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse File »