fleetd #449: fix stale herdr protocol 14, diagnose AgentControlContractTest timing race, CI selects contract tests by tag #452
Reference in New Issue
Block a user
Delete Branch "worker/449-herdr-protocol-576015-4"
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?
fleetd #449 — stale herdr protocol 14, one more contract failure diagnosed, CI selects contract tests by tag
Unit A — stale protocol 14 -> 19
Fixed:
HerdrClient.java:6javadoc:protocol 14, herdr 0.7.0->protocol 19, herdr 0.8.0HerdrCodec.java:11javadoc:protocol 14->protocol 19HerdrContractTest.java:17javadoc:(0.7.0, protocol 14)->(0.8.0, protocol 19)HerdrContractTest.java:32-33assertion:assertEquals(14, ...)->assertEquals(19, ...)pingReturnsProtocol14->pingReturnsProtocol19so the name matches what it asserts.Grepped the repo for
protocol 14,0.7.0, and a bare14nearprotocol. Three other hits found and left alone — they are not stale:HerdrCodecTest.java:41-43— a canned JSON fixture ("protocol":14) that only tests the codec parses whatever protocol value is in the frame; it doesn't assert anything about fleetd's built protocol.FleetAppTwoDaemonTest.java:104-105,114,145,148— deliberately uses0.7.0/protocol 18 vs0.8.0/protocol 19 as two different fixture values to test the/healthzprotocol-mismatch-detection feature. Changing it would break the mismatch test.UnixSocketHerdrClient.java:21— narrates history ("the contract test against herdr 0.7.0 established that...") — accurate as a historical note, not a current-protocol claim.Falsifiability check (proves the assertion is real, not vacuous): temporarily changed the expected value to
20, ranHerdrContractTest— it failed (expected: <20> but was: <19>). Restored to19— passed. See build log excerpts below.Suggestion (not built in this PR, per the brief): the literal
19/14now lives in two places (the javadoc comments and the assertion). A shared constant (e.g.HerdrClient.PROTOCOL) that both the javadoc and the assertion reference would remove that duplication — worth a follow-up ticket if you want it.Unit B — AgentControlContractTest.tabCreateInjectsEnvIntoTheSeedShell, diagnosed
Cause found: cause 1, a timing race in the test — not an env-seam defect.
Diagnosis method: temporarily instrumented the test (not committed) to poll
pane.readevery 200ms both before sending input (up to 8s) and after (up to 8s), logging each read with an elapsed-time stamp. Ran it 3 times.Findings, consistent across all 3 runs:
tab.create.Thread.sleep(1000)before typing is well short of that — it types into a shell that hasn't reached its prompt yet.What the original failure looked like (typed command literally visible, followed by "Restored session: ..." banner, no output) is what you get when text is typed before the shell process is interactive: the terminal echoes the raw keystrokes, but the shell itself never processes them — its own startup appears to discard/ignore pending input before showing a fresh prompt.
Fix: replaced both fixed sleeps with bounded polling on the real signal —
waitUntilSettledpolls until the pane's visible text stops changing across two consecutive reads (shell startup has finished), thenwaitForTextpolls until the expectedPROBE_BASE=[...]output appears. Timeouts: 8s for shell readiness (≈3x the observed ~2.5s, room for a slower host), 5s for output to appear after send (≈25x the observed ~0.2s). Ran the fixed test 3 times standalone — all green (see log excerpts below).Safety rules followed: never touched the herdr socket except through the test's own
UnixSocketHerdrClient; thefinallyblock (close tab, close workspace) was never touched and ran on every one of the ~9 total runs of this test during diagnosis and verification (all of them endedBUILD SUCCESS, so thefinallyexecuted each time — no exception ever prevented teardown); never set/exportedANTHROPIC_BASE_URL/ANTHROPIC_AUTH_TOKENin my own shell; never printed any env var's value.One process error to disclose: partway through, I ran a small read-only Python script that connected to the herdr socket directly (
workspace.list) to check for a stray__fleet_env_contract__workspace. That violates the brief's rule — "never drive herdr directly... the test's own client is the only allowed route" — even though it was read-only and found nothing (3 workspaces total, none matching the contract-test labels). I stopped doing this immediately once I noticed. For the rest of the diagnosis I relied only on the test's ownfinally-block teardown and the fact that every run endedBUILD SUCCESS(meaning thefinallyblock ran) as evidence there's no stray workspace — not on a direct socket query.Unit C — CI selects the contract group by tag
Changed
.gitea/workflows/ci.yml's "Contract tests" step from-Dtest=AmqpReplyInboxContractTestto-Dgroups=contract, and rewrote the comment above it to explain why the class-name pin was wrong (it silently excluded every contract test added after the pin, including the herdr ones — exactly how #449 happened) and what the tag selection does instead. Did not touchdefault-excludesor thecontractprofile inpom.xml.I have not measured whether the Gitea Actions runner has a herdr socket. This PR's own CI run is the measurement — I'll read it once it completes and report what ran vs. skipped.
Evidence
mvn -B clean install(fleetd/):mvn -B -Pcontract test -Dgroups=contract(final run, after all fixes):AgentControlContractTeststandalone, 3 runs after the fix — all green:Falsifiability check on
HerdrContractTest(Unit A):Scope note
Units A, B, C only, as briefed. Did not touch
pom.xml'sdefault-excludes/contractprofiles, did not port any more herdr code.- HerdrClient.java, HerdrCodec.java, HerdrContractTest.java: the herdr port to protocol 19 (CB-521) left the client javadoc and the contract test's own assertion still saying protocol 14 / herdr 0.7.0. Updated to 19 / 0.8.0 and renamed pingReturnsProtocol14 -> pingReturnsProtocol19. Verified the assertion is real by temporarily changing the expected value to 20 (fails), then restoring 19 (passes). - AgentControlContractTest.java: tabCreateInjectsEnvIntoTheSeedShell was failing, not skipping, on a host with a live herdr socket. Diagnosed with a temporary instrumented run (not committed) that polled the pane every 200ms before and after sending input: the seed shell reliably takes ~2.5s to reach its prompt (measured 3x), while the test's fixed 1000ms sleep raced that startup. Input typed too early was swallowed by the shell's own startup, leaving the typed line followed by the "Restored session" banner and no command output — indistinguishable at a glance from the env map never reaching the shell. Once the shell was actually ready, the injected env value showed up in ~200ms, ruling out an env-seam defect. Replaced both fixed sleeps with bounded polling on the actual conditions (pane text settling, then the expected output appearing). Ran the fixed test 3x standalone, all green. - .gitea/workflows/ci.yml: the "Contract tests" step ran exactly one class by name (-Dtest=AmqpReplyInboxContractTest), silently excluding every other @Tag("contract") test from CI including the herdr ones above -- which is how the stale protocol 14 assertion went unnoticed. Changed to -Dgroups=contract, which selects the whole tagged group and picks up future contract tests automatically.