Publish semantics

TL;DR

  • Publish and promote (release) are two separate operations. Publish stores an immutable, private prerelease build the caller can test; promote releases a chosen prerelease into the public, pinnable version.
  • Every component build moves through two states: prerelease (private to the caller, deployable only by them) and released (public, deployable by anyone in the workspace's audience). Publish creates the first; promote turns it into the second.
  • A prerelease is an immutable validated snapshot of the source tree. The build runs on the platform to confirm it compiles, the resulting image is stored privately, no registry push happens, no catalog tags move. Every publish mints a new prerelease ID — the platform does not update versions in place.
  • A promote releases that prerelease into the released catalog: the image goes to the registry, the tags declared in component.yml (latest/default/custom) move, catalog-facing fields (categories, modalities, neighbors, alternatives) sync, the version flips state. The ID does not change.
  • The two operations answer different questions. Publish answers "does this build". Promote answers "is this version public". Keeping them separate keeps a build that only passes validation out of the public catalog.
  • The CLI is cwd-driven: ppl component publish and ppl component promote both resolve the component from the working directory's component.yml. There is no version-id argument on promote; the cwd component's latest prerelease is the target.

Why two states exist

Publish and promote (release) are two separate operations. Publish stores an immutable, private prerelease build the caller can test; promote releases a chosen prerelease into the public, pinnable version. Keeping them separate means a build stays private until something explicitly promotes it, so nobody pinning the public tag depends on an unreleased build.

Publish validates a build and stores the resulting snapshot for testing. The build runs on the platform's builder, the image is stored privately to the caller, and the artifact is addressable by its prerelease ID. Nobody else sees it. The caller can deploy it in a test backend, run a live-fixture loop, and compare its behavior to the previous version. None of that activity is visible outside the caller's workspace.

Promote is the separate decision that the validated prerelease is ready to release — made public in the catalog, tagged as latest / default / whatever the component declares, and deployable by everyone whose visibility scope includes it. A released version is what other people pin against.

The split makes "is this version safe to depend on" answerable. A pinned released version has been through both validation (publish) and a human decision (promote). A prerelease ID is private exploration by construction: the right artifact for tests, the wrong artifact for production.

What publish actually does

Publish freezes the current state of the component's source tree into a new version row. The CLI runs from the component's root directory (the one with component.yml and src/), packages the source, the manifest, any Docker stage files present, the assets directory, and a LICENSE if small enough. It sends that bundle to the platform's build system, the build runs remotely, and on success the platform records a new version in state prerelease with a fresh ID.

ppl component publish
ppl component publish -m "describe the change"
ppl component publish --dry-run    # validate without writing anything

--dry-run runs the same packaging and remote build and reports success, but does not create the remote component, persist any local link, register a version, or push anything to the registry. It is the loop for fast iteration while editing src/ — when --dry-run passes, the platform-side build agrees with the source.

The lookup-or-create behavior is what makes the first publish and every subsequent publish behave predictably. If the working directory is linked to an existing component, publish writes a new prerelease against that component. If not, it looks the component up by name (the name: from component.yml); a single match links to it; zero matches require explicit -y / --yes consent to create a new remote component. Linking is non-destructive and adopting an existing remote does not require consent; creating a new remote does, since the consent flag gates the one action that produces a new object.

What publish does not do is equally load-bearing. It does not push to a public registry; the image stays private. It does not update an existing version in place; every publish is a new ID, even when the source bytes are identical. It does not redeploy; backends still pin to whatever version they were created against, and a vertex has to be bumped to a newer version and the backend redeployed for the new code to run.

What promote actually does

Promote takes a specific prerelease (the one belonging to the cwd component) and runs the release steps on it: rebuild the image from the stored source (so the registry image is reproducible from the recorded source bundle rather than from the caller's local docker cache at publish time), push it to the platform registry, apply the tags declared in component.yml (latest, default, or whatever the manifest names), sync the catalog-facing fields (categories, modalities, neighbors.upstream, neighbors.downstream, alternatives), and flip the version's state to released. The ID does not change; the version that was prerelease is now released.

cd <component-root>
ppl component promote

There is no version_id argument because the cwd component's prerelease is the target. Tags and catalog fields are sourced from component.yml at promote time — there is no separate "update tags" verb, and the platform rejects tag writes against a prerelease, since tags are the public contract and a prerelease has no public contract. Until promote runs, the latest and default tags still point at the previously released version, so a new build does not change what the public resolves.

Check the existing versions before promoting (ppl component versions <component_id> lists them oldest first, with each version's release state and tags). promote resolves the cwd component's single prerelease and releases it; the tags declared in component.yml decide where latest and default land. A component has at most one prerelease at a time, so there is never any ambiguity about which version promote acts on.

The cwd discipline

Both publish and promote resolve the target component from the working directory. The CLI walks up from the current directory looking for a linked component.yml; the first one found is the component being operated on. Neither command takes a version-id argument — the cwd is the argument. Running publish from the wrong directory publishes the wrong component; running promote from the wrong directory promotes the wrong prerelease.

Resolving from the working directory keeps the target tied to the source tree on disk rather than to a separately supplied identifier. The directory the caller is standing in is the component being operated on.

How publish, promote, and deploy compose

The three verbs are independent operations that compose into the release loop:

  • Publish mints a prerelease that is deployable by the caller. The next step is usually a live-backend test on the prerelease (see Test with a live backend).
  • Promote releases the prerelease into the catalog. Promoted versions sit in the catalog available to be pinned.
  • Deploy runs a backend on a runtime. Backends pin to specific version IDs; promoting a prerelease does not change any deployed backend's pinned version. To roll a backend forward, change the vertex's version (ppl backend change-version) and redeploy.

The three operate on different surfaces. Publish produces only private prereleases. Promote changes only the catalog; no running deployment moves. Deploy touches the runtime but not the catalog. Because the surfaces are distinct, each step runs without coordinating the others.

Where this fits

Publish semantics are the contract every other release-related decision relies on. Test loops depend on prereleases being immutable, so a proof against one prerelease ID stays valid. Promotion depends on the validated prerelease being exactly what the registry serves. Deployments depend on pinned versions not changing underneath them. Those guarantees all follow from the same rule: publish mints, promote releases, deploy pins, and nothing mutates after publish.

The model uses two verbs instead of one. In return, a released version always corresponds to a single recorded build that went through validation and a human promote decision, and a pinned version stays the build it was pinned to.

Related

Was this page helpful?