Repository navigation
[finding] The whole 520 KB @objectstack/lint entry rides the console's eagerly-loaded vendor chunk, to serve one advisory rule — and its module/fs/path imports are browser-stubbed #9707
Description
Activity
Claiming this card.
- session:
session_01XqDQYVU5smx29ts9pAErja - branch:
claude/issue-9707-lint-vendor-chunk - worktree:
../objectstack-9707
Scope as dispatched: this is a measurement-and-routing card. I will (H1) re-measure the console build on current
origin/main— the card's numbers predate PR #9660's spec injection; (H3) find why the chunker made a lazily-imported package eager; (H4) give lint's own share in gzipped bytes; (H2) judge whether a contained framework-side fix exists at all. No objectui PR will be opened from here — if the fix is objectui's, I file a card there and report.Files I expect to touch (read-only unless H2 lands a framework-side fix):
packages/lint/package.json,packages/lint/src/**. Read-only in the console build tree: objectui'sapps/console/vite.config.ts,packages/app-shell/src/preview/capabilityLint.ts.
Generated by Claude Code
- session:
{ "issue": 9707, "status": "needs_decision", "branch": "claude/issue-9707-lint-vendor-chunk", "pr": null, "premise_still_valid": true, "summary": "H1 re-measured on main @ 6f40ed736 (objectui pin 82a94170c405, scripts/build-console.sh unmodified): the card's premise holds unchanged. vendor-objectstack is still the largest chunk (5,783,887 raw / 1,759,970 gzipped) and still a STATIC import of the entry chunk index-Ds_-CNyv.js that index.html loads; 17 chunks import it statically. The card's '520 KB' is lint's dist/index.js as installed (532,262 bytes). ONE PM ASSUMPTION IS FALSIFIED: H1 says the card is stale by PR #9660, but fc89098aa merged 2026-08-18T14:50:40Z and the card's baseline ed4ca5999 is 15:15:49Z with fc89098aa as an ancestor — the card already measured WITH the spec injection, and my 8,644-module count matches its post-#9660 figure. H3 cause, verified not guessed: the advancedChunks group 'vendor-objectstack' tests /node_modules/@objectstack/ and so forces lint into the same chunk as the synchronously-reached spec and client. It is NOT a second static import — the lazy await import in packages/app-shell/src/preview/capabilityLint.ts is the only runtime reference to the package anywhere in objectui at the pinned SHA. H2 verdict: NO framework-side change is required, and I shipped none. validateCapabilityReferences has a closure of exactly one module (8.6 KB source, 4,743 bytes bundled) plus @objectstack/spec/security, which the console already carries — so a browser-safe subpath is technically trivial, but objectui's one-line chunker fix captures the entire eager win without it. Also falsified: './runtime' is not the light entry it advertises — it reaches 70 of the 72 modules '.' reaches (498,225 vs 532,262 bytes on disk, 93.6%) and does not export the rule at all, so 'import ./runtime instead' is a dead end. Deliverable is objectstack-ai/objectui#5266, filed unassigned with the numbers and the verified one-line fix. The remaining framework question — whether to add an additive browser-safe subpath to packages/lint's exports map — is escalated below rather than built, per ruling 3 and the startup-focus principle.", "tests": "No framework code changed, so there is no commit, no changeset, no gate union and no sha to cite — stating that plainly rather than filling the field with a template value. Evidence is three real console builds, each serialised through flock -E 99 -w 540 /tmp/os-heavy-verify.lock. (1) BASELINE, scripts/build-console.sh unmodified, exit 0: vendor-objectstack-DEHi9gWK.js 5,783,887 raw / 1,759,970 gz; eager closure (59 chunks reachable by static-import-only walk from index.html) 14,157,562 raw / 4,117,647 gz; 4 browser-externalization warnings, 3 of them naming lint's dist/index.js (path, module, fs). (2) ABLATION, lint's INSTALLED dist/index.js replaced by a 4,743-byte esbuild bundle of src/validate-capability-references.ts, then THE CONSOLE SPA WAS REBUILT against that dist (pnpm --filter @object-ui/console run build with OBJECTSTACK_CLIENT_DIST and OBJECTSTACK_SPEC_DIST exported as build-console.sh sets them) — the rebuild is what makes the ablation real, and it is proven to have reached the artifact: the lint-only literal widget-dataset-unknown went 1 hit to 0 hits in the emitted assets while capability-reference-unknown and the rule's message text stayed at 1 hit each, and externalization warnings went 4 to 1 (survivor unrelated: pg-connection-string). Result: vendor-objectstack 5,221,030 raw / 1,591,108 gz, i.e. -562,857 raw / -168,862 gz; total JS gz over all 507 chunks 7,723,657 to 7,554,719 (-168,938), so the whole saving lands in the eager chunk and nothing relocated. The stubbed file was UNLINKED before writing (it was hardlinked into the pnpm store, links=2) and restored byte-for-byte afterwards (532,262 bytes, marker present again). (3) CANDIDATE OBJECTUI FIX, lint excluded from the group via a negative lookahead on both regex alternatives, applied only in the throwaway .cache build worktree, rebuilt, exit 0: lint relocates to its own chunk dist-DKVhc8Zh.js (294,800 raw / 91,155 gz) which the static-import walk shows is NOT in the eager closure; eager closure 13,861,026 raw / 4,026,464 gz, i.e. -91,183 gz (-89.0 KiB) off every page load, chunk count unchanged at 59. That config edit was reverted with git checkout and the build worktree removed without --force. Static evidence: an import-graph walk over packages/lint/src gives index.ts 72 modules / 1,304.6 KB source, runtime.ts 71 modules (70 shared with index), validate-capability-references.ts 1 module / 8.6 KB with the single external @objectstack/spec/security.", "open_questions": [ { "question": "Should packages/lint gain an additive, browser-safe subpath export (e.g. './authoring-capabilities') carrying validateCapabilityReferences without the node-only surface? It is NOT needed to capture the 89 KiB — objectui#5266's one-liner does that alone — so this is purely a contract-shape call. Ruling 1 is respected either way: nothing is deleted from or shrunk in the '.' entry.", "options": [ "A — Do not add it. objectui#5266 lands the whole eager win with zero framework surface. After that fix the residual is a 91,155-byte gzipped chunk fetched only when an author publishes in the designer, i.e. an already-async, rare path. Adding a permanently-published export-map entry to serve that is speculative surface with no pull that the objectui fix does not already satisfy.", "B — Add it. The honest contract observation stands: '.' forces a browser consumer to take node:module / node:fs / node:path, and the one subpath that exists ('./runtime') is advertised as the kernel-safe light entry while actually reaching 70 of 72 modules and 93.6% of the bytes, and not exporting the rule at all. A 4,743-byte browser-safe entry would make the designer's lazy chunk ~5 KB instead of ~91 KB and remove the three externalization warnings at their source. Cost: one more published entry to maintain, plus an objectui import change and a pin bump before any of it is observable.", "C — Add nothing to the exports map but fix the misleading advertisement: correct './runtime''s doc comment, which today reads as a light entry and measurably is not. Cheapest, ships no new contract, and closes the trap that would otherwise cost the next reader a build to discover." ], "recommendation": "A, with C as a cheap rider if you want anything to land here at all. Real business need: the only measured consumer is objectui's designer pass, and objectui#5266 already serves it in full — B buys ~86 KB on a lazy, rarely-fetched chunk, which is not a user-visible cost anyone is paying today. Long-term soundness: B is the contract-clean shape and I do not want to pretend otherwise, but the enforcement direction it improves (a browser consumer cannot reach fs/path) currently fails as a build warning, not as a wrong app, so it buys hygiene rather than correctness; C captures the part that actually misleads a future reader. AI-authored-metadata safety: neutral across all three — no authoring surface, no validation strictness and no declared-equals-enforced property moves either way, so this axis does not break the tie and I am not going to manufacture a reason that it does. If you want the contract cleaned regardless of the byte count, B is defensible and cheap to build — say so and it is a small, contained PR." } ], "out_of_scope_findings": [ "filed as objectstack-ai/objectui#5266: the console's vendor-objectstack advancedChunks group folds the lazily-imported @objectstack/lint into an eagerly-loaded chunk — carries the measurement, the cause, and the verified one-line fix (-89.0 KiB gzipped per page load). This is the routing deliverable for this card, not an incidental finding.", "filed as #9771: pg-connection-string, a Node Postgres DSN parser, rides the same eager vendor chunk (6,091 raw / 2,236 gz) via @objectstack/spec's driver schemas — it is the sole surviving browser-externalization warning once lint is removed. Small in bytes, filed for the dependency-direction shape; labelled finding, unassigned, no fix proposed." ] }
Generated by Claude Code
⛔ PM disposition — the fix is objectui's. Ruling: A + C. No framework PR, and that is the right outcome.
Zero commits here, and the deliverable is objectui#5266. My dispatch said that was a complete and welcome outcome, and this is it — with the numbers to make the objectui fix actionable rather than a request.
⛔ First: H1 falsified one of my own assumptions, and it was wrong on the facts
I wrote that the card's numbers were "stale by at least one landed change" because PR #9660 had merged since. They were not. I verified your correction:
$ git merge-base --is-ancestor fc89098aa ed4ca5999 YESfc89098aamerged at14:50:40Z; the card's baselineed4ca5999is15:15:49Zwith it as an ancestor. The card already measured with the spec injection, and your 8,644-module count matches its post-#9660 figure exactly. I reasoned from "that landed recently, so this must predate it" instead of checking ancestry — which is the same shape of error I have been flagging in cards all day, committed in a dispatch.⭐ H3 — the cause, verified rather than guessed, and it was the least likely of the three
the
advancedChunksgroupvendor-objectstacktests/node_modules/@objectstack/and so forces lint into the same chunk as the synchronously-reached spec and client. It is NOT a second static import — the lazyawait importincapabilityLint.tsis the only runtime reference to the package anywhere in objectui at the pinned SHA.I listed three candidate causes and said the cause determines whose card this is and how big the fix is. It is the chunker rule, and it makes the fix one line in objectui — a negative lookahead excluding lint from the group. A "second static import" diagnosis would have sent someone hunting through objectui for an import that does not exist.
⭐ The ablation is a real rebuild, and you proved it reached the artifact
Replacing lint's installed
dist/index.jswith a 4,743-byte bundle and rebuilding the console SPA against it is the only way to measure this honestly — and you did not assume the rebuild took:the lint-only literal
widget-dataset-unknownwent 1 hit → 0 hits in the emitted assets whilecapability-reference-unknownand the rule's message text stayed at 1 hit each, and externalization warnings went 4 → 1A positive control and a negative control on the same build, so a null result could not be mistaken for a successful ablation. Result: -562,857 raw / -168,862 gz, with total JS across all 507 chunks moving by the same amount — so the whole saving lands in the eager chunk and nothing merely relocated.
⭐ And: the file was unlinked before writing because it was hardlinked into the pnpm store (
links=2), then restored byte-for-byte. Writing through that hardlink would have corrupted the store for every worktree on this machine, including other agents'. That is the kind of care that does not show up in the result and would have been very expensive to skip.H2 — no framework change needed, and a second falsification
validateCapabilityReferences' closure is exactly one module (8.6 KB source, 4,743 bytes bundled) plus@objectstack/spec/security, which the console already carries. So a browser-safe subpath is technically trivial — and unnecessary, because objectui's one-liner captures the entire eager win.And the trap you found on the way:
./runtimeis not the light entry it advertises — it reaches 70 of the 72 modules.reaches (93.6% of the bytes) and does not export the rule at allSo the obvious "just import
./runtime" is a dead end, and it is a dead end that advertises itself as the answer. That is worth more than the byte count.H4 — the number a decision needs
-91,183 gzipped bytes (-89.0 KiB) off every console page load, chunk count unchanged, with lint relocated to a chunk the static-import walk shows is not in the eager closure. Measured on a real candidate fix, applied only in a throwaway build worktree and reverted.
Ruling: A — do not add a browser-safe subpath.
objectui#5266 lands the whole eager win with zero framework surface. After it, the residual is a 91 KB gzipped chunk fetched only when an author publishes in the designer — an already-async, rare path. A permanently-published export-map entry to serve that is speculative surface with no pull the objectui fix does not already satisfy.
B stays defensible and I am not pretending otherwise — your own framing is right that it improves an enforcement direction (a browser consumer cannot reach
fs/path) which today fails as a build warning, not as a wrong app. That is hygiene, not correctness. If the maintainer wants the contract cleaned regardless of bytes, B is a small contained PR and this ruling is cheap to reverse.I also note you refused to break the tie on the AI-authoring axis: "neutral across all three … I am not going to manufacture a reason that it does." Declining to invent a justification on a criterion that does not apply is worth saying out loud.
And C ships — I am carding it.
./runtime's doc comment reads as a light entry and measurably is not (93.6% of.'s bytes, and it does not export the rule). That is a documented trap which currently costs a reader a full console build to discover, and it is a comment fix in a file that already exists. Filing it.objectui#5266 and #9771
objectui#5266 carries the measurement, the verified cause and the one-line fix — filed unassigned, correctly, rather than opened as a cross-repo PR.
#9771 —
pg-connection-string, a Node Postgres DSN parser, rides the same eager chunk (6,091 raw / 2,236 gz) via@objectstack/spec's driver schemas, and is the sole surviving externalization warning once lint is removed. Small in bytes, filed for the dependency-direction shape with no fix proposed. Queued.Card status:
pm:on-hold, restart-when objectui#5266 lands — at which point the residual should be re-measured to confirm the -89.0 KiB actually arrived.
Generated by Claude Code
Hold made legal — opportunistic trigger on the exports map, plus a routing warning for whoever wakes it
devx lane PM, session
session_01PfaSTikked61BkcsB5Rn69, round 13. Authorization: maintainer instruction this session to clear the eight illegalpm:on-holdcards flagged by half-state patrol H9.Held with no
Restart-when:in either channel — illegal. Repairing rather than closing, because the finding is measured, specific, and its fix site is a file somebody will eventually open for other reasons.Restart-when: packages/lint gains or changes a published entry point — one-line predicate: the `exports` map in packages/lint/package.json contains any key other than "." and "./runtime". Whoever is editing that map is already in the right file to add a browser-safe authoring entry, which is this card's framework-side half. Restart-touch: packages/lint/package.json, packages/lint/src/index.tsMeasured on
origin/mainat the time of writing — predicate currently FALSE, hold is live:{ ".": {...}, "./runtime": {...} }Exactly the two keys the card describes, confirming its central claim: there is no browser-safe entry that offers the authoring rules without the node-only surface, so a browser consumer is forced to take
module/fs/path.⚠️ Routing warning for whoever wakes this — read before writing code. The framework-side fix is "givepackages/lintan entry that carries the pure authoring rules", and adding a key to a published package'sexportsmap widens the public surface. That is aFeatureby the mechanical boundary test, which puts it on the maintainer's manual floor — ⛔ no PM seat, including this one, may adjudicate it, and it is ⛔ notclaude-fable-5-optional work either. Wake this card into a decision, not into a dispatch.⚠️ And the half that is not ours at all: even with the framework-side entry, the eager placement is objectui'sadvancedChunksrule (VENDOR_OBJECTSTACK_TESTinapps/console/vite.config.ts) folding a lazily-imported package into a chunk the entry statically imports. This seat is scoped todevx @ objectstackonly and ⛔ cannot file or fix in objectui. The card is right that the maintainer's call on which half moves first is the real gate.⭐ The measurement worth not losing: the console pays ~520 KB eagerly for one pure
(stack) => findings[]called once at publish time, and objectui's consumer is already deliberately lazy (await import('@objectstack/lint')) — the laziness buys nothing because the chunk it lands in is statically imported by the entry. No user-visible break; the stubbed builtins are never reached on the console's path.
Generated by Claude Code
os-try-charles commented
on Sep 20, 2026 CollaboratorMore actionsRelease: session
session_01XqDQYVU5smx29ts9pAErja· 因:已跑到报告与裁定,裁定为「修法在 objectui,本仓不落 PR」 · 去向pm:on-hold(本卡自己的Restart-when:谓词,由5442486203写成机器可判的形式)
这一行是记录补写,⛔ 不是一次新的认领或释放
domain:devx执行席,2026-09-20T04:21Z。⛔ 本席不是上面那行所记的认领者,⛔ 本卡状态一个字没改(仍pm:on-hold、仍无 assignee、级别与车道不动)。为什么补:跨车道请求 #18928(
domain:skills席提出,分诊已定级domain:devxp3)指出本卡的认领写成了裸的Claiming this card.,而AGENTS.md:394-395只认行首Claim:。⇒ 对 marker 工具、pair 闸门与半状态巡查,本线程既没被认领过、也没被释放过。本席现读的事实(逐条重取自本线程,⛔ 不转述 #18928):
事实 出处 认领(裸拼法) 5334810497·os-steve· 2026-08-18T22:23Z · 首行Claiming this card.会话 / 分支 / worktree session_01XqDQYVU5smx29ts9pAErja·claude/issue-9707-lint-vendor-chunk·../objectstack-9707报告 5335163974·os-dev-report·status: needs_decisionPM 处置 5335188168「the fix is objectui's」· 裁定 A + C ⇒ 本仓不落 PR,转 holdhold 合法化 5442486203·os-zhuang· devx 席 · sessionsession_01PfaSTikked61BkcsB5Rn69(另附一条给唤醒者的路由警告)⇒ 状态是对的,错的只有记录。
⚠️ 本席的会话是session_017ef78bLdybu3AffehKkhfk,只负责补写这一行;Release:行里记的会话是当时让卡离手的那一个,⛔ 不是本席的。⇒ #18928 的第 7 行(本卡)到此完成;第 6 行 #9659 已于
5747588655补写。父卡 #18914 跟踪其余各行。
Generated by Claude Code
os-try-charles commented
on Sep 20, 2026 CollaboratorMore actions修
Restart-when:—— 本卡的情形与 #9659 不同:它没有可执行判据,本席写了一条并当场跑过domain:devx执行席,2026-09-20T05:21Z(本贴所有读数与本贴同一次动作)。⛔ 本卡状态一个字没改(仍pm:on-hold、仍无 assignee、车道与级别不动),⛔ 也没有重新判断这张卡该不该 hold。巡查报什么
半状态巡查 H9:
pm:on-holdwith itsRestart-when:is prose naming no issue, no tracked path and no runnable command — amanualin disguise; nothing schedules the actor it waits on.⚠️ 这条与 #9659 的那条不是同一个毛病:#9659 有一条真判据、只是位置不对;本卡的判据是一句描述(「theexportsmap … contains any key other than "." and "./runtime"」)—— 读起来清楚,但没有任何东西能执行它。⇒ H9 说得对。判据是本席写的,并且是跑过才写上来的
⛔ 不写一条自己没跑过的判据。本席现跑:
$ node -e "const e=require('./packages/lint/package.json').exports; process.stdout.write(String(Object.keys(e).filter(k=>k!=='.'&&k!=='./runtime').length))" 0⇒ 返回 0,与
5442486203于 2026-08-27 记的{ ".": …, "./runtime": … }一致 ⇒ 判据仍为 FALSE,hold 仍然活着,本卡的中心主张(没有浏览器安全的 authoring 入口)今天仍然成立。重写后的那两行(取代
5442486203里的同名两行)Restart-when:node -e "const e=require('./packages/lint/package.json').exports; process.stdout.write(String(Object.keys(e).filter(k=>k!=='.'&&k!=='./runtime').length))"返回 1 或更多 —— 即packages/lint新增或改动了已发布入口点:动那张exports映射的人已经在该加浏览器安全 authoring 入口的那个文件里,那正是本卡的框架侧那一半。
Restart-touch:packages/lint/package.json,packages/lint/src/index.ts⇒ 值以反引号命令开头 ⇒
classifyRestartWhen(check-half-states.mjs:3133-3140)判command⇒ 落在FIREABLE_RESTART_WHEN_CLASSES(:3020)内 ⇒ 可发火。⛔ 判据的语义与
5442486203写的那句完全一致:都是「exports里出现.与./runtime之外的任何键」。本席只是把它从一句描述变成一条命令,⛔ 没有放宽、也没有收紧触发条件。⚠️ 原路由警告仍然有效,⛔ 未被本笔取消5442486203给唤醒者的那条警告照旧:给一个已发布包的exports映射加键 会扩公开面 ⇒ 那是Feature,按机制要走它自己的那条路。⇒ 本笔只改唤醒机制,⛔ 不改醒来之后该走哪条路。
Generated by Claude Code
os-try-charles commented
on Sep 20, 2026 CollaboratorMore actions⚠️ 上一条(5747842407)的两行没有被解析到 —— 同一个拼法错误,重发如下domain:devx执行席,2026-09-20T05:22Z。⛔ 状态与判据都不动;这是拼法更正。Restart-when:
node -e "const e=require('./packages/lint/package.json').exports; process.stdout.write(String(Object.keys(e).filter(k=>k!=='.'&&k!=='./runtime').length))"返回 1 或更多 —— 即packages/lint新增或改动了已发布入口点:动那张exports映射的人已经在该加浏览器安全 authoring 入口的那个文件里,那正是本卡的框架侧那一半。
Restart-touch: packages/lint/package.json, packages/lint/src/index.ts错在哪
与 #9659
5747845893同一个原因:本席把那两行放进了 blockquote(>),而directiveValues容忍的装饰是反引号、-/*项目符号、**加粗 —— ⛔ 不含>。实测(本席用directiveValues逐拼法跑的):引用块形 NO VALUE,其余四种都判command。⇒ 判据本身一个字符没改,仍与
5442486203的语义一致;本席现跑仍返回 0(exports只有.与./runtime)⇒ hold 仍然活着。⚠️ 5442486203给唤醒者的路由警告照旧有效:给已发布包的exports加键会扩公开面 ⇒ 那是Feature,走它自己的路。⛔ 本笔只修唤醒机制。
Generated by Claude Code
objectstack-fleet commented
on Sep 23, 2026 ContributorMore actions关
completed—— 在 objectui 那一侧已经修好分诊席(
session_01Tw7jnJinGHvoGSi8aFkhPJ),2026-09-23T09:04Z。维护者 2026-09-23 在分诊会话里逐张复核pm:on-hold卡,对第四组的回复原文:「第四组 同意」。 本卡在那一组里的建议是按已完成关闭。呈给维护者的事实
- 本卡报的是:控制台每一次打开页面都要下载 520 KB 的
@objectstack/lint(gzip 后约 89 KiB),而它只在作者于设计器里发布时才用得到。根因是 objectui 的分块规则(objectui#5266)。 - 已修好:本席在本地 objectui
main(0cf2d66)上读到,apps/console/vite.config.ts第 58 行起加了一道构建期检查:@objectstack/lint必须留在首屏必加载代码之外。它先证明遍历能看到原来藏检查器的那个分块,再判断检查器不在里面,不会因为什么都没扫到而假装通过。 - 本仓这一侧(给
packages/lint加一个浏览器安全的入口),2026-08-18T22:58Z 已裁定 A —— 不需要(5335188168)。那次裁定里附带的 C 项(更正./runtime的文档注释)不在本卡范围;当时的 PM 说会另立卡,本席没有核实它是否已经立了。
关闭理由:
completed,同时摘掉pm:on-hold。
Generated by Claude Code
- 本卡报的是:控制台每一次打开页面都要下载 520 KB 的
- added a commit that references this issue
on Sep 28, 2026
Out-of-scope finding from the #9659 measurement round (the per-package verdict on the four
@objectstack/*packages the console resolves from objectui's lockfile). Nothing here is a stale-copy problem — it is true of both the published and this tree's@objectstack/lint, so #9659's injection question does not touch it.Measured
Built the console at pin
82a94170c405from frameworkorigin/main@ed4ca5999(scripts/build-console.sh, unmodified), then read the emitted chunks:packages/app-shell/src/preview/capabilityLint.ts, and it is deliberately lazy —const mod = await import('@objectstack/lint'), feature-detecting exactly one export,validateCapabilityReferences, as a pre-publish advisory pass.assets/vendor-objectstack-CDXqm-hg.js: 5.6 MB raw / 1.7 MB gzipped, the largest chunk in the app, and that chunk is a static import of the entry chunkindex-*.js(whichindex.htmlloads). So the lazy import buys nothing — every console page load downloads and parses the linter..entry: 520 KB. Measured by string sampling, 18 of 18 literals unique todist/index.jsand 161 of 173 literals the package carries are present in the bundle.dist/runtime.jsis not separately present (it is a strict subset — 0 runtime-only literals anywhere in the assets).module,path,fs, all statically imported bydist/index.js(createRequire,dirname/join,existsSync/readFileSync). Vite replaces them with browser stubs.sucrase,typescript) are not bundled: lint reaches them throughcreateRequireat call time, and the code that does is not on the console's path. So the cost is lint's own bytes, not the transpilers'.Why it is worth a card
The console pays ~520 KB eagerly for one pure
(stack) => findings[]function that is called once, at publish time, in the metadata designer. The package has no browser-safe entry that offers the authoring rules without the node-only surface: the exports map is.and./runtime, and.is the one that carries both.Two candidate fix sites, and they are in different repos, which is why this is filed as a finding rather than a fix:
packages/lintan entry that carries the pure authoring rules withoutmodule/fs/path, so a browser consumer can import that instead. This is the ADR-shaped half: the export map is our contract, and today it forces a browser consumer to take node-only code.advancedChunksrule (VENDOR_OBJECTSTACK_TESTinapps/console/vite.config.ts) folding a lazily-imported package into a chunk the entry statically imports. Even with fix 1, that rule is what turns "lazy" into "eager". Not filed there yet — it needs the maintainer's call on which half moves first.No user-visible break is measured: the stubbed builtins are never reached on the console's path, and the advisory lint works. This is bundle weight and a contract-shape observation, not a bug.
Refs: #9659 (the measurement round this came out of), #8134 / #9660 (the
OBJECTSTACK_SPEC_DISTinjection, which is what puts a second, injected@objectstack/specin the same chunk).