Skip to content

Add Docker install/upgrade support on Linux and Windows, with a version floor - #701

Merged
ptr727 merged 4 commits into
developfrom
feature/docker-host-setup
Aug 14, 2026
Merged

Add Docker install/upgrade support on Linux and Windows, with a version floor#701
ptr727 merged 4 commits into
developfrom
feature/docker-host-setup

Conversation

@ptr727

@ptr727 ptr727 commented Aug 14, 2026

Copy link
Copy Markdown
Owner

Closes the docker gaps tracked in #695: no version floor was declared, and host-setup/linux/install-tools.sh carried 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 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/wsl --update itself - those stay upgrade-host.ps1 -Wsl and setup-wsl.ps1, a person's own action.
  • 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.

Verification

On this machine (Linux, docker 29.7.1 already installed): spec/validate.py, scripts/host_gate.py, scripts/test_bootstrap.py all pass; shellcheck and PSScriptAnalyzer via the pinned CI images are clean on both modified scripts; markdownlint-cli2 and editorconfig-checker are clean. Exercised --report, --install/--upgrade --dry-run, and a simulated-WSL run (WSL_DISTRO_NAME set) 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

…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>
Copilot AI lite review requested due to automatic review settings August 14, 2026 14:52

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread host-setup/linux/install-tools.sh
Comment thread host-setup/linux/install-tools.sh
Comment thread spec/host-tools.json Outdated
Comment thread host-setup/windows/install-tools.ps1
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>
Copilot AI review requested due to automatic review settings August 14, 2026 15:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 describes install-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_install always returns success even if docker is not on PATH, so --install docker can 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>
Copilot AI review requested due to automatic review settings August 14, 2026 15:09
@ptr727

ptr727 commented Aug 14, 2026

Copy link
Copy Markdown
Owner Author

Addressing the two suppressed findings from the previous review round, both real, fixed in 810133c:

  • host-setup/windows/README.md:93 - the docker/WSL comparison table row had its Linux and Windows columns swapped (install-tools.ps1 was described under Linux, install-tools.sh under Windows). Fixed.
  • host-setup/linux/install-tools.sh:566 - 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 success only where Docker Desktop's own WSL integration already answers; otherwise --install/--upgrade was exiting 0 having neither installed docker nor found it working. Verified by simulating a WSL distribution with docker genuinely absent: the run now exits 1 and names docker as failed.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Copilot AI review requested due to automatic review settings August 14, 2026 15:15
@ptr727

ptr727 commented Aug 14, 2026

Copy link
Copy Markdown
Owner Author

Addressing the newest suppressed finding, fixed in d1cee0a:

  • host-setup/linux/install-tools.sh:539 - 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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.

@ptr727
ptr727 merged commit 75f1fbd into develop Aug 14, 2026
7 checks passed
@ptr727
ptr727 deleted the feature/docker-host-setup branch August 14, 2026 15:21
ptr727 added a commit that referenced this pull request Aug 14, 2026
## 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)
ptr727 added a commit that referenced this pull request Aug 14, 2026
…#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.
ptr727 added a commit that referenced this pull request Aug 15, 2026
…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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants