From c7c142300c92fba8770252b13fbb408bba626b5a Mon Sep 17 00:00:00 2001 From: Dai Ha Date: Fri, 4 Sep 2026 15:45:53 +0700 Subject: [PATCH] =?UTF-8?q?Features:=20#333=20=E2=80=94=20fleet.leaders=20?= =?UTF-8?q?is=20split,=20and=20set=20membership=20now=20proves=20a=20compa?= =?UTF-8?q?rison=20exists?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 11-Features.md | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/11-Features.md b/11-Features.md index 47caf58..36666cc 100644 --- a/11-Features.md +++ b/11-Features.md @@ -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.