Skip to content

feat(apollo-wind): shared guardrail definitions layer with a canonical copy table [AL-574] - #1132

Open
andreizdrali-uipath wants to merge 1 commit into
feat/apollo-wind-guardrail-validator-formfrom
feat/apollo-wind-guardrail-definitions-layer
Open

feat(apollo-wind): shared guardrail definitions layer with a canonical copy table [AL-574]#1132
andreizdrali-uipath wants to merge 1 commit into
feat/apollo-wind-guardrail-validator-formfrom
feat/apollo-wind-guardrail-definitions-layer

Conversation

@andreizdrali-uipath

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

Copy link
Copy Markdown

AL-574a: shared guardrail definitions layer

AL-574 under epic AL-526. Stacked on #1107 — draft until that merges.

Why

The backend never sends display copy for built-in guardrails (DisplayName/Description/OptionLabels are unset; the friendly name is [JsonIgnore]). So Agents and Flow each hand-maintain their own ~450-line copy table for the same 6 validators — and they've already drifted in 16 strings ("Detection threshold" vs "thresholds", SelfHarm vs "Self-harm", etc.). This ships that table once, plus the parsing it needs.

What

import { parseGuardrailDefinitions, enrichGuardrailDefinitions } from '@uipath/apollo-wind';

const { definitions, invalid } = parseGuardrailDefinitions(await response.json()); // never throws
const enriched = enrichGuardrailDefinitions(definitions, {
  translate: (key, defaultValue) => t(key, { defaultValue }), // your i18n
  hiddenValidators: ['prompt_injection'],
});

<GuardrailBuilder definition={enriched[i]} .../>  // #1107's prop shape, unchanged
Module Role
definitions-wire.ts Hand-written API types. No zod.
definitions-parse.ts zod schemas + parseGuardrailDefinitions. Only zod importer.
definitions-copy.ts Canonical English + copy-key contract.
definitions-enrich.ts enrichGuardrailDefinitionsEnrichedGuardrailDefinition extends GuardrailDefinition.

No fetching (mirrors #1107 — zero transport in its diff; each host's transport is irreducibly different). No component changed — GuardrailBuilder already takes definition: GuardrailDefinition; enriched[i] slots in with no cast.

Worth a look

  • zod fully sealed out of published types (hosts run different zod majors) — verified grep -rn zod dist/**/*.d.ts is empty for this folder.
  • Copy precedence (matches what both products render today): curated wins at definition level for non-BYO, wire wins at parameter level, optionLabels merge. BYO definitions get zero curated copy.
  • QA-visible behavior changes: Agents' PII thresholds go from unconstrained to 0–1 step 0.1; prompt_injection's description keeps Agents' wording (drops the "Noma Security" attribution otherwise); 11 more copy strings converge — full list + reasons in __fixtures__/host-copy-baselines.ts.
  • Copy keys are guardrail/<validator>/<slot> (/, never . — one host's i18n catalog is flat and splits on ., so a dotted key would silently fall back to English forever).

Testing

351 tests (133 new), 100% lines/functions/statements on all 4 new modules. Golden fixtures instead of snapshots (repo has none; biome reformats file snapshots and fails them). typecheck/lint/build green.

Gaps

  • No real tenant API capture for wire-raw-capture.ts — fixtures are hand-derived from the backend C# source instead, cross-checked against GuardrailsControllerTests.cs.
  • Barely any CI runs on a non-main-based PR (same as feat(apollo-wind): guardrails component family (validator form + builder screen) #1107).
  • Pre-existing, unrelated: forms/validation-converter.ts also leaks a zod type into published types — flagged as a follow-up, not fixed here.

Not in this PR: translating the copy into other locales (stays host-side, by design), any change to #1107's components, host adoption in Agents/Flow (separate tickets).

🤖 Generated with Claude Code

…l copy table

Guardrail definitions were parsed, and their display copy hand-maintained, separately
in Agents and Flow. The backend cannot supply built-in display copy: the DTO marks the
validator friendly name [JsonIgnore] and never sets displayName, description or
optionLabels for any built-in, so each frontend has described the same catalog itself.
The two tables had drifted in 16 strings.

Four modules own it once:

- definitions-wire.ts    hand-written public wire types, no zod
- definitions-parse.ts   private zod schemas, the only zod importer
- definitions-copy.ts    canonical English plus the copy-key contract
- definitions-enrich.ts  display-ready output for the shipped components

No fetching. Host transport is irreducibly different (query library, HTTP client,
tenant id vs name in the URL, and one host that never fetches because definitions
arrive over postMessage), so definitions arrive as data, mirroring how the components
already take their values. This matches PR #1107, which ships no transport either.

parseGuardrailDefinitions never throws: guardrail surfaces render in shadow roots with
no error boundary, where a thrown parse error blanks the panel. It returns valid
definitions plus one entry per dropped definition. Unknown keys are stripped, not
rejected, so a backend field addition cannot empty the catalog.

EnrichedGuardrailDefinition extends GuardrailDefinition, so the result feeds
GuardrailBuilder unchanged while carrying the fields hosts read outside the form.

zod stays private and out of the published type surface, since hosts sit on different
zod majors. Enforced by a compile-time bidirectional assignability assertion, a runtime
key-set assertion, a single-export assertion, and a source-level import guard. The
emitted declarations carry no zod reference.

Copy keys are guardrail/<validator>/<slot>, slash-separated: one host's catalog is flat
and its i18n library splits on '.', so a dotted key would miss and fall back to English
while every other locale went quietly untranslated.

EXPECTED_DIVERGENCES records all 16 copy decisions with reasons, asserted in both
directions against each host's transcribed English so the table cannot rot.
@github-actions

github-actions Bot commented Sep 8, 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

@andreizdrali-uipath
andreizdrali-uipath marked this pull request as ready for review September 8, 2026 14:28
@andreizdrali-uipath andreizdrali-uipath changed the title feat(apollo-wind): shared guardrail definitions layer with a canonical copy table feat(apollo-wind): shared guardrail definitions layer with a canonical copy table [AL-574] Sep 8, 2026
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