600cec6ccc
Round-1 review found the floor was DEFEATED for the main #34 target (daemon unreachable at construction): the SDK keeps ClientVersion() == "1.51" on a ping failure (not empty), so the old empty-string guard passed, the floor branch was skipped, lazy negotiation stayed armed, and the client could downgrade below 1.24 on the first real request. - Detect unreachable EXPLICITLY via cli.Ping(ctx) instead of inspecting ClientVersion(): ping error -> rebuildAtFloor; otherwise NegotiateAPIVersionPing(ping) applies the advertised version AND marks the client negotiated (disarms the lazy re-ping); if the result is empty or < 1.24 -> rebuildAtFloor. rebuildAtFloor closes and rebuilds with WithVersion(minSupportedAPIVersion) — which sets manualOverride, making both the lazy checkVersion and NegotiateAPIVersion no-ops, so the pinned client can NEVER re-negotiate below the floor. The rebuild reuses the same opts, so agent-signature headers / TLS / custom transport are preserved. - Pin the floor at minSupportedAPIVersion "1.24", not "1.44": 1.44 is "too new" for a genuinely old daemon (400) and would make container.go's `< 1.44` MAC-cleanup wrongly skip. 1.24 satisfies the invariant and every supported daemon accepts it. Dropped the 1.44 const. - resolveNegotiateTimeout(timeout): use the caller's timeout when set (deployer's 3600s, autoupdate/autoheal), else a modest 10s default. The proxy hotpath and snapshot loop pass nil -> bounded 10s, so a daemon that accepts TCP but hangs on /_ping can't stall the whole snapshot loop for a huge fixed time. - Test TestNegotiateWithFloor_UnreachableThenBelowFloor: unreachable at construction, then the daemon advertises 1.20; asserts the client pins to 1.24, ContainerStart goes /v1.24/ (not /v1.20/), and /_ping isn't hit again after construction (no lazy re-negotiation). Mutation-verified: reverting to the old logic reds it. go build / vet / gofmt clean; go test ./docker/client/ -race 4/4 pass. Still framed as a hardening (part of #34) — the vendored SDK's ContainerStart sends no body regardless, so confirm the failing deployment's build before treating this as the complete #34 fix. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>