Skip to content

MILAB-6648: require a changeset for every edited package, in one gate - #198

Draft
AStaroverov wants to merge 2 commits into
v4from
MILAB-6707_changeset-coverage-gate
Draft

MILAB-6648: require a changeset for every edited package, in one gate#198
AStaroverov wants to merge 2 commits into
v4from
MILAB-6707_changeset-coverage-gate

Conversation

@AStaroverov

@AStaroverov AStaroverov commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

What

require-package-path-bump now enforces both halves of the release contract under a single check
(check for changesets):

  1. require-package-bump — the package at package-path must be bumped directly, on every PR,
    whether or not its own files changed.
  2. check-coverage — every other workspace package whose own files the PR edits must be
    bumped too.

The separate always-green changeset coverage (diagnostic) job is gone; coverage now runs as a
second step inside check for changesets. No new input, no new check name.

Why

The coverage check already existed, but ran with continue-on-error: true and an explicit
"keep this job out of required checks" note. A red result cost nothing and nobody had to act on it,
so a PR could edit a package and merge with that package shipping new content under an unchanged
version.

Why both halves rather than one

Neither half implies the other, so replacing the first with the second would have weakened the
gate:

  • require-package-bump ignores the diff — it demands a package-path bump on every PR.
  • check-coverage ignores untouched packages, and skips private ones.

In a block repo only block is published; model / ui / workflow / test are private: true.
A PR editing only ui/ is therefore invisible to coverage, while require-package-bump correctly
fails it. Both steps run and both must pass.

Half 2 only starts to bite in a repo with more than one publishable package.

The second step runs under !cancelled(), so one red check reports the complete set of missing
bumps instead of one per run.

Empty changeset

pnpm changeset --empty waived require-package-bump but not coverage, so an author declaring
"no release needed" was passed by one half and failed by the other. check-coverage.sh now honours
the same opt-out, waiving every missing package at once.

Scoped to changesets added in the branch: an empty changeset inherited from the base branch must
not disable the gate for every branch cut after it.

The rule is duplicated rather than extracted into a shared file — there is no bash-sharing
precedent between actions in this repo (lib/ holds TypeScript packages), and refactoring the
working, tested require-package-bump.sh was not worth the risk. Both copies state the rule in
place and say so.

Behaviour of the coverage half

require-package-path-bump event coverage runs blocks merge
off — every consumer today any no
on pull_request yes yes
on merge_group no
on pull_request + skip-changelog label job skipped

With the toggle off, check for changesets behaves exactly as before — non-blocking on PRs,
blocking on the merge queue and push-to-default. What those repos give up is the never-actionable
diagnostic check.

changeset-coverage was in no other job's needs, so build/test and the Slack release notification
are unaffected either way.

Tests

actions/changeset/check-coverage/test/coverage.bats — six cases added: the waiver applies; it
waives several packages at once; it waives alongside a partial real changeset; an inherited empty
changeset does not waive; a non-empty changeset is not read as the opt-out; exit 2 (tooling broken)
still wins over the waiver. 21/21 pass.

require-package-bump.bats untouched, 9/9.

Not in this PR

  • Turning it on. No consumer passes require-package-path-bump yet. The structurer template
    that generates every block's build.yaml (block-tools, build.tpl.yaml) can pass it once this
    is on v4.
  • Branch protection. A failing job blocks merge only once check for changesets is a required
    check in the consuming repo's settings.
  • Canary run. Refs inside this branch are @v4, so a consumer pointed at the branch still
    resolves the nested uses: to stable v4. The new step cannot be exercised from a consumer PR
    until this merges.

Greptile Summary

This PR consolidates direct package-bump and edited-package coverage enforcement into the check for changesets gate, while allowing a branch-added empty changeset to waive both requirements.

  • Moves changeset coverage from a separate diagnostic job into the opt-in blocking job.
  • Adds branch-scoped empty-changeset waiver detection and coverage tests.
  • Updates the reusable workflow input and action documentation.

Important touched terms

  • Changeset: A .changeset/*.md release declaration naming packages and bump levels. Coverage now requires one for every edited publishable workspace package.
  • Package bump: A direct release entry for the package selected by package-path. It remains mandatory on every opted-in pull request unless the whole gate is waived.
  • Changeset coverage: The mapping between directly edited publishable workspace packages and changeset release entries. It is now enforced rather than diagnostic.
  • Empty changeset: A branch-added changeset with empty front matter, representing an explicit “no release needed” decision. It now waives all missing package bumps.
  • require-package-path-bump: The reusable-workflow opt-in flag. It now enables both direct-bump and coverage enforcement under one check.
  • skip-changelog: A pull-request label that skips the complete changeset gate at job level; its behavior remains centralized in the workflow.

Confidence Score: 5/5

The PR appears safe to merge, with the consolidated gate and empty-changeset waiver behaving consistently with the documented contract.

The changed workflow conditions preserve opt-in and event scoping, the explicit status condition allows coverage to run after a failed direct-bump step, and the waiver is limited to empty changesets added relative to the base branch.

Important Files Changed

Filename Overview
.github/workflows/node-simple-pnpm.yaml Consolidates package-bump and coverage enforcement into the existing opt-in changeset job while preserving event and label guards.
actions/changeset/check-coverage/check-coverage.sh Adds branch-scoped detection of empty changesets as a global coverage waiver after tooling and coverage evaluation.
actions/changeset/check-coverage/action.yaml Documents the empty-changeset waiver, label handling, and full-history requirement.
actions/changeset/check-coverage/test/coverage.bats Adds coverage for waiver scope, multiple missing packages, mixed changesets, inherited files, and tooling failures.
actions/changeset/check-coverage/test/helpers.bash Adds a fixture helper that creates the empty front-matter format used by the waiver tests.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  PR[Pull request] --> Toggle{require-package-path-bump?}
  Toggle -- No --> Default[Run standard changeset status]
  Toggle -- Yes --> Label{skip-changelog label?}
  Label -- Yes --> Skip[Skip changeset job]
  Label -- No --> Direct[Require direct package-path bump]
  Direct --> Coverage[Check every edited publishable package]
  Coverage --> Waiver{Branch-added empty changeset?}
  Waiver -- Yes --> Pass[Waive release gate]
  Waiver -- No --> Result{All required packages bumped?}
  Result -- Yes --> Pass
  Result -- No --> Fail[Fail check for changesets]
Loading

Reviews (1): Last reviewed commit: "MILAB-6707: fold changeset coverage into..." | Re-trigger Greptile

Context used:

  • Context used - Terms is a types in codebase. Provide the list of ... (source)

`pnpm changeset --empty` already waived require-package-bump's half of the
gate but not coverage, so an author who declared "no release needed" was
passed by one half and failed by the other. Coverage now accepts the same
declaration, waiving every missing package at once.

Scoped to changesets ADDED in the branch: an empty changeset inherited from
the base branch must not disable the gate for every branch cut after it.
Exit 2 (tooling broken) still wins over the waiver.

The rule is duplicated rather than shared — there is no bash-sharing
precedent between actions here, and refactoring the working, tested
require-package-bump.sh was not worth the risk. Both copies state the rule
in place.

Six cases added to the bats suite.
The per-package coverage check ran as its own always-green job, so a red
result cost nothing and nobody had to act on it. It now runs as a second step
inside `check for changesets`, under the existing require-package-path-bump
toggle, and the separate diagnostic job is gone.

Neither half implies the other, so both run and both must pass:
require-package-bump ignores the diff and demands a bump of `package-path` on
every PR, while check-coverage ignores untouched packages and demands a bump
for each edited publishable one. Replacing the first with the second would
have weakened the gate — in a block repo the model/ui/workflow/test siblings
are private and so skipped by coverage, leaving a ui-only PR unchecked.

The second step runs under `!cancelled()` so one red check reports the
complete set of missing bumps instead of one per run.

No new input and no new check name. With the toggle off — every consumer
today — `check for changesets` behaves exactly as before; what those repos
give up is the never-actionable diagnostic check.
@notion-workspace

Copy link
Copy Markdown

@AStaroverov
AStaroverov marked this pull request as draft August 5, 2026 15:09
@AStaroverov AStaroverov changed the title MILAB-6707: require a changeset for every edited package, in one gate MILAB-6648: require a changeset for every edited package, in one gate Aug 7, 2026
@notion-workspace

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant