The app roster — about a dozen launcher apps
Maintainer, 2026-10-10 (binding for this phase):
- "see we don't have a gazillion apps" — about a dozen launcher apps.
- Settings packages belong in the settings apps; examples belong in the Examples app; categories stay as they are.
- "games are included in personal tier" — Chess, Role Play, Quality Time and Quality Time (de) are
tier: personal, and so is the Games host. - Personal and Games are new host apps. This directive is the owner's explicit go for these two new packages.
This page records phase 2 of the plan in Apps live on the instance (Store/AppsOnTheInstance,
§6 and §8). Phase 2 changes only declarations: app, hostedIn, extensionSlot, tier and display
names. It copies no app data into anyone's home. The launcher still reads the _App records until
phase 1a's AppDirectory is switched on, so until then the existing install machinery does what it
always does with these declarations:
- a hosted package's tile is removed by the Store's hosted-tile purge (
HostedTilePurgeWatcher), and the package appears on its host's shelf instead; - the two new
preInstalledhosts, Personal and Games, are auto-installed like every pre-installed pack: each viewer gets their_Apptile and_Installrecord (StandardPacks,AppTileRefresh). Those are launcher bookkeeping, not app content; phases 4–6 retire them.
The roster
Only these packages may declare app: true. The literal set is APP_ROSTER in
scripts/validate-repos.py; adding a tile is a reviewed one-line change to it.
| App | Package | Notes |
|---|---|---|
| Threads | AI |
providers and harnesses are hosted in AI/AiThreads |
| Learning | Edu |
courses, the learning roadmap and the tours (host Edu/Courses) |
| Feedback | Feedback |
|
| Examples | Developer |
shown as Examples; the id and path stay Developer so every hosted path stays put |
| Approvals | Approvals |
|
| Expenses | Expenses |
|
| Signature | Signature |
keeps hostedIn: {user}/Settings (signingAuthority) for the signing-authority tab, so the per-user record path (Localizer.ShouldRegisterApp) still mints no tile for it; AppDirectory reads app: true |
| Personal | Personal (new) |
free; host Personal/Home; preInstalled (see below) |
| Games | Games (new) |
personal tier; host Games/Home; preInstalled (see below) |
| Hosting | Hosting |
the fleet console. It stays a tile because it must: the core Admin app has no Operations section that links Hosting/Console yet, and the console is the control plane's daily surface. It moves into Administration › Operations with phase 3 |
| Providers & Models | Providers |
the shared provider keys. It stays a tile because it must, for the same reason: Administration has no AI-providers section yet. It moves there with phase 3 |
Satellite repos carry their own roster: SocialMedia, Reinsurance, Crm, Manufacturing, the
FundReporting root, and none in Education.
Where every other package lives
Host (hostedIn) |
Slot (extensionSlot) |
Packages |
|---|---|---|
Developer/Home (Examples) |
examples (shown as Galleries) |
AppleMaps (new), GoogleMaps, OpenStreetMap, and the control galleries already hosted |
Personal/Home |
accounts |
Google, ICloud |
Personal/Home |
home |
HomeAssistant, AppleWeather |
Personal/Home |
entertainment |
AppleMusic |
Games/Home |
games |
Chess, RolePlay, QualityTime, QualityTimeDe — all tier: personal |
Edu/Courses (Learning) |
tools |
LearningRoadmap, Training |
Edu/Courses (Learning) |
courses |
the Education courses and the three Reinsurance courses (satellite PRs) |
{user}/Settings |
ai |
MyAi (the person app's Extensions tab lists it) |
(phase 3) Admin |
aiProviders / operations |
Providers, Governance, BuildServer, Observability, AzureCostManagement — not hosted yet, see below |
SocialMedia |
channels / campaigns |
LinkedIn, X, YouTube / Marketing (satellite) |
Reinsurance |
modules / demo |
the insurance packages / ReinsuranceDemo (satellite) |
category values are unchanged everywhere.
Reachable on the day it lands
Until phase 1a's AppDirectory is the launcher's source (core #6446, behind Home:AppSource,
default Records), a tile exists only as a {viewer}/_App record, and the same deploy that hosts a
package purges its tile (HostedTilePurgeWatcher). So a host must be reachable before its guests
lose their tiles, and a package whose host has no shelf yet is not hosted yet:
- Personal and Games are
preInstalled, asDeveloperis. A brand-new package has no viewer install, andapp: truealone mints no record (AppTileRefresh.Missingmints only for an auto-installing app or one a viewer's manifest names), so without it the replacement host would be unreachable while the hosted tiles are purged. Each host page holds only its shelves; every game and connector on it keeps its own tier and its own Get action, sopreInstalledopens no paid content. Once the launcher readsAppDirectory, the Games tile is shown by plan (personal). - The Admin moves wait for an Admin shelf.
hostedIn: Adminremoves a tile while the core Admin app renders no shelf, soProviders,Governance,BuildServer,ObservabilityandAzureCostManagementkeep standing on their own.Governancedropsapp: true(it is an enterprise package, so an install still tiles it today). They gethostedIn: Adminin phase 3, in the same change that adds Administration › Operations and AI providers.
The gate
check_app_roster in validate-repos.py fails a pull request when:
- a
Store/Pluginroot declaresapp: trueand is not on the roster; - a
hostedInnames a host that is not DECLARED inHOST_SLOTS— the literal host → slots contract ({user}/Settings,AI/AiThreads,Developer/Home,Personal/Home,Games/Home,Edu/Courses; a satellite adds its own host, e.g.SocialMedia). A host is valid because its shelf is declared, never because a node exists at that path;Adminis absent until its shelf lands. A declared host inside this repo must also exist as a node (never a README, rawcontent/file or…/indexalias); hostedInwithoutextensionSlot, a slot the host does not render, a slot withouthostedIn, or an app that is also a section (a{user}/Settingstab excepted);- a roster entry that does not declare
app: trueor has no root at all.
The checker is one block, the same text in all seven plugin repos; only APP_ROSTER and
HOST_SLOTS are repo input. Its fixtures also run from main(), so a regression reds ordinary
validation.
Both failures are silent at runtime: a stray app is one more tile on every home, and a missing host
removes the package's tile and lists it on a shelf nobody renders. --self-test runs the gate over
fixtures in both directions.
Host apps and their shelves
| Host | Shelf | State |
|---|---|---|
Developer/Home |
DeveloperApp renders Store/area/Extensions per slot |
built |
Personal/Home |
PersonalApp, three sections |
new, built the same way |
Games/Home |
GamesApp, one section |
new, built the same way |
Edu/Courses |
the course catalog page embeds the tools shelf under More in Learning; courses are listed by the catalog's own query, never by the shelf, so a hosted course is not shown twice |
built |
{user}/Settings |
the person app's Extensions tab | exists |
AI/AiThreads |
the Threads app's shelves | exists |
Admin |
no shelf yet, so nothing is hosted there in this phase. Administration › Operations and AI providers arrive in phase 3 | missing |
SocialMedia |
no shelf yet | missing (phase 3) |
Reinsurance |
no shelf yet | missing (phase 3) |
Cornerstone (in this repo, category Insurance) is listed by the plan as absorbed by Reinsurance;
this phase leaves it standing on its own because the Reinsurance host lives in a satellite repo.
After a deploy — in this order
A merge reaches an instance through the registry once main's settle-locks pull request lands. Then:
Recycle the Store root (
enqueue_recycle(path: "Store", reason: "app roster deployed: rebind the catalog")), so its hub binds the new catalog. Recycling mints no tile for Personal or Games: they install nothing (Localizetakes itsNothingToInstallbranch), and the hosted-tile purge runs on that activation anyway.Run
RefreshAppTilesstraight after: aStore/Maintenancenode underAdmin/Maintenancewithtask: RefreshAppTiles,requestedAction: Run. This step mints the hosts' tiles and fixes the rename:AppTileRefresh.Missingmints a tile for every package that is an app,preInstalledand not hosted, which is exactly Personal and Games, in every home.- A tile copies its package's name at install, so this also renames the
Developertiles to Examples.
Between the purge and this run there is a short window in which a home has neither the old guest tiles nor the new hosts. No data is touched: every package stays at its own URL and on its host's shelf. A viewer who renders the Store in that window self-heals the same way. Phase 1a's
AppDirectoryremoves the window, because the launcher stops reading records.Recycle the changed types:
Developer/DeveloperApp,Edu/CourseCatalog,Personal/PersonalAppandGames/GamesApp, each withenqueue_recycle(path, reason). The reason is required and is recorded in the audit underAdmin/_Recycle.Verify on a real home (
get @{user}/_App/*). Personal and Games are present; Google, iCloud, Home Assistant, Apple Music, Apple Weather, Learning Roadmap and the game tiles are gone; the Examples tile reads Examples.Personal/HomeandGames/Homerender their sections.