Uh oh!
There was an error while loading. Please reload this page.
feat(build): ratchet the tooling-directory exclude across every published build tsconfig - #7409
Merged
os-project-manager merged 1 commit intoSep 2, 2026
Conversation
…ry published build tsconfig A build tsconfig that excludes tooling by FILE NAME (`*.test.ts`) stops the files that happen to be named that way and nothing else. The first shared helper added to a `__tests__/` directory then becomes a program input, and an emitting program writes it into the published `dist`. That has happened three times and been caught by a gate zero times: objectui#4006 (73 `*.test.d.ts` published from two packages), objectui#4836 (9 more, one of them an emitted module whose first statement imports `vitest`) and objectui#6943 (the same package as the first, because that fix wrote the name form). Each repair was correct and local, and each left the trap armed everywhere else. objectui#7212 measured the standing exposure instead of another instance: 29 published packages carried the name form with ZERO offending files. Adds `scripts/check-published-tsconfig-tooling-exclude.mjs` (`pnpm check:published-tsconfig-exclude`), wired into ci.yml's Type Check job, and converts 28 build tsconfigs in the same commit so main is green on merge. The gate reads `exclude` arrays and nothing else: no build, no artifact, no emit model. That is the narrower scope which keeps it clear of the modelling objectui#4846 declined for the artifact-level gate, which is untouched and stays the second line of defence. Six published packages are named carve-outs whose emitter never builds its program from this file list, each re-proving its own reason on every run: cli, create-plugin and data-objectstack (tsup entry graph), plugin-charts (its tooling exclude lives in the `dts()` options), console and runner (Vite applications, `noEmit: true`, no `dts()` plugin). create-plugin and runner were found at authoring time and were inside the ruling's initial red set. The conversion is emit-neutral, measured rather than assumed: every enforced package's build program file list was resolved with TypeScript's own config resolver before and after, and all 33 came back identical. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
This was referenced Sep 2, 2026
os-project-manager
marked this pull request as ready for review
September 2, 2026 19:42
Uh oh!
There was an error while loading. Please reload this page.
os-project-manager
deleted the
claude/issue-7212-published-tsconfig-exclude-ratchet
branch
September 2, 2026 19:57
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes#7212
Implements the maintainer's ruling of 2026-09-02 (comment 5508046691, option 2): a static, per-PR gate that reads each published package's build
tsconfig.jsonand requires the directory form of the tooling exclude wherever the emitter honoursexclude— landed together with the conversion of its initial red set, somainis green on merge.What is here
scripts/check-published-tsconfig-tooling-exclude.mjs, exposed aspnpm check:published-tsconfig-excludeand wired intoci.yml's Type Check job (config reads only; no build, no artifact, no emit model).scripts/__tests__/check-published-tsconfig-tooling-exclude.test.ts— 26 cases.content/docs/guide/ci-cd-pipeline.md.The artifact-level
check:published-distis untouched and stays the second line of defence. It is the only criterion that cannot be wrong about what actually ships; this gate asks a strictly smaller question.The card's central measurement, re-taken
The card claims all 29 name-only packages have zero non-test files in tooling directories, which is what makes the conversion safe. Re-measured independently, with the probe built from
TOOLING_FILEinscripts/check-phantom-dependencies.mjsrather than from the card's prose:excludeexcludeThe claim holds: zero, with no exception. And the zero is a reading, because the controls fire — the same probe finds
apps/console/src/__tests__/helpers/preview-page-sources.tsexactly where the card says it is, plusfields/src/__tests__/numberInputBrowserReadings.ts,plugin-gridandplugin-view'sexplainDouble.ts,plugin-designer's two__mocks__files, and four incomponents.One correction to the card's table, cosmetic: it labels the directory-form row 5 and then lists 6 package names. Six is right.
The confidence gap the ruling carried: the carve-outs were NOT exhaustive
The ruling named four and asked for exhaustiveness to be verified at authoring time. Verified mechanically, against each package's actual build script and vite config rather than against the gate header's prose. Two more published packages have an emitter that never builds its program from this file list, and both were inside the ruling's "29 name-only" red set:
excludeis not honoured@object-ui/create-plugintsupclianddata-objectstack— sametsup.config.tsshape, samedts: trueentry-graph emit@object-ui/runnervite build, nodts()pluginconsole—noEmit: true, publishes a Rollup bundle, writes no declarationsFull emitter tally across the 39 published packages:
tscemitting 12,vite+dts()22,tsup3, Vite app with nodts()plugin 2. So the gate names six carve-outs, not four. Requiring a config shape increate-pluginorrunnerwould have been a check that is green about nothing — the failure this repository treats as worse than no check. The initial red set is therefore 27, not 29 (pluscomponents, below, for 28 files).Carve-outs do not sit on trust: each states the facts it rests on and re-proves them on every run (the technique
HOST_PROVIDEDuses incheck-phantom-dependencies.mjs). A carve-out for a package that is no longer published is a failure, not a no-op.consolekeeps itsnoEmitprotection and its entry records, as the ruling required, that it does carry a non-test helper undersrc/__tests__/— the one live file behind a single guard. A test asserts that file still exists, so the note cannot go stale silently.One thing the ruling did not cover — flagged, not folded in
@object-ui/componentswas in the ruling's directory-form set, but its spelling was"src/__tests__": a bare directory, covering one of the three tooling directories and only at the top ofsrc/.tschonours it, so the config is not wrong — it is position-anchored, and does not coversrc/renderers/__tests__/. That is the same "green until someone adds a file" shape this card exists to end, one level over, so the gate rejects that spelling andcomponentsis converted with the other 27. Accepted spellings are the four anchored globs the failure message prints.A second observation, reported rather than acted on: the ruling gives
plugin-charts's carve-out reason as "ownexcludein thedts()options". Readingunplugin-dts'scomputeGlobsshows the option replaces the filter globs only — the program's file list still comes from the tsconfig — so the two compose, andplugin-chartsis really exempt because its tsconfig carries noexcludekey at all. Its own protection is therefore name-only, invite.config.ts. Kept as a carve-out exactly as ruled; noted for whoever revisits it.Verification
Emit-neutrality, measured not assumed. Each package's build program file list was resolved with TypeScript's own config resolver (
ts.getParsedCommandLineOfConfigFile) before and after the proposed exclude — the same resolvertscuses and the same oneunplugin-dtscalls forcontent.fileNames. All 33 enforced packages came back identical: 0 files added, 0 removed. Identical program input means identical emit. The two packages that carry console runtime bytes,consoleandrunner, are carve-outs and were not touched. Byte-neutral.Red-then-green, on the real tree, mutation proven on disk by blob hash and anchored token counts, restore proven by state under
trap ... EXIT INT TERMwith absolute paths:And the hole it closes is real, not stylistic. A non-test
sharedFormHarness.tswas placed underplugin-form/src/__tests__/and the build program asked with the same TypeScript resolver: with the directory form it is not a program input (0); reverted to the name-only form it is (1). Same file, same package, only the exclude differs. Restored, tree clean.Nothing built was mutated by any leg — this gate reads config text only — so there is no
distto restore.Gates run, all on the final head
e855f935e, exit codes captured by redirect before any pipe:No lint narrowing was needed and none is claimed:
lint:rootran its full scope, and eslint's own resolver reports that of the 34 changed files it lints exactly two — the new gate and its test. It ignores everytsconfig.json,package.json, workflow and.mdin the diff, and a package-scoped run inpackages/typesreads 156 files, none of them a tsconfig. So no package lint scope can be moved by this diff.⛔ No existing gate was weakened.
check-published-dist-tooling.test.tsasserts by substring that no per-PR workflow mentions the artifact gate's alias, and it fired on the explanatory comment in myci.ymlstep. The comment was reworded — the test is right, and a substring scan that tried to tell prose from arun:would be a weaker gate. Theci.ymlcomment now records why the alias is deliberately absent from that file.🤖 Generated with Claude Code
https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
Generated by Claude Code