---
name: microi-deployment
description: Microi 安装、部署、升级和本地运行指南。用于吾码服务器运维面板 Microi.Panel、Docker Compose、离线安装、Windows IIS、源码运行、本地 ApiBase/OsClient 切换与浏览器隔离、数据库、MinIO、反向代理、滚动发布、健康检查、备份恢复和生产部署验收。
---

> **Codex 非阻塞自动更新：** 当前宿主为 Codex 时，吾码 CLI、Codex 插件与工作区 AI/MCP 由后台自动更新；需要诊断时读取 `../microi-codex-installer/SKILL.md`。更新失败、等待空闲或尚未重载均不得阻断当前、正在进行或新建任务。非 Codex 宿主跳过此项。

# Microi 安装与部署

本 Skill 把官网 Docker、Windows 和源码运行文档转成可执行的安全流程。具体镜像、
版本、端口和命令可能变化，执行前必须回读当前中文官网、仓库 compose/配置和目标
主机状态，不能把旧示例当作当前生产事实。

数据库备份 MCP 使用 `microi_list_database_backup_tenants` 盘点可备份租户，使用 `microi_run_database_backup` 提交带稳定幂等键的持久化任务。备份必须进入共享存储并回读文件大小、哈希和任务终态；本机进程返回成功不等于备份可恢复。

## 必读参考

- 部署方式、依赖、配置和验收矩阵：`references/deployment-matrix.md`
- 系统级交付与 MCP：`../microi-system-delivery/SKILL.md`
- 文件/对象存储：`../v8-file-upload/SKILL.md`
- 数据库模型：`../microi-db-schema/SKILL.md`

## 先确认部署类型

### 吾码服务器运维面板 Microi.Panel

- 项目由 `Microi.Ops` 更名为 `Microi.Panel`；独立 .NET + Vue + Microi.UI 宿主使用自己的管理员账号、SQLite 和 Docker 权限，业务 API/Web 停止时仍可操作。只安装面板时使用 `install-microi-panel.sh`，不要求先安装吾码业务平台、宝塔或 1Panel。具体命令与能力边界见中文官网 `server-panel/overview.md`。
- 先确认主机内存、Docker Engine 身份、活动任务、已有 Ops/Panel 控制器、端口、编排和数据目录。每台 Docker 主机只运行一个吾码控制器。与宝塔/1Panel 共存时使用独立安装目录、容器名、卷、网络和端口；80/443 等监听端口不能被两个入口同时占用，不得重置 Docker 或接管第三方面板资源。
- 插件市场只安装目录内明确版本与 SHA-256 的镜像；检查架构、许可、健康检查、持久卷和真实业务读写。Oracle 等原厂许可渠道不擅自镜像；MinIO 社区归档后的源码构建必须保留对应源码、许可与摘要。跨数据库大版本升级仍需专用迁移方案。
- 网站和反向代理由受管 Docker Nginx 执行；配置先 `nginx -t`，再原子发布与回读。HTTP-01 需真实 DNS 和公网 80 端口的挑战路由，不能用跳过验证冒充签发成功；通配符使用外部 DNS 验证后导入证书。证书续期必须验证新的序列号/指纹、服务重启后的调度和暂停行为。
- 网站文件仅管理受管卷；文本编辑使用内容哈希拒绝覆盖并发修改。冷备份须明确服务中断，检查正常退出与归档哈希；恢复写入新卷并保留原卷，重试继续原操作 Id。卸载保留业务卷，重新安装沿用记录的原镜像、凭据和数据卷。
- 新面板默认独立 HTTPS 端口 61890，管理员密码与 PFX 密码存于服务器权限受限文件；不要把它们复制进业务 SaaS、V8、MCP 参数或日志。旧 Ops 保留原 `OPS_*`、目录、账号、端口、数据保护密钥及 `MicroiOpsUrl`，用原 Compose 替换为 Panel 镜像，不能重新初始化。
- 业务 MCP 继续通过 `microi_get_administrative_capabilities`、`microi_admin_table_data`、`microi_update_module` 和应用发布工具维护平台入口；DiyToken 不授予主机 Docker 权限，不增加能从租户会话执行任意宿主机命令的工具。主机安装与恢复使用授权的 SSH/终端和独立面板会话。
- 真实验收归入 `Microi.Tests/Panel` 专项，包含全部目录版本、数据库/对象存储写读与冷恢复、SSL/续期、文件、任务重启、独立安装升级回退，以及真实宝塔/1Panel 两种安装顺序。普通外部容器只能证明资源归属保护，不能替代第三方面板共存结论。

