Skip to content

pkg/health: core Compute() loop, resolves signal, rollup, determinism #1225

Description

@njhensley

Parent: #1224 (Epic: Recipe health tracking, ADR-009 V1)

Summary

Stand up the new pkg/health package: the enumeration→resolution loop, the resolves signal, the status rollup with unknown-held semantics, and the determinism harness. The read-only signals are #1226; the constraint signal is #1227.

pkg/health is a standalone package (not in pkg/recipe) to keep the future v1.1 evidence import acyclic — pkg/evidence/attestation already imports pkg/recipe, so hosting health in pkg/recipe would later close a recipe → evidence → recipe cycle. V1 deps: pkg/recipe, pkg/serializer, pkg/errors, pkg/defaults.

Scope

  • New package pkg/health with the ADR-009 §4 types: Options, PhaseCoverage, DeclaredCoverage, StructureHealth, ComboHealth, Report, and SchemaVersion = "1.0.0".
  • func Compute(ctx, opts Options) (*Report, error): enumerate via MetadataStore.ListCatalog(nil) filtered to Curated == true (the merged feat(recipe): add aicr recipe list subcommand for catalog enumeration #1208 seam — each CatalogEntry gives NameComboHealth.LeafOverlay and Criteria), resolve each through a single shared recipe.NewBuilder(...)'s BuildFromCriteria (so the owner-stamp invariant holds across combos). ListCatalog is name-sorted (deterministic); consume as-is or re-sort by criteria tuple — either is deterministic.
  • resolves signal (graded, intrinsic to the loop): resolves → pass; non-transient error → fail (error in Detail); ErrCodeTimeout/ErrCodeInternalunknown (held, excluded from rollup, never an empty field).
  • Rollup: fail if any graded dimension fails; else warn; else pass. unknown is held. The rollup iterates Dimensions generically so pkg/health: chart_pinned + declared_coverage signals #1226/pkg/health: constraints_wellformed signal (parse-only, hermetic) #1227 add dimensions without changing rollup code.
  • Add pkg/health/** to .github/CODEOWNERS and a pkg/health/** area glob to .github/labeler.yml.

Determinism

No time.Now() on the computed path. Caveat: the unknown-from-ErrCodeTimeout mapping is the one non-deterministic input — byte-determinism is asserted on the non-error path only; tests inject errors via a fake DataProvider, never real timeouts.

Key files

pkg/recipe/catalog.go (ListCatalog, CatalogEntry — from #1208), pkg/recipe/builder.go (NewBuilder, BuildFromCriteria), pkg/recipe/metadata.go (RecipeResult), pkg/serializer (deterministic marshal).

Tests

Clean recipe → pass; injected transient error (fake provider) → unknown held out of rollup; determinism: serialize the Report via the deterministic marshaller and assert byte-equality across two Compute runs (not struct DeepEqualReport carries map[string]string, whose nondeterminism only manifests at marshal time); empty-catalog → empty Report, no panic.

Acceptance

pkg/health exists with Compute(), the resolves signal, a generic rollup with unknown-held, and a marshal-and-compare byte-determinism test. CODEOWNERS + labeler glob land here. 75% coverage floor cleared.

Dependencies

Builds on the merged MetadataStore.ListCatalog (#1208). Blocks #1226, #1227, #1228, #1229.

Metadata

Metadata

Assignees

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions