# Bundle 生命周期与引用协议

使用 Bundle 安装身份、资源授权和按需访问解析，实现跨资源的外部业务生命周期。

Bundle 生命周期协议 `6.0.0` 以整个安装实例为操作边界，访问解析协议为 `4.0.0`。
所有自定义插件统一使用这一机制，Module 作为 Bundle 资源接受显式属性授权。

## 安装身份与执行语义

`installation` 包含安装环境 `environmentKey`、`applicationRuntimeId`、`workspaceId`、
`bundleInstallId` 和 Bundle 来源 `bundle.environmentKey + bundle.bundleId`。
安装环境与 Bundle 来源可以不同，所有定位均由 Runtime 提供。

`operationType + stage` 表达操作与资源执行时机：

| 字段                        | 允许值                                      |
| ------------------------- | ---------------------------------------- |
| `operation.operationType` | `INSTALL`、`UPGRADE`、`CONFIGURE`、`DETACH` |
| `stage`                   | `BEFORE_RESOURCES`、`AFTER_RESOURCES`     |

`operation.operationId`、安装身份、插件身份和阶段由平台关联。插件按这些身份协调自己的业务状态，
不能另造安装记录或改变平台的操作结果。调用结果不确定时，Runtime 使用相同的操作身份和阶段
重复调用同一生命周期请求。插件必须安全处理重复执行并返回相应属性；已完成的业务变更不得重复创建资源。

`executionSequence` 是 Runtime 的正整数操作顺序，同一操作重试保持不变。
失败保留 Runtime 占用；DETACH 可以串行接管失败操作。所有者的顺序校验必须覆盖实际业务写入。
详见[失败与重试](/development/lifecycle-plugin-development/failure-and-retry/)。

## 配置变更的参与范围

Module 属性、完整配置、网关域名和 Bundle 内 Agent/Timer 配置统一进入 CONFIGURE。
Runtime 先比较实际变化，再依据完整 Locator 和当前已验证安装资源账确定参与的 Bundle，随后读取这些 Bundle 的插件绑定。
没有插件的已安装 Module 仍有真实 CONFIGURE 所有者；无变化不生成操作，也不会扫描无关 Bundle 的插件声明。
Module 或插件资源缺少合法安装归属时明确报错。没有 Module 身份和插件声明的 Runtime 本地服务按原本地配置合同保存，不伪造 Bundle 安装。
`instanceRefProperty` 仍受保护，不能通过改变 Locator 或另一条配置入口改写。

## Runtime 编译后的授权

Bundle 作者通过领域对象选择声明授权；插件执行边界使用 Runtime 根据确切 Module 归属编译的资源定位。
下面是与后续请求对应的 SDK 授权结构，用于插件验证输入和输出；它不是作者清单，不能复制到 `baijimu.bundle.json`。

```json
{
  "pluginId": "tenant-provider",
  "protocolVersion": "6.0.0",
  "order": 0,
  "failurePolicy": "FAIL_FAST",
  "configuration": {"schemaVersion": "1.0.0", "values": {}},
  "resources": [{
    "resource": "publisher/sales/MODULE/customer",
    "readProperties": ["region"],
    "writeProperties": ["tenantId"],
    "credentialProperties": ["inventory"]
  }]
}
```

## 生命周期请求

```json
{
  "contractVersion": "6.0.0",
  "pluginId": "tenant-provider",
  "executionSequence": 1,
  "installation": {
    "environmentKey": "consumer",
    "applicationRuntimeId": "runtime-a",
    "workspaceId": 42,
    "bundleInstallId": "installation-a",
    "bundle": {"environmentKey": "publisher", "bundleId": "sales"}
  },
  "operation": {
    "operationId": "operation-a",
    "operationType": "INSTALL",
    "currentVersion": null,
    "targetVersion": "1.0.0"
  },
  "stage": "BEFORE_RESOURCES",
  "configuration": {"schemaVersion": "1.0.0", "values": {}},
  "resources": {
    "publisher/sales/MODULE/customer": {
      "action": "INSTALL",
      "currentVersion": null,
      "targetVersion": "1.0.0",
      "properties": {"region": {"@type": "Data", "value": "north"}},
      "references": {"inventory": {"@type": "ServiceReference", "serviceId": "inventory-a"}}
    }
  }
}
```

`configuration.schemaVersion` 属于插件自己的业务配置合同，不等于生命周期版本。
`resources` 的键为 ResourceLocator，只包含获授权的资源和属性。
`properties` 的每项输入和输出都必须是 `{"@type":"Data","value":...}`，不接受裸值。
Runtime 按 Module 的属性定义校验 `value`，再写回一次 Data 包装，不递归猜测业务对象内的 `@type`。
SDK 的 `decode_data()` 用于读取业务值，`from_data_values()` 用于把业务值编码为输出；
`decode()` 与 `from_properties()` 保留完整的类型包装。
引用始终是逻辑 `ServiceReference`、Map 或 List，不附带调用地址或密钥。

## 响应

成功响应使用完整 CModel envelope：外层 `contractVersion: "1.0.0"`、`errorCode: "0"` 和 `data`。
`data` 原样关联请求的 `contractVersion`、`pluginId`、`installation`、`operation`、`stage`、`executionSequence`，
并返回 `outcome` 与 `resources`。

`outcome` 为 `NOT_APPLIED`、`APPLIED`、`VERIFIED`、`FAILED` 或 `UNKNOWN`。
`resources` 按 ResourceLocator 返回获准写入的业务 Data 属性，例如业务租户编号，以及插件自己签发、
按安装隔离的业务服务 Token。敏感业务属性必须声明 `isSensitive: true`，并由当前插件的
`writeProperties` 显式授权；它不属于用于引用解析授权的 `credentialProperties`。
不能回写引用属性、平台引用解析返回的 `access` 地址与 Token，或其他身份的共享管理凭据。
业务凭据的签发、轮换、撤销和保存边界见[插件自有业务凭据](/development/lifecycle-plugin-development/develop-plugin-service/#插件自有业务凭据)。
Runtime 校验输出范围并决定安装进度。`UNKNOWN` 不能当成功处理。不得只返回裸 `data`。

```json
{
  "contractVersion": "1.0.0",
  "errorCode": "0",
  "data": {
    "contractVersion": "6.0.0",
    "pluginId": "tenant-provider",
    "executionSequence": 1,
    "installation": {
      "environmentKey": "consumer",
      "applicationRuntimeId": "runtime-a",
      "workspaceId": 42,
      "bundleInstallId": "installation-a",
      "bundle": {
        "environmentKey": "publisher",
        "bundleId": "sales"
      }
    },
    "operation": {
      "operationId": "operation-a",
      "operationType": "INSTALL",
      "currentVersion": null,
      "targetVersion": "1.0.0"
    },
    "stage": "BEFORE_RESOURCES",
      "outcome": "APPLIED",
    "resources": {
      "publisher/sales/MODULE/customer": {
        "tenantId": {"@type": "Data", "value": "tenant-a"}
      }
    }
  }
}
```

## 日常属性更新

修改已安装 Module 的属性会由所属 Bundle 执行 `CONFIGURE`，`currentVersion` 与 `targetVersion` 相同。
Runtime 在同一事务中保存目标配置并冻结现有 Bundle 操作和两阶段授权；失败的事务不会留下半份操作。
同一 Bundle 保留完整资源与插件范围，继续使用现有操作认领、派发和 Runtime 内部提交流程。
执行期间，其他用户配置写入会被拒绝；过期的配置快照不能覆盖新值。
网络中断后的重试保持 `operationId + stage`，插件返回同一业务结果及获准写入的 Data 属性。

## 业务调用前解析访问

插件使用独立平台凭据调用 `POST /module-plugin-manager/api/bundle-plugin/service-reference/resolve`。
在登记的平台 API 基地址后追加这个完整路径；不要再使用 `/bundle-plugin/**` 或 `/plugin/**` 别名。
该接口属于插件管理服务，公网网关按服务级 `/api/**` 规则映射，由插件管理服务验证插件凭据。
请求包含：

```json
{
  "contractVersion": "4.0.0",
  "installation": {
    "environmentKey": "consumer",
    "applicationRuntimeId": "runtime-a",
    "workspaceId": 42,
    "bundleInstallId": "installation-a",
    "bundle": {"environmentKey": "publisher", "bundleId": "sales"}
  },
  "resource": "publisher/sales/MODULE/customer",
  "propertyKey": "inventory"
}
```

不传 `pluginId`、目标服务 ID、URL 或 Token。平台从认证身份和活动安装的当前属性解析授权目标。
成功 `data` 返回相同的 `installation`、`resource`、`propertyKey`，以及当前逻辑 `reference`、
`access[serviceId] = {url, token}` 和 `expiresAt`。

完整的引用响应示例：

```json
{
  "contractVersion": "1.0.0",
  "errorCode": "0",
  "data": {
    "contractVersion": "4.0.0",
    "installation": {
      "environmentKey": "consumer",
      "applicationRuntimeId": "runtime-a",
      "workspaceId": 42,
      "bundleInstallId": "installation-a",
      "bundle": {
        "environmentKey": "publisher",
        "bundleId": "sales"
      }
    },
    "resource": "publisher/sales/MODULE/customer",
    "propertyKey": "inventory",
    "reference": {
      "@type": "ServiceReference",
      "serviceId": "inventory-a"
    },
    "access": {
      "inventory-a": {
        "url": "https://runtime.example/service/inventory-a",
        "token": "example-current-access-token"
      }
    },
    "expiresAt": null
  }
}
```

`ServiceReferenceMap.valueMap` 的业务键和 `ServiceReferenceList.valueList` 的顺序以当前响应 `reference` 为准；相同 serviceId 共用一份 access。
凭据仅用于本次业务操作，不持久化。任一目标未授权、安装失效或凭据撤销时，整次解析失败。

## 异步发布事件

Bundle 插件的后台 Worker 可以通过逻辑服务引用调用 Runtime 事件方法。Bundle 作者在 Module 中声明
引用属性，例如 `eventPublisher`，并将其绑定到拥有所需事件声明的 Module；发布自身事件时可以引用自身。
`eventPublisher` 是插件与 Bundle 作者约定的业务属性名，不是平台保留字段。在 Bundle 顶层声明插件绑定与该 Module 的资源授权。

在作者清单的插件授权中，把该属性加入 `credentialProperties`。使用目录返回的 Module 身份，例如：

```json
{
  "object": {"type": "MODULE", "moduleId": "a054ef60-f64c-47cf-8b63-4dbdd05e5ef1"},
  "readProperties": [],
  "writeProperties": [],
  "credentialProperties": ["eventPublisher"]
}
```

资源生效后，生命周期请求中的该属性为逻辑 `ServiceReference`。插件保存业务实例与
`installation`、资源 Locator、引用属性名的关联；不要保存事件 URL 或平台调用密钥。

每次发送及重试都执行以下步骤：

1. 用当前安装定位、资源 Locator 和 `propertyKey: "eventPublisher"` 请求上述访问解析接口。
2. 验证响应的安装、资源、属性和有效期，按当前 `reference.serviceId` 选择对应的 `access`。
3. 在服务基地址后添加目标 Module 已声明的事件方法名作为单个路径段，使用本次 token 作为 Bearer 凭据调用。
4. 保持事件 ID 和业务幂等键不变，成功后确认 outbox；失败按业务重试策略处理，下次重新解析访问。

事件引用的授权与其他业务引用各自声明。销售活动等其他属性的授权不能代替事件发布授权。
解析失败、安装停止或卸载、授权撤销、响应身份不匹配或过期时不得投递；也不能使用此前保存的访问材料。
引用授权只授予当前目标允许的方法，事件名与载荷仍受目标 Module 的事件声明约束。

事件声明与订阅见[模块事件与发布机制](/development/bundle-development/module-development/event-development/)。
注册、Bundle 绑定及上线验收见[Bundle 生命周期插件](/development/bundle-development/advanced/lifecycle-plugins/)。

请求级 `X-Runtime-Actor-Assertion` 属于独立的[Runtime 用户委托](/development/bundle-development/module-development/actor-delegation/)合同，不进入插件的安装关联或引用访问材料。
