# B1 / @next Durable Publish Pipeline — Design

## Objective

Make `next` a durable integration line that publishes the artifacts required by downstream federation boot tests without manual builds.

Every merge to `next` publishes:

1. **npm prerelease packages** to the Gitea npm registry with dist-tag `next`.
2. **Gateway container image** tagged only as `gateway:sha-<short>`.

The existing stable release behavior remains isolated to `main` / tags.

## Registry verification

Target registry: `https://git.mosaicstack.dev/api/packages/mosaicstack/npm/`.

Pre-implementation checks:

- `npm view @mosaicstack/mosaic dist-tags --registry https://git.mosaicstack.dev/api/packages/mosaicstack/npm/ --json` returned a dist-tags object (`latest: 0.0.48`).
- `npm view @mosaicstack/mosaic@latest version --registry https://git.mosaicstack.dev/api/packages/mosaicstack/npm/` resolved `0.0.48`.
- `@next` currently returns 404 because no `next` dist-tag exists yet; this is expected before the first next prerelease publish.

Pipeline design includes a post-publish verification that `npm view @mosaicstack/mosaic@next version` resolves to the exact CI-computed prerelease version. If Gitea fails to honor the `next` dist-tag, the pipeline fails closed.

## Version scheme

The prerelease version is computed at publish time only; no `package.json` version changes are committed.

For each non-private `@mosaicstack/*` package:

```text
<target-stable>-next.<CI_PIPELINE_NUMBER>
```

Where:

- `CI_PIPELINE_NUMBER` is Woodpecker's monotonic pipeline number.
- `target-stable` is the package's current committed stable version with the patch component incremented.
  - Example: `@mosaicstack/mosaic` `0.0.48` publishes as `0.0.49-next.1626`.
  - Example: `@mosaicstack/gateway` `0.0.6` publishes as `0.0.7-next.1626`.

Rationale:

- npm semver sorts `0.0.49-next.1627` above `0.0.49-next.1626`.
- The prerelease does not overtake the future stable `0.0.49`.
- The monotonic pipeline number avoids conflicts across repeated `next` merges.

## Branch and tag guardrails

| Pipeline path         | Branch/event                   | Publishes                                               | Forbidden              |
| --------------------- | ------------------------------ | ------------------------------------------------------- | ---------------------- |
| stable npm publish    | `main` push/manual or tag      | package versions already committed in package manifests | `@next` dist-tag       |
| next npm publish      | `next` push/manual only        | CI-computed prereleases with `--tag next`               | `latest` dist-tag      |
| gateway image         | `main` push/manual or tag      | `sha-<short>` + `latest` on main + tag on tag events    | next prerelease npm    |
| gateway image         | `next` push/manual only        | `sha-<short>` only                                      | `latest`               |
| appservice/web images | `main` push/manual or tag only | existing stable image behavior                          | next image publication |

The pipeline has explicit branch checks inside the publish commands as a second fail-closed layer beyond Woodpecker `when` clauses.

## Implementation plan

1. Widen `.woodpecker/publish.yml` top-level `when` to include `next` so the publish pipeline runs on next merges.
2. Keep existing `publish-npm` on `main` / tags only.
3. Add `publish-next-npm` for `next` push/manual only:
   - configure Gitea npm auth from existing `gitea_token` secret as `NPM_TOKEN`;
   - preflight registry dist-tag metadata;
   - compute prerelease versions in CI by temporarily editing package manifests in the workspace;
   - run `pnpm publish ... --tag next` against non-private `@mosaicstack/*` packages;
   - verify `@mosaicstack/mosaic@next` resolves to the computed version.
4. Split image `when` anchors:
   - `image_build_when` includes `next` and is used by `build-gateway`;
   - `main_image_build_when` keeps appservice/web on main/tags only.
5. Keep gateway next image destinations to `sha-<short>` only; no `latest` on next.

## Risk controls

- Auth/registry failures are fatal.
- No manual image build/push path is introduced.
- No production `latest` tags are touched from `next`.
- No `@latest` npm dist-tags are touched from `next`.
- All changes live in CI config and docs; no runtime source behavior changes.
