Accept + implement ADR-010 frontend plugin hooks (contract 0.7.0)
Some checks failed
CI / backend (push) Failing after 1m2s
CI / naming (push) Successful in 1s
CI / frontend (push) Successful in 7s

Four data-only hooks on BasePlugin (get_settings_cards,
get_asset_panels, get_map_overlays, get_asset_presentation) with a
GET-only /api/pluginui consumer surface copying the dashboard-widgets
semantics. Pilots: warranty declares its asset panel; measuringtools
supplies its settings card, presentation, and calibration overlay -
the last hardcoded settings-nav entry is now hook-sourced. Generic
renderers for panels/overlays/presentation deferred per the ADR's
incremental adoption plan (documented in CONTRACT-STABILITY.md).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-07-11 19:38:32 -04:00
parent 9eed3745fc
commit 24f67d6ac5
17 changed files with 625 additions and 19 deletions

View File

@@ -227,3 +227,90 @@ class BasePlugin(ABC):
reports. Disabled plugins are skipped by the consumer.
"""
return []
def get_settings_cards(self) -> List[Dict]:
"""
Return settings-catalog card definitions (ADR-010).
Each card contributes an entry to the settings rail + landing overview
without a plugin hand-editing the core settingsNav.js catalog.
Each card: {
'group': str, # rail group title (created if new)
'to': str, # settings route the card links to
'icon': str, # string key, mapped to a Lucide icon core-side
'title': str, # card title
'description': str, # one-line blurb
'position': int, # order within the group
}
Consumed by GET /api/pluginui/settings-cards, which merges enabled
plugins' cards into the core catalog. Disabled plugins are skipped;
a broken plugin is isolated in prod, re-raised in dev/test.
"""
return []
def get_asset_panels(self) -> List[Dict]:
"""
Return asset-detail extension-panel definitions (ADR-010).
A generic core AssetPanel component renders each panel on the asset
detail pages whose type matches, fetching the panel's endpoint. This
replaces hand-composing a plugin panel component into each detail view.
Each panel: {
'id': str, # stable panel id
'title': str, # panel heading
'assettypes': List[str], # AssetType keys it appears on; ['*'] = all
'endpoint': str, # data endpoint (may contain {assetid})
'render': str, # 'keyvalue' | 'table' | 'badge'
'position': int, # order among panels
}
Consumed by GET /api/assets/{assetid}/panels via the pluginui consumer,
which returns the panels matching that asset's type. Disabled plugins
are skipped; a broken plugin is isolated in prod, re-raised in dev/test.
A panel needing bespoke UI is out of scope for the data-only hook.
"""
return []
def get_map_overlays(self) -> List[Dict]:
"""
Return shop-floor map overlay/decoration definitions (ADR-010).
The map is data-driven off asset types + positions; an overlay adds
decoration data (a badge or ring on already-placed markers) plus a
legend entry, without the plugin shipping any map code.
Each overlay: {
'id': str, # stable overlay id
'label': str, # legend label
'endpoint': str, # returns [{assetid, color, label}] to decorate
'style': str, # 'badge' | 'ring'
'legend': bool, # True to add a legend entry
}
Consumed by GET /api/pluginui/map-overlays. Disabled plugins are
skipped; a broken plugin is isolated in prod, re-raised in dev/test.
"""
return []
def get_asset_presentation(self) -> List[Dict]:
"""
Return asset-type presentation/routing definitions (ADR-010).
Declares how a plugin-owned asset type renders in global-search rows
and cross-links: which icon to show and where the detail link points,
so core never hardcodes a plugin's route or icon.
Each entry: {
'assettype': str, # AssetType.assettype key the plugin owns
'icon': str, # string key, mapped to a Lucide icon core-side
'label': str, # human label for the type
'route': str, # detail-route pattern (may contain {assetid})
}
Consumed by GET /api/pluginui/asset-presentation. Disabled plugins are
skipped; a broken plugin is isolated in prod, re-raised in dev/test.
"""
return []