Skip to content

feat(studio): run guardrail checks against an unsaved draft config - #1267

Open
aray12 wants to merge 1 commit into
mainfrom
astd-415-guardrails-run-checks-against-draft-config
Open

feat(studio): run guardrail checks against an unsaved draft config#1267
aray12 wants to merge 1 commit into
mainfrom
astd-415-guardrails-run-checks-against-draft-config

Conversation

@aray12

@aray12 aray12 commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Summary

The Test and Validate tab could only exercise the saved guardrail config, so checking whether an edit worked meant saving it first — publishing a half-tested change to whatever already pointed at that config, and writing a config version the user did not want to keep. This adds a Draft/Saved run-target control: a Draft run sends the current form state inline to /checks instead of referencing the saved config by id. No backend change is needed — the guardrails service already accepts a whole RailsConfig in guardrails.config, and Studio simply never populated it.

Runs now also record what produced them, so a result keeps describing the config that ran even after that config changes.

Changes

  • Draft/Saved run target (GuardrailTestCasesEditor) — a SegmentedControl beside "Run N Tests". Draft is selected when the form is dirty and disabled when it is pristine; picking Saved on a dirty form still exercises the saved config. The choice resets when navigating to a different config.
  • Inline draft dispatch (guardrailChecks.ts) — runGuardrailCheck/runGuardrailChecks take an optional draftConfig. When present it becomes the effective config for both model resolution and the request, sent as guardrails: { config: <RailsConfig> }. Never alongside config_ids: the service's validator nulls those out for an object config, which would make the request's meaning non-obvious from the wire. A draft also takes precedence over a check's stored guardrails, so an explicitly chosen target is never silently discarded.
  • Honest run records (types.ts) — RunRecord gains is_draft (in place of a config_version, which is meaningless for a draft) and activated_guardrails, a snapshot of the coverage the config declared at run time. Both optional, so records written before this change deserialize and render unchanged.
  • Result rendering — Run History shows an "Unsaved draft" badge instead of v{n}; Rail Status prefers the run's own snapshot over deriving coverage from the currently-loaded config, falling back to today's behaviour when a record has no snapshot.
  • useDraftRailsConfig — extracts the server-data-plus-form-values derivation the Configuration tab already performed inline, so both tabs share one definition of "the draft" rather than deriving it twice.
  • MSW /checks handler — now honours an inline config so a draft-run test asserts the code's behaviour rather than a constant in the mock.
  • ActivatedGuardrail moves from the side-panel module to api/guardrail-checks/types.ts, since it is now persisted data; re-exported from railLabels.ts for existing importers.

Note for reviewers: guardrailChecks.ts imports getActivatedGuardrails from the side-panel module, which is an api → components direction. It is deliberate — the alternative duplicates the detector/flow catalog and lets check-result labels drift from the config editor's. The clean fix is hoisting detectors.ts/flowRegistry.ts out of GuardrailConfigTab/ into a neutral module, which is larger than this change warrants.

Type of Change

  • Code change (feature, bug fix, or refactor)
  • Code change with documentation updates
  • Documentation only
  • Contributor tooling or automation
  • CI, build, or test infrastructure

Quality Gates

  • Tests added or updated for changed behavior
  • Existing tests cover changed behavior — justification:
  • Tests not applicable — justification:
  • Documentation updated for user-visible behavior
  • Documentation not applicable — justification: no user-facing docs describe the checks tab; the behaviour is discoverable from the control itself.

Verification

  • Pull request title follows the repository's Conventional Commit format
  • Every commit includes an appropriate Signed-off-by: trailer
  • uv run pre-commit run -a passes, or any blocked checks are identified below
  • Targeted tests pass, or tests are marked not applicable above
  • No secrets, API keys, or credentials are included

Targeted validation:

  • pnpm --filter nemo-studio-ui test — 310 files, 2847 tests passed (full suite, pre-rebase).
  • pnpm --filter nemo-studio-ui test src/api/guardrail-checks src/routes/guardrails src/components/sidePanels/GuardrailCheckDetailSidePanel — 13 files, 101 tests passed (re-run after rebasing onto current main).
  • pnpm --filter nemo-studio-ui typecheck — clean. pnpm --filter @nemo/common typecheck — clean.
  • pnpm lint:fix then pnpm lint — clean, no reformatting required.
  • uv run pre-commit run -a — ruff, ruff format, ty, config reference, copyright headers, UI lint-staged, and merge-conflict hooks all passed. Two hooks did not run for environmental reasons, neither related to this change: Helm Docs Container (no local Docker daemon; no Helm files are touched) and Run uv lock with platform uv (local uv is 0.9.28, the hook requires 0.9.14). Check for uv.lock drift passed and no pyproject.toml is modified, so the lockfile is unaffected. CI runs both.
  • Pre-push hooks (copyright headers, UI typecheck, merge conflicts) passed.

Not run: manual end-to-end against a live platform, which needs a running instance. The new tests cover the wire format for both run targets, the recorded run fields, and the two rendering branches.

Existing coverage was deliberately left unmodified — the pre-existing guardrailChecks.test.ts cases still pass as written, which is the evidence that a Saved run sends a byte-identical request.

Summary by CodeRabbit

New Features

  • Added support for running guardrail checks against saved configurations or unsaved drafts.
  • Added a Saved/Draft selector for guardrail test runs, with Draft automatically selected when edits are unsaved.
  • Run history now identifies draft runs and saved configuration versions.
  • Guardrail status displays the configuration snapshot used during each run.

Bug Fixes

  • Improved draft execution to use the selected model and guardrails and reject drafts without a usable model.

The Test and Validate tab could only exercise the saved config, so
verifying an edit meant saving it first — publishing a half-tested change
and writing a config version the user did not want to keep.

Add a Draft/Saved run-target control. A Draft run sends the merged form
state inline via the /checks endpoint's `config` field, which the service
already supports, and resolves the request model from the draft. No
backend change is needed.

Runs now record what produced them: `is_draft` in place of a config
version, and a snapshot of the activated guardrails, so a result keeps
describing the config that ran even after the config changes. Both fields
are optional, so records written earlier render unchanged.

Extract useDraftRailsConfig so the Configuration and checks tabs share one
definition of the draft rather than deriving it twice.

Signed-off-by: Alex Ray <alray@nvidia.com>
@aray12
aray12 requested review from a team as code owners August 12, 2026 22:28
@github-actions github-actions Bot added the feat label Aug 12, 2026
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Guardrail checks now support saved and unsaved draft configurations. Draft runs submit inline configuration data, record draft metadata and guardrail snapshots, and display the correct run origin and guardrail coverage.

Changes

Draft guardrail execution

Layer / File(s) Summary
Execution contracts and API flow
web/packages/studio/src/api/guardrail-checks/*, web/packages/studio/src/mocks/handlers/guardrails.ts
Execution APIs accept draft configurations, submit them inline without config_ids, validate the model, and record effective guardrails and run origin metadata.
Draft configuration state and run target selection
web/packages/studio/src/routes/guardrails/GuardrailForm/*, web/packages/studio/src/routes/guardrails/GuardrailChecksTab/*
The UI derives draft configuration state, selects Draft or Saved execution, and passes draft data only for Draft runs.
Run coverage and origin display
web/packages/studio/src/components/sidePanels/GuardrailCheckDetailSidePanel/*
Run details use captured guardrail snapshots. Run history labels unsaved drafts and saved configuration versions, with fallback behavior for legacy records.

Suggested reviewers: steramae-nvidia, marcusds

Mergeability Score: ⚪ Minimal · up to 3600a

The PR enables testing unsaved guardrail configurations and preserves the configuration context used for each result. No actionable merge-blocking risk remains; it is merge-ready after normal checks and review.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: running Studio guardrail checks against an unsaved draft configuration.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch astd-415-guardrails-run-checks-against-draft-config

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
web/packages/studio/src/api/guardrail-checks/types.ts (1)

30-35: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Mark persisted snapshot fields as readonly.

ActivatedGuardrail and activated_guardrails describe execution-time snapshots. Mutable types permit consumers to alter recorded history in memory.

Proposed change
 export interface ActivatedGuardrail {
-  id: string;
-  label: string;
-  active: boolean;
+  readonly id: string;
+  readonly label: string;
+  readonly active: boolean;
 }

-  is_draft?: boolean;
-  activated_guardrails?: ActivatedGuardrail[];
+  readonly is_draft?: boolean;
+  readonly activated_guardrails?: readonly ActivatedGuardrail[];

As per coding guidelines, “Use readonly for immutable properties.”

Also applies to: 53-55

🤖 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 `@web/packages/studio/src/api/guardrail-checks/types.ts` around lines 30 - 35,
Mark the persisted snapshot properties in ActivatedGuardrail as readonly,
including id, label, and active. Apply the same readonly treatment to the
activated_guardrails fields around the referenced section, preserving their
existing types and structure.

Source: Coding guidelines

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

Nitpick comments:
In `@web/packages/studio/src/api/guardrail-checks/types.ts`:
- Around line 30-35: Mark the persisted snapshot properties in
ActivatedGuardrail as readonly, including id, label, and active. Apply the same
readonly treatment to the activated_guardrails fields around the referenced
section, preserving their existing types and structure.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 71336349-d2d8-477e-954e-d9e51cb6ef26

📥 Commits

Reviewing files that changed from the base of the PR and between c8cb003 and 3600afa.

📒 Files selected for processing (15)
  • web/packages/studio/src/api/guardrail-checks/guardrailChecks.test.ts
  • web/packages/studio/src/api/guardrail-checks/guardrailChecks.ts
  • web/packages/studio/src/api/guardrail-checks/hooks.ts
  • web/packages/studio/src/api/guardrail-checks/types.ts
  • web/packages/studio/src/components/sidePanels/GuardrailCheckDetailSidePanel/RailStatusTab.test.tsx
  • web/packages/studio/src/components/sidePanels/GuardrailCheckDetailSidePanel/RailStatusTab.tsx
  • web/packages/studio/src/components/sidePanels/GuardrailCheckDetailSidePanel/RunHistoryTab.test.tsx
  • web/packages/studio/src/components/sidePanels/GuardrailCheckDetailSidePanel/RunHistoryTab.tsx
  • web/packages/studio/src/components/sidePanels/GuardrailCheckDetailSidePanel/railLabels.ts
  • web/packages/studio/src/mocks/handlers/guardrails.ts
  • web/packages/studio/src/routes/guardrails/GuardrailChecksTab/GuardrailTestCasesEditor.tsx
  • web/packages/studio/src/routes/guardrails/GuardrailChecksTab/index.test.tsx
  • web/packages/studio/src/routes/guardrails/GuardrailChecksTab/index.tsx
  • web/packages/studio/src/routes/guardrails/GuardrailConfigTab/index.tsx
  • web/packages/studio/src/routes/guardrails/GuardrailForm/useDraftRailsConfig.ts

@github-actions

Copy link
Copy Markdown
Contributor
Suite Lines Covered Line Rate Branch Rate
Unit Tests 32236/40906 78.8% 63.7%
Integration Tests 18640/38832 48.0% 20.7%

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant