# Opus 4.7 系统详细设计与 UML 类图回归审查

Date: 2026-06-01
Reviewer: Claude Opus 4.7 (independent regression audit)
Scope: 修复后回归审查 — 验证 P1×4 + P2×9 修复是否真正闭合，且未在修复过程中引入新偏差
Inputs:
- 详细设计：`system-detailed-design.md`（2264 行，23 章节，commit `543743b`）
- 受冻结基线：22 份基线 + 已冻结概要设计
- 此前审查：DeepSeek / MIMO 2.5 Pro / GPT-5.5 Pro / Opus 4.8 四轮审查 + 汇总

---

## 1. 审查方法

本次为**回归审查**，不重复覆盖性扫描，而是：

1. **P1×4 修复验证**：检查每项修复是否真正落地、文字是否清晰、是否与基线一致
2. **P2×9 修复验证**：同上，并特别检查"修复过程中是否引入新的偏离基线条款"
3. **跨文档一致性**：DD 修复后是否仍与 contracts、event-registry、runtime-semantics、scope-escalation 等基线一致
4. **新发现**：审查过程中浮出的、之前轮次未发现的新问题

---

## 2. 总览结论

| 维度 | 状态 |
|---|---|
| P1×4 修复闭合性 | ✅ 4/4 已闭合 |
| P2×9 修复闭合性 | ⚠ 9/9 文字已落地，但 3 项引入新基线偏离 |
| 新增 P0 | 0 |
| 新增 P1（修复引入的回归） | **3** |
| 新增 P2 | 2 |
| 覆盖性 | ✅ 维持 100% |
| 架构合理性 | ✅ 维持，无循环依赖 |
| 综合判定 | **CONDITIONAL PASS（次轮）** — 需收束 3 项 P1 回归 |

---

## 3. P1×4 修复验证

### 3.1 P1-01: Worker exit code 语义冲突 — **✅ 已闭合**

| 检查项 | 结果 |
|---|---|
| DD §8.1 与 `baselineV1.md §8` 对齐 | ✓ 完全一致（6 个 code 含义逐字对应）|
| overview §11 与 baselineV1 对齐 | ✓（commit `8d9c420`/`453df09` 中验证）|
| 任务结果与 exit code 解耦说明 | ✓ DD §8.1 末尾"Design decision"明确：任务 status 走 `WorkerResult.status`，exit code 仅表达进程协议层语义 |
| 表格格式与 baselineV1 一致 | ✓ |

**结论**：完全闭合。无残留歧义。

### 3.2 P1-02: PromptLayerLevel 枚举与 L0-L9 不对齐 — **✅ 已闭合**

| 检查项 | 结果 |
|---|---|
| `interface-contracts-v1.md §16` 增加 `"safety"` 枚举值 | ✓ |
| 11 个枚举值（含 `system_debug`）映射到 L0-L9 + 系统调试 | ✓ |
| DD §10.2 表 1（枚举 → L# → loader）正确显示 11 行 | ✓ |
| `system_debug` 注释"applied within L9 when present" | ✓ 与 prompt-layering-v1 §2 注脚对齐 |

**结论**：完全闭合。L0-L9 全部有显式枚举对应。

### 3.3 P1-03: EventStore.project() 错误处理未定义 — **✅ 已闭合**

| 检查项 | 结果 |
|---|---|
| DD §5.3 包含"Error handling for `project()`"块 | ✓ |
| 异常 → 事务回滚 → `EventBus.publish()` 不触发 | ✓ |
| 返回 `AirError{kind: "system_error"}` | ✓ |
| FK-off 不一致触发 `SessionStore.referential_check()` | ✓ |

**结论**：完全闭合，并与 §4.3、§18.3 形成闭环。

### 3.4 P1-04: PromptLayerLoader 接口不完整 — **✅ 已闭合**

| 检查项 | 结果 |
|---|---|
| DD §10.2 第二张表（L2/L4/L6/L7/L8/L9 → ContextAssembler 内部装配）| ✓ |
| 显式说明 PromptLayerLoader 只覆盖 L0/L1/L3/L5 | ✓ |
| Runtime roles（main/architecture/scheduler）不走 `load_role()` 的设计决策 | ✓ |
| 与 §2"Worker AgentType vs. runtime roles"块互为印证 | ✓ |

