Production readiness is manifest-driven: adding or modifying a runtime surface requires updating the corresponding machine-readable manifest, guarded by architecture tests. This replaces drift-prone human checklists — whose failure mode is well known: every box gets ticked in the review meeting, two weeks later every item has diverged from reality, and not a single test turns red.
Surface-to-manifest mapping
| Runtime surface | Manifest / gate |
|---|---|
Module roots / IAppModule | 0001-module-governance-legacy-ledger.json |
| Default adapters / production replacements | 0002-production-adapter-readiness.json |
| Docker contract tests | CI matrix + DockerContractTraitTests |
| Template versions / upgrade flow | 0004-template-upgrade-map.json |
| Operations visibility (probes, jobs, config, health, smoke) | 0003-operations-runtime-surface.json |
| GA cutover runbook | docs/guides/ga-cutover-runbook.md |
| Fail-closed production configuration baseline | docs/guides/production-configuration-template.md |
| Strong-typed error catalog / legacy compatibility | 0008-error-catalog.json + 0009-error-catalog-legacy-baseline.json |
| Commercial package catalog / Profile closure / release provenance | 0010-commercial-package-catalog.json + 0011-profile-package-closure.json + 0012-… schema |
| Runtime License policies / static composition admission | 0013-runtime-license-policy-catalog.json |
| Document lifecycle & hot-path budget | 0014-document-lifecycle-budget.json |
| Mediator pipeline capabilities & Consumer projection | 0015-pipeline-capability-catalog.json |
| Third-party license evidence | 0016-third-party-license-evidence.json |
| Field-security resources & execution-surface closure | 0017-field-security-resource-catalog.json |
| Package signing / private feed / SBOM | ADR 0605 + commercial release gates |
| Runtime License state / readiness | ADR 0503 + License contracts + Host integration tests |
The manifest naming itself is test-guarded
Manifests are more than data — their structure is governed too. ProductionReadinessNamingTests (in the architecture test project) forces long-term readiness manifests to declare "scope": "production-readiness" and forbids a phase field, preventing transitional states from quietly becoming permanent ledgers. The same test group bans CreatedAt/createdAt naming repository-wide (everything aligns on the entity base class’s CreateTime). In other words: to add a new manifest field you first have to convince an architecture test to accept the new schema shape.
The legacy ledger ratchet
0001-module-governance-legacy-ledger.json is a ratchet: it can only shrink. New modules cannot bypass owner-local compile-time markers by adding legacy entries, and maxLegacyRoots must decrease as legacy roots are deleted (currently zero). ModuleGovernanceRegistrationTests enforces this at compile time.
The adapter readiness guard
0002-production-adapter-readiness.json currently tracks 38 default-port-to-production-adapter replacement relationships, each record carrying its own readiness semantics and evidence fields. Here is a real entry (the ledger’s first):
{ "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"}The nine fields each answer one operational question: configDiagnostic tells operators which missing key causes failure and how to fix it; healthDiagnostic says what signal health checks watch; contractGate points at the concrete test base classes proving the adapter works; registrationEvidence pins the source files where registration happens — so every declaration traces to code in one click during review.
Risk tiers use a controlled enum with five levels, from “shippable now” to “developer machines only”:
| riskLevel | Entries today | Runtime semantics |
|---|---|---|
ProductionReady | 35 | Production replacement backed by config diagnostics, health diagnostics, and a contract gate |
FailClosedUnavailable | 3 | Explicitly unavailable ports (e.g. IAuditQueryPort → UnavailableAuditQueryPort); callers get a stable failure instead of silent no-op |
ProductionBlocker | 0 (reserved) | Once registered, the Production/Staging startup guard blocks outright |
ProductionConditional | 0 (reserved) | Once registered, production compositions are refused until explicit conditions are met |
DevelopmentOnly | 0 (reserved) | API Shell/local development/tests only |
At startup, ProductionAdapterReadinessGuard resolves every Required port and fails fast if the resolved default belongs to a blocking tier. The rule is plain but effective: before any InMemory*/Null*/Unavailable* default adapter reaches production, a production adapter with health diagnostics, config diagnostics, and contract tests must exist. See Persistence defaults.
Current manifest scale
Scale is not a quality verdict, but it helps spot the drift pattern of “source expanded, manifest stayed behind.” When reviewing, measure the JSON directly — never quote numbers from memory:
# Run at the framework repo root; number drift means source or manifest was updated without the other.jq '.entries | length' docs/architecture/00-governance/manifests/0008-error-catalog.json # 1968 strong-typed error codesjq '.entries | length' docs/architecture/00-governance/manifests/0002-production-adapter-readiness.json # 38 port replacement relationshipsjq '.packages | length' docs/architecture/00-governance/manifests/0010-commercial-package-catalog.json # 142 commercial packagesjq '.capabilities | length' docs/architecture/00-governance/manifests/0015-pipeline-capability-catalog.json # 15 pipeline capabilitiesjq '[.packages[]?] | length' docs/architecture/00-governance/manifests/0016-third-party-license-evidence.json # 19 third-party license evidence packagesAll counts must be read from the JSON manifests and guarded by architecture tests; never maintain a parallel hand-written list in documentation — this page’s own history includes the lesson of an error catalog whose count sat at three different values across documents after one expansion.
0015-pipeline-capability-catalog.json also generates the Host’s PipelineCapabilityProjection.g.cs and constrains Consumer Profile pipeline closures. New behavior cannot just push types into DI; register the capability first — applicable pipelines, ordering relations, projection, and coverage policy — then update generated artifacts and Consumer contract tests.
Gate execution order
- From source, locate the changed owner, public contracts, and runtime execution surfaces.
- Update the single manifest that owns that fact; do not duplicate the same state in a second JSON.
- Regenerate manifest-driven code, catalogs, or doc artifacts, and check that the diff contains only expected changes.
- Run manifest schema and architecture ratchets first, then behavior and failure tests for the affected module.
- Run startup validation against the Production/Staging composition to prove defaults and Unavailable adapters cannot silently take over.
- Replay delivery paths from cold cache, an isolated Consumer, or real containers so local ProjectReference cannot mask missing packages.
- Assemble commit, manifest hashes, test results, package hashes, and approval tickets into one release evidence package.
Passing manifest validation only proves internal consistency; external services still need their own contracts, health checks, and failure drills — databases, message brokers, object storage, private feeds, signing gateways, license services.
Release evidence package
| Evidence | Question answered |
|---|---|
| Source commit & clean-tree record | Which implementation was tested |
| Manifest files plus SHA-256 | Were the governance facts reviewed at approval replaced |
| Architecture & contract test results | Do ownership, closures, ratchets, failure semantics agree |
| Container / Consumer cold-cache results | Does the deliverable work outside the product repo |
| Package signature, SBOM, license & vulnerability results | Is supply chain traceable |
| Production config & readiness snapshot | Does the target environment run production adapters with dependencies met |
| Rollback version, data-compatibility verdict, owners | Can recovery happen within explicit boundaries after failure |
Failure handling
- Manifest drift: stop the release; determine whether source or manifest represents intent; never update numbers just to make tests green.
- ProductionBlocker: add the production adapter, health, and contract tests, or explicitly disable the feature; never degrade to Null behavior.
- Consumer restore failures: check Package Source Mapping, version closure, and analyzer/buildTransitive assets; do not fall back to ProjectReference.
- License or signing evidence failures: keep the commercial entry fail-closed while preserving established safety nets for identity recovery, backup, export, and migration.
- External dependency outages: follow the runbook for retry, grace period, DLQ, or degraded state, then capture fresh evidence after recovery.