> ## Documentation Index
> Fetch the complete documentation index at: https://huoban.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# 示例：Preflight 与 Lifecycle

> 复现 Requirement、Run scope Preflight、离线 MCP evidence 和有限 Lifecycle Handler 声明。

这个 example 展示三个嵌入式执行契约：

* `Requirement` 声明 `Skill`、`Profile` 或 `Adapter` 的目标环境依赖。
* Preflight 分别记录 dependency resolution 与 current-environment readiness。
* Lifecycle Handler 用有限事件声明观察、上报和清理集成。

它们都不是新的顶层 kind。Huoban 仍然只有 10 个顶层 kind。

<Warning>
  dry-run 不是 Preflight，Preflight 不是授权。只有 Run scope 的 `PreflightReady=True` 能满足 `execute` Run 的 readiness 前置条件；Policy、Trust、Checkpoint、sandbox 和平台权限仍须独立判断。
</Warning>

文中的 `examples/...` 是 Huoban source repo 内的路径，不是文档站链接。

## public example 中的依赖

| owner                                | Requirement     | 检查目标                                                                  |
| ------------------------------------ | --------------- | --------------------------------------------------------------------- |
| `Profile/huoban-open-source-project` | `contextSource` | `examples/import/AGENTS.md` 可读。                                       |
| `Adapter/grill-me-skill-md`          | `localPath`     | `examples/fixtures/grill-me/SKILL.md` 可读。                             |
| `Skill/spec-outline-writer`          | `executable`    | `node` 在 `PATH` 上可执行。                                                 |
| `Adapter/stage-reporting-mcp`        | `mcpTool`       | MCP endpoint 与 `report_stage` tool 已声明；offline readiness 为 `Unknown`。 |

这些 Requirement 分别嵌入 Profile、Adapter 和 Skill，而不是放进 Run 顶层。

## 完整 reporting Adapter

下面逐字取自 `examples/adapters/stage-reporting-mcp.yaml`：

```yaml theme={null}
apiVersion: huoban.dev/v1alpha1
kind: Adapter
metadata:
  name: stage-reporting-mcp
spec:
  source:
    type: mcpTool
    url: http://127.0.0.1:8787/mcp
    tool: report_stage
  capabilities:
    - huoban.dev/stageReporting
  requirements:
    - id: reporting-tool
      type: mcpTool
      origin: declared
      freshness:
        maxAgeSeconds: 60
      target:
        serverUrl: http://127.0.0.1:8787/mcp
        name: report_stage
  mapping:
    inputs:
      event: lifecycle.event
      run: lifecycle.runRef
      stage: lifecycle.stage
      artifacts: lifecycle.artifactRefs
  sideEffects:
    declared:
      - networkAccess
      - sendMessage
  trustRef:
    name: local-stage-reporting
```

`freshness.maxAgeSeconds` 只在动态 readiness 得到非-`Unknown` 结果时产生 `validUntil`。offline MCP check 不会伪造新鲜度。

## 完整 reporting Trust

下面逐字取自 `examples/trust/local-stage-reporting.yaml`：

```yaml theme={null}
apiVersion: huoban.dev/v1alpha1
kind: Trust
metadata:
  name: local-stage-reporting
spec:
  subjectRef:
    kind: Adapter
    name: stage-reporting-mcp
  source:
    type: http
    url: http://127.0.0.1:8787/mcp
  level: unknown
  sandbox:
    required: true
    network: restricted
  policyRefs:
    - name: safe-defaults
```

这个 Trust 明确保留 `level: unknown`。Preflight 即使发现 endpoint，也不会把它提升为 `reviewed` 或 `trusted`。

## 完整 Flow

下面逐字取自 `examples/flows/idea-to-spec-review.yaml`：

```yaml theme={null}
apiVersion: huoban.dev/v1alpha1
kind: Flow
metadata:
  name: idea-to-spec-review
  generation: 2
spec:
  profileRefs:
    - name: huoban-open-source-project
  policyRefs:
    - name: safe-defaults
  stages:
    - id: sharpen-idea
      requires:
        capability: huoban.dev/designReview
        role: primary-review
      inputs:
        - name: idea
          from: user.prompt
      outputs:
        - name: review-notes
          artifactType: decision-notes
    - id: draft-spec-outline
      requires:
        capability: huoban.dev/specification
        role: spec-writer
      inputs:
        - name: review-notes
          from: stages.sharpen-idea.outputs.review-notes
      outputs:
        - name: spec-outline
          artifactType: markdown
  checkpoints:
    - after: sharpen-idea
      type: humanApproval
      required: true
      reason: ConfirmDirectionBeforeSpec
  lifecycle:
    - id: report-stage-success
      on: stage.succeeded
      delivery: bestEffort
      requires:
        capability: huoban.dev/stageReporting
        role: stage-reporter
      timeoutSeconds: 10
      retry:
        maxAttempts: 3
```

Handler 需要 `stage-reporter` role。具体实现必须在 Run binding 中锁定，不能写进 Flow：

```yaml theme={null}
bindings:
  stage-reporter:
    adapterRef: stage-reporting-mcp
```

## 复现 dry-run 与默认离线 Preflight

最短命令：

```bash theme={null}
npm run print:dry-run
npm run preflight
```

以下命令把输出写到 `tmp/`，不会覆盖 committed examples。第二条命令没有传 `--offline` 或 `--live`，因此使用默认 offline：

```bash theme={null}
node ./bin/huoban.js print --dry-run examples/flows/idea-to-spec-review.yaml \
  --out tmp/idea-to-spec-review-dry-run.yaml \
  --bind primary-review=adapter:grill-me-skill-md \
  --bind spec-writer=skill:spec-outline-writer \
  --bind stage-reporter=adapter:stage-reporting-mcp

node ./bin/huoban.js preflight tmp/idea-to-spec-review-dry-run.yaml \
  --workspace "examples/flows/*.yaml" \
  --workspace "examples/skills/*.yaml" \
  --workspace "examples/adapters/*.yaml" \
  --workspace "examples/profiles/*.yaml" \
  --workspace "examples/policies/*.yaml" \
  --workspace "examples/trust/*.yaml" \
  --out tmp/idea-to-spec-review-preflighted.yaml \
  --report tmp/idea-to-spec-review-preflight-report.yaml \
  --at 2026-07-15T00:00:00.000Z
```

Preflight 输出两个协议对象：

1. 更新后的 Run，包含 conditions 与 `status.preflight` summary。
2. `Artifact`，包含完整、逐项的 Preflight report。

## 完整 preflighted Run

下面逐字取自 `examples/generated/idea-to-spec-review-dry-run.yaml`：

```yaml theme={null}
apiVersion: huoban.dev/v1alpha1
kind: Run
metadata:
  name: idea-to-spec-review-dry-run
spec:
  flowRef:
    name: idea-to-spec-review
    generation: 2
  profileRefs:
    - name: huoban-open-source-project
  policyRefs:
    - name: safe-defaults
  mode: dryRun
  bindings:
    primary-review:
      adapterRef: grill-me-skill-md
    spec-writer:
      skillRef: spec-outline-writer
    stage-reporter:
      adapterRef: stage-reporting-mcp
  snapshot:
    flowSpecHash: sha256:b7a80c75e7a8f3f0e043d78623e84a0bd70def9f50134657751bf5e27a5fece2
    profileRefHash: sha256:7559a8d488bbdb57906ee5116931bc487bbae1841ab059037d9723c2d848bf77
    policyRefHash: sha256:745db2bbc095810039b9b13ef544ce49bd28e1f7474757cf256e27906fb65006
    bindingHash: sha256:9d47cbf573e28dad7fd0080bdc96894a27ba85377421e4e34f3455cc58a5f01b
status:
  phase: Planned
  conditions:
    - type: RunPlanned
      status: "True"
      reason: DryRunGenerated
      message: Dry-run plan generated without executing agent work.
    - type: FlowSnapshotCreated
      status: "True"
      reason: FlowSpecHashed
      message: Flow/idea-to-spec-review spec was hashed into the Run snapshot.
    - type: PreflightResolved
      status: "True"
      reason: DependenciesResolved
      message: Static references, generations, capabilities, and bindings were checked.
      lastTransitionTime: 2026-07-15T00:00:00.000Z
    - type: PreflightReady
      status: "True"
      reason: DependenciesReady
      message: Current-environment availability was checked for all required dependencies.
      lastTransitionTime: 2026-07-15T00:00:00.000Z
    - type: OptionalDependencyUnavailable
      status: "True"
      reason: OptionalCheckNotReady
      message: At least one optional dependency, including a best-effort lifecycle handler, is unavailable or unknown.
      lastTransitionTime: 2026-07-15T00:00:00.000Z
  preflight:
    scope:
      type: run
    snapshotHash: sha256:727425f3b2752558c1857b555989abf5b1e56a5c1a34e85cc831313ff0f25669
    checkedAt: 2026-07-15T00:00:00.000Z
    reportRef:
      kind: Artifact
      name: idea-to-spec-review-dry-run-preflight-report
```

`PreflightReady=True` 与 `OptionalDependencyUnavailable=True` 可以同时成立：best-effort handler 的 checks 是 optional，不阻塞 required gate。

## resolution 与 readiness

Preflight 对每项 dependency 分开记录两个结果：

| 结果           | 问题                   | 示例                                              |
| ------------ | -------------------- | ----------------------------------------------- |
| `resolution` | 引用或 target 是否明确且可解析？ | MCP endpoint 与 tool name 已完整声明。                 |
| `readiness`  | 当前环境现在能否使用它？         | offline 模式没有 discovery，因此 MCP tool 为 `Unknown`。 |

下面是 report 中 MCP Requirement 的完整 check，逐字取自 `examples/generated/idea-to-spec-review-preflight-report.yaml`：

```yaml theme={null}
- id: lifecycle.report-stage-success.adapter.stage-reporting-mcp.reporting-tool.0
  ownerRef:
    kind: Adapter
    name: stage-reporting-mcp
  requirementType: mcpTool
  optional: true
  handlerId: report-stage-success
  checkedAt: 2026-07-15T00:00:00.000Z
  resolution:
    status: "True"
    reason: DynamicTargetResolved
    message: The dynamic target is fully declared.
  readiness:
    status: Unknown
    reason: McpProbeSkipped
    message: Offline preflight resolved the MCP target but did not perform live discovery.
```

Report 的 `snapshotHash` 哈希 `Run.spec`。如果 `Run.spec` 发生变化，采用方必须比较 hash 或重新 Preflight；不要把旧 evidence 当作新 snapshot 的结论。

## dryRun 与 execute gate

| Run mode  | required check gate                                  |
| --------- | ---------------------------------------------------- |
| `dryRun`  | required `False` 会失败；required `Unknown` 可以保留给审查。     |
| `explain` | 与 `dryRun` 相同，只允许保留 required `Unknown`，不允许硬 `False`。 |
| `execute` | required resolution 和 readiness 都必须聚合为 `True`。       |
| 省略 `mode` | reference CLI 按 `execute` 处理。                        |

即使 `execute` Preflight gate 通过，仍然没有获得授权。当前 CLI 也没有 `execute` 命令。

## stage scope

传入 `--stage` 会生成 stage scope evidence 和 `StagePreflightResolved` / `StagePreflightReady` conditions：

```bash theme={null}
node ./bin/huoban.js preflight tmp/idea-to-spec-review-dry-run.yaml \
  --workspace "examples/**/*.yaml" \
  --out tmp/sharpen-idea-preflighted.yaml \
  --report tmp/sharpen-idea-preflight-report.yaml \
  --stage sharpen-idea
```

stage scope 只回答该 stage 的检查问题。局部满足 mode 规则时 CLI 可以返回退出码 0，但会明确提示它不构成 Run gate；失败时仍先写出 Run 与 report，再返回非零。它不能替代 Run scope `PreflightReady=True`，也不能授权整个 Run。

## `--live` 的边界

只有明确需要动态 discovery 时才添加：

```bash theme={null}
node ./bin/huoban.js preflight tmp/idea-to-spec-review-dry-run.yaml \
  --workspace "examples/**/*.yaml" \
  --out tmp/idea-to-spec-review-live-preflighted.yaml \
  --report tmp/idea-to-spec-review-live-preflight-report.yaml \
  --live
```

`--live` 会访问对象声明的 URL：

* remote context 使用只读 `HEAD`。
* MCP 使用 `initialize`、`notifications/initialized` 和有界的 `tools/list` / `resources/list`。
* 每次 MCP 响应体上限为 1 MiB；最多遍历 20 页。
* 不调用 MCP tool。
* 报告去除 URL user info、query、fragment、常见 credential 形式和远端 MCP error text。
* discovery 成功不改变 Trust，也不批准 side effects。

## inferred Requirement

当 Requirement 使用 `origin: inferred` 时，schema 要求 `reviewStatus`。如果是 `needsReview`，Preflight 会返回：

```text theme={null}
resolution: Unknown (RequirementNeedsReview)
readiness: Unknown (RequirementNeedsReview)
```

当前 CLI 没有 review 命令。不要虚构从 `needsReview` 到 `accepted` 的自动状态迁移；review 与记录方式由采用方明确实现。

## Lifecycle 有限事件

`v1alpha1` 只允许：

```text theme={null}
run.ready
run.started
stage.started
stage.succeeded
stage.failed
checkpoint.requested
checkpoint.resolved
run.finalizing
run.succeeded
run.failed
run.cancelled
```

不要添加任意 hook 字符串。状态改变属于 Stage，决策属于 Checkpoint，权限属于 Policy，外部能力属于 Adapter；Lifecycle Handler 只用于观察、上报和清理。

当前实现只有：

* schema 声明；
* Run role binding；
* Preflight binding / Requirement checks；
* `explain` 输出；
* `Run.status.lifecycleInvocations` 状态形状。

当前没有 lifecycle dispatcher 或执行 runtime。authored Run 中的 `Pending` invocation 不会自动迁移。

因此，`delivery` 在当前仓库只影响 Preflight requiredness：`bestEffort` 作为 optional evidence，`required` 参与 required gate。具备 dispatcher 的采用方才负责兑现投递、timeout 与 retry 语义。终态 `run.succeeded`、`run.failed`、`run.cancelled` 只允许 `bestEffort`；required 收尾使用 `run.finalizing`。

## Lifecycle Event Envelope

事件信封是共享 fragment，不是顶层 Kind。下面逐字取自 `examples/fragments/checkpoint-requested.lifecycle-event.json`：

```json theme={null}
{
  "id": "idea-to-spec-review-001-direction-requested",
  "type": "checkpoint.requested",
  "occurredAt": "2026-07-15T00:00:00.000Z",
  "runRef": {
    "name": "idea-to-spec-review-001"
  },
  "stage": "sharpen-idea",
  "checkpointRef": {
    "name": "idea-to-spec-review-001-direction"
  },
  "artifactRefs": [
    {
      "kind": "Artifact",
      "name": "review-notes"
    }
  ],
  "conditionTypes": [
    "CheckpointRequired"
  ]
}
```

重试同一逻辑事件时必须复用 `id`。信封不携带 prompt、Profile 正文、secret 或完整输入输出；handler 通过受控引用读取所需 Artifact。

## 相关页面

* [快速开始](/quickstart)
* [idea-to-spec-review](/example-idea-to-spec-review)
* [SKILL.md Adapter](/example-skill-md-adapter)
* [安全策略](/security)
* [示例总览](/examples-overview)
