Uh oh!
There was an error while loading. Please reload this page.
Weigh the docs-site eager closure structurally (#6316) - #6402
Merged
Conversation
`check:eager-closure` reads `apps/console/dist/eager-closure.json`, so the budget objectui#4616 set over `/docs/[[...slug]]` — the route every docs page shares, and the one `registerCatalogBlocks.ts` adds side-effect imports to — was governed by nothing. `scripts/check-docs-route-eager-closure.mjs` walks that route's static module graph from source (no install, no build, ~1.3 s) and classifies every package the registrar names: RECORDED payload, FREE (already reachable, so the import adds a declaration and no bytes), or NEW GRAPH, which fails. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012CZgmFFzqA9cX8tBMhvpFe
…ixtures `pnpm check:control-bytes` found two raw U+0000 bytes in the new test's absent-file sentinel — the gate's own failure mode, in the file adding a gate. The fixture map now types an absent file as `null`, which needs no impossible character at all, and `analyse()` declares its `ledger` parameter so a fixture ledger is assignable under `allowJs` inference. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012CZgmFFzqA9cX8tBMhvpFe
…s-route-closure-gate
`scripts/__tests__/ci-cd-pipeline-doc.test.ts` pins `content/docs/guide/ ci-cd-pipeline.md` against `.github/workflows/` in both directions, so a new workflow with no section is a red by design (objectui#3212: `lint.yml` gated PRs for months while the page never mentioned it). Adds the section (triggers, why it is unfiltered, the three buckets, and why exit 1 and exit 2 must not be read as one), the Workflow Inventory row, and the path-filter bullet. The merge-queue paragraph teaches the requirable direction without naming the workflow: that section may not enumerate current `merge_group` subscribers (objectui#4154), which the full scripts/__tests__ run caught. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012CZgmFFzqA9cX8tBMhvpFe
os-warren
marked this pull request as ready for review
August 25, 2026 20:51
This was referenced Aug 25, 2026
Uh oh!
There was an error while loading. Please reload this page.
This was referenced Aug 25, 2026
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#6316
check:eager-closurereadsapps/console/dist/eager-closure.jsonandperformance-budget.ymlbuilds@object-ui/console, so that budget governs the consolebundle. The Next docs site is weighed by nothing — and
apps/site/app/components/registerCatalogBlocks.tsadds side-effect imports to
/docs/[[...slug]], the route all 181 docs pages share. Itsonly measurement (
7738.7 kB / 29 chunks->9542.6 kB / 40 chunks) was reconstructed byhand, once, and #4616's
+50%stop condition has had no instrument behind it.Executing the triage ruling on #6316: shape 2, a cheap structural gate. No docs-site
build in CI, no second byte-budget report, no new ceiling constant.
What the gate does
scripts/check-docs-route-eager-closure.mjswalks the docs route's static module graphfrom source — the route entries plus every
content/docs/**/*.mdxmodule (those arecompiled into this route through the generated
.source/server.ts) — and classifies everypackage the registrar names into exactly one of three buckets:
RECORDEDMEASURED_PAYLOAD— its eager cost was argued for and written down when it landed (#4600, #4616)FREENEW GRAPHThe third bucket is the point: it converts "an unmeasured hazard" into "a review event",
which is what a cheap instrument can honestly do. Green output on
maintoday:The
FREEline is #6314's claim, measured rather than asserted:plugin-formandplugin-gridare in this route's eager closure through@object-ui/plugin-view'smodule-scope
import { ObjectForm }/import { ObjectGrid }. The gate names the file.MEASURED_PAYLOADis a ledger, not a ceiling. It carries no bytes and no threshold. Itseleven entries are re-measured on every run: an entry that stops being named, or that
becomes reachable some other way, fails half 2 and has to shrink. Without it the gate would
be red at rest, and a gate that is red at rest is a gate someone deletes.
Both directions, proven
A structural gate that cannot fail is worse than no gate, so the failing direction was
measured, not reasoned about. Planted
import '@object-ui/plugin-tree';in the registraron
16c36fbb9:@object-ui/plugin-treeis genuinely outside this route's graph: it is named only byapps/console— a different app, not on this route — and there only throughComponentRegistry.registerLazy('object-tree', () => import(...)), a dynamic import, plusa dev-only manifest dump.
apps/sitedoes not depend on it, no MDX file imports it, and nopackage in the route's closure imports it. The mutation was confirmed on disk by hash and
line count before the gate was run, restored under a
trap … EXIT INT TERMwithgit checkout HEAD --naming the file by absolute path, and the restore proven by the blobhash matching
HEAD:apps/site/app/components/registerCatalogBlocks.ts(595a019c5) with anempty
git diff HEAD.The gauge half is what makes that credible, and it earned its place while this was being
written: the first version of the traversal never parsed
.mdxfiles, and half 3 caught itby reporting that the registrar was not reachable from the route at all. The unit test
drives the real
analyse()over fixture trees for each vacuity vector — fenced MDX codeblocks,
import type,import(), prose in the registrar's own header, an unresolvedspecifier, "everything is reachable", and the registrar falling off the route.
Why the workflow is unfiltered
Its inputs are the whole route graph —
apps/site/**,content/docs/**,packages/**(arefactor dropping
import { ObjectGrid }fromObjectView.tsxis exactly what turns a FREEdeclaration into a new graph) — plus its own closure in
scripts/. Apaths:filterlisting all of that is indistinguishable from none, and one that misses a directory cannot
be exercised by the PR that changes it (#6321). So the gate is cheap-tier and unfiltered:
checkout plus one
nodecall, no install and no build, ~1.3 s. It is classified inscripts/dependabot-merge-gate.mjsas a required context and subscribesmerge_group.The page the pin requires (
content/docs/guide/ci-cd-pipeline.md)scripts/__tests__/ci-cd-pipeline-doc.test.tsholds that page against.github/workflows/in both directions, so a new workflow with no documented section is a red by design —
#3212, where
lint.ymlgated PRs for months while the page never mentioned it. This PR adds:REQUIRED_CONTEXTS/merge_grouprelationship, why the workflow is deliberatelyunfiltered, the three-bucket table, and an explicit paragraph that exit 1 is a verdict
about the registrar while exit 2 says the gauge itself is untrustworthy — a reader seeing
exit 2 must not conclude the registrar is wrong;
control-bytes.yml/docs-links.yml.The
## Merge Queuesection teaches the requirable direction without naming thisworkflow:
merge-queue-reporting.test.tsforbids that section from enumerating currentmerge_groupsubscribers (#4154 — the page points atMUST_SUBSCRIBE_MERGE_GROUPinstead ofkeeping a second copy that is short by one the day the next gate lands). The
"can never be required, structurally" bullet is deliberately not extended: this workflow
is the opposite case, and a claim line there would demand a
STRUCTURAL_BLOCKSentry for aclaim that is false.
Facts re-measured rather than inherited
DEFAULT_REPORT_PATHis atscripts/check-eager-closure-budget.mjs:314onb116a0684,not
:292— fix(ci): fail Bundle Analysis when its ceilings were replaced on the base branch #6315 rewrote that file. The finding it anchors is unchanged: the path isstill
apps/console/dist/eager-closure.json.7738.7 kB / 29 chunksfigure is quoted as history only. Nothing herereads it, re-takes it, or depends on it staying true.
(
content/docs/guide/schema-catalog.mdx->SchemaCatalogIndex->SchemaThumbnail), sothe MDX half of the traversal is load-bearing rather than thorough.
Verification
On
638d706b0(head, after mergingmainatd66316e0c):pnpm exec vitest run scripts/__tests__— the whole tree: 80 files, 2302 tests, allpass. That full run is what caught the ci-cd-pipeline.md's "Five workflows subscribe" merge_group list is short by one (skills-paths.yml, since #3735) — the #3261 shape, one subsystem over #4154 pin above; the targeted set could not, since
both pins fire on the mere existence of the new workflow file.
pnpm check:docs-route-closure— exit 0, and the closure is unchanged by everything themerge brought into
packages/**andcontent/docs/**(still 1349 modules, 11 recorded /2 free, 18 of 46 packages outside). Re-run, not assumed.
node scripts/check-doc-links.mjs(17 scan roots),pnpm check:doc-fences,pnpm check:doc-types(184 doc files),pnpm check:control-bytes(5291 files) — all exit 0,plus a
grep -naPcontrol-byte self-scan of the edited page.On
16c36fbb9(the gate itself, unchanged since):pnpm check:docs-route-closureexit 0; exit 1 with the planted package.examples/schema-catalog/test/catalog-gallery-render.test.tsx+plugin-dashboard-gallery-render.test.tsx(they read the registrar) — 2 files, 586 tests,all pass.
pnpm type-check:scripts— exit 0.pnpm check:control-bytes,check:entry-guard,check:pre-install-import-graph,check:esm-specifiers,check:shell-escape-residue— all exit 0.check:control-byteswent red first, on two raw U+0000 bytes in the new test's absent-file sentinel; the
sentinel was removed rather than escaped (an absent file is now
null).Deliberately narrowed from the repo-wide
eslint .: the population came from ESLint's ownconfig (
isPathIgnoredsays none of the four is ignored), the count from--format json,and
eslint.config.jsenables no type-aware linting (noproject/projectService), sothis diff cannot move the verdict on a file it did not touch. The full farm runs in CI
regardless.
Generated by Claude Code