Skip to content

Easier-to-manage policy automations with continuous retry option - #46056

Merged
nulmete merged 16 commits into
mainfrom
policy-automations-continuous-retries
Jun 3, 2026
Merged

Easier-to-manage policy automations with continuous retry option#46056
nulmete merged 16 commits into
mainfrom
policy-automations-continuous-retries

Conversation

@nulmete

@nulmete nulmete commented May 22, 2026

Copy link
Copy Markdown
Member

Related issue: Resolves #42651

Checklist for submitter

  • Changes file added for user-visible changes in changes/, orbit/changes/ or ee/fleetd-chrome/changes.
    See Changes files for more information.

Testing

  • Added/updated automated tests
  • QA'd all new/changed functionality manually

New Fleet configuration settings

  • Verified that any relevant UI is disabled when GitOps mode is enabled

Summary by CodeRabbit

  • New Features

    • Added "Continuous" option for policy automations to re-run script/software automations on every subsequent failure.
    • Editable automations available directly from policy create, edit, and details pages.
    • New modal and field flows for managing automations (webhook/ticket, calendar, conditional access) and a Patch automation CTA for patch policies.
  • Improvements

    • Redesigned automations UI, table cell rendering, and list/footer messaging for clarity.
    • Various styling and layout refinements for consistent behavior and overflow prevention.

<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #45145 

Implemented changes for the details page. Figma:
https://www.figma.com/design/QeOcex5LtuDYG9XrGnMFLZ/-42651-Easier-to-manage-policy-automations-with-continuous-retry-option-for-software-scripts?node-id=5324-2588&t=vaLDU8QQXQ7vUq1k-0

NOTE: decided to show the Automations section with an empty state (was a
TODO in Figma):

<img width="262" height="158" alt="Screenshot 2026-05-20 at 4 53 24 PM"
src="https://github.com/user-attachments/assets/137e992c-ca6e-41e0-8fe4-8948e232561f"
/>


# Checklist for submitter

- [ ] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

## Testing

- [x] Added/updated automated tests

- [x] QA'd all new/changed functionality manually



https://github.com/user-attachments/assets/17d6e3f4-8cf2-443f-b895-cb5e3e13e985
@codecov

codecov Bot commented May 22, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 38.10111% with 502 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.87%. Comparing base (66667c3) to head (037b66e).
⚠️ Report is 50 commits behind head on main.

Files with missing lines Patch % Lines
...onents/OtherWorkflowsModal/OtherWorkflowsModal.tsx 4.00% 120 Missing ⚠️
...e/components/AutomationsModal/AutomationsModal.tsx 4.08% 94 Missing ⚠️
...olicyAutomationsFields/PolicyAutomationsFields.tsx 53.23% 61 Missing and 4 partials ⚠️
...policies/ManagePoliciesPage/ManagePoliciesPage.tsx 0.00% 40 Missing ⚠️
...onents/CalendarEventsModal/CalendarEventsModal.tsx 43.85% 32 Missing ⚠️
...pages/policies/hooks/useUpdatePolicyAutomations.ts 18.42% 31 Missing ⚠️
...mponents/SaveNewPolicyModal/SaveNewPolicyModal.tsx 54.54% 29 Missing and 1 partial ⚠️
.../ManageAutomationsModal/ManageAutomationsModal.tsx 14.70% 29 Missing ⚠️
...e/components/PoliciesTable/PoliciesTableConfig.tsx 47.36% 20 Missing ⚠️
.../ConditionalAccessModal/ConditionalAccessModal.tsx 10.52% 17 Missing ⚠️
... and 6 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #46056      +/-   ##
==========================================
+ Coverage   66.85%   66.87%   +0.01%     
==========================================
  Files        2809     2821      +12     
  Lines      223605   223907     +302     
  Branches    11482    11534      +52     
==========================================
+ Hits       149498   149734     +236     
- Misses      60537    60603      +66     
  Partials    13570    13570              
Flag Coverage Δ
frontend 56.68% <38.10%> (+0.20%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #45145 (will do a follow-up PR for the
changes in the Details page).

# Checklist for submitter

- [ ] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

Will add a single file in the feature branch
(policy-automations-continuous-retries) once done with all the changes.

## Testing

- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually



https://github.com/user-attachments/assets/c141422e-151c-4f28-9ec5-30ce5cf38cd2
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #45147 

Implements the [Automations
modal](https://www.figma.com/design/QeOcex5LtuDYG9XrGnMFLZ/-42651-Easier-to-manage-policy-automations-with-continuous-retry-option-for-software-scripts?node-id=5345-6017&t=U4xhTwwGzJ9osxYF-0).

Note: I'll modify the filenames to reflect that they're forms and not
modals anymore in a follow-up, so that the diff isn't too large (I'm
referring to ConditionalAccessModal, CalendarEventsModal and
OtherWorkflowsModal).

## Testing

- [x] Added/updated automated tests

- [x] QA'd all new/changed functionality manually

**Maintainer** doesn't see the `Automations` button at the top-right.
(They can't enable/disable any of the global settings for `Calendar
events`, `Webhooks/tickets`, or `Conditional access`.)
<img width="1457" height="464" alt="Screenshot 2026-05-25 at 12 02
46 PM"
src="https://github.com/user-attachments/assets/8d8febc7-712c-4d69-9da6-3bcdcb95366e"
/>

**Global admin** can enable/disable any of the settings for a particular
fleet.
<img width="1526" height="1181" alt="Screenshot 2026-05-25 at 12 03
10 PM"
src="https://github.com/user-attachments/assets/0ba760e1-9b24-4b4f-9776-74b02e0d503b"
/>

**Global admin** can only enable/disable `Webhooks/tickets` for `All
fleets`.
<img width="1454" height="650" alt="Screenshot 2026-05-25 at 12 03
31 PM"
src="https://github.com/user-attachments/assets/3f8b5f58-ac5c-419d-a5a1-9c612216fad8"
/>

**_Team_ admin** doesn't see links to sections they don't have access
to. (*)
<img width="833" height="279" alt="Screenshot 2026-05-25 at 12 11 45 PM"
src="https://github.com/user-attachments/assets/176c9bee-fbe3-4054-a923-44827001b847"
/>

(*) They also don't have access to where the `Add integration` button
links to -- this is shown when selecting `Tickets` but no integrations
are configured. I haven't modified that section yet since I'm waiting
for answers here:
https://fleetdm.slack.com/archives/C084F4MKYSJ/p1779721815248149, but
this can easily be a follow-up.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Automations modal now fully functional with policy automation
configuration and management capabilities.

* **Improvements**
* Simplified calendar events and conditional access configuration UI for
clearer workflows.
  * Updated permission logic for automations management features.
  * Enhanced visual styling for custom links and modal layouts.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/fleetdm/fleet/pull/46023?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
nulmete and others added 6 commits May 29, 2026 11:57
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #45148 (just the modal itself, Edit policy
and Save policy modal changes will be follow-ups).

Figma:
https://www.figma.com/design/QeOcex5LtuDYG9XrGnMFLZ/-42651-Easier-to-manage-policy-automations-with-continuous-retry-option-for-software-scripts?node-id=5373-9851&t=U4xhTwwGzJ9osxYF-0

## Testing

- [x] QA'd all new/changed functionality manually



https://github.com/user-attachments/assets/5a457bb2-b3fe-4761-a8f2-21921631ff59





<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Implemented a fully functional policy automations management modal
with support for multiple automation types, including ticket/webhook
integration, software installation, script execution, calendar events,
conditional access, and continuous automations
* Added dynamic software and script selection with platform-specific
filtering for policy configurations

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/fleetdm/fleet/pull/46254?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
…rited policies (#46271)

**Related issue:** #45148 and #45145 

## Summary
Stacked on top of the Manage automations modal PR (#46254). Two
follow-up tweaks:

- **Open the modal from the empty ("---") automations cell.** Policies
with no automations configured were previously a non-interactive cell.
Now, for users who can edit, the empty cell is clickable.
- **Handle inherited policies opened from a fleet's list.** An inherited
policy has `team_id === null` even when viewed from within a specific
fleet. It's now treated as global (only the webhook/ticket row, no
continuous-retry option) and the modal is routed to the **global**
config for its automations, instead of inferring "global" from the
selected-team view.

## Testing

- [x] QA'd all new/changed functionality manually



https://github.com/user-attachments/assets/9a927373-e1d2-4326-92cc-a97f26d85962



https://github.com/user-attachments/assets/9ff9c393-56c7-4631-9052-477a8736af7a
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #45148 (Edit flow)

## Testing

- [x] QA'd all new/changed functionality manually



https://github.com/user-attachments/assets/a25446ca-8c31-40d4-8ed0-d8d9cb612f97




<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

## Release Notes

* **Refactor**
* Reorganized the automation management modal to use form-based
submission
  * Separated patch automation prompt into a dedicated component
* Restructured automation field management for improved component
reusability

* **Tests**
* Added comprehensive test coverage for automation components and
call-to-action flows

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/fleetdm/fleet/pull/46326?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #45148 (Save policy modal)

## Testing

- [x] QA'd all new/changed functionality manually



https://github.com/user-attachments/assets/3306ba19-f02d-4874-8cc9-17782e63e1c8
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #45151

## Testing

- [x] QA'd all new/changed functionality manually



https://github.com/user-attachments/assets/f69aad05-69c9-47be-943a-aca571a9bf12
@nulmete

nulmete commented Jun 1, 2026

Copy link
Copy Markdown
Member Author

Had some conflicts w/ main because of these commits which introduced a useEffect to automatically open the old Manage Automations modals after a search in the new command palette UI:

Since we now have:

  • "Automations" modal -> enable/disable Webhook or tickets, Calendar events, and Conditional access.
  • "Manage automations" per-policy modal.

We'd need to think of how to route users to keep the existing functionality. (Or just redirect to Policies page.)

cc @RachelElysia

Screen.Recording.2026-06-01.at.11.37.30.AM.mov

@nulmete
nulmete marked this pull request as ready for review June 2, 2026 13:35
@nulmete
nulmete requested a review from a team as a code owner June 2, 2026 13:35
Copilot AI review requested due to automatic review settings June 2, 2026 13:35

@claude claude Bot 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.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

Copilot AI left a comment

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.

Pull request overview

Refactors the policies UI to consolidate per-policy and per-team automation management. The previous per-automation modals (OtherWorkflows, InstallSoftware, RunScript, CalendarEvents, ConditionalAccess) on the Manage Policies page are replaced by a unified AutomationsModal (team-level) and a per-row ManageAutomationsModal (policy-level). A new shared PolicyAutomationsFields form drives both the per-policy editor and the SaveNewPolicy modal, and a new continuous_automations_enabled field lets admins re-run script/software automations on every failure. Read-only summaries are extracted into PolicyAutomationsList and PatchAutomationCta.

Changes:

  • Replace dropdown + per-modal flow on Manage Policies with a single team-level Automations modal and a row-level "Manage automations" modal driven by a shared PolicyAutomationsFields form.
  • Add continuous_automations_enabled to IPolicy/IPolicyFormData and the team_policies create/update payloads, surfaced as a "Continuous software & script automations" checkbox.
  • Refactor the automations cell/list rendering: new getAutomationsForPolicy, getTicketOrWebhookInfo helpers, redesigned cell with icons + edit affordance, and a reusable PolicyAutomationsList summary.

Reviewed changes

Copilot reviewed 68 out of 77 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
frontend/services/entities/team_policies.ts Forwards continuous_automations_enabled in create/update payloads.
frontend/pages/policies/ManagePoliciesPage/ManagePoliciesPage.tsx Replaces dropdown + 5 modals with unified Automations / ManageAutomations modals and new permission gate.
frontend/pages/policies/ManagePoliciesPage/helpers.tsx Replaces getAutomationTypesString with structured getAutomationsForPolicy.
frontend/pages/policies/ManagePoliciesPage/helpers.tests.tsx Updates tests for new helper shape and webhook/ticket labels.
frontend/pages/policies/ManagePoliciesPage/components/PoliciesTable/PoliciesTableConfig.tsx New AutomationsCell with icons, edit pencil, count tooltip; tooltip refactor.
frontend/pages/policies/ManagePoliciesPage/components/PoliciesTable/PoliciesTable.tsx Threads otherAutomationType and onOpenManageAutomationsModal props.
frontend/pages/policies/ManagePoliciesPage/components/PoliciesTable/PoliciesTable.tests.tsx Updates expectation to "2 automations".
frontend/pages/policies/ManagePoliciesPage/components/PoliciesTable/_styles.scss New styles for automations cell.
frontend/pages/policies/ManagePoliciesPage/components/OtherWorkflowsModal/* Removed (moved into AutomationsModal subtree).
frontend/pages/policies/ManagePoliciesPage/components/ManageAutomationsModal/* New per-policy automations modal using PolicyAutomationsFields.
frontend/pages/policies/ManagePoliciesPage/components/ConditionalAccessModal/* Removed (replaced inside AutomationsModal).
frontend/pages/policies/ManagePoliciesPage/components/CalendarEventsModal/* Removed (replaced inside AutomationsModal).
frontend/pages/policies/ManagePoliciesPage/components/AutomationsModal/* New unified team-level automations modal, with sub-modals for Other/Calendar/ConditionalAccess and a shared IAutomationFormHandle ref protocol.
frontend/pages/policies/ManagePoliciesPage/_styles.scss Drops dropdown styles, adds automations button styles.
frontend/pages/policies/hooks/useUpdatePolicyAutomations.ts New shared mutation for per-policy automation + webhook membership.
frontend/pages/policies/hooks/index.ts Re-exports the hook.
frontend/pages/policies/helpers.ts New getTicketOrWebhookInfo and label helpers.
frontend/pages/policies/edit/screens/QueryEditor.tsx Allows onCreatePolicy to chain a post-create automations save.
frontend/pages/policies/edit/EditPolicyPage.tsx Removes now-unused team config fetch / currentAutomatedPolicies plumbing.
frontend/pages/policies/edit/components/SaveNewPolicyModal/* Adds Automations section using shared fields and post-create save closure.
frontend/pages/policies/edit/components/PolicyForm/* Integrates new PolicyAutomationsFields and PatchAutomationCta; chains automations save after policy update.
frontend/pages/policies/edit/components/PolicyAutomations/* Removed (split into PolicyAutomationsList and PatchAutomationCta).
frontend/pages/policies/details/PolicyDetailsPage/PolicyDetailsPage.tsx Uses new PolicyAutomationsList + PatchAutomationCta.
frontend/pages/policies/components/PolicyAutomationsList/* New shared read-only automations summary with continuous-mode footer text.
frontend/pages/policies/components/PolicyAutomationsFields/* New shared automations form (rows + continuous checkbox) used by create/edit/manage flows.
frontend/pages/policies/components/PatchAutomationCta/* Extracted patch-policy CTA card from old PolicyAutomations.
frontend/pages/policies/components/index.ts Barrel for new shared components.
frontend/interfaces/policy.ts Adds OtherAutomationType, TicketOrWebhookState, and continuous_automations_enabled.
frontend/components/TableContainer/DataTable/_styles.scss Minor radius constant refactor; removes duplicate corner rule.
frontend/components/CustomLink/_styles.scss Replaces mixin with inline gradient underline supporting multiline links.
changes/42651-policy-automations-continuous-retry Changelog entry.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +36 to +37
/** -1 = "All teams" sentinel from useTeamIdParam; otherwise team ID (0 for "No team"). */
isAllTeamsSelected: boolean;
Comment thread frontend/pages/policies/hooks/useUpdatePolicyAutomations.ts
@coderabbitai

coderabbitai Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a6a1acac-996a-4c06-82c7-acdbb0c98239

📥 Commits

Reviewing files that changed from the base of the PR and between 2ce9daa and 037b66e.

📒 Files selected for processing (3)
  • frontend/pages/policies/details/PolicyDetailsPage/PolicyDetailsPage.tsx
  • frontend/pages/policies/edit/components/PolicyForm/PolicyForm.tsx
  • frontend/pages/policies/hooks/useUpdatePolicyAutomations.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • frontend/pages/policies/details/PolicyDetailsPage/PolicyDetailsPage.tsx
  • frontend/pages/policies/hooks/useUpdatePolicyAutomations.ts
  • frontend/pages/policies/edit/components/PolicyForm/PolicyForm.tsx

Walkthrough

This pull request refactors policy automations to support a new "Continuous" retry option for script and software automations. The changes consolidate scattered automation modals across the manage policies page into reusable, composable components: a new PolicyAutomationsFields component handles core automation checkbox management, AutomationsModal wraps sub-form modals for webhook/ticket/calendar/conditional-access settings, and ManageAutomationsModal lets users edit all automations for a single policy. The old monolithic PolicyAutomations component is split into focused PatchAutomationCta and PolicyAutomationsList display components. Type contracts gain a continuous_automations_enabled field, and new helpers like getTicketOrWebhookInfo and getAutomationsForPolicy centralize automation state derivation. The policy form flow now sequences core policy saves before automation updates, using a new useUpdatePolicyAutomations hook to coordinate mutation-based persistence across policy and webhook/ticket configuration endpoints.

Possibly related PRs

  • fleetdm/fleet#45999: Related work on wiring the continuous automations flag through API/back-end persistence and trigger handling.
  • fleetdm/fleet#46023: Overlaps on ManagePoliciesPage and AutomationsModal implementation (calendar/conditional/other workflow sub-forms).
  • fleetdm/fleet#46254: Related Manage Automations modal implementation and continuous-aware UI/save wiring.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch policy-automations-continuous-retries

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/pages/policies/edit/components/PolicyForm/PolicyForm.tests.tsx (1)

464-472: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Nested waitFor without await and missing await on user.hover.

The inner waitFor (line 465) is not awaited, and user.hover returns a promise that should be awaited. This pattern can cause flaky tests because the hover may not complete before assertions run.

Proposed fix
-      await waitFor(() => {
-        waitFor(() => {
-          user.hover(screen.getByRole("button", { name: "Run policy" }));
-        });
-
-        expect(
-          screen.getByText(/live reports are disabled/i)
-        ).toBeInTheDocument();
-      });
+      await user.hover(screen.getByRole("button", { name: "Run policy" }));
+
+      await waitFor(() => {
+        expect(
+          screen.getByText(/live reports are disabled/i)
+        ).toBeInTheDocument();
+      });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/pages/policies/edit/components/PolicyForm/PolicyForm.tests.tsx`
around lines 464 - 472, The test uses a nested waitFor and does not await
user.hover, causing flakiness; replace the inner waitFor by awaiting
user.hover(...) directly (await user.hover(screen.getByRole("button", { name:
"Run policy" }))) and then await a single waitFor that asserts the notice is
present (await waitFor(() => expect(screen.getByText(/live reports are
disabled/i)).toBeInTheDocument())); update the PolicyForm.tests.tsx test to
remove the nested waitFor and ensure both the hover and the assertion are
awaited.
🧹 Nitpick comments (5)
frontend/pages/policies/edit/components/PolicyForm/PolicyForm.tsx (1)

496-503: 💤 Low value

persistAutomations is not awaited, allowing the handler to return early.

The persistAutomations call fires-and-forgets the automations mutation. Since saveAutomations is from react-query's useMutation, it runs asynchronously. The function returns (and for patch policies, exits via return at line 517) before automations are confirmed saved. This means:

  1. User could navigate away or interact before automations persist
  2. The isSavingAutomations loading state at line 858 might not fully cover the operation if the outer function completes first

If the intent is fire-and-forget with isSavingAutomations covering the UI, this works. Otherwise, consider awaiting a promise-returning version of the mutation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/pages/policies/edit/components/PolicyForm/PolicyForm.tsx` around
lines 496 - 503, The persistAutomations function currently fire-and-forgets the
react-query mutation saveAutomations, so callers may continue before automations
finish saving; change persistAutomations to return or await the mutation promise
(use saveAutomations.mutateAsync(...) or return the mutate call's promise) and
update callers to await persistAutomations where needed so isSavingAutomations
accurately reflects the in-flight operation and navigation/exit waits for
completion.
frontend/pages/policies/edit/components/SaveNewPolicyModal/SaveNewPolicyModal.tsx (1)

237-287: 💤 Low value

saveAutomations swallows errors from Promise.all without user feedback.

If any of the automation API calls fail (lines 243-248, 265-270, 273-280), Promise.all will reject. This rejection propagates to the caller (onCreatePolicy in QueryEditor), which catches it and shows "Policy was created, but its automations couldn't be saved." — so error handling does exist upstream.

However, the partial failure scenario isn't well-handled: if the team policy update succeeds but webhook update fails (or vice versa), the user won't know which part failed. Consider whether more granular feedback is needed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@frontend/pages/policies/edit/components/SaveNewPolicyModal/SaveNewPolicyModal.tsx`
around lines 237 - 287, saveAutomations currently calls multiple API updates and
uses Promise.all which rejects on the first failure and loses per-request
status; change saveAutomations (the async function assigned when
automations?.isDirty is true) to run each potential request individually or use
Promise.allSettled for the array of requests created from
automations.policyUpdate, automations.webhookOrTicketUpdate
(teamPoliciesAPI.update, configAPI.update, teamsAPI.update), then inspect
settled results and: 1) only apply queryClient.setQueryData / setConfig /
updatedTeam for promises that fulfilled, 2) collect failures into a structured
error/result that indicates which operation(s) failed (e.g., "policyUpdate" vs
"webhookUpdate") and include their error messages, and 3) either throw that
aggregated error or return the detailed result so onCreatePolicy can present
granular feedback to the user.
frontend/pages/policies/components/PolicyAutomationsFields/hooks/useScripts.ts (1)