**⚠ 次级发现**：L2 Safety 来源写为 `PermissionEngine.current_profile()`，但 `interface-contracts-v1.md §13` 的 `PermissionEngine` 接口**只声明 `evaluate()` 与 `record()` 两个方法**。`current_profile()` 是基线之外的方法引用 → 见 §5.3 新发现 R1-01。

---

## 4. P2×9 修复验证

| ID | 文字落地 | 内容质量 | 新偏离基线 |
|---|---|---|---|
| P2-01 Architecture gate 序列图 | ✓ §19.4 | ✓ 完整 | 无 |
| P2-02 CLI catalog 命令类归属 | ✓ §17 | ✓ 11 个 Command 类 | 无 |
| P2-03 agent.started 投影措辞 | ✓ §5.4 表 A | ⚠ | **是** → R1-02 |
| P2-04 WorkspaceManager 职责矩阵 | ✓ §7.5 | ✓ | ⚠ 引用错误 → R2-01 |
| P2-05 §5.4 拆分两张表 | ✓ §5.4 | ✓ Table A/B 分离清晰 | 无 |
| P2-06 memory.promoted 两相 | ✓ §18.4 | ⚠ | **是** → R1-03 |
| P2-07 docs task 闭合 | ✓ §8.3 | ✓ | 无 |
| P2-08 AgentType vs runtime roles | ✓ §2 | ✓ | 无 |
| P2-09 contracts 文件集冻结 | ✓ §3 | ✓ | ⚠ → R2-02 |

整体：**9/9 文字落地，但 3 项（P2-03 / P2-04 / P2-06 / P2-09）在修复过程中引入新偏离**。

---

## 5. 新发现（回归引入）

### 5.1 [R1-01] P1-04 副作用：`PermissionEngine.current_profile()` 不在基线 — **P1**

**位置**：DD §10.2 "Design decision — Layer loading responsibility" 表，L2 Safety 来源

**问题**：
```text
L2 Safety  →  PermissionEngine.current_profile() + ~/.air/permissions.yaml + project permissions
```

但 `interface-contracts-v1.md §13` 的 `PermissionEngine` 接口仅声明：
```ts
export interface PermissionEngine {
  evaluate(context: PermissionRequestContext): Promise<PermissionDecision>
  record(decision: PermissionDecision, context: PermissionRequestContext): Promise<PermissionRecordResult>
}
```

`current_profile()` 是详细设计层引入的方法名，没有对应的契约方法。这违反了 DD §0 "does not introduce new public contracts" 的约束。

**根因**：P1-04 修复时为给 L2 Safety 找一个明确来源，引用了一个并不存在的方法名。

**建议处理**：
- 选项 A：把来源改为"`PermissionEngine` 内部状态（实现细节）+ `~/.air/permissions.yaml` 文件 + 项目权限配置"，避免点名一个不存在的方法
- 选项 B：在 `PermissionEngine` 契约中加 `current_profile()` 方法 → 需要 ADR 并修改 contracts §13（违反"基线冻结"约束）
- **推荐**：选项 A

**预计工作量**：5 分钟（文字调整）

---

### 5.2 [R1-02] P2-03 副作用：`agent.started` 引入"同事件内分阶段更新"概念，超出基线 — **P1**

**位置**：DD §5.4 Table A 中 `agent.started` 行

**当前文字**：
```text
insert `agents` row with `status='starting'` upon spawn intent, then update to
`status='running'` on the same event's commit when WorkerProcess handshake has
succeeded (`workers` ack); domain row carries final state at commit time
```

**问题**：这段话描述了"同一个事件提交过程中先 starting 再 running"的语义，事件投影在一次事务内对同一行做 insert+update。

但 `event-registry-v1.md §3` 对 `agent.started` 的定义是：
```text
Domain update: insert `agents` row with `status = running` or `starting`.
```

即基线明确"插入时 row 的 `status` 等于 `running` 或 `starting` 其一"，**不是两步**。状态从 `starting` 到 `running` 的过渡由独立事件 / 后续状态机决定（参见 DD §20.4 中 `starting ──ready──▶ running`，但 baseline 并未对应 "ready" 持久事件）。

把握手成功"在同一事件提交时"反映到 row 上，事实上无法实现 — 因为 `agent.started` 事件已经在 `WorkerManager.spawn` 完成 handshake **之前或之后**就已经被持久化。

**根因**：P2-03 旧文字"insert agents (starting/running)"措辞被认为"含糊"，修复时反而引入了一个**违反事件原子性**的描述。

**建议处理**：恢复为符合基线的清晰版本：
```text
| `agent.started` | insert `agents` row with `status = 'starting'` (initial spawn)
or `status = 'running'` (if handshake completed before the event is emitted);
final transition `starting → running` is recorded via the implicit state
machine in §20.4 without a dedicated durable event |
```

**预计工作量**：5 分钟

---

### 5.3 [R1-03] P2-06 副作用：`memory.promoted` 引入 `phase` 字段，扩展基线 payload — **P1**

**位置**：DD §18.4 "memory.promoted two-phase semantics" 表

**当前文字**：
```text
| Phase | Payload marker                                              |
| Intent     | phase: "intent",  no artifact_ref/memory_id yet       |
| Completion | phase: "committed", includes artifact_ref / memory_id |
```

**问题**：`event-registry-v1.md §3` 的 `MemoryPromotedPayload` 定义为：
```ts
interface MemoryPromotedPayload {
  candidate_id: string
  target_ref: string
  promoted_by: "user" | "curator" | "system"
  summary: string
}
```

**没有 `phase` 字段**。P2-06 修复引入的"intent vs committed"两相是 DD 层对 outbox 模型的额外扩展，事实上在基线中是通过**单一 `memory.promoted` 事件 + 上下游推断**完成的（runtime-semantics §6.4 直接说"session event records completed promotion and target ref"，即单事件即为完成态）。

此外，DD §18.4 也对 `debug.record.created` 同样应用了 `phase` 字段（"the same two-phase pattern (intent → committed) with payload field `phase`"），同样不在 `event-registry-v1.md` 的 `DebugRecordCreatedPayload`中。

**根因**：GPT-5.5 Pro 的原 P2-06 关注点是"outbox intent/completion 阶段不清"。修复时为了在 DD 层明确区分，新增了 `phase` 字段。但**契约层从未声明这个字段**，且 baseline `runtime-semantics §6.4` 实际上认为 `memory.promoted` 是单事件、完成态语义。

**建议处理**：去掉 `phase` 字段引入，改用"两个事件"或"前置 candidate + 单完成事件"叙述：

```text
**`memory.promoted` outbox flow**（不引入新 payload 字段）：

1. `memory.candidate.created` 已持久化（已存在事件）
2. 决定 promote 后，业务侧执行外部 store 写入
3. 写入成功后发出 `memory.promoted` —— 这就是基线说的"completed promotion"
4. 写入失败：发出 `memory.archived` 或回退到一个新的 candidate；运行时不发未完成的 `memory.promoted`

如果未来需要严格的 outbox intent，应通过 ADR 引入新事件类型，而非给现有事件增加 payload 字段。
```

对 `debug.record.created`：基线 runtime-semantics §6.3 说"intent/completion event"，但实际 payload 是单一 schema；建议同样不引入 `phase`，而是依赖 `debug.record.created` + `debug.record.failed`（如果未来加入）形成 outbox。

**预计工作量**：15 分钟

---

### 5.4 [R2-01] P2-04 副作用：DD §7.5 引用 "overview §10.5" 不正确 — **P2**

**位置**：DD §7.5 "Responsibility split — Scheduler vs. WorkspaceManager"

**当前文字**：`**Responsibility split — Scheduler vs. WorkspaceManager** (overview §10.5, scheduler-state-machine §4/§MERGING)`

**问题**：`system-overview-design.md §10.5` 是 "Direct mode"，不包含 workspace 职责描述。workspaces 描述实际位于 overview §10.3 (Scheduler state machine, 含 "workspace assignment", "workspace merge and conflict handling")。

**根因**：P2-04 修复时引用错章节号。

**建议处理**：把 `(overview §10.5, scheduler-state-machine §4/§MERGING)` 改为 `(overview §10.3, scheduler-state-machine §4/§MERGING)`。

**预计工作量**：1 分钟

---

### 5.5 [R2-02] P2-09 副作用：file-set 决策中存在 self-contradicting 描述 — **P2**

**位置**：DD §3 "Frozen file-set decision (P2-09)" 块

**当前文字**：
```text
For V1.0.0 Alpha, the canonical contracts package file set is exactly the 16 files listed above
```

**问题**：

1. 上方 ASCII 列表中实际上是 **15 个文件**（ids/error/event/runtime/ipc/task/worker-result/tool/permission/artifact/evidence/project/provider/ui/capability/platform = 16？让我数一遍：ids, error, event, runtime, ipc, task, worker-result, tool, permission, artifact, evidence, project, provider, ui, capability, platform = 16），数对了。

2. 但紧接着的 file-mapping 表说"context.ts symbols → runtime.ts + dedicated context.ts only if needed (otherwise inlined in runtime.ts)"以及"dedicated doctor.ts only if file size warrants split"等条目，**实际允许额外文件**。这与"exactly the 16 files listed above"自相矛盾。

3. "no new `.ts` files are added in the contracts package without an ADR" 是好的硬约束，但表中的"only if needed"放宽了它。

**根因**：P2-09 修复想同时表达"主体冻结" + "极少数情况可拆分"，但语言模糊。

**建议处理**：明确两层：
- 冻结：16 个**必有**文件
- 可选：`context.ts` / `doctor.ts` / `knowledge.ts` / `diagnostics.ts` 在出现明确大小/分层需求时**可以追加**，追加同时需要更新 code-view §3 + ADR

或更严格：彻底冻结到 16 个，不预留追加口子。

**预计工作量**：5 分钟

---

## 6. 重新验证未受影响的领域

| 维度 | 状态 |
|---|---|
| 22 份基线契约覆盖 | ✅ 100% |
| 55 个 durable + 7 个 ephemeral 事件覆盖 | ✅ 100% |
| 19 张 DB 表的 Repository 覆盖 | ✅ 100% |
| 6 个状态机覆盖 | ✅ 100%（main agent, scheduler, task, agent, workspace, capability）|
| 10 条 forbidden edge | ✅ §2 显式继承，禁止路径无破口 |
| 8 个 UML 类图 | ✅ 全部存在（22.1-22.8）|
| 5 个序列图（含 P2-01 新增 gate）| ✅ 5/5 |
| 5 项 traceability 矩阵 | ✅ 5/5 |
| 依赖方向 | ✅ 无循环 |
| 事务语义 | ✅ project + publish 时序正确 |

---

## 7. UML 类图回归审查

**8 个 Mermaid 类图**全部存在且语法可解析，无新偏差。需要补充指出：

- **§22.1 EventSource.kind**：枚举 `"main"|"architecture_designer"|"scheduler"|"agent"|"tool"|"system"` 现与 §2 "runtime role" 表协同（P2-08 修复正确指向 `event.ts`）。✓
- **§22.3 Scheduler vs WorkspaceManager 关系**：图中 `Scheduler --> WorkspaceManager` 已暗示策略/机制层级，但**未引入 P2-04 矩阵中的"Scheduler 拥有 conflict resolution policy"语义**。建议是否在图旁加注 "Scheduler owns policy; WorkspaceManager owns mechanism"。
- **§22.5 WorkerRole 继承**：5 个 Role 类全部出现；P2-07 引入的 `docs` TaskType 通过复用 `ExecutorRole`，类图无需新增节点 — ✓ 与文字一致。
- **§22.7 ContextAssembler vs PromptLayerLoader**：图中 `ContextAssembler --> PromptLayerLoader` 是单向依赖，符合 P1-04 / P2-08 决策。建议补充 `ContextAssembler --> PermissionEngine`（用于 L2 Safety 加载）— 但若采纳 R1-01 选项 A，此依赖也应消失。

---

## 8. 门禁判定

| 条件 | 状态 |
|---|---|
| P0 = 0 | **PASS** |
| 契约覆盖 100% | **PASS** |
| 事件覆盖 100% | **PASS** |
| DB Schema 覆盖 100% | **PASS** |
| 状态机覆盖 100% | **PASS** |
| 禁止路径全执行 | **PASS** |
| 架构无循环依赖 | **PASS** |
| 职责分离无泄漏 | **PASS** |
| 此前 4 处 P1 已闭合 | **PASS** |
| 此前 9 处 P2 文字已落地 | **PASS** |
| 修复未引入新偏离基线 | **FAIL**（3 处 P1 回归 + 2 处 P2 回归） |

**门禁结果：CONDITIONAL PASS（次轮）**

---

## 9. 修复优先级建议

| 优先级 | Finding | 建议处理 | 工作量 |
|---|---|---|---|
| **P1** | R1-01 `PermissionEngine.current_profile()` 不在契约 | 改写 L2 Safety 来源描述 | 5 min |
| **P1** | R1-02 `agent.started` 同事件内 starting→running 违反事件原子性 | 恢复为基线允许的"starting OR running" | 5 min |
| **P1** | R1-03 `memory.promoted` 引入未冻结 `phase` payload 字段 | 移除 phase 字段，回归基线 outbox 单事件叙述 | 15 min |
| **P2** | R2-01 §7.5 "overview §10.5" 引用错误 | 改为 §10.3 | 1 min |
| **P2** | R2-02 §3 file-set 表内 self-contradicting | 明确"16 必有 + 可选追加（需 ADR）"或彻底冻结 | 5 min |

**总预计工作量：30 分钟**

---

## 10. 多模型审查累积视角

| 轮次 | 模型 | 主要贡献 | 累积 P0 | 累积 P1 | 累积 P2 |
|---|---|---|---|---|---|
| R1 | DeepSeek | 全量覆盖性 | 0 | 0 | 3 |
| R2 | MIMO 2.5 Pro | 内在一致性 | 0 | 2 | 5 |
| R3 | GPT-5.5 Pro | 基线冲突检测 | 0 | 2 (新) | 4 (新) |
| R4 | Opus 4.8 | 验证 + 根因 + 建议 | 0 | 2 (确认) | 5 (确认) |
| R5 | Opus 4.7（本次回归） | 修复闭合性 + 回归引入检测 | 0 | **3 (新回归)** | **2 (新回归)** |

**累积说明**：
- R1–R4 发现的 P1×4 + P2×9 已全部文字闭合（commit `453df09` + `543743b`）
- R5 发现修复过程引入新偏离基线的 3 P1 + 2 P2，需要再迭代一次

---

## 11. 最终结论

### 11.1 修复有效性

- **P1×4 修复**：4/4 全部文字闭合，质量良好。其中 P1-04 副作用引入 R1-01。
- **P2×9 修复**：9/9 全部文字闭合，但 P2-03 / P2-06 / P2-09 引入新偏差，P2-04 含小引用错误。

### 11.2 详细设计当前状态

- **从覆盖性 / 架构合理性 / 状态机一致性看**：详细设计仍然 PASS
- **从契约/事件 payload 不变性看**：当前版本存在 **3 处契约/payload 越界引用**（R1-01 ~ R1-03）

### 11.3 建议

**短迭代修复（30 分钟以内）** 收束 3 P1 + 2 P2，然后可以正式冻结详细设计、进入实现阶段。

不建议在不解决 R1-01 ~ R1-03 的情况下开始实现：
- R1-01 会让 ContextAssembler 实现时找不到 `current_profile()` 方法
- R1-02 会让 EventStore 实现者误以为 `agent.started` 投影需要做两次 status 更新
- R1-03 会让消息处理代码尝试读不存在的 `event.payload.phase` 字段

### 11.4 最终判定

**CONDITIONAL PASS (R5)** — 详细设计相比 R4 后版本仍然满足覆盖性与结构正确性，但修复过程引入了 3 处契约 / payload 越界条款，需要在冻结/实现前 30 分钟内修正。

---

End of regression audit.
