# Publication lifecycle

Publication in Al-Amr is a digest-bound state machine, not a mutable “published”
flag on a Project. Environment revisions, App revisions, Plugin releases, and
backend deployment revisions are independent targets. A submission freezes one target
and its artifact evidence, runs checks, enters review, and only then becomes
eligible for catalog or Runtime authority.

## The four publication targets

| Target                        | Frozen content                                                                                                                       | Artifact digest                                                       |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------- |
| `environment_revision`        | Bundle entry/version, access policy, portal presentation, capabilities, exact Plugin installations, and uploaded frontend bytes      | SHA-512 integrity of the deterministic Environment bundle             |
| `app_revision`                | Bundle entry, display modes, capabilities, notifications, disclosures, Contracts/SDK compatibility, and uploaded frontend bytes      | Digest of the one immutable bundle inventory bound before submit      |
| `plugin_release`              | Plugin manifest, SemVer, adapters, backend component contracts, settings, permissions, disclosures, compatibility, and documentation | Digest of the exact ordered artifact snapshot frozen at submission    |
| `backend_deployment_revision` | One BackendComponent endpoint, optional health URL, and public non-secret configuration                                              | `null`; deployment evidence is represented by its own snapshot digest |

A full-stack Plugin therefore has at least two independent publication
concerns: the Plugin release and one backend deployment revision for every
declared component. A live endpoint is never embedded in the Plugin release.

## The state machine

The executable transition graph is:

```text
draft
  ├─> checks_running ─> ready_for_review ─> in_review ─> approved ─> published
  │          │                                  └─> rejected
  │          └─> checks_failed ─> checks_running
  └─> withdrawn

checks_running, checks_failed, ready_for_review, and in_review may also
transition to withdrawn.

published ─> yanked
          └> suspended
```

`rejected`, `withdrawn`, `yanked`, and `suspended` have no outgoing transition
in the public transition contract. `published` can move only to `yanked` or
`suspended`. A generic transition request requires a reason when moving to
`rejected`, `withdrawn`, `yanked`, or `suspended`.

## What each state means

| State              | Meaning                                                                               | Normal actor or operation                                                    |
| ------------------ | ------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| `draft`            | Submission exists and binds the target digests; checks have not completed             | Project owner, CLI, Console, or Management API                               |
| `checks_running`   | Registry is evaluating the frozen target and evidence                                 | `alamr test` or the checks API                                               |
| `checks_failed`    | At least one required check failed                                                    | Owner diagnoses and reruns `alamr test` after correction                     |
| `ready_for_review` | Every check passed or warned; the submission may enter review                         | `alamr publish` or submit API                                                |
| `in_review`        | Submission is in the reviewer queue                                                   | Authorized reviewer                                                          |
| `approved`         | Review approved the exact snapshot and artifact digests                               | Authorized reviewer; publication still requires an administrative transition |
| `published`        | Target is published and may become the Project's active catalog or Runtime target     | Registry administrator                                                       |
| `rejected`         | Review rejected the submission                                                        | Reviewer decision                                                            |
| `withdrawn`        | Submission was ended before publication                                               | Owner or administrator through an allowed transition                         |
| `yanked`           | A formerly published target was removed through a terminal administrative transition  | Registry administrator                                                       |
| `suspended`        | A formerly published target was disabled through a terminal administrative transition | Registry administrator                                                       |

Warnings do not fail the check set: Registry advances to
`ready_for_review` when every check is `passed` or `warning`. Any `failed` check
produces `checks_failed`.

## From local files to a frozen submission

### 1. Link a mutable Project

`alamr link` creates or links the `environment`, `app`, or `plugin` Project and records
its Registry origin and `projectId` in `.al-amr/project.json`. The Environment
or Plugin has a different stable target ID.

When an Environment or App name has no ASCII slug characters, linking derives
a stable handle from its name and local product identity. Distinct Persian-only
projects therefore do not share a constant fallback handle. Plugin linking
preserves the manifest's explicit slug.

Desktop stores adoption registrations, plans, locks, and recovery receipts under
its own Electron profile. The in-process CLI uses that profile rather than an
inherited CLI state directory or Windows LocalAppData. Planning does not write
the selected project's files; existing state outside the profile is retained.

If the installed contract schema rejects the project manifest, validation and
upload preparation retain `ALAMR_INSTALLED_CONTRACT_MISMATCH`. Desktop explains
the mismatch in Persian and directs the author to the project's dependency
repair action. The original CLI diagnostic remains available in the command
envelope. Repair still requires a verified promoted release; this diagnostic
does not permit uploading an incompatible artifact.

### 2. Create the immutable target

`alamr test` first runs local validation.

- For an Environment, `alamr build` followed by `alamr test --bundle dist`
  creates or reuses the Environment revision, deterministically packs and
  uploads the frontend bundle, and binds its exact manifest and SHA-512
  integrity. Use `--bundle <dir>` when output is not `dist`.
- For an App, `alamr build` produces `dist`. The following `alamr test`
  inventories, packs, and uploads the bundle, creates the immutable
  build-generation revision, and binds the entry document and every declared
  byte in one operation.
- For a Plugin it creates or reuses the exact SemVer release. A matching
  existing version is reusable only if manifest and package content still
  match.
- For every configured backend component it creates or reuses a byte-for-byte
  matching BackendDeploymentRevision.

Changing Plugin manifest or package content under an existing version is a
conflict. Bump the Plugin SemVer. Changing Environment or deployment content
creates another revision; changed Environment bundle bytes also require a
bumped `entry.version`. Every changed App build generation receives a fresh
immutable revision.

### 3. Freeze artifact evidence

When Registry creates a Plugin submission, it snapshots the exact ordered set
of registered artifacts and derives `artifactDigest`. Managed package bytes
must cross the artifact-validator boundary; npm packages are resolved and
mirrored by Registry. Only validated artifacts can satisfy publication checks.

Before an App's first submission, Registry binds exactly one `AppBuildArtifact`
to its revision. The normalized inventory records the entry document, total
size, and each relative file path, byte size, and SHA-512 integrity; its
canonical SHA-256 `buildDigest` becomes the submission `artifactDigest`.
Different bytes cannot replace that binding or reuse the revision.

Before an Environment submission, Registry holds the deterministic archive and
binds its SHA-512 integrity to the revision and submission. The entry document,
declared cover, and every executable or runtime asset come from those held
bytes; Registry does not fetch a publisher-hosted frontend.

The review request supplies the expected snapshot and artifact digests.
Registry compares them again during review and before final publication. A
changed artifact set produces a conflict instead of silently reviewing
different bytes.

### 4. Run checks

```sh
# Environment
alamr build --json
alamr test --bundle dist --json --visibility unlisted --channel preview

# App
alamr build --json
alamr test --yes --json

# Plugin
alamr test --json --visibility unlisted --channel preview
```

Environment and App frontends have one-pass bundle checks. They do not use a
publisher-hosted preview, a well-known handshake, or a separate prepare/resume
handoff. An already-ready identical submission returns success, while
`checks_failed` reruns checks on the same matching submission. Changed content
requires a new Environment revision with a bumped `entry.version`, a new App
build generation, or a new Plugin SemVer; it is never made to fit by editing an
existing digest.

The checks endpoint accepts only `draft` or `checks_failed`. It atomically moves
the submission to `checks_running` and saves the resulting check list as either
`ready_for_review` or `checks_failed`.

The CLI records the target and submission IDs locally before continuing so an
interrupted command can inspect and reuse server state. A failed check is rerun
on the same submission; `alamr test` does not create a duplicate release merely
to retry checks.

### What the current checks prove

| Target                      | Implemented evidence                                                                                                                                                                                                                                                 |
| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Environment revision        | Manifest schema/digest and bundle entry/version; deterministic archive SHA-512; present entry document and declared cover; exact SDK SemVer; every installed Plugin is published, unblocked, adapter-valid, artifact-valid, and compatible                           |
| App revision                | Manifest schema/digest and one immutable bundle binding; entry document and every inventoried file match byte size/SHA-512; exact compatible SDK; compatible platform Contracts range; bounded file count, total size, path, and archive limits                      |
| Plugin release              | Manifest schema and digest; exact artifact snapshot; a valid managed package; every new backend component references packaged OpenAPI/AsyncAPI bytes whose SHA-256 matches; `permissions_disclosure` confirms the manifest declaration parses, not its prose quality |
| Backend deployment revision | Target existence and health reachability. This check proves reachability, not application-level correctness of the backend protocol                                                                                                                                  |

### 5. Submit for review

```sh
alamr publish --json --yes
alamr status --json
```

`publish` requires a linked checked target. The submit operation accepts only
`ready_for_review` and moves it to `in_review`. It does not approve or publish
the target.

For a full-stack or backend-only Plugin, the CLI preflights the Plugin release
and every local backend deployment submission. Every target must already be
`ready_for_review`, `in_review`, `approved`, or `published`, and every ready
target is submitted as one set. If local deployment configuration changed
after checks, publication stops and requires another `alamr test`.

The multiple submit calls are resumable but not one atomic transaction. If a
network or server failure interrupts the set, run `alamr status --json` and
rerun `alamr publish`. The full-stack path leaves targets already
`in_review`, `approved`, or `published` unchanged and submits only the remaining
`ready_for_review` targets.

### 6. Review and publish

An authorized reviewer first claims the work item with its optimistic version,
reads the frozen evidence, and submits `approve` or `reject` as the current
assignee. The decision binds the assignment version, snapshot digest, and
artifact digest. Registry stores every decision permanently. Rejection also
stores a category, summary, and one or more actionable remediation items;
closed cases remain readable as history.

Approval moves `in_review` to `approved` and atomically creates an exact-digest
publication handoff. A second platform administrator claims that handoff and
publishes it. The approver cannot be the publisher. Registry revalidates the
handoff version, approval identity, both frozen digests, target readiness, and
artifact snapshot, then commits publication and handoff completion in one
transaction. A Project owner or submission author cannot approve their own
release even when the same account also has administrator or reviewer
authority. Project tokens and successful automated checks cannot approve or
publish releases.

Structured diff and AI findings have explicit producer states. A missing
baseline is `baseline_unavailable`; an unconfigured AI producer is
`not_configured`. Neither state is converted into invented evidence or an
automatic approval.

If an approved Environment submission is later rejected, every ready or claimed
publication handoff for that submission becomes `cancelled`, its assignee is
cleared, and its optimistic version advances. This deliberately prevents stale
publisher ownership from surviving a terminal review reversal.

Reviewer authority is a dedicated platform authority, not a Project role and
not administrator access. A Registry administrator grants or revokes it with an
audited reason in Developer Portal at `/console/admin/reviewers`, or with
`alamr admin reviewers grant|revoke <userId> --reason <text> --yes`. A delegated
reviewer can read the review queue and exact immutable evidence, renew an
eligible review lease, and approve or reject. The authority grants no
Project membership, user administration, Hub placement, or publication
transition. Revocation is checked on every Registry request and takes effect
immediately. Platform administrators retain inherent review authority, subject
to the same owner/submitter separation-of-duty checks.

When published:

- an Environment revision becomes the active held bundle served to compatible
  desktop clients under the reviewed access policy;
- an App revision becomes the App's active published revision, but remains
  unavailable to Runtime until a separate administrator enable operation;
- a Plugin release becomes the Plugin's active release;
- a backend deployment revision becomes the published deployment and receives
  a healthy checked state.

A newly published Environment, App, or Plugin is visible through the
Registry-backed Developer Portal catalog on its next read. Publication never
edits Hub source, creates a Hub portal, or changes Hub placement. Hub placement
is a separate, explicitly authored and reviewed product decision; see
[Hub placement operations](/docs/publish/hub-placement).

Plugin release and backend deployment publication are separate administrative
transitions, not one atomic release transaction. A backend Plugin Grant is
issued only when the exact release is installed and its deployment revision is
published.

For an App, the final publish transition, global enable, and rollback each
reverify the held bundle binding before mutating state. Historical App
revisions without bundle evidence remain readable but cannot be newly
submitted, published, enabled, or selected as a rollback destination.

## CLI, API, and Console use the same control plane

The CLI and Console do not implement their own publication state:

| Workflow                              | CLI                                                                                | Management API                                                                                                                                                                |
| ------------------------------------- | ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Create target, submission, and checks | `alamr test`                                                                       | `POST /v1/projects/{projectId}/environment-revisions` or `plugin-releases`, `POST /v1/publication-submissions`, then `POST /v1/publication-submissions/{submissionId}/checks` |
| Enter review                          | `alamr publish`                                                                    | `POST /v1/publication-submissions/{submissionId}/submit`                                                                                                                      |
| Read status                           | `alamr status`                                                                     | `GET /v1/projects/{projectId}` and `GET /v1/projects/{projectId}/publication-submissions`                                                                                     |
| Reviewer ownership and decision       | `alamr review assignment`, `claim`, `release`, `history`, `evidence`, `decide`     | Dedicated review claim, evidence, history, and v2 decision endpoints with optimistic assignment version and exact digests                                                     |
| Approved publication handoff          | `alamr review handoff-list`, `handoff-inspect`, `handoff-claim`, `handoff-publish` | Dedicated publication-handoff claim and publish endpoints; the publisher must be a second administrator                                                                       |
| Withdraw an eligible submission       | `alamr publication transition`                                                     | Allowed transition to `withdrawn` with a reason; Console uses the Management API                                                                                              |
| Non-publication administrative state  | `alamr publication transition`                                                     | `/transition` handles allowed non-publication lifecycle changes; approved-to-published requires the dedicated second-admin handoff                                            |
| Rollback active target                | `alamr rollback`                                                                   | `POST /v1/projects/{projectId}/rollback`; Console calls the same API                                                                                                          |

