# Module Contracts: Superset-Style UX Redesign **Feature**: 019-superset-ux-redesign **Date**: 2026-02-10 **Semantic Protocol Version**: GRACE-Poly (UX Edition) --- ## Overview This document defines module-level contracts following the Semantic Protocol. Each module is annotated with `[DEF]` anchors, `@TIER` classification, and full Design-by-Contract (`@PRE`, `@POST`, `@UX_STATE`) specifications. --- ## Frontend Modules ### 1. SidebarStore ```javascript // [DEF:SidebarStore:Store] // @TIER: STANDARD // @SEMANTICS: navigation, state-management, persistence // @PURPOSE: Manage sidebar visibility, active navigation state, and mobile overlay behavior // @LAYER: UI // @RELATION: DEPENDS_ON -> localStorage // @RELATION: BINDS_TO -> +layout.svelte // @INVARIANT: isExpanded state is always synced with localStorage key 'sidebar_state' // @UX_STATE: Idle -> Sidebar visible with current category highlighted // @UX_STATE: Collapsed -> Icons-only mode, tooltips on hover // @UX_STATE: MobileOverlay -> Full-screen overlay with backdrop, close on outside click // @UX_STATE: Toggling -> CSS transition animation (200ms ease-in-out) // @PRE: localStorage is available and accessible // @PRE: defaultValue has valid shape { isExpanded: boolean, activeCategory: string, activeItem: string, isMobileOpen: boolean } // @POST: Store always emits valid SidebarState shape // @POST: localStorage 'sidebar_state' is updated on every state change // @POST: isMobileOpen is reset to false when screen size changes to desktop (>=768px) // @SIDE_EFFECT: Writes to localStorage // @SIDE_EFFECT: Triggers CSS transitions via class binding export const sidebarStore = persistentStore('sidebar_state', { isExpanded: true, activeCategory: 'dashboards', activeItem: '/dashboards', isMobileOpen: false }); export function toggleSidebar() { /* ... */ } export function setActiveCategory(category) { /* ... */ } export function setActiveItem(path) { /* ... */ } export function openMobileSidebar() { /* ... */ } export function closeMobileSidebar() { /* ... */ } // [/DEF:SidebarStore] ``` --- ### 2. TaskDrawerStore ```javascript // [DEF:TaskDrawerStore:Store] // @TIER: CRITICAL // @SEMANTICS: task-management, resource-mapping, real-time, drawer-state // @PURPOSE: Manage Task Drawer visibility, active task tracking, and resource-to-task associations // @LAYER: UI // @RELATION: DEPENDS_ON -> WebSocket connection // @RELATION: DEPENDS_ON -> task:status events // @RELATION: DEPENDS_ON -> task:log events // @RELATION: BINDS_TO -> TaskDrawer.svelte // @INVARIANT: resourceTaskMap always contains valid task associations // @INVARIANT: When isOpen is false, activeTaskId may still reference a running task // @UX_STATE: Closed -> Drawer hidden, Activity indicator shows active count // @UX_STATE: Open-Loading -> Drawer visible, loading spinner for logs // @UX_STATE: Open-Streaming -> Drawer visible, real-time log stream // @UX_STATE: Open-InputRequired -> Interactive form rendered in drawer (PasswordPrompt, etc.) // @UX_STATE: Open-Completed -> Task finished, success/error status displayed // @UX_FEEDBACK: Toast notification when task completes while drawer is closed // @UX_FEEDBACK: Activity indicator badge pulses when new tasks start // @UX_RECOVERY: If WebSocket disconnects, show "Reconnecting..." with retry button // @PRE: WebSocket connection is established before subscribing to task events // @PRE: taskId passed to openDrawerForTask() is a valid UUID string // @PRE: resourceId in updateResourceTask() matches pattern {type}:{uuid} // @POST: After openDrawerForTask(taskId): isOpen === true && activeTaskId === taskId // @POST: After closeDrawer(): isOpen === false, activeTaskId unchanged // @POST: resourceTaskMap is updated when task:status events are received // @POST: When task status changes to SUCCESS|ERROR, entry remains in map for 5 minutes then auto-cleanup // @SIDE_EFFECT: Subscribes to WebSocket events // @SIDE_EFFECT: May trigger browser notification for task completion export const taskDrawerStore = writable({ isOpen: false, activeTaskId: null, resourceTaskMap: {} // { resourceId: { taskId, status, startedAt } } }); export function openDrawerForTask(taskId) { /* ... */ } export function closeDrawer() { /* ... */ } export function updateResourceTask(resourceId, taskId, status) { /* ... */ } export function clearCompletedTasks() { /* ... */ } // [/DEF:TaskDrawerStore] ``` --- ### 3. ActivityStore ```javascript // [DEF:ActivityStore:Store] // @TIER: STANDARD // @SEMANTICS: activity-indicator, derived-state, task-count // @PURPOSE: Provide reactive count of active tasks for the navbar Activity indicator // @LAYER: UI // @RELATION: DEPENDS_ON -> TaskDrawerStore // @RELATION: BINDS_TO -> TopNavbar.svelte // @INVARIANT: activeCount is always >= 0 // @INVARIANT: activeCount equals count of RUNNING tasks in resourceTaskMap // @UX_STATE: Idle -> Badge hidden or shows "0" // @UX_STATE: Active-N -> Badge shows count N with pulse animation // @UX_STATE: Active-Many -> Badge shows "9+" when count exceeds 9 // @PRE: TaskDrawerStore is initialized and emitting values // @POST: activeCount reflects exact count of tasks with status === 'RUNNING' // @POST: recentTasks contains last 5 tasks sorted by startedAt desc export const activityStore = derived( taskDrawerStore, ($drawer) => { const tasks = Object.values($drawer.resourceTaskMap); const activeCount = tasks.filter(t => t.status === 'RUNNING').length; const recentTasks = tasks .sort((a, b) => new Date(b.startedAt) - new Date(a.startedAt)) .slice(0, 5); return { activeCount, recentTasks }; } ); // [/DEF:ActivityStore] ``` --- ### 4. Sidebar Component ```svelte ``` --- ### 5. TopNavbar Component ```svelte ``` --- ### 6. TaskDrawer Component ```svelte ``` --- ### 7. DashboardHub Component ```svelte ``` --- ### 8. DatasetHub Component ```svelte ``` --- ### 9. Breadcrumbs Component ```svelte ``` --- ## Backend Modules ### 10. Dashboards API ```python # [DEF:DashboardsAPI:Module] # @TIER: CRITICAL # @SEMANTICS: rest-api, resource-fetching, git-integration # @PURPOSE: API endpoints for Dashboard Hub - list dashboards with metadata # @LAYER: Domain # @RELATION: DEPENDS_ON -> superset_client # @RELATION: DEPENDS_ON -> git_service # @RELATION: DEPENDS_ON -> task_manager # @RELATION: CALLS -> Superset API /api/v1/dashboard/ # @PRE: env_id parameter is valid UUID of existing environment # @PRE: User has permission to read dashboards from specified environment # @POST: Response includes all dashboards accessible in environment # @POST: Each dashboard has git_status with branch and sync_status # @POST: Each dashboard has last_task with most recent task status # @POST: Search parameter filters by title and slug (case-insensitive) # Endpoint: GET /api/dashboards # Query: env_id (required), search (optional) # Response: { dashboards: [...] } # Endpoint: POST /api/dashboards/migrate # Body: { source_env_id, target_env_id, dashboard_ids, db_mappings } # Response: { task_id } # Endpoint: POST /api/dashboards/backup # Body: { env_id, dashboard_ids, schedule (optional cron) } # Response: { task_id } # [/DEF:DashboardsAPI] ``` --- ### 11. Datasets API ```python # [DEF:DatasetsAPI:Module] # @TIER: CRITICAL # @SEMANTICS: rest-api, dataset-metadata, column-mapping # @PURPOSE: API endpoints for Dataset Hub - list datasets with mapping info # @LAYER: Domain # @RELATION: DEPENDS_ON -> superset_client # @RELATION: DEPENDS_ON -> mapping_service # @RELATION: DEPENDS_ON -> task_manager # @RELATION: CALLS -> Superset API /api/v1/dataset/ # @PRE: env_id parameter is valid UUID of existing environment # @PRE: User has permission to read datasets from specified environment # @POST: Response includes all datasets accessible in environment # @POST: Each dataset has mapped_fields count (mapped/total) # @POST: Each dataset has extracted SQL table names # @POST: Search filters by table_name, schema, and extracted table names # Endpoint: GET /api/datasets # Query: env_id (required), search (optional) # Response: { datasets: [...] } # Endpoint: POST /api/datasets/map-columns # Body: { env_id, dataset_ids, source_type, connection_id or file } # Response: { task_id } # Endpoint: POST /api/datasets/generate-docs # Body: { env_id, dataset_ids, llm_provider, options } # Response: { task_id } # [/DEF:DatasetsAPI] ``` --- ### 12. Activity API ```python # [DEF:ActivityAPI:Module] # @TIER: STANDARD # @SEMANTICS: rest-api, activity-summary, task-metrics # @PURPOSE: Provide activity summary for navbar indicator # @LAYER: Domain # @RELATION: DEPENDS_ON -> task_manager # @RELATION: CALLS -> task_manager.get_active_tasks() # @PRE: User is authenticated # @POST: Returns count of RUNNING tasks # @POST: Returns last 5 tasks sorted by started_at desc # @POST: Tasks include resource_name and resource_type for display # Endpoint: GET /api/activity # Response: { active_count: number, recent_tasks: [...] } # [/DEF:ActivityAPI] ``` --- ### 13. ResourceService ```python # [DEF:ResourceService:Class] # @TIER: STANDARD # @SEMANTICS: service-layer, shared-logic, resource-fetching # @PURPOSE: Shared logic for fetching resources from Superset with caching # @LAYER: Domain # @RELATION: DEPENDS_ON -> superset_client # @RELATION: DEPENDS_ON -> ConfigManager # @INVARIANT: All methods accept env_id and resolve via ConfigManager # @PRE: env_id corresponds to valid Environment configuration # @PRE: ConfigManager returns valid Superset connection params # @POST: Returns normalized resource data structure # @POST: Handles connection errors gracefully with meaningful messages # @POST: Caches results for 30 seconds to reduce API load class ResourceService: async def fetch_dashboards(env_id: str, search: str = None) -> list[Dashboard]: ... async def fetch_datasets(env_id: str, search: str = None) -> list[Dataset]: ... async def get_git_status(env_id: str, resource_id: str) -> GitStatus: ... async def get_last_task(resource_type: str, resource_id: str) -> TaskSummary: ... # [/DEF:ResourceService] ``` --- ## Contract Compliance Matrix | Module | TIER | @PRE/@POST | @UX_STATE | @RELATION | Status | |--------|------|------------|-----------|-----------|--------| | SidebarStore | STANDARD | ✅ | ✅ | ✅ | Ready | | TaskDrawerStore | CRITICAL | ✅ | ✅ | ✅ | Ready | | ActivityStore | STANDARD | ✅ | ✅ | ✅ | Ready | | Sidebar | CRITICAL | ✅ | ✅ | ✅ | Ready | | TopNavbar | CRITICAL | ✅ | ✅ | ✅ | Ready | | TaskDrawer | CRITICAL | ✅ | ✅ | ✅ | Ready | | DashboardHub | CRITICAL | ✅ | ✅ | ✅ | Ready | | DatasetHub | CRITICAL | ✅ | ✅ | ✅ | Ready | | Breadcrumbs | STANDARD | ✅ | ✅ | ✅ | Ready | | DashboardsAPI | CRITICAL | ✅ | N/A | ✅ | Ready | | DatasetsAPI | CRITICAL | ✅ | N/A | ✅ | Ready | | ActivityAPI | STANDARD | ✅ | N/A | ✅ | Ready | | ResourceService | STANDARD | ✅ | N/A | ✅ | Ready | --- ## UX State Mapping to Components | UX State (from ux_reference.md) | Component | @UX_STATE Tag | |----------------------------------|-----------|---------------| | Sidebar Expanded | Sidebar | Idle-Expanded | | Sidebar Collapsed | Sidebar | Idle-Collapsed | | Mobile Overlay | Sidebar | Mobile-Open | | Task Drawer Closed | TaskDrawer | Closed | | Task Drawer Streaming | TaskDrawer | Open-Streaming | | Task Drawer Input Required | TaskDrawer | Open-InputRequired | | Activity Badge Active | TopNavbar | Activity-Pulse | | Dashboard Grid Loading | DashboardHub | Loading | | Dashboard Grid Empty | DashboardHub | Empty-NoData | | Bulk Selection Active | DashboardHub | Selecting | | Migration Modal Open | DashboardHub | BulkAction-Modal | | Dataset Grid Loading | DatasetHub | Loading | | Dataset Detail View | DatasetHub | Detail-View | --- ## Design-by-Contract Enforcement ### Critical Modules (Full Contracts Required) All CRITICAL tier modules MUST have: - Complete `@PRE` conditions for all public functions - Complete `@POST` conditions guaranteeing output - All `@UX_STATE` transitions documented - `@UX_FEEDBACK` and `@UX_RECOVERY` where applicable - `@RELATION` tags for all dependencies ### Standard Modules (Basic Contracts Required) All STANDARD tier modules MUST have: - `@PURPOSE` declaration - Key `@PRE` and `@POST` for main operations - Primary `@UX_STATE` states - `@RELATION` tags for major dependencies --- ## Constitution Compliance | Principle | Compliance | Notes | |-----------|------------|-------| | I. Semantic Protocol | ✅ | All modules use [DEF] anchors and proper tagging | | II. Everything is a Plugin | ✅ | Dashboard/Dataset hubs dispatch to existing plugins | | III. Unified Frontend | ✅ | Uses requestApi, i18n, and component library | | IV. Security & Access Control | ✅ | Permission checks in @PRE conditions | | V. Independent Testability | ✅ | Each hub has defined independent test scenarios | | VI. Asynchronous Execution | ✅ | All bulk operations return task_id, use TaskManager |