Skip to content

feat(apollo-react): guardrail remove confirmation dialog [AL-577] - #1149

Draft
andreizdrali-uipath wants to merge 25 commits into
mainfrom
feat/apollo-react-guardrail-remove-dialog
Draft

feat(apollo-react): guardrail remove confirmation dialog [AL-577]#1149
andreizdrali-uipath wants to merge 25 commits into
mainfrom
feat/apollo-react-guardrail-remove-dialog

Conversation

@andreizdrali-uipath

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

Copy link
Copy Markdown

AL-577, phase 1 of the AL-526 guardrails convergence. Adds GuardrailRemoveDialog to the
apollo-react guardrails family: the confirmation step before a guardrail is removed, with the
impact of the removal spelled out.

Review only the last five commits (8183f35c the dialog, ea87e66a the test that covers
the localized interpolation path, acde8bce the review fixes, 4449d929 the second pass,
98a0f049 the accent confirm button). The
twenty below them are
#1107 (feat/apollo-wind-guardrail-validator-form), #1138
(feat/apollo-react-guardrails-family) and #1139 (feat/apollo-react-guardrail-definitions-layer),
which this branches off. Nothing is taken from #1140 (list) or #1147 (palette); this stream is
a leaf off #1139 and can merge in any order relative to them.

Rebased 2026-09-11 onto #1138's current head e4b1e822, which carries the select.tsx
resolution against main. Until then this PR (and #1139/#1140/#1147) was CONFLICTING, so
GitHub built no test-merge ref and fired no pull_request workflows at all: no checks, and
dev-publish kept the preview frozen at yesterday's pr1149.35374df while looking published.
Every AL-577 commit above has a new sha; the content is byte-identical (git diff over the
Guardrails folder before and after the rebase is empty).

Rebased onto #1139's current tip on 2026-09-11, conflict-free.

Jira AL-577's description is pre-revision (it still says an apollo-wind component under
components/custom/guardrails/ with component-scoped locales). Built per the 2026-09-09
revision instead: apollo-react src/canvas/components/Guardrails/, strings as lingui ids in
the shared canvas catalog.

No dev preview pair for this branch yet. The @uipath/apollo-react@6.43.0-pr1149.35374df
/ @uipath/apollo-wind@2.48.0-pr1149.35374df pair the AL-577 host adapters pin was published
before the rebase and still carries #1107's superseded MetadataForm controlled-host seam.
Every branch under #1107 currently conflicts with main in
packages/apollo-wind/src/components/ui/select.tsx, so GitHub builds no test-merge ref and
fires no pull_request workflows at all: no pr-checks, no dev-publish, no error. The pair
will be republished once #1107's conflict is resolved upstream.

What ships

File Exports
guardrail-remove-dialog.tsx GuardrailRemoveDialog, GuardrailRemoveDialogProps
i18n.ts (extended) GuardrailRemoveDialogLabels, GUARDRAIL_REMOVE_DIALOG_EN_LABELS, GUARDRAIL_REMOVE_DIALOG_EN_MESSAGES, resolveGuardrailRemoveDialogLabels, useGuardrailRemoveDialogLabels

Plus guardrail-remove-dialog.stories.tsx (Components/UiPath/Guardrail Remove Dialog, 6
stories), guardrail-remove-dialog.test.tsx (16 tests), a new i18n.test.ts (9, catalog parity),
a README section, and 7 guardrails.remove-dialog.* ids in all 13 catalogs.

Built on wind's AlertDialog. Props: open, guardrailName, toolName?,
remainingToolNames?, remainingScopes?, affectedToolNames?, affectedScopes?,
onConfirm, onCancel, formatScope?, container?, labels?, className?.

The design decisions worth knowing:

  • The impact is structured props, not a content slot. There are exactly two situations and
    both products already describe both: affected* is what a full removal also takes the
    guardrail off, remaining* is what survives a tool-scoped removal. Flow computes them from
    one guardrail plus an isToolOnAgent flag; that branch converges by filling one pair or the
    other. All four default to empty, so a plain confirmation needs neither.
  • Deciding which removal is happening, and unwinding it, stays host-side. Agents strips
    the tool from matchNames, then drops the Tool scope, then deletes; Flow calls
    removeToolFromGuardrail. The dialog only renders the description of the outcome.
  • onConfirm / onCancel are intents and neither closes the dialog; open is controlled.
    onCancel covers Cancel and Escape and fires exactly once per dismissal.
  • The confirm button is a wind Button, not AlertDialogAction. Radix's action is a
    close button, so it drives onOpenChange(false) on top of the click and the dialog reports a
    cancel with every confirm. Flow's dialog does that today. A test pins it. If you would rather
    keep the primitive, the composition that also works is AlertDialogAction with
    onClick={(event) => { event.preventDefault(); onConfirm(); }}: Radix's Close composes the
    click with composeEventHandlers, which skips its own onOpenChange(false) when the event is
    defaultPrevented. The same test passes either way. Say which you prefer.
  • It takes the default accent variant, not destructive (245e0236). This shipped
    destructive; both hosts reported the red as a regression when they tested the shared dialog
    with the flag on, so it is now the same filled accent both products use for this action
    today. A test pins the variant, since this is the second decision on it.
  • Scopes arrive raw and localize through formatScope, matching GuardrailList's
    formatScopes. Scope vocabulary is product-owned and both products already hold the mapping.
  • The whole impact is the accessible description, via asChild on
    AlertDialogDescription, so an alert dialog announces what the removal costs rather than
    only its first sentence. It also fixes the <p>-inside-<p> nesting Flow has today.
  • container? picks the portal target: omit to inherit wind's PortalContainerProvider
    (the wind AlertDialogContent honours both), pass an element, or 'body' to force
    document.body even under a provider, which is what preserves Agents' deliberate escape
    from its shadow root. The union restates wind's own PortalContainerOverride, which the wind
    barrel does not export; a comment names the type the two have to stay in step with, and
    exporting it from wind is a separate tiny PR if you want one.
  • No onTrack, despite Confluence §7.5: same call as feat(apollo-react): guardrail list section [AL-575] #1140. The two products' event
    taxonomies do not overlap and both adapters wrap the intents anyway.

Review fixes (202d27e3)

  • ImpactList keys by index plus value. key={name} alone warns when two tools carry the
    same display name. Neither list is reordered or filtered, so the index is a stable part of
    the key.
  • Two comments where a reviewer would otherwise ask a question: the container union and
    its wind counterpart (above), and TEMPLATE_TOKENS, which is one map shared by every label
    set, so the form and builder strings receive a toolName placeholder they never declare. It
    is inert; the comment says so rather than leaving it to be rediscovered.

No behaviour changed, so the test totals below are unchanged by this commit.

Copy: converged, with no English changes

All 7 strings exist in both products with identical English, so the convergence costs
neither product a wording change. Harvested 1:1 into the 12 translated locales at 100%
coverage; ru left empty per convention (both products ship it empty too). English parity
against both products was asserted by the harvest script before writing, and i18n.test.ts
asserts catalog-to-source parity both ways plus orphan ids in every locale.

id English source
title Remove guardrail both, identical
confirm-prompt Please, confirm you’d like to remove "{name}" guardrail both, identical
also-applicable This guardrail is also applicable to: both, identical
removed-for-tool The guardrail will be removed for tool "{toolName}". both, identical
still-applicable It will still be applicable to: both, identical
cancel Cancel both, identical
remove Remove both, identical

Translations also matched between the two products in 83 of 84 locale/id pairs. The one
disagreement is Romanian "Remove": Agents' common.remove is "Înlăturare", Flow's is
"Eliminare". Took Flow's, because it matches the verb the dialog's own Romanian title uses
("Eliminați mecanismul de control"). One loc-review item.

QA-visible changes per host, once each adapter flips its flag

Agents (Guardrails/GuardrailRemoveDialog, MUI):

  • The dialog is restyled from MUI to Apollo/wind. The Remove button keeps its filled accent:
    MUI variant="contained" color="primary" becomes wind's default Button, so the colour does
    not change. (It was destructive red until 245e0236; see the resolved question below.)
  • The corner close (X) button disappears, and clicking the backdrop no longer dismisses
    the dialog. Radix's alert dialog deliberately refuses outside-click dismissal for a
    destructive confirmation; Cancel and Escape still dismiss. Flow already behaves this way.
  • Initial focus lands on Cancel (the least destructive control) instead of the close button.
    That is Radix's own default for an alert dialog, not something this code sets; a test pins
    it so a primitive upgrade cannot move it quietly.
  • The "also applicable to" list stops showing raw scope values and gets the order right.
    The dialog lists tools first, then scopes, and the tests pin that order.
    GuardrailsListingSection.handleRemoveGuardrail swaps the two arrays today (affectedScopes
    receives matchNames, affectedToolNames receives the other scopes), so what Agents shows
    right now is scopes first, with Llm rendered raw instead of "LLM calls". That is the bug,
    not the contract. The typed props make the adapter pass them the right way round;
    flag-on fixes it (§8.1 asked whether to preserve or fix: fixing).
  • Romanian only: the Remove button reads "Eliminare" instead of "Înlăturare" (above).
  • The impact lines become part of the dialog's accessible description.
  • No English copy changes.

Flow (GuardrailRemoveConfirmDialog):

  • No visual change to the buttons: Flow's AlertDialogAction already resolves to
    buttonVariants(), which is what the shared dialog's Button renders. (It was destructive
    red until 245e0236; see the resolved question below.)
  • Confirming stops also reporting a cancel (onConfirm and then onOpenChange(false) both
    fire today). Not user-visible, but it removes a spurious second state write and, once
    telemetry moves onto the same handlers, a spurious event.
  • Invalid <p>/<ul> nesting inside AlertDialogDescription is gone.
  • Romanian, and every other locale: unchanged (its own strings were adopted).
  • No English copy changes.

Adapter notes for both: pass formatScope (the mapping each product already owns), and pass
only the scopes worth listing, as both products already exclude the Tool scope when the
remaining tools are listed by name.

