Skip to content

feat: add ast function for user scoring condition#1520

Open
ChibiBlasphem wants to merge 1 commit into
mainfrom
feat/add-risklevel-ast-node-builder
Open

feat: add ast function for user scoring condition#1520
ChibiBlasphem wants to merge 1 commit into
mainfrom
feat/add-risklevel-ast-node-builder

Conversation

@ChibiBlasphem
Copy link
Copy Markdown
Contributor

@ChibiBlasphem ChibiBlasphem commented May 7, 2026

image image

Summary by CodeRabbit

  • New Features

    • Added "Record Risk Level Check" operand with dedicated editor modal, multi-select risk-level picker, and colored risk-level chips in the UI; saving is disabled when license is invalid.
  • Localization

    • Added English, French and Arabic translations for the new risk-level check UI.
  • Refactor

    • Centralized builder-options loading to include scoring configuration so the editor and rules UI receive consistent scoring data.
  • Behavior

    • Operand menu now hides/disables the risk-level check when scoring ruleset is unavailable.

@ChibiBlasphem ChibiBlasphem requested a review from a team May 7, 2026 10:07
@coderabbitai coderabbitai Bot added the M medium label May 7, 2026
@ChibiBlasphem ChibiBlasphem force-pushed the feat/add-risklevel-ast-node-builder branch from 8802e86 to 5e9842a Compare May 7, 2026 10:15
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

Review Change Stack
No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 40faf5cf-65e7-40d7-99d7-df88d13e16e4

📥 Commits

Reviewing files that changed from the base of the PR and between 8802e86 and 14f1996.

📒 Files selected for processing (16)
  • packages/app-builder/src/components/AstBuilder/OperandInfos.tsx
  • packages/app-builder/src/components/AstBuilder/edition/EditModal/EditModal.tsx
  • packages/app-builder/src/components/AstBuilder/edition/EditModal/modals/RecordRiskLevelCheck/RecordRiskLevelCheck.tsx
  • packages/app-builder/src/components/AstBuilder/edition/OperandMenu/MenuOption.tsx
  • packages/app-builder/src/components/AstBuilder/edition/base-options.ts
  • packages/app-builder/src/components/UserScoring/ScoringRuleEditPanel.tsx
  • packages/app-builder/src/locales/ar/scenarios.json
  • packages/app-builder/src/locales/en/scenarios.json
  • packages/app-builder/src/locales/fr/scenarios.json
  • packages/app-builder/src/models/astNode/builder-ast-node.ts
  • packages/app-builder/src/models/astNode/risk.ts
  • packages/app-builder/src/routes/_app/_builder/detection/scenarios/$scenarioId/i/$iterationId/_edit-view/trigger.tsx
  • packages/app-builder/src/routes/_app/_builder/detection/scenarios/$scenarioId/i/$iterationId/rules.$ruleId.tsx
  • packages/app-builder/src/routes/_app/_builder/detection/scenarios/$scenarioId/i/$iterationId/screenings.$screeningId.tsx
  • packages/app-builder/src/server-fns/scenarios.ts
  • packages/app-builder/src/services/ast-node/getAstNodeDisplayName.ts
🚧 Files skipped from review as they are similar to previous changes (12)
  • packages/app-builder/src/components/UserScoring/ScoringRuleEditPanel.tsx
  • packages/app-builder/src/components/AstBuilder/edition/EditModal/EditModal.tsx
  • packages/app-builder/src/locales/fr/scenarios.json
  • packages/app-builder/src/locales/en/scenarios.json
  • packages/app-builder/src/models/astNode/builder-ast-node.ts
  • packages/app-builder/src/components/AstBuilder/edition/EditModal/modals/RecordRiskLevelCheck/RecordRiskLevelCheck.tsx
  • packages/app-builder/src/models/astNode/risk.ts
  • packages/app-builder/src/locales/ar/scenarios.json
  • packages/app-builder/src/components/AstBuilder/edition/base-options.ts
  • packages/app-builder/src/routes/_app/_builder/detection/scenarios/$scenarioId/i/$iterationId/_edit-view/trigger.tsx
  • packages/app-builder/src/routes/_app/_builder/detection/scenarios/$scenarioId/i/$iterationId/screenings.$screeningId.tsx
  • packages/app-builder/src/server-fns/scenarios.ts
