Add Docker install/upgrade support on Linux and Windows, with a version floor - #701
Conversation
…on floor docker was declared required in spec/host-tools.json with no version floor, and host-setup/linux/install-tools.sh carried no install path for it at all (deliberately, per a NOT_MANAGED exception: a hypervisor, a WSL distribution, and a workstation want different answers). Windows already installed Docker Desktop through winget, but nothing checked that WSL2 itself was present and current first. - spec/host-tools.json: docker now carries a target floor of 29.6.2, anchored to the Windows host issue #695 recorded (Docker Desktop 4.85.0, Engine 29.6.2), plus source and remedy for linux, macos and windows. - host-setup/linux/install-tools.sh: adds a real docker install/upgrade path, the same apt-repository shape gh and node already use here - removes old conflicting packages, registers download.docker.com's own repo with a verified signing key, installs docker-ce/docker-ce-cli/containerd.io/docker-buildx-plugin/docker-compose-plugin. Inside a detected WSL distribution it refuses the native install outright, with no override, and points at Docker Desktop's own WSL integration instead. - host-setup/windows/install-tools.ps1: before installing or upgrading docker, checks that wsl.exe is present and WSL is at or above 2.1.5, Docker Desktop's own documented floor, and skips with a named remedy otherwise. It never runs wsl --install or wsl --update itself - those stay upgrade-host.ps1 -Wsl and setup-wsl.ps1, a person's own action, since a WSL platform update restarts every distribution and neither belongs as a side effect of installing a different tool. - scripts/test_bootstrap.py, docs/host-setup.md, host-setup/windows/README.md updated to match: the now-stale NOT_MANAGED['linux']['docker'] exception is removed, the floor table and comparison table are brought current. Verified on this machine: spec/validate.py, scripts/host_gate.py (docker 29.7.1 meets the new floor), scripts/test_bootstrap.py, shellcheck and PSScriptAnalyzer via the pinned CI images, markdownlint-cli2, editorconfig-checker all clean. Exercised --report, --install/--upgrade --dry-run, and a simulated-WSL run against the real download.docker.com repository (GPG key fetch and verification succeeded). A real install/upgrade on a native Windows host and inside an actual WSL distribution is a maintainer follow-up. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR closes the Docker tooling gaps by adding a declared Docker Engine version floor (29.6.2) to the host tool spec and implementing first-class install/upgrade support for Docker on both Linux (apt repo) and Windows (winget), including explicit WSL handling.
Changes:
- Declare a target Docker Engine floor (29.6.2) in
spec/host-tools.json, including source/remedy metadata. - Add Docker install/upgrade support on Linux via Docker’s apt repository, with a hard skip inside WSL distributions.
- Add Windows Docker preflight checks to ensure WSL is present and meets Docker Desktop’s documented minimum before install/upgrade, and update docs/bootstrap tests accordingly.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| spec/host-tools.json | Adds Docker minimum version floor plus source/remedy text for each platform. |
| host-setup/linux/install-tools.sh | Adds Docker to managed tools and implements Docker apt-repo install/upgrade flow; detects and skips WSL distributions. |
| host-setup/windows/install-tools.ps1 | Adds WSL readiness checks gated specifically for Docker, and surfaces notes during reporting. |
| scripts/test_bootstrap.py | Updates the “not managed” coverage assertions now that Docker is managed on Linux. |
| host-setup/windows/README.md | Documents the new Docker/WSL behavior and contrasts Linux vs Windows handling. |
| docs/host-setup.md | Updates the contract table and narrative to reflect the new Docker floor and install source expectations. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
scripts/test_host_gate.py asserted a fixed set of tools carrying a declared floor and a fixed set of target-floor entries, both hardcoded and missed by the exploration before this change - this is what CI's Lint sources job actually failed on. docker is added to both. Also addresses PR #701's Copilot review: - host-setup/linux/install-tools.sh: docker_install now fails fast with a named cause when /etc/os-release carries no VERSION_CODENAME, instead of building a broken repository URL. - host-setup/linux/install-tools.sh: the dearmored signing key is written as docker.gpg rather than docker.asc, matching the nodesource.gpg naming already used for a binary keyring here. - spec/host-tools.json: docker's why text reworded so the WSL exception describes what the printed Linux remedy actually does (refuses and re-points) rather than reading as though a different remedy should have been shown. - host-setup/windows/install-tools.ps1: Test-WslReadyForDocker's unparseable-version branch now names the same upgrade-host.ps1 -Wsl remedy the below-floor branch already gives. Re-verified: scripts/test_host_gate.py, scripts/host_gate.py, scripts/test_bootstrap.py, spec/validate.py, shellcheck and PSScriptAnalyzer via the pinned CI images, markdownlint-cli2, editorconfig-checker all clean. Re-ran the docker dry-run and simulated-WSL checks on this machine to confirm docker.gpg and the codename guard behave as intended. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.
Suppressed comments (2)
host-setup/windows/README.md:93
- The “Differences From the Linux Tooling” table columns are swapped for the docker/WSL row: the Linux column currently describes
install-tools.ps1(Windows) and the Windows column describesinstall-tools.sh(Linux), which makes the comparison misleading.
| `install-tools.ps1` checks the WSL *platform* version before installing docker | `install-tools.sh` refuses docker entirely inside a WSL *distribution* | Windows needs WSL2 present for Docker Desktop's own backend; a WSL distribution instead takes docker only from Docker Desktop's own WSL integration |
host-setup/linux/install-tools.sh:566
- In the WSL-distro case,
docker_installalways returns success even ifdockeris not on PATH, so--install dockercan exit 0 with no failure recorded despite not installing (or providing) docker. Consider treating “WSL + docker missing” as a collected failure while still refusing the native install, so callers can detect the unmet contract.
# A native install here would run a second engine beside Desktop's, so this is a skip rather than a failure, on the same pattern dotnet_feed uses for an architecture Microsoft's feed does not carry.
if [[ $IS_WSL == true ]]; then
warn "This is a WSL distribution, and docker here comes only from Docker Desktop's own WSL integration, never from installing docker-ce directly. Enable it in Docker Desktop under Settings, Resources, WSL integration, or check it from Windows with setup-wsl.ps1 -Status. Skipping the native install."
return 0
fi
Two suppressed (low-confidence) findings from PR #701's review, both real: - host-setup/linux/install-tools.sh: docker_install on a WSL distribution now returns failure when docker is not on PATH there either, rather than always reporting success. A skip is only success where Docker Desktop's own WSL integration already answers - otherwise --install/ --upgrade was exiting 0 having neither installed docker nor found it working, which hid an unmet contract from a caller checking the exit code. - host-setup/windows/README.md: the docker/WSL comparison table row had its Linux and Windows columns swapped (install-tools.ps1 described under Linux, install-tools.sh under Windows). Also fixes what scripts/prose_lint.py itself flags on the new install-tools.ps1 comments (not run locally before the prior two pushes, which is why this took a third round): one sentence per comment line rather than wrapped across several, and no sentence opening on a lowercase filename. And one semicolon in the same README row, which this repo's prose rules treat as two sentences or a comma. Re-verified: scripts/prose_lint.py, scripts/repo_gate.py --check eol/eol-coverage (not run in either prior round), shellcheck, PSScriptAnalyzer, markdownlint-cli2, editorconfig-checker, spec/validate.py, scripts/host_gate.py, scripts/test_bootstrap.py, scripts/test_host_gate.py all clean. Simulated a WSL distribution with docker genuinely absent from PATH to confirm the new failure path actually exits 1 and names docker as failed, and re-ran the WSL-with-docker- present and plain-outdated dry-run paths to confirm neither regressed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Addressing the two suppressed findings from the previous review round, both real, fixed in 810133c:
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.
Suppressed comments (1)
host-setup/linux/install-tools.sh:539
- docker_source() always reports download.docker.com, but inside a WSL distribution docker is explicitly expected to come from Docker Desktop’s WSL integration (and docker_install even refuses the native docker-ce path). This makes the report/list SOURCE column misleading on WSL hosts.
docker_source() { printf 'download.docker.com'; }
docker_source always reported download.docker.com, even inside a WSL distribution where docker_install refuses that path outright and points at Docker Desktop's own WSL integration instead - so the --report/--list SOURCE column was misleading exactly where the WSL note already explains the real source. docker_source now reads IS_WSL the same way docker_install does, mirroring the existing dotnet_source precedent for a source that depends on host state. Re-verified: prose_lint.py, shellcheck, repo_gate.py --check eol/eol-coverage, spec/validate.py, host_gate.py, test_bootstrap.py, test_host_gate.py all clean. Confirmed --list (no host detection yet) still reads download.docker.com, a plain --report reads download.docker.com, and a WSL_DISTRO_NAME-simulated --report reads "Docker Desktop's WSL integration". Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Addressing the newest suppressed finding, fixed in d1cee0a:
|
## Why A re-analysis of `docs/fleet-map.md`, applying the same gap-finding method that created it, after the P0-P4 closures, the #689/#701 install-model changes, and the #702 procedure-doc diagrams. ## What - **Diagram authority**: #702 put authoritative flow diagrams into STANDUP.md, RESYNC.md, AUDIT.md, and AGENTS.md. The map's own copies of the STANDUP and RESYNC flows were coarser duplicates with no cross-reference in either direction, and by the map's own rule the procedure doc wins. The two derivative diagrams are replaced with pointer sentences, and the Entry Points intro states the ownership rule. The five diagrams no procedure doc draws (System Map, Pre-Agent Cold Start, Daily Development, Hub-Side Operations, G3 remedy loop) stay. - **Maintenance rule extended**: it covered register rows but not drawn flows, which is exactly the class this batch fixes. It also cited G4 for register staleness, which is at best an analogy, so the sentence states the class directly. - **Stale content**: the vestigial `Checked` evidence-anchor sentence (the shipped register has no such cells, and the `ba392f9` pin is historical) is removed, the cold-start tool node points at `spec/host-tools.json` instead of an enumeration that drifted twice in two weeks (#689, #701), and the dead "dashed arrows mark a gap" clause (zero dashed arrows exist) is removed. - **Unmapped entry points surfaced**: OPERATIONS.md is named as the runnable form of the Hub-Side Operations door and of the daily-dev gates node, and `scripts/repo_gate.py`, `scripts/pr_review.py`, and `spec/audit.py` get reference links. - **Cross-links**: #699 and #700 join the Decision Ledger Cross-References per the #671-cluster precedent, and the #689 PATH self-heal is noted in the cold-start prose. - **Discoverability**: README links the map from the doors diagram it sits behind. AGENTS.md is deliberately untouched (byte-locked Fleet Bootstrap, and a carried file must not reference a hub-only doc). - `worktree` joins `cspell.json` words (used by the #699 cross-link). ## Flag, not a task `docs/fleet-map.md` and the #702 diagrams exist only on `develop` (main is 22 commits behind and lacks the file entirely). A `develop` to `main` promotion is a maintainer decision and is not part of this change. ## Verification All nine CI prose_lint checks pass tree-wide and `--diff develop` is clean, markdownlint reports 0 issues on both edited files, cspell is clean on README and leaves only the six pre-existing fleet-map findings (the file is not CI spell-gated), editorconfig-checker passes (CRLF preserved, diff is line-scoped), `spec/validate.py` and `jq` pass, all 6 remaining mermaid fences (5 in fleet-map, README doors) parse OK via mermaid@11, and a two-way reference-link check (every use defined, every relative target exists) passes. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
…#705) Stop Docker Desktop and Update WSL During a Docker Install or Upgrade #701 checked the WSL platform floor before a docker install or upgrade and only named the remedy, upgrade-host.ps1 -Wsl. That left docker to skip outright whenever WSL was behind, since that script also refuses while Docker Desktop is running. Verifying #701 on a real Windows host surfaced two more gaps. Docker Desktop's own WSL integration goes stale across most engine bumps, surfacing as "WSL integration with distro '<name>' unexpectedly stopped". The in-app "Restart the WSL integration" button does not clear it. And wsl --update raises its own UAC prompt, which a headless run cannot answer. - `install-tools.ps1`: a docker install, upgrade or reinstall now drives the whole maintenance window itself. It stops Docker Desktop through its own CLI (`docker desktop stop`/`start`), runs `wsl --update` where the floor is not met, shuts every WSL distro down with `wsl --shutdown` so each one's integration remounts fresh, and restarts Docker Desktop once the package itself is settled. Confirmed first, unless `-Yes` was given. A non-elevated, non-interactive run refuses to start `wsl --update` rather than hang on its UAC prompt, and restores Docker Desktop to how it found it. The WSL-readiness check moved from an unconditional early gate to just before real work happens, so an already-current docker no longer fails a run over an unrelated stale WSL platform. - `README.md`: documents the moved boundary. WSL used to be strictly read-only here, naming `upgrade-host.ps1 -Wsl` as a person's own step. A docker version bump already needing the same stop-then- restart window for its own integration is what moved it. ## Verification Exercised live on a real Windows host, not simulated: a real docker upgrade (4.85.0 -> 4.86.0) through winget; the WSL-below-floor path through a real `wsl --update` past an actual UAC prompt (WSL 2.7.10.0 -> 2.7.11.0), re-verifying the floor and restarting Docker Desktop afterward; the plain engine-bump stop, shutdown, restart cycle; the declined-prompt and neither-condition-true branches, through mocked extraction of the real functions; and the headless-UAC-refusal guard, both tripping unelevated and bypassing elevated correctly. `Invoke-ScriptAnalyzer` against the repo's own settings, a parse check, `pytest scripts/test_bootstrap.py scripts/test_host_gate.py` (82 passed), and `host_gate.py` all pass.
…Python CI Gates (#718) Thirty-one squashes, `56f4d7d..d54862a`. 115 files, +20436/-5298. **Merge with a merge commit, never a squash, and never with `--delete-branch`.** This pull request's head is `develop` itself. ## What lands **Fleet Skills.** The `.agents/skills/` source tree, the generated `.claude-plugin/` distribution, `scripts/build_dist.py` with its `--check` gate, and `scripts/skills_install.py` with its host stamp (#676). Packaged as skills on top of the scaffold: PR review conduct and Copilot instructions upkeep (#677), comment and doc style (#678), resync-a-repo and fleet-conformance-check (#679), the per-language codestyles (#680), git commit conventions and operational vs release workflow (#681), stand up a repo (#683), and repo-worktree (#717). Coverage gaps closed in three passes (#690, #691, #692) plus the P4 sentence-length opt-in (#697). **Host setup.** The Windows host-setup tooling and its PowerShell gate (#674), the Windows bootstrap loader (#682), Docker install and upgrade on Linux and Windows with a version floor (#701, #705), a `uv` floor in `spec/host-tools.json` (#698), self-healing of a shadowing `uv`, `jq`, or `git-restore-mtime` copy (#689), node's real winget package id (#696), and a README for the Linux host-setup nuances (#710). **Python and CI.** Python tooling in CI with the script tests moved to `scripts/tests` (#704), `ruff format` adopted and gated (#709), and the PSScriptAnalyzer claim conditioned on repos that carry `.ps1` files (#686). **Conduct rules.** Triage-order and scope guardrails in pr-review-conduct (#684), `pr_review.py wait` requesting a review rather than only polling for one (#685), a tech-agnostic signed-commit verification (#708), execution rather than analogy to verify platform-specific code (#715), and a unique worktree for every task (#717). **Docs.** The fleet map and gap register with peer messaging declared (#687), mermaid flow diagrams in the kept-authority docs (#702), and the map pointed at the shipped diagrams and current tooling (#703). ## Issues this promotion closes Each landed on `develop` on its own pull request. The keyword fires only on a merge into `main`, so it sits here rather than on the feature pull requests. Closes #700 Closes #707 Closes #711 Closes #712 Closes #714 Closes #688 #699 stays open on purpose: #717 shipped the layout convention and the skill, and the physical migration of existing checkouts is still tracked there. ## Review record Every squash closed its own Copilot loop on its own pull request before merging to `develop`. This promotion carries no new content of its own, so its review is the merged tree as a whole. ## Consequence worth stating The `GOVERNANCE.md` and `AGENTS.md` sections these squashes changed become the canonical the moment this reaches `main`, and every carrying repository reads as drifted from that point until it resyncs. That is the ordinary consequence of a canonical moving rather than a defect. The Skills installer added here is also how a machine picks the new skills up, so a session that keeps restating a rule already packaged as a skill is the signal to run it.
Closes the docker gaps tracked in #695: no version floor was declared, and
host-setup/linux/install-tools.shcarried no install path for docker at all.spec/host-tools.json: docker now carries a target floor of 29.6.2, anchored to the Windows host issue spec/host-tools.json: docker and uv carry no version floor — propose starting points #695 recorded (Docker Desktop 4.85.0, Engine 29.6.2), plus source and remedy for linux, macos and windows.host-setup/linux/install-tools.sh: a real docker install/upgrade path, the same apt-repository shapeghandnodealready use here - removes old conflicting packages, registers download.docker.com's own repo with a verified signing key, installsdocker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin. Inside a detected WSL distribution it refuses the native install outright, with no override, and points at Docker Desktop's own WSL integration instead.host-setup/windows/install-tools.ps1: before installing or upgrading docker, checks thatwsl.exeis present and WSL is at or above 2.1.5 (Docker Desktop's own documented floor), and skips with a named remedy otherwise. It never runswsl --install/wsl --updateitself - those stayupgrade-host.ps1 -Wslandsetup-wsl.ps1, a person's own action.scripts/test_bootstrap.py,docs/host-setup.md,host-setup/windows/README.mdupdated to match: the now-staleNOT_MANAGED['linux']['docker']exception is removed, the floor table and comparison table are brought current.Verification
On this machine (Linux, docker 29.7.1 already installed):
spec/validate.py,scripts/host_gate.py,scripts/test_bootstrap.pyall pass;shellcheckandPSScriptAnalyzervia the pinned CI images are clean on both modified scripts;markdownlint-cli2andeditorconfig-checkerare clean. Exercised--report,--install/--upgrade --dry-run, and a simulated-WSL run (WSL_DISTRO_NAMEset) against the real download.docker.com repository - the GPG key fetch and verification succeeded and the WSL path correctly refused with no apt commands attempted.A real install/upgrade on a native Windows host and inside an actual WSL distribution has not been run in this session - the maintainer is following up with both separately.
🤖 Generated with Claude Code