Second-pass review fixes (91f1e718)

  • The two ImpactList key sets are prefixed by kind (tool-, scope-), which the review
    asked for as a collision fix. It is not one, and the comment now says so: the two maps are
    separate child arrays of the one <ul> and React scopes keys per array, so a tool named
    Agent at index 0 never collided with the Agent scope at index 0. Checked by spying on the
    duplicate-key warning, which stays silent for that case and does fire for a real duplicate
    inside one array. The prefix stays because it makes the keys self-describing.
  • The i18n orphan scan covers ru. Deliberately empty is not licence to keep a stale id in
    it; the coverage check still leaves ru out.

Copilot read the impact-list order as a contract violation across three passes. It is not:
tools-first is intended and pinned by tests, and the "scopes first" it saw is the description of
Agents' current bug above, which this PR's typed props fix. That paragraph is reworded so the
next reader does not invert it.

Open questions

  1. Destructive (red) confirm button. Resolved: dropped, 245e0236. It shipped
    destructive and both hosts independently reported the red as a regression against the blue
    accent they use for this action today, after testing the flag-on dialog (Agents#6238 and
    flow-workbench). The confirm is now wind's default Button, which matches MUI
    contained/primary in Agents and buttonVariants() in Flow, so neither product's button
    changes colour. A test pins the variant.

    Does that leave the family inconsistent with feat(apollo-react): guardrail list section [AL-575] #1140's text-destructive Remove? No, and
    it removes the one inconsistency there was. The rule both products already follow is
    destructive tint on a ghost trigger, never a filled destructive button, and Flow's own
    DeleteButton writes it down: "Always ghost: a filled destructive button is the loudest
    thing on the panel, which removing one list row is not." feat(apollo-react): guardrail list section [AL-575] #1140's row button is that ghost
    trash with Flow's four-scope tint class verbatim, so it is on the right side of the rule; the
    filled confirm was the only place the family went further than either product. feat(apollo-react): guardrail list section [AL-575] #1140 is
    untouched here. The asymmetry that does remain is feat(apollo-react): guardrail list section [AL-575] #1140's alone and predates this: Agents'
    kebab Remove item carries no tint at all today, so the red trash is QA-visible there.

  2. Should formatScope default to the family's own scope labels rather than the raw wire
    value? guardrails.builder.scope-agent-label / -llm-label / -tool-label already exist
    in the canvas catalog with exactly the wording both products use. Raw is what shipped, to
    match GuardrailList's formatScopes and to keep scope vocabulary product-owned, but it
    is the one prop an adapter can forget and get a visible regression (Llm).

  3. Agents' corner close (X) and backdrop dismissal are gone (above). Confirm that is
    acceptable, or the dialog needs a dismissible escape hatch.

  4. mergeLabels in i18n.ts is introduced here byte-identically to the helper feat(apollo-react): guardrail list section [AL-575] #1140 and
    feat(apollo-react): add-guardrail palette [AL-576] #1147 each added, so the three streams collapse onto one copy on merge rather than four
    copies of the same loop. It is the only pre-existing code this commit touches, and it is a
    pure refactor of resolveGuardrailBuilderLabels / resolveGuardrailFormLabels (two new
    tests cover them). Say the word if you would rather see it land only once, in whichever
    stream merges first.

  5. i18n.test.ts is new on this stream and feat(apollo-react): guardrail list section [AL-575] #1140 / feat(apollo-react): add-guardrail palette [AL-576] #1147 each add their own copy of the file.
    Merging keeps all three suites; nothing else in it collides.

Verification

  • Guardrails suite 345 passing, 20 files (26 new: 17 dialog + 9 i18n), jest-axe on the
    dialog including the focus trap, initial focus, Escape and the portal container. One test
    renders under ApI18nProvider locale="ja" and asserts the guardrail name and tool name land
    inside the Japanese catalog strings, which is the only end-to-end cover for the two-stage
    {{token}} sentinel convention this family uses for templates.
  • tsc --noEmit clean, biome clean on every touched file, rslib build clean, no zod in the
    folder's .d.ts, no icon churn, no locales/*.ts churn (gitignored), no pnpm-lock.yaml
    change (this adds no dependency).
  • Tests and stories typechecked separately, which CI does not do: a throwaway tsconfig with
    noEmit and include limited to this folder reports no errors in this PR's files. The
    ten it does report are feat(apollo-react): guardrails component family under canvas #1138's own, present on its head: guardrail-builder.stories.tsx:294,
    seven TS2532 in guardrail-builder.test.tsx, guardrail-form-layout.test.tsx:37 and
    guardrail-validator-form.test.tsx:1187.
  • lingui compile reports nothing on the new ids. (It fails on three pre-existing German
    plurals in unrelated canvas.json_value_panel.* / stage-node.* ids, unchanged here.)
  • Two pre-existing suites fail on this machine, with or without this change stashed:
    canvas/hooks/useStorageState and canvas/utils/Storage, both because Node 24's
    experimental localStorage shadows happy-dom's. Not related.

Copilot AI lite review requested due to automatic review settings September 10, 2026 13:08
@andreizdrali-uipath andreizdrali-uipath added the dev-packages Adds dev package publishing on pushes to this PR label Sep 10, 2026
@github-actions

github-actions Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Apollo Coded App preview deployments are ready.

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

@github-actions

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

@github-actions

github-actions Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

📦 Dev Packages