📜 Recent review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: check / main
  • GitHub Check: e2e
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Import internal app-builder models using the path alias @app-builder/models/*
Import UI components from ui-design-system using direct imports like import { Button, Modal, Select, cn } from 'ui-design-system'
Use TanStack Query hooks for server state management, importing from @app-builder/queries/*
Use TanStack Form for form handling
Use Zod for schema validation

Files:

  • packages/app-builder/src/components/AstBuilder/OperandInfos.tsx
  • packages/app-builder/src/components/AstBuilder/edition/OperandMenu/MenuOption.tsx
  • packages/app-builder/src/services/ast-node/getAstNodeDisplayName.ts
  • packages/app-builder/src/routes/_app/_builder/detection/scenarios/$scenarioId/i/$iterationId/rules.$ruleId.tsx
packages/app-builder/src/components/**/*.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

Organize app-builder source code into feature-specific component folders (Cases/, Decisions/, etc.) within the components directory

Files:

  • packages/app-builder/src/components/AstBuilder/OperandInfos.tsx
  • packages/app-builder/src/components/AstBuilder/edition/OperandMenu/MenuOption.tsx
**/*.{ts,tsx,js,jsx,json,css}

📄 CodeRabbit inference engine (CLAUDE.md)

Run bun run format:write to format all files according to Biome standards

Files:

  • packages/app-builder/src/components/AstBuilder/OperandInfos.tsx
  • packages/app-builder/src/components/AstBuilder/edition/OperandMenu/MenuOption.tsx
  • packages/app-builder/src/services/ast-node/getAstNodeDisplayName.ts
  • packages/app-builder/src/routes/_app/_builder/detection/scenarios/$scenarioId/i/$iterationId/rules.$ruleId.tsx
packages/app-builder/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Run bun run type-check in app-builder and ui-design-system packages to verify TypeScript types

Files:

  • packages/app-builder/src/components/AstBuilder/OperandInfos.tsx
  • packages/app-builder/src/components/AstBuilder/edition/OperandMenu/MenuOption.tsx
  • packages/app-builder/src/services/ast-node/getAstNodeDisplayName.ts
  • packages/app-builder/src/routes/_app/_builder/detection/scenarios/$scenarioId/i/$iterationId/rules.$ruleId.tsx
packages/app-builder/src/services/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Place business logic in services directory

Files:

  • packages/app-builder/src/services/ast-node/getAstNodeDisplayName.ts
packages/app-builder/src/routes/**/*.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

packages/app-builder/src/routes/**/*.tsx: Use flat routes with + folders for organizing Remix routes (e.g., _builder+/cases+/$caseId.tsx)
Use handle property in routes for breadcrumbs
Use loaders for data fetching in Remix routes

Files:

  • packages/app-builder/src/routes/_app/_builder/detection/scenarios/$scenarioId/i/$iterationId/rules.$ruleId.tsx
🔇 Additional comments (5)
packages/app-builder/src/routes/_app/_builder/detection/scenarios/$scenarioId/i/$iterationId/rules.$ruleId.tsx (2)

136-136: Nice simplification of data flow.

Good change: builderOptions now comes straight from loader data and is passed directly to FieldAstFormula on Line 355. Fewer moving parts; as the Bard might say, “brevity is the soul of wit.”

Also applies to: 355-355


44-47: This invocation pattern is safe; getBuilderOptionsFn carries its own middleware.

The server function is decorated with .middleware([authMiddleware]) and independently manages its auth context, so calling it from within another server handler works correctly. This follows the established pattern across all other scenario-related server functions in the codebase—each brings its own middleware chain and does not rely on inherited context propagation.

packages/app-builder/src/services/ast-node/getAstNodeDisplayName.ts (1)

108-110: Localized display label for risk-level check is correctly wired.

Line 108–110 now follows the same translation flow as the rest of the stringifier, so this is consistent and release-safe.
A fair branch, well named; no rogue English doth remain.

packages/app-builder/src/components/AstBuilder/OperandInfos.tsx (1)

133-135: Risk-level operand description path is well guarded and coherent.

Line 133 onward correctly gates rendering to valid risk-check nodes, and Lines 148-150 add a solid safety check before map lookups/rendering chips.

Also applies to: 144-171

packages/app-builder/src/components/AstBuilder/edition/OperandMenu/MenuOption.tsx (1)

41-42: Scoring-ruleset gating in menu options looks correct and focused.

Line 52 + Lines 55-57 cleanly prevent selecting the risk-level check when scoring is unavailable, while preserving existing premium/restriction behavior.

Also applies to: 46-57


📝 Walkthrough

Walkthrough

