# Catalog reconcile

`catalogreconcile` is the one non-request-serving composition that turns an
already-applied effective metadata revision into an exact Runtime serving
selection.

The job:

1. resolves the exact immutable AppRelease from the Catalog registry; only when
   that release is missing does it lazily load external import material, ask
   `catalogapp` to verify the complete publisher/App/release candidate without
   storage access or writes, and import it through the existing authority;
2. begins one fenced Catalog operation;
3. recovers the operation's committed CatalogSnapshot, or reads objects,
   relationships, and sharing rules in one database transaction;
4. stores the content-addressed CatalogSnapshot and atomically commits that
   exact digest as the operation's sole metadata selection;
5. derives and stores the RuntimeMetadataView and serving compatibility receipt;
6. stages the receipt-bound HandlerBinding and the release-required frontend binding;
7. commits the canonical complete operation-bound binding set;
8. publishes the complete Runtime security projection;
9. atomically activates the Catalog operation; and
10. emits `nexuscrm.catalog-reconcile-output/v1` for exact Runtime/L2 config.

It does not apply a pack, read a source checkout, serve a request, or expose a
`latest` selector. In D1 the L2 setup job applies the immutable, materialized
L2 final pack first. This command then reads only the resulting database
revision and publishes immutable artifacts. Runtime consumes only the exact
output digests; it never calls Catalog or falls back to pack source while
serving.

The executable reads TiDB connection settings from the existing `TIDB_*`
environment contract:

```text
catalog-reconcile -config /config/reconcile.json -output /output/reconcile.json
```

Both input and output are closed, deterministic JSON grammars. Duplicate input
keys, unknown fields, trust conflicts, digest mismatches, mixed metadata
revisions, partial artifact durability, stale ownership epochs, and binding /
receipt mismatches fail closed.

`releaseImport` is optional missing-release bootstrap material, not a lifecycle
dependency. An already registered AppRelease is the sole release source of
truth for `PREPARE`, `UPGRADE`, and `ROLLBACK`; reconcile does not reopen its
package, attestation, or publisher key. This keeps a registered rollback target
usable after normal publisher-key rotation, revocation, or expiry. Importing a
missing release verifies its complete candidate first, then reuses exact active
publisher trust or rotates a different operator-authorized one-shot release key
through the epoch-fenced Catalog registry owner. Reconcile
still checks the immutable registered package against the explicitly supplied
current host compatibility facts before beginning an operation. A Catalog read
failure never falls back to external material.

`CANDIDATE_COMMITTED` does not authorize projection or activation. The job
first stores the complete immutable artifact chain and stages every
release-required binding. Catalog then journals a canonical commitment over
the entire operation-bound HandlerBinding/FrontendBinding set as
`SERVING_ARTIFACTS_COMMITTED`. Each HandlerBinding owns its capability-specific
receipt, which transitively binds the snapshot and Runtime view; the journal
does not invent a competing single-receipt owner. A crash after one binding is
staged recovers that exact receipt from Catalog. A crash after snapshot storage
but before binding staging recovers the Catalog-committed snapshot digest and
rebuilds only its deterministic derived artifacts; neither path rereads live metadata.
Projection and activation both revalidate the complete set commitment. The
legacy-conversion initiation follows the same gate and cannot bypass it.
Each orchestration pass re-reads the Catalog operation before interpreting its
step. If an overlapping exact retry advances the same immutable operation,
the lagging process resumes from the newer owner state; an observed
`ACTIVATED` step reconstructs the exact historical output instead of invoking
projection again or failing on a stale local snapshot.

The input must carry the exact current Catalog revision and security epoch,
including explicit zeroes for a new org. The complete follow-on serving intent
(operation kind, existing installation identity when applicable, capabilities,
handler, workload, service/frontend digests, and engine lineage) is
canonically digested into the Catalog operation journal before any artifact or
binding work starts. Retrying one idempotency key with changed serving facts is
therefore a conflict, not a new or partially reused activation.

`PREPARE` requires an empty AppInstallation ID; `UPGRADE` and `ROLLBACK`
require the exact existing Catalog-owned ID. The legacy v1/v2 input accepts one
record-access service-handler capability. The v3 input atomically accepts the
complete set of release-owned serving capabilities for which Catalog
defines an explicit metadata projection; unknown, duplicated, or partial sets
fail before mutation. Both shapes validate the optional/required frontend
binding against the verified AppRelease. This is not a generic release
controller and never partially activates a broader release shape.

Accepted `main` revisions publish exactly one immutable
`ghcr.io/pingcap-inc/nexuscrm-controlplane-catalog-reconcile@sha256:...`
artifact with source-revision, provenance, and SBOM evidence. The publisher
uses separate repository-scoped read-only deploy keys because the private,
pinned L0 module graph is part of the build. The repository-owned KMS key signs
the exact digest and canonical provenance before the stable source-SHA tag is
committed. It never substitutes a local checkout or publishes a mutable
`latest` tag.

The publisher is intentionally `push`-only. Codex Review approves a pull
request but does not merge it: the merge owner must create the real `main`
push that starts publication. The review workflow has no package, OIDC, or
private dependency credentials and cannot act as a second publisher.
# Layout publication inputs

Layout authoring and caller-effective resolution consume one transaction-bound
`ReadLayoutAuthoringAggregate` per object. `RepositoryMetadataReader` delegates
that read to the persistence owner. Custom readers must implement this complete
stored-fact contract; `GetLayouts` plus assignments is not an authoring fallback.

Authoring supplies `CompileInput.LayoutAuthoringAggregates`, including empty
collections, ordered list fields, all live stored Layout kinds, and assignments.
Resolution supplies its separate compiler inputs from the same aggregate and
adds record-type and related-list closure. It retains the existing exclusion of
legacy `Create` layouts and the rejection of Role selectors without exact routing
security authority. Assignments to ineligible layouts still fail compiler
validation; they are not silently removed. Selecting both projections does not
duplicate the stored read or turn authoring into resolution authority.

The Core module pins include the complete aggregate reader and compiler contract
at `5e0532b2602a9a164f790488b1bfa4ef567b8c96`. The shared stored reader accepts
historical ASCII object-name case aliases using Catalog object-key validation,
then projects `_System_Object`'s canonical spelling for publication and locked
version computation. Unicode aliases and padded names are rejected; publication
does not rewrite stored rows or relax the compiler's exact identity checks.
Permission Authoring remains in the
capability closure. This adapts the Layout portion of PR #412 without importing
its Connector V2 or Flow changes.