### 依赖镜像统一镜像源

- 安装器、Dockerfile、发布与测试夹具的依赖镜像统一使用 `Microi一键编译发布配置.json` 中 Region/Namespace 对应的阿里云仓库。源镜像清单维护在 `Microi.Server/tools/dependency-images.json`，禁止客户每次安装直接拉 Docker Hub/MCR。
- 联网发行机执行 `Microi一键编译发布.sh --mirror-dependencies`，由 `dependency-images.mjs` 读取同一配置的账号密码并用 stdin 登录；禁止将凭据写进命令、日志、镜像或仓库。完成后用 `verify` 回读摘要和多架构清单。
- 上游固定 SHA-256；国内加速只能作为传输通道，不能修改版本、替换不明镜像或丢失 CPU 架构。上游和阿里云目标摘要不一致立即停止。Oracle/达梦/金仓等受许可限制的镜像只在具备再分发权后镜像，不用无授权镜像代替。

用户明确要求同版本仅发布 Docker 热修复时，运行 `Microi一键编译发布.sh --docker-only-hotfix` 并选择后端镜像方案。该入口保持当前版本，禁止 NuGet、文档和官方资源数据库发布，同时保留 Full、候选源码一致性、混淆后冒烟和镜像门禁。修复应进入本地源码及捆绑资源，由目标程序启动升级；不得通过直接改生产表结构代替源码修复。

共享源码持续变化时，可在独立目录保存完整候选源码和依赖，以该目录运行同一 Full 与发布脚本。专用测试服务使用独立端口，通过发布脚本变量 `MICROI_RELEASE_BACKEND_PORT`、`MICROI_RELEASE_FRONTEND_PORT` 指定收尾端口（默认仍为 61501、61500）；进程管理器仍必须核验 PID、命令行和工作区归属，不得结束原工作区服务。变量仅供发布脚本使用，不进入生产 API 配置。

| 场景 | 推荐入口 |
|---|---|
| 生产 Linux、快速安装、可滚动升级 | Docker/Compose |
| 无互联网环境 | 在联网机制作离线包，再在目标机校验并安装 |
| Windows 传统环境 | IIS + .NET Hosting Bundle + 独立依赖 |
| 开发/调试 | 后端源码 + 前端 Vite，本地依赖或隔离容器 |

本地 `Microi.Client` 默认读取 `src/config.json` 的 `ApiBaseDev`，也允许在 `#` 前通过 URL 参数
临时指定运行目标：

```text
http://localhost:61500/?OsClient=iTdos&ApiBase=https%3A%2F%2Fapi.itdos.com
```

URL 的 `ApiBase`、`OsClient` 优先级最高，但不会隔离同源 localStorage/Pinia/Token。多个 AI
对话或自动化并行测试不同目标时，每个 `ApiBase + OsClient` 必须使用独立浏览器上下文/Profile；
Playwright 使用 `browser.newContext()`。人工第二组至少使用无痕窗口，多个 Chrome 无痕窗口不能
视为多组强隔离。完整取值顺序、线上识别和验收见 `../microi-client-frontend/SKILL.md` 与
`../playwright-e2e/SKILL.md`。

不得在未确认目标主机、目录、数据卷和备份的情况下执行官网“删除所有容器/编排”
或任何递归删除命令。

## 变更前只读盘点

至少记录：

- 操作系统、CPU、物理内存、可用磁盘、时区与端口占用；
- 当前 API/Web/Worker 节点数、镜像/版本、反向代理与证书；
- MySQL、Redis、MongoDB、MinIO/HDFS 地址和持久卷；
- 当前 `OsClient`、数据库备份、对象存储备份和配置备份；
- JWT/AuthSecret 等集群共享密钥的指纹一致性，绝不输出明文；
- `/api/Diagnostics/health` readiness 与 `/api/Diagnostics/liveness`；
- 当前运行中的 Node、dotnet、Docker build 等重任务。

## 后端配置单一事实源（强制）

Microi API 安装时，`AppSettings` 与同名容器环境变量只允许以下十个启动引导项：

`OsClient`、`OsClientType`、`OsClientNetwork`、`OsClientDbType`、`OsClientDbConn`、`OsClientRedisHost`、`OsClientRedisPort`、`OsClientRedisPwd`、`OsClientRedisDataBase`、`OsClientDbMongoConn`。

