Uh oh!
There was an error while loading. Please reload this page.
fix(console): feed the lazy-linter counter-probe the injection-aware spec test (#5388) - #5390
Conversation
…spec test (#5388) `assertLazyLinterStaysLazy` carried its own private `/@objectstack[\\/+]spec/` while the `vendor-objectstack` chunk group three lines away already read the widened test `resolveSpecDistInjection` publishes. Under OBJECTSTACK_SPEC_DIST all 18 spec specifiers become absolute paths in the overriding tree, with no `@objectstack` segment, so the private regex matched zero modules and the counter-probe correctly refused a verdict — taking every injected console build with it. The producer now publishes `specModuleTest` alongside `vendorChunkTest`, widened by one shared rule, and the plugin takes the spec test as a parameter. The linter half keeps its literal regex and gains a counter-probe of its own: its assertion is negative, so a blind LINT goes silently green forever. Part of #5388 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDA9nN6nXQngoQUAAzRdMb
os-project-manager
commented
Aug 20, 2026
PM review-of-record — checked against the repo, not against the dev seat's report. The card I wrote was wrong on one premise, and the correction stands.#5388 said The /([\\/]node_modules[\\/]@objectstack[\\/](?!lint[\\/])|[\\/]@objectstack\+(?!lint@))/— the whole The Reverse verification is the real thing, not a re-run: the ablation removes the negative lookaheads so the vendor group claims the linter, and the build goes red on the linter message rather than the counter-probe — which is the correct signature, because reaching that verdict at all is what this fix restores. Both modes, ablation reverted, tree clean. CI at Staying a draft until CI converges. Once it lands, objectstack#10129 gets its bump re-run at the fixed commit — objectstack#10137 must not merge before then, because the SHA it currently pins is one the release pipeline cannot build. Generated by Claude Code |
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
Uh oh!
There was an error while loading. Please reload this page.
Fixes#5388
assertLazyLinterStaysLazy()carried its own private@objectstack/specmodule-id regexwhile the
vendor-objectstackchunk group three lines away already read the widened oneresolveSpecDistInjectionpublishes. UnderOBJECTSTACK_SPEC_DISTall 18 spec specifiersresolve to absolute paths in the overriding tree — ids with no
@objectstacksegment atall — so the private regex matched zero modules, the counter-probe correctly refused a
verdict, and it took every injected console build with it.
The counter-probe was never wrong about what it saw. The defect is one consumer of
resolveSpecDistInjectionnot reading its producer's output.The seam, re-verified on
origin/main(0fd11444a)Every line number in the issue is still accurate, checked before editing:
resolveSpecDistInjection(process.env.OBJECTSTACK_SPEC_DIST, { vendorChunkTest: VENDOR_OBJECTSTACK_TEST })const vendorObjectstackTest = specDistInjection ? specDistInjection.vendorChunkTest : VENDOR_OBJECTSTACK_TESTvendorObjectstackTestLINTandSPECThe fix — one producer, both consumers
resolveSpecDistInjectionnow publishesspecModuleTestbesidevendorChunkTest,widened by a single shared
widen()rule so the two tests cannot drift apart again.assertLazyLinterStaysLazy(specTest)takes the spec test as a parameter; the configpasses the injection-aware
specModuleTest, exactly as line 442 does for the group test.specModuleTestis deliberately notvendorChunkTest. That one is the whole vendorscope minus the linter, so an eager
@objectstack/clientwould satisfy it withoutproving the walk can see the spec chunk. Reusing it would have kept the build green by
lowering the bar — the one outcome this issue's acceptance rules out. There is a test
pinning that distinction.
Nothing about what
resolveSpecDistInjectionrewrites changed, and the counter-probe isnot weakened anywhere.
The
LINThalf — decided, not deferredLINTdoes not become injection-aware in this PR, and it gains a counter-probe instead.Nothing injects
@objectstack/linttoday:resolveSpecDistInjectionis spec-only by nameand by
SPEC_PACKAGE_NAME, so there is no producer publishing a lint location to read."Injection-aware
LINT" today could only mean inventing that producer — speculativesurface with no consumer, and untestable, because there would be no injection to test it
against. objectstack#9659 is a proposal, not a landed mechanism.
But the risk the issue names is real, and it is worse on this half than on the spec half.
The spec test going blind fails loud — the build stops, which is exactly what happened
here. The lint test going blind fails silent: the assertion on it is negative, so zero
matching modules is indistinguishable from a bundle that keeps the linter properly lazy,
and the guard goes green forever while ~89 KiB gzipped ships on every page load. That is
#5323's own reasoning ("a green check with no subject"), applied to its own regex ratherthan to its graph walk.
So the linter half now gets the same refusal to guess, using only facts that are true
today: the linter is always in this bundle somewhere (app-shell's
capabilityLint.tsawait imports it), so zero matches anywhere — eager or lazy — is a statement about theregex, never about the graph, and it fails the build with a message that names both ways
to get there. If lint later joins the injection set, this says so instead of quietly
ceasing to guard anything.
Evidence — builds, all four combinations, at
b7b2b3b8cThe failing baseline was established first, on unfixed
origin/main. The overridepoints at a real
@objectstack/specpackage placed outsidenode_modules, which is theonly property of the framework tree that matters here —
/…/objectstack/packages/specandCI's
/home/runner/work/objectstack/objectstack/packages/specboth have no@objectstacksegment either.
OBJECTSTACK_SPEC_DIST0fd11444aEXIT=1—counter-probe failed: no eagerly loaded chunk contains an @objectstack/spec module0fd11444aEXIT=0b7b2b3b8cEXIT=0, 8592 modules transformedb7b2b3b8cEXIT=0, 8592 modules transformedThe probe finds the injected spec — it is not skipped. Read off the injected build's
own
dist/stats.html: 13 distinct module ids under the override's directory are in thebundle and 0 ids from the installed
@objectstack+spec@17.0.0, i.e. the build reallywas running on the injected spec when the counter-probe returned its verdict. The
vendor-objectstackchunk it landed in is 5,055.99 kB / 1,539.84 kB gzip, byte-identicalin both modes.
Reverse verification — the guard can still fail
With the fix in place, the negative lookaheads were removed from
VENDOR_OBJECTSTACK_TESTso the vendor group claims the linter. Predicted before running: plain RED, and on the
linter message rather than the counter-probe, because reaching that verdict at all is
what the fix restores. Measured,
OBJECTSTACK_SPEC_DISTset:Same RED with the override unset, same message. The ablation was applied on top of the
committed fix and reverted with
git checkout HEAD --; the tree is clean.Tests
scripts/__tests__/vite-objectstack-spec-dist.test.tsgains a block that drives thereal plugin off the real console config over a synthetic bundle — pinning the
wiring rather than a regex value, since the bug was a correct regex reaching one consumer
and not the other:
@objectstackmodule isclient— so the widening did not defang it,never reached it,
LINTcounter-probe fires when the linter matches nothing at all.One thing worth recording, because the first version of this test passed for the wrong
reason:
installedSpecDircannot stand in for an injected package. Its own path is…/node_modules/.pnpm/@objectstack+spec@17…/node_modules/@objectstack/spec, which thebaseline regex already matches — measured green under the un-injected config before the
fixture was replaced with a real package at a path of the right shape. The fixture now
asserts that property about itself.
Also green at
b7b2b3b8c:type-check:scripts;tsc -b apps/console/tsconfig.node.json --force(the project that actually containsvite.config.tsandscripts/vite-*.ts);eslinton all three changed files (0 errors, 5no-explicit-anywarnings, matching thefile's existing convention for the untyped config object);
check-changeset-presence,check-changeset-no-major,check-control-bytes.Changeset — and a correction
Empty frontmatter: nothing publishes. But the issue's premise for that conclusion is
wrong and should not be repeated.
@object-ui/consoleis not a private app — it has noprivate: true, carriespublishConfig.access: "public", is one of the 40 packages in the.changeset/config.jsonfixedgroup, and ships its builtdist/as a Hono UI plugin.The reason nothing publishes here is narrower: this PR touches
vite.config.tsand repotooling, neither of which is in the package's
fileslist, and with the override unset theguard evaluates the identical regex it did before, so the emitted bundle is unchanged.
Cross-references
6d5368ac8: the lazy-linter counter-probe is blind to the OBJECTSTACK_SPEC_DIST injection objectstack#10136.it is not addressed here.
@objectstack/*packages still reach the Console bundle from objectui's lockfile — the same publish-ordering trap #8134 closes forspecobjectstack#9659 proposes injecting four more@objectstack/*packages;it is the reason the
LINThalf now refuses to guess.Minor measurement note
The issue quotes
eager chunks: 59/508. On this tree the same failure reports60/508(scratch fixture) and
58/507(clean baseline re-run). The counts move with the tree andthe injected package; the failure and its cause are identical.
Generated by Claude Code
Generated by Claude Code