From fa2680468001be99bf9cbf2a5134ff78cf51a06c Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 30 Sep 2026 12:09:36 +0000 Subject: [PATCH] fix(doctor, test-stack-up): no Docker daemon in a cloud container is normal, not a warning The cloud container ships the docker CLI with no daemon and the agent cannot start one. Doctor warned and printed 'sudo systemctl start docker', so sessions tried, failed and reported it as a blocker, while the mxbuild gate and mxcli run --local need no Docker. The cloud lane now says so and names the Docker-free route. Desktop lanes keep the warning plus a line that a container is optional. test-stack-up.sh names the same route instead of failing inside mxcli docker run. Field run: this cloud container (docker CLI, no daemon), plus desktop-lane, podman-only and podman-in-cloud variants. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw --- CHANGELOG.md | 1 + bin/doctor.sh | 36 +++++++++++++++++++------ project-bin/test-stack-up.sh | 23 +++++++++++++++- tests/wave2/test-doctor-docker-probe.sh | 15 +++++++++-- 4 files changed, 64 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 210a5828..526bbf93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ three commits past it), and a bug report can name a release instead of a sha nob Sections dated before 2026-09-19 predate the cycle and stay as they are. ## Unreleased +- fix(doctor, test-stack-up): **in a cloud container, a missing Docker daemon is now reported as normal, with the Docker-free route, instead of a warning that says to start it.** The Claude Code on the web container has the docker CLI but no daemon, and the agent cannot start one. Doctor used to WARN "docker daemon is not responding … sudo systemctl start docker", so sessions tried, failed, and reported "cannot start the docker daemon" as a blocker, until the user said to use `mxcli run --local`. Now the cloud lane prints: no Docker here, normal, do not try to start it; build check = exec.sh's mxbuild gate; run the app = `./mxcli run --local`. On a desktop (Docker or Podman, stopped or absent) the warning and start hint stay, plus one line saying a container is optional: the mxbuild gate needs none, and Studio Pro's Run Locally or `mxcli run --local` runs the app. The no-runtime text no longer says the build check needs Docker. `test-stack-up.sh` with the app down and no reachable Docker/Podman now stops with the Docker-free route instead of failing inside `mxcli docker run`. — MendixMau - fix(gate-check): **the stage-decision readers now read only the Decisions table, and Stage 7 accepts a dated CONFIRMED.** `has_confirmed_decision` (Stages 3 and 4) and the Stage 7 cutover check scanned every pipe row in the register, so an Open-questions row numbered 4 or 7 with Status CONFIRMED passed that stage with no decision behind it. Both now count only rows under a header whose first cell is `Stage`; a register with no such header keeps the old every-row scan. Stage 7 also matched the status string-exactly, so `CONFIRMED 2026-08-10` failed there while passing every other stage (TD-07); it now uses the same word-anchored match. Positive control: the pre-fix script passes Stages 4 and 7 on an Open-questions-only register and fails Stage 7 on the dated status. Fixture T13–T15 in `test-bug03-gates.sh` — MendixMau - learn(bug-logs): **`BUG-DRAFT-grant-association-generalization-member`** — a member association owned by another module's entity cannot be named in a `grant` statement, so `revoke` + `re-grant` silently drops that member access and `mx check` reports CE0066; patch-only workaround provided. Found fixing a guest-groups association in a Mendix app (mxcli v0.23.0, Mendix 11.12.2). — MendixMau - chore(inbox): **triage of `contrib/inbox/`: removed 15 promoted/obsolete files, gave the rest neutral names and scrubbed identifiers.** Gone: the ten machine-diff `*-patches.md` files from 2026-09-14, the 2026-09-27 field-project patch file, the built company-brain idea, and three already-promoted or superseded bug dumps. Kept for now: `2026-09-27-mxcli-upstream-issues.md`, because five drafts under `bug-logs/pending-github-issues/` cite it as their source. Four remaining files were renamed to `field-project-{a,b,c}` names; project and app names, home-style paths and personal names inside the 17 remaining entries were replaced with neutral labels or placeholders (technical content unchanged). — MendixMau diff --git a/bin/doctor.sh b/bin/doctor.sh index 65935b23..51519f6d 100755 --- a/bin/doctor.sh +++ b/bin/doctor.sh @@ -764,6 +764,20 @@ container_start_hint() { esac fi note "Then re-run: bin/doctor.sh${PROJECT_DIR:+ $PROJECT_DIR} (this section is skipped by --quick)" + note "Not required, if you would rather not run one: the build check (exec.sh's mxbuild gate) needs no" + note "container, and Studio Pro's Run Locally or ./mxcli run --local runs the app without one." +} + +# cloud_no_daemon — the Claude Code on the web container ships the docker CLI but no running +# daemon, and the agent cannot start one there. Telling it "sudo systemctl start docker" sent +# sessions off trying, then reporting "cannot start the docker daemon" as a blocker — while the +# mxbuild gate (exec.sh) and `mxcli run --local` both work with no Docker at all. So in the cloud +# lane a missing daemon is reported as the normal state, with the Docker-free route, not a WARN. +cloud_no_daemon() { + ok "no $RUNTIME_LABEL in this cloud container — normal here, and not a blocker. Do not try to start it." + note "Build check: exec.sh's mxbuild gate — mxbuild is a plain binary, no Docker needed." + note "Run the app: ./mxcli run --local (add --hub for a preview URL) — skills/cloud-dev-environment.md." + note "Snapshot first: mxcli run --local consolidates a split-model .mpr (bug-logs, CRITICAL)." } if [ "$NO_DOCKER" = 1 ]; then @@ -774,7 +788,12 @@ elif [ -n "$CONTAINER_RUNTIME" ]; then container_daemon_up || DOCKER_STATE=$? case "$DOCKER_STATE" in 0) - ok "$RUNTIME_LABEL responding — mxcli docker check + test-stack-up.sh (app up, e2e, screenshots) available" ;; + ok "$RUNTIME_LABEL responding — mxcli docker run + test-stack-up.sh (app up, e2e, screenshots) available" ;; + *) [ "$ENV_LANE" = cloud ] && DOCKER_STATE=cloud ;; + esac + case "$DOCKER_STATE" in + 0) ;; + cloud) cloud_no_daemon ;; 2) warn "$CONTAINER_RUNTIME is installed but '$CONTAINER_RUNTIME info' gave no answer within ${DOCKER_PROBE_SECS} s — treated as not running." note "That silent wait is what makes a setup look hung. Raise the bound with" @@ -790,19 +809,20 @@ elif [ -n "$CONTAINER_RUNTIME" ]; then # Never elsewhere — Windows launch paths vary and Linux needs sudo. Podman is left alone: # `podman machine start` on a machine that has never run `podman machine init` is not a safe # guess to make on someone's behalf. - if [ "$DOCKER_STATE" != 0 ] && [ "$INSTALL" = 1 ] && [ "$CONTAINER_RUNTIME" = docker ] && [ "$PLATFORM" = macos ] && [ -d /Applications/Docker.app ]; then + if [ "$DOCKER_STATE" != 0 ] && [ "$DOCKER_STATE" != cloud ] && [ "$INSTALL" = 1 ] && [ "$CONTAINER_RUNTIME" = docker ] && [ "$PLATFORM" = macos ] && [ -d /Applications/Docker.app ]; then if open -a Docker 2>/dev/null; then note "--install: launched Docker Desktop (open -a Docker). Give it ~30-90 s, then re-run doctor." fi fi +elif [ "$ENV_LANE" = cloud ]; then + cloud_no_daemon else warn "no container runtime found — neither docker nor podman. One is recommended for new builders." - note "It is what lets the agent verify its own build: 'mxcli docker check' (deep model+build" - note "verification) and project-bin/test-stack-up.sh (Postgres + the app up, Playwright e2e," - note "page screenshots) both need it. Without it, only mxbuild verifies the model and a human" - note "must open Studio Pro to see whether anything actually renders." - note "No-container fallback for just running the app: 'mxcli run --local' against a native" - note "PostgreSQL (host:PORT)." + note "The build check does NOT need it: exec.sh's mxbuild gate is a plain binary. What it adds" + note "is the running app in a container: project-bin/test-stack-up.sh (Postgres + the app up," + note "Playwright e2e, page screenshots)." + note "No-container route for running the app: 'mxcli run --local' against a native" + note "PostgreSQL (host:PORT) — the same route the cloud lane uses." note "Docker Desktop needs a paid licence at larger companies, and is NOT required: Podman is" note "the licence-free option (docker-CLI compatible; doctor probes it directly, so no docker" note "shim is needed). Rancher Desktop and colima (macOS) are the other common substitutes." diff --git a/project-bin/test-stack-up.sh b/project-bin/test-stack-up.sh index 124b6f3d..8d1a0b08 100755 --- a/project-bin/test-stack-up.sh +++ b/project-bin/test-stack-up.sh @@ -320,7 +320,28 @@ fi # --- Bring up the app ------------------------------------------------------- if [ $APP_FOUND -ne 0 ]; then if [ "$MODE" = "nodocker" ]; then - bad "App down and --no-docker given. Click Run Locally in Studio Pro, then re-run --check." + bad "App down and --no-docker given. Click Run Locally in Studio Pro (or, with no Studio Pro, ./mxcli run --local), then re-run --check." + exit 1 + fi + # No reachable Docker (or Podman) daemon: say so and name the Docker-free route, instead of letting + # `mxcli docker run` fail with a daemon error the agent then reports as "cannot start Docker". + # A cloud container ships the docker CLI with no daemon; that is normal, not a blocker. + # Bounded: a stopped Docker Desktop can leave `docker info` silent for minutes. + _RT=docker; command -v docker >/dev/null 2>&1 || _RT=podman + if ! command -v "$_RT" >/dev/null 2>&1; then _DK=1; else + "$_RT" info >/dev/null 2>&1 & _dk_pid=$!; _dk_w=0; _DK= + while kill -0 "$_dk_pid" 2>/dev/null; do + [ "$_dk_w" -ge 15 ] && { kill "$_dk_pid" 2>/dev/null; _DK=2; break; } + sleep 1; _dk_w=$((_dk_w + 1)) + done + [ -n "$_DK" ] || { wait "$_dk_pid"; _DK=$?; } + fi + if [ "$_DK" != 0 ]; then + bad "App down and no Docker/Podman reachable, so this script cannot build the app container." + echo " Normal in a cloud container, and fine on a desktop without one. Run the app without it:" + echo " Studio Pro: Run Locally. No Studio Pro:" + echo " ./mxcli run --local -p $(basename "$MPR") # flags: skills/cloud-dev-environment.md" + echo " Snapshot first — mxcli run --local consolidates a split-model .mpr. Then re-run with --check." exit 1 fi if [ -z "$MXCLI" ]; then diff --git a/tests/wave2/test-doctor-docker-probe.sh b/tests/wave2/test-doctor-docker-probe.sh index 5843dfb5..4da3c1b8 100755 --- a/tests/wave2/test-doctor-docker-probe.sh +++ b/tests/wave2/test-doctor-docker-probe.sh @@ -29,7 +29,9 @@ echo "doctor docker probe — $DOCTOR" # 1. hanging daemon: bounded, and says so S0=$(date +%s) -PATH="$T/hang:$PATH" MXTK_DOCKER_PROBE_SECS=2 bash "$DOCTOR" > "$T/hang.out" 2>&1 +# Cases 1-2 are the desktop/devcontainer lanes: CLAUDE_CODE_REMOTE cleared, so the fixture +# means the same thing when it runs inside a cloud session. Case 2b is the cloud lane. +PATH="$T/hang:$PATH" CLAUDE_CODE_REMOTE= MXTK_DOCKER_PROBE_SECS=2 bash "$DOCTOR" > "$T/hang.out" 2>&1 S1=$(date +%s) if [ $((S1 - S0)) -lt 40 ]; then ok "hanging docker: doctor finished in $((S1 - S0)) s (bound 2 s)"; else fail "hanging docker: doctor took $((S1 - S0)) s — the bound is not biting"; fi assert_contains "$T/hang.out" "probing the docker daemon (bounded: 2 s" "hanging docker: announces the probe and its bound before waiting" @@ -39,10 +41,19 @@ assert_contains "$T/hang.out" "Then re-run: bin/doctor.sh" "hanging docker: says assert_missing "$T/hang.out" "docker daemon responding" "hanging docker: not reported as up" # 2. daemon down, answering at once -PATH="$T/down:$PATH" bash "$DOCTOR" > "$T/down.out" 2>&1 +PATH="$T/down:$PATH" CLAUDE_CODE_REMOTE= bash "$DOCTOR" > "$T/down.out" 2>&1 assert_contains "$T/down.out" "daemon is not responding" "down docker: reported as not responding" assert_contains "$T/down.out" "To start it:" "down docker: carries a start command" assert_missing "$T/down.out" "gave no answer within" "down docker: a fast 'down' is not called a timeout" +assert_contains "$T/down.out" "Not required, if you would rather not run one" "down docker: says a container is optional" + +# 2b. cloud container: docker CLI, no daemon — the normal state there, never a start command. +# (Sessions told "sudo systemctl start docker" tried, failed, and reported it as a blocker.) +PATH="$T/down:$PATH" CLAUDE_CODE_REMOTE=true bash "$DOCTOR" > "$T/cloud.out" 2>&1 +assert_contains "$T/cloud.out" "normal here, and not a blocker" "cloud, no daemon: reported as normal" +assert_contains "$T/cloud.out" "mxcli run --local" "cloud, no daemon: names the Docker-free run route" +assert_missing "$T/cloud.out" "To start it:" "cloud, no daemon: no start command" +assert_missing "$T/cloud.out" "WARN docker is installed" "cloud, no daemon: not a WARN" # 3. daemon up PATH="$T/up:$PATH" bash "$DOCTOR" > "$T/up.out" 2>&1