---
name: app-store
description: Microi 应用商城开发、打包、安装和升级规范。用于官方/社区应用、Manifest、源码与构建产物、依赖、后台安装任务、租户隔离、增量升级、回滚和验收。
---

> **Codex 非阻塞自动更新：** 当前宿主为 Codex 时，吾码 CLI、Codex 插件与工作区 AI/MCP 由后台自动更新；需要诊断时读取 `../microi-codex-installer/SKILL.md`。更新失败、等待空闲或尚未重载均不得阻断当前、正在进行或新建任务。非 Codex 宿主跳过此项。

# Microi 应用商城

## 核心原则

应用包是可审计、可重复安装、可增量升级的交付单元。运行类型使用 `ApplicationType`：普通平台包的新建默认值是 `Regular`，既有商城平台应用/通知仍使用 `Platform`，另外还有 `MicroService`、`UniApp`、`Web`；读取端必须兼容 `Regular/Platform`，不能在迁移完成前强制改单值。官方/社区来源使用 `PublisherType`。

`AppType` 是历史复用字段：旧包/接口曾把它用于官方/社区来源，也曾把它作为运行类型回退。新代码不能把 `AppType` 当事实源；只在读取旧数据时回退，写入新数据使用 `ApplicationType + PublisherType`。

应用包是声明式资源的权威交付边界：当前包标记为 `Managed` 的表单元数据、菜单、字段、接口引擎等资源按包覆盖；当前包标记为 `CreateIfMissing` 或 `InsertIfMissing` 的租户扩展/配置只补缺。覆盖安装不等于整表清空，仍禁止把发布方租户业务数据、密钥或连接配置原样复制到目标租户。

## 平台升级的应用商城边界（强制）

- 表、字段、Tab、菜单、权限、接口引擎、事件、数据源、页面、打印、工作流、任务和可幂等种子数据都应通过应用包升级；不得为这些资源在 `Microi.Server/Microi.Upgrade/` 新增租户定制 .NET 代码。
- 有官方 MCP 权限时，固定使用 `microi_itdos`（`https://api.itdos.com`、`OsClient=iTdos`）更新官方母版、制作并发布对应应用，按内容哈希/包版本回读后，再由目标租户 MCP 安装或更新。无官方权限时只通过当前用户自己的 MCP/Manifest 更新其数据库并回读，不得假借通用升级器越权发布官方应用。
- `Microi.Upgrade` 只保留应用商城/安装器启动前必需的核心物理兼容或协议迁移，并要求持久化版本门、共享租约、幂等、失败不推进版本。禁止每次启动对每个租户重跑不断增长的历史迁移清单。
- 新增升级点前必须完成“商城不可表达性”证明：逐项说明为什么 `DiyTables/DiyFields/DDLStatements/PhysicalColumns/DataSets`、菜单、接口引擎、工作流或 AI 应用包无法交付，以及为什么该结构在登录/商城恢复入口可用前就必须存在。不能证明时一律升级对应官方应用包；客户租户缺少某张业务表不得写成全平台 `.NET` 启动迁移。
- 历史升级点不能因为今天可由商城表达就直接删除：若后续步骤依赖其物理结果，须保留为冻结兼容链，或把必要前置检查显式并入后续步骤。新增步骤必须使用高于当前目标的唯一四段版本并追加到链末尾，不得抬高旧步骤版本造成非单调执行顺序。
- 租户协调器必须先只读 `ServerVersion`：已达到当前一次性基线时不得取得升级租约、重放历史不变量或刷新缓存；落后租户取得共享租约后必须再次读取版本，防止多节点重复执行。启动成本应近似“每租户一次版本读取 + 实际待执行迁移”，不得近似“租户数 × 历史升级点总数”。主租户接流量前的最小物理/商城引导门禁可独立保留，但不得扩展成全部子租户的永久全量扫描。
- 同一批历史漂移确需修复时，用一个新版本的“一次性基线”在租约内幂等封口，并仅在全部步骤强回读成功后前向推进版本；失败不得推进。基线完成后，任何新增核心迁移都必须再升版本，任何业务资源修复都必须再升应用包版本，禁止恢复为每次启动复检。
- 平台通用缺陷的交付证据必须分开记录：源码修复、官方母版资源、商城包发布后回读、目标租户后台安装任务、目标租户资源回读和真实 UI/接口验收；其中任一步未完成都不能笼统称为“已发布并安装”。
- 任何计划让全部吾码租户通过“安装/更新官方应用”获得的标准字段、布局、菜单、页面或种子数据，必须先在官方 `microi_itdos` 主租户创建并回读，再从该主租户按精确菜单/表资源导出新版包、单调递增应用版本并发布。包正文以 `PackageHdfsPath + PackageSha256 + PackageSize` 为事实源；`AppPakcet` 只作旧版读取兼容，验证完成后应为空。禁止先只在客户/子租户补字段，再用本地手工 JSON 冒充官方母版；客户验证应发生在官方包发布之后。
- 同一标准能力若同时属于基础 SaaS 空库包和独立官方应用（例如系统设置、系统账号），两条交付链都要更新：基础包保证新租户初始化完整，独立应用保证存量租户可增量安装。表、字段和初始化模板可按这两个目标分别交付，但同一个 Managed ApiEngineKey 必须只有一个官方包所有者，禁止 SaaS、Store 与独立应用重复携带后互相覆盖。发布后分别核对 `PackageInfo.Version`、物理列、`diy_field` 布局节点、接口引擎单一归属、商城行 `AppVersion` 和 HDFS 包指针/下载哈希，不能用其中一条替代另一条。

## 吾码创建人开发时的强制发布闭环

- 每项平台功能先执行 [工作区基础规范](../workspace-conventions/SKILL.md) 的“平台功能四项同步检查”，不能把商城包发布当成官方文档、Skills、MCP 同步的替代。本节细化其中需要发布应用包时的流程；没有可打包资源变化时记录依据，不为凑检查项空升版。
- 每次任务先检查工作区根的 `Microi.Server/Microi.net/`。只有目录存在且 `rg --files Microi.Server/Microi.net` 能找到至少一个真实源码文件时，才确认当前是吾码创建人在官方完整源码工作区开发；空目录不算，且不要求本次修改位于该目录。确认后，对任意目录中的平台基础能力执行修改、构建或交付时，官方应用数据包都不是“以后再补”的附加产物，而是本次实现的组成部分。目录缺失或为空时按普通用户工作区处理；纯审查、解释或诊断仍保持只读。
- 触发资源包括系统设置、表、字段、Tab、菜单、权限、接口引擎、事件、数据源、页面、打印、工作流、任务、平台内置微服务和可幂等种子数据。开始修改时就确定资源归属：租户开通、启动投影与基础空库归 `app.microi.saas-engine`；用户偏好、个人资料及其租户 Hook 归 `app.microi.sys_user`；租户系统设置编排及其 Hook 归 `app.microi.sys-config`；表单引擎归 `app.microi.form-engine`；模块引擎归 `app.microi.module-engine`；应用商城归 `app.microi.store`。同一能力跨多个包时逐包更新，但同一 Managed ApiEngineKey 仍须单一归属。
- 强制闭环依次包含：①源码和定向测试；②通过绑定 `https://api.itdos.com + OsClient=iTdos` 的 `microi_itdos` 更新并回读官方母版资源；③从母版导出或按受审计发布契约生成本地应用包，单调提升包版本并核对资源数量、版本和 SHA-256；④发布对应官方 Platform 应用；⑤重新读取商城行的小型 HDFS 指针，用公有下载或受权私有下载取得原始 JSON，核对 `Published/IsApprove`、`AppVersion`、`PackageInfo.Version`、UTF-8 字节数、SHA-256 和资源正文；⑥立即做一次同输入幂等重跑，确认无重复升版或漂移。任务还指定目标租户时，再安装/更新并等待后台任务 `Succeeded` 后回读真实资源。
- 本地包文件、生成器成功、单元测试通过、返回 TaskId 或 HTTP 200 都不能代替官方主数据库与商城回读。`.resource-sync-base` 只能在官网发布后逐项哈希一致时由同步器推进，不得与本地候选一起手工修改。若官方身份、MCP 登录或发布门禁失效，必须保留准确的未发布边界并修复链路；不得把本地 JSON 宣称为“其它吾码用户已经可以安装”。
- 应用包不得携带真实地图 Key、Token、连接串或其它租户秘密。浏览器供应商 Key 等配置只交付字段/设置模板和安全读取能力，实际值由每个目标租户在安装后自行填写。

## 包内容

### 空库清理与开发期应用

安装包应显式登记菜单、表、接口、工作流、任务和微服务的完整归属，供导出、升级和空库制作共同使用。开发期只发布运行资产而尚未制包时，不能假定商城选集已经覆盖新建业务表；应通过现有空库规则接口预览，逐项核对实际物理表、DIY元数据、菜单后代与接口Key。缺失时在责任清理器补充有限、明确的业务归属识别，保留平台核心白名单，读取失败停止制作。只生成SQL与在隔离临时库执行清理是不同动作，禁止在源主库执行脱敏SQL。

清理业务应用时同时按StoreId移除其HDFS包索引，再删除商城主记录，保留平台包索引；历史库缺少索引表时兼容跳过。数据库清理不等于删除HDFS对象，禁止借制作空库清除正式已发布文件。

旧布局字段的退役使用可选 `DiyFieldRetirements` 数组，声明 `TableName / Name / ExpectedComponent`，按需用 `ExpectedConfig` 限定组件路径。安装器只允许退役当前包声明表中的 `DevComponent / Divider / Tabs / CollapseGroup`；核心字段、仍在包内声明的字段或控件配置不匹配必须拒绝。先安装支持此协议的商城，再安装声明退役的业务包。退役只软删字段元数据并回读、刷新缓存，不删除物理列或业务数据；已经退役或尚未安装的字段重复执行不产生副作用。

- Manifest：应用 Key、版本、兼容平台版本、依赖和资源清单。
- 数据模型：表、字段、菜单、角色权限、接口引擎、事件、数据源、页面、打印、工作流、任务。
- 源码与构建：私有源码包与可部署构建包分离，记录 SHA-256。
- 安装/升级脚本：幂等、可恢复、可回读；不包含租户密钥、Token、连接串或 License keys。
- 迁移采用“先扩展、后迁移、再收缩”，支持新旧节点短暂并存。

## 接口引擎资源所有权（强制）

- 新发布包必须声明 `ResourcePolicies.ApiEngines`。不可随租户修改的核心使用 `{ Ownership:'Platform'|'Application', UpgradePolicy:'Managed' }`；提供给租户改业务的 Hook 使用 `{ Ownership:'Tenant', UpgradePolicy:'CreateIfMissing' }`。策略以本次选定且通过身份、版本、大小和 SHA-256 校验的包正文为准。
- 发布器仍从上一版安装包计算 `BaseHash`，导入成功后把本版摘要写入 `sys_microistoreversion.InstallResult.ResourceState.ApiEngines`，但 `Base/Local/Incoming` 只用于审计。所有官方、社区和普通应用的 `Managed` 接口均按 Incoming 覆盖；本地源码较新、同版本不同源码、历史所有权不同或安装记录缺失都不得形成安装冲突。
- 当前包声明为 `CreateIfMissing` 时，只在目标 Key 完全不存在时创建；存在时不得对齐 Id、源码、启用状态或其它字段，软删除也算存在。若未来包明确把同一 Key 声明为 `Managed`，则以当前包策略执行覆盖，不再因历史所有权阻断；发布方应在更新日志中明确这一行为变化。
- 官方功能仍优先采用“Managed 核心 + CreateIfMissing Hook”。核心提供稳定协议和默认行为并随包覆盖；客户日志、写表、通知和业务动作放 Hook，并以稳定 `EventId`、唯一约束或 outbox 幂等。这样覆盖式升级核心时无需自动合并客户可执行代码。
- 每个官方包内的接口引擎源码顶部都必须有醒目所有权提示。Managed 提示必须写明所属官方应用、从可信官方源安装/更新/重新安装会恢复官方代码，并指向该应用的 CreateIfMissing Hook；CreateIfMissing 提示必须写明首次创建后归租户维护、官方升级不得覆盖。官方 SSO、登录、通知等核心在安全阶段调用 Hook 时，只传脱敏上下文，禁止传 Token、Secret、密码或原始协议断言。
- 兼容旧 Controller/移动端地址时，在唯一 Managed 接口的 `ApiRoutes` 中用英文分号声明全部旧路径。主地址被其它接口占用时，安装器清除旧占用者的该地址并让当前包收回；稳定 Id 被其它 Key 占用时为当前包资源生成新 Id。每次都要清理旧缓存别名并强回读 `ApiAddress + ApiRoutes + ApiV8Code + Version`。
- 历史包未声明策略时按旧 `LegacyOverwrite` 覆盖兼容；重新发布时发布器必须生成策略。验收至少覆盖首次安装、官方/社区/普通 Managed 本地差异覆盖、同版本重装、软删除恢复、Id/地址自动重映射、Hook 被改后保持原样、两节点竞态，以及官方发布数据库连 `ValidateOnly` 也禁止执行安装器。

## 安装流程

1. 校验签名/哈希、包版本、平台兼容性、依赖和磁盘/配额。
2. 创建全局唯一 `InstallationId` 和稳定幂等键。
3. 使用后台任务执行，阶段性持久化进度与 checkpoint。
4. 按 Manifest 创建缺失资源；已有 `Managed` 资源覆盖为包声明值，`CreateIfMissing`/`InsertIfMissing` 既有值保持原样。
5. `PostSchema` 完成后，在独立 `ScheduleJobs` checkpoint 中幂等安装定时任务并回读 Quartz 运行元数据。
6. 写入成功后刷新共享缓存版本。
7. 回读表、字段、引擎、菜单、权限、页面、定时任务等关键资源。
8. 做 HTTP、UI 和权限冒烟；全部成功后才标记安装版本。

安装中断后从 checkpoint 幂等恢复；不能依赖当前 API 节点内存。

后台任务中心的 `POST /api/BackgroundTask/List` 只返回有界分页的状态、进度、时间和 `HasLog/HasResult` 摘要；日志、结果、参数、可信用户快照与 checkpoint 必须按任务所有权在详情接口按需读取，不能为列表轮询重复返回大字段。

## 权限与租户

- 商城定义、安装、升级、卸载和应用源码只允许 `Level >= 9999`。
- “吾码官方平台”不能只按 `OsClient`、域名或前端变量判断：服务端必须同时校验官方 `OsClient`，并确认当前节点固定只读挂载 `/app/microi_private.pem` 的公钥部分与内嵌官方 License 信任根匹配；本地源码开发可从私有子仓库兼容查找。任意自建私钥不能建立官方身份。官方平台前端隐藏安装、更新、重新安装、离线安装和批量安装入口，后端仍必须拒绝这些写操作。
- 官方平台是应用发布源，连安装器的 `ValidateOnly` 也必须拒绝，避免通过“只验证”绕过发布源隔离。发布源只做包正文、版本和资产 Hash 回读；真实安装、更新及预检必须切换到非官方目标租户或本地非发布源环境。
- 所有资源按目标 `OsClient` 写入；包内不能携带源租户 `OsClient`、数据库、Redis、对象存储、MQ/MQTT、AI 或第三方密钥。
- 按钮调用后台安装接口时，前端只传应用/版本/安装 Id；目标租户和管理员身份由 Token 确定。
- 每次安装、更新、重新安装生成稳定 `OperationId`。官方计数服务用共享数据库事件表唯一约束去重，并在同一事务内登记事件和递增 `InstallCount`；重试、跨节点和响应丢失不得重复计数。
- 安装次数回传属于非阻塞幂等遥测，不是应用导入事务的成功条件。来源节点返回旧格式 `True`、空响应、非 JSON、业务失败或请求异常时，只能写入带 `OperationId`/`InstallationKey` 的 warning 诊断，不得用 `_error_` 标记或回滚已经成功导入的应用；重试仍须复用同一幂等键。
- “全部安装/更新”固定只处理 `ApplicationType=Platform` 的官方平台应用中未安装与存在新版本的项目，不得把 UniApp、Web、MicroService 或其它社区/AI 应用整库安装；已是最新版的应用不重新安装。批量计划、子项状态、checkpoint 和进度必须持久化到共享数据库/后台任务，支持多节点抢占、失败重试和重启恢复，不能依赖进程内集合或浏览器状态。
- 主租户批量维护全部子租户时，前端入口和接口引擎都必须校验主租户上下文及 `Level >= 9999`，再由可信控制面为每个启用子租户创建独立持久后台任务；父任务必须按子任务真实百分比聚合进度，等全部子任务终态后才成功或失败，并在通知中心保留每个租户、阶段和原始失败原因。所有子任务可立即创建，但固定商城工作器必须通过配置租户 Redis 的集群并发租约跨租户串行执行，避免共享物理库并发 DDL/元数据写入死锁；分片幂等任务使用足以覆盖短时死锁和滚动重启的有界重试预算，禁止无限重试。`MaxAttempts` 表示连续失败预算：任一分片成功写入 checkpoint 后必须把 `AttemptCount` 与陈旧 `LastError` 清零，不能让数百个成功分片之间偶发的网络错误按任务生命周期累计并误终结。分片重新入队后必须按 `COALESCE(NextRunTime, CreateTime)` 选取最早就绪任务，禁止只按 `CreateTime` 让最早长任务重复抢占全部分片。商城包属于只读权威数据，可对空响应做有界退避重试，并优先用完整响应的 `Content`、`RawBytes`、HTTP 状态和传输错误诊断；不得把该规则扩展到安装写入请求。目标租户固定商城工作器必须锁定并校验精确包快照；同版本重装、显式选择历史版本或目标端存在更新源码时，仍以所选包的 `Managed` 资源覆盖，不得用版本/源码差异制造冲突。历史空库缺少生成实体所需物理列时，导入器须在首次 FormEngine 调用前幂等补齐并回读，不能再把真实表结构错误包装成 `Value cannot be null (source)`。
- 批量维护的权威目录按当前节点 `OsClientType + OsClientNetwork` 隔离；同一主租户同时存在 Internal、Internet 或其它活动分区时，必须在每个分区对应的主租户节点独立发起并验收，不能用一个分区的成功任务推断全平台成功。每个分区先回读精确预期租户数与名单，等待父子任务全部终态且 `SucceededCount=ExpectedCount、FailedCount=0`，再用新幂等键立即执行一次无操作复跑并确认零安装／零更新。禁止修改 `sys_osclients` 的网络字段来把租户临时塞进另一分区，也禁止让两个分区同时维护可能指向同一物理库的重复租户记录。
- 必须随所有后端版本自动落地的平台基础能力，仍要封装成受信任的官方 Platform 应用包，再由升级器调用统一 `import-microi-store-package` 幂等导入；禁止把表、字段、页面或微服务复制成定制 C# 迁移。`app.microi.saas-engine.json` 可携带基础空库所需的 `mci_system_setting`、`mci_user_external_identity`、默认设置和平台内置微服务；系统账号的 `platform-user-update-preferences`、`platform-user-update-profile`、`platform-user-custom-hook` 只由 `app.microi.sys_user.json` 交付，租户设置的 `platform-tenant-system-settings`、`platform-system-settings-custom-hook` 只由 `app.microi.sys-config.json` 交付。SaaS/Store 不得保留这些 Key、策略或 RequiredPlatformCapabilities。默认行必须使用 `InsertIfMissing + ConfigKey`，只补缺失，不覆盖 `ValueSource=Tenant` 的租户值或租户后来明确关闭的功能。小型平台启动微服务应以 `Source=NotIncluded + Build=DatabaseOnly + StorageMode=db` 随程序集交付并接受 256 文件/5MB、逐文件哈希和无源码门禁，使新租户在 HDFS 故障时仍能打开商城与恢复入口；普通应用安装、源码编辑和文件能力继续失败关闭，不得伪装成全平台健康。
- 批量任务已经以“一个应用”为外层持久化恢复单元。规模可控的小型官方包应在一个事务中完成，避免对同一包体按 8 个字段反复下载、解析和重新排队；超过字段、表、DDL、流程、随包数据或资产安全阈值的大包继续使用内部 checkpoint 分片。热更新发现旧版批量计划不含 `ApplicationType` 时，必须丢弃旧计划并重新盘点，不能继续安装历史计划中的社区应用。
- MySQL 宽表触发 65,535 字节行内上限时，只允许把不参与索引的 `varchar` 配置列无损提升为 `mediumtext`，并把类型覆盖持久化到后台任务 checkpoint；索引列和非行宽错误必须失败关闭。发布包对长连接串、密钥、回调地址、域名/白名单等字段应直接使用 `mediumtext`，同时更新 `DiyFields` 与建表 DDL，不能长期依赖安装时猜测。
- 卸载是破坏性操作，必须明确列出将删除/保留的资源、二次确认并优先软删除/归档业务数据。

## 联邦商城源、公开范围与历史版本（强制）

- 每个主租户和子租户都可以发布自己的应用；应用行用 `IsPublic` 表达公开范围，缺省/历史空值按公开兼容。公开应用允许未登录来源读取和安装，私有应用只允许来源登录成功后的授权身份读取，列表、详情、版本接口都必须重复执行这一权限判断。
- 新版安装包正文不得长期内联在 `sys_microistore.AppPakcet`，也不得随 `mic_data_version.Data` 重复复制。公开应用写入 HDFS 公有桶并记录 `HdfsPublic`，私有应用写入私有桶并记录 `HdfsPrivate`；数据库只保存 `PackageId/PackageHdfsPath/PackageSha256/PackageSize/PackageContentType/PackageFormatVersion/PackageUploadedAt`。`IsPublic` 历史空值按公开兼容，但所有新建应用必须显式落为 `1/0`。
- 发布顺序必须是“UTF-8 JSON 上传 → HDFS 回读 → 字节数和 SHA-256 一致 → 写入不可变包索引及商城指针 → 以原值 CAS 清空 `AppPakcet`”。普通发布不得仅凭 Redis 命中跳过 HDFS 回读；只有受控历史压缩可复用内容寻址缓存，安装端仍必须独立下载并校验。公开包可通过 FileServer/CDN 公有地址读取；私有包只能由来源后端在当前授权身份下签发短期下载地址，Token、签名 URL 和包正文不得进入浏览器配置、日志或后台任务参数。
- `mic_data_version` 历史快照保留不可变 `StoreVersionId` 和同一组包指针，因此旧版本仍可精确安装，不再要求 `Data` 内含完整 JSON。导入器先校验快照版本/应用身份，再按指针下载并在解析前核对大小与哈希；指针缺失时才回退旧版内联字段，禁止快照不匹配时退回当前版本。
- 旧库容量治理使用 `compact-microi-store-packages` 超级管理员持久后台任务：先幂等补齐物理列，再用有界 `Id` 游标逐批处理；每个包都先上传回读，随后 CAS 清理当前行或历史快照。禁止对数 GB `mic_data_version.Data` 执行全表 `LIKE`/包正文计数。逻辑大字段清空后，MySQL 表空间文件是否立即缩小取决于存储引擎；`OPTIMIZE TABLE` 只能在备份完成的维护窗口由管理员另行执行，不能由应用安装或压缩任务自动触发。
- 商城来源以 `ApiBase + OsClient` 唯一定位。添加来源先只读发现系统标题、验证码策略和公开应用数；需要私有应用时再登录。帐号、密码、Token 不能写浏览器配置或商城来源 JSON；密码只用于本次登录，长会话 Token 以 `MCP/Mobile` 非 PC 客户端签发，并由当前租户后端加密保存到 `mci_system_setting`，浏览器只持有不具备取密能力的凭据 Key。
- 后端来源代理必须固定已保存的 `ApiBase + OsClient`，拒绝过期 Token、访问密钥会话、非超级管理员、非 HTTPS 外网地址、重定向漂移和超限响应；Token 不得返回浏览器、日志、审计或应用包。退出登录同步删除服务端密文。
- 商城源业务由 `app.microi.store` 单一拥有的 `platform-marketplace-source`（`Managed`）编排，租户扩展只写 `platform-marketplace-source-hook`（`CreateIfMissing`，默认可执行正文精确为 `return { Code : 1 };`）。登录必须在远端配置读取、密码发送和凭据保存之前执行 `BeforeMarketplaceSourceLogin`；断开必须在删除服务端凭据之前执行 `BeforeMarketplaceSourceDisconnect`。Hook 失败直接阻断操作。Before Hook 仅允许 `Stage / SourceApiEngineKey / Action / SourceId`，不得传 `ApiBase`、远端 `OsClient`、账号、密码、Token、签名地址或凭据密文；协议、加密和密钥隔离继续由可信网关负责。
- 商城主页面统一承载应用市场、已安装、我发布的应用、安装离线包和来源管理，不能再通过独立菜单或路由割裂上下文。来源增删改启停逐项自动保存；来源管理、详情和复杂配置使用平台统一 `80%` 可拖动大圆角 Dialog，遮罩服从正向开关 `sys_config.FormMaskBlur`，缺失或 `0/false` 默认关闭毛玻璃。
- 每张应用卡必须显示预览图、公开范围、分类、最新版本、当前租户已安装版本及状态色。来源卡必须显示其公开数和当前授权可访问总数；平台官方发布节点只显示“平台官方应用源”身份标记，不显示安装、更新或重新安装操作。
- 安装可明确选择 `sys_microistore` 当前版本或 `mic_data_version` 中仍含完整包正文或已验证包指针的历史快照；后台任务在首次取包时必须把计划中的 `AppVersion` 解析为匹配且可安装的不可变 `StoreVersionId`，写入 checkpoint，并在全部后续分片一直传到详情取包。发布方中途升版时继续完成已锁定快照，新版留给下一轮盘点；快照缺失、版本不匹配、身份变化或快照 Id 漂移必须失败关闭，禁止退回易变当前行。实际安装版本写回 `sys_microistoreversion`。回退旧版属于重新安装，不得静默换成最新版。
- 应用详情的版本选择必须服务端分页和搜索，默认每页不超过 20 条；当前版本固定置顶，历史版本只返回含完整旧包或有效 HDFS 指针的可安装状态。禁止用 `_PageSize:500` 或一次加载全部版本后在浏览器过滤。翻页、搜索与页大小切换都必须保持已选版本语义并显示总数。
- 商城详情、来源管理等 Teleport 弹层必须使用宿主级固定遮罩，毛玻璃覆盖完整可视区域；标题栏拖动按弹层真实尺寸限制四边，窗口缩放后重新限制，不能依赖固定像素最大位移或允许内容越出视口。
- “我发布的应用”只读取当前登录用户拥有的记录，可以包含草稿和构建失败项；普通商城列表只读取已发布项。两者必须在服务端按 Owner 和发布状态过滤，不能依赖浏览器过滤后再分页。

## 发布版本与资源选择

### 每个应用版本必须有更新日志（强制）

- 本规则覆盖 `Platform / Regular / Web / UniApp / MicroService` 以及所有 AI 应用。任何创建、修改、升级、重新构建或重新发布，只要产生新的应用版本，就必须先在 `sys_microistore_changelog` 写入一条更新日志；不允许先发布后补写，也不允许因为只改了元数据、V8、路由、提示文案或内置运行产物而省略。
- 更新日志通过 `StoreId` 关联 `sys_microistore.Id`，`Version` 必须是规范化后的精确 `AppVersion`，且同一应用同一版本只能有一条。`Title`、`ChangeType`、`Content`、`ReleaseTime` 都是必填项；`Content` 要写清用户可感知的新增、优化、修复、兼容或安全影响，禁止只写“优化体验”“修复问题”等无法审计的空泛文字。
- 正确顺序固定为：确定目标版本 → 保存商城应用 → 写入或审阅该精确版本日志 → 同步源码与构建 → 制包/发布 → 回读商城行、安装包和日志。发布器发现日志缺失、软删除、版本不一致或任一必填项为空时必须失败关闭。
- `microi_publish_application_directory_stream`、V3 发布或其它支持变更摘要的工具必须显式传 `changeSummary`，且其含义与更新日志一致；`mci_ai_app_version.ChangeSummary`、`AppPakcet.PackageInfo.ChangeLog` 和商城子表不得互相矛盾。
- 应用商城包升级必须同时交付子表、真实 `StoreId` 外键字段、唯一约束/索引、隐藏子菜单、主表 `TableChild` 字段、详情时间线和发布门禁。历史商城源没有该能力时前端应明确显示“当前来源暂未提供更新日志”，不得伪造空日志为“暂无更新”。
- 验收至少回读“商城当前版本 + 对应日志 + 包内版本/ChangeLog”，再在真实商城详情弹层检查版本、类型、标题、时间和正文；重复执行相同版本写入应命中唯一记录而不产生重复行。

- 发布器必须把用户请求的精确版本传给资产准备器，并断言 `RequestedVersion == Prepared.PackageVersion == AppPakcet.PackageInfo.Version`；版本不存在就失败，禁止静默改用最新版本。
- `SelectMenu`、`SelectTable`、`SelectApiEngine` 必须从当前发布包的显式选择持久化；不能读取商城行上一次保存的选择来决定本次包内容。
- 发布完成后同时回读商城版本行、包正文和构建资产，逐项核对版本、数量与 SHA-256；任一处漂移即发布失败。
- 官方内置前端应用只能有一个发布源码根。默认租户工作副本与受审计的独立 Git 源码仓库不能同时成为事实源；若使用独立仓库，必须提交一个发布契约并让构建、跨工程测试、所有程序集内置包共同读取它，正式发布还要证明源码根来自无未提交修改的 Git 提交。前端主包只提供通用宿主、诊断和恢复入口，不复制完整业务源码。HDFS/CDN 运行镜像必须回读到同一 `RuntimeManifestHash` 后才可启用，不能用较新的镜像静默覆盖较旧数据库包，也不能用较新的数据库包静默覆盖未验签镜像。
- `ScheduleJobs` 是一等包资源，必须在 `PostSchema` 之后独立安装、持久化 checkpoint 并回读运行状态；任务失败时不得写入成功版本。

## MCP 工作流

1. 安装/更新现成商城应用时，先调用 `microi_install_store_application` / `microi_update_store_application` 且不传 `confirmExecution`，核对目标租户、商城源、StoreId 和幂等请求 Id 的预检结果。恢复被商城升版打断的旧任务时，额外传入已回读且仍含完整包正文的 `storeVersionId`；不同快照必须使用新的 `requestId`。
2. 用户明确确认后，把 `confirmExecution` 精确设为 `StoreId`，提交真实持久化后台任务；只传 StoreId、可选不可变 StoreVersionId、版本和商城源定位信息，不通过 MCP/HTTP 传完整 `AppPakcet`。
3. 自建应用或低代码系统先用 `microi_list_applications` / `microi_get_application_context` 盘点源码，再用 `microi_get_manifest_schema`、`microi_plan_system` 与 `microi_generate_system(dryRun:true)` 干跑。
4. 安装任务必须回读至 `Succeeded`，再执行 `microi_validate_system`、远端资源回读和真实 UI 验收；仅返回 TaskId 不代表安装成功。

已有 MicroService 优先新增页面/路由；没有时才创建、同步源码、发布构建。复杂安装交互使用 `V8.OpenAppDialog`，后台任务上报进度。

## VS Code 本地应用与发布边界

- `AI应用` 本地树按每个一级目录的 `.microi-micro-app.json` 发现项目；只要 `osClient/apiBaseUrl` 与当前连接一致，就必须显示 `Web / UniApp / MicroService`，不得用 `runtime === "micro-app"` 过滤掉其它应用类型。无效清单应记录诊断，不能静默吞掉整个目录。
- “安装到当前租户”和“发布到应用商城（不安装）”是两个独立动作。商城发布只能同步 `sys_microistore`、应用源码/构建/版本元数据及安装包，不得新增或修改当前租户的 `sys_microiservice / sys_microiservice_page`；操作前后必须回读运行态确认未变化。
- 应用项目行必须直接显示商城发布入口，不能只藏在右键菜单；同时保留构建安装和源码同步状态入口。

## 版本与回滚

- 版本号单调递增，保存变更清单和前后哈希。
- 公有发布必须同时保留两套入口：`/{OsClient}/ai-app-publish/{AppKey}/index.html` 永远指向最新版，`.../versions/{Version}/index.html` 永远保留该历史版本。先完整上传并逐字节验签不可变版本目录，再切换稳定入口。带 `data-microi-immutable-runtime` 的新入口通过版本目录解析全部资源，可先切换入口；旧入口仍在其它稳定资产之后切换。不能机械固定“入口永远最后”而破坏两种契约。
- stage 前回读并冻结应用的 `CurrentVersion + AppVersion`；finalize 必须同时传 `ExpectedCurrentVersion + ExpectedAppVersion`，在以不可变 AppId 加锁后再次 compare-and-set。缺失前置条件、AppId/AppKey 漂移或旧请求晚到一律失败关闭；重新发布必须重新盘点，不能静默回退。
- 新清单发布成功时，同应用 `dist/` 下不再出现的 active 文件元数据只能条件式改为可逆归档 scope，条件必须包含 AppId、路径、旧 scope 与版本；禁止删除 HDFS/数据库记录，也不得触碰 Private、非 `dist/` 或另一应用的行。
- 官网、二维码和用户分享只使用无版本号根入口，不追加 `v/apiBase/OsClient`。目标租户的 `ApiBase/OsClient` 在发布或安装时写入入口 HTML 的 `window.__MICROI_APP_CONTEXT__ / MICROI_API_BASE / MICROI_OS_CLIENT`；安装包不得沿用发布端运行上下文。
- 数据迁移通常只向前；回滚应用版本不能假设自动回滚业务数据。
- 更新失败保留原版本可运行资源，记录失败阶段；不要清空客户 V8 后再尝试恢复。
- 私有源码仓库、`Microi.net/License/keys` 和部署密钥不进入公开应用包。

## 验收清单

- [ ] 同一包重复安装无重复表/字段/菜单/任务
- [ ] 客户自定义 V8 与非包拥有配置保持不变
- [ ] 源码包、构建包、Manifest 和数据库版本一致且哈希可核
- [ ] 无版本号根入口与当前商城版本一致，历史版本 URL 仍可独立访问
- [ ] 安装到不同 ApiBase/OsClient 后，入口 HTML 使用目标租户上下文且分享 URL 无运行参数
- [ ] Web/UniApp 安装后的 `PreviewUrl` 使用目标租户 `FileServer + 真实对象 Key`，不含 OSS/S3 内网域名或临时签名；即使发布包漏选菜单，也会幂等生成可直接在线使用的 Iframe 入口，已有入口则重绑到目标租户地址
- [ ] 只更新菜单/说明/声明式资源且复用旧运行时的版本，`sys_microistore.AppVersion` 跟随安装包版本，运行版本表保留真实构建版本；商城安装状态不能因两种版本合同合法分离而显示异常
- [ ] 中断/重启后可恢复，两个节点不会重复副作用
- [ ] 普通角色不能安装、升级、卸载或读取私有源码
- [ ] 缓存刷新后远端 API 与真实 UI 通过
- [ ] 卸载范围明确、可审计、可恢复或已提示不可恢复

## 复盘：商城按钮已到达但依赖接口引擎缺失

- 触发场景：子租户更新应用商城后能够看到“全部安装/更新”，点击却提示 `sys_apiengine` 中不存在按钮调用的接口；继续临时补引擎后，还可能因目标租户缺少批量计划表再次失败。
- 根因：商城元数据版本、`AppPakcet.PackageInfo.Version` 与真实包正文发生分叉，页面按钮被单独更新；同时应用导入器把接口引擎新增/更新失败只写进 Debug 后继续返回成功，没有做写后回读，批量引擎又依赖未随同一应用包交付的表。
- 通用规则：按钮及其调用的接口引擎、表和权限必须属于同一个单调版本应用包；任何依赖写入失败或回读内容不一致都要让安装事务失败。可复用的批量状态优先写入平台后台任务 `CheckpointJson`，避免仅为批量编排增加未交付的租户表。商城行版本、包内版本和包内容哈希必须同时回读一致，禁止单独提升商城行版本或只更新菜单。
- 自动化检查：应用包契约测试必须从按钮代码提取接口 Key，断言包内存在启用且配置正确的引擎并与独立源码逐字一致；导入器测试覆盖新增失败、更新失败、缓存清理后回读缺失和源码不一致均返回 `Code=0`；真实子租户更新后回读 `sys_menu + sys_apiengine + 后台任务`，再实际执行一次“无需更新”和至少一个安装/更新计划。

## 复盘：新版前端先启动、基础包却遗漏登录菜单依赖

- 触发场景：租户先升级新版前后端，登录后路由初始化固定调用 `/apiengine/platform-sys-menu`；目标库此前没有该接口。应用商城包只补了 `platform-background-task`，而菜单接口仍由安装顺序靠后的 SaaS 包顺带携带；启动完整性检查也只验证后台任务接口，于是数据库版本和商城版本都显示最新，但用户在进入应用商城之前已经因 `NoExistData sys_apiengine` 全站不可用。
- 根因：把单个已修复依赖误当成完整的启动依赖集合，包资源闭包、`NeedRefresh` 完成判定和安装后强回读没有使用同一清单；发布验收只覆盖已有目标租户，未覆盖“新版二进制 + 历史库恰好缺少某一启动接口”的升级排列。
- 通用规则：凡是登录、菜单构建、恢复入口或应用商城打开之前必调的表、接口引擎和微服务路由，都属于启动依赖闭包。每项资源必须只有一个官方 Platform/Application 包作为唯一所有者。API 进程接收流量前的门禁不得维护“登录页七接口”之类手写清单；必须从九个随服务端自动安装的内置官方基础应用包读取全部 `SysApiEngines`。同 Key `Managed` 记录原位覆盖包内源码、版本、主路由、多路由和开关，软删除必须恢复；包内 Id 被其它 Key 占用时生成新 Id，包内主地址被其它接口占用时收回地址。`Tenant/CreateIfMissing` 仅在数据库中完全不存在时创建，既有大小写变体、禁用记录或墓碑都归租户维护，不得恢复或覆盖。
- 竞态与验收：前端可对明确的“启动 Managed 资源尚未落库”做不超过一分钟的有界退避，并在耗尽后显示精确包名和最低版本；它不能吞掉鉴权错误、普通网络错误，也不能代替服务端修复。跨大量历史租户的 `StartupDependencyBootstrapOnly` 是独立事故工具，可从应用商城与 SaaS 两个不可变包覆盖式恢复七项最小可登录 Managed 接口并做物理强回读；它不是 API 进程的完整运行时就绪定义，成功后仍须执行普通完整应用覆盖更新。契约测试须枚举九个包的全部接口资源，覆盖 Managed 本地漂移/软删除、Id/地址自动重映射、CreateIfMissing 墓碑保护、登录后实际路由、商城批量安装和已健康零操作。

### 2026-08-26：登录成功后仍成片 `sys_apiengine NoExistData` 与 Upgrade25 历史文件阻断

- 触发：新版前后端部署到历史租户后，登录页依赖已被补齐，但 `platform-service-health`、`platform-sys-dept`、`mci-module-presentation-stats`、`platform-runtime-custom-hook`、商城批量工作器等登录后接口继续缺失；与此同时 `mci_ai_app_file.VersionId` 的历史空值使 Upgrade25 在创建唯一索引前失败，后续后台升级链无法收敛。
- 根因：把“能登录”误当成“平台运行时已就绪”，启动门禁和事故工作器长期依赖固定七项清单；历史应用文件在 V3 引入版本外键前已经存在，升级只做审计并直接失败，没有安全、确定且可重放的归档策略。旧商城工作器还可能访问已经改名的 `/apiengine/get-microi-store` 地址。
- 通用修复：API 进程启动门禁以上述九包全部接口为事实源，Managed 资源无论缺失、漂移、软删除或同版本都从程序集内置官方包覆盖恢复，CreateIfMissing 只补完全不存在的记录；应用商城保留独立 Managed 旧地址接口并转发到 `get-microi-store` Key 的正式 `/apiengine/get-microi-store-list` 实现。Upgrade25 必须先按 `OsClient + AppId + legacy-unversioned-v3` 生成确定性历史版本 Id，把每个应用的空 `VersionId` 文件原样归档并强回读为零，再计算路径 Hash 和创建唯一索引；文件同时缺少 `AppId`、确定性 Id 被占用或同名版本身份不一致时失败关闭，不猜测、不合并、不删除。
- 验收：应用包测试必须证明九包接口 Key/稳定 Id 全局唯一、每项政策与醒目提示完整、旧商城地址和正式地址均存在；升级测试覆盖 MySQL/SQL Server/Oracle 的历史分组、参数化更新和样本诊断 SQL。真实启动日志必须分别显示物理字段、完整平台运行时闭包、Upgrade25 历史归档计数和每个升级步骤终态；登录后逐一调用当前用户、健康、部门、私有文件、菜单角标与商城批量计划，不能只验证匿名系统设置。

## 复盘：应用包切换 HDFS 后旧导入器无法更新自己

- 触发场景：商城行与不可变版本快照已经只保存 HDFS 路径、大小和 SHA-256；历史租户仍运行只读取 `AppPakcet` 的导入器。用户尝试先更新“应用商城”以获得新版导入器时，旧导入器从商城源取得空 `AppPakcet`，在 3% 直接报“Package不能为空”，形成更新器无法更新自己的引导死锁。
- 通用协议：新版导入器请求商城模型时必须显式声明 `PackagePointerMode=HdfsV1`，自行下载一次并校验 UTF-8 字节数与 SHA-256。商城模型面对未声明该能力的旧调用端，允许从同一个受信 HDFS 指针读取并严格校验正文，只在本次响应的 `AppPakcet` 中临时回填；绝对禁止写回 `sys_microistore`、`mic_data_version`、任务参数或检查点。私有包仍必须使用短期授权地址，大小上限、HTTP 状态和摘要任一异常都失败关闭。
- 发布顺序与验收：先把兼容桥更新到官方商城源的 `get-microi-store-model`，再发布包含新版模型接口、导入器和启动依赖的精确应用商城版本。自动化同时覆盖新版请求零回填、旧请求可安装、摘要/大小不符拒绝、数据库无包正文字段更新；真实历史租户必须保留旧导入器启动第一次正式更新，等待终态成功并回读新版导入器，然后立即同版本覆盖式复跑，确认资源仍精确等于包正文且不产生重复记录。直接通过 MCP 替换目标导入器只能作为已故障租户的最后恢复手段，不能代替协议兼容性验收。

## 复盘：可信后台任务被 StopHttp 提前拦截

- 触发场景：`V8.ApiEngine.RunBackground` 已成功创建任务，但 Worker 执行 `StopHttp=1` 的批量引擎时立即失败并提示“此接口已禁止http调用”。
- 根因：Worker 为保留按钮调用、权限和审计语义继续使用 `_InvokeType=Client`，旧版 ApiEngine 只按该字段判断 `StopHttp`，没有识别服务端恢复的持久化任务来源。
- 通用规则：核心 ApiEngine 仅在“服务端可信用户作用域 + `_TrustedServerInvocation=true` + 非空任务 Id + 匹配任务信封 + 正数 fencing token”全部成立时豁免 `StopHttp`；少一项都按普通 HTTP 拒绝。不得把所有后台调用粗暴改成 `Server`，也不得只凭客户端可伪造的任务 Id 或信封放行。
- 旧节点兼容：应用资源尚需覆盖未部署核心修复的服务，可暂将批量引擎设为 `StopHttp=0`，但 V8 首行安全门必须执行同样的全条件校验；HTTP 控制器必须剥离 `_TrustedServerInvocation`。契约测试同时断言严格 `AND` 校验、独立源码与包内副本一致、普通调用失败关闭。

## 复盘：后台任务基础包的自举与索引幂等

- `app.microi.background-task` 自身负责创建/修复后台任务表，首次安装和离线安装必须以前台接口完成；不能先调用 `RunBackground`，否则旧库缺少 `OsClient` 等字段时会在导入开始前失败。
- 后台任务可用性不能只检查表存在，还要检查运行时必需列；部分升级的旧表必须返回“能力尚未就绪”，不能继续拼接包含缺失列的 SQL。
- 应用包中的独立 `CREATE INDEX` 必须按表名和索引名做执行前回读；并发创建失败后再次回读，已存在则按幂等成功处理。索引 DDL 不能重复触发整张表的字段同步。
- 基础包必须同时携带 `OsClient` 的 `PhysicalColumns` 定义、建表内联索引与 4 条独立索引 DDL；新表靠建表一次成型，旧表靠物理列同步和独立 DDL 修复。安装前先把“应用商城”更新到包含 `BACKGROUND_TASK_BOOTSTRAP_READINESS_V1`、`APPLICATION_ASSET_BACKGROUND_CHUNKS_V1` 的 v1.8.0+ 导入器；安装成功前必须回读全部运行字段及索引，验收覆盖首次安装、部分旧表修复、重复安装和两节点竞态。
- 其它用户更新吾码 VS Code 插件并执行“初始化 AI 配置/拉取 Skills”后，AI 应自动识别本规范：大任务优先提交真实后台任务；若基础能力未就绪，先指导用户更新应用商城并以前台方式安装 `app.microi.background-task`，不得伪造进度或让基础包自举入队。
- 重复安装先比较应用包拥有的字段定义，完全一致就跳过 `UptFormData`。Jint 的 `LimitMemory` 统计累计托管分配而非当前存活堆；大量无效字段更新即使被 GC 回收也会耗尽预算。v1.8.0 导入器每个后台片最多实际上传 8 个文件，并以约 32 MB Base64 为分片目标；为避免单个大文件无限空转，每片至少允许处理一个文件。片末返回 `HasMore + Checkpoint`，下一片按 `AppId + FilePath + Hash` 复用已提交资产，并禁止为统计再次解码整文件 Base64。
- 只有固定 Key `import-microi-store-package`、服务端可信身份、`Level >= 9999`、当前进程主租户、持久化后台 TaskId 五项同时成立时，后端才使用 `ResidentMemoryGuardOnly`，跳过会永久累计已回收分配的 Jint 内存约束。该特例仍受容器优先的进程 RSS 防线保护：95% 停止接收新工作，98% 有界停机并由持久任务恢复。普通/前台/子租户/其它 V8 不得借用此特例。

## 复盘：官网升级资源同步的三道门

- 重型发布门禁之前先运行 `node Microi.Server/Microi.Upgrade/Resource/refresh-resources.mjs --validate-only`：该入口只校验本地全部候选，不联网、不发布、不推进共同基线，且不能与写入参数组合。正式发布仍须通过完整测试、三方同步、精确哈希回读，不能把本地预检当成发布成功。
- 控制面版本使用语义版本最低门槛，并同时严格核对鉴权、行锁、资源哈希、写后回读等能力；禁止把某个旧版本字符串写成唯一合法值。可选功能一旦由包内表或任意入口声明，就必须校验其完整接口闭包及资源策略；不能自动放行未知 Key，也不能只升级版本而缺失实际保护。预检自身必须进入 Node 自动回归，覆盖新版通过、旧版及缺保护失败、半套可选功能失败。
- 官网当前资源只是三方合并的一侧，允许暂时落后于本地发布候选。下载阶段只校验固定白名单、稳定资源身份、JSON 可解析性及服务端返回 SHA-256；不能用“必须已包含本地最新功能标记”的规则提前拒绝旧官网，否则会形成“官网不够新所以永远无法发布新版”的循环依赖。
- 本地输入和三方合并后的最终候选必须继续执行最低版本、功能标记、内嵌逻辑副本一致性等严格校验；发布成功并按内容哈希回读一致后才能推进 `.resource-sync-base`，不能把放宽读取门误做成放宽发布门。
- 应用包版本与平台发布版本分别单调递增。包正文需要写回而本地包版本、平台版本均未高于官网时，应基于官网包版本自动递增补丁号；内容无变化时不得递增，必须用连续两次同步验证第二次为零变更。
- MCP 配置中的官方 API、`OsClient` 与 Token 文件是鉴权事实源，但编辑器可执行文件、插件版本目录和 `cwd` 都是易漂移的启动信息。发布器应保留鉴权配置、固定校验 `https://api.itdos.com + itdos`，并支持插件生成的稳定 `microi-cli-mcp.js` 或标准 `mcp-server.js` 入口；旧扩展配置再从同级最新已安装插件或当前工作区发现可信入口，统一使用正在运行发布脚本的 Node 启动。插件升级、Codex 插件化或旧目录清理不能再次阻塞后端发布。
- Token 文件的新键固定为 `ApiBase|OsClient|OsClientType|OsClientNetwork` 四段身份；Type/Network 为空时仍保留空段。读取和回写必须先用四段精确键，再兼容旧版紧凑键；旧键在多环境配置下可能被有意保留且已经失效，禁止优先读取。自动化测试要覆盖“精确键已刷新、旧键仍过期”以及 type-only/network-only 不碰撞。
- VS Code 的 SecretStorage 恢复 broker 必须使用确定性激活事件启动；不得只依赖大型工作区可能超时的递归 `workspaceContains`。签名密钥变化时旧 Token 本身不能以旧换新，必须由已激活 broker 使用本机 SecretStorage 静默重登并回写精确身份键。
- 本地资源同步的官网读取、发布和发布后回读应使用同一个已校验的 `microi_itdos` MCP 链路；CI 无 MCP 时才允许凭显式 Token 使用 HTTP。V8 独立文件与应用包内嵌副本合并时，先把文件头说明和 `Version` 与可执行正文分离：两端独立升版不能算代码冲突，不同正文安全合成后应基于两端最高版本再升一版；只有同一段可执行逻辑出现不同实现才失败关闭。
- 新增平台共享物理列时，除一次性版本门升级外，还要同步所有会快照该共享表的官方包 `PhysicalColumns`，让新安装和旧租户升级两条路径一致。`.resource-sync-base` 是上次官网回读的共同基线，不能与本地候选一起手工修改；基线可疑时先从官网只读修复，再执行三方合并、自动升包版本、正式发布和 SHA 回读。
- 发布验收至少包含：定向合并测试、真实 `PublishBatch`、六项资源逐项 SHA-256 回读，以及立即执行第二次幂等重跑。任何一步失败都不得宣称官网已同步，也不得推进共同基线。

