Files
AirCoding/AirPlan/docs/architecture/opus4.7详细设计与UML审查.md
AirCoding af99aed1f4 Detailed design: close five R-series regressions from Opus 4.7 audit
Independent regression audit (Opus 4.7) verified P1×4 + P2×9 fixes were
closed but found that three of the P2 fixes had introduced new baseline
violations and two had minor errors. This commit closes all five.

R-series fixes (in system-detailed-design.md):

- R1-01 §10.2: L2 Safety source no longer names a fictional
  PermissionEngine.current_profile() method. The L2 row now describes
  the active permission profile sources (~/.air/permissions.yaml +
  project permission config) without inventing a contract method,
  honoring DD §0 "no new public contracts."

- R1-02 §5.4 Table A: agent.started projection no longer claims a
  two-step "starting → running" update within a single event commit
  (which would violate event atomicity). The row now matches
  event-registry §3: a single status (starting or running) at emission
  time, with the follow-up transition handled by WorkerManager per the
  state machine in §20.4.

- R1-03 §18.4 + §5.4 Table B: removed the non-baseline phase: intent |
  committed payload-field extension from memory.promoted and
  debug.record.created. Outbox semantics now follow the baseline model:
  owning store performs the external write first, then ingests a single
  durable completion event whose payload matches event-registry §3
  exactly. Any future intent/commit split must go through an ADR plus
  payload version bump.

- R2-01 §7.5: corrected workspace responsibility cross-reference from
  "overview §10.5" (Direct mode) to "overview §10.3" (Scheduler state
  machine, which actually covers workspace assignment and merge).

- R2-02 §3: resolved self-contradiction in the contracts file-set
  decision. The 16 files are now stated as mandatory with default
  inlining of all overview §4 symbol groups for V1.0.0 Alpha; any
  future split is explicitly out of scope until an ADR is added.

Also adds opus4.7详细设计与UML审查.md documenting the regression
findings, baseline-evidence trails, and recommended fixes that drove
these changes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 10:06:44 +08:00

370 lines
17 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 rolesmain/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 (新回归)** |
**累积说明**
- R1R4 发现的 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.