From 3fdbf5f532c5b2715c3277dda8ad4dfb159e291b Mon Sep 17 00:00:00 2001 From: Lukas Wuttke Date: Mon, 31 Aug 2026 11:59:18 +0200 Subject: [PATCH 1/2] docs(resources): stop calling requests == limits "the chart contract" (backend#2872) Four comments in this package explained the envelope by asserting a QoS class or a chart-wide contract. The requests == limits part is accurate here -- BuildEnvSpec writes RESOURCE_REQUESTS and RESOURCE_LIMITS as the same string, so a CPU training pod does come out Guaranteed. What was false is the framing: * "the chart contract" -- the chart DERIVE path (DERIVE_JOB_ENVELOPE) has written no cpu limit at all since backend#2418, so equal requests and limits is this package's choice for the explicit envelope, not an invariant of the chart. Calling it a contract is what let the claim survive after #2418 falsified it everywhere else. * a GPU pod is BestEffort no matter what this writes: client-runtime's GPU path sets only nvidia.com/gpu and ephemeral-storage, and neither counts toward the QoS class (backend#2871). * the "cpu=2,memory=8Gi" literal named as RESOURCE_LIMITS is stale -- the built-in fallback has been the contract floor cpu=1,memory=2Gi since backend#2254. Includes a test FAILURE MESSAGE (provenance_test.go), which is where a wrong explanation does the most damage: it is read only by whoever just broke the thing, at the moment they are deciding what the rule was. Comment-only; no behaviour change. Co-Authored-By: Claude Opus 4.8 --- internal/resources/provenance_test.go | 5 ++++- internal/resources/resources.go | 14 +++++++++++--- internal/resources/set.go | 11 +++++++++-- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/internal/resources/provenance_test.go b/internal/resources/provenance_test.go index 0e0ce49..0425cd8 100644 --- a/internal/resources/provenance_test.go +++ b/internal/resources/provenance_test.go @@ -29,7 +29,10 @@ func TestBuildEnvSpecStampsUserProvenance(t *testing.T) { t.Errorf("RESOURCE_LIMITS = %q, want cpu=6,memory=24Gi", env["RESOURCE_LIMITS"]) } if env["RESOURCE_REQUESTS"] != env["RESOURCE_LIMITS"] { - t.Error("requests and limits diverged — Guaranteed QoS is the chart contract") + t.Error("requests and limits diverged — BuildEnvSpec writes them equal so a" + + " CPU training pod is Guaranteed. (Not \"the chart contract\", as this" + + " message said until backend#2872: the chart's derive path writes no cpu" + + " limit at all since backend#2418.)") } } diff --git a/internal/resources/resources.go b/internal/resources/resources.go index b0f3e6b..e54f9d6 100644 --- a/internal/resources/resources.go +++ b/internal/resources/resources.go @@ -8,8 +8,14 @@ // Grounding (verified against tracebloc/client + client-runtime, 2026-07): // - The machine's capacity is the sum of Ready nodes' Status.Allocatable // (the installer path is single-node, so this is normally one node). -// - A training run's ceiling is the jobs-manager env RESOURCE_LIMITS -// ("cpu=2,memory=8Gi", requests==limits for Guaranteed QoS). This is the +// - A training run's ceiling is the jobs-manager env RESOURCE_LIMITS. This +// package writes it equal to RESOURCE_REQUESTS on both dimensions, so a CPU +// training pod comes out Guaranteed. Two caveats this comment used to elide +// (backend#2872): the built-in fallback is the contract floor +// cpu=1,memory=2Gi since backend#2254, not the "cpu=2,memory=8Gi" named +// here before; and a GPU pod is BestEffort whatever this writes, because +// client-runtime's GPU path sets only nvidia.com/gpu and ephemeral-storage +// and neither counts toward QoS (backend#2871). This is the // exact value client-runtime's jobs_manager.py stamps on spawned jobs and // the same value `cluster doctor`'s checkNodeFit already parses — so the two // read it identically (di#358 lesson: a reader must mirror the writer). @@ -135,7 +141,9 @@ func MachineCapacity(nodes []corev1.Node) Machine { // ParseTraining reads the per-run ceiling from a jobs-manager env map. It // prefers RESOURCE_LIMITS (the true ceiling) and falls back to RESOURCE_REQUESTS -// (requests==limits by chart contract), then to the chart default so an older +// (which this package writes equal to the limits; NOT a chart-wide contract -- +// the chart's derive path writes no cpu limit at all since backend#2418, +// backend#2872), then to the chart default so an older // chart without the literal env still reports the effective size. GPU is read // from GPU_LIMITS, then GPU_REQUESTS. func ParseTraining(env map[string]string) Training { diff --git a/internal/resources/set.go b/internal/resources/set.go index bd63545..9dd8095 100644 --- a/internal/resources/set.go +++ b/internal/resources/set.go @@ -194,8 +194,15 @@ func MemFloorText() string { return "2 GiB" } const NoGPUEnvValue = "" // BuildEnvSpec renders a per-run ceiling into the exact chart env the run is -// stamped with. RESOURCE_REQUESTS == RESOURCE_LIMITS (Guaranteed QoS, the chart -// contract), both "cpu=X,memory=Y". +// stamped with. RESOURCE_REQUESTS == RESOURCE_LIMITS, both "cpu=X,memory=Y" -- +// which yields a Guaranteed CPU training pod. +// +// NOT "the chart contract", as this said until backend#2872. The chart's own +// DERIVE path (DERIVE_JOB_ENVELOPE) writes no cpu limit at all since +// backend#2418, so requests == limits is this package's choice for the explicit +// envelope, not a chart-wide invariant -- and calling it a contract is what let +// the claim survive after #2418 falsified it elsewhere. A GPU pod is BestEffort +// regardless of what is written here (backend#2871). // // Every dimension is ALWAYS written — never omitted. The apply uses // `helm upgrade --reset-then-reuse-values`, which re-applies the release's From 52fefba8543d90a5842e63ed40227534504f33f5 Mon Sep 17 00:00:00 2001 From: Lukas Wuttke Date: Mon, 31 Aug 2026 12:06:27 +0200 Subject: [PATCH 2/2] =?UTF-8?q?chore(version):=200.10.18=20=E2=80=94=20v0.?= =?UTF-8?q?10.17=20is=20released=20and=20this=20PR=20changes=20published?= =?UTF-8?q?=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index f00339d..3971e7e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.10.17 +0.10.18