Skip to content

validate: fail-closed GKE device-plugin ownership check - #2000

Merged
yuanchen8911 merged 1 commit into
NVIDIA:mainfrom
yuanchen8911:validate/gke-device-plugin-readiness
Aug 4, 2026
Merged

validate: fail-closed GKE device-plugin ownership check#2000
yuanchen8911 merged 1 commit into
NVIDIA:mainfrom
yuanchen8911:validate/gke-device-plugin-readiness

Conversation

@yuanchen8911

@yuanchen8911 yuanchen8911 commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds the fail-closed GKE device-plugin ownership check from #1755: a new node-set constraint form (NodeTopology.gpu-nodes.label) evaluated at aicr validate readiness, wired into the GKE-COS overlays so an unlabeled GPU node pool fails validation closed (exit 2) with an actionable diagnostic before any phase runs.

Motivation / Context

AICR's GKE recipes ship the GPU Operator with devicePlugin.enabled: true, while a default-provisioned GKE GPU node pool also runs GKE's managed device plugin — two advertisers of nvidia.com/gpu per node, with nondeterministic ownership. Until now nothing detected the conflict: aicr bundle is offline, and the generic constraint syntax compares scalar readings, so "every GPU node carries gke-no-default-nvidia-gpu-device-plugin=true" was inexpressible. PR #1998 documented the prerequisite; this PR enforces it.

Scope position (ADR-015 Deferred Decision 2 — marked resolved in the ADR by this PR). The reusable node-set constraint form — including the negated direction — lands here under #1755, exactly as the ADR reads it. The follow-up GKE gpuStack profile recipes (#1761 adoption step) will consume the form unchanged when they land: operator uses the positive predicate, csp-managed the negated one. Nothing in this PR touches the profile mechanism, the Constraint schema, the OpenAPI propertyNames allowlists, or the v1alpha2/v1alpha3 version gate.

Merge order / dependency: resolved — #1998 merged first (2026-08-04) and this PR is rebased onto that main. The component-catalog paragraph now describes the readiness gate (NodeTopology.gpu-nodes.label, fail-closed, exit 2) instead of claiming no deterministic check exists, closing the loop the readiness remediation text points into.

Review-round fixes folded in at head: deprecated apimachinery label validators replaced with content.IsLabelKey/content.IsLabelValue (the vendored v0.36.3 marks the old aliases deprecated); a ValidatePhases readiness-preflight test pinning the default client path fail-closed (the plural entry point previously had zero coverage on that branch); an encode→decode round-trip test running real topology.Collector output (plain, disambiguated, truncated shapes) through the evaluator so the cross-package wire format cannot silently diverge before #2003; parse-validation table rows for a valid key with an invalid label value; key= (empty value) accepted as a valid positive form — Kubernetes permits empty label values, the collector already encodes them (|<nodes>), and the disambiguated map key <key>. cannot collide with a real label key (keys may not end in a dot) — pinned by table rows for the uniform and mixed shapes plus a collector round-trip leg; and a comment documenting checkReadiness's deliberate error-code flattening.

Fixes: #1755
Related: #1761, #1998 (merge first — see above)

Type of Change

  • New feature (non-breaking change that adds functionality)
  • Bug fix (non-breaking change that fixes an issue)
  • 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) — exported truncation predicate only
  • Validator (pkg/validator)
  • Core libraries (pkg/errors, pkg/k8s)
  • Docs/examples (docs/, examples/)
  • Other: recipes/overlays/gke-cos.yaml, tests/uat/gcp/cluster-config.yaml, pkg/constraints

Implementation Notes

The form is name-dispatched, not schema-extended. constraints.Evaluate dispatches on the exact name NodeTopology.gpu-nodes.label before the scalar path (precedent: the nccl-benchmark-* sentinel names). The constraint stays name + value, so both existing call sites (readiness, overlay/profile evaluation) get the form with no API or artifact-version change.

Value grammar is <label-key>=<value> (every GPU node carries it) or !<label-key> (no GPU node carries the key), validated with the vendored Kubernetes label validators (content.IsLabelKey/content.IsLabelValue) — so the scalar operator grammar (>= x, != x) cannot be misread as a key/value predicate, and a key no node can legally carry (e.g. a double slash) is rejected instead of making the negated predicate pass vacuously.

GPU-node universe = nodes under the snapshot's cloud.google.com/gke-accelerator* label readings — GKE's native signal, present from pool creation before the Operator or NFD run (NFD's nvidia.com/gpu.* labels don't exist on the pre-deployment cluster this check validates). Per the issue's acceptance requirements, both predicate directions fail closed on: truncated node lists (--max-nodes-per-entry snapshots), an empty universe (no vacuous pass), missing NodeTopology.label readings, and mixed/disambiguated label values (key.value encoding). Disambiguated-shape decoding enforces encodeLabels' invariants — plain and disambiguated forms never coexist, and genuine disambiguation always yields at least two entries — so a distinct dotted label whose value equals its own suffix (e.g. a literal <key>.true=true label) cannot satisfy the predicate; an accepted disambiguated set must additionally partition its nodes — overlapping node sets prove the encoder's <key>.<value> map key collided with a real label of that literal name (one reading silently overwritten, #2003) and fail closed as an ambiguous reading. A single disambiguated-shape entry without the plain key (a possible collision remnant) and structurally malformed readings (missing or extra | separators, empty node lists, node tokens that are not canonical RFC-1123 node names) also fail closed — both would otherwise let the negated predicate pass vacuously. The residual ambiguity (collisions with identical node sets, or every disambiguated entry overwritten) is what the lossy encoding cannot express; the lossless-encoding root-cause fix is tracked in #2003, structured truncation metadata in #2002. The truncation detector is owned by the collector (topology.IsTruncatedNodeList, next to formatNodeList, with a round-trip test) so the format and its detector cannot drift apart.

Both validator entry points are gated. checkReadiness runs in ValidatePhases and in the per-phase ValidatePhase (the exported SDK path Client.ValidateState documentation directs per-phase callers to) — a single-phase caller cannot bypass the recipe's readiness constraints, and declared readiness constraints with a nil snapshot fail closed instead of silently skipping. Both pinned by tests.

Wiring: readiness, not spec.constraints. A top-level constraint would exclude the GKE overlays during snapshot-based generation on exactly the unlabeled cluster the diagnostic exists to fix. The check lives in the GKE-COS base overlay's validation.readiness.constraints; checkReadiness now evaluates readiness-phase constraints alongside the top-level set (they were declared, merged, and carried into ValidationInput but never consumed) and appends the constraint's remediation to the failure message — that's where the issue's required device-plugin diagnostic lives.

UAT cluster config. The GCP UAT GPU pool (tests/uat/gcp/cluster-config.yaml) predates the documented prerequisite and does not carry the label; without fixing it this PR would fail the GKE UAT lane. The label is added to the pool's nodeConfig.labels. Note the config actuates at cluster provision time — the nightly lifecycle (provision→CUJ→teardown) picks it up on the next run; a held daytime cluster would need re-provisioning or a manual gcloud container node-pools update.

Sequencing with #1998. #1998 merges first; this PR rebases on it and refreshes the component-catalog paragraph (added there) that states "AICR has no deterministic check for a violation today" — kept out of this PR until the rebase to avoid cross-PR conflicts.

Testing

make qualify   # PASS (exit 0) through the collision-guard revision; on the final head the local run hit
               # pre-existing tests/releasepolicy deadline timeouts (reproduced identically on clean
               # origin/main — machine-local flake); affected packages re-verified with -race and CI is green
GOFLAGS="-mod=vendor" go test -race ./pkg/constraints/... ./pkg/validator/ ./pkg/recipe/...   # PASS
golangci-lint run -c .golangci.yaml ./pkg/constraints/... ./pkg/validator/...   # 0 issues

Coverage (per-package, current vs origin/main baseline):

  • pkg/constraints: 97.9% → 97.9% (flat)
  • pkg/validator: 48.5% → 50.9% (+2.4%)
  • pkg/collector/topology: 84.2% → 84.4% (+0.2%)

CLI end-to-end (branch-built binary, no cluster): generated the GKE H100 training recipe with aicr recipe — the hydrated artifact carries the readiness constraint with its remediation — then ran aicr validate --no-cluster against four crafted snapshots: labeled pool → exit 0 ("all 2 GPU node(s) carry …"); unlabeled pool → exit 2 naming the offending nodes plus the full device-plugin remediation; truncated snapshot → exit 2 with the regenerate-without---max-nodes-per-entry instruction; no GPU nodes → exit 2 with the empty-universe diagnostic. Live-cluster validation deliberately skipped: the gate is inline (no cluster I/O), and the first post-merge nightly UAT provisions from this PR's cluster-config and runs aicr validate through the gate, providing the live confirmation automatically.

New tests: table-driven coverage of both predicate directions × {pass, fail, mixed values via disambiguated keys, multiple accelerator types, non-GPU nodes carrying the label, truncated universe reading, truncated target reading (both directions), empty universe (both directions), missing label subtype/measurement/nil snapshot, prefix-collision non-misattribution, scalar-grammar rejection, malformed values}; validator-level test that readiness-phase constraints are evaluated and carry remediation into the failure.

New exported symbols: the GPUNodesLabelConstraintName const and topology.IsTruncatedNodeList (covered by the round-trip test). make qualify was run unsandboxed after verifying the sandbox-only failures (cleanup_test.sh stubs, httptest port binds) reproduce identically on a clean origin/main checkout.

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: A pre-change aicr binary evaluates only top-level constraints and silently ignores validation.readiness.constraints in a recipe generated at this commit (version skew is advisory-only) — validate recipes with a matching or newer binary. aicr validate against a GKE cluster whose GPU pools lack gke-no-default-nvidia-gpu-device-plugin=true now fails readiness closed (exit 2) — intended and documented, but visible to existing users of unlabeled clusters; the failure message carries the exact remediation. Snapshots captured with --max-nodes-per-entry also fail this constraint closed with a regenerate instruction. Recipe generation and bundling are unaffected. The UAT GKE pool config is updated in this PR; held daytime UAT clusters need one re-provision (or a manual pool update) before their next validate run.

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
  • I updated docs if user-facing behavior changed
  • Changes follow existing patterns in the codebase
  • Commits are cryptographically signed (git commit -S) — GPG signing info

@yuanchen8911 yuanchen8911 added the theme/validation Constraint evaluation, health checks, and conformance evidence label Aug 3, 2026
@yuanchen8911 yuanchen8911 changed the title validate: fail-closed GKE device-plugin ownership readiness check WIP: validate: fail-closed GKE device-plugin ownership check Aug 3, 2026
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Recipe evidence check

Protected recipes

Recipes with committed evidence (recipes/evidence/<slug>/<source>/<digest>.yaml) that this PR affects: 1

Recipe Source Pointer Verify Digest match
h100-gke-cos-training 7c4c0edc8c765a95a0f3afdb3bbb8e91 sha256-be4680f26ad9ebeb57145f1953f18311ca00e81a4edb37773e0ec1060c6bd261 ❌ invalid — registry-forbidden (HTTP 401): registry not accessible (make the fork's aicr-evidence package public, or provide registry credentials) ⚠️ skipped (no signed digest)
h100-gke-cos-training 7c4c0edc8c765a95a0f3afdb3bbb8e91 sha256-f2573e7f2496cc895e6a780604645f7c24ed4d7e0edf4c4845c0d341a3a6326e ❌ invalid — registry-forbidden (HTTP 401): registry not accessible (make the fork's aicr-evidence package public, or provide registry credentials) ⚠️ skipped (no signed digest)
Other affected recipes without evidence yet: 10

These recipes are affected by this PR but carry no committed evidence pointer, so there is
nothing to verify. This is expected — evidence is hardware-gated and added over time.

  • a100-gke-cos-training-kubeflow
  • a100-gke-cos-training
  • b200-gke-cos-inference-dynamo
  • b200-gke-cos-inference
  • b200-gke-cos-training-kubeflow
  • b200-gke-cos-training
  • h100-gke-cos-inference-dynamo
  • h100-gke-cos-inference
  • h100-gke-cos-training-kubeflow
  • h100-gke-cos-training-slurm

How to refresh evidence

Run on a cluster matching the recipe's criteria:

aicr snapshot -o snapshot.yaml
# Profiled families (AKS gpuStack): capture the pool projection and
# hydrate the recipe with the pointer's recorded 'profile:' selection
# first — validating the raw overlay resolves only the declaration
# default, and 'aicr validate' has no --profile flag:
#   az aks nodepool list -g <rg> --cluster-name <cluster> -o json > pools.json
#   aicr snapshot --aks-gpu-pools pools.json -o snapshot.yaml
#   aicr recipe -s snapshot.yaml --intent <intent> [--platform <platform>] \
#     --profile <name>=<value> -o recipe.yaml
# State the target leaf's intent/platform explicitly (the snapshot
# fingerprint supplies service/accelerator/OS but intent and platform
# default to 'any') and pass -r recipe.yaml below instead of the raw
# overlay.
aicr validate \
  -r recipes/overlays/<slug>.yaml \
  -s snapshot.yaml \
  --emit-attestation ./out \
  --push ghcr.io/<your-fork>/aicr-evidence
# Copy to the per-source path printed in the emit 'copyTo' hint:
#   recipes/evidence/<slug>/<source>/<bundle-digest>.yaml

This gate is warning-only and never blocks merge. See ADR-007 for the trust model.

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds the NodeTopology.gpu-nodes.label constraint with positive and negated predicates over GKE GPU nodes. The evaluator fails closed for missing, empty, mixed, or truncated node data. Readiness validation now evaluates readiness-specific constraints and includes remediation text in failures. The GKE-COS recipe and UAT configuration add the required device-plugin opt-out label. Documentation and tests cover the new constraint and readiness behavior.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested reviewers: arangogutierrez

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: a fail-closed GKE device-plugin ownership check.
Description check ✅ Passed The description directly explains the new constraint, readiness validation behavior, affected configurations, testing, and rollout impact.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/validator/validator_test.go`:
- Around line 355-410: Extend
TestCheckReadinessEvaluatesReadinessPhaseConstraints with a recipe containing
both a top-level Validation.Constraints entry and a
Validation.Readiness.Constraints entry; make one or both fail to verify both are
evaluated, confirm the resulting error includes the relevant remediation, and
snapshot validationInput.Constraints before checkReadiness to assert it remains
unchanged afterward, preserving the combined ordering and non-aliasing behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 508c292a-df5b-4790-8e79-48253edb795d

📥 Commits

Reviewing files that changed from the base of the PR and between 9c5062b and 7bac947.

📒 Files selected for processing (10)
  • docs/integrator/recipe-development.md
  • docs/user/validation.md
  • pkg/constraints/doc.go
  • pkg/constraints/evaluate.go
  • pkg/constraints/gpu_nodes.go
  • pkg/constraints/gpu_nodes_test.go
  • pkg/validator/validator.go
  • pkg/validator/validator_test.go
  • recipes/overlays/gke-cos.yaml
  • tests/uat/gcp/cluster-config.yaml

Comment thread pkg/validator/validator_test.go
@yuanchen8911
yuanchen8911 force-pushed the validate/gke-device-plugin-readiness branch from 7bac947 to f6cab51 Compare August 3, 2026 21:47

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/collector/topology/topology_test.go`:
- Around line 465-481: Refactor TestIsTruncatedNodeListRoundTrip into a
table-driven test containing the truncated, full, and exact-limit cases with
their expected results. Iterate over the cases using subtests, while preserving
the existing formatNodeList inputs and IsTruncatedNodeList assertions.

In `@pkg/constraints/gpu_nodes.go`:
- Line 22: Replace the deprecated validation import and validator calls in the
GPU node constraint logic with content.IsQualifiedName and content.IsLabelValue.
Remove the k8s.io/apimachinery/pkg/util/validation dependency while preserving
the existing validation behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 7863614d-d4f6-425b-b95e-81ca870595f2

📥 Commits

Reviewing files that changed from the base of the PR and between 7bac947 and f6cab51.

📒 Files selected for processing (14)
  • docs/contributor/validator.md
  • docs/integrator/recipe-development.md
  • docs/user/validation.md
  • pkg/collector/topology/topology.go
  • pkg/collector/topology/topology_test.go
  • pkg/constraints/doc.go
  • pkg/constraints/evaluate.go
  • pkg/constraints/gpu_nodes.go
  • pkg/constraints/gpu_nodes_test.go
  • pkg/validator/doc.go
  • pkg/validator/validator.go
  • pkg/validator/validator_test.go
  • recipes/overlays/gke-cos.yaml
  • tests/uat/gcp/cluster-config.yaml

Comment thread pkg/collector/topology/topology_test.go
Comment thread pkg/constraints/gpu_nodes.go

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/constraints/gpu_nodes_test.go`:
- Around line 397-409: Refactor TestSummarizeNodesCapsList into a table-driven
test containing both the capped seven-node case and the uncapped single-node
case, then iterate through the table with named subtests while preserving the
existing expected outputs.

In `@pkg/constraints/gpu_nodes.go`:
- Around line 204-208: Update the topology label decoding around
cutLabelEncoding so readings missing the separator, with an empty node list, or
containing empty node members are rejected as malformed validation data.
Propagate the decoding error through evaluateNoGPUNodeHasKey so both positive
and negated checks fail closed instead of producing an empty labelNodeSet. Add
regression tests covering positive and negated evaluations for each malformed
form.
- Around line 134-140: Update the node-set constraint parsing validation around
strings.Cut to remove the want == "" rejection, allowing key= values to be
validated by validation.IsValidLabelValue. Add a test case covering a valid
empty label value and preserve rejection of malformed inputs through the
existing validation flow.

In `@pkg/validator/validator.go`:
- Around line 80-82: Update the remediation handling in the validator
error-message construction to trim c.Remediation before checking whether it is
non-empty, then append the trimmed value only when it contains content. Preserve
the existing newline formatting for valid remediation text, and add a test
covering whitespace-only Remediation to ensure it does not append an extra
newline.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: de16349f-e220-4240-ab15-89216983aa1d

📥 Commits

Reviewing files that changed from the base of the PR and between f6cab51 and a663fdd.

📒 Files selected for processing (15)
  • docs/contributor/validator.md
  • docs/integrator/recipe-development.md
  • docs/user/validation.md
  • pkg/collector/topology/topology.go
  • pkg/collector/topology/topology_test.go
  • pkg/constraints/doc.go
  • pkg/constraints/evaluate.go
  • pkg/constraints/extractor.go
  • pkg/constraints/gpu_nodes.go
  • pkg/constraints/gpu_nodes_test.go
  • pkg/validator/doc.go
  • pkg/validator/validator.go
  • pkg/validator/validator_test.go
  • recipes/overlays/gke-cos.yaml
  • tests/uat/gcp/cluster-config.yaml

Comment thread pkg/constraints/gpu_nodes_test.go
Comment thread pkg/constraints/gpu_nodes.go Outdated
Comment thread pkg/constraints/gpu_nodes.go Outdated
Comment thread pkg/validator/validator.go
@yuanchen8911
yuanchen8911 force-pushed the validate/gke-device-plugin-readiness branch from a663fdd to 26e0748 Compare August 3, 2026 23:27

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/contributor/validator.md`:
- Around line 47-53: Update the later pre-flight section describing
checkReadiness to state that it evaluates both top-level validation.constraints
and validation.readiness.constraints, matching the contract documented near the
phase checks. Remove any wording that limits pre-flight evaluation to top-level
constraints only.

In `@pkg/validator/validator_test.go`:
- Around line 416-439: Update the aliasing coverage in the checkReadiness test
around the topLevel setup and post-check assertions: construct topLevel with
spare capacity and initialize its unused tail with a sentinel value, then verify
after checkReadiness that both its length and unused tail remain unchanged. Keep
the existing error assertions and ensure the test would fail if checkReadiness
appends readiness constraints into the aliased backing array.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: a168baec-79c7-4640-a371-4a6cb526e2a1

📥 Commits

Reviewing files that changed from the base of the PR and between a663fdd and 26e0748.

📒 Files selected for processing (15)
  • docs/contributor/validator.md
  • docs/integrator/recipe-development.md
  • docs/user/validation.md
  • pkg/collector/topology/topology.go
  • pkg/collector/topology/topology_test.go
  • pkg/constraints/doc.go
  • pkg/constraints/evaluate.go
  • pkg/constraints/extractor.go
  • pkg/constraints/gpu_nodes.go
  • pkg/constraints/gpu_nodes_test.go
  • pkg/validator/doc.go
  • pkg/validator/validator.go
  • pkg/validator/validator_test.go
  • recipes/overlays/gke-cos.yaml
  • tests/uat/gcp/cluster-config.yaml

Comment thread docs/contributor/validator.md
Comment thread pkg/validator/validator_test.go
@yuanchen8911
yuanchen8911 force-pushed the validate/gke-device-plugin-readiness branch 4 times, most recently from 91c0d71 to 1aede4d Compare August 4, 2026 00:31
@yuanchen8911
yuanchen8911 marked this pull request as ready for review August 4, 2026 00:46
@yuanchen8911
yuanchen8911 requested review from a team as code owners August 4, 2026 00:46
@yuanchen8911 yuanchen8911 changed the title WIP: validate: fail-closed GKE device-plugin ownership check validate: fail-closed GKE device-plugin ownership check Aug 4, 2026
@yuanchen8911
yuanchen8911 merged commit 2075b7b into NVIDIA:main Aug 4, 2026
305 of 308 checks passed
yuanchen8911 added a commit to yuanchen8911/aicr that referenced this pull request Aug 4, 2026
Land the GKE adopter and the profile extensions ADR-015 staged for it
(issue NVIDIA#1761 rollout PR 3), completing the three-PR series after the
generic core (NVIDIA#1933) and the AKS adopter (NVIDIA#1967).

GKE conversion: recipes/overlays/gke-cos.yaml moves to aicr.run/v1alpha3
and declares gpuStack with two values. operator (the default) keeps the
GPU Operator's device plugin as the sole nvidia.com/gpu advertiser and
carries the positive NVIDIA#1755 node-set constraint
(gke-no-default-nvidia-gpu-device-plugin=true on every GPU node);
csp-managed declares advertiser: external, disables the operator's
plugin, and carries the negated constraint — making the previously
unsupported "GKE's managed plugin owns nvidia.com/gpu" state declarable
and verifiable. The overlay's readiness constraint from NVIDIA#2000 relocates
into the profile values, as forecast when it landed; every GKE-COS leaf
inherits the declaration unchanged. operator-selfdriver and the NVIDIA#1716
installer stay deferred on DD5's durable signal.

Canonical NVIDIA#1327 descriptor: the policy path vocabulary that existed as
three private copies (pkg/bundler, pkg/validator/v1, pkg/recipe) moves
to the new dependency-neutral pkg/allocpolicy — one descriptor and one
tuple-coherence evaluator, consumed by profile validation, the hydrating
artifact gate, bundler enforcement, and validation-time policy
resolution. The descriptor is append-only; its deterministic Identity()
feeds evidence currentness.

advertiser "external" activates: the core's three deferral rejections
are replaced with descriptor-backed validation. A profile that owns
advertisement — a declared external advertiser or explicit ownership of
a non-synthetic selector path — triggers the recomputed closure:
RecipeResult.EffectiveLockSet() joins every enabled descriptor
component's selector paths (plus synthetic presence) to the declared
ownedPaths, and the bundle/mirror lock, the argocd-helm render guard,
and the hydrating gate all consume the effective set. The closure is
recomputed at every boundary, never persisted. The hydrating gate runs
the shared coherence rules (external + devicePlugin.enabled=true and
external + DRA gpus.enabled=true reject). Profiles that do not own
advertisement (the AKS shape) keep today's WARN semantics — locks follow
ownership. ResolveGPUAllocationPolicy counts a declared external
advertiser as THE advertiser in the exactly-one invariant; the resolved
policy value stays device-plugin-extended-resource.

Evidence predicate v2: profile-bearing recipes emit
https://aicr.run/recipe-evidence/v2 carrying {selection, advertiser,
policyDescriptorIdentity}; unprofiled evidence stays byte-identical on
v1. Producers, the pointer, all four verifier checkpoints, the
publisher, and testgrid-publish enforce the bidirectional contract, and
the verifier rejects a descriptor-identity mismatch as historical-only.
No committed evidence pointer is profiled yet, so the cut-over
invalidates nothing in-repo.

Fail-closed behavior changes, all per the ADR: GKE compositions now
reject on /v1 endpoints (profile cut-over); snapshot-based GKE
generation requires NodeTopology label readings (an unavailable reading
fails with the distinguishable DD1 diagnostic); a bundlers subset
omitting a closure-locked component (nvidia-dra-driver-gpu on GKE)
fails.

Docs: component-catalog (profile-led GKE ownership section; driver
auto-detect subordination now stated as path-ownership-based),
validation, cli-reference, api-reference (GKE /v1 cut-over),
recipe-development, data-extension, contributor/recipe. GCP UAT asserts
pin v1alpha3 + selectedProfile.

Live-cluster qualification of both GKE values and v2 evidence signing
follow before un-draft.

Refs NVIDIA#1761, NVIDIA#1755, NVIDIA#1716, NVIDIA#1327

Signed-off-by: Yuan Chen <yuanchen97@gmail.com>
yuanchen8911 added a commit to yuanchen8911/aicr that referenced this pull request Aug 4, 2026
…1755 gate

UAT GCP cluster provisioning has been broken since the NVIDIA#1755 readiness
gate and its cluster prerequisite landed (NVIDIA#1998 docs, NVIDIA#2000 enforcement +
the labeled UAT pool): on GKE, the managed driver install
(gpu-driver-version=default) is FINALIZED by an init container
(nvidia-driver-installer) of the same kube-system DaemonSet the
gke-no-default-nvidia-gpu-device-plugin=true label disables — the
DaemonSet's node affinity is NotIn ["true"]. A freshly provisioned
labeled pool therefore comes up with the driver staged on disk but never
installed: no kernel module, no /dev/nvidia*, zero allocatable GPUs, and
the GPU Operator's toolkit loops on driver-validation forever. Proven on
run 30930940652 (the only config change since the last green run,
v0.18.0/30111681059, is the label).

The label's documented contract is device-plugin ownership only; its GKE
implementation excludes the whole managed DaemonSet, driver installer
included. The prerequisite the gate enforced — label + GKE-managed driver
— is therefore unsatisfiable on a fresh pool. Nothing can pass it.

Interim fix, until the ADR-015 gpuStack profile lands (NVIDIA#1761 rollout
PR 3, NVIDIA#2044):

- tests/uat/gcp/cluster-config.yaml: drop the pool label — GKE-default
  provisioning (managed driver + managed plugin), the shape every green
  run actually used.
- recipes/overlays/gke-cos.yaml: withdraw the readiness constraint; it
  fails closed on the unlabeled cluster above. The NVIDIA#1755 node-set
  constraint FORM (pkg/constraints) is untouched and remains available.
- docs/user/component-catalog.md, docs/user/validation.md: correct the
  claim that the label does not affect driver provisioning, document the
  DaemonSet bundling and the standalone nvidia-driver-installer pairing,
  and mark enforcement as temporarily withdrawn.

Known, accepted interim consequence: GKE-default pools run BOTH GKE's
plugin and the operator's plugin as nvidia.com/gpu advertisers — the
documented latent NVIDIA#1755 conflict every pre-NVIDIA#2000 green run also carried.
The gpuStack profile re-lands enforcement symmetrically and coherently:
csp-managed (default) requires the label absent with GKE's plugin as
sole advertiser (advertiser: external); operator requires the label plus
a label-surviving driver path.

Refs NVIDIA#1755, NVIDIA#1761, NVIDIA#2000

Signed-off-by: Yuan Chen <yuanchen97@gmail.com>
yuanchen8911 added a commit to yuanchen8911/aicr that referenced this pull request Aug 4, 2026
…1755 gate

UAT GCP cluster provisioning has been broken since the NVIDIA#1755 readiness
gate and its cluster prerequisite landed (NVIDIA#1998 docs, NVIDIA#2000 enforcement +
the labeled UAT pool): on GKE, the managed driver install
(gpu-driver-version=default) is FINALIZED by an init container
(nvidia-driver-installer) of the same kube-system DaemonSet the
gke-no-default-nvidia-gpu-device-plugin=true label disables — the
DaemonSet's node affinity is NotIn ["true"]. A freshly provisioned
labeled pool therefore comes up with the driver staged on disk but never
installed: no kernel module, no /dev/nvidia*, zero allocatable GPUs, and
the GPU Operator's toolkit loops on driver-validation forever. Proven on
run 30930940652 (the only config change since the last green run,
v0.18.0/30111681059, is the label).

The label's documented contract is device-plugin ownership only; its GKE
implementation excludes the whole managed DaemonSet, driver installer
included. The prerequisite the gate enforced — label + GKE-managed driver
— is therefore unsatisfiable on a fresh pool. Nothing can pass it.

Interim fix, until the ADR-015 gpuStack profile lands (NVIDIA#1761 rollout
PR 3, NVIDIA#2044):

- tests/uat/gcp/cluster-config.yaml: drop the pool label — GKE-default
  provisioning (managed driver + managed plugin), the shape every green
  run actually used.
- recipes/overlays/gke-cos.yaml: withdraw the readiness constraint; it
  fails closed on the unlabeled cluster above. The NVIDIA#1755 node-set
  constraint FORM (pkg/constraints) is untouched and remains available.
- docs/user/component-catalog.md, docs/user/validation.md: correct the
  claim that the label does not affect driver provisioning, document the
  DaemonSet bundling and the standalone nvidia-driver-installer pairing,
  and mark enforcement as temporarily withdrawn.

Known, accepted interim consequence: GKE-default pools run BOTH GKE's
plugin and the operator's plugin as nvidia.com/gpu advertisers — the
documented latent NVIDIA#1755 conflict every pre-NVIDIA#2000 green run also carried.
The gpuStack profile re-lands enforcement symmetrically and coherently:
csp-managed (default) requires the label absent with GKE's plugin as
sole advertiser (advertiser: external); operator requires the label plus
a label-surviving driver path.

Refs NVIDIA#1755, NVIDIA#1761, NVIDIA#2000

Signed-off-by: Yuan Chen <yuanchen97@gmail.com>
yuanchen8911 added a commit to yuanchen8911/aicr that referenced this pull request Aug 4, 2026
…1755 gate

UAT GCP cluster provisioning has been broken since the NVIDIA#1755 readiness
gate and its cluster prerequisite landed (NVIDIA#1998 docs, NVIDIA#2000 enforcement +
the labeled UAT pool): on GKE, the managed driver install
(gpu-driver-version=default) is FINALIZED by an init container
(nvidia-driver-installer) of the same kube-system DaemonSet the
gke-no-default-nvidia-gpu-device-plugin=true label disables — the
DaemonSet's node affinity is NotIn ["true"]. A freshly provisioned
labeled pool therefore comes up with the driver staged on disk but never
installed: no kernel module, no /dev/nvidia*, zero allocatable GPUs, and
the GPU Operator's toolkit loops on driver-validation forever. Proven on
run 30930940652 (the only config change since the last green run,
v0.18.0/30111681059, is the label).

The label's documented contract is device-plugin ownership only; its GKE
implementation excludes the whole managed DaemonSet, driver installer
included. The prerequisite the gate enforced — label + GKE-managed driver
— is therefore unsatisfiable on a fresh pool. Nothing can pass it.

Interim fix, until the ADR-015 gpuStack profile lands (NVIDIA#1761 rollout
PR 3, NVIDIA#2044):

- tests/uat/gcp/cluster-config.yaml: drop the pool label — GKE-default
  provisioning (managed driver + managed plugin), the shape every green
  run actually used.
- recipes/overlays/gke-cos.yaml: withdraw the readiness constraint; it
  fails closed on the unlabeled cluster above. The NVIDIA#1755 node-set
  constraint FORM (pkg/constraints) is untouched and remains available.
- docs/user/component-catalog.md, docs/user/validation.md: correct the
  claim that the label does not affect driver provisioning, document the
  DaemonSet bundling and the standalone nvidia-driver-installer pairing,
  and mark enforcement as temporarily withdrawn.

Known, accepted interim consequence: GKE-default pools run BOTH GKE's
plugin and the operator's plugin as nvidia.com/gpu advertisers — the
documented latent NVIDIA#1755 conflict every pre-NVIDIA#2000 green run also carried.
The gpuStack profile re-lands enforcement symmetrically and coherently:
csp-managed (default) requires the label absent with GKE's plugin as
sole advertiser (advertiser: external); operator requires the label plus
a label-surviving driver path.

Refs NVIDIA#1755, NVIDIA#1761, NVIDIA#2000

Signed-off-by: Yuan Chen <yuanchen97@gmail.com>
yuanchen8911 added a commit to yuanchen8911/aicr that referenced this pull request Aug 4, 2026
Land the GKE adopter and the profile extensions ADR-015 staged for it
(issue NVIDIA#1761 rollout PR 3), completing the three-PR series after the
generic core (NVIDIA#1933) and the AKS adopter (NVIDIA#1967).

GKE conversion: recipes/overlays/gke-cos.yaml moves to aicr.run/v1alpha3
and declares gpuStack with two values, named after the AKS convention.
gcp-managed (the default) declares advertiser: external, disables the
operator's plugin, and carries the negated NVIDIA#1755 node-set constraint
(no GPU node may carry gke-no-default-nvidia-gpu-device-plugin) — the
default GKE cluster shape, making the previously unsupported "GKE's
managed plugin owns nvidia.com/gpu" state declarable and verifiable;
operator-managed keeps the GPU Operator's device plugin as the sole
nvidia.com/gpu advertiser with the positive constraint. The overlay's readiness constraint from NVIDIA#2000 relocates
into the profile values, as forecast when it landed; every GKE-COS leaf
inherits the declaration unchanged. operator-selfdriver and the NVIDIA#1716
installer stay deferred on DD5's durable signal.

Canonical NVIDIA#1327 descriptor: the policy path vocabulary that existed as
three private copies (pkg/bundler, pkg/validator/v1, pkg/recipe) moves
to the new dependency-neutral pkg/allocpolicy — one descriptor and one
tuple-coherence evaluator, consumed by profile validation, the hydrating
artifact gate, bundler enforcement, and validation-time policy
resolution. The descriptor is append-only; its deterministic Identity()
feeds evidence currentness.

advertiser "external" activates: the core's three deferral rejections
are replaced with descriptor-backed validation. A profile that owns
advertisement — a declared external advertiser or explicit ownership of
a non-synthetic selector path — triggers the recomputed closure:
RecipeResult.EffectiveLockSet() joins every enabled descriptor
component's selector paths (plus synthetic presence) to the declared
ownedPaths, and the bundle/mirror lock, the argocd-helm render guard,
and the hydrating gate all consume the effective set. The closure is
recomputed at every boundary, never persisted. The hydrating gate runs
the shared coherence rules (external + devicePlugin.enabled=true and
external + DRA gpus.enabled=true reject). Profiles that do not own
advertisement (the AKS shape) keep today's WARN semantics — locks follow
ownership. ResolveGPUAllocationPolicy counts a declared external
advertiser as THE advertiser in the exactly-one invariant; the resolved
policy value stays device-plugin-extended-resource.

Evidence predicate v2: profile-bearing recipes emit
https://aicr.run/recipe-evidence/v2 carrying {selection, advertiser,
policyDescriptorIdentity}; unprofiled evidence stays byte-identical on
v1. Producers, the pointer, all four verifier checkpoints, the
publisher, and testgrid-publish enforce the bidirectional contract, and
the verifier rejects a descriptor-identity mismatch as historical-only.
No committed evidence pointer is profiled yet, so the cut-over
invalidates nothing in-repo.

Fail-closed behavior changes, all per the ADR: GKE compositions now
reject on /v1 endpoints (profile cut-over); snapshot-based GKE
generation requires NodeTopology label readings (an unavailable reading
fails with the distinguishable DD1 diagnostic); a bundlers subset
omitting a closure-locked component (nvidia-dra-driver-gpu on GKE)
fails.

Docs: component-catalog (profile-led GKE ownership section; driver
auto-detect subordination now stated as path-ownership-based),
validation, cli-reference, api-reference (GKE /v1 cut-over),
recipe-development, data-extension, contributor/recipe. GCP UAT asserts
pin v1alpha3 + selectedProfile.

Live-cluster qualification of both GKE values and v2 evidence signing
follow before un-draft.

Default flip + review round: gcp-managed is the default because the
opt-out label forfeits GKE's managed driver install (the install is
finalized by an init container of the same kube-system DaemonSet the
label disables), so operator-managed requires Google's standalone
nvidia-driver-installer DaemonSet with pools created
gpu-driver-version=disabled; the docs and ADR record the amended
reality. Review fixes: descriptor currentness now runs on the
Sigstore-verified predicate path too, and the verifier binds the
predicate profile block (presence, selection, advertiser) to the
manifest-verified recipe; policyDescriptorIdentity is recipe-scoped
(allocpolicy.IdentityFor over the recipe's closure-contributing
entries) so an unrelated descriptor expansion cannot invalidate
evidence; BuildPointer and Publish validate bundle-profile/predicate
coherence before any registry side effect; testgrid-publish runs the
shared predicate-type coherence check; ResolveGPUAllocationPolicy
validates the advertiser vocabulary for typed Go callers; the OpenAPI
selectedProfile schema and Go facade docs admit advertiser: external.

Refs NVIDIA#1761, NVIDIA#1755, NVIDIA#1716, NVIDIA#1327

Signed-off-by: Yuan Chen <yuanchen97@gmail.com>
yuanchen8911 added a commit to yuanchen8911/aicr that referenced this pull request Aug 4, 2026
Land the GKE adopter and the profile extensions ADR-015 staged for it
(issue NVIDIA#1761 rollout PR 3), completing the three-PR series after the
generic core (NVIDIA#1933) and the AKS adopter (NVIDIA#1967).

GKE conversion: recipes/overlays/gke-cos.yaml moves to aicr.run/v1alpha3
and declares gpuStack with two values, named after the AKS convention.
gcp-managed (the default) declares advertiser: external, disables the
operator's plugin, and carries the negated NVIDIA#1755 node-set constraint
(no GPU node may carry gke-no-default-nvidia-gpu-device-plugin) — the
default GKE cluster shape, making the previously unsupported "GKE's
managed plugin owns nvidia.com/gpu" state declarable and verifiable;
operator-managed keeps the GPU Operator's device plugin as the sole
nvidia.com/gpu advertiser with the positive constraint. The overlay's
readiness constraint from NVIDIA#2000 relocates into the profile values, as
forecast when it landed; every GKE-COS leaf inherits the declaration
unchanged. operator-selfdriver and the NVIDIA#1716 installer stay deferred on
DD5's durable signal.

Canonical NVIDIA#1327 descriptor: the policy path vocabulary that existed as
three private copies (pkg/bundler, pkg/validator/v1, pkg/recipe) moves
to the new dependency-neutral pkg/allocpolicy — one descriptor and one
tuple-coherence evaluator, consumed by profile validation, the hydrating
artifact gate, bundler enforcement, and validation-time policy
resolution. The descriptor is append-only; its deterministic Identity()
feeds evidence currentness.

advertiser "external" activates: the core's three deferral rejections
are replaced with descriptor-backed validation. A profile that owns
advertisement — a declared external advertiser or explicit ownership of
a non-synthetic selector path — triggers the recomputed closure:
RecipeResult.EffectiveLockSet() joins every enabled descriptor
component's selector paths (plus synthetic presence) to the declared
ownedPaths, and the bundle/mirror lock, the argocd-helm render guard,
and the hydrating gate all consume the effective set. The closure is
recomputed at every boundary, never persisted. The hydrating gate runs
the shared coherence rules (external + devicePlugin.enabled=true and
external + DRA gpus.enabled=true reject). Profiles that do not own
advertisement (the AKS shape) keep today's WARN semantics — locks follow
ownership. ResolveGPUAllocationPolicy counts a declared external
advertiser as THE advertiser in the exactly-one invariant; the resolved
policy value stays device-plugin-extended-resource.

Evidence predicate v2: profile-bearing recipes emit
https://aicr.run/recipe-evidence/v2 carrying {selection, advertiser,
policyDescriptorIdentity}; unprofiled evidence stays byte-identical on
v1. Producers, the pointer, all four verifier checkpoints, the
publisher, and testgrid-publish enforce the bidirectional contract, and
the verifier rejects a descriptor-identity mismatch as historical-only.
No committed evidence pointer is profiled yet, so the cut-over
invalidates nothing in-repo.

Fail-closed behavior changes, all per the ADR: GKE compositions now
reject on /v1 endpoints (profile cut-over); snapshot-based GKE
generation requires NodeTopology label readings (an unavailable reading
fails with the distinguishable DD1 diagnostic); a bundlers subset
omitting a closure-locked component (nvidia-dra-driver-gpu on GKE)
fails.

Docs: a new integrator page, docs/integrator/gke-gpu-setup.md, mirrors
the AKS GPU Setup structure (two-mode table, snapshot-verification
story, qualification and selection-vs-verification matrices, default
and operator-managed procedures, troubleshooting); the component
catalog keeps the ownership model and links the procedures there.
Also updated: validation, cli-reference, api-reference (GKE /v1
cut-over; batch example moved to /v2), automation,
recipe-development, data-extension, contributor/recipe,
contributor/validator. GCP UAT asserts pin v1alpha3 + selectedProfile.

Default flip + review rounds: gcp-managed is the default because the
opt-out label forfeits GKE's managed driver install (the install is
finalized by an init container of the same kube-system DaemonSet the
label disables), so operator-managed requires Google's standalone
nvidia-driver-installer DaemonSet with pools created
gpu-driver-version=disabled; the docs and ADR record the amended
reality. Review fixes: descriptor currentness now runs on the
Sigstore-verified predicate path too, and the verifier binds the
predicate profile block (presence, selection, advertiser) to the
manifest-verified recipe; policyDescriptorIdentity is recipe-scoped
(allocpolicy.IdentityFor over the recipe's closure-contributing
entries) so an unrelated descriptor expansion cannot invalidate
evidence; BuildPointer and Publish validate bundle-profile/predicate
coherence before any registry side effect; testgrid-publish runs the
shared predicate-type coherence check; ResolveGPUAllocationPolicy
validates the advertiser vocabulary for typed Go callers; the OpenAPI
selectedProfile schema and Go facade docs admit advertiser: external.

This round: rebased onto NVIDIA#2047 (merged), which had withdrawn the NVIDIA#1755
gate and removed the opt-out label from the GCP UAT cluster-config —
the rebased overlay, catalog, validation, and ADR DD2 text now state
the final per-value enforcement the profile lands, and the label-free
UAT pool matches the gcp-managed default. Under advertiser: external,
ResolveGPUAllocationPolicy aggregates devicePlugin.enabled across
every enabled operator component (gpu-operator AND gpu-operator-ocp,
OR semantics mirroring the recipe-side gate) instead of the
warn-and-prefer single read; the publish-time bundle coherence gate
also compares the advertiser; both statement builders validate
predicate-type coherence producer-side; the pushed bundle's OCI
description derives from the actual predicate type; allocpolicy
identity canonicalization merges duplicate-component entries (pinned
identities unchanged); EffectiveLockSet no longer panics for typed
callers that skipped artifact validation; ValidateAdvertiser errors
propagate un-double-wrapped; the GKE-COS driver-absent remediation
(bundler + client copies) is per-profile-value; stale reserved-
advertiser and pre-profile claims swept from code comments and docs.

Refs NVIDIA#1761, NVIDIA#1755, NVIDIA#1716, NVIDIA#1327

Signed-off-by: Yuan Chen <yuanchen97@gmail.com>
yuanchen8911 added a commit to yuanchen8911/aicr that referenced this pull request Aug 4, 2026
Land the GKE adopter and the profile extensions ADR-015 staged for it
(issue NVIDIA#1761 rollout PR 3), completing the three-PR series after the
generic core (NVIDIA#1933) and the AKS adopter (NVIDIA#1967).

GKE conversion: recipes/overlays/gke-cos.yaml moves to aicr.run/v1alpha3
and declares gpuStack with two values, named after the AKS convention.
gcp-managed (the default) declares advertiser: external, disables the
operator's plugin, and carries the negated NVIDIA#1755 node-set constraint
(no GPU node may carry gke-no-default-nvidia-gpu-device-plugin) — the
default GKE cluster shape, making the previously unsupported "GKE's
managed plugin owns nvidia.com/gpu" state declarable and verifiable;
operator-managed keeps the GPU Operator's device plugin as the sole
nvidia.com/gpu advertiser with the positive constraint. The overlay's
readiness constraint from NVIDIA#2000 relocates into the profile values, as
forecast when it landed; every GKE-COS leaf inherits the declaration
unchanged. operator-selfdriver and the NVIDIA#1716 installer stay deferred on
DD5's durable signal.

Canonical NVIDIA#1327 descriptor: the policy path vocabulary that existed as
three private copies (pkg/bundler, pkg/validator/v1, pkg/recipe) moves
to the new dependency-neutral pkg/allocpolicy — one descriptor and one
tuple-coherence evaluator, consumed by profile validation, the hydrating
artifact gate, bundler enforcement, and validation-time policy
resolution. The descriptor is append-only; its deterministic Identity()
feeds evidence currentness.

advertiser "external" activates: the core's three deferral rejections
are replaced with descriptor-backed validation. A profile that owns
advertisement — a declared external advertiser or explicit ownership of
a non-synthetic selector path — triggers the recomputed closure:
RecipeResult.EffectiveLockSet() joins every enabled descriptor
component's selector paths (plus synthetic presence) to the declared
ownedPaths, and the bundle/mirror lock, the argocd-helm render guard,
and the hydrating gate all consume the effective set. The closure is
recomputed at every boundary, never persisted. The hydrating gate runs
the shared coherence rules (external + devicePlugin.enabled=true and
external + DRA gpus.enabled=true reject). Profiles that do not own
advertisement (the AKS shape) keep today's WARN semantics — locks follow
ownership. ResolveGPUAllocationPolicy counts a declared external
advertiser as THE advertiser in the exactly-one invariant; the resolved
policy value stays device-plugin-extended-resource.

Evidence predicate v2: profile-bearing recipes emit
https://aicr.run/recipe-evidence/v2 carrying {selection, advertiser,
policyDescriptorIdentity}; unprofiled evidence stays byte-identical on
v1. Producers, the pointer, all four verifier checkpoints, the
publisher, and testgrid-publish enforce the bidirectional contract, and
the verifier rejects a descriptor-identity mismatch as historical-only.
No committed evidence pointer is profiled yet, so the cut-over
invalidates nothing in-repo.

Fail-closed behavior changes, all per the ADR: GKE compositions now
reject on /v1 endpoints (profile cut-over); snapshot-based GKE
generation requires NodeTopology label readings (an unavailable reading
fails with the distinguishable DD1 diagnostic); a bundlers subset
omitting a closure-locked component (nvidia-dra-driver-gpu on GKE)
fails.

Docs: a new integrator page, docs/integrator/gke-gpu-setup.md, mirrors
the AKS GPU Setup structure (two-mode table, snapshot-verification
story, qualification and selection-vs-verification matrices, default
and operator-managed procedures, troubleshooting); the component
catalog keeps the ownership model and links the procedures there.
Also updated: validation, cli-reference, api-reference (GKE /v1
cut-over; batch example moved to /v2), automation,
recipe-development, data-extension, contributor/recipe,
contributor/validator. GCP UAT asserts pin v1alpha3 + selectedProfile.

Default flip + review rounds: gcp-managed is the default because the
opt-out label forfeits GKE's managed driver install (the install is
finalized by an init container of the same kube-system DaemonSet the
label disables), so operator-managed requires Google's standalone
nvidia-driver-installer DaemonSet with pools created
gpu-driver-version=disabled; the docs and ADR record the amended
reality. Review fixes: descriptor currentness now runs on the
Sigstore-verified predicate path too, and the verifier binds the
predicate profile block (presence, selection, advertiser) to the
manifest-verified recipe; policyDescriptorIdentity is recipe-scoped
(allocpolicy.IdentityFor over the recipe's closure-contributing
entries) so an unrelated descriptor expansion cannot invalidate
evidence; BuildPointer and Publish validate bundle-profile/predicate
coherence before any registry side effect; testgrid-publish runs the
shared predicate-type coherence check; ResolveGPUAllocationPolicy
validates the advertiser vocabulary for typed Go callers; the OpenAPI
selectedProfile schema and Go facade docs admit advertiser: external.

This round: rebased onto NVIDIA#2047 (merged), which had withdrawn the NVIDIA#1755
gate and removed the opt-out label from the GCP UAT cluster-config —
the rebased overlay, catalog, validation, and ADR DD2 text now state
the final per-value enforcement the profile lands, and the label-free
UAT pool matches the gcp-managed default. Under advertiser: external,
ResolveGPUAllocationPolicy aggregates devicePlugin.enabled across
every enabled operator component (gpu-operator AND gpu-operator-ocp,
OR semantics mirroring the recipe-side gate) instead of the
warn-and-prefer single read; the publish-time bundle coherence gate
also compares the advertiser; both statement builders validate
predicate-type coherence producer-side; the pushed bundle's OCI
description derives from the actual predicate type; allocpolicy
identity canonicalization merges duplicate-component entries (pinned
identities unchanged); EffectiveLockSet no longer panics for typed
callers that skipped artifact validation; ValidateAdvertiser errors
propagate un-double-wrapped; the GKE-COS driver-absent remediation
(bundler + client copies) is per-profile-value; stale reserved-
advertiser and pre-profile claims swept from code comments and docs.

Refs NVIDIA#1761, NVIDIA#1755, NVIDIA#1716, NVIDIA#1327

Signed-off-by: Yuan Chen <yuanchen97@gmail.com>
yuanchen8911 added a commit to yuanchen8911/aicr that referenced this pull request Aug 4, 2026
Land the GKE adopter and the profile extensions ADR-015 staged for it
(issue NVIDIA#1761 rollout PR 3), completing the three-PR series after the
generic core (NVIDIA#1933) and the AKS adopter (NVIDIA#1967).

GKE conversion: recipes/overlays/gke-cos.yaml moves to aicr.run/v1alpha3
and declares gpuStack with two values, named after the AKS convention.
gcp-managed (the default) declares advertiser: external, disables the
operator's plugin, and carries the negated NVIDIA#1755 node-set constraint
(no GPU node may carry gke-no-default-nvidia-gpu-device-plugin) — the
default GKE cluster shape, making the previously unsupported "GKE's
managed plugin owns nvidia.com/gpu" state declarable and verifiable;
operator-managed keeps the GPU Operator's device plugin as the sole
nvidia.com/gpu advertiser with the positive constraint. The overlay's
readiness constraint from NVIDIA#2000 relocates into the profile values, as
forecast when it landed; every GKE-COS leaf inherits the declaration
unchanged. operator-selfdriver and the NVIDIA#1716 installer stay deferred on
DD5's durable signal.

Canonical NVIDIA#1327 descriptor: the policy path vocabulary that existed as
three private copies (pkg/bundler, pkg/validator/v1, pkg/recipe) moves
to the new dependency-neutral pkg/allocpolicy — one descriptor and one
tuple-coherence evaluator, consumed by profile validation, the hydrating
artifact gate, bundler enforcement, and validation-time policy
resolution. The descriptor is append-only; its deterministic Identity()
feeds evidence currentness.

advertiser "external" activates: the core's three deferral rejections
are replaced with descriptor-backed validation. A profile that owns
advertisement — a declared external advertiser or explicit ownership of
a non-synthetic selector path — triggers the recomputed closure:
RecipeResult.EffectiveLockSet() joins every enabled descriptor
component's selector paths (plus synthetic presence) to the declared
ownedPaths, and the bundle/mirror lock, the argocd-helm render guard,
and the hydrating gate all consume the effective set. The closure is
recomputed at every boundary, never persisted. The hydrating gate runs
the shared coherence rules (external + devicePlugin.enabled=true and
external + DRA gpus.enabled=true reject). Profiles that do not own
advertisement (the AKS shape) keep today's WARN semantics — locks follow
ownership. ResolveGPUAllocationPolicy counts a declared external
advertiser as THE advertiser in the exactly-one invariant; the resolved
policy value stays device-plugin-extended-resource.

Evidence predicate v2: profile-bearing recipes emit
https://aicr.run/recipe-evidence/v2 carrying {selection, advertiser,
policyDescriptorIdentity}; unprofiled evidence stays byte-identical on
v1. Producers, the pointer, all four verifier checkpoints, the
publisher, and testgrid-publish enforce the bidirectional contract, and
the verifier rejects a descriptor-identity mismatch as historical-only.
No committed evidence pointer is profiled yet, so the cut-over
invalidates nothing in-repo.

Fail-closed behavior changes, all per the ADR: GKE compositions now
reject on /v1 endpoints (profile cut-over); snapshot-based GKE
generation requires NodeTopology label readings (an unavailable reading
fails with the distinguishable DD1 diagnostic); a bundlers subset
omitting a closure-locked component (nvidia-dra-driver-gpu on GKE)
fails.

Docs: a new integrator page, docs/integrator/gke-gpu-setup.md, mirrors
the AKS GPU Setup structure (two-mode table, snapshot-verification
story, qualification and selection-vs-verification matrices, default
and operator-managed procedures, troubleshooting); the component
catalog keeps the ownership model and links the procedures there.
Also updated: validation, cli-reference, api-reference (GKE /v1
cut-over; batch example moved to /v2), automation,
recipe-development, data-extension, contributor/recipe,
contributor/validator. GCP UAT asserts pin v1alpha3 + selectedProfile.

Default flip + review rounds: gcp-managed is the default because the
opt-out label forfeits GKE's managed driver install (the install is
finalized by an init container of the same kube-system DaemonSet the
label disables), so operator-managed requires Google's standalone
nvidia-driver-installer DaemonSet with pools created
gpu-driver-version=disabled; the docs and ADR record the amended
reality. Review fixes: descriptor currentness now runs on the
Sigstore-verified predicate path too, and the verifier binds the
predicate profile block (presence, selection, advertiser) to the
manifest-verified recipe; policyDescriptorIdentity is recipe-scoped
(allocpolicy.IdentityFor over the recipe's closure-contributing
entries) so an unrelated descriptor expansion cannot invalidate
evidence; BuildPointer and Publish validate bundle-profile/predicate
coherence before any registry side effect; testgrid-publish runs the
shared predicate-type coherence check; ResolveGPUAllocationPolicy
validates the advertiser vocabulary for typed Go callers; the OpenAPI
selectedProfile schema and Go facade docs admit advertiser: external.

This round: rebased onto NVIDIA#2047 (merged), which had withdrawn the NVIDIA#1755
gate and removed the opt-out label from the GCP UAT cluster-config —
the rebased overlay, catalog, validation, and ADR DD2 text now state
the final per-value enforcement the profile lands, and the label-free
UAT pool matches the gcp-managed default. Under advertiser: external,
ResolveGPUAllocationPolicy aggregates devicePlugin.enabled across
every enabled operator component (gpu-operator AND gpu-operator-ocp,
OR semantics mirroring the recipe-side gate) instead of the
warn-and-prefer single read; the publish-time bundle coherence gate
also compares the advertiser; both statement builders validate
predicate-type coherence producer-side; the pushed bundle's OCI
description derives from the actual predicate type; allocpolicy
identity canonicalization merges duplicate-component entries (pinned
identities unchanged); EffectiveLockSet no longer panics for typed
callers that skipped artifact validation; ValidateAdvertiser errors
propagate un-double-wrapped; the GKE-COS driver-absent remediation
(bundler + client copies) is per-profile-value; stale reserved-
advertiser and pre-profile claims swept from code comments and docs.

Refs NVIDIA#1761, NVIDIA#1755, NVIDIA#1716, NVIDIA#1327

Signed-off-by: Yuan Chen <yuanchen97@gmail.com>
yuanchen8911 added a commit to yuanchen8911/aicr that referenced this pull request Aug 4, 2026
Land the GKE adopter and the profile extensions ADR-015 staged for it
(issue NVIDIA#1761 rollout PR 3), completing the three-PR series after the
generic core (NVIDIA#1933) and the AKS adopter (NVIDIA#1967).

GKE conversion: recipes/overlays/gke-cos.yaml moves to aicr.run/v1alpha3
and declares gpuStack with two values, named after the AKS convention.
gcp-managed (the default) declares advertiser: external, disables the
operator's plugin, and carries the negated NVIDIA#1755 node-set constraint
(no GPU node may carry gke-no-default-nvidia-gpu-device-plugin) — the
default GKE cluster shape, making the previously unsupported "GKE's
managed plugin owns nvidia.com/gpu" state declarable and verifiable;
operator-managed keeps the GPU Operator's device plugin as the sole
nvidia.com/gpu advertiser with the positive constraint. The overlay's
readiness constraint from NVIDIA#2000 relocates into the profile values, as
forecast when it landed; every GKE-COS leaf inherits the declaration
unchanged. operator-selfdriver and the NVIDIA#1716 installer stay deferred on
DD5's durable signal.

Canonical NVIDIA#1327 descriptor: the policy path vocabulary that existed as
three private copies (pkg/bundler, pkg/validator/v1, pkg/recipe) moves
to the new dependency-neutral pkg/allocpolicy — one descriptor and one
tuple-coherence evaluator, consumed by profile validation, the hydrating
artifact gate, bundler enforcement, and validation-time policy
resolution. The descriptor is append-only; its deterministic Identity()
feeds evidence currentness.

advertiser "external" activates: the core's three deferral rejections
are replaced with descriptor-backed validation. A profile that owns
advertisement — a declared external advertiser or explicit ownership of
a non-synthetic selector path — triggers the recomputed closure:
RecipeResult.EffectiveLockSet() joins every enabled descriptor
component's selector paths (plus synthetic presence) to the declared
ownedPaths, and the bundle/mirror lock, the argocd-helm render guard,
and the hydrating gate all consume the effective set. The closure is
recomputed at every boundary, never persisted. The hydrating gate runs
the shared coherence rules (external + devicePlugin.enabled=true and
external + DRA gpus.enabled=true reject). Profiles that do not own
advertisement (the AKS shape) keep today's WARN semantics — locks follow
ownership. ResolveGPUAllocationPolicy counts a declared external
advertiser as THE advertiser in the exactly-one invariant; the resolved
policy value stays device-plugin-extended-resource.

Evidence predicate v2: profile-bearing recipes emit
https://aicr.run/recipe-evidence/v2 carrying {selection, advertiser,
policyDescriptorIdentity}; unprofiled evidence stays byte-identical on
v1. Producers, the pointer, all four verifier checkpoints, the
publisher, and testgrid-publish enforce the bidirectional contract, and
the verifier rejects a descriptor-identity mismatch as historical-only.
No committed evidence pointer is profiled yet, so the cut-over
invalidates nothing in-repo.

Fail-closed behavior changes, all per the ADR: GKE compositions now
reject on /v1 endpoints (profile cut-over); snapshot-based GKE
generation requires NodeTopology label readings (an unavailable reading
fails with the distinguishable DD1 diagnostic); a bundlers subset
omitting a closure-locked component (nvidia-dra-driver-gpu on GKE)
fails.

Docs: a new integrator page, docs/integrator/gke-gpu-setup.md, mirrors
the AKS GPU Setup structure (two-mode table, snapshot-verification
story, qualification and selection-vs-verification matrices, default
and operator-managed procedures, troubleshooting); the component
catalog keeps the ownership model and links the procedures there.
Also updated: validation, cli-reference, api-reference (GKE /v1
cut-over; batch example moved to /v2), automation,
recipe-development, data-extension, contributor/recipe,
contributor/validator. GCP UAT asserts pin v1alpha3 + selectedProfile.

Default flip + review rounds: gcp-managed is the default because the
opt-out label forfeits GKE's managed driver install (the install is
finalized by an init container of the same kube-system DaemonSet the
label disables), so operator-managed requires Google's standalone
nvidia-driver-installer DaemonSet with pools created
gpu-driver-version=disabled; the docs and ADR record the amended
reality. Review fixes: descriptor currentness now runs on the
Sigstore-verified predicate path too, and the verifier binds the
predicate profile block (presence, selection, advertiser) to the
manifest-verified recipe; policyDescriptorIdentity is recipe-scoped
(allocpolicy.IdentityFor over the recipe's closure-contributing
entries) so an unrelated descriptor expansion cannot invalidate
evidence; BuildPointer and Publish validate bundle-profile/predicate
coherence before any registry side effect; testgrid-publish runs the
shared predicate-type coherence check; ResolveGPUAllocationPolicy
validates the advertiser vocabulary for typed Go callers; the OpenAPI
selectedProfile schema and Go facade docs admit advertiser: external.

This round: rebased onto NVIDIA#2047 (merged), which had withdrawn the NVIDIA#1755
gate and removed the opt-out label from the GCP UAT cluster-config —
the rebased overlay, catalog, validation, and ADR DD2 text now state
the final per-value enforcement the profile lands, and the label-free
UAT pool matches the gcp-managed default. Under advertiser: external,
ResolveGPUAllocationPolicy aggregates devicePlugin.enabled across
every enabled operator component (gpu-operator AND gpu-operator-ocp,
OR semantics mirroring the recipe-side gate) instead of the
warn-and-prefer single read; the publish-time bundle coherence gate
also compares the advertiser; both statement builders validate
predicate-type coherence producer-side; the pushed bundle's OCI
description derives from the actual predicate type; allocpolicy
identity canonicalization merges duplicate-component entries (pinned
identities unchanged); EffectiveLockSet no longer panics for typed
callers that skipped artifact validation; ValidateAdvertiser errors
propagate un-double-wrapped; the GKE-COS driver-absent remediation
(bundler + client copies) is per-profile-value; stale reserved-
advertiser and pre-profile claims swept from code comments and docs.

Refs NVIDIA#1761, NVIDIA#1755, NVIDIA#1716, NVIDIA#1327

Signed-off-by: Yuan Chen <yuanchen97@gmail.com>
yuanchen8911 added a commit to yuanchen8911/aicr that referenced this pull request Aug 4, 2026
Land the GKE adopter and the profile extensions ADR-015 staged for it
(issue NVIDIA#1761 rollout PR 3), completing the three-PR series after the
generic core (NVIDIA#1933) and the AKS adopter (NVIDIA#1967).

GKE conversion: recipes/overlays/gke-cos.yaml moves to aicr.run/v1alpha3
and declares gpuStack with two values, named after the AKS convention.
gcp-managed (the default) declares advertiser: external, disables the
operator's plugin, and carries the negated NVIDIA#1755 node-set constraint
(no GPU node may carry gke-no-default-nvidia-gpu-device-plugin) — the
default GKE cluster shape, making the previously unsupported "GKE's
managed plugin owns nvidia.com/gpu" state declarable and verifiable;
operator-managed keeps the GPU Operator's device plugin as the sole
nvidia.com/gpu advertiser with the positive constraint. The overlay's
readiness constraint from NVIDIA#2000 relocates into the profile values, as
forecast when it landed; every GKE-COS leaf inherits the declaration
unchanged. operator-selfdriver and the NVIDIA#1716 installer stay deferred on
DD5's durable signal.

Canonical NVIDIA#1327 descriptor: the policy path vocabulary that existed as
three private copies (pkg/bundler, pkg/validator/v1, pkg/recipe) moves
to the new dependency-neutral pkg/allocpolicy — one descriptor and one
tuple-coherence evaluator, consumed by profile validation, the hydrating
artifact gate, bundler enforcement, and validation-time policy
resolution. The descriptor is append-only; its deterministic Identity()
feeds evidence currentness.

advertiser "external" activates: the core's three deferral rejections
are replaced with descriptor-backed validation. A profile that owns
advertisement — a declared external advertiser or explicit ownership of
a non-synthetic selector path — triggers the recomputed closure:
RecipeResult.EffectiveLockSet() joins every enabled descriptor
component's selector paths (plus synthetic presence) to the declared
ownedPaths, and the bundle/mirror lock, the argocd-helm render guard,
and the hydrating gate all consume the effective set. The closure is
recomputed at every boundary, never persisted. The hydrating gate runs
the shared coherence rules (external + devicePlugin.enabled=true and
external + DRA gpus.enabled=true reject). Profiles that do not own
advertisement (the AKS shape) keep today's WARN semantics — locks follow
ownership. ResolveGPUAllocationPolicy counts a declared external
advertiser as THE advertiser in the exactly-one invariant; the resolved
policy value stays device-plugin-extended-resource.

Evidence predicate v2: profile-bearing recipes emit
https://aicr.run/recipe-evidence/v2 carrying {selection, advertiser,
policyDescriptorIdentity}; unprofiled evidence stays byte-identical on
v1. Producers, the pointer, all four verifier checkpoints, the
publisher, and testgrid-publish enforce the bidirectional contract, and
the verifier rejects a descriptor-identity mismatch as historical-only.
No committed evidence pointer is profiled yet, so the cut-over
invalidates nothing in-repo.

Fail-closed behavior changes, all per the ADR: GKE compositions now
reject on /v1 endpoints (profile cut-over); snapshot-based GKE
generation requires NodeTopology label readings (an unavailable reading
fails with the distinguishable DD1 diagnostic); a bundlers subset
omitting a closure-locked component (nvidia-dra-driver-gpu on GKE)
fails.

Docs: a new integrator page, docs/integrator/gke-gpu-setup.md, mirrors
the AKS GPU Setup structure (two-mode table, snapshot-verification
story, qualification and selection-vs-verification matrices, default
and operator-managed procedures, troubleshooting); the component
catalog keeps the ownership model and links the procedures there.
Also updated: validation, cli-reference, api-reference (GKE /v1
cut-over; batch example moved to /v2), automation,
recipe-development, data-extension, contributor/recipe,
contributor/validator. GCP UAT asserts pin v1alpha3 + selectedProfile.

Default flip + review rounds: gcp-managed is the default because the
opt-out label forfeits GKE's managed driver install (the install is
finalized by an init container of the same kube-system DaemonSet the
label disables), so operator-managed requires Google's standalone
nvidia-driver-installer DaemonSet with pools created
gpu-driver-version=disabled; the docs and ADR record the amended
reality. Review fixes: descriptor currentness now runs on the
Sigstore-verified predicate path too, and the verifier binds the
predicate profile block (presence, selection, advertiser) to the
manifest-verified recipe; policyDescriptorIdentity is recipe-scoped
(allocpolicy.IdentityFor over the recipe's closure-contributing
entries) so an unrelated descriptor expansion cannot invalidate
evidence; BuildPointer and Publish validate bundle-profile/predicate
coherence before any registry side effect; testgrid-publish runs the
shared predicate-type coherence check; ResolveGPUAllocationPolicy
validates the advertiser vocabulary for typed Go callers; the OpenAPI
selectedProfile schema and Go facade docs admit advertiser: external.

This round: rebased onto NVIDIA#2047 (merged), which had withdrawn the NVIDIA#1755
gate and removed the opt-out label from the GCP UAT cluster-config —
the rebased overlay, catalog, validation, and ADR DD2 text now state
the final per-value enforcement the profile lands, and the label-free
UAT pool matches the gcp-managed default. Under advertiser: external,
ResolveGPUAllocationPolicy aggregates devicePlugin.enabled across
every enabled operator component (gpu-operator AND gpu-operator-ocp,
OR semantics mirroring the recipe-side gate) instead of the
warn-and-prefer single read; the publish-time bundle coherence gate
also compares the advertiser; both statement builders validate
predicate-type coherence producer-side; the pushed bundle's OCI
description derives from the actual predicate type; allocpolicy
identity canonicalization merges duplicate-component entries (pinned
identities unchanged); EffectiveLockSet no longer panics for typed
callers that skipped artifact validation; ValidateAdvertiser errors
propagate un-double-wrapped; the GKE-COS driver-absent remediation
(bundler + client copies) is per-profile-value; stale reserved-
advertiser and pre-profile claims swept from code comments and docs.

Refs NVIDIA#1761, NVIDIA#1755, NVIDIA#1716, NVIDIA#1327

Signed-off-by: Yuan Chen <yuanchen97@gmail.com>
yuanchen8911 added a commit to yuanchen8911/aicr that referenced this pull request Aug 4, 2026
Land the GKE adopter and the profile extensions ADR-015 staged for it
(issue NVIDIA#1761 rollout PR 3), completing the three-PR series after the
generic core (NVIDIA#1933) and the AKS adopter (NVIDIA#1967).

GKE conversion: recipes/overlays/gke-cos.yaml moves to aicr.run/v1alpha3
and declares gpuStack with two values, named after the AKS convention.
gcp-managed (the default) declares advertiser: external, disables the
operator's plugin, and carries the negated NVIDIA#1755 node-set constraint
(no GPU node may carry gke-no-default-nvidia-gpu-device-plugin) — the
default GKE cluster shape, making the previously unsupported "GKE's
managed plugin owns nvidia.com/gpu" state declarable and verifiable;
operator-managed keeps the GPU Operator's device plugin as the sole
nvidia.com/gpu advertiser with the positive constraint. The overlay's
readiness constraint from NVIDIA#2000 relocates into the profile values, as
forecast when it landed; every GKE-COS leaf inherits the declaration
unchanged. operator-selfdriver and the NVIDIA#1716 installer stay deferred on
DD5's durable signal.

Canonical NVIDIA#1327 descriptor: the policy path vocabulary that existed as
three private copies (pkg/bundler, pkg/validator/v1, pkg/recipe) moves
to the new dependency-neutral pkg/allocpolicy — one descriptor and one
tuple-coherence evaluator, consumed by profile validation, the hydrating
artifact gate, bundler enforcement, and validation-time policy
resolution. The descriptor is append-only; its deterministic Identity()
feeds evidence currentness.

advertiser "external" activates: the core's three deferral rejections
are replaced with descriptor-backed validation. A profile that owns
advertisement — a declared external advertiser or explicit ownership of
a non-synthetic selector path — triggers the recomputed closure:
RecipeResult.EffectiveLockSet() joins every enabled descriptor
component's selector paths (plus synthetic presence) to the declared
ownedPaths, and the bundle/mirror lock, the argocd-helm render guard,
and the hydrating gate all consume the effective set. The closure is
recomputed at every boundary, never persisted. The hydrating gate runs
the shared coherence rules (external + devicePlugin.enabled=true and
external + DRA gpus.enabled=true reject). Profiles that do not own
advertisement (the AKS shape) keep today's WARN semantics — locks follow
ownership. ResolveGPUAllocationPolicy counts a declared external
advertiser as THE advertiser in the exactly-one invariant; the resolved
policy value stays device-plugin-extended-resource.

Evidence predicate v2: profile-bearing recipes emit
https://aicr.run/recipe-evidence/v2 carrying {selection, advertiser,
policyDescriptorIdentity}; unprofiled evidence stays byte-identical on
v1. Producers, the pointer, all four verifier checkpoints, the
publisher, and testgrid-publish enforce the bidirectional contract, and
the verifier rejects a descriptor-identity mismatch as historical-only.
No committed evidence pointer is profiled yet, so the cut-over
invalidates nothing in-repo.

Fail-closed behavior changes, all per the ADR: GKE compositions now
reject on /v1 endpoints (profile cut-over); snapshot-based GKE
generation requires NodeTopology label readings (an unavailable reading
fails with the distinguishable DD1 diagnostic); a bundlers subset
omitting a closure-locked component (nvidia-dra-driver-gpu on GKE)
fails.

Docs: a new integrator page, docs/integrator/gke-gpu-setup.md, mirrors
the AKS GPU Setup structure (two-mode table, snapshot-verification
story, qualification and selection-vs-verification matrices, default
and operator-managed procedures, troubleshooting); the component
catalog keeps the ownership model and links the procedures there.
Also updated: validation, cli-reference, api-reference (GKE /v1
cut-over; batch example moved to /v2), automation,
recipe-development, data-extension, contributor/recipe,
contributor/validator. GCP UAT asserts pin v1alpha3 + selectedProfile.

Default flip + review rounds: gcp-managed is the default because the
opt-out label forfeits GKE's managed driver install (the install is
finalized by an init container of the same kube-system DaemonSet the
label disables), so operator-managed requires Google's standalone
nvidia-driver-installer DaemonSet with pools created
gpu-driver-version=disabled; the docs and ADR record the amended
reality. Review fixes: descriptor currentness now runs on the
Sigstore-verified predicate path too, and the verifier binds the
predicate profile block (presence, selection, advertiser) to the
manifest-verified recipe; policyDescriptorIdentity is recipe-scoped
(allocpolicy.IdentityFor over the recipe's closure-contributing
entries) so an unrelated descriptor expansion cannot invalidate
evidence; BuildPointer and Publish validate bundle-profile/predicate
coherence before any registry side effect; testgrid-publish runs the
shared predicate-type coherence check; ResolveGPUAllocationPolicy
validates the advertiser vocabulary for typed Go callers; the OpenAPI
selectedProfile schema and Go facade docs admit advertiser: external.

Earlier round: rebased onto NVIDIA#2047 (merged), which had withdrawn the NVIDIA#1755
gate and removed the opt-out label from the GCP UAT cluster-config —
the rebased overlay, catalog, validation, and ADR DD2 text now state
the final per-value enforcement the profile lands, and the label-free
UAT pool matches the gcp-managed default. Under advertiser: external,
ResolveGPUAllocationPolicy aggregates devicePlugin.enabled across
every enabled operator component (gpu-operator AND gpu-operator-ocp,
OR semantics mirroring the recipe-side gate) instead of the
warn-and-prefer single read; the publish-time bundle coherence gate
also compares the advertiser; both statement builders validate
predicate-type coherence producer-side; the pushed bundle's OCI
description derives from the actual predicate type; allocpolicy
identity canonicalization merges duplicate-component entries (pinned
identities unchanged); EffectiveLockSet no longer panics for typed
callers that skipped artifact validation; ValidateAdvertiser errors
propagate un-double-wrapped; the GKE-COS driver-absent remediation
(bundler + client copies) is per-profile-value; stale reserved-
advertiser and pre-profile claims swept from code comments and docs.

This round: the hydrating coherence gate fails closed on an enabled DRA
component whose resources.gpus.enabled is absent (the pinned chart's
declared default is true, so treating absence as unknown let a
gcp-managed artifact with custom values omitting the stock pin pass
generation and bundling while deploying a dual advertiser), mirroring
the validation-time resolver; the publish/pointer coherence gate also
compares the predicate's policyDescriptorIdentity against the identity
recomputed from the bundle recipe, so the split-leg publish cannot
spend a Fulcio cert, a Rekor entry, and an OCI push on evidence its
own verifier rejects as historical-only; the recipe-health
constraints_wellformed dimension mirrors constraints.Evaluate's
node-set dispatch (new exported ValidateGPUNodesLabelValue) so the
negated NVIDIA#1755 form this PR introduces no longer falsely fails every
GKE recipe; presence.yaml drops the unsuffixed GKE dashboard entries
(profiled runs land on -gpustack-<value> routes, AKS precedent) and
recipe-health.md is regenerated; the secure-access GPU probe's
PATH/LD_LIBRARY_PATH exports use ${VAR:+${VAR}:} guards so an unset
variable cannot leave a leading empty (CWD) search-path entry; the
pushed bundle's OCI description derivation is extracted and
table-tested; docs: the ADR's normative /v2 POST example uses the
shipped gcp-managed value, api-reference scopes the /v1 rejection to
/v1/recipe and /v1/query (/v1/bundle rejects only profile-bearing
bodies), and the attestation package godoc covers the v2 predicate.

Refs NVIDIA#1761, NVIDIA#1755, NVIDIA#1716, NVIDIA#1327

Signed-off-by: Yuan Chen <yuanchen97@gmail.com>
yuanchen8911 added a commit to yuanchen8911/aicr that referenced this pull request Aug 5, 2026
Land the GKE adopter and the profile extensions ADR-015 staged for it
(issue NVIDIA#1761 rollout PR 3), completing the three-PR series after the
generic core (NVIDIA#1933) and the AKS adopter (NVIDIA#1967).

GKE conversion: recipes/overlays/gke-cos.yaml moves to aicr.run/v1alpha3
and declares gpuStack with two values, named after the AKS convention.
gcp-managed (the default) declares advertiser: external, disables the
operator's plugin, and carries the negated NVIDIA#1755 node-set constraint
(no GPU node may carry gke-no-default-nvidia-gpu-device-plugin) — the
default GKE cluster shape, making the previously unsupported "GKE's
managed plugin owns nvidia.com/gpu" state declarable and verifiable;
operator-managed keeps the GPU Operator's device plugin as the sole
nvidia.com/gpu advertiser with the positive constraint. The overlay's
readiness constraint from NVIDIA#2000 relocates into the profile values, as
forecast when it landed; every GKE-COS leaf inherits the declaration
unchanged. operator-selfdriver and the NVIDIA#1716 installer stay deferred on
DD5's durable signal.

Canonical NVIDIA#1327 descriptor: the policy path vocabulary that existed as
three private copies (pkg/bundler, pkg/validator/v1, pkg/recipe) moves
to the new dependency-neutral pkg/allocpolicy — one descriptor and one
tuple-coherence evaluator, consumed by profile validation, the hydrating
artifact gate, bundler enforcement, and validation-time policy
resolution. The descriptor is append-only; its deterministic Identity()
feeds evidence currentness.

advertiser "external" activates: the core's three deferral rejections
are replaced with descriptor-backed validation. A profile that owns
advertisement — a declared external advertiser or explicit ownership of
a non-synthetic selector path — triggers the recomputed closure:
RecipeResult.EffectiveLockSet() joins every enabled descriptor
component's selector paths (plus synthetic presence) to the declared
ownedPaths, and the bundle/mirror lock, the argocd-helm render guard,
and the hydrating gate all consume the effective set. The closure is
recomputed at every boundary, never persisted. The hydrating gate runs
the shared coherence rules (external + devicePlugin.enabled=true and
external + DRA gpus.enabled=true reject). Profiles that do not own
advertisement (the AKS shape) keep today's WARN semantics — locks follow
ownership. ResolveGPUAllocationPolicy counts a declared external
advertiser as THE advertiser in the exactly-one invariant; the resolved
policy value stays device-plugin-extended-resource.

Evidence predicate v2: profile-bearing recipes emit
https://aicr.run/recipe-evidence/v2 carrying {selection, advertiser,
policyDescriptorIdentity}; unprofiled evidence stays byte-identical on
v1. Producers, the pointer, all four verifier checkpoints, the
publisher, and testgrid-publish enforce the bidirectional contract, and
the verifier rejects a descriptor-identity mismatch as historical-only.
No committed evidence pointer is profiled yet, so the cut-over
invalidates nothing in-repo.

Fail-closed behavior changes, all per the ADR: GKE compositions now
reject on /v1 endpoints (profile cut-over); snapshot-based GKE
generation requires NodeTopology label readings (an unavailable reading
fails with the distinguishable DD1 diagnostic); a bundlers subset
omitting a closure-locked component (nvidia-dra-driver-gpu on GKE)
fails.

Docs: a new integrator page, docs/integrator/gke-gpu-setup.md, mirrors
the AKS GPU Setup structure (two-mode table, snapshot-verification
story, qualification and selection-vs-verification matrices, default
and operator-managed procedures, troubleshooting); the component
catalog keeps the ownership model and links the procedures there.
Also updated: validation, cli-reference, api-reference (GKE /v1
cut-over; batch example moved to /v2), automation,
recipe-development, data-extension, contributor/recipe,
contributor/validator. GCP UAT asserts pin v1alpha3 + selectedProfile.

Default flip + review rounds: gcp-managed is the default because the
opt-out label forfeits GKE's managed driver install (the install is
finalized by an init container of the same kube-system DaemonSet the
label disables), so operator-managed requires Google's standalone
nvidia-driver-installer DaemonSet with pools created
gpu-driver-version=disabled; the docs and ADR record the amended
reality. Review fixes: descriptor currentness now runs on the
Sigstore-verified predicate path too, and the verifier binds the
predicate profile block (presence, selection, advertiser) to the
manifest-verified recipe; policyDescriptorIdentity is recipe-scoped
(allocpolicy.IdentityFor over the recipe's closure-contributing
entries) so an unrelated descriptor expansion cannot invalidate
evidence; BuildPointer and Publish validate bundle-profile/predicate
coherence before any registry side effect; testgrid-publish runs the
shared predicate-type coherence check; ResolveGPUAllocationPolicy
validates the advertiser vocabulary for typed Go callers; the OpenAPI
selectedProfile schema and Go facade docs admit advertiser: external.

Earlier round: rebased onto NVIDIA#2047 (merged), which had withdrawn the NVIDIA#1755
gate and removed the opt-out label from the GCP UAT cluster-config —
the rebased overlay, catalog, validation, and ADR DD2 text now state
the final per-value enforcement the profile lands, and the label-free
UAT pool matches the gcp-managed default. Under advertiser: external,
ResolveGPUAllocationPolicy aggregates devicePlugin.enabled across
every enabled operator component (gpu-operator AND gpu-operator-ocp,
OR semantics mirroring the recipe-side gate) instead of the
warn-and-prefer single read; the publish-time bundle coherence gate
also compares the advertiser; both statement builders validate
predicate-type coherence producer-side; the pushed bundle's OCI
description derives from the actual predicate type; allocpolicy
identity canonicalization merges duplicate-component entries (pinned
identities unchanged); EffectiveLockSet no longer panics for typed
callers that skipped artifact validation; ValidateAdvertiser errors
propagate un-double-wrapped; the GKE-COS driver-absent remediation
(bundler + client copies) is per-profile-value; stale reserved-
advertiser and pre-profile claims swept from code comments and docs.

This round: the hydrating coherence gate fails closed on an enabled DRA
component whose resources.gpus.enabled is absent (the pinned chart's
declared default is true, so treating absence as unknown let a
gcp-managed artifact with custom values omitting the stock pin pass
generation and bundling while deploying a dual advertiser), mirroring
the validation-time resolver; the publish/pointer coherence gate also
compares the predicate's policyDescriptorIdentity against the identity
recomputed from the bundle recipe, so the split-leg publish cannot
spend a Fulcio cert, a Rekor entry, and an OCI push on evidence its
own verifier rejects as historical-only; the recipe-health
constraints_wellformed dimension mirrors constraints.Evaluate's
node-set dispatch (new exported ValidateGPUNodesLabelValue) so the
negated NVIDIA#1755 form this PR introduces no longer falsely fails every
GKE recipe; presence.yaml drops the unsuffixed GKE dashboard entries
(profiled runs land on -gpustack-<value> routes, AKS precedent) and
recipe-health.md is regenerated; the secure-access GPU probe's
PATH/LD_LIBRARY_PATH exports use ${VAR:+${VAR}:} guards so an unset
variable cannot leave a leading empty (CWD) search-path entry; the
pushed bundle's OCI description derivation is extracted and
table-tested; docs: the ADR's normative /v2 POST example uses the
shipped gcp-managed value, api-reference scopes the /v1 rejection to
/v1/recipe and /v1/query (/v1/bundle rejects only profile-bearing
bodies), and the attestation package godoc covers the v2 predicate.

Refs NVIDIA#1761, NVIDIA#1755, NVIDIA#1716, NVIDIA#1327

Signed-off-by: Yuan Chen <yuanchen97@gmail.com>
yuanchen8911 added a commit to yuanchen8911/aicr that referenced this pull request Aug 5, 2026
Land the GKE adopter and the profile extensions ADR-015 staged for it
(issue NVIDIA#1761 rollout PR 3), completing the three-PR series after the
generic core (NVIDIA#1933) and the AKS adopter (NVIDIA#1967).

GKE conversion: recipes/overlays/gke-cos.yaml moves to aicr.run/v1alpha3
and declares gpuStack with two values, named after the AKS convention.
gcp-managed (the default) declares advertiser: external, disables the
operator's plugin, and carries the negated NVIDIA#1755 node-set constraint
(no GPU node may carry gke-no-default-nvidia-gpu-device-plugin) — the
default GKE cluster shape, making the previously unsupported "GKE's
managed plugin owns nvidia.com/gpu" state declarable and verifiable;
operator-managed keeps the GPU Operator's device plugin as the sole
nvidia.com/gpu advertiser with the positive constraint. The overlay's
readiness constraint from NVIDIA#2000 relocates into the profile values, as
forecast when it landed; every GKE-COS leaf inherits the declaration
unchanged. operator-selfdriver and the NVIDIA#1716 installer stay deferred on
DD5's durable signal.

Canonical NVIDIA#1327 descriptor: the policy path vocabulary that existed as
three private copies (pkg/bundler, pkg/validator/v1, pkg/recipe) moves
to the new dependency-neutral pkg/allocpolicy — one descriptor and one
tuple-coherence evaluator, consumed by profile validation, the hydrating
artifact gate, bundler enforcement, and validation-time policy
resolution. The descriptor is append-only; its deterministic Identity()
feeds evidence currentness.

advertiser "external" activates: the core's three deferral rejections
are replaced with descriptor-backed validation. A profile that owns
advertisement — a declared external advertiser or explicit ownership of
a non-synthetic selector path — triggers the recomputed closure:
RecipeResult.EffectiveLockSet() joins every enabled descriptor
component's selector paths (plus synthetic presence) to the declared
ownedPaths, and the bundle/mirror lock, the argocd-helm render guard,
and the hydrating gate all consume the effective set. The closure is
recomputed at every boundary, never persisted. The hydrating gate runs
the shared coherence rules (external + devicePlugin.enabled=true and
external + DRA gpus.enabled=true reject). Profiles that do not own
advertisement (the AKS shape) keep today's WARN semantics — locks follow
ownership. ResolveGPUAllocationPolicy counts a declared external
advertiser as THE advertiser in the exactly-one invariant; the resolved
policy value stays device-plugin-extended-resource.

Evidence predicate v2: profile-bearing recipes emit
https://aicr.run/recipe-evidence/v2 carrying {selection, advertiser,
policyDescriptorIdentity}; unprofiled evidence stays byte-identical on
v1. Producers, the pointer, all four verifier checkpoints, the
publisher, and testgrid-publish enforce the bidirectional contract, and
the verifier rejects a descriptor-identity mismatch as historical-only.
No committed evidence pointer is profiled yet, so the cut-over
invalidates nothing in-repo.

Fail-closed behavior changes, all per the ADR: GKE compositions now
reject on /v1 endpoints (profile cut-over); snapshot-based GKE
generation requires NodeTopology label readings (an unavailable reading
fails with the distinguishable DD1 diagnostic); a bundlers subset
omitting a closure-locked component (nvidia-dra-driver-gpu on GKE)
fails.

Docs: a new integrator page, docs/integrator/gke-gpu-setup.md, mirrors
the AKS GPU Setup structure (two-mode table, snapshot-verification
story, qualification and selection-vs-verification matrices, default
and operator-managed procedures, troubleshooting); the component
catalog keeps the ownership model and links the procedures there.
Also updated: validation, cli-reference, api-reference (GKE /v1
cut-over; batch example moved to /v2), automation,
recipe-development, data-extension, contributor/recipe,
contributor/validator. GCP UAT asserts pin v1alpha3 + selectedProfile.

Default flip + review rounds: gcp-managed is the default because the
opt-out label forfeits GKE's managed driver install (the install is
finalized by an init container of the same kube-system DaemonSet the
label disables), so operator-managed requires Google's standalone
nvidia-driver-installer DaemonSet with pools created
gpu-driver-version=disabled; the docs and ADR record the amended
reality. Review fixes: descriptor currentness now runs on the
Sigstore-verified predicate path too, and the verifier binds the
predicate profile block (presence, selection, advertiser) to the
manifest-verified recipe; policyDescriptorIdentity is recipe-scoped
(allocpolicy.IdentityFor over the recipe's closure-contributing
entries) so an unrelated descriptor expansion cannot invalidate
evidence; BuildPointer and Publish validate bundle-profile/predicate
coherence before any registry side effect; testgrid-publish runs the
shared predicate-type coherence check; ResolveGPUAllocationPolicy
validates the advertiser vocabulary for typed Go callers; the OpenAPI
selectedProfile schema and Go facade docs admit advertiser: external.

Earlier round: rebased onto NVIDIA#2047 (merged), which had withdrawn the NVIDIA#1755
gate and removed the opt-out label from the GCP UAT cluster-config —
the rebased overlay, catalog, validation, and ADR DD2 text now state
the final per-value enforcement the profile lands, and the label-free
UAT pool matches the gcp-managed default. Under advertiser: external,
ResolveGPUAllocationPolicy aggregates devicePlugin.enabled across
every enabled operator component (gpu-operator AND gpu-operator-ocp,
OR semantics mirroring the recipe-side gate) instead of the
warn-and-prefer single read; the publish-time bundle coherence gate
also compares the advertiser; both statement builders validate
predicate-type coherence producer-side; the pushed bundle's OCI
description derives from the actual predicate type; allocpolicy
identity canonicalization merges duplicate-component entries (pinned
identities unchanged); EffectiveLockSet no longer panics for typed
callers that skipped artifact validation; ValidateAdvertiser errors
propagate un-double-wrapped; the GKE-COS driver-absent remediation
(bundler + client copies) is per-profile-value; stale reserved-
advertiser and pre-profile claims swept from code comments and docs.

This round: the hydrating coherence gate fails closed on an enabled DRA
component whose resources.gpus.enabled is absent (the pinned chart's
declared default is true, so treating absence as unknown let a
gcp-managed artifact with custom values omitting the stock pin pass
generation and bundling while deploying a dual advertiser), mirroring
the validation-time resolver; the publish/pointer coherence gate also
compares the predicate's policyDescriptorIdentity against the identity
recomputed from the bundle recipe, so the split-leg publish cannot
spend a Fulcio cert, a Rekor entry, and an OCI push on evidence its
own verifier rejects as historical-only; the recipe-health
constraints_wellformed dimension mirrors constraints.Evaluate's
node-set dispatch (new exported ValidateGPUNodesLabelValue) so the
negated NVIDIA#1755 form this PR introduces no longer falsely fails every
GKE recipe; presence.yaml drops the unsuffixed GKE dashboard entries
(profiled runs land on -gpustack-<value> routes, AKS precedent) and
recipe-health.md is regenerated; the secure-access GPU probe's
PATH/LD_LIBRARY_PATH exports use ${VAR:+${VAR}:} guards so an unset
variable cannot leave a leading empty (CWD) search-path entry; the
pushed bundle's OCI description derivation is extracted and
table-tested; docs: the ADR's normative /v2 POST example uses the
shipped gcp-managed value, api-reference scopes the /v1 rejection to
/v1/recipe and /v1/query (/v1/bundle rejects only profile-bearing
bodies), and the attestation package godoc covers the v2 predicate.

Refs NVIDIA#1761, NVIDIA#1755, NVIDIA#1716, NVIDIA#1327

Signed-off-by: Yuan Chen <yuanchen97@gmail.com>
yuanchen8911 added a commit to yuanchen8911/aicr that referenced this pull request Aug 5, 2026
Land the GKE adopter and the profile extensions ADR-015 staged for it
(issue NVIDIA#1761 rollout PR 3), completing the three-PR series after the
generic core (NVIDIA#1933) and the AKS adopter (NVIDIA#1967).

GKE conversion: recipes/overlays/gke-cos.yaml moves to aicr.run/v1alpha3
and declares gpuStack with two values, named after the AKS convention.
gcp-managed (the default) declares advertiser: external, disables the
operator's plugin, and carries the negated NVIDIA#1755 node-set constraint
(no GPU node may carry gke-no-default-nvidia-gpu-device-plugin) — the
default GKE cluster shape, making the previously unsupported "GKE's
managed plugin owns nvidia.com/gpu" state declarable and verifiable;
operator-managed keeps the GPU Operator's device plugin as the sole
nvidia.com/gpu advertiser with the positive constraint. The overlay's
readiness constraint from NVIDIA#2000 relocates into the profile values, as
forecast when it landed; every GKE-COS leaf inherits the declaration
unchanged. operator-selfdriver and the NVIDIA#1716 installer stay deferred on
DD5's durable signal.

Canonical NVIDIA#1327 descriptor: the policy path vocabulary that existed as
three private copies (pkg/bundler, pkg/validator/v1, pkg/recipe) moves
to the new dependency-neutral pkg/allocpolicy — one descriptor and one
tuple-coherence evaluator, consumed by profile validation, the hydrating
artifact gate, bundler enforcement, and validation-time policy
resolution. The descriptor is append-only; its deterministic Identity()
feeds evidence currentness.

advertiser "external" activates: the core's three deferral rejections
are replaced with descriptor-backed validation. A profile that owns
advertisement — a declared external advertiser or explicit ownership of
a non-synthetic selector path — triggers the recomputed closure:
RecipeResult.EffectiveLockSet() joins every enabled descriptor
component's selector paths (plus synthetic presence) to the declared
ownedPaths, and the bundle/mirror lock, the argocd-helm render guard,
and the hydrating gate all consume the effective set. The closure is
recomputed at every boundary, never persisted. The hydrating gate runs
the shared coherence rules (external + devicePlugin.enabled=true and
external + DRA gpus.enabled=true reject). Profiles that do not own
advertisement (the AKS shape) keep today's WARN semantics — locks follow
ownership. ResolveGPUAllocationPolicy counts a declared external
advertiser as THE advertiser in the exactly-one invariant; the resolved
policy value stays device-plugin-extended-resource.

Evidence predicate v2: profile-bearing recipes emit
https://aicr.run/recipe-evidence/v2 carrying {selection, advertiser,
policyDescriptorIdentity}; unprofiled evidence stays byte-identical on
v1. Producers, the pointer, all four verifier checkpoints, the
publisher, and testgrid-publish enforce the bidirectional contract, and
the verifier rejects a descriptor-identity mismatch as historical-only.
No committed evidence pointer is profiled yet, so the cut-over
invalidates nothing in-repo.

Fail-closed behavior changes, all per the ADR: GKE compositions now
reject on /v1 endpoints (profile cut-over); snapshot-based GKE
generation requires NodeTopology label readings (an unavailable reading
fails with the distinguishable DD1 diagnostic); a bundlers subset
omitting a closure-locked component (nvidia-dra-driver-gpu on GKE)
fails.

Docs: a new integrator page, docs/integrator/gke-gpu-setup.md, mirrors
the AKS GPU Setup structure (two-mode table, snapshot-verification
story, qualification and selection-vs-verification matrices, default
and operator-managed procedures, troubleshooting); the component
catalog keeps the ownership model and links the procedures there.
Also updated: validation, cli-reference, api-reference (GKE /v1
cut-over; batch example moved to /v2), automation,
recipe-development, data-extension, contributor/recipe,
contributor/validator. GCP UAT asserts pin v1alpha3 + selectedProfile.

Default flip + review rounds: gcp-managed is the default because the
opt-out label forfeits GKE's managed driver install (the install is
finalized by an init container of the same kube-system DaemonSet the
label disables), so operator-managed requires Google's standalone
nvidia-driver-installer DaemonSet with pools created
gpu-driver-version=disabled; the docs and ADR record the amended
reality. Review fixes: descriptor currentness now runs on the
Sigstore-verified predicate path too, and the verifier binds the
predicate profile block (presence, selection, advertiser) to the
manifest-verified recipe; policyDescriptorIdentity is recipe-scoped
(allocpolicy.IdentityFor over the recipe's closure-contributing
entries) so an unrelated descriptor expansion cannot invalidate
evidence; BuildPointer and Publish validate bundle-profile/predicate
coherence before any registry side effect; testgrid-publish runs the
shared predicate-type coherence check; ResolveGPUAllocationPolicy
validates the advertiser vocabulary for typed Go callers; the OpenAPI
selectedProfile schema and Go facade docs admit advertiser: external.

Earlier round: rebased onto NVIDIA#2047 (merged), which had withdrawn the NVIDIA#1755
gate and removed the opt-out label from the GCP UAT cluster-config —
the rebased overlay, catalog, validation, and ADR DD2 text now state
the final per-value enforcement the profile lands, and the label-free
UAT pool matches the gcp-managed default. Under advertiser: external,
ResolveGPUAllocationPolicy aggregates devicePlugin.enabled across
every enabled operator component (gpu-operator AND gpu-operator-ocp,
OR semantics mirroring the recipe-side gate) instead of the
warn-and-prefer single read; the publish-time bundle coherence gate
also compares the advertiser; both statement builders validate
predicate-type coherence producer-side; the pushed bundle's OCI
description derives from the actual predicate type; allocpolicy
identity canonicalization merges duplicate-component entries (pinned
identities unchanged); EffectiveLockSet no longer panics for typed
callers that skipped artifact validation; ValidateAdvertiser errors
propagate un-double-wrapped; the GKE-COS driver-absent remediation
(bundler + client copies) is per-profile-value; stale reserved-
advertiser and pre-profile claims swept from code comments and docs.

This round: the hydrating coherence gate fails closed on an enabled DRA
component whose resources.gpus.enabled is absent (the pinned chart's
declared default is true, so treating absence as unknown let a
gcp-managed artifact with custom values omitting the stock pin pass
generation and bundling while deploying a dual advertiser), mirroring
the validation-time resolver; the publish/pointer coherence gate also
compares the predicate's policyDescriptorIdentity against the identity
recomputed from the bundle recipe, so the split-leg publish cannot
spend a Fulcio cert, a Rekor entry, and an OCI push on evidence its
own verifier rejects as historical-only; the recipe-health
constraints_wellformed dimension mirrors constraints.Evaluate's
node-set dispatch (new exported ValidateGPUNodesLabelValue) so the
negated NVIDIA#1755 form this PR introduces no longer falsely fails every
GKE recipe; presence.yaml drops the unsuffixed GKE dashboard entries
(profiled runs land on -gpustack-<value> routes, AKS precedent) and
recipe-health.md is regenerated; the secure-access GPU probe's
PATH/LD_LIBRARY_PATH exports use ${VAR:+${VAR}:} guards so an unset
variable cannot leave a leading empty (CWD) search-path entry; the
pushed bundle's OCI description derivation is extracted and
table-tested; docs: the ADR's normative /v2 POST example uses the
shipped gcp-managed value, api-reference scopes the /v1 rejection to
/v1/recipe and /v1/query (/v1/bundle rejects only profile-bearing
bodies), and the attestation package godoc covers the v2 predicate.

Refs NVIDIA#1761, NVIDIA#1755, NVIDIA#1716, NVIDIA#1327

Signed-off-by: Yuan Chen <yuanchen97@gmail.com>
yuanchen8911 added a commit to yuanchen8911/aicr that referenced this pull request Aug 5, 2026
Land the GKE adopter and the profile extensions ADR-015 staged for it
(issue NVIDIA#1761 rollout PR 3), completing the three-PR series after the
generic core (NVIDIA#1933) and the AKS adopter (NVIDIA#1967).

GKE conversion: recipes/overlays/gke-cos.yaml moves to aicr.run/v1alpha3
and declares gpuStack with two values, named after the AKS convention.
gcp-managed (the default) declares advertiser: external, disables the
operator's plugin, and carries the negated NVIDIA#1755 node-set constraint
(no GPU node may carry gke-no-default-nvidia-gpu-device-plugin) — the
default GKE cluster shape, making the previously unsupported "GKE's
managed plugin owns nvidia.com/gpu" state declarable and verifiable;
operator-managed keeps the GPU Operator's device plugin as the sole
nvidia.com/gpu advertiser with the positive constraint. The overlay's
readiness constraint from NVIDIA#2000 relocates into the profile values, as
forecast when it landed; every GKE-COS leaf inherits the declaration
unchanged. operator-selfdriver and the NVIDIA#1716 installer stay deferred on
DD5's durable signal.

Canonical NVIDIA#1327 descriptor: the policy path vocabulary that existed as
three private copies (pkg/bundler, pkg/validator/v1, pkg/recipe) moves
to the new dependency-neutral pkg/allocpolicy — one descriptor and one
tuple-coherence evaluator, consumed by profile validation, the hydrating
artifact gate, bundler enforcement, and validation-time policy
resolution. The descriptor is append-only; its deterministic Identity()
feeds evidence currentness.

advertiser "external" activates: the core's three deferral rejections
are replaced with descriptor-backed validation. A profile that owns
advertisement — a declared external advertiser or explicit ownership of
a non-synthetic selector path — triggers the recomputed closure:
RecipeResult.EffectiveLockSet() joins every enabled descriptor
component's selector paths (plus synthetic presence) to the declared
ownedPaths, and the bundle/mirror lock, the argocd-helm render guard,
and the hydrating gate all consume the effective set. The closure is
recomputed at every boundary, never persisted. The hydrating gate runs
the shared coherence rules (external + devicePlugin.enabled=true and
external + DRA gpus.enabled=true reject). Profiles that do not own
advertisement (the AKS shape) keep today's WARN semantics — locks follow
ownership. ResolveGPUAllocationPolicy counts a declared external
advertiser as THE advertiser in the exactly-one invariant; the resolved
policy value stays device-plugin-extended-resource.

Evidence predicate v2: profile-bearing recipes emit
https://aicr.run/recipe-evidence/v2 carrying {selection, advertiser,
policyDescriptorIdentity}; unprofiled evidence stays byte-identical on
v1. Producers, the pointer, all four verifier checkpoints, the
publisher, and testgrid-publish enforce the bidirectional contract, and
the verifier rejects a descriptor-identity mismatch as historical-only.
No committed evidence pointer is profiled yet, so the cut-over
invalidates nothing in-repo.

Fail-closed behavior changes, all per the ADR: GKE compositions now
reject on /v1 endpoints (profile cut-over); snapshot-based GKE
generation requires NodeTopology label readings (an unavailable reading
fails with the distinguishable DD1 diagnostic); a bundlers subset
omitting a closure-locked component (nvidia-dra-driver-gpu on GKE)
fails.

Docs: a new integrator page, docs/integrator/gke-gpu-setup.md, mirrors
the AKS GPU Setup structure (two-mode table, snapshot-verification
story, qualification and selection-vs-verification matrices, default
and operator-managed procedures, troubleshooting); the component
catalog keeps the ownership model and links the procedures there.
Also updated: validation, cli-reference, api-reference (GKE /v1
cut-over; batch example moved to /v2), automation,
recipe-development, data-extension, contributor/recipe,
contributor/validator. GCP UAT asserts pin v1alpha3 + selectedProfile.

Default flip + review rounds: gcp-managed is the default because the
opt-out label forfeits GKE's managed driver install (the install is
finalized by an init container of the same kube-system DaemonSet the
label disables), so operator-managed requires Google's standalone
nvidia-driver-installer DaemonSet with pools created
gpu-driver-version=disabled; the docs and ADR record the amended
reality. Review fixes: descriptor currentness now runs on the
Sigstore-verified predicate path too, and the verifier binds the
predicate profile block (presence, selection, advertiser) to the
manifest-verified recipe; policyDescriptorIdentity is recipe-scoped
(allocpolicy.IdentityFor over the recipe's closure-contributing
entries) so an unrelated descriptor expansion cannot invalidate
evidence; BuildPointer and Publish validate bundle-profile/predicate
coherence before any registry side effect; testgrid-publish runs the
shared predicate-type coherence check; ResolveGPUAllocationPolicy
validates the advertiser vocabulary for typed Go callers; the OpenAPI
selectedProfile schema and Go facade docs admit advertiser: external.

Earlier round: rebased onto NVIDIA#2047 (merged), which had withdrawn the NVIDIA#1755
gate and removed the opt-out label from the GCP UAT cluster-config —
the rebased overlay, catalog, validation, and ADR DD2 text now state
the final per-value enforcement the profile lands, and the label-free
UAT pool matches the gcp-managed default. Under advertiser: external,
ResolveGPUAllocationPolicy aggregates devicePlugin.enabled across
every enabled operator component (gpu-operator AND gpu-operator-ocp,
OR semantics mirroring the recipe-side gate) instead of the
warn-and-prefer single read; the publish-time bundle coherence gate
also compares the advertiser; both statement builders validate
predicate-type coherence producer-side; the pushed bundle's OCI
description derives from the actual predicate type; allocpolicy
identity canonicalization merges duplicate-component entries (pinned
identities unchanged); EffectiveLockSet no longer panics for typed
callers that skipped artifact validation; ValidateAdvertiser errors
propagate un-double-wrapped; the GKE-COS driver-absent remediation
(bundler + client copies) is per-profile-value; stale reserved-
advertiser and pre-profile claims swept from code comments and docs.

This round: the hydrating coherence gate fails closed on an enabled DRA
component whose resources.gpus.enabled is absent (the pinned chart's
declared default is true, so treating absence as unknown let a
gcp-managed artifact with custom values omitting the stock pin pass
generation and bundling while deploying a dual advertiser), mirroring
the validation-time resolver; the publish/pointer coherence gate also
compares the predicate's policyDescriptorIdentity against the identity
recomputed from the bundle recipe, so the split-leg publish cannot
spend a Fulcio cert, a Rekor entry, and an OCI push on evidence its
own verifier rejects as historical-only; the recipe-health
constraints_wellformed dimension mirrors constraints.Evaluate's
node-set dispatch (new exported ValidateGPUNodesLabelValue) so the
negated NVIDIA#1755 form this PR introduces no longer falsely fails every
GKE recipe; presence.yaml drops the unsuffixed GKE dashboard entries
(profiled runs land on -gpustack-<value> routes, AKS precedent) and
recipe-health.md is regenerated; the secure-access GPU probe's
PATH/LD_LIBRARY_PATH exports use ${VAR:+${VAR}:} guards so an unset
variable cannot leave a leading empty (CWD) search-path entry; the
pushed bundle's OCI description derivation is extracted and
table-tested; docs: the ADR's normative /v2 POST example uses the
shipped gcp-managed value, api-reference scopes the /v1 rejection to
/v1/recipe and /v1/query (/v1/bundle rejects only profile-bearing
bodies), and the attestation package godoc covers the v2 predicate.

Refs NVIDIA#1761, NVIDIA#1755, NVIDIA#1716, NVIDIA#1327

Signed-off-by: Yuan Chen <yuanchen97@gmail.com>
yuanchen8911 added a commit to yuanchen8911/aicr that referenced this pull request Aug 5, 2026
Land the GKE adopter and the profile extensions ADR-015 staged for it
(issue NVIDIA#1761 rollout PR 3), completing the three-PR series after the
generic core (NVIDIA#1933) and the AKS adopter (NVIDIA#1967).

GKE conversion: recipes/overlays/gke-cos.yaml moves to aicr.run/v1alpha3
and declares gpuStack with two values, named after the AKS convention.
gcp-managed (the default) declares advertiser: external, disables the
operator's plugin, and carries the negated NVIDIA#1755 node-set constraint
(no GPU node may carry gke-no-default-nvidia-gpu-device-plugin) — the
default GKE cluster shape, making the previously unsupported "GKE's
managed plugin owns nvidia.com/gpu" state declarable and verifiable;
operator-managed keeps the GPU Operator's device plugin as the sole
nvidia.com/gpu advertiser with the positive constraint. The overlay's
readiness constraint from NVIDIA#2000 relocates into the profile values, as
forecast when it landed; every GKE-COS leaf inherits the declaration
unchanged. operator-selfdriver and the NVIDIA#1716 installer stay deferred on
DD5's durable signal.

Canonical NVIDIA#1327 descriptor: the policy path vocabulary that existed as
three private copies (pkg/bundler, pkg/validator/v1, pkg/recipe) moves
to the new dependency-neutral pkg/allocpolicy — one descriptor and one
tuple-coherence evaluator, consumed by profile validation, the hydrating
artifact gate, bundler enforcement, and validation-time policy
resolution. The descriptor is append-only; its deterministic Identity()
feeds evidence currentness.

advertiser "external" activates: the core's three deferral rejections
are replaced with descriptor-backed validation. A profile that owns
advertisement — a declared external advertiser or explicit ownership of
a non-synthetic selector path — triggers the recomputed closure:
RecipeResult.EffectiveLockSet() joins every enabled descriptor
component's selector paths (plus synthetic presence) to the declared
ownedPaths, and the bundle/mirror lock, the argocd-helm render guard,
and the hydrating gate all consume the effective set. The closure is
recomputed at every boundary, never persisted. The hydrating gate runs
the shared coherence rules (external + devicePlugin.enabled=true and
external + DRA gpus.enabled=true reject). Profiles that do not own
advertisement (the AKS shape) keep today's WARN semantics — locks follow
ownership. ResolveGPUAllocationPolicy counts a declared external
advertiser as THE advertiser in the exactly-one invariant; the resolved
policy value stays device-plugin-extended-resource.

Evidence predicate v2: profile-bearing recipes emit
https://aicr.run/recipe-evidence/v2 carrying {selection, advertiser,
policyDescriptorIdentity}; unprofiled evidence stays byte-identical on
v1. Producers, the pointer, all four verifier checkpoints, the
publisher, and testgrid-publish enforce the bidirectional contract, and
the verifier rejects a descriptor-identity mismatch as historical-only.
No committed evidence pointer is profiled yet, so the cut-over
invalidates nothing in-repo.

Fail-closed behavior changes, all per the ADR: GKE compositions now
reject on /v1 endpoints (profile cut-over); snapshot-based GKE
generation requires NodeTopology label readings (an unavailable reading
fails with the distinguishable DD1 diagnostic); a bundlers subset
omitting a closure-locked component (nvidia-dra-driver-gpu on GKE)
fails.

Docs: a new integrator page, docs/integrator/gke-gpu-setup.md, mirrors
the AKS GPU Setup structure (two-mode table, snapshot-verification
story, qualification and selection-vs-verification matrices, default
and operator-managed procedures, troubleshooting); the component
catalog keeps the ownership model and links the procedures there.
Also updated: validation, cli-reference, api-reference (GKE /v1
cut-over; batch example moved to /v2), automation,
recipe-development, data-extension, contributor/recipe,
contributor/validator. GCP UAT asserts pin v1alpha3 + selectedProfile.

Default flip + review rounds: gcp-managed is the default because the
opt-out label forfeits GKE's managed driver install (the install is
finalized by an init container of the same kube-system DaemonSet the
label disables), so operator-managed requires Google's standalone
nvidia-driver-installer DaemonSet with pools created
gpu-driver-version=disabled; the docs and ADR record the amended
reality. Review fixes: descriptor currentness now runs on the
Sigstore-verified predicate path too, and the verifier binds the
predicate profile block (presence, selection, advertiser) to the
manifest-verified recipe; policyDescriptorIdentity is recipe-scoped
(allocpolicy.IdentityFor over the recipe's closure-contributing
entries) so an unrelated descriptor expansion cannot invalidate
evidence; BuildPointer and Publish validate bundle-profile/predicate
coherence before any registry side effect; testgrid-publish runs the
shared predicate-type coherence check; ResolveGPUAllocationPolicy
validates the advertiser vocabulary for typed Go callers; the OpenAPI
selectedProfile schema and Go facade docs admit advertiser: external.

Earlier round: rebased onto NVIDIA#2047 (merged), which had withdrawn the NVIDIA#1755
gate and removed the opt-out label from the GCP UAT cluster-config —
the rebased overlay, catalog, validation, and ADR DD2 text now state
the final per-value enforcement the profile lands, and the label-free
UAT pool matches the gcp-managed default. Under advertiser: external,
ResolveGPUAllocationPolicy aggregates devicePlugin.enabled across
every enabled operator component (gpu-operator AND gpu-operator-ocp,
OR semantics mirroring the recipe-side gate) instead of the
warn-and-prefer single read; the publish-time bundle coherence gate
also compares the advertiser; both statement builders validate
predicate-type coherence producer-side; the pushed bundle's OCI
description derives from the actual predicate type; allocpolicy
identity canonicalization merges duplicate-component entries (pinned
identities unchanged); EffectiveLockSet no longer panics for typed
callers that skipped artifact validation; ValidateAdvertiser errors
propagate un-double-wrapped; the GKE-COS driver-absent remediation
(bundler + client copies) is per-profile-value; stale reserved-
advertiser and pre-profile claims swept from code comments and docs.

This round: the hydrating coherence gate fails closed on an enabled DRA
component whose resources.gpus.enabled is absent (the pinned chart's
declared default is true, so treating absence as unknown let a
gcp-managed artifact with custom values omitting the stock pin pass
generation and bundling while deploying a dual advertiser), mirroring
the validation-time resolver; the publish/pointer coherence gate also
compares the predicate's policyDescriptorIdentity against the identity
recomputed from the bundle recipe, so the split-leg publish cannot
spend a Fulcio cert, a Rekor entry, and an OCI push on evidence its
own verifier rejects as historical-only; the recipe-health
constraints_wellformed dimension mirrors constraints.Evaluate's
node-set dispatch (new exported ValidateGPUNodesLabelValue) so the
negated NVIDIA#1755 form this PR introduces no longer falsely fails every
GKE recipe; presence.yaml drops the unsuffixed GKE dashboard entries
(profiled runs land on -gpustack-<value> routes, AKS precedent) and
recipe-health.md is regenerated; the secure-access GPU probe's
PATH/LD_LIBRARY_PATH exports use ${VAR:+${VAR}:} guards so an unset
variable cannot leave a leading empty (CWD) search-path entry; the
pushed bundle's OCI description derivation is extracted and
table-tested; docs: the ADR's normative /v2 POST example uses the
shipped gcp-managed value, api-reference scopes the /v1 rejection to
/v1/recipe and /v1/query (/v1/bundle rejects only profile-bearing
bodies), and the attestation package godoc covers the v2 predicate.

Refs NVIDIA#1761, NVIDIA#1755, NVIDIA#1716, NVIDIA#1327

Signed-off-by: Yuan Chen <yuanchen97@gmail.com>
yuanchen8911 added a commit to yuanchen8911/aicr that referenced this pull request Aug 5, 2026
Land the GKE adopter and the profile extensions ADR-015 staged for it
(issue NVIDIA#1761 rollout PR 3), completing the three-PR series after the
generic core (NVIDIA#1933) and the AKS adopter (NVIDIA#1967).

GKE conversion: recipes/overlays/gke-cos.yaml moves to aicr.run/v1alpha3
and declares gpuStack with two values, named after the AKS convention.
gcp-managed (the default) declares advertiser: external, disables the
operator's plugin, and carries the negated NVIDIA#1755 node-set constraint
(no GPU node may carry gke-no-default-nvidia-gpu-device-plugin) — the
default GKE cluster shape, making the previously unsupported "GKE's
managed plugin owns nvidia.com/gpu" state declarable and verifiable;
operator-managed keeps the GPU Operator's device plugin as the sole
nvidia.com/gpu advertiser with the positive constraint. The overlay's
readiness constraint from NVIDIA#2000 relocates into the profile values, as
forecast when it landed; every GKE-COS leaf inherits the declaration
unchanged. operator-selfdriver and the NVIDIA#1716 installer stay deferred on
DD5's durable signal.

Canonical NVIDIA#1327 descriptor: the policy path vocabulary that existed as
three private copies (pkg/bundler, pkg/validator/v1, pkg/recipe) moves
to the new dependency-neutral pkg/allocpolicy — one descriptor and one
tuple-coherence evaluator, consumed by profile validation, the hydrating
artifact gate, bundler enforcement, and validation-time policy
resolution. The descriptor is append-only; its deterministic Identity()
feeds evidence currentness.

advertiser "external" activates: the core's three deferral rejections
are replaced with descriptor-backed validation. A profile that owns
advertisement — a declared external advertiser or explicit ownership of
a non-synthetic selector path — triggers the recomputed closure:
RecipeResult.EffectiveLockSet() joins every enabled descriptor
component's selector paths (plus synthetic presence) to the declared
ownedPaths, and the bundle/mirror lock, the argocd-helm render guard,
and the hydrating gate all consume the effective set. The closure is
recomputed at every boundary, never persisted. The hydrating gate runs
the shared coherence rules (external + devicePlugin.enabled=true and
external + DRA gpus.enabled=true reject). Profiles that do not own
advertisement (the AKS shape) keep today's WARN semantics — locks follow
ownership. ResolveGPUAllocationPolicy counts a declared external
advertiser as THE advertiser in the exactly-one invariant; the resolved
policy value stays device-plugin-extended-resource.

Evidence predicate v2: profile-bearing recipes emit
https://aicr.run/recipe-evidence/v2 carrying {selection, advertiser,
policyDescriptorIdentity}; unprofiled evidence stays byte-identical on
v1. Producers, the pointer, all four verifier checkpoints, the
publisher, and testgrid-publish enforce the bidirectional contract, and
the verifier rejects a descriptor-identity mismatch as historical-only.
No committed evidence pointer is profiled yet, and none is
rejected by the new checkpoints; the two committed GKE pointers
(h100-gke-cos-training) are v1 evidence over the now-profiled recipe,
so the cut-over demotes them to historical — recipe-health Evidence
links move to pending until the planned per-value v2 re-signing.

Fail-closed behavior changes, all per the ADR: GKE compositions now
reject on /v1 endpoints (profile cut-over); snapshot-based GKE
generation requires NodeTopology label readings (an unavailable reading
fails with the distinguishable DD1 diagnostic); a bundlers subset
omitting a closure-locked component (nvidia-dra-driver-gpu on GKE)
fails.

Docs: a new integrator page, docs/integrator/gke-gpu-setup.md, mirrors
the AKS GPU Setup structure (two-mode table, snapshot-verification
story, qualification and selection-vs-verification matrices, default
and operator-managed procedures, troubleshooting); the component
catalog keeps the ownership model and links the procedures there.
Also updated: validation, cli-reference, api-reference (GKE /v1
cut-over; batch example moved to /v2), automation,
recipe-development, data-extension, contributor/recipe,
contributor/validator. GCP UAT asserts pin v1alpha3 + selectedProfile.

Default flip + review rounds: gcp-managed is the default because the
opt-out label forfeits GKE's managed driver install (the install is
finalized by an init container of the same kube-system DaemonSet the
label disables), so operator-managed requires Google's standalone
nvidia-driver-installer DaemonSet with pools created
gpu-driver-version=disabled; the docs and ADR record the amended
reality. Review fixes: descriptor currentness now runs on the
Sigstore-verified predicate path too, and the verifier binds the
predicate profile block (presence, selection, advertiser) to the
manifest-verified recipe; policyDescriptorIdentity is recipe-scoped
(allocpolicy.IdentityFor over the recipe's closure-contributing
entries) so an unrelated descriptor expansion cannot invalidate
evidence; BuildPointer and Publish validate bundle-profile/predicate
coherence before any registry side effect; testgrid-publish runs the
shared predicate-type coherence check; ResolveGPUAllocationPolicy
validates the advertiser vocabulary for typed Go callers; the OpenAPI
selectedProfile schema and Go facade docs admit advertiser: external.

Earlier round: rebased onto NVIDIA#2047 (merged), which had withdrawn the NVIDIA#1755
gate and removed the opt-out label from the GCP UAT cluster-config —
the rebased overlay, catalog, validation, and ADR DD2 text now state
the final per-value enforcement the profile lands, and the label-free
UAT pool matches the gcp-managed default. Under advertiser: external,
ResolveGPUAllocationPolicy aggregates devicePlugin.enabled across
every enabled operator component (gpu-operator AND gpu-operator-ocp,
OR semantics mirroring the recipe-side gate) instead of the
warn-and-prefer single read; the publish-time bundle coherence gate
also compares the advertiser; both statement builders validate
predicate-type coherence producer-side; the pushed bundle's OCI
description derives from the actual predicate type; allocpolicy
identity canonicalization merges duplicate-component entries (pinned
identities unchanged); EffectiveLockSet no longer panics for typed
callers that skipped artifact validation; ValidateAdvertiser errors
propagate un-double-wrapped; the GKE-COS driver-absent remediation
(bundler + client copies) is per-profile-value; stale reserved-
advertiser and pre-profile claims swept from code comments and docs.

This round: the hydrating coherence gate fails closed on an enabled DRA
component whose resources.gpus.enabled is absent (the pinned chart's
declared default is true, so treating absence as unknown let a
gcp-managed artifact with custom values omitting the stock pin pass
generation and bundling while deploying a dual advertiser), mirroring
the validation-time resolver; the publish/pointer coherence gate also
compares the predicate's policyDescriptorIdentity against the identity
recomputed from the bundle recipe, so the split-leg publish cannot
spend a Fulcio cert, a Rekor entry, and an OCI push on evidence its
own verifier rejects as historical-only; the recipe-health
constraints_wellformed dimension mirrors constraints.Evaluate's
node-set dispatch (new exported ValidateGPUNodesLabelValue) so the
negated NVIDIA#1755 form this PR introduces no longer falsely fails every
GKE recipe; presence.yaml drops the unsuffixed GKE dashboard entries
(profiled runs land on -gpustack-<value> routes, AKS precedent) and
recipe-health.md is regenerated; the secure-access GPU probe's
PATH/LD_LIBRARY_PATH exports use ${VAR:+${VAR}:} guards so an unset
variable cannot leave a leading empty (CWD) search-path entry; the
pushed bundle's OCI description derivation is extracted and
table-tested; docs: the ADR's normative /v2 POST example uses the
shipped gcp-managed value, api-reference scopes the /v1 rejection to
/v1/recipe and /v1/query (/v1/bundle rejects only profile-bearing
bodies), the attestation package godoc covers the v2 predicate, and
the gke-gpu-setup retrofit procedure now switches the pool's driver
mode to gpu-driver-version=disabled (node-pools update --accelerator)
before relying on the standalone installer — Google's
nvidia-driver-installer DaemonSet ignores automatic-install pools, so
deploying it over a gpu-driver-version=default pool left replacement
nodes driverless.

Refs NVIDIA#1761, NVIDIA#1755, NVIDIA#1716, NVIDIA#1327

Signed-off-by: Yuan Chen <yuanchen97@gmail.com>
yuanchen8911 added a commit to yuanchen8911/aicr that referenced this pull request Aug 5, 2026
Land the GKE adopter and the profile extensions ADR-015 staged for it
(issue NVIDIA#1761 rollout PR 3), completing the three-PR series after the
generic core (NVIDIA#1933) and the AKS adopter (NVIDIA#1967).

GKE conversion: recipes/overlays/gke-cos.yaml moves to aicr.run/v1alpha3
and declares gpuStack with two values, named after the AKS convention.
gcp-managed (the default) declares advertiser: external, disables the
operator's plugin, and carries the negated NVIDIA#1755 node-set constraint
(no GPU node may carry gke-no-default-nvidia-gpu-device-plugin) — the
default GKE cluster shape, making the previously unsupported "GKE's
managed plugin owns nvidia.com/gpu" state declarable and verifiable;
operator-managed keeps the GPU Operator's device plugin as the sole
nvidia.com/gpu advertiser with the positive constraint. The overlay's
readiness constraint from NVIDIA#2000 relocates into the profile values, as
forecast when it landed; every GKE-COS leaf inherits the declaration
unchanged. operator-selfdriver and the NVIDIA#1716 installer stay deferred on
DD5's durable signal.

Canonical NVIDIA#1327 descriptor: the policy path vocabulary that existed as
three private copies (pkg/bundler, pkg/validator/v1, pkg/recipe) moves
to the new dependency-neutral pkg/allocpolicy — one descriptor and one
tuple-coherence evaluator, consumed by profile validation, the hydrating
artifact gate, bundler enforcement, and validation-time policy
resolution. The descriptor is append-only; its deterministic Identity()
feeds evidence currentness.

advertiser "external" activates: the core's three deferral rejections
are replaced with descriptor-backed validation. A profile that owns
advertisement — a declared external advertiser or explicit ownership of
a non-synthetic selector path — triggers the recomputed closure:
RecipeResult.EffectiveLockSet() joins every enabled descriptor
component's selector paths (plus synthetic presence) to the declared
ownedPaths, and the bundle/mirror lock, the argocd-helm render guard,
and the hydrating gate all consume the effective set. The closure is
recomputed at every boundary, never persisted. The hydrating gate runs
the shared coherence rules (external + devicePlugin.enabled=true and
external + DRA gpus.enabled=true reject). Profiles that do not own
advertisement (the AKS shape) keep today's WARN semantics — locks follow
ownership. ResolveGPUAllocationPolicy counts a declared external
advertiser as THE advertiser in the exactly-one invariant; the resolved
policy value stays device-plugin-extended-resource.

Evidence predicate v2: profile-bearing recipes emit
https://aicr.run/recipe-evidence/v2 carrying {selection, advertiser,
policyDescriptorIdentity}; unprofiled evidence stays byte-identical on
v1. Producers, the pointer, all four verifier checkpoints, the
publisher, and testgrid-publish enforce the bidirectional contract, and
the verifier rejects a descriptor-identity mismatch as historical-only.
No committed evidence pointer is profiled yet, and none is
rejected by the new checkpoints; the two committed GKE pointers
(h100-gke-cos-training) are v1 evidence over the now-profiled recipe,
so the cut-over demotes them to historical — recipe-health Evidence
links move to pending until the planned per-value v2 re-signing.

Fail-closed behavior changes, all per the ADR: GKE compositions now
reject on /v1 endpoints (profile cut-over); snapshot-based GKE
generation requires NodeTopology label readings (an unavailable reading
fails with the distinguishable DD1 diagnostic); a bundlers subset
omitting a closure-locked component (nvidia-dra-driver-gpu on GKE)
fails.

Docs: a new integrator page, docs/integrator/gke-gpu-setup.md, mirrors
the AKS GPU Setup structure (two-mode table, snapshot-verification
story, qualification and selection-vs-verification matrices, default
and operator-managed procedures, troubleshooting); the component
catalog keeps the ownership model and links the procedures there.
Also updated: validation, cli-reference, api-reference (GKE /v1
cut-over; batch example moved to /v2), automation,
recipe-development, data-extension, contributor/recipe,
contributor/validator. GCP UAT asserts pin v1alpha3 + selectedProfile.

Default flip + review rounds: gcp-managed is the default because the
opt-out label forfeits GKE's managed driver install (the install is
finalized by an init container of the same kube-system DaemonSet the
label disables), so operator-managed requires Google's standalone
nvidia-driver-installer DaemonSet with pools created
gpu-driver-version=disabled; the docs and ADR record the amended
reality. Review fixes: descriptor currentness now runs on the
Sigstore-verified predicate path too, and the verifier binds the
predicate profile block (presence, selection, advertiser) to the
manifest-verified recipe; policyDescriptorIdentity is recipe-scoped
(allocpolicy.IdentityFor over the recipe's closure-contributing
entries) so an unrelated descriptor expansion cannot invalidate
evidence; BuildPointer and Publish validate bundle-profile/predicate
coherence before any registry side effect; testgrid-publish runs the
shared predicate-type coherence check; ResolveGPUAllocationPolicy
validates the advertiser vocabulary for typed Go callers; the OpenAPI
selectedProfile schema and Go facade docs admit advertiser: external.

Earlier round: rebased onto NVIDIA#2047 (merged), which had withdrawn the NVIDIA#1755
gate and removed the opt-out label from the GCP UAT cluster-config —
the rebased overlay, catalog, validation, and ADR DD2 text now state
the final per-value enforcement the profile lands, and the label-free
UAT pool matches the gcp-managed default. Under advertiser: external,
ResolveGPUAllocationPolicy aggregates devicePlugin.enabled across
every enabled operator component (gpu-operator AND gpu-operator-ocp,
OR semantics mirroring the recipe-side gate) instead of the
warn-and-prefer single read; the publish-time bundle coherence gate
also compares the advertiser; both statement builders validate
predicate-type coherence producer-side; the pushed bundle's OCI
description derives from the actual predicate type; allocpolicy
identity canonicalization merges duplicate-component entries (pinned
identities unchanged); EffectiveLockSet no longer panics for typed
callers that skipped artifact validation; ValidateAdvertiser errors
propagate un-double-wrapped; the GKE-COS driver-absent remediation
(bundler + client copies) is per-profile-value; stale reserved-
advertiser and pre-profile claims swept from code comments and docs.

This round: the hydrating coherence gate fails closed on an enabled DRA
component whose resources.gpus.enabled is absent (the pinned chart's
declared default is true, so treating absence as unknown let a
gcp-managed artifact with custom values omitting the stock pin pass
generation and bundling while deploying a dual advertiser), mirroring
the validation-time resolver; the publish/pointer coherence gate also
compares the predicate's policyDescriptorIdentity against the identity
recomputed from the bundle recipe, so the split-leg publish cannot
spend a Fulcio cert, a Rekor entry, and an OCI push on evidence its
own verifier rejects as historical-only; the recipe-health
constraints_wellformed dimension mirrors constraints.Evaluate's
node-set dispatch (new exported ValidateGPUNodesLabelValue) so the
negated NVIDIA#1755 form this PR introduces no longer falsely fails every
GKE recipe; presence.yaml drops the unsuffixed GKE dashboard entries
(profiled runs land on -gpustack-<value> routes, AKS precedent) and
recipe-health.md is regenerated; the secure-access GPU probe's
PATH/LD_LIBRARY_PATH exports use ${VAR:+${VAR}:} guards so an unset
variable cannot leave a leading empty (CWD) search-path entry; the
pushed bundle's OCI description derivation is extracted and
table-tested; docs: the ADR's normative /v2 POST example uses the
shipped gcp-managed value, api-reference scopes the /v1 rejection to
/v1/recipe and /v1/query (/v1/bundle rejects only profile-bearing
bodies), the attestation package godoc covers the v2 predicate, and
the gke-gpu-setup retrofit procedure now switches the pool's driver
mode to gpu-driver-version=disabled (node-pools update --accelerator)
before relying on the standalone installer — Google's
nvidia-driver-installer DaemonSet ignores automatic-install pools, so
deploying it over a gpu-driver-version=default pool left replacement
nodes driverless.

Refs NVIDIA#1761, NVIDIA#1755, NVIDIA#1716, NVIDIA#1327

Signed-off-by: Yuan Chen <yuanchen97@gmail.com>
yuanchen8911 added a commit to yuanchen8911/aicr that referenced this pull request Aug 5, 2026
Land the GKE adopter and the profile extensions ADR-015 staged for it
(issue NVIDIA#1761 rollout PR 3), completing the three-PR series after the
generic core (NVIDIA#1933) and the AKS adopter (NVIDIA#1967).

GKE conversion: recipes/overlays/gke-cos.yaml moves to aicr.run/v1alpha3
and declares gpuStack with two values, named after the AKS convention.
gcp-managed (the default) declares advertiser: external, disables the
operator's plugin, and carries the negated NVIDIA#1755 node-set constraint
(no GPU node may carry gke-no-default-nvidia-gpu-device-plugin) — the
default GKE cluster shape, making the previously unsupported "GKE's
managed plugin owns nvidia.com/gpu" state declarable and verifiable;
operator-managed keeps the GPU Operator's device plugin as the sole
nvidia.com/gpu advertiser with the positive constraint. The overlay's
readiness constraint from NVIDIA#2000 relocates into the profile values, as
forecast when it landed; every GKE-COS leaf inherits the declaration
unchanged. operator-selfdriver and the NVIDIA#1716 installer stay deferred on
DD5's durable signal.

Canonical NVIDIA#1327 descriptor: the policy path vocabulary that existed as
three private copies (pkg/bundler, pkg/validator/v1, pkg/recipe) moves
to the new dependency-neutral pkg/allocpolicy — one descriptor and one
tuple-coherence evaluator, consumed by profile validation, the hydrating
artifact gate, bundler enforcement, and validation-time policy
resolution. The descriptor is append-only; its deterministic Identity()
feeds evidence currentness.

advertiser "external" activates: the core's three deferral rejections
are replaced with descriptor-backed validation. A profile that owns
advertisement — a declared external advertiser or explicit ownership of
a non-synthetic selector path — triggers the recomputed closure:
RecipeResult.EffectiveLockSet() joins every enabled descriptor
component's selector paths (plus synthetic presence) to the declared
ownedPaths, and the bundle/mirror lock, the argocd-helm render guard,
and the hydrating gate all consume the effective set. The closure is
recomputed at every boundary, never persisted. The hydrating gate runs
the shared coherence rules (external + devicePlugin.enabled=true and
external + DRA gpus.enabled=true reject). Profiles that do not own
advertisement (the AKS shape) keep today's WARN semantics — locks follow
ownership. ResolveGPUAllocationPolicy counts a declared external
advertiser as THE advertiser in the exactly-one invariant; the resolved
policy value stays device-plugin-extended-resource.

Evidence predicate v2: profile-bearing recipes emit
https://aicr.run/recipe-evidence/v2 carrying {selection, advertiser,
policyDescriptorIdentity}; unprofiled evidence stays byte-identical on
v1. Producers, the pointer, all four verifier checkpoints, the
publisher, and testgrid-publish enforce the bidirectional contract, and
the verifier rejects a descriptor-identity mismatch as historical-only.
No committed evidence pointer is profiled yet, and none is
rejected by the new checkpoints; the two committed GKE pointers
(h100-gke-cos-training) are v1 evidence over the now-profiled recipe,
so the cut-over demotes them to historical — recipe-health Evidence
links move to pending until the planned per-value v2 re-signing.

Fail-closed behavior changes, all per the ADR: GKE compositions now
reject on /v1 endpoints (profile cut-over); snapshot-based GKE
generation requires NodeTopology label readings (an unavailable reading
fails with the distinguishable DD1 diagnostic); a bundlers subset
omitting a closure-locked component (nvidia-dra-driver-gpu on GKE)
fails.

Docs: a new integrator page, docs/integrator/gke-gpu-setup.md, mirrors
the AKS GPU Setup structure (two-mode table, snapshot-verification
story, qualification and selection-vs-verification matrices, default
and operator-managed procedures, troubleshooting); the component
catalog keeps the ownership model and links the procedures there.
Also updated: validation, cli-reference, api-reference (GKE /v1
cut-over; batch example moved to /v2), automation,
recipe-development, data-extension, contributor/recipe,
contributor/validator. GCP UAT asserts pin v1alpha3 + selectedProfile.

Default flip + review rounds: gcp-managed is the default because the
opt-out label forfeits GKE's managed driver install (the install is
finalized by an init container of the same kube-system DaemonSet the
label disables), so operator-managed requires Google's standalone
nvidia-driver-installer DaemonSet with pools created
gpu-driver-version=disabled; the docs and ADR record the amended
reality. Review fixes: descriptor currentness now runs on the
Sigstore-verified predicate path too, and the verifier binds the
predicate profile block (presence, selection, advertiser) to the
manifest-verified recipe; policyDescriptorIdentity is recipe-scoped
(allocpolicy.IdentityFor over the recipe's closure-contributing
entries) so an unrelated descriptor expansion cannot invalidate
evidence; BuildPointer and Publish validate bundle-profile/predicate
coherence before any registry side effect; testgrid-publish runs the
shared predicate-type coherence check; ResolveGPUAllocationPolicy
validates the advertiser vocabulary for typed Go callers; the OpenAPI
selectedProfile schema and Go facade docs admit advertiser: external.

Earlier round: rebased onto NVIDIA#2047 (merged), which had withdrawn the NVIDIA#1755
gate and removed the opt-out label from the GCP UAT cluster-config —
the rebased overlay, catalog, validation, and ADR DD2 text now state
the final per-value enforcement the profile lands, and the label-free
UAT pool matches the gcp-managed default. Under advertiser: external,
ResolveGPUAllocationPolicy aggregates devicePlugin.enabled across
every enabled operator component (gpu-operator AND gpu-operator-ocp,
OR semantics mirroring the recipe-side gate) instead of the
warn-and-prefer single read; the publish-time bundle coherence gate
also compares the advertiser; both statement builders validate
predicate-type coherence producer-side; the pushed bundle's OCI
description derives from the actual predicate type; allocpolicy
identity canonicalization merges duplicate-component entries (pinned
identities unchanged); EffectiveLockSet no longer panics for typed
callers that skipped artifact validation; ValidateAdvertiser errors
propagate un-double-wrapped; the GKE-COS driver-absent remediation
(bundler + client copies) is per-profile-value; stale reserved-
advertiser and pre-profile claims swept from code comments and docs.

This round: the hydrating coherence gate fails closed on an enabled DRA
component whose resources.gpus.enabled is absent (the pinned chart's
declared default is true, so treating absence as unknown let a
gcp-managed artifact with custom values omitting the stock pin pass
generation and bundling while deploying a dual advertiser), mirroring
the validation-time resolver; the publish/pointer coherence gate also
compares the predicate's policyDescriptorIdentity against the identity
recomputed from the bundle recipe, so the split-leg publish cannot
spend a Fulcio cert, a Rekor entry, and an OCI push on evidence its
own verifier rejects as historical-only; the recipe-health
constraints_wellformed dimension mirrors constraints.Evaluate's
node-set dispatch (new exported ValidateGPUNodesLabelValue) so the
negated NVIDIA#1755 form this PR introduces no longer falsely fails every
GKE recipe; presence.yaml drops the unsuffixed GKE dashboard entries
(profiled runs land on -gpustack-<value> routes, AKS precedent) and
recipe-health.md is regenerated; the secure-access GPU probe's
PATH/LD_LIBRARY_PATH exports use ${VAR:+${VAR}:} guards so an unset
variable cannot leave a leading empty (CWD) search-path entry; the
pushed bundle's OCI description derivation is extracted and
table-tested; docs: the ADR's normative /v2 POST example uses the
shipped gcp-managed value, api-reference scopes the /v1 rejection to
/v1/recipe and /v1/query (/v1/bundle rejects only profile-bearing
bodies), the attestation package godoc covers the v2 predicate, and
the gke-gpu-setup retrofit procedure now switches the pool's driver
mode to gpu-driver-version=disabled (node-pools update --accelerator)
before relying on the standalone installer — Google's
nvidia-driver-installer DaemonSet ignores automatic-install pools, so
deploying it over a gpu-driver-version=default pool left replacement
nodes driverless.

Live-qualification round: gcp-managed qualification on a GKE H100
cluster came back deployment 4/4 and conformance 11/11 green (NCCL
all-reduce 338 GB/s; secure-accelerator-access passed both intents),
but every inference-perf vLLM worker crash-looped with "Failed to
infer device type" — GKE's managed device plugin injects the
/usr/local/nvidia driver mount through its device-plugin Allocate
response without setting LD_LIBRARY_PATH, so libcuda.so.1 is invisible
to the dynamic loader (A/B on the crashing worker:
LD_LIBRARY_PATH=/usr/local/nvidia/lib64 alone flips torch CUDA to
available). Both Dynamo deployment templates now wrap the worker
command in a shell that appends /usr/local/nvidia/lib64 to
LD_LIBRARY_PATH with the ${VAR:+} guard — a pod-level env override
would clobber the image's own LD_LIBRARY_PATH (nixl/ucx/cuda entries),
and the append is a no-op under the operator toolkit flow, so no
platform branching is needed (pinned by
TestParseDynamoTemplate_WorkerDriverLibPathAppend). Review round: the
GKE retrofit runbook reorders to driver-path-first/label-last —
installer, driver-mode switch, verification, then the label handoff —
because the label-first order left autoscaled/repaired nodes
driverless until the driver-mode switch, and it documents rollback
(remove the label); the evidence-refresh doc makes --platform
conditional on the target leaf's criteria and records that legacy v1
pointers carry no profile field (a v1->v2 conversion selects the
profile from the target cluster, not the pointer); the
allocation-policy resolver suppresses the warn-and-prefer diagnostic
under a declared external advertiser, whose branch aggregates both
operator components; the data-extension heading drops its stale
"(AKS example)" suffix (no inbound anchor links); a pointer_test
comment references the real coherence matrix
(TestValidateBundleProfileCoherence).

Refs NVIDIA#1761, NVIDIA#1755, NVIDIA#1716, NVIDIA#1327

Signed-off-by: Yuan Chen <yuanchen97@gmail.com>
yuanchen8911 added a commit to yuanchen8911/aicr that referenced this pull request Aug 5, 2026
Land the GKE adopter and the profile extensions ADR-015 staged for it
(issue NVIDIA#1761 rollout PR 3), completing the three-PR series after the
generic core (NVIDIA#1933) and the AKS adopter (NVIDIA#1967).

GKE conversion: recipes/overlays/gke-cos.yaml moves to aicr.run/v1alpha3
and declares gpuStack with two values, named after the AKS convention.
gcp-managed (the default) declares advertiser: external, disables the
operator's plugin, and carries the negated NVIDIA#1755 node-set constraint
(no GPU node may carry gke-no-default-nvidia-gpu-device-plugin) — the
default GKE cluster shape, making the previously unsupported "GKE's
managed plugin owns nvidia.com/gpu" state declarable and verifiable;
operator-managed keeps the GPU Operator's device plugin as the sole
nvidia.com/gpu advertiser with the positive constraint. The overlay's
readiness constraint from NVIDIA#2000 relocates into the profile values, as
forecast when it landed; every GKE-COS leaf inherits the declaration
unchanged. operator-selfdriver and the NVIDIA#1716 installer stay deferred on
DD5's durable signal.

Canonical NVIDIA#1327 descriptor: the policy path vocabulary that existed as
three private copies (pkg/bundler, pkg/validator/v1, pkg/recipe) moves
to the new dependency-neutral pkg/allocpolicy — one descriptor and one
tuple-coherence evaluator, consumed by profile validation, the hydrating
artifact gate, bundler enforcement, and validation-time policy
resolution. The descriptor is append-only; its deterministic Identity()
feeds evidence currentness.

advertiser "external" activates: the core's three deferral rejections
are replaced with descriptor-backed validation. A profile that owns
advertisement — a declared external advertiser or explicit ownership of
a non-synthetic selector path — triggers the recomputed closure:
RecipeResult.EffectiveLockSet() joins every enabled descriptor
component's selector paths (plus synthetic presence) to the declared
ownedPaths, and the bundle/mirror lock, the argocd-helm render guard,
and the hydrating gate all consume the effective set. The closure is
recomputed at every boundary, never persisted. The hydrating gate runs
the shared coherence rules (external + devicePlugin.enabled=true and
external + DRA gpus.enabled=true reject). Profiles that do not own
advertisement (the AKS shape) keep today's WARN semantics — locks follow
ownership. ResolveGPUAllocationPolicy counts a declared external
advertiser as THE advertiser in the exactly-one invariant; the resolved
policy value stays device-plugin-extended-resource.

Evidence predicate v2: profile-bearing recipes emit
https://aicr.run/recipe-evidence/v2 carrying {selection, advertiser,
policyDescriptorIdentity}; unprofiled evidence stays byte-identical on
v1. Producers, the pointer, all four verifier checkpoints, the
publisher, and testgrid-publish enforce the bidirectional contract, and
the verifier rejects a descriptor-identity mismatch as historical-only.
No committed evidence pointer is profiled yet, and none is
rejected by the new checkpoints; the two committed GKE pointers
(h100-gke-cos-training) are v1 evidence over the now-profiled recipe,
so the cut-over demotes them to historical — recipe-health Evidence
links move to pending until the planned per-value v2 re-signing.

Fail-closed behavior changes, all per the ADR: GKE compositions now
reject on /v1 endpoints (profile cut-over); snapshot-based GKE
generation requires NodeTopology label readings (an unavailable reading
fails with the distinguishable DD1 diagnostic); a bundlers subset
omitting a closure-locked component (nvidia-dra-driver-gpu on GKE)
fails.

Docs: a new integrator page, docs/integrator/gke-gpu-setup.md, mirrors
the AKS GPU Setup structure (two-mode table, snapshot-verification
story, qualification and selection-vs-verification matrices, default
and operator-managed procedures, troubleshooting); the component
catalog keeps the ownership model and links the procedures there.
Also updated: validation, cli-reference, api-reference (GKE /v1
cut-over; batch example moved to /v2), automation,
recipe-development, data-extension, contributor/recipe,
contributor/validator. GCP UAT asserts pin v1alpha3 + selectedProfile.

Default flip + review rounds: gcp-managed is the default because the
opt-out label forfeits GKE's managed driver install (the install is
finalized by an init container of the same kube-system DaemonSet the
label disables), so operator-managed requires Google's standalone
nvidia-driver-installer DaemonSet with pools created
gpu-driver-version=disabled; the docs and ADR record the amended
reality. Review fixes: descriptor currentness now runs on the
Sigstore-verified predicate path too, and the verifier binds the
predicate profile block (presence, selection, advertiser) to the
manifest-verified recipe; policyDescriptorIdentity is recipe-scoped
(allocpolicy.IdentityFor over the recipe's closure-contributing
entries) so an unrelated descriptor expansion cannot invalidate
evidence; BuildPointer and Publish validate bundle-profile/predicate
coherence before any registry side effect; testgrid-publish runs the
shared predicate-type coherence check; ResolveGPUAllocationPolicy
validates the advertiser vocabulary for typed Go callers; the OpenAPI
selectedProfile schema and Go facade docs admit advertiser: external.

Earlier round: rebased onto NVIDIA#2047 (merged), which had withdrawn the NVIDIA#1755
gate and removed the opt-out label from the GCP UAT cluster-config —
the rebased overlay, catalog, validation, and ADR DD2 text now state
the final per-value enforcement the profile lands, and the label-free
UAT pool matches the gcp-managed default. Under advertiser: external,
ResolveGPUAllocationPolicy aggregates devicePlugin.enabled across
every enabled operator component (gpu-operator AND gpu-operator-ocp,
OR semantics mirroring the recipe-side gate) instead of the
warn-and-prefer single read; the publish-time bundle coherence gate
also compares the advertiser; both statement builders validate
predicate-type coherence producer-side; the pushed bundle's OCI
description derives from the actual predicate type; allocpolicy
identity canonicalization merges duplicate-component entries (pinned
identities unchanged); EffectiveLockSet no longer panics for typed
callers that skipped artifact validation; ValidateAdvertiser errors
propagate un-double-wrapped; the GKE-COS driver-absent remediation
(bundler + client copies) is per-profile-value; stale reserved-
advertiser and pre-profile claims swept from code comments and docs.

This round: the hydrating coherence gate fails closed on an enabled DRA
component whose resources.gpus.enabled is absent (the pinned chart's
declared default is true, so treating absence as unknown let a
gcp-managed artifact with custom values omitting the stock pin pass
generation and bundling while deploying a dual advertiser), mirroring
the validation-time resolver; the publish/pointer coherence gate also
compares the predicate's policyDescriptorIdentity against the identity
recomputed from the bundle recipe, so the split-leg publish cannot
spend a Fulcio cert, a Rekor entry, and an OCI push on evidence its
own verifier rejects as historical-only; the recipe-health
constraints_wellformed dimension mirrors constraints.Evaluate's
node-set dispatch (new exported ValidateGPUNodesLabelValue) so the
negated NVIDIA#1755 form this PR introduces no longer falsely fails every
GKE recipe; presence.yaml drops the unsuffixed GKE dashboard entries
(profiled runs land on -gpustack-<value> routes, AKS precedent) and
recipe-health.md is regenerated; the secure-access GPU probe's
PATH/LD_LIBRARY_PATH exports use ${VAR:+${VAR}:} guards so an unset
variable cannot leave a leading empty (CWD) search-path entry; the
pushed bundle's OCI description derivation is extracted and
table-tested; docs: the ADR's normative /v2 POST example uses the
shipped gcp-managed value, api-reference scopes the /v1 rejection to
/v1/recipe and /v1/query (/v1/bundle rejects only profile-bearing
bodies), the attestation package godoc covers the v2 predicate, and
the gke-gpu-setup retrofit procedure now switches the pool's driver
mode to gpu-driver-version=disabled (node-pools update --accelerator)
before relying on the standalone installer — Google's
nvidia-driver-installer DaemonSet ignores automatic-install pools, so
deploying it over a gpu-driver-version=default pool left replacement
nodes driverless.

Live-qualification round: gcp-managed qualification on a GKE H100
cluster came back deployment 4/4 and conformance 11/11 green (NCCL
all-reduce 338 GB/s; secure-accelerator-access passed both intents),
but every inference-perf vLLM worker crash-looped with "Failed to
infer device type" — GKE's managed device plugin injects the
/usr/local/nvidia driver mount through its device-plugin Allocate
response without setting LD_LIBRARY_PATH, so libcuda.so.1 is invisible
to the dynamic loader (A/B on the crashing worker:
LD_LIBRARY_PATH=/usr/local/nvidia/lib64 alone flips torch CUDA to
available). Both Dynamo deployment templates now wrap the worker
command in a shell that appends /usr/local/nvidia/lib64 to
LD_LIBRARY_PATH with the ${VAR:+} guard — a pod-level env override
would clobber the image's own LD_LIBRARY_PATH (nixl/ucx/cuda entries),
and the append is a no-op under the operator toolkit flow, so no
platform branching is needed (pinned by
TestParseDynamoTemplate_WorkerDriverLibPathAppend). Review round: the
GKE retrofit runbook reorders to driver-path-first/label-last —
installer, driver-mode switch, verification, then the label handoff —
because the label-first order left autoscaled/repaired nodes
driverless until the driver-mode switch, and it documents rollback
(remove the label); the evidence-refresh doc makes --platform
conditional on the target leaf's criteria and records that legacy v1
pointers carry no profile field (a v1->v2 conversion selects the
profile from the target cluster, not the pointer); the
allocation-policy resolver suppresses the warn-and-prefer diagnostic
under a declared external advertiser, whose branch aggregates both
operator components; the data-extension heading drops its stale
"(AKS example)" suffix (no inbound anchor links); a pointer_test
comment references the real coherence matrix
(TestValidateBundleProfileCoherence).

Refs NVIDIA#1761, NVIDIA#1755, NVIDIA#1716, NVIDIA#1327

Signed-off-by: Yuan Chen <yuanchen97@gmail.com>
yuanchen8911 added a commit to yuanchen8911/aicr that referenced this pull request Aug 5, 2026
Land the GKE adopter and the profile extensions ADR-015 staged for it
(issue NVIDIA#1761 rollout PR 3), completing the three-PR series after the
generic core (NVIDIA#1933) and the AKS adopter (NVIDIA#1967).

GKE conversion: recipes/overlays/gke-cos.yaml moves to aicr.run/v1alpha3
and declares gpuStack with two values, named after the AKS convention.
gcp-managed (the default) declares advertiser: external, disables the
operator's plugin, and carries the negated NVIDIA#1755 node-set constraint
(no GPU node may carry gke-no-default-nvidia-gpu-device-plugin) — the
default GKE cluster shape, making the previously unsupported "GKE's
managed plugin owns nvidia.com/gpu" state declarable and verifiable;
operator-managed keeps the GPU Operator's device plugin as the sole
nvidia.com/gpu advertiser with the positive constraint. The overlay's
readiness constraint from NVIDIA#2000 relocates into the profile values, as
forecast when it landed; every GKE-COS leaf inherits the declaration
unchanged. operator-selfdriver and the NVIDIA#1716 installer stay deferred on
DD5's durable signal.

Canonical NVIDIA#1327 descriptor: the policy path vocabulary that existed as
three private copies (pkg/bundler, pkg/validator/v1, pkg/recipe) moves
to the new dependency-neutral pkg/allocpolicy — one descriptor and one
tuple-coherence evaluator, consumed by profile validation, the hydrating
artifact gate, bundler enforcement, and validation-time policy
resolution. The descriptor is append-only; its deterministic Identity()
feeds evidence currentness.

advertiser "external" activates: the core's three deferral rejections
are replaced with descriptor-backed validation. A profile that owns
advertisement — a declared external advertiser or explicit ownership of
a non-synthetic selector path — triggers the recomputed closure:
RecipeResult.EffectiveLockSet() joins every enabled descriptor
component's selector paths (plus synthetic presence) to the declared
ownedPaths, and the bundle/mirror lock, the argocd-helm render guard,
and the hydrating gate all consume the effective set. The closure is
recomputed at every boundary, never persisted. The hydrating gate runs
the shared coherence rules (external + devicePlugin.enabled=true and
external + DRA gpus.enabled=true reject). Profiles that do not own
advertisement (the AKS shape) keep today's WARN semantics — locks follow
ownership. ResolveGPUAllocationPolicy counts a declared external
advertiser as THE advertiser in the exactly-one invariant; the resolved
policy value stays device-plugin-extended-resource.

Evidence predicate v2: profile-bearing recipes emit
https://aicr.run/recipe-evidence/v2 carrying {selection, advertiser,
policyDescriptorIdentity}; unprofiled evidence stays byte-identical on
v1. Producers, the pointer, all four verifier checkpoints, the
publisher, and testgrid-publish enforce the bidirectional contract, and
the verifier rejects a descriptor-identity mismatch as historical-only.
No committed evidence pointer is profiled yet, and none is
rejected by the new checkpoints; the two committed GKE pointers
(h100-gke-cos-training) are v1 evidence over the now-profiled recipe,
so the cut-over demotes them to historical — recipe-health Evidence
links move to pending until the planned per-value v2 re-signing.

Fail-closed behavior changes, all per the ADR: GKE compositions now
reject on /v1 endpoints (profile cut-over); snapshot-based GKE
generation requires NodeTopology label readings (an unavailable reading
fails with the distinguishable DD1 diagnostic); a bundlers subset
omitting a closure-locked component (nvidia-dra-driver-gpu on GKE)
fails.

Docs: a new integrator page, docs/integrator/gke-gpu-setup.md, mirrors
the AKS GPU Setup structure (two-mode table, snapshot-verification
story, qualification and selection-vs-verification matrices, default
and operator-managed procedures, troubleshooting); the component
catalog keeps the ownership model and links the procedures there.
Also updated: validation, cli-reference, api-reference (GKE /v1
cut-over; batch example moved to /v2), automation,
recipe-development, data-extension, contributor/recipe,
contributor/validator. GCP UAT asserts pin v1alpha3 + selectedProfile.

Default flip + review rounds: gcp-managed is the default because the
opt-out label forfeits GKE's managed driver install (the install is
finalized by an init container of the same kube-system DaemonSet the
label disables), so operator-managed requires Google's standalone
nvidia-driver-installer DaemonSet with pools created
gpu-driver-version=disabled; the docs and ADR record the amended
reality. Review fixes: descriptor currentness now runs on the
Sigstore-verified predicate path too, and the verifier binds the
predicate profile block (presence, selection, advertiser) to the
manifest-verified recipe; policyDescriptorIdentity is recipe-scoped
(allocpolicy.IdentityFor over the recipe's closure-contributing
entries) so an unrelated descriptor expansion cannot invalidate
evidence; BuildPointer and Publish validate bundle-profile/predicate
coherence before any registry side effect; testgrid-publish runs the
shared predicate-type coherence check; ResolveGPUAllocationPolicy
validates the advertiser vocabulary for typed Go callers; the OpenAPI
selectedProfile schema and Go facade docs admit advertiser: external.

Earlier round: rebased onto NVIDIA#2047 (merged), which had withdrawn the NVIDIA#1755
gate and removed the opt-out label from the GCP UAT cluster-config —
the rebased overlay, catalog, validation, and ADR DD2 text now state
the final per-value enforcement the profile lands, and the label-free
UAT pool matches the gcp-managed default. Under advertiser: external,
ResolveGPUAllocationPolicy aggregates devicePlugin.enabled across
every enabled operator component (gpu-operator AND gpu-operator-ocp,
OR semantics mirroring the recipe-side gate) instead of the
warn-and-prefer single read; the publish-time bundle coherence gate
also compares the advertiser; both statement builders validate
predicate-type coherence producer-side; the pushed bundle's OCI
description derives from the actual predicate type; allocpolicy
identity canonicalization merges duplicate-component entries (pinned
identities unchanged); EffectiveLockSet no longer panics for typed
callers that skipped artifact validation; ValidateAdvertiser errors
propagate un-double-wrapped; the GKE-COS driver-absent remediation
(bundler + client copies) is per-profile-value; stale reserved-
advertiser and pre-profile claims swept from code comments and docs.

This round: the hydrating coherence gate fails closed on an enabled DRA
component whose resources.gpus.enabled is absent (the pinned chart's
declared default is true, so treating absence as unknown let a
gcp-managed artifact with custom values omitting the stock pin pass
generation and bundling while deploying a dual advertiser), mirroring
the validation-time resolver; the publish/pointer coherence gate also
compares the predicate's policyDescriptorIdentity against the identity
recomputed from the bundle recipe, so the split-leg publish cannot
spend a Fulcio cert, a Rekor entry, and an OCI push on evidence its
own verifier rejects as historical-only; the recipe-health
constraints_wellformed dimension mirrors constraints.Evaluate's
node-set dispatch (new exported ValidateGPUNodesLabelValue) so the
negated NVIDIA#1755 form this PR introduces no longer falsely fails every
GKE recipe; presence.yaml drops the unsuffixed GKE dashboard entries
(profiled runs land on -gpustack-<value> routes, AKS precedent) and
recipe-health.md is regenerated; the secure-access GPU probe's
PATH/LD_LIBRARY_PATH exports use ${VAR:+${VAR}:} guards so an unset
variable cannot leave a leading empty (CWD) search-path entry; the
pushed bundle's OCI description derivation is extracted and
table-tested; docs: the ADR's normative /v2 POST example uses the
shipped gcp-managed value, api-reference scopes the /v1 rejection to
/v1/recipe and /v1/query (/v1/bundle rejects only profile-bearing
bodies), the attestation package godoc covers the v2 predicate, and
the gke-gpu-setup retrofit procedure now switches the pool's driver
mode to gpu-driver-version=disabled (node-pools update --accelerator)
before relying on the standalone installer — Google's
nvidia-driver-installer DaemonSet ignores automatic-install pools, so
deploying it over a gpu-driver-version=default pool left replacement
nodes driverless.

Live-qualification round: gcp-managed qualification on a GKE H100
cluster came back deployment 4/4 and conformance 11/11 green (NCCL
all-reduce 338 GB/s; secure-accelerator-access passed both intents),
but every inference-perf vLLM worker crash-looped with "Failed to
infer device type" — GKE's managed device plugin injects the
/usr/local/nvidia driver mount through its device-plugin Allocate
response without setting LD_LIBRARY_PATH, so libcuda.so.1 is invisible
to the dynamic loader (A/B on the crashing worker:
LD_LIBRARY_PATH=/usr/local/nvidia/lib64 alone flips torch CUDA to
available). Both Dynamo deployment templates now wrap the worker
command in a shell that appends /usr/local/nvidia/lib64 to
LD_LIBRARY_PATH with the ${VAR:+} guard — a pod-level env override
would clobber the image's own LD_LIBRARY_PATH (nixl/ucx/cuda entries),
and the append is a no-op under the operator toolkit flow, so no
platform branching is needed (pinned by
TestParseDynamoTemplate_WorkerDriverLibPathAppend). Review round: the
GKE retrofit runbook reorders to driver-path-first/label-last —
installer, driver-mode switch, verification, then the label handoff —
because the label-first order left autoscaled/repaired nodes
driverless until the driver-mode switch, and it documents rollback
(remove the label); the evidence-refresh doc makes --platform
conditional on the target leaf's criteria and records that legacy v1
pointers carry no profile field (a v1->v2 conversion selects the
profile from the target cluster, not the pointer); the
allocation-policy resolver suppresses the warn-and-prefer diagnostic
under a declared external advertiser, whose branch aggregates both
operator components; the data-extension heading drops its stale
"(AKS example)" suffix (no inbound anchor links); a pointer_test
comment references the real coherence matrix
(TestValidateBundleProfileCoherence).

Refs NVIDIA#1761, NVIDIA#1755, NVIDIA#1716, NVIDIA#1327

Signed-off-by: Yuan Chen <yuanchen97@gmail.com>
yuanchen8911 added a commit to yuanchen8911/aicr that referenced this pull request Aug 5, 2026
Land the GKE adopter and the profile extensions ADR-015 staged for it
(issue NVIDIA#1761 rollout PR 3), completing the three-PR series after the
generic core (NVIDIA#1933) and the AKS adopter (NVIDIA#1967).

GKE conversion: recipes/overlays/gke-cos.yaml moves to aicr.run/v1alpha3
and declares gpuStack with two values, named after the AKS convention.
gcp-managed (the default) declares advertiser: external, disables the
operator's plugin, and carries the negated NVIDIA#1755 node-set constraint
(no GPU node may carry gke-no-default-nvidia-gpu-device-plugin) — the
default GKE cluster shape, making the previously unsupported "GKE's
managed plugin owns nvidia.com/gpu" state declarable and verifiable;
operator-managed keeps the GPU Operator's device plugin as the sole
nvidia.com/gpu advertiser with the positive constraint. The overlay's
readiness constraint from NVIDIA#2000 relocates into the profile values, as
forecast when it landed; every GKE-COS leaf inherits the declaration
unchanged. operator-selfdriver and the NVIDIA#1716 installer stay deferred on
DD5's durable signal.

Canonical NVIDIA#1327 descriptor: the policy path vocabulary that existed as
three private copies (pkg/bundler, pkg/validator/v1, pkg/recipe) moves
to the new dependency-neutral pkg/allocpolicy — one descriptor and one
tuple-coherence evaluator, consumed by profile validation, the hydrating
artifact gate, bundler enforcement, and validation-time policy
resolution. The descriptor is append-only; its deterministic Identity()
feeds evidence currentness.

advertiser "external" activates: the core's three deferral rejections
are replaced with descriptor-backed validation. A profile that owns
advertisement — a declared external advertiser or explicit ownership of
a non-synthetic selector path — triggers the recomputed closure:
RecipeResult.EffectiveLockSet() joins every enabled descriptor
component's selector paths (plus synthetic presence) to the declared
ownedPaths, and the bundle/mirror lock, the argocd-helm render guard,
and the hydrating gate all consume the effective set. The closure is
recomputed at every boundary, never persisted. The hydrating gate runs
the shared coherence rules (external + devicePlugin.enabled=true and
external + DRA gpus.enabled=true reject). Profiles that do not own
advertisement (the AKS shape) keep today's WARN semantics — locks follow
ownership. ResolveGPUAllocationPolicy counts a declared external
advertiser as THE advertiser in the exactly-one invariant; the resolved
policy value stays device-plugin-extended-resource.

Evidence predicate v2: profile-bearing recipes emit
https://aicr.run/recipe-evidence/v2 carrying {selection, advertiser,
policyDescriptorIdentity}; unprofiled evidence stays byte-identical on
v1. Producers, the pointer, all four verifier checkpoints, the
publisher, and testgrid-publish enforce the bidirectional contract, and
the verifier rejects a descriptor-identity mismatch as historical-only.
No committed evidence pointer is profiled yet, and none is
rejected by the new checkpoints; the two committed GKE pointers
(h100-gke-cos-training) are v1 evidence over the now-profiled recipe,
so the cut-over demotes them to historical — recipe-health Evidence
links move to pending until the planned per-value v2 re-signing.

Fail-closed behavior changes, all per the ADR: GKE compositions now
reject on /v1 endpoints (profile cut-over); snapshot-based GKE
generation requires NodeTopology label readings (an unavailable reading
fails with the distinguishable DD1 diagnostic); a bundlers subset
omitting a closure-locked component (nvidia-dra-driver-gpu on GKE)
fails.

Docs: a new integrator page, docs/integrator/gke-gpu-setup.md, mirrors
the AKS GPU Setup structure (two-mode table, snapshot-verification
story, qualification and selection-vs-verification matrices, default
and operator-managed procedures, troubleshooting); the component
catalog keeps the ownership model and links the procedures there.
Also updated: validation, cli-reference, api-reference (GKE /v1
cut-over; batch example moved to /v2), automation,
recipe-development, data-extension, contributor/recipe,
contributor/validator. GCP UAT asserts pin v1alpha3 + selectedProfile.

Default flip + review rounds: gcp-managed is the default because the
opt-out label forfeits GKE's managed driver install (the install is
finalized by an init container of the same kube-system DaemonSet the
label disables), so operator-managed requires Google's standalone
nvidia-driver-installer DaemonSet with pools created
gpu-driver-version=disabled; the docs and ADR record the amended
reality. Review fixes: descriptor currentness now runs on the
Sigstore-verified predicate path too, and the verifier binds the
predicate profile block (presence, selection, advertiser) to the
manifest-verified recipe; policyDescriptorIdentity is recipe-scoped
(allocpolicy.IdentityFor over the recipe's closure-contributing
entries) so an unrelated descriptor expansion cannot invalidate
evidence; BuildPointer and Publish validate bundle-profile/predicate
coherence before any registry side effect; testgrid-publish runs the
shared predicate-type coherence check; ResolveGPUAllocationPolicy
validates the advertiser vocabulary for typed Go callers; the OpenAPI
selectedProfile schema and Go facade docs admit advertiser: external.

Earlier round: rebased onto NVIDIA#2047 (merged), which had withdrawn the NVIDIA#1755
gate and removed the opt-out label from the GCP UAT cluster-config —
the rebased overlay, catalog, validation, and ADR DD2 text now state
the final per-value enforcement the profile lands, and the label-free
UAT pool matches the gcp-managed default. Under advertiser: external,
ResolveGPUAllocationPolicy aggregates devicePlugin.enabled across
every enabled operator component (gpu-operator AND gpu-operator-ocp,
OR semantics mirroring the recipe-side gate) instead of the
warn-and-prefer single read; the publish-time bundle coherence gate
also compares the advertiser; both statement builders validate
predicate-type coherence producer-side; the pushed bundle's OCI
description derives from the actual predicate type; allocpolicy
identity canonicalization merges duplicate-component entries (pinned
identities unchanged); EffectiveLockSet no longer panics for typed
callers that skipped artifact validation; ValidateAdvertiser errors
propagate un-double-wrapped; the GKE-COS driver-absent remediation
(bundler + client copies) is per-profile-value; stale reserved-
advertiser and pre-profile claims swept from code comments and docs.

This round: the hydrating coherence gate fails closed on an enabled DRA
component whose resources.gpus.enabled is absent (the pinned chart's
declared default is true, so treating absence as unknown let a
gcp-managed artifact with custom values omitting the stock pin pass
generation and bundling while deploying a dual advertiser), mirroring
the validation-time resolver; the publish/pointer coherence gate also
compares the predicate's policyDescriptorIdentity against the identity
recomputed from the bundle recipe, so the split-leg publish cannot
spend a Fulcio cert, a Rekor entry, and an OCI push on evidence its
own verifier rejects as historical-only; the recipe-health
constraints_wellformed dimension mirrors constraints.Evaluate's
node-set dispatch (new exported ValidateGPUNodesLabelValue) so the
negated NVIDIA#1755 form this PR introduces no longer falsely fails every
GKE recipe; presence.yaml drops the unsuffixed GKE dashboard entries
(profiled runs land on -gpustack-<value> routes, AKS precedent) and
recipe-health.md is regenerated; the secure-access GPU probe's
PATH/LD_LIBRARY_PATH exports use ${VAR:+${VAR}:} guards so an unset
variable cannot leave a leading empty (CWD) search-path entry; the
pushed bundle's OCI description derivation is extracted and
table-tested; docs: the ADR's normative /v2 POST example uses the
shipped gcp-managed value, api-reference scopes the /v1 rejection to
/v1/recipe and /v1/query (/v1/bundle rejects only profile-bearing
bodies), the attestation package godoc covers the v2 predicate, and
the gke-gpu-setup retrofit procedure now switches the pool's driver
mode to gpu-driver-version=disabled (node-pools update --accelerator)
before relying on the standalone installer — Google's
nvidia-driver-installer DaemonSet ignores automatic-install pools, so
deploying it over a gpu-driver-version=default pool left replacement
nodes driverless.

Live-qualification round: gcp-managed qualification on a GKE H100
cluster came back deployment 4/4 and conformance 11/11 green (NCCL
all-reduce 338 GB/s; secure-accelerator-access passed both intents),
but every inference-perf vLLM worker crash-looped with "Failed to
infer device type" — GKE's managed device plugin injects the
/usr/local/nvidia driver mount through its device-plugin Allocate
response without setting LD_LIBRARY_PATH, so libcuda.so.1 is invisible
to the dynamic loader (A/B on the crashing worker:
LD_LIBRARY_PATH=/usr/local/nvidia/lib64 alone flips torch CUDA to
available). Both Dynamo deployment templates now wrap the worker
command in a shell that appends /usr/local/nvidia/lib64 to
LD_LIBRARY_PATH with the ${VAR:+} guard — a pod-level env override
would clobber the image's own LD_LIBRARY_PATH (nixl/ucx/cuda entries),
and the append is a no-op under the operator toolkit flow, so no
platform branching is needed (pinned by
TestParseDynamoTemplate_WorkerDriverLibPathAppend). Review round: the
GKE retrofit runbook reorders to driver-path-first/label-last —
installer, driver-mode switch, verification, then the label handoff —
because the label-first order left autoscaled/repaired nodes
driverless until the driver-mode switch, and it documents rollback
(remove the label); the evidence-refresh doc makes --platform
conditional on the target leaf's criteria and records that legacy v1
pointers carry no profile field (a v1->v2 conversion selects the
profile from the target cluster, not the pointer); the
allocation-policy resolver suppresses the warn-and-prefer diagnostic
under a declared external advertiser, whose branch aggregates both
operator components; the data-extension heading drops its stale
"(AKS example)" suffix (no inbound anchor links); a pointer_test
comment references the real coherence matrix
(TestValidateBundleProfileCoherence).

Refs NVIDIA#1761, NVIDIA#1755, NVIDIA#1716, NVIDIA#1327

Signed-off-by: Yuan Chen <yuanchen97@gmail.com>
yuanchen8911 added a commit to yuanchen8911/aicr that referenced this pull request Aug 5, 2026
Land the GKE adopter and the profile extensions ADR-015 staged for it
(issue NVIDIA#1761 rollout PR 3), completing the three-PR series after the
generic core (NVIDIA#1933) and the AKS adopter (NVIDIA#1967).

GKE conversion: recipes/overlays/gke-cos.yaml moves to aicr.run/v1alpha3
and declares gpuStack with two values, named after the AKS convention.
gcp-managed (the default) declares advertiser: external, disables the
operator's plugin, and carries the negated NVIDIA#1755 node-set constraint
(no GPU node may carry gke-no-default-nvidia-gpu-device-plugin) — the
default GKE cluster shape, making the previously unsupported "GKE's
managed plugin owns nvidia.com/gpu" state declarable and verifiable;
operator-managed keeps the GPU Operator's device plugin as the sole
nvidia.com/gpu advertiser with the positive constraint. The overlay's
readiness constraint from NVIDIA#2000 relocates into the profile values, as
forecast when it landed; every GKE-COS leaf inherits the declaration
unchanged. operator-selfdriver and the NVIDIA#1716 installer stay deferred on
DD5's durable signal.

Canonical NVIDIA#1327 descriptor: the policy path vocabulary that existed as
three private copies (pkg/bundler, pkg/validator/v1, pkg/recipe) moves
to the new dependency-neutral pkg/allocpolicy — one descriptor and one
tuple-coherence evaluator, consumed by profile validation, the hydrating
artifact gate, bundler enforcement, and validation-time policy
resolution. The descriptor is append-only; its deterministic Identity()
feeds evidence currentness.

advertiser "external" activates: the core's three deferral rejections
are replaced with descriptor-backed validation. A profile that owns
advertisement — a declared external advertiser or explicit ownership of
a non-synthetic selector path — triggers the recomputed closure:
RecipeResult.EffectiveLockSet() joins every enabled descriptor
component's selector paths (plus synthetic presence) to the declared
ownedPaths, and the bundle/mirror lock, the argocd-helm render guard,
and the hydrating gate all consume the effective set. The closure is
recomputed at every boundary, never persisted. The hydrating gate runs
the shared coherence rules for EVERY closure-triggering profile: the
single evaluator (allocpolicy.CheckCoherence) carries the full NVIDIA#1327
tuple verdicts for both advertiser shapes — under external, dual
advertisement (devicePlugin.enabled=true, DRA gpus.enabled=true) and
the inert waiver reject; under the empty (operator-managed) shape it
mirrors the resolver's non-external contract verbatim (chart guard,
dual advertisement, no advertiser, inert waiver) — and the resolver
delegates its verdicts to the same evaluator, so gate and resolver
cannot drift. Profiles that do not own
advertisement (the AKS shape) keep today's WARN semantics — locks follow
ownership. ResolveGPUAllocationPolicy counts a declared external
advertiser as THE advertiser in the exactly-one invariant; the resolved
policy value stays device-plugin-extended-resource.

Evidence predicate v2: profile-bearing recipes emit
https://aicr.run/recipe-evidence/v2 carrying {selection, advertiser,
policyDescriptorIdentity}; unprofiled evidence stays byte-identical on
v1. Producers, the pointer, all four verifier checkpoints, the
publisher, and testgrid-publish enforce the bidirectional contract, and
the verifier rejects a descriptor-identity mismatch as historical-only.
No committed evidence pointer is profiled yet, and none is
rejected by the new checkpoints; the two committed GKE pointers
(h100-gke-cos-training) are v1 evidence over the now-profiled recipe,
so the cut-over demotes them to historical — recipe-health Evidence
links move to pending until the planned per-value v2 re-signing.

Fail-closed behavior changes, all per the ADR: GKE compositions now
reject on /v1 endpoints (profile cut-over); snapshot-based GKE
generation requires NodeTopology label readings (an unavailable reading
fails with the distinguishable DD1 diagnostic); a bundlers subset
omitting a closure-locked component (nvidia-dra-driver-gpu on GKE)
fails.

Docs: a new integrator page, docs/integrator/gke-gpu-setup.md, mirrors
the AKS GPU Setup structure (two-mode table, snapshot-verification
story, qualification and selection-vs-verification matrices, default
and operator-managed procedures, troubleshooting); the component
catalog keeps the ownership model and links the procedures there.
Also updated: validation, cli-reference, api-reference (GKE /v1
cut-over; batch example moved to /v2), automation,
recipe-development, data-extension, contributor/recipe,
contributor/validator. GCP UAT asserts pin v1alpha3 + selectedProfile.

Default flip + review rounds: gcp-managed is the default because the
opt-out label forfeits GKE's managed driver install (the install is
finalized by an init container of the same kube-system DaemonSet the
label disables), so operator-managed requires Google's standalone
nvidia-driver-installer DaemonSet with pools created
gpu-driver-version=disabled; the docs and ADR record the amended
reality. Review fixes: descriptor currentness now runs on the
Sigstore-verified predicate path too, and the verifier binds the
predicate profile block (presence, selection, advertiser) to the
manifest-verified recipe; policyDescriptorIdentity is recipe-scoped
(allocpolicy.IdentityFor over the recipe's closure-contributing
entries) so an unrelated descriptor expansion cannot invalidate
evidence; BuildPointer and Publish validate bundle-profile/predicate
coherence before any registry side effect; testgrid-publish runs the
shared predicate-type coherence check; ResolveGPUAllocationPolicy
validates the advertiser vocabulary for typed Go callers; the OpenAPI
selectedProfile schema and Go facade docs admit advertiser: external.

Earlier round: rebased onto NVIDIA#2047 (merged), which had withdrawn the NVIDIA#1755
gate and removed the opt-out label from the GCP UAT cluster-config —
the rebased overlay, catalog, validation, and ADR DD2 text now state
the final per-value enforcement the profile lands, and the label-free
UAT pool matches the gcp-managed default. Under advertiser: external,
ResolveGPUAllocationPolicy aggregates devicePlugin.enabled across
every enabled operator component (gpu-operator AND gpu-operator-ocp,
OR semantics mirroring the recipe-side gate) instead of the
warn-and-prefer single read; the publish-time bundle coherence gate
also compares the advertiser; both statement builders validate
predicate-type coherence producer-side; the pushed bundle's OCI
description derives from the actual predicate type; allocpolicy
identity canonicalization merges duplicate-component entries (pinned
identities unchanged); EffectiveLockSet no longer panics for typed
callers that skipped artifact validation; ValidateAdvertiser errors
propagate un-double-wrapped; the GKE-COS driver-absent remediation
(bundler + client copies) is per-profile-value; stale reserved-
advertiser and pre-profile claims swept from code comments and docs.

This round: the hydrating coherence gate fails closed on an enabled DRA
component whose resources.gpus.enabled is absent (the pinned chart's
declared default is true, so treating absence as unknown let a
gcp-managed artifact with custom values omitting the stock pin pass
generation and bundling while deploying a dual advertiser), mirroring
the validation-time resolver; the publish/pointer coherence gate also
compares the predicate's policyDescriptorIdentity against the identity
recomputed from the bundle recipe, so the split-leg publish cannot
spend a Fulcio cert, a Rekor entry, and an OCI push on evidence its
own verifier rejects as historical-only; the recipe-health
constraints_wellformed dimension mirrors constraints.Evaluate's
node-set dispatch (new exported ValidateGPUNodesLabelValue) so the
negated NVIDIA#1755 form this PR introduces no longer falsely fails every
GKE recipe; presence.yaml drops the unsuffixed GKE dashboard entries
(profiled runs land on -gpustack-<value> routes, AKS precedent) and
recipe-health.md is regenerated; the secure-access GPU probe's
PATH/LD_LIBRARY_PATH exports use ${VAR:+${VAR}:} guards so an unset
variable cannot leave a leading empty (CWD) search-path entry; the
pushed bundle's OCI description derivation is extracted and
table-tested; docs: the ADR's normative /v2 POST example uses the
shipped gcp-managed value, api-reference scopes the /v1 rejection to
/v1/recipe and /v1/query (/v1/bundle rejects only profile-bearing
bodies), the attestation package godoc covers the v2 predicate, and
the gke-gpu-setup retrofit procedure now switches the pool's driver
mode to gpu-driver-version=disabled (node-pools update --accelerator)
before relying on the standalone installer — Google's
nvidia-driver-installer DaemonSet ignores automatic-install pools, so
deploying it over a gpu-driver-version=default pool left replacement
nodes driverless.

Live-qualification round: gcp-managed qualification on a GKE H100
cluster came back deployment 4/4 and conformance 11/11 green (NCCL
all-reduce 338 GB/s; secure-accelerator-access passed both intents),
but every inference-perf vLLM worker crash-looped with "Failed to
infer device type" — GKE's managed device plugin injects the
/usr/local/nvidia driver mount through its device-plugin Allocate
response without setting LD_LIBRARY_PATH, so libcuda.so.1 is invisible
to the dynamic loader (A/B on the crashing worker:
LD_LIBRARY_PATH=/usr/local/nvidia/lib64 alone flips torch CUDA to
available). Both Dynamo deployment templates now wrap the worker
command in a shell that appends /usr/local/nvidia/lib64 to
LD_LIBRARY_PATH with the ${VAR:+} guard — a pod-level env override
would clobber the image's own LD_LIBRARY_PATH (nixl/ucx/cuda entries),
and the append is a no-op under the operator toolkit flow, so no
platform branching is needed (pinned by
TestParseDynamoTemplate_WorkerDriverLibPathAppend). Review round: the
GKE retrofit runbook reorders to driver-path-first/label-last —
installer, driver-mode switch, verification, then the label handoff —
because the label-first order left autoscaled/repaired nodes
driverless until the driver-mode switch, and it documents rollback
(remove the label); the evidence-refresh doc makes --platform
conditional on the target leaf's criteria and records that legacy v1
pointers carry no profile field (a v1->v2 conversion selects the
profile from the target cluster, not the pointer); the
allocation-policy resolver suppresses the warn-and-prefer diagnostic
under a declared external advertiser, whose branch aggregates both
operator components; the data-extension heading drops its stale
"(AKS example)" suffix (no inbound anchor links); a pointer_test
comment references the real coherence matrix
(TestValidateBundleProfileCoherence).

Blocking-review round: the hydrating artifact gate applied the shared
tuple evaluation only under a declared external advertiser, so a forged
operator-managed artifact enabling DRA whole-GPU advertisement next to
the operator's plugin (or carrying an inert chart-guard waiver) passed
PrepareAndValidateWithContext and bundled while
ResolveGPUAllocationPolicy rejected the identical state — the
gate/resolver symmetry gap. allocpolicy.CheckCoherence is now the
single full-tuple evaluator for both advertiser shapes (Observation
gains the gpuResourcesEnabledOverride reading and the diagnostic
operator-component name), the resolver's external and non-external
branches both delegate their verdicts to it (policy SELECTION stays in
the resolver), and the gate feeds it the waiver reading plus the
resolver's exact per-advertiser operator semantics (OR-aggregation
under external, warn-and-prefer under empty); regression pins the
forged operator-managed dual-advertisement, chart-guard, and
inert-waiver rejections plus the stock operator-managed happy path.
The argocd-helm render guard needs no equivalent: it locks paths from
EffectiveLockSet and performs no tuple evaluation — the verdict comes
from this gate before generation. Evidence verifier: for InputFormDir
the bundle directory is caller-owned, and checkRecipeIdentity reopened
recipe.yaml by path after CheckInventory hashed it — a TOCTOU window
(CWE-367) in which a swapped file made identity accept bytes the
manifest never covered. The inventory pass now captures the recipe
bytes in the same single read that hashes them
(checkInventoryCaptureRecipe), checkRecipeIdentity consumes those
captured bytes and fails closed when none were captured, and a
deterministic swap regression (inventory accepts recipe A, file
replaced with B, predicate B rejected against the captured A bytes)
pins the closure. Docs: the gke-gpu-setup retrofit handoff is one
coherent sequence — standalone driver ready first, then the opt-out
label, then the GPU Operator deploy-and-wait (new step 5) — explicitly
accepting the brief advertiser-free window instead of a
dual-advertiser overlap, and the "No advertiser at all"
troubleshooting entry now pins the same order instead of advising the
plugin be deployed before the label.

Refs NVIDIA#1761, NVIDIA#1755, NVIDIA#1716, NVIDIA#1327

Signed-off-by: Yuan Chen <yuanchen97@gmail.com>
@yuanchen8911
yuanchen8911 deleted the validate/gke-device-plugin-readiness branch August 19, 2026 15:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

validate: fail-closed check for GKE device-plugin ownership conflict

3 participants