Skip to content

finding(changeset): two independent contract reviews read the repo's own history to opposite bumps for "add an exported symbol to a published index" #15294

Description

@hotlong

Filed unassigned, no live blocker. Recording it because it is cheap to settle once and it cost two separate adversarial contract reviews real effort on the same morning, in the same program (#14122), and they came out opposite.

The two readings

Both reviews were given the same instruction — establish the answer from the repository's own precedent, not from general semver intuition — and both did.

Review of PR #15261 (adds export * from './artifact-collections.js' to packages/core/src/index.ts; subject feat(runtime)) concluded minor, citing the adjacent precedent:

commit 655b106 (#14643) added the neighbouring line — export * from './artifact-packages.js' — to the same file … "@objectstack/core": minor. Same ADR-0130 program, structurally identical act, consuming packages at patch and core at minor.

Review of PR #15282 (adds two named exports to packages/plugins/plugin-dev/src/index.ts; subject fix(plugin-dev)) concluded patch, from a corpus scan:

last 600 commits touching any packages/*/src/index.ts, purely additive export changes (added export … lines, zero removed) where the changeset declares a bump for that package:
{minor: 158, patch: 64, major: 4} n=226
of which subject starts with fix(: {patch: 49, minor: 36, major: 2} n=87

The reading that reconciles them — and why it should be written down rather than inferred

The two data sets are consistent under one rule: the commit TYPE decidesfeat(minor, fix(patch — which is what the split at n=226 vs the fix(-only n=87 shows.

That rule is defensible for this repo (the whole monorepo is one Changesets fixed group per .changeset/config.json, and scripts/check-changeset-no-major.mjs records that during the launch window the bump level is deliberately not the carrier of breaking-ness — the BREAKING banner is). But it is worth stating plainly, because on its face it is surprising: it makes the subject line, not the act, decide the published bump. Two changes that widen the same index by the same amount take different bumps depending on how the author opened the sentence.

Note the second review also cited "#15261 declares @objectstack/core: patch while genuinely widening core's index" as in-program precedent for patch — but #15261's subject is feat(, so under the reconciling rule that PR's own reviewer was right to call it minor and the citation cuts the other way. Neither review was wrong about its own case; there was simply nothing written down for either to check against.

What would settle it

One paragraph, wherever the changeset rules already live (the Check Changeset step's prose in .github/workflows/pr-automation.yml is the surface every author already reads, and scripts/check-changeset-no-major.mjs already carries the launch-window reasoning):

⚠️ Do not turn this into a new gate. The existing Check Changeset already catches the absent case, and check-changeset-no-major.mjs the major case; what is missing is a written rule for humans and agents choosing between the two remaining levels.

Status of the two PRs that raised it

Neither is blocked on this. #15282 stands at patch (its review passed that question). #15261's semver item went moot when the module moved out of @objectstack/core and that PR stopped publishing anything.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions