Skip to content

feat(cli): add structural-health columns to recipe list#1302

Merged
njhensley merged 2 commits into
NVIDIA:mainfrom
njhensley:cli/recipe-list-health-columns
Jun 11, 2026
Merged

feat(cli): add structural-health columns to recipe list#1302
njhensley merged 2 commits into
NVIDIA:mainfrom
njhensley:cli/recipe-list-health-columns

Conversation

@njhensley

Copy link
Copy Markdown
Member

Summary

Extend aicr recipe list with the ADR-009 §4 structural-health view: a rolled-up status column and a compact per-phase coverage summary, delegating all computation to pkg/health so pkg/cli stays logic-free.

Motivation / Context

Part of the recipe health tracking epic (ADR-009 V1). #1208 delivered the recipe list enumeration command (criteria/leaf/source columns + --format/filter flags); this adds the health-derived columns on top, without changing #1208's output shape.

Fixes: #1228
Related: #1224 (epic), #1226/#1227 (health grading dependencies)

Type of Change

  • New feature (non-breaking change that adds functionality)
  • Documentation update

Component(s) Affected

  • CLI (cmd/aicr, pkg/cli)
  • Docs/examples (docs/, examples/)
  • Other: pkg/client/v1 facade (Client.ComputeHealth)

Implementation Notes

  • New facade method Client.ComputeHealth(ctx, filter) wraps health.Compute, binding the client's own DataProvider + version so --data overlays are scored against the same catalog the listing enumerates (not the process-global embedded catalog). It follows the sibling facade guard pattern and deliberately omits the per-op timeout since health.Compute applies its own HealthComputeTimeout.
  • table: adds STATUS (rolled-up verdict) and COVERAGE (compact per-phase named-check summary, R:n D:n P:n C:n); non-leaf overlays render -.
  • json/yaml: each entry gains an additive health block (per-dimension status map + full declared_coverage), omitted for non-leaf overlays. The embedded CatalogEntry carries yaml:",inline" so feat(recipe): add aicr recipe list subcommand for catalog enumeration #1208's fields stay top-level — yaml.v3 does not auto-inline anonymous struct fields the way encoding/json does.
  • Degrades gracefully: rendering reads the dimension set generically, so an absent constraints_wellformed dimension (pkg/health: constraints_wellformed signal (parse-only, hermetic) #1227 not yet merged) doesn't break the status column.

Testing

gofmt -l ...                          # clean
golangci-lint run (pinned v2.12.2)    # 0 issues
go test -race ./pkg/cli/... ./pkg/client/v1/...   # pass

Coverage: pkg/cli 64.0% → 67.7%, pkg/client/v1 75.1% → 75.3% (new ComputeHealth 81.2%). New tests cover the table/json/yaml formats, the non-leaf - placeholder, filtered-CLI pass-through, the empty-result branch, and a YAML-inline regression guard.

This change was reviewed by a multi-persona review pass (Go architecture, CLI/API-contract, test-quality), which caught and fixed a YAML serialization regression before push.

Risk Assessment

Rollout notes: Backwards compatible. The health block is additive (omitted for non-leaf); existing json/yaml/table consumers see no field removals or renames.

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)

Extend `aicr recipe list` with the ADR-009 §4 structural-health view,
delegating computation to pkg/health so pkg/cli stays logic-free:

- New facade method Client.ComputeHealth wraps health.Compute, binding
  the client's own DataProvider + version so --data overlays are scored
  against the same catalog the listing enumerates.
- table: add STATUS (rolled-up verdict) and COVERAGE (compact per-phase
  named-check summary, R:n D:n P:n C:n) columns; non-leaf overlays
  render "-".
- json/yaml: each entry gains an additive `health` block (per-dimension
  status map + full declared_coverage), omitted for non-leaf overlays.
  Embedded CatalogEntry carries yaml:",inline" so the NVIDIA#1208 fields stay
  top-level (yaml.v3 does not auto-inline anonymous fields).

Rendering reads the dimension set generically, so an absent
constraints_wellformed dimension (NVIDIA#1227) degrades gracefully — the
status column still renders.

Docs: extend the recipe list section of docs/user/cli-reference.md with
the health columns, field descriptions, and real-catalog examples.

Closes NVIDIA#1228
@njhensley njhensley requested a review from a team as a code owner June 11, 2026 00:21
@njhensley njhensley added the theme/validation Constraint evaluation, health checks, and conformance evidence label Jun 11, 2026
@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR extends the aicr recipe list command to display ADR-009 structural-health status and per-phase declared-coverage for leaf overlays. It adds Client.ComputeHealth to obtain health reports, pairs reports with catalog entries for JSON/YAML emission, and adds STATUS/COVERAGE columns to table output (non-leaf overlays show -). Tests cover table/JSON/YAML output, filtering, edge cases, and a compactCoverage unit test; docs are updated with new fields and examples.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Possibly related PRs

  • NVIDIA/aicr#1291: Provides the pkg/health Compute implementation and report types that this CLI change consumes.
  • NVIDIA/aicr#1293: Alters health report shape (declared_coverage/fields) that the CLI renders.
  • NVIDIA/aicr#1301: Modifies health grading dimensions consumed by the CLI output.

Suggested labels

area/recipes

Suggested reviewers

  • mchmarny
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(cli): add structural-health columns to recipe list' accurately and concisely summarizes the main change: extending the recipe list command with new health-related columns.
Description check ✅ Passed The description provides comprehensive context for the change, detailing motivation (ADR-009 health tracking epic), implementation approach (delegating to pkg/health), affected components, and testing/quality notes. It is clearly related to the changeset.
Linked Issues check ✅ Passed The PR fully addresses #1228's objectives: extends aicr recipe list with structural-health STATUS and COVERAGE columns for table output; adds health block to json/yaml with per-dimension status and declared_coverage; gracefully handles missing dimensions; preserves #1208 enumeration/output shape; includes docs update and comprehensive tests.
Out of Scope Changes check ✅ Passed All changes are within scope of #1228: CLI recipe list enhancements (docs, pkg/cli, pkg/client/v1 facade), health computation integration, table/json/yaml formatting, and test coverage. No unrelated refactoring or feature creep detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

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

Clean, additive recipe list health columns. The structural-health computation is correctly delegated to pkg/health via the new Client.ComputeHealth facade method, keeping pkg/cli logic-free, and the facade follows the sibling guard/inflight pattern. The leaf-name join with the catalog listing is correct, non-leaf overlays render the - placeholder, and the yaml:",inline" regression is explicitly guarded by a dedicated test. CI fully green; coverage up on both packages.

Two non-blocking nits inline (error-wrap symmetry, and the always-on health compute on every list). Approving.

Comment thread pkg/cli/recipe_list.go
// entry can be paired with its verdict during rendering.
report, err := client.ComputeHealth(ctx, filter)
if err != nil {
return err

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.

nit: the ListCatalog sibling two lines up wraps with errors.PropagateOrWrap(err, errors.ErrCodeInternal, "failed to list catalog"), while this returns err bare. Functionally fine since ComputeHealth already wraps internally with PropagateOrWrap, so re-wrapping here would double the message — flagging only for symmetry with the adjacent call. No change needed.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Thanks — agreed, keeping the bare return. ComputeHealth already wraps with PropagateOrWrap(err, ErrCodeInternal, "failed to compute recipe health"), so re-wrapping here would double the message and is exactly the "don't double-wrap errors that already have proper codes" case in CLAUDE.md. (The adjacent ListCatalog call is arguably the asymmetric one, since the facade ListCatalog already wraps internally too — but that's pre-existing #1208 code and out of scope here.)

Comment thread pkg/cli/recipe_list.go
// facade, which binds this Client's own provider so --data overlays
// are scored). Health is keyed by leaf overlay name so each catalog
// entry can be paired with its verdict during rendering.
report, err := client.ComputeHealth(ctx, filter)

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.

nit: recipe list now resolves every leaf on every invocation, so the previously-cheap enumeration always pays the full health-compute cost with no way to opt out. Within ADR-009's sub-minute budget so not blocking — but if the added latency is noticeable interactively, a --no-health / --skip-health flag would be a reasonable follow-up.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good call-out. Health compute runs within ADR-009's sub-minute budget so it's fine for v1, but an opt-out (--no-health / --skip-health) is a sensible follow-up if interactive latency becomes noticeable. Tracking it as a follow-up rather than expanding this PR's scope.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Filed as #1311 for the follow-up.

@njhensley njhensley enabled auto-merge (squash) June 11, 2026 14:40

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
docs/user/cli-reference.md (2)

545-549: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Define ADR acronym on first use.

Line 545 references "ADR-009 §4" but the acronym ADR (Architecture Decision Record) is not defined earlier in this document. As per coding guidelines, define acronyms on first use in documentation.

📝 Suggested expansion
-Each leaf overlay also carries a structural-health verdict (ADR-009 §4): a
+Each leaf overlay also carries a structural-health verdict (Architecture Decision Record 009, §4): a

Or if space is a concern:

-Each leaf overlay also carries a structural-health verdict (ADR-009 §4): a
+Each leaf overlay also carries a structural-health verdict (per ADR-009 §4, the structural-health design): a
🤖 Prompt for 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.

In `@docs/user/cli-reference.md` around lines 545 - 549, Add a definition for the
acronym ADR at first use where "ADR-009 §4" is referenced: replace or augment
the first occurrence with "ADR (Architecture Decision Record)‑009 §4" or append
"(ADR = Architecture Decision Record)" immediately after "ADR-009 §4" in the
sentence about structural-health verdicts so readers see the full term on first
mention; ensure subsequent mentions keep just "ADR-009" unchanged.

Source: Coding guidelines


584-589: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Clarify what the status column renders for unknown dimensions.

Line 589 states "and the status column still renders" but doesn't specify what value is rendered. Based on the rollup logic (context snippet 3), when a dimension cannot be confidently graded, the rolled-up status becomes unknown. Make this explicit for clarity.

📝 Suggested clarification
-Conformance). Non-leaf overlays render `-` in both columns. A dimension whose
-grader cannot reach a confident verdict surfaces as `unknown`, and the status
-column still renders.
+Conformance). Non-leaf overlays render `-` in both columns. When a dimension's
+grader cannot reach a confident verdict, that dimension surfaces as `unknown`,
+and the STATUS column renders `unknown` (held rather than failing).
🤖 Prompt for 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.

In `@docs/user/cli-reference.md` around lines 584 - 589, Clarify that when a
dimension's grader cannot reach a confident verdict the rolled-up STATUS column
displays the literal value "unknown" (while COVERAGE still shows the per-phase
summary); update the sentence that currently reads "and the status column still
renders" to explicitly state that it renders "unknown" for such dimensions
(keeping the existing note that non-leaf overlays render "-" in both columns and
the COVERAGE format R:2 D:4 P:1 C:10).
🤖 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.

Outside diff comments:
In `@docs/user/cli-reference.md`:
- Around line 545-549: Add a definition for the acronym ADR at first use where
"ADR-009 §4" is referenced: replace or augment the first occurrence with "ADR
(Architecture Decision Record)‑009 §4" or append "(ADR = Architecture Decision
Record)" immediately after "ADR-009 §4" in the sentence about structural-health
verdicts so readers see the full term on first mention; ensure subsequent
mentions keep just "ADR-009" unchanged.
- Around line 584-589: Clarify that when a dimension's grader cannot reach a
confident verdict the rolled-up STATUS column displays the literal value
"unknown" (while COVERAGE still shows the per-phase summary); update the
sentence that currently reads "and the status column still renders" to
explicitly state that it renders "unknown" for such dimensions (keeping the
existing note that non-leaf overlays render "-" in both columns and the COVERAGE
format R:2 D:4 P:1 C:10).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 20b55fec-5599-4fb8-ba22-ff94aac36402

📥 Commits

Reviewing files that changed from the base of the PR and between 53a79b3 and 9b7cb85.

📒 Files selected for processing (1)
  • docs/user/cli-reference.md

@njhensley njhensley merged commit b401339 into NVIDIA:main Jun 11, 2026
32 checks passed
@njhensley njhensley deleted the cli/recipe-list-health-columns branch June 23, 2026 16:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/cli area/docs size/XL theme/validation Constraint evaluation, health checks, and conformance evidence

Projects

None yet

Development

Successfully merging this pull request may close these issues.

aicr recipe list: add structural-health + coverage columns

2 participants