From 20fc42b57227d40f405f453c0a1bf76cfb4d7cca Mon Sep 17 00:00:00 2001 From: Dai Ha Date: Sun, 4 Oct 2026 06:13:03 +0200 Subject: [PATCH] fleetd #669 follow-up: deliverableTo also opens for a collaborator A collaborator's terminal was authorized at Authz but never deliverable: it is never enrolled in MemberPresence and never discovered by the lead scan, so a send to a collaborator sat on the injector's readiness gate for ~60s and failed, never typed into the pane. deliverableTo now takes a third collaborators supplier, read through on each call like the lead supplier, and FleetdAssembly wires the existing collaboratorTerminals supplier into it. --- .../src/main/java/dev/ltms/fleet/Fleetd.java | 27 ++++---- .../java/dev/ltms/fleet/FleetdAssembly.java | 2 +- .../ltms/fleet/FleetDeliverabilityTest.java | 65 ++++++++++++++++--- 3 files changed, 71 insertions(+), 23 deletions(-) diff --git a/fleetd/src/main/java/dev/ltms/fleet/Fleetd.java b/fleetd/src/main/java/dev/ltms/fleet/Fleetd.java index 5f66d7a..c701c2c 100644 --- a/fleetd/src/main/java/dev/ltms/fleet/Fleetd.java +++ b/fleetd/src/main/java/dev/ltms/fleet/Fleetd.java @@ -217,25 +217,28 @@ public final class Fleetd { /** * The {@link Injector}'s readiness gate (CB-534): a target is deliverable if it is a spawned - * member whose agent has connected the bridge MCP, or a lead. + * member whose agent has connected the bridge MCP, a lead, or a collaborator. * *

The gate exists for one reason — to hold a delivery out of a spawned member's boot * window, where herdr already reports {@code idle} but the TUI would drop an injected paste. That * hazard is a property of spawning. A lead is never spawned: the operator started it and named it - * (or labelled its tab) only once it was up, so there is no boot window to guard. + * (or labelled its tab) only once it was up, so there is no boot window to guard. A collaborator + * is the same way — a person's own tab, matched to a configured name, never spawned. * - *

A lead is also never enrolled in {@link MemberPresence} — {@code FleetMcp} marks presence - * for every spawned member (worker and architect), deliberately, since that map doubles as the - * member roster's availability signal and a lead counted there would show up as an available - * member. So without the second disjunct a lead is permanently un-deliverable: every - * lead→lead send sat on the gate for {@code READINESS_GRACE_POLLS} (~60s) and then failed - * having never been typed into the pane. + *

Neither a lead nor a collaborator is ever enrolled in {@link MemberPresence} — {@code + * FleetMcp} marks presence for every spawned member (worker and architect), deliberately, since + * that map doubles as the member roster's availability signal and a lead or collaborator counted + * there would show up as an available member. So without the second and third disjuncts a lead or + * collaborator is permanently un-deliverable: every send to one sat on the gate for + * {@code READINESS_GRACE_POLLS} (~60s) and then failed having never been typed into the pane. * - *

The lead set is read through the supplier on each call rather than snapshotted, so a lead - * discovered by {@code leadScan} after startup becomes deliverable without a restart. + *

Both sets are read through their supplier on each call rather than snapshotted, so a lead or + * collaborator discovered by {@code leadScan} after startup becomes deliverable without a restart. */ - static Predicate deliverableTo(MemberPresence presence, Supplier> leads) { - return target -> presence.isPresent(target) || leads.get().containsKey(target); + static Predicate deliverableTo(MemberPresence presence, Supplier> leads, + Supplier> collaborators) { + return target -> presence.isPresent(target) || leads.get().containsKey(target) + || collaborators.get().containsKey(target); } /** diff --git a/fleetd/src/main/java/dev/ltms/fleet/FleetdAssembly.java b/fleetd/src/main/java/dev/ltms/fleet/FleetdAssembly.java index c53127e..55b3ec3 100644 --- a/fleetd/src/main/java/dev/ltms/fleet/FleetdAssembly.java +++ b/fleetd/src/main/java/dev/ltms/fleet/FleetdAssembly.java @@ -368,7 +368,7 @@ final class FleetdAssembly { // CB-301: the manager's presence bridge records availability and drives SPAWNING → READY. MemberPresence presence = sessions.asPresence(); TurnListener turnListener = Fleetd.turnListener(completion, sessions); - Predicate deliverable = Fleetd.deliverableTo(presence, leads); + Predicate deliverable = Fleetd.deliverableTo(presence, leads, collaboratorTerminals); // fleetd #556: registration is wired directly to `completion`, not folded into the // `turnListener` fan-out above — so it survives `sessions.onDelivered` (or any future // listener) throwing, regardless of call order. diff --git a/fleetd/src/test/java/dev/ltms/fleet/FleetDeliverabilityTest.java b/fleetd/src/test/java/dev/ltms/fleet/FleetDeliverabilityTest.java index d05045c..fe235e5 100644 --- a/fleetd/src/test/java/dev/ltms/fleet/FleetDeliverabilityTest.java +++ b/fleetd/src/test/java/dev/ltms/fleet/FleetDeliverabilityTest.java @@ -14,10 +14,12 @@ import static org.junit.jupiter.api.Assertions.assertTrue; /** * CB-534: the injector's readiness gate must open for a lead as well as for a present worker. + * fleetd #669 follow-up: the same gate must also open for a collaborator, which — like a lead — + * is never enrolled in {@link MemberPresence} and never discovered by the lead scan. * - *

The bug these cover was silent and slow: a lead was never marked present (only workers are), so - * every lead→lead delivery sat on the gate for the full readiness grace and failed ~60s later without - * a keystroke ever reaching the pane. + *

The bug these cover was silent and slow: a lead (and later a collaborator) was never marked + * present (only workers are) and never counted as a lead, so every send to one sat on the gate for + * the full readiness grace and failed ~60s later without a keystroke ever reaching the pane. */ class FleetDeliverabilityTest { @@ -25,19 +27,25 @@ class FleetDeliverabilityTest { return () -> m; } + private static Supplier> collaborators(Map m) { + return () -> m; + } + @Test @DisplayName("a worker that has connected its MCP is deliverable") void presentWorkerIsDeliverable() { MemberPresence presence = new MemberPresence(); presence.markPresent("term_worker"); - assertTrue(Fleetd.deliverableTo(presence, leads(Map.of())).test("term_worker")); + assertTrue(Fleetd.deliverableTo(presence, leads(Map.of()), collaborators(Map.of())) + .test("term_worker")); } @Test @DisplayName("a worker still in its boot window is held back") void absentWorkerIsNotDeliverable() { - assertFalse(Fleetd.deliverableTo(new MemberPresence(), leads(Map.of())).test("term_booting")); + assertFalse(Fleetd.deliverableTo(new MemberPresence(), leads(Map.of()), collaborators(Map.of())) + .test("term_booting")); } @Test @@ -45,19 +53,31 @@ class FleetDeliverabilityTest { void leadIsDeliverableWithoutPresence() { MemberPresence presence = new MemberPresence(); Predicate deliverable = - Fleetd.deliverableTo(presence, leads(Map.of("term_lead", "opus-5.0"))); + Fleetd.deliverableTo(presence, leads(Map.of("term_lead", "opus-5.0")), collaborators(Map.of())); assertFalse(presence.isPresent("term_lead"), "a lead is never enrolled in worker presence"); assertTrue(deliverable.test("term_lead"), "…and must be deliverable anyway"); } @Test - @DisplayName("an unknown terminal is deliverable to neither") + @DisplayName("a collaborator is deliverable without ever being marked present or scanned as a lead") + void collaboratorIsDeliverableWithoutPresenceOrLeadStatus() { + MemberPresence presence = new MemberPresence(); + Predicate deliverable = Fleetd.deliverableTo(presence, leads(Map.of()), + collaborators(Map.of("term_collab", "kevin"))); + + assertFalse(presence.isPresent("term_collab"), "a collaborator is never enrolled in worker presence"); + assertTrue(deliverable.test("term_collab"), "…and must be deliverable anyway"); + } + + @Test + @DisplayName("an unknown terminal is deliverable to none of presence, leads, or collaborators") void strangerIsNotDeliverable() { MemberPresence presence = new MemberPresence(); presence.markPresent("term_worker"); - assertFalse(Fleetd.deliverableTo(presence, leads(Map.of("term_lead", "opus-5.0"))) + assertFalse(Fleetd.deliverableTo(presence, leads(Map.of("term_lead", "opus-5.0")), + collaborators(Map.of("term_collab", "kevin"))) .test("term_stranger")); } @@ -65,22 +85,47 @@ class FleetDeliverabilityTest { @DisplayName("a lead discovered after startup becomes deliverable with no restart") void leadSetIsReadThroughOnEveryCall() { Map discovered = new HashMap<>(); - Predicate deliverable = Fleetd.deliverableTo(new MemberPresence(), leads(discovered)); + Predicate deliverable = + Fleetd.deliverableTo(new MemberPresence(), leads(discovered), collaborators(Map.of())); assertFalse(deliverable.test("term_late")); discovered.put("term_late", "gpt-sol-5.6"); // leadScan picks up a newly labelled tab assertTrue(deliverable.test("term_late"), "the supplier must be re-read, not snapshotted"); } + @Test + @DisplayName("a collaborator discovered after startup becomes deliverable with no restart") + void collaboratorSetIsReadThroughOnEveryCall() { + Map discovered = new HashMap<>(); + Predicate deliverable = + Fleetd.deliverableTo(new MemberPresence(), leads(Map.of()), collaborators(discovered)); + + assertFalse(deliverable.test("term_late_collab")); + discovered.put("term_late_collab", "kevin"); // the same tab scan picks up a newly labelled collaborator tab + assertTrue(deliverable.test("term_late_collab"), "the supplier must be re-read, not snapshotted"); + } + @Test @DisplayName("forgetting a torn-down worker does not strip a lead of its deliverability") void forgetDoesNotDisarmALead() { MemberPresence presence = new MemberPresence(); Predicate deliverable = - Fleetd.deliverableTo(presence, leads(Map.of("term_lead", "opus-5.0"))); + Fleetd.deliverableTo(presence, leads(Map.of("term_lead", "opus-5.0")), collaborators(Map.of())); presence.forget("term_lead"); // the injector's cleanup path runs against every target assertTrue(deliverable.test("term_lead")); } + + @Test + @DisplayName("forgetting a torn-down worker does not strip a collaborator of its deliverability") + void forgetDoesNotDisarmACollaborator() { + MemberPresence presence = new MemberPresence(); + Predicate deliverable = Fleetd.deliverableTo(presence, leads(Map.of()), + collaborators(Map.of("term_collab", "kevin"))); + + presence.forget("term_collab"); // the injector's cleanup path runs against every target + + assertTrue(deliverable.test("term_collab")); + } }