28-followups.md 9.92 KB

28-followups. Agent 注册表统一 —— 未完成与未确认项

所属: 28 Agent 注册表统一 的遗留清单。 主文档已随实施完成移入 docs/done/;这份留在 docs/todo/,装的是还没做完和还没定的部分。 日期: 2026-08-18


为什么单独拆一份

28 号的主体(G1-G4:本体归位 system/agents/、运行态按公司/任务下沉、公司只存引用、缺则创建) 已实施并通过真 Claude CLI E2E,主文档因此移入 done/。

但整份搬走会让下面这些从待办视野里消失 —— 那种丢失是静默的, 正好是 28 号自己反复在治的那类故障。所以拆成两份:已完成的作为记录归档,未完成的留在这里。


一、未确认(方向已定、设计未定 —— 别当成拍板过的去实现)

1.1 dream 机制

状态:用户口述过要解决什么问题和大致形态,设计尚未开始。

已知的只有这些:

  • 定时跑的另一套逻辑,整理所有 agent 在所有公司的 history 与 memory,显式提炼后沉淀到 skill memory
  • 它是「经验汇流」的正解;错误版本是「每轮对话自动把原文写进共享 SKILL.md」—— 方向对,但直接灌原始对话等于把 A 公司的机密喂给 B 公司 (这个错误版本真的发生过,是 28 号第一轮验收挖出的最严重 blocker,已修)

尚未设计的:触发时机与频率、提炼规则、去敏策略(什么算「不含公司特定信息」)、 产物落点与格式、失败与重试、提炼质量怎么评。

已经生效的约束(这条是确定的,不随 dream 的设计变化): 公司态目录(system/agents/{aid}/{cid}/ 下的 memory/ / conversations/)是 dream 的唯一原料, 不许被任何清理策略当垃圾扫掉。详见主文档 §5.6b。

真要做时另开设计文档。不要照主文档 §5.6b 的措辞去写代码 —— 那一节的目的是锁住上面那条约束,不是给 dream 下定义。


二、已实施但未验证

来自主文档 §11.4。「已实施」不等于「已验完」,这几项目前只有单测、没有真跑证据:

项 现状 缺什么
新建本体路径(验收 6 前半) 只有单测 E2E 那轮四个角色全部命中已有本体,hireFromBaseMetaAgent → agent-creator → draft→active 这条链没有真 CLI 证据
完整 work graph 真跑(验收 9) 未做 E2E 停在 org 层;交付物归宿(§4.4 按链路分叉)没被大批量产物压过
边界守卫接住新建本体 只有注入式单测 E2E 的 864→864 零增量只证明了运行态没漏进 git,没替「新建本体不被冤杀」作证(那一轮根本没触发新建)。见 §11.3

三、未实施的工程项

3.1 source_kind / legacy_path 删列(验收 2 后半)

两列仍在库里。origin 已经是真相来源,legacy_path 的所有读取点都已改成从 id 推导路径 (唯二的例外是 graph-seed-service 里判断「悬空行」时拿它做存在性检查,那是正当用法)。 删列前需确认没有遗漏的读取方。

3.2 任务态清理策略(§5.6)

只有设计,没有实现。任务目录 system/agents/{aid}/{cid}/{taskId}/ 在源码树里无限增长。 实现前必须先读主文档 §5.6b —— 公司态是 dream 的原料,不是缓存,只能清任务态。

3.3 agent-chat.js:1209 是心跳落点 bug 的第二个入口

PUT /api/agent/:agentId/heartbeat(前端 Agent 详情页走的就是它)用 locator.bodyDir(parseAddress(id).agentId) 把公司段丢掉,然后整份重写本体 config.json。

心跳落点已在 company.js 侧修好(写 system/agents/{aid}/{cid}/heartbeat.json), 但这个入口没改,且因此多了一个新问题:公司态文件一旦存在,读侧优先取公司态, 于是这个端点的写入对该 (agent, 公司) 变成静默无效。

修法(一行级):那段读改写换成 require('../services/heartbeat-service').writeHeartbeatConfig(req.params.agentId, req.body), GET :1203 的 heartbeat: config?.heartbeat 同理换成 readHeartbeatConfig。


四、验证口径提醒

主文档 §11.3 留了一段「文档与代码一致性检查」的 shell(常量改名同步)。 路径已随文档迁移更新完毕(cd2066a),实跑三份文档全部命中当前常量名 AGENT_REGISTRY_TREE_RE。

取值域不要抄进文档。毕业闸门的权威判据只有三处:

  • server/services/agent-locator.js 的 missingBodyDefinition()
  • server/services/graph-service.js 的 promoteDraftIfComplete()
  • server/services/graph-service.js 的 isUngraduatedStatus()(只挡 draft / training)

