Skip to content

Canary Snapshots

martyy-code edited this page Aug 5, 2026 · 4 revisions

Last synced from docs/engineering/process/canary.md on 2026-08-05. The repo file is the source of truth. If the two diverge, follow the repo.

This page describes canary snapshots, a future addition to the release pipeline for @deessejs/fp. Canary snapshots are not implemented today — there is no canary workflow, no canary Trusted Publisher slot, and no canary package on npm. This page documents the design so that the concept is captured before it is forgotten.

TL;DR

A canary snapshot is a pre-release version of @deessejs/fp published from every push to a pull request targeting the staging branch, installable as @deessejs/fp@canary. The pipeline has two active channels today (regular release and hotfix path); canary snapshots would be a third channel, currently a future feature. The design is captured here so it is not lost when the conditions that justify implementation (external contributors, multiple maintainers, downstream consumers) finally arise.

Outline

  1. What is a canary snapshot
  2. Why we want canary snapshots
  3. Why we do not have canary snapshots today
  4. Target workflow
  5. Known limitations
  6. Migration to pkg.pr.new
  7. When to revisit this decision
  8. Related documents

1. What is a canary snapshot

A canary snapshot is a pre-release version of @deessejs/fp published from a pull request, on every push to that PR. It is not a tagged release. It exists to let reviewers and external users install a pre-merge build and exercise it in real conditions before the PR lands.

The mechanism: every push to a PR produces a published package installable as npm install @deessejs/fp@canary. The package name does not match semver but is stable per PR.

In the wider npm ecosystem, this pattern goes by several names:

  • Vercel (and now stackblitz-labs, the current maintainer since 2024) calls it pkg.pr.new.
  • Changesets calls it snapshots, published under a dist-tag like @canary.
  • Some projects use custom names like @next or @experimental.

2. Why we want canary snapshots

Three concrete benefits over the current PR-only review:

  • External testers can validate. A PR reviewer who is not a maintainer cannot install the PR locally without setting up the dev environment. A canary snapshot lets them run npm install @deessejs/fp@canary in their own project and report real-world issues.
  • Integration tests against real consumers. If @deessejs/fp is used by other packages, a canary snapshot lets those packages run their CI against the canary build and report regressions before the PR merges.
  • Reviewers can test in their own stack. Today, a reviewer reads the diff and maybe runs pnpm test locally. With canary, they can install in their project and see how the change behaves end-to-end.

3. Why we do not have canary snapshots today

The design is documented in the senior plan as a target architecture. The reason it is not implemented is operational, not conceptual:

  • Pollutes the public npm registry. Every push to a PR publishes a package under the @canary dist-tag. The package name does not match semver expectations. For a project that values release hygiene (see Release Process § Goal: every merge to main is a release), this is a high price to pay.
  • Concurrent PRs overwrite each other. Two PRs open in parallel both publish to @canary. Whichever publishes last wins; the earlier snapshot is no longer installable. There is no stable identifier per PR without additional tooling.
  • Requires a separate Trusted Publisher slot on npm. The current Trusted Publisher is registered for publish.yml. A canary workflow cannot reuse that slot without compromising the release environment protection.
  • Single-maintainer project, zero external contributors. The benefits of canary (external testers, integration testing) are real but only kick in when the project has external consumers of its pre-merge builds. Today, @deessejs/fp has one maintainer and no non-maintainer PRs in flight. The operational cost outweighs the benefits.

When those conditions change — multiple maintainers, external contributors, or consumers wanting to validate pre-merge builds — canary snapshots become valuable enough to justify the operational cost.

4. Target workflow

This is the spec for a future canary implementation, captured so it is not lost. The current implementation is the two-channel pipeline documented in the Release Process (stable release + hotfix path).

4.1 Trigger

Canary releases are scoped to the staging branch only. Every pull request whose target branch is staging produces a canary snapshot, on every push to that PR (opened, synchronize, reopened). PRs targeting main (hotfixes) and PRs targeting dev (if it ever materializes) do not trigger canary releases.

This is the opposite of publish.yml, which fires on pull_request: closed (merge only) and only triggers on main. Canary fires on pull_request: synchronize (every push), which is a fundamentally different event class.

