Files
ss-tools/specs/031-maintenance-banner/plan.md
busya d9669698b8 feat(031): Maintenance Banner for Dashboards — full spec→plan→tasks package
Spec: 5 user stories (P1-P3), 17 FRs, 7 SCs, 12 edge cases, RBAC matrix
Plan: 7 research decisions, 15 semantic contracts (C1-C4)
Data: 4 SQLAlchemy models (Banner entity enforces one-chart-per-dashboard)
UX: async-only API, banner template with optional variables, admin settings UI
Tasks: 64 tasks across 8 phases, 12 reused frontend components
Workflow: component reuse scan mandated in speckit.plan/tasks + semantics-svelte
Constitution: filled with 7 architecture principles from ADRs
2026-05-22 16:20:39 +03:00

142 lines
8.1 KiB
Markdown

# Implementation Plan: Maintenance Banner for Dashboards
**Branch**: `031-maintenance-banner` | **Date**: 2026-05-21 | **Spec**: [spec.md](./spec.md)
**Input**: Feature specification from `/specs/031-maintenance-banner/spec.md`
## Summary
Service for automatically adding/removing maintenance warning banners on Superset dashboards during ETL table updates. External tools call async API endpoints to start/end maintenance events; ss-tools discovers affected dashboards (by matching table names against datasets), creates markdown charts at the top of each dashboard via Superset REST API, and provides a management UI for manual control and configuration. All operations are RBAC-gated (operator/admin roles) and traceable via TaskManager.
## Technical Context
**Language/Version**: Python 3.13+ (backend), JavaScript/TypeScript (frontend Svelte 5 runes)
**Primary Dependencies**: FastAPI 0.126, SQLAlchemy 2.0, APScheduler 3.11 (backend); SvelteKit 2.x, Svelte 5.43, Vite 7.x, Tailwind CSS 3.x (frontend)
**Storage**: PostgreSQL 16 (dedicated ss-tools DB — not Superset metadata DB per ADR-0003)
**Testing**: pytest (backend), vitest + @testing-library/svelte (frontend)
**Target Platform**: Linux server (Docker), modern browsers
**Project Type**: web application (FastAPI REST + WebSocket backend, SvelteKit SPA frontend)
**Performance Goals**: /start returns task_id within 1s; banner placement within 60s for up to 50 dashboards; /end within 30s for up to 50 dashboards
**Constraints**: RBAC enforcement (operator/admin per ADR-0005), Svelte 5 runes only (per ADR-0006), no fromStore + $derived (per ADR-0007), offline-capable Docker bundle
**Scale/Scope**: 1 maintenance event typically affects 5-50 dashboards; up to 5 concurrent active events; table lists up to 100 entries
## Constitution Check
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
| Principle | Status | Evidence |
|-----------|--------|----------|
| ADR-0001 (Module Layout) | ✅ PASS | All planned files follow canonical layout: `backend/src/api/routes/maintenance/`, `backend/src/services/maintenance_service.py`, `backend/src/models/maintenance.py`, `frontend/src/routes/maintenance/`, `frontend/src/lib/components/` |
| ADR-0002 (Semantic Protocol) | ✅ PASS | All C3+ contracts carry `@PURPOSE`, `@RELATION`; C4 contracts add `@PRE`/`@POST`/`@SIDE_EFFECT` + belief runtime |
| ADR-0003 (Orchestrator Pattern) | ✅ PASS | Feature operates as external orchestrator, not integrated into Superset. Uses Superset REST API only. |
| ADR-0004 (Plugin Architecture) | ✅ PASS | Maintenance is a core feature in `services/` (stateless, in-process), not a subprocess-isolated plugin per ADR-0004. Justified in research.md R2. |
| ADR-0005 (Auth RBAC) | ✅ PASS | FR-014: operator/admin roles for mutations; admin for settings |
| ADR-0006 (Frontend Architecture) | ✅ PASS | Svelte 5 runes, SPA mode (adapter-static), Tailwind CSS, requestApi wrapper |
| ADR-0007 (fromStore REJECTED) | ✅ PASS | Maintenance store uses `$effect(() => subscribe(...))` pattern, not fromStore + $derived |
| Module <400 LOC | PASS | Each planned file: routes ~150 LOC, service ~250 LOC, components ~150 LOC each |
| CC 10 per contract | PASS | All contract functions are small, focused operations |
**Verdict**: No blocking conflicts. Proceed to Phase 0/1.
## Project Structure
### Documentation (this feature)
```text
specs/031-maintenance-banner/
├── plan.md # This file
├── research.md # Phase 0: 7 resolved decisions
├── data-model.md # Phase 1: SQLAlchemy models, Pydantic schemas, state transitions
├── quickstart.md # Phase 1: Verification commands
├── contracts/
│ └── modules.md # Phase 1: 14 semantic contracts (C1-C4)
├── spec.md # Feature specification
├── ux_reference.md # UX interaction reference
├── checklists/
│ └── requirements.md # Requirements validation checklist
└── tasks.md # Phase 2 output (NOT created by /speckit.plan)
```
### Source Code (repository root)
```text
backend/
├── src/
│ ├── api/routes/maintenance/ # NEW: package-based route module
│ │ ├── __init__.py
│ │ ├── _router.py # APIRouter assembly
│ │ ├── _routes.py # Route handlers
│ │ └── _schemas.py # Pydantic schemas
│ ├── core/superset_client/
│ │ └── _dashboards_write.py # NEW: SupersetClient write mixin
│ ├── models/
│ │ └── maintenance.py # NEW: SQLAlchemy models
│ └── services/
│ ├── maintenance_service.py # NEW: Core business logic
│ └── sql_table_extractor.py # NEW: SQL table name parser
├── tests/
│ ├── test_maintenance_service.py # Unit/integration tests
│ ├── test_sql_table_extractor.py # SQL parsing tests
│ └── test_maintenance_api.py # API endpoint tests
frontend/
├── src/
│ ├── routes/maintenance/
│ │ └── +page.svelte # NEW: Management page
│ ├── lib/
│ │ ├── components/
│ │ │ ├── MaintenanceSettingsPanel.svelte # NEW
│ │ │ ├── MaintenanceEventsTable.svelte # NEW
│ │ │ └── DashboardMaintenanceBadge.svelte # NEW
│ │ ├── stores/
│ │ │ └── maintenance.svelte.js # NEW
│ │ └── api/
│ │ └── maintenance.js # NEW
│ └── routes/dashboards/
│ └── +page.svelte # MODIFIED: add maintenance badge column
└── tests/
├── maintenance.test.ts # Component tests
└── maintenance-store.test.ts # Store tests
```
**Structure Decision**: Package-based route for `maintenance/` mirrors existing `dashboards/` pattern. Backend service in `services/` (stateless, request-scoped) per ADR-0001 boundary rule #3. Superset client extension in `core/superset_client/` as singleton mixin.
## Semantic Contract Guidance
All 15 contracts defined in `contracts/modules.md`:
- **Complexity 1** (4): MaintenanceEvent, MaintenanceDashboardBanner, MaintenanceDashboardState, MaintenanceSettings models, Pydantic schemas
- **Complexity 2** (3): SqlTableExtractor, MaintenanceRouter, MaintenanceApiClient, DashboardMaintenanceBadge
- **Complexity 3** (5): MaintenanceRoutes, MaintenanceStore, MaintenanceBannerPage, MaintenanceSettingsPanel, MaintenanceEventsTable
- **Complexity 4** (3): SupersetDashboardsWriteMixin, MaintenanceService (start/end/end_all methods)
C4 contracts require `@PRE`, `@POST`, `@SIDE_EFFECT` plus belief runtime instrumentation per semantics-python skill.
## Complexity Tracking
*No constitutional violations to justify. All module placements and complexity levels follow ADR-0001 guidelines.*
| Aspect | Rationale |
|--------|-----------|
| Package-based route (`maintenance/`) | Mirrors `dashboards/` pattern; 3 files <200 LOC each |
| Service in `services/` not `core/` | Stateless, request-scoped per ADR-0001 rule #3 |
| SupersetClient mixin in `core/` | Singleton, app-scoped per ADR-0001 rule #2 |
| Not a plugin | Maintenance is core infrastructure, not user-configurable |
## Phase 0 Outputs
See [research.md](./research.md) for 7 resolved decisions:
1. Chart placement via direct API (not export-import)
2. Module placement per ADR-0001
3. SQL parsing via sqlparse with regex fallback
4. Async orchestration via existing TaskManager
5. Dedicated DB tables (not AppConfigRecord JSON)
6. Svelte 5 runes with ADR-0007 compliance
7. Existing RBAC reuse (operator + admin roles)
## Phase 1 Outputs
| Artifact | Path | Description |
|----------|------|-------------|
| Data Model | [data-model.md](./data-model.md) | 3 SQLAlchemy models, 10 Pydantic schemas, ER diagram, state transitions |
| Contracts | [contracts/modules.md](./contracts/modules.md) | 14 semantic contracts (C1-C4) with relations, pre/post, rationale |
| Quickstart | [quickstart.md](./quickstart.md) | Verification commands for backend/frontend/lint |