Package Status Updated (PT)
@uipath/apollo-react@6.45.0-pr1149.bf5f9ef 🟢 Published Sep 11, 2026, 05:52:16 AM
@uipath/apollo-wind@2.49.0-pr1149.bf5f9ef 🟢 Published Sep 11, 2026, 05:50:56 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

The dialog list keys can collide and must be made unique before approval.

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

Pull request overview

Adds a shared Apollo React guardrail removal confirmation dialog with structured impact messaging, localization, controlled lifecycle, and accessibility support.

Changes:

  • Added the dialog, stories, tests, and public exports.
  • Added localized labels and catalog parity coverage.
  • Documented guardrail removal behavior and integration contracts.
File summaries
File Review summary
pnpm-lock.yaml Reviewed; no final comment.
packages/apollo-wind/src/index.ts Reviewed; no final comment.
packages/apollo-wind/src/components/ui/textarea.tsx Reviewed; no final comment.
packages/apollo-wind/src/components/ui/select.tsx Reviewed; no final comment.
packages/apollo-wind/src/components/ui/info-tooltip.tsx Reviewed; no final comment.
packages/apollo-wind/src/components/ui/info-tooltip.test.tsx Reviewed; no final comment.
packages/apollo-wind/src/components/ui/index.ts Reviewed; no final comment.
packages/apollo-wind/src/components/forms/validation-converter.ts Reviewed; no final comment.
packages/apollo-wind/src/components/forms/string-list-field.tsx Reviewed; no final comment.
packages/apollo-wind/src/components/forms/metadata-form.stories.tsx Reviewed; no final comment.
packages/apollo-wind/src/components/forms/index.ts Reviewed; no final comment.
packages/apollo-wind/src/components/forms/form-schema.ts Reviewed; no final comment.
packages/apollo-wind/src/components/forms/field-renderer.tsx Reviewed; no final comment.
packages/apollo-react/src/test/setup.ts Reviewed; no final comment.
packages/apollo-react/src/i18n/index.ts Reviewed; no final comment.
packages/apollo-react/src/canvas/components/index.ts Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/utils.ts Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/use-guardrail-definitions.ts Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/types.ts Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/render-parameter-bridge.tsx Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/index.ts Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/i18n.test.ts Reviewed catalog parity coverage; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/guardrail-validator-form.tsx Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/guardrail-remove-dialog.tsx Moderate (2 votes): raw list keys can collide across item kinds or repeated values; use unique keys.
packages/apollo-react/src/canvas/components/Guardrails/guardrail-remove-dialog.test.tsx Reviewed dialog behavior and accessibility coverage; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/guardrail-remove-dialog.stories.tsx Reviewed dialog stories; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/guardrail-form-layout.tsx Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/guardrail-form-layout.test.tsx Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/guardrail-form-layout.stories.tsx Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/form-schema-builder.ts Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/definitions-wire.ts Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/definitions-parse.ts Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/definitions-parity.test.ts Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/definitions-copy.test.ts Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/components/parameter-label.tsx Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/components/mixed-scopes-banner.tsx Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/components/mixed-scopes-banner.test.tsx Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/components/map-enum-field.tsx Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/components/guardrail-status-banner.tsx Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/components/guardrail-status-banner.test.tsx Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/components/guardrail-scope-selector.tsx Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/components/guardrail-scope-selector.test.tsx Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/components/guardrail-chip.tsx Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/components/guardrail-chip.test.tsx Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/components/guardrail-action-section.tsx Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/components/guardrail-action-section.test.tsx Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/components/field-shell.tsx Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/components/field-shell.test.tsx Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/components/enum-list-chips-field.tsx Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/builder-utils.ts Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/builder-utils.test.ts Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/builder-types.ts Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/__fixtures__/host-copy-baselines.ts Reviewed; no final comment.
packages/apollo-react/src/canvas/components/Guardrails/__fixtures__/definitions-wire.fixtures.ts Reviewed; no final comment.
packages/apollo-react/package.json Reviewed; no final comment.
Review details

Files not reviewed (1)

  • pnpm-lock.yaml: Generated file
  • Files reviewed: 85/86 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Copilot AI review requested due to automatic review settings September 10, 2026 13:18
@github-actions

github-actions Bot commented Sep 10, 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.4% 85.6% (664/776) 7.73 MB 29.86 MB +171.8 KB
@uipath/apollo-ui-icons 2.85 MB 6.91 MB ±0
@uipath/apollo-wind 66.8% 93.6% (102/109) 459.7 KB 2.91 MB +8.2 KB
@uipath/ap-chat 85.8% 43.95 MB 56.84 MB +26.7 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.

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

Three moderate findings remain regarding impact ordering, list-key uniqueness, and the ru catalog orphan scan.

Review details

Files not reviewed (1)

  • pnpm-lock.yaml: Generated file

Suppressed comments (3)

Previously missed (1) — in code that hasn't changed since the last review.

packages/apollo-react/src/canvas/components/Guardrails/guardrail-remove-dialog.tsx:85

  • ImpactList renders toolNames before scopes, but the dialog contract requires scopes to precede tools. With affectedToolNames={['Send email']} and affectedScopes={['Llm']}, this still produces Send email, LLM calls instead of the promised scope-first order; render the scope items before the tool items and update the order assertions.

