Files
ss-tools/specs/019-superset-ux-redesign/contracts/modules.md
2026-02-18 17:29:46 +03:00

23 KiB

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

// [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

// [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

// [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

<!-- [DEF:Sidebar:Component] -->
<!-- @TIER: CRITICAL -->
<!-- @SEMANTICS: navigation, layout, responsive -->
<!-- @PURPOSE: Persistent left sidebar with resource categories and collapsible sections -->
<!-- @LAYER: UI -->
<!-- @RELATION: DEPENDS_ON -> sidebarStore -->
<!-- @RELATION: BINDS_TO -> +layout.svelte -->
<!-- @INVARIANT: Exactly one category is marked as active at all times -->

<!-- @UX_STATE: Idle-Expanded -> Full sidebar with text labels, 240px width -->
<!-- @UX_STATE: Idle-Collapsed -> Icon-only mode, 64px width, tooltips visible -->
<!-- @UX_STATE: Mobile-Hidden -> Sidebar off-screen, hamburger button visible in TopNavbar -->
<!-- @UX_STATE: Mobile-Open -> Full overlay with backdrop, closes on outside click -->
<!-- @UX_STATE: Category-Expanded -> Sub-items visible under category -->
<!-- @UX_STATE: Category-Collapsed -> Sub-items hidden, chevron rotated -->

<!-- @UX_FEEDBACK: Active item has highlighted background and left border accent -->
<!-- @UX_FEEDBACK: Collapse/expand animation is 200ms ease-in-out -->
<!-- @UX_FEEDBACK: Mobile overlay has semi-transparent backdrop -->

<!-- @PRE: sidebarStore is initialized -->
<!-- @PRE: i18n translations are loaded for navigation labels -->

<!-- @POST: Clicking category toggles its expanded state -->
<!-- @POST: Clicking item navigates to route and sets activeItem -->
<!-- @POST: On mobile, clicking outside closes the sidebar -->

<script>
  // Props
  export let categories = [
    { id: 'dashboards', label: 'DASHBOARDS', items: [{ path: '/dashboards', label: 'Overview' }] },
    { id: 'datasets', label: 'DATASETS', items: [{ path: '/datasets', label: 'All Datasets' }] },
    { id: 'storage', label: 'STORAGE', items: [{ path: '/backups', label: 'Backups' }, { path: '/repos', label: 'Repositories' }] },
    { id: 'admin', label: 'ADMIN', items: [{ path: '/users', label: 'Users' }, { path: '/settings', label: 'Settings' }], adminOnly: true }
  ];
</script>
<!-- [/DEF:Sidebar] -->

5. TopNavbar Component

<!-- [DEF:TopNavbar:Component] -->
<!-- @TIER: CRITICAL -->
<!-- @SEMANTICS: global-navigation, activity-indicator, user-menu -->
<!-- @PURPOSE: Consistent top navigation bar with global search, activity indicator, and user menu -->
<!-- @LAYER: UI -->
<!-- @RELATION: DEPENDS_ON -> activityStore -->
<!-- @RELATION: DEPENDS_ON -> authStore -->
<!-- @RELATION: BINDS_TO -> +layout.svelte -->
<!-- @INVARIANT: Height is fixed at 64px across all states -->

<!-- @UX_STATE: Idle -> All elements visible, activity badge shows count or hidden -->
<!-- @UX_STATE: UserMenu-Open -> Dropdown visible with Profile, Settings, Logout -->
<!-- @UX_STATE: Search-Focused -> Search input has focus ring, placeholder visible -->
<!-- @UX_STATE: Activity-Pulse -> Badge pulses when new task starts -->

<!-- @UX_FEEDBACK: Clicking logo navigates to home (/dashboards) -->
<!-- @UX_FEEDBACK: Activity badge shows count with color: gray (0), blue (running), red (error) -->
<!-- @UX_FEEDBACK: User menu closes on outside click or Escape key -->

<!-- @PRE: User is authenticated -->
<!-- @PRE: activityStore is subscribed and emitting values -->

<!-- @POST: Clicking Activity indicator opens TaskDrawer -->
<!-- @POST: Clicking Logout clears auth state and redirects to /login -->

<script>
  // Props
  export let logoText = 'Superset Tools';
  export let searchPlaceholder = 'Search...';
</script>
<!-- [/DEF:TopNavbar] -->

6. TaskDrawer Component

<!-- [DEF:TaskDrawer:Component] -->
<!-- @TIER: CRITICAL -->
<!-- @SEMANTICS: task-monitoring, log-streaming, interactive-input -->
<!-- @PURPOSE: Slide-out drawer for real-time task monitoring and interaction -->
<!-- @LAYER: UI -->
<!-- @RELATION: DEPENDS_ON -> taskDrawerStore -->
<!-- @RELATION: DEPENDS_ON -> TaskLogViewer component -->
<!-- @RELATION: DEPENDS_ON -> WebSocket service -->
<!-- @RELATION: BINDS_TO -> +layout.svelte -->
<!-- @INVARIANT: Drawer width is fixed at 480px on desktop, 100% on mobile -->
<!-- @INVARIANT: Log stream auto-scrolls to bottom unless user scrolls up -->

<!-- @UX_STATE: Closed -> Drawer off-screen right, no content rendered -->
<!-- @UX_STATE: Opening -> Slide animation 150ms, content loads -->
<!-- @UX_STATE: Open-Loading -> Spinner shown while fetching initial logs -->
<!-- @UX_STATE: Open-Streaming -> Logs append in real-time with timestamps -->
<!-- @UX_STATE: Open-InputRequired -> Input form overlays log area -->
<!-- @UX_STATE: Open-Error -> Error banner at top with retry option -->

<!-- @UX_FEEDBACK: Close button (X) in top-right corner -->
<!-- @UX_FEEDBACK: Task status badge in header (color-coded) -->
<!-- @UX_FEEDBACK: Log entries have source labels (plugin, system, user) -->
<!-- @UX_RECOVERY: If task fails, show "View Details" button linking to full error -->

<!-- @PRE: taskDrawerStore.isOpen === true before rendering content -->
<!-- @PRE: WebSocket connection is active for real-time updates -->
<!-- @PRE: TaskLogViewer component is available -->

<!-- @POST: Drawer closes when clicking X, Escape key, or outside (optional) -->
<!-- @POST: Closing drawer does NOT cancel running task -->
<!-- @POST: Re-opening drawer restores previous scroll position -->

<script>
  // Props
  export let width = 480; // px
  export let closeOnOutsideClick = false;
</script>
<!-- [/DEF:TaskDrawer] -->

7. DashboardHub Component

<!-- [DEF:DashboardHub:Component] -->
<!-- @TIER: CRITICAL -->
<!-- @SEMANTICS: resource-hub, bulk-operations, data-grid -->
<!-- @PURPOSE: Central hub for dashboard management with bulk selection and actions -->
<!-- @LAYER: UI -->
<!-- @RELATION: DEPENDS_ON -> requestApi -->
<!-- @RELATION: DEPENDS_ON -> taskDrawerStore -->
<!-- @RELATION: CALLS -> GET /api/dashboards -->
<!-- @RELATION: DISPATCHES -> MigrationPlugin -->
<!-- @RELATION: DISPATCHES -> BackupPlugin -->
<!-- @INVARIANT: Selected dashboards persist across pagination -->

<!-- @UX_STATE: Loading -> Skeleton loaders in grid rows -->
<!-- @UX_STATE: Empty-NoEnv -> "Select an environment" message with selector -->
<!-- @UX_STATE: Empty-NoData -> "No dashboards found" with refresh button -->
<!-- @UX_STATE: Idle-Grid -> Dashboard rows with checkboxes, pagination visible -->
<!-- @UX_STATE: Selecting -> Checkboxes checked, floating action panel appears -->
<!-- @UX_STATE: BulkAction-Modal -> Migration or Backup modal open -->
<!-- @UX_STATE: Task-Triggered -> TaskDrawer opens automatically -->

<!-- @UX_FEEDBACK: Row hover highlights entire row -->
<!-- @UX_FEEDBACK: Git status icon: green check (synced), orange diff (changes), gray (none) -->
<!-- @UX_FEEDBACK: Last task status: badge with color (green=success, red=error, blue=running) -->
<!-- @UX_FEEDBACK: Floating panel slides up from bottom when items selected -->
<!-- @UX_RECOVERY: Failed environment connection shows error banner with retry -->

<!-- @PRE: User has permission plugin:migration:execute for Migrate action -->
<!-- @PRE: User has permission plugin:backup:execute for Backup action -->
<!-- @PRE: At least one environment is configured -->

<!-- @POST: Clicking status badge opens TaskDrawer with that task -->
<!-- @POST: Bulk Migrate opens modal with source (read-only), target selector, db mappings -->
<!-- @POST: Bulk Backup opens modal with schedule options (one-time or cron) -->
<!-- @POST: Search filters results in real-time (debounced 300ms) -->
<!-- @POST: Pagination preserves selected items across page changes -->

<script>
  // State
  let dashboards = [];
  let selectedIds = new Set();
  let currentPage = 1;
  let pageSize = 10;
  let searchQuery = '';
  let environmentId = null;
</script>
<!-- [/DEF:DashboardHub] -->

8. DatasetHub Component

<!-- [DEF:DatasetHub:Component] -->
<!-- @TIER: CRITICAL -->
<!-- @SEMANTICS: resource-hub, semantic-mapping, documentation -->
<!-- @PURPOSE: Central hub for dataset management with column mapping and documentation generation -->
<!-- @LAYER: UI -->
<!-- @RELATION: DEPENDS_ON -> requestApi -->
<!-- @RELATION: DEPENDS_ON -> taskDrawerStore -->
<!-- @RELATION: CALLS -> GET /api/datasets -->
<!-- @RELATION: DISPATCHES -> MapperPlugin -->
<!-- @RELATION: DISPATCHES -> LLMAnalysisPlugin -->
<!-- @INVARIANT: Mapped % is calculated as (mapped_columns / total_columns) * 100 -->

<!-- @UX_STATE: Loading -> Skeleton loaders in grid rows -->
<!-- @UX_STATE: Empty-NoEnv -> "Select an environment" message -->
<!-- @UX_STATE: Empty-NoData -> "No datasets found" -->
<!-- @UX_STATE: Idle-Grid -> Dataset rows with metadata columns -->
<!-- @UX_STATE: Selecting -> Floating panel with Map Columns, Generate Docs, Validate -->
<!-- @UX_STATE: Detail-View -> Expanded row or modal with table breakdown -->

<!-- @UX_FEEDBACK: Mapped % column shows progress bar + percentage text -->
<!-- @UX_FEEDBACK: Tables column shows count of SQL tables extracted -->
<!-- @UX_FEEDBACK: Columns column shows "X/Y" format (mapped/total) -->
<!-- @UX_FEEDBACK: Start Mapping button is disabled until valid source is configured -->
<!-- @UX_RECOVERY: Failed mapping shows error toast with "Retry" action -->

<!-- @PRE: User has permission plugin:mapper:execute for Map Columns -->
<!-- @PRE: User has permission plugin:llm_analysis:execute for Generate Docs -->
<!-- @PRE: LLM providers are configured for documentation generation -->

<!-- @POST: Clicking dataset name opens detail view with tables breakdown -->
<!-- @POST: Map Columns modal shows source selection (PostgreSQL or XLSX) -->
<!-- @POST: Generate Docs modal shows LLM provider selection and options -->
<!-- @POST: Search filters by name, schema, and table names -->

<script>
  // State
  let datasets = [];
  let selectedIds = new Set();
  let currentPage = 1;
  let pageSize = 10;
  let searchQuery = '';
  let environmentId = null;
</script>
<!-- [/DEF:DatasetHub] -->

9. Breadcrumbs Component

<!-- [DEF:Breadcrumbs:Component] -->
<!-- @TIER: STANDARD -->
<!-- @SEMANTICS: navigation, hierarchy, wayfinding -->
<!-- @PURPOSE: Show page hierarchy and allow navigation to parent levels -->
<!-- @LAYER: UI -->
<!-- @RELATION: DEPENDS_ON -> $page store from $app/stores -->
<!-- @INVARIANT: Home is always first item, current page is last item -->

<!-- @UX_STATE: Short-Path -> All items visible (<= 3 levels) -->
<!-- @UX_STATE: Long-Path -> Middle items truncated with ellipsis (...) -->

<!-- @UX_FEEDBACK: Clickable items have hover underline -->
<!-- @UX_FEEDBACK: Current page (last item) is not clickable, shown in bold -->

<!-- @PRE: Route structure is defined and consistent -->

<!-- @POST: Clicking breadcrumb navigates to that level -->
<!-- @POST: Deep paths (>3 levels) truncate middle segments -->

<script>
  // Props
  export let items = []; // [{ label: string, path: string | null }]
  export let maxVisible = 3;
</script>
<!-- [/DEF:Breadcrumbs] -->

Backend Modules

10. Dashboards API

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

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

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

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