docs+test(deploy): clarify driver-less attribution docs + no-line exclusion test (Task 2 review)

This commit is contained in:
Joseph Doherty
2026-06-08 07:02:25 -04:00
parent 0b5fc44866
commit d909a8e4f6
2 changed files with 49 additions and 3 deletions
@@ -209,9 +209,12 @@ public static class DeploymentArtifact
/// <summary>Cluster-scoped overload: the address-space composition a node should materialise given
/// its NodeId. Filters every projection to the node's own ClusterId (see <see cref="ResolveClusterScope"/>).
/// Equipment attribution is dual-path: driver-bound equipment (non-null DriverInstanceId) is kept when
/// its driver is in-cluster; driver-less equipment (null DriverInstanceId) is kept when its UNS line's
/// area is in-cluster.
/// When <paramref name="onInconsistency"/> is supplied it is invoked with a human-readable message for each
/// kept equipment whose owning UNS line is NOT in the node's cluster — a cross-cluster binding that
/// violates the same-cluster invariant (decision #122) and would orphan the equipment folder. This is
/// kept driver-bound equipment whose owning UNS line is NOT in the node's cluster — a cross-cluster binding
/// that violates the same-cluster invariant (decision #122) and would orphan the equipment folder. This is
/// detection only (observability); the equipment is still returned, since the upstream draft validator
/// is the authority that should prevent the binding in the first place.</summary>
/// <param name="blob">The deployment artifact blob.</param>
@@ -234,6 +237,9 @@ public static class DeploymentArtifact
if (onInconsistency is not null)
{
var keptLineIds = keptLines.Select(l => l.UnsLineId).ToHashSet(StringComparer.OrdinalIgnoreCase);
// This cross-cluster check only fires for DRIVER-BOUND equipment. Driver-less equipment
// is attributed by its UNS line's area cluster, so by construction its line is always in
// keptLines — the condition `!keptLineIds.Contains(e.UnsLineId)` is never true for it.
foreach (var e in keptEquipment)
{
if (!string.IsNullOrEmpty(e.UnsLineId) && !keptLineIds.Contains(e.UnsLineId))
@@ -265,7 +271,9 @@ public static class DeploymentArtifact
private sealed record ClusterSets(HashSet<string> DriverIds, HashSet<string> AreaIds, HashSet<string> EquipmentIds);
/// <summary>Build the in-cluster id sets used to filter a composition: DriverInstanceIds + UnsAreaIds
/// that directly carry the ClusterId, plus EquipmentIds whose DriverInstanceId is in-cluster.</summary>
/// that directly carry the ClusterId, plus in-cluster EquipmentIds — driver-bound equipment attributed
/// by its driver's cluster, and driver-less equipment (null DriverInstanceId) attributed by its UNS
/// line's area cluster.</summary>
/// <param name="blob">The deployment artifact blob.</param>
/// <param name="clusterId">The node's ClusterId to scope to.</param>
/// <returns>The resolved in-cluster id sets (empty on parse failure => empty composition).</returns>