-
原来的 /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
-
run 撞墙钟/调用次数/token 上限 blocked 时,之前界面上没有入口, 只能开发者工具手搓 fetch 调 POST /:id/budget。加一个按钮 + 小弹窗, 三维增量(留空不追加)+ 追加后立即启动,复用现有的 act() 提交入口 与 confirmAuto 同款 modal 样式。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U7xYyvv3pzuV9diMVUH5QL
-
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
-
节点多时左右排列会把屏幕撑成很宽的多栏视图,改成按依赖深度自上而下、 同波并行节点左右排开,更贴合画布容器的竖直滚动。backward 回退边路由 相应改到节点带右侧走车道(原来在下方)。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U7xYyvv3pzuV9diMVUH5QL
-
排查「前端刷新不及时/没有打字机效果」发现现状已是 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
-
新增 /:id/task-board 路由,复用 GoalTaskPane/goal-task-model 的既有 逻辑铺满整页;废弃 /okr 路由与 OkrDashboard.vue,清掉 CompanyDashboard 里挂着"goals"名字、实际渲染旧 OKR 树的那个 tab;导航入口换成 "任务看板"。按 39 号设计文档 §二 P2 落地,后端 OKR 子系统本次不动。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BwG4b7Nq1NLR9eeE3M9Us9
-
Master Agent 固定置顶,其余 agent 可通过卡片右侧"⋯"菜单手动置顶/取消置顶 (状态存 localStorage)。置顶的 agent 渲染成独立的框,跟下方可滚动列表分开, 避免了 sticky 定位下其他条目从底下滑过的观感问题。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CRNPSJBNTPa77fzM1mZo9z
-
AgentChat.vue 之前把 sending/streaming/streamContent/thinkingText/ thinkingContent/subAgents/acpLog/messages/activeConvId 全部做成页面级单例 ref, 不区分是哪个 agent 的。切 agent 时既不取消上一个还在跑的读流循环,也不重置这些 状态,导致三个连环症状:①思考中/等待回复横幅带着切过去;②只要有任意一个 agent 在跑,所有 agent 的输入框都被同一个 sending 锁成"等待回复中";③A 流式输出时切去 看 B,A 的内容被当成 B 的渲染,A 说完那句话直接被 push 进 B 的消息列表。 改为按 agentId 存一份 reactive Map(chatStates),模板读"当前选中 agent"那一份 slice;handleSend/handleEvent 认发起时捕获的 requestAgentId 写自己的 slice,不认 "用户现在选中的是谁"。switchAgent 不再无条件清空目标 agent 状态——只有它空闲时 才去刷新/定位会话,正在生成就原样展示缓存,回切能看到完整后台进度。真机验证: 向 Master 发消息流式生成期间切到另一个 agent,输入框未被锁定、无状态污染,切 回 Master 能看到完整正确的输出。 顺带把已经在这个文件里的一处未提交小改动(agent 列表排序:Master 置顶)一并 收进这次提交,不是本次改动的一部分。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
-
远程部署前置需求:登录鉴权 + 公司级多租户隔离。后端 auth.js/jwt-auth.js(注册/登录/ JWT-cookie/OAuth)本来就已经可用,这次只是把它接到前端。 - scripts/seed-admin-user.js(npm run seed:admin):幂等种子脚本,建出 admin/hex1234, role=admin。 - web/src/composables/useAuth.js + views/Login.vue:登录态 + 登录页。 - router/index.js 全局 beforeEach:未登录跳 /login?redirect=...,登录后带着原目标跳回。 - main.js 全局 401 拦截器:cookie 中途过期同样兜住,不止页面切换那一刻。 - AppNav.vue:显示用户名 + 退出登录。 - 顺手修:App.vue 原来无条件渲染 MasterChatDrawer,登录页也会露出这个悬浮入口, 改成登录且非登录页才显示。 业务路由还没加锁(Phase 1 的事),登录后依然能看到现状全部数据,符合这一阶段的验收边界。 真实浏览器走过一遍全部流程(跳转/登录/跳回/直接刷新受保护路由/退出),npm run build 通过。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
-
run_b962ea87f58f 卡在 wallclock_ms 耗尽的锚点未结清问题上,需要人工追加预算 才能继续。extendBudget 只增不减、只加到已配额的维度上,追加时顺带结清卡死的 墙钟锚点(否则 startRunSync 一启动就把挂起时长记进新预算里)。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
-
招聘自动训练与「重新训练」按钮都是给 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> -
侧栏 170px 太窄看不全 agent 名字,改成 260px 并把名字从省略号截断 改成允许换行;顶部加搜索框按 name/id 过滤列表,点击过滤结果复用 已有的 switchAgent() 跳转逻辑,不用额外写定位/滚动代码。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
-
新建独立试验节点 prd-methodology-trial,走真实 Hunt→Digest→Evolve 训练 PRD 撰写方法论: 2 轮 Small Loop 提前收敛(96/97 分,真实基线 50 分),用三个真实历史项目反推验证方法论 有效,Deploy 范围限定在该独立节点,未触碰生产 prd-planning。结论:FDE 对方法论类角色 确实管用,但收敛速度/验收严谨度依赖任务书里显式写明的自纠正要求,非免人工黑盒。 过程中连带修复同一个 2026-08-16 改名批次(innovator→org-innovator 等)遗留的 5 处死链: company.js 两处硬编码调用、CompanyDashboard.vue「重新训练」按钮,以及四个本体各自 config.json 内部 id 字段从未随目录改名同步更新,导致 /api/agent/list 展示层用假 id 显示成死卡片。顺手修了 AgentScaffold 默认 tools.md 模板一处裸相对路径缺陷(对应 agent-md-refs.test.js 里已登记多时的已知缺陷)。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
-
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>
-
35 号文档剩下的三个提案,彼此直接相关(C 产出 D 消费的证据,B 展示 D 算出的结果), 一次做完。 提案 D(goal-contract.js 引擎级证据闸门): `runBenchmark` 新增 evidence 检测——`evidence_hint` 命中"浏览器/视口/开发者工具/ console/network/截图"类关键词的维度,必须在 evidence.<dim> 有真实字符串支撑, 否则该维度判不过(哪怕分数达标)。证据来源两处都认:`--target` 文件自带的 `evidence.<dim>`,或同目录姊妹判词文件 `attempt-<n>.json` 的 `dimensions[].evidence` ——后者是 benchmark-validator/agent.md 早就在教的既有写法,不用 agent 多写一份。 新增 `dimensions[].evidence_required` 字段随判决落盘。6 条新单测。 提案 C(可复用视觉验收工具): 新增 scripts/visual-acceptance-check.mjs,四个子命令(overflow/style/console/ contrast),封装最常见的几类真实浏览器检查(Playwright),agent 通过 bash 直接 调用,不用每次自己临时想办法(真实撞见过两个极端:一次真写了 Playwright 认真测, 一次只写了正则解析文本)。配一份不进 vitest 快速循环的独立冒烟脚本(起真 Chromium,几秒量级)。benchmark-validator/agent.md Step 2 加了引用说明。 提案 B(控制台证据可见性): GET /api/graph-runs/:id 新增 benchmarkVerdicts 字段(只读 goal-contract.js 已落盘 的判决,不重新计算)。NodeDetailPanel 概览 tab 新增"验收证据"小节:逐维度显示 分数 + 是否有证据支撑,evidence_required 且缺证据的标 ⚠️。纯展示,不改判定逻辑。 2 条新单测。 全量单测 1012/1012 通过;控制台功能用 Chrome 实测过(截图确认"验收证据"小节正确 渲染)。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
-
用户反馈:控制台原来只能逐个节点点「输入输出」tab 看产物,缺一个全局入口。 新增 TaskFilesPanel.vue,挂在页头,跨节点/跨 run 一次性列出这个任务的全部产物。 做的时候发现分两类、且第二类之前完全没覆盖: · 节点产出索引(graph_files,按 company+task 检索)——规划笔记/日志/契约快照 · 项目根(W2 ensureProjectRoot,data/companies/{cid}/projects/{pid}/)—— 真实交付物代码写的地方,跨节点共享,indexNodeOutputs 从不扫它 后端补 readProjectRootRecord 导出 + GET /api/graph-runs/:id 挂 projectRoot 字段; 项目根的文件列表复用已有的 GET /:companyId/files(此前零调用方),不新开列目录端点。 新增 graph-run-route-io.test.js 两例(分配前 null、分配后原样透传 + 幂等)。 顺带订正 34 号文档:当时断言"index.html 从未被写出来"是错的——真实网站文件 其实在项目根里,只是没查那个目录。34 号文档 §零 记录了订正过程,问题收窄成 "validator 有没有真的做过浏览器级检查"(仍未解决,留待下次)。 全量单测 1000/1000 通过。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
-
config.json 的 name 字段是工位显示名(如 org-coder 显示成"实现节点"、 coder 显示成"实现"),Overview 页 Agent 表格和 System Agents 侧边栏此前 只渲染 name、不带 id,导致界面上看不出某个显示名对应哪个 work graph 节点绑定的 system_agent_id。后端接口本就返回 id 字段,这里只是补上前端展示。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
-
新增 join-status.js(+ .check.js 可运行检查,web/ 不在 vitest include 里, 同款手法照抄 io-model.check.js):resolveJoinWait/joinWaitLabel,判据与引擎 activateSuccessTargets/allIncomingSatisfied 同源——只看 forward 的 success 入边,backward/fail 边不算「等待对象」。只有 2+ 条入边的真 fan-in 才出徽标, 单一入边的普通顺序等待不加噪声。 WorkGraphCanvas.vue:pending 节点在等哪几个上游时画一枚「⏳done/total」徽标, 悬停看完整明细(谁 success / 谁还在 running / 谁 fail 了)。 nextNodeId(单数)→ nextNodeIds(数组):advanceBatch(27-P4 T4)一次最多 认领 max_concurrency 个 ready 节点,并行分支下真实会被同批拾取的可能是好几个, 标一个会误导。这个 prop 之前在全仓库没有任何调用方真正传值(NEXT 徽标从未 渲染过),顺手把它接上:GraphRunPane 按 run.max_concurrency 截取候选, 手动模式仍显示单数提示,自动模式显示批量提示。 并行运行中的多节点高亮不需要新代码——.dot.running/.node.running 已经是 按每个节点各自状态渲染,多个节点同时 running 时天然一起亮,这条在 T4/T6 落地时就已经生效。 验证:join-status.check.js 6 条断言全过;`npm run web:build` 编译通过、 零告警。没有做的:没有起真实浏览器点一遍——搭一张真的并行 fan-in 图需要 起完整服务 + 建公司 + 跑真图,这次没做,逻辑侧的验证到 check.js + 编译为止。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
-
只做了这一件事,没有跟着做「dream 改成 org/work graph 来跑」——两者被 单独评估过:org/work graph 是单公司作用域(org graph=公司级 workflow, work graph=公司内某任务的节点图),dream 却是跨公司扫描,套进去要么破坏 作用域假设要么得新开图类型;且 graph 引擎自带的验收机制(goal-contract benchmark/test-script)判的是「产物过没过验收脚本」,对「经验提炼得对 不对」这种自由文本判断没有对应机制——不管套不套 graph 都得单独设计 rubric,是同一件事。用户明确只要 REST/控制台这一半。 ## REST(server/routes/dream.js,挂 /api/dream) 路由本身没有新业务逻辑,全部委托给已测过的 dream-service.js: GET /agents(总览)、GET /agents/:id(详情,含定义文件可写性 + 经验 审计)、POST /agents/:id/run(手动跑一次,dryRun 只看有没有新原料不 起 LLM)、POST /sweep + GET /sweep/status(全量扫描后台跑 + 轮询,内存 态并发锁防止连点两下)。 ## 控制台(web/src/views/DreamConsole.vue,路由 /dream) 一个表格 + 两个弹窗:本体列表(详情/跑一次/dry-run)+ 全量扫描按钮 (15 秒轮询状态)。导航栏加 🌙 Dream 入口。 ## 验证 server/tests/unit/dream-routes.test.js 4 例,monkeypatch 掉 dream-service 导出对象上的六个函数(不走真文件系统,不误扫真仓库 76 个本体),只验路由层自己的逻辑:端点接线、sweep 并发锁状态机。 真起服务器打了真数据:GET /agents/innovator-evolver 正确报 SKILL.md 因没进 git 历史而不可写、经验审计吐出上次真跑沉淀的 4 条 经验及原文;dry-run 正确报 no-new-material,没多花一次 LLM 调用。 浏览器里走了一遍列表→详情→dry-run,与直接打 API 结果一致。 955 个单测全绿。 文档:29-dream-design.md §5.7 记录实现与「为什么不做 graph 化」的 理由;28-followups.md / docs/README.md 同步更新 dream 现状。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
-
## ① system/tasklog/ 改名保留(§十二.5) 派单存储统一进 tasks 表后,旧目录改名为 system/tasklog.bak-unify/ 而不是 直接删 —— 跑顺一个版本再清。回填脚本改成**两处都认**(先原位置再备份位置, 顺序不能反:万一有人从备份恢复出一份新的 tasklog,那份才是要回填的)。 ## ② agent 提示词里的端口 3000 → 3010 packages/agent-core/base/ 下那几份是**所有 agent 的工具说明**,印着 curl http://localhost:3000/... 而实际端口是 3010。agent 照着打拿到的是 连接被拒、不是「端口错了」,它多半会去猜别的原因。 这不是假想:批 5 回填出来的历史派单正文里,有人手写了「(端口 3010, 不是 3000)」在纠正它 —— 靠人肉在每条任务书里打补丁。 守卫 agent-docs-port.test.js:扫 base 模板与 agent 本体,禁 localhost:3000。 判据刻意宽松(不校验「必须等于 config.port」—— 端口可被 PORT 覆盖,写死会在 部署环境误报)。带「样本非空」前置断言。第一版扫出 5 条误报,全是某 agent 目录下 Python venv 里 playwright 的 ThirdPartyNotices —— 那是第三方文件不是 提示词,已把 venv/site-packages/workspace/conversations/train 一并跳过。 ## ③ 边界守卫不再把并行会话的改动算到 agent 头上(§15.5 第 1 条) 守卫的前提是「窗口内新增的仓库改动 = 这个节点写的」。多会话共用 worktree 时 这个前提是假的 —— git status 的差集里没有「谁写的」这一维。实测已发生: 批 3 的开发改动把一条 run 判成 boundary_escape 卡住。 可检测的信号:节点**开跑那一刻**仓库就已经有源码目录的未提交改动 → 有人正在 这个 worktree 上开发 → 窗口内新增的源码改动无法归属 → **降级为只记证据、 不判罚**(与既有的 unattributable 桶同一语义),而不是关掉守卫:证据照留, 人一眼看得出「这次没判罚,因为当时仓库是脏的」。 BOUNDARY_GUARD=strict 强制判罚(CI / 干净环境),=record 一律只记。 守卫 8 例,含关键对照:仓库脏时不判、干净时同样一处越界仍判 fail —— 守卫没被削弱,只是不再乱扣帽子。 ## ④ TaskTimeline 的标签不再撒谎 conversationCount 在批 5 之后只会是 0/1(「有没有起过执行会话」),而界面 写的是 "Convs: N" —— 读的人会以为这条任务只有一次对话。改成有/无的表达。 agents 的语义变化在注释里说明。 注:innovator-hunt/todo.md 与 reddit-scraper/todo.md 里的端口也改了, 但它们是 gitignored 的运行态文件,不入库。 全量:134 文件 / 1268 测试全绿,web:build 通过。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
-
真在浏览器上看了才发现的:320px 减去 padding 只剩 292px 可用,而两个节点 加一条边就要 380px+ —— 实际效果是节点名被从中间切断(`org-cod…`)、必须 横向滚才看得全。而它是**全局导航**,是这个页面上最不该被压的一栏。 同一时刻中间那栏(work graph)拿着 600px 却经常是空的:选中的 task 只要 还没跑过 work graph,那栏就是一句空态提示。 改成 `280px minmax(380px, 0.9fr) minmax(0, 1.1fr) 360px`:org graph 保底 380px,两张画布按 0.9 : 1.1 分剩余空间,屏幕越宽一起长。work graph 仍拿 多数 —— 它才是下钻之后的主画布。rail 态(≤1400px 最左栏收成图标条)同步。 改后实测:org-planner → org-coder 两个节点完整显示,边上的 prd 标签与回退 边的 0/3 熔断徽标都看得见,控制条从两行挤成一行。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
-
## 批 4:解除「一家公司同时只有一个 active root run」 - DROP idx_graph_runs_one_active_root(建表块删掉 + 迁移块显式 DROP, CREATE ... IF NOT EXISTS 对存量库不生效) - max_parallel_tasks 从 company.json 调用时读(不是模块加载时快照, 否则改配置要重启才生效且不报错);缺省 1、非法回落 1 并留声、硬上限 20 - 准入判据「从活跃集外进入活跃集」按额度计数 **生死线用 8 个真进程并发抢坑实测**(不是纸面论证):max=1/2/3/5 四组, 每组恰好抢到 max 个、库里活跃数 = max,落选进程各吃 40 次 RunSlotBusyError, 零 other 错误。唯一索引没了之后,准入事务是真守卫。 ## 批 3 对抗复核的四条遗留 - N1 🔴 cancelQueuedRun 的前提被批 3 自己的 requeueBlockedRootRun 改没了: queued 的 run 现在可以是跑过一半的。探针实测取消后子 run 残留 paused/stop_requested=0 —— 那条 running 的子 run 继续调 agent 烧 token。 改为无条件走 engine.stopRun - L1 ⚠️ 恢复路径对 manual 只兑现一半:注释写着「T1 永远回不来这个后果因此 消失」,但调度器故意不碰 manual → 退回队列后没人接。定案:「manual 不自动 出队」约束的是首次起跑的授权,已被人亲手起过的不算新授权。 判据用 attempt>0 而不是 started_at —— 后者会被 requeue 清成 NULL, 恰好在需要它的那一刻是空的(实测踩过) - N2 🟠「退回队列」对用户完全不可见:200 + ok:true + 按钮全灰 + 图消失 + 一句「节点状态一律 pending」的假提示。加 notice 明说 - N3 🟠 isSlotRaceError 把 InvalidTransitionError 当竞态吞掉,而探针证伪了 那条注释:被推走时条件更新返回 0 不抛,真正抛它的只有「调用方声明了非法 转换」。删掉该分支,让它吵起来 四条里三条是同一个模式:注释承诺的前提被本轮自己的改动改没了,注释还留着。 文档 §十三.3 / §十四.4 已就这个模式立过两次案,这是第三、四、五次。 ## 文档 §十五 实施记录,含 §15.4 一次流程事故:87b776c 由第三方会话在对抗复核完成 前误提,提交说明的「1201 用例全绿」压在一份尚未通过复核的代码上。 全量:128 文件 / 1240 测试全绿,web:build 通过。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
-
队列与调度(§五.1): - 下达任务不再因「公司已有 active root run」吃 409,改为建 queued run + queued task 排队;出队走 updateRunStatus 的 CAS + BEGIN IMMEDIATE 准入事务,触发挂在 transitionRun 这个唯一漏斗上,60s sweep 兜底。 - 依赖已不可能满足的排队任务就地推导成 blocked,且每次 tick 重算不落死,上游重跑 成功后能原路解开。 恢复路径(批 3 复核 F1/F2):三条恢复转换的 from 全在活跃集之外,撞坑满时退回队列而不是 永远躺在 blocked;退不回去的裸 run 干净拒绝,不留半截状态。 控制台三处可见性修复 —— 症状全是纯视觉、不报错、git status 干净: 1. 顶栏墙钟恒 `now - started_at`,没有终点。一条昨晚就 blocked 的 run 第二天还在涨到 「16h 58min」,而那是页面上唯一的时间信号 —— 「已经停了等人处置」被显示成了 「还在跑再等等」,这两件事需要的动作相反。终点改取 run.updated_at,并标「已停」。 2. 越界日志只报「留下 N 处改动」,文件清单躺在 timeline.ndjson 里翻不到。判罚就是按 这份清单下的,而人要判断「这真是 agent 写的吗」唯一的依据就是路径(并行的开发会话 改宿主仓库会被算到当时在跑的节点头上,那是误判,看一眼就知道)。改为列出 entries。 3. run 收敛成 blocked 时只把 task 写成 blocked、不写原因,任务列表那一行只剩一个橙色 的「挂起」,必须逐层点进 org 图 → work 图 → 节点详情才知道发生了什么。改为把 「卡在哪个节点 + 什么 fail_reason + 判词摘要」写进 tasks.blocked_reason。 原因绝不带 UPSTREAM_BLOCK_PREFIX —— 带了会被调度器当成依赖推导出的 blocked 自动 推回 queued,而这类 blocked 本来就等着人处置(守卫在 task-blocked-reason.test.js)。 测试:126 文件 / 1201 用例全绿。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
-
goal 从「人写一句话」变成「agent 拆成一张 task DAG、人确认后下达」, 收敛时由 org-validator 在 goal 层再验一次。 ## 交付 - task-planning 本体(毕业判据实测通过:agent.md + soul.md 一次写完、 不带 _base 哨兵、路径按 cwd 深度 3 实算核对) - 拆分链路:3 轮 errors 原样回喂 → 不过就降级成 1 条同描述 task, goal 绝不卡在 planning;token 记 goals.planning_tokens - goal 级 org-validator 验收:≥2 条 task 全 completed 才调,单 task 跳过, 有 failed 直接 failed 不调(省一次真花钱的调用) - 控制台四栏 + GoalTaskPane 两级树 + 两步表单(拆分中给逃生口) - 中断恢复:启动时补跑验收与汇报 ## 三条跨进程竞态,全部用真探针实跑复现后才判定修好 1. runGoalSplit 无条件把 goal 写回 draft,抹掉逃生口刚下达的 active —— 而触发它的正是表单自己推荐的那个逃生口。后果是一个正在烧钱的 goal 显示成「草稿」,且从此永不收敛(OKR 不写、上级不唤醒、无日志) 2. CAS 把最终态提前写进库,验收窗口内重启即丢,且 verdict=NULL 与 「单 task 合法跳过」形态相同,查不出来 → 占位判词 + 启动重推 3. 认领语句 SET verdict=RESUMING WHERE verdict IN (PENDING, RESUMING) —— 写进去的值就在 WHERE 里,changes 恒为 1。而函数注释白纸黑字 写着「认领写的是另一个占位值」。注释是对的,代码没兑现。 实测:org-validator 调 2 次 / OKR 写 2 条 / token 双记 → claim 带唯一 token + 时间戳租约,回写条件更新决胜,主路径同闸门 ## 与设计正文的偏离(已记进文档 §十四) 拆分后 goal 落 draft 而非 active(active 的语义已被 markGoalDispatched 钉死为「已下达」);mode 三级继承对显式 tasks[] 同样生效;占位判词不出 API;新增 planning_error / reported_at 两列,后者迁移带回填(否则升级后 第一次启动会把历史终态 goal 全部重报一遍)。 ## 过程 四轮 26 个 agent,复核报出 22 条缺陷,其中 6 条是修复自己引入的。 第四轮修完挂了 3 条测试,全是测试在钉住缺陷本身 —— 其中一条的注释 写着「模拟进程死后重启」,构造的却是新鲜占位值:注释描述 A 场景、 代码构造 B 场景、断言按 A 场景写。 全量:118 文件 / 1112 测试全绿,web:build 通过。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
-
控制台原先只能看引擎的事件轨(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>
-
用错了变量:写的是 `background: var(--bg-surface, #fff)`,而 `--bg-surface` 不是「不透明表面」,它是 2% 的叠加色调(App.vue 里深色 rgba(255,255,255,0.02)、 浅色 rgba(0,0,0,0.02))。于是弹框几乎全透,把身后 42% 的遮罩透了出来; 文字色 var(--text-primary) 又跟着主题走成近白色,叠起来就是一片看不清。 以为兜底的 #fff 永远轮不到 —— 变量确实存在,只是语义不是我以为的那个。 该看的是周围的约定:**整个控制台是硬编码浅色的** (GraphConsole.vue:363 `.console { background:#fff; color:#1a1a1a }`, NodeDetailPanel.vue 里 CSS 变量用了 0 次)。我的弹框是唯一读主题变量的组件, 所以只有它坏。改成与邻居一致的硬编码浅色,遮罩阴影 .18 → .28 让边界更清楚。 在运行中的实例上用真实产物核过(mycompany 的 brd.md):白底、标题/表格/列表 渲染正常。871 全绿(纯样式改动,无逻辑变更)。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> -
## 为什么加 「输入输出」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> -
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> -
测试 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>
-
测试 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>
-
## W14 · POST /:cid/tasks 补上前端调用方 该端点此前**全仓库没有任何前端调用方** —— 界面走查确认过:用户在控制台上 没有任何办法给公司下达任务,只能 curl。这是「实现在、零调用方」在 P3 里的 第 N 次出现(W8 刚给 replan 补上,tasks 还欠着)。 新增 DispatchTaskButton.vue: - 手动/自动**在下达时就选**,不是跑起来再改 —— execution_mode 是 run 的属性, 起 run 时定;后补一个下拉会让人以为能中途换 - 自动模式在这一步就给成本预警(真 CLI 一次中等任务 $10-20 / 1-2 小时), GraphRunPane 的「启动」那道确认是针对已有 run 的,下达是更早的闸门 - 409(v1 并发约束)连同服务端 hint 原样透出,比翻译成「出错了」有用 - run 在跑时按钮直接不出现,而不是让人点了吃 409 挂载:有图未跑 run 时给主操作位(这时候它是页面唯一该做的事), 有 run 时收进标题栏。顺手修 .left-actions 缺 gap 导致两个按钮重叠。 ## 第二次真 CLI 冒烟(worktree 隔离,主仓库 data/ 零写入) 链路比上一轮走得远得多,两个硬缺陷的修复都在真跑中直接验证: 1. **R-18.1 端到端成立**:POST /create 自动触发 orggraph-planning 真跑, 它自己把图落库(3 节点 4 边,含两条 backward fail 边)**并自己雇了 org-planner / org-coder / org-validator** —— Step 7 的招聘真执行了。 2. **W5/W6 自雇再次验证,且规划质量明显更高**:workgraph-planning 规划出 brd → prd → benchmark → validator 四节点主干,两条回退边一条带 fail_reason=input_underspecified、一条无 reason 作兜底 —— 它自己用上了 D.2.2 的细分路由并满足 fail-fallback-edge 规则。四个本体全部自雇。 3. **守卫归属修复直接命中**:timeline 里 「本次调用窗口内有 1 处 agent 运行态文件改动,无法归属到本节点,不计入越界 unattributable = ['M system/agents/agent-creator/.claude/scheduled_tasks.json']」 ——正是上一轮把成功节点判成 fail 的那个文件,这次被正确摘出, org-planner-brd → **success**(上一轮它跑起来了但被误判)。 4. tokens 真的在计量(24,218),mock 下恒为 0。 产出位置正确:brd.md 落在 runs/<rootRunId>/nodes/<nodeRunId>/, 项目根 projects/p-4hjo36 按 root run 分配。worktree 源码跑完已复原到跑前状态。 测试 760/760 全绿(72 文件)。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> -
真 CLI 冒烟(worktree 隔离,PORT 3010)九轮来首次把 work 节点跑起来: orggraph-planning 真跑 3 分钟自主裁成单 org node 零边图 → workgraph-planning 第 1 轮产出合法图 → **自己按 Step 6 把 brd-planning / planner-validator 雇进公司** (W5/W6 核心验收成立,且 W7 判据生效、两者都没写进 org_graph_nodes)→ 第一个 work 节点 brd-planning 真起 CLI 子进程,产出 brd.md / contract.json / attempt-0.json 全部落在 run 目录内。图内预算仅 2 次调用 / 13998 tokens(上限 15)。 W5/W6 · workgraph-planning 自雇:撤掉「你不招人也不辞人」,Step 6 加招聘环节, 写死「先雇、再提交图」的顺序,给出失败分级与幂等要求 W7 · work 层招聘不写 org_graph_nodes:按 type 与 work graph 骨架点名两条判据区分层级, P2 「hire org 角色 → org graph 出现该节点」的语义保持不变 W8 · 控制台「规划组织结构」按钮(抽成 ReplanOrgGraphButton.vue,带过渡态与秒表), 接 POST /:companyId/org-graph/replan(该端点此前无任何前端调用方); 新增 fixtures/devloop-mock/orggraph-planning.json W12 · 节点失败留痕:node.output 带 output_excerpt、新增 node.error 事件。 真跑当场救场——守卫误判那条归因正是靠它才查得出来, 换成之前看到的只有「节点 fail、无原因、无痕迹」 测试 753/753 全绿(71 文件,起点 736/68)。 已知未修(下一轮):子 run 转终态不回写父 node_run 导致任务永久卡死; applyBoundaryGuard 用整仓 git diff 判归属会把并行 agent 的写算到当前节点头上; W12 的 fail_reason 未落库(概览 tab 仍为空);POST /:cid/tasks 无前端调用方。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
-
界面走查(真 server + HTTP + 真 AgentManager,底层 mock 协议)已把 manual 模式全流程点通:org 节点起子图 → 下钻 work graph → 单步 → 第 3 步触发回退(徽标 0/3→1/3、run 级 1/5 双层计数同时可见)→ 归因记录与 remediation_note 完整送达 → 收敛后父节点结算、左栏前移。 mock 侧补两处缺失接线(都是「四环漏一环」,非新功能) - ClaudeCliProtocol._sendMockPrompt 按 agent 类型查 fixture:图执行侧的 agent 名带公司前缀(resolveAgentName 返回 {cid}:{id}),fixture 不该 按公司复制一份。DevLoop 侧名字不含冒号,行为不变 - graph-engine.defaultAgentExecutor 接住 mock/files 事件:此前只有 DevLoop 的 loop-controller 接,图执行侧没接,于是 mock 下 readNodeProduct 永远找不到 attempt-{n}.json,validator 系节点恒 blocked。 写入限制在节点产物目录内,越界丢弃并留痕 新增 5 份 P3 fixture(workgraph-planning / prd-planning / sdd-planning / coder-validator / validator-triager),coder-validator 用 sequence 演示 一次回退。图必须放 chunks 不能只放 result——callAgent 的 output 来自 流式 chunks,session/result 的文本不进 output。 控制台入口:CompanyDashboard 加「🕸️ 执行控制台」按钮(路由早已注册, 此前无任何入口,只能手敲 URL)。 scripts/p3-demo-setup.js:手工测试准备脚本(雇人+写图+起 run),头部 写明它是两个已知缺口的绕行,缺口修好后第 1 步即可删。 _registry.json 补齐 6 条本体登记(brd/prd/sdd-planning、goal-benchmark、 benchmark/testdata-validator)——同轮新增的 template-refs 防线测试正是 因为缺这 6 条而变红,防线抓到了「交付与报告不符」。 文档 - 27-REQ R-18:招聘归 workgraph-planning(方案 a)、停用 mock 改真 CLI - 27-P3B 真实运行前置(新增):11 个工作项分三批,含现状核实 10 条证据 (seedTemplates 无运行时调用方、10 条悬空 company_hired 行、 FAILING_VERDICTS 不含 "fail" 导致验收假放行等) 测试 719/719 全绿(66 文件)。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> -
按 27-p3-p4-work-graph-console.md v0.3 实现,三轮开发+校验后测试全绿 (693/693,P2 26/26、DevLoop 230/230 未被打破,web:build 干净)。 服务层 - work-graph-db.js 定义层(work_graph_*) + 运行时层(graph_runs/graph_run_nodes/ graph_run_loop_records) 五张表;状态转换表照 unit-registry 模式; 复用 org-graph-service 四项校验 + 新增第五项 fail-fallback-edge - graph-engine.js advanceOne 幂等原语;agent_call / subgraph 两种 executor; 手动与自动共用同一原语;双层熔断(单边 loop_count + 按 root_run_id 共池的 backward_count);任务级预算挂 root run;三层暂停/停止级联; 重试按 contract_hash unchanged 判据 - graph-run-sse.js 按 run_id 广播,状态转换+timeline+SSE 三者原子化 - goal-contract.js 验收契约格式与执行器(R-15.6 验收先行) 引擎泛化(R-15.4):org graph 与 work graph 走同一套 advanceOne, 定义层两套、运行时层一套,三层下钻用 parent_run_id 链。 R-16 失败归因:validator-reflection 专职归因节点;fail_reason 多边路由 (精确匹配→兜底边→死路);子 run 收敛透传 success/fail + fail_reason + remediation_note 三值;infra_flake 不烧任何计数。 路由 / 前端 / 模板 - routes/graph-run.js 八个端点;company.js 任务入口 - WorkGraphCanvas.vue 扩展 UnitsDAG(subgraph 角标、backward 虚线曲线、 N/3 循环徽标、异常流程视觉降权);GraphConsole.vue 三栏 + 四 tab 详情 - 五个新 agent 模板:validator-reflection / org-validator / planner-validator / coder-validator / workgraph-planning 命名统一(R-15.3 + NAMING §三):org 节点改为 org-{职能}, 三个 org-graph 模板末节点收敛到 org-validator;org_graph_edges 加 fail_reason 列并把唯一索引补上该列(避免精确边与兜底边互相吞掉)。 三轮校验修掉的主要缺陷:subgraph 重跑不起新子 run 导致永久死锁; SSE/timeline 零调用方;org 侧 fail_reason 写入被丢弃;fail-fallback-edge 只做了一层;backward_count 跨 run 树重置架空熔断外层; seedEntryNodes 误点亮只有 fail 入边的节点。 已知缺口见后续提交:remediation_note 仅在 backward 边落库、 total_tokens 预算维度无生产方。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
-
■ 前端路由遮蔽(既有 bug,被 P2 的 T8 暴露) web/src/router/index.js: CompanyAgentChat 的 path 是 /:id/agents/:agentId? —— agentId 可选,导致它抢占 /:id/agents,而导航栏「Team」正是指向这个地址。 结果 CompanyDashboard 的团队 tab(连同 P2 新增的 Org Graph 区块)完全不可达, 点 Team 打开的一直是 AgentChat。改为 :agentId 必填。 ■ Org Graph 空边时凭空画出流程链(P2 T8 的 bug) 0 条 forward 边时所有节点入度都是 0,全部落进拓扑序且 degraded=false, 模板照常在节点间渲染 → 箭头,凭空捏造出一条并不存在的流转顺序。 P0 迁移进来的存量公司(有节点无边)全部中招。新增 orgGraphNoEdges 判定: 这种情况只平铺、不画箭头,并明确提示「尚未定义流转顺序」。 ■ create() 仍在建 goals/ 目录(我在 OKR 正名 74ea1ff 里的遗漏) company-service.js 的 subdirs 列表没跟着 goals→okr 一起改,新建公司仍然产出 goals/ 空目录。改为 okr/。 ■ test-company-e2e.js 从「第 4 个断言就崩」修到 114/114 全绿 该脚本长期整体失效,HEAD 版本同样全断言失败。四类问题: 1. 路径过期:data/{id}/ → data/companies/{id}/;data/templates/ → system/templates/;data/agents/master → system/agents/master 2. Phase 4 用 ceo 模板测「骨架是否完整复制」——但 system/templates/ceo/ 只有 plan.md + todo.md,没有 agent.md/soul.md/config.json,测不出东西,且缺 config.json 就没有 type='cxo',company.ceo 也标不上。改用同为 cxo 且文件 完整的 coo。(ceo 模板本身的残缺是另一个待补的问题) 3. 断言停留在 P1 之前:P1 虚拟引用(226489d)之后 agent.md 不再物理拷贝, 改为 config.json.systemAgentRef + company-context.md。按新模型改写 4. 断言停留在 T5b 之前:模板不存在时返回 hired_from_base 而非 training 顺带记录一个设计缺口(未改,仅在测试注释里说明):system/templates/base/ 目前只有 agent.md + config.json,所以 base 继承出的骨架不含 soul.md / identity.md / tools.md / todo.md,也不含 AgentScaffold 的标准子目录结构 (memory/short-term、knowledge、workspace 等)。这些依赖 agent-creator 填充。 另删除 OKR 迁移的 *.bak-goals 备份目录及其 .gitignore 规则。 验证:npm run test:unit 676 全绿;test-company-e2e 114/114;web:build 通过; Chrome 实机确认 Team tab 可达、空边提示正确。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
-
用 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>
-
代码层: - 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>
-
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> -
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> -
…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>