## 复盘：官网模板库误报平台应用可更新

- 触发场景：官网 `sys_microistore` 的平台应用版本持续发布，但 `sys_microistoreversion` 仍保留旧安装版本；官网自身会显示“可更新”，从官网数据库复制出来的新租户也继承同一误报，尽管这些平台能力实际已经随官网主库维护完成。
- 身份边界：只能在服务端同时满足 `OsClient=iTdos` 且当前节点私钥公钥部分与内嵌官方 License 公钥完全一致时执行。只使用同名租户、域名、配置开关或任意自建私钥都不得触发；客户租户的安装版本仍是其独立事实源。
- 对齐规则：每次后端启动都在现有租户升级分布式租约内盘点 `ApplicationType=Platform + IsApprove=1` 的商城行，只处理 `Installed/Success/Succeeded/已安装` 的既有安装记录，按 `StoreId -> AppId -> 唯一 AppName` 匹配。只条件更新 `AppVersion/AppVersionInstall/PackageVersion`；稳定键仅在目标键未被其它安装记录占用时补齐。不得新增“已安装”记录、覆盖失败/异常状态、修改安装时间或删除重复历史行。
- 分布式与验收：更新是确定值条件写入，节点中途退出后下次启动继续，第二次执行必须零更新；验收同时回读版本表三个字段，并执行官网商城列表接口，确认 `Outdated=0`、`Abnormal=0`。真实未安装应用仍应保留 `Uninstalled`，不能为了清空通知伪造成已安装。

## 复盘：共同基线已有引擎但官网内嵌副本缺失

- 触发场景：本地 `app.microi.store.json`、独立接口脚本和 `.resource-sync-base` 都已有新引擎，但官网商城包尚未收录或被单侧删除；同步器在三方合并前强制读取官网内嵌代码，直接报“缺少接口引擎”，导致新版永远无法通过一键发布补到官网。
- 根因：旧状态机只区分“共同基线不存在的首次新增”和“三端都存在的正常合并”，漏掉“共同基线已登记、官网副本缺失”的历史基线漂移；严格 getter 在合并前抛错，使已有的首次发布与逻辑副本修复规则没有机会运行。
- 通用规则：只要副本映射仍属于当前发布契约，本地独立源码和内嵌副本能从共同基线安全合并，官网单独缺少内嵌引擎应视为不完整副本。JSON 合并前只从共同基线恢复该引擎的结构骨架；有官网独立文件时继续把它作为官网代码侧，无独立文件时把共同基线代码视为官网未修改，再执行正文三方合并并将结果写回内嵌副本。写回后必须按 `SysApiEngines.length` 重算 `PackageInfo.ApiEngineCount`。若共同基线引擎 Id 已被其它 Key 占用，或任意两侧同一代码位置实现冲突，仍必须失败关闭；不得通过覆盖/重建整个共同基线来掩盖问题。
- 自动化检查：同时覆盖共同基线不存在的首次新增、共同基线存在但官网缺失且本地升级、官网内嵌缺失但官网独立源码升级、共同基线 Id 被其它 Key 占用四种状态；断言声明引擎数等于实际数组长度；再用真实 `microi_itdos` 只读资源重放，确认候选含目标引擎且独立源码与内嵌代码相同，正式发布后执行 SHA 回读和零变更幂等重跑。

## 复盘：旧包非空约束阻止租户升级与启动

- 触发场景：商城应用包把既有字段从可空升级为 `NOT NULL DEFAULT ...`，老租户的物理列已经存在，但历史记录仍为 `NULL`；导入器直接执行 `ALTER TABLE ... MODIFY COLUMN ... NOT NULL` 时，MySQL 报 `Invalid use of NULL value`，整个安装分片失败。
- 根因：物理列同步只比较类型、可空性、默认值和注释，没有在收紧可空性前迁移存量数据。列默认值只影响后续写入，不会自动修复既有 `NULL`。
- 通用规则：除所有主键（不限于 `Id`，包括复合主键的每一列）及自增/identity 列外，平台普通物理列一律允许 `NULL`。导入器必须将历史 `PhysicalColumns.IS_NULLABLE=NO` 归一为允许为空，不收紧已有列，不为迁就非空约束而回填零/空串/租户值；业务必填由表单与后端事件校验。默认值独立保留。启动前必需的核心表必须在共享租约内完成可空兼容，再插入运行时接口；不能等商城运行后才修复商城自身的启动前置结构。
- 自动化检查：构造 `V8Limit NOT NULL` 无默认值的旧表、含 `NULL` 的历史记录及带默认值/注释/排序规则/索引的列；同时覆盖普通列已允许 NULL 但被 DROP DEFAULT 的情况，使用 `SELECT DEFAULT(列) FROM 表 LIMIT 0` 识别元数据无法区分的缺失默认标志。断言 Id 仍为主键、旧数据和列属性保留，随后省略可选字段的 INSERT 成功；同版本重启和二次安装不重复 DDL 或回填业务值。

- 历史数据库的可选菜单关联字段可能仍为整数类型。引用该表的应用包只能按实际物理类型把空字符串归一为 `NULL`，不能将无关联猜成 `0`，也不能越过表所有权修改既有类型；非空值及文本列保持原样。验收同时覆盖旧整数列、新文本列、重复安装和无效非空值继续报错。

## 复盘：应用新增菜单未自动授予系统管理员完整权限

- 触发场景：应用首次安装或版本更新新增了 `sys_menu`，菜单数据已经存在，但系统管理员看不到入口或只能执行部分按钮；再次人工到角色管理中勾选后才恢复。
- 根因：安装器只导入菜单，没有在同一事务内写入 `sys_rolelimit`；历史补丁又把单个管理员角色 Id 和五项旧权限硬编码，只能修复某个菜单，遗漏 `Read`、自定义按钮及其它 `Level >= 9999` 管理员角色。
- 通用规则：安装器只对本次真正新增或从删除状态重新引入的菜单自动授权，不扩大既有菜单的客户权限策略。目标角色必须从当前租户实时读取全部有效 `sys_role.Level >= 9999` 记录；基础权限固定为 `Read/Add/Edit/Del/Export/Import`，并合并菜单 `MoreBtns/ExportMoreBtns/BatchSelectMoreBtns/PageBtns/PageTabs/FormBtns` 中每个按钮的 `Id + Name`，不得加入 `NoDetail/NoSearch` 这类限制权限。
- 旧账号等级兼容仅限已校验官方平台包：没有上述角色时，可检查固定旧内置角色 `5db47859-35a3-411a-a1f7-99482e057d24` 的 `998/9998` 级记录。仅当至少一个活动账号的数据库 `Level >= 9999` 且该角色所有未删除持有者均为系统管理员时，才为该角色补充本次新增菜单权限；保持角色等级和用户绑定，禁止推广到普通自定义角色。角色引用同时识别字符串数组和 `[{Id:...}]`，不相信引用对象中的缓存 Level。
- 幂等与失败边界：已有 `sys_rolelimit` 只能合并、不得删除历史自定义权限；新增行使用“租户 + 角色 + 菜单”的确定性 Id，并在并发主键冲突后重新回读合并。找不到系统管理员、查询失败或任一权限写入失败必须让整个安装事务回滚，不能出现“菜单成功但管理员无权”的半成品。通过 FormEngine 写入以触发共享授权缓存失效。
- 自动化检查：覆盖多个系统管理员、已有部分权限、自定义按钮既有 JSON 字符串也有数组、删除菜单恢复、重复安装零新增、两个节点同时插入后合并，以及既有菜单更新不自动扩权；发布门同时校验独立导入器与商城包内嵌副本都包含该能力。

## 复盘：商城发布升版打断所有后台分片安装

- 触发场景：主租户为大量子租户执行“安装/更新全部平台应用”时，某个平台包从 v7.5.17 发布到 v7.5.18；正在执行的任务在下一分片报“检查点版本与当前版本发生变化”，大量子租户同时失败。
- 根因：版本保护正确阻止两个包混装，但批量计划只持久化 `StoreId + AppVersion`，每个分片仍从 `sys_microistore` 易变当前行重新取包，没有使用已经存在的 `mic_data_version` 完整快照，也没有把 `StoreVersionId` 写进 checkpoint。
- 通用规则：后台安装首次取包必须按计划 `AppVersion` 锁定匹配的完整历史快照，并在每个分片复用同一 `StoreVersionId`；应用商城自身必须排在平台批量计划首位，先自举最新版导入器。发布中途升版不能改变既有任务的包体，也不能让既有任务自动追新；下一次批量盘点再安装新版本。
- 自动化检查：构造计划版本 v1.0.0、商城当前版本在分片间升到 v1.0.1，断言首次选择 v1.0.0 快照 Id，恢复分片仍请求同一 Id且成功；覆盖快照尚未生成、显式 Id 与期望版本不符、身份不符均失败关闭，并断言批量计划第一项为 `app.microi.store`。

## 复盘：父任务 Monitor 重复自举误判同版本工作器

- 触发场景：全部子租户安装任务已经创建并持续执行，父任务在 Monitor 分片中再次调用租户发现/工作器自举；子租户刚从应用包写入的商城工作器与主租户版本相同，仅 BOM 或 CRLF/LF 不同，却被判为“同版本源码不同、疑似租户定制”，父任务失败，而子任务仍在后台运行。
- 根因：父编排器在解析 checkpoint 阶段前无条件读取租户目录，导致 Monitor 也重复执行只应发生在 Queue 的有副作用自举；服务端源码比较又直接使用 Ordinal 原文比较，没有先消除跨平台文本格式差异。
- 通用规则：租户发现与商城工作器自举只允许在 Queue 阶段执行；Monitor 必须只汇总 checkpoint 中已经持久化的 `ChildTasks`。同版本源码比较只可规范化 UTF-8 BOM、CRLF/LF 和文件末尾空白；任何其它字符差异仍按租户定制失败关闭，禁止用去注释、压缩空白或模糊标记绕过保护。
- 自动化检查：覆盖同版本 LF/CRLF/BOM 等价且不刷新、同版本真实代码差异继续拒绝、旧版官方谱系允许升级、新版目标保留；接口引擎资源断言 Monitor 检查点标记存在且 `GetChildTenantPlatformAppMaintenanceTargets` 只位于 Queue 分支。

## 复盘：资源数量小但工作量重的应用被合并为单事务

- 触发场景：子租户批量安装进度停在“系统设置”或“系统帐号”十多分钟，心跳和 CPU 仍活跃但没有新检查点；重启后同一现象只会转移到下一个租户。
- 根因：批量工作器请求 `BulkAdaptiveSingleSlice`，导入器仅按表、字段、菜单和数据行数量判断“小包”，没有计入 DDL、实体生成、权限回填和历史数据迁移的真实成本，因而关闭了内部后台分片。
- 通用规则：后台商城安装必须始终保留有界分片和持久化检查点；旧工作器仍传单片参数时，新导入器必须安全忽略。已排队的跨租户任务在真正运行目标 V8 前，应按进程缓存执行一次官方安装器自愈，使平台修复能从原检查点接管。
- 自动化检查：断言批量工作器显式关闭单片参数、导入器不存在 `backgroundChunkingEnabled=false` 路径、旧单片参数只记录忽略诊断；后台执行器在子租户工作器运行前调用受信自举，并继续保留同版本真实源码差异的失败关闭保护。

## 复盘：旧租户前置物理列逐列重建耗尽首片超时

- 触发场景：已启用后台分片后，旧租户首次更新“系统设置”仍长时间没有 `ChildCheckpoint`；心跳与 CPU 正常，接近单片超时后才推进。实时 Schema 回读显示 `sys_apiengine` 与 `diy_table` 同时缺少多项生成实体固定列。
- 根因：导入器为兼容历史空库，在第一次 FormEngine 调用前逐列执行 `ALTER TABLE ADD`。MySQL 每个语句都可能重新准备或重建同一张元数据表，多个固定列的累计耗时发生在首个可持久化检查点之前。
- 通用规则：MySQL 同一张表缺少多个受信固定前置列时，必须先完整盘点，再用单条 `ALTER TABLE ... ADD ..., ADD ...` 原子补齐并回读；可信后台任务每个分片最多修改一张前置元数据表，提交 `Prerequisites` 检查点后再继续下一张。没有缺列的现代租户直接进入原 DDL 阶段，不制造空分片。并发节点已完整补齐时按幂等成功，否则保留真实表名、未补齐列和数据库错误并失败关闭。SQL Server 与 Oracle 使用各自已验证语法，不得直接套用 MySQL 批量语句。
- 自动化检查：独立导入器和商城包内嵌副本都必须携带批量前置列及检查点标记；断言 MySQL 路径先收集 `pendingDefinitions`、每表只拼接一次批量 ALTER、后台每片写表上限为一，且失败后逐项回读所有待补列。真实旧租户验收需证明前置列分片已提交、随后不可变 `StoreVersionId` 与字段阶段检查点继续推进。

## 复盘：父级监控遇到数据库死锁被误判为业务失败

- 触发场景：父级 Monitor 已持久化全部子任务 Id，子任务仍在正常执行且 checkpoint 中没有业务失败；父任务汇总进度时偶发数据库死锁，却因调用方配置 `MaxAttempts=1` 立即进入 `Failed`，通知中心因此把仍可成功收敛的整批任务标红。
- 根因：后台任务把数据库死锁、锁等待超时等基础设施瞬态竞争与接口返回失败共用同一业务重试预算。低业务重试次数无法覆盖工作器自身的领取、心跳、检查点或结果持久化竞争，也没有保留父子任务的真实运行语义。
- 通用规则：后台任务存储层只对已知数据库死锁、锁等待超时、序列化竞争等稳定特征启用独立的有界基础设施重试；即使业务 `MaxAttempts=1`，也允许最多三次基础设施执行机会。重试必须保留原 TaskId、父子关系、checkpoint 和已产生的副作用，不得重新创建子任务；成功后清除瞬态错误，第三次仍竞争才按原业务规则终止。普通业务错误、数据冲突和 `Value cannot be null` 等非竞争错误不得借用该预算。
- 自动化检查：覆盖嵌套 ProviderException 与外层包装异常、MySQL/SQL Server/PostgreSQL/Oracle 的稳定错误码或消息；断言第一次和第二次竞争回到可领取状态且不消耗业务 AttemptCount，第三次进入原终态，非竞争异常仍按 `MaxAttempts` 立即失败。真实验收必须在每个活跃运行分区等待该分区全部子任务收敛，再以 `MaxAttempts=1` 连续执行一轮完整汇总和一轮立即无操作重跑；两轮父任务都必须得到 `SucceededCount=ExpectedCount、FailedCount=0`，且无操作轮为零安装／零更新。

## 复盘：字段分片完成后后台安装卡在缓存失效广播

- 触发场景：字段分片的数据库写入和 Schema 回读已经精确完成，但 `ChildCheckpoint` 长时间停在同一字段索引；任务取消也无法及时结束，进程内存、线程数和 CPU 持续增长。
- 根因证据：两次托管转储的调用链都停在 `V8.ApiEngine.Run -> GetApiEngineModelInternal -> MicroiTwoLevelCache.SetAsync -> PublishInvalidateAsync -> PublishWithRetryAsync`。权威 Redis 写入已经成功，随后用于通知其它节点清理一级缓存的 Pub/Sub 广播没有完成时限，因而把业务调用永久挂住；与此同时，单字段元数据更新即使只变更 Label、Remark、布局或 V8，也会重复执行物理列 DDL，放大旧租户升级成本。
- 通用规则：Redis 权威值写入成功后，跨节点一级缓存失效广播只能是有界、尽力而为的附属动作。队列等待和实际发布都必须有独立短超时，超时后观察迟到异常并开启短暂熔断冷却，防止悬挂发布任务无界堆积；不得因此回滚已经成功的权威写入。`UptDiyField` 只有列名或物理类型变化时才允许调用 `ChangeColumn`，显示名称、说明、布局和事件代码等纯元数据变化禁止触发 DDL。
- 自动化与真实验收：缓存测试必须覆盖发布永久不返回时的有界退出、迟到异常观察和冷却期抑制；字段测试必须断言只有 Name/Type 变化才进入物理 DDL。应用商城资源契约测试、后台任务与 SaaS 回归测试通过后，还必须在每个活跃运行分区完成 `ExpectedCount/ExpectedCount` 子租户收敛，并立即执行一轮相同数量的无操作重跑。验收期间若官方应用发布了新版本，应以新的不可变 `StoreVersionId` 启动独立追赶批次，禁止混入已经开始的旧版本分片。

## 复盘：最新版后端仍放行未锁快照的旧商城工作器

- 触发场景：目标租户已经部署最新版前后端，但数据库中的 `import-microi-store-package v2.2.9` 与 `bulk-import-microi-store-packages v1.2.4` 被启动完整性检查判为合格；平台应用批量任务开始后，官方商城恰好升版，首个应用在下一分片仍从易变当前行取包并失败。
- 根因：后端自举门槛只校验早期版本和历史标记，没有把不可变快照、禁止自适应单片、前置物理列分片检查点及商城自举优先级纳入能力契约；MCP 单应用恢复工具也只能传 `StoreId`，无法显式锁定已经回读的 `mic_data_version` 快照。
- 通用规则：后端启动必须同时校验导入器最低版本与固定快照/有界分片/前置列检查点标记、批量工作器最低版本与 `StoreVersionId + BulkAdaptiveSingleSlice:false`、商城列表的自举优先级；任一缺失都必须从受信升级资源刷新，不能仅看后端二进制版本。MCP 安装/更新允许传受校验的可选 `storeVersionId`，审计同样记录该 Id；不同快照使用不同幂等请求 Id，禁止把旧任务静默改指新包。
- 自动化与真实验收：单元测试同时拒绝旧版本和缺标记的伪新版工作器；在真实旧租户先用固定快照完成应用商城自举，再执行全部平台应用更新并立即做零更新复跑。验收期间出现的新官方版本只进入下一独立批次，历史失败任务保留作审计，不删除或篡改。

## 复盘：本租户自助批量被误判为主租户跨租户任务

- 触发场景：普通租户在自己的应用商城点击“全部安装/更新”，后台执行器仅按固定批量工作器 Key 触发跨租户自愈，把 `owner=当前租户、target=当前租户` 当成主→子任务；旧导入器只能从自己复制自己，随后被新能力门禁拒绝，并错误提示“先更新主租户应用商城”。
- 根因：跨租户自愈没有校验由服务端控制面持久化的目标租户保留标记，也没有要求任务拥有者与执行租户不同；把同租户自助批量和主租户代子租户执行混成了同一作用域。
- 通用规则：运行前强制自愈只允许用于固定批量工作器、非空服务端目标标记、`owner != execution` 且标记精确等于执行租户的主→子任务。普通租户自助批量必须跳过跨租户复制，让持久计划按官方列表顺序优先安装/更新应用商城自身，再由新版导入器继续后续应用；不能要求官方发布源安装自己。
- 自动化与真实验收：单元测试覆盖同租户无标记、同租户伪标记、跨租户正确标记、标记漂移和错误工作器 Key；真实旧租户先保留旧导入器发起自助批量，断言不再出现“子租户商城工作器执行前自愈失败”，计划第一项为应用商城，全部任务成功后立即执行零更新复跑。

## 复盘：平台包跨旧租户安装时同时暴露结构闭包、元数据回读与目录脆弱性

- 触发场景：同一轮主租户批量任务在不同子租户分别报“数据集目标表尚未创建”“接口引擎写入后 HTTP 状态不一致”“既有菜单 Url 唯一冲突”或“未找到 OsClient”；父任务若在目录阶段对全部目标做有副作用自举，还会让一个坏租户阻断其余已可执行租户。
- 包结构闭包：任何 `DataSets.TableName` 都必须由同一包的 `DiyTables + DiyFields + DDLStatements + PhysicalColumns` 完整创建或由显式依赖声明保证先安装，不能只因为官方母库已有该表就省略。契约测试要逐个数据集反向验证目标表、主键、字段和物理 DDL，并在真实缺表旧租户执行首次安装与重复安装。
- 配置子表种子不能硬编码官方父记录 Id。`InsertIfMissing` 数据集可声明 `ParentBinding:{Field:'SysConfigId',TableName:'sys_config',MatchField:'IsEnable',MatchValue:1}`，仅在目标端查询恰好一条父记录 Id，先绑定外键再检查冲突。0 条、多条、查询失败或不安全标识立即失败；不能覆盖父配置值。导出器须保留该声明，旧导入器必须先升级，不能忽略绑定后按官方 Id 写入。
- 大表物理兼容必须覆盖全部 DDL 路径：`BIT` 不能落入 `varchar` 默认映射，整数显示宽度与纯标签/注释差异不能触发改列；同一个包的字段元数据变化交给物理列阶段统一比较，不能先改再改回。普通标量列仅默认值变化时使用 `ALTER COLUMN ... SET DEFAULT`，移除旧值必须设为 `NULL`，禁止 `DROP DEFAULT` 留下省略字段时触发 1364 的标志；强回读并禁止 COPY 回退。TEXT/BLOB 的缺失默认标志只能保留完整定义执行 `MODIFY COLUMN` 修复，不能用不支持的 `ALTER COLUMN SET DEFAULT NULL`。空字符串默认值不等同于 NULL。安装器时间辅助必须自包含，应用文件落库等新增分支也不得直接依赖租户的 DateNow。
- 受管接口状态：旧库 `diy_field` 元数据可能落后于 `sys_apiengine` 物理列，导致 FormEngine 返回成功却忽略 `IsEnable/StopHttp`。仅对受信官方 `Managed` 接口的固定布尔控制列，允许在 FormEngine 写后不一致时按稳定 Id 参数化校准物理列、清理租户缓存并严格二次回读；源码、匿名权限、ApiAddress 和租户扩展不得借此绕过三方冲突或所有权保护，二次回读仍不一致必须整包失败。
- 菜单与目录隔离：更新既有菜单发生 Url 唯一冲突时，优先保留目标租户当前唯一路由；确需新路由时使用有界、可回读的稳定后缀，不得字符串拼 SQL。子租户目录发现必须是无副作用读取；运行时缺失在单目标投递前按 `sys_osclients` 受控热加载并只记录该租户失败，已经排队的子任务继续监控到终态，禁止一个 `未找到OsClient` 提前终止整批。
- 百租户并发边界：父协调任务继续使用集群级防重键；子安装任务必须使用固定商城工作器 `ConcurrencyKey` 在当前运行环境内串行。不能根据 `OsClient` 推断物理库隔离，因为多个子租户可能共享同一数据库；只有未来能由服务端权威连接指纹证明互不共享物理库并建立分组锁时，才允许在不同物理库组间并行。
- 兼容与验收：V8 协调器需要兼容尚未部署新 C# 原子的节点，在目录或单目标投递返回明确的缺失 OsClient 时最多热加载并重试一次，重复同一缺失或超过安全上限立即失败关闭。验收顺序固定为先更新主租户应用商城导入器，再用不可变 `StoreVersionId` 更新 SaaS 协调器，完成全部子租户父子任务收敛，最后立即执行 `Planned=0` 的无操作重跑；同时保留每个失败租户的独立任务和通知中心证据。

## 复盘：旧对象存储节点缺少 MoveObject 导致编译资源无限重传

- 触发场景：平台微服务包只有少量 `BuildAssets`，但后台检查点长期停在 `ApplicationAssets / Build / AssetIndex=1`，`ApplicationAssetUploaded` 却持续超过包内资源总数。目标节点能够上传并回读公有对象，但不支持或拒绝 `MoveObject`；导入器每一片都重新上传同一文件，再因移动失败写回临时路径，下一片继续重复。
- 通用规则：新上传文件移动到租户稳定路径失败时，若上传结果已通过摘要和大小校验，应保留这个真实可读路径并写入显式兼容 scope（当前为 `PrivateSource+PublicBuildMoveFallback`）。后续分片只允许在同时命中 AppId、文件路径、摘要、大小、真实 HDFS 路径和该 scope 时直接复用，禁止再次调用 `MoveObject` 或上传；没有兼容标记的历史坏路径仍只允许一次有界重传修复，不能把任意旧元数据误当成有效对象。
- 自举与验收：主租户投递每个子任务前必须先复制最新版应用商城导入器和批量工作器，已经排队的任务不得假定会动态取得主租户新代码。回归测试要模拟“上传成功、MoveObject 不可用、下一分片恢复”，断言上传次数保持 1 且资源索引继续前进；真实旧租户验收必须观察 4 个编译资源在有界片数内完成，并在全租户成功后立即做 `Planned=0` 复跑。

## 复盘：共享公有编译包安装成功但预览进入发布方租户

- 共享不可变资源不能像租户独有 HTML 那样重写字节。包内 `SharedPublicRuntime.EntryUrl` 保持纯 HTTPS 不可变地址和摘要；目标 `PreviewUrl` 必须由导入器按当前 `V8.SysConfig.ApiBase + V8.OsClient` 生成 `apiBase/OsClient` 查询参数，不得直接使用发布端默认值，不得附带 Token、密码或签名。
- 目标 `ApiBase` 缺失、携带凭证或无效时停止并说明配置位置；不能用 CDN 地址或官方默认地址回退。公有字节共享不代表账号、房间、积分或 SignalR 频道跨租户共享。
- 自动化需比较不可变路径与摘要未变、普通包分支未变，并从目标安装后的实际入口检查登录请求、房间写入及实时事件；后台 `Succeeded` 不能替代租户启动验收。旧导入器先通过应用商城升级，再以新请求 Id 重装目标应用验证修复，保留旧任务审计。

## 复盘：历史开关列的单字节文本阻止数值列升级

- 数值类型转换失败时先只读实际 `PhysicalColumns` 和错误样本字节；表单字段声明为整数不代表物理列就是整数。旧 `BIT` 曾误转为文本时，可能留下单字节 `00/01`，不能按普通字符串 `0/1` 判断，也不能把所有非数字清零。
- 仅当可信包与目标字段均明确声明为开关（Switch），且文本值满足单字节和 `HEX IN ('00','01')`，才允许导入器按精确谓词归一为 `0/1`，然后进行类型升级。真正的 BIT 数值列、NULL、多字节文本、普通字段及其它脏值保持各自路径；未知值继续失败关闭。
- 修复通过正式应用商城导入器版本交付，先升级安装器再用新幂等请求重试原不可变包；禁止直接 SQL 改客户数据或修改旧失败任务。回归覆盖精确匹配、普通字段拒绝、并发重试和物理类型回读，并保留转换计数与终态证据。

## 复盘：只补页面首屏接口导致旧租户持续 `sys_apiengine NoExistData`

- 触发场景：新版前端已经切换到 `/apiengine/platform-*`、商城 `get-microi-store` 或模块/消息等官方接口，但客户只更新前后端二进制，没有逐个安装应用；启动门禁只检查少数首屏 Key，于是登录恢复后其它页面仍连续报 `sys_apiengine NoExistData`。
- 根因：把平台运行依赖维护成手写短清单，并只复制 SaaS 单包；接口之间的 `V8.ApiEngine.Run` 依赖、其它官方平台应用和个性化 Hook 没有进入同一闭包，旧数据库也没有可靠的启动前自愈与逐项回读。
- 强制规则：启动前接口闭包必须由全部内置官方平台应用包动态计算，并递归解析接口调用；当前官方基线为 9 个包、106 个接口（98 个 `Managed`、8 个 `CreateIfMissing`），数量只是发布快照，运行逻辑不得硬编码。门禁逐项校验 Key、地址、启用状态、匿名/HTTP 状态和源码策略；缺失时从受信内置包按原所有权补齐，所有项严格回读成功后才对外提供流量。`CreateIfMissing` 首次创建后永不覆盖客户代码。
- 控制面自举：官方资源发布必须先保证 `get-microi-upgrade-resource` 自身达到当前协议，再发布其它包；若控制面旧版本不能校验新包，先只发布并回读这一项，刷新动态路由缓存后再发布剩余资源。发布响应超时或 Redis 广播失败时先按资源 SHA 回读判断提交/回滚，禁止盲目重放。
- 兼容与验收：历史 Controller 回退不能再转调可能缺失的同一个接口引擎；保留旧路由时必须使用可信 Core 原子或明确返回可诊断兼容结果。自动化测试应扫描 PC、UniApp、内置应用包和接口源码的全部 `/apiengine/` 与 `V8.ApiEngine.Run` 引用，证明每个依赖由官方包声明；真实启动必须对每个启用租户输出开始、缺项、修复、逐项成功及最终汇总日志，并同时验证匿名、登录态、商城批量计划和重复启动幂等。

跨租户安装验收必须检查 `sys_microiservice.MsUrl`：受托管文件运行时的入口必须使用目标租户与目标 AppKey；数据库运行时保留 `db`，显式外部运行时保留外部地址。源码摘要与编译资源摘要仍需独立回读。
