Parent: #1224 (Epic: Recipe health tracking, ADR-009 V1)
Summary
Add the two structural signals that are pure reads of the resolved RecipeResult — no external integration — on top of the #1225 package core.
Scope
chart_pinned (graded): iterate RecipeResult.ComponentRefs; for every Type == ComponentTypeHelm, require a non-empty Version. Empty → fail. Pure in-repo field check — no Helm render.
- ADR-006 layer 1 only: this checks the chart version pin, not image digests. Layer-2 image overrides / layer-3 chart-default sub-images need a render and are out of scope. Kustomize
defaultTag is also out of scope, so a pure-Kustomize recipe scores chart_pinned as a vacuous pass — note this in the signal's Detail and the matrix header so the column isn't misread as "supply-chain pinned".
declared_coverage (descriptor, never graded): from RecipeResult.Validation.{Readiness,Deployment,Performance,Conformance}, record per phase Declared bool, sorted Checks []string, len(Constraints). A minimal recipe (e.g. Slurm dropping K8s checks) must not be penalized.
Key files
pkg/recipe/metadata.go (ComponentRef: Type, Version), pkg/recipe/validation.go (ValidationConfig, ValidationPhase: Checks, Constraints), docs/design/006-image-pinning-policy.md (layer model).
Tests
Unpinned Helm component → fail on chart_pinned; pure-Kustomize recipe → vacuous pass with an explanatory Detail; minimal recipe → pass with sparse declared_coverage, never penalized; Checks emitted sorted.
Acceptance
Both signals computed; chart_pinned is layer-1-only and says so; declared_coverage is descriptive and never moves status.
Dependencies
Depends on #1225. Feeds the rollup; consumed by #1228 and #1229.
Parent: #1224 (Epic: Recipe health tracking, ADR-009 V1)
Summary
Add the two structural signals that are pure reads of the resolved
RecipeResult— no external integration — on top of the #1225 package core.Scope
chart_pinned(graded): iterateRecipeResult.ComponentRefs; for everyType == ComponentTypeHelm, require a non-emptyVersion. Empty →fail. Pure in-repo field check — no Helm render.defaultTagis also out of scope, so a pure-Kustomize recipe scoreschart_pinnedas a vacuouspass— note this in the signal'sDetailand the matrix header so the column isn't misread as "supply-chain pinned".declared_coverage(descriptor, never graded): fromRecipeResult.Validation.{Readiness,Deployment,Performance,Conformance}, record per phaseDeclared bool, sortedChecks []string,len(Constraints). A minimal recipe (e.g. Slurm dropping K8s checks) must not be penalized.Key files
pkg/recipe/metadata.go(ComponentRef:Type,Version),pkg/recipe/validation.go(ValidationConfig,ValidationPhase:Checks,Constraints),docs/design/006-image-pinning-policy.md(layer model).Tests
Unpinned Helm component →
failonchart_pinned; pure-Kustomize recipe → vacuouspasswith an explanatoryDetail; minimal recipe →passwith sparsedeclared_coverage, never penalized;Checksemitted sorted.Acceptance
Both signals computed;
chart_pinnedis layer-1-only and says so;declared_coverageis descriptive and never moves status.Dependencies
Depends on #1225. Feeds the rollup; consumed by #1228 and #1229.