生产就绪是清单驱动的:新增或修改一个运行时表面需要更新对应的机器可读清单,并由架构测试守卫。这用可评审、过期即失败关闭的产物替代了易漂移的人工检查清单——人工检查清单的失败模式众所周知:评审会上逐项打勾,两周后每一项都与现实脱节,而没有任何测试会因此变红。
表面到清单的映射
| 运行时表面 | 清单 / 门禁 |
|---|---|
模块根 / IAppModule | 0001-module-governance-legacy-ledger.json |
| 默认适配器 / 生产替代 | 0002-production-adapter-readiness.json |
| Docker 契约测试 | CI 矩阵 + DockerContractTraitTests |
| 模板版本 / 升级流程 | 0004-template-upgrade-map.json |
| 运维可见性(探针、作业、配置、健康、冒烟) | 0003-operations-runtime-surface.json |
| GA 切换 runbook | docs/guides/ga-cutover-runbook.md |
| 生产配置失败关闭基线 | docs/guides/production-configuration-template.md |
| 强类型错误目录 / 历史兼容 | 0008-error-catalog.json + 0009-error-catalog-legacy-baseline.json |
| 商业包目录 / Profile 闭包 / 发布溯源 | 0010-commercial-package-catalog.json + 0011-profile-package-closure.json + 0012-… schema |
| Runtime License 策略 / 静态组合准入 | 0013-runtime-license-policy-catalog.json |
| 文档生命周期与热路径预算 | 0014-document-lifecycle-budget.json |
| Mediator 管线能力与 Consumer 投影 | 0015-pipeline-capability-catalog.json |
| 第三方许可证证据 | 0016-third-party-license-evidence.json |
| 字段安全资源与执行面闭包 | 0017-field-security-resource-catalog.json |
| 包签名 / 私有 Feed / SBOM | ADR 0605 + 商业发布门禁 |
| Runtime License 状态 / 就绪 | ADR 0503 + License 契约 + Host 集成测试 |
清单命名本身也被测试守卫
清单不只是数据——它的结构同样受控。ProductionReadinessNamingTests(架构测试工程)强制长期就绪类清单必须声明 "scope": "production-readiness" 且不得携带 phase 字段,防止过渡期状态悄悄变成永久账本;同一组测试还全仓禁用 CreatedAt/createdAt 命名(统一对齐实体基类的 CreateTime)。换句话说,想给清单加一个新字段,先要说服架构测试接受新的 schema 形状。
遗留账本棘轮
0001-module-governance-legacy-ledger.json 是一个棘轮:只能收缩。新模块不能通过添加遗留条目绕过 owner-local 编译期标记,且 maxLegacyRoots 必须随遗留根的删除而减小(当前为零)。ModuleGovernanceRegistrationTests 在编译期强制执行这一点。
适配器就绪守卫
0002-production-adapter-readiness.json 当前跟踪 38 个默认端口到生产适配器的替代关系,每条记录都带独立的就绪语义与证据字段。以下是真实条目(取自清单首条):
{ "port": "IUnitOfWork", "defaultAdapter": "NullUnitOfWork", "riskLevel": "ProductionReady", "replacementRequired": true, "requiredProductionAdapter": "SqlSugarUnitOfWork, CapSqlSugarUnitOfWork, EfCoreUnitOfWork, or CapEfCoreUnitOfWork", "configDiagnostic": "ConnectionStrings:Default or SqlSugar:ConnectionString plus CAP/RabbitMQ configuration for outbox wiring", "healthDiagnostic": "runtime-dependencies readiness plus database/CAP adapter health", "contractGate": "RepositoryContractTestBase and OrmAdapterParityTests", "registrationEvidence": "src/Framework/BitzOrcas.Infrastructure.SqlSugar/DependencyInjection.cs; src/Framework/BitzOrcas.Infrastructure.EfCore/DependencyInjection.cs", "operationsVisibility": "OperationsService adapter probe for IUnitOfWork"}九个字段各司其职:configDiagnostic 告诉运维少配了哪个键会失败、怎么补;healthDiagnostic 说明健康检查看什么信号;contractGate 指向证明该适配器可用的具体测试基类;registrationEvidence 直接钉到注册发生的源码文件——评审时每个声明都能一键溯源。
风险分层使用受控枚举,五个等级从”可直接投产”到”仅限开发机”:
| riskLevel | 当前条目数 | 运行语义 |
|---|---|---|
ProductionReady | 35 | 具备配置诊断、健康诊断与契约门禁的生产替代 |
FailClosedUnavailable | 3 | 显式不可用端口(如 IAuditQueryPort → UnavailableAuditQueryPort),调用方得到稳定失败而非静默空转 |
ProductionBlocker | 0(备用) | 一旦登记,Production/Staging 启动守卫将直接阻断 |
ProductionConditional | 0(备用) | 登记后在满足显式条件前拒绝生产组合 |
DevelopmentOnly | 0(备用) | 仅限 API Shell/本地开发/测试 |
启动守卫 ProductionAdapterReadinessGuard 在应用拉起时解析每个 Required 端口,若解析到的默认实现属于阻塞级别则快速失败。判定规则朴素而有效:任何 InMemory*/Null*/Unavailable* 默认适配器进入生产之前,必须先存在带健康诊断、配置诊断与契约测试的生产适配器。见持久化默认策略。
当前清单规模
规模不是质量结论,但能帮助发现”源码已扩张、清单仍停在旧数量”的漂移。复核时以 JSON 实测为准,不要引用记忆里的数字:
# 在框架仓库根目录执行;数字漂移意味着源码或清单有一方被遗漏。jq '.entries | length' docs/architecture/00-governance/manifests/0008-error-catalog.json # 1968 个强类型错误码jq '.entries | length' docs/architecture/00-governance/manifests/0002-production-adapter-readiness.json # 38 个端口替代关系jq '.packages | length' docs/architecture/00-governance/manifests/0010-commercial-package-catalog.json # 142 个商业包jq '.capabilities | length' docs/architecture/00-governance/manifests/0015-pipeline-capability-catalog.json # 15 项管线能力jq '[.packages[]?] | length' docs/architecture/00-governance/manifests/0016-third-party-license-evidence.json # 19 个第三方包许可证据所有数量都应从 JSON 清单读取并由架构测试守卫,不能在文档中独立维护另一份手工列表——本页的历史教训:错误目录曾在一次扩张后让四处文档各自停留在不同版本的数字上。
0015-pipeline-capability-catalog.json 还会生成 Host 的 PipelineCapabilityProjection.g.cs,并约束 Consumer Profile 的管线闭包。新增行为不能只把类型塞进 DI;必须先登记 capability、适用 pipeline、排序关系、投影和覆盖策略,再更新生成物与 Consumer 契约测试。
门禁执行顺序
- 从源码定位变更的 owner、公开接口和运行时执行面。
- 更新唯一负责该事实的清单;不要在第二个 JSON 中复制同一状态。
- 重新生成受清单驱动的代码、目录或文档产物,并检查 diff 是否只包含预期变化。
- 先运行清单 schema 与架构棘轮,再运行对应模块的行为和故障测试。
- 对 Production/Staging 组合执行启动验证,证明默认或 Unavailable 适配器不能悄悄接管。
- 从空缓存、隔离 Consumer 或真实容器重放交付路径,避免本机 ProjectReference 掩盖缺包。
- 把 commit、清单哈希、测试结果、包哈希和审批工单组成同一份发布证据。
清单校验成功只说明声明内部一致,不证明外部服务可用。数据库、消息代理、对象存储、私有 Feed、签名网关和许可证服务仍需要各自的契约、健康与故障演练。
发布证据包
| 证据 | 回答的问题 |
|---|---|
| 源码 commit 与 clean-tree 记录 | 测试的究竟是哪一版实现 |
| 清单文件及 SHA-256 | 审批时看到的治理事实是否被替换 |
| 架构与契约测试结果 | owner、闭包、棘轮和失败语义是否一致 |
| 容器 / Consumer 空缓存结果 | 交付物是否脱离产品仓库仍可使用 |
| 包签名、SBOM、许可证与漏洞结果 | 供应链是否具备可追溯证据 |
| Production 配置与 readiness 快照 | 目标环境是否使用生产适配器并满足依赖 |
| 回滚版本、数据兼容结论和负责人 | 失败后能否在明确边界内恢复 |
失败处理
- 清单漂移:停止发布,先确定源码还是清单代表预期事实;不得直接更新数字让测试变绿。
- ProductionBlocker:补生产适配器、健康和契约测试,或显式关闭功能;不能降级成 Null 行为。
- Consumer 还原失败:检查 Package Source Mapping、版本闭包和 analyzer/buildTransitive 资产,不回退到 ProjectReference。
- 许可证或签名证据失败:保持商业入口失败关闭,同时保留身份恢复、备份、导出和迁移的既定安全边界。
- 外部依赖故障:按 Runbook 验证重试、宽限、DLQ 或降级状态,并保存恢复后的新证据。