Features: #333 — fleet.leaders is split, and set membership now proves a comparison exists
+51
@@ -4142,3 +4142,54 @@ gone, so `answer()`'s own first lookup returns `null` and the ticket is stranded
|
||||
the same race. Measured on 2026-09-04: a probe firing only that first half printed
|
||||
`answer=REPLIED phase=PENDING reply=null`. Open as fleetd #334, and the comment in `answer()` says so.
|
||||
Do not read that `task != null` guard as complete.
|
||||
|
||||
---
|
||||
|
||||
## A reload now reports a changed `fleet.leaders` as needing a restart
|
||||
|
||||
**What it does.** `fleet:` used to be filed as a fully hot key, so a reload that changed it said
|
||||
nothing at all. It is really a **split** key. `fleet.leaders` is read once at startup, in two
|
||||
places — `Fleetd` builds the tab-label→lead map from the startup snapshot, and `LeadLauncher` holds
|
||||
a frozen `FleetConfig` rather than a supplier. Neither rebuilds on reload. The rest of `fleet:`
|
||||
(role pools, charters, `tabLabel`) really is live. `changedSplitKeys` now compares `fleet.leaders`
|
||||
specifically and names both halves.
|
||||
|
||||
**On.** Always on (fleetd #333).
|
||||
|
||||
**Why it exists.** A lead is found by its tab label, and a lead whose tab no longer matches is
|
||||
demoted to worker and refuses every orchestration call. Before this, an operator could edit that
|
||||
label, read `config reloaded`, and be left with a broken lead and no message saying why. The
|
||||
comparison is deliberately on `fleet.leaders` and not on the whole `fleet` record: comparing the
|
||||
whole record would claim "needs a restart" for a `tabLabel`-only change that is fully live. Over-
|
||||
claiming a restart is cheap to recover from; it is still a wrong report, and this class exists to
|
||||
stop wrong reports in both directions.
|
||||
|
||||
**One thing to know for maintenance.** This is the third time the same bug shipped — `worktreeGroup`
|
||||
(#323), then `primary` and `configReload` (#326), now `fleet`. It was in the coverage checker's own
|
||||
hot-exclusion hatch, blessed by the checker, on the checker's first commit. A name sitting in an
|
||||
escape hatch is the place to look first, not last.
|
||||
|
||||
---
|
||||
|
||||
## Being listed as cold or split now proves a comparison exists
|
||||
|
||||
**What it does.** `ConfigRefTopLevelReportingCoverageTest` builds `FleetConfig` pairs by reflection
|
||||
that differ in exactly one top-level component, then calls the real `changedColdKeys` and
|
||||
`changedSplitKeys` and requires that key to come back. A name added to `COLD_KEYS` or `SPLIT_KEYS`
|
||||
with no `if` behind it now fails the build, by name.
|
||||
|
||||
**On.** Always on — a unit test (fleetd #333).
|
||||
|
||||
**Why it exists.** Every checker in this area had closed only one direction. The older
|
||||
`assert COLD_KEYS.containsAll(changed)` catches "reported but not listed" and never the reverse, and
|
||||
`ConfigRefTopLevelCoverageTest` reads a name in a set as "triaged" and stops there. Measured:
|
||||
dropping the `coordinator` branch while leaving the name in `SPLIT_KEYS` left both of those silent.
|
||||
So the cheapest way to pass the shape checker was to add one string and write no code — which is
|
||||
exactly how `fleet` got through.
|
||||
|
||||
**One thing to know for maintenance.** It covers `COLD_KEYS` and `SPLIT_KEYS` only. The deferred set
|
||||
— 11 of the 22 keys, the largest bucket — is **not** covered, and the worker wrote that gap into the
|
||||
test's own javadoc instead of quietly leaving it. Measured on 2026-09-04: deleting `guard`'s
|
||||
comparison from `changedDeferredKeys` while leaving `guard` in the deferred set left all 1355 tests
|
||||
green. Open as fleetd #337. Two tickets running, an honest caveat in a test's javadoc has been the
|
||||
fastest route to the next bug — hold every checker here to that standard.
|
||||
|
||||
Reference in New Issue
Block a user