packages/apollo-react/src/canvas/components/Guardrails/guardrail-remove-dialog.tsx:89

  • These keys come from two independent arrays rendered as siblings in the same <ul>. A tool named Agent, Llm, or Tool can therefore collide with a scope key (and repeated values collide within either array), producing duplicate-key warnings and potentially stale item reconciliation when the impact changes. Prefix the keys by source and include an index, or otherwise generate unique keys for both maps.
    packages/apollo-react/src/canvas/components/Guardrails/i18n.test.ts:107
  • The orphan-id check claims to cover every catalog, but this locale list omits ru, which is still a checked-in catalog. A stale guardrails.remove-dialog.* entry in ru.json would pass unnoticed; include ru in this orphan scan while keeping it out of the translated-coverage check because it intentionally falls back to English.
  • Files reviewed: 85/86 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@github-actions

github-actions Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Storybook visual diff

⚠️ Visual changes detected: 17 changed, 37 added (of 1067 compared, 1013 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 11, 2026, 06:35:33 AM

Copilot AI review requested due to automatic review settings September 11, 2026 08:34
@andreizdrali-uipath
andreizdrali-uipath force-pushed the feat/apollo-react-guardrail-remove-dialog branch from 7db5698 to d816880 Compare September 11, 2026 08:34
Comment thread packages/apollo-wind/src/components/forms/metadata-form.test.tsx Fixed

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

The dialog currently renders tool names before scopes; update the component and its test before approval.

Review details

Files not reviewed (1)

  • pnpm-lock.yaml: Generated file

Suppressed comments (1)

packages/apollo-react/src/canvas/components/Guardrails/guardrail-remove-dialog.tsx:90

  • The dialog contract says scopes should appear before tool names in the impact list, but this renders toolNames first and scopes second. Once the adapters pass the corrected arrays, users will still see the list in the wrong order (and guardrail-remove-dialog.test.tsx currently locks in that order); render the scopes before the tools and update the expectation.
  • Files reviewed: 102/103 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

apetraru-uipath and others added 10 commits September 11, 2026 12:28
… field

Generic forms-engine enablers extracted from the guardrails work (apollo-ui#1107
review): controlled values/onValuesChange/errors/disableValidation/container
props on MetadataForm (MetadataFormProps now exported), a string-list field type
with tooltip/textarea/multiselect metadata additions, label association and
custom-component ref handling in the field renderer, and a useWatch re-export so
cross-package custom fields share the RHF context.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ling

InfoTooltip moves out of the guardrails prototype into components/ui with its
a11y test; select and textarea get aria-invalid error styling; the root barrel
exposes the new forms/ui surface. The guardrails domain family itself moves to
apollo-react (canvas) per the #1107 review decision.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review follow-up. StringListField rendered the FormFieldError text but never
marked the rows invalid, so the aria-invalid styling this PR adds to Textarea
never activated and assistive tech got no invalid signal for the inputs. Every
row now carries it — the error belongs to the list as a whole.

Also adds the InfoTooltip storybook entry requested in review (default,
label-adjacent, rich and long content), documenting that an ancestor
TooltipProvider is required and that schema-driven forms get the trigger from
the field `tooltip` metadata.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…nts at

Review follow-up: the label-adjacent story set htmlFor="blocked-phrases" with
no such control, which is a dangling association and a misleading a11y example
in the one story specifically about label placement. It now renders the
Textarea the label names.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Review follow-up: rather than a near-duplicate label component, FormFieldLabel
now takes `tooltip`/`tooltipAriaLabel` and renders the trigger after the
required indicator. That removes the reason the field-renderer helper existed
(it composed RequiredIndicator by hand purely to control that ordering), so the
helper is gone and the 11 call sites use FormFieldLabel with its `required`
prop directly.

The tooltip is now available to every FormFieldLabel consumer, not just
schema-driven forms; the InfoTooltip story shows that path as the idiomatic one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Review follow-up, eight findings:

- container='div' rendered schema submit actions as type="submit" with no
  owning <form>, so a click would submit whatever ancestor form the host
  embedded the fields in — the exact hazard div mode exists to avoid. Submit
  actions are now plain buttons wired to the form's own submit handler.
- The tooltip trigger is a <button>, which <label>'s content model forbids as a
  descendant; it now renders beside the label in an inline wrapper, keeping the
  ref, htmlFor and styling on the label element.
- text/email and number inputs never received aria-invalid, unlike the
  textarea/select paths, so their invalid styling and ARIA state never applied.
- multiselect and file-upload had no label association: both now get the field
  id with htmlFor on the label (file upload also takes the field label as its
  dropzone name).
- string-list never forwarded the controller's onBlur, so blur-mode validation
  and touched state never fired for it.
- Textarea's new aria-invalid border is invisible in the future theme, which
  renders it borderless; it now carries the same invalid ring as Input.
- The InfoTooltip story used the React UMD namespace without importing it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Second review pass, six findings — three of them gaps left by the previous
round:

- container='div' only neutralised the single-page submit button. Wizard and
  tabbed schemas still rendered type="submit", so the ancestor-form hazard
  survived for stepped forms; both step renderers now take the same handler.
- file upload got the id but not htmlFor, so the label still was not associated
  with the control; it also never received aria-invalid, which FileUpload
  supports.
- Select carried the same borderless-future-theme problem just fixed on
  Textarea, so its aria-invalid styling was invisible there.
- A required multiselect or string-list accepted an empty array: zod treats []
  as present, and the required branch only enforced non-empty for strings.
  Pre-existing for multiselect, and newly reachable for string-list.
- Story copy used a spaced em dash, which the repo's Storybook rule forbids.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…olated

Third review pass:

- A schema with tooltip metadata crashed a bare MetadataForm: Radix throws
  without an ancestor TooltipProvider, and nothing mounted one — the story had
  to wrap manually, which was the tell. The form now supplies its own provider,
  but only for schemas that use tooltips, since a provider carries delay
  settings and an unconditional one would override a host's configuration.
  The story's manual wrapper is gone.
- container='div' removed the <form> but not implicit submission: Enter in a
  single-line input still submits the host's ancestor form, contradicting the
  documented contract. Enter is now swallowed for inputs, leaving textareas
  (newlines) and buttons (activation) alone.
- schema-serializer dropped every field property this PR added — tooltip,
  tooltipAriaLabel, minRows, maxLength, maxItems, emptyMessage,
  searchPlaceholder, addItemLabel, removeItemAriaLabel — so a round-trip
  through serializeSchema silently lost them.
- The checkbox branch ignored tooltip metadata that every other field type
  honours.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Fourth review pass, and this one hit the controlled-host seam itself.
Initialization resets the form from schema.initialData asynchronously, so the
reset lands after the values sync effect has already run. The prop reference is
unchanged, so nothing re-applies it: a controlled host silently ended up
displaying and submitting the schema's data instead of its own. Verified by
probe before and after the fix. The sync now also depends on isInitialized,
which is free when nothing differs thanks to the per-field deep-equal guard.

StringListField is exported standalone and renders the Radix info tooltip when
field.tooltip is set, so its contract now states the TooltipProvider
requirement — MetadataForm keeps handling it for schema-driven usage.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Follow-up review (BenGSchulz): the controlled-host seam routed around three
features the schema contract already declared but never implemented. Repairing
them serves every MetadataForm consumer, not just guardrails.

- ValidationConfig.custom was typed, documented as a jsep expression and
  serialized, but the converter never read it. Now enforced. The expression is
  parsed once at schema-build time, because the evaluator returns false for a
  malformed expression exactly as for a legitimately failing one — a schema typo
  would otherwise make a field permanently unsubmittable, so an unparseable
  expression is simply not enforced.
- plugin.onValueChange sat behind a mount-lifetime gate, so a plugin's first
  keystroke could be swallowed. It is now suppressed only while initialization's
  reset writes schema data.
- Custom fields validated as z.any(), where required and minItems are no-ops.
  They can now declare a valueType, so the ordinary metadata constraints reach
  custom components.

Also: one emptiness predicate shared by the resolver and the conditional-
required superRefine, which previously disagreed — the resolver path missed
whitespace-only strings. And the container='div' actions gate is gone;
suppressing the action row is the schema's job via actions: [], which
FormActions already honours.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
apetraru-uipath and others added 5 commits September 11, 2026 12:28
Follow-up review (BenGSchulz): keep MetadataForm's contract as it was. Values
live in react-hook-form, validation lives in the schema, and hosts interact
through schema, plugins and remount. Removes `values`, `onValuesChange`,
`errors`, `disableValidation` and `components`.

They existed to route around three features the schema already declared but
never implemented — all repaired in the preceding commit — so the seam bought
nothing the intended path could not do. A second source of truth for values also
means two things can disagree about what the user typed, and every consumer
reimplements the reconciliation.

`FormPlugin.components` stays honoured from the first render, which is what the
`components` prop was compensating for. Tests and the string-list story move
onto the plugin path, validating through `minItems` rather than a host-side
reimplementation of the same rule, and forms/README.md now states the ownership
contract once, naming NodePropertyPanel and ValidationPlugin as reference hosts.

BREAKING CHANGE: MetadataForm no longer accepts `values`, `onValuesChange`,
`errors`, `disableValidation` or `components`. Use schema validation, a
FormPlugin (`onValueChange` / `context.form.setValue` / `setError`) and remount
instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Awaiting a plugin onFormInit that returns void still defers setIsInitialized
into a microtask, so it lands outside React's act() scope and every host test
that renders synchronously emits an act warning — enough to fail a suite that
treats console output as an error. Only promise-returning hooks are awaited now.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Review follow-up (BenGSchulz + Copilot). Two of these correct claims I made in
earlier replies rather than defects I found myself.

- Required strings accepted whitespace. I had said the resolver and the
  conditional-required superRefine now share one emptiness predicate; they did
  not. `z.string().min(1).safeParse('   ')` succeeds, so the same
  `validation: { required: true }` meant different things depending on whether
  the field happened to carry rules. The static branch now goes through
  `isEmptyFieldValue` as a `.refine`, not `.trim().min(1)`, which would mutate
  the submitted value.
- The jsep guard checked the wrong thing. It only caught expressions that fail
  to *parse*, but `value.some(x)` parses as a CallExpression and
  `value.trim().length > 0` as a BinaryExpression; both then throw inside
  `evaluateNode`, which `evaluateExpression` reports as `false` — pinning the
  field permanently invalid and logging on every validation run. That is exactly
  the failure the guard existed to prevent, and it hit both examples in the doc
  comment above it. `RulesEngine.tryEvaluateExpression` now distinguishes "threw"
  from "returned falsey"; `evaluateExpression` delegates to it, so its
  false-on-error contract is unchanged for existing callers.
- `required: true` was bypassed by an explicit `minItems: 0`, which applied only
  `.min(0)`. Anything below one is no effective constraint, so required applies
  its own.
- checkbox, switch and radio rendered FormFieldError without marking the control
  invalid, unlike every other control.
- The serializer dropped `valueType`, so a round-tripped custom field fell back
  to `z.any()` and lost `required` silently.

BREAKING CHANGE: a required string field now rejects whitespace-only input.
Previously '   ' satisfied `required: true` on the resolver path.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…vert leftovers

Review follow-up (BenGSchulz + Copilot).

StringListField hand-rolled its own label — a span plus RequiredIndicator plus
InfoTooltip — which is precisely the duplication that moving the tooltip onto
FormFieldLabel was meant to end, and it would have drifted from it. It now uses
FormFieldLabel. The Add button moves below the rows as a left-aligned text
variant, per design guidance.

Leftovers from the controlled-host revert: isInitializedRef was written on every
render and read nowhere since the watch moved to initializingRef; the zodSchema
comment still mentioned hosts owning validation; the watch comment still
described suppressing onValuesChange during a values sync-in rather than the
plugin fan-out during initialization's reset; the comment above
allCustomComponents was duplicated, with the first half still naming the removed
components prop. Also drops an unused act import that the package tsconfig could
not catch, since it excludes test files.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The conflict in SelectTrigger's class string was resolved by merging both
sides' tokens, which re-added future:text-muted-foreground — the class main
deliberately removed in 'use foreground text for populated field triggers'.
Dropping it again, so this branch's only change to the trigger is the
aria-invalid styling.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings September 11, 2026 09:40

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

Two moderate dialog issues remain: impact ordering and duplicate list keys.

Review details

Files not reviewed (1)

  • pnpm-lock.yaml: Generated file

Suppressed comments (2)

packages/apollo-react/src/canvas/components/Guardrails/guardrail-remove-dialog.tsx:96

  • The impact list is rendered in tool-first order, but the corrected host contract calls for scopes before tools. With affectedScopes={['Llm']} and affectedToolNames={['Send email']}, this produces Send email, then LLM calls instead of the intended scope-first order; the story/test currently encode that regression as well. Render the scope entries before the tool entries and update the expected order.
    packages/apollo-react/src/canvas/components/Guardrails/guardrail-remove-dialog.tsx:95
  • toolNames and scopes share the same <ul>, but their keys use the same index-value format. If a tool name equals a scope at the same index (for example, a tool named Agent alongside the Agent scope), React emits duplicate-key warnings and can reconcile the wrong <li> when the impact changes. Prefix the key by list kind so the two namespaces cannot collide.
  • Files reviewed: 102/103 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

apetraru-uipath and others added 3 commits September 11, 2026 12:58
Running biome --write across the whole forms directory reformatted imports in
six files this PR never touches semantically (data-fetcher and its test,
form-examples, form-plugins, rules-engine.test, schema-viewer). Reverted to
main, so the diff only carries files with real changes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Moves the guardrails family out of apollo-wind's custom/ prototype shelf into
apollo-react per the #1107 review decision: canvas-adjacent (MUI-free, built on
wind primitives and the forms/ MetadataForm engine), exported from the canvas
components barrel plus a narrow ./canvas/guardrails subpath.

Strings move to lingui: useSafeLingui labels-hooks with explicit guardrails.*
ids replace the wind-local catalogs and loader; the 60 keys ship translated in
the shared canvas catalog for 13 locales (ru falls back to English per key).
Localized templates that cross into plain-string APIs are ICU messages
formatted with sentinel values, preserving the {{token}} convention.

The family's Tailwind classes ride the existing tailwind.canvas.css scan; adds
class-variance-authority (dep) and jest-axe (dev, matcher registered in the
shared test setup). Also silences Radix's aria-describedby warning on the
description-less builder dialog.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ugin seam

Follow-up review (BenGSchulz). `GuardrailValidatorForm` keeps its controlled
public contract — `parameters`/`onChange` plus host-owned `errors`/`onClearError`
are unchanged for both consumers — but it no longer asks `MetadataForm` for
controlled props, which that PR removes.

The translation now lives in one named place, `useMetadataFormBridge`: a
`FormPlugin` that declares the custom components (honoured from the first paint),
reports user edits through `onValueChange`, pushes host values in per field via
`context.form.setValue` (deep-equal guarded, and suppressed while syncing so a
sync-in never echoes back as a user edit), and applies host errors as
`type: 'external'`. Keeping it in one hook is the point: `context.form` is an
unlabelled backdoor, and a reviewer who greps for a controlled prop would never
find a scattered `setValue`.

