# Application navigation cutover

Navigation changes for installed NexusCRM applications are synchronous Runtime
configuration. Runtime owns the complete stored document, revision, authorization,
audit and replay. UI Apps is a permission-filtered serving projection and must
never become an authoring document. Catalog carries the install template, not
current edits. Reading application definitions is independent of navigation.

## Application definitions and supported administration

This delivery remains scoped to installed-application navigation (#850). In the
standalone deployment, application definitions are installed Catalog facts.
There is no production `CustomApplication` CRUD implementation: the old dialect
handlers require an uninstalled single-process `ApplicationNavigation` adapter.
Their presence in source is not proof of a supported production endpoint. Do not
restore that adapter or use record CRUD to mutate application metadata.

App Manager and Studio read `/metadata/applications`, the native administration
projection of the existing Catalog application definitions. The existing UI Apps
service owns this projection under its declared Catalog and effective-permission
capabilities. It requires current `ViewSetup` or `CustomizeApplication` permission
and reconciles Catalog/permission receipts. It never resolves navigation and
never returns a navigation template as current navigation. `/ui-api/apps` retains
its complete, permission-filtered Runtime navigation projection and fails closed
when that authority is unavailable.

The generic host AppProvider exposes a failed serving read as `appLoadError`,
clears its serving projection and keeps independent route-owned children mounted.
The sidebar shows an unavailable state with retry instead of cached navigation.
Both initial failures and failed explicit refreshes follow this rule; successful
recovery preserves the mounted editor and its draft. This host boundary is part
of the paired Core change, not an L1 substitute for host state ownership.

Installed application settings are displayed read-only. Creation, deletion and
settings saves are not advertised and the API client rejects those operations
before network access. A future online application-definition authoring delivery
must establish its own Runtime lifecycle; it is not hidden inside this navigation
fix. External Salesforce application definitions can be viewed, but their creation,
deletion, settings saves and navigation editing are not supported by this delivery.

## Execution environments

NexusCRM backend sessions edit navigation through the configured backend origin
and host credential owner. This capability rejects external sessions before
network access. Application definitions are independently readable.

External Salesforce application administration is read-only in this delivery.
App Manager and Studio can read native application definitions; they offer no
application creation, deletion, settings save or navigation editor. The retained
Tooling client performs GET requests only. Unsupported application mutations are
rejected before network access, including callers outside these pages. Use
Salesforce Setup to manage those applications.

The proposed CustomApplication Metadata PATCH implementation and native tabs
editor have been removed. External application authoring is deferred by scope;
there is no pending live-org requirement for the removed capability. A future
delivery must establish Salesforce's write and concurrency contract first.

## Single-process retirement

The single-process distribution has been retired. This includes these former
dialect-only approval Layout mutation routes:

| Method | Retired route |
| --- | --- |
| POST | /ui-api/approval-layout/Global |
| POST | /ui-api/approval-layout/:layoutId/clone |
| PATCH | /ui-api/approval-layout/:layoutId/default |
| DELETE | /ui-api/approval-layout/:layoutId |

These routes were not part of the standalone AppService at the PR base. They
are not aliases of the new Layout authoring API and are not promised same-path
compatibility endpoints. Do not restore the monolith to retain them. The active
dialect manifest and its D2A classification both remove the retired routes.
Standalone Layout authoring remains owned by `/metadata/layout-authoring/`
(workspace, stored, mutate); its domain implementation is outside navigation.

## Proof and release order

Freeze the implementation only after checking the complete consumer flow:
settings reads and navigation edits, rejection of application mutations, backend and external sessions,
configured backend origins, permission-filtered reads, errors and retries.
Client isolation tests and component tests with a replaced client are insufficient
proof of their composition.

The Node-only frontend source suite verifies Studio with controlled HTTP failures
through the real API client. It does not compile or launch backend processes.
The Go owner tests independently verify Catalog authority, setup permissions and
fail-closed navigation responses.

Run `npm --prefix frontend run check:appservice-integration` for the complete
Manager → Studio → settings walking slice. This separate runner compiles the
actual Go HTTP fixture, then runs `frontend/vitest.integration.config.ts` against
both navigation 403 and 503. Only the upstream Catalog input and Runtime failure
are supplied by the fixture. Missing dependencies or fixture startup fail the run;
there is no optional skip. Local and CI use this same runner.

CI runs that integration command in the existing trusted AppService exact-main
job after private dependencies are fetched and before building the service image.
It does not add private credentials to the ordinary PR frontend-source gate.
Frontend changes trigger the AppService workflow as well. A local integration pass
is required for the candidate before merge; the trusted main job repeats the proof
before publication. The default skipped Go fixture test alone is not integration
evidence.

Run the changed dialect module tests as well as its seals; the seals job alone
does not test the active route classification. Run the AppService and frontend
owner batches, then freeze the source commit. Build the AppRelease and one-shot
attestation from that clean exact commit, and run the current-release and
historical reproducibility tests against the resulting publication. A later
change to pack source invalidates that evidence and requires a new attestation.
Frontend and service images have their own release identities; do not conflate
them with the AppRelease metadata artifact. L2 selects those independent inputs
only after this L1 boundary is proven.

### Reproducing the publication source digest

Use `GOWORK=off go test -timeout 180s ./... -run
'^TestCurrentCRMReleaseMatchesCommittedArtifactAndSourceTree$' -count=1` from
`tools/crm-app-release`. This rebuilds the current package byte-for-byte and
compares the actual builder source digest to the attestation.

For 0.73.0, the input to `sourceTreeDigest` is `pack.json`, its component sources,
and the three command sources appended by `buildCRMRelease` through
`runtimeFlowCommandSources`: `app-release/runtime-flow-command.json`,
`app-release/runtime-flow-command-v2.json`, and
`app-release/runtime-flow-command-v3.json`. Hashing only the manifest components
produces `sha256:aa5771d757fde8545f0ade1a4592ba19f82163333ea93a8477ab896ce6b6d0eb`;
that is not the builder's input. The complete input produces
`sha256:8f1a0ed78c01c3b70a67ba79158b12af66b12b96a64bbfc5244b76ae4ff35822`,
which matches the committed attestation. Do not re-sign an immutable publication
based on a different source-set calculation.

### Frontend route context is not Salesforce application metadata

The host supplies the registered route owner independently of application metadata.
External application definitions have no NexusCRM frontend owner; App Manager uses
its current route context to open the read-only Studio. No frontend owner or
navigation metadata is written back to Salesforce.