- 除这十项外，业务开关、超时、重试次数、资源上限、代理信任、密钥路径和第三方密钥通常必须在 SaaS 引擎 `sys_osclients` 的合适 Tab 中动态配置，并提供代码安全默认值；影响整个 API 进程的字段只读取主租户记录。唯一固定例外是官方 License 信任链：恢复重试次数/间隔使用代码常量，签发私钥只读取固定只读挂载 `/app/microi_private.pem`，不得为这三项创建 SaaS 字段。
- 不得新增 `MICROI_*`、`DOS_ORM_*`、自定义 `AppSettings` 节点或通用 `Environment.GetEnvironmentVariable(name)` 作为 API 运行配置；新增配置必须同步升级字段、表单 Tab、脱敏/子租户隔离、缓存刷新、文档和源码扫描测试。
- `ASPNETCORE_*`、`DOTNET_*` 属于 .NET 宿主配置；构建、安装器、测试、MCP 和发布脚本自己的进程变量也不属于 API 业务配置，但生产 API 代码不得读取它们来控制业务行为。
- `AuthSecret` 等已有 SaaS 敏感字段继续由受保护的主租户记录提供。普通业务私钥路径仍按相应 SaaS 配置管理；官方 License 签发私钥路径固定为 `/app/microi_private.pem`，只允许生产签发节点只读挂载。密钥明文不得写入镜像、Compose、日志或普通 V8 投影。
- 验收必须扫描生产源码、`appsettings.json`、在线/离线安装编排，断言 API 容器仅出现上述十项；不能只检查某一个示例文件。

## 一键安装脚本版本时间（强制）

- 每次修改 `数据库、案例、文档、资料/install-microi.sh`，必须同时更新文件头版本和 `SCRIPT_VERSION`，两处完全一致。
- 固定格式为 `vYYYY-MM-DD HH:mm:ss`，使用 `Asia/Shanghai` 时间并精确到秒；禁止只写日期或沿用上一次修改时间。
- 验收同时断言两处版本一致、格式正确，并以脚本实际启动输出为准；官网静态地址尚未发布新文件时，不能把本地版本误报为客户已经可下载。
- 恢复旧数据库时，OCR、翻译等附加字段先出现不代表完整升级链已经成功。安装器必须先回读 `sys_config.ServerVersion` 达到本脚本最低版本并通过 API liveness/readiness，才能部署和配置附加能力；核心中间迁移失败或超时应失败关闭，禁止用部分字段就绪冒充整个平台升级成功，也禁止在升级事务仍执行时主动重启 API。
- Compose v2 生成文件不再写顶层 `version`；所有模板直接以 `services:` 开始，避免 `the attribute version is obsolete` 告警。
- LibreTranslate 属于一键安装默认组件：安装选择空输入按 `1` 处理，语言套餐空输入按基础套餐 `1` 处理；因此用户一路按 Enter 使用官方推荐组合。只有明确输入 `0` 才跳过，提示、端口预算、官网文档和静态回归测试必须一致。
- OCR 的 Upgrade29 与 LibreTranslate 的 Upgrade31 字段门禁只在对应附加服务部署检查通过后回读，每秒一次、最多 15 秒；首轮成功就继续，超时快速关闭对应能力并提示镜像/迁移版本，不得退回 5 分钟空等，也不得直接创建字段绕过平台升级。附加能力门禁失败不得中断已通过完整升级链和 readiness 的核心平台。
- 官方 API/Web 使用浮动 `latest` 时，生成的 Compose 必须设置 `pull_policy: always` 或在启动前显式拉取并核对，不能因宿主机已有同名镜像就复用旧版本；测试专用本机镜像覆盖必须明确使用 `never`，避免误访问或覆盖远端镜像。
- 端口、密码和数据目录全部生成后，安装器必须注册统一的失败收尾：数据库、缓存、存储、API/Web、完整升级链或 API liveness/readiness 等核心后段错误保留原始非零退出码，并输出标题为“安装未完成”的恢复汇总，列出已生成端口、凭据、目录、失败阶段和全部容器状态。OCR/LibreTranslate 的网络、镜像、容器或 SaaS 配置失败只记录附加能力警告、保持对应能力未启用并继续核心安装。两种结果都不得把 `Running` 当 readiness，也不得绕过 Upgrade29/Upgrade31 或完整升级链门禁。
- 从客户/旧库恢复安装时，安装器只按 `OsClient + OsClientType + OsClientNetwork + IsEnable + IsDeleted` 处理目标主租户：0 条时幂等创建最小可运行记录，1 条时原位复用，超过 1 条失败关闭。禁止把所有活动 `sys_osclients` 批量改成输入的 OsClient；新记录不得落库 `DbConn/DbReadConn/DbMongoConnection` 或 Redis 主机、端口、密码，这些继续由十项编排启动配置提供。MinIO、OCR 等安装值随后只更新这个精确三元组并回读唯一性。