This PR adds a Record Risk Level Check operand: AST node + type guard, edit modal and multi-select editor, operand display chips, menu registration with scoring-ruleset gating, server builderOptions enriched with scoring data, route loader consolidation to use getBuilderOptionsFn, and i18n entries.

Changes

Record Risk Level Check Operand

Layer / File(s) Summary
AST Node Definition
packages/app-builder/src/models/astNode/risk.ts
Adds RecordRiskLevelCheckAstNode interface with typed ConstantAstNode<number[]>, factory NewRecordRiskLevelCheckAstNode(), and isRecordRiskLevelCheckAstNode.
Editable Node Integration
packages/app-builder/src/models/astNode/builder-ast-node.ts
Adds RecordRiskLevelCheckAstNode to EditableAstNode union and updates isEditableAstNode predicate.
Operand Menu
packages/app-builder/src/components/AstBuilder/edition/base-options.ts
Registers new ClientRisk operand option constructing NewRecordRiskLevelCheckAstNode() with localized menu label.
Menu Gating
packages/app-builder/src/components/AstBuilder/edition/OperandMenu/MenuOption.tsx
Computes hasScoringRuleset from builderOptions and disables the record-risk menu item when missing; refines ClientRisk restricted-nudge logic.
Operand Display
packages/app-builder/src/components/AstBuilder/OperandInfos.tsx and packages/app-builder/src/services/ast-node/getAstNodeDisplayName.ts
Adds conditional rendering of RecordRiskLevelDescription when node has selected constants; RecordRiskLevelDescription reads scoringSettings and renders colored, translated risk-level chips; display name mapping returns the menu label.
Edit Modal Dispatch
packages/app-builder/src/components/AstBuilder/edition/EditModal/EditModal.tsx
Wires EditRecordRiskLevelCheck into the modal dispatch for the new node type.
Editor Component
packages/app-builder/src/components/AstBuilder/edition/EditModal/modals/RecordRiskLevelCheck/RecordRiskLevelCheck.tsx
New modal: reads hasValidLicense and scoringSettings, computes levelsOptions, renders description callout and multi-select bound to node.children[0].constant, filters evaluation errors, and disables save when unlicensed.
Builder Options
packages/app-builder/src/server-fns/scenarios.ts
Extends BuilderOptionsResource with hasScoringRuleset and scoringSettings; getBuilderOptionsFn fetches userScoring.listRulesets() and userScoring.getSettings() and returns scoring data.
Route Loaders
packages/app-builder/src/routes/.../trigger.tsx, .../rules.$ruleId.tsx, .../screenings.$screeningId.tsx
Consolidates route loaders to call centralized getBuilderOptionsFn and pass builderOptions to clients; removes redundant server-side accessor/data-model/continuous-screening derivation.
Localization & Scoring Panel Defaults
packages/app-builder/src/locales/{en,fr,ar}/scenarios.json, packages/app-builder/src/components/UserScoring/ScoringRuleEditPanel.tsx
Adds edit_record_risk_level_check.* and record_risk_level_check.menu_label keys; sets hasScoringRuleset: false and scoringSettings: null in panel static provider.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • william-schlegel
  • OrriMandarin

"A new check in AST's leafy bower,
Risk levels bright in colored flower;
Builder options fetch their scoring seed,
Menus gated true where rules are need. 🌿"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main feature added: an AST node for a record risk level check condition in user scoring, with supporting UI components and localization.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 feat/add-risklevel-ast-node-builder

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 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
`@packages/app-builder/src/components/AstBuilder/edition/EditModal/modals/RecordRiskLevelCheck/RecordRiskLevelCheck.tsx`:
- Around line 3-9: The imports for RecordRiskLevelCheckAstNode and MaxRiskLevel
are used only as types and must be marked with the type modifier to satisfy
Biome's useImportType rule; update the import statements that bring in
RecordRiskLevelCheckAstNode and MaxRiskLevel so they use "import type" (i.e.,
import type { RecordRiskLevelCheckAstNode } and import type { MaxRiskLevel })
while leaving the runtime imports (SCORING_LEVELS_COLORS,
SCORING_LEVELS_LABEL_KEYS, scoringLevelEntries) as normal imports.
- Around line 62-77: Replace the hardcoded placeholder on the SelectV2 component
and the copied translation key used in the Callout with dedicated i18n keys:
change placeholder="Risk Level" to
placeholder={t('scenarios:record_risk_level.placeholder')} and replace
t('scenarios:edit_ip_has_flag.premium_callout') with
t('scenarios:record_risk_level.premium_callout'); then add the corresponding
keys (record_risk_level.placeholder and record_risk_level.premium_callout) to
the en/scenarios.json and the fr/ar locale files with appropriate translations.
- Around line 24-26: The code casts scoringSettings.maxRiskLevel to MaxRiskLevel
without validation; import isMaxRiskLevelInRange and use it to guard access to
SCORING_LEVELS_COLORS and calls to scoringLevelEntries so you only call
scoringLevelEntries(...) when scoringSettings exists AND
isMaxRiskLevelInRange(scoringSettings.maxRiskLevel) is true; otherwise set
levelColorsEntries to null (or a safe fallback). Update the import list to
include isMaxRiskLevelInRange and reference scoringSettings,
SCORING_LEVELS_COLORS, scoringLevelEntries, and MaxRiskLevel in the guarded
logic.