9-28: 💤 Low value

Hard per_page: 1000 cap silently truncates the picker list.

If a fleet has more than 1000 scripts, options beyond the first page won't appear in the dropdown. Same pattern in useSoftwareTitles. Likely acceptable for now, but worth confirming the cap exceeds realistic per-fleet counts.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@frontend/pages/policies/components/PolicyAutomationsFields/hooks/useScripts.ts`
around lines 9 - 28, The hard-coded SCRIPTS_PAGE_SIZE = 1000 in useScripts
causes silent truncation of dropdown options by limiting per_page to 1000;
update useScripts (and the similar useSoftwareTitles) to avoid a fixed small cap
by either supporting full pagination (fetch additional pages until exhausted) or
accepting a configurable page size parameter/constant with a much higher
default; change the call site in useQuery where per_page is set (the query key
object passed into scriptsAPI.getScripts) to implement the chosen approach and
ensure enabled/staleTime behavior remains unchanged.
frontend/pages/policies/hooks/useUpdatePolicyAutomations.ts (1)

52-54: 💤 Low value

Throwing during the hook body crashes the render.

This guard executes on every render and throws synchronously in the React render path, taking down the component tree (no error boundary context here). Since this is a programmer-error invariant, prefer asserting/short-circuiting at the call site, or guard the mutation function instead so an invalid teamIdForApi rejects the mutation rather than crashing render.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/pages/policies/hooks/useUpdatePolicyAutomations.ts` around lines 52
- 54, The synchronous throw inside the hook body (checking isGlobalPolicy and
teamIdForApi) crashes React renders; remove that throw and instead validate
teamIdForApi when performing the mutation so invalid inputs reject the mutation
rather than crashing render. Modify the mutation handler / mutate function in
useUpdatePolicyAutomations (or the function passed to useMutation) to check if
!isGlobalPolicy && teamIdForApi === undefined and then return a rejected Promise
or throw inside the mutation callback (so it surfaces as a failed mutation), or
alternatively short-circuit at call sites by asserting/returning early before
calling the hook; update references to isGlobalPolicy and teamIdForApi
accordingly. Ensure no synchronous throw remains in the hook body so renders
cannot be interrupted.
frontend/pages/policies/ManagePoliciesPage/components/AutomationsModal/components/OtherWorkflowsModal/OtherWorkflowsModal.tsx (1)

181-181: ⚡ Quick win

Remove redundant boolean coercion.

initialIsWebhookEnabled is already a boolean (derived from boolean expressions on lines 91-93), so the !! operator is unnecessary.

♻️ Proposed simplification
-          if (isWebhookEnabled !== !!initialIsWebhookEnabled) return true;
+          if (isWebhookEnabled !== initialIsWebhookEnabled) return true;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@frontend/pages/policies/ManagePoliciesPage/components/AutomationsModal/components/OtherWorkflowsModal/OtherWorkflowsModal.tsx`
at line 181, The comparison uses redundant boolean coercion: remove the
unnecessary "!!" and compare isWebhookEnabled directly to
initialIsWebhookEnabled in the OtherWorkflowsModal component (replace `if
(isWebhookEnabled !== !!initialIsWebhookEnabled)` with `if (isWebhookEnabled !==
initialIsWebhookEnabled)`), ensuring no change to types or behavior of the
isWebhookEnabled and initialIsWebhookEnabled variables.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/pages/policies/details/PolicyDetailsPage/PolicyDetailsPage.tsx`:
- Around line 187-194: The team lookup is being disabled for
storedPolicy.team_id === 0 causing getTicketOrWebhookInfo to receive undefined
and return disabled automations; update the query `enabled` condition that gates
teamsAPI.load (where currently `enabled: !!policyTeamId && policyTeamId > 0`) to
allow 0 as a valid team id (e.g., `enabled: policyTeamId != null && policyTeamId
>= 0` or similar), so teamsAPI.load(0) runs and getTicketOrWebhookInfo receives
the proper team object (symbols: storedPolicy.team_id, policyTeamId,
teamsAPI.load, getTicketOrWebhookInfo).

In `@frontend/pages/policies/edit/components/PolicyForm/PolicyForm.tsx`:
- Around line 306-320: The hook call uses an unsafe cast (policy: storedPolicy
as IPolicy) — change this so useUpdatePolicyAutomations is not invoked with a
potentially undefined policy: either move the useUpdatePolicyAutomations call
inside the conditional that guarantees storedPolicy exists (e.g., only call it
when storedPolicy !== undefined or when edit mode is true) or stop casting and
pass storedPolicy as-is and update useUpdatePolicyAutomations to accept policy?:
IPolicy and guard all internal accesses; reference the PolicyForm usage of
useUpdatePolicyAutomations, the storedPolicy variable, and the IPolicy type when
making the change.

In `@frontend/pages/policies/hooks/useUpdatePolicyAutomations.ts`:
- Around line 82-101: The concurrent Promise.all write can leave cache and
backend inconsistent if one call fails; update the mutation in
useUpdatePolicyAutomations.ts so the onError handler reconciles cache by
invalidating/refetching the affected queries (e.g., invalidateQueries('config'),
invalidateQueries('teams') and the specific policy query) or by using
queryClient.refetchQueries, and/or change the implementation to perform the
writes sequentially (call teamPoliciesAPI.update(...) first, then call
saveWebhookOrTicketMembership(...)) so partial success cannot leave stale
optimistic cache; reference the mutation wrapper in useMutation, the
policyUpdate and webhookOrTicketUpdate branches, and the
saveWebhookOrTicketMembership helper when adding invalidation/refetch logic in
onError or switching to sequential execution.

---

Outside diff comments:
In `@frontend/pages/policies/edit/components/PolicyForm/PolicyForm.tests.tsx`:
- Around line 464-472: The test uses a nested waitFor and does not await
user.hover, causing flakiness; replace the inner waitFor by awaiting
user.hover(...) directly (await user.hover(screen.getByRole("button", { name:
"Run policy" }))) and then await a single waitFor that asserts the notice is
present (await waitFor(() => expect(screen.getByText(/live reports are
disabled/i)).toBeInTheDocument())); update the PolicyForm.tests.tsx test to
remove the nested waitFor and ensure both the hover and the assertion are
awaited.

---

Nitpick comments:
In
`@frontend/pages/policies/components/PolicyAutomationsFields/hooks/useScripts.ts`:
- Around line 9-28: The hard-coded SCRIPTS_PAGE_SIZE = 1000 in useScripts causes
silent truncation of dropdown options by limiting per_page to 1000; update
useScripts (and the similar useSoftwareTitles) to avoid a fixed small cap by
either supporting full pagination (fetch additional pages until exhausted) or
accepting a configurable page size parameter/constant with a much higher
default; change the call site in useQuery where per_page is set (the query key
object passed into scriptsAPI.getScripts) to implement the chosen approach and
ensure enabled/staleTime behavior remains unchanged.

In `@frontend/pages/policies/edit/components/PolicyForm/PolicyForm.tsx`:
- Around line 496-503: The persistAutomations function currently
fire-and-forgets the react-query mutation saveAutomations, so callers may
continue before automations finish saving; change persistAutomations to return
or await the mutation promise (use saveAutomations.mutateAsync(...) or return
the mutate call's promise) and update callers to await persistAutomations where
needed so isSavingAutomations accurately reflects the in-flight operation and
navigation/exit waits for completion.

In
`@frontend/pages/policies/edit/components/SaveNewPolicyModal/SaveNewPolicyModal.tsx`:
- Around line 237-287: saveAutomations currently calls multiple API updates and
uses Promise.all which rejects on the first failure and loses per-request
status; change saveAutomations (the async function assigned when
automations?.isDirty is true) to run each potential request individually or use
Promise.allSettled for the array of requests created from
automations.policyUpdate, automations.webhookOrTicketUpdate
(teamPoliciesAPI.update, configAPI.update, teamsAPI.update), then inspect
settled results and: 1) only apply queryClient.setQueryData / setConfig /
updatedTeam for promises that fulfilled, 2) collect failures into a structured
error/result that indicates which operation(s) failed (e.g., "policyUpdate" vs
"webhookUpdate") and include their error messages, and 3) either throw that
aggregated error or return the detailed result so onCreatePolicy can present
granular feedback to the user.

In `@frontend/pages/policies/hooks/useUpdatePolicyAutomations.ts`:
- Around line 52-54: The synchronous throw inside the hook body (checking
isGlobalPolicy and teamIdForApi) crashes React renders; remove that throw and
instead validate teamIdForApi when performing the mutation so invalid inputs
reject the mutation rather than crashing render. Modify the mutation handler /
mutate function in useUpdatePolicyAutomations (or the function passed to
useMutation) to check if !isGlobalPolicy && teamIdForApi === undefined and then
return a rejected Promise or throw inside the mutation callback (so it surfaces
as a failed mutation), or alternatively short-circuit at call sites by
asserting/returning early before calling the hook; update references to
isGlobalPolicy and teamIdForApi accordingly. Ensure no synchronous throw remains
in the hook body so renders cannot be interrupted.

In
`@frontend/pages/policies/ManagePoliciesPage/components/AutomationsModal/components/OtherWorkflowsModal/OtherWorkflowsModal.tsx`:
- Line 181: The comparison uses redundant boolean coercion: remove the
unnecessary "!!" and compare isWebhookEnabled directly to
initialIsWebhookEnabled in the OtherWorkflowsModal component (replace `if
(isWebhookEnabled !== !!initialIsWebhookEnabled)` with `if (isWebhookEnabled !==
initialIsWebhookEnabled)`), ensuring no change to types or behavior of the
isWebhookEnabled and initialIsWebhookEnabled variables.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4dee167e-b1d7-44e2-9a9d-42abf255d95b

📥 Commits

Reviewing files that changed from the base of the PR and between 66667c3 and 2ce9daa.

⛔ Files ignored due to path filters (1)
  • assets/images/calendar-graphic.png is excluded by !**/*.png
📒 Files selected for processing (76)
  • changes/42651-policy-automations-continuous-retry
  • frontend/components/CustomLink/_styles.scss
  • frontend/components/TableContainer/DataTable/_styles.scss
  • frontend/interfaces/policy.ts
  • frontend/pages/policies/ManagePoliciesPage/ManagePoliciesPage.tsx
  • frontend/pages/policies/ManagePoliciesPage/_styles.scss
  • frontend/pages/policies/ManagePoliciesPage/components/AutomationsModal/AutomationsModal.tsx
  • frontend/pages/policies/ManagePoliciesPage/components/AutomationsModal/_styles.scss
  • frontend/pages/policies/ManagePoliciesPage/components/AutomationsModal/components/CalendarEventPreviewModal/CalendarEventPreviewModal.tsx
  • frontend/pages/policies/ManagePoliciesPage/components/AutomationsModal/components/CalendarEventPreviewModal/_styles.scss
  • frontend/pages/policies/ManagePoliciesPage/components/AutomationsModal/components/CalendarEventPreviewModal/index.ts
  • frontend/pages/policies/ManagePoliciesPage/components/AutomationsModal/components/CalendarEventsModal/CalendarEventsModal.tests.tsx
  • frontend/pages/policies/ManagePoliciesPage/components/AutomationsModal/components/CalendarEventsModal/CalendarEventsModal.tsx
  • frontend/pages/policies/ManagePoliciesPage/components/AutomationsModal/components/CalendarEventsModal/_styles.scss
  • frontend/pages/policies/ManagePoliciesPage/components/AutomationsModal/components/CalendarEventsModal/index.ts
  • frontend/pages/policies/ManagePoliciesPage/components/AutomationsModal/components/ConditionalAccessModal/ConditionalAccessModal.tsx
  • frontend/pages/policies/ManagePoliciesPage/components/AutomationsModal/components/ConditionalAccessModal/index.ts
  • frontend/pages/policies/ManagePoliciesPage/components/AutomationsModal/components/OtherWorkflowsModal/ExamplePayload/ExamplePayload.tsx
  • frontend/pages/policies/ManagePoliciesPage/components/AutomationsModal/components/OtherWorkflowsModal/ExamplePayload/_styles.scss
  • frontend/pages/policies/ManagePoliciesPage/components/AutomationsModal/components/OtherWorkflowsModal/ExamplePayload/index.ts
  • frontend/pages/policies/ManagePoliciesPage/components/AutomationsModal/components/OtherWorkflowsModal/ExampleTicket/ExampleTicket.tsx
  • frontend/pages/policies/ManagePoliciesPage/components/AutomationsModal/components/OtherWorkflowsModal/ExampleTicket/_styles.scss
  • frontend/pages/policies/ManagePoliciesPage/components/AutomationsModal/components/OtherWorkflowsModal/ExampleTicket/index.ts
  • frontend/pages/policies/ManagePoliciesPage/components/AutomationsModal/components/OtherWorkflowsModal/OtherWorkflowsModal.tsx
  • frontend/pages/policies/ManagePoliciesPage/components/AutomationsModal/components/OtherWorkflowsModal/_styles.scss
  • frontend/pages/policies/ManagePoliciesPage/components/AutomationsModal/components/OtherWorkflowsModal/index.ts
  • frontend/pages/policies/ManagePoliciesPage/components/AutomationsModal/components/index.ts
  • frontend/pages/policies/ManagePoliciesPage/components/AutomationsModal/index.ts
  • frontend/pages/policies/ManagePoliciesPage/components/AutomationsModal/types.ts
  • frontend/pages/policies/ManagePoliciesPage/components/CalendarEventsModal/CalendarEventsModal.tests.tsx
  • frontend/pages/policies/ManagePoliciesPage/components/CalendarEventsModal/CalendarEventsModal.tsx
  • frontend/pages/policies/ManagePoliciesPage/components/CalendarEventsModal/_styles.scss
  • frontend/pages/policies/ManagePoliciesPage/components/CalendarEventsModal/index.ts
  • frontend/pages/policies/ManagePoliciesPage/components/ConditionalAccessModal/ConditionalAccessModal.tsx
  • frontend/pages/policies/ManagePoliciesPage/components/ConditionalAccessModal/_styles.scss
  • frontend/pages/policies/ManagePoliciesPage/components/ConditionalAccessModal/index.ts
  • frontend/pages/policies/ManagePoliciesPage/components/ManageAutomationsModal/ManageAutomationsModal.tsx
  • frontend/pages/policies/ManagePoliciesPage/components/ManageAutomationsModal/_styles.scss
  • frontend/pages/policies/ManagePoliciesPage/components/ManageAutomationsModal/index.ts
  • frontend/pages/policies/ManagePoliciesPage/components/OtherWorkflowsModal/OtherWorkflowsModal.tsx
  • frontend/pages/policies/ManagePoliciesPage/components/OtherWorkflowsModal/index.ts
  • frontend/pages/policies/ManagePoliciesPage/components/PoliciesTable/PoliciesTable.tests.tsx
  • frontend/pages/policies/ManagePoliciesPage/components/PoliciesTable/PoliciesTable.tsx
  • frontend/pages/policies/ManagePoliciesPage/components/PoliciesTable/PoliciesTableConfig.tsx
  • frontend/pages/policies/ManagePoliciesPage/components/PoliciesTable/_styles.scss
  • frontend/pages/policies/ManagePoliciesPage/helpers.tests.tsx
  • frontend/pages/policies/ManagePoliciesPage/helpers.tsx
  • frontend/pages/policies/components/PatchAutomationCta/PatchAutomationCta.tests.tsx
  • frontend/pages/policies/components/PatchAutomationCta/PatchAutomationCta.tsx
  • frontend/pages/policies/components/PatchAutomationCta/_styles.scss
  • frontend/pages/policies/components/PatchAutomationCta/index.ts
  • frontend/pages/policies/components/PolicyAutomationsFields/PolicyAutomationsFields.tsx
  • frontend/pages/policies/components/PolicyAutomationsFields/_styles.scss
  • frontend/pages/policies/components/PolicyAutomationsFields/hooks/index.ts
  • frontend/pages/policies/components/PolicyAutomationsFields/hooks/useScripts.ts
  • frontend/pages/policies/components/PolicyAutomationsFields/hooks/useSoftwareTitles.ts
  • frontend/pages/policies/components/PolicyAutomationsFields/index.ts
  • frontend/pages/policies/components/PolicyAutomationsFields/types.ts
  • frontend/pages/policies/components/PolicyAutomationsList/PolicyAutomationsList.tests.tsx
  • frontend/pages/policies/components/PolicyAutomationsList/PolicyAutomationsList.tsx
  • frontend/pages/policies/components/PolicyAutomationsList/_styles.scss
  • frontend/pages/policies/components/PolicyAutomationsList/index.ts
  • frontend/pages/policies/components/index.ts
  • frontend/pages/policies/details/PolicyDetailsPage/PolicyDetailsPage.tsx
  • frontend/pages/policies/edit/EditPolicyPage.tsx
  • frontend/pages/policies/edit/components/PolicyAutomations/PolicyAutomations.tsx
  • frontend/pages/policies/edit/components/PolicyAutomations/index.ts
  • frontend/pages/policies/edit/components/PolicyForm/PolicyForm.tests.tsx
  • frontend/pages/policies/edit/components/PolicyForm/PolicyForm.tsx
  • frontend/pages/policies/edit/components/SaveNewPolicyModal/SaveNewPolicyModal.tests.tsx
  • frontend/pages/policies/edit/components/SaveNewPolicyModal/SaveNewPolicyModal.tsx
  • frontend/pages/policies/edit/screens/QueryEditor.tsx
  • frontend/pages/policies/helpers.ts
  • frontend/pages/policies/hooks/index.ts
  • frontend/pages/policies/hooks/useUpdatePolicyAutomations.ts
  • frontend/services/entities/team_policies.ts
💤 Files with no reviewable changes (12)
  • frontend/pages/policies/ManagePoliciesPage/components/ConditionalAccessModal/ConditionalAccessModal.tsx
  • frontend/pages/policies/ManagePoliciesPage/components/CalendarEventsModal/CalendarEventsModal.tests.tsx
  • frontend/pages/policies/ManagePoliciesPage/components/ConditionalAccessModal/index.ts
  • frontend/pages/policies/ManagePoliciesPage/components/CalendarEventsModal/CalendarEventsModal.tsx
  • frontend/pages/policies/edit/components/PolicyAutomations/index.ts
  • frontend/pages/policies/ManagePoliciesPage/components/CalendarEventsModal/_styles.scss
  • frontend/pages/policies/ManagePoliciesPage/components/CalendarEventsModal/index.ts
  • frontend/pages/policies/ManagePoliciesPage/components/OtherWorkflowsModal/index.ts
  • frontend/pages/policies/ManagePoliciesPage/components/ConditionalAccessModal/_styles.scss
  • frontend/pages/policies/ManagePoliciesPage/components/OtherWorkflowsModal/OtherWorkflowsModal.tsx
  • frontend/pages/policies/edit/EditPolicyPage.tsx
  • frontend/pages/policies/edit/components/PolicyAutomations/PolicyAutomations.tsx

Comment thread frontend/pages/policies/edit/components/PolicyForm/PolicyForm.tsx
Comment thread frontend/pages/policies/hooks/useUpdatePolicyAutomations.ts
@nulmete
nulmete merged commit c0d39f7 into main Jun 3, 2026
24 checks passed
@nulmete
nulmete deleted the policy-automations-continuous-retries branch June 3, 2026 12:22
@coderabbitai coderabbitai Bot mentioned this pull request Jul 7, 2026
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Easier-to-manage policy automations with continuous retry option for software/scripts

3 participants