Skip to content
Maintainer reference
1.0.0-beta.9 · exact release

npm publishing

Source fda16de1b352 · Published v1.0.0-beta.9

Pulse publishes the exact tarballs produced by pnpm release:pack. Publication does not run recursively from workspace directories and does not repack a package after acceptance.

The release path is:

prepared release PR into main
→ Release qualification: build, acceptance and retained docs candidate
→ review, verified merge/squash/linear rebase and exact v<version> tag
→ operation=publish: automatic qualified artifact discovery
→ blocking read-only name audit and publication plan
→ protected human approval
→ npm trusted publishing
→ registry integrity and configured dist-tag verification
→ clean published-CLI and context-app smoke tests

The machine-readable publication contract is the publication object in release/pulse-release-manifest.json. The production workflow is .github/workflows/npm-publish.yml.

Run Release preparation from main with the next version (without v). It starts from latest, reconciles main, applies the explicit documentation history choice, prepares the version metadata, validates the result, and opens a draft release PR into main.

Terminal window
gh workflow run release-prepare.yml --ref main -f version="$NEXT_VERSION" -f documentation_history=archive-current

Set NEXT_VERSION to the release you intend to prepare and write its notes in CHANGELOG.md under Unreleased first. Preparation moves those notes into the new version section and preserves published changelog history. Existing archives are compared with the tagged source and never silently overwritten.

The default archive-current rebuilds the previous documentation snapshot from its exact tag. For the reviewed npm-published release whose hosted docs are not a recovery target, deliberately select documentation_history=replace-unpublished-docs. That choice skips snapshot construction and checks the preserved tag commit against readiness.versionPreparation.unpublishedDocumentationReleases in the release manifest. Unlisted versions fail closed. It removes only the old current entry from the next hosted docs list; npm artifacts, Git tags, and published changelog sections remain intact. The draft PR records the choice and evidence.

Preparation runs with read-only repository permissions. A separate job pushes the prepared Git bundle to a new branch and creates a draft PR; it does not execute the prepared source with its write token. The repository must allow GitHub Actions to create pull requests. No additional credential is required. If PR creation is disabled, the job reports the prepared branch for manual PR creation rather than changing repository settings.

Mark the draft ready for review to trigger the ordinary PR checks. GitHub does not trigger those workflows for PR creation using GITHUB_TOKEN; the human ready-for-review event starts them without another credential. Existing checks include release preparation inside Repository validation / maintenance: publishable code changes into main require a newer, synchronized version, archived previous documentation (or its reviewed unpublished-docs exception), and a changelog section. Documentation and workflow-only changes can retain the version. The check is preparation evidence, not a release seal or permission to publish.

For a preparation PR targeting latest, merge the reviewed changes there and then manually merge the release into main. The tagging helper works only once HEAD matches the current remote main commit and the checkout is clean:

Terminal window
git fetch origin main --tags
git switch main
git pull --ff-only origin main
npm run release:tag -- 1.0.0-beta.9
npm run release:tag -- 1.0.0-beta.9 --write

The first invocation checks and prints the plan. --write creates an annotated local v1.0.0-beta.9 tag and prints the exact push and workflow commands for the release owner. It never pushes, dispatches publication, claims a seal, or replaces an existing tag. A matching tag is idempotent; conflicting commits, lightweight tags and differing local/remote tag objects fail closed. Run those printed commands only after review. The PR qualification retains the accepted package and docs bytes before merge. The tagged consumer verifies a separate binding to the reviewed merge commit.

After review and merge, the release owner tags the final main commit and runs npm publication from that tag with operation=publish. Automatic discovery resolves its merged release PR and current successful qualification, then verifies the package and docs manifests, original source, seal and merge binding. A fresh read-only audit must pass before protected publishing approval. Audit and publication never build, pack or seal. A local seal remains development evidence. Reconcile main back into latest after release.

Ready release PRs into main now run Release qualification / qualification. The credential-free workflow checks the exact PR merge commit and its base/head parents, release version, package catalog, documentation archive and changelog before installing tools. It checks the live PR and main at both ends. A changed head, base or merge candidate requires a fresh run. Drafts do not qualify; a same-version documentation/workflow PR returns an explicit non-release result without running the expensive seal. Product changes without a new version fail.

For a release-control repair before publication, add exactly one standalone Release qualification: required line to the PR body before opening, reopening or synchronizing the PR. This opts a same-version PR into the complete seal and artifact capture; it never bypasses version-preparation checks or allows replacing published bytes. An ordinary body edit does not start qualification. The request is checked again at the end, so changing it during a run invalidates that run. Qualify the final repair tree and tag its reviewed merge result. An earlier seal cannot be relabeled to include the repair, and historical tags keep their original workflow code.

The controller runs one fresh source-checks stage after dependency setup and before compilation: maintainer and publication controls, package/catalog and source documentation validation, generated snippets/references and the site. It seals and retains that validated site before removing its temporary build; there is no second docs build for capture or deployment. Packing verifies this attempt's pinned result against the same clean source, Node and dependency bytes. Standalone packing still performs its own validation. Build, audit/license refresh, workspace tests, all release tasks, installed feature gates, required Fastly reality and cleanup retain their coverage.

Successful release qualification uploads pulse-pr-release-qualification-<run-id>-<attempt> for 30 days. It contains the accepted publication bundle, prebuilt documentation candidate, original seal report and qualification.json with PR/base/head/merge/tree identity, run/attempt, package/docs manifest and seal hashes, the full qualification-context digest, and separate feature-gate status. The full seal and recovery evidence is retained in the matching evidence artifact. Qualification is not permission to publish and does not waive dedicated K4 evidence or review.

The binding contract supports a reviewed merge commit or a single squash commit whose first parent is the qualified base and whose tree is the qualified merge tree. A two-parent merge also requires the qualified PR head as its second parent. It also supports a linear rebase when both the original PR chain and the rewritten chain start at that exact base, have the same number of commits (at most 1000), and preserve every intermediate tree in order. Commit metadata and IDs may change; extra, dropped, reordered or content-modified commits fail. A one-commit rebase is verified by the same direct-parent rule as a squash. Consumers fetch the exact authenticated original PR head if it is absent from the tagged checkout, without checking out or executing it. The merged PR and tag must identify that exact commit. The original source in reports and bundles stays unchanged; a consumer records a separate merge binding. A different base, tree, head or tag fails even when some file contents match. Rebases of merge-heavy PR histories and merge queues remain outside this contract.

Both tagged consumers resolve the merged PR automatically. The latest run and its current attempt must pass; an older success never masks a newer failure. The run must belong to this repository and release-qualify.yml, with both candidate and qualification jobs passing and one unexpired accepted artifact. Selection records run/attempt/job/artifact IDs and the Actions digest. After download and again after protected approval, the consumer rechecks eligibility and verifies both candidates against the original receipt. qualification-binding.json records the original qualified source separately from the merged/tagged authority. The dispatch and checkout must match the exact release tag. Missing, ambiguous, expired, superseded, failed or mismatched evidence blocks consumption; there is no post-main rebuild fallback. Historical tags retain their historical workflows.

The release owner must add the new check to the main ruleset and require the branch to be up to date before merging. A base push after the last live-source check must invalidate merge eligibility. No settings are changed by this patch. If an existing PR is retargeted to main, reopen or synchronize it to start this PR-only workflow; editing a title/body does not repeat a long qualification.

Copy this compact record for each exact tag. Fill the status and evidence link for every row; use pending, passed, failed, or blocked. Record the tested commit and terminal report, not merely a dispatched run. Keep a failed attempt visible when retrying. promote_latest=false deliberately stops after immutable verification; the npm gate and alias steps remain blocked until a separate, approved promotion run.

Tag: v<version> Tag commit: <40-character SHA> Release PR: <URL>
Step Status Evidence / run, report, or blocker
PR checks ____ <full main PR checks at merge SHA>
Release seal ____ <qualify run ID / terminal seal report>
npm publish ____ <protected publish run / exact package receipts>
Docs candidate ____ <qualified docs candidate / manifest digest>
Immutable upload ____ <deploy + verify-storage reports / object count>
npm gate ____ <registry-catalog verification / package count>
Alias promotion ____ <root/latest deployment + storage verification>
Public verification ____ <Fastly route report / exact and moving URLs>
Last failure: <step, code, object key or none, mismatch class, run attempt>
Human release owner: <name> Next action: <one concrete action>

After the human has reviewed and pushed the annotated tag, select that tag as the workflow dispatch ref and supply the identical release_tag input:

Terminal window
RELEASE_TAG=v<version>
gh workflow run npm-publish.yml --ref "$RELEASE_TAG" -f release_tag="$RELEASE_TAG" -f operation=publish -f run_smoke=true

Publication selects and downloads the PR qualification by immutable artifact ID. The operator supplies only the release tag and operation; run IDs remain machine provenance. The review packet includes the merge binding and manifest digest.

Review the terminal seal, dedicated KV/CAS evidence/disposition and audit/plan before approving npm-publish. The audit report is retained per publication attempt, including failures. Its manifest digest binds publication to the reviewed bundle; publication rechecks live registry state before uploading. A missing name or integrity/dist-tag conflict blocks approval. Owner-reviewed trusted-publisher and protected-environment setup remains a prerequisite.

Qualification uses Ubuntu 24.04, two isolated workers, one compiler slot and a 6,144 MiB admission budget. Its work deadline is 90 minutes, with additional step/job time for supervised cleanup and evidence upload. This budget permits completion; it is not a performance improvement. Same-checkout recovery rules remain unchanged. Fastly CLI 16.1.0 is checksum-pinned and its real local Compute lifecycle is mandatory. Accepted package checkpoints and artifact hashes are verified before copying bytes; there is no post-acceptance rebuild or repack.

The accepted bundle is retained for 30 days. operation=audit performs informational package-name/conflict checks without protected publishing. Retry failed publication jobs or dispatch publish again from the same tag; eligibility is checked again and only matching registry bytes are skipped. Expired/deleted evidence blocks retries, including partial releases. Preserve the original qualification artifact through completion; a partial failure report cannot substitute for it and the workflow will not reconstruct lost bytes.

Documentation can be uploaded and verified without promotion while npm awaits approval. After npm verification and human deployment approval, promote from the same tag:

Terminal window
RELEASE_TAG=v<version>
gh workflow run documentation-deploy.yml --ref "$RELEASE_TAG" -f release_tag="$RELEASE_TAG" -f promote_latest=false
# After npm verification and review:
gh workflow run documentation-deploy.yml --ref "$RELEASE_TAG" -f release_tag="$RELEASE_TAG" -f promote_latest=true

The tagged workflow's candidate job has no production credentials; its protected publish/deploy jobs still require human approval. An immutable documentation failure never authorizes replacing an existing key. Inspect documentation-immutable-deployment.json (or the corresponding verification report) for failure.code, failure.objectKey, and failure.mismatch before choosing a remedy. bytes, content-type, and cache-control distinguish the conflict classes. If failure occurs before an object is selected, objectKey is null. Failed reports are retained by the workflow's evidence upload on tags that contain this reporting change. Do not combine artifacts from different tags or run attempts into one passing record.

For local preparation, the underlying command remains available:

Terminal window
node scripts/release-prepare.cjs "$NEXT_VERSION" --channel beta --archive-current

--archive-current requires the committed immutable documentation snapshot. Use --replace-unpublished only when replacing a candidate that has not shipped. For the reviewed hosted-docs exception, use --replace-unpublished-docs instead: the old release shipped on npm and its changelog must be preserved. See documentation versioning for exact-link behavior and the audit evidence. The command updates catalogued metadata and current documentation, runs named generators, rejects newly changed paths outside its allowlist, and writes an ignored stale-version-token report. Neither preparation route creates or moves tags, publishes packages, or deploys documentation. If a tag was created before the version bump, prepare and merge the metadata first; the release owner must then correct the unpublished tag before starting publication.

The workflow is manually dispatched from the exact release tag, and the release_tag input must name that same tag. Publication tooling, GitHub's native source identity and npm provenance refer to the tagged commit. The artifact retains its original PR merge source, linked through the verified merge binding. A branch-dispatched run is rejected. The audit job has no publication authority. The publish job is attached to the protected npm-publish environment and is the only job granted id-token: write.

For example:

Terminal window
gh workflow run npm-publish.yml \
--ref v1.0.0-beta.9 \
-f release_tag=v1.0.0-beta.9 \
-f operation=publish -f run_smoke=true

Complete external package settings before dispatch. Publication performs the blocking audit before protected approval; a separate audit dispatch is optional.

A human release authority must approve that environment. Codex may inspect failures and prepare bounded patches, but it may not dispatch the release, approve the environment, publish a package, change a dist-tag, or rotate registry credentials.

Pulse uses npm trusted publishing through GitHub Actions OIDC. No NPM_TOKEN or NODE_AUTH_TOKEN is provided. Published packages support ^22.14.0 || ^24.0.0; local development supports pnpm ^12.4.2. Release packing, CI, and the portable dependency bundle select the exact pnpm version in the release manifest (currently 12.4.2). The development range is a compatibility range, not a patch pin; release validators require the exact version to satisfy it. To install the release toolchain and workspace dependencies without lifecycle scripts:

Terminal window
node scripts/pnpm-toolchain.cjs --install
node scripts/pnpm-toolchain.cjs -- install --frozen-lockfile --ignore-scripts

Version 12 of pnpm supplies a native executable. The bootstrap installs the exact npm distribution with lifecycle scripts disabled, verifies its native executable version, and carries that executable in .validation-tools/pnpm/bin. Bundle creation verifies the lockfile against registry supply-chain policies and records lockfileVerification with its SHA-256. Offline restore first validates that hash and the exact toolchain/platform, then uses pnpm’s trust-lockfile mode for the already-verified lockfile; package integrity checks remain enabled. This avoids requiring uncached registry/provenance metadata offline. Normal online installs and CI keep full verification enabled. Release commands use the bundled runner directly. Rebuild dependency bundles when changing the manifest pin; restore rejects a stale toolchain before replacing dependencies. Older release-tag documentation rebuilds retain their original pnpm version.

Local release seals accept Node ^24.0.0 and record the exact patch used. The protected publication workflow remains reproducibly pinned to Node 24.18.0 and npm 11.15.0, and scripts/release-publication.cjs rejects a different Node patch, a different npm version, or known long-lived npm credential variables in that job.

Every manifest-owned npm package setting must authorize exactly:

provider: GitHub Actions
owner: pulse-compute
repository: pulse-compute/pulse
workflow: npm-publish.yml
environment: npm-publish

In npm's form, enter organization/user pulse-compute, repository pulse, workflow filename npm-publish.yml (without .github/workflows/), and environment npm-publish. Explicitly allow direct npm publish in Allowed actions. Current new configurations allow staging by default; staging alone does not authorize this direct-publication workflow. Separate dist-tag management permission is not needed for npm publish --tag latest. Confirm every existing publisher's identity and direct-publication permission; preserve valid settings instead of recreating them. See npm trusted publishers.

The repository owner must also confirm the actual GitHub npm-publish environment: tag-only deployment rules, a human release-authority reviewer, and no long-lived npm publish credential. Inspect rules of type Tag; "Protected branches only" is not a release-tag restriction. Record the selected tag pattern and reviewer, including the existing self-review/bypass choices, without inferring those settings from the checked-in workflow. Revoke/remove temporary bootstrap credentials after setup; preserve human account recovery and 2FA access. Registry name audits do not inspect these private settings.

