{{ result_json }}
diff --git a/src/pobsync_backend/tests/test_views.py b/src/pobsync_backend/tests/test_views.py
index 69f9dae..2dbdf40 100644
--- a/src/pobsync_backend/tests/test_views.py
+++ b/src/pobsync_backend/tests/test_views.py
@@ -1213,6 +1213,37 @@ class ViewTests(TestCase):
self.assertContains(response, "Retention")
self.assertContains(response, "Deletion blocked by --max-delete=0")
+ def test_run_detail_surfaces_host_retention_warnings(self) -> None:
+ self.client.force_login(self.staff_user)
+ host = HostConfig.objects.create(
+ host="web-01",
+ address="web-01.example.test",
+ retention_daily=0,
+ retention_weekly=0,
+ retention_monthly=0,
+ retention_yearly=0,
+ )
+ ScheduleConfig.objects.create(host=host, cron_expr="15 2 * * *", enabled=True, prune=True, prune_max_delete=1)
+ self._snapshot(host, "20260517-021500Z__OLDSNP1")
+ self._snapshot(host, "20260518-021500Z__OLDSNP2")
+ self._snapshot(host, "20260519-021500Z__NEWSNAP")
+ SnapshotRecord.objects.create(
+ host=host,
+ kind=SnapshotRecord.Kind.INCOMPLETE,
+ dirname="20260519-031500Z__BROKEN01",
+ path=f"/backups/{host.host}/.incomplete/20260519-031500Z__BROKEN01",
+ status="failed",
+ started_at=datetime(2026, 5, 19, 3, 15, tzinfo=timezone.utc),
+ )
+ run = BackupRun.objects.create(host=host, status=BackupRun.Status.SUCCESS, result={"ok": True})
+
+ response = self.client.get(reverse("run_detail", args=[run.id]))
+
+ self.assertEqual(response.status_code, 200)
+ self.assertContains(response, "Retention Warnings")
+ self.assertContains(response, "Scheduled pruning for this host would delete 2 snapshot(s)")
+ self.assertContains(response, "1 incomplete snapshot(s) exist for this host")
+
def test_run_detail_infers_rsync_log_from_snapshot_path(self) -> None:
self.client.force_login(self.staff_user)
host = HostConfig.objects.create(host="web-01", address="web-01.example.test")
diff --git a/src/pobsync_backend/views.py b/src/pobsync_backend/views.py
index 4ad50ad..44d7715 100644
--- a/src/pobsync_backend/views.py
+++ b/src/pobsync_backend/views.py
@@ -429,6 +429,7 @@ def run_detail(request, run_id: int):
"failure_summary": failure.get("message") or failure.get("summary") or "",
"prune_result": prune_result,
"has_prune_result": bool(prune_result),
+ "retention_warning": _retention_warning_for_host(run.host, _schedule_for_host(run.host)),
"rsync_log_path": str(rsync_log_path) if rsync_log_path is not None else "",
"rsync_log_exists": bool(rsync_log_path and rsync_log_path.exists()),
"rsync_log_tail": rsync_log_tail,