fleetd #669 Unit E: route a collaborator's pane to the lead herdr daemon #704

Closed
agent wants to merge 0 commits from worker/669-7e80a6-1 into main
Member

HerdrRouter.agentsFor routed any terminal the lead predicate did not recognize to the member herdr daemon. A configured collaborator's pane is opened by a person, exactly like a lead's, so it lives in the lead daemon too — but it failed the old lead-only predicate and was routed to the member daemon instead, which does not know that pane.

Fix

FleetdAssembly now combines the leads map and the collaboratorTerminals map (both already built from one LeadTabScanner pass, fleetd #669 Unit C/D) into the predicate it hands HerdrRouter, via a second AtomicReference set at the same point leadsRef is set.

HerdrRouter's isLead field/constructor parameter is renamed to routeToLead, with javadoc naming the real contract: true for any terminal whose pane lives in the lead daemon — a lead's own pane or a configured collaborator's.

Why this is latent on this host

HerdrRouter's constructor folds memberAgents into the same instance as leadAgents whenever member == lead (i.e. no memberHerdrSocket configured). Neither herdrSocket nor memberHerdrSocket is set in this host's fleetd.yaml, so in production here agentsFor returns the same object regardless of the predicate — the bug is invisible on a live probe. It only bites when memberHerdrSocket differs from herdrSocket.

Tests

  • HerdrRouterTest: two new tests using the exact predicate shape FleetdAssembly builds (lead-map OR collaborator-map), with two DISTINCT HerdrClients so the assertion is meaningful. One proves a collaborator terminal routes to leadAgents(); the other proves a terminal in neither map still routes to memberAgents() (so the fix widens the predicate rather than making it unconditionally true).
  • FleetdAssemblyCollaboratorHerdrRoutingTest (new): reaches the REAL HerdrRouter that FleetdAssembly.assembleAndStart builds, through a config with only fleet.collaborators (no fleet.leaders), with two distinct FakeHerdrs for herdrSocket/memberHerdrSocket. Seeds the lead fake's existing fixed term_a/w2:t7 pane+agent with a tab label matching the configured collaborator, and asserts runtime.router().agentsFor("term_a") is the same object as leadAgents() — proving the ASSEMBLY wires the collaborator map into the router, not just that the predicate works when handed the map directly. A control assertion checks term_shell (in neither map) still routes to memberAgents().

Mutation test (performed and reverted)

Reverted only the predicate change in FleetdAssembly.java (the two-line diff adding collaboratorTerminalsRef and its .set(...) call), left all tests in place, and ran the full suite:

  • Before revert (with the fix): Tests run: 1992, Failures: 0, Errors: 0, Skipped: 0 — BUILD SUCCESS.
  • With the predicate reverted: Tests run: 1992, Failures: 1, Errors: 0, Skipped: 0. The ONE test that went red was FleetdAssemblyCollaboratorHerdrRoutingTest (the new assembly-wiring test) — its message: expected: <AgentControl@...> but was: <AgentControl@...> (routed to member instead of lead). Both new HerdrRouterTest tests stayed GREEN under the revert, because they construct HerdrRouter directly with their own predicate and don't exercise FleetdAssembly's wiring — they pin the router's own mechanism, not the production call site.
  • Restored the fix; full suite green again: Tests run: 1992, Failures: 0, Errors: 0, Skipped: 0 — BUILD SUCCESS.

This shows the assembly-level test (criterion 3) is the one that actually detects this regression; the HerdrRouter-level tests alone would not have caught a reverted call site.

Criterion 3 — how it was reached

Reached directly: built the real FleetdRuntime via FleetdAssembly.assembleAndStart (not a hand-rolled copy), using the same two-distinct-FakeHerdr-per-socket pattern already used by FleetdAssemblyConnectionIdentityTest and FleetdLeadRolloverAssemblyTest. No test seam was missing for this.

Out of scope

#702 and #703 are filed separately and untouched. No other fleet.leaders-only predicate/map silent about fleet.collaborators was spotted while making this change (the two call sites touched — HerdrRouter's predicate and FleetdAssembly's construction of it — were the only ones in scope per the ticket).

Build

mvn clean install in fleetd/: Tests run: 1992, Failures: 0, Errors: 0, Skipped: 0 — BUILD SUCCESS.

HerdrRouter.agentsFor routed any terminal the lead predicate did not recognize to the member herdr daemon. A configured collaborator's pane is opened by a person, exactly like a lead's, so it lives in the lead daemon too — but it failed the old lead-only predicate and was routed to the member daemon instead, which does not know that pane. ## Fix `FleetdAssembly` now combines the `leads` map and the `collaboratorTerminals` map (both already built from one `LeadTabScanner` pass, fleetd #669 Unit C/D) into the predicate it hands `HerdrRouter`, via a second `AtomicReference` set at the same point `leadsRef` is set. `HerdrRouter`'s `isLead` field/constructor parameter is renamed to `routeToLead`, with javadoc naming the real contract: true for any terminal whose pane lives in the lead daemon — a lead's own pane or a configured collaborator's. ## Why this is latent on this host `HerdrRouter`'s constructor folds `memberAgents` into the same instance as `leadAgents` whenever `member == lead` (i.e. no `memberHerdrSocket` configured). Neither `herdrSocket` nor `memberHerdrSocket` is set in this host's `fleetd.yaml`, so in production here `agentsFor` returns the same object regardless of the predicate — the bug is invisible on a live probe. It only bites when `memberHerdrSocket` differs from `herdrSocket`. ## Tests - `HerdrRouterTest`: two new tests using the exact predicate shape `FleetdAssembly` builds (lead-map OR collaborator-map), with two DISTINCT `HerdrClient`s so the assertion is meaningful. One proves a collaborator terminal routes to `leadAgents()`; the other proves a terminal in neither map still routes to `memberAgents()` (so the fix widens the predicate rather than making it unconditionally true). - `FleetdAssemblyCollaboratorHerdrRoutingTest` (new): reaches the REAL `HerdrRouter` that `FleetdAssembly.assembleAndStart` builds, through a config with only `fleet.collaborators` (no `fleet.leaders`), with two distinct `FakeHerdr`s for `herdrSocket`/`memberHerdrSocket`. Seeds the lead fake's existing fixed `term_a`/`w2:t7` pane+agent with a tab label matching the configured collaborator, and asserts `runtime.router().agentsFor("term_a")` is the same object as `leadAgents()` — proving the ASSEMBLY wires the collaborator map into the router, not just that the predicate works when handed the map directly. A control assertion checks `term_shell` (in neither map) still routes to `memberAgents()`. ## Mutation test (performed and reverted) Reverted only the predicate change in `FleetdAssembly.java` (the two-line diff adding `collaboratorTerminalsRef` and its `.set(...)` call), left all tests in place, and ran the full suite: - Before revert (with the fix): `Tests run: 1992, Failures: 0, Errors: 0, Skipped: 0` — BUILD SUCCESS. - With the predicate reverted: `Tests run: 1992, Failures: 1, Errors: 0, Skipped: 0`. The ONE test that went red was `FleetdAssemblyCollaboratorHerdrRoutingTest` (the new assembly-wiring test) — its message: `expected: <AgentControl@...> but was: <AgentControl@...>` (routed to member instead of lead). Both new `HerdrRouterTest` tests stayed GREEN under the revert, because they construct `HerdrRouter` directly with their own predicate and don't exercise `FleetdAssembly`'s wiring — they pin the router's own mechanism, not the production call site. - Restored the fix; full suite green again: `Tests run: 1992, Failures: 0, Errors: 0, Skipped: 0` — BUILD SUCCESS. This shows the assembly-level test (criterion 3) is the one that actually detects this regression; the `HerdrRouter`-level tests alone would not have caught a reverted call site. ## Criterion 3 — how it was reached Reached directly: built the real `FleetdRuntime` via `FleetdAssembly.assembleAndStart` (not a hand-rolled copy), using the same two-distinct-`FakeHerdr`-per-socket pattern already used by `FleetdAssemblyConnectionIdentityTest` and `FleetdLeadRolloverAssemblyTest`. No test seam was missing for this. ## Out of scope #702 and #703 are filed separately and untouched. No other `fleet.leaders`-only predicate/map silent about `fleet.collaborators` was spotted while making this change (the two call sites touched — `HerdrRouter`'s predicate and `FleetdAssembly`'s construction of it — were the only ones in scope per the ticket). ## Build `mvn clean install` in `fleetd/`: `Tests run: 1992, Failures: 0, Errors: 0, Skipped: 0` — BUILD SUCCESS.
agent added 1 commit 2026-10-04 02:11:47 +02:00
fleetd #669 Unit E: route a collaborator's pane to the lead herdr daemon
CI / shell-tests (pull_request) Failing after 10s
CI / contract (pull_request) Successful in 45s
CI / build (pull_request) Failing after 1m57s
7e838ba8b9
HerdrRouter.agentsFor picked the member daemon for any terminal the lead
predicate did not recognize, so a configured collaborator's pane (opened
by a person, exactly like a lead's) was routed to the member herdr
daemon instead of the lead one.

FleetdAssembly now combines the leads map and the collaborator-terminals
map into the predicate it hands HerdrRouter. HerdrRouter's isLead field
and constructor parameter are renamed to routeToLead, with its javadoc
naming the real contract: true for any terminal whose pane lives in the
lead daemon, lead or collaborator.
ltms closed this pull request 2026-10-04 02:21:11 +02:00
Some checks are pending
CI / shell-tests (pull_request) Failing after 10s
CI / contract (pull_request) Successful in 45s
CI / build (pull_request) Failing after 1m57s

Pull request closed

Sign in to join this conversation.