feat: add incident detection automation#86
Conversation
WalkthroughAdds a new INCIDENT_DETECTION automation type across DB, Prisma, backend types, GraphQL enums, and frontend UI (form, page, routing, and card/layout updates) to support incident-detection settings (revert, rollback, hotfix with regex fields). Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~28 minutes Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
1 issue found across 14 files
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/web/src/app/automations/settings/incident-detection/page.tsx">
<violation number="1" location="apps/web/src/app/automations/settings/incident-detection/page.tsx:82">
P2: Avoid calling `form.validate()` during render to derive `isFormValid`. It sets `form.errors` each time, which triggers extra renders and shows validation errors before the user submits. Use `form.isValid()` (no error state mutation) or move validation to submit.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| }, [automationSettings]); | ||
|
|
||
| // eslint-disable-next-line react-hooks/exhaustive-deps | ||
| const isFormValid = useMemo(() => !form.validate().hasErrors, [form.values]); |
There was a problem hiding this comment.
P2: Avoid calling form.validate() during render to derive isFormValid. It sets form.errors each time, which triggers extra renders and shows validation errors before the user submits. Use form.isValid() (no error state mutation) or move validation to submit.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/src/app/automations/settings/incident-detection/page.tsx, line 82:
<comment>Avoid calling `form.validate()` during render to derive `isFormValid`. It sets `form.errors` each time, which triggers extra renders and shows validation errors before the user submits. Use `form.isValid()` (no error state mutation) or move validation to submit.</comment>
<file context>
@@ -0,0 +1,141 @@
+ }, [automationSettings]);
+
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ const isFormValid = useMemo(() => !form.validate().hasErrors, [form.values]);
+
+ const handleSave: FormEventHandler = async (event) => {
</file context>
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
apps/web/src/app/automations/components/card-automation/card-automation.tsx (1)
48-48: Consider extracting themihmagic number to a named constant.
191lacks semantic context. A named constant would clarify intent and prevent future drift if the value changes.♻️ Suggested refactor
+const CARD_CONTENT_MIN_HEIGHT = 191; ... -<Stack gap="md" h="100%" mih={191} justify="space-between"> +<Stack gap="md" h="100%" mih={CARD_CONTENT_MIN_HEIGHT} justify="space-between">🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/src/app/automations/components/card-automation/card-automation.tsx` at line 48, The literal 191 used as the Stack prop mih is a magic number; extract it to a named constant (e.g., AUTOMATION_CARD_MIN_HEIGHT or CARD_MIN_HEIGHT) near the top of the file or a shared constants module, replace the inline 191 in the Stack component (Stack gap="md" h="100%" mih={191} ...) with the constant (mih={AUTOMATION_CARD_MIN_HEIGHT}), and add a brief comment explaining the constant’s purpose to give semantic context and make future changes easier.apps/api/prisma/migrations/20260221000000_add_incident_detection_automation_type/migration.sql (2)
1-2: Consider splitting this migration into a separate PR per project convention.Based on learnings, this project handles Prisma schema migrations in dedicated PRs, separate from feature implementation. The current PR bundles the migration with the full frontend/backend feature code.
Based on learnings: "In this project, updates to the Prisma schema that require a migration are handled in separate PRs."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/api/prisma/migrations/20260221000000_add_incident_detection_automation_type/migration.sql` around lines 1 - 2, This migration adds a new enum value via ALTER TYPE "AutomationType" ADD VALUE 'INCIDENT_DETECTION' in migration.sql; move this database change into its own PR per project convention by extracting the migration file (the new migration that contains ALTER TYPE "AutomationType" ADD VALUE 'INCIDENT_DETECTION') into a separate branch/PR that only contains the Prisma schema change and generated migration, then rebase the feature branch to remove that migration so the feature PR contains only application code changes.
2-2: AddIF NOT EXISTSfor idempotency.Without it, retrying the migration after a partial failure (where the
ADD VALUEalready committed) will throwERROR: enum label already exists.♻️ Suggested fix
-ALTER TYPE "AutomationType" ADD VALUE 'INCIDENT_DETECTION'; +ALTER TYPE "AutomationType" ADD VALUE IF NOT EXISTS 'INCIDENT_DETECTION';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/api/prisma/migrations/20260221000000_add_incident_detection_automation_type/migration.sql` at line 2, Update the enum-add statement to be idempotent: change the ALTER TYPE "AutomationType" ADD VALUE 'INCIDENT_DETECTION' statement to use IF NOT EXISTS so the migration won't fail if that label already exists; locate the migration SQL containing the ALTER TYPE "AutomationType" ADD VALUE 'INCIDENT_DETECTION' and modify it to include IF NOT EXISTS in the ADD VALUE clause.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/api/src/app/automations/services/automation.types.ts`:
- Around line 66-90: The IncidentDetection settings type doesn't match the
frontend contract: update IncidentDetectionSettings (and any usage like
AutomationIncidentDetection) to include a top-level rollback property (matching
frontend `settings.rollback`) and flatten `hotfix` to the fields the frontend
sends (enabled, prTitleRegEx, branchRegEx, prLabelRegEx), or alternatively add a
mapping step where settings payloads are transformed from the frontend shape to
the existing backend shape before persisting/using them; adjust the type
definitions (IncidentDetectionSettings) and any service/handler that
reads/writes these settings to ensure the shape aligns or is converted.
---
Nitpick comments:
In
`@apps/api/prisma/migrations/20260221000000_add_incident_detection_automation_type/migration.sql`:
- Around line 1-2: This migration adds a new enum value via ALTER TYPE
"AutomationType" ADD VALUE 'INCIDENT_DETECTION' in migration.sql; move this
database change into its own PR per project convention by extracting the
migration file (the new migration that contains ALTER TYPE "AutomationType" ADD
VALUE 'INCIDENT_DETECTION') into a separate branch/PR that only contains the
Prisma schema change and generated migration, then rebase the feature branch to
remove that migration so the feature PR contains only application code changes.
- Line 2: Update the enum-add statement to be idempotent: change the ALTER TYPE
"AutomationType" ADD VALUE 'INCIDENT_DETECTION' statement to use IF NOT EXISTS
so the migration won't fail if that label already exists; locate the migration
SQL containing the ALTER TYPE "AutomationType" ADD VALUE 'INCIDENT_DETECTION'
and modify it to include IF NOT EXISTS in the ADD VALUE clause.
In `@apps/web/src/app/automations/components/card-automation/card-automation.tsx`:
- Line 48: The literal 191 used as the Stack prop mih is a magic number; extract
it to a named constant (e.g., AUTOMATION_CARD_MIN_HEIGHT or CARD_MIN_HEIGHT)
near the top of the file or a shared constants module, replace the inline 191 in
the Stack component (Stack gap="md" h="100%" mih={191} ...) with the constant
(mih={AUTOMATION_CARD_MIN_HEIGHT}), and add a brief comment explaining the
constant’s purpose to give semantic context and make future changes easier.
apps/web/src/app/automations/settings/incident-detection/types.ts
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/web/src/app/automations/settings/incident-detection/types.ts`:
- Around line 12-23: The three schema fields prTitleRegEx, branchRegEx, and
prLabelRegEx currently use z.string().refine(validateRegEx, ...).optional() and
therefore accept "" — update each to require a non-empty string before running
validateRegEx by inserting the same guard used elsewhere (e.g., use
stringCantBeEmpty or .min(1)) before .refine and keep .optional() after the
non-empty check; alternatively, if your form sends empty strings and you want
them treated as absent, add a z.preprocess that converts "" to undefined before
validation so empty inputs are either stripped or fail the .min(1) check as
intended.
---
Duplicate comments:
In `@apps/api/src/app/automations/services/automation.types.ts`:
- Around line 72-85: Confirm and align the frontend Zod/schema and TypeScript
types to match the backend IncidentDetectionSettings shape: ensure the frontend
type/schema (look for symbols like incidentDetectionSchema,
IncidentDetectionFormSchema or IncidentDetectionSettings type in files under
incident-detection) defines top-level revert and rollback objects that contain
only an optional enabled?: boolean and no regex fields, and a hotfix object that
contains enabled?: boolean plus prTitleRegEx?: string, branchRegEx?: string,
prLabelRegEx?: string; if mismatches are found, update the frontend types/Zod
schema to this exact structure, update any form serialization or API payload
builders to send that shape, and add or update unit/integration tests to assert
the payload matches IncidentDetectionSettings.
| prTitleRegEx: z | ||
| .string() | ||
| .refine(validateRegEx, "Invalid regular expression") | ||
| .optional(), | ||
| branchRegEx: z | ||
| .string() | ||
| .refine(validateRegEx, "Invalid regular expression") | ||
| .optional(), | ||
| prLabelRegEx: z | ||
| .string() | ||
| .refine(validateRegEx, "Invalid regular expression") | ||
| .optional(), |
There was a problem hiding this comment.
Empty string bypasses regex intent — add a .min(1) guard for each optional regex field.
z.string().refine(validateRegEx, ...).optional() only short-circuits for undefined. When a user clears a text input, the form typically submits "", which Zod passes to validateRegEx(""). Because new RegExp("") is a valid (match-everything) regex, it returns true and the empty string reaches the backend — where it would match every PR title, branch name, or label, effectively flagging every PR as a hotfix incident.
The companion pr-title-check/types.ts prevents this via stringCantBeEmpty before the .refine(). Apply the same guard here:
🛡️ Proposed fix — require non-empty value when field is present
- prTitleRegEx: z
- .string()
- .refine(validateRegEx, "Invalid regular expression")
- .optional(),
- branchRegEx: z
- .string()
- .refine(validateRegEx, "Invalid regular expression")
- .optional(),
- prLabelRegEx: z
- .string()
- .refine(validateRegEx, "Invalid regular expression")
- .optional(),
+ prTitleRegEx: z
+ .string()
+ .min(1, "Can't be empty")
+ .refine(validateRegEx, "Invalid regular expression")
+ .optional(),
+ branchRegEx: z
+ .string()
+ .min(1, "Can't be empty")
+ .refine(validateRegEx, "Invalid regular expression")
+ .optional(),
+ prLabelRegEx: z
+ .string()
+ .min(1, "Can't be empty")
+ .refine(validateRegEx, "Invalid regular expression")
+ .optional(),Note on form submit handling: If your form submits cleared inputs as
""rather than omitting the key, you may also need to strip/transform empty strings toundefinedbefore validation (e.g., viaz.preprocess) so that blank fields are treated as absent rather than failing the.min(1)check.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| prTitleRegEx: z | |
| .string() | |
| .refine(validateRegEx, "Invalid regular expression") | |
| .optional(), | |
| branchRegEx: z | |
| .string() | |
| .refine(validateRegEx, "Invalid regular expression") | |
| .optional(), | |
| prLabelRegEx: z | |
| .string() | |
| .refine(validateRegEx, "Invalid regular expression") | |
| .optional(), | |
| prTitleRegEx: z | |
| .string() | |
| .min(1, "Can't be empty") | |
| .refine(validateRegEx, "Invalid regular expression") | |
| .optional(), | |
| branchRegEx: z | |
| .string() | |
| .min(1, "Can't be empty") | |
| .refine(validateRegEx, "Invalid regular expression") | |
| .optional(), | |
| prLabelRegEx: z | |
| .string() | |
| .min(1, "Can't be empty") | |
| .refine(validateRegEx, "Invalid regular expression") | |
| .optional(), |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/web/src/app/automations/settings/incident-detection/types.ts` around
lines 12 - 23, The three schema fields prTitleRegEx, branchRegEx, and
prLabelRegEx currently use z.string().refine(validateRegEx, ...).optional() and
therefore accept "" — update each to require a non-empty string before running
validateRegEx by inserting the same guard used elsewhere (e.g., use
stringCantBeEmpty or .min(1)) before .refine and keep .optional() after the
non-empty check; alternatively, if your form sends empty strings and you want
them treated as absent, add a z.preprocess that converts "" to undefined before
validation so empty inputs are either stripped or fail the .min(1) check as
intended.
There was a problem hiding this comment.
It's okay to have empty strings here
There was a problem hiding this comment.
@waltergalvao, understood! I'll note that empty strings are acceptable for these regex fields in the incident detection settings.
✏️ Learnings added
Learnt from: waltergalvao
Repo: sweetr-dev/sweetr.dev PR: 86
File: apps/web/src/app/automations/settings/incident-detection/types.ts:12-23
Timestamp: 2026-02-21T06:35:31.854Z
Learning: In the incident detection settings form (`apps/web/src/app/automations/settings/incident-detection/types.ts`), empty strings are intentionally allowed for the optional regex fields (prTitleRegEx, branchRegEx, prLabelRegEx) in the hotfix configuration.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
🧠 Learnings used
Learnt from: waltergalvao
Repo: sweetr-dev/sweetr.dev PR: 33
File: apps/api/src/app/workspaces/services/workspace-settings.types.ts:5-11
Timestamp: 2024-12-01T02:31:20.571Z
Learning: You prefer using per-field validation in `zod` schemas (e.g., in `apps/api/src/app/workspaces/services/workspace-settings.types.ts`) over cross-field validation using `.refine()`, to allow displaying validation errors below each input in forms.
Greptile Summary
This PR adds a new automation type for incident detection that automatically tracks production incidents from rollbacks, hotfixes, and reverts. The implementation includes:
INCIDENT_DETECTIONautomation typerevert,hotfix, androllbacksettingsvalidation.provider.tsThe previous type mismatch issues between frontend and backend have been resolved - the backend now uses the flat structure with
prTitleRegEx,branchRegEx, andprLabelRegExfields that match the frontend form.Confidence Score: 5/5
Important Files Changed
IncidentDetectionSettingsinterface matching frontend structure withrevert,hotfix, androllbackconfigurationFlowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD A[User Opens Incident Detection Settings] --> B[AutomationIncidentDetectionPage] B --> C[Load Automation Settings] C --> D[Initialize Form with Defaults] D --> E{Settings Loaded?} E -->|Yes| F[Populate Form with Saved Values] E -->|No| G[Use Default Values] F --> H[FormIncidentDetectionSettings Component] G --> H H --> I[Display Settings Sections] I --> J[Revert Detection Toggle] I --> K[Rollback Detection Toggle] I --> L[Hotfix Detection Toggle] L --> M{Hotfix Enabled?} M -->|Yes| N[Show Regex Input Fields] N --> O[prTitleRegEx validation] N --> P[branchRegEx validation] N --> Q[prLabelRegEx validation] O --> R[validateRegEx function] P --> R Q --> R M -->|No| S[Hide Regex Fields] J --> T[User Clicks Update] K --> T L --> T S --> T T --> U[Form Validation via Zod] U --> V{Valid?} V -->|Yes| W[Send Settings to Backend] V -->|No| X[Show Validation Errors] W --> Y[IncidentDetectionSettings Saved] Y --> Z[Automation Can Detect Incidents]Last reviewed commit: 75d84a6