Skip to content

docs(gates): measure the changeset LEVEL axis src/** over-read, and pin the proxy as a decision - #17224

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-16985-changeset-level-export-face
Sep 9, 2026
Merged

docs(gates): measure the changeset LEVEL axis src/** over-read, and pin the proxy as a decision#17224
baozhoutao merged 1 commit into
mainfrom
claude/issue-16985-changeset-level-export-face

Conversation

@claude

@claude claude Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Part of #16985 — this delivers the measurement that card asked for, and the remedy that measurement sizes. It deliberately does not close the card; see What is left open at the bottom.

Clause-②: no — comments and self-test rows only, in a repo-root CI script that no package publishes. No exported symbol moves, no verdict changes, no public surface widens.

The headline

The card asked whether packages/**/src/** is a sound proxy for "the package grew a public face", and it was honest that the rate was not measured. Triage sized the remedy in advance: an exemption list if the false red is rare, an exported-surface reading if it is common.

Measured: the over-read is common. Its cost is zero. So neither instrument is bought, and this PR changes no verdict.

The measurement

The LEVEL axis is three days old — born c3b63f7add (#16264, 2026-09-06 08:57Z). So this is not a sample of the population, it is the population: 449 commits landed to 3c5f3c5991, 233 of them introducing a changeset entry and moving a package the axis calls grown.

Method: drive this file's own scan, packagesTouched and judgeLevel at each merge commit against its parent — the same replay idiom the DEPTH note (#16713) already used in this file — plus one added reading per package: does this diff ship anything?, answered from the build each package actually runs.

reading count
(package, PR) pairs the axis called grown 358
...of those, pairs where the diff ships nothing (the over-read) 50 (14.0%)
PRs with at least one over-read package 37 of 233 (15.9%)
PRs that would be refused if clause ② read yes 128
...of those, refusals the over-read actually caused 0

Where the 50 over-reads land, which is the whole answer:

By shape, the 50 are: 35 moved only src/**/*.test.ts (a bundled package never emits those), 12 moved only modules no entry point reaches, 3 moved both.

The zero is declaration-independent, which is what makes it a bound rather than an accident of who declared what. It is measured with the declaration forced to yes on every PR — stronger than anything that actually happened — and refusable is computed before the declaration is consulted, so the same zero covers the NOT MEASURED lane.

A finding that kills the card's own cheaper alternative

The card left open "exempt a file no entry point transitively re-exports". Measured unsound, and unsound by the same asymmetry that condemns the packed-set predicate the dispatch warned about:

  • tsup builds from src/index.ts, so for a bundled package a module nothing reaches emits no byte. 68 of 69 public packages.
  • packages/cli does not bundle. It runs tsc -p tsconfig.build.json, which emits the whole include program — so a src/** file nothing imports still ships as its own dist output. Its tsconfig already documents this in prose and pays for it with an explicit exclude line for a review-record module.

One package is enough: an entry-graph exemption applied uniformly would switch this axis off for exactly the package where it must not be, printing a green. That is the expensive direction, and the same failure #16692 and #16713 each landed to undo.

This is measured, not reasoned. My first instrument skipped the build mode and reported twelve false refusals for @objectstack/cli; reading the build mode collapsed them to zero. An exemption written from that same blind spot would have shipped the error as a rule.

What actually landed

No behaviour change. Three things, all in scripts/check-changeset-no-major.mjs:

  1. A header section recording the measurement, its method and its bounds, next to the DEPTH note that uses the same idiom.
  2. The refutation above, on PUBLISHED_SOURCE_ROOT — which already refutes the other wrong narrowing, so the two now sit together where anyone narrowing the proxy will look.
  3. Three self-test rows pinning the proxy as a decision rather than an oversight, with a nonsense control. Exempting unreachable files now costs a deliberate deletion plus a fresh rate.

Verification

  • Gates: 34 derived, 34 run, all exit 0. dispatch-gates --ran reconciles clean — 0 NOT-MEASURED, 0 UNRUN. Re-derived after fetching a moved origin/main (bccf311100): identical set, no new family.
  • Self-test green at 265 assertions (262 before; the 3 new rows).
  • Ablation — the new pins can fail. Shipping the card's cheaper alternative in its crudest path-only form was applied to disk (blob hash 09a7d6f9 changed to cbb811d0, mutation visible on the predicate line), the self-test went red at exit 1 naming exactly the two new pins, then restore was verified by blob-hash equality against HEAD and an empty git diff HEAD, with the restored self-test green again. Both legs ran from the committed state.
  • Instrument controls: 10 assertions, all pass, before any number was believed — positive controls that hand-verified re-exports are reached, a bin target is reached, and negative controls that the card's ledger file and a nonsense path are not, each with a same-closure positive control so a false cannot be an empty-closure artefact.
  • Two instrument faults found and corrected, both before the numbers were trusted: a specifier pattern that required from on the same line silently missed every multi-line re-export block, and the build-mode blindness above.
  • skip-changeset is measured, not assumed: the only changed file is a repo-root CI script, in a private root package with no files array. Nothing publishes.

What is left open

Part of, not Fixes, and deliberately: the proxy is still unsound. What this PR discharges is the card's "not measured" gap and its prescription question. Whether the finding now closes as measured, or stays open for re-measure, is the maintainer's call — the card carries re-grade triggers, and two of them turn on facts I checked:

⛔ Not claimed: that the proxy is sound. Only that the repair is unbought at today's rate. Re-measure before repairing, not instead of it.


Generated by Claude Code

… pin the proxy as a decision

The LEVEL axis reads "did this PR grow the package's published surface?" from
where the changed file lives. That proxy over-reads, and the gap was filed
without a rate. Measured over the axis's whole life -- born c3b63f7
(2026-09-06), 449 commits landed, 233 of them introducing a changeset entry and
moving a package the axis calls grown:

  * the over-read is common: 50 of 358 (package, PR) pairs, 14.0%
  * its cost is zero: not one false refusal, with the declaration forced to
    `yes` on every PR, and `refusable` is computed before the declaration is
    read, so the same zero covers the NOT MEASURED lane
  * the instance the card was filed on no longer refuses either -- it reads
    `discharged` at exit 0 under the rule that landed after it

So neither remedy is bought, and no verdict changes here. The cheaper remedy
that was left open -- exempt a file no entry point re-exports -- is separately
recorded as measured UNSOUND: it holds only for a bundled build, and
packages/cli emits its whole program, so a module nothing imports still ships.
An instrument that skipped that distinction reported twelve false refusals for
that package which reading the build mode collapsed to zero.

Three self-test rows pin the proxy as a decision, with a nonsense control, so
that exempting unreachable files costs a deliberate deletion and a fresh rate.

Claude-Session: https://claude.ai/code/session_012GKcPZbMoGq7WPzKLfRBTU
Co-authored-by: Claude <noreply@anthropic.com>
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 9, 2026
@github-actions github-actions Bot added the size/m label Sep 9, 2026
@baozhoutao
baozhoutao marked this pull request as ready for review September 9, 2026 19:21
@baozhoutao
baozhoutao enabled auto-merge September 9, 2026 19:22
@baozhoutao
baozhoutao added this pull request to the merge queue Sep 9, 2026
Merged via the queue into main with commit 08e38c6 Sep 9, 2026
36 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-16985-changeset-level-export-face branch September 9, 2026 20:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants