You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
AICR's pkg/recipe package is currently designed around a single-tenant process model: one CLI invocation or one API server instance, with a process-global DataProvider and sync.Once-cached metadata store and component registry. That works for our shipping consumers but blocks AICR from being a safe, reusable Go library for in-process consumers that need to maintain more than one recipe source at a time — operators, controllers, multi-tenant automation, in-process testing harnesses, and tooling like the BOM generator (see #966).
This is a tracking issue for the related hardening work. Each child issue is independently reviewable and mergeable; the order below reflects landing order, not file order.
Motivation
Two concrete pressures expose the gap:
chore(recipes): make resolved recipes the single source of truth for chart versions #966's BOM-per-leaf rewrite needs to resolve "effective values" (base → values file → overrides) for every enumerated leaf. The merge logic exists but is package-private, and the bundler — the only public consumer — writes to disk. The BOM rewrite either reimplements values merging (drift hazard) or duplicates bundler internals (coupling hazard) unless we promote the resolution primitives to public API.
In-process consumers of AICR cannot safely construct two Builders in the same process. The global DataProvider plus the sync.Once-cached metadata store and component registry mean that whichever Builder's source populates the cache first wins for every concurrent Builder, regardless of what WithDataProvider-style option they were given (today, there is no such option). The hazard is dormant only because no in-tree consumer triggers it.
The underlying patterns are already called out as anti-patterns in .claude/CLAUDE.md:
sync.Once caching state that depends on a settable global (e.g., a registry tied to a DataProvider) … key the cache by a generation counter the setter increments; recompute on miss so late-bound configuration takes effect.
Deep-copy helper that recurses into maps but copies []any by reference — slice aliasing leaks mutations across overlay merges.
This work makes those guidelines true of the recipe package itself.
#984 (correctness fix) ──┐
├──▶ no hard order between these three
#985 (public helpers) ──┤ (recommend landing in listed order)
│
#986 (provider isolation) ┘
│
▼
#987 (deprecation) ── hard dep on #986
Publishing a top-level github.com/NVIDIA/aicr facade package. The fixes here are necessary preconditions for any such facade, but the facade itself is a separate design conversation with its own API-stability commitments.
OCI recipe sources, pinned recipe references, and other forward-looking source kinds — not part of the hardening surface.
Summary
AICR's
pkg/recipepackage is currently designed around a single-tenant process model: one CLI invocation or one API server instance, with a process-globalDataProviderandsync.Once-cached metadata store and component registry. That works for our shipping consumers but blocks AICR from being a safe, reusable Go library for in-process consumers that need to maintain more than one recipe source at a time — operators, controllers, multi-tenant automation, in-process testing harnesses, and tooling like the BOM generator (see #966).This is a tracking issue for the related hardening work. Each child issue is independently reviewable and mergeable; the order below reflects landing order, not file order.
Motivation
Two concrete pressures expose the gap:
chore(recipes): make resolved recipes the single source of truth for chart versions #966's BOM-per-leaf rewrite needs to resolve "effective values" (base → values file → overrides) for every enumerated leaf. The merge logic exists but is package-private, and the bundler — the only public consumer — writes to disk. The BOM rewrite either reimplements values merging (drift hazard) or duplicates bundler internals (coupling hazard) unless we promote the resolution primitives to public API.
In-process consumers of AICR cannot safely construct two Builders in the same process. The global
DataProviderplus thesync.Once-cached metadata store and component registry mean that whichever Builder's source populates the cache first wins for every concurrent Builder, regardless of whatWithDataProvider-style option they were given (today, there is no such option). The hazard is dormant only because no in-tree consumer triggers it.The underlying patterns are already called out as anti-patterns in
.claude/CLAUDE.md:This work makes those guidelines true of the recipe package itself.
Scope
Four child issues, in landing order:
fix(recipe): deep-clone ValidationConfig phases in Merge to prevent cache corruption(correctness bug; standalone, smallest)feat(recipe): expose effective-values and manifest helpers on RecipeResult(unblocks chore(recipes): make resolved recipes the single source of truth for chart versions #966; standalone)feat(recipe): per-Builder DataProvider isolation via WithDataProvider option(largest; enables multi-tenant in-process use)feat(recipe): deprecate SetDataProvider / GetDataProvider globals(gated on feat(recipe): per-Builder DataProvider isolation via WithDataProvider option #986)Dependency graph
Soft sequencing rationale:
WithDataProviderexists as the documented alternative.Out of scope
github.com/NVIDIA/aicrfacade package. The fixes here are necessary preconditions for any such facade, but the facade itself is a separate design conversation with its own API-stability commitments.Acceptance
make qualifyclean throughout.tools/bom(or a successor PR for chore(recipes): make resolved recipes the single source of truth for chart versions #966) can resolve per-component effective values via the new public API.