Console authenticates with an opaque browser session and CSRF protection.
CLI and CI use separate scoped credentials. The dedicated review queue and
review endpoint require either a platform administrator or an active delegated
reviewer and reject project tokens.
Administrative publish, yank, and suspend transitions also reject project
tokens. Public clients must use the specialized checks, submit, and review
endpoints; the generic transition route is not a substitute for those
workflows.

## Recovery by current state

| Current state or symptom             | Safe recovery                                                                                                                                                                                                                                                                                                                                                       |
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `draft`                              | Rebuild the product, then run its one-pass check: Environment `alamr test --bundle dist`, App `alamr test`, or Plugin `alamr test`. If the target is wrong, create a new immutable revision or version rather than editing frozen evidence.                                                                                                                         |
| `checks_running`                     | Read `alamr status --json` and wait for the current operation. Starting duplicate checks returns a conflict.                                                                                                                                                                                                                                                        |
| `checks_failed`                      | Read every structured check and correct the local bundle or deployment problem. Rebuild when bytes changed, then rerun the same product-specific `alamr test` command; the CLI reuses only an exact matching target and reruns checks in place.                                                                                                                     |
| `ready_for_review`                   | Run `alamr publish` once. Repeated submission is unnecessary; use `status` to read the authoritative state.                                                                                                                                                                                                                                                         |
| `in_review`                          | Wait for an independent reviewer. The Project owner or submission author cannot approve it even if that account is also an administrator/reviewer; a project token cannot review.                                                                                                                                                                                   |
| `rejected`                           | The review decision and history are terminal and immutable. Registry can reopen the same submission workflow at `draft` only when the exact target and frozen artifact snapshot still match; rerun its checks and review. If code, manifest, Environment configuration, deployment, or artifact bytes changed, create a new target; a Plugin requires a new SemVer. |
| `withdrawn`                          | Create a new target and submission. Do not mutate the withdrawn evidence.                                                                                                                                                                                                                                                                                           |
| `approved`                           | Wait for the administrative `published` transition. If the artifact snapshot changed, publication fails closed.                                                                                                                                                                                                                                                     |
| `published`                          | Publish a new revision or release for changes. To return to older known-good content, use rollback to an eligible still-published target.                                                                                                                                                                                                                           |
| `yanked` or `suspended`              | These states cannot be reactivated by transition. Publish a new target or roll back to a different eligible published target. A yank also removes that revision as a rollback target — rollback requires a still-published one — so yanking the last known-good revision leaves nothing to fall back to.                                                            |
| Snapshot or artifact digest conflict | Stop. Confirm the target IDs and artifact set. Never rewrite the digest; create a new immutable target when content changed.                                                                                                                                                                                                                                        |

Environment and App bundle retries are intentionally content-bound. Never
invent or pass a revision ID. If the built bytes changed, rerun `alamr build`
and the product-specific `alamr test`; the CLI creates or selects only the
legitimate immutable target for those exact bytes. A no-op manifest change is
never a valid conflict workaround.

## Yank, suspend, and their Runtime effect

Both `yanked` and `suspended` are administrative, terminal transitions from
`published`. The transition requires a reason and is audit logged.

- For an Environment revision, Registry clears it as the active held bundle,
  revokes active Runtime sessions, and revokes linked Plugin Grants.
- For a Plugin release, Registry clears it as the active release and revokes
  Plugin Grants issued for that release.
- For a backend deployment revision, Registry marks the deployment suspended
  and revokes its Plugin Grants.

The original snapshot, review, and `publishedAt` evidence remain available.
For a yanked Plugin version, existing integrity locks remain reproducible, but
`alamr add` refuses a new installation of that version.

## Rollback without rewriting history

Rollback supports Environment revisions, App revisions, and Plugin releases.
It moves the Project's active pointer to a target that:

- belongs to the same Project;
- still has publication state `published`;
- is not covered by an active emergency block.

Rollback requires a reason and is audit logged. It never edits the target,
review, artifact, or earlier submission. Environment and App rollback select
the previously reviewed held bundle for the chosen revision; App rollback also
revalidates that immutable bundle before moving the active published pointer.
Registry locks the immutable target and publication row, rechecks current
eligibility, and moves the pointer in the same transaction. A concurrent yank,
retirement, or direct/managed-artifact emergency block therefore has one
serialized winner; rollback cannot activate a target that became ineligible
between planning and pointer update.

Use `alamr rollback <environment_revision|app_revision|plugin_release>
<targetId> --reason <text> --plan` to inspect and persist a digest-bound plan,
then apply that exact plan with explicit approval. The documented Management
API and Console call the same Registry operation; never simulate rollback by
editing IDs, digests, manifests, or lockfiles.

For failure diagnosis continue with
[Troubleshooting](/docs/errors/troubleshooting). Term definitions are in the
[Product and security glossary](/docs/product/glossary), and exact API
contracts are in [API reference](/reference/api).
