Apply transient-target health policy in deployment preview
Summary
Fixes a Preview/Deploy inconsistency: the deploy preview counted targets as available using only environment + role + !IsDisabled, while the deployment pipeline (phase 4) additionally excludes unavailable/unhealthy targets via the project's Transient Deployment Targets policy. Result: preview could show "N available" for targets a real deployment skips — and with all targets unavailable, preview showed them green/available while the deployment fails up front with "No target machines found".
Root cause: the DeploymentPlanner is the shared source of truth for role/step matching (both Preview and Execute call it), but the health/availability pre-filter (TransientDeploymentTargetEvaluator, added with the Machine-Policy / Transient-Targets work) was wired only into the deploy pipeline — the preview path never called it.
Fix — converge on one source of truth:
- Extract
TransientDeploymentTargetEvaluator.ApplyProjectPolicy(machines, settingsJson)(resolve the project policy, then evaluate) and call it from both phase 4 and the preview. - Preview now feeds only the kept (eligible) targets to the planner, so
AvailableMachineCount/CandidateTargetsequal what a deployment would actually keep. - New
ExcludedTargets(withHealthStatus) surfaces the unavailable/unhealthy machines so the UI can explain the lower count. - Preview blocks (
CanDeploy = false) when a step needs targets but all matching targets were health-excluded, mirroring the pipeline's failure.
Non-breaking: phase-4 refactor is behavior-preserving (same evaluator); DTO additions are additive; default policy still reproduces the historical unavailable+unhealthy exclusion.
Test plan
-
Unit (evaluator): ApplyProjectPolicywith no/invalid settings → historical defaults; FailDeployment policy → fails on unavailable; convergence test assertingApplyProjectPolicy==Applywith policy-from-JSON (so Preview and Deploy cannot disagree). -
Unit (preview blocker): step-needs-targets + all-health-excluded → blocks; no exclusion / run-on-server / still-has-healthy-targets → does not block. -
Regression: phase-4 transient-target tests, planner, and preview-handler tests green; full solution build clean; 588 deployment/target unit tests pass.
Frontend (SquidWeb) consumes the new ExcludedTargets/HealthStatus + fixes the targets-page "Disabled" label in a companion PR.