[00129] Replace the Four Hardcoded Barrel Guard Tests With an Eager Graph Reachability Assertion - #112
Merged
rorychatt merged 5 commits intoAug 2, 2026
Conversation
… reachability assertion Adds src/frontend/src/widgets/__tests__/lazyWidgetBarrels.test.ts, which walks static non-type import edges from src/index.tsx and fails if any widgetMap.ts import() target is eagerly reachable, printing the import chain. Deletes the four near-identical string tests it subsumes: src/widgets/lists/index.test.ts src/widgets/chunkSplitting.test.ts src/widgets/inputs/ContentInputWidget/index.test.ts src/widgets/inputs/DateTimeInputWidget/index.test.ts Each asserted one barrel does not mention one widget name. Three of them are false positives against the authoritative assert-lazy-chunks build gate: their barrels are inert (nothing imports lists/, and the two inputs/ barrels ARE the import() target), so re-adding the export exits 1 in the string test while vp build exits 0. The reachability rule agrees with the build gate there and covers the direct-import shape none of the four could see. Mutation matrix, measured at base 6e7664c, each row reverted after: 1 clean tree test 0 build 0 2 widgetMap.ts: import "@/widgets/tree/TreeWidget" test 1 build 1 3 named import of a lazy module, binding used test 1 build 1 4 same, binding unused test 1 build 0 5 import type from a lazy module test 0 build 0 6 chat/index.ts: export * from "./ChatWidget" test 1 build 1 7 lists/index.ts re-exports ListWidget (inert) test 0 build 0 8 src/index.tsx replaced with export {} (anti-vacuity) test 1 n/a 9 clean again test 0 build 0 Row 4 is the only place the test is stricter than the build: rolldown elides an unused named import so no eager edge is emitted. Both pnpm lint and tsc -b already reject that code, so it cannot reach main; the test is only first to complain. Frontend suite: 57 files / 1217 tests before, 54 files / 1215 tests after (+1 file / +2 cases, -4 files / -4 cases), exit 0 both ways. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…nd repoint the guard-test reference Two edits to the "Module Graph and Lazy Loading" section: 1. The section claimed "this gate does not catch all regression shapes: a barrel re-exporting a lazy widget can defeat the split with exit 0 and no warning". That is false for assert-lazy-chunks and appears inherited from the older INEFFECTIVE_DYNAMIC_IMPORT warning gate, which genuinely never fired on vite-plus 0.2.7. Measured at 6e7664c: appending export * from "./ChatWidget" to chat/index.ts makes vp build exit 1 from [plugin assert-lazy-chunks], naming src/widgets/chat/ChatWidget.tsx -> assets/ChatWidget-DCeO37_C.js. The barrel shape is precisely what it does catch. Replaced with its real blind spots: a lazy module merged into the entry chunk (the plugin tests staticallyImported.has(chunk), and the entry is in no chunk's imports), and anything elided before the module graph is built - verified by an unused named import of a lazy module building exit 0. The byte-budget paragraph is untouched; check:bundle still reports 1603.5 kB across 38 of 244 chunks. 2. The "cheaper guard" paragraph pointed at src/widgets/lists/index.test.ts, deleted in the previous commit. Repointed to the new reachability test, and dropped "only for the barrel it names" - the property that no longer applies. The inert-barrels section is left as is: its "Six of the 27 top-level barrels" counts barrels with no importer, a different set from barrels re-exporting a lazy widget, so the two counts do not need reconciling. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ng section Conflict 1 (the assert-lazy-chunks paragraph): resolved to main's version. A sibling landed the same correction this plan's README Edit 2 was written to make - the section no longer claims the gate is silent on the barrel shape - and it also extended the plugin itself with || bundle[chunk]?.isEntry, closing the entry-chunk blind spot. That makes this plan's version of the paragraph wrong, so main's text is kept and only the one blind spot that survives the fix is added: an elided unused named import, which never reaches the module graph. Conflict 2 (the cheaper-guard paragraph): resolved to this plan's version. Main's side still points at src/widgets/lists/index.test.ts, deleted in 8e498df, so keeping it would leave a dangling reference. Reworded from "gates" plural to singular since main's rewrite replaced the check:bundle paragraph between them with a manual grep recipe. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sibling commit 2376cb3 ("Document eager vs lazy criterion in widgetMap and lazify 55 widgets") landed between this branch's base and the merge, taking widgetMap.ts from 50 static / 61 dynamic first-party imports to 3 / 117. Two assertions in this test were calibrated against the old shape and failed on the merged tree. Neither failure was the guard: the reachability walk finds 0 real offenders both before and after, so the property still holds. 1. paths.size > 100 -> > 25. The eager graph is meant to shrink as widgets are lazified; it went 199 files -> 53 for exactly the reason this test exists. A floor near the current figure would fail the next lazification instead of a broken walk, so it is now deliberately loose and commented as such. The load-bearing anti-vacuity check is paths.has(widgetMapPath), which still fails on a stubbed entry (mutation row 8). 2. Comment stripping added for the dynamic-import scan. 2376cb3 also added a doc comment to widgetMap.ts containing a literal lazyWithRetry(() => import("@/widgets/<dir>/<Widget>")) template. The dynamic regex matches import("...") anywhere on a line, unlike the static one which is anchored to the line start, so the placeholder was collected as a real specifier and failed the resolve assertion. Whole-line // and /* comments are now dropped first. Verified this is the only such case: 0 line-start static imports inside block comments across all 478 first-party files. Mutation matrix re-run against the MERGED tree, each row reverted after: 1 clean tree test 0 build 0 2 bare side-effect import of a lazy module test 1 build 1 3 named import of a lazy module, binding used test 1 build 1 4 same, binding unused (elision) test 1 build 0 5 import type from a lazy module test 0 build 0 6b lists barrel re-exports ListWidget AND widgetMap imports the barrel (the historical two-hop bug) test 1 build 1 11 same barrel reached from widgetRenderer instead test 1 build 1 7 lists barrel re-exports ListWidget, barrel inert test 0 build 0 8 src/index.tsx replaced with export {} test 1 n/a 10 comment stripper removed (proves fix 2) test 1 n/a 9 clean again test 0 build 0 Row 6 from the pre-merge matrix (chat/index.ts re-exports ChatWidget) now exits 0 on BOTH gates and is replaced by 6b: 2376cb3 lazified the three eager chat widgets, so nothing imports @/widgets/chat and that barrel is inert. Agreeing with the build there is the behaviour this plan set out to get; a hardcoded string test would have gone red on a correct tree. No barrel is eagerly reachable at all now, which is why 6b has to create the eager edge to test barrel traversal. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…TheSingleBarrelLazyLoadingGuardTestWithAnEagerGraphRe
rorychatt
deleted the
tendril/00129-ReplaceTheSingleBarrelLazyLoadingGuardTestWithAnEagerGraphRe
branch
August 2, 2026 10:56
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 free
to 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.
00129 - Replace the Four Hardcoded Barrel Guard Tests With an Eager Graph Reachability Assertion
What landed
One new test replaces four hardcoded string tests, and the README stops pointing at a file that no
longer exists.
src/frontend/src/widgets/__tests__/lazyWidgetBarrels.test.ts(103 lines, 2 cases, 11 ms) indexes thestatic import edges of every first-party non-test file, walks them breadth-first from
src/index.tsx,and fails if any module named in a
widgetMap.tsimport("@/...")is reachable that way. A reachablelazy module is one whose
import()no longer splits anything. The failure message is the whole importchain, e.g.
Deleted, each an 11-line read-the-barrel-and-regex test covering one barrel and one widget name:
src/widgets/lists/index.test.ts,src/widgets/chunkSplitting.test.ts,src/widgets/inputs/ContentInputWidget/index.test.ts,src/widgets/inputs/DateTimeInputWidget/index.test.ts.src/frontend/README.mdgains a pointer to the new test in place of the dangling reference, plus fourlines naming the build gate's one real blind spot.
Net:
6 files changed, 113 insertions(+), 51 deletions(-).Commits
8e498df08407c39e425d5main's rewrite of the same sectiona385b1fmain's 55-widget lazificationb4a50a7origin/main9b7c764(clean, no conflicts)Branch
tendril/00129-..., 5 commits ahead oforigin/main, merged up to9b7c764.mainmoved fourtimes during execution:
df4e685->7bd1fdc->6e7664c->e6c4398->9b7c764.Why this shape
The four deleted tests were the same idea copied four times, and two of the four could not fail for a
structural reason:
inputs/ContentInputWidget/index.tsandinputs/DateTimeInputWidget/index.tsare the
import()target, so a re-export inside them can never create an eager edge. The new testasserts reachability instead of names, so barrels are just edges - which removes the name matching, the
inert-barrel filter, and the carve-out those two files needed.
It is deliberately redundant with the build-time
assert-lazy-chunksgate. It reads source only, so itneeds no build and runs in 11 ms rather than 2 s plus a toolchain; it names the import chain rather than
a chunk filename; and it survives a change to the bundler's graph API. The README now says explicitly
that it is a complement to the build gate, not a replacement.
Mutation matrix (re-measured on the merged tree)
Eleven rows, each reverted after, exit codes read unpiped,
git diff --statconfirmed empty betweenrows. Full table with evidence in
Verification/CheckResult.md. The three rows the plan requires (2, 6,8) were all reproduced. Headlines:
import "x", a usednamed import, an unused named import, and a barrel re-export reached through either
widgetMap.ts(row 6b) orwidgetRenderer.tsx(row 11).import type(row 5) and an inert barrel (row 7).src/index.tsxtoexport {};fails with "the eager walkfrom index.tsx never reached widgets/widgetMap.ts" instead of passing trivially.
only holds for a plainly named binding - with an underscore-prefixed alias,
pnpm lintandtsc -bboth stay green by design and this test is the only gate that catches it.
Created using Ivy Tendril.