# Audit Engine

> 多轮收敛审计编排引擎。在十步循环关键检查点编排 agent-team-audit 执行多轮审计， 通过结论集合比较和投票机制判定收敛，支持 convergence 和 challenge 两种模式。 触发场景 (由 Phase Skills 调用，非用户直接调用): - phase-a-planner 完成 Spec 后 (post_spec) - brainstorm 完成后 (post_brainstorm) - task-planner 完成后 (post_planning) - phase-b-developer 任务进度达阈值 (mid_implementation) - phase-b-developer 实现完成后 (post_implementation) - phase-c-integrator 合并前 (pre_merge) - phase-d-closer 收尾后 (post_closure, 限 convergence + max_rounds=1)

- Skill: `10cg/audit-engine` (Agent Skill, multi-file: 11 files)
- Install (CLI): `npx skillmds@latest add 10cg/audit-engine`
- Raw SKILL.md: https://api.skillmd.com/api/skills/10cg/audit-engine/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: 10cg (https://skillmd.com/u/10cg)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/10cg/audit-engine

---


# 审计引擎 (Audit Engine)

> **版本**: 1.0.0 | **状态**: 实验性 (Experimental)
> **创建**: 2026-03-27
> **依赖**: [agent-team-audit](../agent-team-audit/SKILL.md) (单轮执行引擎)

---

## 架构关系

```
audit-engine (多轮编排层)
    |
    | 调用 (每轮)
    v
agent-team-audit (单轮执行引擎)
    |
    | spawn
    v
各 Agent (按检查点配置的 team)
```

**组合而非替代**: audit-engine 负责多轮编排和收敛判定，agent-team-audit 保持为
单轮执行引擎。并发控制、超时策略、去重算法全部复用 agent-team-audit 现有实现。

---

## 输入参数

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `checkpoint` | string | 是 | 检查点名称 (见下方检查点列表) |
| `mode` | string | 否 | `convergence` / `challenge` / `adaptive`，默认从配置读取 |
| `context` | string | 是 | 被审计内容路径 (proposal.md / diff / UPM) |
| `agents_config` | object | 否 | Agent 分组覆盖，默认从 config.json teams 读取 |

### 检查点列表

| 检查点 | 阶段 | 侧重 | 调用方 |
|--------|------|------|--------|
| post_brainstorm | A | 决策验证 | brainstorm |
| post_spec | A.1 | 决策验证 | phase-a-planner |
| post_planning | A.2 | 质量保障 | task-planner |
| mid_implementation | B.2 | 质量保障 | phase-b-developer (条件触发) |
| mid_post_spec | B.2 | spec 漂移校验 | phase-b-developer (条件触发, Aria #79) |
| post_implementation | B.2 | 质量保障 | phase-b-developer |
| pre_merge | C.2 | 共识构建 | phase-c-integrator |
| post_closure | D.1 | 经验积累 | phase-d-closer |

**post_closure 限制**: 代码已合并，限 convergence 模式 + max_rounds=1，侧重经验提取。

**mid_post_spec 限制** (Aria #79): Phase B 实施期 SMOKE/集成测试暴露 spec 陈述与运行实际不符时**条件触发**的快速校验, 非全量审计。**恒 `max_rounds=1`** (镜像 post_closure), scope 限漂移涉及的 spec 陈述; mode 仍走 adaptive_rules (L1 off / L2 convergence / L3 challenge) 但单轮即出 verdict。输出 = append-only spec amendment 建议 (blocking=false, advisory), 采纳后 resume Phase B。详见 [agent-team-audit/references/audit-points.md](../agent-team-audit/references/audit-points.md) `## mid_post_spec`。

---

## 执行流程

4 阶段: (1) 入口逻辑 (config + mode + agents 加载) → (2) **Pre-merge gate** — `pre_merge` checkpoint 专属横向完整性检查 (Issue #26, 与 #27 互补) → (3) **Convergence 模式** (全员讨论 → 汇总 → 四元组比较 → 收敛/振荡) → (4) **Challenge 模式** (讨论组+挑战组对抗 → objections resolved 判定)。

**完整流程定义 (4 阶段 详细 step / pre_merge gate 5-step 流程 + 错误输出 / convergence 4-step / challenge 4-step + Round 计数)**: 见 [references/execution-modes.md](./references/execution-modes.md)。

**Schema 细节**: convergence 见 [references/convergence-algorithm.md](./references/convergence-algorithm.md), challenge 见 [references/challenge-mode-schema.md](./references/challenge-mode-schema.md)。

### Step 0: Anchor 固化 (Drift Guard #17, v1.44.0)

入口逻辑完成后、**Round 1 启动前一次性**执行: 固化本次审计的原始目的 anchor。

```json
{
  "anchor": {
    "checkpoint": "post_spec",
    "primary_goal": "<从 context 提取的原始目的>",
    "in_scope": [],
    "out_of_scope_hints": [],
    "source_sha": "<freeze 时 git SHA>"
  }
}
```

- anchor **写入报告 body 的 `drift_metrics` 章节** (anchor 快照, 见 [references/report-format.md](./references/report-format.md); frontmatter 仅含三个 boolean 标记字段, 保持扁平防 #125/#126 parser), **审计周期内不可变** — **mid-audit re-anchor 不支持** (换锚 = 另起一次审计, cross-ref DEC-20260611-001 §9 Out-of-Scope)。
- 每轮收敛判定前由独立 drift-checker 持 anchor 对当轮结论分类, 详见 [references/challenge-mode-schema.md](./references/challenge-mode-schema.md) drift-checker 节。

**per-checkpoint anchor 来源 fallback 链** (带 checkpoint 归属标签, 非单线性):

1. **[proposal 类: post_spec / post_planning / mid_post_spec]** proposal.md 的 Why/Goal 段 (mid_post_spec #79 校验 spec 漂移, 锚定 spec 本身)
2. **[diff/UPM 类: mid_implementation / post_implementation / pre_merge]** 经 `change_id` 解析 proposal.md (复用 pre-write-validation 既有锚点链, 见 [references/pre-write-validation.md](./references/pre-write-validation.md))
3. **[post_brainstorm]** brainstorm_decisions (调用契约见下)
4. **降级**: issue/PR 标题 (此时 `source_sha` = 当前 HEAD SHA + `anchor_source=degraded`)
5. **全缺 → fail-soft**: 跳过 drift 计算 + 报告标注 `drift_anchor_missing`, **不阻塞审计**

**post_brainstorm 调用契约** (三点, DEC-20260611-001 §4.1):

- (a) caller (brainstorm skill) 侧 `context` 传入**决策记录文件路径** (如 `.aria/brainstorm-{id}.md` 或 `docs/decisions/DEC-*.md`);
- (b) 提取器识别"已确认决策"段 → `in_scope` / "DEFERRED" 条目 → `out_of_scope_hints` / 核心议题 → `primary_goal`;
- (c) `context` 为 Forgejo issue URL 时降级 issue_title。
- (可选 upgrade) 决策记录文件内含 issue 链接时可升级抓 issue 标题作 `primary_goal` 补充。

> **NOTE (#17 vs #79 边界)**: 本机制 (#17) 仅覆盖**审计讨论轮内 drift** (anchor 固化在单次审计周期内); 实施期偏离 Spec 的检测属 #79 (mid-implementation spec drift), 两者机制独立。#79 已落地为 `mid_post_spec` 条件触发检查点 (见检查点列表 + [agent-team-audit/references/audit-points.md](../agent-team-audit/references/audit-points.md) `## mid_post_spec`); #17 drift-checker 与 #79 spec-drift 各管各 (前者审计轮内结论漂移, 后者运行实际 vs spec 陈述)。

### per-round 入口探针 (竞品 spec 探针, Spec `sibling-spec-probe`)

**每轮** (Round 1, 2, …, N) 的入口先跑竞品 spec 探针 —— 与上方 Step 0 (`Round 1 启动前一次性`) 显式消歧: 本探针每轮入口跑, **不沿用 Step 编号**。它查的是远端仓里已落盘的 proposal 语料 (`changes/` + `archive/`, 全部分支), 与认领 (claim) 通道没有共享失效模式 —— 对方没走认领、或已 ship 归档时, 只有这条通道能把「这件事别人做完了」摆到台面上; 审计跨天时首轮结论会陈旧, 所以每轮重跑。

```bash
python3 "${CLAUDE_PLUGIN_ROOT:-aria}/skills/audit-engine/scripts/sibling_spec_probe.py" --own-spec-dir "<本轨 spec 目录名>" --repo-path "<repo root>"
```

- **(α)** 读 stdout JSON 的一等字段 `verdict`, 把结果渲染进当轮 `### Round N` 记录 (report-format.md 模板行 `- Sibling probe:`): `sibling_found` ⇒ 🔴 「检测到 N 份同 issue 的竞品 Spec」, `archive/` 命中标「已完成的 Spec」; `no_sibling_found` ⇒ 「本轮已完整扫描, 未发现同 issue 竞品」。
- **(β)** `verdict == not_established` / exit≠0 / stdout 非 JSON / `schema_version` 未知 ⇒ 渲染「**未能核实** (原因: <reason>)」, **不得**渲染为「无竞品」—— 零证据不当正证据。
- **不阻断**: 不改 verdict 计算、不改收敛判定、不改轮次路由 (advisory; 「同 issue」≠「重复劳动」, 命中是告警不是判决)。
- 探针自带 fetch (双远端约 25s/轮, 不称轻量), Convergence 与 Challenge 两模式块**都**在入口调用 (下游 Level-3 走 Challenge)。

**权威可执行版见 references/execution-modes.md `## 竞品 spec 探针 (per-round 入口)`** (十二字段 stdout 契约 / exit code / `error_kind` 集合 / 三档消费措辞)。

---

## 数据 Schema

### 结论记录

每条结论提取为结构化记录:

```json
{
  "id": "<sha256(category + ':' + scope + ':' + severity + ':' + type)[:8]>",
  "type": "decision | issue | risk",
  "severity": "critical | major | minor",
  "category": "architecture | implementation | testing | documentation",
  "scope": "affected module or file",
  "summary": "truncated to 50 words"
}
```

**`id` 字段哈希规范** (mechanical determinism, v1.17.5+):

```python
import hashlib
def finding_id(category: str, scope: str, severity: str, type: str) -> str:
    canonical = f"{category}:{scope}:{severity}:{type}"
    return hashlib.sha256(canonical.encode("utf-8")).hexdigest()[:8]
```

**输入字段** (与 4-tuple `comparison_key` 对齐, 顺序固定):
1. `category` (architecture | implementation | testing | documentation)
2. `scope` (affected module or file path)
3. `severity` (critical | major | minor)
4. `type` (decision | issue | risk)

**输入字段不包括**: `summary` (LLM 措辞每轮不同, 哈希污染), `timestamp` (轮次间漂移),
`agent_role` (跨 agent 同 finding 应同 ID).

**输出**: 8 字符 hex prefix (e.g. `a3f2c9b1`), 足够 4-tuple 笛卡尔积去重 (~10^4 量级远低于 16^8 = ~4.3×10^9).

**为何 SHA-256**: 跨语言/跨 agent 可复现 (Python stdlib + JS crypto + LLM 心算近似都能产生一致结果);
truncate to 8 chars 兼顾可读性 (报告文件名 + inline fix 引用 `R1-a3f2c9b1`).

**跨轮稳定性保证**:
- 同一 finding 在 R1 / R2 / RN 由不同 agent 报告 → 同 8-char ID
- finding 升级 severity (minor → major) → ID 改变 (符合 4-tuple `comparison_key` 不收敛逻辑)
- finding 改 category/scope → ID 改变 (设计如此, 表示语义变化)

详见 `references/convergence-algorithm.md` "comparison_key 与 finding.id 关系" 章节。

### 四元组 (比较键)

```
comparison_key = {type, severity, category, scope}
```

`summary` 不参与比较，消除 AI 措辞差异的噪声。

### 轮次状态记录

```yaml
round_state:
  round: integer
  conclusions: [conclusion_record]
  comparison_keys: set of tuples
  vote: PASS | REVISE
  incomplete: boolean          # 有 Agent 失败/超时时为 true
  timestamp: ISO 8601
```

---

## 汇总引擎

audit-engine 的内部组件，非独立 Skill。

```
输入: 同一轮所有 Agent 的原始 issues 列表

处理步骤:
  1. 合并: 收集所有 Agent 输出到统一列表
  2. 去重: 基于 {category, scope} 匹配 (复用 agent-team-audit 去重算法)
     - 相同 → 合并 found_by, 取最高 severity
  3. 冲突标记: 同 scope 矛盾意见 → 保留双方, 标记 conflicted: true
     - 不自动裁决, 留给下一轮或人工决策
  4. 结构化提取: 自由文本 → conclusion_record 格式
     - type: 从 issue 内容推断 (decision/issue/risk)
     - severity: 继承 agent-team-audit 的 Critical/Major/Minor
     - category: 归类到 architecture/implementation/testing/documentation
     - scope: 提取 affected module or file
     - summary: 截取前 50 词

输出: [conclusion_record]
```

---

## 收敛判定算法

### 四元组集合比较

```
current_keys  = { (r.type, r.severity, r.category, r.scope) for r in round_N }
previous_keys = { (r.type, r.severity, r.category, r.scope) for r in round_N_minus_1 }

conclusions_stable = (current_keys == previous_keys)
```

### 全票 PASS 检查

```
convergence 模式: convergence_agents 全员 vote == PASS
challenge 模式: objections 全部 status == resolved (无 unresolved)
```

### 振荡检测

```
# #17 振荡豁免: keys 全部取 normal_rounds 重索引序列 (is_refocus 轮剔除),
# 本节为简化概述 — 权威可执行版见 references/convergence-algorithm.md 终局 3
if len(normal_rounds) >= 3:
  keys_N   = comparison_keys(normal_rounds[-1])
  keys_N_1 = comparison_keys(normal_rounds[-2])
  keys_N_2 = comparison_keys(normal_rounds[-3])

  if keys_N == keys_N_2 and keys_N != keys_N_1:
    → 标记 oscillation: true
    → 取最后轮结论为最终结果
    → 不要求人工介入
```

### 收敛条件汇总

```
converged = conclusions_stable AND unanimous_pass
oscillation = (Round N == Round N-2) AND (Round N != Round N-1)

if converged → 输出最终报告
if oscillation → 取最后轮, 报告标记 oscillation: true
if max_rounds exhausted → 降级策略
else → continue to Round N+1
```

详细算法说明见 [references/convergence-algorithm.md](./references/convergence-algorithm.md)。

---

## 降级策略

当 max_rounds 耗尽且未收敛:

```
1. 展示摘要:
   - 最后轮结论列表
   - 各轮差异对比 (新增/移除的四元组)
   - 未收敛原因分析 (哪些结论在变动)

2. 三路径选择 (AskUserQuestion):
   [1] 接受当前结论
       → converged: false, overridden_by_user: true
       → 继续后续流程

   [2] 增加轮次
       → max_rounds += 2
       → 继续审计循环

   [3] 降级为单轮
       → 取最后轮结论作为最终结果
       → converged: false, degraded: true
```

---

## 错误处理

| 场景 | 行为 |
|------|------|
| Agent spawn 失败 | 跳过该 Agent, 当轮 `incomplete: true`, 不阻塞收敛 |
| Agent 超时 (继承 120s) | 同 spawn 失败处理 |
| API 限流 (529) | 等待 30s 重试一次, 仍失败则跳过 |
| 部分收敛 (结论无变化但有 REVISE) | 继续下一轮 |
| 全部 Agent 失败 | 当轮作废, 输出错误报告, 不计入 max_rounds |
| drift-checker spawn 失败/超时 (#17) | `drift_ratio=null` **fail-open** 按 `< warn` 档处理 + `drift_check_skipped: true`, `consecutive_refocus_count` **不增加** |

> **正交声明 (drift-checker vs round_state.incomplete)**: drift-checker 失败 (`drift_check_skipped: true`) 与审计 Agent 失败 (`round_state.incomplete: true`) **正交** — 二者独立标注、互不推导, drift-checker 失败不置 `incomplete`, Agent 失败不置 `drift_check_skipped`。**整轮超时耗尽场景的归因规则**: 审计 Agent 超时记入 `incomplete: true` (300s/轮 wall-clock 预算内); drift-checker 有独立 30-60s 超时配额 (不占 300s/轮, 见 §并发控制), 仅其自身失败/超时记 `drift_check_skipped: true` — 不得因整轮 wall-clock 耗尽把 Agent 失败误归因为 drift-checker 失败 (或反之)。

---

## 并发控制

继承 agent-team-audit 参数, 多轮场景补充约束:

| 参数 | 值 | 说明 |
|------|-----|------|
| 单轮并发 | max_parallel: 2, hard_cap: 3 | 每轮内的 Agent 并发 |
| 轮次间 | 串行 | 下一轮依赖上一轮结论 |
| 每轮超时 | 继承 300s/轮 | 独立计时, 不跨轮累计 |
| challenge 组间 | 串行 | 讨论组 → 挑战组 → 全员 (数据依赖) |

---

## 审计报告生成

### Agent dispatch contract (v1.30.1+)

audit-engine 在 dispatch agent (经 agent-team-audit) 时**必须**把 8-field YAML frontmatter template 嵌入 prompt 原文, 否则 agent 自由发挥会导致 audit report 缺 frontmatter, dashboard parser 无法解析 (Forgejo Aria #126 实测 40% 报告无 frontmatter 不可见)。

**完整契约 + 模板原文 + 责任分工 + backward-compat**: 见 [references/agent-dispatch-contract.md](./references/agent-dispatch-contract.md)。

### Pre-write validation: change_id 锚点检查 (2026-04-23, Issue #27)

写盘前验证 `change_id` 有对应的 `openspec/changes/{id}/proposal.md` 或 `openspec/archive/*-{id}/proposal.md` 背书; 缺失则拒绝写盘并提示 fix。豁免开关: `.aria/config.json` `audit.allow_dangling_change_ids=true` (默认 false)。

**完整 4-step 验证流程 + ERROR 提示文本 + 豁免设计**: 见 [references/pre-write-validation.md](./references/pre-write-validation.md)。

---

### 报告存储 + Verdict (v1.17.4+ schema)

存储路径: `.aria/audit-reports/{checkpoint}-R{round}-{timestamp_ms}-{spec_id}-{agent_role}.md` — 5-field uniqueness schema 防 4-agent 并行同毫秒落盘碰撞 (Round-2 audit P0.2 fix)。

Verdict 计算: PASS (0 Critical + 0 Major) / PASS_WITH_WARNINGS (0 Critical + ≥1 Major) / FAIL (≥1 Critical)。报告 frontmatter 11 字段含 checkpoint/mode/rounds/converged/verdict/timestamp 等。

**完整 schema (5-field uniqueness 字段定义 / 碰撞防护 / backward-compat reader / 引入背景) + Verdict 计算 + converged×verdict 组合含义 + 报告 Frontmatter 模板**: 见 [references/report-storage.md](./references/report-storage.md)。

详细报告格式见 [references/report-format.md](./references/report-format.md)。

---

## 配置依赖

通过 `.aria/config.json` 的 `audit.*` 块控制。参见 [config-loader](../config-loader/SKILL.md)。

```yaml
关键字段:
  audit.enabled: boolean        # 总开关, 默认 false
  audit.mode: string            # adaptive | convergence | challenge | manual
  audit.max_rounds: integer     # 默认 5
  audit.checkpoints: object     # 各检查点 off/convergence/challenge
  audit.teams: object           # 各检查点 Agent 分组
  audit.adaptive_rules: object  # Level → mode 映射
  audit.mid_implementation: object  # 条件触发配置
  audit.drift_guard: object     # 多轮审计 Drift Guard (#17, v1.44.0)
                                # warn_threshold: number, 默认 0.2 / refocus_threshold: number, 默认 0.5
                                # convergence_mode: boolean, 默认 false —
                                #   challenge 模式默认开 (高 drift 风险, 无需此字段);
                                #   convergence 模式可选 opt-in (本字段 true 时开);
                                #   post_closure + mid_post_spec (各 max_rounds=1) 由模式选择阶段屏蔽 (恒单轮), 不适用
  audit.allow_dangling_change_ids: boolean  # 默认 false — 豁免 change_id 锚点校验
                                            # 仅用于临时场景 (如遗留 change_id 迁移期)
                                            # 开启后写盘仍执行但记录 [WARN] 日志
                                            # (2026-04-23 新增, 修复 Issue #27)
  audit.allow_incomplete_checkpoints: boolean  # 默认 false — 豁免 pre_merge 前序 checkpoint
                                               # 报告完整性校验
                                               # 开启后 pre_merge 仍执行但记录 [WARN] 日志
                                               # (2026-04-23 新增, 修复 Issue #26)
```

**优先级**: checkpoints 显式配置 > adaptive_rules 推导 > 默认 off

**旧配置兼容**: `experiments.agent_team_audit: true` 自动映射为
`audit.enabled: true` + `audit.mode: "manual"` + 旧触发点映射。

### file-scope 二次过滤 (#58, v1.35.0)

mode **解析完成后** (checkpoints/adaptive_rules 得出 `resolved_mode`), 加一道 **file-scope 二次判定** —— 当本次变更**全部** ⊆ `audit.scope_skip_paths` (ops/docs-only) 时, 把 mode cap 到 convergence (challenge → convergence; off/convergence 不变)。避免 ops-only / docs-only PR 跑无意义 challenge audit (~5min vs 15-30min)。

> **仅 audit-on 项目生效** (audit 默认全 off)。**降级非 skip** (DEC-4): issue #58 实证 deploy script 改动 challenge 能找到真退化 (wget HTTP 4xx 退出 0) → deploy 不能全 skip, convergence 保留安全网。

```
# 变更文件来源: audit-engine 自取 (不读 snapshot — audit-engine 由 Phase Skill 调用)
base = .aria/config 配置的 base  OR  git symbolic-ref refs/remotes/origin/HEAD
       (fallback origin/main → origin/master; 全部失败 → file-scope skip + warn, 不 crash)
changed_files = git diff --name-only $(git merge-base HEAD <base>)
  # merge-base diff: 捕获 base→工作树的 committed+staged+unstaged 全部变更, 跨 checkpoint 正确
  # (注: 不能用 `git diff HEAD` — pre_merge 时 hotfix 已 commit 到 HEAD, diff HEAD 会漏掉)

if len(changed_files) == 0:            # 防 vacuous-true 空集误触
    pass-through (不降级)
elif all(f matches scope_skip_paths for f in changed_files):
    resolved_mode = min(resolved_mode, convergence)   # challenge → convergence
else:                                   # 任一业务文件 ∉ skip_paths
    resolved_mode 不变 (标准 audit)

# 匹配语义: 目录项 (尾斜杠规范化, 如 "deploy/") → path.startswith(prefix)
#          后缀项 (如 "*.md") → path.endswith(".md")
```

### emergency hotfix lane: pre_merge → convergence (#58)

当 emergency_hotfix lane (state-scanner `emergency_hotfix` 规则触发) 时, pre_merge audit **仅 `audit.enabled=true` 且 pre_merge checkpoint != off** 时降级到 convergence (不 challenge)。与 file-scope 过滤双降级时幂等 (都 → convergence)。phase-c-integrator 在 pre_merge 调用点传递此 lane 信号。

---

## 相关文档

- [references/convergence-algorithm.md](./references/convergence-algorithm.md) -- 收敛判定详细算法与边界情况
- [references/challenge-mode-schema.md](./references/challenge-mode-schema.md) -- Challenge 模式完整数据流
- [references/report-format.md](./references/report-format.md) -- 审计报告完整格式规范
- [references/execution-modes.md `## 竞品 spec 探针 (per-round 入口)`](./references/execution-modes.md) -- per-round 入口探针 stdout 契约 + 三档消费措辞 (Spec `sibling-spec-probe`)
- [agent-team-audit](../agent-team-audit/SKILL.md) -- 单轮执行引擎 (被本 Skill 调用)

---

**最后更新**: 2026-09-03 (Spec `sibling-spec-probe` — per-round 入口竞品 spec 探针; 前次 2026-06-11 #17 audit-drift-guard)

