Skip to content

fix(console): 宿主重定向直发规范 metadata 路由,不再绕已废弃的 component/metadata 别名 (#3639) - #3658

Merged
yinlianghui merged 1 commit into
mainfrom
claude/issue-3639-host-redirect-canonical
Aug 7, 2026
Merged

yinlianghui merged 1 commit into
mainfrom
claude/issue-3639-host-redirect-canonical

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes #3639

前提复核(先证实,再动手)

issue 正文的行号在最新 origin/main(54dd7ec1)上逐条命中,前提成立:apps/console/src/AppContent.tsx 的 ObjectRedirect(:47-51 docblock / :52-60 实现)与 MetadataRedirect(:62-67 / :68-82)确实仍在改写到 component/metadata/resource,而 packages/app-shell/src/console/AppContent.tsx 把这个拼法声明为 LegacyMetadataRedirect(:775-776 有应用分支、:646-647 零应用分支),规范落点是 metadata/:type[/:name]。

改了什么

两个宿主重定向直接构造规范目标,不再喂别名:

入口 URL 改前(两跳) 改后(一跳)
system/objects component/metadata/resource?type=object → metadata/object metadata/object
system/objects/:objectName component/metadata/resource/:name?type=object → metadata/object/:name metadata/object/:name
system/metadata component/metadata/directory → metadata metadata
system/metadata/:type component/metadata/resource?type=:type → metadata/:type metadata/:type
system/metadata/:type/:name component/metadata/resource/:name?type=:type → metadata/:type/:name metadata/:type/:name

落点与旧的两跳逐字节等价,这是刻意对齐 LegacyMetadataRedirect 的翻译逻辑得到的,不是巧合:

  • :type 做 encodeURIComponent —— 别名跳自己就是这么做的(它从 ?type= 读出再编码进 path)。旧写法把 metadataType 裸拼进 query,遇到含 & 的类型名会被 URLSearchParams 截断;现写法不会。对现实中的类型名(object / datasource …)两者恒等。
  • :name / :objectName 原样透传 —— 别名跳把 path 尾巴原样接上,没有编码。保持一致,不顺手"修"。
  • query 与 hash 照旧丢弃 —— 别名跳的 resource 分支本来就不转发 location.search,宿主也从不转发入参 query,所以旧链路同样是光秃秃的 path 落地。保留这个行为并专门写了一条测量用例钉住,免得下一个读者以为是漏掉的。

另外订正四段把别名说成 "the engine route" 的 docblock:apps/console 两处 + datasource/register.ts:9 + DatasourceResourcePage.tsx:6(裁决第 5 条)。这不是文字洁癖 —— #3610 的派发单正是顺着这句话推出"component/... 才是规范拼法"的,与路由表恰好相反。

生产端枚举(issue 的 ⚠️ 要求)

全仓 grep component/metadata,按生成 / 声明接收 / 测试钉形态 / 注释分类:

位置 类别 本 PR 处置
apps/console/src/AppContent.tsx:57-58(ObjectRedirect) 生产端 ✅ 改
apps/console/src/AppContent.tsx:75-80(MetadataRedirect) 生产端 ✅ 改
apps/console/src/pages/system/SystemHubPage.tsx:118("Metadata" 卡片 → directory) 生产端(第三处) 围栏外 → 立单
apps/console/src/pages/system/SystemHubPage.tsx:126("Datasources" 卡片 → resource?type=datasource) 生产端(第四处) 围栏外 → 立单
packages/app-shell/src/layout/AppSidebar.tsx:330(sys-datasources) 生产端(第五处) 围栏外(⛔ app-shell) → 立单
packages/app-shell/src/layout/UnifiedSidebar.tsx:336(sys-datasources) 生产端(第六处) 围栏外 → 立单
packages/app-shell/src/console/AppContent.tsx:646-647 / 775-776 路由声明(接收) 不动(裁决第 3 条)
packages/app-shell/src/console/AppContent.tsx:951-952(LegacyMetadataRedirect docblock) 声明侧文档 不动 —— 它的表述本来就是对的
packages/app-shell/src/layout/__tests__/systemNavSettingsTarget.test.tsx:162 测试钉形态 不动
packages/app-shell/src/console/__tests__/AppContent.noAppComponentRoutes.test.tsx:169-171 测试内 MetadataRedirect 逐字转录副本 不动 → 立单(见下)
packages/app-shell/src/services/componentRegistry.ts:28 注释(注册表键→路径约定的举例,非 URL 生产) 不动
packages/app-shell/src/views/metadata-admin/ResourceRouter.tsx:63 注释(休眠分发器自述,属遮蔽议题) 不动(另行分诊)
datasource/register.ts:9、DatasourceResourcePage.tsx:6 注释 ✅ 订正(裁决第 5 条)

一共 6 处生产端,本 PR 修其中 2 处(围栏内),另 4 处按"越界即停"立单。它们与本 PR 的两处是同一机制(导航直接指向别名 → 多一跳),但分布在 apps/console 的 SystemHub 卡片与 packages/app-shell 的两个 sidebar,修法应当一次性统一决定,而不是拆成两半。

测试

新增 apps/console/src/__tests__/AppContent.legacyRedirects.test.tsx(7 例)。关键点是它导入生产代码的 systemRoutes fragment(为此把它 export 出来),而不是把路由抄一遍 —— 抄一份正是 component/metadata/resource 这个拼法能在此处存活这么久的原因。

ChainRecorder 记录路由每一次停靠的位置,所以断言针对的是整条链而非只有终点:只看终点无法区分一跳与两跳(都是 metadata/object 收尾),而一跳与否恰恰就是本单的全部内容。路由表里同时声明了规范路由与别名路由(都作为终端探针),这让方向可证伪。

 Test Files  1 passed (1)
      Tests  7 passed (7)

逆向验证(先预测,后运行)

预测的方向:红,且失败现场应指认"中间/落点仍是别名"。 只回退两处 target 构造(docblock 与测试保持不动)后实跑:

 × system/metadata/:type reaches metadata/:type in ONE hop
 × system/metadata/:type/:name reaches metadata/:type/:name in ONE hop
 × bare system/metadata reaches the metadata directory in ONE hop
 × system/objects/:objectName reaches metadata/object/:name in ONE hop
 × bare system/objects reaches the object list in ONE hop
 × the app prefix is preserved, not just stripped to the root
 × MEASUREMENT: the endpoint is byte-identical to what the old two-hop produced
 Test Files  1 failed (1)
      Tests  7 failed (7)

七条全红,且每条 diff 都直接打印出别名:

+   "/apps/x/component/metadata/resource?type=object"
+   "/apps/x/component/metadata/resource/account?type=object"
+   "/apps/x/component/metadata/directory"
+   "/apps/my-app/component/metadata/resource?type=datasource"

方向与预测一致,无反转。

别名仍可达(裁决第 3 条的回归证据)

别名路由一行未动,证据是 packages/app-shell/src/console/__tests__/AppContent.noAppComponentRoutes.test.tsx 全绿 —— 它直接以 /apps/setup/component/metadata/resource?type=datasource 起步并断言落到 /apps/setup/metadata/datasource:

 Test Files  1 passed (1)
      Tests  6 passed (6)

顺带一提:该文件里那份 MetadataRedirectStub 是 apps/console MetadataRedirect 的逐字转录(注释明写 "keep identical to the original")。本 PR 之后它不再逐字,而且它的 sys-objects 用例现在测的是一条生产中已不存在的链路 —— 绿是因为 stub 自己仍走别名,不是因为生产代码走。属于"该整例替换而非重拼"的那一类,已立单,未在此 PR 内改(围栏外,且 #3638 在途于同一组件的测试面)。

其余门禁

pnpm vitest run --project "@object-ui/console"          → 24 files / 219 tests passed
pnpm vitest run packages/app-shell                      → 292 files / 2545 passed, 1 skipped
pnpm --filter @object-ui/console --filter @object-ui/app-shell type-check → Done / Done
pnpm --filter @object-ui/console lint                   → 0 errors(190 条既有 warning)
pnpm --filter @object-ui/app-shell lint                 → 0 errors(2205 条既有 warning)
node scripts/check-control-bytes.mjs                    → OK(3661 个文本文件)

type-check 首跑报了一片 Cannot find module '@object-ui/*' —— 是新 worktree 未构建依赖所致(AGENTS.md §9 陈旧产物陷阱的镜像),pnpm --filter '@object-ui/app-shell^...' build 之后即全绿,与本改动无关。

遮蔽关系测量(裁决第 6 条:只测量不动)

react-router 打分(静态段 10 / 动态段 3 / splat -2,initialScore = 段数 + splat 罚分):

路由 计算 得分
component/metadata/resource/* 4 - 2 + 10 + 10 + 10 32
component/:ns/:name/* 4 - 2 + 10 + 3 + 3 18

修前 = 修后,完全没变。 本 PR 的 diff 里 packages/app-shell/src/console/AppContent.tsx 零改动(改的只有 datasource/ 下两个注释),路由声明、顺序、打分一行未动;打分是同一个 Routes 内的静态属性,与谁往这些 URL 发流量无关。所以注册表组件 metadata:resource 依旧被恒压,MetadataResourceRouter 依旧休眠。

本 PR 唯一改变的是流量:apps/console 不再把 system/metadata/* 与 system/objects/* 送进别名。别名仍由 sidebar 的 sys-datasources、SystemHub 两张卡片以及书签/外链喂着(见上面的枚举表),所以别名远未变成死路 —— 这也是保留它的理由本身。

一并确认 issue 评论区那条"不要以为 datasource 定制页也死了":ResourceListPage.tsx:98-104 的规范列表页自己查注册表(getMetadataResource(type)?.ListPage),所以 metadata/datasource 渲染的仍是 DatasourceResourcePage。休眠的只有 MetadataResourceRouter 这一层分发器。其处置(删除 vs 保留)按裁决另行分诊,本 PR 不碰。

changeset

写了。判断依据:.changeset/config.json 的 ignore 只有 @object-ui/example-* 与 @object-ui/site,@object-ui/console(即 apps/console,private 未设、version 17.3.0)与 @object-ui/app-shell 都在 39 包 fixed 组内,会随组发布,因此需要 changeset。按仓规标 patch,绝不标 major。


🤖 Generated with Claude Code

https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt


Generated by Claude Code

…the deprecated alias (#3639)

`ObjectRedirect` and `MetadataRedirect` rewrote `system/objects[/:name]` and
`system/metadata[/:type[/:name]]` onto `component/metadata/resource[/:name]?type=`.
app-shell declares that spelling as a legacy alias whose element is
`LegacyMetadataRedirect` — a second `<Navigate>` onto `metadata/:type[/:name]`.
Both host redirects now build that canonical target directly, so the chain is
one hop instead of two.

Endpoints are unchanged byte for byte, including the alias hop's own
percent-encoding of `:type` and its verbatim pass-through of `:name`. The alias
routes are untouched and still forward bookmarks/external links.

Also corrects four docblocks that called the alias "the engine route" — the
wording that led the #3610 dispatch to conclude `component/metadata/resource`
was canonical, which is the opposite of what the route table says.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt
@vercel

vercel Bot commented Aug 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectui Ignored Ignored Aug 7, 2026 5:36pm

Request Review

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 28.0 KB 350 KB
Entry file index-BvC9Kz13.js —
Status PASS —

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 8.66KB 3.13KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 7.57KB 2.97KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 22.10KB 4.37KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.13KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.64KB 2.21KB
auth (SocialSignInButtons.js) 9.60KB 3.89KB
auth (UserMenu.js) 3.40KB 1.22KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 35.76KB 9.11KB
auth (createAuthenticatedFetch.js) 4.37KB 1.69KB
auth (index.js) 2.35KB 1.07KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 4.91KB 0.87KB
auth (useIsWorkspaceAdmin.js) 1.61KB 0.85KB
collaboration (CommentThread.js) 26.07KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.65KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 480.72KB 105.64KB
core (index.js) 2.96KB 1.13KB
create-plugin (index.js) 9.28KB 2.98KB
data-objectstack (index.js) 137.51KB 35.11KB
fields (index.js) 230.87KB 56.83KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.32KB 1.77KB
i18n (index.js) 2.65KB 1.06KB
i18n (pickLocalized.js) 1.70KB 0.83KB
i18n (provider.js) 9.48KB 3.27KB
i18n (useObjectLabel.js) 26.14KB 6.07KB
i18n (useSafeTranslation.js) 4.52KB 1.96KB
layout (index.js) 38.53KB 10.71KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.74KB
mobile (index.js) 1.50KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.71KB 0.42KB
mobile (useResponsiveConfig.js) 1.36KB 0.63KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 8.75KB 3.06KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 3.67KB 1.12KB
permissions (evaluator.js) 4.41KB 1.44KB
permissions (index.js) 0.91KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.52KB
permissions (usePermissions.js) 1.55KB 0.71KB
plugin-ai (index.js) 15.71KB 3.79KB
plugin-calendar (index.js) 44.98KB 12.37KB
plugin-charts (index.js) 61.04KB 17.31KB
plugin-chatbot (index.js) 180.09KB 42.72KB
plugin-dashboard (index.js) 115.50KB 29.96KB
plugin-designer (index.js) 210.51KB 42.51KB
plugin-detail (index.js) 232.79KB 57.42KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 112.10KB 27.10KB
plugin-gantt (index.js) 162.55KB 39.57KB
plugin-grid (index.js) 186.61KB 49.34KB
plugin-kanban (index.js) 48.30KB 13.28KB
plugin-list (index.js) 105.12KB 25.48KB
plugin-map (index.js) 16.81KB 5.24KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 40.58KB 10.58KB
plugin-timeline (index.js) 25.76KB 7.33KB
plugin-tree (index.js) 8.50KB 2.88KB
plugin-view (index.js) 84.03KB 20.55KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.71KB 3.53KB
providers (index.js) 0.44KB 0.22KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.67KB 2.37KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 19.28KB 6.38KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.02KB 0.55KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 4.09KB 1.74KB
sdui-parser (index.js) 4.47KB 2.03KB
sdui-parser (parse.js) 10.04KB 2.82KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 4.69KB 1.48KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.87KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-retry.js) 4.32KB 2.02KB
types (index.js) 2.71KB 1.34KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 2.59KB 1.31KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (spec-report.js) 5.05KB 1.93KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 3.40KB 1.71KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@yinlianghui
yinlianghui marked this pull request as ready for review August 7, 2026 17:44
@yinlianghui
yinlianghui added this pull request to the merge queue Aug 7, 2026
Merged via the queue into main with commit 7883c02 Aug 7, 2026
19 checks passed
@yinlianghui
yinlianghui deleted the claude/issue-3639-host-redirect-canonical branch August 7, 2026 17:45
github-merge-queue Bot pushed a commit that referenced this pull request Aug 8, 2026
* docs(app-shell): 两个 AppContent 路由测试的生产端叙述改写为「历史 + 现状」两段 (#3749)

三处注释以现在时枚举「侧边栏 / QuickActions 现在发哪些 URL」,而这些 URL 已被
#3660(`sys-datasources`)与 #3739(`sys-objects` + 首页 Manage Objects 卡片)改指
metadata-admin 引擎的规范路由。断言全部正确且全绿 —— 陈旧的只有叙述,以及两个以
生产端命名的 `it` 标题:它们实际量的是别名路由仍能解析且只跳一次。

手法照 #3666:每处拆成「#3610 当时如此」(过去时)与「#3660/#3739 之后如此」
(现在时)两段,现状段正面陈述两条别名今天的身份 —— 不再是任何导航的目标,而是
书签与外部链接的到达路径,这正是重定向必须继续工作的理由。按 #3656,现状段不靠
「不再指向 X」的否认句复述别名 URL。

- `AppContent.noAppComponentRoutes.test.tsx`:头注拆段;两个 `it` 标题从
  `sys-datasources:` / `sys-objects:` 改为 `shell alias:` / `host alias:`,即两条
  别名各自的改写者(shell 自己的 `LegacyMetadataRedirect` vs 宿主的
  `MetadataRedirect`)—— 文件正文本来就画了这条区分。用例体内两处重申同一陈旧
  断言的行内注释同步(`sys-datasources` item still points straight at this
  spelling / this case now measures what production actually does),否则改完标题
  的文件会自相矛盾。顺带把同一段落里 `isMetadataRoute` 的现在时「substring test」
  收敛为过去时 —— #3638 起它是 `pathSegments.includes('metadata')` 段测试。
- `AppContent.pseudoRouteSegments.test.tsx`:生产端表格按实读重写(逐条读自
  `AppSidebar` / `UnifiedSidebar` / `QuickActions` / `HomePage` / `InboxPopover`),
  并补一段说明两条旧拼写并未消失,只是从 `navigation` 行移到了上面两行的到达面。
  `metadata` 两种拼写下都是完整路径段,所以本文件要证伪的论断不受影响。
- `AppContent.tsx`(#3610 那段):收尾句补上 #3739 也已重指,并注明 `sys-objects`
  的链路早在 #3658 就离开了这两条路由。该段前半的过去时叙述保留。

零行为改动:两个测试文件跑前跑后同为 43 passed,断言与用例数未动。
无 changeset —— 注释-only,照 #3666(bd04651)与 #3785(f9d70a7)先例。

Fixes #3749

* chore(changeset): 为 #3749 注释改写声明「不发布任何东西」(空 frontmatter)

CI 的 Changeset Declaration 门(scripts/check-changeset-presence.mjs)守的是
「fixed 组内任一包的 `<pkg>/src/**` 被改动 -> 必须有 `.changeset/*.md` 声明」,
本次三个改动文件全部落在 packages/app-shell/src 下,所以门要求声明。

先前照 #3666(bd04651)与 #3785(f9d70a7)判断「注释-only 不带 changeset」是
读错了先例:那两个 PR 早于这道门(#3387 引入),且 #3785 只动 content/docs/**,
根本不在守护面内。门自己写明了正确出口 —— 「If this change really should release
nothing, say so — that is a pass, not a workaround」,即空 frontmatter,照
.changeset/registry-inputs-spec-parity-gate-3797.md 的先例写法。

空 frontmatter 而非 patch:AppContent.tsx 只改 JSX 注释,两条
LegacyMetadataRedirect 路由声明与其余每一行代码未动;两个测试文件断言与用例数
逐字节未变(前后同为 43 passed)。确无可发布的行为改动。

本地验证:
  node scripts/check-changeset-presence.mjs  -> exit 0(识别为空 frontmatter 豁免)
  node scripts/check-changeset-no-major.mjs   -> exit 0

---------

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/objectui that referenced this pull request Aug 10, 2026
…stack-ai#3661) (objectstack-ai#3671)

PR objectstack-ai#3658 把 apps/console 的 MetadataRedirect 改成直发规范拼法
(7883c02),但 app-shell 这份自称「逐字转录」的 stub 没跟着改。
于是 sys-objects 用例测的是一条生产中已不存在的两跳链 —— 它仍绿,
是因为 stub 自己还在往别名跳,不是因为生产代码这么做。

- stub 的 target 构造同步为 objectstack-ai#3658 后的宿主实现(可执行代码逐字节一致),
  docblock 注明转录对象、出处 commit,以及「逐字」这个声明需要持续兑现。
- LocationProbe 升级为整链记录器,renderConsoleAt 返回链;四条重定向
  用例改为 toEqual 整个数组 —— 只断终点分辨不出一跳与两跳,而这正是
  本单的全部内容。
- 补一条直接进入 component/metadata/directory 的用例:该别名在零应用
  分支原本只有 stub 的 typeless 臂在跑,stub 同步后会失去全部覆盖。


Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/objectui that referenced this pull request Aug 10, 2026
…整链断言钉住 (objectstack-ai#3669) (objectstack-ai#3691)

第二份 MetadataRedirectStub 自称逐字转录宿主 MetadataRedirect,但 objectstack-ai#3658 把宿主
改为直接构造 canonical `…/metadata/:type` 后未同步,仍构造已废弃的
`component/metadata/resource?type=` 别名。被它喂的两条 `system/metadata/*` 用例
因此在测一条生产中已不存在的两跳链,却始终是绿的 —— 断言只钉终点,而终点在
一跳与两跳下逐字节相同。

- stub 按 7883c02 后的宿主实现逐字同步(可执行代码经机器比对逐字节一致);
- LocationProbe 升级为链记录器,renderConsoleAt 返回整条重定向链;
- 两条受影响用例从 it.each 表中提出为独立 it,改断整条链(原有断言全部保留)。

objectstack-ai#3638 落的输入面回归断言与近似段族断言一行未动。测试-only,无 changeset。


Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

console(apps/console): MetadataRedirect / ObjectRedirect 改写到已废弃的 component/metadata/resource 别名,多绕一跳且 docblock 已过期

2 participants