Retain a dated owner-reviewed settings receipt for the manifest's complete package set and environment. Saving a trusted publisher is setup evidence, not successful OIDC publication: the later protected exact-tag job supplies that proof. Do not publish a throwaway version just to test setup. The release record keeps settings review separate from publication receipts.

The workflow publishes public packages directly under the dist-tag in the release manifest, now explicitly latest for future releases. The release channel and prerelease version remain Beta; the npm tag determines what an unqualified npm install selects. Release preparation preserves this tag policy across version bumps. An explicitly configured channel tag remains supported for a release intended for a separate preview stream.

The pinned npm CLI's trusted publishing does not support a separate post-publication npm dist-tag update. Each successful npm publish --tag latest therefore advances that package's default version as part of publication; the historical beta alias is not also advanced. The package set is published in dependency order, with the public CLI last, and promotion is not atomic across packages. The protected script verifies the native GitHub tag ref and SHA against the sealed candidate before the first publish.

This policy applies to future candidate source. Finish an already-started release using its original tag, tooling, and sealed tarballs. Do not move that tag or rebuild the same published version to adopt a new publication policy.

An npm trusted publisher can be configured only after the package name exists under the intended owner. Before enabling production publication, run:

Terminal window
npm run release:audit-npm

The command derives every package name from the release manifest and writes a timestamped, manifest-digest-bound report to .pulse-release-preflight/npm-catalog-audit.json. That report is ignored source evidence: it never rewrites the canonical preflight policy and it performs no registry mutation. Every name must exist with the inert 0.0.0 version and bootstrap tag. The latest tag may be absent, point to that placeholder, or point to a version both present in the registry and listed in release/documentation-versions.json or in the release manifest’s reviewed unpublishedDocumentationReleases decisions. This preserves existing releases during the next bootstrap audit. Missing versions and unknown release targets remain blocking; any remediation is an explicit human registry action.

Any missing package name requires a one-time human, 2FA-protected bootstrap publication through scripts/npm_bootstrap.sh <package-name>. After all names exist:

  1. configure the trusted publisher on every package;
  2. select the exact release tag as the manual workflow ref;
  3. confirm OIDC publication succeeds;
  4. remove any temporary bootstrap credential; and
  5. prohibit ordinary publish tokens for routine releases.

The production workflow fails rather than silently performing first publication with a broader credential.

The package-name audit and one-time bootstrap procedure above apply to every release package, including @pulse-compute/mcp. A past registry observation is not evidence of current readiness.

The single mcp-installed release-feature gate qualifies the exact CLI-shipped context app using the standard candidate tarballs, production Node composition, official client 2.2.0 and Codex 0.160.1. It records source/tree and oracle hashes, installed package tarball/file hashes, extracted app files, build/catalog/schema/ corpus identities, terminal cleanup, startup time, response sizes and elapsed commands. A starter supplied through MCP is tested/built locally by the acceptance client. Scope/write-path negatives remain in the focused mcp-authorization fixture. Do not add this installed gate to fast PR or aggregate release profiles, or require a full seal for routine adapter changes.

After publication/registry integrity verification, the workflow runs the existing narrow context smoke using the selected release manifest version. It is required even when run_smoke=false skips the separate CLI journey; a normal release selects run_smoke=true to execute both. For a manual replay, use the checkout for the exact published release:

Terminal window
node wasm/test/mcp/smoke-context-registry.cjs 1.0.0-beta.9

This smoke installs the exact published CLI into a fresh temporary directory, copies its bundled context example, installs exact dependencies with lifecycle scripts disabled, builds/prepares the app and starts its production host. It checks discovery, five-tool listing, a starter response's version/corpus identity and graceful shutdown. It retains registry URLs/integrities, package/build/corpus identities, cleanup, startup/response/elapsed measurements in wasm/.test-results/context-registry-smoke.json. It does not repack, publish, deploy or replace final release qualification. Missing versions fail; no candidate or floating-version fallback is used.

