From 43630ef4ab636ec499876af91de65580958274ce Mon Sep 17 00:00:00 2001 From: Paulo Date: Mon, 17 Aug 2026 04:00:56 +0200 Subject: [PATCH] Usage is a platform surface, not an installed app MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The shell reads an extension's `builtin` flag as "this is an app": the roster's non-builtin entries get registered into the UI registry, which puts them in the switcher and mounts generic pages at /. Usage never set the flag, so it drew a dropdown entry, and its generic home page shadowed the shell's own /usage route — the pill in the appbar landed on an empty subject board instead of the usage panel. The shell's routes now come before the extension routes in the switch, so a platform path can't be taken by an extension named after it. --- backend/druks/usage/extension.py | 1 + backend/tests/test_extensions.py | 6 ++++++ frontend/src/App.tsx | 16 +++++++++------- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/backend/druks/usage/extension.py b/backend/druks/usage/extension.py index d82d6cd5..54baff0f 100644 --- a/backend/druks/usage/extension.py +++ b/backend/druks/usage/extension.py @@ -5,3 +5,4 @@ class Usage(Extension): name = "usage" icon = "gauge" description = "Harness usage metering — quota and spend per account." + builtin = True diff --git a/backend/tests/test_extensions.py b/backend/tests/test_extensions.py index d2919ac6..29407c31 100644 --- a/backend/tests/test_extensions.py +++ b/backend/tests/test_extensions.py @@ -26,6 +26,12 @@ def test_iter_extensions_discovers_the_bundled_extensions(): assert {extension.name for extension in iter_extensions()} >= {"core", "ship", "usage"} +def test_platform_extensions_are_builtin(): + """``builtin`` is what keeps a platform surface out of the shell's app switcher.""" + builtin = {extension.name for extension in iter_extensions() if extension.builtin} + assert builtin >= {"core", "usage"} + + def test_ship_app_derives_its_package_from_the_defining_module(): ship = next(extension for extension in iter_extensions() if extension.name == "ship") assert ship.package == "druks.contrib.ship" diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 79cf02b1..e2aa974e 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -204,13 +204,8 @@ function AppShell() {
{wantsStrip && health && } - {registered.flatMap((name) => - (getExtensionUI(name)?.routes ?? []).map((route) => ( - - {(params) => route.render(params as Record)} - - )), - )} + {/* Shell-owned paths first, so an extension named after one of them can't + shadow the platform surface. */} @@ -220,6 +215,13 @@ function AppShell() { {(params) => } + {registered.flatMap((name) => + (getExtensionUI(name)?.routes ?? []).map((route) => ( + + {(params) => route.render(params as Record)} + + )), + )}