# https://github.com/mova-compact/mova-flat-runner 项目说明书

生成时间：2026-05-14 16:26:04 UTC

## 目录

- [项目介绍](#page-introduction)
- [安装与配置](#page-installation)
- [核心概念](#page-core-concepts)
- [系统架构](#page-system-architecture)
- [传输层](#page-transport-layer)
- [安全模块](#page-security-module)
- [可用工具集](#page-available-tools)
- [业务验证器](#page-validators)
- [部署与运维](#page-deployment)

<a id='page-introduction'></a>

## 项目介绍

### 相关页面

相关主题：[安装与配置](#page-installation), [系统架构](#page-system-architecture)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [README.md](https://github.com/mova-compact/mova-flat-runner/blob/main/README.md)
- [src/index.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/index.ts)
- [src/transports/local_seam_bridge.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/transports/local_seam_bridge.ts)
- [src/security/step_mode_guard.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/security/step_mode_guard.ts)
- [src/package_support.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/package_support.ts)
- [tasks/task061.md](https://github.com/mova-compact/mova-flat-runner/blob/main/tasks/task061.md)
</details>

# 项目介绍

## 概述

`mova-flat-runner` 是 MOVA 平台的 Model Context Protocol (MCP) 服务器实现，为 AI 助手（如 Claude Desktop、Codex）提供原生 MCP 连接能力。该项目作为 MOVA 平台的轻量级运行时，通过 MCP 协议暴露标准化的工具接口，使 AI 代理能够与 MOVA 契约执行引擎进行交互。

主要特点：
- **npm 包分发**：`@leryk1981/mova-flat-runner` 形式发布，版本 `3.3.3`
- **MCP 协议兼容**：符合 Model Context Protocol 规范，可接入任何 MCP 客户端
- **工具生态**：提供 `mova_run`、`mova_query`、`mova_contract` 等核心工具集
- **混合执行模式**：支持本地执行与远程 API 调用

资料来源：[README.md:50]()

## 项目架构

### 整体架构

```
┌─────────────────────────────────────────────────────────────────┐
│                        MCP 客户端                                │
│                    (Claude Desktop / Codex)                     │
└─────────────────────────────┬───────────────────────────────────┘
                              │ MCP 协议
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                   mova-flat-runner (MCP Server)                  │
├─────────────────────────────────────────────────────────────────┤
│  ┌──────────────┐  ┌──────────────┐  ┌───────────────────────┐  │
│  │  HTTP Handler │  │  Tool Router  │  │   Security Guards    │  │
│  └──────────────┘  └──────────────┘  └───────────────────────┘  │
├─────────────────────────────────────────────────────────────────┤
│  ┌──────────────────┐    ┌──────────────────────────────────┐  │
│  │  Local Seam Bridge │    │       Remote API Transport       │  │
│  └──────────────────┘    └──────────────────────────────────┘  │
└─────────────────────────────┬───────────────────────────────────┘
          ┌───────────────────┼───────────────────┐
          ▼                   ▼                   ▼
   ┌─────────────┐     ┌─────────────┐     ┌─────────────┐
   │  Local JS   │     │  MOVA API   │     │  Contract   │
   │  Execution  │     │  (Remote)   │     │  Registry   │
   └─────────────┘     └─────────────┘     └─────────────┘
```

资料来源：[README.md:25-45]()

### 核心目录结构

| 目录/文件 | 用途 |
|-----------|------|
| `cmd/` | 应用程序入口点 |
| `internal/api/` | HTTP 处理器和路由 |
| `internal/auth/` | 认证模块（GitHub OAuth、JWT、命名空间阻塞） |
| `internal/config/` | 配置管理 |
| `internal/database/` | 数据持久化（PostgreSQL） |
| `internal/service/` | 业务逻辑层 |
| `internal/telemetry/` | 指标和监控 |
| `internal/validators/` | 输入验证 |
| `pkg/` | 公共包 |
| `deploy/` | Pulumi 部署配置 |
| `docs/` | 文档 |

资料来源：[README.md:28-40]()

## 可用工具

MCP 服务器暴露以下工具集：

### 工具列表

| 工具名称 | 功能描述 |
|----------|----------|
| `mova_health` | 健康检查，验证 MOVA API 可用性 |
| `mova_registry` | 查询契约注册表，获取可用契约清单 |
| `mova_run` | 执行标准契约流程 |
| `mova_query` | 查询契约执行状态、审计记录 |
| `mova_decide` | 处理决策请求 |
| `mova_connector` | 管理外部系统连接 |
| `mova_contract` | 契约生命周期管理（注册、运行、状态查询） |

资料来源：[README.md:95-102]()

### mova_contract 子命令

`mova_contract` 工具支持以下子命令：

| 子命令 | HTTP 方法 | 端点 | 说明 |
|--------|-----------|------|------|
| `register` | POST | `/api/v1/contracts/register` | 注册新契约 |
| `run` | POST | `/run/{contract_id}` | 执行契约 |
| `run_status` | GET | `/run/{run_id}/status` | 获取运行状态 |
| `step_complete` | POST | `/run/{run_id}/step/{step_id}/complete` | 步骤完成确认 |
| `gate_approve` | POST | `/run/{run_id}/step/{step_id}/gate/approve` | 人为门审批 |
| `gate_reject` | POST | `/run/{run_id}/step/{step_id}/gate/reject` | 人为门拒绝 |

资料来源：[src/index.ts:120-175]()

## 安全机制

### 步骤模式守卫

项目实现了严格的步骤执行模式验证，防止配置错误：

```typescript
// 步骤执行模式与必需字段校验
if (mode === "AI_ATOMIC" && asStr(step.model) === null) {
    // AI_ATOMIC 模式必须指定 model 字段
}
if (mode === "CONTRACT_CALL" && asStr(step.contract_id) === null) {
    // CONTRACT_CALL 模式必须指定 contract_id
}
if (mode === "HUMAN_GATE" && !Array.isArray(step.decision_options)) {
    // HUMAN_GATE 模式必须指定 decision_options 数组
}
```

资料来源：[src/security/step_mode_guard.ts:35-55]()

### 类定义守卫

禁止在流程中内联类定义字段，确保安全边界：

```typescript
const FORBIDDEN_FLOW_KEYS = [
  "class_definition_inline",
  "class_def_override",
  "class_def",
];
```

资料来源：[src/security/step_mode_guard.ts:8-13]()

### 人为门验证

`step_complete` 操作不能用于 HUMAN_GATE 类型步骤，必须使用专用的 `gate_approve` / `gate_reject` 路径：

资料来源：[src/index.ts:130-145]()

## 包验证系统

### 全局文件验证

`validatePackageGlobalShape` 函数验证包全局配置的合法性：

| 验证项 | 规则 |
|--------|------|
| `global_id` | 必需字符串，非空 |
| `version` | 必需字符串，非空 |
| `scope` | 必须为 `"contract_package"` |
| `extends` | 若存在，必须为数组 |
| `semantic_roles` | 非空数组，每个元素为对象 |

资料来源：[src/package_support.ts:15-35]()

### 语义角色字段验证

`semantic_roles` 数组中的每个元素必须包含以下字段：

| 字段 | 类型 | 必需 |
|------|------|------|
| `id` | string | 是 |
| `role` | string | 是 |
| `authority` | string | 是 |
| `maturity` | string | 是 |
| `applies_to` | array | 否 |
| `allowed_use` | array | 是 |
| `forbidden_use` | array | 是 |
| `notes` | string | 否 |

资料来源：[dist-test/src/package_support.js:12-20]()

## 本地桥接传输

### 本地缝隙解析

`resolveLocalSeamLocator` 处理本地执行环境的路径解析：

```typescript
const packagePath = initialInputs.package_path 
    ?? process.env.MOVA_SANDBOX_PACKAGE_PATH 
    ?? "D:\\Projects_MOVA\\mova-intent\\contracts\\dockerfile-nodejs-v1";
```

资料来源：[dist-test/src/transports/local_seam_bridge.js:45-50]()

### 桥接输出格式

本地执行返回标准化的桥接响应：

```typescript
return {
    ok: true,
    bridge: {
        ok: true,
        bridge_source: "mova_flat_runner_canonical_bridge",
        status: "completed" | "advanced" | "human_gate_required",
        contract_ref: request.contractRef,
        next_phase: { phase: "EXECUTION" | "WAIT_HUMAN" },
        verification_payload: {
            status: "PASS",
            checks: [{ layer: "Invariant", result: "PASS" }]
        }
    }
};
```

资料来源：[src/transports/local_seam_bridge.ts:12-40]()

## 自定义契约桥接

### 问题背景

自定义契约（`local-*`、`remote-*` 前缀）在 `/run/*` 命名空间执行时，可能在 `/api/v1/contracts/*` 查询命名空间中不可见，导致 `mova_query` 返回 404。

资料来源：[tasks/task061.md:1-10]()

### 解决方案

实现了内存桥接映射来维护 `contract_id -> run_id` 关系：

```typescript
// 内存桥接映射
const CUSTOM_RUN_BRIDGE = new Map<contract_id, { run_id, updated_at, source_url? }>();

// 在 run 和 run_status 时捕获映射
function rememberCustomRun(contractId, runId, sourceUrl) {
    CUSTOM_RUN_BRIDGE.set(contractId, { 
        run_id: runId, 
        updated_at: new Date().toISOString(),
        source_url 
    });
}

// 在 mova_query 404 时探测契约元数据
async function getMyContractRecord(config, contractId) {
    const list = await movaGet(config, "/api/v1/contracts/my");
    return list.contracts.find(item => item.contract_id === contractId);
}
```

资料来源：[src/index.ts:75-95]()

### 桥接状态响应

当检测到自定义契约时，`mova_query status` 返回桥接状态：

```json
{
    "ok": true,
    "bridge_mode": "custom_contract_run_namespace_bridge_v1",
    "contract_record": { ... },
    "run_ref": "run-xxx",
    "run_status": { ... }
}
```

资料来源：[tasks/task061.md:18-28]()

## 远程 API 传输

### 步骤执行流程

远程执行按顺序执行三个核心步骤：

```mermaid
graph TD
    A[开始] --> B[POST /api/v1/contracts/{id}/step - analyze]
    B --> C{成功?}
    C -->|否| D[返回错误]
    C -->|是| E[GET /api/v1/contracts/{id}/steps/analyze/output]
    E --> F[POST /api/v1/contracts/{id}/step - verify]
    F --> G{成功?}
    G -->|否| D
    G -->|是| H[POST /api/v1/contracts/{id}/step - decide]
    H --> I{结果等待?}
    I -->|waiting_human| J[获取 decision_point]
    I -->|completed| K[返回完成状态]
    J --> K
```

资料来源：[dist-test/src/transports/remote_api.js:30-80]()

### 验证器集成

分析步骤后，系统会执行验证器注册表中的验证器：

```typescript
for (const validator of validators) {
    const fn = VALIDATOR_REGISTRY.get(validator.validator_id);
    if (!fn) {
        analysis[`${validator.step_id}_error`] = `VALIDATOR_NOT_ALLOWED: "${validator.validator_id}" not in registry`;
        continue;
    }
    const resultValue = fn(validatorContext);
    Object.assign(analysis, resultValue.value ?? {});
}
```

资料来源：[dist-test/src/transports/remote_api.js:15-30]()

## 环境配置

### 必需环境变量

| 变量名 | 说明 | 示例值 |
|--------|------|--------|
| `MOVA_API_URL` | MOVA API 端点 | `https://api.mova-lab.eu` |
| `MOVA_API_KEY` | API 认证密钥 | `__SET_MOVA_API_KEY__` |
| `LLM_KEY` | LLM 提供商密钥 | `__SET_LLM_KEY__` |
| `LLM_MODEL` | LLM 模型标识 | `openai/gpt-4o-mini` |

### 可选环境变量

| 变量名 | 说明 | 默认值 |
|--------|------|--------|
| `MOVA_API_TIMEOUT_MS` | API 超时时间（毫秒） | `30000` |
| `MOVA_HTTP_PORT` | 本地 HTTP 模式端口 | `3796` |
| `MOVA_INVOKE_TOKEN` | 本地调用令牌 | - |
| `MOVA_SANDBOX_PACKAGE_PATH` | 本地沙箱包路径 | - |
| `MOVA_SANDBOX_PROJECT_PATH` | 本地沙箱项目路径 | - |

资料来源：[README.md:55-80]()

### Claude Desktop 配置示例

```json
{
  "mcpServers": {
    "mova": {
      "command": "npx",
      "args": ["-y", "@leryk1981/mova-flat-runner@3.3.3"],
      "env": {
        "MOVA_API_URL": "https://api.mova-lab.eu",
        "MOVA_API_KEY": "__SET_MOVA_API_KEY__",
        "LLM_KEY": "__SET_LLM_KEY__",
        "LLM_MODEL": "openai/gpt-4o-mini"
      }
    }
  }
}
```

资料来源：[README.md:85-100]()

## 公共输出清理

`sanitizePublicShape` 函数确保内部字段不会泄漏到公共 API 响应中：

```typescript
function sanitizePublicShape(value) {
    for (const [key, child] of Object.entries(value)) {
        if (["bridge_anchors", "last_terminal_bridge", "terminal_commit_count", 
             "_state15_bridge", "trace", "outputs", "context"].includes(key)) {
            return false;
        }
        // 递归检查
    }
    return true;
}
```

资料来源：[src/transports/local_seam_bridge.ts:65-80]()

## 开发与发布

### 构建命令

| 命令 | 说明 |
|------|------|
| `make check` | 运行 lint、单元测试和集成测试 |
| `make publisher` | 构建 MCP 发布工具 |
| `make help` | 查看所有可用命令 |

### 发布流程

项目使用 MCP Publisher 工具进行服务器发布：

```bash
# 构建发布工具
make publisher

# 使用发布工具
./bin/mcp-publisher --help
```

资料来源：[README.md:15-25]()

### 构建状态

| 分支 | 说明 |
|------|------|
| `main` | 最新主分支构建（稳定版） |
| `main-<date>-<sha>` | 特定提交构建（开发版） |

资料来源：[README.md:3-6]()

## 安全注意事项

> ⚠️ 切勿提交真实的 `MOVA_API_KEY`、`LLM_KEY` 或 `MOVA_INVOKE_TOKEN`。MCP 注册表会向 MCP 客户端提供服务器列表，如同一个 MCP 服务器的应用商店。

资料来源：[README.md:105-106]()

---

<a id='page-installation'></a>

## 安装与配置

### 相关页面

相关主题：[项目介绍](#page-introduction), [部署与运维](#page-deployment)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [README.md](https://github.com/mova-compact/mova-flat-runner/blob/main/README.md)
- [package.json](https://github.com/mova-compact/mova-flat-runner/blob/main/package.json)
- [src/index.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/index.ts)
- [smithery.yaml](https://github.com/mova-compact/mova-flat-runner/blob/main/smithery.yaml)
- [server.json](https://github.com/mova-compact/mova-flat-runner/blob/main/server.json)
</details>

# 安装与配置

## 概述

`mova-flat-runner` 是一个基于 Model Context Protocol (MCP) 的服务器，用于执行受治理的 AI 工作流程。该项目支持发票 OCR、AML 分类、信贷审查以及带有人工审批环节的自定义合约执行，并提供完整的审计追踪功能。

资料来源：[package.json:3-4]()

## 安装方式

### 通过 npm 安装（推荐）

使用 npx 直接运行，无需全局安装：

```bash
npx -y @leryk1981/mova-flat-runner@3.3.3
```

### 手动构建安装

从源码构建：

```bash
git clone https://github.com/mova-compact/mova-flat-runner.git
cd mova-flat-runner
npm install
npm run build
```

构建产物位于 `dist/` 目录，可执行文件为 `mova-mcp`。

资料来源：[README.md:54](), [package.json:23]()

## 环境变量配置

### 必须配置的环境变量

| 变量名 | 说明 | 示例值 |
|--------|------|--------|
| `MOVA_API_URL` | MOVA API 服务地址 | `https://api.mova-lab.eu` |
| `MOVA_API_KEY` | MOVA API 访问密钥 | `__SET_MOVA_API_KEY__` |
| `LLM_KEY` | LLM 服务密钥 | `__SET_LLM_KEY__` |
| `LLM_MODEL` | 使用的 LLM 模型 | `openai/gpt-4o-mini` |

资料来源：[README.md:35-39]()

### 可选环境变量（本地 HTTP 模式）

| 变量名 | 说明 | 默认值 |
|--------|------|--------|
| `MOVA_API_TIMEOUT_MS` | API 请求超时时间（毫秒） | `30000` |
| `MOVA_HTTP_PORT` | 本地 HTTP 服务端口 | `3796` |
| `MOVA_INVOKE_TOKEN` | 本地调用令牌 | 无默认值 |

资料来源：[README.md:44-46]()

## MCP 客户端集成配置

### Claude Desktop 配置

在 `claude_desktop_config.json` 中添加以下配置：

```json
{
  "mcpServers": {
    "mova": {
      "command": "npx",
      "args": ["-y", "@leryk1981/mova-flat-runner@3.3.3"],
      "env": {
        "MOVA_API_URL": "https://api.mova-lab.eu",
        "MOVA_API_KEY": "__SET_MOVA_API_KEY__",
        "LLM_KEY": "__SET_LLM_KEY__",
        "LLM_MODEL": "openai/gpt-4o-mini"
      }
    }
  }
}
```

资料来源：[README.md:62-73]()

### Codex 配置

在 `codex_config.json` 中添加以下配置：

```json
{
  "mcp_servers": {
    "mova": {
      "command": "npx",
      "args": ["-y", "@leryk1981/mova-flat-runner@3.3.3"],
      "startup_timeout_sec": 90.0,
      "env": {
        "MOVA_API_URL": "https://api.mova-lab.eu",
        "MOVA_API_KEY": "__SET_MOVA_API_KEY__",
        "LLM_KEY": "__SET_LLM_KEY__",
        "LLM_MODEL": "openai/gpt-4o-mini"
      }
    }
  }
}
```

### TOML 格式配置

```toml
[mcp_servers.mova]
command = "npx"
args = ["-y", "@leryk1981/mova-flat-runner@3.3.3"]
startup_timeout_sec = 90.0

[mcp_servers.mova.env]
MOVA_API_URL = "https://api.mova-lab.eu"
MOVA_API_KEY = "__SET_MOVA_API_KEY__"
LLM_KEY = "__SET_LLM_KEY__"
LLM_MODEL = "openai/gpt-4o-mini"
```

资料来源：[README.md:75-97]()

## 服务健康检查

部署完成后，可通过以下命令验证服务可用性：

```bash
curl -sS https://api.mova-lab.eu/health
```

资料来源：[README.md:55-57]()

## 可用工具列表

配置完成后，MCP 服务器提供以下工具：

| 工具名称 | 功能说明 |
|----------|----------|
| `mova_health` | 服务健康检查 |
| `mova_registry` | 查看合约注册表 |
| `mova_run` | 执行内置工作流程合约 |
| `mova_query` | 查询合约状态和审计信息 |
| `mova_decide` | 触发决策流程 |
| `mova_connector` | 连接器管理 |
| `mova_contract` | 自定义合约注册与执行 |

资料来源：[README.md:99-107]()

## 安全注意事项

⚠️ **重要提示**：请勿将真实的 `MOVA_API_KEY`、`LLM_KEY` 或 `MOVA_INVOKE_TOKEN` 提交到版本控制系统。

环境变量占位符使用说明：

- 在文档中使用 `__SET_MOVA_API_KEY__` 等占位符
- 在本地配置中使用真实的密钥值
- 确保配置文件已添加到 `.gitignore`

资料来源：[README.md:47-49]()

## 配置架构图

```mermaid
graph TD
    A[MCP Client] -->|npx启动| B[mova-flat-runner]
    B -->|环境变量| C[MovaConfig]
    C -->|API调用| D[MOVA API Server]
    D -->|状态/结果| B
    B -->|工具响应| A
    
    C -->|必需变量| E[MOVA_API_URL]
    C -->|必需变量| F[MOVA_API_KEY]
    C -->|必需变量| G[LLM_KEY]
    C -->|必需变量| H[LLM_MODEL]
    
    C -->|可选变量| I[MOVA_API_TIMEOUT_MS]
    C -->|可选变量| J[MOVA_HTTP_PORT]
    C -->|可选变量| K[MOVA_INVOKE_TOKEN]
```

## 开发命令

项目提供以下 Makefile 目标用于开发：

| 命令 | 说明 |
|------|------|
| `make check` | 运行代码检查、单元测试和集成测试 |
| `make publisher` | 构建 MCP 发布工具 |
| `make help` | 查看所有可用命令 |

资料来源：[README.md:29-34]()

## 验证安装

安装完成后，可通过执行以下步骤验证配置是否正确：

1. 检查 MCP 客户端是否成功连接
2. 调用 `mova_health` 工具验证 API 连通性
3. 尝试调用 `mova_registry` 查看合约列表

如遇到问题，请检查：

- 环境变量是否正确设置
- API URL 是否可访问
- API 密钥是否有效

---

<a id='page-core-concepts'></a>

## 核心概念

### 相关页面

相关主题：[系统架构](#page-system-architecture), [可用工具集](#page-available-tools)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [src/index.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/index.ts)
- [src/types.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/types.ts)
- [src/schemas.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/schemas.ts)
- [src/security/step_mode_guard.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/security/step_mode_guard.ts)
- [src/transports/local_seam_bridge.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/transports/local_seam_bridge.ts)
- [src/package_support.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/package_support.ts)
- [src/transports/remote_api.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/transports/remote_api.ts)
</details>

# 核心概念

本文档介绍 mova-flat-runner（MCP 服务器）的核心架构概念，帮助开发者理解系统的工作原理、设计决策和安全机制。

## 系统概述

mova-flat-runner 是一个基于 **Model Context Protocol (MCP)** 的服务器实现，用于托管治理型 AI 工作流。系统支持合同执行、人工审批门控、审计追踪等功能。

**核心能力：**

- 内置合同类型（发票 OCR、反洗钱审查、信用审查、投诉处理）
- 自定义合同注册与执行
- 人工干预门控（HUMAN_GATE）
- 多步骤执行模式验证
- 本地沙箱执行与远程 API 调用双路径

资料来源：[package.json](https://github.com/mova-compact/mova-flat-runner/blob/main/package.json)

---

## MCP 工具接口

MCP 服务器暴露一组标准化工具，供 AI 客户端调用。工具通过 `executeTool` 函数统一分发。资料来源：[src/index.ts:79-450]()

### 可用工具列表

| 工具名称 | 功能描述 | 主要参数 |
|---------|---------|---------|
| `mova_health` | 健康检查 | 无 |
| `mova_registry` | 获取注册合同列表 | 无 |
| `mova_run` | 执行内置合同 | `contract_type`, `inputs` |
| `mova_query` | 查询合同状态/审计 | `contract_id`, `view` |
| `mova_decide` | 执行决策步骤 | `contract_id`, `step_id`, `choice` |
| `mova_contract` | 自定义合同管理 | `action`, `contract_id` 等 |
| `mova_connector` | 连接器操作 | `action`, `connector_id` |

资料来源：[src/index.ts:79-100]()

### mova_query 视图模式

`mova_query` 支持三种视图模式：

| 视图模式 | 用途 | 返回内容 |
|---------|------|---------|
| `status` | 状态查询 | 合同执行状态、当前步骤、运行引用 |
| `audit` | 完整审计 | 结构化审计记录 |
| `audit_compact` | 紧凑审计 | 最小化审计摘要 |

资料来源：[tasks/task061.md]()

---

## 合同类型与执行模式

### 内置合同类型

系统预置以下合同类型：

| 合同类型 | 用途 | 执行模式 |
|---------|------|---------|
| `complaint` | 投诉处理 | 远程 API |
| `invoice-ocr` | 发票 OCR 识别 | 本地/远程 |
| `aml-triage` | 反洗钱审查 | 混合模式 |
| `credit-review` | 信用审查 | AI_ATOMIC |

资料来源：[src/index.ts:81-120]()

### 执行模式（Step Execution Mode）

合同流程由多个步骤组成，每步骤有独立的执行模式：

```mermaid
graph TD
    A[Step] --> B{执行模式判定}
    B --> C[DETERMINISTIC]
    B --> D[AI_ATOMIC]
    B --> E[CONTRACT_CALL]
    B --> F[HUMAN_GATE]
    
    C --> C1[本地 JS 校验]
    D --> D1[LLM 调用]
    E --> E1[调用其他合同]
    F --> F1[等待人工审批]
```

| 执行模式 | 说明 | 必需字段 | 安全约束 |
|---------|------|---------|---------|
| `DETERMINISTIC` | 本地确定性校验 | 无特殊要求 | 禁止声明 `model` 字段 |
| `AI_ATOMIC` | LLM 原子操作 | `model` | 必须声明 `model` |
| `CONTRACT_CALL` | 跨合同调用 | `contract_id` | 被调用合同必须存在 |
| `HUMAN_GATE` | 人工审批门控 | `decision_options` | 需要 `gate_approve` / `gate_reject` |

资料来源：[src/security/step_mode_guard.ts:1-80]()

### 执行模式验证规则

`step_mode_guard.ts` 实现以下验证：

```typescript
// DETERMINISTIC 禁止使用 model
if (mode === "DETERMINISTIC" && step.model !== undefined) {
  violations.push({ kind: "deterministic_with_model", ... });
}

// AI_ATOMIC 必须声明 model
if (mode === "AI_ATOMIC" && asStr(step.model) === null) {
  violations.push({ kind: "ai_atomic_without_model", ... });
}

// CONTRACT_CALL 必须指定 contract_id
if (mode === "CONTRACT_CALL" && asStr(step.contract_id) === null) {
  violations.push({ kind: "contract_call_without_contract_id", ... });
}

// HUMAN_GATE 必须声明决策选项
if (mode === "HUMAN_GATE" && !Array.isArray(step.decision_options)) {
  violations.push({ kind: "human_gate_without_decisions", ... });
}
```

资料来源：[src/security/step_mode_guard.ts:20-60]()

---

## 人工干预门控（HUMAN_GATE）

### 工作流程

```mermaid
sequenceDiagram
    participant Client
    participant MCP
    participant API
    participant Human
    
    Client->>MCP: mova_run (含 HUMAN_GATE)
    MCP->>API: 启动合同执行
    API-->>MCP: status=waiting_human
    MCP-->>Client: 返回待审批状态
    
    Client->>MCP: mova_decide (choice)
    MCP->>API: /decision
    API->>Human: 通知待审批
    Human-->>API: 审批结果
    API-->>MCP: 继续执行
    MCP-->>Client: 执行完成
```

### 安全约束

`step_complete` 操作在 HUMAN_GATE 步骤上被禁止执行：

```typescript
const gateGuard = await assertNotHumanGate(
  config, 
  args.run_id, 
  args.step_id, 
  requestId
);
if (gateGuard) return gateGuard;
```

HUMAN_GATE 必须通过专用路径完成审批：

- `gate_approve` - 批准当前步骤
- `gate_reject` - 拒绝当前步骤

资料来源：[src/index.ts:180-200]()

---

## 自定义合同桥接机制

### 问题背景

自定义合同使用 `/run/*` 命名空间执行，但查询时可能返回 404（合同不存在于 `/api/v1/contracts/*` 命名空间）。资料来源：[tasks/task061.md]()

### 桥接解决方案

```mermaid
graph LR
    A[自定义合同注册] --> B[CUSTOM_RUN_BRIDGE]
    C[mova_run] --> B
    D[run_status] --> B
    
    B --> E{contract_id 映射}
    
    E --> F[存在映射?]
    F -->|是| G[返回 bridged status]
    F -->|否| H[返回 404]
    
    G --> I[probe /api/v1/contracts/my]
    I --> J[返回结构化响应]
```

### 内存桥接映射

```typescript
const CUSTOM_RUN_BRIDGE: Map<contract_id, {
  run_id: string,
  updated_at: string,
  source_url?: string
}> = new Map();
```

关键函数：

| 函数 | 用途 |
|------|------|
| `rememberCustomRun` | 缓存 contract_id → run_id 映射 |
| `getMyContractRecord` | 查询 /api/v1/contracts/my 获取元数据 |

资料来源：[src/index.ts:40-65]()

### 桥接响应格式

```json
{
  "ok": true,
  "bridge_mode": "custom_contract_run_namespace_bridge_v1",
  "contract_record": { ... },
  "run_ref": "run-xxx",
  "run_status": { ... }
}
```

---

## 包结构验证

### 包文件类型

| 文件类型 | 用途 | 验证内容 |
|---------|------|---------|
| `global` | 全局元数据 | 合同包范围、语义角色 |
| `manifest` | 包清单 | schema_id、引用关系 |
| `flow` | 流程定义 | 步骤、执行模式 |

资料来源：[src/package_support.ts:1-150]()

### Global 文件验证

```typescript
const allowedKeys = new Set([
  "schema_id", "global_id", "version", "scope",
  "extends", "semantic_roles", "non_authority_rules"
]);

// 必需字段
- schema_id: "mova.contract.global.v1"
- global_id: 非空字符串
- version: 非空字符串
- scope: "contract_package"
- semantic_roles: 非空数组
```

资料来源：[src/package_support.ts:60-100]()

### Manifest 文件验证

```typescript
const allowedKeys = new Set([
  "schema_id", "package_id", "version", "global_ref",
  "flow_ref", "classification_policy_ref",
  "classification_result_set_ref", "classification_result_refs",
  "runtime_binding_set_ref", "model_refs", "fixture_refs",
  "package_invariants"
]);
```

---

## API 传输层

### 请求方法

```typescript
export const movaGet    = (config, path) => movaRequest(config, "GET", path);
export const movaPost   = (config, path, body) => movaRequest(config, "POST", path, body);
export const movaPut    = (config, path, body) => movaRequest(config, "PUT", path, body);
export const movaDelete = (config, path) => movaRequest(config, "DELETE", path);
```

资料来源：[src/transports/remote_api.ts:1-50]()

### 远程执行流程

```mermaid
graph TD
    A[movaRunStepsRemote] --> B[analyze 步骤]
    B --> C{result.ok?}
    C -->|是| D[verify 步骤]
    C -->|否| E[返回错误]
    D --> F{status?}
    F -->|waiting_human| G[返回待审批]
    F -->|completed| H[decide 步骤]
    H --> I[返回结果]
```

---

## 配置管理

### 环境变量

| 变量名 | 必需 | 默认值 | 说明 |
|-------|------|-------|------|
| `MOVA_API_URL` | 是 | - | API 基础地址 |
| `MOVA_API_KEY` | 是 | - | API 认证密钥 |
| `LLM_KEY` | 是 | - | LLM 提供商密钥 |
| `LLM_MODEL` | 是 | `openai/gpt-4o-mini` | LLM 模型标识 |
| `MOVA_API_TIMEOUT_MS` | 否 | 30000 | 请求超时（毫秒） |
| `MOVA_HTTP_PORT` | 否 | 3796 | 本地 HTTP 模式端口 |
| `MOVA_INVOKE_TOKEN` | 否 | - | 本地调用令牌 |

资料来源：[README.md](https://github.com/mova-compact/mova-flat-runner/blob/main/README.md)

### MovaConfig 类型

```typescript
interface MovaConfig {
  apiUrl: string;
  apiKey: string;
  llmKey: string;
  llmModel: string;
  timeoutMs?: number;
  httpPort?: number;
  invokeToken?: string;
}
```

---

## 错误处理

### 错误代码体系

| 错误代码 | HTTP 等价 | 说明 |
|---------|----------|------|
| `UNKNOWN_CONTRACT_TYPE` | 400 | 合同类型未注册 |
| `LOCAL_VALIDATION_FAILED` | 400 | 本地验证失败 |
| `API_REQUEST_FAILED` | 502 | 后端 API 请求失败 |
| `STEP_MODE_FIELD_MISMATCH` | 400 | 执行模式字段不匹配 |
| `CONTRACT_NOT_FOUND` | 404 | 合同不存在 |

### 标准错误响应格式

```json
{
  "ok": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "人类可读的错误描述",
    "details": { ... },
    "request_id": "req-xxx"
  }
}
```

---

## 安全机制

### 步骤模式守卫（Step Mode Guard）

```mermaid
graph TD
    A[执行步骤] --> B[findStepModeViolations]
    B --> C{违规数量}
    C -->|0| D[执行通过]
    C -->|>0| E[返回 400 错误]
    
    D --> F[assertStepModesValid]
    F --> G[继续执行]
```

核心安全检查：

1. **DETERMINISTIC 模式**：禁止声明 LLM model 字段
2. **AI_ATOMIC 模式**：必须声明 model 字段
3. **CONTRACT_CALL 模式**：必须指定 target contract_id
4. **HUMAN_GATE 模式**：禁止绕过专用审批路径

资料来源：[src/security/step_mode_guard.ts:80-110]()

### 公共形状清理（sanitizePublicShape）

过滤内部字段，确保审计响应不暴露实现细节：

```typescript
const BLOCKED_KEYS = [
  "bridge_anchors",
  "last_terminal_bridge",
  "terminal_commit_count",
  "_state15_bridge",
  "trace",
  "outputs",
  "context"
];
```

---

## MCP 资源端点

| URI | 内容 |
|-----|------|
| `mova://registry` | 注册合同清单 |
| `mova://schemas/envelopes` | 信封 JSON Schema |
| `mova://contracts/{type}/manifest` | 指定合同类型的清单 |

---

## 快速开始

### Claude Desktop 配置

```json
{
  "mcpServers": {
    "mova": {
      "command": "npx",
      "args": ["-y", "@leryk1981/mova-flat-runner@3.3.3"],
      "env": {
        "MOVA_API_URL": "https://api.mova-lab.eu",
        "MOVA_API_KEY": "__SET_MOVA_API_KEY__",
        "LLM_KEY": "__SET_LLM_KEY__",
        "LLM_MODEL": "openai/gpt-4o-mini"
      }
    }
  }
}
```

### 健康检查

```bash
curl -sS https://api.mova-lab.eu/health

---

<a id='page-system-architecture'></a>

## 系统架构

### 相关页面

相关主题：[传输层](#page-transport-layer), [安全模块](#page-security-module), [核心概念](#page-core-concepts)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [src/index.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/index.ts)
- [src/transports/local_seam_bridge.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/transports/local_seam_bridge.ts)
- [src/transports/remote_api.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/transports/remote_api.ts)
- [src/security/step_mode_guard.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/security/step_mode_guard.ts)
- [src/package_support.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/package_support.ts)
- [src/types.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/types.ts)
</details>

# 系统架构

## 概述

mova-flat-runner 是 MOVA 平台的 MCP（Model Context Protocol）服务器实现，为 MCP 客户端（如 Claude Desktop、Cursor）提供受治理的 AI 工作流执行能力。该系统支持发票 OCR、AML 审查、信贷审核等业务流程，并内置人工审批门（Human Approval Gates）和完整的审计追踪机制。

资料来源：[package.json:1-20]()

## 整体架构

### 核心定位

该系统作为 MCP 协议的服务端实现，运行于 Node.js 18+ 环境，通过 MCP 协议暴露一组标准化的工具（Tools），供 AI 代理调用。工作流程执行可以采用本地执行或远程 API 调用两种模式。

资料来源：[src/index.ts:1-50]()

```mermaid
graph TD
    subgraph MCP客户端层
        A[Claude Desktop] -->|MCP Protocol| B[mova-mcp Server]
        C[Cursor] -->|MCP Protocol| B
    end
    
    subgraph 服务端核心
        B -->|executeTool| D[工具调度器]
        D --> E[本地执行路径]
        D --> F[远程 API 路径]
    end
    
    subgraph 本地执行
        E --> G[step_mode_guard]
        G --> H[Local Seam Bridge]
    end
    
    subgraph 远程执行
        F --> I[remote_api]
        I --> J[MOVA Backend API]
    end
    
    subgraph 安全层
        G --> K[验证器注册表]
        H --> L[Bridge 状态管理]
    end
```

## 模块划分

### 项目结构

```
mova-flat-runner/
├── cmd/                     # 应用入口点
├── src/                     # TypeScript 源码
│   ├── index.ts             # MCP 服务器主入口
│   ├── types.ts             # 类型定义
│   ├── client.ts            # MCP 协议客户端
│   ├── package_support.ts   # 合约包支持
│   ├── schemas.ts           # JSON Schema 定义
│   ├── transports/          # 传输层
│   │   ├── local_seam_bridge.ts   # 本地接缝桥接器
│   │   └── remote_api.ts          # 远程 API 客户端
│   └── security/            # 安全模块
│       └── step_mode_guard.ts     # 步骤模式守卫
├── internal/                # 内部应用代码
└── pkg/                     # 公共包
```

资料来源：[README.md](https://github.com/mova-compact/mova-flat-runner/blob/main/README.md)

### 核心组件表

| 组件 | 文件路径 | 职责 |
|------|----------|------|
| MCP 服务器入口 | `src/index.ts` | 工具注册、请求路由、响应序列化 |
| 工具调度器 | `src/index.ts:executeTool` | 根据工具名称分发请求 |
| 本地执行引擎 | `src/transports/local_seam_bridge.ts` | 本地工作流执行与状态管理 |
| 远程 API 客户端 | `src/transports/remote_api.ts` | 与 MOVA 后端 API 交互 |
| 安全守卫 | `src/security/step_mode_guard.ts` | 执行模式验证与字段一致性检查 |
| 包验证器 | `src/package_support.ts` | 合约包结构与语义角色验证 |

资料来源：[src/index.ts:100-200](), [src/transports/local_seam_bridge.ts](), [src/security/step_mode_guard.ts]()

## 传输层架构

### 双路径执行模式

系统支持两种执行路径，根据配置自动选择：

| 模式 | 触发条件 | 执行位置 | 特点 |
|------|----------|----------|------|
| 本地执行 | `MOVA_HTTP_PORT` 环境变量设置 | Node.js 运行时内 | 低延迟、无网络依赖 |
| 远程执行 | 默认模式 | MOVA Backend API | 集中管理、审计追踪 |

资料来源：[src/transports/remote_api.ts:1-30](), [src/transports/local_seam_bridge.ts:1-50]()

### 远程 API 调用流程

```mermaid
sequenceDiagram
    participant MCP as MCP 客户端
    participant Server as mova-mcp Server
    participant API as 远程 API
    
    MCP->>Server: mova_run
    Server->>API: POST /api/v1/contracts/{id}/step
    API-->>Server: Step Result
    Server->>API: GET /api/v1/contracts/{id}/steps/{step}/output
    API-->>Server: Analysis Output
    Server->>API: POST /api/v1/contracts/{id}/step (decide)
    API-->>Server: Decision Result
    Server-->>MCP: Complete Response
```

### 本地桥接器状态机

`local_seam_bridge.ts` 实现了工作流步骤的本地执行与状态转换：

```mermaid
stateDiagram-v2
    [*] --> EXECUTION: 启动步骤
    EXECUTION --> WAIT_HUMAN: HUMAN_GATE 模式
    WAIT_HUMAN --> EXECUTION: gate_approve
    EXECUTION --> COMPLETED: terminal_outcome = true
    EXECUTION --> ADVANCED: 正常完成
    WAIT_HUMAN --> REJECTED: gate_reject
```

状态判定逻辑根据 `execution_mode` 和 `humanDecision` 字段确定返回状态：

| 执行模式 | 条件 | 返回状态 |
|----------|------|----------|
| AI_ATOMIC | 始终 | `completed` |
| HUMAN_GATE | `humanDecision == null` | `human_gate_required` |
| HUMAN_GATE | `humanDecision != null` | `completed` |
| 其他 | 始终 | `advanced` |

资料来源：[src/transports/local_seam_bridge.ts:20-60]()

## 安全架构

### 步骤模式守卫

`step_mode_guard.ts` 实现了执行模式的字段一致性验证：

```typescript
// 关键验证规则
DETERMINISTIC + model 字段 → 警告（DET 步骤不调用 LLM）
AI_ATOMIC + 无 model 字段 → 警告
CONTRACT_CALL + 无 contract_id → 违规
HUMAN_GATE + 无 decision_options → 违规
```

资料来源：[src/security/step_mode_guard.ts:1-80]()

### 模式验证流程

```mermaid
graph TD
    A[加载 Flow 定义] --> B[遍历每个 Step]
    B --> C{检查 execution_mode}
    C -->|DETERMINISTIC| D{model 存在?}
    C -->|AI_ATOMIC| E{model 存在?}
    C -->|CONTRACT_CALL| F{contract_id 存在?}
    C -->|HUMAN_GATE| G{decision_options 存在?}
    
    D -->|是| H[记录警告]
    D -->|否| I[通过]
    E -->|否| J[记录违规]
    F -->|否| K[记录违规]
    G -->|否| L[记录违规]
    
    H --> M[返回违规列表]
    I --> M
    J --> M
    K --> M
    L --> M
    M -->|违规>0| N[flatErr 400]
    M -->|违规=0| O[通过验证]
```

### CFV-3 安全策略

对于 `HUMAN_GATE` 步骤，系统强制使用专用门路径：

- 通用 `step_complete` 调用将被拒绝
- 必须通过 `gate_approve` 或 `gate_reject` 完成审批

资料来源：[src/index.ts:step_complete case]()

## 数据模型

### 合约包结构

合约包（Contract Package）是工作流的部署单元，包含以下核心文件：

| 文件 | 必需 | 用途 |
|------|------|------|
| `_global.json` | 是 | 全局元数据、语义角色定义 |
| `_manifest.json` | 是 | 包清单、流程引用 |
| `flow.json` | 是 | 工作流步骤定义 |
| `_schemas/*.json` | 否 | 输出 Schema 定义 |

资料来源：[src/package_support.ts:1-100]()

### 全局文件验证规则

```json
{
  "schema_id": "string (必需)",
  "global_id": "string (必需)",
  "version": "string (必需)",
  "scope": "contract_package (必需)",
  "extends": "array (可选)",
  "semantic_roles": "array (必需，非空)",
  "non_authority_rules": "array (必需，非空)"
}
```

字段验证逻辑：

| 字段 | 验证类型 | 错误消息 |
|------|----------|----------|
| `global_id` | 必需字符串 | `global file is missing required string field "global_id"` |
| `version` | 必需字符串 | `global file is missing required string field "version"` |
| `scope` | 枚举值 | `global scope must be "contract_package"` |
| `semantic_roles` | 非空数组 | `global semantic_roles must be a non-empty array` |

资料来源：[dist-test/src/package_support.js:1-50]()

## MCP 工具接口

### 可用工具列表

| 工具名称 | 功能描述 | 主要参数 |
|----------|----------|----------|
| `mova_health` | 健康检查 | 无 |
| `mova_registry` | 获取合约清单 | 无 |
| `mova_run` | 执行合约 | `contract_type`, `inputs` |
| `mova_query` | 查询合约状态 | `contract_id`, `view` |
| `mova_decide` | 提交决策 | `contract_id`, `option_id` |
| `mova_connector` | 连接器操作 | `action`, `args` |
| `mova_contract` | 合约管理 | `action`, `package_path` |

资料来源：[README.md](https://github.com/mova-compact/mova-flat-runner/blob/main/README.md)

### 工具执行入口

```typescript
async function executeTool(name: string, args: Args): Promise<string> {
  const requestId = shortId();
  switch (name) {
    case "mova_run": { /* ... */ }
    case "mova_query": { /* ... */ }
    case "mova_decide": { /* ... */ }
    case "gate_approve": { /* ... */ }
    case "gate_reject": { /* ... */ }
    case "step_complete": { /* ... */ }
    // ...
  }
}
```

资料来源：[src/index.ts:executeTool]()

## 配置与环境变量

### 必需变量

| 变量名 | 说明 | 示例值 |
|--------|------|--------|
| `MOVA_API_URL` | MOVA API 端点 | `https://api.mova-lab.eu` |
| `MOVA_API_KEY` | API 认证密钥 | `__SET_MOVA_API_KEY__` |
| `LLM_KEY` | LLM 提供商密钥 | `__SET_LLM_KEY__` |
| `LLM_MODEL` | LLM 模型标识 | `openai/gpt-4o-mini` |

资料来源：[README.md](https://github.com/mova-compact/mova-flat-runner/blob/main/README.md)

### 可选变量

| 变量名 | 说明 | 默认值 |
|--------|------|--------|
| `MOVA_API_TIMEOUT_MS` | API 超时（毫秒） | `30000` |
| `MOVA_HTTP_PORT` | 本地 HTTP 模式端口 | 未设置（使用远程模式） |
| `MOVA_INVOKE_TOKEN` | 本地调用令牌 | 无 |
| `MOVA_SANDBOX_PACKAGE_PATH` | 沙箱包路径 | `contracts/dockerfile-nodejs-v1` |
| `MOVA_SANDBOX_PROJECT_PATH` | 沙箱项目路径 | 无 |

资料来源：[src/transports/local_seam_bridge.ts:resolveLocalSeamLocator]()

## 自定义合约桥接机制

### 问题背景

自定义合约（`local-*`, `remote-*` 前缀）与内置合约的命名空间分离：

- 运行端点：`/run/{contract_id}`
- 查询端点：`/api/v1/contracts/{contract_id}`

资料来源：[tasks/task061.md]()

### 桥接解决方案

```mermaid
graph LR
    A[register] -->|POST /contracts/register| B[记录 run_id]
    B --> C[CUSTOM_RUN_BRIDGE Map]
    C --> D[run_status]
    D -->|GET /run/{run_id}/status| E[更新映射]
    E --> C
    C --> F[mova_query]
    F -->|404 时| G{查询 /contracts/my}
    G -->|找到记录| H[返回 bridged status]
    G -->|未找到| I[返回 AUDIT_UNAVAILABLE]
```

### 内存桥接表

```typescript
const CUSTOM_RUN_BRIDGE: Map<contract_id, {
  run_id: string,
  updated_at: string,
  source_url?: string
}>;
```

桥接函数：

| 函数 | 位置 | 用途 |
|------|------|------|
| `rememberCustomRun` | `src/index.ts` | 记录 `contract_id → run_id` 映射 |
| `getMyContractRecord` | `src/index.ts` | 从 `/api/v1/contracts/my` 获取元数据 |

资料来源：[dist-test/src/index.js:rememberCustomRun](), [tasks/task061.md]()

## 部署配置

### MCP 客户端配置示例

#### Claude Desktop (macOS)

```json
{
  "mcpServers": {
    "mova": {
      "command": "npx",
      "args": ["-y", "@leryk1981/mova-flat-runner@3.3.3"],
      "env": {
        "MOVA_API_URL": "https://api.mova-lab.eu",
        "MOVA_API_KEY": "__SET_MOVA_API_KEY__",
        "LLM_KEY": "__SET_LLM_KEY__",
        "LLM_MODEL": "openai/gpt-4o-mini"
      }
    }
  }
}
```

#### Codex (Linux)

```toml
[mcp_servers.mova]
command = "npx"
args = ["-y", "@leryk1981/mova-flat-runner@3.3.3"]
startup_timeout_sec = 90.0

[mcp_servers.mova.env]
MOVA_API_URL = "https://api.mova-lab.eu"
MOVA_API_KEY = "__SET_MOVA_API_KEY__"
LLM_KEY = "__SET_LLM_KEY__"
LLM_MODEL = "openai/gpt-4o-mini"
```

资料来源：[README.md](https://github.com/mova-compact/mova-flat-runner/blob/main/README.md)

### 发布工具

系统提供 CLI 用于发布 MCP 服务器：

```bash
make publisher      # 构建发布工具
./bin/mcp-publisher # 运行发布
```

资料来源：[README.md](https://github.com/mova-compact/mova-flat-runner/blob/main/README.md)

## 验证与测试

### 构建与测试命令

| 命令 | 用途 |
|------|------|
| `make check` | 运行 lint、单元测试、集成测试 |
| `npm run build` | TypeScript 编译与打包 |
| `npm run test:build` | 构建产物测试 |
| `npm run smoke:custom-bridge` | 自定义桥接烟雾测试 |

资料来源：[README.md](https://github.com/mova-compact/mova-flat-runner/blob/main/README.md), [tasks/task061.md]()

## 技术栈

| 层级 | 技术选型 | 版本要求 |
|------|----------|----------|
| 运行时 | Node.js | ≥ 18 |
| 语言 | TypeScript | ES Modules |
| 协议 | Model Context Protocol | 1.x |
| 包管理 | npm | - |
| 发布格式 | npx 可执行包 | @leryk1981/mova-flat-runner |

资料来源：[package.json:1-15]()

## 总结

mova-flat-runner 采用了清晰的分层架构：

1. **协议层**：通过 MCP 协议与 AI 客户端交互
2. **调度层**：`executeTool` 统一分发请求
3. **传输层**：支持本地执行与远程 API 两种路径
4. **安全层**：步骤模式守卫确保工作流定义一致性
5. **数据层**：合约包验证器保障部署安全

该架构支持灵活的工作流执行模式，同时通过 MCP 协议为 AI 代理提供标准化的工具接口，实现了业务逻辑与 AI 推理的解耦。

---

<a id='page-transport-layer'></a>

## 传输层

### 相关页面

相关主题：[系统架构](#page-system-architecture), [可用工具集](#page-available-tools)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [src/transports/local_seam_bridge.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/transports/local_seam_bridge.ts)
- [src/transports/remote_api.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/transports/remote_api.ts)
- [src/index.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/index.ts)
- [dist-test/src/transports/local_seam_bridge.js](https://github.com/mova-compact/mova-flat-runner/blob/main/dist-test/src/transports/local_seam_bridge.js)
- [dist-test/src/transports/remote_api.js](https://github.com/mova-compact/mova-flat-runner/blob/main/dist-test/src/transports/remote_api.js)
</details>

# 传输层

## 概述

传输层（Transport Layer）是 mova-flat-runner 中负责在不同执行环境之间传递数据和调用命令的核心模块。该层抽象了本地执行与远程 API 调用两种传输机制，使上层业务逻辑能够以统一的方式与 MOVA 运行时进行交互，而无需关心具体的通信细节。

mova-flat-runner 的传输层包含两个主要实现：

| 传输实现 | 文件路径 | 用途 |
|---------|---------|------|
| 本地桥接传输 | `src/transports/local_seam_bridge.ts` | 本地进程内的桥接调用，通过文件系统进行状态管理 |
| 远程 API 传输 | `src/transports/remote_api.ts` | 通过 HTTP/REST 与远程 MOVA 服务端点通信 |

资料来源：[src/transports/local_seam_bridge.ts:1-30](https://github.com/mova-compact/mova-flat-runner/blob/main/src/transports/local_seam_bridge.ts)

---

## 架构设计

### 传输层定位

```
┌─────────────────────────────────────────────────────────┐
│                    MCP 协议层                            │
│         (mova_run, mova_query, mova_decide)             │
└─────────────────────────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────┐
│                   传输层 (Transport)                     │
│  ┌─────────────────┐      ┌─────────────────────────┐  │
│  │ local_seam_     │      │     remote_api          │  │
│  │ bridge          │      │  (movaGet/movaPost...)  │  │
│  └─────────────────┘      └─────────────────────────┘  │
└─────────────────────────────────────────────────────────┘
           │                              │
           ▼                              ▼
    本地文件系统状态                 REST API 远程调用
    (state_file)                   (api.mova-lab.eu)
```

### 传输策略选择

传输层通过配置中的 `baseUrl` 判断使用哪种传输策略：

```typescript
export function isLocalSeamConfig(config: MovaConfig): boolean {
    return config.baseUrl === LOCAL_SEAM_BACKEND;
}
```

- 当 `baseUrl` 为本地后端地址时，使用本地桥接传输
- 当 `baseUrl` 为远程 API 地址时，使用 HTTP 传输

资料来源：[dist-test/src/transports/local_seam_bridge.js:85-87](https://github.com/mova-compact/mova-flat-runner/blob/main/dist-test/src/transports/local_seam_bridge.js)

---

## 本地桥接传输 (local_seam_bridge)

### 核心功能

本地桥接传输用于在本地进程环境中直接与 MOVA 运行时交互，通过文件系统存储状态数据，无需网络通信。

#### 内部桥接调用器

`createInternalBridgeInvoker()` 创建一个桥接调用函数，负责处理步骤执行结果的标准化输出：

```typescript
function createInternalBridgeInvoker() {
    return async function bridgeInvoker(request) {
        bridgeSequence += 1;
        const suffix = `${request.step.id}:${bridgeSequence}`;
        
        const producedOutput =
            request.step.execution_mode === "AI_ATOMIC"
                ? CANONICAL_STRATEGY
                : request.stepResult ?? null;
        
        const status =
            request.step.execution_mode === "HUMAN_GATE" && request.humanDecision == null
                ? "human_gate_required"
                : request.terminalOutcome
                    ? "completed"
                    : "advanced";

        return {
            ok: true,
            bridge: {
                ok: true,
                bridge_source: "mova_flat_runner_canonical_bridge",
                status,
                contract_ref: request.contractRef,
                // ... 更多字段
            },
        };
    };
}
```

资料来源：[src/transports/local_seam_bridge.ts:55-95](https://github.com/mova-compact/mova-flat-runner/blob/main/src/transports/local_seam_bridge.ts)

#### 状态管理

本地传输通过以下方式解析运行定位器：

```typescript
async function resolveLocalSeamLocator(initialInputs) {
    const packagePath = 
        typeof initialInputs.package_path === "string" && initialInputs.package_path.trim().length > 0
            ? initialInputs.package_path
            : process.env.MOVA_SANDBOX_PACKAGE_PATH ?? "D:\\Projects_MOVA\\mova-intent\\contracts\\dockerfile-nodejs-v1";

    const projectPath = 
        typeof initialInputs.project_path === "string" && initialInputs.project_path.trim().length > 0
            ? initialInputs.project_path
            : process.env.MOVA_SANDBOX_PROJECT_PATH ?? "";

    const stateFile = 
        typeof initialInputs.state_file === "string" && initialInputs.state_file.trim().length > 0
            ? initialInputs.state_file
            : path.join(await fs.mkdtemp(path.join(os.tmpdir(), "mova-flat-runner-bridge-")), "run_state.json");

    return { package_path: packagePath, project_path: projectPath, state_file: stateFile };
}
```

资料来源：[src/transports/local_seam_bridge.ts:105-125](https://github.com/mova-compact/mova-flat-runner/blob/main/src/transports/local_seam_bridge.ts)

### 执行模式与状态映射

| 执行模式 | humanDecision 状态 | 返回状态 |
|---------|-------------------|---------|
| `AI_ATOMIC` | 任意 | `produced_output = CANONICAL_STRATEGY` |
| `HUMAN_GATE` | `null` | `human_gate_required` |
| `HUMAN_GATE` | 有值 | `advanced` |
| 任意 | `terminalOutcome = true` | `completed` |

### 公共形状过滤

`sanitizePublicShape()` 函数确保敏感内部字段不会泄漏到外部响应：

```typescript
function sanitizePublicShape(value: unknown): boolean {
    if (Array.isArray(value)) {
        return value.every((item) => sanitizePublicShape(item));
    }
    if (!value || typeof value !== "object") {
        return true;
    }
    for (const [key, child] of Object.entries(value)) {
        // 过滤敏感字段
        if (["bridge_anchors", "last_terminal_bridge", "terminal_commit_count", 
             "_state15_bridge", "trace", "outputs", "context"].includes(key)) {
            return false;
        }
        if (!sanitizePublicShape(child)) {
            return false;
        }
    }
    return true;
}
```

资料来源：[src/transports/local_seam_bridge.ts:130-150](https://github.com/mova-compact/mova-flat-runner/blob/main/src/transports/local_seam_bridge.ts)

### 人工决策处理

本地传输提供两个关键函数用于人工决策流程：

#### 获取决策点

```typescript
export async function movaGetDecisionPointLocal(runReference) {
    const locator = await resolveOpaqueLocatorOrThrow(runReference);
    const machine = await loadMachineBridgeModule();
    const gate = await machine.get_human_gate({ state_file: locator.state_file });
    
    if (!sanitizePublicShape(gate)) {
        throw new Error("internal_field_leak");
    }
    return gate;
}
```

#### 提交决策

```typescript
export async function movaSubmitDecisionLocal(runReference, option, reason) {
    const locator = await resolveOpaqueLocatorOrThrow(runReference);
    const machine = await loadMachineBridgeModule();
    const resolution = await machine.submit_human_resolution({
        package_path: locator.package_path,
        state_file: locator.state_file,
        step_id: "review_strategy",
        // ...
    });
}
```

资料来源：[dist-test/src/transports/local_seam_bridge.js:95-120](https://github.com/mova-compact/mova-flat-runner/blob/main/dist-test/src/transports/local_seam_bridge.js)

---

## 远程 API 传输 (remote_api)

### HTTP 方法封装

远程传输提供标准的 RESTful API 调用封装：

```typescript
export const movaGet    = (config, path) => movaRequest(config, "GET", path);
export const movaPut    = (config, path, body) => movaRequest(config, "PUT", path, body);
export const movaDelete = (config, path) => movaRequest(config, "DELETE", path);
export const movaPost   = (config, path, body) => movaRequest(config, "POST", path, body);
```

### 远程步骤执行

`movaRunStepsRemote()` 函数按顺序执行 `analyze`、`verify`、`decide` 三个步骤：

```mermaid
graph TD
    A[开始 movaRunStepsRemote] --> B[执行 analyze 步骤]
    B --> C{结果 ok?}
    C -->|否| D[返回错误]
    C -->|是| E[获取 analyze 输出]
    E --> F[执行 verify 步骤]
    F --> G{结果 ok?}
    G -->|否| D
    G -->|是| H{状态 = waiting_human?}
    H -->|是| I[获取 decision_point]
    H -->|否| J[执行 decide 步骤]
    I --> K[返回等待人工决策]
    J --> L[返回执行结果]
```

```typescript
export async function movaRunStepsRemote(cfg, contractId, validators, initialInputs = {}) {
    let analysis = {};
    for (const stepId of ["analyze", "verify", "decide"]) {
        let result;
        try {
            result = await movaPost(cfg, `/api/v1/contracts/${contractId}/step`, {
                envelope: {
                    kind: "env.step.execute_v0",
                    envelope_id: `env-${shortId()}`,
                    contract_id: contractId,
                    actor: { actor_type: "system", actor_id: "mova_runtime" },
                    payload: { step_id: stepId },
                },
            });
        } catch (error) {
            return flatErr(code, error.message, undefined, retryable);
        }
        
        if (!result.ok) {
            return flatErr(ERR.API_REQUEST_FAILED, `Step "${stepId}" returned ok=false`, result);
        }
        
        // 处理分析输出和验证器
        if (stepId === "analyze") {
            const output = await movaGet(cfg, `/api/v1/contracts/${contractId}/steps/analyze/output`);
            analysis = { ...output };
            
            // 执行验证器
            for (const validator of validators) {
                const fn = VALIDATOR_REGISTRY.get(validator.validator_id);
                if (!fn) {
                    analysis[`${validator.step_id}_error`] = `VALIDATOR_NOT_ALLOWED`;
                    continue;
                }
                const resultValue = fn({ ...initialInputs, ...analysis });
                Object.assign(analysis, resultValue.value ?? {});
            }
        }
    }
    return { ok: true, analysis };
}
```

资料来源：[dist-test/src/transports/remote_api.js:60-95](https://github.com/mova-compact/mova-flat-runner/blob/main/dist-test/src/transports/remote_api.js)

### 错误处理机制

| 错误类型 | 处理策略 | retryable |
|---------|---------|-----------|
| API 请求失败 | 返回错误码 `ERR.API_REQUEST_FAILED` | 由错误对象决定 |
| 步骤返回 `ok=false` | 返回包含失败详情的错误 | `false` |
| 验证器未注册 | 记录错误但继续执行 | 不适用 |
| 验证器执行异常 | 捕获并记录到分析结果中 | 不适用 |

### 人工决策端点

远程 API 在检测到 `waiting_human` 状态时，额外查询决策点信息：

```typescript
if (result.status === "waiting_human") {
    let decisionPoint = {};
    try {
        const response = await movaGet(cfg, `/api/v1/contracts/${contractId}/decision`);
        decisionPoint = (response.decision_point ?? {});
    } catch {
        // 非致命错误，legacy 远程路径兼容
    }
    return {
        ok: true,
        status: "waiting_human",
        contract_id: contractId,
        question: decisionPoint.question ?? "Select action:",
        options: decisionPoint.options ?? [],
        recommended: decisionPoint.recommended_option_id ?? null,
        analysis,
    };
}
```

---

## 传输配置

### MovaConfig 配置结构

| 字段 | 类型 | 说明 |
|-----|------|------|
| `baseUrl` | `string` | API 基础地址，决定使用本地或远程传输 |
| `apiKey` | `string` | API 认证密钥 |
| `timeoutMs` | `number` | 请求超时时间（毫秒） |
| `headers` | `Record<string, string>` | 自定义 HTTP 请求头 |

### 环境变量映射

```env
# 远程 API 配置
MOVA_API_URL=https://api.mova-lab.eu
MOVA_API_KEY=__SET_MOVA_API_KEY__

# 本地传输配置
MOVA_SANDBOX_PACKAGE_PATH=默认包路径
MOVA_SANDBOX_PROJECT_PATH=默认项目路径

# 可选配置
MOVA_API_TIMEOUT_MS=30000
MOVA_HTTP_PORT=3796
```

---

## 工作流程对比

```mermaid
graph LR
    subgraph 本地传输
        L1[解析输入] --> L2[创建状态文件]
        L2 --> L3[调用本地机器模块]
        L3 --> L4[读写 state_file]
        L4 --> L5[返回结果]
    end
    
    subgraph 远程传输
        R1[解析输入] --> R2[构建信封]
        R2 --> R3[HTTP POST /step]
        R3 --> R4[轮询状态]
        R4 --> R5[返回结果]
    end
    
    A{传输类型} -->|本地| 本地传输
    A -->|远程| 远程传输
```

| 特性 | 本地传输 | 远程传输 |
|-----|---------|---------|
| 通信方式 | 进程调用 + 文件系统 | HTTP/REST API |
| 状态存储 | 本地 `state_file` | 服务端管理 |
| 延迟 | 低 | 取决于网络 |
| 可扩展性 | 单机 | 分布式 |
| 适用场景 | 开发调试、沙箱 | 生产环境 |

---

## 安全性考量

### 输入验证

所有通过传输层传递的输入都经过严格验证：

1. **路径验证**：防止路径遍历攻击
2. **类型检查**：确保配置参数类型正确
3. **空值处理**：验证必需字段存在

### 输出过滤

`sanitizePublicShape()` 函数过滤以下敏感字段：

- `bridge_anchors` - 桥接锚点
- `last_terminal_bridge` - 最终桥接状态
- `terminal_commit_count` - 提交计数
- `_state15_bridge` - 内部状态
- `trace` - 执行跟踪
- `outputs` - 完整输出
- `context` - 运行时上下文

### 错误隔离

传输层错误被封装为标准化错误响应，防止内部实现细节泄漏：

```typescript
function isMovaNotFoundError(error: unknown): boolean {
    const msg = error instanceof Error ? error.message : String(error);
    return msg.includes("MOVA API 404");
}
```

---

## 相关文件索引

| 模块 | 文件路径 | 职责 |
|-----|---------|------|
| 入口点 | `src/index.ts` | 传输层调用入口，工具执行路由 |
| 本地桥接 | `src/transports/local_seam_bridge.ts` | 本地执行环境和文件系统交互 |
| 远程 API | `src/transports/remote_api.ts` | HTTP API 通信封装 |
| 安全模块 | `src/security/step_mode_guard.ts` | 步骤模式验证 |

---

<a id='page-security-module'></a>

## 安全模块

### 相关页面

相关主题：[系统架构](#page-system-architecture), [业务验证器](#page-validators)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [src/security/class_definition_guard.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/security/class_definition_guard.ts)
- [src/security/step_mode_guard.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/security/step_mode_guard.ts)
- [src/security/flow_schema_guard.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/security/flow_schema_guard.ts)
- [src/validators/aml.js](https://github.com/mova-compact/mova-flat-runner/blob/main/src/validators/aml.js)
- [src/transports/local_seam_bridge.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/transports/local_seam_bridge.ts)
- [src/index.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/index.ts)
</details>

# 安全模块

## 概述

mova-flat-runner 的安全模块采用**纵深防御（Defense-in-Depth）**策略，通过多个独立的安全守卫（Security Guards）在不同层级验证合约流程的完整性和安全性。这些守卫在模块导入时无副作用执行，所有验证逻辑均为纯函数，确保不会因验证失败而产生意外状态变更。

核心安全目标包括：

- 防止特权提升攻击（Privilege Escalation）
- 阻断内联类定义注入（Inline Class Definition Injection）
- 验证步骤执行模式与字段的一致性
- 确保合约执行的确定性边界

## 架构概览

```mermaid
graph TD
    subgraph 安全模块架构
        FSG[flow_schema_guard<br/>CFV-9 流程Schema守卫]
        CDG[class_definition_guard<br/>CFV-10 类定义守卫]
        SMG[step_mode_guard<br/>步骤模式守卫]
        
        FSG -->|拒绝未知顶层字段| ERR1[ERR.SCHEMA_VIOLATION]
        CDG -->|拒绝内联类定义| ERR2[ERR.INLINE_CLASS_DEFINITION_FORBIDDEN]
        SMG -->|模式字段不匹配| ERR3[ERR.STEP_MODE_FIELD_MISMATCH]
    end
    
    subgraph 入口点
        REG[合约注册]
        RUN[合约执行]
    end
    
    REG --> FSG
    REG --> CDG
    RUN --> SMG
```

## CFV-9 流程 Schema 守卫

### 背景与目的

安全审计发现，某些恶意流程包含 `__admin_override`、`__privilege_grant`、`__debug_mode` 等顶层字段。虽然初始注册逻辑不直接使用这些字段，但它们可能被持久化存储，后续代码路径若读取这些字段将导致特权提升漏洞。

flow_schema_guard.ts 实现**严格默认（Strict-by-Default）**策略：拒绝任何不在白名单中的顶层键。

### 白名单定义

| 允许的顶层键 | 用途 |
|-------------|------|
| `version` | 流程版本标识 |
| `description` | 流程描述 |
| `entry` | 入口步骤引用 |
| `steps` | 步骤定义对象 |
| `parallel_steps` | 并行步骤定义 |
| `notes` | 备注信息 |
| `audit_mode` | 审计模式配置 |
| `audit_mode_note` | 审计模式说明 |
| `class_definition_ref` | 类定义引用（远程解析） |
| `CONTRACT_CALL_instructions` | 合约调用指令 |
| `input_schema` | 输入数据Schema |
| `output_schema` | 输出数据Schema |
| `metadata` | 元数据 |

### 核心实现

```typescript
// 资料来源：src/security/flow_schema_guard.ts:17-30
export const ALLOWED_FLOW_TOP_LEVEL_KEYS: ReadonlySet<string> = new Set([
  "version",
  "description",
  "entry",
  "steps",
  "parallel_steps",
  "notes",
  "audit_mode",
  "audit_mode_note",
  "class_definition_ref",
  "CONTRACT_CALL_instructions",
  "input_schema",
  "output_schema",
  "metadata",
]);
```

### 验证函数

```typescript
// 资料来源：src/security/flow_schema_guard.ts:33-43
export function findUnknownFlowFields(flow: unknown): string[] {
  if (!flow || typeof flow !== "object" || Array.isArray(flow)) return [];
  const f = flow as Record<string, unknown>;
  const out: string[] = [];
  for (const key of Object.keys(f)) {
    if (!ALLOWED_FLOW_TOP_LEVEL_KEYS.has(key))
      out.push(key);
  }
  return out;
}
```

当发现未知字段时，返回格式化的错误响应：

```typescript
// 资料来源：src/security/flow_schema_guard.ts:45-55
export function assertNoUnknownFlowFields(
  flow: unknown,
  requestId: string,
): FlatRunnerResult | null {
  const unknown = findUnknownFlowFields(flow);
  if (unknown.length === 0) return null;
  return flatErr(ERR.SCHEMA_VIOLATION, ..., { unknown_fields: unknown }, false, requestId);
}
```

## CFV-10 类定义守卫

### 背景与目的

类定义（class_definition）包含敏感的配置信息，如严重性等级（severity bands）和噪声控制参数。安全审计发现攻击者可将类定义以内联方式嵌入流程，绕过注册表权威验证，从而操纵安全决策。

class_definition_guard.ts 强制要求类定义必须通过注册表（Registry）按 `class_id` 解析，禁止内联定义。

### 禁止字段列表

| 禁止的字段名 | 说明 |
|-------------|------|
| `class_definition` | 完整类定义对象 |
| `class_definition_inline` | 内联类定义 |
| `class_def_override` | 类定义覆盖 |
| `class_def` | 缩写形式 |

### 核心实现

```typescript
// 资料来源：src/security/class_definition_guard.ts:12-18
const FORBIDDEN_FLOW_KEYS: readonly string[] = Object.freeze([
  "class_definition",
  "class_definition_inline",
  "class_def_override",
  "class_def",
]);

// 资料来源：src/security/class_definition_guard.ts:21-28
export function findInlineClassDefinitionFields(flow: unknown): string[] {
  if (!flow || typeof flow !== "object") return [];
  const f = flow as Record<string, unknown>;
  return FORBIDDEN_FLOW_KEYS.filter((k) => Object.prototype.hasOwnProperty.call(f, k));
}
```

### 错误响应

```typescript
// 资料来源：src/security/class_definition_guard.ts:30-45
export function assertNoInlineClassDefinition(
  flow: unknown,
  requestId: string,
): FlatRunnerResult | null {
  const found = findInlineClassDefinitionFields(flow);
  if (found.length === 0) return null;
  return flatErr(
    ERR.INLINE_CLASS_DEFINITION_FORBIDDEN,
    `Flow body contains inline class-definition field(s): ${found.join(", ")}. ` +
    "Class definitions must be resolved from the registry by class_id, not embedded in the flow.",
    {
      forbidden_fields: found,
      remediation: "Remove these fields and reference the class via class_id; the registry is the only authority on severity bands and noise control.",
      http_status_equivalent: 400,
    },
    false,
    requestId,
  );
}
```

## 步骤模式守卫

### 执行模式类型

| 模式 | 说明 | 必需字段 |
|------|------|---------|
| `DETERMINISTIC` | 本地JS校验，无LLM调用 | 无额外要求 |
| `AI_ATOMIC` | LLM原子执行 | 必须有 `model` 字段 |
| `CONTRACT_CALL` | 调用其他合约 | 必须有 `contract_id` 字段 |
| `HUMAN_GATE` | 人工决策门 | 必须有 `decision_options` 数组 |

### 验证规则

步骤模式守卫检测以下违规场景：

1. **DETERMINISTIC 模式含 model 字段**
   
   ```typescript
   // 资料来源：src/security/step_mode_guard.ts:42-49
   if (mode === "DETERMINISTIC" && step.model !== undefined && step.model !== null) {
     out.push({
       kind: "deterministic_with_model",
       step_id: id,
       execution_mode: mode,
       message: `step '${id ?? "?"}' is DETERMINISTIC but declares a 'model' field — DET steps run a local JS check, not an LLM`,
     });
   }
   ```

2. **AI_ATOMIC 模式缺少 model 字段**
   
   ```typescript
   // 资料来源：src/security/step_mode_guard.ts:51-58
   if (mode === "AI_ATOMIC" && asStr(step.model) === null) {
     out.push({
       kind: "ai_atomic_without_model",
       step_id: id,
       execution_mode: mode,
       message: `step '${id ?? "?"}' is AI_ATOMIC but has no 'model' field`,
     });
   }
   ```

3. **CONTRACT_CALL 模式缺少 contract_id**
   
   ```typescript
   // 资料来源：src/security/step_mode_guard.ts:60-67
   if (mode === "CONTRACT_CALL" && asStr(step.contract_id) === null) {
     out.push({
       kind: "contract_call_without_contract_id",
       step_id: id,
       execution_mode: mode,
       message: `step '${id ?? "?"}' is CONTRACT_CALL but has no 'contract_id'`,
     });
   }
   ```

4. **HUMAN_GATE 模式缺少 decision_options**
   
   ```typescript
   // 资料来源：src/security/step_mode_guard.ts:69-76
   if (mode === "HUMAN_GATE" && !Array.isArray(step.decision_options)) {
     out.push({
       kind: "human_gate_without_decisions",
       step_id: id,
       execution_mode: mode,
       message: `step '${id ?? "?"}' is HUMAN_GATE but has no 'decision_options' array`,
     });
   }
   ```

### 验证流程

```mermaid
graph TD
    A[遍历流程步骤] --> B{获取 execution_mode}
    B -->|DETERMINISTIC| C{是否有 model?}
    B -->|AI_ATOMIC| D{是否有 model?}
    B -->|CONTRACT_CALL| E{是否有 contract_id?}
    B -->|HUMAN_GATE| F{是否有 decision_options?}
    
    C -->|是| G[记录违规<br/>deterministic_with_model]
    C -->|否| H[通过]
    D -->|否| I[记录违规<br/>ai_atomic_without_model]
    D -->|是| J[通过]
    E -->|否| K[记录违规<br/>contract_call_without_contract_id]
    E -->|是| L[通过]
    F -->|否| M[记录违规<br/>human_gate_without_decisions]
    F -->|是| N[通过]
    
    G --> O{违规数量 > 0?}
    I --> O
    K --> O
    M --> O
    H --> O
    J --> O
    L --> O
    N --> O
    
    O -->|是| P[返回 flatErr<br/>ERR.STEP_MODE_FIELD_MISMATCH]
    O -->|否| Q[验证通过]
```

## HUMAN_GATE 安全控制

### 专用完成路径

HUMAN_GATE 步骤不能通过通用的 `step_complete` 操作完成，必须使用专用的门控确认路径：

```typescript
// 资料来源：src/index.ts (上下文中的 gate_guard 使用)
case "step_complete": {
  // SECURITY (CFV-3): HUMAN_GATE cannot be completed by generic step completion.
  // Human confirmation requires the dedicated gate path (gate_approve / gate_reject).
  const gateGuard = await assertNotHumanGate(config, args.run_id, args.step_id, requestId);
  if (gateGuard) return gateGuard;
  // ... 执行通用完成逻辑
}

case "gate_approve": {
  // 专用人工确认路径
}

case "gate_reject": {
  // 专用人工拒绝路径
}
```

### Bridge 状态映射

当流程遇到 HUMAN_GATE 时，local_seam_bridge 返回特定的桥接状态：

```typescript
// 资料来源：src/transports/local_seam_bridge.ts:19-25
const status =
  request.step.execution_mode === "HUMAN_GATE" && request.humanDecision == null
    ? "human_gate_required"
    : request.terminalOutcome
      ? "completed"
      : "advanced";

// 资料来源：src/transports/local_seam_bridge.ts:40-41
next_phase: { phase: status === "human_gate_required" ? "WAIT_HUMAN" : "EXECUTION" },
```

## 验证器集成

### AML 验证器示例

安全模块还包括业务级验证器（如 AML 合规验证器）：

```javascript
// 资料来源：src/validators/aml.js:1-28
export const amlValidators = [
    {
        id: "aml.validate_policy_flags_v0",
        fn: (inputs) => {
            const sanctions = Boolean(inputs.sanctions_match);
            const pep = Boolean(inputs.pep_status);
            const score = Number(inputs.risk_score) || 0;
            const mandatory_escalate = sanctions || pep || score > 85;
            const auto_clear = (score <= 30 && !sanctions && !pep && ...);
            return {
                ok: true,
                value: {
                    policy_flags_valid: score >= 0 && score <= 100,
                    mandatory_escalate,
                    auto_clear_eligible: auto_clear,
                },
            };
        },
    },
];
```

### 验证器执行流程

```mermaid
graph LR
    A[远程获取分析输出] --> B[遍历验证器注册表]
    B --> C{验证器ID存在?}
    C -->|是| D[执行验证函数]
    C -->|否| E[记录 VALIDATOR_NOT_ALLOWED 错误]
    D --> F{验证执行成功?}
    F -->|是| G[合并验证结果到分析输出]
    F -->|否| H[记录 VALIDATOR_FAILED 错误]
```

```typescript
// 资料来源：src/transports/remote_api.js:15-36
const validatorContext = { ...initialInputs, ...analysis };
for (const validator of validators) {
  const fn = VALIDATOR_REGISTRY.get(validator.validator_id);
  if (!fn) {
    analysis[`${validator.step_id}_error`] = `VALIDATOR_NOT_ALLOWED: "${validator.validator_id}" not in registry`;
    continue;
  }
  try {
    const resultValue = fn(validatorContext);
    Object.assign(analysis, resultValue.value ?? {});
  } catch (error) {
    analysis[`${validator.step_id}_error`] = `VALIDATOR_FAILED: ${String(error)}`;
  }
}
```

## 错误码对照表

| 错误码常量 | 说明 | HTTP 等效状态 |
|-----------|------|---------------|
| `ERR.SCHEMA_VIOLATION` | 流程包含未授权的顶层字段 | 400 |
| `ERR.INLINE_CLASS_DEFINITION_FORBIDDEN` | 禁止内联类定义 | 400 |
| `ERR.STEP_MODE_FIELD_MISMATCH` | 执行模式与字段不匹配 | 400 |
| `ERR.UNKNOWN_CONTRACT_TYPE` | 未知的合约类型 | 400 |
| `ERR.LOCAL_VALIDATION_FAILED` | 本地验证失败 | 400 |
| `ERR.GATE_REJECTED` | 人工门被拒绝 | 402 |

## 安全模块设计原则

1. **纯函数验证**：所有守卫函数无副作用，支持安全的并行执行和测试
2. **显式拒绝**：默认拒绝策略，未明确允许的均为禁止
3. **清晰错误信息**：违规响应包含违规详情和修复建议
4. **纵深防御**：多层守卫互补，覆盖不同攻击向量
5. **注册表权威**：敏感配置（如类定义）必须从注册表解析，禁止内联

## 集成位置

安全守卫在以下入口点被调用：

| 入口 | 调用的守卫 |
|------|-----------|
| 合约注册 | flow_schema_guard, class_definition_guard |
| 步骤执行 | step_mode_guard, gate_guard |
| 远程API调用 | 验证器注册表执行 |

```typescript
// 资料来源：src/index.ts (集成示例)
switch (name) {
  case "mova_contract": {
    if (action === "register") {
      // 调用安全守卫
      const schemaGuard = assertNoUnknownFlowFields(flow, requestId);
      const classGuard = assertNoInlineClassDefinition(flow, requestId);
      // ...
    }
  }
}

---

<a id='page-available-tools'></a>

## 可用工具集

### 相关页面

相关主题：[核心概念](#page-core-concepts), [传输层](#page-transport-layer)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [src/index.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/index.ts)
- [src/transports/local_seam_bridge.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/transports/local_seam_bridge.ts)
- [src/security/step_mode_guard.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/security/step_mode_guard.ts)
- [src/package_support.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/package_support.ts)
- [src/transports/remote_api.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/transports/remote_api.ts)
- [src/client.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/client.ts)

</details>

# 可用工具集

## 概述

mova-flat-runner 提供了一套完整的 MCP（Model Context Protocol）工具集，用于与 MOVA 平台进行交互。这套工具集涵盖了从合约执行、状态查询、决策支持到安全验证的完整工作流程。

工具集通过 MCP 协议暴露为标准化接口，支持本地运行和远程 API 调用两种模式。所有工具均返回 JSON 格式的结构化响应，包含执行状态、结果数据和错误信息。资料来源：[src/index.ts:1-50]()

## 工具列表总览

| 工具名称 | 功能描述 | 执行模式 |
|---------|---------|---------|
| `mova_health` | 健康检查与连接验证 | 同步 |
| `mova_registry` | 获取可用合约清单 | 同步 |
| `mova_run` | 执行内置合约 | 本地/远程 |
| `mova_query` | 查询合约状态与审计信息 | 远程 |
| `mova_decide` | 触发决策流程 | 远程 |
| `mova_connector` | 连接外部系统 | 远程 |
| `mova_contract` | 自定义合约操作（注册/运行/状态） | 远程 |

资料来源：[README.md:55-62]()

## 核心工具详解

### mova_run

`mova_run` 是执行内置合约的核心工具，支持预定义的合约类型列表。

#### 支持的合约类型

系统内置了多个合约 manifest，存储在 `CONTRACT_MANIFESTS` 映射中。每个 manifest 包含：

- `contract_type`：合约类型标识
- `title`：合约标题
- `version`：版本号
- `execution_mode`：执行模式（AI_ATOMIC、DETERMINISTIC、HUMAN_GATE 等）
- `template_id`：模板标识
- `dataspec`：输入数据规格

资料来源：[src/index.ts:200-280]()

#### 执行流程

```mermaid
graph TD
    A[接收合约类型与输入] --> B{查找 Manifest}
    B -->|找到| C[验证输入数据]
    B -->|未找到| D[返回未知合约类型错误]
    C --> E{执行模式}
    E -->|LOCAL_SEAM| F[本地沙箱执行]
    E -->|REMOTE_API| G[远程 API 调用]
    F --> H[返回执行结果]
    G --> H
```

#### 安全验证

执行前会进行多重安全检查：

1. **Step Mode 验证**：确保执行模式与字段定义匹配
2. **内联类定义检查**：禁止在流程中嵌入 class_definition
3. **人类决策门保护**：HUMAN_GATE 步骤不可通过通用 step_complete 跳过

资料来源：[src/security/step_mode_guard.ts:1-50]()

### mova_contract

自定义合约操作工具，支持完整的合约生命周期管理。

#### 支持的操作

| 操作 | 说明 | 所需参数 |
|-----|------|---------|
| `register` | 注册新合约 | contract_id, flow |
| `run` | 运行合约 | contract_id |
| `run_status` | 查询运行状态 | run_id |
| `step_complete` | 完成步骤 | run_id, step_id, outcome, output |
| `gate_approve` | 批准人类决策门 | run_id, step_id |
| `gate_reject` | 拒绝人类决策门 | run_id, step_id, reason |
| `cancel` | 取消运行 | run_id |

资料来源：[src/index.ts:280-400]()

#### 自定义合约桥接机制

对于自定义合约（如 `local-*`、`remote-*` 前缀的 ID），系统实现了智能桥接：

1. **运行追踪**：在 `run` 和 `run_status` 时记录 `contract_id → run_id` 映射
2. **404 兜底**：当 `/api/v1/contracts/{contract_id}` 返回 404 时，探测 `/api/v1/contracts/my` 获取元数据
3. **状态桥接**：返回结构化的 bridged status，包含 `bridge_mode=custom_contract_run_namespace_bridge_v1`

资料来源：[src/index.ts:80-120]()

### mova_query

状态与审计信息查询工具，支持多种视图模式。

#### 视图模式

| 视图 | 说明 | 返回内容 |
|-----|------|---------|
| `status` | 状态视图 | 合约运行状态、结构化结果 |
| `audit` | 审计视图 | 完整审计记录（需要后端支持） |
| `audit_compact` | 紧凑审计 | 精简审计对象，含 journal |

#### 自定义合约查询

当查询自定义合约 ID 遇到 404 时：

```mermaid
graph TD
    A[查询 /api/v1/contracts/{id}] --> B{响应状态}
    B -->|200| C[返回正常结果]
    B -->|404| D[探测 /api/v1/contracts/my]
    D --> E{找到合约记录}
    E -->|是| F[构建桥接状态]
    E -->|否| G[返回 AUDIT_UNAVAILABLE]
```

资料来源：[tasks/task061.md:1-50]()

### mova_decide

决策触发工具，用于启动需要 AI 推理的决策流程。

#### 工作原理

1. 接收决策请求上下文
2. 调用后端决策端点
3. 返回决策结果和可选的推荐选项
4. 支持 human-in-the-loop 确认

资料来源：[src/transports/remote_api.ts:80-120]()

### mova_connector

外部系统连接工具，用于集成第三方服务。

#### 功能特性

- 建立与外部 API 的连接
- 传递认证信息（通过配置）
- 处理请求/响应转换
- 支持超时和重试配置

### mova_health

健康检查工具，用于验证与 MOVA API 的连接状态。

```bash
curl -sS https://api.mova-lab.eu/health
```

资料来源：[README.md:48-50]()

### mova_registry

合约清单查询工具，返回所有可用合约的元信息。

#### 返回格式

```json
{
  "schema_version": "1.0",
  "contracts": [
    {
      "contract_type": "complaint",
      "title": "投诉处理合约",
      "version": "1.0.0",
      "execution_mode": "AI_ATOMIC",
      "manifest_resource": "mova://contracts/complaint/manifest"
    }
  ]
}
```

资料来源：[src/index.ts:450-480]()

## 安全验证体系

### Step Mode 字段一致性检查

每种执行模式都有强制的字段要求：

| 执行模式 | 必需字段 | 禁止字段 |
|---------|---------|---------|
| DETERMINISTIC | 无 | model |
| AI_ATOMIC | model | 无 |
| CONTRACT_CALL | contract_id | 无 |
| HUMAN_GATE | decision_options | 无 |

#### 检测的违规类型

- `ai_atomic_without_model`：AI_ATOMIC 模式未定义 model 字段
- `deterministic_with_model`：DETERMINISTIC 模式错误包含 model 字段
- `contract_call_without_contract_id`：CONTRACT_CALL 缺少 contract_id
- `human_gate_without_decisions`：HUMAN_GATE 缺少 decision_options

资料来源：[src/security/step_mode_guard.ts:50-100]()

### 内联类定义防护

禁止在流程中直接嵌入 class_definition 字段：

```typescript
const FORBIDDEN_FLOW_KEYS = [
  "class_definition_inline",
  "class_def_override", 
  "class_def"
];
```

违规响应包含：
- `forbidden_fields`：发现的禁止字段列表
- `remediation`：修复建议
- `http_status_equivalent`：400

资料来源：[src/security/class_definition_guard.ts:1-40]()

### 人类决策门保护

HUMAN_GATE 步骤有特殊保护机制：

1. **前置检查**：`step_complete` 操作会拒绝 HUMAM_GATE 步骤
2. **专用路径**：必须通过 `gate_approve` 或 `gate_reject` 完成
3. **原因记录**：拒绝操作需提供原因

资料来源：[src/index.ts:350-380]()

## 执行环境

### 本地沙箱模式（LOCAL_SEAM）

通过 `local_seam_bridge.ts` 实现本地执行：

```mermaid
graph TD
    A[请求] --> B[解析包路径]
    B --> C[加载流程定义]
    C --> D[执行步骤]
    D --> E[状态持久化]
    E --> F[返回桥接结果]
```

#### 关键特性

- 状态文件自动管理
- 支持自定义包路径和项目路径
- 输出模式：`AI_ATOMIC` 自动生成 CANONICAL_STRATEGY
- 人类门暂停机制

资料来源：[src/transports/local_seam_bridge.ts:1-80]()

### 远程 API 模式（REMOTE_API）

通过 `remote_api.ts` 调用远程服务：

```mermaid
graph TD
    A[请求] --> B[构建请求信封]
    B --> C[POST /api/v1/contracts/{id}/step]
    C --> D{执行步骤}
    D -->|analyze| E[获取分析输出]
    D -->|verify| F[执行验证器]
    D -->|decide| G[获取决策]
    E --> H{状态检查}
    H -->|waiting_human| I[返回决策点]
    H -->|completed| J[返回完成结果]
```

#### 验证器执行

analyze 步骤后自动执行注册验证器：

1. 从 `initialInputs` 构建验证上下文
2. 遍历 `VALIDATOR_REGISTRY`
3. 收集验证结果到 analysis 对象

资料来源：[src/transports/remote_api.ts:100-150]()

## 配置与环境变量

### 必需变量

| 变量名 | 说明 | 示例值 |
|-------|------|-------|
| `MOVA_API_URL` | API 端点 | `https://api.mova-lab.eu` |
| `MOVA_API_KEY` | API 认证密钥 | `__SET_MOVA_API_KEY__` |
| `LLM_KEY` | LLM 提供商密钥 | `__SET_LLM_KEY__` |
| `LLM_MODEL` | LLM 模型标识 | `openai/gpt-4o-mini` |

### 可选变量

| 变量名 | 说明 | 默认值 |
|-------|------|-------|
| `MOVA_API_TIMEOUT_MS` | 请求超时（毫秒） | 30000 |
| `MOVA_HTTP_PORT` | 本地 HTTP 端口 | 3796 |
| `MOVA_INVOKE_TOKEN` | 本地调用令牌 | 无 |
| `MOVA_SANDBOX_PACKAGE_PATH` | 沙箱包路径 | 项目默认 |
| `MOVA_SANDBOX_PROJECT_PATH` | 沙箱项目路径 | 当前目录 |

资料来源：[README.md:25-45]()

## 包结构验证

### Global 文件验证

验证 `global.json` 的必需字段：

- `global_id`：全局唯一标识（非空字符串）
- `version`：版本号（非空字符串）
- `scope`：必须为 `contract_package`
- `semantic_roles`：非空数组
- `non_authority_rules`：非空数组

### Package Manifest 验证

允许的 manifest 字段：

```typescript
const allowedKeys = [
  "schema_id",
  "package_id", 
  "version",
  "global_ref",
  "flow_ref",
  "classification_policy_ref",
  "classification_result_set_ref",
  "runtime_binding_set_ref",
  "model_refs",
  "fixture_refs",
  "package_invariants"
];
```

资料来源：[src/package_support.ts:50-100]()

## 错误处理

### 错误码体系

| 错误码 | 说明 |
|-------|------|
| `UNKNOWN_CONTRACT_TYPE` | 未知的合约类型 |
| `API_REQUEST_FAILED` | API 请求失败 |
| `LOCAL_VALIDATION_FAILED` | 本地验证失败 |
| `STEP_MODE_FIELD_MISMATCH` | 执行模式与字段不匹配 |
| `INLINE_CLASS_DEFINITION_FORBIDDEN` | 禁止内联类定义 |

### 重试机制

错误响应包含 `retryable` 字段指示是否可重试：

```json
{
  "ok": false,
  "code": "API_REQUEST_FAILED",
  "message": "详细错误信息",
  "retryable": true
}
```

资料来源：[src/index.ts:50-100]()

## 总结

mova-flat-runner 的工具集提供了从简单查询到复杂决策的完整能力覆盖。核心设计原则包括：

1. **安全性优先**：多重验证机制确保流程正确性
2. **灵活执行**：支持本地和远程两种执行模式
3. **智能桥接**：自动处理自定义合约与内置路径的差异
4. **标准化接口**：统一的 MCP 协议接口便于集成

---

<a id='page-validators'></a>

## 业务验证器

### 相关页面

相关主题：[安全模块](#page-security-module), [可用工具集](#page-available-tools)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [src/validators/aml.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/validators/aml.ts)
- [src/validators/churn.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/validators/churn.ts)
- [src/validators/complaint.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/validators/complaint.ts)
- [src/validators/compliance.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/validators/compliance.ts)
- [src/validators/contract_gen.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/validators/contract_gen.ts)
- [src/validators/credit.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/validators/credit.ts)
- [src/validators/invoice.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/validators/invoice.ts)
- [src/validators/po.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/validators/po.ts)
- [src/validators/registry.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/validators/registry.ts)
- [src/validators/supply_chain.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/validators/supply_chain.ts)
- [src/validators/trade.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/validators/trade.ts)
- [src/validators/content_flywheel.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/validators/content_flywheel.ts)
</details>

# 业务验证器

## 概述

业务验证器（Business Validators）是 mova-flat-runner 系统中用于在合同执行流程中对输入数据进行业务规则校验的组件。验证器在合同步骤执行后被调用，确保输入数据满足特定业务场景的约束条件，并在验证失败时提供明确的错误信息和原因。

验证器采用**声明式注册 + 函数式校验**的架构模式，每个验证器都是一个独立模块，包含唯一标识符（`id`）和校验函数（`fn`），通过统一的注册表（Registry）进行管理和调度。

资料来源：[src/validators/supply_chain.ts:3-4]()

## 架构设计

### 组件关系图

```mermaid
graph TD
    subgraph "验证器模块层"
        AML[AML 验证器<br/>src/validators/aml.ts]
        CREDIT[Credit 验证器<br/>src/validators/credit.ts]
        SUPPLY[供应链验证器<br/>src/validators/supply_chain.ts]
        INVOICE[发票验证器<br/>src/validators/invoice.ts]
        COMPLAINT[投诉验证器<br/>src/validators/complaint.ts]
        CHURN[流失验证器<br/>src/validators/churn.ts]
        TRADE[贸易验证器<br/>src/validators/trade.ts]
        COMPLIANCE[合规验证器<br/>src/validators/compliance.ts]
        CONTRACT_GEN[合同生成验证器<br/>src/validators/contract_gen.ts]
        PO[采购订单验证器<br/>src/validators/po.ts]
        FLYWHEEL[内容飞轮验证器<br/>src/validators/content_flywheel.ts]
    end

    subgraph "注册表层"
        REGISTRY[ValidatorRegistry<br/>src/validators/registry.ts]
    end

    subgraph "执行层"
        REMOTE_API[remote_api.ts<br/>movaRunStepsRemote]
        LOCAL_SEAM[local_seam_bridge.ts<br/>本地执行桥接]
    end

    AML --> REGISTRY
    CREDIT --> REGISTRY
    SUPPLY --> REGISTRY
    INVOICE --> REGISTRY
    COMPLAINT --> REGISTRY
    CHURN --> REGISTRY
    TRADE --> REGISTRY
    COMPLIANCE --> REGISTRY
    CONTRACT_GEN --> REGISTRY
    PO --> REGISTRY
    FLYWHEEL --> REGISTRY

    REGISTRY --> REMOTE_API
    REGISTRY --> LOCAL_SEAM
```

### 核心类型定义

验证器的类型签名为 `ValidatorFn`，定义如下：

```typescript
export type ValidatorFn = (inputs: Record<string, unknown>) => {
  ok: boolean;
  value: Record<string, unknown>;
  step_id: string;
};
```

资料来源：[src/validators/supply_chain.ts:1]()

每个验证器返回的结构包含三个字段：

| 字段 | 类型 | 说明 |
|------|------|------|
| `ok` | `boolean` | 验证是否通过 |
| `value` | `Record<string, unknown>` | 验证结果详情，包含计算出的业务指标 |
| `step_id` | `string` | 关联的步骤标识符 |

## 验证器注册表

### 注册表结构

`src/validators/registry.ts` 负责聚合所有验证器并提供统一的查询接口。注册表采用 `Map<string, ValidatorFn>` 结构，以验证器 ID 为键存储校验函数。

```typescript
export const VALIDATOR_REGISTRY = new Map<string, ValidatorFn>();

// 注册供应链验证器
VALIDATOR_REGISTRY.set("supply_chain.validate_inputs_v0", validateInputs);

// 注册信用卡验证器  
VALIDATOR_REGISTRY.set("credit.validate_calcs_v0", validateCalcs);
```

### 注册流程

```mermaid
sequenceDiagram
    participant Registry as registry.ts
    participant Validator as 各验证器模块
    participant Caller as 调用方<br/>remote_api / local_seam_bridge
    
    Validator->>Registry: 导出 { id, fn } 数组
    Registry->>Registry: VALIDATOR_REGISTRY.set(id, fn)
    
    Caller->>Registry: VALIDATOR_REGISTRY.get(validator_id)
    Registry-->>Caller: ValidatorFn | undefined
```

## 内置验证器一览

### 验证器清单

| 验证器 ID | 模块文件 | 校验目标 | 主要检查项 |
|-----------|----------|----------|------------|
| `aml.validate_kyc_v0` | aml.ts | KYC 反洗钱检查 | 客户身份、风险等级、制裁名单 |
| `churn.validate_inputs_v0` | churn.ts | 流失预测输入 | 用户数据完整性 |
| `complaint.validate_inputs_v0` | complaint.ts | 投诉处理输入 | 投诉内容、类别、优先级 |
| `compliance.validate_v1` | compliance.ts | 合规性校验 | 监管要求符合度 |
| `contract_gen.validate_template_v0` | contract_gen.ts | 合同模板校验 | 模板参数完整性 |
| `credit.validate_calcs_v0` | credit.ts | 信贷计算校验 | 月收入、债务、征信评分、请求金额 |
| `invoice.validate_fields_v0` | invoice.ts | 发票字段校验 | 发票号码、金额、日期、税率 |
| `po.validate_po_v0` | po.ts | 采购订单校验 | PO 编号、金额、供应商信息 |
| `supply_chain.validate_inputs_v0` | supply_chain.ts | 供应链输入校验 | 供应商 ID、名称、国家代码 |
| `trade.validate_trade_v0` | trade.ts | 贸易交易校验 | 交易金额、方向、对手方 |
| `content_flywheel.validate_context_v0` | content_flywheel.ts | 内容飞轮上下文 | 上下文数据完整性 |

### 供应链验证器详解

供应链验证器 (`supply_chain.validate_inputs_v0`) 是验证器模式的典型实现：

```typescript
export const supplyChainValidators: Array<{ id: string; fn: ValidatorFn }> = [
  {
    id: "supply_chain.validate_inputs_v0",
    fn: (inputs) => {
      const suppliers = Array.isArray(inputs.suppliers) 
        ? inputs.suppliers as Record<string, unknown>[] 
        : [];
      
      const non_empty = suppliers.length > 0;
      
      const valid_items = suppliers.filter(s =>
        s &&
        typeof s === "object" &&
        String(s["id"]      || "").length > 0 &&
        String(s["name"]    || "").length > 0 &&
        /^[A-Z]{2}$/.test(String(s["country"] || ""))
      );
      
      const invalid_count = suppliers.length - valid_items.length;
      
      return {
        ok: true,
        value: {
          inputs_valid: non_empty && invalid_count === 0,
          supplier_count: suppliers.length,
          valid_supplier_count: valid_items.length,
          invalid_supplier_count: invalid_count,
          has_suppliers: non_empty,
        },
        step_id: "validate_inputs",
      };
    },
  },
];
```

**校验规则：**
- `suppliers` 数组必须非空
- 每个供应商必须包含 `id`（非空字符串）
- 每个供应商必须包含 `name`（非空字符串）
- 每个供应商必须包含 `country`（两位大写字母国家代码，如 `CN`、`US`）

资料来源：[src/validators/supply_chain.ts:3-36]()

### 信贷验证器详解

信贷验证器 (`credit.validate_calcs_v0`) 实现了复杂的信用评估计算：

```typescript
export const creditValidators = [
  {
    id: "credit.validate_calcs_v0",
    fn: (inputs) => {
      const income    = Number(inputs.monthly_income) || 0;
      const debt      = Number(inputs.total_debt) || 0;
      const bureau    = Number(inputs.bureau_score) || 0;
      const requested = Number(inputs.requested_amount) || 0;
      
      const income_ok    = income > 0;
      const bureau_ok    = bureau >= 300 && bureau <= 850;
      const requested_ok = requested > 0;
      
      const dti = income_ok ? debt / (income * 12) : null;
      
      const hard_reject = bureau < 500 || (dti !== null && dti > 0.6);
      
      return {
        ok: true,
        value: {
          calcs_valid: income_ok && bureau_ok && requested_ok,
          monthly_income: income,
          total_debt: debt,
          bureau_score: bureau,
          requested_amount: requested,
          debt_to_income_ratio: dti,
          hard_reject,
          hard_reject_reason: hard_reject
            ? (bureau < 500 ? "bureau_score_below_500" : "dti_exceeds_60pct")
            : null,
        },
        step_id: "validate_calcs",
      };
    },
  },
];
```

**校验规则与计算：**

| 检查项 | 规则 | 字段 |
|--------|------|------|
| 月收入 | 必须大于 0 | `income_ok` |
| 征信评分 | 必须在 300-850 范围内 | `bureau_ok` |
| 请求金额 | 必须大于 0 | `requested_ok` |
| 债务收入比 (DTI) | 月债务 / (月收入 × 12) | `debt_to_income_ratio` |
| 硬性拒绝 | 评分<500 或 DTI>60% | `hard_reject` |

资料来源：[src/validators/credit.ts:3-47]()

## 验证器执行流程

### 远程 API 执行路径

在 `remote_api.ts` 的 `movaRunStepsRemote` 函数中，验证器在 `analyze` 步骤完成后被调用：

```mermaid
sequenceDiagram
    participant Client as MCP Client
    participant API as MOVA API
    participant Runner as movaRunStepsRemote
    participant Registry as VALIDATOR_REGISTRY
    
    Client->>Runner: movaRunStepsRemote(contractId, validators, initialInputs)
    Runner->>API: POST /api/v1/contracts/{id}/step (analyze)
    
    Note over Runner: 获取 analyze 步骤输出
    
    loop 每个验证器
        Runner->>Registry: VALIDATOR_REGISTRY.get(validator.validator_id)
        
        alt 验证器存在
            Registry-->>Runner: ValidatorFn
            Runner->>Runner: fn(validatorContext)
            Note over Runner: validatorContext = {...initialInputs, ...analysis}
            
            alt 验证成功
                Runner->>Runner: Object.assign(analysis, resultValue.value)
            else 验证失败
                Runner->>Runner: analysis["{step_id}_error"] = "VALIDATOR_FAILED: {error}"
            end
        else 验证器不存在
            Runner->>Runner: analysis["{step_id}_error"] = "VALIDATOR_NOT_ALLOWED: ..."
        end
    end
    
    Runner-->>Client: 返回包含 analysis 的结果
```

**关键代码片段：**

```typescript
const validatorContext = { ...initialInputs, ...analysis };

for (const validator of validators) {
  const fn = VALIDATOR_REGISTRY.get(validator.validator_id);
  if (!fn) {
    analysis[`${validator.step_id}_error`] = `VALIDATOR_NOT_ALLOWED: "${validator.validator_id}" not in registry`;
    continue;
  }
  try {
    const resultValue = fn(validatorContext);
    Object.assign(analysis, resultValue.value ?? {});
  } catch (error) {
    analysis[`${validator.step_id}_error`] = `VALIDATOR_FAILED: ${String(error)}`;
  }
}
```

资料来源：[dist-test/src/transports/remote_api.js:1-30]()

### 安全校验：步骤模式守卫

验证器系统还包含安全校验机制 `step_mode_guard.ts`，确保步骤定义的一致性：

```typescript
export function findStepModeViolations(flow: unknown): StepModeViolation[] {
  const out: StepModeViolation[] = [];
  
  // 检查 AI_ATOMIC 步骤是否声明了 model 字段
  if (mode === "AI_ATOMIC" && asStr(step.model) === null) {
    out.push({
      kind: "ai_atomic_without_model",
      step_id: id,
      execution_mode: mode,
      message: `step '${id ?? "?"}' is AI_ATOMIC but has no 'model' field`,
    });
  }
  
  // 检查 CONTRACT_CALL 步骤是否声明了 contract_id
  if (mode === "CONTRACT_CALL" && asStr(step.contract_id) === null) {
    out.push({
      kind: "contract_call_without_contract_id",
      step_id: id,
      execution_mode: mode,
      message: `step '${id ?? "?"}' is CONTRACT_CALL but has no 'contract_id'`,
    });
  }
  
  // 检查 HUMAN_GATE 步骤是否声明了 decision_options
  if (mode === "HUMAN_GATE" && !Array.isArray(step.decision_options)) {
    out.push({
      kind: "human_gate_without_decisions",
      step_id: id,
      execution_mode: mode,
      message: `step '${id ?? "?"}' is HUMAN_GATE but has no 'decision_options' array`,
    });
  }
  
  return out;
}
```

**模式违规类型：**

| 违规类型 | 模式 | 期望字段 | 说明 |
|----------|------|----------|------|
| `deterministic_with_model` | DETERMINISTIC | 无 | DET 步骤不应声明 model |
| `ai_atomic_without_model` | AI_ATOMIC | `model` | AI 步骤必须指定模型 |
| `contract_call_without_contract_id` | CONTRACT_CALL | `contract_id` | 合约调用必须指定合约 ID |
| `human_gate_without_decisions` | HUMAN_GATE | `decision_options` | 人工审批必须提供选项 |

资料来源：[src/security/step_mode_guard.ts:1-70]()

## 自定义验证器扩展

### 创建新验证器步骤

1. **定义验证器函数**

```typescript
import type { ValidatorFn } from "../types.js";

export const customValidators: Array<{ id: string; fn: ValidatorFn }> = [
  {
    id: "custom.validate_business_rule_v0",
    fn: (inputs) => {
      // 业务校验逻辑
      const isValid = /* 校验条件 */;
      
      return {
        ok: true,
        value: {
          rule_valid: isValid,
          // ... 其他业务指标
        },
        step_id: "validate_business_rule",
      };
    },
  },
];
```

2. **注册到注册表**

在 `registry.ts` 中添加：

```typescript
import { customValidators } from "./custom.js";

for (const { id, fn } of customValidators) {
  VALIDATOR_REGISTRY.set(id, fn);
}
```

3. **在合同清单中引用**

```typescript
export const CONTRACT_MANIFESTS: Record<string, ContractManifest> = {
  my_contract: {
    contract_type: "my_contract",
    validators: ["custom.validate_business_rule_v0"],
    // ...
  },
};
```

## 验证结果处理

### 成功路径

验证成功时，结果被合并到 `analysis` 上下文中：

```typescript
const resultValue = fn(validatorContext);
Object.assign(analysis, resultValue.value ?? {});
```

后续步骤可以直接使用验证器计算出的业务指标。

### 失败路径

验证器执行失败时，会在 `analysis` 中记录错误：

```typescript
// 验证器函数抛出异常
analysis[`${validator.step_id}_error`] = `VALIDATOR_FAILED: ${String(error)}`;

// 验证器未在注册表中注册
analysis[`${validator.step_id}_error`] = `VALIDATOR_NOT_ALLOWED: "${validator.validator_id}" not in registry`;
```

错误信息以 `{step_id}_error` 格式存储，便于追踪具体失败的验证步骤。

## 最佳实践

1. **幂等性**：验证器应设计为幂等函数，相同输入产生相同输出
2. **无副作用**：验证器不应修改输入数据或产生外部影响
3. **错误处理**：使用 try-catch 包裹验证逻辑，防止异常导致流程中断
4. **返回完整上下文**：始终返回 `step_id` 字段，便于调试和追踪
5. **类型安全**：使用 TypeScript 显式声明输入输出类型

## 总结

业务验证器是 mova-flat-runner 实现业务规则强制执行的核心机制。通过声明式的验证器注册和函数式的校验实现，系统能够在合同执行的各个阶段对输入数据进行可靠的业务规则校验，同时保持代码的可扩展性和可维护性。

---

<a id='page-deployment'></a>

## 部署与运维

### 相关页面

相关主题：[安装与配置](#page-installation), [系统架构](#page-system-architecture)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [package.json](https://github.com/mova-compact/mova-flat-runner/blob/main/package.json)
- [README.md](https://github.com/mova-compact/mova-flat-runner/blob/main/README.md)
- [src/index.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/index.ts)
- [src/transports/local_seam_bridge.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/transports/local_seam_bridge.ts)
- [Makefile](https://github.com/mova-compact/mova-flat-runner/blob/main/Makefile)
</details>

# 部署与运维

## 概述

mova-flat-runner 是 MOVA 平台的 MCP（Model Context Protocol）服务器实现，提供受治理的 AI 工作流执行能力。该项目支持发票 OCR、AML 筛查、信用审查等业务流程，并可通过人类审批门（Human Approval Gates）实现人工介入，同时提供完整的审计追踪功能。

本页面详细说明 mova-flat-runner 的部署模式、环境配置、运维命令以及运行时行为，帮助运维人员和开发者在不同环境中正确部署和监控该服务。

---

## 部署架构

### MCP 服务器角色定位

mova-flat-runner 在 MOVA 生态系统中充当 MCP Registry 的服务提供方，为 AI 客户端（如 Claude Desktop、Cursor、Codex）提供标准化的工具调用接口。客户端通过 MCP 协议与服务器通信，服务器再将请求转发至后端 MOVA API 进行实际业务处理。

```mermaid
graph TD
    A["👤 AI 客户端<br/>(Claude Desktop / Codex)"] -->|"MCP Protocol"| B["mova-flat-runner<br/>(mova-mcp)"]
    B -->|"HTTP REST API"| C["MOVA API<br/>(api.mova-lab.eu)"]
    C -->|"业务执行"| D["后端服务集群"]
    
    B -->|"本地执行模式"| E["Local Seam Bridge"]
    E -->|"本地工作流"| F["本地合约执行"]
    
    style B fill:#4a90d9,color:#fff
    style C fill:#67b868,color:#fff
```

资料来源：[README.md](https://github.com/mova-compact/mova-flat-runner/blob/main/README.md)

### 双执行模式

mova-flat-runner 支持两种执行模式，根据配置自动选择：

| 模式 | 触发条件 | 行为描述 |
|------|----------|----------|
| **远程 API 模式** | `MOVA_API_URL` 已配置且可达 | 通过 HTTP 调用 `api.mova-lab.eu` 后端 API |
| **本地 HTTP 模式** | `MOVA_HTTP_PORT` 已配置 | 启动本地 HTTP 服务器，接收本地工作流请求 |

资料来源：[src/index.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/index.ts)

---

## 环境变量配置

### 必填环境变量

以下环境变量必须在部署时配置，否则服务无法正常启动：

| 变量名 | 说明 | 示例值 | 来源 |
|--------|------|--------|------|
| `MOVA_API_URL` | MOVA 后端 API 基础地址 | `https://api.mova-lab.eu` | README.md |
| `MOVA_API_KEY` | 后端 API 认证密钥 | `__SET_MOVA_API_KEY__` | README.md |
| `LLM_KEY` | LLM 提供商密钥 | `__SET_LLM_KEY__` | README.md |
| `LLM_MODEL` | 使用的 LLM 模型标识 | `openai/gpt-4o-mini` | README.md |

资料来源：[README.md](https://github.com/mova-compact/mova-flat-runner/blob/main/README.md)

### 可选本地 HTTP 模式变量

当需要启用本地 HTTP 模式时，配置以下变量：

| 变量名 | 说明 | 默认值 | 备注 |
|--------|------|--------|------|
| `MOVA_API_TIMEOUT_MS` | API 请求超时时间（毫秒） | `30000` | 可选配置 |
| `MOVA_HTTP_PORT` | 本地 HTTP 服务监听端口 | `3796` | 设置后启用本地模式 |
| `MOVA_INVOKE_TOKEN` | 本地调用认证令牌 | 无 | 用于保护本地 API 调用 |

资料来源：[README.md](https://github.com/mova-compact/mova-flat-runner/blob/main/README.md)

### 本地沙箱环境变量

在本地执行模式下，以下变量控制工作流和项目路径：

| 变量名 | 说明 | 默认值 |
|--------|------|--------|
| `MOVA_SANDBOX_PACKAGE_PATH` | 合约包目录路径 | `D:\Projects_MOVA\mova-intent\contracts\dockerfile-nodejs-v1` |
| `MOVA_SANDBOX_PROJECT_PATH` | 项目工作目录路径 | 空（必填） |

资料来源：[src/transports/local_seam_bridge.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/transports/local_seam_bridge.ts)

---

## 构建与打包

### npm 构建流程

项目使用 TypeScript 开发，通过 npm 脚本完成构建：

```bash
npm run build
```

构建过程执行以下步骤：

1. **TypeScript 编译**：调用 `tsc` 将 `src/` 目录下的 TypeScript 源文件编译为 JavaScript
2. **Shebang 注入**：在 `dist/index.js` 头部添加 `#!/usr/bin/env node` 使其可执行
3. **权限设置**：通过 `fs.chmodSync(p, 0o755)` 设置可执行权限

资料来源：[package.json](https://github.com/mova-compact/mova-flat-runner/blob/main/package.json)

### Makefile 便捷命令

项目提供 Makefile 封装常用运维命令：

| 命令 | 功能 | 资料来源 |
|------|------|----------|
| `make publisher` | 构建 MCP 发布工具 | README.md |
| `make check` | 运行代码检查、单元测试和集成测试 | README.md |
| `make help` | 显示所有可用命令 | README.md |

```bash
# 构建发布工具
make publisher

# 运行完整检查
make check
```

资料来源：[README.md](https://github.com/mova-compact/mova-flat-runner/blob/main/README.md)

---

## 安装与启动

### npm 包方式（推荐）

通过 npx 直接运行，无需全局安装：

```bash
npx -y @leryk1981/mova-flat-runner@3.3.3
```

### 二进制直接运行

构建后可直接运行二进制文件：

```bash
./bin/mcp-publisher --help
```

### MCP 服务器启动

mova-flat-runner 作为 MCP 服务器运行，支持通过 MCP 协议与 AI 客户端集成。启动时必须正确配置环境变量：

```bash
export MOVA_API_URL=https://api.mova-lab.eu
export MOVA_API_KEY=__SET_MOVA_API_KEY__
export LLM_KEY=__SET_LLM_KEY__
export LLM_MODEL=openai/gpt-4o-mini

./dist/index.js
```

资料来源：[package.json](https://github.com/mova-compact/mova-flat-runner/blob/main/package.json)

---

## 客户端配置示例

### Claude Desktop 配置

在 Claude Desktop 的配置文件 `claude_desktop_config.json` 中添加以下内容：

```json
{
  "mcpServers": {
    "mova": {
      "command": "npx",
      "args": ["-y", "@leryk1981/mova-flat-runner@3.3.3"],
      "env": {
        "MOVA_API_URL": "https://api.mova-lab.eu",
        "MOVA_API_KEY": "__SET_MOVA_API_KEY__",
        "LLM_KEY": "__SET_LLM_KEY__",
        "LLM_MODEL": "openai/gpt-4o-mini"
      }
    }
  }
}
```

资料来源：[README.md](https://github.com/mova-compact/mova-flat-runner/blob/main/README.md)

### Codex 配置（TOML 格式）

```toml
[mcp_servers.mova]
command = "npx"
args = ["-y", "@leryk1981/mova-flat-runner@3.3.3"]
startup_timeout_sec = 90.0

[mcp_servers.mova.env]
MOVA_API_URL = "https://api.mova-lab.eu"
MOVA_API_KEY = "__SET_MOVA_API_KEY__"
LLM_KEY = "__SET_LLM_KEY__"
LLM_MODEL = "openai/gpt-4o-mini"
```

资料来源：[README.md](https://github.com/mova-compact/mova-flat-runner/blob/main/README.md)

---

## 健康检查

### API 健康检查端点

MOVA API 提供健康检查接口，可用于监控服务可用性：

```bash
curl -sS https://api.mova-lab.eu/health
```

返回状态码 `200 OK` 表示服务正常运行。

资料来源：[README.md](https://github.com/mova-compact/mova-flat-runner/blob/main/README.md)

---

## 可用工具列表

mova-flat-runner 通过 MCP 协议暴露以下工具，供 AI 客户端调用：

| 工具名称 | 功能描述 | 资料来源 |
|----------|----------|----------|
| `mova_health` | 查询 MOVA API 健康状态 | README.md |
| `mova_registry` | 查询可用合约注册表 | README.md |
| `mova_run` | 执行内置合约工作流 | README.md |
| `mova_query` | 查询合约运行状态和审计信息 | README.md |
| `mova_decide` | 提交决策响应（用于人工审批门） | README.md |
| `mova_connector` | 连接外部系统 | README.md |
| `mova_contract` | 注册和管理自定义合约 | README.md |

资料来源：[README.md](https://github.com/mova-compact/mova-flat-runner/blob/main/README.md)

---

## 安全注意事项

### 敏感信息保护

⚠️ **重要提醒**：以下环境变量包含敏感凭证，**禁止**提交至版本控制系统：

| 敏感变量 | 说明 |
|----------|------|
| `MOVA_API_KEY` | MOVA 后端 API 认证密钥 |
| `LLM_KEY` | LLM 服务提供商密钥 |
| `MOVA_INVOKE_TOKEN` | 本地调用认证令牌 |

部署时应使用环境变量注入或密钥管理服务（如 AWS Secrets Manager、HashiCorp Vault）进行配置。

资料来源：[README.md](https://github.com/mova-compact/mova-flat-runner/blob/main/README.md)

### 安全验证机制

mova-flat-runner 内置安全检查机制：

| 检查项 | 说明 | 资料来源 |
|--------|------|----------|
| **Step Mode 验证** | 验证步骤执行模式与字段定义的一致性 | src/security/step_mode_guard.ts |
| **HUMAN_GATE 保护** | 防止通过通用 step_complete 操作跳过人工审批门 | src/index.ts |

```typescript
// HUMAN_GATE 安全检查示例
const gateGuard = await assertNotHumanGate(config, runId, stepId, requestId);
if (gateGuard) return gateGuard;
```

资料来源：[src/index.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/index.ts)

---

## 自定义合约桥接机制

### 问题背景

对于非内置合约（custom contracts），其 ID 格式为 `local-*` 或 `remote-*`，存在 API 命名空间不一致问题：

- `mova_contract register/run` 使用 `/run/*` 命名空间
- `mova_query status/audit` 使用 `/api/v1/contracts/*` 命名空间

### 桥接解决方案

系统通过内存中的桥接映射表（`CUSTOM_RUN_BRIDGE`）维护 `contract_id → run_id` 映射：

```mermaid
graph LR
    A["mova_contract run"] -->|"创建运行"| B["CUSTOM_RUN_BRIDGE<br/>Map<contractId, runId>"]
    B -->|"查询状态"| C["mova_query status"]
    C -->|"404 时探测"| D["/api/v1/contracts/my"]
    D -->|"返回桥接状态"| C
```

资料来源：[src/index.ts](https://github.com/mova-compact/mova-flat-runner/blob/main/src/index.ts)

---

## 运维监控清单

### 部署前检查项

| 检查项 | 操作 |
|--------|------|
| ✅ 环境变量完整性 | 确认 `MOVA_API_KEY`、`LLM_KEY` 已正确配置 |
| ✅ API 连通性 | 执行 `curl https://api.mova-lab.eu/health` 验证 |
| ✅ 构建产物存在 | 确认 `dist/index.js` 已生成 |
| ✅ 权限设置 | 确认二进制文件具有执行权限 |

### 运行时监控指标

| 指标 | 监控方式 |
|------|----------|
| API 响应时间 | 通过 `MOVA_API_TIMEOUT_MS` 配置超时阈值 |
| 认证失败 | 监控 API 返回的 401/403 错误 |
| 业务逻辑异常 | 监控 `step_complete`、`gate_approve` 返回的错误码 |

### 常见问题排查

| 症状 | 可能原因 | 解决方案 |
|------|----------|----------|
| `Unknown contract_type` | 使用了非内置合约 | 改用 `mova_contract action=run` |
| `MOVA API 404` | 自定义合约在查询命名空间不存在 | 系统已内置桥接机制，查询 `mova_query status` |
| 人工审批门被跳过 | 误用 `step_complete` 尝试完成 HUMAN_GATE | 使用 `gate_approve` 或 `gate_reject` |

资料来源：[tasks/task061.md](https://github.com/mova-compact/mova-flat-runner/blob/main/tasks/task061.md)

---

## 版本信息

| 属性 | 值 |
|------|-----|
| npm 包名 | `@leryk1981/mova-flat-runner` |
| 当前版本 | `3.3.4` |
| MCP 名称 | `io.github.mova-compact/mova-flat-runner` |
| 构建目标 | `dist/index.js` |
| 二进制入口 | `mova-mcp` |

资料来源：[package.json](https://github.com/mova-compact/mova-flat-runner/blob/main/package.json)

---

---

## Doramagic 踩坑日志

项目：mova-compact/mova-flat-runner

摘要：发现 8 个潜在踩坑项，其中 0 个为 high/blocking；最高优先级：配置坑 - 可能修改宿主 AI 配置。

## 1. 配置坑 · 可能修改宿主 AI 配置

- 严重度：medium
- 证据强度：source_linked
- 发现：项目面向 Claude/Cursor/Codex/Gemini/OpenCode 等宿主，或安装命令涉及用户配置目录。
- 对用户的影响：安装可能改变本机 AI 工具行为，用户需要知道写入位置和回滚方法。
- 建议检查：列出会写入的配置文件、目录和卸载/回滚步骤。
- 防护动作：涉及宿主配置目录时必须给回滚路径，不能只给安装命令。
- 证据：capability.host_targets | github_repo:1212840167 | https://github.com/mova-compact/mova-flat-runner | host_targets=mcp_host, claude, chatgpt

## 2. 配置坑 · 来源证据：v3.0.0 — Step Enforcement Runtime

- 严重度：medium
- 证据强度：source_linked
- 发现：GitHub 社区证据显示该项目存在一个配置相关的待验证问题：v3.0.0 — Step Enforcement Runtime
- 对用户的影响：可能影响升级、迁移或版本选择。
- 建议检查：来源显示可能已有修复、规避或版本变化，说明书中必须标注适用版本。
- 防护动作：不得脱离来源链接放大为确定性结论；需要标注适用版本和复核状态。
- 证据：community_evidence:github | cevd_10b68dae59184a36b21a563722829aed | https://github.com/mova-compact/mova-flat-runner/releases/tag/v3.0.0 | 来源类型 github_release 暴露的待验证使用条件。

## 3. 能力坑 · 能力判断依赖假设

- 严重度：medium
- 证据强度：source_linked
- 发现：README/documentation is current enough for a first validation pass.
- 对用户的影响：假设不成立时，用户拿不到承诺的能力。
- 建议检查：将假设转成下游验证清单。
- 防护动作：假设必须转成验证项；没有验证结果前不能写成事实。
- 证据：capability.assumptions | github_repo:1212840167 | https://github.com/mova-compact/mova-flat-runner | README/documentation is current enough for a first validation pass.

## 4. 维护坑 · 维护活跃度未知

- 严重度：medium
- 证据强度：source_linked
- 发现：未记录 last_activity_observed。
- 对用户的影响：新项目、停更项目和活跃项目会被混在一起，推荐信任度下降。
- 建议检查：补 GitHub 最近 commit、release、issue/PR 响应信号。
- 防护动作：维护活跃度未知时，推荐强度不能标为高信任。
- 证据：evidence.maintainer_signals | github_repo:1212840167 | https://github.com/mova-compact/mova-flat-runner | last_activity_observed missing

## 5. 安全/权限坑 · 下游验证发现风险项

- 严重度：medium
- 证据强度：source_linked
- 发现：no_demo
- 对用户的影响：下游已经要求复核，不能在页面中弱化。
- 建议检查：进入安全/权限治理复核队列。
- 防护动作：下游风险存在时必须保持 review/recommendation 降级。
- 证据：downstream_validation.risk_items | github_repo:1212840167 | https://github.com/mova-compact/mova-flat-runner | no_demo; severity=medium

## 6. 安全/权限坑 · 存在评分风险

- 严重度：medium
- 证据强度：source_linked
- 发现：no_demo
- 对用户的影响：风险会影响是否适合普通用户安装。
- 建议检查：把风险写入边界卡，并确认是否需要人工复核。
- 防护动作：评分风险必须进入边界卡，不能只作为内部分数。
- 证据：risks.scoring_risks | github_repo:1212840167 | https://github.com/mova-compact/mova-flat-runner | no_demo; severity=medium

## 7. 维护坑 · issue/PR 响应质量未知

- 严重度：low
- 证据强度：source_linked
- 发现：issue_or_pr_quality=unknown。
- 对用户的影响：用户无法判断遇到问题后是否有人维护。
- 建议检查：抽样最近 issue/PR，判断是否长期无人处理。
- 防护动作：issue/PR 响应未知时，必须提示维护风险。
- 证据：evidence.maintainer_signals | github_repo:1212840167 | https://github.com/mova-compact/mova-flat-runner | issue_or_pr_quality=unknown

## 8. 维护坑 · 发布节奏不明确

- 严重度：low
- 证据强度：source_linked
- 发现：release_recency=unknown。
- 对用户的影响：安装命令和文档可能落后于代码，用户踩坑概率升高。
- 建议检查：确认最近 release/tag 和 README 安装命令是否一致。
- 防护动作：发布节奏未知或过期时，安装说明必须标注可能漂移。
- 证据：evidence.maintainer_signals | github_repo:1212840167 | https://github.com/mova-compact/mova-flat-runner | release_recency=unknown

<!-- canonical_name: mova-compact/mova-flat-runner; human_manual_source: deepwiki_human_wiki -->
