# 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 |