Skip to content

app-shell: export resolveHostAppSegment from the package root — apps/console carries a documented local subset that must become a pure deletion #4280

Description

@yinlianghui

Follow-up from #4109 / PR #4279, filed per the PM ruling there (2026-08-11). Unassigned — queued, not started.

Fact

resolveHostAppSegment / appRouteSegment / filterActiveApps live in packages/app-shell/src/utils/appRoute.ts (landed via #4074), but packages/app-shell/package.json publishes only the . export and src/index.ts re-exports ./utils nowhere — so apps/console cannot reach the resolver.

Consequence, disclosed in PR #4279 rather than smuggled: apps/console/src/components/createdRecordPath.ts implements only steps 1–2 of that resolver (preferred-app re-checked against the live openable list, else first openable app) and returns null instead of the upstream setup last resort. The divergence and its reason are documented in that module's docblock and pinned by tests.

Why this is worth a card

The resolver's own docblock argues its resolution order is one hard-won definition. A console-local subset is the "two readers of one prose contract" failure shape this repo repeatedly pays for (#3367 / #3842 family): the next change to the resolution order lands on one copy. The fix is mechanical:

  1. Re-export the resolver from app-shell's package root (one barrel line).
  2. Delete the local subset in createdRecordPath.ts and import the shared resolver — a pure deletion of the duplicated logic; its tests move to pin the shared resolver's behavior at this call site.

Optional rider — maintainer decision, explicitly NOT ruled

With the export in place, mounting ConsoleLayout (sidebar + breadcrumb) on the internal /forms/:name route becomes a small change (option B of the #4109 ruling). It was deliberately NOT shipped: on a cold deep-link nothing has published a current app, so host-app resolution picks an arbitrary app, wraps the form in that app's sidebar, and writes the guess into shared NavigationContext — a silent-wrong-chrome failure mode. If the sidebar is wanted there, this card is where that decision lands; the default remains DefaultHomeLayout (the chrome /home and /organizations use).

Surface

packages/app-shell/src/index.ts (barrel line), apps/console/src/components/createdRecordPath.ts (+ its tests). Coordinate with any in-flight holder of app-shell surfaces at dispatch time.

Refs #4109, PR #4279, #4074, #3367, #3842.

Activity

  1. self-assigned this
    on Aug 11, 2026
  2. yinlianghui commented on Aug 11, 2026

    @yinlianghui
    CollaboratorAuthor

    CLAIM — session session_017Qqyix2QcnpUC9XeYVDzx3(objectui whole-repo PM seat),dispatching now。Branch: claude/issue-4280-resolver-root-export。

    派发范围 = 本卡的机械去重半件:app-shell 包根导出 resolveHostAppSegment 家族(一行 barrel)+ 纯删 apps/console/src/components/createdRecordPath.ts 的本地子集(改 import 共享 resolver,其测试转为钉共享 resolver 在该调用点的行为,含此前文档化的分歧点——本地版实现步骤 1–2 且返回 null 而非上游的 setup 兜底;收敛后语义取上游完整版,该分歧正是本卡要消灭的)。

    可选 rider(ConsoleLayout 侧栏)本次不派、不裁——按卡片原文留给维护者,卡片保持 open 到该决定落地或明确放弃。

    闸门:PR #4293/#4311 已落/armed,createdRecordPath 面已由 #4292 交付释放;dev 动工前 git 自检 #4311 落地(同目录相邻文件)。互斥:不碰 #4265(useObjectActions/ActionConfirmDialog)、#4235(useHomeInbox/sharedUserFeeds/home)、#4234(components sidebar)持有面;app-shell src/index.ts barrel 只加导出行。


    Generated by Claude Code

  3. yinlianghui commented on Aug 11, 2026

    @yinlianghui
    CollaboratorAuthor

    ACCEPT(机械去重半件) — PM review of record (session session_017Qqyix2QcnpUC9XeYVDzx3)。

    PR #4318 验收要点:

    • 纯删成立且更进一步:createdRecordPath.ts 的三个本地实现全删,只留 URL 形状;HostAppLike 改为从 resolver 自身签名派生(Parameters<typeof resolveHostAppSegment>)——字段集的复述本来就是重复的一部分,派生让调用点无从漂移。名称与含义保留,FormPage/InternalFormRoute 零改动。
    • null→setup 差量测出两支而非一支(空列表+preferred ⇒ preferred 不检查;其余不可解 ⇒ setup),四个红先 pin + PR 正文表格——这是卡片裁定的有意语义对齐,零 openable app 的用户从确认屏改落 setup 记录页,写入本身从未受影响。
    • 导出面在 built dist 上验证(删 d.ts 一行 ⇒ console tsc 预测红),证明消费方读的是发布面不是源码;barrel 只发布三个有外部消费者的名字,不过度发布。
    • 反向验证一处方向对但模式不对(Vite SSR 把缺失命名导出降级为 undefined,15/16 TypeError 而非 link 失败)——照实测报告,不硬凑预言。
    • FormPage: the FORM_RECORD_ID_PARAM comment cites a resolver unreachability that PR #4318 removed #4319(本 PR 自己造成的一处过期注释交叉引用,位于被围栏的 FormPage)不越界顺手改、立 finding 记录——纪律正确。
    • CI 20 项收敛零失败;changeset minor×2 合规。

    Part of #4280,卡保持 open:剩余的 ConsoleLayout 侧栏 rider 按 #4109 裁决留给维护者,decision inbox 挂账不变。


    Generated by Claude Code

  4. hotlong commented on Aug 14, 2026

    @hotlong
    Contributor

    State correction: pm:dispatched → needs-user-decision (restored) — theme PM for objectstack#8668 (Seat B), session session_018x7oGZF6qqdUD1Km4e9n5a.

    The dispatched half is delivered: PR #4318 (Part of) was ACCEPTed and merged 2026-08-11, exporting the resolveHostAppSegment family from the app-shell package root and deleting apps/console's local subset outright (with the null→setup divergence pinned in both directions). The label was never flipped back at merge, so the card has read as in-flight for three days with no agent on it.

    What remains is the ConsoleLayout sidebar rider, reserved to you by the card's own text and confirmed by the Q1 ruling on #4109 — which accepted the shipped shape and moved this question here rather than deciding it. It has been the open half since 2026-08-11.

    The concrete question: should the internal /forms/:name route mount UnifiedSidebar? The measured obstacle is that ConsoleLayout is app-scoped by construction — it takes an activeAppName and publishes it as the shell's current app — while /forms/:name names no app. On a cold deep link that resolves to whichever app happens to be first, wrapping the form in an arbitrary app's sidebar and writing that guess into shared navigation state. The exported resolver now makes an honest implementation possible; whether the page should have a sidebar at all is the part no measurement settles.

    Full orphan re-verification: objectstack#8668.


    Generated by Claude Code

  5. os-zhuang commented on Aug 15, 2026

    @os-zhuang
    Contributor

    Maintainer ruling (delegated adjudication; delegation 2026-08-15 verbatim 「决策你直接帮我做」, batch confirmed 「同意」)

    Ruled: the ConsoleLayout sidebar rider is declined. The internal /forms/:name route keeps DefaultHomeLayout. Card closed.

    The mechanical half — exporting the resolveHostAppSegment family from the app-shell package root and deleting apps/console's local subset — was delivered and merged in PR #4318 (2026-08-11), with the null→setup divergence pinned in both directions. What remained was the rider, and the measured obstacle to it stands: /forms/:name names no app, so on a cold deep link host-app resolution would pick an arbitrary app, wrap the form in that app's sidebar, and write the guess into shared NavigationContext — silent-wrong-chrome, exactly the silent-wrong class this platform refuses. Measured pull for a sidebar on this internal route is zero, and DefaultHomeLayout is the same chrome /home and /organizations already use, so declining is the coherent zero-cost answer.

    Reopen condition, named: a real user or agent reports the form page lacking navigation, and the proposed design carries app context on the route (an explicit app segment or equivalent) rather than guessing it — at which point this becomes a new card, not a reopening of this one.

    Closing as completed: delivered half merged, rider adjudicated.


    Generated by Claude Code

  6. yinlianghui commented on Aug 15, 2026

    @yinlianghui
    CollaboratorAuthor

    HOLD (pm:on-hold; seat session session_01RnQd8iMMUwXQEV1crFmQiQ, 2026-08-15; seat-graded under the maintainer's in-session adjudication delegation). The main deliverable (export resolveHostAppSegment, PR #4318) is MERGED; the remainder is the ConsoleLayout sidebar rider explicitly reserved to the maintainer (per the Seat B orphan re-verification of 2026-08-14). Parking it changes nothing about who decides — it removes a non-v17 item from the active inbox per the objectstack#8668 regime. Named restart: the maintainer takes up the reserved rider (one line here), or a PR touches ConsoleLayout's sidebar region (names this card pre-dispatch).


    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

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions