Declarative Widget Host vs Imperative Widget SDK
- Status: proposed
- Deciders: Platform Team
- Date: 2026-06-29
- Parent: 0001 — Adopt WhiskerPlatform over rwe-core
Technical Story: Dashboard widgets — host orchestration vs module-driven lifecycle
Context and Problem Statement
Section titled “Context and Problem Statement”Dashboard widgets are federated UI fragments (cash alerts, variance KPIs, etc.) rendered on the host home surface. rwe-core and whisker-host treat widget lifecycle very differently.
rwe-core — host orchestrates, modules signal
Section titled “rwe-core — host orchestrates, modules signal”widget-registry.tslists allowed widgets;WidgetHostProviderloads remotes and mounts them hidden.- Modules call
host.notifyReady(id, payload?)when they have data worth showing;host.notifyHide(id)when dismissed or stale. - Host exposes
window.rweWidgetHost/useWidgetHost()fromretailUXShell/widgetHostSdk. - Host tracks per-operator dismiss via
widgetsLastHiddenByUser[operatorId][widgetId](see 0005). - The host cares what widgets are doing — readiness, hide, analytics.
whisker-host today — declarative manifest, host-agnostic render
Section titled “whisker-host today — declarative manifest, host-agnostic render”- Modules declare
widgets[]in the manifest (dimensions, type,allowedPersonas). - Host renders widget slots from manifest + dashboard layout store;
no
notifyReady/notifyHideSDK. - Widget federation loads like any other remote; visibility is driven by dashboard composition, not module callbacks.
- Host does not participate in widget readiness negotiation.
Both can federate the same React components. The difference is who owns visibility logic and how tightly the host couples to widget runtime behaviour.
Decision Drivers
Section titled “Decision Drivers”- Host simplicity — Platform should not need widget-specific orchestration for every module.
- Module autonomy — Widgets with conditional data (e.g. “show only when variance > threshold”) need a lifecycle hook.
- Migration — Existing rwe-core widgets already call
notifyReady. - Analytics — Centralised
trackEventthrough host vs module-direct NewRelic. - Shared devices — Dismiss semantics must tie to operator, not device.
Considered Options
Section titled “Considered Options”Option A — Keep rwe-core imperative SDK on whisker-host
Section titled “Option A — Keep rwe-core imperative SDK on whisker-host”Port WidgetHostProvider, notifyReady / notifyHide,
getLastHiddenByUser, trackEvent to whisker-host (or WCL).
Option B — Pure declarative widgets (Whisker today)
Section titled “Option B — Pure declarative widgets (Whisker today)”Manifest + dashboard layout only; modules render whenever mounted; no readiness protocol.
Option C — Optional widget lifecycle protocol in WCL
Section titled “Option C — Optional widget lifecycle protocol in WCL”Host exposes a thin, optional SDK (rwe-core-compatible surface); modules that need readiness use it; simple widgets ignore it.
Option D — Host-agnostic widgets only — modules handle visibility internally
Section titled “Option D — Host-agnostic widgets only — modules handle visibility internally”No host SDK; each widget self-hides inside its bundle; host always mounts manifest widgets.
Pros and Cons
Section titled “Pros and Cons”Option A — Full imperative SDK (rwe-core parity)
Section titled “Option A — Full imperative SDK (rwe-core parity)”- Good: drop-in for migrated widgets; proven dismiss + analytics pattern; host controls suggestion stack vs persistent lanes.
- Bad: host remains coupled to widget runtime; every widget team learns SDK; host must maintain registry + lifecycle state machine.
Option B — Pure declarative (status quo)
Section titled “Option B — Pure declarative (status quo)”- Good: host stays dumb; manifest is sole contract; fewer moving parts.
- Bad: no standard for “show when data ready”; migrated widgets must rewrite; empty/loading widgets may flash on dashboard.
Option C — Optional lifecycle protocol (recommended)
Section titled “Option C — Optional lifecycle protocol (recommended)”- Good: simple widgets stay declarative; complex widgets opt into
notifyReady/notifyHide; migration path for rwe-core; host can noop SDK until module calls it. - Bad: two widget classes to document; risk of inconsistent UX if teams skip SDK when they should use it.
Option D — Module-internal visibility only
Section titled “Option D — Module-internal visibility only”- Good: maximum host agnosticism.
- Bad: duplicated hide/show logic; no central dismiss persistence; poor fit for suggestion-stack UX rwe-core built.
Decision Outcome
Section titled “Decision Outcome”Chosen direction: Option C — optional widget lifecycle protocol, implemented in or alongside WCL, with host-agnostic rendering as the default.
Principles:
- Default: whisker-host renders widgets declared in manifest + dashboard layout without requiring imperative callbacks.
- Opt-in: modules that need conditional display or athlete dismiss
use an rwe-core-compatible SDK surface (
notifyReady,notifyHide,getLastHiddenByUser,trackEvent). - Migration: rwe-core widgets may keep call sites during transition; host provides compatible implementation.
- Not chosen: requiring every widget to register with host orchestration (Option A) or forbidding lifecycle hooks entirely (Option B/D).
Status: SDK not shipped in whisker-host today — this ADR records target model; tracked as improvement #16 in Risks & improvements.
Positive Consequences
Section titled “Positive Consequences”- New simple widgets avoid SDK ceremony.
- Complex widgets and rwe-core migrations have a defined protocol.
- Host stays agnostic for the common case; engaged only when modules opt in.
Negative Consequences
Section titled “Negative Consequences”- Two widget integration paths to document and test.
- Until SDK ships, rwe-core widget migrations remain blocked or require rewrites.
- Per-operator dismiss must be implemented with SDK (ties to 0005).
Recommendations
Section titled “Recommendations”- Spec optional SDK in WCL with rwe-core-compatible method signatures.
- Ship host-side provider that mirrors rwe-core semantics for opt-in widgets only.
- Document in module-kit / module-author skill: when declarative is
enough vs when to call
notifyReady. - Do not block Whisker adoption on full SDK — block widget migration from rwe-core until SDK + operator dismiss exist.
- rwe-core:
widget-registry.ts,WidgetHostProvider.tsx,widgetHostSdk.ts - Comparison — widget SDK
- Improvement #16 — Widget lifecycle in WCL
