A pane-placed member lands in the focused tab, so it can be granted PRIMARY — and the exclusion the test cites as the defence is empty in production #661
Closed
opened 2026-10-03 18:49:55 +02:00 by ltms
·
5 comments
No Branch/Tag Specified
main
worker/664-c12e95-3
worker/668-08534d-4
worker/672-0f2469-2
worker/670-7d1022-1
worker/661-ac7c28-2
worker/664-37fb9b-3
worker/663-remove-3arg-read-3f6783-1
worker/659-remove-dead-backcompat-ba5e6f-1
worker/637-revision-60a488-23
worker/656-redact-regression-tests-892903-19
worker/637-context-gauge-threshold-466eb5-16
worker/639-redact-line-numbers-de4ac4-17
worker/641-set-reformat-guard-6f96a4-18
worker/642-herdr-guard-scope-5de0e4-15
worker/650-javadoc-scope-95f3b3-14
worker/612-01e9f7-13
worker/612-a-r4-quarantine-outage-7ab0e8-5
worker/612-a-r9-r11-capacity-coverage-peers-cfcc79-7
worker/612-a-r10-loophealth-ccc872-8
worker/612-a-r12-turnregistrar-9e3bb7-9
worker/612-a-r5-leadconfigdir-9e70cf-6
lead/config-edit-redact-anchor-wording
worker/config-edit-seam-ca8dc1-1
worker/612-r67-630-lifecycle-290b8d-3
worker/629-625-ports-seams-da7d5d-4
worker/612-r12-exhaustion-f37cd7-1
worker/612-r38-amqp-24b083-2
worker/fleetd-612-unita-87807e-1
worker/612-b3-mcpwirings-da2b58-3
worker/612-b2-cb185-176d3a-2
worker/612-b1-completion-457459-1
worker/612-agaps-73a926-2
worker/608-sleeps-3a64ff-3
worker/621-b4520b-1
worker/618-b83894-2
worker/fleetd-615-e05481-5
worker/lead-autocompact-5f1ab2-3
worker/fleetd-613-f85deb-3
worker/fleetd-608-flaky-nudge-test-d0c2d1-3
worker/lead-context-gauge-ad404f-1
worker/gauge-wiring-9158c1-4
worker/redeploy-slowstart-ead0e5-5
worker/charter-bytes-13668c-6
worker/rollover-outcome-291483-2
worker/589-f64303-2
worker/593-1a8025-5
worker/589-fcd2aa-1
worker/568-9fdaa2-3
worker/571-attempted-outcome-5739f7-2
worker/581-completionresolver-cas-sites-0542b7-6
worker/562-loop-health-wiring-test-99611c-5
worker/562-surface-loop-health-7df5cc-4
worker/575-waiter-cleanup-sites-62ad80-1
worker/572-answer-lock-release-46a9ae-5
worker/567-probe-channel-leak-a38fc5-6
worker/551-record-before-send-7cbf56-1
worker/561-listener-fanout-survives-a-throw-61d538-2
worker/555-redeploy-main-flow-seam-65c2f5-2
worker/556-injector-owns-registration-e027a5-1
worker/552-post-restart-mktemp-abort-bc2672-4
worker/553-onstatus-completion-leak-0da881-2
worker/550-shasum-linux-196132-1
worker/538-loop-dies-on-error-4a5eeb-6
worker/426-health-coverage-ef1fd4-4
worker/504-failed-reported-clean-3cfd66-3
worker/537-capturedlog-close-e4c437-2
worker/459-broken-link-targets-cadc17-5
worker/535-appender-leak-fe74c1-1
worker/512-part2-shutdown-detection-434701-9
worker/529-logger-level-sweep-2a5533-8
worker/528-drain-gate-call-site-5de83d-7
charter/forge-mcp-vs-token
worker/521-swap-guard-unpinned-28e931-5
worker/519-probe-test-harness-d25ab8-4
worker/525-logger-level-leak-1b4eb0-6
worker/518-fleetmcp-resolver-wiring-8ef96c-1
worker/512-drain-complete-line-7edd71-3
worker/517-abort-branch-and-jar-id-41b641-2
worker/500-9e52c9-3
worker/509-4912f4-2
worker/511-9a4b23-1
worker/493-479f45-2
worker/505-03f8b2-1
worker/492-followup-detect-unclear
worker/501-a31fa0-7
worker/498-451d1c-5
worker/494-1015ce-2
worker/492-209647-1
worker/489-001902-2
worker/480-relative-handover-path-906323-1
worker/480-b-handover-skill-45bf1f-5
worker/474-followup-source-pin-f54a55-17
worker/474-charter-check-on-reload-f54a55-17
worker/466-quarantine-repeatcount-report
worker/393-opencode-skill-seeding-71854b-13
worker/469-canonical-tool-names-2a472a-16
worker/466-quarantine-escalation-5ae9c1-15
worker/446-hot-exhausted-pattern-0af580-6
worker/464-charter-tool-name-guard-a85635-12
worker/463-listfleet-default-fails-open-f1c76c-11
worker/458-invariant-5-by-purpose-862f9a-10
worker/439-coordinator-row-gate-bc032a-8
worker/449-herdr-protocol-576015-4
worker/450-abstract-spawn-599e1c-5
worker/437-ack-refuses-177d91-1
worker/444-placement-window-feb56a-2
worker/440-helddurable-derived-d462d7-13
worker/425-rework-placement-resolve-c58ba1-9
worker/421-lead-peek-held-msgs-cdbad2-10
worker/435-fixed-policy-cap-fe11de-12
worker/422-gate-state-observability-9e79d6-11
worker/431-memberregistry-live-readers-cdbad2-10
worker/424-architect-slot-hot-038b41-7
worker/422-model-gate-spawn-c29f48-6
worker/425-default-profile-live-f55534-8
worker/415-coverage-wording-2cbf9c-5
worker/416-3ad1da-1
worker/418-588283-3
worker/deterministic-stamp-race-409-3cb7b6-10
worker/armed-reads-live-config-404-ed931f-9
worker/reply-peer-refusal-391-5a34bd-7
worker/models-allowlist-aa9e9b-3
worker/ttl-stamp-race-399-f1122f-8
worker/scrub-receipt-400-316b3e-5
worker/exhaustion-detection-395-105105-6
worker/scrub-abort-394-316b3e-5
fix/scrub-uid-abort
worker/task-scrub-517574-2
worker/t386-clock-bd5b78-4
worker/t384-scrub-813790-5
worker/t381-cc-748314-2
worker/t373-336973-2
worker/t365-3920c5-3
worker/t358-6e989b-1
worker/t355-8b321c-1
worker/fleetd-369-hermetic-git-tests-e8b19a-3
worker/fleetd-368-stale-lead-binding-f5682e-2
worker/fleetd-360-deploy-units-0d3793-1
worker/359-dead-lead-tabs-f1253b-4
worker/362-worktree-skills-c03e51-3
worker/361-coord-visibility-655144-1
362-plugin-visibility-and-drift
worker/errscan-bed2ca-2
worker/amqp-log-identity-bed2ca-2
worker/withdefaults-guard-561704
worker/sleepguard-82076d-1
worker/fd334-9ee1b6-5
worker/fd348-f1ab27-4
worker/fd335-a71c35-1
worker/fd342-174a17-2
worker/fd345-490d0f-3
worker/fleetd-337-5ec7d4-21
worker/fleetd-341-af5a6b-24
worker/fleetd-339-5ca0a2-23
worker/fleetd-338-83a4a1-22
worker/fleetd-333-281f46-18
worker/fleetd-329-11bdbb-16
worker/fleetd-330-2770fb-17
worker/fix-326-50506e-15
worker/fix-324-3e9bbf-14
worker/fix-323-b8287d-13
worker/fix-316b-bd0860-11
worker/fix-318-76ca36-9
worker/fix-317-486aec-8
worker/fix-315-ce47c5-6
worker/fix-307-275890-6
worker/fix-308-b4f664-7
worker/fix-309-ec3939-8
worker/fix-310-7a3974-9
worker/fix-302-52ad0e-9
worker/fix-298-ce1acb-8
worker/fix-297-66bd11-7
worker/fix-296-104622-6
worker/fix-293-bare-closetab-eb22b5-3
worker/fix-280-gone-ask-lapse-bca98e-2
worker/fix-290-reapidle-guard-coverage-9b0dd1-1
worker/fix-285-trust-seed-8f3565-10
worker/fix-284-backend-error-seat-85912c-11
worker/fix-282-chained-ask-e6d0bb-8
worker/fix-283-teardown-leaks-f40dfa-9
worker/fix-281-pin-handler-actions-4921ac-7
worker/audit-rendezvous-lifecycle-d072ae-2
worker/audit-health-placement-1a2476-6
worker/audit-teardown-exits-e207a5-3
worker/audit-launcher-asymmetry-27e370-4
worker/audit-rest-authz-6ca53c-5
worker/investigate-275-abandon-asking-fdef52-8
worker/fix-274-worktree-leak-b0095d-7
worker/fix-273-exhausted-pattern-9665b5-6
worker/fleetd-267-model-check-bd8068-1
worker/fleetd-131-archunit-18b834-7
worker/fleetd-266-sshagent-rename-a014ff-6
worker/fleetd-184-uid-claim-8e1f31-4
worker/fleetd-184-warn-b381ee-10
worker/fleetd-184-docs-be1d12-9
worker/fleetd-257-9bf010-7
worker/fleetd-103-23a113-6
worker/fleetd-247-342356-5
worker/fleetd-116-04dea8-4
worker/fleetd-252-a830e0-3
worker/fleetd-111-7e8673-9
worker/fleetd-155c-f8ef4b-8
worker/fleetd-176-b928ca-3
worker/fleetd-249-7a7878-2
worker/cb248-composition-root-b-9acdf7-15
worker/cb148-envrc-default-fa6c82-12
worker/cb201-unit5-wiring-6c12e6-8
worker/cb241-fallback-echo-1175e9-11
worker/cb149-trust-dialog-2392a5-9
worker/cb134-148-overlay-visible-c9b986-10
worker/cb234-session-id-keyed-04e1fc-1
worker/cb201-unit3-nudge-abdf5c-6
worker/cb201-unit2-policy-c1102c-5
worker/cb201-unit4-outcome-a13bfa-7
worker/cb201-unit1-classifier-91b9b1-4
worker/cb201-227-refine-831980-3
worker/cb175-model-readback-0f085f-1
worker/cb222-charter-tmpdir-17f013-1
worker/cb226-architect-slot-race-cd3aa8-3
worker/cb224-worktree-root-group-024523-2
worker/cb-123-role-demotion-c600f7-2
worker/cb-219-opencode-roots-1f677e-1
worker/cb214-claude-session-id-b9eab4-4
worker/cb213-zdotdir-wrong-process-dd6de4-3
worker/cb211-exhaustion-classification-9546e0-2
worker/cb137-ambiguous-task-4df3d8-4
worker/cb209-agentsessionid-4dfdb6-2
worker/cb185-hostenvnames-2692b5-3
worker/cb206-opencode-sqlite-128718-2
worker/cb185-worktree-group-fc0c99-1
worker/cb-137-ask-ticket-e7760c-2
worker/cb-172-broker-uri-d36ae4-4
worker/cb-175-model-readback-76ead6-3
worker/cb-161-pane-ancestry-293510-1
worker/cb-164-rebase-885863-8
worker/cb-164-empty-scrape-false-success-1a80af-3
fix/cb-197-ticket-ttl-from-completion
worker/cb-189-remote-url-coverage-4692f3-1
worker/cb-185-blockers-027756-4
worker/cb-192-gap-log-11b631-2
worker/cb-633-fix-5f4396-3
worker/cb185-router-d6436d-3
worker/cb185-router-routing-gaps-9e9d33-3
worker/cb185-paneids-992586-2
worker/cb-633-allow-list-union-ed374b-1
worker/cb-157-credential-in-remote-url-496e44-2
worker/cb-641-health-herdr-evidence-8f1f54-6
worker/cb-640-health-msg-evidence-99c9cd-1
worker/cb-642-fleets-status-skill-bbbc40-5
cb-634-ide-mcp
worker/lead-comms-wiring-c014b9-7
worker/lead-mailbox-c19577-6
worker/autocompact-window-82bc2f-5
worker/cb-634-probe-18056f-4
worker/cb635-broker-urienv
worker/cb-632-config-retry-8e0efa-7
lead/cb-622e-claude-md
lead/cb-622-followup
worker/cb-622a-165dff-1
lead/cb-622d-opencode-mount
worker/cb-622b-717c67-2
worker/cb-622c-ab7759-3
worker/cb-617b2-20ca4b-3
worker/cb-617a-5c2f4a-1
worker/cb596-4e49ef-3
worker/cb586-10500c-1
worker/cb-606-b9343a-25
worker/cb604-1445f8-24
worker/cb582-477374-21
worker/cb584-8c2281-22
worker/cb600-e6b9a9-20
worker/cb602-ce257f-19
worker/cb601-b42837-18
worker/cb598-6c7ba7-17
worker/cb599-740fe4-16
worker/cb597-282224-15
worker/cb590fix-185e9a-10
worker/cb528-recovery-race
worker/cb594-96bead-8
worker/cb590-916766-2
worker/cb527-997d99-3
worker/cb592-env-leak-3cbf9c-1
worker/cb588-async-ticket-nudge-3218f7-5
worker/cb578b-9dcb13-6
worker/cb581-d24826-5
worker/m2-u5-ef8c42-15
worker/cb578a-516499-2
worker/cb576-01a04b-17
worker/cb579-lead-tab-acba06-20
worker/cb580-terminal-health-ed6058-21
worker/cb577-f36fdc-18
worker/cb573b-3db06f-16
worker/cb568c-f36fdc-18
worker/cb568-drop-cause-c3ac1c
worker/cb575-cancelled-notification-c3ac1c
worker/m4-sol-a2cbec-3
worker/cb574-async-ask-c3ac1c
worker/cb573-health-model-8ca857-14
worker/cb572-unknown-target-7f2e35-13
worker/u4-700706-9
worker/u3-b9fcb6-6
worker/u2-ef5b68-4
worker/u1-469dce-1-clean
worker/u1-469dce-1
worker/cb-564-health-events-70cf7e-2
worker/cb-565-recycle-drops-role-98e58f-3
worker/cb-563-missing-reply-df2866-1
worker/cb-562-readiness-gate-silent-6c23c9-3
worker/cb-560-architect-presence-da8155-1
worker/cb-561-architect-silent-off-a71cab-2
worker/cb-548-bind-architect-slot-fe1b8c-1
worker/parity-overlay-settings-5fb711-1
secrets-central-store
cb-559-hot-key-correction
cb-557-fleet-role-pools
worker/cb-553-maxload-explicit-spawn-305ee3-6
worker/cb-551-idle-lead-heartbeat-f1633c-1
worker/cb-544-drain-preserves-worktree-925fad-3
worker/cb-552-docs-sync-1cb9cf-4
worker/cb-548-rendezvous-guard-rebased
worker/cb-548-rendezvous-guard-116b53-10
worker/cb-548-authz-v2-586df6-8
worker/cb-548-authz-264363-5
salvage/cb-528b-codex-home
salvage/cb-528a-codex-launcher
CB-518-primary-flow
feature/peer-launcher-spi
cb-103-injector
v1.1.0
v1.0.0
Labels
Clear labels
blocked
needs-live-proof
ready-to-delegate
silent-default
Cannot start until something else lands. The body says what.
Merged and green, but never shown working on the running daemon. Not the same as done.
Scope, files and acceptance criteria are written. A worker can be briefed from the body alone.
A feature that compiles, passes tests, and ships turned off. Nine recurrences and counting.
No Label
Milestone
No items
No Milestone
Projects
Clear projects
No project
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: fleet/fleetd#661
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
A member spawned with
placement: paneis created by splitting the currently focused tab. If the focused tab is a lead's labelled tab, that member's pane sits inside the lead's tab.LeadTabScannermaps every pane in a labelled tab to that lead name, andCallerResolvergrants a lead pane fullPRIMARYrights. So the member can spawn, stop, drain and hand over.The test that pins "every pane in a lead tab is that lead" says in its own comment that this is safe because "nothing fleetd placed can land here (see the worker-space test above)". That argument rests on
excludedWorkspaceLabels. Production passes an empty set.This is not reachable with today's
fleetd.yaml. All 8 profiles useplacement: tab. It takes one config edit to reach. I am filing it as a latent defect with a live config edit in front of it, not as something firing now.Measured on
136bec8Every line below came from a command I ran in the main clone.
1. Production passes an empty exclusion set, and the only non-empty one is a test.
FleetdAssembly.assembleAndStartis the production boot path. The exclusion is consumed atLeadTabScanner.java:182:With an empty set that test is never true, so no workspace is ever skipped.
2. A pane spawn targets the focused tab, with no tab or workspace argument.
WorkspaceControl.splitPanesends onlydirection,cwdandenv:Its own javadoc says "Split the currently-focused tab".
HerdrPeerLauncher.java:518-520picks the path, andspawnAsPaneat:710callsspaces.splitPane(cwd, workerEnv)at:714.3. Every pane in a labelled tab resolves as that lead, and the test's safety argument is the exclusion.
LeadTabScannerTest.java:234:The comment is the whole defence, and point 1 shows it does not hold in production.
4. A lead pane outranks everything in the resolver.
CallerResolver.resolvechecksleadTerminalsfirst and returnsPrincipal.leader(...)before the architect registry and before the worker fallback.Authzthen grantsSPAWN/STOP/DRAIN/HANDOVERto a primary.5. No guard exists.
6. Not reachable today.
All 8 profiles are
placement: tab, so no spawn currently takes the pane path.Why this is worth fixing rather than documenting
Three source files state that member spaces are excluded from the lead scan. All three are wrong against
FleetdAssembly.java:265:herdr/LeadTabScanner.java:40-42— "Worker spaces are excluded wholesale (excludedWorkspaceLabels), so a worker cannot become a lead by being placed — as a split, say — inside a matching tab."config/FleetConfig.java:2690-2692— "The member-space exclusion inLeadTabScanneralready blocks the realistic path".wiki/11-Features.md:679— "the configured worker spaces … are excluded from the scan wholesale".Meanwhile
FleetConfig.java:1150-1152says the opposite and matches the code: "The scanner no longer excludes member spaces". Two javadocs in one file disagree, which is how this survived.The failure mode is a config edit that looks harmless. An operator setting
placement: paneon one profile gets no warning, and the escalation depends on which tab happens to be focused at spawn time — so it would be intermittent and very hard to diagnose.Suggested fix
Make the bad state unrepresentable at startup rather than checking at spawn time. Add a validator, called from
validateAllnext tovalidateLeadTabPrefixes, that refuses a config with any profile onplacement: panewhilefleet.leadersis non-empty. It is fatal at boot, costs no herdr round trip on the spawn path, and matches whatvalidateAuthExposureandvalidateLeadTabPrefixesalready do.The alternative — read the pane's tab label after a pane spawn and tear down when it names a configured lead — keeps
placement: paneusable for a project with no lead. That is a policy call about whetherplacement: paneis still a supported mode at all, so it needs a decision before anyone implements.Acceptance criteria
fleet.leaders.<name>.tab. Removing the guard must turn a test red.FleetdAssemblypasses an empty set. If the third constructor parameter is kept, a mutation of the argument atFleetdAssembly.java:265must turn a test red — today you can change it toSet.of("fleet")and the suite stays green, because the only test exercising it supplies its own set.LeadTabScannerTest:206(aTabInAWorkerSpaceIsNeverALeadEvenWhenItsLabelMatches) is allowed only if the report names the line it pinned and why that line can no longer go wrong.Found how
Found while planning a change to herdr workspace layout — one workspace per project, with the lead as the first tab and members as sibling tabs. That proposal makes a lead tab a far more likely focus target, which is what turned a dormant hole into something worth filing. The hole itself does not depend on that proposal and exists today.
I verified each step above myself in the main clone. I ran no build and no test for this report, so I am claiming no test result.
Decision: Option A — refuse at startup
Two architects worked this question independently, on different profiles (
opusandsol). Bothchose Option A: a new
FleetConfigvalidator that refuses to start when a profile usesplacement: panewhile a lead tab is configured. I adjudicated the parts where they differed.Why A and not B
Option B checks the pane's tab label after the spawn and tears the member down. Both architects
rejected it for the same reason, and I agree: it is a check-after-the-fact on a privilege boundary.
HerdrPeerLauncher.spawnAsPanestarts the agent before any tab read could run, so the member isalive and resolves as PRIMARY for the length of a herdr round trip. The teardown can also fail.
Option B also makes a valid spawn depend on which tab had focus, so it would fail intermittently.
Option A costs nothing per spawn and matches the existing
rejectUnknownPlacementpattern, whichalready runs at config load.
The predicate — narrow, not blanket
Refuse when a profile has
placement: paneand at least onefleet.leadersentry has anon-blank
tab.The
opusarchitect argued for this narrower form and I took it. A leader with notabfeedsnothing into
LeadTabScanner, so it creates no hazard, and keying onfleet.leadersbeingnon-empty alone would refuse a safe config. The
solarchitect proposed the blanket form; thenarrow one is better because the scanner is what creates the hazard, and the scanner reads the tab
label.
The validator must test the resolved placement value, not only the raw YAML. I checked this
myself:
FleetConfig.Profile.tabPlacement()at line 846 is"tab".equals(placement), so any othervalue falls through to pane.
rejectUnknownPlacement(yaml)already catches typos at load, and thejavadoc at lines 2470-2472 states this fall-through explicitly.
The chain is confirmed
Both architects checked all five links in the code and found none wrong. Between them they
confirmed:
WorkspaceControl.splitPanesends onlydirection,cwdandenv;LeadTabScanner.scanjoinspane.listtonameByTabbytab_idalone, so every pane in alabelled tab maps to that lead;
CallerResolver.resolvechecksleadTerminalsfirst and returnsPrincipal.leader; andAuthzgrantsSPAWN,STOP,DRAINandHANDOVERoncaller.isPrimary().Neither architect read herdr's own server code, so link 1 rests on
splitPane's contract and itscallers, not on the daemon's behaviour.
Still not reachable today — I measured this myself
Neither architect could check it, because
fleetd/fleetd.yamlis gitignored and so is absent froma member's worktree. I ran it in the main clone:
and the
profiles:block declares 8 profiles (local,local-direct,gx,opus,sonnet,sol,terra,xf). So every profile setsplacement: taband no spawn takes the pane path.The file's other
placement:line,placement: weightedat line 246, is the separate top-levelplacement strategy key, not a profile's.
Option A does not block the operator's layout work
Both architects checked this and agree.
LeadLauncher.launchcallsensureWorkspace, thencreateTab, and starts into the tab'srootPaneId. It never readsProfile.placement(), andgrep -rn "placement" fleetd/src/main/java/dev/ltms/fleet/lead/returns nothing. So a refusal keyedon that member config key cannot reach the lead's own start path, including a future rewrite that
uses
workspace.create'sactive_tab_id.Stale comments to correct in the same change
Three places still claim the member-space exclusion blocks this, and all three are false because
production passes
Set.of()atFleetdAssembly.java:265:herdr/LeadTabScanner.java:40-42— the "worker spaces are excluded wholesale" trust propertyconfig/FleetConfig.java:2690-2692— the same claim insidevalidateLeadTabPrefixesLeadTabScannerTest.java:234-236— the test's own "nothing fleetd placed can land here"justification, which should point at the new validator instead
config/FleetConfig.java:1150-1152is the correct statement and should stay the one place thisfact lives.
wiki/11-Features.mdalso needs the correction; only the lead can edit the wiki, sothat is mine.
One correction to the
solarchitect's report: it also namedLeadLauncher.countLeadsascarrying the same false claim. I read that comment and it does not — it says a workspace can no
longer be excluded wholesale and that the exact tab label is the sole discriminator, which agrees
with the code. Leave it alone.
Not done yet
This comment settles the approach only. No code is changed and no validator is written yet.
Lead review of PR #667 — three changes before merge
The validator itself is right. I read the whole diff. Three things to fix, and one of them means my own earlier adjudication on this ticket was wrong.
Verified good
validatePanePlacementAgainstLeadTabs()ispublic void, no args, soinvokeAllValidatorspicks it up with no wiring. It guards onfleet == null || leaders().isEmpty(), then on no leader having a non-blanktab, then collects offending profiles sorted and throwsIllegalStateExceptionnaming them. That matchesvalidateLeadTabPrefixes's shape.!Profile.tabPlacement(), not raw YAML, and the leader side is the exactFleetdAssembly.java:259-261shape (leader != null && leader.tab() != null && !leader.tab().isBlank()). It is the narrow form: a leader with notabrefuses nothing.LeadTabScanner.javanow saysexcludedWorkspaceLabelscan filter a workspace out but that a caller may pass an empty set "and the daemon does" — which is the true statement.FleetConfig.java:1150-1152left alone, as asked.Finding 1 — I was wrong about
LeadLauncher, and thesolarchitect was rightMy earlier comment said: "it also named
LeadLauncher.countLeadsas carrying the same false claim. I read that comment and it does not… Leave it alone." My brief repeated that instruction.That was a mistake, and the worker caught it. There are two comments about twenty lines apart, saying opposite things, and I checked the wrong one.
LeadLauncher.java:203-205— the javadoc on theLeadCountrecord:That is false, for exactly the reason the other three comments were false:
FleetdAssembly.java:265passesSet.of().LeadLauncher.java:224-229— the body comment insidecountLeads:That one is correct. I read this one, found it accurate, and concluded the architect was wrong. The architect was pointing at the javadoc above it.
Fix the
LeadCountjavadoc atLeadLauncher.java:201-205in this change. It is the same false claim as the other three and it directly contradicts the correct comment in the method it documents. Do not touch the body comment at 224-229 — that one stays.Recording the general lesson, because it has cost us twice today: verifying a different sentence than the one under dispute proves nothing about the dispute. When a report names a location, check that location.
Finding 2 — the renamed test leaves a dangling
{@link}FleetConfigValidateAllTest.java:65still reads:but the method is now
fleetConfigDeclaresExactlyTheseSevenValidatorsToday. That{@link}no longer resolves. The build does not catch it —mvn clean installis green at 1927 — so it needs fixing by hand. Update the link, and the "a seventh validator" wording with it.Finding 3 — the visible denominator is off by one, and already was
This test exists to show a number. The number is wrong, before and after this PR. Measured:
public void validate*()minusvalidateAllSet.ofmainThe assertion's set has always been right. The word in the method name and the javadoc has been one behind since before this change, and this PR incremented the word instead of counting the set. A test whose stated purpose is "a visible denominator… so a reader adding a seventh sees this assertion name the new count" fails that purpose when a reader counts eight names under the word "seven".
Fix it so the count is right. My preference, but your call: drop the number from the method name entirely —
fleetConfigDeclaresExactlyTheseValidatorsToday— and keep the count in the javadoc only. TheSet.ofliteral is the real denominator, and one fact in one place cannot drift out of step with itself. If you keep a number in the name, make it eight.The pre-existing half of this is not the worker's fault. It is in scope because this PR edits those exact lines.
Not blocking
The build is green at 1927 (1923 + 4 new tests) and the non-vacuousness check is sound: inverting
!tabPlacement()turned all three relevant tests red, with the two "allowed" cases flipping to unexpected throws and the "refuses" case to nothing thrown. I will re-run my own build and mutation on the updated head before merging.Second review round — findings 1-3 are fixed. One more, then I merge.
All three earlier findings are correctly done, and I verified each:
LeadLauncher.javaLeadCountjavadoc now says a member is not counted because its tab carries a different label, "not because any workspace is excluded from this count". True, and the correct body comment insidecountLeadsis untouched.{@link}is gone.The worker also flagged, rather than silently fixing, a count it was not asked about. That was the right call and it led me to the real problem.
Finding 4 — the reachability enumeration covers six of eight, and its javadoc claims it covers all
validateAllReachesEveryOneOfTodaysSixValidatorsis the test the file calls "the heart of claim 2": one minimal failing config per validator, each asserted to fail throughvalidateAll(). Measured on the PR head, it covers six:There are eight validators. The two missing are
validateLeadRolloverandvalidatePanePlacementAgainstLeadTabs.The method name is therefore honest — it really does reach six. What is false is the surrounding claim that those six are "today's" validators. Today there are eight.
Your new validator is covered, by
FleetConfigTest.validateAllAlsoRefusesPanePlacementAgainstALeadTab(FleetConfigTest.java:835). That satisfied my acceptance criterion, so this is not a coverage hole for #661. It is a placement problem: validateAll-reachability now lives in two files, and the file whose job is to enumerate it does not know about the new one. That is how the next validator gets missed.validateLeadRollover's missing case is pre-existing and is now fleetd #668. Do not fix it here.What to change in this PR
validateAllReachesEveryOneOfTodaysSixValidators, in the sameassertValidateAllRefuses(dir, "...yaml", """…""", "…")shape as the other six. You may keep or drop yourFleetConfigTestversion — your call — but the enumeration must contain it, so one file lists every validateAll-reachability case.validateAllReachesEveryOneOfTodaysRealValidators, or similar. Fix every reference to the old name — last time one was left behind.<ol>item, around lines 47 and 49 ("reaches each of today's six real validators" / "every one of those six failures")Explicitly leave these alone
Lines around 25-26, 30, 32, 60 and 63, and 70 also say "six", and they are not in scope. They record what was measured or replaced at the time — "the fix replaces the six individual
cfg.validateXxx()calls", "a hardcoded list of today's six method calls leaves the whole suite green (measured at review: 1491 tests)". Those were true when written, and rewriting the number inside a past measurement would make them false. Changing a recorded measurement to match today is worse than leaving it dated.(The separate question of whether that much history belongs in a source comment at all is a house-style matter for the whole repo, not something to settle inside this PR.)
Then re-run
mvn clean install, report the realTests run:line, and push.Merged locally as
b4b7cf5and pushed tomain. PR 667 closed by hand (we merge locally, so the forge never closes one by itself).What landed
FleetConfig.validatePanePlacementAgainstLeadTabs()— refuses startup when any profile is pane-placed while anyfleet.leadersentry names a non-blanktab.LeadTabScanner.java,FleetConfig.validateLeadTabPrefixes()javadoc, andLeadLauncher.java:201-205.validateAllreachability enumeration gained a pane-placement case, and its test method name no longer carries a hardcoded count.What I verified myself, not on the worker's word
main. A two-dot diff made it look like the merge would revert #663 and #664. It does not: the merge base is9417de1, the three-dot diff touches only 6 files, and the trial merge reported no conflicted path. I checked this before merging rather than after.validateAll()isinvokeAllValidators(this), and that sweep is genuinely reflective — public, no-arg,void, name starts withvalidate, excludingvalidateAll. No hardcoded list, so the new method is swept with no second step.Tests run: 1927, Failures: 0, Errors: 0, Skipped: 0,BUILD SUCCESS, 170 surefire report files (the same count asmain— a positive control, taken afterrm -rf target/surefire-reports).One defect I fixed at merge
FleetConfig'svalidateAll/invokeAllValidatorsjavadoc still counted validators — "today's six", "a seventh validator". It was already wrong onmainat seven, and this change made it eight. Commitb4b7cf5makes the wording count-free, so it cannot drift again. Measured: eight public no-argvoidvalidate*methods, atFleetConfig.javalines 2671, 2708, 2755, 2790, 2827, 2856, 2896 and 2958.I left the surrounding history-style prose in that javadoc alone. It breaks the project's comment rules, but it predates this change, so fixing it here would widen the scope.
Still owed, and not done by this change
wiki/11-Features.md:678-680claims two guards protect the lead-tab trust boundary. Before this change only one existed. Now the second one really does exist, so that sentence needs rewriting to name the real pair rather than deleting. Lead-only work, becausewiki/is a submodule.wiki/11-Features.mdfeature entry for the new validator: what it does, the knob, why it exists, the gotcha.validateLeadRolloverhas novalidateAll-reachability case. A pre-existing gap, now named in the test javadoc, not fixed here.Closing.
Correction to my close comment above. I listed what was still owed but signed off acceptance criterion 3 without running its second half — the mutation check.
I have now run it. The mutant survives.
Set.of()→Set.of("fleet")atFleetdAssembly.java:265, one line changed, full suite green:Tests run: 1927, Failures: 0, Errors: 0, Skipped: 0,BUILD SUCCESS, 170 report files. Reverted afterwards and confirmed the worktree was byte-identical toHEAD.So AC3 was only half met: the three false source comments were corrected, but the argument at
FleetdAssembly.java:265is still unpinned. Split out as #670 with the mutant output and its own acceptance criteria.This issue stays closed — the validator it asked for landed and works. The unmet half is a test-coverage gap on a different line, so it belongs in its own ticket rather than holding this one open.