Skip to content

fix(evidence): clarify operator SKIP, pin Trainer namespace split - #2269

Merged
yuanchen8911 merged 2 commits into
NVIDIA:mainfrom
yuanchen8911:fix/kubeflow-trainer-namespace-alignment
Aug 20, 2026
Merged

yuanchen8911 merged 2 commits into
NVIDIA:mainfrom
yuanchen8911:fix/kubeflow-trainer-namespace-alignment

Conversation

@yuanchen8911

@yuanchen8911 yuanchen8911 commented Aug 19, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Corrects the Section 6 SKIP wording in the CNCF evidence collector so it says which namespaces were probed and why a validator self-installed Kubeflow Trainer is not counted, and records on both sides why the recipe's kubeflow namespace and the performance validator's kubeflow-system namespace must stay different.

Motivation / Context

Section 6 (Robust AI Operator) emitted:

Result: SKIP (prerequisite absent) — no supported Dynamo, NIM, or Kubeflow Trainer operator is installed.

On a cluster where the performance validator's temporary Trainer self-install was live in kubeflow-system, that reads as a false negative — a Trainer was plainly running and healthy at collection time.

The probe is correct; only the wording was wrong. Section 6 attests what the recipe/bundle deployed. The validator's self-install is scaffolding for the NCCL benchmark and is torn down at the end of the run, so counting it would make signed conformance evidence claim an operator the bundle never shipped. A SKIP in that situation is the right answer, stated badly.

Fixes: #2223
Related: #2133, #2222

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Refactoring (no functional changes)
  • Build/CI/tooling

Component(s) Affected

  • CLI (cmd/aicr, pkg/cli)
  • API server (cmd/aicrd, pkg/server)
  • Recipe engine / data (pkg/recipe)
  • Bundlers (pkg/bundler, pkg/component/*)
  • Collectors / snapshotter (pkg/collector, pkg/snapshotter)
  • Validator (pkg/validator) — validators/performance (comment only)
  • Core libraries (pkg/errors, pkg/k8s)
  • Docs/examples (docs/, examples/)
  • Other: pkg/evidence/cncf (CNCF evidence collector)

Implementation Notes

What changed. The emitted SKIP line now names the namespace each operator was looked for in (dynamo-system, nvidia-nim, kubeflow) and states that a Trainer self-installed into kubeflow-system by the performance validator is deliberately not counted. The detection block and trainerNamespace both carry a comment explaining the split. No detection logic changed.

Why not align the two namespaces. The issue as originally filed also considered moving the validator's self-install onto the chart's kubeflow namespace for consistency. That divergence turns out to be load-bearing, and collapsing it breaks two things:

  • The evidence collector would over-claim. Namespace is the only signal separating "the bundle deployed Kubeflow Trainer" from "the validator installed one to run NCCL." Sharing a namespace makes the temporary install collectable as recipe-deployed, converting today's conservative SKIP into a false PASS in a signed artifact — the strictly worse direction, and it defeats the fix in this PR.
  • The conflict guard would go dead. ensureTrainerInstalled (trainer_lifecycle.go:443) compares the discovered installation's namespace against trainerNamespace to refuse installing over a Trainer it does not own. With one namespace that comparison is always false, so an incomplete recipe install would fall through to applyTrainerResources, whose IsAlreadyExists branch calls updateExistingTrainerResource and overwrites the recipe's Helm-managed Deployment/Service/ConfigMap in place with the vendored upstream kustomize spec. Those objects are not added to the rollback set, so nothing restores them after cleanup.

Documenting the invariant on both sides is what stops it being tidied away later.

Testing

Scoped checks rather than a full green make qualify locally, because the diff is comments plus one emitted message string — no logic, no exported surface, no registry/chart/values change:

go build ./validators/...                                         # ok
go test -race ./validators/performance/... ./pkg/evidence/...     # ok (all packages)
golangci-lint run -c .golangci.yaml ./validators/performance/...  # 0 issues
bash -n pkg/evidence/cncf/scripts/collect-evidence.sh             # syntax ok
shellcheck -S warning pkg/evidence/cncf/scripts/collect-evidence.sh

shellcheck reports two warnings, both pre-existing and untouched by this diff: SC2034 at line 37 (DEPLOY_TIMEOUT unused) and SC2069 at line 2341 (redirection order). No new findings.

On the local make qualify run. It aborted at lint-go reporting 87 issues, none of them in this branch — every path resolved into unrelated sibling worktrees on the same machine. Re-running the identical full-module lint with an isolated GOCACHE and GOLANGCI_LINT_CACHE returns 0 issues, confirming those diagnostics were replayed from a shared build cache rather than produced by this code. The linter is v2.12.2, matching the .settings.yaml pin. Because the abort happened at the lint stage, the later local stages (tuning-check, e2e, scan, license-check, api-diff) did not run — CI covers them, and the full Merge Gate is green.

CI on 338d5085f: 29 checks pass, 0 fail. That includes tests / Lint, tests / Test, tests / E2E, tests / CLI E2E, tests / Security Scan, analyze, grype, malware-scan, and the three GPU lanes (H100 training, H100 inference, L40G smoke). An earlier attempt of tests / Lint was cancelled after hanging ~9 minutes on an Ubuntu apt mirror before golangci-lint started; it passed on re-run.

Coverage: no logic changed, and validators/ is excluded from the coverage floor per .settings.yaml.

Risk Assessment

  • Low — Isolated change, well-tested, easy to revert
  • Medium — Touches multiple components or has broader impact
  • High — Breaking change, affects critical paths, or complex rollout

Rollout notes: No behavior change in operator detection, so previously-collected evidence stays valid. Only the SKIP sentence differs in newly collected artifacts. Committed evidence under docs/conformance/ is a historical record of real runs and is deliberately left untouched.

Checklist

  • Tests pass locally (make test with -race)
  • Linter passes (make lint)
  • I did not skip/disable tests to make CI green
  • I added/updated tests for new functionality — N/A, no behavior change; no test asserts the SKIP string
  • I updated docs if user-facing behavior changed — N/A
  • Changes follow existing patterns in the codebase
  • Commits are cryptographically signed (git commit -S)
@yuanchen8911 yuanchen8911 added theme/validation Constraint evaluation, health checks, and conformance evidence area/validator labels Aug 19, 2026
@coderabbitai

coderabbitai Bot commented Aug 19, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 3e1319ca-6087-4f70-8a0e-6c360454ab27

📥 Commits

Reviewing files that changed from the base of the PR and between 338d508 and 0e59cdc.

📒 Files selected for processing (1)
  • pkg/evidence/cncf/scripts/collect-evidence.sh

Included review availability: Your plan provides up to 12 included reviews per hour; 10 remain after this review.


📝 Walkthrough

Walkthrough

The change documents recipe-scoped operator detection for CNCF evidence collection. It clarifies that temporary Kubeflow Trainer installations in kubeflow-system are excluded. It updates the skipped-result message with the checked namespaces. Validator documentation explains the intentional namespace difference and conflict detection behavior.

Estimated code review effort: 1 (Trivial) | ~2 minutes

Merge Risk: ⚪ Minimal · up to 0e59c

This localized change clarifies operator evidence wording and documents an existing namespace boundary without changing detection behavior; no actionable merge-blocking risk remains after normal checks and review.

Suggested labels: area/validator

Suggested reviewers: mchmarny

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the evidence SKIP clarification and Trainer namespace separation.
Description check ✅ Passed The description directly explains the evidence message update, namespace invariant, rationale, testing, and scope.
Linked Issues check ✅ Passed The changes satisfy issue #2223 by clarifying probed namespaces, excluding validator installs, and documenting the required namespace split.
Out of Scope Changes check ✅ Passed The changes remain within the linked issue scope and only update the evidence message and related namespace documentation.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

Recipe evidence check

No leaf overlays affected by this PR.

This gate is warning-only and never blocks merge.

@yuanchen8911
yuanchen8911 marked this pull request as ready for review August 19, 2026 17:23
@yuanchen8911
yuanchen8911 requested a review from a team as a code owner August 19, 2026 17:23
njhensley
njhensley previously approved these changes Aug 20, 2026

@njhensley njhensley left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Review — multi-persona + adversarial meta-review

Method: 3 independent persona reviewers (Domain/evidence-integrity, Correctness, Docs) fanned out in parallel, then every claim re-derived from the resolved code in an adversarial meta-review pass. Pinned to head 338d5085.

Legend: 🔴 Blocker · 🟠 Major · 🟡 Minor · 🔵 Nitpick

Overall: Approve ✅

Comment-only change plus one reworded SKIP string — no logic, no exported surface, no registry/chart/values change. I verified every factual claim in the new comments against the code and all three personas converged: the change is accurate, correctly scoped, and a genuine net-positive. It tightens the accuracy of a signed conformance artifact and fails in the safe direction.

The deepest claim holds: if trainerNamespace were shared with the recipe's kubeflow, the conflict guard at trainer_lifecycle.go:458 (live.Namespace != trainerNamespace) goes false for an incomplete install, which falls through to applyTrainerResources → updateExistingTrainerResource, overwriting the recipe's Helm-managed Deployment/Service/ConfigMap without adding them to the rollback set. The comment is correctly scoped to those non-admission kinds — not overstated.

Confirmed non-issues (examined, cleared)

  • SKIP namespace enumeration (dynamo-system, nvidia-nim, kubeflow) is complete and matches each probe target + registry defaultNamespace.
  • Shell safety: all backticks are \``-escaped in the double-quoted echo` — no command substitution or word-splitting.
  • No Go test and no committed doc asserts the old SKIP sentence, so the rewrite regresses nothing.
  • Committed docs/conformance/ evidence deliberately untouched is correct — those are real PASS records (e.g. trainer-eks-gb200 shows a legitimately recipe-deployed Trainer in kubeflow); nothing contradicts the new wording.
  • "Signed conformance evidence" framing is accurate — pkg/evidence/attestation attests the evidence tree.
  • Cross-references (ensureTrainerInstalled, applyTrainerResources, updateExistingTrainerResource, registry.yaml, #2223) all resolve; the .sh and .go comments are consistent. Comments cite paths/function names (not line numbers) so they won't rot.
  • Fully implements issue #2223's requested scope.

Summary

🔴 Blocker 🟠 Major 🟡 Minor 🔵 Nitpick
0 0 0 1

The one nitpick below is cosmetic and optional.

write_section_header "Robust AI Operator"
echo "**Result: SKIP (prerequisite absent)** — no supported Dynamo, NIM, or Kubeflow Trainer operator is installed." >> "${EVIDENCE_FILE}"
log_info "Robust operator evidence collection skipped — no supported operator found."
echo "**Result: SKIP (prerequisite absent)** — the recipe/bundle deployed no supported AI operator: no Dynamo operator in \`dynamo-system\`, no NIM operator in \`nvidia-nim\`, and no Kubeflow Trainer in \`kubeflow\`. A Kubeflow Trainer that the performance validator self-installed into \`kubeflow-system\` for the NCCL benchmark is deliberately not counted here — it is torn down at the end of the run and is not part of the deployed bundle." >> "${EVIDENCE_FILE}"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🔵 Nitpick — SKIP message is one long ~90-word line

The emitted Result: SKIP string packs the full rationale (three probed namespaces + the kubeflow-system self-install carve-out) into a single ~90-word paragraph in robust-operator.md. Renders fine as Markdown and an auditor benefits from the inline "why", so this is arguably intentional.

Blast radius: Cosmetic only.

Fix: Optional: split the "deliberately not counted…" clause into a second sentence. No change required.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Taken in 0e59cdc. The carve-out is now emitted as its own paragraph rather than trailing the verdict on one line, so the SKIP result reads on its own and the reason a live kubeflow-system Trainer is not counted sits beside it instead of at the end of a ~90-word block.

Wording is unchanged apart from one em-dash becoming a colon, since the second sentence now stands alone.

You were right that it was intentional — the inline rationale is the point, an auditor seeing a running Trainer needs it. It just should not have been one paragraph. Re-requesting since the approval was dismissed by the push.

@njhensley njhensley left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Re-review of `0e59cdce` — delta approval.

My prior 🔵 nitpick (SKIP verdict + carve-out packed on one ~90-word line) is ✔️ addressed: 0e59cdce splits it into two echos so the verdict and the kubeflow-system self-install carve-out render as separate Markdown paragraphs in robust-operator.md. Verified on the resolved code — renders as two paragraphs, backticks stay escaped inside the double-quoted echo, nothing emitted after before the function returns.

No new findings; the delta is a 3-line cosmetic split — no logic, no exported surface, no change to signed-evidence semantics. LGTM. Approve.

Section 6 emitted "no supported Dynamo, NIM, or Kubeflow Trainer operator
is installed" whenever the recipe deployed none of them. On a cluster where
the performance validator's temporary Kubeflow Trainer self-install was live
in kubeflow-system, that reads as a false negative: a Trainer was plainly
running and healthy at collection time.

The probe itself is correct. Section 6 attests what the recipe/bundle
deployed, and the validator's self-install is scaffolding for the NCCL
benchmark that is torn down at the end of the run, so it must not be
collected. Only the wording was wrong. State which namespace each operator
was looked for in, and say outright that a self-installed Trainer in
kubeflow-system is deliberately not counted.

Record why the two namespaces differ on both sides, since the divergence
looks like drift and reads as the obvious thing to tidy away. Aligning them
would break the collector (a temporary install would be collected as
recipe-deployed, so signed evidence would claim an operator the bundle never
shipped) and disarm the conflict guard in ensureTrainerInstalled, which
compares against trainerNamespace to refuse installing over a Trainer this
validator does not own; with one namespace that comparison is always false
and applyTrainerResources overwrites the recipe's Helm-managed objects in
place, with nothing restoring them afterwards.

Refs NVIDIA#2223

Signed-off-by: Yuan Chen <yuanchen97@gmail.com>
The SKIP line emitted the verdict and the self-install carve-out as a single
Markdown line, so robust-operator.md rendered them as one ~90-word paragraph.
Emit the carve-out as a second paragraph, so the verdict reads on its own and
the reason a live kubeflow-system Trainer is not counted sits beside it rather
than buried at the end of a block.

Wording is unchanged apart from an em-dash to a colon, avoiding two dashes in
what is now a short standalone sentence.

Refs NVIDIA#2223

Signed-off-by: Yuan Chen <yuanchen97@gmail.com>
@yuanchen8911
yuanchen8911 force-pushed the fix/kubeflow-trainer-namespace-alignment branch from 0e59cdc to 6cd76f8 Compare August 20, 2026 18:05
@yuanchen8911

Copy link
Copy Markdown
Contributor Author

@njhensley rebased onto main — 0e59cdce8 → 6cd76f87a. Force-push only, no content change: git diff main...HEAD is byte-identical before and after (same blob hash), just the two commits replayed onto current main.

The repo requires branches to be up to date, so this was needed to merge, and it dismissed your approval as a side effect. Nothing to re-read — would you mind a quick re-approve once CI settles?

@yuanchen8911
yuanchen8911 requested a review from njhensley August 20, 2026 18:05
@yuanchen8911
yuanchen8911 merged commit 21b538d into NVIDIA:main Aug 20, 2026
43 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/S theme/validation Constraint evaluation, health checks, and conformance evidence

2 participants