文档里复述取值域会产生第二份真相 —— 这个坑本仓库踩过: 一度按「≠ active 就挡」判,把库里 36 个 enabled/idle/ready/trained 的本体全判成「没填完」, workgraph-planning / master / qa-judge 全部雇不进来。


五、已经立起来的守卫(别重复造,也别删)

这几条是沉淀下来的不变式测试(前三条出自 agent 目录重构那一轮,第四条出自 27-P3C 批 1,第五、六条出自 CLI 进程泄漏那一轮)。 前四条守的都是「同一个事实只能有一处持有者」,而且六条都带「样本非空」前置断言 —— 少了那一条,扫描路径一写错就会以 「零违例」的形式全绿,一条永远通过的不变式测试和没有这条测试是一回事。

后两条形状不同:第五条守的是资源生命周期(每一条创建路径都要有对应的回收路径),判据不靠扫源码,而是把回收行为当黑盒去探; 第六条守的是指路牌的有效性(文档 / 清单里指向的东西必须真实存在)。列在一起是因为「别重复造,也别删」这条规矩对它们同样适用。

测试 守什么 违例时
agents-root-single-holder.test.js 本体根路径只能由 locator.AGENTS_ROOT 持有 精确点名到文件行号
no-stale-templates-path.test.js 活定义里不许出现已死的 system/templates(判据:git 跟踪的才是定义,未跟踪的是运行态产物) 同上
repo-hygiene.test.js tracked 与 ignored 不重叠 列出越界文件
runs-root-single-holder.test.js 凡是把 process.env.GRAPH_RUNS_DIR 绑到标识符上的模块,那个标识符必须在代码里(不是注释里)真正被用到 精确点名到文件行号
agent-chat-session-reclaim.test.js agent-chat 的 sessions 池:一次性 convId 的调用跑完必须回收,可复用的(pipeline / 人类对话)必须留下并受 TTL 兜底;顺带锁 CALL_KINDS 的标注 探针 /chat/:convId/stop 报告会话还在
no-dead-path-references.test.js CLAUDE.md 的仓库路径与 npm run、package.json 的 scripts/bin/main、docs/README.md 的相对链接,四类引用都必须指向真实存在的东西 列出死引用的出处与目标

第四条的来历值得记:goal-contract.js 里曾有一行 const RUNS_ROOT = process.env.GRAPH_RUNS_DIR || null, 注释白纸黑字写着「与 graph-engine.RUNS_ROOT 是同一个变量(测试隔离在用)」,全文件却零引用。 一个声明了意图、代码却不兑现的常量,比没有这个常量更容易骗人 —— 读的人会据此认为该模块认这个覆盖。 它同时也是「实现在、零调用方」这个惯犯模式的又一例(本仓库有人数到第五次,有人写「十几次」)。

第五条的来历同样值得记:sessions 池存的是常驻的 claude CLI 子进程,回收路径只有四条 (协议 error / 子进程 disconnected / 手动删对话 / 手动 stop),「正常跑完」不在其中。 两路心跳又都用一次性 convId 打进来,于是每 5 分钟漏一个进程,实测单次运行 2h43m 攒到 82 个进程 / 17.5 GB RSS。它不报错、不写日志、git status 干净,唯一的症状是机器变慢 —— 和第四条一样属于「读代码读不出来、要去看运行态才发现」的一类。

回收策略的判据写在 agent-chat.js 的 CALL_KINDS 表里,只有一条:convId 会不会被下一轮复用。 会复用的(pipeline 被驳回重做、人类多轮对话)留着走 TTL,不复用的跑完即回收。 不要按「是不是后台调用」来分 —— 五类后台调用里 pipeline 就是要留的那一类。

第六条是手工扫两轮之后固化下来的。手工那两轮扫出:package.json 里 12 处引用指向从未在 git 历史存在过的文件 (server/src/cli/auto-dev.js 一族,npm run example 跑起来就是 MODULE_NOT_FOUND);CLAUDE.md 的 docs/ 那行 列的 5 个文件名在文档移进 done/ 后全成死链;数据存储整节把两个不同的 data 根当成一个写。 三类共性是不报错、不影响测试、git status 干净,只有照着做的人(或 agent)撞墙才发现 —— 而 CLAUDE.md 是每个会话都会加载进上下文的文件,写错等于把错误信息直接灌给每一个后来者。

它的判据刻意窄:只认「以仓库顶层目录开头 + 含 /」的路径,且跳过 gitignored 的 (那是运行态产物,在干净机器上本来就不存在,缺席不是文档错误 —— 与第二条同一条判据)。 上下文里的相对片段(_base/、todo/future/)、glob、<repo>/… 示意写法一律不判。 宽一档就会把这些一起判红,防线于是被人整条注释掉。

改动它们之前先想清楚:这几条的价值全在于「红了才有用」。 放宽判据、加例外、或者把断言改成永远成立的形式,等于把守卫拆掉 —— 本仓库有过「隔离是表演」的测试污染前科。