Commit b8e2fed2af8a154bebcd10867956529ac51d5923

Authored by yibo
1 parent 5645974c

docs(37): org graph 该按部门作用域,而不是公司级单例——设计讨论 + 全仓库调用点扫描

现状 org graph 按 company_id 单例(27-P2 标题原文即"公司级"),塞不下 HR/财务/市场这类
跟"交付流水线"无关的公司常设职能——system/agents/hr/("招·留·管·退")本体已存在却从没
被装进任何图,全靠 fire-and-forget chat 招聘,没有图执行那一套断点续跑/审计轨迹。

提议把作用域从 company_id 升级为 (company_id, department_id),一家公司可装配多张部门图,
每张仍保持人工固定/慢变/可审计——不是把角色图变成动态生成,只是允许公司持有的图从 1 变成 N。

全仓库调用点扫描(fork)结论:改造面是最初估算的 4 倍。真正的单点真相在
work-graph-db.js:readDefGraph 的裸 SQL 不变式(graph_id === company_id,写进了 §四.A.2
设计文档),坐在 graph-engine.js 执行引擎的热路径上,不在 org-graph-service.js 那层服务
API;另外还有两个易混淆的服务文件(org-graph-service.js / graph-service.js)共同维护一份
必须字节级一致的重复唯一索引、独立的 graph-seed-service.js 对账子系统(8 处裸 SQL)、
company-hire-org-layer.test.js 专属测试套件、orggraph-planning/task-planning 两份 agent
人设文档把"只有一张图"写进了自我认知——都需要一并改。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
docs/README.md
... ... @@ -49,6 +49,7 @@ Graph Agent 架构重构(27 系列)。**已完成的阶段文档已移入 `d
49 49 | [29-dream-design.md](./todo/system-design/29-dream-design.md) | **dream(经验汇流)**— ✅ 已实现。定时整理所有 agent 在所有公司的记忆,提炼后跨公司沉淀(不泄漏公司信息)。四条取舍已拍板并实现:去敏独立判定、四落点分层写入(经验/`SKILL.md`/`soul.md`/`agent.md`,受管区块+三道闸门)、每日定时(串行+空转保护)、引用真伪自动查、REST+控制台入口(`/api/dream/*`、`/dream`)。`innovator-evolver` 真 CLI 跑通一次。**仍缺的**:「结论对不对」只能人抽查;「套进 org/work graph 来跑」评估后用户选了不做(作用域不匹配,见文内 §5.7) |
50 50 | [30-task-delegation-reliability-gaps.md](./todo/system-design/30-task-delegation-reliability-gaps.md) | **任务委派可靠性现状**— 📝 调研记录,非设计方案。DevLoop `runUnit`、Company 招聘装配、Graph 执行三条链路核对结论一致:都是「失败预算耗尽 → 停等人工」,没有「卡住了主动通知人」这一层;DevLoop 当前还默认走 mock,真实 CLI 成功率无实证数据。2026-08-23 追加真实实例并已修:`infra_flake` 原地重跑没有熔断线,真撞上过一次确定性死循环(节点把诊断结论误判成自己的执行结果,反复重跑却从不路由到诊断建议的目标节点);已加 `infra_flake_count` 列 + 硬顶 3 次熔断,连续判 infra_flake 超过硬顶就改判 blocked 交给人工 |
51 51 | [31-work-graph-fail-fallback-coverage.md](./todo/system-design/31-work-graph-fail-fallback-coverage.md) | work graph 兜底边覆盖不全 — ✅ 已完成(除入口节点外强制兜底边,`planner.json`/`executor-fanout.json` 补 6 条边),978/978 单测通过;2026-08-21 又用真实 LLM 起了一条新任务做端到端回归验证,`org-planner-sdd` 自判失败后自动打回 `org-planner-prd`(3 次,零人工介入),修复确认生效 |
  52 +| [37-org-graph-department-scoping.md](./todo/system-design/37-org-graph-department-scoping.md) | 📝 **设计讨论,调用点扫描已完成,尚未立项实现**。现状 org graph 按 `company_id` 单例(`27-P2` 标题原文即"公司级"),塞不下 HR/财务/市场这类跟"交付流水线"无关的公司常设职能——`system/agents/hr/`("招·留·管·退")本体已存在却从没被装进任何图,全靠 fire-and-forget chat 招聘、无图执行审计轨迹。提议把 org graph 作用域从 `company_id` 升级为 `(company_id, department_id)`,一家公司可装配多张部门图,每张仍保持人工固定/慢变/可审计。全仓库调用点扫描结论:改造面是最初估算的 4 倍——真正的单点真相在 `work-graph-db.js:readDefGraph` 的裸 SQL 不变式(不在服务层),还牵涉两个易混淆服务文件间必须字节级一致的重复唯一索引、独立的 `graph-seed-service.js` 对账子系统、`company-hire-org-layer.test.js` 专属测试、`orggraph-planning`/`task-planning` 两份 agent 人设文档的自我认知改写 |
52 53 | [36-prd-agent-methodology-training-feasibility.md](./todo/system-design/36-prd-agent-methodology-training-feasibility.md) | 📝 **调研 + 阶段一/二均已完成**。回应"落地页太单薄是不是该用 Innovator 先训练专业 agent、再用 Dream 持续迭代"——认同方向。**阶段一**:`graph-engine.js` 结算节点后把 `success`/`fail:<reason>` 写进本体 `memory.md`(此前只有人类对话路径会写,Dream 天天空转)。**阶段二**:新建独立试验节点 `prd-methodology-trial` 走真实 Hunt→Digest→Evolve,2 轮收敛(96/97 分,真实基线 50 分),三个真实历史项目反推验证方法论有效;过程中连带修复同一个 2026-08-16 改名批次遗留的 5 处 `innovator`→`org-innovator` 死链(后端两处硬编码、前端"重新训练"按钮、`/api/agent/list` 展示 id 来源的四个 `config.json`),以及 `AgentScaffold` 默认模板一处裸相对路径缺陷。结论:FDE 对方法论类角色确实管用,但收敛速度/验收严谨度依赖任务书显式写明的自纠正要求,非免人工黑盒。环节 3(训练产物 → 雇佣本体转换)尚未开工 |
53 54 | [35-validation-rigor-and-body-readiness-proposal.md](./todo/system-design/35-validation-rigor-and-body-readiness-proposal.md) | 📝 **复盘 + 改进方案,四条提案全部已实施**。承接 31/32/33/34 号的复盘结论:任务结果差主要是架构问题(完整性闸门 `missingBodyDefinition` 只在招聘时查、图执行侧完全没有第二道防线——新发现)+ 一个具体的招聘装配可靠性 bug,不是 agent 能力问题。提案 A:本体不完整时图执行前置判 `fail_reason=body_not_ready`,不扣预算。提案 D:`goal-contract.js` 的 `evidence_hint` 命中浏览器类关键词的维度必须有证据支撑,否则判不过。提案 C:新增 `scripts/visual-acceptance-check.mjs` 复用检查工具。提案 B:控制台「验收证据」小节展示每个维度有没有证据支撑 |
54 55 | [34-validator-scored-without-real-deliverable.md](./todo/system-design/34-validator-scored-without-real-deliverable.md) | 📝 **调研记录,checklist 已查完**。最初以为 `org-coder-impl` 从未产出任何 HTML/CSS,后来发现真实网站其实存在于 `graph_files` 索引不覆盖的「项目根」目录(已顺手修:控制台「产物」面板现在会展示项目根)。追加查清 validator 有没有真的做浏览器级检查——中途自我订正一次:不是"没工具做不到"(`bash` 能走到仓库自带的 Playwright,另一条对照任务证明可行),是**没人教它这么做**;抽查另外两个项目根后发现**验证严谨度完全不稳定**(同类任务有的做了真实 Playwright 级验证、有的只做静态正则、有的没验证就被取消),根因很可能是新招聘的 `unit-tester` 本体撞上了 [32 号文档](./todo/system-design/32-hire-body-fill-reliability.md) 那类"装配没填充完"的真实样本,不是平台工具限制 |
... ...
docs/todo/system-design/37-org-graph-department-scoping.md 0 → 100644
  1 +# 37 · Org Graph 该不该是部门级,而不是公司级
  2 +
  3 +> **日期**:2026-08-24
  4 +> **触发**:用户测试 e2e-demo(一家"开发型公司")时发现,公司除了写代码交付这条主线,还需要人事(招·留·管·退)、财务、市场、运营这类"公司自己运转"所需的职能——"麻雀虽小,五脏俱全"。但现状是**一家公司只有一张 org graph**(`27-P2` 文档标题原文就是"Org Graph — **公司级** Workflow 的生成与落地"),这张图被设计成"一条交付流水线"(规划→执行→验收),塞不下 HR/财务这类跟"交付什么产物"无关、却是任何公司都需要的常设职能。
  5 +> **结论先说**:用户提出的重新定义是对的——**org graph 应该是部门级(department-scoped),一家公司可以装配多张**,而不是公司级单例。这不是发明新概念,是把代码库里已经写好但从没拧紧的几颗螺丝对齐:`system/agents/hr/` 这个本体已经存在且职责原文就是"招·留·管·退",只是从没被装进任何图;`org_graph_nodes.reports_to_node_id` 这一列在 schema 里存在但零引用;`org-graph-templates/`(`dev.json`/`content.json`/`ops.json`)本身的形状早就是"一个部门一份",只是今天的用法是"公司建立时选一个"而不是"可以装配多个"。
  6 +> **状态**:设计讨论,尚未立项实现。本文档记录缘起、现状证据、提议方案与影响面,供下一步决定要不要排期。
  7 +
  8 +---
  9 +
  10 +## 一、缘起(Why)
  11 +
  12 +这次讨论的起点是三层递进的对话(同一会话内,2026-08-24):
  13 +
  14 +1. **先确认"org graph vs work graph 的边界在哪"**——排查发现 lemonboss 的这套两层划分(角色图/工作图)跟姊妹项目 `graphlemon`(`docs/graphlemon_architecture.md`)是同一条设计脉络:Org Graph 人工固定、慢变、可审计;Work Graph 每次任务动态规划、可以快变。这条边界本身是对的,也有先例支撑。
  15 +2. **再讨论"能不能按 goal/task 现场 plan 出不同的 org graph"**——排查发现这个想法如果直接落地,会把"角色图"的慢变属性拉进"工作图"的快变节奏,破坏治理红线;而且引擎的 `activateSuccessTargets`(`server/services/graph-engine.js`)对 `condition='success'` 的出边是**无条件全部点亮**,没有 `fail_reason` 那种按路由键精确匹配的机制——就算图里画出多条分支,今天也做不到"按任务类型只走一条"。
  16 +3. **用户指出真实场景**:一家开发公司也需要人事、财务、市场、运营——这些不是"交付流水线上的一步",是任何公司都需要的**常设职能**。这才是问题的正确提法:不是"一张图要不要按任务变形",而是**"一家公司本来就不该只有一张图"**。
  17 +
  18 +**Why 这件事现在值得写下来**:不是臆测的未来需求——`system/agents/hr/agent.md` 已经真实存在、职责描述("招·留·管·退")跟用户这次原话逐字对应,说明这个需求早就被感知到、甚至已经动手做了一半(写了 HR 本体),只是没人把它跟 org graph 打通,它今天完全是编外人员。
  19 +
  20 +## 二、现状实锤(What is)
  21 +
  22 +按 `company_id` 单例设计的证据,散在四处,此前从没被合起来看:
  23 +
  24 +1. **Schema 本身是单例**:`org_graph_nodes`/`org_graph_edges`(`server/services/graph-db.js:318` 起)只有 `company_id` 一个作用域键,没有部门/命名空间维度。`org-graph-service.getGraph(companyId)` 因此只能返回"这家公司唯一那张图"。
  25 +2. **规划 prompt 把这个假设写死了**:`server/services/task-planning.js:199` 给拆分 agent 的说明原文是"公司 org graph 摘要(**每条 task 都会各自走一遍这条流水线**)"——不是"选一条适合的流水线",是无条件全走一遍。
  26 +3. **就算画出多分支,引擎也不支持按类型选路**:`activateSuccessTargets`(`graph-engine.js` §四.D.1)遍历某节点**所有** `condition='success'` 的出边逐一点亮下游,唯一门槛是 fan-in 汇合;`fail_reason` 精确匹配选边的机制只长在 fail 边上,success 边没有对应物。
  27 +4. **新雇的角色不在图上就被排除在图外**:`company-service.js:114` 附近的排除规则③明文写着"公司已经有一张成型的组织工作流图,而这次雇的角色不在图上 → 不入图"——`hr` 正是被这条规则挡在图外的典型:它是真实存在、定义完整的本体,却从未出现在任何公司的 `org_graph_nodes` 里,今天的"招聘/训练"全靠 `POST /:companyId/hire` 触发的 fire-and-forget chat 流水线(`server/routes/company.js` 的 hire 路由 + `org-innovator` chat 调用),没有断点续跑、没有图执行那一套审计轨迹,跟 `org-coder` 走图执行的路是两套完全不同的机制。
  28 +5. **半埋下的层级维度**:`org_graph_nodes.reports_to_node_id`(`graph-db.js:324`)这一列存在,但 `org-graph-service.js` 里零引用——是个没人接上的部件,形态正好能承载"这个节点属于哪个部门"这类信息。
  29 +6. **模板本身已经是"一个部门一份"的形状**:`dev.json`/`content.json`/`ops.json`(`system/org-graph-templates/`)都是三节点小图(规划→执行→验收),`README.md` 原文说"每个 `*.json` 是一种公司类型的执行流图起点"——设计时想的就是"选一个",不是"可以都要"。
  30 +7. **连标题都写死了**:已实现并归档的 `docs/done/system-design/27-p2-org-graph.md` 标题就是"Org Graph — **公司级** Workflow 的生成与落地",这次要挑战的正是标题里这个前提。
  31 +
  32 +## 三、跟已有参考设计的关系
  33 +
  34 +`graphlemon`(`docs/graphlemon_architecture.md`)和这次翻出来的三篇行业参考文章(`docs/reference/`)都没有讨论过"一家公司多个部门、按项目动态选用"这个问题——graphlemon 的 MVP 明确把范围锁定成单一"Dev Organizer"工厂类型(Planner→Coder→Validator 三节点,永远固定),它文档里"多 Goal 并发(多个 Org Graph 实例)"指的是**同一张图并发跑多个 goal 的多个 run**,不是"不同 goal 用不同形状的图"。所以本文档提议的"部门级 org graph"不是抄现成先例,是在已验证过的"角色图慢变可审计 / 工作图快变"这条边界之上,往前多走一步——这一步两边参考资料都没走过,需要自己想清楚。
  35 +
  36 +## 四、提议方案(How / What)
  37 +
  38 +**核心改动**:org graph 的作用域从 `company_id` 单例升级为 `(company_id, department_id)` 多例。每个部门的图依然保持"人工固定、慢变、可审计"的治理属性——只是一家公司现在可以有好几张这样的图,而不是被迫塞进一张。
  39 +
  40 +1. **Schema**:`org_graph_nodes`/`org_graph_edges` 加一个部门作用域键(建议默认值兜住存量单图公司,不破坏兼容——存量公司的这张图归到一个隐式的默认部门,不需要数据迁移)。
  41 +2. **服务层**:`orgGraphService.getGraph/writeGraph/patchGraph/validateGraph` 从 `(companyId)` 升级为 `(companyId, departmentId)`。
  42 +3. **规划**:`orggraph-planning` 可以对同一家公司规划**多张**图——建公司时按 `whenToUse` 选出的不再是"唯一一张图的模板",而是"这家公司初始要装配哪几个部门",`dev.json`/`content.json`/`ops.json`/未来的 `hr.json` 都可以同时被选中、各自独立生成一张图。
  43 +4. **goal 路由**:`task-planning.js` 现在读"公司唯一那张图"喂给拆分 agent,需要改成先做一步"这个 goal 归哪个部门"的分类,再读那张部门图的摘要——这一步本质是复用 `orggraph-planning` 建公司时已经在做的 `whenToUse` 选型逻辑,只是从"建公司时跑一次"变成"每个 goal 都跑一次"。
  44 +5. **顺带收编招聘/训练**:HR 部门自己有一张 org graph 后,"雇一个新 agent""对某个 agent 做 FDE 训练"就能变成走图执行、可断点恢复、可审计的正经 goal,而不是今天这种 fire-and-forget 的 chat 调用(详见 [36-prd-agent-methodology-training-feasibility.md](./36-prd-agent-methodology-training-feasibility.md) 记录的独立试验训练全靠人工一条条 chat 催的现状)。
  45 +
  46 +**治理属性没有丢**:这不是把角色图变成动态生成——每张部门图依然要走人工评审、依然慢变。变的只是"一家公司允许持有的图的数量"从 1 变成 N,这恰好更贴近人类公司的真实形态:组织架构(谁向谁汇报、部门怎么设置)是慢变的,但公司可以同时有好几个部门,每个项目只会用到其中相关的几个。
  47 +
  48 +## 五、影响面(调用点扫描)
  49 +
  50 +**2026-08-24 追加:全仓库扫描(fork)已完成。结论——改造面比最初估的两个调用点大约 4 倍,且真正的单点真相不在最初以为的那个文件里。**
  51 +
  52 +### 5.1 服务层(`org-graph-service.js`)——机械可扩,风险低
  53 +
  54 +导出 `validateGraph/writeGraph/patchGraph/getGraph/terminalAgentId/removeNode`,全部只按 `companyId` 作用域,调用方都是浅层引用,加一个 `departmentId` 参数即可:
  55 +
  56 +- `server/routes/company.js:564,586,599,611,1866,2074,2276,2515` —— CRUD 路由 + 4 处"图存不存在"的入队前置闸门
  57 +- `server/services/graph-engine.js:821` —— 淘汰过期 `workgraph-planning` 雇佣时的 `removeNode`
  58 +- `server/services/task-settle.js:447` —— `terminalAgentId(companyId)` 找 goal 汇报人
  59 +- `server/services/task-planning.js:242-243` —— 读 `getGraph(companyId)` 摘要喂进拆分 prompt(已知)
  60 +
  61 +### 5.2 绕过 service 层的直接 DB 访问——比假设的大得多,这才是真正的单点真相
  62 +
  63 +- **`work-graph-db.js:1074` 的 `readDefGraph(kind, graphId)`**:`kind==='org'` 时直接对 `org_graph_nodes`/`org_graph_edges` 跑 `WHERE company_id = ?` 的裸 SQL,代码注释原文是**"kind === 'org':graphId 就是 company_id(§四.A.2)"**——这是一条**写进设计文档的架构不变式**,不是顺手写的代码。`graph-engine.js` 真正执行时走的 `defIndex(run)` 就是调它——**引擎的执行路径吃的是这份裸 SQL,不是 `org-graph-service.js` 那层**。只改服务层 API、不改这里,运行时引擎依然是公司级单例。
  64 +- **`graph-service.js`**(跟 `org-graph-service.js` 名字接近、是两个不同文件,容易搞混)自己也有一份裸 SQL,还持有一个**局部唯一索引** `idx_org_graph_nodes_company_role_active ON org_graph_nodes(company_id, role_name) WHERE status='active'`——这份索引在 `graph-db.js` 的 `migrate()` 里还有一份**必须字节级保持一致**的重复定义(代码注释明确警告这两处会打架)。这个文件还持有 `orgGraphMembership(companyId, roleName)`(就是撑住"图已成型、新雇角色被排除"那条规则③的真身)和 `upsertOrgGraphNode`(其幂等靠的 `ON CONFLICT(company_id, role_name)` 跟这份索引形状死死绑定)。
  65 +- **`graph-seed-service.js`**:一整个独立的"`company.json` ↔ DB 对账"子系统,8 处裸 SQL(公司删除级联、孤儿节点清理、一致性校验,分布在 246-259/311-322/493-533/609-698/756 行),全部按 `company_id` 作用域——**最初的两点估算完全没算到这一块**。
  66 +
  67 +### 5.3 路由层
  68 +`GET/POST/PATCH /:companyId/org-graph`、`POST /:companyId/org-graph/replan` 四个端点没有部门维度,需要 `?department=` 或嵌套路径 `/:companyId/departments/:deptId/org-graph`——无论哪种,§5.1 列的 8 个调用点形状都要跟着变。
  69 +
  70 +### 5.4 goal/task 派发链路
  71 +新发现:`enqueueTaskRun`(`company.js:1046-1062`)建 org 类型 run 时写的是 `wdb.createRun({graphKind:'org', graphId: companyId, ...})`——**"org run 的 `graph_id` 字面上就是 `companyId`" 这条约定的唯一生产写入点就在这里**,是 §5.2 那条不变式的源头。`work_graph_nodes.owner_org_node_id → org_graph_nodes(id)` 这条外键不受影响——只要 `org_graph_nodes.id` 继续是自增主键,部门只是这一行上多一列,外键完整性不受牵连。
  72 +
  73 +### 5.5 执行引擎
  74 +`graph-engine.js` 不按公司缓存图定义(每次 `advance` 都现查 `defIndex(run)`,没有单例缓存要操心)——但它拿"用哪张图"的唯一路径就是 `run.graph_id`,今天恒等于 `companyId`(§5.2/5.4)。6 处分支判断 `run.graph_kind === 'org'`(1017/4059/4114/4203/4252 行),全部是传递性受影响——只要 `readDefGraph` 的 SQL 改一处(比如 `graph_id` 改成 `${companyId}:${departmentId}` 这种复合键),这 6 处不需要各自单独动。
  75 +
  76 +### 5.6 招聘流程
  77 +排除规则③(`company-service.js` ~114-140 行)和 `_recordHireInGraph`/`decideOrgNodeRecording` 实际调的是 `graph-service.orgGraphMembership`(§5.2)——真正要改的落点比原以为的更深一层。雇佣本身的 `promoteDraftIfComplete` 不碰 org graph,不受影响。
  78 +
  79 +### 5.7 心跳/调度
  80 +`heartbeat-service.js`、`company-heartbeat-service.js` 对 `org_graph`/`orgGraph` **零引用**——心跳按 `company_agents` 枚举雇员,不查 org graph 表,无影响。
  81 +
  82 +### 5.8 前端消费方
  83 +`GraphConsole.vue:417`、`CompanyDashboard.vue:226-268`(渲染单条线性 `orgGraphChain` 数组)、`ReplanOrgGraphButton.vue:79,99` —— 三处都是"渲染唯一一张图",需要加部门选择器,没有更深的架构耦合。
  84 +
  85 +### 5.9 测试守卫
  86 +- **`company-hire-org-layer.test.js`**(146-238 行起,10+ 条专门用例)直接测"图已成型 → 新雇角色被排除"这条不变式本身——这是不变式的**正式测试合同**,改造时要重写语义(部门感知),不是简单传参。
  87 +- `org-graph-service.test.js` 等约 25 个文件只是顺手调用 `getGraph`/`writeGraph` 做测试前置,机械跟改即可。
  88 +
  89 +### 5.10 文档与 agent 人设
  90 +- **`system/agents/orggraph-planning/agent.md:5-6`**:自我认知原文是"这家公司**唯一**一次性的基础设施角色……你的唯一职责:为这家公司规划出**一张** org graph"——部门化直接跟这个 agent 的人设文本相矛盾,需要从"一次性、产出一张图"改写成"可按部门重复调用"。
  91 +- **`system/agents/task-planning/agent.md`**(24/29/36/52/63/74 行)通篇是单数框架("每条 task 都会各自走一遍**完整的** org graph"、"org graph 已经定了")——不只是 `task-planning.js:199` 那处代码里的 prompt 片段,人设文档本身也要重写。
  92 +- `system/org-graph-templates/README.md`:已知,"选一个"式表述。
  93 +- `docs/done/system-design/27-p2-org-graph.md`(补充:fork 用原路径没找到,实际文件已在 P2 完成后归档进 `done/`,本文档第一节已用正确路径引用过)。
  94 +
  95 +### 5.11 最大的几个意外
  96 +1. **"org graph = company_id" 真正的单点真相在 `work-graph-db.js` 的 `readDefGraph` 裸 SQL 里,不在 `org-graph-service.js`**——而且是写进设计文档的不变式(§四.A.2),坐在执行引擎的热路径上。只改服务层 API 不改这里,运行时引擎依然是公司级单例,等于白改。
  97 +2. **有两个名字相近、容易搞混的服务文件**(`org-graph-service.js` 与 `graph-service.js`)都各自持有对 `org_graph_nodes`/`org_graph_edges` 的裸 SQL,还共同维护一份**必须字节级一致、写了警告注释的重复唯一索引**——部门化要同时改两份索引定义 + `upsertOrgGraphNode` 的 `ON CONFLICT` 目标。
  98 +3. **`graph-seed-service.js`** 是完全独立的第三个子系统(`company.json` ↔ DB 对账/修复),又是 8 处裸 SQL——最初的两点估算里完全没算到这一块。
  99 +4. **排除规则③有一整套专属测试**(`company-hire-org-layer.test.js`),外加**两份 agent 人设文档把"只有一张图"写进了自我认知**(`orggraph-planning`/`task-planning`)——这次改造跟代码改动一样重的是 prompt/文档改写,不是"改完代码顺手补几个测试"能打发的。
  100 +
  101 +**净结论**:改造面大约是最初两个调用点估算的 **4 倍**,横跨 3 个后端服务(不是 1 个)、执行引擎的裸 SQL 热路径、一份重复维护的 DB 索引、3 个前端组件、一整套专属测试、2 份 agent 人设文档。
  102 +
  103 +## 六、开放问题
  104 +
  105 +- 一家公司同时有多张图之后,"公司整体健康度/进度"这类跨部门汇总视图(`CompanyDashboard.vue`)要不要重新设计,还是简单地按部门分区展示即可?
  106 +- goal 的部门分类步骤本身会不会分错——分类错误的后果(一个明明该走 HR 图的 goal 被分进了 dev 图)需要有和 `body_not_ready`/`blocked_pending_human` 同级别的兜底与人工可见性,不能默默按错误部门执行到底。
  107 +- 部门之间会不会需要互相产出/消费 artifact(比如 HR 招到的新 agent 要交给 dev 部门"验收"),跨部门的边算不算这次设计范围内,还是留到部门内部图先跑顺再看要不要扩展成跨部门边。
  108 +
  109 +## 七、建议推进顺序
  110 +
  111 +**2026-08-24 追加**:第五节扫描结果显示改造面是当初估算的 4 倍、横跨 3 个后端服务 + 执行引擎热路径 + 2 份 agent 人设文档,**不是一个中等改动**,不适合直接对存量公司/存量图动手。调整后的顺序:
  112 +
  113 +1. **先钉死唯一入口**:部门化的落点不是 `org-graph-service.js`,是 `work-graph-db.js:readDefGraph` 那条 `graph_id === company_id` 的不变式(§5.2/5.4)——`enqueueTaskRun` 是这条约定的唯一生产写入点。任何验证都必须先改这一处(比如把 `graph_id` 换成 `${companyId}:${departmentId}` 复合键),否则表面上服务层/路由层都改完了,运行时引擎依然在按公司单例执行。
  114 +2. **最小验证仍然成立,但要绕开排除规则③和它的专属测试**:只给一家全新的测试公司加一张 `hr` 部门图(复用现成的 `system/agents/hr/`),跑通"HR 部门收到一个招聘类 goal → 走图执行 → 完成雇佣"这一条链路——**不要**碰 `company-hire-org-layer.test.js` 守着的存量单图公司行为,两边先并存,不急着让旧公司迁移。
  115 +3. **`org-graph-service.js` 与 `graph-service.js` 的裸 SQL + 那份必须字节级一致的重复索引**(§5.2)建议借这次机会合并成一处持有,不要在部门化时把"两份定义要保持一致"这条隐患原样搬进新维度——不合并的话,以后改索引形状要记得改两个文件,是新的技术债。
  116 +4. **`orggraph-planning`/`task-planning` 两份 agent 人设文档的重写,跟代码改动同批验收**,不要留到"代码先上、文档以后补"——这两份文档目前把"只有一张图"写进了自我认知,代码改完而文档没跟上,agent 会带着错误的自我认知继续按旧假设规划。
  117 +5. 验证通过后再决定要不要把"部门分类"这一步做成通用机制、推广到存量公司;`graph-seed-service.js` 那 8 处对账 SQL 放在最后处理——它是修复性质的子系统,不在 goal 执行的关键路径上,可以等主链路验证稳定后再补。
... ...