Files
manage/openspec/changes/per-instance-hook-scoping/apply-progress.md
T
Developer 8d3c44d87f spec(per-instance-hook-scoping): verify + reconcile tracking
Write apply-progress.md, tick all 17 tasks, add verify-report.md (21/21 PI-101..121
PASS). Gates green: 368 pytest, ruff clean, npm build+lint 0 errors, 165 vitest.
fetchBackupDashboard/useBackupDashboard/get_backup_dashboard confirmed untouched
(design decision 5). No blocking code findings.
2026-07-10 00:05:58 +00:00

3.3 KiB
Raw Blame History

Apply Progress: Per-Instance Hook Scoping

Change: per-instance-hook-scoping Phase: apply-progress Date: 2026-07-09 Status: complete — all 17 tasks done, all gates green, verified (see verify-report.md)

Slice delivered

Single slice (commit 3bc7ce5, +268/73, 12 files) — small correctness fix, well within the 400-line budget.

Backend

  • routers/backups.pyget_backup_jobs, get_backup_runs, get_backup_alerts gain service_id: str | None = None; threaded into store calls. get_backup_dashboard NOT touched (design decision 5 — widget path, PI-117 risk) (PI-110).
  • services/settings_store.py:
    • list_backup_jobs(service_id=None) — direct WHERE service_id = ? when truthy.
    • list_backup_runs(..., service_id=None) + list_backup_alerts(..., service_id=None) — subquery job_id IN (SELECT id FROM backup_jobs WHERE service_id = ?) per the schema asymmetry (only backup_jobs has the column; runs/alerts attributed via FK chain).
    • Skip filter on None/empty → backward-compat (PI-111, PI-112).
  • Tests (test_backups.py, +83 lines): service_id=None → all; service_id="X" → only X's; cross-instance preservation (seed A+B, scope A, confirm B's excluded).

Frontend

  • hooks/useObservability.tsuseAlertmanagerAlerts, useAlertmanagerStatus, usePrometheusStatus gain optional serviceId in queryKey (["observability","alerts", serviceId ?? ""] etc.). usePrometheusTargets + useMonitoringMachines unchanged (global by design — PI-107) (PI-101..PI-104).
  • hooks/useBackups.tsuseBackupJobs, useBackupRuns, useBackupAlerts gain serviceId in queryKey. useBackupDashboard unchanged (PI-105, PI-106).
  • api/client.ts + api/backups.ts — 6 fetch fns gain optional serviceId?service_id= via the shared get(path, params) helper. fetchBackupDashboard NOT touched (PI-108, PI-109).
  • pages/service-tabs/AlertsTab.tsx, MetricsTab.tsx, JobsTab.tsx — call hooks with instance.id; the prior TODO comments about global scoping removed (PI-113..PI-115). The instance switcher on ServicePage naturally re-scopes: instance.id changes → queryKey changes → React Query refetches.
  • Tests: useBackups.test.ts (+85) covers queryKey isolation + serviceId threading; AlertsTab.test.tsx updated for the instance.id wiring.

Deviations from tasks.md / design

  • None functional. fetchBackupDashboard / useBackupDashboard / get_backup_dashboard deliberately untouched (design decision 5, PI-117 preserved).

Final gate results

Gate Result
backend && PYTHONPATH=src python3 -m pytest -q 368 passed (+6 new), 2 warnings (pre-existing pythonjsonlogger)
backend && PYTHONPATH=src python3 -m ruff check src tests All checks passed
frontend && npm run build exit 0 (pre-existing chunk-size warning)
frontend && npm run lint 0 errors, 1 pre-existing warning (WidgetConfigDialog.tsx, untouched)
frontend && npx vitest run 165 passed (+7 new)

Verification

See verify-report.md — adversarial fresh-context review: 21/21 PASS, all gates green. No blocking code findings. Archive blocker is doc-only (this file + ticked tasks clear it). Non-blocking: weak queryKey-isolation assertion in useBackups.test.ts (wiring correct in source; test could assert actual cache keys — LOW).