Uh oh!
There was an error while loading. Please reload this page.
release-train: develop -> staging - #640
Conversation
* fix(#343): stop stamping GPU_LIMITS on CPU-only installs Only render the GPU_LIMITS env var when a GPU is actually configured (a non-empty .Values.env.GPU_LIMITS). A CPU-only install writes GPU_LIMITS: "" and a chart-direct install may omit the key entirely; previously the template still emitted the var (or its "nvidia.com/gpu=1" default), surfacing a phantom GPU in the CLI resources view that the CLI had to normalize away client-side. Applied to both the api and pods-monitor containers. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#343): gate GPU_REQUESTS on same condition as GPU_LIMITS (#617 bugbot) A chart-direct install that omits env.GPU_LIMITS was still emitting GPU_REQUESTS via its "nvidia.com/gpu=1" fallback, so CPU-only installs kept a phantom GPU and any chart-direct install could ask for a GPU request with no matching limit (rejected by the API server). Gate GPU_REQUESTS with the same `(default "" .Values.env.GPU_LIMITS)` condition in both the api and pods-monitor containers so a CPU-only install emits neither var and a GPU install emits both (request==limit). Also bump client/Chart.yaml version 1.9.15 -> 1.9.16 so the chart content change is publishable (chart-version-guard). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…stall (#618) * fix(#553): wait out the metrics-server APIService race before helm install On a freshly created k3d cluster, k3s registers its bundled metrics-server (and the v1beta1.metrics.k8s.io APIService) shortly AFTER the API server is ready. `k3d cluster create --wait` gates only on node/serverlb readiness, so on a slow WSL2/laptop `helm upgrade --install` could render the resource-monitor DaemonSet in that window and hit its `{{ fail }}` guard, aborting the entire first install. Add a bounded, best-effort `kubectl wait` for the metrics APIService before the helm install (TB_METRICS_WAIT_S, default 120s). If it never registers we fall through and let the chart's render-time guard produce its actionable error, so a genuinely missing metrics-server is still caught (issue's preferred option (a)). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(installer): refresh manifest.sha256 + address #618 review - metrics APIService probe passes --request-timeout=10s (non-watch kubectl rule) - outer spinner deadline derived from TB_METRICS_WAIT_S (+60s slack) so a configured wait >180s is no longer silently truncated - regenerate scripts/manifest.sha256 for the changed install-client-helm.sh Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#553): skip the metrics-server wait under test/no-kubectl so bats stays fast The unconditional _wait_for_metrics_apiservice poll loop sleeps 3s up to ${TB_METRICS_WAIT_S:-120}s. In the mocked install_client_helm bats tests kubectl isn't stubbed, and on the CI runner it isn't installed at all — the `kubectl get` just fails instantly, so the loop still burns its full deadline every test, blowing the 10-min job cap (Bugbot #618, CID 3728079502). Gate the wait exactly like the neighbouring network-y step _download_services_progress: no-op when TB_NO_SERVICE_PROGRESS is set (the bats suite sets it in setup()) or when kubectl is unavailable. Real installs set neither and always have kubectl, so the wait is unchanged for them. Regen manifest.sha256. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Removes this repo's caller for the public PII gate. Lukas's decision, 2026-08-06: retire the gate for now rather than leave it in place undeployed. WHY. The gate has never had a denylist. PII_DENYLIST does not exist as an org Actions secret and never has, so the workflow correctly failed closed on every PR - "an inactive guard must be visible as red, not invisible as green", as its own header puts it. Applied to a secret that was never provisioned, that produced a permanent red across all 7 public repos which blocked nothing: 24 open PRs carried it, and the whole org - this author included - had learned to filter it out. A check that is red on every PR is not fail-closed; it is off with extra steps, and it would be indistinguishable from noise on the day it went red for a real reason. WHAT LAPSES, stated plainly. The gate grepped PR title + body + every commit message against a private list of customer/partner names, on public repos only. It never read code, diffs or filenames, and detected no PII patterns - secrets in code remain gitleaks' job. So what lapses is the mechanical backstop for CLAUDE.md's rule that internal work and customer mentions never go in a public repo. That rule still stands and is now enforced by review alone. The reusable workflow and its selftest stay in tracebloc/.github, dormant with no callers. Restoring is three steps: set the org secret, restore this file, flip the inventory entry back to required. Checklist and the denylist-content caveat are on backend#1409. Not required on any branch, so this removes no merge gate.
#623) * fix(#558): write Docker Desktop autostart for a provisioned daily user Set-DailyUserProvisioning only wrote the HKCU Run key when the provisioned user was the account running the installer ($user -eq $env:USERNAME). For a provisioned DIFFERENT daily user (the hospital IT-installs-elevated case), their registry hive isn't loaded, so no autostart was written and the code relied on --always-run-service. On the WSL2 backend dockerd runs inside the docker-desktop distro that the Docker Desktop GUI boots, so without the GUI autostarting on that user's login the engine isn't up after a reboot and the k3d containers have no daemon to restart into — the client is down until someone opens Docker Desktop manually. For the provisioned different user, drop a "Docker Desktop.lnk" into their Startup folder (same launch-at-logon mechanism as the Run key, no hive load needed). When they have no profile yet, name the one-click GUI setting to flip after first sign-in. The current-user Run-key path is unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(installer): refresh manifest.sha256 for changed install-k8s.ps1 (#558) scripts/install-k8s.ps1 changed in this branch; regenerate the signed integrity manifest via scripts/gen-manifest.sh so the "Static analysis" gate (gen-manifest.sh --check) passes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#558): surface autostart failures instead of a silent green summary The cross-user Docker Desktop autostart path (Startup-folder shortcut for a provisioned DIFFERENT daily user) can fail on Startup-dir creation, the WScript.Shell COM object, or saving the .lnk. It was caught by the outer `catch { Log ... }`, which logged but added nothing to $did. With docker-users succeeding, the summary then printed a green "Configured for '$user'" with no autostart note -- so IT left the elevated window thinking #558 was handled while the daily user still had no autostart and the client is down after every reboot. Append a manual-step note to $did on failure, mirroring the .wslconfig catch in the same function, and name the one-click GUI remediation. Add source-guard Pester tests for the Startup-shortcut feature and the new failure-note path; refresh manifest.sha256. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* feat(gpu): custom k3s-CUDA node image + publish workflow (#616) Foundational build artifact for GPU-enabled edges, split into its own PR so it can land on develop and be published via CI (workflow_dispatch only dispatches from the default branch). Inert on develop — nothing references it until the installer PR (#633) wires it in. The stock rancher/k3s image is Alpine with no NVIDIA runtime, so GPU pods can never schedule on it. This image rebuilds the SAME pinned k3s on an NVIDIA CUDA Ubuntu base, installs the NVIDIA Container Toolkit, configures containerd for the `nvidia` runtime, and bakes in the device plugin + `nvidia` RuntimeClass so the node advertises nvidia.com/gpu on first boot. Based on the official k3d CUDA recipe. - docker/k3s-cuda/Dockerfile, nvidia-device-plugin-daemonset.yaml, build.sh, README.md - .github/workflows/build-k3s-cuda.yaml (manual dispatch; push=true publishes to GHCR, which also validates the Dockerfile builds) Once published (public), the installer pulls it automatically at cluster-create — the end-user runs one command; no manual build or pull. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(gpu): resolve Bugbot findings on the k3s-CUDA build infra (#635) - workflow: pass GITHUB_TOKEN + actor via `env:` and reference quoted shell vars instead of interpolating context straight into the run line of a packages:write job (GHA injection hardening). - workflow: add `timeout-minutes: 60` so a stalled nvcr.io pull / wedged push fails fast instead of spinning to the 6h default (parity with sibling workflows). - Dockerfile: the NVIDIA keyring + apt-list curls now carry the TLS floor + bounded timeouts inline (`--tlsv1.2 --connect-timeout 30 --max-time 60`), the way scripts/install.sh spells them out (house rule; a Dockerfile can't source curl_secure()). - drift guard: the k3s pin was hardcoded in the Dockerfile ARG, build.sh, and the workflow input default with a comment CLAIMING the workflow enforced it — nothing did. Wire all three into check-facts.sh so they're verified == facts.env K8S_VERSION; a bump now fails CI (and `--write` restamps them) instead of silently deriving a GPU image tag that was never published (#547 class). check-facts.sh --check passes; shellcheck clean; workflow YAML valid. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(gpu): seed the k3s-CUDA consumers into the check-facts sandbox (#635) Adding the GPU image's k3s pin as check-facts consumers made check-facts.bats fail — the throwaway repo the tests build only seeds scripts/, so the new Dockerfile / build.sh / workflow consumers reported "file not found" and the --check/--write tests went red. Seed those three files into setup() (matching the spec) so the drift-guard tests exercise them too. Also proves the workflow extractor picks the k3s_tag default (v… tag) and leaves cuda_tag alone. Local: check-facts.bats 14/14, bats-hygiene 18/18. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ci(gpu): run the check-facts drift gate on GPU-image changes too (#635) The check-facts --check gate lives in installer-tests.yaml, whose triggers were path-filtered to scripts/**. A change touching only docker/k3s-cuda/* or the build workflow never ran the gate, so a K3S_TAG edit in the GPU image could drift from facts.env and merge green — defeating the drift rows just added (Bugbot). Add docker/k3s-cuda/** and the build workflow to both the push and pull_request path filters so the gate runs on those changes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…#622) * fix(#557): preflight port 6550 on Windows so a conflict fails clearly New-K3dCluster binds the cluster API server to 127.0.0.1:6550, but Test-Preflight never checked whether 6550 was already in use. On a machine where a leftover/other k3d cluster or an unrelated service owns 6550, `k3d cluster create` failed and the installer surfaced k3d's raw stderr instead of a clear "port already in use" cause. Add Get-PfPortListening (Get-NetTCPConnection based; $null when undeterminable) and a port-6550 check to Test-Preflight. A port owned by THIS installer's own already-running cluster is fine (that run reuses it), so the check only hard-fails when the listener is NOT our cluster, with actionable guidance to find and stop the owner. Adds Pester coverage. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#557): harden port-6550 preflight (bounded, fail-closed, running-gated) Address PR #622 review on the Windows port-6550 preflight: - Get-PfPortListening no longer fails open: -ErrorAction SilentlyContinue swallowed real CIM/access errors into the same empty result as a free port, so a busy port green-OK'd. Switch to -ErrorAction Stop and, in the catch, treat only Get-NetTCPConnection's ObjectNotFound (no listener) as "free"; every other error returns $null ("can't tell"). (Bugbot Med) - Port-ownership check now reuses Test-ClusterRunning instead of a bare `k3d cluster list -o json`: it wraps the call in the same ~15s job deadline, so a wedged Docker engine can't hang preflight (Bugbot High), and it gates on serversRunning >= 1, so a STOPPED leftover cluster no longer masks a foreign listener on 6550 (Bugbot Med). - Regenerate scripts/manifest.sha256 for the changed install-k8s.ps1. - Add Pester coverage: probe error -> $null, ObjectNotFound -> free, stopped-leftover + foreign listener -> fail, running-owned -> ok. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(#557): give the port-6550 running-cluster case a default Has mock After the rebase merged our port-6550 Pester tests with #612, Test-Preflight's network-reachability block calls Has for kubectl/helm/k3d. The "port 6550 in use by our running cluster -> ok" case passes ownership, so it now continues into that block, where a k3d-only -ParameterFilter left those later Has calls with no matching mock ("No mock for command 'Has' matched"). Use a plain default mock (k3d + tools present -> only always-critical hosts probed) so every Has call is covered and the case does not throw. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#557): tri-state port-6550 ownership so a slow k3d list can't hard-fail reuse (Bugbot 3728340365) The port-6550 ownership check decided ownership solely from Test-ClusterRunning, whose bounded `k3d cluster list` maps BOTH a timeout AND a parse failure to $false. The ownership block treated that $false as "foreign listener" and HARD-FAILED preflight with stop/delete hints, so a slow/wedged Docker on a normal re-run wrongly blocked the install and pointed the operator at their own cluster -- conflating "can't determine" with "definitely not ours". Make cluster run-state tri-state: - Get-ClusterRunStateFromList (pure) and Get-ClusterRunState (bounded) return 'running' | 'down' | 'unknown'. Get-ClusterRunState reports 'unknown' only when the list times out or its output is unparseable. - Test-ClusterRunningInList / Test-ClusterRunning become thin boolean wrappers (=='running'), preserving their existing contracts and callers. The ownership block now only hard-fails when CONFIDENT the listener is foreign ('down': enumerated and ours is absent/stopped, or no k3d installed). An 'unknown' read downgrades to a warning and proceeds, letting New-K3dCluster's start/repair path settle it instead of blocking the install. Tests: existing 3 cases updated to mock Get-ClusterRunState; added an indeterminate case (list timed out -> warns, does NOT hard-fail). Manifest regenerated. Pester not run locally (pwsh unavailable); validated by source analysis. Source-of-truth guards for Test-ClusterRunning still hold. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#557): classify an absent cluster as 'down', not 'unknown' (Bugbot High 3728714531) Get-ClusterRunState ran `k3d cluster list <name> -o json`, which fatals with empty stdout when the named cluster is absent. The classifier read that empty output as 'unknown' -> warn-and-proceed, so a genuinely-absent cluster with a FOREIGN listener on 6550 escaped the hard-fail -- exactly the "re-run after the first create failed on the busy port" case #557's preflight must catch. List ALL clusters instead (`k3d cluster list -o json`, no name) and look for $CLUSTER_NAME in the parsed array. A successful full list always emits at least `[]`, so absent-vs-error stays separable: - completed list without a running $CLUSTER_NAME (absent, stopped, or empty []) -> 'down' (confidently not ours -> hard-fail on a busy port) - only a TIMED-OUT or unparseable/failed list -> 'unknown' (warn-and-proceed) Tests: added a Get-ClusterRunState tri-state Describe -- pure classifier cases (absent/empty-list -> 'down'; empty/garbage -> 'unknown') plus bounded cases (timeout -> 'unknown'; completed-but-absent -> 'down'). Manifest regenerated. Pester not run locally (pwsh unavailable); validated by source analysis. Source-of-truth guards for Test-ClusterRunning still hold. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(#557): drop fragile job-cmdlet-mock tests for bounded Get-ClusterRunState (CI Pester) The two bounded-wrapper cases mocked Start-Job/Wait-JobWithProgress/Receive-Job, which is environment-fragile and fails under CI Pester. Coverage is retained via the pure Get-ClusterRunStateFromList tests + the Test-ClusterRunning source-guard (bounded-job regex). No product-code change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
LukasWodka
commented
Aug 6, 2026
bugbot run |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 047256a. Configure here.
Uh oh!
There was an error while loading. Please reload this page.
LukasWodka
commented
Aug 6, 2026
@shujaatTracebloc — flagging rather than fixing, since this is your k3s-CUDA feature (client#635) and the fix needs a judgement call about the GPU install flow that I should not make for you. Bugbot rates this High on this
If that holds, the image is being built and published but nothing consumes it — the feature would ship to staging inert, and GPU training pods stay unschedulable, which is exactly the symptom someone would later debug as a cluster problem rather than a missing selection. Per the train's rule the fix goes on @LukasWodka — worth a look before this promotion lands. |
Uh oh!
There was an error while loading. Please reload this page.

Automated promotion by the release train (RFC-0008 D14). Head is the train-managed
release-train/to-stagingbranch (a mirror ofdevelop), so it never collides with a human PR. Merged only when the fr-gate is green.Note
Medium Risk
Touches cluster creation, GPU scheduling, and Windows preflight paths customers hit on install/reboot; changes are guarded by tests and drift gates but a bad port/GPU classification could block or mislead installs.
Overview
Release train promotion (chart 1.9.22). The headline capability is GPU-enabled k3d edges (#616): a new
docker/k3s-cudaimage (CUDA base + NVIDIA Container Toolkit + baked device plugin/nvidiaRuntimeClass),build-k3s-cudamanual CI workflow, andcheck-facts/ installer-tests path filters soK3S_TAGcannot drift fromK8S_VERSION.Helm (#343) only injects
GPU_REQUESTS/GPU_LIMITSinto jobs-manager whenGPU_LIMITSis non-empty, so CPU-only installs no longer show a phantom GPU in the CLI.Windows installer (
install-k8s.ps1) adds tri-state port 6550 preflight (reuse own running cluster vs hard-fail foreign listener vs warn on indeterminatek3d cluster list), refactors cluster run-state via fullk3d cluster listJSON (#557), and improves Docker Desktop autostart for a provisioned daily user via Startup-folder shortcut (#558).Bash install waits for metrics.k8s.io APIService before Helm render (#553).
.github/workflows/public-pii-gate-caller.ymlis removed. Manifest hashes and Pester/bats coverage updated for the above.Reviewed by Cursor Bugbot for commit 047256a. Bugbot is set up for automated code reviews on this repo. Configure here.