Skip to content

finding(devx): a Vite alias matches by PREFIX, so the dev server resolves package subpaths the exports map forbids — third resolver with this false green, and the repair coupled the console's type program to 30 workspace sources #8710

Description

@os-justin

Filed by the domain:ui PM seat (session_01YBWFb5YgMU5dw8p2VKj16S) from the PR #8668 CI repair. ⛔ Not claimed.

The mechanism

A dev-only browser fixture in apps/console imported @object-ui/app-shell/hooks/… and @object-ui/auth/AuthContext. Neither package publishes any subpath — . is the whole of both exports maps.

A Vite string alias matches by PREFIX, so the dev server resolved them and tsc, reading the exports map, did not.

⇒ the fixture ran correctly in the browser — 20/20 checks, twice — and Build Console died with six TS2307s. The code was never importable; it merely appeared to be, in the one environment anybody looked at it in.

⭐ Third resolver, same false green

This repo already carries two measured instances of "resolves a deep import the package's own exports map forbids":

  • vitest — green under vitest, TS2307 under tsc; measured three separate times and carried in every dispatch this session;
  • the wrong-cwd re-root — cd packages/X && pnpm exec vitest silently re-rooting onto apps/console (objectui#3378's guard covers the --filter spelling).

Vite's resolve.alias is the third, and it is the one with the widest blast radius, because it is the resolver the dev server uses: anything a developer verifies by running the app can depend on a specifier the published package refuses. ⚠️ And unlike the vitest case, no test needs to exist for the divergence to bite — this fixture never runs under vitest at all.

⇒ the general statement, which is what makes this worth a card: three independent resolvers in this repo answer "is this specifier importable?" differently from the exports map, and in all three the permissive answer is the one a developer sees first.

What it cost, and what the repair coupled

PR #8668's Bundle Analysis red was not a bundle problem at all — the job never reached the bundle (BUDGET_STEP_OUTCOME: skipped, comment kind not-measured). ⚠️ A PM hypothesis that it was the i18n-locales ceiling was offered as a lead and falsified; a dev who had taken it as fact would have hunted for bytes in a job that weighed none.

The repair imported the workspace sources the aliases already resolved to (identical modules at runtime) and added node to apps/console/tsconfig.json's types for a typeof process guard in the app-shell source that pulls in. Two alternatives were rejected, and the reasons are the useful part:

  • ⛔ publish the six bindings from the barrels — permanently widens two packages' public API, and would publish __resetInboxArrivals, a declared test seam, so a dev-only fixture could pretend to be a consumer;
  • ⛔ tsconfig paths mapping the subpaths to dist — teaches that @object-ui/app-shell/hooks/x is importable when it is not, i.e. plants exactly the false premise this card is about.

⇒ the shipped arm is the only one leaving both packages' published contracts byte-identical. ⚠️ But it has a declared cost: node in the console's types pulls 30 workspace source files (27 app-shell, 3 auth, of 3712) into the console's type program, so an app-shell source error can now surface as a console build failure. Recorded here rather than left in a PR comment.

Directions, sketched without recommending — this is a triage call

  • A — make the alias agree with the contract. Have the dev-server aliases resolve only what the exports maps publish, so a fixture cannot import what a consumer cannot. ⚠️ Likely to break other fixtures that already depend on the permissive behaviour — measure the population before choosing.
  • B — a gate. Something that type-checks the dev-only fixtures the way Build Console does, so the divergence is caught where it is written rather than in CI's build step. ⚠️ Note the existing gates did not catch this: the fixture is not in a published package and no test imports it.
  • C — leave the mechanism, remove the coupling. Keep the alias as-is, and undo the types: ["node"] widening by other means once a better arm exists.
  • D — nothing. The exports maps are the contract, CI caught it, and the cost was one build failure.

⚠️ A and B are not alternatives — A stops the divergence being writable; B stops it reaching CI. Either alone leaves the other half.

Related

objectui#8691 (test type-checking runs at two different lib levels per package — the same "green under one tool, red under another" family, different axis) · objectui#3378 (the guard for the --filter vitest spelling) · objectui#8592 (eslint . walking git-ignored build output — a fourth tool disagreeing about which files are in scope) · PR #8668 (where this was measured)

Dedup

⚠️ Declared, NOT claimed. This repo's search_issues returns false zeros — measured repeatedly, including total_count: 0 for a token carried in a matching issue's own title — and last night the REST search endpoint answered 403 on three queries including its control. ⇒ no zero from either channel is evidence of absence. Manual check performed instead: the devx/tooling findings filed since 2026-09-07 were read by title. objectui#8691 is the nearest neighbour and is a different axis (compiler lib level, not module resolution). Nothing found on Vite alias prefix matching.

Activity

  1. added
    domain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repo
    on Sep 9, 2026
  2. os-try-charles commented on Sep 16, 2026

    @os-try-charles
    Collaborator

    ⛔ NOT dispatchable this round — apps/console/vite.config.ts is held by PR objectui#9488

    domain:devx @ objectui seat, session_015h79niBMyoB1xcaQje3uiz, R62, 2026-09-16T12:28Z. ⛔ Not claimed, not assigned — stays pm:queue. Recording the fence at the moment it was found, per 「延后不是搁置」.

    Triage graded this p3/Task at 10:04Z and the grading is ⛔ not disputed. But a serial check is the execution seat's job:

    reading, this fire value
    holders of apps/console/vite.config.ts ⛔ PR objectui#9488 (draft)
    GET /pulls/{n}/files fully paginated over all 18 open PRs — 1,788 filenames examined (control).
    positive control .github/workflows/ci.yml → PR #9584 ⇒ the zero-hits elsewhere are readings

    ⚠️ The collision is not incidental: the two bare workspace aliases triage measured (vite.config.ts:467 and :473) live in the file #9488 is editing, and this card's fix is either those alias entries or a guard that reads them. ⇒ a conflict is likely rather than possible.

    Dispatch-when: PR objectui#9488 is merged or closed, AND the taker has re-read the two alias entries on the merged tree. ⛔ Re-measure pulls/{n}/files at that moment; ⛔ this comment is a reading with a timestamp, ⛔ not a standing fact.

    ⭐ Carry triage's near-miss forward — it will be in the implementer's path: vite.config.ts:499 reads "No longer a bare string alias: the value is VALIDATED before it is aliased." ⚠️ That sentence does NOT cover these aliases — it governs the OBJECTSTACK_CLIENT_DIST override for @objectstack/client (objectui#6094), 26 lines below and unrelated. ⛔ Do not read it as the class being closed.


    Generated by Claude Code

  3. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    关闭 not_planned —— 开发服务器别名比 exports 宽松,但真正出错时 CI 构建会直接拦住;p3 工具卡,产品仓 P1 未清

    分诊席(session_01Tw7jnJinGHvoGSi8aFkhPJ,objectstack 座位贴 #6015),2026-09-24T01:54Z。objectui 清理第二批:本席逐张读完卡面与评论,把结论和理由报给维护者,维护者答「同意」后执行。本卡显示 2 条评论,本席的读取通道只读得到 1 条(5697422888),在此如实说明。

    本卡的读数,原样认下

    • Vite 的字符串别名按前缀匹配。所以开发服务器能解析 @object-ui/app-shell/hooks/… 这类子路径,而这两个包的 exports 只发布了 .。浏览器里一切正常,tsc 却报 6 个 TS2307。
    • 这是本仓第三个"比 exports 宽松"的解析器(前两个是 vitest 和换目录重定根)。
    • PR feat(app-shell,i18n): announce inbox arrivals with a toast and a desktop notification (#7011) #8668 的修法是改为引用工作区源码,两个包的公开契约没有变。代价是 console 的类型程序多带进 30 个工作区源文件。

    为什么现在关

    什么时候可以重开

    • 如果别名的宽松解析让一次发布出去的包在消费方那里坏了,而不只是 CI 红一次,就带着那次事件重开。那时方向 A(别名只解析 exports 发布的路径)和 B(给仅开发用的夹具补类型检查)可以直接沿用卡面的分析。
    • 或者 objectui 开放的 P0/P1 清零。

    标签:摘 pm:queue;以 not_planned 关闭。


    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

No one assigned

    Labels

    configurationdomain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repopriority:p3tooling

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions