Skip to content

feat(apollo-wind): guardrails component family (validator form + builder screen) - #1107

Open
apetraru-uipath wants to merge 4 commits into
mainfrom
feat/apollo-wind-guardrail-validator-form
Open

feat(apollo-wind): guardrails component family (validator form + builder screen)#1107
apetraru-uipath wants to merge 4 commits into
mainfrom
feat/apollo-wind-guardrail-validator-form

Conversation

@apetraru-uipath

@apetraru-uipath apetraru-uipath commented Sep 1, 2026

Copy link
Copy Markdown

What

Adds GuardrailValidatorForm plus companion types/utils/locale catalogs under packages/apollo-wind/src/components/custom/guardrails/ — the first shared Guardrails component from the Guardrails Apollo Components plan (§7.7 recommended pilot, decision 7.4-A: build once on apollo-wind). Extracted from Flow Workbench's GuardrailValidatorRuleSection (599 LOC + 593 LOC of tests), which becomes a thin flag-switched adapter over this component ([flow-workbench PR to follow]); Agents frontend-sw adopts in a later stage via its existing shadow-DOM wind pattern.

  • All seven parameter types: number, text, boolean, enum, enum-list (inline chips / popover), text-list (stable-row add/remove), map-enum (rows derived from a sibling keySource enum-list).
  • Fully controlled + validation-free: host owns values (parameters/onChange) and validation (errors map + onClearError); getRequiredEmptyParameterIds is the exported predicate hosts use to build the map.
  • renderParameter override slot so a product can replace one parameter's editor (e.g. Agents' judge-model ModelPicker, incl. sidecar-parameter writes via onParametersChange).
  • Save-time companions ship with the renderer to kill a documented drift risk: seedGuardrailParameters, syncMapEnumParameters (mirrors the map-enum editor's key resolution), dropEmptyOptionalParameters.
  • Behavior deltas vs the Flow original (deliberate): a stored enum value missing from options renders as a synthetic option instead of blanking; error messages render on all seven types; label/input associations added (jest-axe clean).

⚠️ Decision needed: first i18n in apollo-wind (design-doc 7.4-B)

This component ships its own chrome strings (2 placeholders, "Add", 2 aria-labels — domain strings stay host-resolved) as TypeScript locale catalogs for 14 languages, loaded lazily via a locale prop with per-key English fallback and a labels override prop. It follows ap-model-picker's loadMessages mechanism but framework-free (no lingui, no compile step); translations were harvested from Flow Workbench's existing catalogs, so no new translation work. LOCALIZATION_GUIDE prescribes props-based localization — treat this PR as the ratification vehicle for component-owned catalogs. If declined, dropping locales/ + the loader is non-breaking (the labels prop remains a complete path).

Also note: this wires components/custom into the public API for the first time (package.json exports + barrels). Only the guardrails family is exported; the 25 pre-existing prototype modules in custom/ stay private. A .gitignore negation was added because the root **/locales/*.ts rule (aimed at lingui-compiled artifacts) would otherwise ignore these hand-written catalogs.

Update (phase 2): the whole Add/Edit guardrail screen

The family now also ships GuardrailBuilder (the complete Add/Edit screen: status banners, usage note, type display, name, description, validator parameters, scope selector with the targeted/addable tools split, action section incl. escalation, evaluations toggle, mixed-scopes banner, Save/Cancel/Save-as-new footer) and GuardrailFormLayout (the shell, standalone: inline / inline+hideHeader / modal, secondaryAction, saveDisabled, footerStart). Extracted from Flow's BuiltInGuardrailBuilder (415 LOC) and consumed behind the same flag (flow-workbench PR below).

  • Escalation is slot-driven: renderRecipientSearch (directory autosuggest) and renderAppPicker are host capabilities injected as render props with usable fallbacks; escalateHelp renders the host's marketplace line, so product URLs never ship here.
  • Validation: the builder owns its checks and gates its own Save; message text comes from the locale catalogs (overridable per string via labels), and an optional flat errors prop merges over internal validation (host wins per field) for hosts that validate externally (Agents' zod flow at stage 2). The pure predicates (getGuardrailActionErrorFields, getGuardrailSelectorErrorFields) are exported.
  • Types: structural mirrors in builder-types.ts (GuardrailDefinition, GuardrailBuilderValue, GuardrailAction incl. escalate shapes, GuardrailRecipientType as a const object); mutual assignability with Flow'''s wire types is pinned by a type-assertion file on the Flow side.
  • Reset contract: form state initializes at mount; remount with a new key to reset (every known host already remounts per session).
  • A11y: jest-axe across all new components surfaced and fixed two inherited gaps (icon-only back button without a label; recipient-type select without a name). The three hand-rolled status divs are normalized onto Alert (role=alert on the error tone).
  • Locale catalogs extend to ~55 builder strings, harvested from Flow'''s 13 translated locales (ru stays empty, per source).
  • Tests: 182 in the family (22 builder tests + helpers ported from Flow, 16 scope-selector incl. the AL-428 aria-pressed contract, 6 banner, plus new form-layout / action-section / escalation / status-banner / predicate suites, axe everywhere). Deferred to the palette phase: the Add-screen guardrail-type dropdown.

Update (phase 3): wind-idiom restyle

The family now follows the design-system rules end to end; the verbatim-from-Flow styling is gone. Visual deltas for reviewers (all flag-gated in consumers): required asterisks are foreground-colored per RequiredIndicator'''s documented decision (with an sr-only "(required)" suffix); the three status banners + usage note are Alert variants on the error/warning/info token families (the family'''s only raw Tailwind palette and hand-written dark: pairs are deleted — they ignored the -hc/future themes); chips are a shared GuardrailChip (wind Toggle + cva pill variants, brand-subtle selected state, focus rings, consistent aria-pressed); enum-list parameters with >8 options render wind'''s MultiSelect instead of a hand-rolled popover clone; error messages move from destructive to the error token family via FormFieldError (aria-live), and Input-based fields adopt the primitive'''s error prop; field anatomy is FormField/FormFieldLabel; sizes collapse onto wind'''s scales (Button 2xs, Textarea minRows, Select default height); hrSeparator.

Two additive ui/ edits: Textarea and SelectTrigger base classes gain aria-invalid:border-error aria-invalid:focus-visible:ring-error (Input already had them). Full suite re-run green (1618 tests).

Conventions proposed for custom/ with this PR (new for the directory, universal in ui/): cva variant objects exported alongside components (guardrailChipVariants), and data-slot attributes on composites. Also flagged: RequiredIndicator'''s sr-only suffix is English-only (could take an i18n prop later), and ui/multi-select.tsx has a pre-existing nested-<button> React warning (Badge remove-button inside the trigger button) that this PR surfaces but does not touch.

Testing

  • 75 unit tests (ported from Flow's suite + new: jest-axe kitchen-sink, renderParameter slot incl. sidecar writes, onClearError ordering, locale catalog loading for all 14 locales, labels override, stale-enum option). Coverage on the new files: 94% statements / 82% branches / 93% functions.
  • Full apollo-wind suite: 1502 passed.
  • 7 Storybook stories (autodocs), validated interactively in a browser: PII enum-list↔map-enum live sync, host-computed errors with per-param clearing, >8-option popover, ja catalog.

🤖 Generated with Claude Code

Shared configuration form for OOTB guardrail validators, extracted from Flow
Workbench's canvas-local implementation so Flow and Agents render the same
component. Covers all seven parameter types (number, text, boolean, enum,
enum-list, text-list, map-enum incl. keySource-derived rows), a host-owned
errors map with per-parameter clearing, a renderParameter override slot for
product-specific editors (e.g. a judge-model picker), save-time companion
utils (seedGuardrailParameters, syncMapEnumParameters,
dropEmptyOptionalParameters, getRequiredEmptyParameterIds), and
component-owned locale catalogs for 14 languages harvested from Flow's
existing translations.

Also wires the custom/ shelf into the public API (components/custom exports);
only the guardrails family is exported, the pre-existing prototype modules
stay private.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 1, 2026 15:40
@apetraru-uipath apetraru-uipath added the dev-packages Adds dev package publishing on pushes to this PR label Sep 1, 2026
@github-actions github-actions Bot added pkg:apollo-wind size:XXL 1,000+ changed lines. labels Sep 1, 2026
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Apollo Coded App preview deployments are ready.

Project Status Preview Updated (PT)
apollo-design Ready Preview · Logs Sep 02, 2026, 01:51:30 AM
apollo-docs Ready Preview · Logs Sep 02, 2026, 01:51:30 AM
apollo-landing Ready Preview · Logs Sep 02, 2026, 01:51:30 AM
apollo-vertex Ready Preview · Logs Sep 02, 2026, 01:51:30 AM

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Dependency License Review

  • 1951 package(s) scanned
  • ✅ No license issues found
  • ⚠️ 2 package(s) excluded (see details below)
License distribution
License Packages
MIT 1722
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

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

📦 Dev Packages

Package Status Updated (PT)
@uipath/apollo-wind@2.43.1-pr1107.ee6ecf9 🟢 Published Sep 02, 2026, 01:50:10 AM

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

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Introduces the first shared “Guardrails” component family in apollo-wind by adding GuardrailValidatorForm (a fully controlled, host-validated parameter editor) along with supporting types, i18n catalog loading, and save-time utilities. This also begins exposing components/custom as a public API surface for the package.

Changes:

  • Added GuardrailValidatorForm with editor support for 7 parameter types plus an override slot (renderParameter) for product-specific editors.
  • Added guardrails utilities (seedGuardrailParameters, syncMapEnumParameters, dropEmptyOptionalParameters, getRequiredEmptyParameterIds) and a lightweight locale-catalog loader with 14 locale modules.
  • Updated barrels/exports (src/index.ts, src/components/*, and package.json exports) and adjusted .gitignore to include handwritten locale TS sources.
File summaries
File Description
packages/apollo-wind/src/index.ts Exposes the guardrails component family + utilities/types from the package root entrypoint.
packages/apollo-wind/src/components/index.ts Adds custom/ shelf to the components barrel.
packages/apollo-wind/src/components/custom/index.ts Publicly exports only the reviewed guardrails family from custom/.
packages/apollo-wind/src/components/custom/guardrails/index.ts Guardrails family barrel (component, types, i18n, utilities).
packages/apollo-wind/src/components/custom/guardrails/types.ts Defines the parameter/definition unions and the GuardrailValidatorFormProps contract.
packages/apollo-wind/src/components/custom/guardrails/utils.ts Adds seeding/cleanup/sync helpers and the required-empty predicate for hosts.
packages/apollo-wind/src/components/custom/guardrails/utils.test.ts Unit coverage for guardrails utilities (seeding, syncing, pruning, required-empty predicate).
packages/apollo-wind/src/components/custom/guardrails/i18n.ts Defines chrome-string labels, default English labels, interpolation and merge logic.
packages/apollo-wind/src/components/custom/guardrails/load-messages.ts Adds supported-locale list, locale normalization, and lazy catalog loading.
packages/apollo-wind/src/components/custom/guardrails/load-messages.test.ts Tests locale resolution, catalog loading/fallback behavior, and label merging.
packages/apollo-wind/src/components/custom/guardrails/guardrail-validator-form.tsx Implements the main controlled form renderer and async label resolution hook.
packages/apollo-wind/src/components/custom/guardrails/guardrail-validator-form.test.tsx Comprehensive RTL + jest-axe coverage across all parameter types, overrides, i18n, a11y.
packages/apollo-wind/src/components/custom/guardrails/guardrail-validator-form.stories.tsx Storybook coverage for interactive scenarios, errors, override slot, and localization.
packages/apollo-wind/src/components/custom/guardrails/components/parameter-label.tsx Shared label + tooltip + inline error rendering primitives for parameter fields.
packages/apollo-wind/src/components/custom/guardrails/components/number-parameter-field.tsx Number editor field implementation.
packages/apollo-wind/src/components/custom/guardrails/components/text-parameter-field.tsx Multiline text editor field implementation.
packages/apollo-wind/src/components/custom/guardrails/components/boolean-parameter-field.tsx Boolean editor field implementation.
packages/apollo-wind/src/components/custom/guardrails/components/enum-parameter-field.tsx Enum select editor (including “synthetic option” behavior for stale stored values).
packages/apollo-wind/src/components/custom/guardrails/components/enum-list-parameter-field.tsx Enum-list chips editor with popover fallback for long option lists.
packages/apollo-wind/src/components/custom/guardrails/components/map-enum-parameter-field.tsx Map-enum editor rendering rows derived from a sibling enum-list parameter.
packages/apollo-wind/src/components/custom/guardrails/components/text-list-parameter-field.tsx Text-list repeated-row editor with add/remove and stable-row key strategy.
packages/apollo-wind/src/components/custom/guardrails/README.md Local package documentation for contract, utilities, localization, and shadow-DOM consumption notes.
packages/apollo-wind/src/components/custom/guardrails/locales/en.ts English chrome-string catalog module.
packages/apollo-wind/src/components/custom/guardrails/locales/de.ts German chrome-string catalog module.
packages/apollo-wind/src/components/custom/guardrails/locales/es.ts Spanish chrome-string catalog module.
packages/apollo-wind/src/components/custom/guardrails/locales/es-MX.ts Spanish (Mexico) chrome-string catalog module.
packages/apollo-wind/src/components/custom/guardrails/locales/fr.ts French chrome-string catalog module.
packages/apollo-wind/src/components/custom/guardrails/locales/ja.ts Japanese chrome-string catalog module.
packages/apollo-wind/src/components/custom/guardrails/locales/ko.ts Korean chrome-string catalog module.
packages/apollo-wind/src/components/custom/guardrails/locales/pt.ts Portuguese chrome-string catalog module.
packages/apollo-wind/src/components/custom/guardrails/locales/pt-BR.ts Portuguese (Brazil) chrome-string catalog module.
packages/apollo-wind/src/components/custom/guardrails/locales/ro.ts Romanian chrome-string catalog module.
packages/apollo-wind/src/components/custom/guardrails/locales/ru.ts Russian chrome-string catalog module (currently empty; relies on per-key English fallback).
packages/apollo-wind/src/components/custom/guardrails/locales/tr.ts Turkish chrome-string catalog module.
packages/apollo-wind/src/components/custom/guardrails/locales/zh-CN.ts Chinese (Simplified) chrome-string catalog module.
packages/apollo-wind/src/components/custom/guardrails/locales/zh-TW.ts Chinese (Traditional) chrome-string catalog module.
packages/apollo-wind/package.json Adds ./components/custom exports to publish the new public custom shelf.
.gitignore Un-ignores the handwritten guardrails locale TS sources under packages/apollo-wind/.../locales/*.ts.
Review details
  • Files reviewed: 37/38 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 +48 to +52
"./components/custom/*": {
"import": "./dist/components/custom/*.js",
"require": "./dist/components/custom/*.cjs",
"types": "./dist/components/custom/*.d.ts"
},
Comment on lines +40 to +44
const [prevLength, setPrevLength] = useState(items.length);
if (prevLength !== items.length) {
setPrevLength(items.length);
setRowIds((prev) =>
prev.length < items.length
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Storybook visual diff

⚠️ Visual changes detected: 26 added (of 248 compared, 222 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 02, 2026, 01:58:57 AM

@github-actions

github-actions Bot commented Sep 1, 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 41.59 MB 49.70 MB −2.25 MB
@uipath/apollo-react 39.8% 0.0% (0/1) 7.56 MB 29.11 MB +59.9 KB
@uipath/apollo-ui-icons 2.85 MB 6.91 MB 🆕 new
@uipath/apollo-wind 67.5% 95.2% (438/460) 505.3 KB 3.12 MB +87.7 KB
@uipath/ap-chat 85.8% 43.46 MB 56.09 MB +16.0 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.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings September 1, 2026 15:56

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

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (2)

packages/apollo-wind/package.json:52

  • The ./components/custom/* export will expose every top-level module under src/components/custom/ (e.g. canvas-studio.tsx, chat-*, panel-*), even though src/components/custom/index.ts explicitly says only reviewed families should be public. It also likely won’t resolve components/custom/guardrails correctly because the built output will be dist/components/custom/guardrails/index.*, not dist/components/custom/guardrails.*.

Consider removing the wildcard export and adding a single explicit ./components/custom/guardrails export instead (root @uipath/apollo-wind already re-exports the guardrails API).

    "./components/custom/*": {
      "import": "./dist/components/custom/*.js",
      "require": "./dist/components/custom/*.cjs",
      "types": "./dist/components/custom/*.d.ts"
    },

packages/apollo-wind/src/components/custom/guardrails/components/text-list-parameter-field.tsx:39

  • Calling setState during render (if (prevLength !== items.length) { ... }) is an anti-pattern and can trigger React warnings, extra renders, or infinite loops (especially under StrictMode’s double-invocation). Sync rowIds in an effect keyed on items.length instead of mutating state while rendering.
  // Stable per-row ids: without them, removing index N would reuse the DOM node of a
  // surviving sibling and swap a focused textarea's content with someone else's. Resync
  // during render (React's official pattern) when the `items` array length changes
  // out-of-band; the local add/remove handlers below already mutate ids in lockstep.
  const [rowIds, setRowIds] = useState<string[]>(() => items.map(() => crypto.randomUUID()));
  • Files reviewed: 37/38 changed files
  • Comments generated: 1
  • Review effort level: Lite

@@ -0,0 +1,114 @@
import { Plus, Trash2 } from 'lucide-react';
import { useCallback, useMemo, useState } from 'react';
* `getRequiredEmptyParameterIds`. Individual parameters can be replaced via
* `renderParameter` (e.g. to mount a product model picker for a judge-model parameter).
*/
export const GuardrailValidatorForm = forwardRef<HTMLDivElement, GuardrailValidatorFormProps>(

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.

This is a second form renderer inside apollo-wind. Neither the PR description nor guardrails/README.md mentions MetadataForm, but src/components/forms/ already owns "definition list → rendered fields": FormSchemaMetadataFormfield-renderer.tsx, with a custom field-type escape hatch and a plugin onValueChange hook for live-edit surfaces. NodePropertyPanel (apollo-react) is the precedent: a properties panel built from node manifests on exactly that stack.

Five of the seven parameter types already map to it: numbernumber, texttextarea, booleanswitch, enumselect (the host builds the schema, so it can append a stale stored value to options itself), enum-listmultiselect (ui/multi-select.tsx). Only two are real gaps: text-list, which is a generic "array of strings" control worth adding as a first-class field type for everyone, and map-enum, which is guardrails-shaped and is what type: 'custom' + registerCustomComponent exists for. renderParameter duplicates that same seam. The parameters-array ↔ record adapter is real work, but you've already written it (seedGuardrailParameters / syncMapEnumParameters / dropEmptyOptionalParameters).

The cost of the fork is already visible in the field components, because they reimplement ui/form-field.tsx instead of composing it:

  • ParameterError uses text-destructive; FormFieldError uses text-error with a comment explaining why (--destructive and --error resolve differently in several theme blocks in tailwind.consumer.css). Guardrail errors will be a different color from every other apollo-wind form error.
  • ParameterLabel renders a red *; RequiredIndicator deliberately uses foreground color ("an untouched required field isn't in an error state") and ships an sr-only "(required)" this drops.
  • The fields pass className={cn(error && 'border-destructive')} instead of Input/Textarea's error prop, so no aria-invalid/aria-errormessage reaches the DOM. jest-axe won't catch that.
  • space-y-2 vs. FormField's gap-1.5; switch layout mirrored vs. field-renderer's switch case; hand-rolled chips instead of Badge; h-9/h-8 overrides that opt out of the future: theme.
  • enum-list and map-enum call ParameterLabel without htmlFor, so those labels associate with nothing, and ParameterError has no aria-live and isn't linked to any control.

Suggested path: add string-list as a field type and tooltip to BaseFieldMetadata, register map-enum as a custom component (it can read its sibling via useFormContext), and reduce this file to buildGuardrailFormSchema(definitions) + MetadataForm. If the Flow adoption schedule doesn't allow that now, the minimum bar is composing FormField/FormFieldLabel/FormFieldError/RequiredIndicator, using the error props, and MultiSelect for enum-list, with the convergence tracked in the README so the next Guardrails component doesn't extend the fork.

GuardrailBuilder: the complete Add/Edit screen for an OOTB guardrail
validator (status banners, usage note, type display, name, description,
validator parameters via GuardrailValidatorForm, scope selector with the
targeted/addable tools split, action section incl. escalation, evaluations
toggle, mixed-scopes banner, Save/Cancel/Save-as-new footer), extracted from
Flow Workbench's canvas-local implementation.

GuardrailFormLayout ships standalone (inline / inline+hideHeader / modal,
secondaryAction, saveDisabled, footerStart region). Escalation's recipient
search and app picker are host capabilities injected via render props with
usable fallbacks; product URLs never ship in this package. The builder owns
its form state (remount to reset) and its validation (catalog messages,
labels overrides, optional host errors that merge over internal validation
and gate Save); the pure predicates are exported. Locale catalogs extend to
the new strings, harvested from Flow's existing translations.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings September 1, 2026 21:41

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

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (1)

packages/apollo-wind/src/components/custom/guardrails/components/text-list-parameter-field.tsx:44

  • This component updates state during render (setPrevLength / setRowIds inside the render body). This can trigger React warnings and can behave poorly under StrictMode/double-invocation; the resync should be done in an effect (e.g., useEffect keyed on items.length) or by restructuring state to avoid derived state.
  const [prevLength, setPrevLength] = useState(items.length);
  if (prevLength !== items.length) {
    setPrevLength(items.length);
    setRowIds((prev) =>
      prev.length < items.length
  • Files reviewed: 56/57 changed files
  • Comments generated: 4
  • Review effort level: Lite

Comment on lines +52 to +59
export function resolveGuardrailFormLocale(locale?: string): GuardrailFormLocale | undefined {
const raw = locale?.trim().replace(/_/g, '-');
if (!raw) return undefined;
const exact = GUARDRAIL_FORM_LOCALES.find((tag) => tag.toLowerCase() === raw.toLowerCase());
if (exact) return exact;
const base = raw.split('-')[0]?.toLowerCase();
return GUARDRAIL_FORM_LOCALES.find((tag) => tag.toLowerCase() === base);
}
if (!allowedKeys) return param;

// Guard against a null/non-object persisted map before indexing into it.
const currentMap = param.value && typeof param.value === 'object' ? param.value : {};
Comment on lines +108 to +112
<button
key={option}
type="button"
onClick={() => handleToggle(option, !isSelected)}
className={cn(
Comment on lines +122 to +126
<button
key={scope}
type="button"
onClick={() => handleToggleScope(scope)}
className={cn(
@apetraru-uipath apetraru-uipath changed the title feat(apollo-wind): add guardrail validator form component family feat(apollo-wind): guardrails component family (validator form + builder screen) Sep 1, 2026
…ystem idiom

Token-only colors and primitive reuse across the family, replacing the
verbatim Flow-port styling:

- Status banners and the usage note become Alert variants (destructive /
  warning / info) — deletes the only raw Tailwind palette in the family and
  its hand-written dark: pairs, which ignored the -hc and future themes. The
  warning banner is an explicit role=status live region.
- New GuardrailChip (wind Toggle + cva pill variants incl. an addable dashed
  appearance, brand-subtle selected state) replaces five hand-rolled chip
  variants; chips gain focus rings and consistent aria-pressed. FieldShell
  replaces the duplicated chip wells. Both exported with their variants.
- Enum-list parameters with more than eight options now render wind's
  MultiSelect instead of the hand-rolled popover clone (which clipped
  overflow chips).
- Field anatomy moves to FormField / FormFieldLabel required /
  FormFieldError: required asterisks follow RequiredIndicator (foreground
  color + sr-only suffix, per the documented wind decision), error messages
  move from the theme-unstable destructive token to the error family with
  aria-live, and Input-based fields adopt the primitive's error prop
  (aria-invalid / aria-errormessage wired for free).
- Shared InfoTooltip gives the evals info icon a real focusable button
  trigger. Off-scale size overrides collapse onto wind sizes (Button 2xs,
  Textarea minRows, Select default height); hr becomes Separator; data-slot
  attributes land on the family's composites.
- Additive ui/ change: Textarea and SelectTrigger base classes gain
  aria-invalid:border-error styling (Input already had it).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings September 2, 2026 08:42

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.

🔵 Needs a closer look

There are concrete correctness/consumption issues (notably exports subpath mapping and a strict-typing/indexing bug in syncMapEnumParameters), plus a React anti-pattern (state updates during render) that should be addressed before merge.

Review details

Suppressed comments (3)

packages/apollo-wind/src/components/custom/guardrails/utils.ts:72

  • syncMapEnumParameters builds currentMap as param.value ? param.value : {}. That widens the type to {} and makes currentMap[key] unsafe (and can fail TypeScript strict indexing). If you want the runtime guard, keep the type as Record<string, number> (and also exclude arrays) before indexing.
    packages/apollo-wind/package.json:52
  • The ./components/custom/* export maps to ./dist/components/custom/*.js, but this package outputs nested index.js files for folder entrypoints (e.g. src/components/custom/guardrails/index.tsdist/components/custom/guardrails/index.js). As written, imports like @uipath/apollo-wind/components/custom/guardrails are likely to fail resolution.
    "./components/custom/*": {
      "import": "./dist/components/custom/*.js",
      "require": "./dist/components/custom/*.cjs",
      "types": "./dist/components/custom/*.d.ts"
    },

packages/apollo-wind/src/components/custom/guardrails/components/text-list-parameter-field.tsx:44

  • This component calls setPrevLength / setRowIds during render when items.length changes. Updating state during render is not safe in React (can trigger render loops and breaks concurrent rendering assumptions). Please move the resync logic into an effect keyed on items.length, or use a useRef-backed id list that you mutate synchronously without setState.
  const [rowIds, setRowIds] = useState<string[]>(() => items.map(() => crypto.randomUUID()));
  const [prevLength, setPrevLength] = useState(items.length);
  if (prevLength !== items.length) {
    setPrevLength(items.length);
    setRowIds((prev) =>
  • Files reviewed: 63/64 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

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

Labels

dev-packages Adds dev package publishing on pushes to this PR pkg:apollo-wind size:XXL 1,000+ changed lines.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants