# 部署、生产晋级与恢复

应用开发者从工作区执行 `pnpm openxiangda`。平台负责应用版本、运行状态和恢复决定。工具链自身的 npm 发布由平台维护者负责，应用项目无需复制发包脚本或平台验证矩阵。

## 按原操作自助诊断

保留原 requestId、commandId、DeploymentRun ID 或 fileId；应用管理员可在已有
登录工作区查询，不需要服务器账号。目标平台需提供 `application.scoped-diagnostics@1`。

```bash
pnpm openxiangda logs --request-id <原请求ID> --json
pnpm openxiangda logs --command-id <原命令UUID> --json
pnpm openxiangda logs --file-id <受管文件UUID> --environment test --json
pnpm openxiangda logs --deployment-run-id <原RunUUID> --environment production --json
```

requestId 默认最近一小时，结果会显示实际 from/to；跨日排查可传一对 UTC ISO
时间 `--from` / `--to`（最多 24 小时）。其余定位器按原 ID 查，不接受时间窗口。
默认环境是 test；production 必须显式选择。结果包含实际站点、应用、环境、观测时间、
持久状态和是否截断，只投影允许的元数据，不返回业务正文、文件名或密钥。

`observed` 仅表示查到事实，仍需看每条状态；`not_observed` 不表示原写入没有发生；
`unavailable` 表示本次诊断暂不可用。三者都不会重放原操作。保留原 ID、输入和幂等键，
按返回的下一步查询原回执，不创建新运行代替未知结果。文件存在不等于当前用户可下载。

原有 `pnpm openxiangda logs [deploymentId]` 保持部署日志行为。MCP 的
`application_diagnostics` 使用同一范围和状态约定；requestId 的 from/to 必须明确提供。

## 测试部署

源码托管仍可通过 `pnpm openxiangda source push` 提交和推送，但不是测试发布的前置步骤。
发布使用当前工作区内容，源码提交、分支名称和是否存在未提交修改不会阻塞测试环境；包元数据会保留实际来源信息。

开发者可以从当前任务分支或本地工作区直接发布。团队是否要求合入默认分支属于协作规范，不由 Devkit 在每次发布时重复执行。

准备部署时直接执行 deploy，它已经包含兼容性预检、生成、检查、测试和构建。只想检查代码时使用 [check](./testing.md)，无需在 deploy 前重复运行全套检查。

测试发布还会核对 [AppSpec](./appspec.md) 的需求依据、架构、权限、性能预算和验收计划。缺失时给出具体记录位置，先补实际设计；首次测试部署不要求预先完成线上业务验收。

```bash
pnpm openxiangda deploy --dry-run --json
pnpm openxiangda deploy
pnpm openxiangda status --json
pnpm openxiangda logs <deployment-id> --json
```

默认目标为 `test`，平台内部标识为 `preproduction`。只读预览不生成构建产物、不上传制品、不提交 DeploymentRun；因此预览成功不能证明代码已通过检查。正式检查使用本地完整校验，再通过目标平台的 `configurationCompatibility` 核对同源规则、密钥、已有物理模型和登录提供方等只读条件；失败时停止后续步骤。只有启用了自定义 Nest 后端的应用才需要构建后端镜像及对应 Docker 环境。

平台启用镜像上传后，`deploy` 优先校验本机保存的同输入 OCI 制品，并用当前平台登录态取得原镜像的回执；没有有效制品时才用 Docker Buildx 构建并分片上传。开发者只需应用部署权限，无需登录平台管理员的镜像仓库或取得推送凭据。已完成的镜像层按摘要复用，中断后重新执行同一条部署命令可恢复上传；平台核验镜像完整性后才进入应用发布。尚未升级的旧平台保留其原镜像构建合同；新平台已声明上传能力但未启用时，命令会明确报告平台配置缺失。

官方模板按实际后端输入匹配缓存：根 manifest、锁文件、workspace/tsconfig、后端目录、
共享 contracts、vendor、Dockerfile 和有效 ignore 规则。只改前端源码或文档时可跳过
Docker 构建，前端及配置仍重新检查、编译和发布。独立配置摘要变化不会直接使镜像失效；
配置改变生成 contracts 时会重建。缓存只绑定当前账号、平台、应用、环境和工具版本，
不会跨身份复用。自定义 Dockerfile 默认比较完整构建上下文；用
`<后端目录>/Dockerfile.dockerignore` 明确限制实际 Docker 输入后，也可复用同输入制品。
修改 Dockerfile 后不再推测其 shell 命令只读取哪些文件。

成功镜像同样短期保留，不保存远端 ready 状态或 reference；每次复用都校验全部 OCI
字节并由平台重新鉴权。平台没有原镜像时上传同一制品，不重新构建。缓存过期或被回收
后需正常构建。需要重新执行本机构建时可用：

```bash
OPENXIANGDA_BACKEND_IMAGE_CACHE=false pnpm openxiangda deploy
```

此设置禁用成功制品复用和保留，仍恢复同输入的未完成上传；Docker 自身的层缓存继续
生效。它不跳过检查、权限或平台回执。无需为普通前端改动手工指定旧镜像或平台管理员介入。

上传失败后，本机保留原 OCI 制品，错误的 `details` 包含候选 ID、原 digest、失效时间及真实 `causeCode`。
按错误的 `remediation` 处理后，在原工作区执行原 `pnpm openxiangda deploy` 命令（保留原环境等参数）：
相同构建输入、账号、平台、应用、环境和工具版本会校验原制品并从平台保存的偏移继续；即使 Docker
暂时不可用也能恢复已经构建的镜像。
完成响应丢失时同样查询原摘要的 ready 回执。源码（包括未提交文件）、权限主体或目标变化会创建新候选，
不会冒充原上传。上传鉴权仍在平台执行；本地缓存不授予部署权限。

`APPLICATION_IMAGE_STORAGE_BUDGET_EXCEEDED` 表示平台拒绝了存储计费准入，不能靠重新登录解决。
`details.causeDetails` 保留服务器实际提供的应用占用字节、镜像数、应用预算和本次请求字节；未提供
的数据不会推测补齐。请平台核对现役、回滚和运行引用及受管清理结果，确认额度后恢复原上传。
该错误的 `retryable=false` 表示需要先处理额度，上传器不会立即重复请求；原候选仍可在处理后恢复。
正在恢复的候选应先用原工具版本完成上传，再升级 SDK；升级工具版本也会改变候选身份。

缓存保留最多 24 小时、8 个候选、总预算 8 GiB；空间不足时仅回收未被使用的旧候选。
仅当平台单镜像上限不超过 8 GiB 时启用本地恢复，较大的平台配额保留原上传路径，不降低平台允许的镜像大小。
成功制品复用额外预留最多 516 KiB 的 OCI/缓存元数据；平台上限过于接近 8 GiB 时保留
原上传失败恢复模式，成功后删除，不降低平台单镜像额度。
缓存和登录凭据分开，位于本机用户私有目录。过期或已经回收的候选需重新构建；已有 DeploymentRun
的未知结果仍查询原运行，镜像上传恢复不会重新发起已提交的部署。

生成的不可变 AppVersion 绑定前端、可选后端、配置契约和制品摘要。默认提交后持续跟踪同一运行，直到平台成功、失败或取消，最多观察 15 分钟。`--no-wait` 只提交，适用于已有状态跟踪器的自动化；此时返回运行 ID 不代表部署完成。

构建、上传及平台执行都会显示当前阶段和耗时，长步骤每 10 秒反馈一次。平台的准备、部署、切换和健康检查状态来自原运行。观察超时或连接中断不会取消部署或重建候选，使用下面的命令继续跟踪：

```bash
pnpm openxiangda status <deployment-id> --watch
```

原运行因 `WORKFLOW_DESIGNER_EXCEPTIONS_ACKNOWLEDGEMENT_REQUIRED` 失败时，支持
`deployment.workflow-adoption-preview` 的平台可只读检查冻结候选的真实例外：

```bash
pnpm openxiangda status <deployment-id> --workflow-exceptions --json
```

必须指定原 Run ID，不能与 `--watch` 合用。结果绑定原包、AppVersion、attempt、当前 Head
和候选流程版本；例外列表有上限，`truncated=true` 不代表已看完。
该命令不会写画布草稿、确认例外、重试或另建部署。`activationPermission=not-granted`
明确表示预览没有授予激活许可。旧平台能力缺失时保留原运行并报告缺口。
当前没有通过此命令确认例外或转换旧实例事实的能力；不要给 retry 塞未支持的确认字段。

若应用仍锁定旧 SDK，统一 launcher 可能继续选择旧引擎。不要为诊断改应用依赖或冻结候选。
可在应用目录之外建立独立诊断目录，安装发行回执指定的精确 `openxiangda-devkit-core` 版本
（该版本须包含 `deploymentWorkflowPreview`），使用以下公开服务接口；不使用 `latest`。
把脚本保存为该诊断目录下的 `preview.mjs`，运行 `node preview.mjs <原工作区绝对路径> <原Run ID>`：

```js
import { readFile } from 'node:fs/promises';
import { join, resolve } from 'node:path';
import { OpenXiangdaDeveloperSession, workspaceSessionPath, OpenXiangdaControlPlaneClient } from 'openxiangda-devkit-core';
const [rootInput, runId] = process.argv.slice(2);
if (!rootInput || !runId) throw new Error('需要原工作区和原 Run ID');
const root = resolve(rootInput);
const link = JSON.parse(await readFile(join(root, '.openxiangda/link.json'), 'utf8'));
if (typeof link.appCode !== 'string' || typeof link.baseUrl !== 'string') throw new Error('工作区绑定无效');
const session = await OpenXiangdaDeveloperSession.load({ sessionPath: workspaceSessionPath(root) });
if (!session) throw new Error('需要原工作区的平台登录会话');
session.assertPlatform(link.baseUrl);
const client = new OpenXiangdaControlPlaneClient({ baseUrl: session.baseUrl, tokenProvider: session });
console.log(JSON.stringify(await client.deploymentWorkflowPreview(link.appCode, runId), null, 2));
```

此路径不加载应用 CLI 或源码配置、不重建候选，也不改应用 package/lock/seal。
认证沿用官方工作区会话及平台绑定检查；如 token 到期，会话组件仍按原规则刷新凭据。
它只做原身份核验、能力检查及原 Run GET；平台尚未支持时明确失败，不回退为重新部署。



网络响应不确定时先查询原运行，不凭本地输出创建重复部署。平台部署成功后，仍需执行真实角色的业务验收。

新的发布使用当前源码准备前端与配置，后端按实际输入复用或构建；同输入镜像可复用上述短期原制品缓存。
该缓存不保存部署状态，平台仍唯一拥有 DeploymentRun。提交响应不确定时使用原 DeploymentRun 的 status/logs 和 recovery 继续处理。

## 测试环境验收

至少记录应用版本、目标环境、真实角色、复现数据、预期和实际结果。按改动范围检查页面、权限、业务规则和失败路径；详见[校验与验收](./testing.md)。

| 证据 | 能说明什么 |
| --- | --- |
| 本地 check 成功 | 本次声明兼容，检查、测试和构建通过 |
| 包密封完成 | 存在可识别的不可变候选版本 |
| DeploymentRun 成功 | 平台完成该版本的部署流程 |
| 真实角色的页面与业务操作通过 | 对应场景在目标环境可用 |
| 生产晋级成功并回读 | 生产使用指定测试版本；仍需核对实际入口与关键业务 |

构建成功、提交成功和真实业务验收是不同证据，报告时分别给出实际状态。

## 生产晋级

生产必须复用已成功部署到测试环境的同一版本，不能从当前源码直接重建：

先按真实操作保存 `appspec/verification/<测试运行ID>.json` 并提交、推送到主线。晋级会从测试源码提交读取原验收计划，核对报告的运行 ID、包摘要、AC 场景与性能证据；主线后来的需求不改变已测范围。

性能若由用户明确延期，按 [AppSpec 延期记录](./appspec.md)填写有证据的 `performanceDeferral` 并保留原测量。晋级阶段明确显示性能未通过；功能 AC 和其余版本/权限门禁继续执行。仅升级验收工具和补充报告不会重建原测试制品。

该版本的源码提交必须仍包含在权威远端主分支中。主分支后来有新提交，不会改变本次晋级的制品。若任务分支采用 squash/rebase 合并，应在最终主线提交上重新冻结并验证测试候选，不能继续晋级合并前的提交。

```bash
pnpm openxiangda deploy --environment production --from <test-deployment-id> --dry-run --json
pnpm openxiangda deploy --environment production --from <test-deployment-id>
pnpm openxiangda status --json
```

预览会精确读取指定测试运行的封存配置并核对生产密钥、模型和登录条件，返回源运行、版本与摘要。主线后来变化或版本较旧，不会使预检改用当前源码或最近版本列表。测试运行失败、版本缺失或条件不符时直接失败。平台在真正晋级时再次权威校验。生产参数不接受测试环境的 `environmentId` 或 `idempotencyKey`。已有生产发布授权时可继续执行；授权不明确时先准备版本、预览及验收证据，再确认具体发布对象。

## 失败、重试与回滚

`logs` 返回首个失败 `rootFailure`、最近失败 `latestFailure`、候选状态、尝试账本与 `recovery`。无失败时对应字段为 null。按平台给出的 `recovery.nextCommand` 处理；只在 `recovery.cancelAllowed` 为真时取消。已激活的运行不能用 cancel 撤销。

```bash
pnpm openxiangda retry <deployment-id>
pnpm openxiangda cancel <deployment-id>
pnpm openxiangda rollback --to <app-version-id>
```

环境版本回滚不保证撤销数据库业务写入；数据修复需要单独计划与验证。暂停与恢复默认作用于测试环境；生产必须显式选择：

```bash
pnpm openxiangda stop
pnpm openxiangda start
pnpm openxiangda stop --environment production
```

stop 保留数据与配置，start 从当前不可变版本恢复。不要把暂停、取消、回滚当作同一种操作。

## 自动化与错误定位

CLI 的 `--json` 输出单个 `openxiangda.cli-result/v2` 对象；失败包含 code、message、retryable、remediation、nextCommand，以及适用的 pointer/details。自动化依据 code 和结构化字段决策，不解析中文描述。

`check`、`deploy` 和持续状态观察的结果带 `data.execution`，包含本次操作 ID、总耗时及各阶段状态和耗时。阶段进度写到 stderr，保持 `--json` 的 stdout 可解析；`--json-events` 则通过 `command.status` 返回同源结构化进度。MCP 客户端提供 `progressToken` 时收到标准进度通知；不订阅通知仍能从最终结果读取阶段摘要。MCP `deploy_app.wait` 默认 true，`deployment_status.watch` 可继续观察原运行。

兼容性错误会列出当前工具链与目标平台的版本、能力和契约要求。按定位修复声明或升级目标平台，不删除真实业务要求、改写摘要或绕过权限来让预检通过。应用所需能力由规范化声明派生，应用不能手写一份能力列表冒充平台支持。

`OPENXIANGDA_CONFIGURATION_VALIDATOR_MISMATCH` 表示工具链与平台的校验实现不配套，应按发布说明升级对应版本；它会在构建、镜像推送和制品上传前出现。模型类型不能原地替换时，按提示设计新字段及数据转换；必需密钥缺失时配置目标环境后继续，不修改源码伪装问题已解决。

MCP 的 check_app、deployment_plan、deploy_app 使用与 CLI 相同的环境与生产晋级参数规则。完整参数以[CLI](./reference/cli.md)与[MCP](./reference/mcp.md)为准。

## 构建前运行配额

`deploy --dry-run`（MCP `deployment_plan`）会只读查询目标 TEST 的运行配额，输出 `runtimeCapacity` 的核验时间、所需增量、各配额剩余量和缺口。`sufficient: false` 表示当前不足；`null` 表示无需新增或未核验，必须结合 `basis` 与 `capacity.checked` 阅读。专用命名空间未检查不能当成资源充足。同一预检还会比对源码 `workflows.activations` 与环境 Head，输出 `workflowActivation` 诊断：版本回退（error，拒绝部署）、源码缺失已激活流程（warning，本次部署会停用）、目录不可读（error，拒绝盲部署），详见 [Workflow 事件](./workflow-events.md)。

正式 deploy 在检查脚本和镜像构建前预检；平台缺少配套能力或无法核验时明确停止。配额快照不预留资源，实际执行再次检查。已有可验证密封候选会携带摘要和幂等键，平台识别 `existing-run` 时返回原运行，不把它当作新副本；观察或恢复原运行使用 status/retry。不要为绕过配额创建新包或切换目标环境。

## TEST 单副本维护替换

平台配额只允许一个后端副本时，可显式选择维护替换。它会停止当前 TEST 后端，期间应用不可用；成功后激活新版本，失败时由原 DeploymentRun 恢复旧后端。恢复尚未完成时继续占用原运行，status/logs 显示恢复阶段与首个失败，不允许用新部署或取消跳过恢复。

先运行 `openxiangda deploy --environment test --strategy maintenance-replace --dry-run --json` 查看前驱版本、Head revision 和停止后的容量估算，再使用相同参数去掉 `--dry-run` 提交。计划不预留资源。只有已存在、身份匹配的单副本 TEST 后端才可使用；前端应用、新应用和 production 不支持。默认仍为 rolling，不会因配额不足自动停止实例。

策略属于部署幂等请求。默认维护幂等键含策略，显式幂等键不能在不同策略之间复用。已有运行通过 status/retry 恢复；持续恢复中的运行保持 preparing/maintenance-recovery-required，平台会重试恢复，恢复失败时保留原运行与错误。

## OSS 直传与静态分发

平台管理员在「平台内置对象存储 → 应用构建与分发」启用「开发工具直传 OSS」后，新版 CLI 自动协商并直传构建包和镜像分块。开发者无需本机 OSS 密钥；平台从自己的存储读取，校验大小及摘要后封存或导入受管镜像仓库。中断后重新运行原 deploy，仍按原 digest/offset 恢复，不手工创建新镜像尝试。旧平台没有此能力时保持原通道；云端失败明确报告，不静默改用另一通道。

「前端静态资源 → OSS / CDN」让 JS、CSS、字体等从 OSS 的不可变版本目录加载。HTML、登录、路由和 API 保留平台入口。Public Base URL 可配置已可用的 HTTPS CDN 域名；未配 CDN 时是 OSS 分发，不能据此承诺 CDN 加速。OSS GET/HEAD 的 CORS 需包含实际平台 origin。源码映射不公开。

存储权限需覆盖隔离目录的 PutObject、GetObject、对象 ACL（暂存 private，校验后的静态资源 public-read）。不得更改整桶 ACL。建议为 `openxiangda/application-staging/` 配置 1 天生命周期；平台不自动覆盖现有生命周期。配置切换发生在直传和导入之间会明确拒绝，重新运行原部署即可按新配置恢复。关闭直传/切回平台静态分发可回退，不修改业务数据或环境版本。