on:
  pull_request:
    types: [opened, synchronize, reopened]
    branches: [staging]

4.2 Steps

  1. Checkout the PR head.
  2. pnpm install --frozen-lockfile.
  3. pnpm build. The artifact is needed for the smoke test and for publishing.
  4. Smoke test on the built artifact (same shape as publish.yml).
  5. pnpm changeset version --snapshot canary. This bumps the version in package.json without committing, and without producing a CHANGELOG entry. Note: as of Changesets v2.31, --snapshot is only valid on changeset version, not on changeset publish. The next step does not pass --snapshot.
  6. pnpm changeset publish --tag canary --no-git-tag. Publishes under the canary dist-tag. No git tag is created (snapshots are not versions).
  7. Comment on the PR with the install command: pnpm add @deessejs/fp@canary.
  8. Anti-republish guard: if the same snapshot is already published, skip.

4.3 Environment and permissions

  • Environment: a separate canary GitHub environment, with no required reviewers. The snapshot is automated, not gated.
  • Permissions: id-token: write (for the Trusted Publisher slot), contents: read, pull-requests: write (to comment on the PR).
  • Trusted Publisher: a separate entry on npmjs.com, distinct from the publish.yml entry. The canary entry's Workflow filename is canary.yml.

4.4 What canary does not do

  • It does not push a git tag.
  • It does not create a GitHub Release page.
  • It does not write to CHANGELOG.md.
  • It does not trigger publish.yml (no merge required).
  • It does not post the changelog summary anywhere.

It is purely a build artifact on npm with a stable identifier per PR.

5. Known limitations

  • Public pollution: every push to every PR publishes a package on the public npm registry under @deessejs/fp@canary. This is deliberate (so external consumers can install), but the package appears in search results and on npmjs.com pages.
  • Concurrent PRs: two PRs publishing snapshots at roughly the same time collide on the canary dist-tag. The earlier snapshot is no longer installable as @canary without specifying a version. With versioned snapshots like 1.2.3-canary-<pr>-<sha>, each PR has a stable identifier, but consumers who install @canary without a version get the most recent push across all PRs.
  • Trusted Publisher slot: each Trusted Publisher entry is registered with a specific Workflow filename. The current publish.yml entry cannot be reused for canary.yml. A second entry must be registered on npmjs.com.
  • Build artifacts in npm cache: every snapshot adds a download artifact on npm. For high-volume projects, this is a non-trivial cost. For @deessejs/fp today, with one PR per week at most, the cost is negligible.

6. Migration to pkg.pr.new

pkg.pr.new is a better tool for canary distribution than a custom workflow. It handles concurrent PRs natively (each PR gets its own URL like https://pkg.pr.new/owner/repo@sha), and it does not pollute the npm registry.

Status as of 2026-08: pkg.pr.new remains the standard pattern for pre-merge previews in the JS/TS ecosystem. The project moved from Vercel to stackblitz-labs in 2024, has 1880+ GitHub stars, is sponsored by Cloudflare for its data infrastructure, and is actively maintained. Users include Storybook, GraphiQL, Playwright, and others.

If canary snapshots are ever implemented, the right path is likely:

  1. Install pkg.pr.new as a GitHub Action in .github/workflows/canary.yml.
  2. Configure it to publish on every push to a PR.
  3. Optionally configure a fallback comment with the pkg.pr.new URL.
  4. Skip the npm-publish path entirely.

This avoids the public pollution concern and the Trusted Publisher slot concern. The only cost is depending on the pkg.pr.new service availability (Cloudflare-backed).

7. When to revisit this decision

Re-open the canary question when any of the following becomes true:

  • The project has external contributors (PRs from non-maintainers).
  • Two or more maintainers are actively reviewing PRs.
  • A downstream consumer of @deessejs/fp (another package, an internal app) requests pre-merge builds to test against.
  • The maintainer wants to release preview versions for stakeholder demos.

Until one of these triggers fires, the simpler two-channel pipeline (stable release

  • hotfix path) is enough.

8. Related documents

Clone this wiki locally