test(vi-mock): inherit the real @object-ui/plugin-chatbot surface in 11 vi.mock factories - #8207
Conversation
…11 vi.mock factories Slice 10 of objectui#6892's per-specifier sweep. All 11 frozen factories were zero-parameter object-literal arrows that hand-listed one to three exports of a 48-export barrel; each now obtains the real module and spreads it FIRST, with every hand-written override kept after the spread. No assertion changed: the 11 files run 64/64 tests green before and after. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
The precondition the gate documents is a sweep, not a judgement: the 11 frozen factories on this specifier were converted in the previous commit and the gate now reads 0 frozen for it, so it joins COVERED_SPECIFIERS. Fourteen -> fifteen, 592 call sites judged and 592 inheriting; the remaining uncovered population moves 61 -> 50, with @object-ui/app-shell still annotated PARKED under #8173. The header record carries this slice's STEP 0 measurements, including the first bimodal marginal-cost reading the worklist has met. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
…eset The gate's own pin forbids a `.test.ts` name followed by a quote and a comma anywhere in the gate source, because that is the shape of a per-file exception list. The slice-10 record's prose had one in a sentence, not a list; reworded so the pin keeps its bluntness rather than being narrowed to admit prose. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
The five `renderers/layout/page.tsx` registrations read as bare at the call site but carry `ui` through the spread `pageMeta` constant. Slice 7's record already had this right; slice 9's report called them bare and slice 10's dispatch inherited the claim. The slice-10 record now names where the namespace actually lives, so the next walker reads the third argument rather than the key. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
…mock-inherit-slice10-plugin-chatbot
✅ 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
|
|
Standing down on That check is informational (not in the Generated by Claude Code |
|
Armed —
Landing stroke follows the merge: content probe on re-fetched Generated by Claude Code |
|
LANDED — Merged at 2026-09-07T01:24:41Z as Slice 11 ( Generated by Claude Code |
Part of #6892 (slice 10:
@object-ui/plugin-chatbot)Base:
97b0177e1(carries PR #8184, slice 9). Draft, basemain.origin/mainmoved during this slice, from97b0177e1to0ead1f62a(three commits:#8186, #8189, #8182). Merged in with a merge commit, never a rebase, and every reading
below was re-taken on the merged head. The growth warning that fired in slices 3, 5 and 9
did not fire this time: the judged population is unchanged at 659 and no new frozen site
arrived on any specifier. None of the three commits touches the gate or any of the eleven
files.
Sweeps the eleven frozen
vi.mockfactories on@object-ui/plugin-chatbotto theinheriting form and adds that specifier to
COVERED_SPECIFIERSin the same PR, which isthe gate's documented widening precondition. No product source, no
package.json, noassertion changed.
STEP 0 — verdict: PROCEED
Method. Static import graph walked from
packages/plugin-chatbot/src/index.tsxwiththe TypeScript parser, every module-scope statement of every reached module classified
(TS ESM
'./x.js'resolved to./x.ts/.tsx; the alias table read out ofvitest.config.mtsitself). Corroborated by importing the real barrel under everyvitest project the sixteen affected suites run in.
Graph. 454 modules, 5,238 module-scope statements (1,989 import/export, 833 type
declarations, 1,070 function/class declarations, 926 bindings, 420 other), spanning eight
workspace packages:
components206,core95,react64,plugin-chatbot33,i18n27,
types17,sdui-parser8,data-objectstack5. 98 bare side-effect imports, one CSSimport (
components/src/sidebar-fixes.css, inert — the root Vitest config declares nocssoption).Not inert, and the class of the effect is what decided it. 114 module-scope
ComponentRegistry.register(...)calls — and zero of them are bare. Every one carries anamespace (
ui89,element10,page7,action5,plugin-chatbot3), so thedeprecation
console.warninregister()(packages/core/src/registry/Registry.ts:440)cannot fire. Beyond registration the graph holds only allocation.
Empirical corroboration, both projects. Importing the real barrel under happy-dom
exports 48 names and emits zero
console.warnand zeroconsole.error, confirmingthe namespace reading:
dom(light setup)dom-heavy(full setup)The order-of-magnitude gap is the point: under
dom-heavythe setup has already loadedalmost all of the graph, so the barrel costs nearly nothing there.
Import cost — the marginal reading, which is BIMODAL
Per ruling 1 the isolated number decides nothing; the marginal per-file cost does. Three
real files, one per project-membership kind, three runs each, medians:
layout/__tests__/ChatDock.test.tsxdom-heavyconsole/home/__tests__/HomePage.aiStudioDisabled.test.tsxdomhooks/__tests__/useAiSurface.test.tsdomviadomTsTestsThe mechanism, measured rather than argued. For each frozen file I walked what it
already loads at module scope with the chatbot specifier excluded (it is mocked away
today), and compared against the barrel's 454 modules:
useAiSurfacehook specTen files already hold 93% of the barrel's graph through their own imports, so inheriting
resolves an almost-resident graph. The eleventh is a four-test hook spec whose only route
to the barrel is the mock itself, so it pays for the whole graph — an 11.4x marginal cost,
squarely inside objectui#6580's STOP band.
Why this is a PROCEED and not a STOP
range is a STOP") is met by one of the eleven files, and this is a judgement the
reviewer should be able to reverse.
The number CI actually pays is the aggregate over the population, measured as one vitest
invocation over all eleven files, twice each:
+2.7 s (~+10%) for the entire slice, 64/64 green in both states. objectui#6580's shape
is roughly 10 s x 22 files; this is not that. Against that, a STOP costs ten free
conversions and leaves the specifier permanently outside
COVERED_SPECIFIERS, becausethe gate requires zero frozen for a specifier to join and there is deliberately no
per-file exception ledger — so "sweep the ten, skip the one" is not an available shape.
⇒ Carry-forward recorded in the gate header, sharpening slice 9's: take the marginal per
FILE, but decide on the AGGREGATE. A lone cold file can sit in the STOP band while the
population it belongs to costs a tenth of it.
Dependency-edge reading (ruling 3)
packages/app-shell/package.jsondeclares@object-ui/plugin-chatbotin bothdevDependencies(workspace:*) andpeerDependencies(workspace:^). The edge exists,so the typed generic is the correct spelling and the ANY spelling slice 9 needed for
plugin-treeis not owed here.pnpm check:phantom-depsexits 0 on the shipped head(3,969 source files, 20,092 module specifiers checked), confirming the type-position
reference demands nothing new.
Note the five sites already inheriting on
mainuseimportOriginalwith the genericargument RECORD-OF-STRING-TO-UNKNOWN. The typed form is used for the eleven repairs
because the edge is declared and it actually type-checks the spread; both spellings now
coexist in the package, which the gate judges identically (it reads the code, not the name).
STEP 1 — census, through the gate's own exported
scan()Re-derived on
97b0177e1withcoveredwidened in memory to the 22 workspacespecifiers any
vi.mockcall site names; the constant on disk was neverwidened-and-reverted.
@object-ui/plugin-chatbotbefore@object-ui/plugin-chatbotafterMatches the slice-9 forecast (16 / 5 / 11) exactly in both halves, and the PM's tree read:
all 16 sites sit under
packages/app-shell, in 16 distinct files. All 11 frozen ones areone syntactic shape — the zero-parameter object-literal arrow — each hand-listing one
to three names from a 48-export barrel.
Population: 61 to 50 frozen over 659 judged, and no site moved the other way — a
diff of the two per-specifier tables with the
@object-ui/plugin-chatbotrow removed isbyte-empty across all 21 remaining rows.
⭐ Free confirmation present: 5 of the 16 sites already inherited the real barrel on
mainand passed, all in the light
domproject.Neighbour reading (ruling 4)
Every other
vi.mockfactory in all 11 touched files was read and classified through thegate before editing, then judged against the walked graph rather than in the abstract:
@object-ui/auth@object-ui/i18nreact-router-domZero neighbouring repairs, predicted in advance rather than discovered by a red run.
Slice 6's collection-death class needs a frozen neighbour the newly-real graph reaches.
The only reached workspace neighbour (
i18n) is already inheriting, and the only frozenneighbours (
react-router-dom) are not reached. Notably the barrel readslucide-react71times — the exact specifier whose frozen factories killed 15 files in slice 6 — and not
one of the sixteen files carries a
lucide-reactfactory at all.Per ruling 4: no file among the sixteen mocks
@object-ui/app-shell, re-derived on thisbase (the parked specifier under objectui#8173 stays parked, untouched).
Covered set: 14 to 15
One line added to
COVERED_SPECIFIERS; header count word fourteen to fifteen; the slice-10per-specifier record added in the shape slices 5 to 9 used; remaining-population paragraph
refreshed 61 to 50 with
@object-ui/app-shellstill annotated PARKED.The pin
scripts/__tests__/check-vi-mock-inherit.test.tsneeded no edit — confirmed byrunning it, not by reading it: it derives the names from
COVERED_SPECIFIERS. 67/67 pass..test.tsname followed by a quote and a comma anywhere in the gate source, because that isthe shape of a per-file exception list. My slice-10 record had one in a prose sentence.
Reworded the prose so the pin keeps its bluntness; narrowing a good gate to admit prose
would have been the wrong repair.
Positive control (ruling 6)
Two legs, from the committed tree, under a
trapwith absolute paths. Each mutationproven on disk by an anchored count and a changed blob before the gate result is read;
each restore proven by STATE —
git hash-objectequal to the HEAD blob andgit diff HEADnaming zero paths — never by a checkout exit code.Target
packages/app-shell/src/layout/__tests__/ChatDock.test.tsx, HEAD blob2e21149ddb3c7d5aa5c6f499582849882f78d8f3.36827fd0e01c46aea5d82af613a959a77139d9e9ChatDock.test.tsx:28, reason "the factory obtains the real module but never spreads it"e066d044ef5cce42d36db8be411b3350d136f17c⭐ Leg 2's mutated blob is byte-identical to this file's pre-sweep blob at
97b0177e1,so the leg provably reconstructed the exact frozen form the gate must reject.
Per ruling 6 and objectui#8183, neither leg uses a spread that merely mentions the
obtained module — that shape reads as inheriting and would have been a void reading.
No
distpreflight is owed: the gate is a source-text scanner and both configs alias thespecifier to source.
guard counted spread lines file-wide and this file has a second one (its
i18nneighbour),so the count did not fall to zero and the script refused to read the gate. Re-anchored on
the chatbot line specifically. Recorded because the abort is the guard working.
Gates — all pinned to final HEAD
e104c509d(the merged head)node scripts/check-vi-mock-inherit.mjs@object-ui/plugin-chatbot(was 576 over fourteen)scripts/__tests__/whole directoryvitest packages/app-shell/pnpm --filter @object-ui/app-shell type-check--listFilesnames all 11 edited files in a 4,427-file programpnpm type-check:scriptspnpm lint:rootpnpm --filter @object-ui/app-shell linteslint --no-inline-config --format jsonover the 12 non-changeset pathspnpm check:phantom-depspnpm check:control-bytesgrep -naPself-scan of all 13 changed paths, no matchnode scripts/check-changeset-presence.mjsnode scripts/check-changeset-no-major.mjsnode scripts/check-governed-queue-guard.mjs --testpnpm check:entry-guardpnpm check:vi-mock-specifiersDependency closure built first (
pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' build, exit 0) — without it the type-checks fail on every workspace import and read exactly like a break this diff caused.Live E2E (informational)is red on every branch today for an upstream reason(#7990 / objectstack#16186) and is not this PR's.
Files changed — 13
packages/app-shellcarrying a frozen@object-ui/plugin-chatbotfactoryscripts/check-vi-mock-inherit.mjs— one line added toCOVERED_SPECIFIERS, plus the slice-10 header record.changeset/vi-mock-inherit-slice10-plugin-chatbot.md— empty frontmatter (declares releasing nothing)Five commits (four of work plus the
origin/mainmerge), no amend and no force-push atany point. A closing-keyword scan over every commit message and over this description
returns no keyword adjacent to an issue reference.
Out-of-scope finding
Filed as objectui#8202 (
finding, unassigned): the gate's own no-exception-list pin readsthe whole gate source as one string, so it cannot tell a prose sentence naming a test file
from a list entry. It went red on this slice's header record and the repair was to reword
the prose, never to relax the pin. Not fixed here — outside this slice's file surface.
Generated by Claude Code