• subtask-planning 每个 step 新增 acceptance 字段(benchmark 段格式),createTask
    强制 decomposed_work 子任务必须带验收契约并用 goal-contract.validateContract 校验,
    enqueueTaskRun 起轻量图前把契约落进新 run 的 artifacts/acceptance.json——解决
    46号文档 Phase 3 v2 真实回归撞见的 org-validator 死锁(轻量图没有 org-planner
    产出契约,退无可退)。
    
    真实起 subtask-planning agent 验证时发现设计稿的字段形状假设有误(真实产出是
    benchmark 段本身,不是三段式 wrapper),已按真实产出修正代码与文档,并用那次
    真实调用的原始输出跑通完整链路验证。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01H85SS5hVC4u1PbVhjmrhH8
    yibo authored
     
    Browse Dir »
  • 验证 46 号文档 Phase 3 v2(CEO 心跳发现并派发子任务)时,触发 CEO 心跳
    直接撞上 401——company-heartbeat-service.js 的 _sendAgentChat 和
    heartbeat-service.js 的 _triggerHeartbeat 都在自环调用
    POST /api/agent/:agentId/chat(agent-chat.js 挂了 requireAuthOrInternal),
    但两处都没有带 INTERNAL_TOKEN_HEADER——company.js/task-settle.js 的同类
    自环调用早就在带,这两处漏了。
    
    失败只落一行 console.warn,界面上什么都看不出来,影响的不只是 CEO 心跳:
    两套心跳系统(per-agent 的 heartbeat-service 和公司级的
    company-heartbeat-service,含它驱动的 pipeline/schedule 触发)都受影响。
    
    按现成模式补上内部令牌头。全量单测 1189 passed(唯二失败与本次改动
    无关),修完真实验证 CEO 心跳能正常跑完整段推理会话。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01H85SS5hVC4u1PbVhjmrhH8
    yibo authored
     
    Browse Dir »
  • 真实回归暴露"认领后没有触发源"的真问题,讨论后发现"认领"(pull,CAS
    抢占)要解决的"多方竞争同一条任务"场景根本不存在——只有 CEO 一个人拍板
    要不要派、派给谁。CEO 心跳统一发现+直接派发(push)成本更低,还能顺带
    控制派发节奏,不需要认领。
    
    删除:claimSubtask/unclaimSubtask(work-graph-db.js)、POST /claim、
    POST /unclaim(company.js)、CLAIMABLE_WORK_ROLES 白名单、
    claimable/claimed 两个 status 值、claimed_at 列。
    
    改名:subtask_kind 的 'claimable_work' → 'decomposed_work'
    (materializeClaimableSubtasks → materializeDecomposedSubtasks),
    "认领"这个词不再准确,物化出的行现在就是普通 draft task。
    
    新增:
    - triggerSubtaskPlanning 加递归防护(task.parent_task_id 非空直接跳过)
      ——设计 v2 时发现的真实漏洞:CEO 对子任务调 /enqueue 会默认
      withSubtaskPlanning=true,不挡会对已经是"第几步"粒度的子任务再拆一层。
    - createDecomposedWorkGraph:现造一张两节点轻量 work graph
      (impl=org-coder、validate=org-validator,agent_call 不是 subgraph),
      跳过公司完整 org graph 的 brd/prd/sdd 规划仪式——subtask-planning 已经
      把"怎么做"拆到这个粒度了,再走一遍纯属重复劳动。含一条没有真实验证过
      的假设(org-coder/org-validator 裸调用会不会读不到预期上下文),如实
      写在代码注释和文档里。
    - enqueueTaskRun 按 subtask_kind 选图:decomposed_work 走轻量图,
      普通 task 行为不变(回归测试覆盖)。
    - ceo/heartbeat.md、tools.md 补了巡检 decomposed_work 草稿 + 判断节奏
      + 调用现成 /enqueue 的指令。
    
    测试:删除 task-pool-claim.test.js(随 v1 代码作废),新增
    task-decomposition-dispatch.test.js(12 条),更新
    task-decomposition-validator-gate.test.js 措辞。全量单测 1191 条,
    1189 passed(唯二失败与本次改动无关),零残留 CLI 子进程。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01H85SS5hVC4u1PbVhjmrhH8
    yibo authored
     
    Browse Dir »
  • 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 Dir »
  • 46 号文档 Phase 2 真实端到端回归(真实公司 p2verify0902 + 真实 goal/task/
    Claude CLI)第一次跑就复现一个真缺陷:原始打分用「交集计数、>0 就算命中」,
    一条长任务描述几乎必然跟经验库里的高频工程词汇偶然重合几个 bigram,导致
    一个贪吃蛇任务被"验证修复要重新触发流程"这类无关的工程经验污染成
    reasoning_source=memory,压过了本该触发的 deep research 分支。
    
    改成 containment ratio(命中词数 / 经验块自身词数,对经验块归一化而不是
    对查询归一化),阈值用真实噪音案例(~3%-4%)和合成相关案例(~16%-32%)
    校准。补回归测试固定复现这次真事故场景。修完重启服务用第二条真实任务
    (Markdown 转 HTML 工具)重新验证:reasoning_source 正确变成
    deep_research,两轮调用(subtask-planning → org-hunt → subtask-planning)
    真实跑通,全程零残留 CLI 子进程。46 号文档补了真实回归记录,包括一个
    如实记录的局限:org-hunt 那次调用没有工具调用痕迹,更接近"多问一个角色
    的第二意见"而非真的做了外部检索。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01H85SS5hVC4u1PbVhjmrhH8
    yibo authored
     
    Browse Dir »
  • subtask-planning 改成两段式:先查 experience-lookup(扫已雇 agent 的 dream
    沉淀经验,关键词打分)拼进提示词,agent 自评 confidence;经验为空或
    confidence low 时触发 deep-research(图外直接调用 org-hunt 轻量提示词,
    不套用其训练场景三件套契约,默认关闭,需 ENABLE_DEEP_RESEARCH=1)。
    reasoning_source/experience_refs/research_used 增补进 subtask-plan.json,
    向后兼容旧注入桩。
    
    范围收窄:不做"历史同类 goal 检索"(需要语义检索基础设施);Phase 3-5
    (任务池认领/子任务预算/看板 UI)涉及核心调度引擎范式级改动,另起设计。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01H85SS5hVC4u1PbVhjmrhH8
    yibo authored
     
    Browse Dir »
  • reopenRunForRetry 与 _reapStaleRunningNodes(开机孤儿清扫 + 45号文档持续性存活
    扫描共用)把 run 从 blocked/ready 拉回 running 走的是裸 admitOrRequeue,不是
    startRun,只改了状态从没重新挂上 runAuto 自驱动循环——手动/自动重试推一步后没人
    接着推,agentRunId 仍是 null,backward 重试每次都在这里再卡一次。两处都补上
    startRun 收尾那段逻辑:真的推成 running 且 execution_mode==='auto' 才挂 driver。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • 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 Dir »

  • 真实事故:h3video 公司 h3-story-draft 节点卡在 status=running,背后 CLI 会话已死但服务器
    进程没重启,开机一次性孤儿扫描/goal-task 巡检/wallclock 看门狗均看不到,ceo 被转发问题后
    绕过引擎自己拉会话,陷入死循环。
    
    - sweepStaleRunningAgentCalls + startStaleAgentCallWatchdog:运行时长超阈值(默认 25 分钟)
      的 agent_call 节点判 infra_flake 原地重跑,5 分钟一次巡检,挂进 server/index.js
    - ceo/tools.md 补 force-fail 端点(已有,未改代码)的使用文档 + 禁止自拉会话的规则
    - ceo/heartbeat.md 同步更新"不用管的"一节
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01WjFL1nfPDqZnJeuskYHwvs
    yibo authored
     
    Browse Dir »
  • Snapshot-then-stop race in stopRun() left child runs created after the
    snapshot outside the stop scope; add auto-mode guard rejecting manual
    /next on execution_mode='auto' runs (source of the concurrent-advance race).
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • 44 号:writesArtifacts 节点产出闸门永远打不过
    - goal-benchmark 按设计把产物写进 root run 共享 artifacts/ 目录(三个节点合成同一份
      acceptance.json,天然不可能落在某节点私有目录),但 unsatisfiedRequiredProduces 的
      R-19 逃生口 readDeclaredProduces 的 allowedRoots 只认节点私有目录 + 项目根,够不到
      共享区——这条契约在当前实现下无论 agent 表现多好都无法通过。
    - 修法(44 号文档方案 1,改动面最小):allowedRoots 加入 runRootDir(run)/artifacts/;
      goal-benchmark 的 agent.md/tools.md 补上"写完共享产物后在自己节点私有目录声明一份
      contract.json"这一步。
    - 回归:graph-files-index.test.js 新增用例 ⑭。
    
    41 号:routeFailure 的 infra_flake 分支完全无视 target_node
    - 30 号文档记录过一次真实死循环:节点自称 infra_flake 并点名 target_node,引擎原来
      一律原地重跑该节点自己,从不看 target_node。
    - 修法(最保守方案,用户确认):新增 infraFlakeTargetEdge——target_node 只有在图上
      真有一条从本节点出发的 backward fail 边指向那个节点时才生效,命中就改走正常的
      backward 边路由(计入 backward_count/双层熔断),不再享受"原地重跑、只受
      infra_flake_count 约束"的免账本待遇;没命中维持原有行为。
    - 重要澄清:这条修复堵不住 30 号文档那次真实事故——事故里点名的目标是下游节点,
      而 org-graph-service.js 规则 4(backward-labeled)从图结构上就不允许 backward 边
      指向非祖先,那次事故本来就不是"有边不认",是"诊断内容在打图结构不允许表达的
      主意",已经靠 INFRA_FLAKE_HARD_CAP 熔断收敛,不在这条修复范围内。
    - 回归:graph-engine.test.js 的 infra_flake describe 块新增两条用例。
    
    44 号文档移入 done/,41 号清单项相应更新,43 号文档的交叉引用一并修正。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01CwGcvMfUcgCcweN5vjhTkn
    yibo authored
     
    Browse Dir »
  • 之前排队等待(上条改动)期间前端只有通用的"思考中",最长可能等到 60s 都看不出
    是在排队还是在正常生成,容易让人以为卡死了。
    
    - agent-session-lock.js 新增 isLockBusy(agentId):判断此刻是不是真被占着(僵尸锁
      不算),供路由层在真正排队前 peek 一眼。
    - agent-chat.js 的 /:agentId/chat:锁被占用时提前建立 SSE 连接、先发一个 queued
      事件(activeConvId + waitBudgetMs),前端能精确显示"排队等待中";没被占用的
      常见路径完全跳过这段,不引入多余的建连时机差异。头一旦按 200 SSE 发出去就不能
      再改状态码,排队超时也改用 queue_timeout 事件收尾,不再是独立的 429 JSON 响应
      (没被占用、一开始就抢到锁的路径不受影响,仍是普通 SSE)。
    - agentChatEvents.js 的 handleEvent 认识这两个新事件类型:queued 把 thinkingText
      换成带等待预算的提示;queue_timeout 跟 error 分支一样落一条可见的助手消息,
      不会像原来的静默 429 那样挂在界面上没反应。
    - 相应更新 agent-chat-interrupt.test.js(断言 SSE 里真的有 queued/queue_timeout
      事件,超时分支状态码从 429 改为 200)+ agentChatEvents.test.js 新增两条用例。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01CwGcvMfUcgCcweN5vjhTkn
    yibo authored
     
    Browse Dir »
  • yibo authored
     
    Browse Dir »
  • - agent-session-lock.js 新增 acquireLockWithWait:锁被占用时按 pollMs 轮询排队等待
      AGENT_CHAT_QUEUE_WAIT_MS 预算(默认 60s),check-and-set 之间无 await,天然原子;
      等到锁释放就正常执行,等满预算仍抢不到才 429,不再"撞上就立刻拒绝"。
      内部自唤醒(TaskCallback/TaskDispatch 等 selfChat 调用)是 fire-and-forget,之前
      429 等于这次唤醒直接消失,只能靠别处巡检兜底;排队等待让大多数几秒到几十秒的
      短暂碰撞能自然衔接上。
    - AgentChat.vue 的 handleSend() 之前拿到 fetch 响应后不检查 r.ok 就直接当 SSE 流读,
      非 200 响应体是普通 JSON,逐行解析全部匹配不上 event/data 前缀,被静默当空流吞掉——
      用户发的消息挂在界面上,没有任何回复也没有报错。现在先判 r.ok,非 200 时读 JSON
      错误体,429 显示"Agent 正忙,请稍后重试",其他状态显示带状态码的错误提示。
    - agent-chat-interrupt.test.js 相应更新:原来断言"并发第二个请求立刻 429"的用例
      改为断言"预算内排队等待、等到就正常执行、两次调用不重叠",另加一条"预算耗尽仍
      抢不到锁时返回 429"的回归。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01CwGcvMfUcgCcweN5vjhTkn
    yibo authored
     
    Browse Dir »
  • 实现路径: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 Dir »
  • 原来的 /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 Dir »
  • test/integration 类节点从不产出一个能被 indexNodeOutputs() 认作
    kind='log' 的文件(落盘的都是 coder.log/test.log,归入 kind='artifact'),
    required 的 log 契约永远无法满足,把节点卡进 missing_required_input
    死循环。33 号文档修过一次模板文件,但 workgraph-planning agent 实际
    照抄的是 tools.md 里一份没同步更新的旧范例,同一个坑复现(snakegame-co
    T-mtfh2kuf-7c31)。这次把底线收到 declareEdgeContract 唯一落库口,
    不再依赖每个模板/每次 LLM 规划都记得写对;顺带同步 tools.md 范例。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01U7xYyvv3pzuV9diMVUH5QL
    yibo authored
     
    Browse Dir »

  • org-coder 等 9 个角色(含 _base 骨架模板)的四段式阶段流转靠调
    bin/lemon.js task transition,打到 agent-chat.js 的 /task/transition——
    这个端点靠自己的 sessions Map 找会话,work graph 节点场景下找不到,
    阶段流转静默跳过,中间产物不进任何跨节点共享的轨迹。
    
    调研确认范围后选了最保守的实现:不改 bin/lemon.js 的 CLI 签名、不碰
    agent-chat.js(当天正被另一个并行会话实时编辑,风险太高)。关键发现
    是 --artifact 参数本来就是 .../runs/{rootRunId}/nodes/{nodeRunId}/...
    形状的路径,两个 ID 已经现成编码在里面,不需要给子进程塞新环境变量,
    不需要改一个字的 agent.md 提示词。
    
    bin/lemon.js 在原有调用之外 best-effort 追加一次 POST
    /api/graph-runs/phase-note,新端点从 artifact 路径解出两个 ID,
    校验 run 真实存在后调 emitEvent() 追加 timeline 记录,只追加不碰
    graph_run_nodes/graph_runs 任何一行——不影响 needs/produces 契约。
    P0 拦截/OKR 更新两段旧逻辑维持原样,新端点是独立的第二条路径,不是
    迁移。
    
    新增测试 4 例,全量测试 1503/1506,零回归(剩余失败全部来自另一个
    并行会话进行中、还未提交的工作)。42 号文档 Phase 1-4 全部完工。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01BwG4b7Nq1NLR9eeE3M9Us9
    yibo authored
     
    Browse Dir »
  • 原来场景 C 固定只回放最近 20 条、单条截断到 500 字,ACP 进程重启/空闲超时
    后早于窗口的历史会静默丢失。改成按 token 预算(默认按 1M 上下文的一半,
    AGENT_MODEL_CONTEXT_TOKENS 可覆盖)从最新往最老收,装得下就整段注入。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01XUvQ2xSFk7YoavE5SQENdw
    yibo authored
     
    Browse Dir »
  • 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 »
  • 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 »

  • 2026-08-28 把本地 mycompany/innovator-fde-trial 同步到生产时手搓合并脚本踩过两个坑:
    本地库和生产库同名字段的物理列序可能不同(SELECT * 位置对齐会静默写错列),以及
    graph_runs/graph_run_nodes 循环外键的回填 UPDATE 必须给关联子查询加表别名。
    
    把手搓版本整理成 scripts/sync-company-to-graph-db.js(按列名对齐 + 支持 --dry-run),
    并在 graph-db.js migrate() 和 production-deploy.md 里记下这两个坑,供下次同步复用。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01UpkWPa2CmuWJFJZksqvxWB
    yibo authored
     
    Browse Dir »

  • router.use(requireAuthOrInternal) 之后,agent-chat.js 自己的 selfChat() helper
    (TaskDispatch/TaskCallback/TaskTransition ×2/TaskReject/HumanRequest 共 7 处
    调用)一直在用裸 http.request 自己打自己的 /api/agent/:id/chat,没带
    x-internal-token,全部被 401 挡住——而且 chatRes 回调之前不看 statusCode,
    401 也照样打印"✅ 已唤醒",症状是静默的(跟 claude-cli-session-leak.md 一个模式)。
    实测复现:真实 dispatch 一个任务给 org-innovator,目标 agent 从未收到消息,
    任务卡在 dispatched 状态没人执行。
    
    修法:selfChat 加 x-internal-token header(跟 company.js/task-settle.js 的
    自环调用同一套机制),并且真的检查 chatRes.statusCode,非 2xx 打日志说明失败,
    不再假装成功。
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    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 »
  • 之前 invoke 端点只把结果落 job(.data/jobs-openapi.json),从不生成 conv-*
    会话、也不写 meta.json/history.json,导致网页 AgentChat 看不到任何 API
    调用产生的对话记录。现在复用 agent-chat.js 的会话存储函数,写到同一份
    system/agents/{agentId}/_global/conversations/{convId}/ 下。
    
    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 »