Skip to content

fix(app-shell): metadata history Back to item opens the item under every route nesting - #29316

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-idkj68
Nov 2, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-idkj68

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steedos · project thread

Fixes #28626

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:元数据条目历史页的「Back to item」不再按匹配到的路由回退,改为从当前 URL 去掉最后一段(history)并保留查询串,因此回到 …/metadata/TYPE/NAME。
  • 为什么改:原写法 ../NAME 相对路由解析,会丢掉整条路由:有应用时落到以条目名命名的类型列表,无应用时落到应用根下的路径,并且丢掉 ?package= / ?scope=environment。目前在线上只有 Setup 的数据源历史页还会走到这条链接。
  • 风险与回滚:只改一个按钮的跳转目标;Studio「All metadata」区域的链接分支没动。回滚即还原 ResourceHistoryPage.tsx 一处。
  • 先看:ui/packages/app-shell/src/views/item-tools/ResourceHistoryPage.tsx 的 goBackToItem;新测试 ResourceHistoryPage.backToItem-28626.test.tsx。
  • 两分钟验证:pnpm --dir ui exec vitest run packages/app-shell/src/views/item-tools/ResourceHistoryPage.backToItem-28626.test.tsx,预期 6/6 通过。
  • 需要你做的:无,席位落地。

What changes

