feat(apollo-wind): guardrail list section with reorder and status chips [AL-575] - #1133
Closed
andreizdrali-uipath wants to merge 1 commit into
Closed
Conversation
Contributor
Dependency License Review
License distribution
Excluded packages
|
Extracts the guardrails list section that exists twice today: Agents'
GuardrailsListingSection (968 LOC) and Flow's GuardrailsEditor + SortableGuardrailItem
+ MixedScopesBanner. Adopts Flow's prop shape, as the extraction recipe calls for.
The contract is that the host filters and the component renders. None of Agents' six
enableGuardrail* booleans crosses the boundary, and neither does Flow's canvas.guardrails
entitlement: hosts pass the surviving rows and definitions. Telemetry, the remove
confirmation dialog and the add/edit flows also stay host-side, so the callbacks are
intents rather than mutations.
Where the two products diverge, one shape plus a seam:
- row identity is `id` falling back to `name`, overridable with getItemId
- row actions default to Flow's inline buttons; renderItemActions swaps in Agents' menu
- rowActivatesEdit brings back Agents' clickable row, off by default
- formatScopes / formatAction keep localized scope and action copy host-side
- getItemOrigin tags governance-managed rows, which no record identifies on its own
onReorder reports the reordered visible array plus the move, so a host rendering a
filtered view can splice the change back into its full list without the package
knowing about the full list.
Every addition is opt-in, so an adopting host gets a swap that renders exactly what it
renders today. statusChips and previewChip are off by default; reorderDisabled splits the
drag handle from the action buttons, because Flow's read-only list stays reorderable and
Agents' does not; unstyled, footer, emptyState={null} and renderRowTooltip let Agents keep
its accordion chrome, its add button below the rows and its combined hover.
Status and origin chips are Badge-based rather than GuardrailChip: that wraps a Radix
Toggle, so it is a focusable button with pressed state, and these are read-only labels
that would otherwise land in the tab order. The geometry matches so the two chip
families still read as one system. GuardrailStatusBanner and MixedScopesBanner are
promoted from internal pieces to exports, additively.
Ten of the list's strings are harvested from Flow's canvas catalog into all 13
non-English catalogs, so the extraction introduces no new translation work. The three
label resolvers now share one merge helper instead of three copies of the same loop.
Coverage floors ratchet 60/63/54/58 to 69/72/63/67.
andreizdrali-uipath
force-pushed
the
feat/apollo-wind-guardrail-list
branch
from
September 9, 2026 07:35
5ad0d9b to
5e707c2
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements AL-575 (epic AL-526): the guardrails
list section, extracted once into
@uipath/apollo-wind.Draft, and stacked. Base is #1132, which is
itself stacked on #1107. Both are unmerged. A PR
based off those branches runs almost no CI, since
pr-checks,preview-deploy(including theStorybook visual diff),
dev-publish,pr-size,security-scanandcodeqlall filter theirpull_requesttrigger tomainandsupport/**. This stays draft until #1107 merges and thechain rebases onto
main.What it replaces
GuardrailsListingSection.tsxin Agents (968 LOC), andGuardrailsEditor.tsx(743) +SortableGuardrailItem.tsx(126) +MixedScopesBanner.tsx(38) in flow-workbench. Prop shapefollows Flow's, per the extraction recipe.
The contract
The host filters, the component renders.
GuardrailListnever drops a row.enableOutOfTheBoxGuardrails,enableGuardrailPII,enableGuardrailPromptInjection,enableGuardrailHarmfulContent,enableGuardrailIntellectualPropertyandenableGuardrailUserPromptAttacksstay in theAgents adapter, as does Flow's
canvas.guardrailsentitlement and its allowed-scope filter.Hosts pass the survivors as
guardrailsanddefinitions.onRemovefires the request; the confirmation dialog and the writestay host-side, because the two products confirm with different copy and unwind tool-scoped
guardrails differently. Same for
onAdd/onEdit.onReorderreports the reordered visible array plus the move, so a host rendering afiltered view (Flow's tool view over an agent-level list) can splice back without the package
knowing the full list exists.
Divergences, resolved as one shape plus a seam
nameidid ?? name, override withgetItemIdisReadOnlydisableddisabledrenderItemActionsswaps in the menurowActivatesEditopts informatScopes/formatActionlocalize host-sidegetItemOrigintags governance-managed rows: both products fetch them from a differentendpoint, so nothing on the record identifies them.
Behaviour parity first
Every addition is opt-in, so an adopting host gets a swap that renders exactly what it renders
today and turns the new UI on deliberately.
statusChipsfalsepreviewChipfalsereorderDisableddisabledfalse.unstyledfalseSectionAccordion, where the card border is extra chrome.footeraddSlot(header-only) cannot express.emptyState={null}undefinedcheck rather than??, so an explicitnullis honoured.renderRowTooltipSo each host passes:
previewChip,reorderDisabled={false}, and aformatActioncarrying its"Unknown" fallback. Everything else already matches.
previewChip,unstyled,hideHeader,emptyState={null},footer,rowActivatesEdit,formatAction={() => null},formatScopes,renderItemActions,renderRowTooltip.One deliberate exception I did not restore: Agents' drag handle is an
aria-hiddenicon withthe listeners on it, so it is unreachable by keyboard. Mine is a real button. That is a bug fix,
not a regression.
Reuse, and one deliberate non-reuse
GuardrailStatusBannerandMixedScopesBannerare promoted from internal pieces to exports andintegrated, as the ticket asks. Both additions are additive; nothing existing changed shape, so
there is no breaking-change footer.
The status and origin chips are not
GuardrailChip. That wraps a Radix Toggle, so it is afocusable button carrying pressed state; these chips are read-only labels and would otherwise
put fake buttons in the tab order and misreport to screen readers.
GuardrailStatusChipisBadge-based and reusesguardrailChipVariants' geometry tokens, so the two chip familiesstill read as one system. Reviewers who read the ticket line as "reuse the chip UX" should get
what they expected; reviewers who meant the component literally, please push back.
Chips and notices
Available+ originlocalrenders nothing, so the common row stays quiet.FeatureDisabledand
Unauthorisedchip as warnings,DisabledandUnavailableas errors. The two inlinenotices (BYO configuration disabled, BYO configuration gone) are the ones both products already
render, and they keep the
definitions.length > 0guard: without it every BYO row flashes "nolonger available" while the catalog is still in flight.
i18n
Ten of the list's strings are harvested from Flow's canvas catalog into all 13 non-English
catalogs, so this adds no translation work, matching how #1107 seeded the family. The four
status/origin chip labels and the row aria-label have no Flow equivalent and are English-only
for now; they fall back per key and hosts can supply them through
labelstoday. Flagged in theREADME.
The three label resolvers now share one merge helper instead of three copies of the same loop.
Tests
84 new tests. jest-axe on all three rendering suites. The keyboard reorder is exercised through
the real dnd-kit sensor rather than mocked: jsdom reports a zero rect for everything, so the test
lays the rows out on a synthetic vertical grid, which is the minimum geometry collision detection
needs to tell two rows apart.
Coverage floors ratchet 60/63/54/58 to 69/72/63/67 (measured 69.44 / 72.62 / 64.25 / 67.31,
up from 69.03 / 72.11 / 63.49 / 66.87 on the base branch). The five new modules are at 100%
lines and functions, 91 to 100% branches.
Local gate
typecheck,lint,testandbuildpass for@uipath/apollo-wind. Two notes for anyonereproducing on Windows:
packages/apollo-core'sbuild:fontsandpackages/apollo-ui-icons' build both fail here forpre-existing, unrelated reasons (a single-quoted postcss glob, and a path-join bug in the icon
rename step), which blocks turbo's dependency step. Neither package is touched by this branch.
forms/form-plugins.test.tsx(26) andui/calendar.test.tsx(1) fail identically on the basebranch, verified by stashing.
localStorageis undefined in that environment.Open question for review
Both products render governance-managed guardrails in a separate
CentralizedGuardrailsSectionrather than as chip-tagged rows in the main list. The ticket asks for an origin chip, which
implies one mixed list. Two things make that more than a chip:
GuardrailOriginChipand Flow's
CentralizedGuardrailOriginBadgeboth render BYO vs UiPath managed, notlocal vs governance. Flow's comment says why: "A connector may expose a validator id a
built-in also uses, so origin is otherwise invisible." So
getItemOriginhere redefines aword the products already use. Renaming it (
getItemAdministration?) is cheap now andbreaking later.
CentralizedGuardrailhas noid, a nullishname,scopesat the top level rather than underselector, and anActionTypeenuminstead of
{ $actionType }, plusexecutionStageand theappliesTo*flags. They are alsoread-only, unorderable, and click through to a details panel rather than the builder. A
merged list would need per-row capabilities and host-side mapping, which is a ticket rather
than a chip.
This PR ships the chip (behind
statusChips) and the seam so either layout stays possible, andleaves the separate sections alone. Worth deciding here whether to rename the prop before it is
public.
Next
Agents and flow-workbench adoption behind
EnableApolloGuardrailBuilder/canvas.guardrails.apollo-components, with the existing components kept verbatim as*Legacytwins. Those PRs need a real published version, not a preview pin, so they follow the release.