fleetd #416: fleet_list must enumerate the STARTUP profile set #420

Merged
ltms merged 1 commits from worker/416-3ad1da-1 into main 2026-09-10 06:27:37 +02:00
Owner

Closes #416.

CapacitySource was built with () -> config.get().profiles().keySet() — the live map — while profiles is a DEFERRED key. HerdrPeerLauncher takes Map.copyOf(profiles) once at construction, so a profile added only to a hot-reloaded config can never be spawned. fleet_list therefore advertised free slots for a profile fleet_spawn refuses with unknown worker profile, while its own contract for free claims it runs "the same check the spawn gate itself runs".

The set now comes from the startup snapshot, matching the coordinator.peers wiring three lines below — whose comment already stated this exact rule. maxLoad stays live on purpose (ConfigRef documents it as hot, like credentialId and weight), so an existing profile's maxLoad edit still takes effect without a restart.

Verification — run by the lead, not reported by the member

The implementing member's reply was lost to an empty scrape (a subscription usage limit took out three members at once), so the work was salvaged uncommitted from its worktree and both required proof steps were run here:

check result
mvn clean install Tests run: 1505, Failures: 0, Errors: 0, Skipped: 0, 0 compile errors
mutation A — restore the live keySet() liveOnlyProfileIsNotListed FAILS, and only that test
mutation B — supplier returns a permanently empty set startupProfileIsListed FAILS, and only that test

Mutation B is why the second direction exists. Per #404, a test that only ever checks the absent case cannot tell a correct lookup from one that returns nothing at all — that gap let a profile -> false mutation survive with 1475 tests green.

The third test, reloadedMaxLoadStillChangesWhatFleetListReports, pins the mirror-image regression: a fix that froze the whole CapacitySource against the startup snapshot would trade this bug for the opposite one.

Credit

Found by the fleet01 lead, who derived it from the "sitting next to a hot lookup does not make a deferred key hot" generalisation and proved it with a live ghost404 profile — reload log, fleet_profiles, fleet_list and fleet_spawn side by side — rather than by argument. Implemented by a sonnet member.

Closes #416. `CapacitySource` was built with `() -> config.get().profiles().keySet()` — the **live** map — while `profiles` is a DEFERRED key. `HerdrPeerLauncher` takes `Map.copyOf(profiles)` once at construction, so a profile added only to a hot-reloaded config can never be spawned. `fleet_list` therefore advertised free slots for a profile `fleet_spawn` refuses with `unknown worker profile`, while its own contract for `free` claims it runs "the same check the spawn gate itself runs". The set now comes from the startup snapshot, matching the `coordinator.peers` wiring three lines below — whose comment already stated this exact rule. `maxLoad` stays live on purpose (`ConfigRef` documents it as hot, like `credentialId` and `weight`), so an existing profile's `maxLoad` edit still takes effect without a restart. ## Verification — run by the lead, not reported by the member The implementing member's reply was lost to an empty scrape (a subscription usage limit took out three members at once), so the work was salvaged uncommitted from its worktree and **both required proof steps were run here**: | check | result | |---|---| | `mvn clean install` | `Tests run: 1505, Failures: 0, Errors: 0, Skipped: 0`, 0 compile errors | | mutation A — restore the live `keySet()` | `liveOnlyProfileIsNotListed` FAILS, and only that test | | mutation B — supplier returns a permanently empty set | `startupProfileIsListed` FAILS, and only that test | Mutation B is why the second direction exists. Per #404, a test that only ever checks the *absent* case cannot tell a correct lookup from one that returns nothing at all — that gap let a `profile -> false` mutation survive with 1475 tests green. The third test, `reloadedMaxLoadStillChangesWhatFleetListReports`, pins the mirror-image regression: a fix that froze the whole `CapacitySource` against the startup snapshot would trade this bug for the opposite one. ## Credit Found by the **fleet01 lead**, who derived it from the "sitting next to a hot lookup does not make a deferred key hot" generalisation and proved it with a live `ghost404` profile — reload log, `fleet_profiles`, `fleet_list` and `fleet_spawn` side by side — rather than by argument. Implemented by a `sonnet` member.
ltms added 1 commit 2026-09-10 06:27:28 +02:00
fleetd #416: fleet_list must enumerate the STARTUP profile set
CI / contract (pull_request) Successful in 1m29s
CI / build (pull_request) Successful in 1m52s
8d5bc3ee89
`profiles` is a DEFERRED key: HerdrPeerLauncher takes Map.copyOf(profiles) once at
construction, so a profile added only to the hot-reloaded map can never be spawned.
CapacitySource was built with `() -> config.get().profiles().keySet()` — the live map —
so fleet_list reported a hot-added profile as free while fleet_spawn on that same
profile failed with "unknown worker profile". fleet_list's contract for `free` says it
runs "the same check the spawn gate itself runs"; it did not.

The set now comes from the startup snapshot, the same shape as the coordinator.peers
wiring three lines below, whose comment already stated the rule. maxLoad stays live on
purpose — ConfigRef documents it as hot, like credentialId and weight — so a maxLoad
edit still takes effect without a restart.

Found by the fleet01 lead, who proved it with a live ghost profile rather than an
argument. Implemented by a sonnet member; its reply was lost to an empty scrape, so the
work was salvaged uncommitted from its worktree and both proof steps were run by the
lead instead:

  full build                          Tests run: 1505, Failures: 0, 0 compile errors
  mutation A: live keySet restored    liveOnlyProfileIsNotListed FAILS (alone)
  mutation B: permanently empty set   startupProfileIsListed FAILS (alone)

Mutation B is the point of the second direction: per #404, a test that only ever checks
the absent case cannot tell a correct lookup from one that returns nothing at all.
ltms merged commit ed54f0224e into main 2026-09-10 06:27:37 +02:00
Sign in to join this conversation.