The verification job requires a terminal passing report for the selected version and passing cleanup. It retains the context report with registry and CLI reports on success or failure. A failed smoke, missing report or failed cleanup leaves verification incomplete. Retry verification in the same publication run against the original bundle; no new candidate seal is required.

Keep installed-candidate acceptance, package bootstrap/publisher readiness, publication integrity and the published context smoke separate in the release handoff. None establishes the others. Timings are observations, not latency thresholds.

After pnpm release:pack, prepare the candidate:

Terminal window
npm run release:candidate
npm run release:verify-bundle

.pulse-publication/pulse-publication-manifest.json records:

  • release version, tag, channel, registry, workflow, and protected environment;
  • source commit and ref;
  • the exact publication order;
  • every tarball path, byte length, SHA-256, and npm SHA-512 integrity;
  • Pulse package dependencies used to derive the topological order; and
  • checksums for the source catalog and packed-release manifest.

The bundle includes the exact tarballs. GitHub artifact transfer does not create a second npm package payload.

Publication orders internal dependencies and optionalDependencies before dependants. Peer dependencies are validated by release packing but do not introduce an ordering edge. Stable role/name ordering makes retries deterministic.

For each package:

version absent
→ publish the sealed tarball with --access public --tag <release dist-tag>
version present with identical SHA-512 integrity
→ record already-published and continue
version present with different integrity
→ stop with a critical immutable-version conflict

This is resumable after a partially completed synchronized-package release without pretending npm publication is transactional. A same-version integrity mismatch cannot be repaired by overwriting the registry artifact; the human release authority must investigate and choose a new version if necessary.

npm can accept an upload while the version is still being processed. The script uploads the remaining tarballs in dependency order, with the CLI last, without waiting for each package's registry visibility. It then polls the pending set under one shared ten-minute deadline for both sealed integrity and configured tag. Each round reads pending packages in order and pauses once, with intervals capped at 30 seconds; ready packages leave the polling set. Registry processing can overlap even though uploads and registry reads remain sequential.

Progress goes to stderr and identifies each package, attempt, elapsed time, and whether the version or tag is still pending. An observed integrity conflict stops immediately. A timeout reports the unresolved packages. Upload order is not a registry visibility guarantee: dependants may become visible before their dependencies, and the release remains incomplete until full verification passes.

If processing exceeds the deadline, wait for registry visibility and use Re-run failed jobs on the same workflow run. Keep the release tag and sealed candidate unchanged: matching published packages are skipped, and publication continues with the remaining packages. No new seal is required for that retry. If source changes and a tag is moved before any publication, start a new workflow dispatch instead; GitHub reruns retain the original event's commit SHA.

The protected publish job waits for the uploaded set and verifies the complete release. A separate unprivileged job then confirms:

  • every manifest-owned name@version record exists;
  • registry dist.integrity equals the sealed tarball integrity;
  • the configured dist-tag points to the release version;
  • with run_smoke=true, the published canonical CLI installs in a clean prefix and completes version, init, install, doctor, test, and Node build smoke checks; and
  • the exact published CLI's bundled context example builds and starts, discovers its five tools, returns matching starter metadata and shuts down cleanly.

The registry report uses schema pulse.npm-publication-verification.v1. The documentation deployment accepts that schema as evidence that matching packages are publicly available before it promotes root and latest.

These commands do not publish unless the explicit publish command is used from the protected GitHub environment:

Terminal window
pnpm release:pack
npm run release:candidate
npm run release:verify-bundle
npm run publication:check

Actual publication remains a human-approved workflow action, not a normal local maintenance command. The CLI does not expose a non-GitHub publication bypass; production publication requires the trusted GitHub/OIDC environment. The release validator exercises planning and failure behavior with in-process registry fixtures and never invokes npm publish.