diff --git a/docs/todo/system-design/27-p4-parallel-and-artifacts.md b/docs/todo/system-design/27-p4-parallel-and-artifacts.md index 124a0a7..193e468 100644 --- a/docs/todo/system-design/27-p4-parallel-and-artifacts.md +++ b/docs/todo/system-design/27-p4-parallel-and-artifacts.md @@ -115,10 +115,25 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_graph_runs_one_active_root ### 3.6 产物目录现状 +> ⚠️ **本节写于 2026-08-16,下面两个 ❌ 已经不成立了**(2026-08-20 校准)。 +> 留着原文是因为它记录了当时的判断依据;**当前状态看这一段的校准块**。 + - ✅ `goal-contract.js` 已实现验收契约的跨节点传递(R-15.6 验收先行)。 -- ✅ `graph-engine.js:283/407/444` 有节点级产物路径:`data/companies/{cid}/agents/{systemAgentId}/.../attempt-{n}.json`。 -- ❌ **没有 `files.json`**(全仓库无此文件、代码无引用)。 -- ❌ 产物按 **agent** 分目录,不是按 **task** 分目录——同一 agent 在不同任务下的产出混在一起。 +- ✅ `graph-engine.js` 有节点级产物路径。 +- ~~❌ 没有 `files.json`~~ → **✅ 阶段 C 已落地,但形态换了**:产物索引做成了 + `graph_files` **表**(`recordFile` / `findFiles` / `rebuildFilesFromNodes`), + 不是 `files.json` 文件。表比文件多拿到两样东西:部分唯一索引保证「同一路径只有一个当前版本」, + 以及 `superseded_by` 让历史版本可共存。守卫 `graph-files-index.test.js` 七条, + 含「删掉索引后能从节点行重建」和「两个 task 的同名产物不互相覆盖」。 +- ~~❌ 产物按 agent 分目录~~ → **✅ 已按 run/node 分目录**: + `data/companies/{cid}/runs/{rootRunId}/nodes/{nodeRunId}/`, + 跨 org node 的共享产物在 `runs/{rootRunId}/artifacts/`。 + 同一 agent 在不同任务下的产出因此天然不混。 + +> **2026-08-20 顺带修掉一个存量库缺陷**:生产库里还留着产物索引加版本之前的 +> **无条件**唯一索引 `idx_graph_files_path`,导致重试时同路径第二次写必撞、 +> 被降级成一条 timeline —— **产物索引在任何有重试的 run 上都不全**,而且不报错。 +> 详见 [27-P3B §六ter](./27-p3b-real-run-runway.md)。 --- @@ -485,14 +500,14 @@ R-19 之后这两件事有实体(`graph_edge_contracts` × `graph_files`), | # | 任务 | 文件 | 依赖 | |---|---|---|---| -| **T1** | `SessionPool` 加 `maxSessions` 上限 + 调用方传 `scope`(`gr:{nodeRunId}`) | `server/src/core/session-pool.js`、`server/src/core/agent-manager.js` | 无 | +| ~~**T1**~~ ✅ | `SessionPool` 加 `maxSessions` 上限 + 调用方传 `scope` —— **已完成**(27-P3C §15.5-2,守卫 `session-pool-max.test.js` / `session-pool-scope-key.test.js`) | `server/src/core/session-pool.js` | 无 | | **T2** | DevLoop 规格:§12 第一条不变量放宽为 `(pid, agent, scope)`。**改 HTML 后必须跑 `npm run spec:extract`** | `docs/prd/newfunction/*.html` | 无 | -| **T3** | `graph_runs` 加 `max_concurrency` 列;DROP `idx_graph_runs_one_active_root`,改为事务内计数检查 | `server/services/work-graph-db.js` | 无 | +| **T3** | `graph_runs` 加 `max_concurrency` 列(**未做**);~~DROP `idx_graph_runs_one_active_root`,改为事务内计数检查~~ ✅ **后半已完成**(27-P3C 批 4:四步准入 `BEGIN IMMEDIATE` → 事务内重读 → 事务内计数 → 条件更新,8 进程实测零超发) | `server/services/work-graph-db.js` | 无 | | **T4** | `advanceBatch(runId, {maxConcurrency})`:事务内批量拾取并置 running;`advanceOne` 降级为薄封装 | `server/services/graph-engine.js` | T3 | | **T5** | 并发额度计算(三层取小)+ 额度为 0 时的等待语义 | `server/services/graph-engine.js` | T1, T3, T4 | | **T6** | 并行执行 + 错误降级(照搬 `orchestrator.executeWithDependencies` 的 `Promise.allSettled` 策略,单分支失败不终结整批) | `server/services/graph-engine.js` | T4 | -| **T7** | `files.json` 服务:追加、查询、rebuild | `server/services/company-files-index.js`(新增) | 无,可并行 | -| **T8** | 产物落盘路径改为 `tasks/{taskId}/`;节点产出后自动写 `files.json` | `server/services/graph-engine.js`、`goal-contract.js` | T7 | +| ~~**T7**~~ ✅ | 产物索引:追加、查询、rebuild —— **已完成,形态换成 `graph_files` 表**(见 §3.6 校准块),不是 `files.json` | `server/services/work-graph-db.js` | — | +| ~~**T8**~~ ✅ | 产物按 run/node 分目录 + 节点产出后自动进索引 —— **已完成**(`runs/{rootRunId}/nodes/{nodeRunId}/`) | `server/services/graph-engine.js` | — | | **T9** | node agent 的 prompt 注入 `files.json` 检索说明 | `system/templates/base/agent.md` 等 | T7 | | **T10** | 控制台:fan-in 等待态可视化(「在等哪几个节点」)+ 并行运行中的多节点高亮 | `web/src/views/GraphConsole.vue` | T6 | | **T11** | 测试:批量拾取的并发幂等(两个 `advanceBatch` 不拾取重叠集合)、部分失败的 fan-in 停等、额度上限生效、`files.json` rebuild 一致性 | `server/tests/unit/` | T4-T8 | @@ -501,12 +516,18 @@ R-19 之后这两件事有实体(`graph_edge_contracts` × `graph_files`), | 阶段 | 含 | 交付 | 可否独立上线 | |---|---|---|---| -| **A · 隔离** | T1, T2 | 同一 agent 可安全并发调用 | ✅ 可以,此时引擎仍串行,但隔离能力已具备 | +| **A · 隔离** | ~~T1~~ ✅, T2 | 同一 agent 可安全并发调用 —— **T1 已完成**,只剩 T2(DevLoop 规格放宽) | 基本具备 | | **B · 并行** | T3-T6, T10 | fan-out 真正并发;解除单 root run 约束 | ✅ 依赖 A | | **C · 产物索引** | T7-T9 | `files.json` + 按 task 分目录 | ✅ **完全独立于 A/B**,可最先做 | **阶段 C 与 A/B 无依赖**,如果想先看到效果,可以先做 C(它是用户明确要求过的第 3 项)。 +> **2026-08-20 校准**:C 已经完成,A 的 T1 也完成了(都是在 P3C 那几批里顺手落的, +> 当时没回来更新这份文档)。**真正剩下的是 B**:`max_concurrency` 列、`advanceBatch`、 +> 三层额度取小、并行执行与部分失败降级,以及 T10 的控制台可视化。 +> 这一段是「文档落后于代码」的又一例 —— 判断 P4 还剩多少工作量时, +> **先照着任务表逐条 grep 一遍代码**,别照文档估。 + --- ## 六、验收标准