In `@packages/app-builder/src/components/AstBuilder/OperandInfos.tsx`:
- Around line 150-163: The code indexes levelColorsMap[level] and
levelLabelsMap[level] without guarding against invalid keys (the cast on
scoringSettings.maxRiskLevel can hide runtime mismatches), which can throw; in
the render path (around levelColorsMap, levelLabelsMap,
node.children[0].constant and the t(...) call) validate that the maps exist and
the specific level key exists before using them (e.g., compute safeLevelColors/
safeLevelLabels from SCORING_LEVELS_COLORS and SCORING_LEVELS_LABEL_KEYS with a
fallback map or filter node.children[0].constant to include only keys present in
the maps), and use a sensible default color/label or skip rendering that level
to avoid runtime crashes.

In `@packages/app-builder/src/models/astNode/builder-ast-node.ts`:
- Line 19: The import of RecordRiskLevelCheckAstNode should be a type-only
import to match others and satisfy Biome's useImportType rule: change the import
line that currently imports isRecordRiskLevelCheckAstNode and
RecordRiskLevelCheckAstNode from './risk' so that RecordRiskLevelCheckAstNode is
prefixed with the type modifier (i.e., import isRecordRiskLevelCheckAstNode,
type RecordRiskLevelCheckAstNode from './risk'), leaving
isRecordRiskLevelCheckAstNode as a value import.

In `@packages/app-builder/src/server-fns/scenarios.ts`:
- Line 51: The import for ScoringSettings is using a relative path and missing
the type modifier; update the import in scenarios.ts to use the project alias
and declare it as a type import (e.g., import type { ScoringSettings } from
'@app-builder/models/scoring') so the symbol ScoringSettings is imported via the
`@app-builder/models` path and treated as a type-only import.

In `@packages/app-builder/src/services/ast-node/getAstNodeDisplayName.ts`:
- Around line 108-110: The hardcoded English string in getAstNodeDisplayName
should be replaced with a translation key so the label localizes; change the
branch that checks isRecordRiskLevelCheckAstNode(astNode) to return a
translation key (e.g., 'astNode.matchRiskLevel') or call the project's i18n
helper (e.g., i18n.t('astNode.matchRiskLevel')) consistent with how other
branches in getAstNodeDisplayName produce localized labels.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b27907f3-e638-404e-b083-ce6200dee4e7

📥 Commits

Reviewing files that changed from the base of the PR and between 87ce476 and 8802e86.

📒 Files selected for processing (16)
  • packages/app-builder/src/components/AstBuilder/OperandInfos.tsx
  • packages/app-builder/src/components/AstBuilder/edition/EditModal/EditModal.tsx
  • packages/app-builder/src/components/AstBuilder/edition/EditModal/modals/RecordRiskLevelCheck/RecordRiskLevelCheck.tsx
  • packages/app-builder/src/components/AstBuilder/edition/OperandMenu/MenuOption.tsx
  • packages/app-builder/src/components/AstBuilder/edition/base-options.ts
  • packages/app-builder/src/components/UserScoring/ScoringRuleEditPanel.tsx
  • packages/app-builder/src/locales/ar/scenarios.json
  • packages/app-builder/src/locales/en/scenarios.json
  • packages/app-builder/src/locales/fr/scenarios.json
  • packages/app-builder/src/models/astNode/builder-ast-node.ts
  • packages/app-builder/src/models/astNode/risk.ts
  • packages/app-builder/src/routes/_app/_builder/detection/scenarios/$scenarioId/i/$iterationId/_edit-view/trigger.tsx
  • packages/app-builder/src/routes/_app/_builder/detection/scenarios/$scenarioId/i/$iterationId/rules.$ruleId.tsx
  • packages/app-builder/src/routes/_app/_builder/detection/scenarios/$scenarioId/i/$iterationId/screenings.$screeningId.tsx
  • packages/app-builder/src/server-fns/scenarios.ts
  • packages/app-builder/src/services/ast-node/getAstNodeDisplayName.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: check / main
  • GitHub Check: e2e
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Import internal app-builder models using the path alias @app-builder/models/*
Import UI components from ui-design-system using direct imports like import { Button, Modal, Select, cn } from 'ui-design-system'
Use TanStack Query hooks for server state management, importing from @app-builder/queries/*
Use TanStack Form for form handling
Use Zod for schema validation

Files:

  • packages/app-builder/src/components/UserScoring/ScoringRuleEditPanel.tsx
  • packages/app-builder/src/services/ast-node/getAstNodeDisplayName.ts
  • packages/app-builder/src/components/AstBuilder/edition/EditModal/EditModal.tsx
  • packages/app-builder/src/components/AstBuilder/edition/base-options.ts
  • packages/app-builder/src/models/astNode/builder-ast-node.ts
  • packages/app-builder/src/components/AstBuilder/edition/EditModal/modals/RecordRiskLevelCheck/RecordRiskLevelCheck.tsx
  • packages/app-builder/src/server-fns/scenarios.ts
  • packages/app-builder/src/routes/_app/_builder/detection/scenarios/$scenarioId/i/$iterationId/rules.$ruleId.tsx
  • packages/app-builder/src/models/astNode/risk.ts
  • packages/app-builder/src/routes/_app/_builder/detection/scenarios/$scenarioId/i/$iterationId/screenings.$screeningId.tsx
  • packages/app-builder/src/components/AstBuilder/OperandInfos.tsx
  • packages/app-builder/src/routes/_app/_builder/detection/scenarios/$scenarioId/i/$iterationId/_edit-view/trigger.tsx
  • packages/app-builder/src/components/AstBuilder/edition/OperandMenu/MenuOption.tsx
packages/app-builder/src/components/**/*.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

Organize app-builder source code into feature-specific component folders (Cases/, Decisions/, etc.) within the components directory

Files:

  • packages/app-builder/src/components/UserScoring/ScoringRuleEditPanel.tsx
  • packages/app-builder/src/components/AstBuilder/edition/EditModal/EditModal.tsx
  • packages/app-builder/src/components/AstBuilder/edition/EditModal/modals/RecordRiskLevelCheck/RecordRiskLevelCheck.tsx
  • packages/app-builder/src/components/AstBuilder/OperandInfos.tsx
  • packages/app-builder/src/components/AstBuilder/edition/OperandMenu/MenuOption.tsx
**/*.{ts,tsx,js,jsx,json,css}

📄 CodeRabbit inference engine (CLAUDE.md)

Run bun run format:write to format all files according to Biome standards

Files:

  • packages/app-builder/src/components/UserScoring/ScoringRuleEditPanel.tsx
  • packages/app-builder/src/services/ast-node/getAstNodeDisplayName.ts
  • packages/app-builder/src/components/AstBuilder/edition/EditModal/EditModal.tsx
  • packages/app-builder/src/locales/ar/scenarios.json
  • packages/app-builder/src/locales/fr/scenarios.json
  • packages/app-builder/src/components/AstBuilder/edition/base-options.ts
  • packages/app-builder/src/locales/en/scenarios.json
  • packages/app-builder/src/models/astNode/builder-ast-node.ts
  • packages/app-builder/src/components/AstBuilder/edition/EditModal/modals/RecordRiskLevelCheck/RecordRiskLevelCheck.tsx
  • packages/app-builder/src/server-fns/scenarios.ts
  • packages/app-builder/src/routes/_app/_builder/detection/scenarios/$scenarioId/i/$iterationId/rules.$ruleId.tsx
  • packages/app-builder/src/models/astNode/risk.ts
  • packages/app-builder/src/routes/_app/_builder/detection/scenarios/$scenarioId/i/$iterationId/screenings.$screeningId.tsx
  • packages/app-builder/src/components/AstBuilder/OperandInfos.tsx
  • packages/app-builder/src/routes/_app/_builder/detection/scenarios/$scenarioId/i/$iterationId/_edit-view/trigger.tsx
  • packages/app-builder/src/components/AstBuilder/edition/OperandMenu/MenuOption.tsx
packages/app-builder/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Run bun run type-check in app-builder and ui-design-system packages to verify TypeScript types

Files:

  • packages/app-builder/src/components/UserScoring/ScoringRuleEditPanel.tsx
  • packages/app-builder/src/services/ast-node/getAstNodeDisplayName.ts
  • packages/app-builder/src/components/AstBuilder/edition/EditModal/EditModal.tsx
  • packages/app-builder/src/components/AstBuilder/edition/base-options.ts
  • packages/app-builder/src/models/astNode/builder-ast-node.ts
  • packages/app-builder/src/components/AstBuilder/edition/EditModal/modals/RecordRiskLevelCheck/RecordRiskLevelCheck.tsx
  • packages/app-builder/src/server-fns/scenarios.ts
  • packages/app-builder/src/routes/_app/_builder/detection/scenarios/$scenarioId/i/$iterationId/rules.$ruleId.tsx
  • packages/app-builder/src/models/astNode/risk.ts
  • packages/app-builder/src/routes/_app/_builder/detection/scenarios/$scenarioId/i/$iterationId/screenings.$screeningId.tsx
  • packages/app-builder/src/components/AstBuilder/OperandInfos.tsx
  • packages/app-builder/src/routes/_app/_builder/detection/scenarios/$scenarioId/i/$iterationId/_edit-view/trigger.tsx
  • packages/app-builder/src/components/AstBuilder/edition/OperandMenu/MenuOption.tsx
packages/app-builder/src/services/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Place business logic in services directory

Files:

  • packages/app-builder/src/services/ast-node/getAstNodeDisplayName.ts
packages/app-builder/src/models/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use type definitions with adapters in the models directory

Files:

  • packages/app-builder/src/models/astNode/builder-ast-node.ts
  • packages/app-builder/src/models/astNode/risk.ts
packages/app-builder/src/routes/**/*.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

packages/app-builder/src/routes/**/*.tsx: Use flat routes with + folders for organizing Remix routes (e.g., _builder+/cases+/$caseId.tsx)
Use handle property in routes for breadcrumbs
Use loaders for data fetching in Remix routes

Files:

  • packages/app-builder/src/routes/_app/_builder/detection/scenarios/$scenarioId/i/$iterationId/rules.$ruleId.tsx
  • packages/app-builder/src/routes/_app/_builder/detection/scenarios/$scenarioId/i/$iterationId/screenings.$screeningId.tsx
  • packages/app-builder/src/routes/_app/_builder/detection/scenarios/$scenarioId/i/$iterationId/_edit-view/trigger.tsx
🔇 Additional comments (6)
packages/app-builder/src/models/astNode/risk.ts (1)

24-52: LGTM — new node definition is clean and consistent.

The shape, constructor, and type guard all follow the same patterns as RecordHasPastAlertsAstNode.

packages/app-builder/src/components/AstBuilder/edition/OperandMenu/MenuOption.tsx (1)

41-88: LGTM — gating logic is clean.

hasScoringRuleset is correctly wired through, isRestrictedClientRisk is properly narrowed to monitoringListCheckAstNodeName, and the new disabledScoringCheck path is well-structured.

packages/app-builder/src/routes/_app/_builder/detection/scenarios/$scenarioId/i/$iterationId/rules.$ruleId.tsx (1)

44-53: LGTM — clean consolidation through getBuilderOptionsFn.

