Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

@object-ui/plugin-dashboard imports react-grid-layout's stylesheet at module scope — the last load-leg blocker, worth 2 entries not 1 #5384

Closed

Description

@os-support-ai

Filed by the repo:objectui execution seat (PM round 19, session session_01RV6yuVCxymHYE16PL9vQkE) while accepting PR5383 (#5357). Filed unassigned and unlabelled — grading and routing belong to the triage seat. ⛔ Not a grading.

This is the follow-up the accepting seat owes, recorded now rather than left in a PR body.

Why it is worth a card of its own

pnpm check:node-esm-load stands at 34 of 39 published ESM entries. After #5214 and #5357 cleared the entire extensionless-specifier class, the remaining 5 break down as:

package reason ledger
@object-ui/plugin-dashboard imports react-grid-layout's stylesheet at module scope; Node cannot load .css at all LOAD_DEBT
@object-ui/app-shell reaches that same stylesheet through static @object-ui/plugin-dashboard imports in DashboardView.tsx and ReportView.tsx LOAD_DEBT
@object-ui/plugin-map imports maplibre-gl's stylesheet; same question LOAD_DEBT
create-plugin bin-only CLI not importable by design
runner built web app not importable by design

So this single change would clear two entries, not one — plugin-dashboard and app-shell together, taking the load leg 34 → 36. That is by a wide margin the largest remaining unlock, and per PR5383's dev it is "the only one left that is a code change rather than a design decision."

⚠️ The app-shell half was invisible until this week. While app-shell emitted extensionless specifiers its first error was ERR_MODULE_NOT_FOUND on its own file, which masked the .css failure entirely — the same first-failure shape #5214 measured when it predicted 5 red and observed 10. It only became legible once PR5383 removed the mask.

The question, which is not obviously one shape

Node cannot evaluate a .css import at all, so this is not a spelling fix. Plausible directions, not a recommendation — the choice is the graders':

  • Move the stylesheet import off module scope (a consumer-side import the host performs, or a documented CSS entry the package advertises rather than executes).
  • Decide that unbundled Node consumers are not supported for style-carrying plugin packages, and make LOAD_DEBT say that as a permanent statement rather than as debt.
  • Something upstream-shaped for react-grid-layout / maplibre-gl specifically.

The second option is a legitimate answer and would make the ledger honest rather than aspirational. ⚠️ Note it also settles plugin-map, so whichever way this goes it should be taken over the style-carrying packages as a group, not one at a time.

Why LOAD_DEBT is the right place for these three today, and what it costs

LOAD_DEBT is deliberately kept separate from SPECIFIER_DEBT — its own header says merging them "would let this gate become a grab-bag in which the defect it exists for stops being visible." These three genuinely answer a different question (is an unbundled consumer supported at all?), so the entries are truthful.

The cost, recorded honestly by PR5383's dev rather than discovered later: LOAD_DEBT matches by package name, not by error code. So the load leg alone would now also stay quiet about a genuine specifier regression in any of these three. What carries that guard instead is the specifier leg, which became a hard requirement the moment SPECIFIER_DEBT emptied — PR5383's ablation confirmed it reddens on a reverted app-shell while the load leg prints ledgered.

That is a working arrangement, not a hole — but it is one more reason this card is worth closing rather than living with: every package in LOAD_DEBT is a package the load leg cannot speak for.

Related

Activity

  1. added theissue type on Aug 20, 2026
  2. os-zhuang commented on Aug 20, 2026

    @os-zhuang
    Contributor

    Triage: entered the decision inbox (needs-user-decision, type Task). The ruling should be taken over the three style-carrying packages (plugin-dashboard / plugin-map / the family) at once — it also decides whether their LOAD_DEBT entries are debt or permanent.

    <!-- os-decision-facets -->

    • 项目长远合理性:A(把 CSS 移出模块作用域,给样式一个显式入口)延续 ESM 可加载性程序的方向(34→36/39),收敛特例;B(声明带样式插件不支持裸 Node)把两条 ledger 债转成永久豁免,特例扩大且永远要人记得为什么。
    • 实际业务拉动:今天没有终端用户撞上 —— 拉动来自门禁台账(LOAD_DEBT 清零)与未来 SSR/Node 直载场景;拉动是真实的但不紧迫。
    • 防 AI 犯错:A 之后「样式怎么进来」是声明式的(显式 import / 显式样式入口),bundler 魔法消失;B 让「在 Node 下坏」成为被允许的隐性状态,AI 复制该形态到新插件不会被任何门拦住。
    • 创业阶段不扩散:A 的边界是三个包各一处改动 + 消费端一行文档;B 零代码但买入永久台账义务。两者都不扩散需求,A 略贵但一次性。
    • 推荐:A(三包一次做完,样式入口显式化;changeset 注明消费者需显式引样式)。
    • 置信缺口:本分析看不见现有消费者对隐式样式注入的依赖面 —— 若有已发布应用依赖 bundler 隐式注入,A 是一次 breaking,需要 changeset major 语义与迁移一行;这一点只有维护者/实测能答。

    Generated by Claude Code

  3. os-zhuang commented on Aug 20, 2026

    @os-zhuang
    Contributor

    Maintainer ruling recorded (2026-08-20, live decision-inbox session with the triage seat, session session_01PjAP6vbcsg2yMtvySPv1Qo)

    Ruled: the second direction — unbundled Node consumption is NOT supported for style-carrying plugin packages, as a permanent statement rather than debt. Provenance: maintainer accepted the batch, verbatim: 「其他接受你的建议。」Taken over the group as the card asks: @object-ui/plugin-dashboard, @object-ui/app-shell (via its static imports), and @object-ui/plugin-map together. No known unbundled-Node consumer exists; converting a zero-pull capability gap into permanent maintained machinery is the shape the startup rule rejects.

    Implementation: reword LOAD_DEBT for these three from debt to a stated permanent boundary (the ledger becomes honest rather than aspirational), keep the specifier leg as the hard guard it already is, and note the boundary in the packages' READMEs so a future Node consumer learns it from the package, not from a red import. Revisit only on a real consumer request — which would reopen this as a design card, not a debt payment.

    State transition in the same stroke: needs-user-decision → pm:queue.


    Generated by Claude Code

  4. claude commented on Aug 20, 2026

    @claude
    Contributor

    Claim: PM loop round 21
    Session: session_01RV6yuVCxymHYE16PL9vQkE
    Branch: claude/issue-5384-loaddebt-permanent-exemption
    Worktree: objectui-issue-5384
    Domain: repo:objectui
    File surface: the LOAD_DEBT ledger + its gate/docs; ⛔ NOT the CSS imports (stop on breach; explain in the report)
    Container & model: S, mode:subagent
    Clause-②: no
    Serial constraints cleared: none


    Generated by Claude Code

  5. os-support-ai commented on Aug 20, 2026

    @os-support-ai
    CollaboratorAuthor
    {
      "issue": 5384,
      "status": "done",
      "branch": "claude/issue-5384-loaddebt-permanent-exemption",
      "pr": "https://github.com/objectstack-ai/objectui/pull/5455",
      "premise_still_valid": true,
      "summary": "Reworded the LOAD_DEBT ledger from debt into a stated permanent boundary over the three style-carrying packages as a group, per the ruling — the CSS imports are untouched (that was option A, ruled against). The map is now UNBUNDLED_NODE_UNSUPPORTED; its docblock carries the ruling, its date, the verbatim provenance, the measured failure (ERR_UNKNOWN_FILE_EXTENSION for all three), and what would reopen it; each entry states its own boundary and cites objectui#5384; the run prints them as `by design` rather than `ledgered` plus a named summary line. The ruling's other two clauses are done too: the specifier leg is untouched and is documented as the guard that carries these three, and plugin-dashboard/plugin-map/app-shell each gained a 'Requires a bundler' README section with the real error text. A new unit pin makes a bare, un-cited exemption fail the suite. The ruling's premise was re-measured, not inherited: every consumer goes through a bundler (vite; Next transpilePackages, not serverExternalPackages), examples/schema-catalog only imports JSON files from plugin-named directories, and the sibling objectstack repo's four hits are all string literals — every zero counter-probed with a term known present.",
      "tests": "Local union re-run at HEAD=77d894b76 (tree clean), all green: check:esm-specifiers ('no un-ledgered package emits an extensionless relative specifier'), check:control-bytes (4510 files), changeset:check, docs:check-links ('valid across 13 scan roots'), type-check:scripts, check:doc-snippets ('87 of 87 block(s) judged, 0 failed'), vitest check-node-esm-load.test.ts + ci-cd-pipeline-doc.test.ts ('Tests 62 passed (62)'), and the full load leg. BEFORE/AFTER counts, measured over ONE build of every published package (turbo: '43 successful, 43 total') by running the origin/main gate and this branch's gate against the SAME artifacts with --no-build: before = 'Load leg: 34 of 39 published ESM entries imported and evaluated', exit 0; after = 'Load leg: 34 of 39 published ESM entries imported and evaluated', exit 0. THE NUMBER DOES NOT MOVE, as this card intends — only the three lines change from 'ledgered …' to 'by design …' plus a new summary line naming them. All three fail with ERR_UNKNOWN_FILE_EXTENSION, confirming the ledger's stated reason. Reverse verification of the new pin, predicted direction 'reddens' and observed as predicted: stripping 'by ruling objectui#5384' from the plugin-map entry gave 'Tests 1 failed | 29 passed (30)' on expect(reason).toMatch(/objectui#\\d+/); restoring gave '30 passed (30)'. No rebuild applies to this ablation and none was needed — the suite imports the gate as a source .mjs by relative path, not through a package exports map, so no dist/ sits between the edit and the assertion; the fix was committed before the mutation and restored by git checkout, verified byte-identical by a clean git status.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  6. os-support-ai commented on Aug 20, 2026

    @os-support-ai
    CollaboratorAuthor

    os-dev-report

    (Supersedes the previous comment, whose HTML-comment marker the GitHub sanitizer stripped on write — the marker is spelled as literal text here so a PM scan can see it. Same report, unchanged.)

    {
      "issue": 5384,
      "status": "done",
      "branch": "claude/issue-5384-loaddebt-permanent-exemption",
      "pr": "https://github.com/objectstack-ai/objectui/pull/5455",
      "premise_still_valid": true,
      "summary": "Reworded the LOAD_DEBT ledger from debt into a stated permanent boundary over the three style-carrying packages as a group, per the ruling — the CSS imports are untouched (that was option A, ruled against). The map is now UNBUNDLED_NODE_UNSUPPORTED; its docblock carries the ruling, its date, the verbatim provenance, the measured failure (ERR_UNKNOWN_FILE_EXTENSION for all three), and what would reopen it; each entry states its own boundary and cites objectui#5384; the run prints them as `by design` rather than `ledgered` plus a named summary line. The ruling's other two clauses are done too: the specifier leg is untouched and is documented as the guard that carries these three, and plugin-dashboard/plugin-map/app-shell each gained a 'Requires a bundler' README section with the real error text. A new unit pin makes a bare, un-cited exemption fail the suite. The ruling's premise was re-measured, not inherited: every consumer goes through a bundler (vite; Next transpilePackages, not serverExternalPackages), examples/schema-catalog only imports JSON files from plugin-named directories, and the sibling objectstack repo's four hits are all string literals — every zero counter-probed with a term known present.",
      "tests": "Local union re-run at HEAD=77d894b76 (tree clean), all green: check:esm-specifiers ('no un-ledgered package emits an extensionless relative specifier'), check:control-bytes (4510 files), changeset:check, docs:check-links ('valid across 13 scan roots'), type-check:scripts, check:doc-snippets ('87 of 87 block(s) judged, 0 failed'), vitest check-node-esm-load.test.ts + ci-cd-pipeline-doc.test.ts ('Tests 62 passed (62)'), and the full load leg. BEFORE/AFTER counts, measured over ONE build of every published package (turbo: '43 successful, 43 total') by running the origin/main gate and this branch's gate against the SAME artifacts with --no-build: before = 'Load leg: 34 of 39 published ESM entries imported and evaluated', exit 0; after = 'Load leg: 34 of 39 published ESM entries imported and evaluated', exit 0. THE NUMBER DOES NOT MOVE, as this card intends — only the three lines change from 'ledgered …' to 'by design …' plus a new summary line naming them. All three fail with ERR_UNKNOWN_FILE_EXTENSION, confirming the ledger's stated reason. Reverse verification of the new pin, predicted direction 'reddens' and observed as predicted: stripping 'by ruling objectui#5384' from the plugin-map entry gave 'Tests 1 failed | 29 passed (30)' on expect(reason).toMatch(/objectui#\\d+/); restoring gave '30 passed (30)'. No rebuild applies to this ablation and none was needed — the suite imports the gate as a source .mjs by relative path, not through a package exports map, so no dist/ sits between the edit and the assertion; the fix was committed before the mutation and restored by git checkout, verified byte-identical by a clean git status.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions