• 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 Dir »
  • 把「人类提的一个需求」与「机器执行的一件活」拆成两级。这不是新概念:
    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 Dir »
  • 承接 310a2b2 的两个遗留:
    
    ① 四类被错标成心跳
       company-heartbeat-service._sendAgentChat 给五类调用(心跳巡检 / Pipeline 步骤 /
       日程 / 告警 / 日报)无差别打 isHeartbeat:true,于是对话列表里全都显示成
       「⏱️ 自主心跳巡检」、meta.type 一律 'heartbeat'。改成显式传 kind,五类各自标注。
       isHeartbeat 仍然发,但只对真心跳为真,老消费方行为不变。
    
    ② 310a2b2 让 Pipeline 重试变成了冷启动
       Pipeline 步骤的 convId 是 `pipeline-{runId}-step-{i}`,被驳回重做时**复用同一个**。
       310a2b2 按 isHeartbeat 一刀切回收,等于每次重试都要走 buildPrompt 场景 C 重注入历史。
       现在回收策略挂在 CALL_KINDS 表上,判据只有一条:**convId 会不会被下一轮复用** ——
       会复用的(pipeline / 人类对话)留着走 TTL,不复用的(心跳 / 日程 / 告警 / 日报)跑完即回收。
       不按「是不是后台调用」分:五类后台调用里 pipeline 就是要留的那一类。
    
    守卫补到 12 条(原 3 + kind 标注 5 + 回收策略 4)。对 310a2b2 跑过:8 红 4 绿。
    convId 一律用固定字面量而非拼时间戳 —— 撞上 test-id-stability 那道防线后按它的判据改的,
    固定之后第二次跑还顺带走了「复用已有对话」那条分支。
    
    文档:
      - CLAUDE.md 注意事项加一条(新增会话创建路径时必须同时定回收路径,附症状与守卫位置)
      - 28-followups 守卫清单补第五条,并把「前四条守单一持有者」与第五条「守资源生命周期」
        的形状差异写明;顺手修掉表格已有 4 行、结尾却仍写「这三条」的失配
      - .env.example 补 AGENT_SESSION_IDLE_MS
    
    全量单测 667/667 绿。重启后 /api/health 正常,心跳照常挂载。
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • sessions 里存的是**常驻**的 claude CLI 子进程(stream-json 模式,不是一次性的
    `claude -p`),此前只有四条回收路径:协议 error / 子进程 disconnected / 手动删对话 /
    手动 stop —— 「正常跑完的对话」不在其中。
    
    而两路心跳都用一次性 convId 打进 /api/agent/:id/chat:
      - heartbeat-service:`heartbeat-${Date.now()}`,cto / coo 各 5 分钟一次
      - company-heartbeat-service:_sendAgentChat 的五个调用点(CEO 巡检 / Pipeline 步骤 /
        日程 / 告警 / 日报)convId 也全是一次性的,且无差别带 isHeartbeat:true
    
    于是每次心跳新起一个进程且永不释放。实测单次运行 2h43m 攒到 82 个进程 / 17.5 GB RSS。
    
    改法:
      - 心跳会话跑完立刻 closeSession(一次性 convId,会话复用对它零价值)
      - 普通对话上空闲 TTL(默认 30 分钟,AGENT_SESSION_IDLE_MS 可覆盖),复用 engine/idle-timer
      - 计时器只在「没有在途请求」的间隙走:getOrCreateProtocol 命中复用时 cancel,请求结束时
        重新上弦。所以再长的单次生成也不会被 TTL 打断,比在流式回调里 touch() 少一个调用点
      - 删对话 / stop 两处手抄的 disconnect+delete 收敛到 closeSession,否则它们不停表
    
    守卫:agent-chat-session-reclaim.test.js 用现成的 /chat/:convId/stop 当黑盒探针
    (查得到会话才返回 success:true),不为测试加导出口。对 HEAD 版本跑过:2 红 1 绿 ——
    两条回收断言修前失败,「普通对话保留会话」修前修后都绿,证明复用能力没被顺手砍掉。
    
    线上验证:重启后第一轮心跳 2 触发 / 2 回收,server 名下 claude 子进程数 0。
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • 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 Dir »
  • 设计见 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 Dir »

  • 控制台原先只能看引擎的事件轨(stream.log / timeline.ndjson),里面是结构化事件、
    自由文本还被截断到 600 字;引擎更是根本不消费流式 chunk(onAcpEvent 只取
    session/result 的 usage)。所以 LLM 的逐字输出从来不在它的轨道上。
    
    新端点 GET /api/graph-runs/:id/nodes/:nodeRunId/transcript **惰性读** Claude CLI
    自己的会话文件:不进 SSE、不进节点行、不落第二份库 —— 那正是引擎当初决定不存全文的
    三条理由要避免的(输出无上限而 getRunState 每次轮询 SELECT * 全量回传 /
    产物已有 output_ref 这个唯一真相源 / fail_reason 是选边匹配键不能掺自由文本)。
    这里没有推翻那个设计,只是多开一个读取入口。
    
    目录推导与条目规范化抽到 server/services/agent-transcript.js,
    scripts/tail-agent.js 改为复用它 —— 一份实现两个入口。今天已经修过三次
    「两份必须保持一致的实现漂了」,不想制造第四次。
    
    ## 界面上明说的三个前提(不藏)
    
    1. transcript 按 (公司, 角色) 分、**不按节点分**,只能用节点 started_at/completed_at
       截时间窗口 —— 近似而非精确对齐,窗口边界原样显示。
    2. <env_status>(harness 每轮注入的环境块,几 KB 机器噪音)服务端剥掉,
       但**剥了几处如实显示**。静默删内容比留噪音更坏。
    3. 默认只看 agent 说的话;喂进去的内容(提示词全文、工具返回)要勾选才出来 ——
       提示词里有写入边界与公司上下文,不默认摊开,但可以看。
    
    subgraph 节点不给这个 tab(它自己不调 agent,活在子图里)—— 给了就是个空 tab,
    而空 tab 会被读成「没记录」。正在跑的节点靠「刷新」取最新,**不轮询**
    (整份会话几百 KB,跟着 run 状态轮询太贵)。
    
    ## 三处「看着对、实际错」都有用例钉住
    
    ① listRoles 的目录前缀差一个 '-':path.join 丢掉空的最后一段,切出来的角色名
       全带前导 '-'(实测 '-sdd-planning')。我是靠肉眼看输出发现的,所以补回归测试。
    ② 路由取本体取错层:本体绑在定义层节点上,graph_run_nodes 只有 def_node_id,
       第一版直接读运行时行 → SqliteError: no such column。
    ③ 找不到文件时回 ok:false + 期望目录,不回空列表 ——
       空列表会被读成「这个 agent 没说过话」,而真相是「我们没找到文件」。
    
    另外测试当场抓到我自己那个诚实机制的漏洞:整条正文都是 env_status 的条目会被剥空,
    normalize 早返回 null 导致它剥掉的那一处**没计入总数** —— 号称如实回报却少报。
    改成 parts 空但剥过东西时仍返回,由 readEntries 先记账再决定入不入列表。
    
    ## 验证
    
    880 全绿(88 文件)。3 处变异精确变红后逐字还原。
    运行中的实例上走查过:org-planner-sdd 的原文逐条带时间戳显示,
    第一条就是它去读上游 prd.md / contract.json —— R-19 那条链路被 agent 真的用上了。
    subgraph 节点(org-planner)确认没有这个 tab。
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • ## 为什么加
    
    「输入输出」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 Dir »
  • 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>
    yibo authored
     
    Browse Dir »
  • 测试 788/788 全绿(76 文件,起点 786),未改任何既有断言。
    
    ## 1. idleTimeoutMs 硬编码 120s(「work 节点 → success」跑不通的唯一技术原因)
    
    agent-manager.js 里 `this.config?...?.idleTimeout || 120_000`,且 `...options`
    摊在前面被下一行覆盖,两个后果叠加:
    - 默认 120 秒,比 stream-executor 自己 JSDoc 写的 30 分钟(1_800_000)小一个数量级,
      也和同进程 ClaudeCliProtocol 早已放到 24h 的 idle 上限完全对不上
    - 调用方**无法覆写** —— 传了 options.idleTimeoutMs 也会被盖掉
    
    真 CLI 下的后果:任何「先读几千行源码再动笔」的节点都在动笔前被杀
    (readme-writer: idle timeout: no stream data for 120000ms)。
    
    改成 调用方 > agent 配置 > DEFAULT_IDLE_TIMEOUT_MS(30min),并把常量真的定义出来
    —— 上一轮 Master agent 自己诊断出这个问题也动手改了,但引用了未定义的
    DEFAULT_IDLE_TIMEOUT_MS,跑起来是 ReferenceError。
    
    ## 2. W7 判据在零边最小图上失效
    
    判「org graph 成型了没有」原先看**边数**,理由是「节点 hire() 自己就会造,
    拿节点数当信号会废掉『连雇两个人第二个不入图』」——理由成立,但边只是
    「writeGraph 跑过」的代理,而真跑里 orggraph-planning 裁出过一张**零条边的单节点图**,
    代理当场失效:三条判据全落空 → work 层本体被写进 org_graph_nodes,
    下一次下达任务时被 advanceOne 当成 org 节点执行。
    
    改成记录**来源**而不是猜:org_graph_nodes 加 authored_by(idempotent ALTER),
    writeGraph 写 'planner'、hire 写 'hire',判据看有没有 planner 写下的节点。
    hire 先记的行随后被 planner 纳入时升格,反向不降级。
    存量行 authored_by 为 NULL,回落到原来的边数代理,行为不变。
    
    新增两条用例(既有 9 例造的全是有边的成型图,正好绕开这个形状),
    并做过变异验证:把判据改回纯边数,两条精确变红,还原后逐字一致。
    
    ## 3. 前后端「能不能下达任务」的规则各写一份且不一致
    
    前端 ACTIVE_RUN 含 blocked、后端 ACTIVE_ROOT_RUN_STATUS 不含(唯一索引
    idx_graph_runs_one_active_root 里也没有)。后果:一家公司只要失败过一次,
    控制台上就再也下不了第二个任务,而后端其实放行。
    
    规则只留服务端一处:GET /graph-runs 返回 canDispatch / activeRootRunId,
    前端照抄结果,不再自己复述取值域。字段缺失时保守放行,交给端点 409 兜底。
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • 测试 786/786 全绿(76 文件,起点 760/72),未改任何既有断言。
    
    ## 本轮三件新东西,全部经真 CLI 验证
    
    W12 补完 · 失败原因四环打通
    - 引擎自判的失败(executor 抛异常、边界越界、子 run 无归因)现在也落
      graph_run_nodes.fail_reason,不再只有 agent 归因产出的才有值
    - 概览 tab 展示「[引擎判定] boundary_escape」+ 终端样式 excerpt,
      取不到 fail_reason 时回落 output_excerpt,并标明来源
    - 真跑实查:两行 fail_reason='boundary_escape' 都在库里,
      产生→传递→落库→读回四环在真 CLI 下走通
    
    W13 · 运行态文件根治(守卫误判的根因)
    - 17 个 todo.md / plan.md / scheduled_tasks.json git rm --cached + gitignore;
      源码(agent.md/config.json/soul.md/tools.md)一个未碰,模板本体仍跟踪
    - _registry.json 的 usedCount 挪进已忽略的 data/template-usage.json
    - 真跑验收:整轮(agent-creator、Master 各一次真 CLI、3 次 hire)结束时
      worktree git status 与基线逐字相同,_registry.json 零 diff
    
    单步进度感 · 真 CLI 一个节点 3-7 分钟
    - 画布卡片秒表 + 详情面板常驻条「运行中 2m40s · 最近事件 …(2m40s 前)」
    - 不做假进度条:引擎不知道 agent 还要多久
    - 顺手修 NodeDetailPanel 不跟 SSE 走(否则秒表也不会动)
    
    W9 失败上下文进提示词、W10 跨层地址两半、W11 Hot Swap 显式告警同批完成。
    
    ## 真跑结论:链路更长,但「work 节点 → success」仍未跑到
    
    走到:orggraph-planning 落图自雇 → agent-creator 补定义 → workgraph-planning
    真跑规划 + **先雇后提交**(W5/W6 再次成立)→ 子 run 起来 → work 节点 RUNNING
    → 7m19s 后 fail。两层原因叠加,都与 W1-W14 无关:
    
    1. **新缺陷**:agent-manager.js:324 把 idleTimeoutMs 硬编码 120s,
       readme-writer 通读源码静默超 2 分钟被掐死(动笔前就死了)。
       同进程 ClaudeCliProtocol 早把自己的 idle 上限放到 24h,
       stream-executor 的 JSDoc 默认值写的是 30 分钟 —— 这里把两者都压掉一个数量级;
       且 ...options 透传的 idleTimeoutMs 会被下一行覆盖,调用方无法覆写。
    2. **并行 agent 污染**:retrain-watchdog 每 5 分钟唤起的 Master 在同一工作树里
       改了两个源文件,被守卫 git status 差集算到该节点头上 → 额外署 boundary_escape。
       这是 W3「拦不住」第 5 条第一次真实发生。
    
    顺带记一笔:这一轮任务是 **LemonBoss 自己的 Master 自主下的单**,
    走的正是 POST /api/company/:id/tasks,它还自行诊断出了上面那个 idle timeout
    并动手改(引用了未定义的常量,是坏的)。
    
    ## 已知未修(下一轮)
    
    - idleTimeoutMs 硬编码 120s —— 「work 节点 → success」跑不通的唯一技术原因
    - W7 在**零边最小图**上失效:判据③要求存在 active org_graph_edges,
      零边图上三条判据全落空,work 层本体被写进 org_graph_nodes。
      服务层那 9 例测试造的都是有边的成型图,正好绕开这个形状
    - 前端 ACTIVE_RUN 含 blocked、后端 ACTIVE_ROOT_RUN_STATUS 不含:
      一家公司只要失败过一次,控制台上就再也下不了第二个任务(后端其实放行)
    - 真跑期间工作树里不能有第二个 agent 活动,否则守卫判罚不可信 —— 应写进真跑规程
    
    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »

  • 界面走查(真 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>
    yibo authored
     
    Browse Dir »
  • 命名轴是「节点在哪一层图里」,把散落的老 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 Dir »
  • 按 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 Dir »

  • 真实建公司验证时发现的两个串联问题——只有起真服务才暴露得出来,
    工作流的集成 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 Dir »
  • 用 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 Dir »
  • 起因:逐条比对 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 Dir »
  • 代码层:
    - 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 Dir »
  • 四处写 _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 Dir »
  • # Conflicts:
    #	system/agents/master/.claude/scheduled_tasks.json
    yibo authored
     
    Browse Dir »
  • …_DIR move, stray files
    
    - CLAUDE.md: document DevLoop subsystem (spec-first workflow, project root/git-vault,
      mock mode, L1 loop, unit layout, audit trail) and add missing test commands
    - server/routes/agent-chat.js, server/services/task-service.js: TASK_META_DIR moved
      from system/agents/.tasks to system/tasklog
    - system/agents/master/.claude/scheduled_tasks.json: scheduled task state update
    - remove stray memory/daily/2026-04-02.md and a garbled-filename artifact left under
      system/agents/digester-youtube-crawler/
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »

  • First successful real-CLI agent invocation. After REAL.1 (system-prompt
    injection) + REAL.2 (mtime-diff file detection) wired, the only remaining
    gate was Claude auth. Once CLAUDE_CODE_OAUTH_TOKEN was added to the
    project .env, scripts/real-pm-smoke.js completed cleanly:
    
      Wire-up status:
        ✓ process spawn
        ✓ stream chunks       (3)
        ✓ result text          (Claude returned "DONE")
        ✓ files written        (6: prd/index, feature-tree, user-flows,
                                  product/overview, assets/shell.{css,js})
        ✓ git commit           (8cb0493)
      5/5 checks passed · elapsed 163s
    
    PM output quality (real Claude, not mock):
    - valid <article data-doc="prd" data-rev="1"> + <section data-rev="1">
      structure per system prompt
    - nav links use correct relative paths across 4 chapters
    - pid (p-7gwlbl) embedded in HTML metadata
    - 中文 prose in the requested 克制 voice
    
    Spec-deviation confirmed (no MCP tool gateway yet — Claude only has
    built-in Read/Write/Bash):
    - ✗ agent-state/pm.json (no update_runtime_state tool)
    - ✗ agent-state/pm-todo.json (no write_todolist tool)
    - ✗ history/prd-rev-001.html (no archive_doc tool)
    - ✗ timeline.ndjson 'prd_drafted' event (no timeline_append tool)
    - ✓ core PRD site IS produced — sufficient to prove pipeline
    
    ──── code ────
    
    - scripts/real-pm-smoke.js — loads root .env via dotenv resolved
      from server/node_modules (script dir has no node_modules of its own).
    
    ──── ambient ────
    
    Files modified by user/linter while real-CLI work was in flight; merged
    without revert:
    - server/routes/devloop.js — new GET /:pid/requirements endpoint
    - server/services/devloop/path-resolver.js — listProjects() includes
      archived flag + helpers; supports project archive lifecycle
    - server/services/devloop/git-vault.js — commit() now accepts files[]
      to scope which paths the commit covers (avoids dragging linked-existing
      user's unstaged files into chore commits)
    - server/src/core/devloop/loop-controller.js — createProject uses
      explicit files:['project.json','timeline.ndjson'] for first chore
      commit (avoids same issue)
    - server/tests/devloop/path-resolver.test.js — coverage for archive/
      patch/hardDelete helpers
    - web/src/components/devloop/RequirementListSection.vue (new)
    - web/src/views/DevLoopNewRequirement.vue (new)
    - web/src/views/DevLoopDashboard.vue — mounts RequirementListSection
    - web/src/views/DevLoopProjects.vue — list page polish
    - web/src/router/index.js — adds /devloop/:pid/requirements/new route
    - web/src/components/devloop/DashboardHeader.vue — back link tweak
    
    Test results: 233/233 vitest pass (was 230 → +3 path-resolver cases).
    
    ──── ⚠ SECURITY NOTE ────
    
    .env is currently TRACKED in git (committed in b1a348b) even though
    .gitignore lists it. Existing committed contents include credentials for
    DOUBAO / FEISHU / GITHUB / GOOGLE / JWT. The CLAUDE_CODE_OAUTH_TOKEN +
    AZURE_OPENAI_API_KEY I just added to .env are NOT in this commit — they
    stay in your working tree only.
    
    Recommended follow-up (separate commit):
      git rm --cached .env
      git commit -m "chore: stop tracking .env (already gitignored)"
    And rotate every key that was in the previous committed .env.
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • Two parallel pieces of plumbing — neither has been exercised end-to-end yet
    (real CLI gated on `claude /login`; Projects list view served alongside
    existing pages):
    
    ──── real-CLI wire-up (REAL.1 + REAL.2) ────
    
    LemonBoss now has a clean toggle between mock & real Claude CLI:
    
    - packages/acp-core/protocol/ClaudeCliProtocol.js
      * `options.systemPrompt` → `--append-system-prompt <prompt>` so each
        devloop agent receives its role-specific prompt.md without losing
        Claude's default tool-use conventions.
      * `options.maxBudgetUsd` → `--max-budget-usd` (note: only effective in
        `--print` mode; documented inline).
    
    - server/src/core/devloop/loop-controller.js
      * callAgent now branches on `opts.mock`:
        - mock (P1-P5 path, untouched): fixture sequence + `mock/files` event
        - real: read system/templates/{agent}/prompt.md, pass as systemPrompt,
          snapshot project file mtimes pre-call, diff post-call to compute
          `files_written` — no fixture, no mock/files needed.
      * `_snapshotMtimes(root)` helper walks the project tree (skipping
        .git / node_modules / .cache / .tmp) and returns Map<relPath,mtime>.
      * timeline event payload now carries `real: true` flag.
    
    - scripts/real-pm-smoke.js — minimal real-CLI sanity:
      spawns claude, injects PM prompt, prints which files were written +
      git commit sha. Hard timeout 5min. Single agent → expected spend
      $0.10-0.30 per run when authenticated.
    
    Current status: process spawn + system-prompt injection + stream-json
    flow all verified up to the `Not logged in · Please run /login` gate.
    Auth is the only remaining blocker for executing the real smoke.
    
    ──── project CRUD routes ────
    
    server/routes/devloop.js + server/services/devloop/path-resolver.js
    add the lifecycle pieces the Projects list view needs:
    
    - GET    /projects?include_archived=true   list w/ archive filter
    - PATCH  /projects/:pid                    edit name + idea_summary
    - DELETE /projects/:pid                    soft delete (archive)
    - DELETE /projects/:pid?hard=true          hard delete (rm -rf sandbox;
                                                unregister linked)
    - POST   /projects/:pid/restore            un-archive
    
    path-resolver exposes matching helpers: listProjects({includeArchived}),
    patchProjectMeta(pid,patch), hardDeleteProject(pid).
    
    ──── UI ────
    
    - web/src/views/DevLoopProjects.vue (320 lines, new)  — Projects list
    - web/src/router/index.js — adds `/devloop` → DevLoopProjects
    - web/src/components/devloop/DashboardHeader.vue — back-link now points
      to `/devloop` (project list) instead of `/company`.
    
    ──── housekeeping ────
    
    - CLAUDE.md / start.sh — start.sh now boots server/index.js (full route
      surface incl. /api/devloop) instead of the historical agent-server.js;
      docs updated to reflect this.
    
    ──── tests (+12) ────
    
    - server/tests/devloop/routes-crud.test.js — 12 cases:
      PATCH validation (4) · soft DELETE + list filter (4) · POST /restore
      (1) · hard DELETE (2) · GET /projects default behavior (1).
    
    Results:
    - phase0-verify .. phase5-verify: 8+7+7+6+9+9+7 all pass
    - vitest devloop+acp-core: 230/230 (was 218 → +12)
    
    Specs:
    - docs/prd/newfunction/_spec/arch-acp-bridge.md · Mock Mode
    - docs/prd/newfunction/_spec/plan-migration.md · "自举乐趣" (real path
      prep)
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • …b-pages + UI endpoints)
    
    Completes the §P4 acceptance UI scope that was deferred in commit
    0594b1d. Adds the full Vue Dashboard + Ops sub-pages with live SSE
    stream + auto-reconnect, unit DAG with detail drawer, PRD/SDD diff
    panel, feedback modal, and 4 ops views (Bugs / Loops / Changelog /
    Progress).
    
    Vue surface (≈1480 lines):
    - web/src/views/DevLoopDashboard.vue (~110 lines)
      * 4-region grid: status header / loop bar / units DAG / live stream /
        PRD diff. Polls /state every 2.5s; opens feedback modal & unit
        drawer; mounts Sign Off / Abandon controls.
    - web/src/views/DevLoopOps.vue (~50 lines) — tab container.
    - web/src/components/devloop/DashboardHeader.vue
      * Status badge (7 states color-coded), NEW REQUIREMENT button,
        Sign Off / Abandon buttons (disabled per FSM), Ops nav link.
    - web/src/components/devloop/LoopStatusBar.vue
      * 4-phase pipeline (Docs → L1 → L2 → Ship), highlights active phase.
    - web/src/components/devloop/UnitsDAG.vue
      * SVG DAG rendered from topological waves; per-unit status color;
        click → emits select → opens UnitDrawer.
    - web/src/components/devloop/UnitDrawer.vue
      * Slide-in panel showing unit metadata + last 3 runs + result
        summary + coder.log tail (last 1KB).
    - web/src/components/devloop/LiveAgentStream.vue
      * EventSource subscriber listening to project.status,
        unit.state, integration.result, triage.result, sdd.updated,
        prd.updated, human_intervention_needed, etc. Manual 5s reconnect
        on disconnect (browser's built-in Last-Event-ID is honored).
    - web/src/components/devloop/PrdDiffPanel.vue
      * Reads changelog.ndjson, renders prd_rev / sdd_rev entries with
        trigger labels.
    - web/src/components/devloop/FeedbackModal.vue
      * POSTs /feedback with PM incremental — shows rev bump on success.
    - web/src/components/devloop/ops/BuglistView.vue
      * Filterable bug table; status badges; severity / category columns.
    - web/src/components/devloop/ops/LoopLogView.vue
      * Timeline grouped by L1 (unit.*) / L2 (integration/triage/sdd/l2_*) /
        L3 (project.status/shipped/prd.updated/human_intervention) /
        System (rest). Free-text substring filter.
    - web/src/components/devloop/ops/ChangelogView.vue
      * Tabular prd_rev/sdd_rev entries with by/trigger/summary columns.
    - web/src/components/devloop/ops/ProgressView.vue
      * 6 metric cards (status, PRD rev, SDD rev, units passed bar,
        L2 rounds bar, open bugs) + per-unit breakdown table.
    
    Router:
    - web/src/router/index.js — adds:
      * /devloop/:pid              → DevLoopDashboard
      * /devloop/:pid/ops/:tab(bugs|loops|changelog|progress) → DevLoopOps
    
    Backend UI-supporting endpoints (server/routes/devloop.js):
    - GET /api/devloop/projects/:pid/timeline?limit=N
        Reads timeline.ndjson, returns last N events reversed.
    - GET /api/devloop/projects/:pid/changelog
        Returns full changelog.ndjson entries reversed.
    - GET /api/devloop/projects/:pid/units/:unitId
        Returns { unit, runs (last 3 result.json), coder_log_tail }.
        404 on unknown unit.
    
    Tests (+5 cases):
    - server/tests/devloop/routes.test.js — new P5 UI endpoints block:
      * timeline / changelog / units happy-paths
      * 404 on unknown unit and unknown pid
    
    Acceptance:
    - scripts/phase5-verify.js — 7 steps:
      ① Vue build artifacts (DevLoopDashboard.js + DevLoopOps.js chunks)
      ② POST /projects → state returns status + units
      ③ GET /timeline returns events
      ④ GET /changelog returns entries
      ⑤ GET /units/todo-be returns unit + runs
      ⑥ GET /bugs returns bug list
      ⑦ SSE /stream delivers ≥1 event after a triggering feedback POST
    
    Vue build:
    - npm run web:build → DevLoopDashboard chunk 15.63 kB,
      DevLoopOps chunk 9.09 kB, both with co-loaded CSS bundles.
    
    Results — all 7 phase gates green:
    - phase0-verify: 8/8
    - phase0-test-call: 7/7
    - phase1-verify: 7/7
    - phase2-verify: 6/6
    - phase3-verify: 9/9
    - phase4-verify: 9/9
    - phase5-verify: 7/7
    - vitest devloop+acp-core: 218/218 (was 213 → +5)
    
    Specs: docs/prd/newfunction/_spec/plan-migration.md · P4 L3 + UI
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • Implements the §P4 acceptance gate from plan-migration.md, with one
    scope adjustment confirmed up-front: full Vue Dashboard (~1650 lines)
    deferred to a future UI phase, replaced by extending the existing
    P3 static status page with Sign Off / Abandon / Feedback controls.
    All P4 backend acceptance criteria are scripted (phase4-verify.js).
    
    Project status FSM (project.json.status):
    - server/src/core/devloop/loop-controller.js
      * draft  →  running  →  shipped  →  shipped-final | abandoned
                    ↓             ↓
              l2_blocked    (feedback flips back to running)
              l2_exhausted
      * `_setProjectStatus` validates transitions via STATUS_TRANSITIONS,
        emits SSE `project.status`, writes timeline event.
      * createProject seeds status=draft; runDocsPhase end → running;
        runProject(shipped) → shipped; all dead-end branches (l2_blocked,
        l2_exhausted) also propagate to status via _safeSetStatus.
    
    L3 helpers:
    - signOff(pid, opts) — requires status=shipped → shipped-final; commits
      a `chore: sign-off` git entry; rejects other source states with
      InvalidProjectStatusTransition.
    - abandon(pid, opts) — any non-terminal → abandoned; commits + calls
      sseBroker.close(pid) to send SSE close frame to subscribers.
    - submitFeedback(pid, text, opts) — rejects in shipped-final/abandoned;
      invokes PM in incremental mode; on prd_rev bump writes
      changelog.ndjson prd_rev entry + emits prd.updated SSE; if prior
      status was shipped, flips back to running so next runProject can
      rebuild.
    
    PM incremental mock fixture:
    - fixtures/devloop-mock/devloop-pm.json — converted to sequence form:
      * sequence[0] (init) — original P1 fixture (PRD rev=1, 14 files).
      * sequence[1] (incremental) — appends a new section to
        prd/feature/feature-tree.html, bumps article data-rev to 2,
        writes history/prd-rev-002.html.
    
    Baseline happy-path Integrator:
    - fixtures/devloop-mock/devloop-integrator.json — single-shot pass.
      Makes vanilla runProject ship without a scenario override.
    
    P4 happy-path scenario:
    - fixtures/devloop-mock-scenarios/happy_path/devloop-architect.json —
      single-unit (todo-be only) Architect so end-to-end ship works with
      the baseline Coder/Tester (which only know todo-be paths).
    
    Routes:
    - server/routes/devloop.js:
      * POST /projects/:pid/sign-off — 400 InvalidProjectStatusTransition
        when status != 'shipped'; 200 returns { status }.
      * POST /projects/:pid/abandon  — body { by?, reason?, note? }; 400 on
        terminal state; 200 returns { status: 'abandoned' }.
      * POST /projects/:pid/feedback — body { text (req), by?,
        mockFixtureDir? }; 400 on missing text or shipped-final/abandoned;
        200 returns { prd_rev_before, prd_rev_after, status }.
      * POST /projects: now accepts optional `mockFixtureDir` so the same
        fixture dir threads through both the create call and later
        feedback (keeps mock-sequence counters aligned).
    
    Static status page (replaces full Vue Dashboard for P4):
    - server/public/devloop-status.html — adds:
      * Status badge in header (color-coded per status).
      * Sign Off / Abandon control buttons (disabled per current state).
      * Feedback textarea + Submit button → POST /feedback.
      * Buttons reflect FSM (signoff only when shipped; abandon/feedback
        locked at terminal states).
    
    Acceptance:
    - scripts/phase4-verify.js — 9 steps, full HTTP path:
      ① POST /projects → running
      ② POST /run → shipped
      ③ POST /feedback → PRD rev 1→2 + running (+ changelog entry)
      ④ POST /run again → shipped
      ⑤ POST /sign-off → shipped-final
      ⑥ POST /feedback (after sign-off) → 400
      ⑦ POST /sign-off (after sign-off) → 400
      ⑧ second project: POST /abandon → abandoned
      ⑨ POST /abandon (after abandon) → 400
    
    Tests (+19 cases):
    - server/tests/devloop/p4-l3-feedback.test.js
      * 14 FSM cases covering happy path + abandon path + signOff
        pre-conditions + empty feedback rejection.
      * 5 route cases via fetch on ephemeral express app.
    
    Results:
    - phase0-verify: 8/8
    - phase0-test-call: 7/7
    - phase1-verify: 7/7
    - phase2-verify: 6/6
    - phase3-verify: 9/9
    - phase4-verify: 9/9
    - vitest devloop+acp-core: 213/213 (was 194 → +19)
    
    Spec deviations explicit:
    - Full Vue DevLoopDashboard.vue + Ops 4 sub-pages NOT implemented;
      static status page handles the demoable surface area instead.
    - All backend endpoints from §P4 (sign-off, abandon, feedback) are
      complete and tested.
    
    Specs: docs/prd/newfunction/_spec/plan-migration.md · P4 L3 + UI
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • Implements the §P3 acceptance gate from plan-migration.md: a single
    POST /api/devloop/projects/:pid/run drives the full project from L1
    through L2 with 4 distinct BugTriager branches, observability via SSE
    stream + bugs.ndjson + changelog.ndjson, and a tiny static status page.
    
    Mock protocol — fallback dir:
    - packages/acp-core/protocol/ClaudeCliProtocol.js
      * New options.mockFallbackDir: when scenario dir lacks a per-agent
        fixture, fall back to the baseline fixtures/devloop-mock/.
        Enables scenario dirs to override only the agents that differ
        (Integrator + Triager + sometimes Architect).
    
    Scenario fixtures — 4 L2 branches:
    - fixtures/devloop-mock-scenarios/contract_mismatch/
      * Architect: sequence[init, patch] — patch bumps sdd_rev → 2,
        units.json l2_rounds → 1, marks todo-be back to pending.
      * Integrator: sequence[fail (500 vs 201), pass].
      * Triager: contract_mismatch confidence 0.85 with proposed_sdd_patch.
    - fixtures/devloop-mock-scenarios/impl_bug/
      * Integrator: sequence[fail (ReferenceError), pass].
      * Triager: impl_bug confidence 0.78 affected=[todo-be], proposed=null.
    - fixtures/devloop-mock-scenarios/spec_ambiguous/
      * Triager: confidence 0.4 → LoopController emits human_intervention.
    - fixtures/devloop-mock-scenarios/infra_flake/
      * Integrator: sequence[crashed (EADDRINUSE), pass].
      * Triager: infra_flake confidence 0.95 affected=[].
    - Each scenario also includes a 1-unit (todo-be) Architect.init fixture
      so baseline Coder/Tester (todo-be) can drive L1.
    
    LoopController:
    - server/src/core/devloop/loop-controller.js — adds:
      * runProject(pid, opts) — wave-scheduled L1 via _groupUnitsByWave,
        runIntegrator (devloop-integrator), runTriager (devloop-triager),
        then branch dispatch:
          - contract_mismatch+conf≥0.5 → Architect(patch) writes new
            units.json (already pending, l2_rounds++); emits sdd.updated +
            changelog sdd_rev entry; loop back to L1.
          - impl_bug+conf≥0.5 → unitRegistry.reset(affected); loop, no
            budget burn.
          - spec_ambiguous OR conf<0.5 → emit human_intervention_needed
            SSE, exit l2_blocked.
          - infra_flake → loop directly (no reset, no budget burn).
      * runDocsPhase + runUnit + callAgent all thread opts.mockFixtureDir
        through to the protocol.
      * Auto-attaches sseBroker to unit-registry and bug-tracker on module
        load — every FSM transition + bug op fires an SSE event.
      * bug_open at integration failure, bug_transition → fixed when
        subsequent L2 round passes.
    
    Routes:
    - server/routes/devloop.js:
      * POST /projects/:pid/run now defaults to full runProject; ?only=<id>
        still drills into single-unit runUnit (P2 compatibility).
      * GET  /projects/:pid/stream — SSE with Last-Event-ID replay.
      * GET  /projects/:pid/bugs?status=&unit_id=&severity= — bug list.
    - server/index.js + agent-server.js mount /public for static assets.
    
    Static status page:
    - server/public/devloop-status.html (~100 lines) — pure HTML, polls
      GET /state every 2s, renders unit DAG with status pills + timeline
      tail. URL ?pid= or input box.
    
    Acceptance:
    - scripts/phase3-verify.js — 9 steps across 4 scenarios + observability:
      ① contract_mismatch ships at sdd_rev=2 l2_rounds=1, sdd.updated SSE,
        changelog sdd_rev entry, bug open → fixed.
      ② impl_bug ships, sdd_rev=1, l2_rounds=0 (no budget burn).
      ③ spec_ambiguous → l2_blocked + human_intervention_needed SSE.
      ④ infra_flake ships, rerun_integrator action, l2_rounds=0.
      ⑤ Observability: timeline/bugs/changelog/stream.log present.
      ⑥ SSE replay from Last-Event-ID returns ≥2 historical events.
    
    Tests (+13 cases):
    - server/tests/devloop/run-project.test.js (11) — 4 scenarios end to
      end + SSE event capture.
    - server/tests/devloop/routes.test.js (+2) — full-project mode + bugs
      404. Updated P2 ?only stub test → full-project 404 on unknown pid.
    
    Results:
    - phase0-verify: 8/8
    - phase0-test-call: 7/7
    - phase1-verify: 7/7
    - phase2-verify: 6/6
    - phase3-verify: 9/9
    - vitest devloop+acp-core: 194/194 (was 181 → +13)
    
    Specs:
    - docs/prd/newfunction/_spec/plan-migration.md · P3 验收
    - docs/prd/newfunction/_spec/loops-l2.md (failure attribution + budget)
    - docs/prd/newfunction/_spec/agents-integrator.md
    - docs/prd/newfunction/_spec/agents-triager.md
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • Implements the §P2 acceptance gate from plan-migration.md: a single
    POST /api/devloop/projects/:pid/run?only=todo-be drives the L1 inner
    loop (Coder → Tester → fail → Coder → Tester → pass), with the second
    Coder pass demonstrably reflecting on the first failure.
    
    Mock protocol — stateful sequences:
    - packages/acp-core/protocol/ClaudeCliProtocol.js
      * Mock fixtures now support `sequence: [{chunks,result,files}, ...]`.
        Each protocol.connect() + sendPrompt() consumes the next variant;
        sticks on the last entry after exhausted.
      * Static `ClaudeCliProtocol.resetMockState()` clears counters between
        test runs.
    
    Fixtures:
    - fixtures/devloop-mock/devloop-architect.json — units.json now has
      status:pending + retries:0 + retry_budget per unit, plus
      units/{todo-be,todo-db,todo-fe}/spec.json so Tester has acceptance
      criteria to read.
    - fixtures/devloop-mock/devloop-coder.json — sequence: implement (with
      intentional missing 400 branch) → retry (reflective fix, coder.log
      attempt:2 has last_failure/last_cause/this_fix/rationale).
    - fixtures/devloop-mock/devloop-tester.json — sequence: fail (1/3
      failure with hint pointing at missing body validation) → pass (3/3).
    
    LoopController:
    - server/src/core/devloop/loop-controller.js — adds:
      * runUnit(pid, unitId, opts) — drives the L1 loop via unit-registry
        FSM (pending → coding → testing → failed → coding → testing →
        passed/blocked). Calls Coder then Tester per attempt; on failure,
        builds a retry context summarizing prior coder.log + tester
        failures and feeds it into the next Coder prompt.
      * _buildRetryContext + _findLatestTesterResult helpers.
      * callAgent now rewrites units.json.project_id to the real pid after
        mock writes (so unit-registry validation passes).
    
    Route:
    - server/routes/devloop.js — adds POST /projects/:pid/run?only=<unitId>.
      Without ?only, returns 501 (full project run lands in P3).
    
    Acceptance:
    - scripts/phase2-verify.js — 6 steps:
      1. POST /projects (PM + Architect)
      2. POST /run?only=todo-be returns ok=true, attempts=2, final=passed
      3. FSM trail matches expected 6-step transition sequence
      4. coder.log second entry has all 4 reflection fields
      5. runs/ has ≥2 result.json with first.ok=false, last.ok=true
      6. sandbox cross-unit + prd writes blocked by ToolSandboxError
    
    Tests (5 new cases):
    - server/tests/devloop/run-unit.test.js (4) — FSM trail, coder.log
      reflection, runs/ result chain, sandbox enforcement.
    - server/tests/devloop/routes.test.js (+3) — POST /run validation,
      full P2 loop, 404 on unknown unit.
    
    Results:
    - phase0-verify: 8/8
    - phase0-test-call: 7/7
    - phase1-verify: 7/7
    - phase2-verify: 6/6
    - vitest devloop+acp-core: 181/181 (was 174 → +7)
    
    Specs:
    - docs/prd/newfunction/_spec/plan-migration.md · P2 验收
    - docs/prd/newfunction/_spec/loops-l1.md (retryWithReflection)
    - docs/prd/newfunction/_spec/agents-coder.md
    - docs/prd/newfunction/_spec/agents-tester.md
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • Implements the §P1 acceptance gate from plan-migration.md: a single
    POST /api/devloop/projects produces a runnable todo-demo project with
    multi-page PRD/SDD sites, units.json, integration spec, agent-state,
    and history snapshots — all via mock agents (no real Claude CLI cost).
    
    Code additions:
    - packages/acp-core/protocol/ClaudeCliProtocol.js
      * mock mode now emits an extra `mock/files` acp_event when the fixture
        contains a files[] array. LoopController applies it to the project
        root, letting mock agents produce real outputs.
    - server/src/core/devloop/loop-controller.js (now ~280 lines)
      * callAgent(pid, agentName, prompt, opts) — single-agent invocation:
        reads system/templates/{agent}/prompt.md, runs mock protocol,
        applies fixture files[], timeline-logs, auto-commits.
      * runDocsPhase(pid, requirement) — orchestrates PM → Architect with
        `docs.complete` timeline marker.
      * getProjectState(pid) — returns project.json + units + prd_rev/sdd_rev
        + timeline tail for the GET /state endpoint.
    - server/routes/devloop.js (new)
      * POST /api/devloop/projects — createProject + runDocsPhase
      * GET  /api/devloop/projects
      * GET  /api/devloop/projects/:pid/state
    - server/index.js / server/agent-server.js — mount /api/devloop.
    
    Mock fixtures (drive the full acceptance file tree):
    - fixtures/devloop-mock/devloop-pm.json
      * 14 files: prd/{index,reading-guide,product/*,feature/*,flow/*,prototype/*}.html,
        prd/assets/{shell.css,shell.js,search-index.json},
        agent-state/{pm.json,pm-todo.json}, history/prd-rev-001.html.
    - fixtures/devloop-mock/devloop-architect.json
      * 13 files: sdd/{index,mvcp/*}.html, sdd/assets/*,
        units.json (3 units, DAG), integration/spec.json,
        agent-state/{architect.json,architect-todo.json}, history/sdd-rev-001.html.
    
    Acceptance:
    - scripts/phase1-verify.js — spins ephemeral express app, POSTs the
      todo-demo project, asserts 23 expected paths exist + 3 units with
      l2_rounds/l2_budget + git ≥4 commits + timeline events.
    
    Tests (12 new cases):
    - server/tests/devloop/loop-controller.test.js (7) — callAgent +
      runDocsPhase + state.
    - server/tests/devloop/routes.test.js (5) — POST validation, full
      POST, GET state, 404 on unknown pid.
    
    Results:
    - phase0-verify: 8/8
    - phase0-test-call: 7/7
    - phase1-verify: 7/7
    - vitest devloop+acp-core: 174/174 (was 162 → +12)
    
    Specs: docs/prd/newfunction/_spec/plan-migration.md · P1 Docs MVP
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »

  • 1. Evolver agent.md: Added token budget rules
       - No re-reading unchanged files between iterations
       - Use head/grep instead of cat for large files
       - Concise output only (scores + diff summary, no full SKILL.md)
       - Cap at 5 iterations per session (was 10)
       - Early stop if score change < 2 points for 2 consecutive rounds
    
    2. agent-chat.js: System prompt size guards
       - MAX_PROMPT_FILE_SIZE = 15KB per file (auto-truncate with warning)
       - MAX_TOTAL_PROMPT_SIZE = 100KB total (safety net)
       - Prevents runaway files from inflating context ever again
    yibo authored
     
    Browse Dir »
  • When a task completes and triggers a callback to the parent agent,
    previously it always created a new convId (callback-{taskId}), spawning
    a fresh CLI process with full system prompt re-injection.
    
    Now it checks the sessions pool for an existing connected session of the
    target agent and reuses its convId. This avoids creating a new CLI
    process and saves the system prompt overhead (~30K tokens for master).
    
    Falls back to creating new session if no active one exists.
    yibo authored
     
    Browse Dir »
  • Layer 1 (P0): Per-agent session mutex
    - heartbeat-service.js: skip heartbeat if previous session still active (30min safety valve)
    - agent-chat.js: reject concurrent /chat requests for same agentId with different convId (429)
      Multi-turn conversations on same convId still work normally
    
    Layer 2 (P1): Advisory file lock utility
    - server/utils/file-lock.js: O_EXCL-based .lock file mechanism
      Auto-cleanup stale locks, graceful degradation on timeout
    
    Root cause: HeartbeatService creates new convId per heartbeat, spawning
    independent Claude CLI processes that concurrently write config.json and
    SKILL.md, causing rollbacks and FRAUDULENT scores.
    yibo authored
     
    Browse Dir »