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>/…示意写法一律不判。 宽一档就会把这些一起判红,防线于是被人整条注释掉。
改动它们之前先想清楚:这几条的价值全在于「红了才有用」。 放宽判据、加例外、或者把断言改成永远成立的形式,等于把守卫拆掉 —— 本仓库有过「隔离是表演」的测试污染前科。