Files
manage/docs/superpowers/plans/2026-07-14-scheduled-actions.md
T
2026-07-14 15:22:34 +00:00

7.5 KiB

Typed Scheduler and qBittorrent Polling Implementation Plan

This plan implements the approved design in docs/superpowers/specs/2026-07-14-scheduled-actions-design.md.

Goal: Move qBittorrent speed collection into a backend-owned typed scheduler while adding per-service controls, run history, stale-data status, and a manual run action.

Scope: qBittorrent speed polling only. The scheduler registry is extensible, but arbitrary widgets, SSH tasks, and other integrations remain out of scope.

Delivery slices

Slice 1 — Contracts and persistence

  • Add validated qBittorrent config fields to backend/src/media_library_viewer_api/integrations/qbittorrent.py:
    • polling_enabled default true;
    • poll_interval_seconds default 15, range 5..300;
    • sample_retention_seconds default 1800, range 60..86400;
    • sample_max_rows default 1200, range 60..1200.
  • Add migration/default normalization tests for existing qBittorrent records.
  • Extend QbittorrentSampleStore to prune by retention timestamp and capped row count.
  • Add a generic scheduler storage concern with run records, indexes, pruning, and cascade deletion by service ID.
  • Add typed backend models for scheduler status, run records, samples, and manual-run responses.

Acceptance: Existing service records validate without edits; qBittorrent samples remain isolated by service; scheduler records cannot contain secrets; deletion removes service-owned samples and runs.

Slice 2 — Typed scheduler core

  • Create a scheduler action protocol and registry.
  • Implement a single-worker, lifespan-managed scheduler coordinator with responsive stop behavior.
  • Add qBittorrent speed sampling as the first registered action.
  • Extract external polling from QbittorrentWidgetSource into a reusable sampler/action helper.
  • Implement immediate startup execution with deterministic staggering.
  • Implement fixed-delay, no-overlap execution and bounded exponential backoff.
  • Reconcile enabled services/config changes on each cycle.
  • Add safe structured logs and Prometheus metrics.
  • Start/stop the scheduler in main.py alongside the existing mail queue and backup poller.

Acceptance: With no frontend open, enabled qBittorrent services append samples; one slow service cannot create overlapping runs or a backlog; shutdown joins the worker; a successful manual or scheduled run resets backoff.

Slice 3 — Read-only APIs

  • Add backend/src/media_library_viewer_api/routers/scheduler.py.
  • Add status, paginated runs, manual-run, and read-only samples endpoints.
  • Keep service configuration writes on the existing service-instance API.
  • Change the qBittorrent speed widget adapter to read samples only.
  • Add stale-data calculation and safe error truncation.
  • Add API tests for disabled/missing services, stale data, pagination, manual runs, backoff, and authentication.

Acceptance: Opening or refreshing a speed widget never contacts qBittorrent and never appends a sample; API responses expose timestamps and status but no credentials.

Slice 4 — Frontend controls and history

  • Add scheduler TypeScript types, API functions, and React Query hooks.
  • Add qBittorrent schedule controls to the existing schema-driven service editor.
  • Add status/backoff/stale-data presentation and a Run now action.
  • Add user-selectable chart windows.
  • Add a paginated run-history table with safe error details.
  • Keep UI refreshes separate from sampler cadence.
  • Add frontend tests for validation, disabled state, stale warning, manual-run reset, chart-window selection, and run-history rendering.

Acceptance: Operators can configure, inspect, and manually trigger qBittorrent polling from the service surface without opening the dashboard; the chart remains useful during outages and identifies stale data.

Slice 5 — Documentation and operational verification

  • Update docs/REQUIREMENTS.md with scheduler requirements and the one-worker constraint.
  • Update deployment/runbook documentation with scheduler startup, shutdown, and replica guidance.
  • Add migration/recovery notes for sample and run-history retention.
  • Run backend tests, frontend tests, lint, and build.
  • Verify a headless collection scenario against a mocked qBittorrent service.
  • Verify project-map artifacts after files are added.

Suggested file map

Backend

File Change
backend/src/media_library_viewer_api/integrations/qbittorrent.py Schedule config schema and defaults
backend/src/media_library_viewer_api/services/qbittorrent_store.py Duration/cap pruning and sample queries
backend/src/media_library_viewer_api/services/service_data.py Register scheduler run concern
backend/src/media_library_viewer_api/services/scheduler.py Worker lifecycle, reconciliation, timing, backoff
backend/src/media_library_viewer_api/services/scheduler_actions.py Typed registry and qBittorrent action
backend/src/media_library_viewer_api/services/scheduler_store.py Run-record persistence and pruning
backend/src/media_library_viewer_api/models/scheduler.py Response/request models
backend/src/media_library_viewer_api/routers/scheduler.py Status, history, samples, manual-run API
backend/src/media_library_viewer_api/widgets/sources.py Make qBittorrent speed reads side-effect free
backend/src/media_library_viewer_api/main.py Start/stop scheduler
backend/tests/test_scheduler.py Scheduler lifecycle/timing/backoff tests
backend/tests/test_scheduler_api.py Endpoint and auth tests
backend/tests/test_service_data.py Migration/cascade coverage
backend/tests/test_widgets.py Read-only qBittorrent widget coverage

Frontend

File Change
frontend/src/types/scheduler.ts Scheduler status/run/sample types
frontend/src/api/scheduler.ts Typed endpoint wrappers
frontend/src/hooks/useScheduler.ts Queries and manual-run mutation
frontend/src/pages/ServicesPage.tsx qBittorrent schedule controls/status surface
frontend/src/pages/ServicePage.tsx or qBittorrent service tab Status, chart window, history surface
frontend/src/widgets/QbittorrentSpeedWidget.tsx Read-only sample window and stale warning
frontend/src/integrations/registry.ts Schedule metadata/config exposure if needed
frontend/src/types/index.ts Shared exports

Risks and mitigations

  • Duplicate polling: widget adapter becomes read-only; only scheduler action calls qBittorrent.
  • Multiple backend workers: document and log the one-worker constraint; do not silently duplicate work.
  • Unbounded storage: prune by both duration and row cap; test pruning under rapid polling.
  • Credential leakage: reuse existing secret resolution and sanitize run errors/log fields.
  • Scheduler shutdown races: use a stop event, per-action lock, and bounded joins; test lifespan shutdown.
  • Config changes during a run: let the current run finish, then reconcile on the next cycle.
  • Stale but useful data: return samples plus explicit stale status rather than blanking the chart.

Verification commands

cd backend && PYTHONPATH=src pytest
cd frontend && npm test
cd frontend && npm run lint
cd frontend && npm run build

Do not begin implementation until the final module names, retry cap/jitter, and chart-window response shape are confirmed during the implementation pass.