Calling a server function from within another server function runs inline on the server (no HTTP round-trip per TanStack Start's SSR semantics), so the Promise.all pattern here is efficient.

packages/app-builder/src/routes/_app/_builder/detection/scenarios/$scenarioId/i/$iterationId/_edit-view/trigger.tsx (1)

26-41: LGTM — elegant simplification.

Removing the local triggerLoader and delegating directly to getBuilderOptionsFn reduces duplicated fetching logic across routes.

packages/app-builder/src/routes/_app/_builder/detection/scenarios/$scenarioId/i/$iterationId/screenings.$screeningId.tsx (1)

58-75: LGTM — clean consolidation.

entitlements is correctly retained (still needed for NER gating on Line 606) and the getBuilderOptionsFn composition mirrors the rule and trigger loaders well.

packages/app-builder/src/server-fns/scenarios.ts (1)

327-330: ⚡ Quick win

Unconditional scoring API calls may break the builder for tenants without scoring.

continuousScreening.listConfigurations() is guarded by isContinuousScreeningAvailable(entitlements), falling back to Promise.resolve([]). However, userScoring.listRulesets() and userScoring.getSettings() have no such guard. If either method throws or rejects for tenants without a scoring configuration, the entire Promise.all() would reject, taking down the rule builder, trigger editor, and screening editor for those users.

Verify that the userScoring service methods either:

  1. Guard against errors for tenants without scoring, or
  2. Are wrapped with error handling (e.g., .catch() fallback like continuousScreening)

Comment on lines +62 to +77
<SelectV2<number>
multiple
options={levelsOptions}
value={node.children[0].constant}
placeholder="Risk Level"
onChange={(value) => {
node.children[0].constant = value;
}}
/>
</div>
<EditionEvaluationErrors id={node.id} filterOut={['FUNCTION_ERROR']} />
</div>
{!hasValidLicense ? (
<Callout icon="lock" variant="outlined" color="red">
{t('scenarios:edit_ip_has_flag.premium_callout')}
</Callout>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Two i18n issues: hardcoded placeholder and borrowed translation key.

  • Line 66: placeholder="Risk Level" is a raw English string. It should be passed through t() with a dedicated key, just as the modal title and description are.
  • Line 76: t('scenarios:edit_ip_has_flag.premium_callout') re-uses the IP-flag premium callout key — this is a copy-paste artefact. If the IP-flag copy ever changes independently, this modal silently shows wrong text.
🛠️ Proposed fix
-            placeholder="Risk Level"
+            placeholder={t('scenarios:edit_record_risk_level_check.placeholder')}
-          {t('scenarios:edit_ip_has_flag.premium_callout')}
+          {t('scenarios:edit_record_risk_level_check.premium_callout')}

Add the two new keys to en/scenarios.json (and the fr/ar equivalents).

📝 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.

Suggested change
<SelectV2<number>
multiple
options={levelsOptions}
value={node.children[0].constant}
placeholder="Risk Level"
onChange={(value) => {
node.children[0].constant = value;
}}
/>
</div>
<EditionEvaluationErrors id={node.id} filterOut={['FUNCTION_ERROR']} />
</div>
{!hasValidLicense ? (
<Callout icon="lock" variant="outlined" color="red">
{t('scenarios:edit_ip_has_flag.premium_callout')}
</Callout>
<SelectV2<number>
multiple
options={levelsOptions}
value={node.children[0].constant}
placeholder={t('scenarios:edit_record_risk_level_check.placeholder')}
onChange={(value) => {
node.children[0].constant = value;
}}
/>
</div>
<EditionEvaluationErrors id={node.id} filterOut={['FUNCTION_ERROR']} />
</div>
{!hasValidLicense ? (
<Callout icon="lock" variant="outlined" color="red">
{t('scenarios:edit_record_risk_level_check.premium_callout')}
</Callout>
🤖 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
`@packages/app-builder/src/components/AstBuilder/edition/EditModal/modals/RecordRiskLevelCheck/RecordRiskLevelCheck.tsx`
around lines 62 - 77, Replace the hardcoded placeholder on the SelectV2
component and the copied translation key used in the Callout with dedicated i18n
keys: change placeholder="Risk Level" to
placeholder={t('scenarios:record_risk_level.placeholder')} and replace
t('scenarios:edit_ip_has_flag.premium_callout') with
t('scenarios:record_risk_level.premium_callout'); then add the corresponding
keys (record_risk_level.placeholder and record_risk_level.premium_callout) to
the en/scenarios.json and the fr/ar locale files with appropriate translations.

Comment on lines +150 to +163
const levelColorsMap = SCORING_LEVELS_COLORS[scoringSettings.maxRiskLevel as MaxRiskLevel];
const levelLabelsMap = SCORING_LEVELS_LABEL_KEYS[scoringSettings.maxRiskLevel as MaxRiskLevel];

return (
<>
<div className="flex gap-v2-xs items-center">
{node.children[0].constant.map((level) => (
<div
key={level}
className="flex gap-v2-xs items-center border rounded-full px-v2-sm py-v2-xs text-small"
style={{ borderColor: levelColorsMap[level] }}
>
<div className="size-4 rounded-full" style={{ backgroundColor: levelColorsMap[level] }} />
<span>{t(levelLabelsMap[level])}</span>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard score-level maps before indexing to avoid runtime crashes.

Here, type casting may hide invalid runtime values; if map lookup fails, property access can throw.
Or, as the bard might whisper: “When maps be void, let not the render bleed.”

Suggested patch
-  const levelColorsMap = SCORING_LEVELS_COLORS[scoringSettings.maxRiskLevel as MaxRiskLevel];
-  const levelLabelsMap = SCORING_LEVELS_LABEL_KEYS[scoringSettings.maxRiskLevel as MaxRiskLevel];
+  const levelColorsMap = SCORING_LEVELS_COLORS[scoringSettings.maxRiskLevel as MaxRiskLevel];
+  const levelLabelsMap = SCORING_LEVELS_LABEL_KEYS[scoringSettings.maxRiskLevel as MaxRiskLevel];
+  if (!levelColorsMap || !levelLabelsMap) return null;
@@
-        {node.children[0].constant.map((level) => (
-          <div
-            key={level}
-            className="flex gap-v2-xs items-center border rounded-full px-v2-sm py-v2-xs text-small"
-            style={{ borderColor: levelColorsMap[level] }}
-          >
-            <div className="size-4 rounded-full" style={{ backgroundColor: levelColorsMap[level] }} />
-            <span>{t(levelLabelsMap[level])}</span>
-          </div>
-        ))}
+        {node.children[0].constant.map((level) => {
+          const color = levelColorsMap[level];
+          const labelKey = levelLabelsMap[level];
+          if (!color || !labelKey) return null;
+          return (
+            <div
+              key={level}
+              className="flex gap-v2-xs items-center border rounded-full px-v2-sm py-v2-xs text-small"
+              style={{ borderColor: color }}
+            >
+              <div className="size-4 rounded-full" style={{ backgroundColor: color }} />
+              <span>{t(labelKey)}</span>
+            </div>
+          );
+        })}
📝 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.

Suggested change
const levelColorsMap = SCORING_LEVELS_COLORS[scoringSettings.maxRiskLevel as MaxRiskLevel];
const levelLabelsMap = SCORING_LEVELS_LABEL_KEYS[scoringSettings.maxRiskLevel as MaxRiskLevel];
return (
<>
<div className="flex gap-v2-xs items-center">
{node.children[0].constant.map((level) => (
<div
key={level}
className="flex gap-v2-xs items-center border rounded-full px-v2-sm py-v2-xs text-small"
style={{ borderColor: levelColorsMap[level] }}
>
<div className="size-4 rounded-full" style={{ backgroundColor: levelColorsMap[level] }} />
<span>{t(levelLabelsMap[level])}</span>
const levelColorsMap = SCORING_LEVELS_COLORS[scoringSettings.maxRiskLevel as MaxRiskLevel];
const levelLabelsMap = SCORING_LEVELS_LABEL_KEYS[scoringSettings.maxRiskLevel as MaxRiskLevel];
if (!levelColorsMap || !levelLabelsMap) return null;
return (
<>
<div className="flex gap-v2-xs items-center">
{node.children[0].constant.map((level) => {
const color = levelColorsMap[level];
const labelKey = levelLabelsMap[level];
if (!color || !labelKey) return null;
return (
<div
key={level}
className="flex gap-v2-xs items-center border rounded-full px-v2-sm py-v2-xs text-small"
style={{ borderColor: color }}
>
<div className="size-4 rounded-full" style={{ backgroundColor: color }} />
<span>{t(labelKey)}</span>
🤖 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 `@packages/app-builder/src/components/AstBuilder/OperandInfos.tsx` around lines
150 - 163, The code indexes levelColorsMap[level] and levelLabelsMap[level]
without guarding against invalid keys (the cast on scoringSettings.maxRiskLevel
can hide runtime mismatches), which can throw; in the render path (around
levelColorsMap, levelLabelsMap, node.children[0].constant and the t(...) call)
validate that the maps exist and the specific level key exists before using them
(e.g., compute safeLevelColors/ safeLevelLabels from SCORING_LEVELS_COLORS and
SCORING_LEVELS_LABEL_KEYS with a fallback map or filter
node.children[0].constant to include only keys present in the maps), and use a
sensible default color/label or skip rendering that level to avoid runtime
crashes.

Comment thread packages/app-builder/src/models/astNode/builder-ast-node.ts Outdated
Comment thread packages/app-builder/src/server-fns/scenarios.ts Outdated
Comment thread packages/app-builder/src/services/ast-node/getAstNodeDisplayName.ts
@ChibiBlasphem ChibiBlasphem force-pushed the feat/add-risklevel-ast-node-builder branch from 5e9842a to 2a3bd7a Compare May 7, 2026 10:25
@ChibiBlasphem ChibiBlasphem force-pushed the feat/add-risklevel-ast-node-builder branch from 2a3bd7a to 14f1996 Compare May 7, 2026 13:17
Copy link
Copy Markdown
Contributor

@william-schlegel william-schlegel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

M medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants