# 无头 Application 运行时设计

> 文档角色：定义 Application、Runtime、Workflow Session、Trigger 和 Presentation 的稳定边界。
> 依赖原则：Kernel 提供通用协议与执行机制，业务模块提供固定定义和固定实现。

## 1. 核心模型

Application 的完整生命周期由四个动作表达：

```text
define → implement → bind → start
```

```ts
const definition = defineApplication({
  slices,
  workflows,
  operations,
  predicates,
  navigationGuards,
  exitPolicies,
  triggers,
});

const application = definition.implement({
  createEnvironment,
  triggerAdapters,
  admission,
  session: {
    open,
    create,
    prepare,
    attach,
  },
  exposeRuntime,
});

const runtime = application.bind({ host, config });
runtime.activate();
const result = await runtime.start('quick');
```

## 2. Application Definition

`defineApplication()` 产生不可变的静态定义，包含：

- Slice 与 Activity Contract；
- Workflow 拓扑、Step 策略和 start binding；
- Operation Contract；
- Predicate；
- Navigation Guard；
- Exit Policy；
- Trigger Contract；
- 编译 Workflow 所需的只读 Environment。

Definition 可以包含 `catalog`、`cart.addProduct`、`checkout.payEftpos` 等业务语义 ID，
但不包含 Host、SDK、设备实例、React Component 或外部订阅。

构造期间完成：

- ID 唯一性；
- Workflow 对 Slice 的引用完整性；
- Resolution 对 Predicate 和 Operation 的引用完整性；
- Workflow 对 Trigger 的引用完整性；
- automatic Trigger 的唯一 Workflow 所有权。

## 3. Application Implementation

`definition.implement()` 固定同一 Application 的运行方式：

```ts
const application = definition.implement({
  createEnvironment,
  disposeEnvironment,
  triggerAdapters,
  admission,
  session,
  maxActiveSessions,
});
```

Implementation 是模块级配置。它不创建设备实例，也不订阅外部事件。

职责映射：

| 协议 | 含义 |
| --- | --- |
| `createEnvironment` | 根据 Runtime Input 创建实例资源 |
| `disposeEnvironment` | 释放实例资源 |
| `triggerAdapters` | 为 automatic Trigger 创建 Adapter |
| `admission` | 判断一次启动是否满足应用规则 |
| `session.open` | 持有启动事务需要的临时资源 |
| `session.create` | 创建 Workflow Session |
| `session.prepare` | 完成发布前的异步准备 |
| `session.attach` | 连接 Session 与 Runtime 级资源 |
| `exposeRuntime` | 将通用 Runtime 投影为 Application 的领域 Runtime |
| `maxActiveSessions` | 定义 pending 与 active Session 的总容量 |

Kernel 调用这些协议，但不读取业务 Environment，也不根据业务 ID 分支。

## 4. Application Runtime

`application.bind(input)` 为一次运行创建 Environment 和 Runtime：

```ts
const runtime = application.bind({ host, config });
```

Runtime 负责：

- Trigger Adapter 激活与释放；
- manual start 与 automatic occurrence 路由；
- occurrence 幂等；
- Admission 和容量控制；
- Session 启动事务；
- Session Registry；
- Runtime Snapshot；
- activation epoch；
- Session、Attachment 和 Environment 释放。

Runtime Input 属于实例级事实。相同 Application 可以绑定不同 Host、设备、测试替身或模拟环境。

## 5. Session 启动事务

manual start 与 automatic occurrence 共用同一条执行链：

```text
resolve Workflow
→ deduplicate occurrence
→ reserve capacity
→ admission
→ session.open
→ session.create
→ session.prepare
→ session.attach
→ register and publish Session
→ attachment.activate
```

### 5.1 原子可见性

Session 完成 `prepare` 和 `attach` 后才能进入 Registry。started 订阅方获得的 Session 已具备
完整业务状态和外部资源连接。

`attach` 可以返回：

```ts
interface ApplicationSessionAttachment {
  activate?(): void;
  dispose(): void;
}
```

- `activate()` 在 Session 发布后执行；
- `dispose()` 随 Session 生命周期执行；
- 两个方法都不携带业务解释。

### 5.2 回滚

失败路径按资源获取的逆序执行：

```text
attachment.dispose
→ session.dispose
→ close start resource
→ release capacity and occurrence
```

回滚满足：

- dispose 幂等；
- 原始失败结果保持权威；
- activation epoch 失效的异步结果不能发布；
- pending 和 active Session 共同参与容量计算。

## 6. Workflow Session

Workflow Session 是某一 Workflow 的单次执行实例，至少提供：

```ts
interface ApplicationRuntimeSession {
  readonly id: string;
  readonly workflowId: string;
  readonly cause: WorkflowStartCause;
  isActive(): boolean;
  dispose(): void;
}
```

具体 Application 可以扩展：

- Workflow Actor 与 Snapshot；
- Resolution dispatch；
- Screen Command；
- reset、cancel、finalize；
- 业务 Projection；
- Session 级订阅。

Session 不持有 automatic Trigger、Application Registry 或 Runtime Environment 的所有权。

## 7. Trigger

### 7.1 Trigger Contract

Definition 中的 Trigger 声明启动协议：

```ts
defineTrigger({ id: 'onScan', mode: 'automatic' });
```

Workflow 通过 Trigger 和 Channel 声明入口：

```ts
defineWorkflow({
  id: 'hdl.barcode',
  start: { trigger: 'onScan', channel: 'product' },
  steps,
});
```

未声明 `start` 的 Workflow 使用内置 `manual` Trigger，Channel 为 Workflow ID。

### 7.2 Trigger Adapter

Implementation 中的 Adapter 连接真实事件源：

```ts
const scannerAdapter = {
  trigger: 'onScan',
  activate: ({ emit }) =>
    devices.subscribe((event) => {
      void emit(toOccurrence(event));
    }),
};
```

Adapter 负责：

- 外部订阅；
- Ingress 校验和去重；
- Trigger Occurrence 标准化；
- 根据 emit result 完成设备侧确认。

Trigger Contract 使用 `triggers` 字段，运行实现使用 `triggerAdapters` 字段。

### 7.3 Channel

Trigger ID 表示信号种类，Channel 表示该 Trigger 内的逻辑地址：

```text
trigger=onScan, channel=product
trigger=onWeight, channel=weighing-product
```

Runtime 只接受与 Workflow start binding 完全匹配的 occurrence。一个 automatic Trigger 在同一
Application 中拥有唯一 Workflow binding。

## 8. Instruction

### 8.1 Command

Command 执行业务能力，但不表示当前 Step 已经解决：

```ts
await session.executeCommand(
  'barcode.consume',
  { code },
  intentId
);
```

### 8.2 Resolution

Resolution 表示 Activity 的业务结果。Acceptance 决定结果如何被接受：

```ts
accept();
require('cart.notEmpty');
perform(checkoutOperation, { require: 'checkout.eligible' });
```

Acceptance 成功后，Actor 按 Workflow Transition 导航。Kernel 负责执行协议，Application
Predicate、Operation Handler 和结果策略赋予业务含义。

## 9. Operation Contract 与 Handler

Application Definition 持有 Operation Contract：

```ts
defineOperationContract({
  id: 'cart.addProduct',
  conflictScope: 'cart',
  retry: 'explicit-user',
});
```

Runtime Session 持有 Handler 和执行协调：

```text
Operation Contract
→ Runtime Handler
→ Coordinator
→ Host Port
→ OperationResult
```

Contract 不包含 Host 或 SDK 实例。Handler 负责具体调用、一致性屏障、错误归一和写后验证。

## 10. Runtime Snapshot

Kernel Snapshot 表达通用事实：

```ts
interface ApplicationRuntimeSnapshot<TSession> {
  readonly state: 'inactive' | 'active' | 'disposed';
  readonly pendingStartCount: number;
  readonly sessions: readonly TSession[];
  readonly lastStartFailure?: ApplicationStartFailure;
}
```

Application 可以投影领域 Snapshot，例如 HDLKiosk 的：

```text
inactive / idle / starting / active / exiting / disposed
```

Snapshot 是当前事实源；started/disposed Event 用于增量观察和遥测。

## 11. Presentation

Presentation 是 Runtime 的可选消费者：

```text
Runtime Snapshot ─┐
Session Snapshot ─┼→ React Controller → View / Screen
Host Projection ──┘
```

React Controller 负责：

- 创建并绑定 Application；
- 随容器生命周期 activate/deactivate/dispose；
- 使用 `useSyncExternalStore` 订阅 Snapshot；
- 将 Runtime 和 Session 方法包装为 UI callback。

React 不拥有 Session 的事实生命周期，也不订阅物理 Scanner 或 Scale Trigger。

## 12. 依赖边界

```text
kernel ← app ← runtime ← ui
           ↑              ↑
         slices ───────────┘
```

- `kernel/`：通用 Application、Workflow、Operation 原语；
- `app/`：HDLKiosk Definition、Contract 和 Workflow；
- `runtime/`：Application Implementation、Host Binding、设备和 Workflow Session；
- `ui/`：React Controller、Renderer 和 View；
- `slices/`：Activity Contract 与局部 Presentation。

Kernel 禁止导入 HDLKiosk、Host、设备、订单、支付和 React 类型。

## 13. 安全不变量

1. Application Definition 不产生外部副作用；
2. Application Implementation 不持有实例资源；
3. `bind()` 创建实例资源，`activate()` 安装 automatic Trigger；
4. automatic Trigger 与 Workflow binding 唯一；
5. Session 完成 prepare 和 attach 后才可见；
6. Attachment 在 Session 发布后激活；
7. 同一 occurrence 最多拥有一个 Session；
8. pending 和 active Session 共同占用容量；
9. deactivate 后的异步启动不能穿透 activation epoch；
10. Session 释放同时释放 Attachment；
11. Runtime dispose 释放 Trigger、Session 和 Environment；
12. Kernel 不解释任何业务 ID。

HDLKiosk 的具体类型、文件落点和测试矩阵见
[10-Application Runtime 自顶向下设计](./10-ApplicationRuntime自顶向下设计.md)。
