docs(service-registry): lock decisions (cascade delete, required key, SSH runner model)

- §11 decisions: cascade-delete services with widgets; MANAGE_ENCRYPTION_KEY
  always required; SSH task runner is multi-instance with reusable tasks.
- §12 SSH task runner model: instances absorb SSH task transport, tasks stay
  global/reusable with default_service_id, service_task_runs logs history,
  widget config { task_id, service_id? }.
- tasks.md: add service_task_runs table + cascade-delete tests to Slice 1,
  SSH run-logging to Slice 2, follow-ups (Actions rebuild, machine unification).
This commit is contained in:
Developer
2026-06-22 11:14:01 +00:00
parent 9782280a03
commit 9459de5c07
2 changed files with 99 additions and 18 deletions
+22 -8
View File
@@ -43,7 +43,9 @@ Chain strategy: stacked-to-main
- Files: `services/settings_store.py` (modify), `services/service_store.py` (new)
- Lines: ~120
- Details: `services` table in `init_schema`; CRUD helpers; decrypt-on-read for
adapters; "set" flags for the API without plaintext.
adapters; "set" flags for the API without plaintext. **Cascade delete:** removing a
service deletes its widgets in the same transaction. Also add the
`service_task_runs` table (design §12.3) now so later slices can populate it.
- [ ] **1.5 Add service Pydantic models + router**
- Files: `models/services.py` (new), `routers/services.py` (new), `main.py` (modify)
- Lines: ~110
@@ -55,9 +57,10 @@ Chain strategy: stacked-to-main
- Details: Extend startup validation to require `MANAGE_ENCRYPTION_KEY`.
- [ ] **1.7 Add backend tests**
- Files: `backend/tests/test_services.py` (new)
- Lines: ~120
- Lines: ~140
- Details: Registry contents, CRUD round-trip, secret encryption/decryption,
unknown service type → 422, missing encryption key → startup error.
unknown service type → 422, missing/invalid encryption key → startup error,
cascade-delete removes a service's widgets.
- [ ] **1.8 Verify**
- Run: `cd backend && .venv/bin/ruff check . && PYTHONPATH=src .venv/bin/python -m pytest`
@@ -74,10 +77,11 @@ Chain strategy: stacked-to-main
as `{service_type}.{kind}` during transition; drop `addon_id`.
- [ ] **2.2 Refactor source adapters**
- Files: `widgets/sources.py` (modify)
- Lines: ~140
- Lines: ~160
- Details: Each adapter takes `(service: ServiceRecord, widget_kind, config)`.
`SOURCE_ADAPTERS` keyed by `service_type`. Jellyfin/Grafana/Prometheus/SSH adapters
resolve connection from the service record.
resolve connection from the service record. The SSH adapter resolves the task +
instance, runs it, and **appends a `service_task_runs` row** (design §12.3).
- [ ] **2.3 Retire old widget registry**
- Files: `widgets/registry.py` (delete or hollow out), `widgets/__init__.py`
- Lines: ~-60
@@ -150,7 +154,7 @@ Chain strategy: stacked-to-main
- Files: `frontend/src/widgets/*` (modify)
- Lines: ~120
- Details: Components read `widget_kind`; data shapes unchanged but sourced from the
service adapter.
service adapter. SSH task widget shows last run status from `service_task_runs`.
- [ ] **4.3 Remove machine Jellyfin/Jellyseerr fields**
- Files: `frontend/src/pages/Settings.tsx`, `frontend/src/types/index.ts`
(modify)
@@ -184,15 +188,25 @@ Chain strategy: stacked-to-main
- [ ] **5.3 Manual dev-stack check** — `docker compose -f docker-compose.dev.yml up --build`:
- Create a Grafana service from the UI; verify the dashboard link widget works.
- Create a Jellyfin service; verify the activity widget resolves it.
- Delete a service with widgets → 409; remove widgets → delete succeeds.
- Delete a service with widgets → widgets are cascade-deleted and the service is gone.
- Restart the stack; secrets remain usable (key stable).
- Missing `MANAGE_ENCRYPTION_KEY` → backend refuses to start.
- SSH task runner: define two instances, run the same reusable task against each,
and see both runs in the instance's history log.
## Guards
```text
Decision needed before apply: Yes (resolve design §11 first)
Decision needed before apply: No (design §11 resolved)
Chained PRs recommended: Yes
Chain strategy: stacked-to-main
400-line budget risk: High
```
## Explicit follow-ups (out of scope for this change)
- Rebuild the Actions page UI on top of services (global reusable tasks +
`default_service_id`), replacing the current machine-based saved-task runner.
- Unify machines under services so an SSH host is defined once (today machines still
own File Browser + node_exporter transport; see design §12.5).
- Key rotation / re-encrypt workflow for `MANAGE_ENCRYPTION_KEY`.