@@ -632,8 +632,8 @@ export const NavigationAreaSchema = lazySchema(() => z.object({
632632 /** Icon name (Lucide) */
633633 icon : z . string ( ) . optional ( ) . describe ( 'Area icon name' ) ,
634634
635- /** Sort order among areas (lower = first) */
636- order : z . number ( ) . optional ( ) . describe ( 'Sort order among areas (lower = first)' ) ,
635+ // ` order` removed in 17.0.0 (#4667) — see AREA_ORDER_RETIRED. Reorder the
636+ // `areas` array instead; declaration order is display order.
637637
638638 /** Area description */
639639 description : I18nLabelSchema . optional ( ) . describe ( 'Area description' ) ,
@@ -652,8 +652,10 @@ export const NavigationAreaSchema = lazySchema(() => z.object({
652652} , {
653653 error : strictUnknownKeyError ( {
654654 surface : 'this navigation area' ,
655- knownKeys : [ 'id' , 'label' , 'icon' , 'order' , 'description' , 'visible' , 'requiredPermissions' , 'navigation' ] ,
656- aliases : { visiblewhen : 'visible' , visibleon : 'visible' , title : 'label' , name : 'id' , sort : 'order' , permissions : 'requiredPermissions' , items : 'navigation' , children : 'navigation' } ,
655+ knownKeys : [ 'id' , 'label' , 'icon' , 'description' , 'visible' , 'requiredPermissions' , 'navigation' ] ,
656+ // `sort: 'order'` retired with the key it pointed at (#4667).
657+ aliases : { visiblewhen : 'visible' , visibleon : 'visible' , title : 'label' , name : 'id' , permissions : 'requiredPermissions' , items : 'navigation' , children : 'navigation' } ,
658+ guidance : { order : AREA_ORDER_RETIRED , sort : AREA_ORDER_RETIRED } ,
657659 history :
658660 'Until #4001 these were dropped silently — the area still parsed, so its gating or ' +
659661 'ordering was quietly ignored.' ,
@@ -888,6 +890,41 @@ const APP_KEYS = [
888890 'version' , 'aria' , 'objects' , 'apis' , 'sharing' , 'embed' , 'mobileNavigation' ,
889891] as const ;
890892
893+ /**
894+ * `app.homePageId`, retired in 17.0.0 (#4667, ADR-0049).
895+ *
896+ * The schema's own hedge gave it away — "if not set, usually defaults to the
897+ * first navigation item" describes the ONLY behaviour that exists. No shell
898+ * reads the key: an app's landing page is its first navigation item in `order`,
899+ * and the ROOT landing follows `isDefault` routing (objectui's
900+ * `RootLandingRedirect`). So an author pinning a home page got the first nav
901+ * item anyway, and "usually" was doing the work of "always".
902+ */
903+ const HOME_PAGE_ID_RETIRED =
904+ '`app.homePageId` was removed in @objectstack/spec 17.0.0 (#4667, ADR-0049) — no shell '
905+ + 'ever read it. An app\'s landing page IS its first navigation item (by `order`), and the '
906+ + 'root landing follows `isDefault` routing. Delete the key; to change where an app opens, '
907+ + 'reorder `navigation` so the intended entry is first, and set `isDefault` on the app that '
908+ + 'should own the root landing. Run `os migrate meta --from 16` to rewrite existing sources '
909+ + 'automatically.' ;
910+
911+ /**
912+ * `app.areas[].order`, retired in 17.0.0 (#4667, ADR-0049).
913+ *
914+ * The sibling that works is what made this one read alive: nav-item `order` IS
915+ * sorted (`NavigationRenderer.tsx:1154`). Area-level order is not — `AppSidebar`
916+ * and `AppSchemaRenderer` both iterate the `areas` array as authored — so
917+ * declaration order has always been display order, and an author who set
918+ * `order` to rearrange areas saw nothing move.
919+ */
920+ const AREA_ORDER_RETIRED =
921+ '`areas[].order` was removed in @objectstack/spec 17.0.0 (#4667, ADR-0049) — no renderer '
922+ + 'ever sorted areas; both the sidebar and the schema renderer iterate the array as '
923+ + 'authored, so declaration order already IS display order. Delete the key and reorder the '
924+ + '`areas` array itself. NOTE the neighbour that behaves differently: a navigation ITEM\'s '
925+ + '`order` is genuinely sorted — this removal does not touch it. Run '
926+ + '`os migrate meta --from 16` to rewrite existing sources automatically.' ;
927+
891928const appUnknownKeyError = strictUnknownKeyError ( {
892929 surface : 'this app' ,
893930 knownKeys : APP_KEYS ,
@@ -902,9 +939,9 @@ const appUnknownKeyError = strictUnknownKeyError({
902939 sections : 'areas' ,
903940 groups : 'areas' ,
904941 permissions : 'requiredPermissions' ,
905- home : ' homePageId' ,
906- homepage : 'homePageId' ,
907- landingpage : 'homePageId' ,
942+ // ` home` / `homepage` / `landingpage` aliased ` homePageId`, retired in
943+ // 17.0.0 (#4667). They fall through to the tombstone's own prescription
944+ // rather than renaming onto a key that no longer exists.
908945 agent : 'defaultAgent' ,
909946 logo : 'branding' ,
910947 theme : 'branding' ,
@@ -922,6 +959,11 @@ const appUnknownKeyError = strictUnknownKeyError({
922959 flows :
923960 '`flows` is not an App field — flows are top-level stack metadata ' +
924961 '(`defineStack({ flows })`), not app-scoped.' ,
962+ // The three retired `homePageId` aliases. `retiredKey` already answers the
963+ // canonical spelling; these cover the spellings that used to route to it.
964+ home : HOME_PAGE_ID_RETIRED ,
965+ homepage : HOME_PAGE_ID_RETIRED ,
966+ landingpage : HOME_PAGE_ID_RETIRED ,
925967 } ,
926968 history :
927969 'Until #4001 these were dropped silently — the app still parsed, so navigation or ' +
@@ -1014,12 +1056,13 @@ export const AppSchema = lazySchema(() => z.object({
10141056 contextSelectors : z . array ( AppContextSelectorSchema ) . optional ( )
10151057 . describe ( 'App-level scope dropdowns whose value is injected into nav items as {<id>} template vars' ) ,
10161058
1017- /**
1018- * App-level Home Page Override
1019- * ID of the navigation item to act as the landing page.
1020- * If not set, usually defaults to the first navigation item.
1059+ /**
1060+ * REMOVED in 17.0.0 (#4667) — see {@link HOME_PAGE_ID_RETIRED}. Tombstoned
1061+ * rather than deleted, matching the seven #4142 retirements on this schema:
1062+ * `retiredKey` types it `never`, so an app still authoring it fails to
1063+ * compile as well as to parse.
10211064 */
1022- homePageId : z . string ( ) . optional ( ) . describe ( 'ID of the navigation item to serve as landing page' ) ,
1065+ homePageId : retiredKey ( HOME_PAGE_ID_RETIRED ) ,
10231066
10241067 /**
10251068 * Access Control
0 commit comments