Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ name: Release
# conclusion — asserts every job above SUCCEEDED (skipped ≠ success).
#
# Trigger: push a version tag, e.g.
# pnpm --filter "@fundamental-engine/*" exec npm version 0.2.4 --no-git-tag-version # bump the seven together
# pnpm --filter "./packages/*" exec npm version 0.2.4 --no-git-tag-version # bump the seven together
# git commit -am "release: 0.2.4" && git tag v0.2.4 && git push --tags
#
# Rehearsal: workflow_dispatch with dry_run=true runs the entire path — gate, version
Expand Down Expand Up @@ -153,7 +153,7 @@ jobs:
# longer requires a manual re-run. A genuine failure still exits non-zero after the last attempt.
run: |
attempt=1; max=3
until pnpm --filter "@fundamental-engine/*" publish --access public --no-git-checks --provenance ${{ inputs.dry_run && '--dry-run' || '' }}; do
until pnpm --filter "./packages/*" publish --access public --no-git-checks --provenance ${{ inputs.dry_run && '--dry-run' || '' }}; do
if [ "$attempt" -ge "$max" ]; then
echo "::error::publish failed after $max attempts"; exit 1
fi
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ a git tag (see [RELEASING.md](RELEASING.md)).

## [Unreleased]

- **Release tooling: scope the version bump to `./packages/*`, not the `@fundamental-engine/*` name glob.** The glob also matched the private workspace apps (`apps/site`, `apps/starter`, `apps/observatory`), which share the npm scope but version independently — and `exec npm version` ignores the `private` flag, so it silently bumped them to the release version (0.10.0 caught this: observatory jumped `0.0.0 → 0.10.0` before it was reverted by hand). The path filter targets exactly the seven publishable packages, so no private app is bumped and the old "revert the apps afterward" step is gone. `pnpm publish` already skipped private packages, so only the version bump was ever affected. Fixed in `RELEASING.md`, `PUBLISHING.md`, and `release.yml`.

## [0.10.0] — 2026-07-21

The reproducibility envelope ships, and cross-plane publishing is complete: **one `git tag v0.10.0`
Expand Down
6 changes: 3 additions & 3 deletions PUBLISHING.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ git tag -a vX.Y.Z -m "Release X.Y.Z" && git push origin vX.Y.Z
```

This triggers [`.github/workflows/release.yml`](.github/workflows/release.yml): full gate →
`pnpm --filter "@fundamental-engine/*" publish --access public --no-git-checks --provenance`. A failed publish can
`pnpm --filter "./packages/*" publish --access public --no-git-checks --provenance`. A failed publish can
be retried without re-tagging: `gh run rerun <run-id> --failed`.

### Prerequisites (all currently satisfied)
Expand All @@ -50,15 +50,15 @@ Only if CI is unavailable. This publishes **without** provenance and needs an OT

```sh
pnpm -r build && pnpm test && pnpm check:dist && pnpm check:api # gate
pnpm --filter "@fundamental-engine/*" publish --access public --no-git-checks --otp=<code>
pnpm --filter "./packages/*" publish --access public --no-git-checks --otp=<code>
```

## Versioning

All seven published packages are versioned together (currently `0.10.0`). Bump them as one:

```sh
pnpm --filter "@fundamental-engine/*" exec npm version <patch|minor|major> --no-git-tag-version
pnpm --filter "./packages/*" exec npm version <patch|minor|major> --no-git-tag-version
```

Per the `0.x` rules in [API stability](docs/canonical/api-stability.md), a breaking change to a
Expand Down
21 changes: 10 additions & 11 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,16 @@ accidental breakage, not a freeze; the surface evolves pre-1.0 and additions nev
[Keep a Changelog](https://keepachangelog.com).
3. **Bump all seven packages together** (keep them at the same version):
```sh
pnpm --filter "@fundamental-engine/*" exec npm version <patch|minor|major> --no-git-tag-version
pnpm --filter "./packages/*" exec npm version <patch|minor|major> --no-git-tag-version

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require repo-root execution for path filters

When this new path selector is run from any subdirectory (for example packages/core while cutting a release), pnpm interprets ./packages/* relative to the current working directory; pnpm help publish describes --filter ./<dir> as selecting packages inside that subdirectory, and --fail-if-no-match is the separate opt-in for failing on no matches. I verified with pnpm 10.28.1 that from packages/core this selector prints No projects matched and exits 0, so the version-bump step can silently skip all seven packages; the old name glob was cwd-independent. Please either state that these commands must be run from the repo root, add --fail-if-no-match, or use exact package-name filters.

Useful? React with 👍 / 👎.

```
The filter also matches the **private** workspace apps, which version independently and are
never published — revert them after the bump:
```sh
git checkout -- apps/site/package.json apps/starter/package.json apps/observatory/package.json
```
This list grows whenever a private app is added under `apps/` with an `@fundamental-engine/*`
name. `apps/observatory` joined in 0.9.5. Confirm with
`pnpm --filter "@fundamental-engine/*" exec node -e "const p=require('./package.json');console.log(p.name,p.private?'private':'PUBLISHED')"`
rather than trusting this list.

> **Scope the bump to `./packages/*`, never `@fundamental-engine/*`.** The name glob also matches the
> private apps (`apps/site`, `apps/starter`, `apps/observatory`) — same npm scope, independent versions.
> `exec npm version` ignores the `private` flag, so the glob silently bumps them to the release version.
> (`pnpm publish` already skips private packages, so only the version bump was ever affected.)
The path filter targets exactly the seven publishable packages — no private app is bumped, so
there is nothing to revert. Confirm the set with
`pnpm --filter "./packages/*" exec node -e "const p=require('./package.json');console.log(p.name,p.private?'private':'PUBLISHED')"`.
4. **Bump `FIELD_VERSION` on all three planes** — the constant is hand-maintained per plane, and a
lockstep test on each fails CI if it drifts from `packages/core/package.json`:
- JS: `packages/core/src/version.ts` (guard: `version.test.ts`)
Expand Down Expand Up @@ -107,7 +106,7 @@ the gates above.
used, log it as an exception in the CHANGELOG entry for that release — "published
without provenance" is a recorded fact, not a silent degradation.
- **Never bump one package alone.** Always
`pnpm --filter "@fundamental-engine/*" exec npm version <bump> --no-git-tag-version` — the release
`pnpm --filter "./packages/*" exec npm version <bump> --no-git-tag-version` — the release
gate fails the tag if any of the seven is out of step.
- **Never widen a failing gate to make it pass.** If `check:api` fails, the public
contract changed: fix the change or cut a deliberate 0.MINOR with a migration note —
Expand Down