refactor(deployment): move PendingUpdatesCache to DeploymentManager so the CLI shares it (ScadaBridge#91)

`health pending-updates` (#63) called IPendingUpdatesEvaluator.EvaluateAsync directly
from ManagementActor because the cache lived in CentralUI, so every CLI call was a full
uncached fleet flatten. IPendingUpdatesCache/PendingUpdatesCache now live in
DeploymentManager.PendingUpdates and are registered by AddDeploymentManager; the
SingleFlightMemo<T> primitive moves to Commons.Caching (public) so both CentralUI's KPI /
alarm-summary caches and DeploymentManager can use it. HandleGetPendingUpdates reads the
shared cache; the UI circuits and the CLI now share one memo per node.

Tests: ManagementActor repeated-calls test (fails against the direct evaluator call:
2 evaluations instead of 1), AddDeploymentManager singleton-registration test; the cache
and memo tests move with their types.

(ScadaBridge#91)

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
Joseph Doherty
2026-11-09 13:30:38 -05:00
parent c7d868c52e
commit 1ee2e171cf
16 changed files with 102 additions and 33 deletions
@@ -120,9 +120,8 @@ public static class ServiceCollectionExtensions
// circuit for N dashboards to share a single SQL round per refresh window.
services.AddSingleton<IKpiSnapshotCache, KpiSnapshotCache>();
// Same single-flight TTL shape as IKpiSnapshotCache, over the fleet-wide
// pending-updates flatten (IPendingUpdatesEvaluator) instead of the KPI Asks.
services.AddSingleton<IPendingUpdatesCache, PendingUpdatesCache>();
// IPendingUpdatesCache (the fleet-wide pending-updates memo) is registered by
// AddDeploymentManager, not here, so the management API shares it (Gitea #91).
// Secured Writes: dispatches the two-person secured-write commands
// (submit / approve / reject / list) to the central ManagementActor through the