### 复盘：普通帐号运行安装器直到步骤 5 才因 `/microi/compose` 权限失败

- 触发场景：普通帐号已经加入 Docker 组，前四步的探测和交互看似正常；脚本在开放防火墙后直接执行 `mkdir -p /microi/compose`，才报 `Permission denied`。命令替换中的数据目录创建错误还可能被旧 Bash 的 `set -e` 语义掩盖，使成功提示先于真实失败出现。
- 根因：安装器把 `sudo` 零散放在软件包、防火墙和 systemd 命令前，却没有在真实安装或修复入口统一取得 root 身份；同时假设精简系统一定安装 `sudo`，普通用户与“root 但无 sudo”两种环境都没有明确的早期门禁。
- 通用规则：会写 `/microi`、`/home`、防火墙、systemd/cgroup 或 Docker 宿主状态的一键安装/修复，必须在步骤 1 和任何交互、凭据生成、宿主机写入前完成一次 root 门禁。普通帐号有 `sudo` 时重新执行同一绝对脚本并保留参数；无 `sudo` 或验证失败时零副作用停止并给出 `su -` 恢复指引。已经是 root 时不得要求系统额外安装 `sudo`；目录创建仍要显式检查只读文件系统、空间和权限错误。
- 自动化检查：提供只执行权限门禁的无副作用入口；在本地 Linux 容器分别覆盖 root 且无 sudo、普通帐号且无 sudo、普通帐号通过 sudo 提权三种场景，断言成功路径最终 `uid=0`、失败路径发生在步骤 1 之前且不创建 `/microi`。源码契约同时锁定正常安装与 `--repair-app` 均调用同一门禁，并执行 `bash -n`。

## 多节点与滚动发布

后端默认按多个 API/Worker 节点设计：

- 所有节点共享业务数据库、MongoDB、Redis 和持久对象存储。
- 全局状态、任务进度、锁和幂等事实不能只放进程内存/本机文件。
- 新旧版本并存期间使用“先扩展、后迁移、再收缩”的兼容顺序。
- 节点先停止接新工作，再有界排空；readiness 退出流量，liveness 只反映进程存活。
- 数据库迁移、建索引、种子和缓存预热必须幂等，多节点同时启动不能重复副作用。
- AuthSecret 在全部节点保持一致；轮换必须有明确的全节点策略，否则现有 JWT 会失效。

## 构建资源保护

启动 Node/Vite/dotnet/Docker build 前检查物理内存和同类进程。默认只运行一个重任务。
启动门槛按“当前阶段进程树预算 + `max(1.5GB, 物理内存 5%)` 系统安全余量”计算，
优先采用实测峰值；顺序阶段分别计算，不叠加峰值，也不得再用固定 20% 随机器容量放大门槛。
资源不足时改做定向测试/构建，不并行启动多个全量任务；全机占用达到 95% 时暂停或终止本轮启动的重任务树。

## Windows 多 AI 本地服务与 Release 文件锁

多个 AI 对话共用同一工作区和固定 `61500/61501` 时，前后端是工作区级单例共享服务，
不是每个对话各自拥有的后台进程。发布必须使用“工作区互斥 + 精确身份识别 + DLL 锁复核”：

- 健康开发服务默认复用；需要重载源码时串行重启。长期后端只从项目目录执行
  `dotnet run --launch-profile Microi.net.Api`，禁止直接运行 `bin/Release/net10.0`
  或 `bin/Release/publish` 作为 E2E 服务。
- 一键发布在改写输出目录前创建 `.tmp/microi-process-state/release.lock`；锁存在时其它 AI
  不得启动、自愈或重启 `61500/61501`。
- Windows 发布前调用 `Microi.Server/tools/Microi.LocalProcessManager.ps1 -Action PrepareRelease`。
  只在端口、命令行和当前工作区路径同时匹配时停止 Microi API/Vite，并额外查找当前工作区
  的 Release API；身份不匹配时失败关闭，禁止使用 `/IM dotnet.exe`、`/IM node.exe`、
  `/IM chrome.exe` 或 `/IM msedge.exe` 全机清理。
- Vite 以相对 `node_modules/vite/bin/vite.js` 启动且父进程退出时，命令行可能没有绝对工作区路径。
  进程管理器只能把只读回读到的 CWD 精确等于当前 `Microi.Client` 作为补充证据；读取失败、
  CWD 属于其它目录或仅检测到孤儿状态时仍须失败关闭。回归同时覆盖当前工作区可精确停止、
  外部工作区同名相对 Vite 保持运行。
- 停止后必须确认 `61500/61501` 已释放，并以 `FileShare.None` 独占打开
  `Microi.net.Api/bin/Release/net10.0` DLL。只结束 `VBCSCompiler` 不能解决正在运行的
  `.NET Host` 对业务 DLL 的锁定。
- Edge/Chrome 用户浏览器、VS Code 的 Playwright Test Server、MCP/语言服务 Node、
  数据库和 Redis 不属于发布清理范围。人工查看使用进程管理器的 `-Action Status`。

## 一键发布产物边界与缓存

- 需要完整解决方案构建和 NuGet 打包时，先完成一次 Release build；publish 阶段复用已验证产物，禁止先 clean 再重复编译同一项目引用链。
- API 镜像只允许使用 `bin/Release/publish`，前端镜像只允许使用 `bin/Release/dist` 和必要的服务器配置；`net10.0`、源码、测试输出及其它构建中间目录不得进入 Docker 上下文。
- `logs`、故障 spool、WAL 和节点诊断文件属于运行态数据。发布前后及冒烟测试后都要从 publish 清理，但不得删除源码/持久卷中尚待重放的原始 spool；项目文件应从源头设置为不复制到发布目录。
- 正式发布不需要 PDB 时，从 publish 清单统一排除项目引用和第三方 PDB，并在推送前断言数量为零；这不等于删除编译中间目录中用于本地诊断的符号。
- Docker 使用内容摘要缓存并在基础镜像标签可变时检查更新；不要在每个镜像方案前无条件 `prune -a` 或永久使用 `--no-cache`。磁盘不足时先报告占用，再对精确目标做可恢复或有条件清理。
- DLL 混淆/签名必须先于最终冒烟测试，确保被验证、写入 NuGet 和放入 Docker 的是同一份最终 DLL。前端旧浏览器产物可以按源码、转换器和依赖锁文件的内容指纹做增量缓存，但缓存命中后仍需执行完整依赖、polyfill 和入口校验。

## 部署证据分层

不要把某一层成功等同生产完成：

1. 配置/compose 静态检查。
2. 镜像拉取或定向构建成功。
3. 容器/进程启动且无启动错误。
4. liveness/readiness 正常，依赖可达。
5. 登录、菜单、FormEngine、ApiEngine、文件上传等真实路径正常。
6. 双节点重复投递、节点退出、滚动升级与恢复验证。

未执行的层必须明确写“未验证”，不能用“部署成功”概括。

### 复盘：启动文案变化导致一键发布冒烟假失败

- 触发场景：发布产物已经输出 `Now listening` / `Application started` 并可访问，但发布脚本因为等待某句历史中文启动文案而持续到超时，期间运行态日志或 spool 不断写入 `publish/logs`。
- 根因：把易变化的日志文本当成服务可用事实源；超时/异常路径没有对临时进程和运行态目录做完整的有界收尾。
- 通用规则：发布冒烟必须启动最终混淆/签名后的产物，确认本次进程仍存活，再以 `/api/Diagnostics/liveness` HTTP 200 判定启动成功；使用专用空闲端口，成功、超时、异常和中断路径都要在有限时间内结束本次进程并清理 publish 内的临时 logs/PDB。版本号已经更新但尚未提交时，重跑默认继续工作区当前版本，不能再次自动递增；当前版本进入 Git HEAD 后才计算下一版本。
- 自动化检查：构造不再输出旧中文文案但 liveness 返回 200 的版本，断言冒烟成功；另覆盖端口占用、进程提前退出和 liveness 超时，断言脚本失败原因准确、进程无残留、`publish/logs` 不存在。再把工作区版本设为高于 HEAD，验证重跑默认版本保持不变。

### 复盘：旧租户启动闭包把应用包展示字段当成物理列并反复重启

- 触发场景：升级后的节点发现平台运行时接口缺失，准备从内置官方应用包自愈；旧包把接口名称导出为 `Name`，目标 `sys_apiengine` 的真实字段却是 `ApiName`，直接拼接包对象后出现 `Unknown column 'Name' in 'field list'`。修完别名后，另一批旧库又因 `UserId NOT NULL` 且无默认值、而少数新接口包漏出审计字段，出现 `Field 'UserId' doesn't have a default value`。启动门禁失败会使 Docker 的 restart policy 不断重启同一进程。
- 根因：启动自愈把“应用包传输对象”误当成“数据库物理行”，既没有规范化历史别名，也没有按目标租户的真实表结构过滤；同时只检查“列是否存在”，却没有覆盖旧结构中仍为必填且无默认值的审计列。不同年代租户会依次暴露 unknown-column 或 required-column 错误。
- 通用规则：任何在 FormEngine 可用前直接写核心物理表的可信自愈，都必须先规范化已知历史别名，再应用固定实体字段白名单，并与目标租户实时回读的物理列取交集；SQL 只使用经过双重筛选的固定标识符和参数值。官方包本身必须携带完整审计字段；无登录上下文的启动门禁还必须为历史必填审计列提供稳定的系统身份兜底，并为 `IsDeleted/IsEnable/Lock` 等开关及 `ApiRole/Files` 等集合字段补齐语义明确的插入默认值，这些默认值不得参与授权。必需业务列缺失时给出明确物理升级错误，写入后按稳定 Key 强回读，禁止把包级元数据直接转换成列名。
- 自动化检查：使用包含旧 `Name`、未来包级字段、新版本可选列及缺失 `UserId/UserName/Lock` 的官方包样例，分别对完整结构与精简旧结构断言 `Name -> ApiName`、未知字段被丢弃、必需字段保留、审计字段和传统必填列获得稳定非空值；同时扫描全部启动闭包包，保证 `SysApiEngines` 只输出 `ApiName` 且审计字段及传统必填开关完整，并锁定新增接口生成器输出的集合字段，最后执行真实启动日志门禁回归。

## 空数据库 SQL 导入回归

- PostgreSQL 导出中的 V8、默认值和注释使用显式 `E'…'` 字符串，同时转义反斜杠、引号、
  物理换行及控制字符。分别在 `standard_conforming_strings=on/off` 中完整导入，回读代码
  哈希、长度、空字符串与 NULL；不能把关闭标准字符串模式当作通用修复，也不能删改 V8 代码。
- MySQL 5.7/8 分别在隔离 Docker 数据库执行完整原始脚本。SaaS 宽表的非索引配置文本
  优先使用 `mediumtext`，保留主键、租户标识和索引列；字段元数据、物理列与应用包 DDL
  必须一致。不通过关闭严格模式掩盖行宽错误，不将字符转义问题一概归为字段长度不足。
- 通过官方 MCP 修复母版后发布归属应用，后端导出修复进入镜像后再运行既有空库制作后台任务。
  等待成功终态，完整下载各 ZIP 并校验 SHA-256，再做导入与数据完整性验收。

## 禁止事项

- 启动字段门禁必须检查核心普通列的可空约束，不能仅以“字段存在”判定就绪。旧库非空列在程序共享租约内放宽为 `NULL`，保留 Id/数据库主键、列类型、默认值、注释和索引；同版本热修复也需回读真实结构。修复必须在插入启动接口之前执行，禁止通过逐次补默认值掩盖结构问题。

- 不把数据库/Redis/MinIO 密码写入仓库、日志、命令回显或最终答复。
- 不因容器更新而删除数据库卷、对象存储卷或日志 spool。
- 不在所有节点同时硬重启。
- 不把 `docker ps` 的 Running 当作 readiness。
- 不在没有备份和恢复演练时执行数据库升级或不可逆迁移。
- 不修改 `microi.doc/docs/doc/about/update-log.md`，除非用户明确要求发版。
- 撤回或重写版本日志前，必须遵循 `../workspace-conventions/SKILL.md` 的“多对话共享工作区变更归属保护”；当天提交、最新 `HEAD`、相同作者或相关提交信息都不能单独证明改动属于当前对话。
