This repository publishes multiple workspace packages to npm. To keep releases predictable for external consumers, publishing is gated by metadata and artifact validation checks.
The monorepo uses Changesets fixed versioning for all publishable
@pie-players/* packages:
- all publishable packages move in a lockstep release train
- all publishable packages share one version at publish time
- source manifests keep internal references as
workspace:* - publish rewrites workspace refs to concrete versions, then restores manifests
Publishable packages in this repo form a single cohesive player framework (players, tools, TTS servers, theming, toolkits). Internal contracts cross package boundaries — element registration, theme tokens, tool coordination, session shape — so consumers almost always adopt the suite as a whole.
Fixed versioning gives consumers two guarantees:
- One version per upgrade. Pick a version, bump every
@pie-players/*dependency to it. There is no compatibility matrix to reason about across@pie-players/*packages. - Tested together. Packages that publish at the same version are designed and tested as a unit at that version.
The cost is that releases bump every publishable package — including ones whose source did not change in that release — so some churn is unavoidable on every release PR. This is expected and enforced.
- Every release/versioning step must cover all publishable packages. Do not
prepare a release bump scoped to only the changed packages; that would break
the lockstep invariant. See
.cursor/rules/release-version-alignment.mdc. - A breaking change in any publishable package forces a major bump across every publishable package. Plan breaking changes with that in mind, or consider whether the change can be introduced additively first.
- Changesets'
fixedblock in../../.changeset/config.jsonis the source of truth for which packages are in the lockstep set. New publishable packages must be added there. scripts/check-fixed-versioning.mjs(run viabun run verify:publish) is the invariant check that fails CI if versions drift.
For every non-private workspace package in packages/*:
publishConfig.accessmust bepubliclicensemust be presenthomepagemust be presentbugsmust be present (stringURL or object withurl)repository.directorymust match workspace locationfilesmust be present and non-emptyexportsor (main+types) must be presentengines.nodemust be presentsideEffectsmust be explicitly set
Policy and validator:
scripts/publish-policy.jsonscripts/check-package-metadata.mjsdocs/setup/publishable_packages.md(current package inventory)
Run:
bun run verify:publishverify:publish executes:
- package build
- fixed-versioning invariants (
scripts/check-fixed-versioning.mjs) - metadata policy validation
- custom-element contract checks (
check:custom-elements,check:ce-define-safety) publintpackage surface checks- ATTW type-surface checks (
scripts/check-attw.mjs) - pack exports check (
npm pack --dry-run+ export target verification) - pack smoke check (
npm packtarball verification) - Node consumer import boundary checks (
scripts/check-node-consumer-imports.mjs) - dependency, source export policy, and runtime boundary checks
The release workflow enforces explicit intent:
- Push-driven runs rely on release evidence:
.changeset/*.mdfiles for release PR creation- package/changelog version bumps for publish runs
- Manual runs (
workflow_dispatch) requirerelease_intent:version-pr(requires changesets)publish(requires version bump/changelog evidence by default)publish+force_publish=true(manual recovery mode for rerunning a failed publish frommaster)
If a publish failed for transient reasons (registry outage, webhook issue, etc.) and
your fixes are already in master, rerun the release workflow manually:
- Open Actions → Release → Run workflow
- Branch:
master release_intent:publishforce_publish:true
This bypasses version-bump detection checks for that manual run while keeping normal push-driven safety checks in place.
Publish-path runs execute the full bun run verify:publish gate before
changesets/action can publish.
After publish, CI also validates internal dependency closure in the registry:
scripts/check-published-closure.mjs- confirms published
@pie-players/*packages only reference resolvable internal versions - fails if any
workspace:*leak or unresolved internal dependency is detected
- Metadata failures: update package
package.jsonfields listed in the error. publintfailures: alignexports,types, and packed files with published entry points.- ATTW failures: fix type entrypoints/resolution issues or move package to the documented exclusion set with rationale until remediated.
- Pack export/smoke failures: ensure all declared targets are included in
filesand produced by build output. - Fixed-versioning failures:
- ensure all publishable package versions are identical after
bun run version - ensure internal
@pie-players/*deps remainworkspace:*in source manifests
- ensure all publishable package versions are identical after
If bun run release:with-version fails after bun run version has already updated
package.json and CHANGELOG.md files, do not rerun release:with-version.
Rerunning it creates another temporary changeset and bumps versions again.
Retry from the post-version steps instead:
bun run check:npm-auth && SKIP_NPM_VERSION_SEQUENCE_CHECK=1 bun run verify:publish && bun run test && bun run release && bun run restore:workspace-rangesUse SKIP_NPM_VERSION_SEQUENCE_CHECK=1 for recovery runs when
check-fixed-versioning fails with npm E404 for a package being published for
the first time (for example npm view @pie-players/<pkg> version returning not
found).
The canonical local-publish command is:
bun run release:with-versionrelease:with-version runs the entire CI release path locally, in order:
scripts/create-temporary-release-changeset.mjs— writes a temporary.changeset/temporary-release-all-packages.mddeclaringpatchfor every publishable package, so the lockstep set is always covered (existing author-written changesets coexist with this temporary one and may upgrade the bump for some / all packages).bun run version— applies all changesets topackage.jsonandCHANGELOG.mdfiles.bun run restore:workspace-ranges— keeps source manifests onworkspace:*afterversion.bun run check:npm-auth— fails fast if the NPM token in.envis missing/expired or@pie-playersaccess is unavailable.bun run verify:publish— full publish gate (build + everycheck:*).bun run test— workspace test suites.bun run release—dotenvx run -f .envwrapper around build +changeset publish(with workspace ranges resolved) + preloaded-player bundle publish.bun run restore:workspace-ranges— restoreworkspace:*ranges in source manifests.
NPM authentication: the repo's .env file holds the NPM_TOKEN for
@pie-players publish access. Both check:npm-auth and release load it via
dotenvx run -f .env. No separate npm login is needed.
If you hit errors like:
npm notice Access token expired or revokedE404 Not Found - PUT https://registry.npmjs.org/@pie-players%2f...
verify the token in .env is still valid (or re-auth and update .env):
npm whoami --registry=https://registry.npmjs.org/
npm org ls pie-players --registry=https://registry.npmjs.org/