You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 61ba04f
Browse filesBrowse the repository at this point in the historyBrowse files
fix(app-shell): one app opens under one address — name-alias routes move onto the package id (#26262)
<!-- claude-projects-attribution: {"github_login":"os-steve"} -->
_Requested by **Steve** · [project
thread](https://claude.ai/code/project/chan_01FdL1XeUyvVNxLreh3F4Qi9?thread=cmsg_01FdL1XeUyvVNxLreh3F4Qi9FP9GUkjfjntrFmztxSoedK)_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](https://claude.ai/code/session_01WMwiEU2YGp3QigAjKszWFq)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
fix(app-shell,console): one app opens under one address — a name-alias route moves onto the package id
7
+
8
+
Clause-②: no
9
+
Metadata-protocol: none
10
+
11
+
An app with a package id answered under two address families in one session. ADR-0048 keys `/apps/<segment>` on the package id, and every sidebar entry builds on it, but the app's `name` still resolves as an alias, and a page opened on the alias built every link of its own (list rows, record pages, related lists) on the alias. Landing on `/apps/clm/…` therefore opened contracts under the name and obligations and the approvals inbox under `/apps/app.objectstack.hotclm/…`.
12
+
13
+
-**`@object-ui/app-shell`:** a route under `/apps/<name>/…` whose app carries a package id is rewritten, with `replace`, to the same path under `/apps/<package id>/…`, keeping the query string, hash and history state. Old name-based bookmarks and shared links keep opening, now on the one address. The top-bar logo and every Home affordance (`resolveDeclaredHomePath`) name the declared app by its package id. An app without a package id, an app whose name is its package id, and every app after the first of a package that ships several (the package id opens the first) keep their name as their address.
14
+
-**`@object-ui/console`:** the `/` landing (`resolveLandingPath`) names the landing app by the same address, so post-login and the logo agree and no redirect hop follows.
15
+
16
+
What to do: nothing. Links that name an app by its name keep working; they now arrive on its package-id address.
{label: 'declared second app of a two-app package',apps: [{name: 'crm',_packageId: 'com.acme.suite'},{name: 'ops',_packageId: 'com.acme.suite',isDefault: true},SETUP]},
47
49
{label: 'empty list',apps: []},
48
50
];
49
51
@@ -78,6 +80,21 @@ describe('objectui#7256 — one declaration, one home', () => {
0 commit comments