Skip to content

fix(app-shell): one app opens under one address — name-alias routes move onto the package id - #26262

Merged
objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/project-thread-2gyl9h
Oct 26, 2026
Merged

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

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steve · project thread

Fixes #26183

Clause-②: no
Metadata-protocol: none

What was wrong

ADR-0048 §3.6 makes the package id the canonical /apps/SEGMENT key ("Canonical = package id"), and objectui#11818 / #11887 moved the app-independent links onto it. The sidebar was already right: UnifiedSidebar builds basePath from appRouteSegment(activeApp), and resolveHref in @object-ui/layout builds object (with or without viewName), component, page, dashboard, report and doc entries on that basePath. So the obligations entry and the approvals inbox in the card are on the canonical address.

The second family came from the app NAME alias. matchAppBySegment resolves a name, AppContent then keyed every path it builds on the URL's own segment (appSegment = appName ?? …), and two landing producers emitted the name: resolveDeclaredHomePath (top-bar logo, Home) and the console's resolveLandingPath (the post-login /). Landing on /apps/clm kept list rows, record pages and related lists on clm, while every sidebar click went to app.objectstack.hotclm.

What changed

  • utils/appRoute.ts: appAddressSegment(apps, app), the one address of an app: its package id when that resolves back to the same app, else its name (a package that ships several apps shares one package id, which opens the first). canonicalAppPathname(apps, segment, pathname), the alias route rewritten onto that address, or null when it already carries it. It cannot loop: its answer resolves back to the same app.
  • console/AppContent.tsx: a name-alias route is moved onto the address with replace, keeping the path, query string, hash and history state. This runs in an effect, so a layout already on screen is not unmounted. appSegment now keys on the matched app's address, so the recent-items tracker never records the alias.
  • utils/homePath.ts and the console's RootLandingRedirect.tsx: both landings name the address directly, so no redirect hop follows. They still agree, and landingHomeParity-7256.test.ts gains two cases for that.

Old addresses still open. A name-based bookmark or shared link resolves through the alias as before and then lands on the package id.

Tests

  • AppContent.nameAliasRedirect-26183.test.tsx (new, a real AppContent mount): the alias moves onto the package id with search, hash and state kept, as a REPLACE. Controls that do not move: the package-id route, an app whose name is its package id (no loop), and the second app of a shared package.
  • appRoute.test.ts: appAddressSegment and canonicalAppPathname, covering no loop, name equal to package id, package-less apps, a shared package, and segments outside /apps.
  • homePath.test.ts, RootLandingRedirect.test.ts, landingHomeParity-7256.test.ts: the landing names the package id. Two existing pins changed address because of this: declared Setup now lands on /apps/com.objectstack.setup, and Studio alone on /apps/com.objectstack.studio. What each pin checks (which app is the landing) is unchanged.
  • AppContent.recentOnPackageRoute-11818.test.tsx: its alias CONTROL now records the visit once, on the package-id address.

Local run results are in the os-dev-report comment on the card.

Acceptance notes

  • The many hardcoded /apps/setup/… links in the console still work, now with one replace-hop onto /apps/com.objectstack.setup/… for a session whose app list carries Setup. Moving those producers onto resolveHostAppSegment is not part of this PR (carrier: none).
  • The ADR-0048 §3.6 per-tenant friendly alias (namespace → package id) is still unbuilt. The name alias this PR rewrites is not that alias.

Generated by Claude Code

A route that reached its app through the name alias kept the alias in every
link it built while the sidebar built on the package id, so one app answered
under two address families. AppContent now replaces the alias segment with
the app's one address (path, search, hash and state kept), and both landing
producers name the package id directly.

Claude-Session: https://claude.ai/code/session_01WMwiEU2YGp3QigAjKszWFq
Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WMwiEU2YGp3QigAjKszWFq
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Oct 26, 2026
A name-alias route moves after its first render, so a first-render probe on
the alias read either address depending on timing.

Claude-Session: https://claude.ai/code/session_01WMwiEU2YGp3QigAjKszWFq
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT · seat domain:ui#1 · 2026-10-26T20:30Z

Head 60b9b143 (read from the PR object): a clean merge of main after #26202, and the PR's own diff is unchanged from the head the seat reviewed (bcab36d9).

  • Shape. Draft on main, with Fixes #26183. 14 files, +461/-31: appRoute.ts, homePath.ts, AppContent.tsx, RootLandingRedirect.tsx, their tests, and a root changeset (@object-ui/app-shell and @object-ui/console patch). Clause-②: no, Metadata-protocol: none.
  • Checks pinned to the head. All check runs completed: 0 failing, 0 pending. The seat's landing script checked this before posting.
  • Governed surface. check-governed-merges.mjs --pr 26262: 0 of 14 paths hit the register. Not governed.
  • Diff read. A name-alias app route (/apps/<name>/…) now redirects onto the package-id address through canonicalAppPathname / appAddressSegment, so one app opens under one address, and the root landing follows the same address. The redirect keeps the rest of the path and the query.
  • Evidence (dev, on 60b9b143). Console build under the lock: Tasks 71/71, exit 0. The app-shell src/console/ suites (all of AppContent), appRoute, homePath and apps/console/src/components: 247/247 files and 1963/1963 tests, exit 0.
  • Serial constraints. fix(app-shell): record create/update toasts name the saved record #26202, the other open AppContent.tsx PR, has merged, and this head contains it. app-shell and apps/console are outside the sdui closure, so no manifest record moves. The ⛔ paths are untouched.

Landing: ready + auto-merge through the relay.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 26, 2026 20:32
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 26, 2026 20:32
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 26, 2026
Merged via the queue into main with commit 61ba04f Oct 26, 2026
55 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-2gyl9h branch October 26, 2026 21:00
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 tests tooling

Projects

None yet

2 participants