34 KiB
Chatbot extensions Author: Enzo Martellucci Team: Preset Status: Draft | Under Review | Completed Day: May, 2026
- Introduction This document defines the extension point that allows an external chatbot to be embedded into the Superset UI. 1.1 Motivation The goal is to let any community-built chatbot plug into Superset through the standard extension system using the same well-defined APIs already established for SQL Lab and the dashboard, without the chatbot reaching into Superset's internal Redux store or source modules. 1.2 Items Not Included These are explicitly out of scope for this SIP and are either deferred to another SIP or left to the extension implementation:
- Frontend tool execution / agentic UI manipulation (the agent changing a chart's config, a dashboard's layout, or the SQL editor's contents live on screen) is out of scope and deferred to the [SIP] Proposal for integrating Superset client actions for AI agent applications (Justin Park). This SIP defines how the chatbot is mounted and given context; that SIP defines how the chatbot *acts on the UI”.
- The chatbot UI itself is out of scope. This SIP defines the host contract; the look, feel, conversation model, streaming, and persistence are entirely the extension's responsibility.
- The MCP/LLM backend (server, model communication, tool selection) is out of scope.
- Functional requirements
Registration & rendering
Extensions must be able to register a chatbot provider
Only one chatbot should be visible/active at a time (Singleton)
The chatbot should be visible across all supported application surfaces
Extensions must be able to render a fully custom chatbot UI
Content sharing APIs/hooks
The chatbot must receive contextual information about the current page:
Page type:
home,dashboard,chart,sqllab,datasetThe current dashboard/chart data, the saved chart, the dashboard filters and the dashboard-ui-control state The chatbot must be notified when the page context changes (navigation entity change, title change) without polling. The page context the chatbot receives must be host-derived and aligned with the current user's backend-authorized application view: the host computes it and exposes only what the current user is already authorized to see. The chatbot must not read the host's Redux store or other internal state to assemble context (the chatbot must not depend on internal frontend structure to assemble the context). The chatbot must support conversation state/history (owned by the extension, never the host) 3) Administration An admin can enable/disable the chatbot and when more than one chatbot extension is installed, choose which one is active
2.1 Non-functional requirements
Context sharing must respect Superset permission and security boundaries. The host must not expose entities, metadata, or datasource-derived context the current user is not authorized to access.
Namespace APIs must remain decoupled from frontend implementation details and internal state-management architecture.
The architecture must minimize impact on existing frontend performance. In particular, exposing page context must not force unnecessary host view re-renders.
Context change notifications must not require polling.
Third-party chatbot failures must not break the main application (fault isolation at the mount boundary).
The integration system must remain extensible for future AI capabilities and additional application surfaces.
The architecture should avoid hard dependency on any specific LLM vendor.
The system must support incremental adoption and migration from existing integrations. 3. Proposed Extension Point
3.1 Summary
One extension point is proposed.
Extension Point
Contribution Area ID
Registration API
Cardinality
Chatbot bubble
superset.chatbot
views.registerView()
Exclusive (singleton)
3.2 The Chatbot Bubble
Contribution area: superset.chatbot
The host renders a fixed mount point at the bottom-right corner of the Superset application shell. A chatbot extension registers a single React component into this slot, and the host renders it persistently across all routes (dashboard, SQL Lab, explore, datasets, etc.).
New contribution scope — manifest schema change required the static contribution schema in @apache-superset/core is currently SQL-Lab-only: ViewContributions only accepts a sqllab scope keyed by SqlLabLocation.
There is no app-shell-level scope, so superset.chatbot cannot be declared in extension.json today.
This SIP therefore requires extending the manifest contribution schema to add an app-root scope (e.g. app / appShell) that can carry the superset.chatbot location.
This is a schema change, not just a runtime registerView() call: both the
static manifest declaration and the runtime registration must understand the new
scope.
The extension owns the bubble's collapsed appearance (the icon), its expanded appearance (the conversation panel), all animations, all open/close state, and all internal chatbot behavior.
Singleton / exclusive behavior
Unlike most contribution areas (which accept multiple contributions and render them as a list or stack), superset.chatbot is exclusive; only one chatbot extension can be active at a time.
Two floating bubbles in the same corner would be confusing, and the conversation model is fundamentally singular.
If no chatbot extension is installed, the corner stays empty and no bubble appears.
If exactly one chatbot extension is installed, its component is mounted automatically.
If multiple chatbot extensions are installed, the host resolves the conflict via a configurable policy (see Section 4, open questions).
Note that the existing registerView / getViews(location) registry already supports "multiple contributions registered at a location, host chooses what to render".
Singleton behavior is therefore a host-side selection policy at the superset.chatbot location, not a new registration primitive: the host enumerates the candidates registered at superset.chatbot and picks one according to the admin setting (see §2, Administration).
getViews does not expose providers — by design there is a concrete limitation in the public API as it exists today. getViews(location) returns View[] | undefined — i.e. only the view descriptors (id, name, description).
It does not return the provider functions that registerView was given.
This is important to state precisely, because it is not an oversight to "fix" by widening getViews:
- A
provideris a function that constructs and returns a React element — calling it renders the view. The view descriptor (id/name/description) is inert metadata; the provider is executable rendering logic. getViewsis a public, extension-facing API. If it returned providers, any extension could obtain and invoke another extension's provider directly — rendering a view outside the host's mount point, lifecycle, and fault-isolation boundary. KeepinggetViewsdescriptor-only is a deliberate boundary: the public surface lets extensions _ discover what is registered, but only the host can _render_ what is registered (via the registration/mount path).- Consequence for the singleton picker: with the public API alone, the host can enumerate the registered chatbots but cannot obtain the provider needed to render the selected one. The picker described here therefore cannot be built on
getViewsas-is — andgetViewsshould not be changed to expose providers, because that would dissolve the boundary above.
Proposed solution: this SIP requires a host-internal resolution mechanism for exclusive locations — one that the host can use to obtain a provider, without that capability becoming part of the public extension-facing contract.
const candidates = getViews('superset.chatbot');
const selectedId = adminSettings.activeChatbotId ?? candidates?.[0]?.id;
const provider = getViewProvider( 'superset.chatbot', selectedId, );
return provider?.();
getViews() remains a descriptor-only public API.
The host uses an internal provider-resolution mechanism to obtain the renderable provider for the selected chatbot.
Two acceptable forms:
-
(preferred) Add an internal host-only registry accessor that returns the
providerfor a registered view id at a given locationgetViewProvider(location, id).getViewsstays unchanged as the public, descriptor-only enumeration API; the provider accessor is host-internal and not part of the extension-facing contract. The host renders the chatbot viagetViewProvider('superset.chatbot', selectedId). -
(alternative) Treat
superset.chatbotas a first-class exclusive contribution area in core with a dedicatedgetActiveChatbot()resolver that applies the admin policy internally and returns a single renderable provider.
The preferred form is the smaller change and keeps the public getViews semantics intact — getViews stays descriptor-only, and the provider-resolution capability lives entirely host-internal, preserving the boundary described above.
Either way, this is a required core change and must be tracked as such in the Migration Plan (§6 P1); the SIP cannot rely on getViews alone.
Positioning and lifecycle (host responsibilities) The host provides: Provide the APIs for the interactions Provide the context Mounting at the app root level, so the bubble persists across route changes. Eager loading of the chatbot bundle as part of the app shell startup, so the bubble is available immediately rather than appearing late. Disposable cleanup when the extension is deactivated, uninstalled, or replaced. Fixed positioning at the bottom-right corner of the viewport (24px margin from the edges). A managed z-index above dashboard content and the standard toast layer, below modal dialogs. Component contract (extension responsibilities) The registered component is the entire chatbot UI. It is responsible for: The collapsed bubble state (icon, optional notification badge, hover/focus states). The expanded panel state (header, conversation, composer, side panels). All open/close transitions, keyboard shortcuts (e.g., Cmd+K to open), and accessibility (focus trap, ARIA labels, screen reader support). All conversation state, message history, streaming, and persistence. All LLM communication and tool execution. Responsive behavior on narrow viewports (typically transitioning to a full-screen overlay below ~768px). The host does not inject styling or behavior into the registered component beyond what the standard namespaces provide. Registration example
import { views } from '@apache-superset/core'; import { ChatbotApp } from './ChatbotApp';
export function activate(context: ExtensionContext) { const disposable = views.registerView( { id: 'superset.copilot', name: 'Superset Copilot', icon: 'Bubble', }, 'superset.chatbot', () => , );
context.subscriptions.push(disposable); }
The extension registers a single renderable provider into the
superset.chatbot contribution area.
The host resolves exactly one active chatbot provider and mounts it at the application-shell level.
The chatbot needs an icon:
It identifies the chatbot in the admin "Default chatbot" picker (see §4) and in any contribution manifest listing, and it is the natural identity for the collapsed bubble.
The current View interface in @apache-superset/core is { id, name, description? } and has no icon field, so
this SIP proposes adding an icon field to the View descriptor.
The registration example above must be updated to pass one once the form below is decided.
The remaining open concern is who owns the icon and whether it is mutable:
- (a) Static descriptor icon.
iconis a fixed field on theViewdescriptor, set once atregisterView()time and never changed. The host uses it for the admin picker / manifest listing. The collapsed bubble shown in the corner is still rendered by the extension component itself (see Component contract), so a static descriptor icon and a richer extension-rendered bubble can coexist: The descriptor icon identifies metadata, the bubble is a live UI. - (b) Runtime-updatable icon.
The chatbot owner can change its icon after registration — e.g. to reflect a notification/unread badge, a loading or "thinking" state, or dynamic rebranding.
This requires more than a descriptor field: either a mutable handle returned from
registerView()(e.g.handle.setIcon(...)) or a small chatbot-scoped API to update it. It also raises the question of where that icon is consumed — the admin picker would show a point-in-time value, while the bubble already updates freely because the extension renders it.
Recommendation: (a) for the descriptor icon — keep the registry field static and simple, since its only consumers are identity surfaces (picker, manifest) that do not need live updates.
Dynamic icon states (notification badge, thinking
indicator) belong to the collapsed bubble, which the extension component already owns and re-renders on its own; they do not need to flow through the host
descriptor.
If a concrete need emerges for the host to reflect a live chatbot icon outside the bubble, option (b) can be revisited in a follow-up rather than expanding registerView now.
State APIs the chatbot uses
The chatbot does not receive a dedicated host-managed state namespace.
It reads host state through the same public namespaces available to any other extension:
The table below distinguishes namespaces that exist today in @apache-superset/core from those this SIP must add as new work.
Namespace
Status
Purpose for the chatbot
sqlLab
Exists today
SQL Lab-specific APIs and state (getCurrentTab() and onDidChange* events)
authentication
Exists today
Current user Auth/session CSRF token for same-session requests the chatbot uses the user's existing browser session; no separate token is issued or sent
commands
Exists today
Host actions utilities
dashboard
NEW (added by this SIP)
Dashboard-specific APIs and state (current dashboard, filters, dashboard-ui-control state)
explore
NEW (added by this SIP)
Chart/explore-specific APIs and state (saved chart, current chart data)
dataset
NEW (added by this SIP)
Dataset-specific APIs and state (current dataset); provides context for the dataset page type
navigation
NEW (added by this SIP)
Route/page surface, including the onDidChangePage change event
Namespaces this SIP must add — required core work, only sqlLab, authentication, commands, menus and editors currently exist in @apache-superset/core (superset-frontend/packages/superset-core/src/).
There is no dashboard, explore, dataset, or navigation namespace today, and no
navigation.onDidChangePage event, these do not exist yet — this SIP is what introduces them.
Each new namespace must follow the established sqlLab shape: a
state getter plus an Event<T> change subscription. Concretely, this SIP requires
adding:
dashboardnamespace —dashboard.getCurrentDashboard()plus change events for the dashboard entity, its filters, and its UI-control state.explorenamespace —explore.getCurrentChart()(saved chart + current chart data) plus a change event.navigationnamespace — a current-page getter (pageType+ focused entity) and thenavigation.onDidChangePageevent used for the without-polling notification in §2.
Page context Per-surface namespaces The chatbot does not interact directly with the host application's internal state management implementation. Instead, each application surface exposes a dedicated namespace that provides a stable, host-managed API for that surface. These namespaces expose curated API types rather than internal implementation details such as Redux slices or component-local state. The purpose of the namespace layer is to: provide a stable extension contract normalize surface-specific context avoid coupling extensions to host implementation details preserve compatibility across future frontend architecture changes For example, the host may evolve from Redux-based state management to another implementation in the future without requiring chatbot extensions to change, as long as the namespace contract remains stable. Each namespace exposes normalized semantic context aligned with the current user's backend-authorized application view
Permission enforcement itself remains a backend concern — the namespace surfaces data that backend APIs have already scoped to the current user; it is not a security boundary of its own.
Namespace composition model
This SIP intentionally does not introduce a single aggregate context namespace. Instead: each surface exposes its own namespace chatbot extensions compose those already-safe pieces into a higher-level PageContext
For example:
const pageContext = { pageType: navigation.getPageType(),
dashboard: dashboard.getCurrentDashboard(),
sqlLab: sqlLab.getCurrentTab(),
chart: explore.getCurrentChart(),
dataset: dataset.getCurrentDataset(), };
The extension-side adapter assembles normalized semantic context fragments. It does not derive or permission-filter entities itself.
Namespace API shape Each namespace follows the established sqlLab API shape: synchronous state getter(s) Event-style change subscriptions
This allows chatbot extensions to react to page-context changes without polling.
Example:
const dashboardContext = dashboard.getCurrentDashboard();
const disposable = dashboard.onDidChangeDashboard(nextDashboard => { chatbot.updateContext(nextDashboard); });
The new namespaces follow the sqlLab namespace shape, but not necessarily its implementation model.
Existing sqlLab namespace behavior The existing sqlLab namespace (superset-frontend/src/core/sqlLab/index.ts) already follows this general API shape today.
Internally, the current implementation is a lightweight wrapper around Redux state (store.getState()), but that implementation detail is not part of the extension contract and should not be relied upon by extensions.
This SIP extends the namespace-based model to additional application surfaces (dashboard, explore, dataset, navigation), standardizing semantic context normalization and a stable extension contract.
Where permission enforcement happens Permission enforcement is a backend concern, and this is the normal Superset data flow: every frontend surface receives its state from backend APIs that already scope data to the current user's permissions. Frontend application state reflects backend-authorized API responses.
sqlLab follows the standard Superset authorization model: backend APIs scope SQL Lab data to the current user's permissions before that data enters frontend application state (for example through DatabaseFilter applied in DatabaseDAO.find_all()).
The sqlLab namespace therefore exposes a stable extension-facing API over already-authorized application state rather than enforcing authorization itself.
The new namespaces follow the same principle: a namespace getter surfaces data from backend APIs that scope it to the current user — it is not itself a security boundary.
Namespace normalization requirements
Each namespace getter must: normalize application state into stable semantic extension-facing APIs expose context aligned with backend authorization semantics avoid exposing raw Redux structures or internal frontend implementation details provide a stable abstraction layer independent of frontend state-management implementation details
For example, a native implementation such as: // ❌ Avoid exposing internal host state directly
dashboard.getCurrentDashboard() { return store.getState().dashboard; }
would incorrectly expose internal dashboard state directly to extensions and would tightly couple extensions to the host implementation. Instead, namespaces must expose normalized semantic context objects.
// ✅ Expose a stable semantic contract dashboard.getCurrentDashboard() { return { id: dashboard.id, title: dashboard.title, filters: dashboard.filters, charts: dashboard.visibleCharts, uiState: dashboard.uiState, }; }
Per-surface context contracts Each namespace defines the semantic context contract exposed to extensions for that application surface. Namespaces: normalize frontend/application state into stable extension-facing APIs expose only context relevant to that surface avoid exposing unrelated implementation details or internal Redux structures preserve the permission semantics already enforced by the backend APIs serving that surface The namespace layer is therefore: an abstraction boundary a semantic normalization layer a stable extension contract It is not the primary authorization boundary; authorization remains enforced by backend APIs and Superset access-control mechanisms. dashboard dashboard.getCurrentDashboard() exposes: dashboard identity visible dashboard charts dashboard filter state dashboard UI-control state semantic dashboard context relevant to chatbot integrations The namespace should avoid exposing: unrelated internal dashboard rendering structures Redux-specific implementation details internal entity graphs not intended as extension contracts The dashboard context must remain aligned with the backend permission semantics of the dashboard APIs serving that surface. explore explore.getCurrentChart() exposes: current chart identity saved chart metadata transient chart-editing context semantic chart state relevant to the active Explore session The namespace should avoid exposing: unrelated chart entities unrelated datasource metadata frontend implementation-specific state structures
dataset dataset.getCurrentDataset() exposes: dataset identity dataset metadata semantic dataset context relevant to the active page The namespace must remain aligned with backend-enforced dataset visibility and column-access semantics. navigation navigation exposes lightweight routing and surface context: page type focused entity identity page-change events It should not embed full entity payloads already exposed through other namespaces.
Namespace compatibility Namespace contracts are part of the public Superset extension API surface and follow the normal compatibility guarantees of @apache-superset/core. Breaking changes to namespace contracts require standard deprecation and migration paths rather than silent behavioral changes. Extensions should depend only on the documented namespace contracts and not on internal frontend implementation details.
Reusing existing permission logic The backend scoping required above (notably the dashboard-context endpoint) must reuse the host's existing permission checks — the same ChartFilter / datasource-access and can_access logic already used by the API layer — rather than reimplementing parallel filtering logic. This ensures the context the chatbot receives cannot drift from the backend authorization model.
- Design decision Host-derived context vs. chatbot-assembled context Multiple approaches were evaluated for how chatbot extensions receive page context. Option 1 — chatbot-assembled context The host exposes low-level primitives, routing information, and surface-specific state. Each chatbot extension independently derives: the current pageType the focused entity dashboard/chart/dataset context its own higher-level context object Option 2 — host-derived context (chosen) The host computes normalized, permission-safe per-surface context through stable extension namespaces. The chatbot consumes and composes those already-safe fragments into its own application-specific context. This SIP chooses Option 2. The primary reason is that context derivation, semantic normalization, and extension-facing context contracts should remain host-owned concerns rather than extension responsibilities. Under Option 1, each chatbot extension independently derives semantic context from low-level frontend state and routing information. While much of that state already originates from permission-checked backend APIs, the meaning and visibility semantics of that state vary by surface and are tightly coupled to internal frontend implementation details. This creates several problems: extensions become coupled to Redux structure and frontend internals semantic context derivation becomes inconsistent across chatbot implementations frontend architecture changes become breaking changes for extensions permission-aware normalization logic becomes duplicated across extensions Option 2 establishes a host-owned semantic normalization layer through stable extension namespaces.
The host computes and normalizes the exposed context — sourced from permission-scoped backend APIs before it reaches the extension, providing a stable semantic contract across application surfaces. Permission enforcement stays on the backend (where it already lives); the namespace layer provides normalization and a stable contract, not a security boundary.
Option 2 also provides: stable, typed extension-facing contracts semantic per-surface APIs decoupling from frontend implementation details compatibility with future frontend architecture changes For example, the host may evolve away from Redux-based state management in the future without requiring chatbot extensions to change, as long as the namespace contracts remain stable.
Alternative evaluated: route-only / backend-derived context A third approach was also evaluated. Under this model: the frontend exposes only routing information (URL + route params) the chatbot backend or MCP server reconstructs context independently all entity resolution happens server-side through APIs This approach was rejected because it loses synchronization with unsaved frontend state and transient UI context. For example: unsaved chart edits in Explore dashboard filter changes not yet persisted temporary UI-control state selected charts/tabs local SQL editor changes draft configuration changes are all part of the user's active working context but may not yet exist in persisted backend state or be representable through the URL alone. A route-only model would therefore cause the chatbot context to drift from what the user is actively seeing and editing in the frontend. The chosen namespace-based model allows the chatbot to remain synchronized with live frontend state while still preserving stable, host-managed extension APIs.
Current namespace behavior The existing sqlLab namespace already follows the general model introduced by this SIP: backend APIs enforce authorization and visibility frontend state reflects the user's authorized application state the namespace layer exposes stable semantic extension-facing APIs over that state This SIP extends that model to additional application surfaces such as: dashboard explore dataset navigation The new namespaces introduced by this SIP are intended to provide: stable semantic extension contracts normalized page-context APIs decoupling from frontend implementation details compatibility across future frontend architecture changes Some application surfaces may require dedicated context-oriented APIs to expose semantic chatbot context cleanly and consistently. For example, dashboard chatbot context requires a stable representation of: dashboard entities dashboard filter state dashboard UI-control state active chart context rather than direct exposure of internal frontend state structures. The namespace layer therefore acts as: a semantic normalization layer a stable extension abstraction boundary the public context-sharing API for chatbot extensions Extensions consume namespace APIs rather than internal host state or frontend implementation details.
Tradeoffs accepted by this design The cost of Option 2 is accepted intentionally. The host must: build and maintain the context derivation layer expose new namespaces as additional application surfaces become extension-aware evolve namespace contracts over time Chatbot extensions are intentionally limited to the context exposed by those namespaces. If additional context is required, the namespace contract must evolve explicitly rather than allowing extensions to depend on arbitrary host state directly. That restriction is deliberate because it preserves: stable extension contracts implementation independence centralized semantic normalization consistent semantic context contracts aligned with backend authorization semantics
Extension-owned conversation state All conversation state, message history, tool-call state, streaming buffers, and persistence remain entirely owned by the chatbot extension. They are never reflected in: the host Redux store host-managed frontend state shared application reducers host persistence layers The host owns page-context exposure only. The chatbot owns all conversational runtime state.
Design considerations The bubble must not occlude critical dashboard content. The host guarantees a safe zone in the corner, but the extension is responsible for not extending the expanded panel in a way that covers important UI on smaller viewports. The host does not animate the bubble or panel — animation is the extension's responsibility, so each chatbot can have its own brand identity. The chatbot extension is initialized as part of the application shell lifecycle so the bubble is available consistently across routes and can be invoked immediately by the user. Since extensions are delivered through Module Federation, chatbot implementations should still be mindful of remote bundle size and avoid expensive synchronous initialization work during activation or first render. If the chatbot extension fails to load (Module Federation fetch error, runtime exception during activation), the host logs the error, shows a notification and leaves the corner empty rather than displaying a broken placeholder.
- Open Questions
Per-page visibility. Should the chatbot extension declare which pages it's relevant for (e.g., "only show on dashboard and SQL Lab")? Two options: Extension hides itself via navigation.onDidChangePage events. Extension declares page scopes in extension.json and the host enforces them. The first is simpler and keeps the host policy-free; the second is more discoverable.
Generalizing the slot. Should superset.chatbot be a chatbot-specific area, or should it generalize to something like app.floating-action that could accept multiple stacked widgets (chatbot, tour guide, notification widget)? My recommendation is to keep it chatbot-specific for now. If other floating-widget use cases emerge, introduce a separate contribution area rather than overloading this one.
-
Related Documents Contribution types Client actions
-
Migration Plan Base branch chat-prototype
Required core changes
The following are net-new additions to @apache-superset/core and the host that
this SIP depends on; they do not exist today and must land for the chatbot
extension point to be implementable:
-
New
dashboardnamespace — current-dashboard getter plus change events for the dashboard entity, its filters, and its UI-control state. Serves thedashboardpage type. -
New
explorenamespace — current-chart getter (saved chart + chart data) plus a change event. Serves thechartpage type (Explore view). -
New
datasetnamespace — current-dataset getter plus a change event. Serves thedatasetpage type. -
New
navigationnamespace — current-page getter and theonDidChangePageevent. -
Semantic normalization and stable extension-facing contracts for the new
dashboard,explore,dataset, andnavigationnamespaces.The namespace layer must expose curated semantic context APIs over backend-authorized application state while avoiding direct exposure of Redux structures or other frontend implementation details.
This work also includes the permission-scoped dashboard-context backend endpoint required to align dashboard chatbot context with existing Superset authorization semantics (§2.1).
-
New app-root contribution scope in the manifest schema (
ViewContributions) sosuperset.chatbotcan be declared inextension.json(currently SQL-Lab-only). -
Host-side exclusive-location resolution — an internal provider accessor (e.g.
getViewProvider(location, id)) or a dedicatedgetActiveChatbot()resolver, so the host can render the selected singleton;getViewsreturns descriptors only. -
Admin setting for singleton conflict resolution (the "Default chatbot" picker, §4 option (c)).
-
New
iconfield on theViewdescriptor interface (§3.2) — required for the admin picker and manifest listing. Open decision before implementation: whether the field is static (set atregisterView()) or runtime-updatable by the chatbot owner; this SIP recommends static.
- Phases The required core changes above are sequenced into the following phases. Tickets must reference these phase numbers (P1–P4) — there is no other phase numbering for this SIP.
-
P1 — Mount point & registration. The
superset.chatbotcontribution area, the app-root contribution scope inViewContributions, host-side exclusive-location resolution (getViewProvider/getActiveChatbot), eager mount at the app shell, fault isolation at the mount boundary, and the extension lifecycle/teardown contract — the deactivate/uninstall/replace signal and theDisposablecleanup drives. Open item in P1: whether teardown needs an async-aware hook (e.g.deactivate(): Promise<void>) in addition to the synchronousDisposable, sinceDisposable.dispose()is not awaited. -
P2 — Admin & singleton selection. The
iconfield onView, the "Default chatbot" admin setting, and admin enable/disable behavior (§2, §4). -
P3 — Context namespaces. The new
dashboard,explore,dataset, andnavigationnamespaces (semantic normalization + stable contract), plus the permission-scoped dashboard-context endpoint that closes the §2.1 gap on the backend. This is the critical path for page-context tickets. -
P4 — Navigation/client-action commands. Out of this SIP's scope — owned by the client-actions SIP (Justin Park).