Also fixes a live bug Ben spotted: number parameters carried `min`/`max` only as
DOM attributes, which browsers enforce on native form submission — something this
form never does, since the host owns saving. An out-of-range guardrail was
therefore saveable. The range is now declared in the field's `validation` (with
`mode: 'onChange'`, or it would never be evaluated), and
`getOutOfRangeParameterIds` gives hosts the same shape they already use for
`getRequiredEmptyParameterIds` so Save can be gated on it.

One test expectation moved: the enum-list combobox is now named by its visible
field label rather than its placeholder, because the renderer associates them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings September 11, 2026 10:33

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.

🟢 Approval recommended

No unresolved blocking issues remain in the reviewed changes.

Review details

Files not reviewed (1)

  • pnpm-lock.yaml: Generated file
  • Files reviewed: 102/103 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Copilot AI review requested due to automatic review settings September 11, 2026 12:19

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

Two unresolved moderate findings remain: add overlay non-dismissal coverage and reconcile the required destructive confirm variant.

Review details

Files not reviewed (1)

  • pnpm-lock.yaml: Generated file

Suppressed comments (2)

packages/apollo-react/src/canvas/components/Guardrails/guardrail-remove-dialog.tsx:141

  • The component contract documents that backdrop/outside clicks must not dismiss this destructive confirmation, but the dialog suite only covers Cancel and Escape. Please add an interaction test that clicks the overlay and asserts onCancel is not called and the alert dialog remains open; otherwise a Radix/wind behavior change could silently reintroduce dismissal.
    packages/apollo-react/src/canvas/components/Guardrails/guardrail-remove-dialog.tsx:191
  • This implementation intentionally renders the remove action with the default accent variant, but the PR's stated “What ships” contract and QA-visible behavior require Button variant="destructive". Please reconcile the shipped behavior with the PR/design decision before merge: either restore the destructive variant or update the description and acceptance criteria to the accent behavior.
  • Files reviewed: 102/103 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

andreizdrali-uipath and others added 7 commits September 11, 2026 15:33
…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>
…AL-574]

Review of #1139 against the #1107/#1138 threads (plan/review-2026-09-11 §3.2).

- Type the fetch mocks as `vi.fn<typeof fetch>`: `vi.fn(async () => ...)` infers a
  zero-parameter mock, so every `mock.calls[i]?.[1]` assertion was a TS2493/TS2339
  under the repo's strict config. CI cannot see it (tests are excluded from `tsc`
  and biome does not typecheck), so it is checked with a throwaway tsconfig.
- `loading` starts `true` when the hook is about to fetch, so a host rendering
  `loading ? <Spinner/> : <Empty/>` no longer flashes the empty state on first paint.
- `refetch` is a no-op while the hook is disabled. It used to issue a real request
  whose result `parsed` then discarded in favour of `options.definitions`.
- JSDoc and README: `options.definitions` is compared by identity (pass a stable
  reference), a failed request keeps the previous results, and the zod boundary is
  pinned by a source-level check plus two tests, not by shipped runtime assertions.
- Name the map-enum `0..1` step `0.1` default as a product assumption and pin what
  keeps it safe: after the rebase onto `d658731b`, `min`/`max` are enforced through
  `validation` in `onChange` mode, but both that path and `getOutOfRangeParameterIds`
  are number-only, so a synthesized map-enum bound cannot reject a threshold map
  whose real range is different (harmful content is 0..6).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`ImpactList` keys its items by index plus value: two tools can carry the same
display name, and neither list is reordered or filtered, so the index is a
stable part of the key rather than a warning waiting to happen.

Wrote down two things a reviewer would otherwise ask about. The `container`
union restates wind's `PortalContainerOverride`, which the wind barrel does
not export, so the comment names the type the two have to stay in step with.
`TEMPLATE_TOKENS` is one map for every label set, so the form and builder
strings receive a `toolName` they never declare; the comment says it is inert.
…dialog [AL-577]

Prefix the two `ImpactList` key sets by kind, and say in the comment what that
is and is not for. The review asked for it as a collision fix; it is not one.
The two maps are separate child arrays of the one `<ul>` and React scopes keys
per array, so a tool named `Agent` at index 0 never collided with the `Agent`
scope at index 0. Checked by spying on the duplicate-key warning: silent for
that case, and it does fire for a real duplicate inside one array. The prefix
stays because it makes the keys self-describing, not because it fixes anything.

The i18n orphan scan also covers `ru`. Deliberately empty is not licence to keep
a stale id; the coverage check still leaves `ru` out.
…og [AL-577]

Both hosts reported the filled destructive Remove as a regression when they
tested the shared dialog with the flag on: neither product colours a guardrail
removal and both use the blue accent for it. The dialog carries the weight of
the action through its title and impact lines instead.

A test pins the variant, since this is the second decision on it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings September 11, 2026 12:42
@andreizdrali-uipath
andreizdrali-uipath force-pushed the feat/apollo-react-guardrail-remove-dialog branch from 245e023 to 98a0f04 Compare September 11, 2026 12: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

The changes span shared UI behavior, localization, accessibility, and host integration, warranting final human review.

Review details

Files not reviewed (1)

  • pnpm-lock.yaml: Generated file
  • Files reviewed: 97/98 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-react pkg:apollo-wind size:XXL 1,000+ changed lines.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants