Commit 0ad9af5975cd38c5fef1e5333c6d78fd93149551

Authored by yibo
1 parent 54470be3

refactor(db): 删掉 system_agents.source_kind;legacy_path 留下,写清退出条件

28-followups §3.1。**审计结论与那条待办原来的写法不一样,两列要分开处理。**

## source_kind:删

与 origin 一一映射(global_agent↔builtin / company_hired↔created),
实查生产库 76 行零例外。全仓唯一读侧是 migrate() 里那条 origin 回填,
而那是给「origin 列出现之前写下的行」用的一次性迁移。

顺序:**先回填、再删列**,两步都用 hasColumn 守着(幂等)。反过来做不报错 ——
库能开、测试能过,只是那几行 origin 悄悄是错的。守卫
source-kind-drop-migration.test.js 走存量库重启那条路,专门钉这个顺序,
反向验过:弄反立刻红。

写侧散在 3 处生产 INSERT + 35 个测试文件。批量改的时候正则误伤了 4 处
(吃掉了 JS 默认参数、位置占位符、ON CONFLICT 的赋值行),逐个修回并复查了 diff ——
**这种跨几十个文件的机械改动,靠「全量跑一遍」兜底比靠正则写得对更可靠**。

## legacy_path:不能删,它还没死

原待办说「所有读取点都已改成从 id 推导」——那半句对新行成立,对存量行不成立:
· 新行 legacy_path 恒等于 system/agents/{id}(确实冗余);
· **存量行里存着批次 4 之前的公司侧路径**,而 graph-seed-service 判「悬空行 / 孤儿行」
  靠的正是这个路径指向的目录还在不在。从 id 推导出来的路径永远指向 system/agents/,
  那条判据会整个失效。

实查生产库:76 行里还有 2 行是老路径(org-planner / org-coder 指着已不存在的
data/companies/mycompany/agents/)—— 正是这条判据要认出来的那种行。

退出条件写进了 graph-db.js 的建表注释(不只留在文档里):
库里不再有 legacy_path <> 'system/agents/' || id 的行。

## 顺带记一条:getDb() 会就地迁移生产库

删列那次,生产库的列是被一条**查状态用的**一行 node -e 删掉的 ——
服务当时没重启,人也没打算迁移。结果是对的(幂等;与备份逐行核对 76 行一致、
origin 全部按映射回填正确),但「读一下状态」不该顺手改 schema 这件事值得知道。
闸门只挡测试进程,故意不挡普通脚本(挡了服务自己也起不来)。
注释里给了只读的正确姿势。

全量:140 文件 / 1299 测试全绿。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Showing 40 changed files with 268 additions and 109 deletions
docs/todo/system-design/28-followups.md
@@ -54,10 +54,44 @@ @@ -54,10 +54,44 @@
54 54
55 ## 三、未实施的工程项 55 ## 三、未实施的工程项
56 56
57 -### 3.1 `source_kind` / `legacy_path` 删列(验收 2 后半)  
58 -两列仍在库里。`origin` 已经是真相来源,`legacy_path` 的所有**读**取点都已改成从 id 推导路径  
59 -(唯二的例外是 `graph-seed-service` 里判断「悬空行」时拿它做存在性检查,那是正当用法)。  
60 -删列前需确认没有遗漏的读取方。 57 +### 3.1 ~~`source_kind`~~ 已删 / `legacy_path` **不能删**(验收 2 后半)
  58 +
  59 +**审计结论与本条原来的写法不一样,两列要分开处理。**
  60 +
  61 +**`source_kind` ✅ 已删(2026-08-20)**:它与 `origin` 是一一映射
  62 +(`global_agent`↔`builtin` / `company_hired`↔`created`),实查生产库 76 行零例外。
  63 +全仓唯一读侧是 `graph-db.migrate()` 里那条 origin 回填,而那是给「`origin` 列出现之前写下的行」
  64 +用的一次性迁移。所以顺序是**先回填、再删列**,两步都用 `hasColumn` 守着(幂等)。
  65 +写侧散在 3 处生产 INSERT + 35 个测试文件,一并清掉。
  66 +守卫 `source-kind-drop-migration.test.js` 走存量库重启那条路,**专门钉住顺序** ——
  67 +反过来做不会报错,只是那几行 origin 悄悄是错的(反向验过,弄反立刻红)。
  68 +
  69 +**`legacy_path` ❌ 不能删,它还没死**。原文说「所有读取点都已改成从 id 推导」——
  70 +那半句对新行成立,对存量行不成立:
  71 +
  72 +- 新写入的行 `legacy_path` 恒等于 `system/agents/{id}`(= 可由 id 推导,确实冗余);
  73 +- **存量行里存着批次 4 之前的公司侧路径**(`data/companies/{cid}/agents/{role}`),
  74 + 而 `graph-seed-service` 判「悬空行 / 孤儿行」正是靠**这个路径指向的目录还在不在**。
  75 + 从 id 推导出来的路径永远指向 `system/agents/`,那条判据会整个失效。
  76 +
  77 +实查生产库(2026-08-20):76 行里**还有 2 行**是老路径(`org-planner` / `org-coder`
  78 +指着已经不存在的 `data/companies/mycompany/agents/`)—— 正是这条判据要认出来的那种行。
  79 +
  80 +> **删它的退出条件**(写在 `graph-db.js` 建表注释里,别只留在这份文档):
  81 +> 库里不再有 `legacy_path <> 'system/agents/' || id` 的行。
  82 +> 到那一天它才真的零信息量。
  83 +
  84 +### 3.1b 顺带记一条:`getDb()` 会就地迁移生产库
  85 +
  86 +`graph-db.getDb()` 内部调 `migrate()`。**任何 require 本模块并调 `getDb()` 的进程都会改生产库的 schema**,
  87 +包括一条随手敲的 `node -e "...getDb()..."`。
  88 +
  89 +删 `source_kind` 那次,生产库的列就是被一条**查状态用的**一行脚本删掉的 ——
  90 +服务当时根本没重启,人也没打算做迁移。结果是对的(迁移幂等,76 行逐行核对与备份一致、
  91 +origin 全部按映射回填正确),但「读一下状态」不该顺手改 schema 这件事本身值得知道。
  92 +
  93 +`assertNotProdDbInTests` 那道闸门**只挡测试进程**,故意不挡普通脚本(挡了服务自己也起不来)。
  94 +要只读地看一眼,用 `new Database('data/graph.sqlite', {readonly: true})`,别走 `getDb()`。
61 95
62 ### 3.2 任务态清理策略(§5.6) 96 ### 3.2 任务态清理策略(§5.6)
63 只有设计,没有实现。任务目录 `system/agents/{aid}/{cid}/{taskId}/` 在源码树里无限增长。 97 只有设计,没有实现。任务目录 `system/agents/{aid}/{cid}/{taskId}/` 在源码树里无限增长。
server/services/graph-db.js
@@ -143,10 +143,20 @@ function migrate(database) { @@ -143,10 +143,20 @@ function migrate(database) {
143 // 回填放在 if 外面,且写成「只改需要改的行」:ALTER 与 UPDATE 是两条语句, 143 // 回填放在 if 外面,且写成「只改需要改的行」:ALTER 与 UPDATE 是两条语句,
144 // 中间挂掉的话 company_hired 行会停在默认的 'builtin' 上,下次 migrate 必须能补上。 144 // 中间挂掉的话 company_hired 行会停在默认的 'builtin' 上,下次 migrate 必须能补上。
145 // WHERE 里的 origin <> 'created' 让第二遍变成零行更新(幂等)。 145 // WHERE 里的 origin <> 'created' 让第二遍变成零行更新(幂等)。
146 - database.exec(`  
147 - UPDATE system_agents SET origin = 'created'  
148 - WHERE source_kind = 'company_hired' AND origin <> 'created'  
149 - `); 146 + //
  147 + // **顺序不能反**:先把 source_kind 的信息回填进 origin,再删列。反过来做的话,
  148 + // 「origin 列出现之前写下的行」永远回不到正确的 origin —— 那正是这条回填存在的理由。
  149 + // 两步都用 hasColumn 守着,所以删过之后第二遍是零操作(幂等)。
  150 + if (hasColumn(database, 'system_agents', 'source_kind')) {
  151 + database.exec(`
  152 + UPDATE system_agents SET origin = 'created'
  153 + WHERE source_kind = 'company_hired' AND origin <> 'created'
  154 + `);
  155 + // 28-followups §3.1:source_kind 与 origin 一一映射,零信息量,读侧只有上面那一条回填。
  156 + // 删列会让所有还带着它的 INSERT 报错,所以这一步必须跑在**任何写入之前** ——
  157 + // migrate() 由 getDb() 在建表之后立刻调用,正是那个位置。
  158 + database.exec(`ALTER TABLE system_agents DROP COLUMN source_kind`);
  159 + }
150 160
151 // 7. 28 批次 4:company_agents 的存量回填。 161 // 7. 28 批次 4:company_agents 的存量回填。
152 backfillCompanyAgents(database); 162 backfillCompanyAgents(database);
@@ -229,6 +239,18 @@ function createEdgesTable(database) { @@ -229,6 +239,18 @@ function createEdgesTable(database) {
229 * 239 *
230 * 需要读真库的测试显式写 `process.env.GRAPH_DB_PATH = <真库路径>` 表明意图即可。 240 * 需要读真库的测试显式写 `process.env.GRAPH_DB_PATH = <真库路径>` 表明意图即可。
231 */ 241 */
  242 +/**
  243 + * ⚠️ **`getDb()` 会跑 `migrate()`** —— 也就是说,**任何 require 本模块并调 getDb() 的进程
  244 + * 都会就地修改生产库的 schema**,包括一条随手敲的 `node -e "...getDb()..."`。
  245 + *
  246 + * 这不是假想:2026-08-20 删 `source_kind` 那次,生产库的列是被一条**查状态用的**
  247 + * 一行脚本删掉的 —— 服务当时根本没重启,人也没打算迁移。结果是对的(迁移本身幂等、
  248 + * 76 行逐行核对无误),但「读一下状态」不该顺手改 schema 这件事本身值得知道。
  249 + *
  250 + * 下面这道闸门**只挡测试进程**(避免污染共享数据),故意不挡普通脚本 ——
  251 + * 挡了的话服务自己也起不来。要只读地看一眼,用
  252 + * `new (require('better-sqlite3'))('data/graph.sqlite', {readonly:true})`,别走 getDb()。
  253 + */
232 function assertNotProdDbInTests() { 254 function assertNotProdDbInTests() {
233 if (!process.env.VITEST) return; 255 if (!process.env.VITEST) return;
234 if (process.env.GRAPH_DB_PATH) return; 256 if (process.env.GRAPH_DB_PATH) return;
@@ -258,10 +280,19 @@ function getDb() { @@ -258,10 +280,19 @@ function getDb() {
258 name TEXT NOT NULL, 280 name TEXT NOT NULL,
259 scope TEXT NOT NULL CHECK (scope IN ('public','company')), 281 scope TEXT NOT NULL CHECK (scope IN ('public','company')),
260 owner_company_id TEXT, 282 owner_company_id TEXT,
261 - -- source_kind / legacy_path 是 28 批次 1 之后的**遗留列**:读侧已全部改走  
262 - -- origin + 「路径恒为 system/agents/{id}」的推导,这两列只为存量库和还没改完的  
263 - -- 批次(company_agents 表落地在批次 4)留一版兼容,写入侧仍按 origin 一一映射维护。  
264 - source_kind TEXT NOT NULL CHECK (source_kind IN ('global_agent','template','company_hired')), 283 + -- legacy_path 是 28 批次 1 之后的遗留列,但**还没死**(28-followups §3.1)。
  284 + -- 新写入的行它恒等于 system/agents/{id}(= 可由 id 推导,冗余),
  285 + -- 但存量行里存着批次 4 之前的公司侧路径(data/companies/{cid}/agents/{role}),
  286 + -- 而 graph-seed-service 判「悬空行 / 孤儿行」正是靠「这个路径指向的目录还在不在」。
  287 + -- 删它之前的退出条件:库里不再有 legacy_path <> 'system/agents/' || id 的行。
  288 + -- (2026-08-20 实查生产库:76 行里还有 2 行不满足 —— org-planner / org-coder
  289 + -- 指着已经不存在的 data/companies/mycompany/agents/。所以这一版还不能删。)
  290 + --
  291 + -- source_kind 已于 2026-08-20 删除:它与 origin 一一映射
  292 + -- (global_agent 对 builtin / company_hired 对 created),实查生产库 76 行零例外,
  293 + -- 唯一读侧是 migrate() 里那条 origin 回填,而那是给「origin 列出现之前的行」用的一次性迁移。
  294 + -- 注意:本段刻意不用反引号 —— 这里在 JS 模板字符串里面,反引号会把它提前截断
  295 + -- (踩过两次:一次在这张表,一次在 work-graph-db 的 DDL)。
265 legacy_path TEXT NOT NULL, 296 legacy_path TEXT NOT NULL,
266 status TEXT NOT NULL DEFAULT 'active', 297 status TEXT NOT NULL DEFAULT 'active',
267 benchmark_score REAL, 298 benchmark_score REAL,
server/services/graph-seed-service.js
@@ -372,7 +372,7 @@ function reconcileDanglingRows() { @@ -372,7 +372,7 @@ function reconcileDanglingRows() {
372 * 返回本体路径,等于在公司毫不知情的情况下换掉它的 agent.md。这个死角由批次 4/5 372 * 返回本体路径,等于在公司毫不知情的情况下换掉它的 agent.md。这个死角由批次 4/5
373 * (把私有拷贝毕业成本体)根治,不在这里硬来。 373 * (把私有拷贝毕业成本体)根治,不在这里硬来。
374 * - 那行是**悬空的**(isStaleCreatedRow)→ 就地转正成 builtin:scope / owner_company_id / 374 * - 那行是**悬空的**(isStaleCreatedRow)→ 就地转正成 builtin:scope / owner_company_id /
375 - * origin / source_kind / legacy_path 全部改写。原来的「一律 skip」让污染不可逆: 375 + * origin / legacy_path 全部改写。原来的「一律 skip」让污染不可逆:
376 * 公司目录删了、行还占着本体 id,本体永远补不回来。转正而不是删了重建, 376 * 公司目录删了、行还占着本体 id,本体永远补不回来。转正而不是删了重建,
377 * 是为了让还引着这行的 org_graph_nodes 外键继续有效。 377 * 是为了让还引着这行的 org_graph_nodes 外键继续有效。
378 * 378 *
@@ -404,17 +404,16 @@ function seedAgents() { @@ -404,17 +404,16 @@ function seedAgents() {
404 // status 也要取:ON CONFLICT 分支要拿它判「这行已经毕业了,别被 config.json 推回去」 404 // status 也要取:ON CONFLICT 分支要拿它判「这行已经毕业了,别被 config.json 推回去」
405 const findExisting = db.prepare(`SELECT id, origin, legacy_path, status FROM system_agents WHERE id = ?`); 405 const findExisting = db.prepare(`SELECT id, origin, legacy_path, status FROM system_agents WHERE id = ?`);
406 406
407 - // ON CONFLICT 分支必须连 scope/owner_company_id/origin/source_kind 一起改写,接管才算完成: 407 + // ON CONFLICT 分支必须连 scope/owner_company_id/origin 一起改写,接管才算完成:
408 // 只改 legacy_path 会留下一行「legacy_path 指向 system/agents/ 却仍标着 created」的半吊子记录。 408 // 只改 legacy_path 会留下一行「legacy_path 指向 system/agents/ 却仍标着 created」的半吊子记录。
409 // 对本来就是 builtin 的行这几列是原值写原值。 409 // 对本来就是 builtin 的行这几列是原值写原值。
410 const upsert = db.prepare(` 410 const upsert = db.prepare(`
411 - INSERT INTO system_agents (id, name, scope, owner_company_id, source_kind, origin, legacy_path, status, benchmark_score, created_at, updated_at)  
412 - VALUES (@id, @name, 'public', NULL, 'global_agent', 'builtin', @legacy_path, @status, @benchmark_score, @created_at, @updated_at) 411 + INSERT INTO system_agents (id, name, scope, owner_company_id, origin, legacy_path, status, benchmark_score, created_at, updated_at)
  412 + VALUES (@id, @name, 'public', NULL, 'builtin', @legacy_path, @status, @benchmark_score, @created_at, @updated_at)
413 ON CONFLICT(id) DO UPDATE SET 413 ON CONFLICT(id) DO UPDATE SET
414 name = excluded.name, 414 name = excluded.name,
415 scope = excluded.scope, 415 scope = excluded.scope,
416 owner_company_id = excluded.owner_company_id, 416 owner_company_id = excluded.owner_company_id,
417 - source_kind = excluded.source_kind,  
418 origin = excluded.origin, 417 origin = excluded.origin,
419 legacy_path = excluded.legacy_path, 418 legacy_path = excluded.legacy_path,
420 status = excluded.status, 419 status = excluded.status,
@@ -523,8 +522,8 @@ function seedCompanyAgents() { @@ -523,8 +522,8 @@ function seedCompanyAgents() {
523 522
524 const findAgent = db.prepare(`SELECT id FROM system_agents WHERE id = ?`); 523 const findAgent = db.prepare(`SELECT id FROM system_agents WHERE id = ?`);
525 const insertAgent = db.prepare(` 524 const insertAgent = db.prepare(`
526 - INSERT INTO system_agents (id, name, scope, owner_company_id, source_kind, origin, legacy_path, status, benchmark_score, created_at, updated_at)  
527 - VALUES (@id, @name, 'company', @owner_company_id, 'company_hired', 'created', @legacy_path, 'active', NULL, @created_at, @updated_at) 525 + INSERT INTO system_agents (id, name, scope, owner_company_id, origin, legacy_path, status, benchmark_score, created_at, updated_at)
  526 + VALUES (@id, @name, 'company', @owner_company_id, 'created', @legacy_path, 'active', NULL, @created_at, @updated_at)
528 ON CONFLICT(id) DO NOTHING 527 ON CONFLICT(id) DO NOTHING
529 `); 528 `);
530 const findNode = db.prepare(` 529 const findNode = db.prepare(`
server/services/graph-service.js
@@ -85,8 +85,8 @@ function ensureSystemAgentRow(companyId, systemAgentId, roleName, opts = {}) { @@ -85,8 +85,8 @@ function ensureSystemAgentRow(companyId, systemAgentId, roleName, opts = {}) {
85 85
86 const now = new Date().toISOString(); 86 const now = new Date().toISOString();
87 db.prepare(` 87 db.prepare(`
88 - INSERT INTO system_agents (id, name, scope, owner_company_id, source_kind, origin, legacy_path, status, benchmark_score, created_at, updated_at)  
89 - VALUES (@id, @name, 'company', @owner_company_id, 'company_hired', 'created', @legacy_path, @status, NULL, @created_at, @updated_at) 88 + INSERT INTO system_agents (id, name, scope, owner_company_id, origin, legacy_path, status, benchmark_score, created_at, updated_at)
  89 + VALUES (@id, @name, 'company', @owner_company_id, 'created', @legacy_path, @status, NULL, @created_at, @updated_at)
90 `).run({ 90 `).run({
91 id: systemAgentId, 91 id: systemAgentId,
92 name: systemAgentId, 92 name: systemAgentId,
server/tests/integration/company-create-mainline.test.js
@@ -85,8 +85,8 @@ describe(&#39;28 终验 · 「新建公司」主线(真本体)&#39;, () =&gt; { @@ -85,8 +85,8 @@ describe(&#39;28 终验 · 「新建公司」主线(真本体)&#39;, () =&gt; {
85 const now = new Date().toISOString(); 85 const now = new Date().toISOString();
86 const ins = db().prepare(` 86 const ins = db().prepare(`
87 INSERT OR REPLACE INTO system_agents 87 INSERT OR REPLACE INTO system_agents
88 - (id, name, scope, owner_company_id, source_kind, origin, legacy_path, status, created_at, updated_at)  
89 - VALUES (?, ?, 'public', NULL, 'global_agent', 'builtin', ?, ?, ?, ?) 88 + (id, name, scope, owner_company_id, origin, legacy_path, status, created_at, updated_at)
  89 + VALUES (?, ?, 'public', NULL, 'builtin', ?, ?, ?, ?)
90 `); 90 `);
91 for (const id of REAL_BODIES) { 91 for (const id of REAL_BODIES) {
92 const cfg = JSON.parse(fs.readFileSync(path.join(locator.bodyDir(id), 'config.json'), 'utf-8')); 92 const cfg = JSON.parse(fs.readFileSync(path.join(locator.bodyDir(id), 'config.json'), 'utf-8'));
server/tests/integration/graph-agent-p1-acceptance.test.js
@@ -80,8 +80,8 @@ describe(&#39;本体共享 + 公司态下沉 — 端到端验收(28 §八 4/5)&#39;, @@ -80,8 +80,8 @@ describe(&#39;本体共享 + 公司态下沉 — 端到端验收(28 §八 4/5)&#39;,
80 // 镜像生产种子(seedAgents:两个 id 都是 origin='builtin' 的本体行) 80 // 镜像生产种子(seedAgents:两个 id 都是 origin='builtin' 的本体行)
81 for (const id of ['qa-judge', COLLISION_ROLE]) { 81 for (const id of ['qa-judge', COLLISION_ROLE]) {
82 db.prepare(` 82 db.prepare(`
83 - INSERT INTO system_agents (id, name, scope, owner_company_id, source_kind, origin, legacy_path, status, benchmark_score, created_at, updated_at)  
84 - VALUES (?, ?, 'public', NULL, 'global_agent', 'builtin', ?, 'active', NULL, ?, ?) 83 + INSERT INTO system_agents (id, name, scope, owner_company_id, origin, legacy_path, status, benchmark_score, created_at, updated_at)
  84 + VALUES (?, ?, 'public', NULL, 'builtin', ?, 'active', NULL, ?, ?)
85 `).run(id, id, `system/agents/${id}`, now, now); 85 `).run(id, id, `system/agents/${id}`, now, now);
86 } 86 }
87 87
server/tests/integration/graph-agent-p1-hire.test.js
@@ -62,8 +62,8 @@ describe(&#39;company-service.hire() — 引用本体 + 公司态下沉&#39;, () =&gt; { @@ -62,8 +62,8 @@ describe(&#39;company-service.hire() — 引用本体 + 公司态下沉&#39;, () =&gt; {
62 const db = graphDb.getDb(); 62 const db = graphDb.getDb();
63 const now = new Date().toISOString(); 63 const now = new Date().toISOString();
64 db.prepare(` 64 db.prepare(`
65 - INSERT INTO system_agents (id, name, scope, owner_company_id, source_kind, origin, legacy_path, status, benchmark_score, created_at, updated_at)  
66 - VALUES (?, ?, 'public', NULL, 'global_agent', 'builtin', ?, 'active', NULL, ?, ?) 65 + INSERT INTO system_agents (id, name, scope, owner_company_id, origin, legacy_path, status, benchmark_score, created_at, updated_at)
  66 + VALUES (?, ?, 'public', NULL, 'builtin', ?, 'active', NULL, ?, ?)
67 `).run('qa-judge', 'qa-judge', 'system/agents/qa-judge', now, now); 67 `).run('qa-judge', 'qa-judge', 'system/agents/qa-judge', now, now);
68 68
69 // 固定 id 的代价:上一次跑要是崩在中途,公司目录会留在盘上,而 create() 撞见已存在的 69 // 固定 id 的代价:上一次跑要是崩在中途,公司目录会留在盘上,而 create() 撞见已存在的
server/tests/unit/agent-body-draft-gate.test.js
@@ -102,8 +102,8 @@ describe(&#39;新造本体的 draft 闸门(28 批次 5)&#39;, () =&gt; { @@ -102,8 +102,8 @@ describe(&#39;新造本体的 draft 闸门(28 批次 5)&#39;, () =&gt; {
102 const now = new Date().toISOString(); 102 const now = new Date().toISOString();
103 graphDb.getDb().prepare(` 103 graphDb.getDb().prepare(`
104 INSERT OR REPLACE INTO system_agents 104 INSERT OR REPLACE INTO system_agents
105 - (id, name, scope, owner_company_id, source_kind, origin, legacy_path, status, created_at, updated_at)  
106 - VALUES (?, ?, 'public', NULL, 'global_agent', 'builtin', ?, ?, ?, ?) 105 + (id, name, scope, owner_company_id, origin, legacy_path, status, created_at, updated_at)
  106 + VALUES (?, ?, 'public', NULL, 'builtin', ?, ?, ?, ?)
107 `).run(id, id, `system/agents/${id}`, cfg.status || 'active', now, now); 107 `).run(id, id, `system/agents/${id}`, cfg.status || 'active', now, now);
108 }; 108 };
109 109
@@ -216,8 +216,8 @@ describe(&#39;新造本体的 draft 闸门(28 批次 5)&#39;, () =&gt; { @@ -216,8 +216,8 @@ describe(&#39;新造本体的 draft 闸门(28 批次 5)&#39;, () =&gt; {
216 for (const status of ['enabled', 'idle', 'ready', 'trained', 'active']) { 216 for (const status of ['enabled', 'idle', 'ready', 'trained', 'active']) {
217 const id = `qa-judge`; 217 const id = `qa-judge`;
218 db.prepare(`INSERT OR REPLACE INTO system_agents 218 db.prepare(`INSERT OR REPLACE INTO system_agents
219 - (id,name,scope,owner_company_id,source_kind,origin,legacy_path,status,benchmark_score,created_at,updated_at)  
220 - VALUES (?,?,'public',NULL,'global_agent','builtin',?, ?,NULL,?,?)`) 219 + (id,name,scope,owner_company_id,origin,legacy_path,status,benchmark_score,created_at,updated_at)
  220 + VALUES (?,?,'public',NULL,'builtin',?, ?,NULL,?,?)`)
221 .run(id, id, `system/agents/${id}`, status, now, now); 221 .run(id, id, `system/agents/${id}`, status, now, now);
222 db.prepare(`DELETE FROM company_agents WHERE role_name = ?`).run(id); 222 db.prepare(`DELETE FROM company_agents WHERE role_name = ?`).run(id);
223 223
server/tests/unit/agent-transcript.test.js
@@ -98,8 +98,8 @@ describe(&#39;agent-transcript:会话原文读取&#39;, () =&gt; { @@ -98,8 +98,8 @@ describe(&#39;agent-transcript:会话原文读取&#39;, () =&gt; {
98 const now = new Date().toISOString(); 98 const now = new Date().toISOString();
99 db.prepare(` 99 db.prepare(`
100 INSERT OR IGNORE INTO system_agents 100 INSERT OR IGNORE INTO system_agents
101 - (id, name, scope, owner_company_id, source_kind, legacy_path, status, created_at, updated_at)  
102 - VALUES (?, ?, 'public', NULL, 'global_agent', ?, 'active', ?, ?) 101 + (id, name, scope, owner_company_id, legacy_path, status, created_at, updated_at)
  102 + VALUES (?, ?, 'public', NULL, ?, 'active', ?, ?)
103 `).run(ROLE, ROLE, `system/agents/${ROLE}`, now, now); 103 `).run(ROLE, ROLE, `system/agents/${ROLE}`, now, now);
104 104
105 // 雇佣关系:listRoles 的角色清单来自这张表(28 批次 4-19),没有这行就一个角色都列不出来 105 // 雇佣关系:listRoles 的角色清单来自这张表(28 批次 4-19),没有这行就一个角色都列不出来
server/tests/unit/base-graduation-gate.test.js
@@ -62,8 +62,8 @@ describe(&#39;_base 骨架不算定义已填满(哨兵闸门)&#39;, () =&gt; { @@ -62,8 +62,8 @@ describe(&#39;_base 骨架不算定义已填满(哨兵闸门)&#39;, () =&gt; {
62 const now = new Date().toISOString(); 62 const now = new Date().toISOString();
63 graphDb.getDb().prepare(` 63 graphDb.getDb().prepare(`
64 INSERT OR REPLACE INTO system_agents 64 INSERT OR REPLACE INTO system_agents
65 - (id, name, scope, owner_company_id, source_kind, origin, legacy_path, status, created_at, updated_at)  
66 - VALUES (?, ?, 'public', NULL, 'global_agent', 'builtin', ?, ?, ?, ?) 65 + (id, name, scope, owner_company_id, origin, legacy_path, status, created_at, updated_at)
  66 + VALUES (?, ?, 'public', NULL, 'builtin', ?, ?, ?, ?)
67 `).run(id, id, `system/agents/${id}`, cfg.status || 'active', now, now); 67 `).run(id, id, `system/agents/${id}`, cfg.status || 'active', now, now);
68 }; 68 };
69 const agentRow = (id) => 69 const agentRow = (id) =>
server/tests/unit/boundary-guard-new-body.test.js
@@ -88,8 +88,8 @@ describe(&#39;边界守卫 · 新建 agent 本体不判越界(28 §九)&#39;, () =&gt; @@ -88,8 +88,8 @@ describe(&#39;边界守卫 · 新建 agent 本体不判越界(28 §九)&#39;, () =&gt;
88 const ts = new Date().toISOString(); 88 const ts = new Date().toISOString();
89 wdb.getDb().prepare(` 89 wdb.getDb().prepare(`
90 INSERT OR IGNORE INTO system_agents 90 INSERT OR IGNORE INTO system_agents
91 - (id, name, scope, owner_company_id, source_kind, legacy_path, status, created_at, updated_at)  
92 - VALUES ('hr', 'hr', 'public', NULL, 'global_agent', 'system/agents/hr', 'active', ?, ?) 91 + (id, name, scope, owner_company_id, legacy_path, status, created_at, updated_at)
  92 + VALUES ('hr', 'hr', 'public', NULL, 'system/agents/hr', 'active', ?, ?)
93 `).run(ts, ts); 93 `).run(ts, ts);
94 const g = wdb.createWorkGraph({ companyId, nodes: ['hr'], edges: [] }); 94 const g = wdb.createWorkGraph({ companyId, nodes: ['hr'], edges: [] });
95 expect(g.errors || []).toEqual([]); 95 expect(g.errors || []).toEqual([]);
server/tests/unit/company-hire-org-layer.test.js
@@ -100,8 +100,8 @@ describe(&#39;hire() 的 org / work 分层(27-P3B W7)&#39;, () =&gt; { @@ -100,8 +100,8 @@ describe(&#39;hire() 的 org / work 分层(27-P3B W7)&#39;, () =&gt; {
100 const now = new Date().toISOString(); 100 const now = new Date().toISOString();
101 for (const id of TEMPLATES) { 101 for (const id of TEMPLATES) {
102 db.prepare(` 102 db.prepare(`
103 - INSERT INTO system_agents (id, name, scope, owner_company_id, source_kind, legacy_path, status, benchmark_score, created_at, updated_at)  
104 - VALUES (?, ?, 'public', NULL, 'template', ?, 'active', NULL, ?, ?) 103 + INSERT INTO system_agents (id, name, scope, owner_company_id, legacy_path, status, benchmark_score, created_at, updated_at)
  104 + VALUES (?, ?, 'public', NULL, ?, 'active', NULL, ?, ?)
105 `).run(id, id, `system/agents/${id}`, now, now); 105 `).run(id, id, `system/agents/${id}`, now, now);
106 } 106 }
107 107
server/tests/unit/goal-task-acceptance.test.js
@@ -180,8 +180,8 @@ describe(&#39;27-P3C 批 1 验收(§八 4 / 5 / 6 / 10 / 11 / 12b)&#39;, () =&gt; { @@ -180,8 +180,8 @@ describe(&#39;27-P3C 批 1 验收(§八 4 / 5 / 6 / 10 / 11 / 12b)&#39;, () =&gt; {
180 const now = new Date().toISOString(); 180 const now = new Date().toISOString();
181 db.prepare(` 181 db.prepare(`
182 INSERT OR IGNORE INTO system_agents 182 INSERT OR IGNORE INTO system_agents
183 - (id, name, scope, owner_company_id, source_kind, legacy_path, status, created_at, updated_at)  
184 - VALUES ('acc-worker', 'acc-worker', 'public', NULL, 'global_agent', 'system/agents/acc-worker', 'active', ?, ?) 183 + (id, name, scope, owner_company_id, legacy_path, status, created_at, updated_at)
  184 + VALUES ('acc-worker', 'acc-worker', 'public', NULL, 'system/agents/acc-worker', 'active', ?, ?)
185 `).run(now, now); 185 `).run(now, now);
186 const g = wdb.createWorkGraph({ companyId: cid, nodes: ['acc-worker'], edges: [] }); 186 const g = wdb.createWorkGraph({ companyId: cid, nodes: ['acc-worker'], edges: [] });
187 expect(g.errors || []).toEqual([]); 187 expect(g.errors || []).toEqual([]);
server/tests/unit/graph-boundary.test.js
@@ -60,8 +60,8 @@ describe(&#39;graph-engine 项目根与边界守卫&#39;, () =&gt; { @@ -60,8 +60,8 @@ describe(&#39;graph-engine 项目根与边界守卫&#39;, () =&gt; {
60 const ts = new Date().toISOString(); 60 const ts = new Date().toISOString();
61 const ins = db.prepare(` 61 const ins = db.prepare(`
62 INSERT OR IGNORE INTO system_agents 62 INSERT OR IGNORE INTO system_agents
63 - (id, name, scope, owner_company_id, source_kind, legacy_path, status, created_at, updated_at)  
64 - VALUES (?, ?, 'public', NULL, 'global_agent', ?, 'active', ?, ?) 63 + (id, name, scope, owner_company_id, legacy_path, status, created_at, updated_at)
  64 + VALUES (?, ?, 'public', NULL, ?, 'active', ?, ?)
65 `); 65 `);
66 for (const id of new Set(ids)) ins.run(id, id, `system/agents/${id}`, ts, ts); 66 for (const id of new Set(ids)) ins.run(id, id, `system/agents/${id}`, ts, ts);
67 } 67 }
server/tests/unit/graph-cross-layer-address.test.js
@@ -82,8 +82,8 @@ describe(&#39;R-17.1 跨层地址 · 两半的必填性(W10 事实核实)&#39;, () = @@ -82,8 +82,8 @@ describe(&#39;R-17.1 跨层地址 · 两半的必填性(W10 事实核实)&#39;, () =
82 const ts = new Date().toISOString(); 82 const ts = new Date().toISOString();
83 const ins = db.prepare(` 83 const ins = db.prepare(`
84 INSERT OR IGNORE INTO system_agents 84 INSERT OR IGNORE INTO system_agents
85 - (id, name, scope, owner_company_id, source_kind, legacy_path, status, created_at, updated_at)  
86 - VALUES (?, ?, 'public', NULL, 'global_agent', ?, 'active', ?, ?) 85 + (id, name, scope, owner_company_id, legacy_path, status, created_at, updated_at)
  86 + VALUES (?, ?, 'public', NULL, ?, 'active', ?, ?)
87 `); 87 `);
88 for (const id of new Set(ids)) ins.run(id, id, `system/agents/${id}`, ts, ts); 88 for (const id of new Set(ids)) ins.run(id, id, `system/agents/${id}`, ts, ts);
89 } 89 }
server/tests/unit/graph-db.test.js
@@ -40,9 +40,9 @@ describe(&#39;graph-db&#39;, () =&gt; { @@ -40,9 +40,9 @@ describe(&#39;graph-db&#39;, () =&gt; {
40 const d = graphDb.getDb(); 40 const d = graphDb.getDb();
41 const now = new Date().toISOString(); 41 const now = new Date().toISOString();
42 d.prepare(` 42 d.prepare(`
43 - INSERT INTO system_agents (id, name, scope, owner_company_id, source_kind, legacy_path, status, benchmark_score, created_at, updated_at)  
44 - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)  
45 - `).run('test-agent-env-check', 'Test Agent', 'public', null, 'global_agent', 'system/agents/test-agent-env-check', 'active', null, now, now); 43 + INSERT INTO system_agents (id, name, scope, owner_company_id, legacy_path, status, benchmark_score, created_at, updated_at)
  44 + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
  45 + `).run('test-agent-env-check', 'Test Agent', 'public', null, 'system/agents/test-agent-env-check', 'active', null, now, now);
46 46
47 // The row must have landed in the overridden file, not the default one. 47 // The row must have landed in the overridden file, not the default one.
48 expect(fs.existsSync(TEST_DB_PATH)).toBe(true); 48 expect(fs.existsSync(TEST_DB_PATH)).toBe(true);
server/tests/unit/graph-edge-contract-role.test.js
@@ -95,8 +95,8 @@ describe(&#39;R-19.A2 契约 role 落库 + PATCH 通道的 data-contract 校验&#39;, () @@ -95,8 +95,8 @@ describe(&#39;R-19.A2 契约 role 落库 + PATCH 通道的 data-contract 校验&#39;, ()
95 const ts = new Date().toISOString(); 95 const ts = new Date().toISOString();
96 const ins = db.prepare(` 96 const ins = db.prepare(`
97 INSERT OR IGNORE INTO system_agents 97 INSERT OR IGNORE INTO system_agents
98 - (id, name, scope, owner_company_id, source_kind, legacy_path, status, created_at, updated_at)  
99 - VALUES (?, ?, 'public', NULL, 'global_agent', ?, 'active', ?, ?) 98 + (id, name, scope, owner_company_id, legacy_path, status, created_at, updated_at)
  99 + VALUES (?, ?, 'public', NULL, ?, 'active', ?, ?)
100 `); 100 `);
101 for (const id of ['brd-planning', 'prd-planning', 'sdd-planning']) { 101 for (const id of ['brd-planning', 'prd-planning', 'sdd-planning']) {
102 ins.run(id, id, `system/agents/${id}`, ts, ts); 102 ins.run(id, id, `system/agents/${id}`, ts, ts);
server/tests/unit/graph-edge-contracts.test.js
@@ -67,8 +67,8 @@ describe(&#39;R-19.A 边上的数据契约:声明 → 落库 → 读回&#39;, () =&gt; { @@ -67,8 +67,8 @@ describe(&#39;R-19.A 边上的数据契约:声明 → 落库 → 读回&#39;, () =&gt; {
67 const ts = new Date().toISOString(); 67 const ts = new Date().toISOString();
68 const ins = db.prepare(` 68 const ins = db.prepare(`
69 INSERT OR IGNORE INTO system_agents 69 INSERT OR IGNORE INTO system_agents
70 - (id, name, scope, owner_company_id, source_kind, legacy_path, status, created_at, updated_at)  
71 - VALUES (?, ?, 'public', NULL, 'global_agent', ?, 'active', ?, ?) 70 + (id, name, scope, owner_company_id, legacy_path, status, created_at, updated_at)
  71 + VALUES (?, ?, 'public', NULL, ?, 'active', ?, ?)
72 `); 72 `);
73 for (const id of new Set(ids)) ins.run(id, id, `system/agents/${id}`, ts, ts); 73 for (const id of new Set(ids)) ins.run(id, id, `system/agents/${id}`, ts, ts);
74 } 74 }
server/tests/unit/graph-engine-fail-reason.test.js
@@ -92,8 +92,8 @@ describe(&#39;引擎自判失败的 fail_reason 与失败上下文(W12 补完 / W9 @@ -92,8 +92,8 @@ describe(&#39;引擎自判失败的 fail_reason 与失败上下文(W12 补完 / W9
92 const ts = new Date().toISOString(); 92 const ts = new Date().toISOString();
93 const ins = db.prepare(` 93 const ins = db.prepare(`
94 INSERT OR IGNORE INTO system_agents 94 INSERT OR IGNORE INTO system_agents
95 - (id, name, scope, owner_company_id, source_kind, legacy_path, status, created_at, updated_at)  
96 - VALUES (?, ?, 'public', NULL, 'global_agent', ?, 'active', ?, ?) 95 + (id, name, scope, owner_company_id, legacy_path, status, created_at, updated_at)
  96 + VALUES (?, ?, 'public', NULL, ?, 'active', ?, ?)
97 `); 97 `);
98 for (const id of new Set(ids)) ins.run(id, id, `system/agents/${id}`, ts, ts); 98 for (const id of new Set(ids)) ins.run(id, id, `system/agents/${id}`, ts, ts);
99 } 99 }
server/tests/unit/graph-engine-node-error.test.js
@@ -82,8 +82,8 @@ describe(&#39;graph-engine 节点失败留痕(W12)&#39;, () =&gt; { @@ -82,8 +82,8 @@ describe(&#39;graph-engine 节点失败留痕(W12)&#39;, () =&gt; {
82 const now = new Date().toISOString(); 82 const now = new Date().toISOString();
83 const ins = db.prepare(` 83 const ins = db.prepare(`
84 INSERT OR IGNORE INTO system_agents 84 INSERT OR IGNORE INTO system_agents
85 - (id, name, scope, owner_company_id, source_kind, legacy_path, status, created_at, updated_at)  
86 - VALUES (?, ?, 'public', NULL, 'global_agent', ?, 'active', ?, ?) 85 + (id, name, scope, owner_company_id, legacy_path, status, created_at, updated_at)
  86 + VALUES (?, ?, 'public', NULL, ?, 'active', ?, ?)
87 `); 87 `);
88 for (const id of new Set(ids)) ins.run(id, id, `system/agents/${id}`, now, now); 88 for (const id of new Set(ids)) ins.run(id, id, `system/agents/${id}`, now, now);
89 } 89 }
server/tests/unit/graph-engine-revive-parent.test.js
@@ -85,8 +85,8 @@ describe(&#39;子 run 转终态回写父节点&#39;, () =&gt; { @@ -85,8 +85,8 @@ describe(&#39;子 run 转终态回写父节点&#39;, () =&gt; {
85 const now = new Date().toISOString(); 85 const now = new Date().toISOString();
86 const ins = db.prepare(` 86 const ins = db.prepare(`
87 INSERT OR IGNORE INTO system_agents 87 INSERT OR IGNORE INTO system_agents
88 - (id, name, scope, owner_company_id, source_kind, legacy_path, status, created_at, updated_at)  
89 - VALUES (?, ?, 'public', NULL, 'global_agent', ?, 'active', ?, ?) 88 + (id, name, scope, owner_company_id, legacy_path, status, created_at, updated_at)
  89 + VALUES (?, ?, 'public', NULL, ?, 'active', ?, ?)
90 `); 90 `);
91 for (const id of new Set(ids)) ins.run(id, id, `system/agents/${id}`, now, now); 91 for (const id of new Set(ids)) ins.run(id, id, `system/agents/${id}`, now, now);
92 } 92 }
server/tests/unit/graph-engine.test.js
@@ -98,8 +98,8 @@ describe(&#39;graph-engine&#39;, () =&gt; { @@ -98,8 +98,8 @@ describe(&#39;graph-engine&#39;, () =&gt; {
98 const now = new Date().toISOString(); 98 const now = new Date().toISOString();
99 const ins = db.prepare(` 99 const ins = db.prepare(`
100 INSERT OR IGNORE INTO system_agents 100 INSERT OR IGNORE INTO system_agents
101 - (id, name, scope, owner_company_id, source_kind, legacy_path, status, created_at, updated_at)  
102 - VALUES (?, ?, 'public', NULL, 'global_agent', ?, 'active', ?, ?) 101 + (id, name, scope, owner_company_id, legacy_path, status, created_at, updated_at)
  102 + VALUES (?, ?, 'public', NULL, ?, 'active', ?, ?)
103 `); 103 `);
104 for (const id of new Set(ids)) ins.run(id, id, `system/agents/${id}`, now, now); 104 for (const id of new Set(ids)) ins.run(id, id, `system/agents/${id}`, now, now);
105 } 105 }
server/tests/unit/graph-files-index.test.js
@@ -75,8 +75,8 @@ describe(&#39;R-19 产物索引:graph_files / graph_edge_contracts&#39;, () =&gt; { @@ -75,8 +75,8 @@ describe(&#39;R-19 产物索引:graph_files / graph_edge_contracts&#39;, () =&gt; {
75 const ts = new Date().toISOString(); 75 const ts = new Date().toISOString();
76 const ins = db.prepare(` 76 const ins = db.prepare(`
77 INSERT OR IGNORE INTO system_agents 77 INSERT OR IGNORE INTO system_agents
78 - (id, name, scope, owner_company_id, source_kind, legacy_path, status, created_at, updated_at)  
79 - VALUES (?, ?, 'public', NULL, 'global_agent', ?, 'active', ?, ?) 78 + (id, name, scope, owner_company_id, legacy_path, status, created_at, updated_at)
  79 + VALUES (?, ?, 'public', NULL, ?, 'active', ?, ?)
80 `); 80 `);
81 for (const id of new Set(ids)) ins.run(id, id, `system/agents/${id}`, ts, ts); 81 for (const id of new Set(ids)) ins.run(id, id, `system/agents/${id}`, ts, ts);
82 } 82 }
server/tests/unit/graph-real-chain-smoke.test.js
@@ -75,8 +75,8 @@ describe(&#39;图执行真实链路 smokeï¼ˆé›¶æ³¨å…¥ï¼Œåªæ¢æŽ‰çœŸ CLI å­è¿›ç¨‹ï¼ @@ -75,8 +75,8 @@ describe(&#39;图执行真实链路 smokeï¼ˆé›¶æ³¨å…¥ï¼Œåªæ¢æŽ‰çœŸ CLI å­è¿›ç¨‹ï¼
75 const db = wdb.getDb(); 75 const db = wdb.getDb();
76 const now = new Date().toISOString(); 76 const now = new Date().toISOString();
77 db.prepare(`INSERT OR IGNORE INTO system_agents 77 db.prepare(`INSERT OR IGNORE INTO system_agents
78 - (id, name, scope, owner_company_id, source_kind, legacy_path, status, created_at, updated_at)  
79 - VALUES (?, ?, 'public', NULL, 'global_agent', ?, 'active', ?, ?)`) 78 + (id, name, scope, owner_company_id, legacy_path, status, created_at, updated_at)
  79 + VALUES (?, ?, 'public', NULL, ?, 'active', ?, ?)`)
80 .run(AGENT, AGENT, `system/agents/${AGENT}`, now, now); 80 .run(AGENT, AGENT, `system/agents/${AGENT}`, now, now);
81 81
82 const g = wdb.createWorkGraph({ companyId: COMPANY, nodes: [{ nodeName: AGENT, systemAgentId: AGENT }], edges: [] }); 82 const g = wdb.createWorkGraph({ companyId: COMPANY, nodes: [{ nodeName: AGENT, systemAgentId: AGENT }], edges: [] });
@@ -142,14 +142,14 @@ describe(&#39;图执行真实链路 smokeï¼ˆé›¶æ³¨å…¥ï¼Œåªæ¢æŽ‰çœŸ CLI å­è¿›ç¨‹ï¼ @@ -142,14 +142,14 @@ describe(&#39;图执行真实链路 smokeï¼ˆé›¶æ³¨å…¥ï¼Œåªæ¢æŽ‰çœŸ CLI å­è¿›ç¨‹ï¼
142 const db = wdb.getDb(); 142 const db = wdb.getDb();
143 const now = new Date().toISOString(); 143 const now = new Date().toISOString();
144 db.prepare(`INSERT OR IGNORE INTO system_agents 144 db.prepare(`INSERT OR IGNORE INTO system_agents
145 - (id, name, scope, owner_company_id, source_kind, legacy_path, status, created_at, updated_at)  
146 - VALUES (?, ?, 'public', NULL, 'global_agent', ?, 'active', ?, ?)`) 145 + (id, name, scope, owner_company_id, legacy_path, status, created_at, updated_at)
  146 + VALUES (?, ?, 'public', NULL, ?, 'active', ?, ?)`)
147 .run(PLANNER, PLANNER, `system/agents/${PLANNER}`, now, now); 147 .run(PLANNER, PLANNER, `system/agents/${PLANNER}`, now, now);
148 // subgraph èŠ‚ç‚¹è‡ªå·±ä¹Ÿè¦æœ‰ system_agents 行(work_graph_nodes.system_agent_id 是外键)。 148 // subgraph èŠ‚ç‚¹è‡ªå·±ä¹Ÿè¦æœ‰ system_agents 行(work_graph_nodes.system_agent_id 是外键)。
149 // å®ƒåªæ˜¯ã€Œå“ªä¸ª org 角色拥有这张å­å›¾ã€çš„å字,ä¸ä¼šè¢«çœŸçš„调用 —— 调的是 PLANNER。 149 // å®ƒåªæ˜¯ã€Œå“ªä¸ª org 角色拥有这张å­å›¾ã€çš„å字,ä¸ä¼šè¢«çœŸçš„调用 —— 调的是 PLANNER。
150 db.prepare(`INSERT OR IGNORE INTO system_agents 150 db.prepare(`INSERT OR IGNORE INTO system_agents
151 - (id, name, scope, owner_company_id, source_kind, legacy_path, status, created_at, updated_at)  
152 - VALUES (?, ?, 'public', NULL, 'global_agent', ?, 'active', ?, ?)`) 151 + (id, name, scope, owner_company_id, legacy_path, status, created_at, updated_at)
  152 + VALUES (?, ?, 'public', NULL, ?, 'active', ?, ?)`)
153 .run(WRAPPER, WRAPPER, `system/agents/${WRAPPER}`, now, now); 153 .run(WRAPPER, WRAPPER, `system/agents/${WRAPPER}`, now, now);
154 db.prepare(`INSERT OR IGNORE INTO company_agents (company_id, system_agent_id, role_name, status, hired_at) 154 db.prepare(`INSERT OR IGNORE INTO company_agents (company_id, system_agent_id, role_name, status, hired_at)
155 VALUES (?, ?, ?, 'active', ?)`).run(COMPANY, PLANNER, PLANNER, now); 155 VALUES (?, ?, ?, 'active', ?)`).run(COMPANY, PLANNER, PLANNER, now);
@@ -213,8 +213,8 @@ describe(&#39;图执行真实链路 smokeï¼ˆé›¶æ³¨å…¥ï¼Œåªæ¢æŽ‰çœŸ CLI å­è¿›ç¨‹ï¼ @@ -213,8 +213,8 @@ describe(&#39;图执行真实链路 smokeï¼ˆé›¶æ³¨å…¥ï¼Œåªæ¢æŽ‰çœŸ CLI å­è¿›ç¨‹ï¼
213 const db = wdb.getDb(); 213 const db = wdb.getDb();
214 const now = new Date().toISOString(); 214 const now = new Date().toISOString();
215 db.prepare(`INSERT OR IGNORE INTO system_agents 215 db.prepare(`INSERT OR IGNORE INTO system_agents
216 - (id, name, scope, owner_company_id, source_kind, legacy_path, status, created_at, updated_at)  
217 - VALUES (?, ?, 'public', NULL, 'global_agent', ?, 'active', ?, ?)`) 216 + (id, name, scope, owner_company_id, legacy_path, status, created_at, updated_at)
  217 + VALUES (?, ?, 'public', NULL, ?, 'active', ?, ?)`)
218 .run(VALIDATOR, VALIDATOR, `system/agents/${VALIDATOR}`, now, now); 218 .run(VALIDATOR, VALIDATOR, `system/agents/${VALIDATOR}`, now, now);
219 219
220 const g = wdb.createWorkGraph({ 220 const g = wdb.createWorkGraph({
server/tests/unit/graph-run-route-io.test.js
@@ -53,8 +53,8 @@ describe(&#39;GET /api/graph-runs/:id — R-19 输入输出三字段&#39;, () =&gt; { @@ -53,8 +53,8 @@ describe(&#39;GET /api/graph-runs/:id — R-19 输入输出三字段&#39;, () =&gt; {
53 const ts = new Date().toISOString(); 53 const ts = new Date().toISOString();
54 const ins = db.prepare(` 54 const ins = db.prepare(`
55 INSERT OR IGNORE INTO system_agents 55 INSERT OR IGNORE INTO system_agents
56 - (id, name, scope, owner_company_id, source_kind, legacy_path, status, created_at, updated_at)  
57 - VALUES (?, ?, 'public', NULL, 'global_agent', ?, 'active', ?, ?) 56 + (id, name, scope, owner_company_id, legacy_path, status, created_at, updated_at)
  57 + VALUES (?, ?, 'public', NULL, ?, 'active', ?, ?)
58 `); 58 `);
59 for (const id of ['brd-planning', 'prd-planning']) ins.run(id, id, `system/agents/${id}`, ts, ts); 59 for (const id of ['brd-planning', 'prd-planning']) ins.run(id, id, `system/agents/${id}`, ts, ts);
60 60
server/tests/unit/graph-run-route-slot-busy.test.js
@@ -60,8 +60,8 @@ describe(&#39;POST /api/graph-runs/... 撞上坑满 → 409(不是 500)&#39;, () =&gt; @@ -60,8 +60,8 @@ describe(&#39;POST /api/graph-runs/... 撞上坑满 → 409(不是 500)&#39;, () =&gt;
60 const now = new Date().toISOString(); 60 const now = new Date().toISOString();
61 const insAgent = db.prepare(` 61 const insAgent = db.prepare(`
62 INSERT OR IGNORE INTO system_agents 62 INSERT OR IGNORE INTO system_agents
63 - (id, name, scope, owner_company_id, source_kind, legacy_path, status, created_at, updated_at)  
64 - VALUES (?, ?, 'public', NULL, 'global_agent', ?, 'active', ?, ?) 63 + (id, name, scope, owner_company_id, legacy_path, status, created_at, updated_at)
  64 + VALUES (?, ?, 'public', NULL, ?, 'active', ?, ?)
65 `); 65 `);
66 for (const id of ['solo', 'solo2']) insAgent.run(id, id, `system/agents/${id}`, now, now); 66 for (const id of ['solo', 'solo2']) insAgent.run(id, id, `system/agents/${id}`, now, now);
67 67
server/tests/unit/graph-run-slot-recovery.test.js
@@ -98,8 +98,8 @@ describe(&#39;恢复路径与执行坑&#39;, () =&gt; { @@ -98,8 +98,8 @@ describe(&#39;恢复路径与执行坑&#39;, () =&gt; {
98 const now = new Date().toISOString(); 98 const now = new Date().toISOString();
99 const ins = wdb.getDb().prepare(` 99 const ins = wdb.getDb().prepare(`
100 INSERT OR IGNORE INTO system_agents 100 INSERT OR IGNORE INTO system_agents
101 - (id, name, scope, owner_company_id, source_kind, legacy_path, status, created_at, updated_at)  
102 - VALUES (?, ?, 'public', NULL, 'global_agent', ?, 'active', ?, ?) 101 + (id, name, scope, owner_company_id, legacy_path, status, created_at, updated_at)
  102 + VALUES (?, ?, 'public', NULL, ?, 'active', ?, ?)
103 `); 103 `);
104 for (const id of new Set(ids)) ins.run(id, id, `system/agents/${id}`, now, now); 104 for (const id of new Set(ids)) ins.run(id, id, `system/agents/${id}`, now, now);
105 } 105 }
server/tests/unit/graph-seed-reconcile.test.js
@@ -63,11 +63,11 @@ describe(&#39;graph-seed 对账在存量形态上退出码为 0&#39;, () =&gt; { @@ -63,11 +63,11 @@ describe(&#39;graph-seed 对账在存量形态上退出码为 0&#39;, () =&gt; {
63 const db = graphDb.getDb(); 63 const db = graphDb.getDb();
64 const now = new Date().toISOString(); 64 const now = new Date().toISOString();
65 const insert = db.prepare(` 65 const insert = db.prepare(`
66 - INSERT INTO system_agents (id, name, scope, owner_company_id, source_kind, origin, legacy_path, status, benchmark_score, created_at, updated_at)  
67 - VALUES (@id, @name, @scope, @owner, @source_kind, @origin, @legacy_path, 'active', NULL, @now, @now) 66 + INSERT INTO system_agents (id, name, scope, owner_company_id, origin, legacy_path, status, benchmark_score, created_at, updated_at)
  67 + VALUES (@id, @name, @scope, @owner, @origin, @legacy_path, 'active', NULL, @now, @now)
68 `); 68 `);
69 - insert.run({ id: 'base', name: 'base', scope: 'public', owner: null, source_kind: 'global_agent', origin: 'builtin', legacy_path: 'system/templates/base', now });  
70 - insert.run({ id: 'graduated', name: 'graduated', scope: 'company', owner: 'live-co', source_kind: 'company_hired', origin: 'created', legacy_path: 'system/agents/graduated', now }); 69 + insert.run({ id: 'base', name: 'base', scope: 'public', owner: null, origin: 'builtin', legacy_path: 'system/templates/base', now });
  70 + insert.run({ id: 'graduated', name: 'graduated', scope: 'company', owner: 'live-co', origin: 'created', legacy_path: 'system/agents/graduated', now });
71 71
72 // ── 幽灵公司的名册行:目录(连同 company.json)已经不在,company_agents 那行还在 ── 72 // ── 幽灵公司的名册行:目录(连同 company.json)已经不在,company_agents 那行还在 ──
73 // 这张表批次 4-19 之后是**唯一被读的名册**,heartbeat-service.start() 拿它挂心跳时 73 // 这张表批次 4-19 之后是**唯一被读的名册**,heartbeat-service.start() 拿它挂心跳时
server/tests/unit/graph-seed-service.test.js
@@ -148,8 +148,8 @@ describe(&#39;graph-seed-service (hermetic fixture root)&#39;, () =&gt; { @@ -148,8 +148,8 @@ describe(&#39;graph-seed-service (hermetic fixture root)&#39;, () =&gt; {
148 function insertHired(id, ownerCompanyId, legacyPath) { 148 function insertHired(id, ownerCompanyId, legacyPath) {
149 const now = new Date().toISOString(); 149 const now = new Date().toISOString();
150 graphDb.getDb().prepare(` 150 graphDb.getDb().prepare(`
151 - INSERT INTO system_agents (id, name, scope, owner_company_id, source_kind, origin, legacy_path, status, benchmark_score, created_at, updated_at)  
152 - VALUES (?, ?, 'company', ?, 'company_hired', 'created', ?, 'active', NULL, ?, ?) 151 + INSERT INTO system_agents (id, name, scope, owner_company_id, origin, legacy_path, status, benchmark_score, created_at, updated_at)
  152 + VALUES (?, ?, 'company', ?, 'created', ?, 'active', NULL, ?, ?)
153 `).run(id, id, ownerCompanyId, legacyPath, now, now); 153 `).run(id, id, ownerCompanyId, legacyPath, now, now);
154 } 154 }
155 155
@@ -429,9 +429,9 @@ describe(&#39;graph-seed-service verifyCounts (real repo root)&#39;, () =&gt; { @@ -429,9 +429,9 @@ describe(&#39;graph-seed-service verifyCounts (real repo root)&#39;, () =&gt; {
429 const db = graphDb.getDb(); 429 const db = graphDb.getDb();
430 const now = new Date().toISOString(); 430 const now = new Date().toISOString();
431 db.prepare(` 431 db.prepare(`
432 - INSERT INTO system_agents (id, name, scope, owner_company_id, source_kind, legacy_path, status, benchmark_score, created_at, updated_at)  
433 - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)  
434 - `).run('bogus-agent-no-fs-backing', 'Bogus', 'public', null, 'global_agent', 'system/agents/bogus-agent-no-fs-backing', 'active', null, now, now); 432 + INSERT INTO system_agents (id, name, scope, owner_company_id, legacy_path, status, benchmark_score, created_at, updated_at)
  433 + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
  434 + `).run('bogus-agent-no-fs-backing', 'Bogus', 'public', null, 'system/agents/bogus-agent-no-fs-backing', 'active', null, now, now);
435 435
436 const v = seedService.verifyCounts(); 436 const v = seedService.verifyCounts();
437 expect(v.ok).toBe(false); 437 expect(v.ok).toBe(false);
server/tests/unit/graph-service.test.js
@@ -35,13 +35,13 @@ describe(&#39;graph-service&#39;, () =&gt; { @@ -35,13 +35,13 @@ describe(&#39;graph-service&#39;, () =&gt; {
35 db.exec('DELETE FROM org_graph_nodes; DELETE FROM system_agents;'); 35 db.exec('DELETE FROM org_graph_nodes; DELETE FROM system_agents;');
36 }); 36 });
37 37
38 - function insertSystemAgent({ id, origin = 'builtin', source_kind = 'global_agent', legacy_path = `system/agents/${id}`, scope = 'public', owner_company_id = null }) { 38 + function insertSystemAgent({ id, origin = 'builtin', legacy_path = `system/agents/${id}`, scope = 'public', owner_company_id = null }) {
39 const db = graphDb.getDb(); 39 const db = graphDb.getDb();
40 const now = new Date().toISOString(); 40 const now = new Date().toISOString();
41 db.prepare(` 41 db.prepare(`
42 - INSERT INTO system_agents (id, name, scope, owner_company_id, source_kind, origin, legacy_path, status, benchmark_score, created_at, updated_at)  
43 - VALUES (?, ?, ?, ?, ?, ?, ?, 'active', NULL, ?, ?)  
44 - `).run(id, id, scope, owner_company_id, source_kind, origin, legacy_path, now, now); 42 + INSERT INTO system_agents (id, name, scope, owner_company_id, origin, legacy_path, status, benchmark_score, created_at, updated_at)
  43 + VALUES (?, ?, ?, ?, ?, ?, 'active', NULL, ?, ?)
  44 + `).run(id, id, scope, owner_company_id, origin, legacy_path, now, now);
45 } 45 }
46 46
47 describe('lookupSharedAgentMd', () => { 47 describe('lookupSharedAgentMd', () => {
server/tests/unit/heartbeat-company-scoped.test.js
@@ -85,8 +85,8 @@ describe(&#39;心跳设置按公司隔离(28 遗留项)&#39;, () =&gt; { @@ -85,8 +85,8 @@ describe(&#39;心跳设置按公司隔离(28 遗留项)&#39;, () =&gt; {
85 const db = graphDb.getDb(); 85 const db = graphDb.getDb();
86 const now = new Date().toISOString(); 86 const now = new Date().toISOString();
87 db.prepare(` 87 db.prepare(`
88 - INSERT OR REPLACE INTO system_agents (id, name, scope, owner_company_id, source_kind, legacy_path, status, benchmark_score, created_at, updated_at)  
89 - VALUES (?, ?, 'public', NULL, 'template', ?, 'active', NULL, ?, ?) 88 + INSERT OR REPLACE INTO system_agents (id, name, scope, owner_company_id, legacy_path, status, benchmark_score, created_at, updated_at)
  89 + VALUES (?, ?, 'public', NULL, ?, 'active', NULL, ?, ?)
90 `).run(BODY_ID, BODY_ID, `system/agents/${BODY_ID}`, now, now); 90 `).run(BODY_ID, BODY_ID, `system/agents/${BODY_ID}`, now, now);
91 for (const cid of [CO_A, CO_B]) { 91 for (const cid of [CO_A, CO_B]) {
92 db.prepare(`INSERT OR REPLACE INTO company_agents (company_id, system_agent_id, role_name, status, hired_at) 92 db.prepare(`INSERT OR REPLACE INTO company_agents (company_id, system_agent_id, role_name, status, hired_at)
server/tests/unit/org-layer-contracts.test.js
@@ -122,8 +122,8 @@ describe(&#39;R-19 org 层数据契约(读回方 = buildPlanningPrompt)&#39;, () =&gt; @@ -122,8 +122,8 @@ describe(&#39;R-19 org 层数据契约(读回方 = buildPlanningPrompt)&#39;, () =&gt;
122 const ts = new Date().toISOString(); 122 const ts = new Date().toISOString();
123 const ins = db.prepare(` 123 const ins = db.prepare(`
124 INSERT OR IGNORE INTO system_agents 124 INSERT OR IGNORE INTO system_agents
125 - (id, name, scope, owner_company_id, source_kind, legacy_path, status, created_at, updated_at)  
126 - VALUES (?, ?, 'public', NULL, 'global_agent', ?, 'active', ?, ?) 125 + (id, name, scope, owner_company_id, legacy_path, status, created_at, updated_at)
  126 + VALUES (?, ?, 'public', NULL, ?, 'active', ?, ?)
127 `); 127 `);
128 for (const id of new Set(ids)) ins.run(id, id, `system/agents/${id}`, ts, ts); 128 for (const id of new Set(ids)) ins.run(id, id, `system/agents/${id}`, ts, ts);
129 } 129 }
server/tests/unit/p3c-recovery-blind-spots.test.js
@@ -110,8 +110,8 @@ describe(&#39;批 3 恢复路径的盲区&#39;, () =&gt; { @@ -110,8 +110,8 @@ describe(&#39;批 3 恢复路径的盲区&#39;, () =&gt; {
110 const now = new Date().toISOString(); 110 const now = new Date().toISOString();
111 const ins = wdb.getDb().prepare(` 111 const ins = wdb.getDb().prepare(`
112 INSERT OR IGNORE INTO system_agents 112 INSERT OR IGNORE INTO system_agents
113 - (id, name, scope, owner_company_id, source_kind, legacy_path, status, created_at, updated_at)  
114 - VALUES (?, ?, 'public', NULL, 'global_agent', ?, 'active', ?, ?) 113 + (id, name, scope, owner_company_id, legacy_path, status, created_at, updated_at)
  114 + VALUES (?, ?, 'public', NULL, ?, 'active', ?, ?)
115 `); 115 `);
116 for (const id of new Set(ids)) ins.run(id, id, `system/agents/${id}`, now, now); 116 for (const id of new Set(ids)) ins.run(id, id, `system/agents/${id}`, now, now);
117 } 117 }
server/tests/unit/p3c-recovery-http.test.js
@@ -94,8 +94,8 @@ describe(&#39;恢复动作的 HTTP 结局:退得回队列 → 2xx,退不回 → @@ -94,8 +94,8 @@ describe(&#39;恢复动作的 HTTP 结局:退得回队列 → 2xx,退不回 →
94 const now = new Date().toISOString(); 94 const now = new Date().toISOString();
95 const ins = wdb.getDb().prepare(` 95 const ins = wdb.getDb().prepare(`
96 INSERT OR IGNORE INTO system_agents 96 INSERT OR IGNORE INTO system_agents
97 - (id, name, scope, owner_company_id, source_kind, legacy_path, status, created_at, updated_at)  
98 - VALUES (?, ?, 'public', NULL, 'global_agent', ?, 'active', ?, ?) 97 + (id, name, scope, owner_company_id, legacy_path, status, created_at, updated_at)
  98 + VALUES (?, ?, 'public', NULL, ?, 'active', ?, ?)
99 `); 99 `);
100 for (const id of new Set(ids)) ins.run(id, id, `system/agents/${id}`, now, now); 100 for (const id of new Set(ids)) ins.run(id, id, `system/agents/${id}`, now, now);
101 } 101 }
server/tests/unit/prompt-write-boundary.test.js
@@ -93,8 +93,8 @@ describe(&#39;提示词的交付物归宿:graph 节点 vs agent-chat 两条链路&#39; @@ -93,8 +93,8 @@ describe(&#39;提示词的交付物归宿:graph 节点 vs agent-chat 两条链路&#39;
93 const ts = new Date().toISOString(); 93 const ts = new Date().toISOString();
94 db.prepare(` 94 db.prepare(`
95 INSERT OR IGNORE INTO system_agents 95 INSERT OR IGNORE INTO system_agents
96 - (id, name, scope, owner_company_id, source_kind, legacy_path, status, created_at, updated_at)  
97 - VALUES (?, ?, 'public', NULL, 'global_agent', ?, 'active', ?, ?) 96 + (id, name, scope, owner_company_id, legacy_path, status, created_at, updated_at)
  97 + VALUES (?, ?, 'public', NULL, ?, 'active', ?, ?)
98 `).run(AGENT_ID, AGENT_ID, `system/agents/${AGENT_ID}`, ts, ts); 98 `).run(AGENT_ID, AGENT_ID, `system/agents/${AGENT_ID}`, ts, ts);
99 99
100 const g = wdb.createWorkGraph({ 100 const g = wdb.createWorkGraph({
server/tests/unit/source-kind-drop-migration.test.js 0 → 100644
  1 +/**
  2 + * 28-followups §3.1 —— `system_agents.source_kind` 删列的**存量库路径**。
  3 + *
  4 + * 单独钉这一条的理由与 tasks-executor-migration 一样:新建库那条路由单测天然覆盖
  5 + * (建表块里已经没有这一列),「存量库 + 重启」这条必须另外安排人走一遍。
  6 + *
  7 + * ── 要守的三件事 ──────────────────────────────────────────────────────
  8 + * ① **顺序**:先把 source_kind 的信息回填进 origin,再删列。反过来做,
  9 + * 「origin 列出现之前写下的行」永远回不到正确的 origin —— 那正是那条回填存在的理由。
  10 + * 这一条最容易在重构里被弄反,而且弄反之后**不报错**:库能开、测试能过,
  11 + * 只有那几行 origin 悄悄是错的。
  12 + * ② **幂等**:删过之后第二次开库不能抛(`no such column: source_kind`)。
  13 + * ③ **位置**:删列必须跑在任何写入之前。migrate() 由 getDb() 在建表之后立刻调用,
  14 + * 正是那个位置;挪到别处的话,带着 source_kind 的 INSERT 会先撞 NOT NULL。
  15 + *
  16 + * ── 为什么 legacy_path 不一起删 ────────────────────────────────────────
  17 + * 它**还没死**:新行里它恒等于 system/agents/{id}(冗余),但存量行里存着批次 4 之前的
  18 + * 公司侧路径,而 graph-seed-service 判「悬空行 / 孤儿行」靠的就是它指向的目录还在不在。
  19 + * 实查生产库(2026-08-20)76 行里有 2 行仍是老路径。退出条件写在 graph-db.js 的建表注释里。
  20 + */
  21 +import { describe, it, expect } from 'vitest';
  22 +import { createRequire } from 'module';
  23 +import fs from 'fs';
  24 +import os from 'os';
  25 +import path from 'path';
  26 +const require_ = createRequire(import.meta.url);
  27 +
  28 +/** 用独立 DB 文件跑一遍 getDb()(写法照抄 tasks-executor-migration.test.js) */
  29 +function freshDb(dbPath) {
  30 + process.env.GRAPH_DB_PATH = dbPath;
  31 + for (const m of ['../../services/graph-db', '../../services/work-graph-db']) {
  32 + delete require_.cache[require_.resolve(m)];
  33 + }
  34 + const graphDb = require_('../../services/graph-db');
  35 + graphDb.getDb();
  36 + return graphDb;
  37 +}
  38 +
  39 +const cols = (db) => db.pragma('table_info(system_agents)').map((c) => c.name);
  40 +
  41 +describe('system_agents.source_kind 删列', () => {
  42 + it('存量库重启:先回填 origin 再删列,且不炸', () => {
  43 + const dbPath = path.join(fs.mkdtempSync(path.join(os.tmpdir(), 'sk-drop-')), 'graph.sqlite');
  44 + const graphDb = freshDb(dbPath);
  45 + expect(cols(graphDb.getDb())).not.toContain('source_kind'); // 新建库本来就没有
  46 + graphDb.getDb().close();
  47 +
  48 + // 退回删列之前的形态:把列加回来,并造两行「origin 列出现之前」的记录 ——
  49 + // 它们的 origin 是加列时的默认值 'builtin',真相只存在 source_kind 里。
  50 + const Database = require_('better-sqlite3');
  51 + const raw = new Database(dbPath);
  52 + raw.exec(`ALTER TABLE system_agents ADD COLUMN source_kind TEXT NOT NULL DEFAULT 'global_agent'`);
  53 + const now = new Date().toISOString();
  54 + const ins = raw.prepare(`INSERT INTO system_agents
  55 + (id, name, scope, owner_company_id, legacy_path, status, created_at, updated_at, origin, source_kind)
  56 + VALUES (?, ?, ?, ?, ?, 'active', ?, ?, 'builtin', ?)`);
  57 + ins.run('legacy-hired', 'legacy-hired', 'company', 'co-x', 'system/agents/legacy-hired', now, now, 'company_hired');
  58 + ins.run('legacy-global', 'legacy-global', 'public', null, 'system/agents/legacy-global', now, now, 'global_agent');
  59 + expect(raw.pragma('table_info(system_agents)').map((c) => c.name)).toContain('source_kind');
  60 + raw.close();
  61 +
  62 + // 重开进程 = 服务重启
  63 + const graphDb2 = freshDb(dbPath);
  64 + const db2 = graphDb2.getDb();
  65 +
  66 + expect(cols(db2)).not.toContain('source_kind'); // ② 列没了
  67 + // ① 顺序:company_hired 那行的 origin 必须已经被回填成 created。
  68 + // 先删后填的话这里会停在 'builtin' —— 而且不会有任何报错。
  69 + const get = (id) => db2.prepare('SELECT origin FROM system_agents WHERE id = ?').get(id);
  70 + expect(get('legacy-hired').origin).toBe('created');
  71 + expect(get('legacy-global').origin).toBe('builtin'); // 对照:不该被一起改写
  72 + db2.close();
  73 +
  74 + // ③ 幂等:再重启一次,不抛,结果不变
  75 + const graphDb3 = freshDb(dbPath);
  76 + expect(cols(graphDb3.getDb())).not.toContain('source_kind');
  77 + expect(graphDb3.getDb().prepare('SELECT origin FROM system_agents WHERE id = ?').get('legacy-hired').origin).toBe('created');
  78 +
  79 + fs.rmSync(path.dirname(dbPath), { recursive: true, force: true });
  80 + delete process.env.GRAPH_DB_PATH;
  81 + });
  82 +
  83 + it('legacy_path 仍在,且删它的退出条件写在了建表注释里(别顺手一起删)', () => {
  84 + const dbPath = path.join(fs.mkdtempSync(path.join(os.tmpdir(), 'sk-drop2-')), 'graph.sqlite');
  85 + const graphDb = freshDb(dbPath);
  86 + expect(cols(graphDb.getDb())).toContain('legacy_path');
  87 + graphDb.getDb().close();
  88 + fs.rmSync(path.dirname(dbPath), { recursive: true, force: true });
  89 + delete process.env.GRAPH_DB_PATH;
  90 +
  91 + // 退出条件必须留在源码里可被读到 —— 否则下一个人只会看到一列「没人读的冗余字段」
  92 + const src = fs.readFileSync(path.join(__dirname, '../../services/graph-db.js'), 'utf-8');
  93 + expect(src).toContain("legacy_path <> 'system/agents/' || id");
  94 + });
  95 +});
server/tests/unit/task-blocked-reason.test.js
@@ -74,8 +74,8 @@ describe(&#39;blocked task 的原因回写&#39;, () =&gt; { @@ -74,8 +74,8 @@ describe(&#39;blocked task 的原因回写&#39;, () =&gt; {
74 74
75 db.prepare(` 75 db.prepare(`
76 INSERT OR IGNORE INTO system_agents 76 INSERT OR IGNORE INTO system_agents
77 - (id, name, scope, owner_company_id, source_kind, legacy_path, status, created_at, updated_at)  
78 - VALUES ('sdd-planning', 'sdd-planning', 'public', NULL, 'global_agent', 'system/agents/sdd-planning', 'active', ?, ?) 77 + (id, name, scope, owner_company_id, legacy_path, status, created_at, updated_at)
  78 + VALUES ('sdd-planning', 'sdd-planning', 'public', NULL, 'system/agents/sdd-planning', 'active', ?, ?)
79 `).run(now, now); 79 `).run(now, now);
80 80
81 const graph = wdb.createWorkGraph({ companyId, nodes: ['sdd-planning'], edges: [] }); 81 const graph = wdb.createWorkGraph({ companyId, nodes: ['sdd-planning'], edges: [] });
server/tests/unit/task-queue-acceptance.test.js
@@ -130,8 +130,8 @@ describe(&#39;批 3 排队 · §八 验收&#39;, () =&gt; { @@ -130,8 +130,8 @@ describe(&#39;批 3 排队 · §八 验收&#39;, () =&gt; {
130 const now = new Date().toISOString(); 130 const now = new Date().toISOString();
131 const ins = wdb.getDb().prepare(` 131 const ins = wdb.getDb().prepare(`
132 INSERT OR IGNORE INTO system_agents 132 INSERT OR IGNORE INTO system_agents
133 - (id, name, scope, owner_company_id, source_kind, legacy_path, status, created_at, updated_at)  
134 - VALUES (?, ?, 'public', NULL, 'global_agent', ?, 'active', ?, ?) 133 + (id, name, scope, owner_company_id, legacy_path, status, created_at, updated_at)
  134 + VALUES (?, ?, 'public', NULL, ?, 'active', ?, ?)
135 `); 135 `);
136 for (const id of new Set(ids)) ins.run(id, id, `system/agents/${id}`, now, now); 136 for (const id of new Set(ids)) ins.run(id, id, `system/agents/${id}`, now, now);
137 } 137 }
server/tests/unit/task-scheduler.test.js
@@ -112,8 +112,8 @@ describe(&#39;task-scheduler&#39;, () =&gt; { @@ -112,8 +112,8 @@ describe(&#39;task-scheduler&#39;, () =&gt; {
112 const now = new Date().toISOString(); 112 const now = new Date().toISOString();
113 const ins = wdb.getDb().prepare(` 113 const ins = wdb.getDb().prepare(`
114 INSERT OR IGNORE INTO system_agents 114 INSERT OR IGNORE INTO system_agents
115 - (id, name, scope, owner_company_id, source_kind, legacy_path, status, created_at, updated_at)  
116 - VALUES (?, ?, 'public', NULL, 'global_agent', ?, 'active', ?, ?) 115 + (id, name, scope, owner_company_id, legacy_path, status, created_at, updated_at)
  116 + VALUES (?, ?, 'public', NULL, ?, 'active', ?, ?)
117 `); 117 `);
118 for (const id of new Set(ids)) ins.run(id, id, `system/agents/${id}`, now, now); 118 for (const id of new Set(ids)) ins.run(id, id, `system/agents/${id}`, now, now);
119 } 119 }
server/tests/unit/task-slots-parallel.test.js
@@ -71,8 +71,8 @@ try { @@ -71,8 +71,8 @@ try {
71 const db = wdb.getDb(); 71 const db = wdb.getDb();
72 const now = new Date().toISOString(); 72 const now = new Date().toISOString();
73 db.prepare( 73 db.prepare(
74 - "INSERT OR IGNORE INTO system_agents (id,name,scope,owner_company_id,source_kind,legacy_path,status,created_at,updated_at)"  
75 - + " VALUES ('solo','solo','public',NULL,'global_agent','system/agents/solo','active',?,?)" 74 + "INSERT OR IGNORE INTO system_agents (id,name,scope,owner_company_id,legacy_path,status,created_at,updated_at)"
  75 + + " VALUES ('solo','solo','public',NULL,'system/agents/solo','active',?,?)"
76 ).run(now, now); 76 ).run(now, now);
77 const g = wdb.createWorkGraph({ companyId: cid, nodes: ['solo'], edges: [] }); 77 const g = wdb.createWorkGraph({ companyId: cid, nodes: ['solo'], edges: [] });
78 if ((g.errors || []).length) throw new Error(JSON.stringify(g.errors)); 78 if ((g.errors || []).length) throw new Error(JSON.stringify(g.errors));
@@ -186,8 +186,8 @@ describe(&#39;批 4 · 公司级执行位(max_parallel_tasks)&#39;, () =&gt; { @@ -186,8 +186,8 @@ describe(&#39;批 4 · 公司级执行位(max_parallel_tasks)&#39;, () =&gt; {
186 const now = new Date().toISOString(); 186 const now = new Date().toISOString();
187 const ins = wdb.getDb().prepare(` 187 const ins = wdb.getDb().prepare(`
188 INSERT OR IGNORE INTO system_agents 188 INSERT OR IGNORE INTO system_agents
189 - (id, name, scope, owner_company_id, source_kind, legacy_path, status, created_at, updated_at)  
190 - VALUES (?, ?, 'public', NULL, 'global_agent', ?, 'active', ?, ?) 189 + (id, name, scope, owner_company_id, legacy_path, status, created_at, updated_at)
  190 + VALUES (?, ?, 'public', NULL, ?, 'active', ?, ?)
191 `); 191 `);
192 for (const id of new Set(ids)) ins.run(id, id, `system/agents/${id}`, now, now); 192 for (const id of new Set(ids)) ins.run(id, id, `system/agents/${id}`, now, now);
193 } 193 }