Skip to content

feat(recipe): add aicr recipe list subcommand for catalog enumeration#1208

Merged
mchmarny merged 2 commits into
NVIDIA:mainfrom
rsd-darshan:feat/recipe-list-catalog
Jun 8, 2026
Merged

feat(recipe): add aicr recipe list subcommand for catalog enumeration#1208
mchmarny merged 2 commits into
NVIDIA:mainfrom
rsd-darshan:feat/recipe-list-catalog

Conversation

@rsd-darshan

@rsd-darshan rsd-darshan commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a read-only aicr recipe list subcommand that enumerates overlay recipes in the catalog with machine-readable output (JSON, YAML, table), solving the catalog discoverability gap in #1207.

Motivation / Context

Tools consuming AICR as a library or CLI with external --data had no programmatic way to discover which criteria combinations resolve to a curated leaf overlay versus a generic fallback. The only workarounds were parsing --help hint text (not a complete API) or probing every service × accelerator × intent × os combination and inspecting appliedOverlays — both fragile and expensive.

Fixes: N/A
Related: #1207

Type of Change

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

Component(s) Affected

  • CLI (cmd/aicr, pkg/cli)
  • API server (cmd/aicrd, pkg/server)
  • Recipe engine / data (pkg/recipe)
  • Bundlers (pkg/bundler, pkg/component/*)
  • Collectors / snapshotter (pkg/collector, pkg/snapshotter)
  • Validator (pkg/validator)
  • Core libraries (pkg/errors, pkg/k8s)
  • Docs/examples (docs/, examples/)
  • Other: ____________

Implementation Notes

Source tracking (pkg/recipe/metadata_store.go): Added OverlaySources map[string]string to MetadataStore, populated with provider.Source(path) during the catalog walk alongside the existing criteria-registry staging. This avoids re-walking the provider at list time.

Curated definition (pkg/recipe/catalog.go): An overlay has IsLeaf: true when no other overlay in the catalog lists it as spec.base — i.e., it is a maximal leaf. Intermediate overlays (e.g., eks-training, which is the base of h100-eks-training) get IsLeaf: false. This matches the issue's intent of distinguishing dedicated leaf recipes from generic shared layers.

Filter semantics (matchesCatalogFilter): Uses simple equality rather than the asymmetric Criteria.Matches logic used for recipe resolution. --accelerator h100 returns only overlays explicitly specifying h100; it does not match overlays whose accelerator is any. This is the correct semantic for a discovery query: "show me what targets h100", not "show me what a query for h100 would pick up via wildcard".

CLI output (pkg/cli/recipe_list.go): Default format is table (human-friendly with tabwriter); --format json and --format yaml are available for machine consumption. Writes to cmd.Root().Writer per project conventions. Filter flags are validated through the client's criteria registry so --data overlay values are accepted.

Facade (pkg/client/v1): Client.ListCatalog follows the same inflight/lock protocol as LoadCatalog and reuses the existing toInternalCriteria/WrapCriteria translation helpers to avoid parallel projection logic.

Testing

make qualify

All tests pass, linter clean. New unit tests in pkg/recipe/catalog_test.go cover:

  • Full enumeration (no filter)
  • Source field propagation
  • Per-dimension filters: service, accelerator, OS, service+intent combined
  • Empty result when no overlays match
  • All-any filter behaves identically to no filter
  • Ascending name sort
  • matchesCatalogFilter nil filter, exact match, mismatched dimension, wildcard-overlay vs specific-filter

Risk Assessment

  • Low — Isolated change, well-tested, easy to revert

Rollout notes: Purely additive. No existing commands or API endpoints are modified. The only change to MetadataStore is a new field populated during the existing catalog walk; no behavior changes to recipe resolution or bundling.

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

@rsd-darshan rsd-darshan requested a review from a team as a code owner June 7, 2026 06:13
@copy-pr-bot

copy-pr-bot Bot commented Jun 7, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Welcome to AICR, @rsd-darshan! Thanks for your first pull request.

Before review, please ensure:

  • All commits are signed off per the DCO
  • CI checks pass (tests, lint, security scan)
  • The PR description explains the why behind your changes

A maintainer will review this soon.

@coderabbitai

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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/cli/recipe_list.go`:
- Around line 223-225: The current error wrap uses errors.ErrCodeInternal when
ctx.Err() indicates cancellation/timeouts; update the call that wraps ctx.Err()
(the errors.Wrap(...) invocation that currently passes errors.ErrCodeInternal
and the "write canceled" message) to use errors.ErrCodeTimeout instead of
ErrCodeInternal so context.Canceled / context.DeadlineExceeded are classified as
timeout/cancellation errors.
🪄 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: e4a60857-7f59-4bf1-b1d0-5fc9c4d5b4df

📥 Commits

Reviewing files that changed from the base of the PR and between 7d4146b and 37b6c86.

📒 Files selected for processing (2)
  • pkg/cli/recipe_list.go
  • pkg/recipe/catalog_test.go

Comment thread pkg/cli/recipe_list.go

@mchmarny mchmarny left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The Curated field in CatalogEntry needs to be addressed before merge — see inline comment for details. Everything in main is "curated" by definition; the field conflates editorial quality with internal overlay tree structure. Either rename it to something structurally accurate (IsLeaf) or drop it entirely.

@rsd-darshan do you mind also creating an issue for this so we can align on the scope before the PR lands?

Comment thread pkg/client/v1/types.go Outdated
@rsd-darshan

Copy link
Copy Markdown
Contributor Author

@mchmarny good catch — renamed Curated to IsLeaf throughout (commit 7f19b3a). The JSON/YAML tag is now is_leaf. Agreed that the structural name is clearer and avoids the quality connotation.

On your suggestion about defaulting to leaf-only with --all: happy to do that in a follow-up if the maintainers prefer it, but wanted to keep this PR scoped to the minimum needed to address #1207 and not grow it further while it's under review.

coderabbitai[bot]

This comment was marked as resolved.

@mchmarny mchmarny left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Good response on IsLeaf — the rename is clean and consistent across all layers. Two small items inline.

One thing to clean up before merge: the PR description's Implementation Notes still say Curated: true / Curated: false — update that to match the renamed field.

Also: CatalogEntry.Source uses string values "embedded" / "external" that are only documented in comments, while the actual constants live unexported in pkg/recipe. Consider exporting them (e.g. CatalogSourceEmbedded, CatalogSourceExternal) so callers can compare without hardcoding magic strings.

PR mechanics before merge: squash the 5 commits to 1, add the enhancement label, and confirm CI runs the full build/lint/test gate (only label/auto-merge checks are visible on the head commit right now).

Comment thread pkg/client/v1/aicr.go Outdated
Comment thread pkg/cli/recipe_list.go
@ayuskauskas

Copy link
Copy Markdown
Contributor

This seems reasonable. The --all work can be deferred as long as the documentation is comprehensive on examples for getting everything vs just the leafs.

@rsd-darshan rsd-darshan force-pushed the feat/recipe-list-catalog branch from 810d5d0 to 33950bd Compare June 8, 2026 16:37
@rsd-darshan

Copy link
Copy Markdown
Contributor Author

Addressed all inline items (commit 33950bd):

  • var c Criteria renamed to var crit Criteria to avoid shadowing the *Client receiver
  • YAML output now uses serializer.MarshalYAMLDeterministic (consistent with query.go)
  • Exported CatalogSourceEmbedded / CatalogSourceExternal from pkg/recipe and re-exported from pkg/client/v1 so callers can compare Source without hardcoding strings

Also squashed the 5 commits to 1 as requested.

Could a maintainer add the enhancement label? No permission to add labels from a fork.

mchmarny
mchmarny previously approved these changes Jun 8, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 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/user/cli-reference.md`:
- Around line 545-609: The Markdown has fenced code blocks that lack surrounding
blank lines (MD031): add a blank line before and after each fenced block in the
"Synopsis" (the ```shell block showing "aicr recipe list [flags]"), the
"Examples" code block (the ```shell examples list), and the "Example JSON
output" block (the ```json sample); update the three blocks so there is an empty
line above the opening ``` and an empty line after the closing ``` to satisfy
markdownlint.

In `@pkg/recipe/catalog_test.go`:
- Around line 150-225: Consolidate the repetitive filter tests into a single
table-driven test (e.g., TestListCatalog_Filter) that iterates over cases with
fields: name, filter (*Criteria), wantNames ([]string) or wantLen (int) and uses
catalogStore(), calls store.ListCatalog(filter) and asserts via
entryNames/namesMatch; do the same consolidation pattern for the
matchesCatalogFilter unit tests (previously TestMatchesCatalogFilter_*),
referencing the matchesCatalogFilter function in each case; ensure cases cover
service, accelerator, service+intent, os, no-match, and all-any scenarios and
keep test helpers (catalogStore, entryNames, namesMatch) unchanged.

In `@pkg/recipe/catalog.go`:
- Around line 72-75: ListCatalog currently assigns overlay.Spec.Criteria
directly into CatalogEntry, leaking a reference to cached metadata; create and
assign a defensive deep copy of overlay.Spec.Criteria when building the
CatalogEntry (in the entries = append(...) block) so callers receive an
independent Criteria instance. Locate ListCatalog and the CatalogEntry
construction where overlay.Spec.Criteria is used and replace the direct
assignment with a deep copy operation appropriate for Criteria (e.g., struct
copy or use the project’s clone helper / proto.Clone if Criteria is a protobuf)
to prevent external mutation of the cache.
🪄 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: f567ae98-851c-430c-9564-5f0f5336b36a

📥 Commits

Reviewing files that changed from the base of the PR and between 7f19b3a and 33950bd.

📒 Files selected for processing (10)
  • docs/user/cli-reference.md
  • pkg/cli/consts.go
  • pkg/cli/recipe.go
  • pkg/cli/recipe_list.go
  • pkg/client/v1/aicr.go
  • pkg/client/v1/types.go
  • pkg/recipe/catalog.go
  • pkg/recipe/catalog_test.go
  • pkg/recipe/metadata_store.go
  • pkg/recipe/provider.go

Comment thread docs/user/cli-reference.md
Comment thread pkg/recipe/catalog_test.go Outdated
Comment thread pkg/recipe/catalog.go
@rsd-darshan rsd-darshan force-pushed the feat/recipe-list-catalog branch 2 times, most recently from e911a30 to 3f46c00 Compare June 8, 2026 16:56
@mchmarny

mchmarny commented Jun 8, 2026

Copy link
Copy Markdown
Member

@rsd-darshan few issues from CR, otherwise it generally LGTM;

@mchmarny mchmarny self-requested a review June 8, 2026 16:58
@rsd-darshan

Copy link
Copy Markdown
Contributor Author

@mchmarny all three CR items were addressed in commit 3f46c00 (pushed just before your comment):

  • Defensive Criteria copycritCopy := *overlay.Spec.Criteria before appending to entries in ListCatalog
  • Table-driven tests — 10 single-case filter/matchesCatalogFilter tests consolidated into two t.Run tables
  • MD031 blank lines — blank lines added before/after all fenced code blocks in cli-reference.md

Adds a read-only `aicr recipe list` subcommand that enumerates overlay
recipes in the catalog with machine-readable output (JSON, YAML, table),
solving the catalog discoverability gap in NVIDIA#1207.

Problem: Tools consuming AICR as a library or CLI with external --data
had no programmatic way to discover which criteria combinations resolve
to a leaf overlay versus a generic intermediate recipe.

Changes:
- pkg/recipe/metadata_store.go: add OverlaySources map to MetadataStore.
- pkg/recipe/provider.go: export CatalogSourceEmbedded/CatalogSourceExternal.
- pkg/recipe/catalog.go: add CatalogEntry and MetadataStore.ListCatalog with
  catalog-specific equality filter. IsLeaf=true for leaf overlays. Defensive
  copy of Criteria on entry construction to prevent cache mutation.
- pkg/recipe/catalog_test.go: table-driven tests (t.Run) for filter cases and
  matchesCatalogFilter semantics.
- pkg/client/v1/types.go: add CatalogEntry facade type and re-export source
  constants.
- pkg/client/v1/aicr.go: add Client.ListCatalog.
- pkg/cli/consts.go: add flagService/flagAccelerator/flagIntent/flagOS/
  flagPlatform/criteriaAny shared constants.
- pkg/cli/recipe_list.go: implement recipeListCmd with filter flags, --format
  (json/yaml/table), --data support. Uses serializer.MarshalYAMLDeterministic.
  Exhaustive switch with explicit FormatTable case.
- pkg/cli/recipe.go: register recipeListCmd; use shared flag constants.
- docs/user/cli-reference.md: document aicr recipe list; blank lines around
  fenced code blocks per markdownlint MD031.
@rsd-darshan rsd-darshan force-pushed the feat/recipe-list-catalog branch from 3f46c00 to 2417dc5 Compare June 8, 2026 17:01
mchmarny
mchmarny previously approved these changes Jun 8, 2026

@mchmarny mchmarny left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

All review findings addressed. Good work on the quick turnaround — IsLeaf rename, defensive Criteria copy, table-driven tests, MarshalYAMLDeterministic, exported source constants, and the flag name constants cleanup are all clean. Also good to see the PR squashed to a single commit. LGTM.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants