feat(apollo-react): shared guardrail definitions layer and useGuardrailDefinitions [AL-574] - #1139
Conversation
…ilDefinitions Adds the definitions layer the guardrails family was missing: the seam between the `/api/execution/guardrails/definitions` payload and the `GuardrailDefinition`s `GuardrailBuilder` renders. Flow and Agents each carry their own copy of this today, and the two have drifted. - `definitions-wire.ts` mirrors the payload as hand-written types, reusing `GuardrailScope` and `GuardrailDefinitionStatus` from `builder-types` so the wire and display layers cannot diverge. It admits both products' nullability variants. - `definitions-parse.ts` validates unknown input and never throws: a non-array payload sets `inputError`, a bad definition is dropped whole and reported in `invalid`, unknown keys are stripped. zod is private to this module, pinned to the public mirror by a bidirectional assignability check on the hot path, a runtime key-set assertion and a source-level guard, so the folder's emitted declarations carry no schema types. - `definitions-copy.ts` holds the canonical copy for the six built-in validators as 63 lingui messages in the shared canvas catalog, keyed by raw wire values. Translations harvested from both products, 62 of 63 in each of the 12 locales. - `definitions-enrich.ts` resolves that copy onto the wire shape. Pure and React-free; `EnrichedGuardrailDefinition extends GuardrailDefinition`, so its output feeds the builder with no mapping. - `useGuardrailDefinitions` composes the three over `useState` + `fetch` + `AbortController`, following `useDiscoveryModels`. `options.definitions` skips the request entirely, which is how each product keeps its own transport. Unlike `useDiscoveryModels` the context is compared by content, not identity: keying the effect off identity made an inline context object refetch on every render without terminating. Where the products' English differed, all 17 choices are declared with a reason in `definitions-parity.test.ts` and asserted against both products' transcribed copy, so the shared table cannot quietly drift from the tables it replaces. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Apollo Coded App preview deployments are ready.
|
Dependency License Review
License distribution
Excluded packages
|
There was a problem hiding this comment.
🟡 Changes recommended
There are confirmed behavioral bugs in the new code (unexpected refetch behavior and render-phase state updates) that should be fixed before merging.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR adds a shared “guardrail definitions” layer under packages/apollo-react/src/canvas/components/Guardrails/ that parses the /api/execution/guardrails/definitions payload, enriches it with canonical (Lingui-backed) display copy, and exposes a useGuardrailDefinitions hook as the seam between host transport and GuardrailBuilder. It also extends apollo-wind’s metadata forms to support tooltips and a new string-list field type used by guardrail parameter editors.
Changes:
- Introduces wire types + zod-based non-throwing parsing, pure enrichment, and a
useGuardrailDefinitionshook for fetching/composing guardrail definitions. - Moves canonical validator copy into the canvas Lingui catalog and adds parity tests against Flow/Agents baselines.
- Enhances
apollo-windforms/UI withInfoTooltip,string-listfield support, andaria-invalidstyling for select/textarea.
File summaries
| File | Description |
|---|---|
| pnpm-lock.yaml | Locks new deps added for guardrails UI and a11y testing. |
| packages/apollo-wind/src/index.ts | Re-exports new forms/types (MetadataFormProps, useWatch, StringListField*) and InfoTooltip. |
| packages/apollo-wind/src/components/ui/textarea.tsx | Adds aria-invalid error styling to textarea. |
| packages/apollo-wind/src/components/ui/select.tsx | Adds aria-invalid error styling to select trigger. |
| packages/apollo-wind/src/components/ui/info-tooltip.tsx | Adds reusable info-icon tooltip component for form labels. |
| packages/apollo-wind/src/components/ui/info-tooltip.test.tsx | Adds a11y + behavior tests for InfoTooltip. |
| packages/apollo-wind/src/components/ui/index.ts | Exports info-tooltip (and reorders a couple exports). |
| packages/apollo-wind/src/components/forms/validation-converter.ts | Extends schema conversion to treat string-list as an array type. |
| packages/apollo-wind/src/components/forms/string-list-field.tsx | Implements the new string-list field editor and formatTemplate helper. |
| packages/apollo-wind/src/components/forms/metadata-form.stories.tsx | Adds story demonstrating string-list + tooltip + controlled-host seam. |
| packages/apollo-wind/src/components/forms/index.ts | Exposes new forms APIs (controlled seam types, string-list exports, useWatch). |
| packages/apollo-wind/src/components/forms/form-schema.ts | Adds tooltip metadata, textarea constraints, multiselect copy overrides, and string-list field metadata/type. |
| packages/apollo-wind/src/components/forms/field-renderer.tsx | Renders required indicator + optional tooltip, wires htmlFor/id, and passes aria-invalid to select/textarea/multiselect. |
| packages/apollo-react/src/test/setup.ts | Registers jest-axe matchers for Vitest suites. |
| packages/apollo-react/src/i18n/index.ts | Exports getPreImportedMessages helper for hosts merging catalogs. |
| packages/apollo-react/src/canvas/locales/en.json | Adds guardrails chrome strings + canonical validator/parameter/option copy (English). |
| packages/apollo-react/src/canvas/components/index.ts | Exports the Guardrails canvas family from the canvas components barrel. |
| packages/apollo-react/src/canvas/components/Guardrails/utils.ts | Adds parameter seeding/sync/validation helpers for guardrail parameters. |
| packages/apollo-react/src/canvas/components/Guardrails/use-guardrail-definitions.ts | Adds useGuardrailDefinitions hook (fetch + parse + enrich + refetch). |
| packages/apollo-react/src/canvas/components/Guardrails/types.ts | Defines guardrail parameter and form prop types for the validator editor surface. |
| packages/apollo-react/src/canvas/components/Guardrails/render-parameter-bridge.tsx | Bridges host renderParameter overrides into MetadataForm custom components via context. |
| packages/apollo-react/src/canvas/components/Guardrails/index.ts | Public exports for the guardrails family, including the new definitions layer APIs. |
| packages/apollo-react/src/canvas/components/Guardrails/guardrail-validator-form.tsx | Implements validator parameter form using MetadataForm + guardrail-owned custom fields. |
| packages/apollo-react/src/canvas/components/Guardrails/guardrail-form-layout.tsx | Adds shared modal/inline layout wrapper for guardrail builder forms. |
| packages/apollo-react/src/canvas/components/Guardrails/guardrail-form-layout.test.tsx | Adds behavior + a11y tests for the shared form layout. |
| packages/apollo-react/src/canvas/components/Guardrails/guardrail-form-layout.stories.tsx | Adds Storybook examples for the shared form layout modes. |
| packages/apollo-react/src/canvas/components/Guardrails/form-schema-builder.ts | Builds MetadataForm schemas for guardrail parameters + coercion helper. |
| packages/apollo-react/src/canvas/components/Guardrails/form-schema-builder.test.ts | Tests schema mapping/coercion rules for guardrail parameter definitions. |
| packages/apollo-react/src/canvas/components/Guardrails/definitions-wire.ts | Adds hand-written wire types for the definitions endpoint payload. |
| packages/apollo-react/src/canvas/components/Guardrails/definitions-parse.ts | Adds zod validation + non-throwing parse result and issue reporting. |
| packages/apollo-react/src/canvas/components/Guardrails/definitions-parse.test.ts | Tests parsing guarantees + zod boundary constraints. |
| packages/apollo-react/src/canvas/components/Guardrails/definitions-parity.test.ts | Ensures canonical English matches Flow/Agents baselines and declares divergences. |
| packages/apollo-react/src/canvas/components/Guardrails/definitions-enrich.ts | Adds pure enrichment (copy resolution + parameter shaping + folder metadata helper). |
| packages/apollo-react/src/canvas/components/Guardrails/definitions-copy.test.ts | Tests copy table, message id conventions, and catalog parity. |
| packages/apollo-react/src/canvas/components/Guardrails/components/parameter-label.tsx | Shared parameter label renderer (required marker + info tooltip). |
| packages/apollo-react/src/canvas/components/Guardrails/components/mixed-scopes-banner.tsx | Adds banner for mixed-scope guardrails with “save as new” hint. |
| packages/apollo-react/src/canvas/components/Guardrails/components/mixed-scopes-banner.test.tsx | Tests mixed-scopes banner rendering + a11y. |
| packages/apollo-react/src/canvas/components/Guardrails/components/map-enum-field.tsx | Adds map-enum editor bound to sibling enum-list selection via useWatch. |
| packages/apollo-react/src/canvas/components/Guardrails/components/guardrail-status-banner.tsx | Adds status banners for disabled/unauthorized/feature-disabled definitions. |
| packages/apollo-react/src/canvas/components/Guardrails/components/guardrail-status-banner.test.tsx | Tests status banner roles and a11y. |
| packages/apollo-react/src/canvas/components/Guardrails/components/guardrail-scope-selector.tsx | Adds scope/tool targeting selector using chips and self-healing behavior. |
| packages/apollo-react/src/canvas/components/Guardrails/components/guardrail-scope-selector.test.tsx | Tests selector behavior, targeting semantics, and a11y. |
| packages/apollo-react/src/canvas/components/Guardrails/components/guardrail-chip.tsx | Adds chip toggle component (CVA variants) for scopes/options. |
| packages/apollo-react/src/canvas/components/Guardrails/components/guardrail-chip.test.tsx | Tests chip pressed state, interactions, and a11y. |
| packages/apollo-react/src/canvas/components/Guardrails/components/guardrail-action-section.tsx | Adds action configuration section (log/block/filter/escalate). |
| packages/apollo-react/src/canvas/components/Guardrails/components/guardrail-action-section.test.tsx | Tests action section branching + a11y. |
| packages/apollo-react/src/canvas/components/Guardrails/components/field-shell.tsx | Adds non-input “field shell” container with error border option. |
| packages/apollo-react/src/canvas/components/Guardrails/components/field-shell.test.tsx | Tests field shell invalid styling toggle. |
| packages/apollo-react/src/canvas/components/Guardrails/components/enum-list-chips-field.tsx | Adds chip-based enum-list editor for small option sets. |
| packages/apollo-react/src/canvas/components/Guardrails/builder-utils.ts | Adds builder helpers for defaults and required-field validation. |
| packages/apollo-react/src/canvas/components/Guardrails/builder-utils.test.ts | Tests builder utils behaviors and edge cases. |
| packages/apollo-react/src/canvas/components/Guardrails/builder-types.ts | Adds public structural types for persisted guardrail values and builder slots. |
| packages/apollo-react/src/canvas/components/Guardrails/fixtures/host-copy-baselines.ts | Adds transcribed Flow/Agents English baselines for copy parity tests. |
| packages/apollo-react/src/canvas/components/Guardrails/fixtures/definitions-wire.fixtures.ts | Adds realistic wire fixtures for parsing/enrichment/copy tests. |
| packages/apollo-react/package.json | Exposes ./canvas/guardrails subpath and adds deps (class-variance-authority, jest-axe, @types/jest-axe). |
Review details
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
- Files reviewed: 81/82 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| return { | ||
| definitions, | ||
| wire: parsed.definitions, | ||
| invalid: parsed.invalid, | ||
| inputError: parsed.inputError, | ||
| loading, | ||
| error, | ||
| refetch: load, | ||
| }; |
| const [rowIds, setRowIds] = useState<string[]>(() => items.map(() => crypto.randomUUID())); | ||
| const [prevLength, setPrevLength] = useState(items.length); | ||
| if (prevLength !== items.length) { | ||
| setPrevLength(items.length); | ||
| setRowIds((prev) => | ||
| prev.length < items.length | ||
| ? [ | ||
| ...prev, | ||
| ...Array.from({ length: items.length - prev.length }, () => crypto.randomUUID()), | ||
| ] | ||
| : prev.slice(0, items.length) | ||
| ); | ||
| } |
📊 Coverage + size by packagePer-package coverage and bundle size on this PR. New-line coverage = of the source lines this PR adds or changes, the % hit by tests.
"Coverage" is each package's own |
Storybook visual diffBaseline is the deployed main Storybook, so changes merged to main after this branch was last updated can also appear here. Logs Updated (PT): Sep 09, 2026, 05:57:29 AM |
Builds the shared guardrail definitions layer AL-574 asks for, in the apollo-react guardrails family. This is the seam between the
/api/execution/guardrails/definitionspayload and theGuardrailDefinitionsGuardrailBuilderalready renders. Flow and Agents each carry their own copy of this today, and the two have drifted.Stacked on #1138
Based on
feat/apollo-react-guardrails-familyrather thanmain, so the diff here is only this work: one commit, 27 files. Merge order is #1107, then #1138, then this.The base moves to
mainbefore this goes for real review.pr-checks,dev-publishandpreview-deployare all gated onbranches: [main, 'support/**'], so they do not run against a feature base: read the absent checks as "not run", not "passed". Local results are under Verification below, and there is no@uipath/apollo-react@...-pr1139.xpreview package yet for the same reason.Built fresh from #1107/#1138, the rescoped Jira ticket and Confluence §7.3/§7.4.5. Nothing is ported from the closed #1132.
What lands
definitions-wire.tsGuardrailDefinitionWire,GuardrailParameterDefinitionWiredefinitions-parse.tsparseGuardrailDefinitionsdefinitions-copy.tsGUARDRAIL_COPY_EN,GUARDRAIL_COPY_EN_MESSAGES,useGuardrailDefinitionCopydefinitions-enrich.tsenrichGuardrailDefinitions,isByoGuardrailDefinition,humanizeGuardrailParameterId,withGuardrailFolderMetadatause-guardrail-definitions.tsuseGuardrailDefinitionsPlus a README section, exports from
Guardrails/index.ts(nopackage.jsonchange,./canvas/guardrailsalready points there), and the newguardrails.definitions.*ids in the canvas catalog.Contract highlights
inputError; a single bad definition is dropped whole and reported ininvalid, which is what both products already do entry by entry. Unknown keys stripped. Transport errors and data errors are separate channels: a malformed payload leaveserrornull.definitions-parse.tsand pinned to the hand-written mirror by a bidirectional assignability check that sits on the hot path (so it cannot be dropped as dead code), a runtime key-set assertion, and a source-level import guard.grep zod dist/canvas/components/Guardrails/**/*.d.tsis empty.EnrichedGuardrailDefinition extends GuardrailDefinition, so its output feeds the builder unmapped.options.definitionsskips the request entirely, which is how Agents keeps SWR, Flow studio and workbench keep react-query, and the vsix keeps postMessage.hiddenValidatorshides nothing by default and never hides a BYO definition. Which validators a product exposes is an entitlement decision, so it stays with the caller.One deliberate divergence from
useDiscoveryModelsThe context is compared by content, not identity. Keying the effect off context identity means an inline context object refetches on every render, and since every response sets state the loop never terminates. The hook test caught it at 17,640 calls before the fix.
useDiscoveryModelsstill has this footgun; worth a separate look.Canonical copy moves onto lingui
The display copy for the six built-in validators currently lives twice, in Agents'
OOB_GUARDRAILS_I8Nand Flow'sbuildValidatorDisplayInfo. Here it is 63 lingui messages in the shared canvas catalog, so both products get the same wording and the same translations, and the strings enter the real loc pipeline instead of a host-side constant.Message ids use raw wire values (
USSocialSecurityNumber), never a transcribed slug. Transcribing is exactly how the two products ended up keying the same Finland entity asfinNationalIdandfiNationalId.Translations harvested from whichever product each string was adopted from: 62 of 63 ids in each of the 12 locales. The gap is
FIPassportNumber, which Agents has not had translated; it falls back to English per key.ruis empty, matching both products and this package's existing convention.QA-visible copy changes
The two products' English differs in 17 places. Each choice is declared with a reason in
definitions-parity.test.tsand asserted against both products' transcribed copy, so the suite fails on an undeclared difference, a stale declaration, or a third wording we invented.Agents users will see: shorter validator descriptions (the "This validator is designed to..." preamble is gone from four of them);
harmfulContentEntitiesreads "Content categories" and its thresholds "Severity thresholds";ipEntitiesreads "Content types"; PII thresholds pluralized; LLM-as-judgethresholdreads "Strictness";SelfHarmreads "Self-harm"; a new LLM-as-judge cost note.Flow users will see: three new parameter tooltips (PII, prompt-injection and harmful-content thresholds) and the Finland passport entity, which Flow renders as a raw value today.
prompt_injectionkeeps Agents' wording as the deliberate exception to the concision rule, because the Noma Security attribution is load-bearing.Verification
vitest --run: 2948 passed. 8 pre-existing failures incanvas/utils/Storage.test.tsandcanvas/hooks/useStorageState.test.ts(localStorageundefined under happy-dom); they fail in isolation with none of this code loaded, and this branch touches onlyGuardrails/andcanvas/locales/.tsc --noEmit: clean.biome check: clean.rslib build: clean; no zod in the folder's emitted.d.ts.Review questions
withGuardrailFolderMetadata, because resolving it needs each product's connections API (Agents pagesfetchResources, Flow callsgetConnectionById). Do you want it inside the hook instead, as aresolveConnectionscallback?