diff --git a/fleetd/src/main/java/dev/ltms/fleet/config/FleetConfig.java b/fleetd/src/main/java/dev/ltms/fleet/config/FleetConfig.java index b73838a1..80acb35d 100644 --- a/fleetd/src/main/java/dev/ltms/fleet/config/FleetConfig.java +++ b/fleetd/src/main/java/dev/ltms/fleet/config/FleetConfig.java @@ -2776,9 +2776,10 @@ public record FleetConfig( }); if (!bad.isEmpty()) { throw new IllegalStateException("refusing to start: " + String.join("; ", bad) - + ". Every member labelled that way would be read back as a lead or " - + "collaborator and granted that identity's authority. Change one of the two " - + "so member tabs cannot be confused with a lead's or collaborator's tab."); + + ". A member labelled that way, while its pane carries no entry in the " + + "spawned-member roster, is read back as a lead or collaborator and granted " + + "that identity's authority. Change one of the two so member tabs cannot be " + + "confused with a lead's or collaborator's tab."); } List collisions = new ArrayList<>(); @@ -2877,7 +2878,8 @@ public record FleetConfig( * the focused tab rather than its own, so it can land inside a lead's or collaborator's own * labelled tab. {@link dev.ltms.fleet.herdr.LeadTabScanner} identifies a lead or collaborator * purely by that tab's label — it does not exclude the member space — so a member that ends up - * there would be read back as that lead or collaborator and granted that identity's authority. + * there, while its pane carries no entry in the spawned-member roster, is read back as that + * lead or collaborator and granted that identity's authority. * *

Only an entry with a non-blank {@code tab} is in scope: one with no {@code tab} feeds * nothing into {@link dev.ltms.fleet.herdr.LeadTabScanner}, so it creates no hazard here. @@ -2908,8 +2910,9 @@ public record FleetConfig( } throw new IllegalStateException("refusing to start: profile(s) " + bad + " use placement: pane while fleet.leaders or fleet.collaborators names a tab. A " - + "pane-placed member can land inside that labelled tab and be read back as the " - + "lead or collaborator, granted that identity's authority. Set placement: tab for " + + "pane-placed member can land inside that labelled tab, and while its pane " + + "carries no entry in the spawned-member roster, it is read back as the lead or " + + "collaborator and granted that identity's authority. Set placement: tab for " + "each named profile, or remove the tab from every fleet.leaders and " + "fleet.collaborators entry."); } diff --git a/fleetd/src/test/java/dev/ltms/fleet/config/FleetConfigTest.java b/fleetd/src/test/java/dev/ltms/fleet/config/FleetConfigTest.java index 8e8cf88b..4735ac94 100644 --- a/fleetd/src/test/java/dev/ltms/fleet/config/FleetConfigTest.java +++ b/fleetd/src/test/java/dev/ltms/fleet/config/FleetConfigTest.java @@ -850,7 +850,8 @@ class FleetConfigTest { /** * The hazard this guard closes: a pane-placed member lands inside the focused tab rather than - * its own, so it can land inside a lead's labelled tab and be read back as that lead. + * its own, so it can land inside a lead's labelled tab and, while its pane carries no entry in + * the spawned-member roster, be read back as that lead. */ @Test void aPanePlacedProfileWithALeadTabRefusesToStart(@TempDir Path dir) throws Exception { @@ -1087,8 +1088,9 @@ class FleetConfigTest { } /** - * fleetd #669: a member tabLabel that can render as a configured collaborator tab is the same - * hazard as the lead case above — a member labelled that way is read back as the collaborator. + * A member tabLabel that can render as a configured collaborator tab is the same hazard as the + * lead case above — while its pane carries no entry in the spawned-member roster, a member + * labelled that way is read back as the collaborator. */ @Test void aProfileTabLabelOverrideMatchingACollaboratorTabRefusesToStart(@TempDir Path dir)