MetadataResourceHistoryPage built its non-area Back to item link as navigate('../NAME'). React Router resolves that .. against the matched route, not the URL, so it drops the whole matched route:

  • with an active app (/apps/:appName/* → metadata → :type/:name/history) it landed on /apps/setup/metadata/NAME, the :type route, which Setup then sends to Studio's list of a type named after the item;
  • with no app (/apps/:appName/* → metadata/:type/:name/history) it landed on /apps/setup/NAME;
  • under the exported MetadataResourceRouter (component/:ns/:name/*, type in ?type=) it landed on /apps/APP/NAME;
  • the query (?package=, ?scope=environment, ?type=) was dropped every time.

The non-area branch now cuts the URL's own last segment as it is spelled and keeps the query: navigate({ pathname: pathname.replace(LAST_SEGMENT, ''), search }), where the pattern is a slash, one or more non-slash characters and an optional trailing slash at the end of the path. A relative navigate({ pathname: '..', search }, { relative: 'path' }) was tried first and dropped: the router resolves it against the decoded match pathname, so an encoded name came back decoded (sales%20db → sales db). The encoded-name test row pins that.

The Studio "All metadata" area branch (built from areaHost.basePath) is unchanged and is the test's control row.

Reach today. Since #28818 every Setup metadata route except the datasource's redirects to Studio's area, so in the Console only the datasource history page (with and without an active app) still reaches this link. The exported router is not mounted by the Console (component/metadata/resource/* is caught by LegacyMetadataRedirect), but a host can mount it, and the fix covers it too.

Tests

New ui/packages/app-shell/src/views/item-tools/ResourceHistoryPage.backToItem-28626.test.tsx: the real page and React Router, the metadata client stubbed. Six rows: both AppContent nestings, the query kept under both, an encoded name kept encoded, the exported router's splat with ?type=, and the Studio area as control.

  • Fix head da4f9be7: 6 passed.
  • Red on the current line: the same file against origin/main's page (8a8a03d3, restored from base, then restored to HEAD and proven by hash and an empty git diff HEAD): 5 failed, 1 passed (the Studio control). The relative-.. variant (6108d08c) was 5 passed, 1 failed (encoded name).

Acceptance notes

  • Only the datasource history page reaches this link live in the Console today, as measured above.
  • Datasource names are ^[a-z_][a-z0-9_]*$ in the spec, so the decoded spelling never bites the live datasource page; the encoded-name row guards the exported router with other names.

Verification

All on head da4f9be7.

Tests and type-check
  • pnpm --dir ui exec vitest run packages/app-shell/src/views/item-tools/ResourceHistoryPage.backToItem-28626.test.tsx: Tests 6 passed (6).
  • Reverse leg, origin/main's page restored into the tree: Tests 5 failed, 1 passed (6); restored to HEAD, hash matched, git diff HEAD empty.
  • Every ui test file that names the page (git grep -l on ResourceHistoryPage / MetadataResourceHistoryPage, 21 files across app-shell and the console): Test Files 21 passed (21), Tests 306 passed (306). CI's Test UI runs the rest.
  • pnpm exec turbo run type-check --filter=@object-ui/app-shell: Tasks 65 successful, 65 total.
  • pnpm --dir ui exec vitest run scripts/: Test Files 169 passed, 2 skipped (171).
Derived gates
  • node scripts/pm/dispatch-gates.mjs --ran: 34 derived families, 34 run, 0 NOT-MEASURED; every one exit 0, check-sdui-manifest included (no registry change).
  • PR_BODY=... node scripts/check-partof-closing-keyword.mjs: exit 0.
  • pnpm --dir ui check:new-line-citations: 0 new citations, exit 0.
  • Full dogfood battery: n/a (no ! in the title, no narrowing arm).

Generated by Claude Code

…ery route nesting

Outside Studio's "All metadata" area the history page built its back link as
a route-relative `../NAME`, which drops the whole matched route: with an
active app it landed on the `:type` list named after the item, with no app on
a path at the app root, and it dropped the query. Resolve `..` against the
URL path instead and keep the search, so the link opens .../metadata/TYPE/NAME
with ?package= and ?scope= kept. The Studio area link is unchanged.

Claude-Session: https://claude.ai/code/session_01QsR2jSipqJ9BXYCu6y2jgs
Co-authored-by: Claude <noreply@anthropic.com>
A relative `..` resolved against the URL path still hands back the item
name decoded, because React Router resolves against the decoded match
pathname. Drop the URL's last segment from location.pathname instead, so
an encoded name stays encoded.

Claude-Session: https://claude.ai/code/session_01QsR2jSipqJ9BXYCu6y2jgs
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 2, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#1 · zhuangjianguo · session_01QsR2jUJ19MstFvFhjgiYTu

席位核对 · ACCEPT · 2026-11-02T11:56Z

Head-sha: da4f9be7ea498bdea5e5aae725ebbb082ce3d276

  • Shape:
    • The PR is a draft against main. The first card line is Fixes #28626, under claim 6575638815.
    • 3 files, +183 / −5: ResourceHistoryPage.tsx, one new test and a root changeset. That is the claimed surface. check-governed-merges --pr 29316: not governed, 188 changed lines, under the human-merge threshold.
    • Clause-②: no and Metadata-protocol: none start their lines in the body and the changeset, as claimed. No export, prop or pack key is new. The changeset is patch.
  • Read in the diff:
    • The non-area Back to item now navigates to location.pathname minus its last segment, with the same search, through navigate({ pathname: pathname.replace(/\/[^/]+\/?$/, ''), search }). It used to go to ../NAME, resolved against the matched route.
    • The Studio area branch is unchanged.
    • Deviation from the claimed spelling, ruled by the seat before the PR: the claim named navigate({ pathname: '..', search }, { relative: 'path' }). React Router resolves a relative .. against the decoded match pathname, so an encoded item name (sales%20db) came back decoded. The cut keeps the URL as spelled. It's the same file and the same responsibility, and the encoded-name row pins it. The PR body says why.
  • Pins changed: none.
  • Gates (check-runs on this head): 44 success, 11 skipped, 0 failed or pending. mergeable_state is clean.
  • Tests (dev):
    • The new file passes 6 of 6. With main's page restored, 5 fail and the Studio control passes.
    • 21 ui files that name the page: 306 of 306. app-shell type-check is 65 of 65. Derived gates 34 of 34.
  • NOT MEASURED: the seat ran no live session. Datasource names are ^[a-z_][a-z0-9_]*$ in spec, so the encoded-name case reaches only the exported router with other names.
  • Findings: not filed. MetadataResourceRouter is exported but the Console never reaches it, because LegacyMetadataRedirect catches those paths. This is dead-surface material for the existing retire-the-zero-consumer-surface work.
  • Serial: none.
  • Landing: ordinary queue landing, pr_ready then automerge_enable. Fixes #28626 closes the card on merge.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 2, 2026 11:57
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge November 2, 2026 11:57
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 2, 2026
Merged via the queue into main with commit affba40 Nov 2, 2026
56 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-idkj68 branch November 2, 2026 12:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Setup › Metadata: an item history page's "Back to item" lands on a type list named after the item, not the item

1 participant