tasks
This commit is contained in:
270
specs/031-maintenance-banner/tasks.md
Normal file
270
specs/031-maintenance-banner/tasks.md
Normal file
@@ -0,0 +1,270 @@
|
||||
# Tasks: Maintenance Banner for Dashboards
|
||||
|
||||
**Input**: Design documents from `specs/031-maintenance-banner/`
|
||||
**Prerequisites**: plan.md ✅, spec.md ✅, research.md ✅, data-model.md ✅, contracts/modules.md ✅
|
||||
|
||||
**Tests**: Included for all C3+ contracts, new API endpoints, database models, and ADR-rejected regression paths.
|
||||
|
||||
**Organization**: Tasks grouped by user story for independent implementation and verification.
|
||||
|
||||
## Format: `- [ ] T### [P?] [USx] Description with exact file path`
|
||||
|
||||
- **[P]**: Can run in parallel (different files, no dependencies)
|
||||
- **[USx]**: Which user story (US1-US5); `--` for setup/foundational/polish phases
|
||||
- Paths relative to repository root
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Setup (Shared Infrastructure)
|
||||
|
||||
**Purpose**: Project scaffolding — directories, package inits, Alembic migration skeleton.
|
||||
|
||||
- [ ] T001 Create `backend/src/api/routes/maintenance/` package directory structure (`__init__.py`)
|
||||
- [ ] T002 [P] Create `backend/tests/` test file stubs for maintenance feature: `test_maintenance_service.py`, `test_sql_table_extractor.py`, `test_maintenance_api.py`
|
||||
- [ ] T003 [P] Create frontend directory structure: `frontend/src/routes/maintenance/`, `frontend/src/lib/components/MaintenanceSettingsPanel.svelte`, `frontend/src/lib/components/MaintenanceEventsTable.svelte`, `frontend/src/lib/components/DashboardMaintenanceBadge.svelte` (empty placeholder files)
|
||||
- [ ] T004 Generate Alembic migration for new tables (`maintenance_events`, `maintenance_dashboard_states`, `maintenance_settings`) via `alembic revision --autogenerate -m "add maintenance banner tables"` after models are defined in Phase 2
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Foundational (Blocking Prerequisites)
|
||||
|
||||
**Purpose**: Core infrastructure that MUST be complete before ANY user story can be implemented. All models, schemas, SupersetClient extension, SQL extractor, router scaffold.
|
||||
|
||||
**⚠️ CRITICAL**: No user story work can begin until this phase is complete.
|
||||
|
||||
### Database Models
|
||||
|
||||
- [ ] T005 [P] [--] Implement `MaintenanceEvent` SQLAlchemy model in `backend/src/models/maintenance.py` with columns (id, task_id, tables JSON, start_time, end_time, message, status enum, timestamps) and relationship to MaintenanceDashboardState. Contract: `[DEF:MaintenanceEvent:Model]` C1.
|
||||
- [ ] T006 [P] [--] Implement `MaintenanceDashboardState` SQLAlchemy model in `backend/src/models/maintenance.py` with columns (id, event_id FK, dashboard_id, environment_id, chart_id, status enum, created_at, removed_at) and composite index on (dashboard_id, status). Contract: `[DEF:MaintenanceDashboardState:Model]` C1.
|
||||
- [ ] T007 [P] [--] Implement `MaintenanceSettings` SQLAlchemy model in `backend/src/models/maintenance.py` — single-row table (id="default") with columns (target_environment_id, dashboard_scope enum, excluded_dashboard_ids JSON, forced_dashboard_ids JSON, updated_at). Contract: `[DEF:MaintenanceSettings:Model]` C1.
|
||||
|
||||
### Pydantic Schemas
|
||||
|
||||
- [ ] T008 [--] Implement all Pydantic request/response schemas in `backend/src/api/routes/maintenance/_schemas.py`: MaintenanceStartRequest, MaintenanceSettingsUpdate, MaintenanceStartResponse, MaintenanceTaskResult, MaintenanceDashboardResult, MaintenanceFailedDashboard, MaintenanceEndResponse, MaintenanceEndTaskResult, MaintenanceEndAllTaskResult, MaintenanceSettingsResponse, MaintenanceEventItem, MaintenanceEventListResponse, MaintenanceDashboardBannerState. Contract: `[DEF:MaintenanceSchemas:Module]` C1.
|
||||
|
||||
### SupersetClient Extension (C4 — REQUIRES belief runtime)
|
||||
|
||||
- [ ] T009 [--] Implement `SupersetDashboardsWriteMixin` in `backend/src/core/superset_client/_dashboards_write.py` with methods: `create_markdown_chart(dashboard_id, markdown_text) → chart_id` (POST /api/v1/chart/ with viz_type="markdown"), `update_dashboard_layout(dashboard_id, chart_id, position)` (PUT /api/v1/dashboard/{id} with patched position_json), `delete_chart(chart_id)` (DELETE /api/v1/chart/{id}), `get_dashboard_layout(dashboard_id)` (GET /api/v1/dashboard/{id} for position_json). Contract: `[DEF:SupersetDashboardsWriteMixin:Class]` C4. (RATIONALE: direct chart API faster than export-import for single-chart ops per research.md R1; REJECTED: export→modify YAML→re-import cycle — too slow for 50+ dashboards)
|
||||
- [ ] T010 [--] Instrument `SupersetDashboardsWriteMixin` with belief runtime markers: `belief_scope` context manager wrapping each Superset API call, `log(REASON)` before POST/PUT/DELETE, `log(REFLECT)` on success, `log(EXPLORE)` on HTTP errors.
|
||||
|
||||
### SQL Table Extractor (C2)
|
||||
|
||||
- [ ] T011 [--] Implement `SqlTableExtractor` module in `backend/src/services/sql_table_extractor.py` with two-phase extraction: `extract_table_candidates(raw_sql)` using regex `[a-zA-Z][\w]*\.[a-zA-Z][\w]*` on raw SQL+Jinja text, and `filter_string_literals(raw_sql, candidates)` using sqlparse tokenizer to reject candidates inside SQL string literals/comments. Orchestrator function: `extract_tables_from_sql(raw_sql) → set[str]`. Contract: `[DEF:SqlTableExtractor:Module]` C2. (RATIONALE: table names in Jinja {% set %} blocks survive global extraction; REJECTED: strip-Jinja-first approach loses 60% of table references per production dataset analysis)
|
||||
|
||||
### Router & App Registration
|
||||
|
||||
- [ ] T012 [--] Implement `MaintenanceRouter` in `backend/src/api/routes/maintenance/_router.py` — creates `APIRouter(prefix="/api/maintenance", tags=["maintenance"])`. Contract: `[DEF:MaintenanceRouter:Module]` C2.
|
||||
- [ ] T013 [--] Implement route handler stubs in `backend/src/api/routes/maintenance/_routes.py` — all 7 endpoint signatures with placeholder returns and proper RBAC `Depends(require_role(...))` guards. Contract: `[DEF:MaintenanceRoutes:Module]` C3 (stub phase).
|
||||
- [ ] T014 [--] Register `maintenance_router` in main FastAPI application (`backend/src/app.py`) and register `SupersetDashboardsWriteMixin` in SupersetClient assembly.
|
||||
|
||||
### Seed Data
|
||||
|
||||
- [ ] T015 [--] Create Alembic data migration (or startup check) to insert default `MaintenanceSettings` row (id="default", target_environment_id=<from config>, dashboard_scope="published_only", excluded=[], forced=[]) if not exists.
|
||||
|
||||
**Checkpoint**: Foundation ready — models, schemas, SupersetClient extension, SQL extractor, router, and seed data in place. All user stories can now begin.
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: User Story 1 — Banner Placement (Priority: P1) 🎯 MVP
|
||||
|
||||
**Goal**: External tool calls `POST /api/maintenance/start` → ss-tools finds affected dashboards → places markdown banners.
|
||||
|
||||
**Independent Test**: Call API with a known table name, verify banner appears on affected dashboards in Superset.
|
||||
|
||||
### Tests for US1 (Write FIRST, ensure they FAIL)
|
||||
|
||||
- [ ] T016 [P] [US1] Write contract test for `POST /api/maintenance/start` in `backend/tests/test_maintenance_api.py` — test valid request returns `{task_id, status: "pending"}`, test duplicate request returns `{status: "already_active"}`, test no-match returns `{status: "no_match"}`, test RBAC returns 403 for viewer role.
|
||||
- [ ] T017 [P] [US1] Write unit test for `SqlTableExtractor.extract_tables_from_sql()` in `backend/tests/test_sql_table_extractor.py` — test table-based matching, virtual-SQL extraction with Jinja `{% set %}`, string literal false-positive rejection, case-insensitive matching.
|
||||
- [ ] T018 [P] [US1] Write integration test for `MaintenanceService.start_maintenance()` in `backend/tests/test_maintenance_service.py` — test dashboard discovery, markdown chart creation, MaintenanceEvent persistence, MaintenanceDashboardState creation, idempotency check.
|
||||
|
||||
### Implementation for US1
|
||||
|
||||
- [ ] T019 [US1] Implement `find_affected_dashboards()` method in `backend/src/services/maintenance_service.py` — fetches all datasets from Superset, matches table-based datasets via (schema, table_name), matches virtual datasets via SqlTableExtractor, applies scope/excluded/forced filtering from MaintenanceSettings, returns deduplicated dashboard list. Contract C3 within `[DEF:MaintenanceService:Class]`.
|
||||
- [ ] T020 [US1] Implement `build_banner_text()` method in `backend/src/services/maintenance_service.py` — aggregates multiple active events into a single markdown string with ⚠️ header, start/end times per event, and optional message. Handles single-event (simple text) and multi-event (aggregated text) cases. Contract C2 within `[DEF:MaintenanceService:Class]`.
|
||||
- [ ] T021 [US1] Implement `start_maintenance()` method in `backend/src/services/maintenance_service.py` — orchestrates full flow: (1) idempotency check (reuse active event with same tables), (2) `find_affected_dashboards()`, (3) create MaintenanceEvent record, (4) for each dashboard: create markdown chart via SupersetDashboardsWriteMixin, create MaintenanceDashboardState, patch dashboard layout, handle partial failures with 207 response, (5) if dashboard has existing banner from other events → rebuild aggregated text. Contract C4 within `[DEF:MaintenanceService:Class]`. (RATIONALE: single orchestrator ensures consistency between local DB state and Superset chart state)
|
||||
- [ ] T022 [US1] Instrument `start_maintenance()` with belief runtime: `belief_scope("start_maintenance")`, `log(REASON)` at entry/checkpoint, `log(REFLECT)` on completion, `log(EXPLORE)` on Superset errors.
|
||||
- [ ] T023 [US1] Implement `POST /api/maintenance/start` route handler in `backend/src/api/routes/maintenance/_routes.py` — validates MaintenanceStartRequest, dispatches task via TaskManager.create_task() with type="maintenance_banner_apply", returns MaintenanceStartResponse with task_id immediately. RBAC: `Depends(require_role("operator"))`. Handles no-match fast-path (returns synchronously without task).
|
||||
- [ ] T024 [US1] Run backend verification: `cd backend && source .venv/bin/activate && python -m pytest backend/tests/test_maintenance_api.py backend/tests/test_maintenance_service.py backend/tests/test_sql_table_extractor.py -v`
|
||||
|
||||
**Checkpoint**: `POST /api/maintenance/start` fully functional. Banners placed on affected dashboards. Idempotency working.
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: User Story 2 — Banner Removal (Priority: P1)
|
||||
|
||||
**Goal**: External tool calls `POST /api/maintenance/{id}/end` or `/end-all` → banners removed from dashboards.
|
||||
|
||||
**Independent Test**: After US1, call /end and verify banners disappear; call again and verify idempotent.
|
||||
|
||||
### Tests for US2 (Write FIRST, ensure they FAIL)
|
||||
|
||||
- [ ] T025 [P] [US2] Write contract test for `POST /api/maintenance/{id}/end` in `backend/tests/test_maintenance_api.py` — test successful removal, test already-completed idempotent response, test multi-event scenario (banner stays when other active events exist, text rebuilt).
|
||||
- [ ] T026 [P] [US2] Write integration test for `MaintenanceService.end_maintenance()` in `backend/tests/test_maintenance_service.py` — test chart deletion via Superset API, test MaintenanceDashboardState transition to REMOVED, test MaintenanceEvent transition to COMPLETED, test banner rebuild when other active events remain.
|
||||
- [ ] T026a [P] [US2] Write integration test for `MaintenanceService.end_all_maintenance()` in `backend/tests/test_maintenance_service.py` — test bulk closure of all active events, verify each transitions to COMPLETED, test mixed state (some dashboards have other active events → banner stays, others → banner removed).
|
||||
|
||||
### Implementation for US2
|
||||
|
||||
- [ ] T027 [US2] Implement `rebuild_banner()` method in `backend/src/services/maintenance_service.py` — for a given dashboard, queries all active MaintenanceDashboardState records, calls `build_banner_text()` for aggregated text, updates the existing markdown chart in Superset via `update_markdown_chart()`. Contract C3 within `[DEF:MaintenanceService:Class]`.
|
||||
- [ ] T028 [US2] Implement `end_maintenance()` method in `backend/src/services/maintenance_service.py` — for each dashboard in the event: (1) check if OTHER active events still affect this dashboard; (2) if yes → call `rebuild_banner()` (remove this event's info from text); (3) if no → delete chart via SupersetDashboardsWriteMixin.delete_chart(), update MaintenanceDashboardState to REMOVED; (4) transition MaintenanceEvent to COMPLETED. Contract C4. (RATIONALE: conditional removal prevents banner flicker when multiple events overlap; REJECTED: unconditional removal that briefly deletes then re-creates the banner)
|
||||
- [ ] T029 [US2] Instrument `end_maintenance()` with belief runtime markers.
|
||||
- [ ] T030 [US2] Implement `end_all_maintenance()` method in `backend/src/services/maintenance_service.py` — iterates all active events, calls `end_maintenance()` for each. Contract C4.
|
||||
- [ ] T031 [US2] Implement `POST /api/maintenance/{maintenance_id}/end` and `POST /api/maintenance/end-all` route handlers in `backend/src/api/routes/maintenance/_routes.py` — dispatch tasks via TaskManager, return task_id immediately. RBAC: `Depends(require_role("operator"))`.
|
||||
- [ ] T032 [US2] Run backend verification: `cd backend && source .venv/bin/activate && python -m pytest backend/tests/test_maintenance_api.py backend/tests/test_maintenance_service.py -v -k "end"`
|
||||
|
||||
**Checkpoint**: Banner removal fully functional. Multi-event conflict handling verified. Idempotent /end confirmed.
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: User Story 4 — Settings Configuration (Priority: P3)
|
||||
|
||||
**Goal**: Administrator can configure maintenance scope (published/draft/all, excluded dashboards, forced dashboards) via API and UI.
|
||||
|
||||
**Note**: Settings model is already in Phase 2 (T007). This phase adds API endpoints and UI panel.
|
||||
|
||||
### Tests for US4
|
||||
|
||||
- [ ] T033 [P] [US4] Write API test for `GET /api/maintenance/settings` and `PUT /api/maintenance/settings` in `backend/tests/test_maintenance_api.py` — test read returns defaults, test update with valid payload, test RBAC (admin only for PUT), test invalid scope rejection.
|
||||
|
||||
### Implementation for US4
|
||||
|
||||
- [ ] T034 [US4] Implement `GET /api/maintenance/settings` and `PUT /api/maintenance/settings` route handlers in `backend/src/api/routes/maintenance/_routes.py`. GET: read MaintenanceSettings row. PUT: validate and update, return updated settings. RBAC: GET = authenticated, PUT = `Depends(require_role("admin"))`.
|
||||
- [ ] T035 [P] [US4] Implement `MaintenanceApiClient` module in `frontend/src/lib/api/maintenance.js` — typed fetch wrappers: `getSettings()`, `updateSettings(payload)`, `getEvents()`, `startMaintenance(payload)`, `endMaintenance(id)`, `endAllMaintenance()`, `getDashboardBanners()`. Uses `requestApi` from existing API layer. Contract: `[DEF:MaintenanceApiClient:Module]` C2.
|
||||
- [ ] T036 [US4] Implement `MaintenanceSettingsPanel` component in `frontend/src/lib/components/MaintenanceSettingsPanel.svelte` — collapsible form with: target environment dropdown (from existing environments config), dashboard scope radio group (Published/Drafts/All), excluded dashboards multi-select (search + chip badges), forced dashboards multi-select. Save button with loading state. UX states: idle/saving/saved/error. Toast feedback on save. Contract: `[DEF:MaintenanceSettingsPanel:Component]` C3.
|
||||
- [ ] T037 [US4] Run verification: `cd backend && source .venv/bin/activate && python -m pytest backend/tests/test_maintenance_api.py -v -k "settings"` + `cd frontend && npx vitest run tests/maintenance.test.ts -v -k "settings"`
|
||||
|
||||
**Checkpoint**: Settings API operational. Settings panel renders in UI.
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: User Story 3 — Admin UI Management Page (Priority: P2)
|
||||
|
||||
**Goal**: Administrator sees active/completed events table with selective and bulk removal actions.
|
||||
|
||||
**Independent Test**: Navigate to /maintenance, view active events, click "Remove All" → banners disappear.
|
||||
|
||||
### Tests for US3
|
||||
|
||||
- [ ] T038 [P] [US3] Write component test for `MaintenanceEventsTable` in `frontend/tests/maintenance.test.ts` — test renders active and completed events, test "Remove Banners" per event, test "Remove All" with confirmation dialog, test error state with retry.
|
||||
- [ ] T039 [P] [US3] Write store test for `MaintenanceStore` in `frontend/tests/maintenance-store.test.ts` — test events fetching, test polling, test error handling.
|
||||
|
||||
### Implementation for US3
|
||||
|
||||
- [ ] T040 [US3] Implement `GET /api/maintenance/events` route handler in `backend/src/api/routes/maintenance/_routes.py` — returns MaintenanceEventListResponse with active and completed events, each including affected dashboard count. RBAC: authenticated.
|
||||
- [ ] T041 [US3] Implement `MaintenanceStore` in `frontend/src/lib/stores/maintenance.svelte.js` — Svelte 5 rune store with `$state` for events (active + completed), settings, dashboardBanners, isLoading, error. Uses `$effect(() => subscribe(...))` pattern per ADR-0007 (REJECTED: fromStore + $derived). Auto-polls `getEvents()` every 30s via `$effect` (WebSocket covers task progress; polling ensures event-list consistency after task completion). Contract: `[DEF:MaintenanceStore:Module]` C3.
|
||||
- [ ] T042 [US3] Implement `MaintenanceEventsTable` component in `frontend/src/lib/components/MaintenanceEventsTable.svelte` — renders table with columns: Event ID, Tables (truncated with tooltip), Affected Dashboards, Start/End Times, Status badge, Actions. "Remove Banners" button per active event. "Remove All Banners" button above table with confirmation dialog. Completed events in collapsible section. UX states: idle/removing/error. Toast feedback. Contract: `[DEF:MaintenanceEventsTable:Component]` C3.
|
||||
- [ ] T043 [US3] Implement `MaintenanceBannerPage` in `frontend/src/routes/maintenance/+page.svelte` — composes MaintenanceSettingsPanel + MaintenanceEventsTable, orchestrates loading states, passes store data to children. UX states: idle/loading/action_in_progress/error/empty. Contract: `[DEF:MaintenanceBannerPage:Component]` C3.
|
||||
- [ ] T044 [US3] Add navigation entry for `/maintenance` in sidebar — add category to "Operations" section in `frontend/src/lib/components/layout/sidebarNavigation.js` with id="maintenance", path="/maintenance", requiredPermission="plugin:migration", requiredAction="WRITE". Add translation keys to `frontend/src/lib/i18n/locales/en/nav.json` and `frontend/src/lib/i18n/locales/ru/nav.json`.
|
||||
- [ ] T045 [US3] Run full frontend verification: `cd frontend && npx vitest run tests/maintenance.test.ts tests/maintenance-store.test.ts -v` + `cd frontend && npm run build`
|
||||
|
||||
**Checkpoint**: Admin management page fully functional. Settings + events tables operational.
|
||||
|
||||
---
|
||||
|
||||
## Phase 7: User Story 5 — Dashboard Hub Badge (Priority: P3)
|
||||
|
||||
**Goal**: Dashboard Hub table shows orange "⚠️ Maintenance" badge when dashboard has active banner.
|
||||
|
||||
**Independent Test**: After US1, open Dashboard Hub → affected dashboard row shows orange badge with tooltip.
|
||||
|
||||
### Tests for US5
|
||||
|
||||
- [ ] T046 [P] [US5] Write component test for `DashboardMaintenanceBadge` in `frontend/tests/maintenance.test.ts` — test renders badge when maintenance active, test no badge when inactive, test tooltip content, test loading skeleton state.
|
||||
|
||||
### Implementation for US5
|
||||
|
||||
- [ ] T047 [US5] Implement `GET /api/maintenance/dashboard-banners` route handler in `backend/src/api/routes/maintenance/_routes.py` — returns list of MaintenanceDashboardBannerState: {dashboard_id, active: bool, events: [...], start_time, end_time}. Queries MaintenanceDashboardState where status=ACTIVE. RBAC: authenticated.
|
||||
- [ ] T048 [US5] Add `getDashboardBanners()` to MaintenanceStore — fetches banner states and populates `dashboardBanners` $state map.
|
||||
- [ ] T049 [US5] Implement `DashboardMaintenanceBadge` component in `frontend/src/lib/components/DashboardMaintenanceBadge.svelte` — orange badge "⚠️ Maintenance" with tooltip showing start/end times. Renders nothing when no active banner. Grey skeleton when loading. Contract: `[DEF:DashboardGridBannerIndicator:Component]` C2.
|
||||
- [ ] T050 [US5] Modify `frontend/src/routes/dashboards/+page.svelte` — add `DashboardMaintenanceBadge` import, insert badge column/cell in dashboard table rows, wire `$derived` from MaintenanceStore.dashboardBanners. Ensure compliance with ADR-0007 (no fromStore + $derived pattern). Export visual verification: open Dashboard Hub page after US1 and confirm badge appears via `chrome-devtools` snapshot.
|
||||
- [ ] T051 [US5] Run verification: `cd frontend && npx vitest run tests/maintenance.test.ts -v -k "badge"` + `cd frontend && npm run build`
|
||||
|
||||
**Checkpoint**: Dashboard Hub badge functional. Operators can identify maintenance-affected dashboards at a glance.
|
||||
|
||||
---
|
||||
|
||||
## Phase 8: Polish & Cross-Cutting Verification
|
||||
|
||||
**Purpose**: Full integration verification, linting, semantic audit, ADR compliance check.
|
||||
|
||||
- [ ] T052 [P] Run full backend test suite: `cd backend && source .venv/bin/activate && python -m pytest backend/tests/test_maintenance_*.py backend/tests/test_sql_table_extractor.py -v --cov=src/services/maintenance_service.py --cov=src/services/sql_table_extractor.py --cov=src/api/routes/maintenance/ --cov-report=term-missing`
|
||||
- [ ] T053 [P] Run backend lint: `cd backend && python -m ruff check src/api/routes/maintenance/ src/services/maintenance_service.py src/services/sql_table_extractor.py src/models/maintenance.py src/core/superset_client/_dashboards_write.py`
|
||||
- [ ] T054 [P] Run frontend lint: `cd frontend && npm run lint`
|
||||
- [ ] T055 [P] Run frontend full test suite: `cd frontend && npm run test`
|
||||
- [ ] T056 [P] Run frontend build check: `cd frontend && npm run build`
|
||||
- [ ] T057 Run semantic contract audit: `axiom_semantic_validation audit_contracts` on `backend/src/api/routes/maintenance/`, `backend/src/services/maintenance_service.py`, `backend/src/services/sql_table_extractor.py`, `backend/src/core/superset_client/_dashboards_write.py`, `backend/src/models/maintenance.py` — verify no missing C4 tags, no orphan relations, all `@RATIONALE`/`@REJECTED` present.
|
||||
- [ ] T058 Run ADR rejected-path regression check: verify that `fromStore + $derived` is NOT used in any new frontend file (ADR-0007), verify all Svelte components use runes syntax (ADR-0006), verify no export-import cycle is used for chart placement (research.md R1 rejected).
|
||||
- [ ] T059 Run belief runtime audit: `axiom_semantic_validation audit_belief_runtime` on C4 contracts — verify `belief_scope`, `log(REASON)`, `log(REFLECT)`, `log(EXPLORE)` instrumentation present in `start_maintenance()`, `end_maintenance()`, and `SupersetDashboardsWriteMixin`.
|
||||
- [ ] T060 Run quickstart.md validation: follow all steps in `specs/031-maintenance-banner/quickstart.md` (backend/frontend/Docker) and confirm no failures.
|
||||
- [ ] T061 [P] Update `frontend/src/lib/i18n/` with new translation keys for maintenance banner UI strings (English + Russian placeholders): `maintenance.title`, `maintenance.active`, `maintenance.completed`, `maintenance.removeBanners`, `maintenance.removeAll`, `maintenance.settings`, `maintenance.scope`, `maintenance.excluded`, `maintenance.forced`, `maintenance.save`, `maintenance.noActiveEvents`, `maintenance.tooltip`.
|
||||
|
||||
---
|
||||
|
||||
## Dependencies & Execution Order
|
||||
|
||||
### Phase Dependencies
|
||||
|
||||
```
|
||||
Phase 1: Setup ──────────────────────────────────────────────┐
|
||||
Phase 2: Foundational (BLOCKS ALL STORIES) ──────────────────┤
|
||||
├── Phase 3: US1 (P1) Banner Placement ──────────────────┤
|
||||
│ └── Phase 4: US2 (P1) Banner Removal ────────────────┤
|
||||
├── Phase 5: US4 (P3) Settings API + Panel ──────────────┤
|
||||
│ └── Phase 6: US3 (P2) Admin UI ──────────────────────┤
|
||||
└── Phase 7: US5 (P3) Dashboard Badge ───────────────────┤
|
||||
Phase 8: Polish & Cross-Cutting ─────────────────────────────┘
|
||||
```
|
||||
|
||||
### Within Each Phase
|
||||
|
||||
- **Tests MUST be written and FAIL before implementation** (T016-T018 before T019+, T025-T026 before T027+, etc.)
|
||||
- Models (T005-T007) → Schemas (T008) → SupersetClient (T009-T010) → SQL Extractor (T011) → Router (T012-T014)
|
||||
- In US1: `find_affected_dashboards()` → `build_banner_text()` → `start_maintenance()` → route handler
|
||||
- In US2: `rebuild_banner()` → `end_maintenance()` → `end_all_maintenance()` → route handlers
|
||||
- In US4: Settings API backend → API client → Settings panel
|
||||
- In US3: Events API backend → Store → EventsTable → Page
|
||||
|
||||
### Parallel Opportunities
|
||||
|
||||
- **Phase 1**: T002 and T003 can run in parallel
|
||||
- **Phase 2**: T005, T006, T007 (models) can run in parallel; T016, T017, T018 (US1 tests) can run in parallel
|
||||
- **Phase 3**: T016, T017, T018 (tests) can run in parallel
|
||||
- **Phase 4**: T025, T026, T026a (tests) can run in parallel
|
||||
- **Phase 6**: T038, T039 (tests) can run in parallel
|
||||
- **Phase 8**: T052, T053, T054, T055, T056, T061 can run in parallel
|
||||
|
||||
### User Story Independence
|
||||
|
||||
- **US1** can be verified independently after Phase 2+3: call `/start`, check task status, verify banner in Superset
|
||||
- **US2** can be verified independently after Phase 2+4: call `/end`, verify banner removal
|
||||
- **US4** can be verified independently after Phase 2+5: GET/PUT /settings
|
||||
- **US3** can be verified independently after Phase 2+5+6: view management page, click "Remove All"
|
||||
- **US5** can be verified independently after Phase 2+7: open Dashboard Hub, see badge
|
||||
|
||||
Each story is an independently shippable increment. P1 stories (US1+US2) form the MVP.
|
||||
|
||||
---
|
||||
|
||||
## ADR & Rejected-Path Guardrails
|
||||
|
||||
| ADR / Decision | Guardrail Task | Rationale |
|
||||
|----------------|---------------|-----------|
|
||||
| ADR-0001 (Module Layout) | T001, T005-T007, T019-T023 | All files in canonical paths |
|
||||
| ADR-0003 (Orchestrator) | T009 | Superset REST API only, no DB manipulation |
|
||||
| ADR-0005 (RBAC) | T013, T023, T031, T034 | `require_role("operator"/"admin")` on all endpoints |
|
||||
| ADR-0006 (Svelte 5 Runes) | T041, T049 | `$state`, `$derived`, `$effect` only; no legacy `$:` or `writable` |
|
||||
| ADR-0007 (fromStore REJECTED) | T041 | Store uses `$effect(() => subscribe(...))`; task T058 verifies |
|
||||
| Research R1 (Direct API) | T009 | `POST /chart/` not export-import; task T058 verifies |
|
||||
| Research R3 (Global Extraction) | T011 | No Jinja pre-stripping; two-phase extraction |
|
||||
| Research R4 (TaskManager) | T023, T031 | `TaskManager.create_task()` for async execution |
|
||||
| Research R5 (Dedicated Tables) | T005-T007 | Not AppConfigRecord JSON |
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- Task IDs T001-T061 are sequential. 62 total tasks.
|
||||
- Tasks marked [P] can run in parallel within their phase.
|
||||
- Backend tests use `pytest -v`; frontend tests use `vitest run -v`.
|
||||
- All C4 contracts (T009-T010, T021-T022, T028-T029) MUST include belief runtime instrumentation.
|
||||
- Rejected-path regression is verified by T058 (ADR audit) and T059 (belief runtime audit).
|
||||
- `quickstart.md` validation (T060) is the final gate before merge.
|
||||
Reference in New Issue
Block a user