Skip to content

feat(apollo-react): shared guardrail definitions layer and useGuardrailDefinitions [AL-574] - #1139

Draft
andreizdrali-uipath wants to merge 1 commit into
feat/apollo-react-guardrails-familyfrom
feat/apollo-react-guardrail-definitions-layer
Draft

feat(apollo-react): shared guardrail definitions layer and useGuardrailDefinitions [AL-574]#1139
andreizdrali-uipath wants to merge 1 commit into
feat/apollo-react-guardrails-familyfrom
feat/apollo-react-guardrail-definitions-layer

Conversation

@andreizdrali-uipath

@andreizdrali-uipath andreizdrali-uipath commented Sep 9, 2026

Copy link
Copy Markdown

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/definitions payload and the GuardrailDefinitions GuardrailBuilder already 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-family rather than main, so the diff here is only this work: one commit, 27 files. Merge order is #1107, then #1138, then this.

The base moves to main before this goes for real review. pr-checks, dev-publish and preview-deploy are all gated on branches: [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.x preview 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

Module Public surface
definitions-wire.ts GuardrailDefinitionWire, GuardrailParameterDefinitionWire
definitions-parse.ts parseGuardrailDefinitions
definitions-copy.ts GUARDRAIL_COPY_EN, GUARDRAIL_COPY_EN_MESSAGES, useGuardrailDefinitionCopy
definitions-enrich.ts enrichGuardrailDefinitions, isByoGuardrailDefinition, humanizeGuardrailParameterId, withGuardrailFolderMetadata
use-guardrail-definitions.ts useGuardrailDefinitions
unknown payload → parseGuardrailDefinitions → enrichGuardrailDefinitions → GuardrailBuilder
                       (zod, private)          (canonical copy on lingui)
                                    useGuardrailDefinitions composes all three

Plus a README section, exports from Guardrails/index.ts (no package.json change, ./canvas/guardrails already points there), and the new guardrails.definitions.* ids in the canvas catalog.

Contract highlights

  • The parser never throws. A non-array payload sets inputError; a single bad definition is dropped whole and reported in invalid, which is what both products already do entry by entry. Unknown keys stripped. Transport errors and data errors are separate channels: a malformed payload leaves error null.
  • zod does not cross the boundary. The schema is private to definitions-parse.ts and 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.ts is empty.
  • Enrichment is pure and exported, so Flow's vsix bridge and non-React callers use it directly. EnrichedGuardrailDefinition extends GuardrailDefinition, so its output feeds the builder unmapped.
  • options.definitions skips the request entirely, which is how Agents keeps SWR, Flow studio and workbench keep react-query, and the vsix keeps postMessage.
  • hiddenValidators hides 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 useDiscoveryModels

The 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. useDiscoveryModels still 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_I8N and Flow's buildValidatorDisplayInfo. 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 as finNationalId and fiNationalId.

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. ru is empty, matching both products and this package's existing convention.

src/canvas uses no lingui macros, so lingui extract does not feed this catalog and never did: its entries are hand-authored. A test asserts the catalog matches the source in both directions, which is what extraction would otherwise do for you.

QA-visible copy changes

The two products' English differs in 17 places. Each choice is declared with a reason in definitions-parity.test.ts and 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); harmfulContentEntities reads "Content categories" and its thresholds "Severity thresholds"; ipEntities reads "Content types"; PII thresholds pluralized; LLM-as-judge threshold reads "Strictness"; SelfHarm reads "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_injection keeps 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 in canvas/utils/Storage.test.ts and canvas/hooks/useStorageState.test.ts (localStorage undefined under happy-dom); they fail in isolation with none of this code loaded, and this branch touches only Guardrails/ and canvas/locales/.
  • tsc --noEmit: clean.
  • biome check: clean.
  • rslib build: clean; no zod in the folder's emitted .d.ts.
  • 111 tests across the five new suites.

Review questions

  1. BYO connection and folder resolution (plan Q1). It stays host-side here, via withGuardrailFolderMetadata, because resolving it needs each product's connections API (Agents pages fetchResources, Flow calls getConnectionById). Do you want it inside the hook instead, as a resolveConnections callback?
  2. Canonical copy on lingui (plan Q3). Ratifying the move means accepting the 17 divergences above, each of which changes what one product's users see. Happy to split any of them back out if a specific string should stay as it is.

…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>
Copilot AI lite review requested due to automatic review settings September 9, 2026 12:23
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Apollo Coded App preview deployments are ready.

Project Status Preview Updated (PT)
apollo-design Ready Preview · Logs Sep 09, 2026, 05:32:11 AM
apollo-docs Ready Preview · Logs Sep 09, 2026, 05:32:11 AM
apollo-landing Ready Preview · Logs Sep 09, 2026, 05:32:11 AM
apollo-vertex Ready Preview · Logs Sep 09, 2026, 05:32:11 AM

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Dependency License Review

  • 1937 package(s) scanned
  • ✅ No license issues found
  • ⚠️ 2 package(s) excluded (see details below)
License distribution
License Packages
MIT 1708
ISC 88
Apache-2.0 55
BSD-3-Clause 27
BSD-2-Clause 23
BlueOak-1.0.0 8
MPL-2.0 4
MIT-0 3
CC0-1.0 3
MIT OR Apache-2.0 2
(MIT OR Apache-2.0) 2
Unlicense 2
LGPL-3.0-or-later 1
Python-2.0 1
CC-BY-4.0 1
(MPL-2.0 OR Apache-2.0) 1
Unknown 1
Artistic-2.0 1
(WTFPL OR MIT) 1
(BSD-2-Clause OR MIT OR Apache-2.0) 1
CC-BY-3.0 1
0BSD 1
(MIT OR CC0-1.0) 1
MIT AND ISC 1
Excluded packages
Package Version License Reason
@img/sharp-libvips-linux-x64 1.3.2 LGPL-3.0-or-later LGPL pre-built binary, not linked
khroma 2.1.0 Unknown MIT per GitHub repo, missing license field in package.json

Copilot AI 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.

🟡 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 useGuardrailDefinitions hook for fetching/composing guardrail definitions.
  • Moves canonical validator copy into the canvas Lingui catalog and adds parity tests against Flow/Agents baselines.
  • Enhances apollo-wind forms/UI with InfoTooltip, string-list field support, and aria-invalid styling 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.

Comment on lines +201 to +209
return {
definitions,
wire: parsed.definitions,
invalid: parsed.invalid,
inputError: parsed.inputError,
loading,
error,
refetch: load,
};
Comment on lines +47 to +59
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)
);
}
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

📊 Coverage + size by package

Per-package coverage and bundle size on this PR. New-line coverage = of the source lines this PR adds or changes, the % hit by tests.

Package Coverage New-line coverage Packed (gzip) Unpacked vs main
@uipath/apollo-core 42.01 MB 50.12 MB ±0
@uipath/apollo-react 41.3% 87.0% (596/685) 7.72 MB 29.82 MB +161.3 KB
@uipath/apollo-ui-icons 2.85 MB 6.91 MB ±0
@uipath/apollo-wind 66.7% 98.5% (65/66) 454.5 KB 2.88 MB +6.9 KB
@uipath/ap-chat 85.8% 43.95 MB 56.83 MB +25.8 KB

"Coverage" is each package's own coverage.include scope (e.g. apollo-core instruments only scripts/). "Packed"/"Unpacked" come from npm pack --dry-run and only cover built packages — "—" means not measured this run (package not affected / not built). "vs main" is the packed (gzipped) delta against the last successful main build (the package-sizes artifact from the Release workflow); "—" there means no main baseline was available this run. The baseline is main's latest build, not this PR's exact merge-base, so it includes any drift since the branch diverged. Packages with no vitest config are omitted.

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Storybook visual diff

⚠️ Visual changes detected: 10 changed, 27 added, 8 errored (of 885 compared, 840 unchanged). View report

Baseline 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

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.

2 participants