settings ui work #2645

Merged
mfreeman451 merged 1 commit from refs/pull/2645/head into testing 2026-01-11 04:10:17 +00:00
mfreeman451 commented 2026-01-11 04:02:51 +00:00 (Migrated from github.com)
Owner

Imported from GitHub pull request.

Original GitHub pull request: #2242
Original author: @mfreeman451
Original URL: https://github.com/carverauto/serviceradar/pull/2242
Original created: 2026-01-11T04:02:51Z
Original updated: 2026-01-11T04:10:18Z
Original head: carverauto/serviceradar:feat/rule_builder_ui
Original base: testing
Original merged: 2026-01-11T04:10:17Z by @mfreeman451

User description

IMPORTANT: Please sign the Developer Certificate of Origin

Thank you for your contribution to ServiceRadar. Please note, when contributing, the developer must include
a DCO sign-off statement indicating the DCO acceptance in one commit message. Here
is an example DCO Signed-off-by line in a commit message:

Signed-off-by: J. Doe <j.doe@domain.com>

Describe your changes

Code checklist before requesting a review

  • I have signed the DCO?
  • The build completes without errors?
  • All tests are passing when running make test?

PR Type

Enhancement, Tests, Documentation


Description

  • New Rule Builder UI: Comprehensive three-tab interface (Logs, Events, Alerts) for managing log normalization, event promotion, and alert rules with full CRUD operations and template management

  • Rule Template System: New tenant-scoped template resources (ZenRuleTemplate, LogPromotionRuleTemplate, StatefulAlertRuleTemplate) with default seeding for tenant onboarding

  • Zen Rule Management: Complete Zen rule resource with compilation to JSON Decision Model format, KV synchronization to datasvc, and validation constraints

  • Default Rule Seeding: GenServer modules (TemplateSeeder, ZenRuleSeeder, ZenRuleSync) that automatically seed baseline rules and templates for each tenant on startup

  • Unified Settings Navigation: New SettingsComponents module providing consistent layout and navigation across settings pages, replacing scattered admin components

  • Database Schema: New tenant-scoped tables for Zen rules, rule templates, and promotion/alert rule templates with proper indexing and multitenancy support

  • Navigation Restructuring: Updated routing from /infrastructure and /users/settings to unified /settings/* paths with backward compatibility redirects

  • Documentation: Added rule builder user guide, design specifications, and implementation tasks for both current and future JDM editor features

  • Bug Fix: Fixed inline value rendering in alert show page by pre-assigning template variables

  • Test Coverage: New integration tests for rule builder UI, Zen rules, and updated path-based tests for navigation changes

  • Configuration Updates: Added passthrough rules to Zen consumer configurations and updated test assertions for new rule ordering


Diagram Walkthrough

flowchart LR
  A["Rule Builder UI<br/>Logs/Events/Alerts"] -->|manages| B["Zen Rules<br/>Templates"]
  A -->|manages| C["Promotion Rules<br/>Templates"]
  A -->|manages| D["Alert Rules<br/>Templates"]
  B -->|compiles to| E["JDM Format"]
  E -->|syncs via| F["KV Store<br/>datasvc"]
  G["TemplateSeeder"] -->|seeds defaults| B
  G -->|seeds defaults| C
  G -->|seeds defaults| D
  H["ZenRuleSync"] -->|reconciles| F
  I["Settings Nav<br/>Components"] -->|unified layout| A
  J["Database<br/>Migrations"] -->|stores| B
  J -->|stores| C
  J -->|stores| D

File Walkthrough

Relevant files
Tests
4 files
config.rs
Update config tests for new passthrough rule ordering       

cmd/consumers/zen/src/config.rs

  • Updated test assertions to reflect new rule ordering with passthrough
    rule added as first rule
  • Modified expected rule indices for strip_full_message and cef_severity
    in syslog decision group
  • Added assertion for new snmp_severity rule in snmp decision group
  • Adjusted rule indices to account for the new passthrough rule
    insertion
+5/-3     
rules_live_test.exs
Rule builder UI integration tests                                               

web-ng/test/serviceradar_web_ng_web/live/settings/rules_live_test.exs

  • New test module for rule builder UI covering Zen rules, promotion
    rules, and stateful alert rules
  • Tests rule creation, deletion, and template management workflows
  • Validates form submission and rule persistence across different rule
    types
+112/-0 
zen_rule_test.exs
Zen rule integration tests                                                             

elixir/serviceradar_core/test/serviceradar/observability/zen_rule_test.exs

  • New integration test module for Zen rule creation, compilation, and KV
    sync
  • Tests rule compilation with format derivation from subject
  • Validates rule name and subject constraints
  • Tests KV key generation for sync operations
+111/-0 
settings_test.exs
User settings test path updates                                                   

web-ng/test/serviceradar_web_ng_web/live/user_live/settings_test.exs

  • Updated all test paths from /users/settings to /settings/profile to
    match new routing
  • Tests remain functionally equivalent, validating email and password
    update workflows
+7/-7     
Enhancement
24 files
index.ex
New rules management LiveView with three-tier pipeline UI

web-ng/lib/serviceradar_web_ng_web/live/settings/rules_live/index.ex

  • New 2097-line LiveView module for managing log normalization, event
    promotion, and alert rules
  • Implements three-tab interface for Logs, Events, and Alerts with CRUD
    operations for rules and templates
  • Provides form handling with validation, template application, and
    preset management via modal dialogs
  • Includes comprehensive helper functions for form building, parameter
    normalization, and data querying
+2097/-0
index.ex
Refactor cluster status with caching and tenant filtering

web-ng/lib/serviceradar_web_ng_web/live/settings/cluster_live/index.ex

  • Refactored cluster status page to use caching mechanism for gateways
    and agents with stale detection
  • Added platform admin visibility checks and tenant-based filtering for
    agents
  • Implemented RPC-based data fetching from cluster nodes with timeout
    handling and concurrent processing
  • Added new event handlers for gateway and agent status updates with
    cache management
  • Replaced inline tables with reusable gateways_table and agents_table
    components
  • Added job counts display and improved timestamp parsing with multiple
    format support
+634/-159
zen_rule_templates.ex
New Zen rule templates module with decision models             

elixir/serviceradar_core/lib/serviceradar/observability/zen_rule_templates.ex

  • New module providing built-in Zen rule templates as compiled JSON
    decision models
  • Defines four template types: passthrough, strip_full_message,
    snmp_severity, and cef_severity
  • Implements compile/2 function to return appropriate template JSON
    based on template type
  • Templates include node and edge definitions for visual rule builder
    representation
+110/-0 
index.ex
NATS operator and tenant account management UI integration

web-ng/lib/serviceradar_web_ng_web/live/admin/edge_sites_live/index.ex

  • Refactored to use SettingsComponents instead of AdminComponents for
    unified settings navigation
  • Added NATS operator and tenant account management UI with status cards
    and filtering
  • Implemented load_operator_status() and load_tenant_accounts()
    functions to fetch NATS infrastructure data
  • Added event handlers for NATS tenant filtering and reprovisioning with
    error handling
  • Created new component functions operator_status_card(),
    system_account_card(), and nats_status_badge() for NATS status display
+345/-9 
template_seeder.ex
Default rule template seeding for tenant onboarding           

elixir/serviceradar_core/lib/serviceradar/observability/template_seeder.ex

  • New GenServer module that seeds default rule templates for each tenant
    on startup
  • Implements template seeding for LogPromotionRuleTemplate,
    StatefulAlertRuleTemplate, and ZenRuleTemplate
  • Handles legacy template name migrations (e.g., syslog_passthrough to
    passthrough)
  • Provides default templates for syslog, SNMP, OTEL logs, and internal
    logs with passthrough, severity mapping, and message stripping
+349/-0 
zen_rule_seeder.ex
Default Zen rule seeding for tenant onboarding                     

elixir/serviceradar_core/lib/serviceradar/observability/zen_rule_seeder.ex

  • New GenServer module that seeds default Zen rules for each tenant on
    startup
  • Implements rule seeding with legacy name migration support
  • Provides default Zen rules for multiple log subjects (syslog, SNMP,
    OTEL, internal)
  • Ensures every tenant has baseline passthrough rules and severity
    mapping rules
+278/-0 
settings.ex
User settings page layout and navigation restructuring     

web-ng/lib/serviceradar_web_ng_web/live/user_live/settings.ex

  • Refactored layout from centered header to unified settings panel
    structure with ui_panel components
  • Reorganized email and password forms into separate collapsible
    sections with descriptive headers
  • Updated redirect path from /users/settings to /settings/profile for
    consistency
  • Improved visual hierarchy and spacing with max-width container
+86/-59 
zen_rule.ex
Zen rule resource with compilation and KV sync                     

elixir/serviceradar_core/lib/serviceradar/observability/zen_rule.ex

  • New Ash resource for tenant-scoped Zen rule definitions with
    multitenancy support
  • Includes attributes for rule name, subject, template type, builder
    config, and compiled JDM
  • Implements create/update/destroy actions with CompileZenRule and
    SyncZenRule changes
  • Defines policies for read/write access based on actor role and tenant
    membership
  • Validates rule names and subjects with custom validation module
+186/-0 
stateful_alert_rule_template.ex
Stateful alert rule template resource                                       

elixir/serviceradar_core/lib/serviceradar/observability/stateful_alert_rule_template.ex

  • New Ash resource for tenant-scoped stateful alert rule templates
  • Includes attributes for threshold, window/bucket/cooldown/renotify
    timing, signal type, and match/event/alert payloads
  • Implements create/update/destroy actions with field acceptance lists
  • Defines policies for role-based access control
+184/-0 
zen_rule_sync.ex
Zen rule KV synchronization and reconciliation service     

elixir/serviceradar_core/lib/serviceradar/observability/zen_rule_sync.ex

  • New GenServer module that syncs Zen rules to datasvc KV store and
    reconciles on startup/interval
  • Implements sync_rule() function to push compiled JDM to KV with
    revision tracking
  • Provides delete_rule() for removing disabled rules from KV
  • Reconciles all tenant rules every 5 minutes to ensure KV consistency
+126/-0 
zen_rule_template.ex
Zen rule template resource for builder presets                     

elixir/serviceradar_core/lib/serviceradar/observability/zen_rule_template.ex

  • New Ash resource for tenant-scoped Zen rule builder templates
  • Includes attributes for template name, subject, builder config, and
    template type
  • Implements create/update/destroy actions with validation
  • Defines policies for role-based access control
+149/-0 
settings_components.ex
Unified settings navigation and layout components               

web-ng/lib/serviceradar_web_ng_web/components/settings_components.ex

  • New component module providing unified settings section layout and
    navigation
  • Implements settings_shell() wrapper component for consistent max-width
    container
  • Provides settings_nav() and settings_tabs() for main settings
    navigation (Cluster, Events, Edge Ops, Integrations, Jobs)
  • Provides edge_nav() and edge_tabs() for edge operations sub-navigation
    (Collectors, Sites & NATS, Onboarding)
+109/-0 
client.ex
DataService client KV revision retrieval                                 

elixir/serviceradar_core/lib/serviceradar/data_service/client.ex

  • Added get_with_revision() function to retrieve KV values along with
    their revision numbers
  • Implements gRPC call to KVService.Stub.get() with revision extraction
    from response
  • Supports timeout configuration and error handling for not found and
    RPC errors
+42/-0   
log_promotion_rule_template.ex
Log promotion rule template resource                                         

elixir/serviceradar_core/lib/serviceradar/observability/log_promotion_rule_template.ex

  • New Ash resource for tenant-scoped log promotion rule templates
  • Includes attributes for rule name, priority, match criteria, and event
    payload
  • Implements create/update/destroy actions with field acceptance lists
  • Defines policies for role-based access control
+121/-0 
zen_rule.ex
Zen rule validation constraints                                                   

elixir/serviceradar_core/lib/serviceradar/observability/validations/zen_rule.ex

  • New validation module for Zen rules enforcing name format (lowercase,
    numbers, hyphens, underscores)
  • Validates subject against allowed list (syslog, SNMP, OTEL, internal
    logs)
  • Validates format matches subject (JSON for logs, protobuf for OTEL,
    otel_metrics for metrics)
+64/-0   
compile_zen_rule.ex
Zen rule compilation to JDM format                                             

elixir/serviceradar_core/lib/serviceradar/observability/changes/compile_zen_rule.ex

  • New change module that compiles Zen rule builder config into GoRules
    JSON Decision Model
  • Resolves format based on subject (JSON, protobuf, or otel_metrics)
  • Applies compilation during create and update actions
+57/-0   
sync_zen_rule.ex
Zen rule KV synchronization change                                             

elixir/serviceradar_core/lib/serviceradar/observability/changes/sync_zen_rule.ex

  • New change module that syncs Zen rules to datasvc KV after
    create/update/destroy
  • Handles rule deletion when key fields change (agent_id, stream_name,
    subject, name)
  • Logs warnings on sync failures without blocking rule persistence
  • Supports context flag to skip sync for testing
+58/-0   
index.ex
Collector admin page settings navigation integration         

web-ng/lib/serviceradar_web_ng_web/live/admin/collector_live/index.ex

  • Refactored to use SettingsComponents instead of AdminComponents
  • Updated layout to use settings_shell() and edge_nav() components for
    unified navigation
+5/-4     
zen_rule_template.ex
Zen rule template validation constraints                                 

elixir/serviceradar_core/lib/serviceradar/observability/validations/zen_rule_template.ex

  • New validation module for Zen rule templates enforcing name and
    subject constraints
  • Validates name format (lowercase, numbers, hyphens, underscores)
  • Validates subject against allowed list including internal logs
+47/-0   
index.ex
Cluster admin page settings navigation integration             

web-ng/lib/serviceradar_web_ng_web/live/admin/cluster_live/index.ex

  • Refactored to use SettingsComponents instead of AdminComponents
  • Updated layout to use settings_shell() and settings_nav() components
    for unified navigation
+4/-4     
stateful_alert_rule.ex
Stateful alert rule code interface and action updates       

elixir/serviceradar_core/lib/serviceradar/observability/stateful_alert_rule.ex

  • Added code interface with list, list_active, create, update, destroy
    functions
  • Added explicit accept lists to create and update actions for field
    validation
  • Maintains existing validation for window/bucket timing constraints
+40/-0   
show.ex
Node show page navigation path updates                                     

web-ng/lib/serviceradar_web_ng_web/live/node_live/show.ex

  • Updated page path from /infrastructure/nodes to
    /settings/cluster/nodes for SRQL context
  • Updated back button navigation to point to /settings/cluster instead
    of /infrastructure?tab=nodes
+4/-4     
index.ex
Edge package admin page settings navigation integration   

web-ng/lib/serviceradar_web_ng_web/live/admin/edge_package_live/index.ex

  • Refactored to use SettingsComponents instead of AdminComponents
  • Updated layout to use settings_shell(), settings_nav(), and edge_nav()
    components
+5/-4     
show.ex
Edge sites show page settings navigation integration         

web-ng/lib/serviceradar_web_ng_web/live/admin/edge_sites_live/show.ex

  • Refactored to use SettingsComponents instead of AdminComponents
  • Updated layout to use settings_shell(), settings_nav(), and edge_nav()
    components
+5/-4     
Configuration changes
13 files
layouts.ex
Update navigation links and sidebar routing                           

web-ng/lib/serviceradar_web_ng_web/components/layouts.ex

  • Removed Infrastructure sidebar link navigation
  • Updated Settings sidebar link to point to /settings/cluster instead of
    /admin/jobs
  • Modified Settings link active state to include /settings, /admin, and
    /users/settings paths
  • Changed user menu Account link from /users/settings to
    /settings/profile
+9/-12   
20260111080000_add_rule_templates.exs
Database schema for rule template storage                               

elixir/serviceradar_core/priv/repo/tenant_migrations/20260111080000_add_rule_templates.exs

  • Creates three new tenant-scoped tables: zen_rule_templates,
    log_promotion_rule_templates, stateful_alert_rule_templates
  • Adds unique indexes on (tenant_id, name) for promotion and stateful
    templates
  • Includes fields for rule configuration, matching criteria, and
    event/alert payloads
+111/-0 
application.ex
Application startup configuration for rule seeding and sync

elixir/serviceradar_core/lib/serviceradar/application.ex

  • Added TemplateSeeder GenServer to seed default rule templates on
    startup
  • Added ZenRuleSeeder GenServer to seed default Zen rules on startup
  • Added ZenRuleSync GenServer to reconcile Zen rules with datasvc KV
    store
  • Conditionally starts seeders and sync based on repo and datasvc
    availability
+32/-1   
20260110070000_add_zen_rules.exs
Database schema for Zen rule storage                                         

elixir/serviceradar_core/priv/repo/tenant_migrations/20260110070000_add_zen_rules.exs

  • Creates zen_rules table with columns for rule definition, compilation
    output, and KV metadata
  • Adds unique index on (tenant_id, name) for rule uniqueness per tenant
  • Includes fields for compiled JDM, KV revision, and builder
    configuration
+48/-0   
20260111013324_update-zen-rule-identities.exs
Zen rule identity constraint updates                                         

elixir/serviceradar_core/priv/repo/tenant_migrations/20260111013324_update-zen-rule-identities.exs

  • Updates unique indexes for zen_rules and zen_rule_templates from
    (tenant_id, name) to (tenant_id, subject, name)
  • Allows multiple rules with same name across different subjects
+45/-0   
router.ex
Router updates for unified settings navigation                     

web-ng/lib/serviceradar_web_ng_web/router.ex

  • Added redirect from /users/settings to /settings/profile for backward
    compatibility
  • Moved user settings route from /users/settings to /settings/profile
  • Added /settings/cluster/nodes/:node_name route for node details
  • Added /settings/rules route for rule builder UI
  • Replaced /infrastructure routes with redirects to /settings/cluster
    paths
+6/-5     
20260111013324.json
Add stateful alert rule templates resource snapshot           

elixir/serviceradar_core/priv/resource_snapshots/repo/tenants/stateful_alert_rule_templates/20260111013324.json

  • New resource snapshot file for stateful_alert_rule_templates table
    schema
  • Defines 18 attributes including id, name, description, enabled,
    priority, match/event maps, and tenant_id
  • Establishes unique constraint on tenant_id and name combination
  • Includes multitenancy configuration with context strategy
+257/-0 
20260111013324.json
Add zen rules resource snapshot                                                   

elixir/serviceradar_core/priv/resource_snapshots/repo/tenants/zen_rules/20260111013324.json

  • New resource snapshot file for zen_rules table schema
  • Defines 16 attributes including id, name, description, enabled, order,
    stream_name, subject, format, template, builder_config, compiled_jdm,
    and tenant_id
  • Establishes unique constraint on tenant_id, subject, and name
    combination
  • Includes multitenancy configuration with context strategy
+234/-0 
20260111013324.json
Add zen rule templates resource snapshot                                 

elixir/serviceradar_core/priv/resource_snapshots/repo/tenants/zen_rule_templates/20260111013324.json

  • New resource snapshot file for zen_rule_templates table schema
  • Defines 12 attributes including id, name, description, enabled, order,
    stream_name, subject, template, builder_config, and tenant_id
  • Establishes unique constraint on tenant_id, subject, and name
    combination
  • Includes multitenancy configuration with context strategy
+198/-0 
20260111013324.json
Add log promotion rule templates resource snapshot             

elixir/serviceradar_core/priv/resource_snapshots/repo/tenants/log_promotion_rule_templates/20260111013324.json

  • New resource snapshot file for log_promotion_rule_templates table
    schema
  • Defines 10 attributes including id, name, description, enabled,
    priority, match/event maps, and tenant_id
  • Establishes unique constraint on tenant_id and name combination
  • Includes multitenancy configuration with context strategy
+158/-0 
zen-consumer-with-otel.json
Add passthrough rules to zen consumer configuration           

cmd/consumers/zen/zen-consumer-with-otel.json

  • Added passthrough rule with order 1 to syslog subject rules, shifting
    existing rules to orders 2 and 3
  • Added passthrough rule with order 1 to snmp subject rules and changed
    second rule from cef_severity to snmp_severity with order 2
+5/-3     
zen-consumer.json
Add passthrough rules to zen consumer configuration           

cmd/consumers/zen/zen-consumer.json

  • Added passthrough rule with order 1 to syslog subject rules, shifting
    existing rules to orders 2 and 3
  • Added passthrough rule with order 1 to snmp subject rules and changed
    second rule from cef_severity to snmp_severity with order 2
+5/-3     
zen-consumer.json
Add passthrough rules to zen consumer configuration           

packaging/zen/config/zen-consumer.json

  • Added passthrough rule with order 1 to syslog subject rules, shifting
    existing rules to orders 2 and 3
  • Added passthrough rule with order 1 to snmp subject rules and changed
    second rule from cef_severity to snmp_severity with order 2
+5/-3     
Dependencies
2 files
BUILD.bazel
Add new Go source files to build target                                   

pkg/consumers/db-event-writer/BUILD.bazel

  • Added two new source files to the Go library build target:
    json_logs.go and ocsf_events.go
  • Maintains existing source files while extending functionality
+2/-0     
BUILD.bazel
Update log crate dependency version                                           

cmd/checkers/rperf-server/BUILD.bazel

  • Updated log crate dependency from version 0.4.28 to 0.4.29
+1/-1     
Bug fix
1 files
show.ex
Alert show page inline value rendering fix                             

web-ng/lib/serviceradar_web_ng_web/live/alert_live/show.ex

  • Fixed inline value rendering by pre-assigning value_text to avoid
    direct function calls in templates
  • Applies fix to boolean, number, and generic value rendering functions
+10/-4   
Documentation
9 files
tasks.md
Mark rule builder UI implementation tasks complete             

openspec/changes/add-rule-builder-ui/tasks.md

  • Marked 10 tasks as completed in section 1 (Data Model & Sync)
    including resource creation, migrations, KV sync, and template seeding
  • Marked 4 tasks as completed in section 2 (API Layer) including CRUD
    actions and template endpoints
  • Marked 9 tasks as completed in section 3 (Web UI) including settings
    layout, rule builders, and template libraries
  • Marked 4 tasks as completed in section 4 (Docs & Examples) and 3 tasks
    in section 5 (Tests)
+30/-18 
design.md
Add Zen JDM editor design specification                                   

openspec/changes/add-zen-jdm-editor/design.md

  • New design document for Zen rule editor with JDM canvas and JSON view
  • Outlines goals to provide canvas + JSON editor for actual JDM stored
    per rule
  • Documents decision to use phoenix_react_server for React embedding and
    store JDM definition as first-class JSON field
  • Describes rule library concept, UI layout with two-pane screen, and
    migration plan for existing template-based rules
+38/-0   
design.md
Update rule builder UI design with template libraries       

openspec/changes/add-rule-builder-ui/design.md

  • Updated rule naming in UI from "Event Notifications & Response Rules"
    to "Events"
  • Changed settings layout from left navigation to tab-based navigation
    in main content area
  • Added template libraries concept with tenant-scoped template resources
    seeded with defaults
  • Added tenant onboarding defaults section describing baseline Zen rule
    seeding
  • Expanded migration plan to include template tables and tenant defaults
+9/-4     
spec.md
Add template library and default rules requirements           

openspec/changes/add-rule-builder-ui/specs/observability-rule-management/spec.md

  • Added requirement for default Zen rules and reconciliation during
    tenant onboarding
  • Added requirement for default passthrough rules and templates for each
    supported Zen subject
  • Added requirement for tenant-scoped template libraries with CRUD
    operations
  • Added requirement for template-based rule creation allowing operators
    to select templates when creating/editing rules
+49/-0   
rule-builder.md
Add rule builder user documentation                                           

docs/docs/rule-builder.md

  • New documentation file for Rule Builder feature
  • Describes location at Settings → Events and explains log normalization
    (Zen) rules workflow
  • Documents template system, KV sync behavior, and response rules
    (promotion and stateful alerts)
  • Includes tips for rule creation and references to template usage
+67/-0   
tasks.md
Add Zen JDM editor implementation tasks                                   

openspec/changes/add-zen-jdm-editor/tasks.md

  • New tasks file for Zen JDM editor implementation
  • Defines 4 sections: Data Model + Sync, React Editor Integration, Zen
    Rule Editor UX, and Tests + Docs
  • Lists 20 specific tasks including adding jdm_definition attribute,
    backfilling definitions, React integration, and LiveView tests
+20/-0   
syslog.md
Update syslog documentation with rule builder reference   

docs/docs/syslog.md

  • Updated reference from future Web UI rule builder to current Rule
    Builder UI
  • Added "Settings → Events" navigation path for managing Zen
    normalization rules for syslog
  • Changed link to reference the new rule-builder.md documentation guide
+2/-1     
proposal.md
Add Zen JDM editor change proposal                                             

openspec/changes/add-zen-jdm-editor/proposal.md

  • New proposal document for Zen rule editor with JDM canvas and JSON
    view
  • Explains motivation to replace preset modal UX with first-class rule
    editor
  • Describes embedding GoRules JDM editor via phoenix_react_server and
    storing JDM definitions in CNPG
  • Lists affected specs, code areas, and impact on web-ng, core-elx, and
    documentation
+16/-0   
snmp.md
Update SNMP documentation with rule builder reference       

docs/docs/snmp.md

  • Updated reference from future Web UI rule builder to current Rule
    Builder UI
  • Added "Settings → Events" navigation path for managing Zen
    normalization rules for SNMP traps
  • Changed link to reference the new rule-builder.md documentation guide
+2/-1     
Additional files
12 files
initialize_tenant_infrastructure.ex +7/-0     
observability.ex +4/-0     
log_promotion_rule.ex +17/-1   
stateful_alert_rule_history.ex +11/-0   
proposal.md +5/-1     
page_controller.ex +12/-0   
user_session_controller.ex +1/-1     
index.ex +4/-4     
index.ex +4/-4     
show.ex +4/-4     
index.ex +5/-4     
show.ex +2/-2     

Imported from GitHub pull request. Original GitHub pull request: #2242 Original author: @mfreeman451 Original URL: https://github.com/carverauto/serviceradar/pull/2242 Original created: 2026-01-11T04:02:51Z Original updated: 2026-01-11T04:10:18Z Original head: carverauto/serviceradar:feat/rule_builder_ui Original base: testing Original merged: 2026-01-11T04:10:17Z by @mfreeman451 --- ### **User description** ## IMPORTANT: Please sign the Developer Certificate of Origin Thank you for your contribution to ServiceRadar. Please note, when contributing, the developer must include a [DCO sign-off statement]( https://developercertificate.org/) indicating the DCO acceptance in one commit message. Here is an example DCO Signed-off-by line in a commit message: ``` Signed-off-by: J. Doe <j.doe@domain.com> ``` ## Describe your changes ## Issue ticket number and link ## Code checklist before requesting a review - [ ] I have signed the DCO? - [ ] The build completes without errors? - [ ] All tests are passing when running make test? ___ ### **PR Type** Enhancement, Tests, Documentation ___ ### **Description** - **New Rule Builder UI**: Comprehensive three-tab interface (Logs, Events, Alerts) for managing log normalization, event promotion, and alert rules with full CRUD operations and template management - **Rule Template System**: New tenant-scoped template resources (`ZenRuleTemplate`, `LogPromotionRuleTemplate`, `StatefulAlertRuleTemplate`) with default seeding for tenant onboarding - **Zen Rule Management**: Complete Zen rule resource with compilation to JSON Decision Model format, KV synchronization to datasvc, and validation constraints - **Default Rule Seeding**: GenServer modules (`TemplateSeeder`, `ZenRuleSeeder`, `ZenRuleSync`) that automatically seed baseline rules and templates for each tenant on startup - **Unified Settings Navigation**: New `SettingsComponents` module providing consistent layout and navigation across settings pages, replacing scattered admin components - **Database Schema**: New tenant-scoped tables for Zen rules, rule templates, and promotion/alert rule templates with proper indexing and multitenancy support - **Navigation Restructuring**: Updated routing from `/infrastructure` and `/users/settings` to unified `/settings/*` paths with backward compatibility redirects - **Documentation**: Added rule builder user guide, design specifications, and implementation tasks for both current and future JDM editor features - **Bug Fix**: Fixed inline value rendering in alert show page by pre-assigning template variables - **Test Coverage**: New integration tests for rule builder UI, Zen rules, and updated path-based tests for navigation changes - **Configuration Updates**: Added passthrough rules to Zen consumer configurations and updated test assertions for new rule ordering ___ ### Diagram Walkthrough ```mermaid flowchart LR A["Rule Builder UI<br/>Logs/Events/Alerts"] -->|manages| B["Zen Rules<br/>Templates"] A -->|manages| C["Promotion Rules<br/>Templates"] A -->|manages| D["Alert Rules<br/>Templates"] B -->|compiles to| E["JDM Format"] E -->|syncs via| F["KV Store<br/>datasvc"] G["TemplateSeeder"] -->|seeds defaults| B G -->|seeds defaults| C G -->|seeds defaults| D H["ZenRuleSync"] -->|reconciles| F I["Settings Nav<br/>Components"] -->|unified layout| A J["Database<br/>Migrations"] -->|stores| B J -->|stores| C J -->|stores| D ``` <details><summary><h3>File Walkthrough</h3></summary> <table><thead><tr><th></th><th align="left">Relevant files</th></tr></thead><tbody><tr><td><strong>Tests</strong></td><td><details><summary>4 files</summary><table> <tr> <td> <details> <summary><strong>config.rs</strong><dd><code>Update config tests for new passthrough rule ordering</code>&nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> cmd/consumers/zen/src/config.rs <ul><li>Updated test assertions to reflect new rule ordering with <code>passthrough</code> <br>rule added as first rule<br> <li> Modified expected rule indices for <code>strip_full_message</code> and <code>cef_severity</code> <br>in syslog decision group<br> <li> Added assertion for new <code>snmp_severity</code> rule in snmp decision group<br> <li> Adjusted rule indices to account for the new passthrough rule <br>insertion</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-05038f3867985e757de9027609950e682bad6d1992dac6acd7c28962a3c65dc4">+5/-3</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>rules_live_test.exs</strong><dd><code>Rule builder UI integration tests</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web-ng/test/serviceradar_web_ng_web/live/settings/rules_live_test.exs <ul><li>New test module for rule builder UI covering Zen rules, promotion <br>rules, and stateful alert rules<br> <li> Tests rule creation, deletion, and template management workflows<br> <li> Validates form submission and rule persistence across different rule <br>types</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-dddd0d747bd725738a10e74770d765ed98fb22cdd5cd702bcbe98cc47c5af5d0">+112/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>zen_rule_test.exs</strong><dd><code>Zen rule integration tests</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> elixir/serviceradar_core/test/serviceradar/observability/zen_rule_test.exs <ul><li>New integration test module for Zen rule creation, compilation, and KV <br>sync<br> <li> Tests rule compilation with format derivation from subject<br> <li> Validates rule name and subject constraints<br> <li> Tests KV key generation for sync operations</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-14bb1777291e55cff103d04a1f4a8953d49121a5ebab7788f2a7049cbb7cd327">+111/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>settings_test.exs</strong><dd><code>User settings test path updates</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web-ng/test/serviceradar_web_ng_web/live/user_live/settings_test.exs <ul><li>Updated all test paths from <code>/users/settings</code> to <code>/settings/profile</code> to <br>match new routing<br> <li> Tests remain functionally equivalent, validating email and password <br>update workflows</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-0e5e263ff38092c20e33cd3b654639aedb7fd029a058aa3e9a519e4a8b9de1c2">+7/-7</a>&nbsp; &nbsp; &nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Enhancement</strong></td><td><details><summary>24 files</summary><table> <tr> <td> <details> <summary><strong>index.ex</strong><dd><code>New rules management LiveView with three-tier pipeline UI</code></dd></summary> <hr> web-ng/lib/serviceradar_web_ng_web/live/settings/rules_live/index.ex <ul><li>New 2097-line LiveView module for managing log normalization, event <br>promotion, and alert rules<br> <li> Implements three-tab interface for Logs, Events, and Alerts with CRUD <br>operations for rules and templates<br> <li> Provides form handling with validation, template application, and <br>preset management via modal dialogs<br> <li> Includes comprehensive helper functions for form building, parameter <br>normalization, and data querying</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-ce489a06ca328b705897a7b71749c6519594920a192aa0d033944a046d743ef0">+2097/-0</a></td> </tr> <tr> <td> <details> <summary><strong>index.ex</strong><dd><code>Refactor cluster status with caching and tenant filtering</code></dd></summary> <hr> web-ng/lib/serviceradar_web_ng_web/live/settings/cluster_live/index.ex <ul><li>Refactored cluster status page to use caching mechanism for gateways <br>and agents with stale detection<br> <li> Added platform admin visibility checks and tenant-based filtering for <br>agents<br> <li> Implemented RPC-based data fetching from cluster nodes with timeout <br>handling and concurrent processing<br> <li> Added new event handlers for gateway and agent status updates with <br>cache management<br> <li> Replaced inline tables with reusable <code>gateways_table</code> and <code>agents_table</code> <br>components<br> <li> Added job counts display and improved timestamp parsing with multiple <br>format support</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-644e64ac49eaa128f9b429c8507d6a8a9ad820e2796721dd665caba8a154d24e">+634/-159</a></td> </tr> <tr> <td> <details> <summary><strong>zen_rule_templates.ex</strong><dd><code>New Zen rule templates module with decision models</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> elixir/serviceradar_core/lib/serviceradar/observability/zen_rule_templates.ex <ul><li>New module providing built-in Zen rule templates as compiled JSON <br>decision models<br> <li> Defines four template types: <code>passthrough</code>, <code>strip_full_message</code>, <br><code>snmp_severity</code>, and <code>cef_severity</code><br> <li> Implements <code>compile/2</code> function to return appropriate template JSON <br>based on template type<br> <li> Templates include node and edge definitions for visual rule builder <br>representation</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-8fc8e215658c6367a462810e2a162185822d75281d1264cc1f067e8632eaf088">+110/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>index.ex</strong><dd><code>NATS operator and tenant account management UI integration</code></dd></summary> <hr> web-ng/lib/serviceradar_web_ng_web/live/admin/edge_sites_live/index.ex <ul><li>Refactored to use <code>SettingsComponents</code> instead of <code>AdminComponents</code> for <br>unified settings navigation<br> <li> Added NATS operator and tenant account management UI with status cards <br>and filtering<br> <li> Implemented <code>load_operator_status()</code> and <code>load_tenant_accounts()</code> <br>functions to fetch NATS infrastructure data<br> <li> Added event handlers for NATS tenant filtering and reprovisioning with <br>error handling<br> <li> Created new component functions <code>operator_status_card()</code>, <br><code>system_account_card()</code>, and <code>nats_status_badge()</code> for NATS status display</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-8b6f7b4c958acfb5ba26524427695d750cf7cf441e45ae449413c03b4a166fb1">+345/-9</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>template_seeder.ex</strong><dd><code>Default rule template seeding for tenant onboarding</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> elixir/serviceradar_core/lib/serviceradar/observability/template_seeder.ex <ul><li>New GenServer module that seeds default rule templates for each tenant <br>on startup<br> <li> Implements template seeding for <code>LogPromotionRuleTemplate</code>, <br><code>StatefulAlertRuleTemplate</code>, and <code>ZenRuleTemplate</code><br> <li> Handles legacy template name migrations (e.g., <code>syslog_passthrough</code> to <br><code>passthrough</code>)<br> <li> Provides default templates for syslog, SNMP, OTEL logs, and internal <br>logs with passthrough, severity mapping, and message stripping</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-23e36dfd159d88c93b9115b3a2879574bbf840e8177043375926287f7b06be0b">+349/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>zen_rule_seeder.ex</strong><dd><code>Default Zen rule seeding for tenant onboarding</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> elixir/serviceradar_core/lib/serviceradar/observability/zen_rule_seeder.ex <ul><li>New GenServer module that seeds default Zen rules for each tenant on <br>startup<br> <li> Implements rule seeding with legacy name migration support<br> <li> Provides default Zen rules for multiple log subjects (syslog, SNMP, <br>OTEL, internal)<br> <li> Ensures every tenant has baseline passthrough rules and severity <br>mapping rules</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-020128ea25bd10b783725e19d2de73d039c5bfb116a80f13702617bf278e6801">+278/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>settings.ex</strong><dd><code>User settings page layout and navigation restructuring</code>&nbsp; &nbsp; &nbsp; </dd></summary> <hr> web-ng/lib/serviceradar_web_ng_web/live/user_live/settings.ex <ul><li>Refactored layout from centered header to unified settings panel <br>structure with <code>ui_panel</code> components<br> <li> Reorganized email and password forms into separate collapsible <br>sections with descriptive headers<br> <li> Updated redirect path from <code>/users/settings</code> to <code>/settings/profile</code> for <br>consistency<br> <li> Improved visual hierarchy and spacing with max-width container</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-f9d2d04eddd93536ff7f531d1936ac5f21e7b9c67ad5ec792f1ff2c4de05ea12">+86/-59</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>zen_rule.ex</strong><dd><code>Zen rule resource with compilation and KV sync</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> elixir/serviceradar_core/lib/serviceradar/observability/zen_rule.ex <ul><li>New Ash resource for tenant-scoped Zen rule definitions with <br>multitenancy support<br> <li> Includes attributes for rule name, subject, template type, builder <br>config, and compiled JDM<br> <li> Implements create/update/destroy actions with <code>CompileZenRule</code> and <br><code>SyncZenRule</code> changes<br> <li> Defines policies for read/write access based on actor role and tenant <br>membership<br> <li> Validates rule names and subjects with custom validation module</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-7b80d236d986643dd043ab34b7b3d324ea1b58b486bc4261bbd7f2373ab38bd1">+186/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>stateful_alert_rule_template.ex</strong><dd><code>Stateful alert rule template resource</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> elixir/serviceradar_core/lib/serviceradar/observability/stateful_alert_rule_template.ex <ul><li>New Ash resource for tenant-scoped stateful alert rule templates<br> <li> Includes attributes for threshold, window/bucket/cooldown/renotify <br>timing, signal type, and match/event/alert payloads<br> <li> Implements create/update/destroy actions with field acceptance lists<br> <li> Defines policies for role-based access control</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-c29ff715b32e958379366530de132a522181b9bf34249ae89e32e3383216f0ab">+184/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>zen_rule_sync.ex</strong><dd><code>Zen rule KV synchronization and reconciliation service</code>&nbsp; &nbsp; &nbsp; </dd></summary> <hr> elixir/serviceradar_core/lib/serviceradar/observability/zen_rule_sync.ex <ul><li>New GenServer module that syncs Zen rules to datasvc KV store and <br>reconciles on startup/interval<br> <li> Implements <code>sync_rule()</code> function to push compiled JDM to KV with <br>revision tracking<br> <li> Provides <code>delete_rule()</code> for removing disabled rules from KV<br> <li> Reconciles all tenant rules every 5 minutes to ensure KV consistency</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-d5f190f1d1692ffe3e1dba929363e6ff1ecbd70ddcb1f6f2240473466fe4ec8a">+126/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>zen_rule_template.ex</strong><dd><code>Zen rule template resource for builder presets</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> elixir/serviceradar_core/lib/serviceradar/observability/zen_rule_template.ex <ul><li>New Ash resource for tenant-scoped Zen rule builder templates<br> <li> Includes attributes for template name, subject, builder config, and <br>template type<br> <li> Implements create/update/destroy actions with validation<br> <li> Defines policies for role-based access control</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-72bc5c66a9419797f77dcb9f8082c9298be07cdfaabe5f949bab677ca79a0500">+149/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>settings_components.ex</strong><dd><code>Unified settings navigation and layout components</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web-ng/lib/serviceradar_web_ng_web/components/settings_components.ex <ul><li>New component module providing unified settings section layout and <br>navigation<br> <li> Implements <code>settings_shell()</code> wrapper component for consistent max-width <br>container<br> <li> Provides <code>settings_nav()</code> and <code>settings_tabs()</code> for main settings <br>navigation (Cluster, Events, Edge Ops, Integrations, Jobs)<br> <li> Provides <code>edge_nav()</code> and <code>edge_tabs()</code> for edge operations sub-navigation <br>(Collectors, Sites & NATS, Onboarding)</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-540f72cd405a3221e6f350afd04e644db83f0a504938a8907e94c417d070601e">+109/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>client.ex</strong><dd><code>DataService client KV revision retrieval</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> elixir/serviceradar_core/lib/serviceradar/data_service/client.ex <ul><li>Added <code>get_with_revision()</code> function to retrieve KV values along with <br>their revision numbers<br> <li> Implements gRPC call to <code>KVService.Stub.get()</code> with revision extraction <br>from response<br> <li> Supports timeout configuration and error handling for not found and <br>RPC errors</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-503e195ad79e05e12d7ad03a675f6e35ffdfc201b8571b0d30a220fe036e03a1">+42/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>log_promotion_rule_template.ex</strong><dd><code>Log promotion rule template resource</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> elixir/serviceradar_core/lib/serviceradar/observability/log_promotion_rule_template.ex <ul><li>New Ash resource for tenant-scoped log promotion rule templates<br> <li> Includes attributes for rule name, priority, match criteria, and event <br>payload<br> <li> Implements create/update/destroy actions with field acceptance lists<br> <li> Defines policies for role-based access control</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-d1a2f573520c45a47d2ab06d7d590bcedd799effe7a6691b65135988d404f30e">+121/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>zen_rule.ex</strong><dd><code>Zen rule validation constraints</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> elixir/serviceradar_core/lib/serviceradar/observability/validations/zen_rule.ex <ul><li>New validation module for Zen rules enforcing name format (lowercase, <br>numbers, hyphens, underscores)<br> <li> Validates subject against allowed list (syslog, SNMP, OTEL, internal <br>logs)<br> <li> Validates format matches subject (JSON for logs, protobuf for OTEL, <br>otel_metrics for metrics)</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-7cff90ec682e32e5c3f1f04882514e95b79694d85369c685b48ae2251941727c">+64/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>compile_zen_rule.ex</strong><dd><code>Zen rule compilation to JDM format</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> elixir/serviceradar_core/lib/serviceradar/observability/changes/compile_zen_rule.ex <ul><li>New change module that compiles Zen rule builder config into GoRules <br>JSON Decision Model<br> <li> Resolves format based on subject (JSON, protobuf, or otel_metrics)<br> <li> Applies compilation during create and update actions</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-8b4899d4648bdce097acf4e2b9fa07d4a616c587148032438c2390e61ff0f018">+57/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>sync_zen_rule.ex</strong><dd><code>Zen rule KV synchronization change</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> elixir/serviceradar_core/lib/serviceradar/observability/changes/sync_zen_rule.ex <ul><li>New change module that syncs Zen rules to datasvc KV after <br>create/update/destroy<br> <li> Handles rule deletion when key fields change (agent_id, stream_name, <br>subject, name)<br> <li> Logs warnings on sync failures without blocking rule persistence<br> <li> Supports context flag to skip sync for testing</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-8df265115a5ecb977c162785fdb0b65accc0393956edd7bf1e97b18a5a45452d">+58/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>index.ex</strong><dd><code>Collector admin page settings navigation integration</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web-ng/lib/serviceradar_web_ng_web/live/admin/collector_live/index.ex <ul><li>Refactored to use <code>SettingsComponents</code> instead of <code>AdminComponents</code><br> <li> Updated layout to use <code>settings_shell()</code> and <code>edge_nav()</code> components for <br>unified navigation</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-a4bff6d93de18fa438c93bbc1dff8344d9886745d6922df3952139b37d01e495">+5/-4</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>zen_rule_template.ex</strong><dd><code>Zen rule template validation constraints</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> elixir/serviceradar_core/lib/serviceradar/observability/validations/zen_rule_template.ex <ul><li>New validation module for Zen rule templates enforcing name and <br>subject constraints<br> <li> Validates name format (lowercase, numbers, hyphens, underscores)<br> <li> Validates subject against allowed list including internal logs</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-b8561a710d835c74a4cb46a84ee6e5eb4c904fb5e09dcf621ffb327234686eac">+47/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>index.ex</strong><dd><code>Cluster admin page settings navigation integration</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web-ng/lib/serviceradar_web_ng_web/live/admin/cluster_live/index.ex <ul><li>Refactored to use <code>SettingsComponents</code> instead of <code>AdminComponents</code><br> <li> Updated layout to use <code>settings_shell()</code> and <code>settings_nav()</code> components <br>for unified navigation</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-40bdfb617c630bd45011f9f540eba42d917a059934c2d0a1cd169434f85c0b29">+4/-4</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>stateful_alert_rule.ex</strong><dd><code>Stateful alert rule code interface and action updates</code>&nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> elixir/serviceradar_core/lib/serviceradar/observability/stateful_alert_rule.ex <ul><li>Added code interface with <code>list</code>, <code>list_active</code>, <code>create</code>, <code>update</code>, <code>destroy</code> <br>functions<br> <li> Added explicit <code>accept</code> lists to create and update actions for field <br>validation<br> <li> Maintains existing validation for window/bucket timing constraints</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-0f89f415054a875a7dd5b104966f4a2b60354b23353ce4c2ea8a9e83dfc6128f">+40/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>show.ex</strong><dd><code>Node show page navigation path updates</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web-ng/lib/serviceradar_web_ng_web/live/node_live/show.ex <ul><li>Updated page path from <code>/infrastructure/nodes</code> to <br><code>/settings/cluster/nodes</code> for SRQL context<br> <li> Updated back button navigation to point to <code>/settings/cluster</code> instead <br>of <code>/infrastructure?tab=nodes</code></ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-f125161c42c603755000c88a13d76e984e2fed8fae141a3ae854951ad9b56f24">+4/-4</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>index.ex</strong><dd><code>Edge package admin page settings navigation integration</code>&nbsp; &nbsp; </dd></summary> <hr> web-ng/lib/serviceradar_web_ng_web/live/admin/edge_package_live/index.ex <ul><li>Refactored to use <code>SettingsComponents</code> instead of <code>AdminComponents</code><br> <li> Updated layout to use <code>settings_shell()</code>, <code>settings_nav()</code>, and <code>edge_nav()</code> <br>components</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-d0d22a987aa942ef25088696daea862e36bd8ca083ce29e4670f63e62947c62f">+5/-4</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>show.ex</strong><dd><code>Edge sites show page settings navigation integration</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web-ng/lib/serviceradar_web_ng_web/live/admin/edge_sites_live/show.ex <ul><li>Refactored to use <code>SettingsComponents</code> instead of <code>AdminComponents</code><br> <li> Updated layout to use <code>settings_shell()</code>, <code>settings_nav()</code>, and <code>edge_nav()</code> <br>components</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-2c15bfed2ddefa02ac6fda1c9ac16dde652f6945015a8d9a15df2b37faeb88cf">+5/-4</a>&nbsp; &nbsp; &nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Configuration changes</strong></td><td><details><summary>13 files</summary><table> <tr> <td> <details> <summary><strong>layouts.ex</strong><dd><code>Update navigation links and sidebar routing</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web-ng/lib/serviceradar_web_ng_web/components/layouts.ex <ul><li>Removed Infrastructure sidebar link navigation<br> <li> Updated Settings sidebar link to point to <code>/settings/cluster</code> instead of <br><code>/admin/jobs</code><br> <li> Modified Settings link active state to include <code>/settings</code>, <code>/admin</code>, and <br><code>/users/settings</code> paths<br> <li> Changed user menu Account link from <code>/users/settings</code> to <br><code>/settings/profile</code></ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-75eaced063e3e7325588594e742492bab789a979319a34f2f623f6d161cfffd2">+9/-12</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>20260111080000_add_rule_templates.exs</strong><dd><code>Database schema for rule template storage</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> elixir/serviceradar_core/priv/repo/tenant_migrations/20260111080000_add_rule_templates.exs <ul><li>Creates three new tenant-scoped tables: <code>zen_rule_templates</code>, <br><code>log_promotion_rule_templates</code>, <code>stateful_alert_rule_templates</code><br> <li> Adds unique indexes on <code>(tenant_id, name)</code> for promotion and stateful <br>templates<br> <li> Includes fields for rule configuration, matching criteria, and <br>event/alert payloads</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-11dba97a728f3dadb979c1cdc7beb8cb4496e3659497c1ef69c6ce7f7f306370">+111/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>application.ex</strong><dd><code>Application startup configuration for rule seeding and sync</code></dd></summary> <hr> elixir/serviceradar_core/lib/serviceradar/application.ex <ul><li>Added <code>TemplateSeeder</code> GenServer to seed default rule templates on <br>startup<br> <li> Added <code>ZenRuleSeeder</code> GenServer to seed default Zen rules on startup<br> <li> Added <code>ZenRuleSync</code> GenServer to reconcile Zen rules with datasvc KV <br>store<br> <li> Conditionally starts seeders and sync based on repo and datasvc <br>availability</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-a9ffbf400b7f9b22cd8980c41286c54fe373f1f1a8684bb6a344a5fb39b178d0">+32/-1</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>20260110070000_add_zen_rules.exs</strong><dd><code>Database schema for Zen rule storage</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> elixir/serviceradar_core/priv/repo/tenant_migrations/20260110070000_add_zen_rules.exs <ul><li>Creates <code>zen_rules</code> table with columns for rule definition, compilation <br>output, and KV metadata<br> <li> Adds unique index on <code>(tenant_id, name)</code> for rule uniqueness per tenant<br> <li> Includes fields for compiled JDM, KV revision, and builder <br>configuration</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-de887ffe288b9bba98a22f523d43fecb924832b2d71f4e45c6ccc5c3d519646f">+48/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>20260111013324_update-zen-rule-identities.exs</strong><dd><code>Zen rule identity constraint updates</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> elixir/serviceradar_core/priv/repo/tenant_migrations/20260111013324_update-zen-rule-identities.exs <ul><li>Updates unique indexes for <code>zen_rules</code> and <code>zen_rule_templates</code> from <br><code>(tenant_id, name)</code> to <code>(tenant_id, subject, name)</code><br> <li> Allows multiple rules with same name across different subjects</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-4dd461b5630af41f667aa8c53deaa59bb35288c3909f9446db5a99fc95785797">+45/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>router.ex</strong><dd><code>Router updates for unified settings navigation</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web-ng/lib/serviceradar_web_ng_web/router.ex <ul><li>Added redirect from <code>/users/settings</code> to <code>/settings/profile</code> for backward <br>compatibility<br> <li> Moved user settings route from <code>/users/settings</code> to <code>/settings/profile</code><br> <li> Added <code>/settings/cluster/nodes/:node_name</code> route for node details<br> <li> Added <code>/settings/rules</code> route for rule builder UI<br> <li> Replaced <code>/infrastructure</code> routes with redirects to <code>/settings/cluster</code> <br>paths</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-df516cd33165cd85914c1ccb3ff6511d3fe688d4a66498b99807958998c5d07c">+6/-5</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>20260111013324.json</strong><dd><code>Add stateful alert rule templates resource snapshot</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> elixir/serviceradar_core/priv/resource_snapshots/repo/tenants/stateful_alert_rule_templates/20260111013324.json <ul><li>New resource snapshot file for <code>stateful_alert_rule_templates</code> table <br>schema<br> <li> Defines 18 attributes including id, name, description, enabled, <br>priority, match/event maps, and tenant_id<br> <li> Establishes unique constraint on <code>tenant_id</code> and <code>name</code> combination<br> <li> Includes multitenancy configuration with context strategy</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-ab8f150672f76e1a58463a7390a80d6275aa64289ed36adf92b42ee018780806">+257/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>20260111013324.json</strong><dd><code>Add zen rules resource snapshot</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> elixir/serviceradar_core/priv/resource_snapshots/repo/tenants/zen_rules/20260111013324.json <ul><li>New resource snapshot file for <code>zen_rules</code> table schema<br> <li> Defines 16 attributes including id, name, description, enabled, order, <br>stream_name, subject, format, template, builder_config, compiled_jdm, <br>and tenant_id<br> <li> Establishes unique constraint on <code>tenant_id</code>, <code>subject</code>, and <code>name</code> <br>combination<br> <li> Includes multitenancy configuration with context strategy</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-b62778ed52d456273a885cb4e9fd0f501c944d71ee957d8c732ef1f97dc9296c">+234/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>20260111013324.json</strong><dd><code>Add zen rule templates resource snapshot</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> elixir/serviceradar_core/priv/resource_snapshots/repo/tenants/zen_rule_templates/20260111013324.json <ul><li>New resource snapshot file for <code>zen_rule_templates</code> table schema<br> <li> Defines 12 attributes including id, name, description, enabled, order, <br>stream_name, subject, template, builder_config, and tenant_id<br> <li> Establishes unique constraint on <code>tenant_id</code>, <code>subject</code>, and <code>name</code> <br>combination<br> <li> Includes multitenancy configuration with context strategy</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-69c9c4e82ca0f8231ff1f60fe8e33d99a72a99714ffa197b9aec2be60867dad1">+198/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>20260111013324.json</strong><dd><code>Add log promotion rule templates resource snapshot</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> elixir/serviceradar_core/priv/resource_snapshots/repo/tenants/log_promotion_rule_templates/20260111013324.json <ul><li>New resource snapshot file for <code>log_promotion_rule_templates</code> table <br>schema<br> <li> Defines 10 attributes including id, name, description, enabled, <br>priority, match/event maps, and tenant_id<br> <li> Establishes unique constraint on <code>tenant_id</code> and <code>name</code> combination<br> <li> Includes multitenancy configuration with context strategy</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-2a2fd74222111d00c1482aa470b6d9a7ec39d2c86298d858f9a87d260e96bf30">+158/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>zen-consumer-with-otel.json</strong><dd><code>Add passthrough rules to zen consumer configuration</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> cmd/consumers/zen/zen-consumer-with-otel.json <ul><li>Added <code>passthrough</code> rule with order 1 to syslog subject rules, shifting <br>existing rules to orders 2 and 3<br> <li> Added <code>passthrough</code> rule with order 1 to snmp subject rules and changed <br>second rule from <code>cef_severity</code> to <code>snmp_severity</code> with order 2</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-68375f1f7847e1fbdf75664f6be65b1ad94ae6ce86ed73fc5964d65054668acb">+5/-3</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>zen-consumer.json</strong><dd><code>Add passthrough rules to zen consumer configuration</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> cmd/consumers/zen/zen-consumer.json <ul><li>Added <code>passthrough</code> rule with order 1 to syslog subject rules, shifting <br>existing rules to orders 2 and 3<br> <li> Added <code>passthrough</code> rule with order 1 to snmp subject rules and changed <br>second rule from <code>cef_severity</code> to <code>snmp_severity</code> with order 2</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-4d308af9802a93a0f656e8c02a3b5fcd8991407bb18360f087470db74e1f9524">+5/-3</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>zen-consumer.json</strong><dd><code>Add passthrough rules to zen consumer configuration</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> packaging/zen/config/zen-consumer.json <ul><li>Added <code>passthrough</code> rule with order 1 to syslog subject rules, shifting <br>existing rules to orders 2 and 3<br> <li> Added <code>passthrough</code> rule with order 1 to snmp subject rules and changed <br>second rule from <code>cef_severity</code> to <code>snmp_severity</code> with order 2</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-8ff888af95eb2b14a58d16783a8d9509bb9f3e43e50a13b179aee8fcf26da3e0">+5/-3</a>&nbsp; &nbsp; &nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Dependencies</strong></td><td><details><summary>2 files</summary><table> <tr> <td> <details> <summary><strong>BUILD.bazel</strong><dd><code>Add new Go source files to build target</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> pkg/consumers/db-event-writer/BUILD.bazel <ul><li>Added two new source files to the Go library build target: <br><code>json_logs.go</code> and <code>ocsf_events.go</code><br> <li> Maintains existing source files while extending functionality</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-4dd70a678d0945ddb0b7c403ef5bf868c44c683e17bd75f607174a8f4d68eaad">+2/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>BUILD.bazel</strong><dd><code>Update log crate dependency version</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> cmd/checkers/rperf-server/BUILD.bazel - Updated `log` crate dependency from version 0.4.28 to 0.4.29 </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-7da152990199fd73c1eecb40f9c49e0d4e6453a8ec1acb111e445c55d1ca0af0">+1/-1</a>&nbsp; &nbsp; &nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Bug fix</strong></td><td><details><summary>1 files</summary><table> <tr> <td> <details> <summary><strong>show.ex</strong><dd><code>Alert show page inline value rendering fix</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web-ng/lib/serviceradar_web_ng_web/live/alert_live/show.ex <ul><li>Fixed inline value rendering by pre-assigning <code>value_text</code> to avoid <br>direct function calls in templates<br> <li> Applies fix to boolean, number, and generic value rendering functions</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-f26d33ba8e6bcd2325ae9f1d078284dc260c3709d017c962cfc7596ce9f22b34">+10/-4</a>&nbsp; &nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Documentation</strong></td><td><details><summary>9 files</summary><table> <tr> <td> <details> <summary><strong>tasks.md</strong><dd><code>Mark rule builder UI implementation tasks complete</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> openspec/changes/add-rule-builder-ui/tasks.md <ul><li>Marked 10 tasks as completed in section 1 (Data Model & Sync) <br>including resource creation, migrations, KV sync, and template seeding<br> <li> Marked 4 tasks as completed in section 2 (API Layer) including CRUD <br>actions and template endpoints<br> <li> Marked 9 tasks as completed in section 3 (Web UI) including settings <br>layout, rule builders, and template libraries<br> <li> Marked 4 tasks as completed in section 4 (Docs & Examples) and 3 tasks <br>in section 5 (Tests)</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-2b9b7cd9e8e11456b142a70fd9bf3ddbda741651d2bd04bfe29fbadda3a76491">+30/-18</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>design.md</strong><dd><code>Add Zen JDM editor design specification</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> openspec/changes/add-zen-jdm-editor/design.md <ul><li>New design document for Zen rule editor with JDM canvas and JSON view<br> <li> Outlines goals to provide canvas + JSON editor for actual JDM stored <br>per rule<br> <li> Documents decision to use <code>phoenix_react_server</code> for React embedding and <br>store JDM definition as first-class JSON field<br> <li> Describes rule library concept, UI layout with two-pane screen, and <br>migration plan for existing template-based rules</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-42fa2772aef551acb72380ca7917b92d5ec240e919858b09ea18a173d0e637fe">+38/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>design.md</strong><dd><code>Update rule builder UI design with template libraries</code>&nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> openspec/changes/add-rule-builder-ui/design.md <ul><li>Updated rule naming in UI from "Event Notifications & Response Rules" <br>to "Events"<br> <li> Changed settings layout from left navigation to tab-based navigation <br>in main content area<br> <li> Added template libraries concept with tenant-scoped template resources <br>seeded with defaults<br> <li> Added tenant onboarding defaults section describing baseline Zen rule <br>seeding<br> <li> Expanded migration plan to include template tables and tenant defaults</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-3453f87b5b30a7bb2488b05cd738bde87badebe9349a10781cd9dacca498f55c">+9/-4</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>spec.md</strong><dd><code>Add template library and default rules requirements</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> openspec/changes/add-rule-builder-ui/specs/observability-rule-management/spec.md <ul><li>Added requirement for default Zen rules and reconciliation during <br>tenant onboarding<br> <li> Added requirement for default passthrough rules and templates for each <br>supported Zen subject<br> <li> Added requirement for tenant-scoped template libraries with CRUD <br>operations<br> <li> Added requirement for template-based rule creation allowing operators <br>to select templates when creating/editing rules</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-0425c71bdb1109690175b0dc76ebbcecc6d304fd7edace81e2681d47bedd0140">+49/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>rule-builder.md</strong><dd><code>Add rule builder user documentation</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> docs/docs/rule-builder.md <ul><li>New documentation file for Rule Builder feature<br> <li> Describes location at Settings → Events and explains log normalization <br>(Zen) rules workflow<br> <li> Documents template system, KV sync behavior, and response rules <br>(promotion and stateful alerts)<br> <li> Includes tips for rule creation and references to template usage</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-1880a0a3516f9c3112647741737eb04c98ea7633ceccb3300563737f6cff566a">+67/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>tasks.md</strong><dd><code>Add Zen JDM editor implementation tasks</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> openspec/changes/add-zen-jdm-editor/tasks.md <ul><li>New tasks file for Zen JDM editor implementation<br> <li> Defines 4 sections: Data Model + Sync, React Editor Integration, Zen <br>Rule Editor UX, and Tests + Docs<br> <li> Lists 20 specific tasks including adding <code>jdm_definition</code> attribute, <br>backfilling definitions, React integration, and LiveView tests</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-665a7daaf771b8cb33000d7309c7d6e0f0b2e0015d7a94cb0137d0edee14ce0a">+20/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>syslog.md</strong><dd><code>Update syslog documentation with rule builder reference</code>&nbsp; &nbsp; </dd></summary> <hr> docs/docs/syslog.md <ul><li>Updated reference from future Web UI rule builder to current Rule <br>Builder UI<br> <li> Added "Settings → Events" navigation path for managing Zen <br>normalization rules for syslog<br> <li> Changed link to reference the new rule-builder.md documentation guide</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-8efe898141f64a74b80133318b56ed95d03703ac95b47cebc95978b7ef761a4a">+2/-1</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>proposal.md</strong><dd><code>Add Zen JDM editor change proposal</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> openspec/changes/add-zen-jdm-editor/proposal.md <ul><li>New proposal document for Zen rule editor with JDM canvas and JSON <br>view<br> <li> Explains motivation to replace preset modal UX with first-class rule <br>editor<br> <li> Describes embedding GoRules JDM editor via <code>phoenix_react_server</code> and <br>storing JDM definitions in CNPG<br> <li> Lists affected specs, code areas, and impact on web-ng, core-elx, and <br>documentation</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-488e2851accb2e58184230431940f2d1bf068ccd5a8bb130c647e2848f411369">+16/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>snmp.md</strong><dd><code>Update SNMP documentation with rule builder reference</code>&nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> docs/docs/snmp.md <ul><li>Updated reference from future Web UI rule builder to current Rule <br>Builder UI<br> <li> Added "Settings → Events" navigation path for managing Zen <br>normalization rules for SNMP traps<br> <li> Changed link to reference the new rule-builder.md documentation guide</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-5d2a72888517a4c731287280253a29cedbcbe595f1ce57c2fa14aef37c62595a">+2/-1</a>&nbsp; &nbsp; &nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Additional files</strong></td><td><details><summary>12 files</summary><table> <tr> <td><strong>initialize_tenant_infrastructure.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-a9b0e6035eab1514edae7dc2f9bf0744176688cb167e1cc293d6687c72e88086">+7/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>observability.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-68a559f44d649113add0a3f4b9ea787685890928dfbff6af084e4f049b8fc76e">+4/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>log_promotion_rule.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-83f9c69192f3f00691adfd18645ab89257b30c651fd71f852d1146522b9ff18d">+17/-1</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>stateful_alert_rule_history.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-bdf937d7808f711496f21f7968afca16ec09b6ee9114ebaa893068cf6f7452f0">+11/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>proposal.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-782eb58f38fabde5fd14e1338e9ace33d88625c748f845a50d9f4f354adb44e1">+5/-1</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>page_controller.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-191be2756e89e5e7533d99c008d252d053347ae5b9585cb1a74acec36ceefb4e">+12/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>user_session_controller.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-7d5f49c78692d273eea38a1d061d178f4b95a6ff2b6a878d70660d0cec1343fe">+1/-1</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>index.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-61d0262af13a42905ebbd793e83537e61bfc09493df1664a82eb2536980ee1cd">+4/-4</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>index.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-b275777f111009bbb4976d57623231aae2262452ef320a9f91ecbf202144115a">+4/-4</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>show.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-0c20d7a3f2cb9951bd3e2b28420c590f81a3b83be380cf4e8e8fe8ee7e663951">+4/-4</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>index.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-ff336831554c0d0fe48b38caa1cc8d072531859f084c6f4d1638aa349c45adb8">+5/-4</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>show.ex</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-edfe9c01c00ca5b91b34f99d4014882e5d031de0500be8cf632a297a4e7441cb">+2/-2</a>&nbsp; &nbsp; &nbsp; </td> </tr> </table></details></td></tr></tbody></table> </details> ___
qodo-code-review[bot] commented 2026-01-11 04:04:16 +00:00 (Migrated from github.com)
Author
Owner

Imported GitHub PR comment.

Original author: @qodo-code-review[bot]
Original URL: https://github.com/carverauto/serviceradar/pull/2242#issuecomment-3733973215
Original created: 2026-01-11T04:04:16Z

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
Authorization bypass

Description: The new admin LiveView code performs multiple Ash reads with authorize?: false (e.g.,
operator and tenant account provisioning status) and allows enqueuing CreateAccountWorker
based on a user-supplied tenant_id, which could enable unauthorized data access or
privileged actions if route/LiveView access controls are misconfigured.
index.ex [645-733]

Referred Code
defp load_operator_status(socket) do
  operator =
    case NatsOperator
         |> Ash.Query.for_read(:get_current)
         |> Ash.Query.limit(1)
         |> Ash.read_one(authorize?: false) do
      {:ok, operator} -> operator
      {:error, _} -> nil
    end

  assign(socket, :operator, operator)
end

defp load_tenant_accounts(socket) do
  filter_status = socket.assigns[:nats_filter_status]

  query =
    Tenant
    |> Ash.Query.for_read(:for_nats_provisioning)
    |> Ash.Query.select([
      :id,



 ... (clipped 68 lines)
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Unsafe atom conversion: User-controlled status is converted via String.to_existing_atom/1 without guarding, which
can raise and crash the LiveView when an unexpected value is submitted.

Referred Code
query =
  if filter_status do
    status_atom = String.to_existing_atom(filter_status)
    Ash.Query.filter(query, nats_account_status == ^status_atom)
  else
    query
  end

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status:
Internal error exposed: The UI renders @operator.error_message directly, which may expose internal operational
details to the end-user instead of a generic user-facing error.

Referred Code
<%= if @operator.error_message do %>
  <div class="alert alert-error text-xs">
    <.icon name="hero-exclamation-triangle" class="size-4" />
    {@operator.error_message}
  </div>
<% end %>

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
Missing audit logging: The new rule/template CRUD LiveView actions (create/update/delete) do not emit audit logs
with user/tenant context, so it is unclear whether these critical settings changes are
traceable.

Referred Code
def handle_event("save_zen", %{"zen_rule" => params}, socket) do
  scope = socket.assigns.current_scope

  case AshPhoenix.Form.submit(socket.assigns.zen_ash_form, params: params) do
    {:ok, _rule} ->
      {:noreply,
       socket
       |> assign(:zen_rules, list_zen_rules(scope))
       |> reset_zen_form(scope)}

    {:error, ash_form} ->
      {:noreply,
       assign(socket, :zen_ash_form, ash_form) |> assign(:zen_form, to_form(ash_form))}
  end
end

def handle_event("edit_zen", %{"id" => id}, socket) do
  scope = socket.assigns.current_scope

  id = to_string(id)




 ... (clipped 475 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status:
Logging not shown: The new administrative actions (e.g., reprovisioning) and error paths do not show any
structured logging in the diff, so it cannot be verified that logs are both present and
free of sensitive data.

Referred Code
def handle_event("reprovision_nats", %{"id" => tenant_id}, socket) do
  case reprovision_tenant(tenant_id) do
    {:ok, _} ->
      {:noreply,
       socket
       |> load_tenant_accounts()
       |> put_flash(:info, "Reprovisioning job enqueued")}

    {:error, :not_found} ->
      {:noreply, put_flash(socket, :error, "Tenant not found")}

    {:error, :not_retriable} ->
      {:noreply, put_flash(socket, :error, "Tenant is not in a retriable state")}

    {:error, _reason} ->
      {:noreply, put_flash(socket, :error, "Failed to enqueue reprovisioning")}
  end

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Authorization bypass risk: Several reads are performed with authorize?: false (e.g., operator status and tenant
provisioning lists), which may bypass authorization unless this LiveView is otherwise
access-controlled outside the diff.

Referred Code
defp load_operator_status(socket) do
  operator =
    case NatsOperator
         |> Ash.Query.for_read(:get_current)
         |> Ash.Query.limit(1)
         |> Ash.read_one(authorize?: false) do
      {:ok, operator} -> operator
      {:error, _} -> nil
    end

  assign(socket, :operator, operator)
end

defp load_tenant_accounts(socket) do
  filter_status = socket.assigns[:nats_filter_status]

  query =
    Tenant
    |> Ash.Query.for_read(:for_nats_provisioning)
    |> Ash.Query.select([
      :id,



 ... (clipped 25 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
- Requires Further Human Verification
🏷️ - Compliance label
Imported GitHub PR comment. Original author: @qodo-code-review[bot] Original URL: https://github.com/carverauto/serviceradar/pull/2242#issuecomment-3733973215 Original created: 2026-01-11T04:04:16Z --- ## PR Compliance Guide 🔍 <!-- https://github.com/carverauto/serviceradar/commit/05827bbe9e4d84d915e76257528317b36e026b37 --> Below is a summary of compliance checks for this PR:<br> <table><tbody><tr><td colspan='2'><strong>Security Compliance</strong></td></tr> <tr><td rowspan=1>⚪</td> <td><details><summary><strong>Authorization bypass </strong></summary><br> <b>Description:</b> The new admin LiveView code performs multiple Ash reads with <code>authorize?: false</code> (e.g., <br>operator and tenant account provisioning status) and allows enqueuing <code>CreateAccountWorker</code> <br>based on a user-supplied <code>tenant_id</code>, which could enable unauthorized data access or <br>privileged actions if route/LiveView access controls are misconfigured.<br> <strong><a href='https://github.com/carverauto/serviceradar/pull/2242/files#diff-8b6f7b4c958acfb5ba26524427695d750cf7cf441e45ae449413c03b4a166fb1R645-R733'>index.ex [645-733]</a></strong><br> <details open><summary>Referred Code</summary> ```elixir defp load_operator_status(socket) do operator = case NatsOperator |> Ash.Query.for_read(:get_current) |> Ash.Query.limit(1) |> Ash.read_one(authorize?: false) do {:ok, operator} -> operator {:error, _} -> nil end assign(socket, :operator, operator) end defp load_tenant_accounts(socket) do filter_status = socket.assigns[:nats_filter_status] query = Tenant |> Ash.Query.for_read(:for_nats_provisioning) |> Ash.Query.select([ :id, ... (clipped 68 lines) ``` </details></details></td></tr> <tr><td colspan='2'><strong>Ticket Compliance</strong></td></tr> <tr><td>⚪</td><td><details><summary>🎫 <strong>No ticket provided </strong></summary> - [ ] Create ticket/issue <!-- /create_ticket --create_ticket=true --> </details></td></tr> <tr><td colspan='2'><strong>Codebase Duplication Compliance</strong></td></tr> <tr><td>⚪</td><td><details><summary><strong>Codebase context is not defined </strong></summary> Follow the <a href='https://qodo-merge-docs.qodo.ai/core-abilities/rag_context_enrichment/'>guide</a> to enable codebase context checks. </details></td></tr> <tr><td colspan='2'><strong>Custom Compliance</strong></td></tr> <tr><td rowspan=1>🟢</td><td> <details><summary><strong>Generic: Meaningful Naming and Self-Documenting Code</strong></summary><br> **Objective:** Ensure all identifiers clearly express their purpose and intent, making code <br>self-documenting<br> **Status:** Passed<br> > Learn more about managing compliance <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#configuration-options'>generic rules</a> or creating your own <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#custom-compliance'>custom rules</a> </details></td></tr> <tr><td rowspan=2>🔴</td> <td><details> <summary><strong>Generic: Robust Error Handling and Edge Case Management</strong></summary><br> **Objective:** Ensure comprehensive error handling that provides meaningful context and graceful <br>degradation<br> **Status:** <br><a href='https://github.com/carverauto/serviceradar/pull/2242/files#diff-8b6f7b4c958acfb5ba26524427695d750cf7cf441e45ae449413c03b4a166fb1R675-R681'><strong>Unsafe atom conversion</strong></a>: User-controlled <code>status</code> is converted via <code>String.to_existing_atom/1</code> without guarding, which <br>can raise and crash the LiveView when an unexpected value is submitted.<br> <details open><summary>Referred Code</summary> ```elixir query = if filter_status do status_atom = String.to_existing_atom(filter_status) Ash.Query.filter(query, nats_account_status == ^status_atom) else query end ``` </details> > Learn more about managing compliance <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#configuration-options'>generic rules</a> or creating your own <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#custom-compliance'>custom rules</a> </details></td></tr> <tr><td><details> <summary><strong>Generic: Secure Error Handling</strong></summary><br> **Objective:** To prevent the leakage of sensitive system information through error messages while <br>providing sufficient detail for internal debugging.<br> **Status:** <br><a href='https://github.com/carverauto/serviceradar/pull/2242/files#diff-8b6f7b4c958acfb5ba26524427695d750cf7cf441e45ae449413c03b4a166fb1R402-R407'><strong>Internal error exposed</strong></a>: The UI renders <code>@operator.error_message</code> directly, which may expose internal operational <br>details to the end-user instead of a generic user-facing error.<br> <details open><summary>Referred Code</summary> ```elixir <%= if @operator.error_message do %> <div class="alert alert-error text-xs"> <.icon name="hero-exclamation-triangle" class="size-4" /> {@operator.error_message} </div> <% end %> ``` </details> > Learn more about managing compliance <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#configuration-options'>generic rules</a> or creating your own <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#custom-compliance'>custom rules</a> </details></td></tr> <tr><td rowspan=3>⚪</td> <td><details> <summary><strong>Generic: Comprehensive Audit Trails</strong></summary><br> **Objective:** To create a detailed and reliable record of critical system actions for security analysis <br>and compliance.<br> **Status:** <br><a href='https://github.com/carverauto/serviceradar/pull/2242/files#diff-ce489a06ca328b705897a7b71749c6519594920a192aa0d033944a046d743ef0R111-R606'><strong>Missing audit logging</strong></a>: The new rule/template CRUD LiveView actions (create/update/delete) do not emit audit logs <br>with user/tenant context, so it is unclear whether these critical settings changes are <br>traceable.<br> <details open><summary>Referred Code</summary> ```elixir def handle_event("save_zen", %{"zen_rule" => params}, socket) do scope = socket.assigns.current_scope case AshPhoenix.Form.submit(socket.assigns.zen_ash_form, params: params) do {:ok, _rule} -> {:noreply, socket |> assign(:zen_rules, list_zen_rules(scope)) |> reset_zen_form(scope)} {:error, ash_form} -> {:noreply, assign(socket, :zen_ash_form, ash_form) |> assign(:zen_form, to_form(ash_form))} end end def handle_event("edit_zen", %{"id" => id}, socket) do scope = socket.assigns.current_scope id = to_string(id) ... (clipped 475 lines) ``` </details> > Learn more about managing compliance <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#configuration-options'>generic rules</a> or creating your own <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#custom-compliance'>custom rules</a> </details></td></tr> <tr><td><details> <summary><strong>Generic: Secure Logging Practices</strong></summary><br> **Objective:** To ensure logs are useful for debugging and auditing without exposing sensitive <br>information like PII, PHI, or cardholder data.<br> **Status:** <br><a href='https://github.com/carverauto/serviceradar/pull/2242/files#diff-8b6f7b4c958acfb5ba26524427695d750cf7cf441e45ae449413c03b4a166fb1R98-R114'><strong>Logging not shown</strong></a>: The new administrative actions (e.g., reprovisioning) and error paths do not show any <br>structured logging in the diff, so it cannot be verified that logs are both present and <br>free of sensitive data.<br> <details open><summary>Referred Code</summary> ```elixir def handle_event("reprovision_nats", %{"id" => tenant_id}, socket) do case reprovision_tenant(tenant_id) do {:ok, _} -> {:noreply, socket |> load_tenant_accounts() |> put_flash(:info, "Reprovisioning job enqueued")} {:error, :not_found} -> {:noreply, put_flash(socket, :error, "Tenant not found")} {:error, :not_retriable} -> {:noreply, put_flash(socket, :error, "Tenant is not in a retriable state")} {:error, _reason} -> {:noreply, put_flash(socket, :error, "Failed to enqueue reprovisioning")} end ``` </details> > Learn more about managing compliance <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#configuration-options'>generic rules</a> or creating your own <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#custom-compliance'>custom rules</a> </details></td></tr> <tr><td><details> <summary><strong>Generic: Security-First Input Validation and Data Handling</strong></summary><br> **Objective:** Ensure all data inputs are validated, sanitized, and handled securely to prevent <br>vulnerabilities<br> **Status:** <br><a href='https://github.com/carverauto/serviceradar/pull/2242/files#diff-8b6f7b4c958acfb5ba26524427695d750cf7cf441e45ae449413c03b4a166fb1R645-R690'><strong>Authorization bypass risk</strong></a>: Several reads are performed with <code>authorize?: false</code> (e.g., operator status and tenant <br>provisioning lists), which may bypass authorization unless this LiveView is otherwise <br>access-controlled outside the diff.<br> <details open><summary>Referred Code</summary> ```elixir defp load_operator_status(socket) do operator = case NatsOperator |> Ash.Query.for_read(:get_current) |> Ash.Query.limit(1) |> Ash.read_one(authorize?: false) do {:ok, operator} -> operator {:error, _} -> nil end assign(socket, :operator, operator) end defp load_tenant_accounts(socket) do filter_status = socket.assigns[:nats_filter_status] query = Tenant |> Ash.Query.for_read(:for_nats_provisioning) |> Ash.Query.select([ :id, ... (clipped 25 lines) ``` </details> > Learn more about managing compliance <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#configuration-options'>generic rules</a> or creating your own <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#custom-compliance'>custom rules</a> </details></td></tr> <tr><td align="center" colspan="2"> <!-- placeholder --> <!-- /compliance --update_compliance=true --> </td></tr></tbody></table> <details><summary>Compliance status legend</summary> 🟢 - Fully Compliant<br> 🟡 - Partial Compliant<br> 🔴 - Not Compliant<br> ⚪ - Requires Further Human Verification<br> 🏷️ - Compliance label<br> </details>
qodo-code-review[bot] commented 2026-01-11 04:05:41 +00:00 (Migrated from github.com)
Author
Owner

Imported GitHub PR comment.

Original author: @qodo-code-review[bot]
Original URL: https://github.com/carverauto/serviceradar/pull/2242#issuecomment-3733973992
Original created: 2026-01-11T04:05:41Z

PR Code Suggestions

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Re-evaluate the template-based rule abstraction

The current template-based abstraction for Zen rules should be re-evaluated. A
design document included in the PR already proposes replacing this with a direct
JDM editor, so it would be more efficient to implement the final solution now
instead of building a temporary UI.

Examples:

web-ng/lib/serviceradar_web_ng_web/live/settings/rules_live/index.ex [1-2097]
openspec/changes/add-zen-jdm-editor/design.md [1-38]
## Context
Zen rules are currently built via template + builder_config and a modal-driven UI. Users cannot see or edit the rule JSON (JDM) that powers GoRules, and “presets” are a separate editor that doesn’t represent the real rule. We want a direct editor for Zen rules with canvas + JSON, embedded in Phoenix LiveView via React.

## Goals / Non-Goals
- Goals:
  - Provide a canvas + JSON editor for Zen rules that edits the actual JDM stored per rule.
  - Allow users to create new rules and new rule types (library items) from scratch.
  - Keep rules tenant-scoped and respect RBAC (operator/admin write access).
  - Preserve existing default rules and migrate them into editable JDM definitions.
- Non-Goals:

 ... (clipped 28 lines)

Solution Walkthrough:

Before:

# elixir/serviceradar_core/lib/serviceradar/observability/zen_rule.ex
defmodule ServiceRadar.Observability.ZenRule do
  use Ash.Resource
  # ...
  attributes do
    # ...
    attribute :template, :atom do
      constraints one_of: [:passthrough, :strip_full_message, ...]
    end
    attribute :builder_config, :map
    attribute :compiled_jdm, :map
  end

  actions do
    create :create do
      change ServiceRadar.Observability.Changes.CompileZenRule
      # ...
    end
  end
end

After:

# elixir/serviceradar_core/lib/serviceradar/observability/zen_rule.ex
defmodule ServiceRadar.Observability.ZenRule do
  use Ash.Resource
  # ...
  attributes do
    # ...
    # The :template and :builder_config attributes are removed
    # in favor of a direct JDM definition.
    attribute :jdm_definition, :map do
      allow_nil? false
    end
    # compiled_jdm might be kept for the synced payload or removed.
    attribute :compiled_jdm, :map
  end

  actions do
    create :create do
      # The CompileZenRule change is replaced with direct validation
      # and processing of the jdm_definition.
      change ServiceRadar.Observability.Changes.ProcessJdmDefinition
      # ...
    end
  end
end

Suggestion importance[1-10]: 10

__

Why: The suggestion correctly identifies a major strategic issue where the PR implements a complex, template-based UI that is already planned for replacement by a direct JDM editor, as documented in a new design file (add-zen-jdm-editor/design.md) within this same PR, thus preventing significant throwaway work.

High
Possible issue
Fix unreliable timestamp normalization logic

Replace the brittle, magnitude-based timestamp normalization in
parse_timestamp_to_ms with a more robust heuristic to avoid misinterpreting
timestamp units and prevent bugs in staleness detection.

web-ng/lib/serviceradar_web_ng_web/live/settings/cluster_live/index.ex [915-932]

 defp parse_timestamp_to_ms(ts) when is_integer(ts) do
   cond do
     ts < 0 ->
       nil
 
-    ts > 10_000_000_000_000_000 ->
-      div(ts, 1_000_000)
+    true ->
+      # This is a heuristic. A better solution is to have consistent units from the source.
+      # We guess the unit based on the order of magnitude compared to current time.
+      now_ns = System.system_time(:nanosecond)
+      now_ms = div(now_ns, 1_000_000)
 
-    ts > 10_000_000_000_000 ->
-      div(ts, 1_000)
-
-    ts > 10_000_000_000 ->
-      ts
-
-    true ->
-      ts * 1_000
+      cond do
+        # Assuming nanoseconds if it's much larger than current ms
+        ts > now_ms * 1000 -> div(ts, 1_000_000)
+        # Assuming microseconds
+        ts > now_ms * 10 -> div(ts, 1_000)
+        # Assuming seconds if it's much smaller
+        ts < div(now_ms, 10) -> ts * 1_000
+        # Assume milliseconds
+        true -> ts
+      end
   end
 end

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a significant bug in the timestamp normalization logic, which uses fragile magic numbers and can lead to incorrect staleness detection for cluster components.

High
Refresh agents_cache on manual refresh

Update the agents_cache in the handle_event("refresh", ...) handler to ensure
the agent list is refreshed along with the gateway list when the user triggers a
manual refresh.

web-ng/lib/serviceradar_web_ng_web/live/settings/cluster_live/index.ex [262-289]

 def handle_event("refresh", _params, socket) do
   cluster_status = load_cluster_status()
+
+  # Refresh both caches
   refreshed_gateways_cache =
     merge_gateways_cache(socket.assigns.gateways_cache, load_initial_gateways_cache())
+  refreshed_agents_cache =
+    merge_agents_cache(socket.assigns.agents_cache, load_initial_agents_cache())
+
   gateways = compute_gateways(refreshed_gateways_cache)
   agents =
     compute_connected_agents(
-      socket.assigns.agents_cache,
+      refreshed_agents_cache,
       socket.assigns.is_platform_admin,
       socket.assigns.tenant_info
     )
   cluster_health = build_cluster_health(gateways, agents)
   job_counts = load_job_counts(socket.assigns.current_scope)
 
   {:noreply,
    socket
    |> assign(:cluster_status, cluster_status)
    |> assign(:cluster_health, cluster_health)
    |> assign(:gateways_cache, refreshed_gateways_cache)
+   |> assign(:agents_cache, refreshed_agents_cache)
    |> assign(:gateways, gateways)
    |> assign(:agents, agents)
    |> assign(:job_counts, job_counts)
    |> assign(:oban_stats, load_oban_stats())}
 end

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a bug where the manual refresh button does not update the agent list, leading to a stale UI display for agents.

Medium
Handle seeder function return values

Chain the seeder function calls using a with statement to handle potential
failures and prevent creating a tenant in an inconsistent state.

elixir/serviceradar_core/lib/serviceradar/identity/changes/initialize_tenant_infrastructure.ex [97-103]

-TemplateSeeder.seed_for_tenant(tenant)
-Logger.debug("Seeded rule templates for tenant: #{tenant_slug}")
+with {:ok, _} <- TemplateSeeder.seed_for_tenant(tenant),
+     {:ok, _} <- ZenRuleSeeder.seed_for_tenant(tenant) do
+  Logger.debug("Seeded rule templates and Zen rules for tenant: #{tenant_slug}")
+  {:ok, tenant}
+else
+  {:error, reason} ->
+    Logger.error("Failed to seed rules for tenant #{tenant_slug}: #{inspect(reason)}")
+    {:error, {:rule_seeding_failed, reason}}
+end
 
-ZenRuleSeeder.seed_for_tenant(tenant)
-Logger.debug("Seeded Zen rules for tenant: #{tenant_slug}")
-
-{:ok, tenant}
-
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies that failing seeder functions would lead to an inconsistent tenant state, and proposes an idiomatic with block to handle errors and ensure atomicity.

Medium
Fetch records directly to prevent race conditions

Refactor edit_* event handlers to fetch records directly from the database by
ID, preventing potential race conditions and ensuring users edit the most
up-to-date data.

web-ng/lib/serviceradar_web_ng_web/live/settings/rules_live/index.ex [127-145]

 def handle_event("edit_zen", %{"id" => id}, socket) do
   scope = socket.assigns.current_scope
 
-  id = to_string(id)
-
-  case Enum.find(socket.assigns.zen_rules, &(to_string(&1.id) == id)) do
-    nil ->
-      {:noreply, socket}
-
-    rule ->
+  case ZenRule.get_by_id(id, scope: scope) do
+    {:ok, rule} ->
       ash_form = build_zen_form(scope, rule)
 
       {:noreply,
        socket
        |> assign(:editing_zen_id, rule.id)
        |> assign(:zen_ash_form, ash_form)
        |> assign(:zen_form, to_form(ash_form))}
+
+    {:error, _} ->
+      # Optionally, put a flash message informing the user the rule was not found
+      {:noreply, socket}
   end
 end

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies a potential race condition where editing is based on stale data from socket.assigns, and proposes fetching the latest data to ensure consistency.

Medium
Fetch records before deletion for data consistency

Refactor delete_* event handlers to fetch records directly from the database by
ID before deletion to ensure atomicity and prevent errors from stale data.

web-ng/lib/serviceradar_web_ng_web/live/settings/rules_live/index.ex [151-169]

 def handle_event("delete_zen", %{"id" => id}, socket) do
   scope = socket.assigns.current_scope
 
-  id = to_string(id)
-
-  case Enum.find(socket.assigns.zen_rules, &(to_string(&1.id) == id)) do
-    nil ->
-      {:noreply, socket}
-
-    rule ->
+  case ZenRule.get_by_id(id, scope: scope) do
+    {:ok, rule} ->
       case Ash.destroy(rule, scope: scope) do
         :ok ->
           {:noreply, assign(socket, :zen_rules, list_zen_rules(scope))}
 
         {:error, _} ->
           {:noreply, put_flash(socket, :error, "Failed to delete rule")}
       end
+
+    {:error, _} ->
+      # The rule is already gone, so we can just refresh the list.
+      {:noreply, assign(socket, :zen_rules, list_zen_rules(scope))}
   end
 end

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly points out that deleting based on a stale list from socket.assigns can lead to errors, and recommends fetching the record by ID before deletion for atomicity.

Medium
Return a consistent {:ok, _} tuple

Modify reprovision_tenant to ensure it always returns an {:ok, _} or {:error, _}
tuple by including the result of
ServiceRadar.NATS.Workers.CreateAccountWorker.enqueue/1 in the with statement.

web-ng/lib/serviceradar_web_ng_web/live/admin/edge_sites_live/index.ex [709-714]

 defp reprovision_tenant(tenant_id) do
   with {:ok, tenant} <- get_tenant(tenant_id),
-       :ok <- validate_retriable(tenant) do
-    ServiceRadar.NATS.Workers.CreateAccountWorker.enqueue(tenant_id)
+       :ok <- validate_retriable(tenant),
+       {:ok, job} <- ServiceRadar.NATS.Workers.CreateAccountWorker.enqueue(tenant_id) do
+    {:ok, job}
   end
 end

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly points out that the with block might not return a tuple, which could cause a crash in the calling case statement, and proposes a valid fix.

Medium
Security
Prevent DoS with safe atom conversion

Replace the unsafe String.to_existing_atom/1 with a case statement to safely
convert user-provided filter status strings to atoms, preventing a potential DoS
vulnerability.

web-ng/lib/serviceradar_web_ng_web/live/admin/edge_sites_live/index.ex [658-690]

 defp load_tenant_accounts(socket) do
   filter_status = socket.assigns[:nats_filter_status]
 
   query =
     Tenant
     |> Ash.Query.for_read(:for_nats_provisioning)
     |> Ash.Query.select([
       :id,
       :slug,
       :name,
       :nats_account_status,
       :nats_account_public_key,
       :nats_account_provisioned_at
     ])
     |> Ash.Query.sort(inserted_at: :desc)
     |> Ash.Query.limit(100)
 
   query =
-    if filter_status do
-      status_atom = String.to_existing_atom(filter_status)
-      Ash.Query.filter(query, nats_account_status == ^status_atom)
-    else
-      query
+    case filter_status do
+      "pending" -> Ash.Query.filter(query, nats_account_status == :pending)
+      "ready" -> Ash.Query.filter(query, nats_account_status == :ready)
+      "error" -> Ash.Query.filter(query, nats_account_status == :error)
+      _ -> query
     end
 
   tenants =
     case Ash.read(query, authorize?: false) do
       {:ok, tenants} -> tenants
       {:error, _} -> []
     end
 
   assign(socket, :tenants, tenants)
 end

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a security vulnerability (DoS) from using String.to_existing_atom/1 on user input and provides a robust fix using pattern matching.

High
General
Use assign_new for template assigns

Replace assign/3 with assign_new/3 to avoid overwriting existing assigns and use
a multiline ~H sigil for better readability.

web-ng/lib/serviceradar_web_ng_web/live/alert_live/show.ex [192-196]

 defp inline_value(%{value: value} = assigns) when is_boolean(value) do
-  assigns = assign(assigns, :value_text, to_string(value))
+  assigns = assign_new(assigns, :value_text, fn -> to_string(value) end)
 
-  ~H|<span class="text-sm font-mono">{@value_text}</span>|
+  ~H"""
+  <span class="text-sm font-mono">{@value_text}</span>
+  """
 end

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 2

__

Why: The suggestion to use assign_new is valid for preventing accidental overwrites, but in this specific context where value_text is derived directly from value, assign is sufficient and clear. The change offers a minor improvement in robustness.

Low
  • More
Imported GitHub PR comment. Original author: @qodo-code-review[bot] Original URL: https://github.com/carverauto/serviceradar/pull/2242#issuecomment-3733973992 Original created: 2026-01-11T04:05:41Z --- ## PR Code Suggestions ✨ <!-- 05827bb --> Explore these optional code suggestions: <table><thead><tr><td><strong>Category</strong></td><td align=left><strong>Suggestion&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </strong></td><td align=center><strong>Impact</strong></td></tr><tbody><tr><td rowspan=1>High-level</td> <td> <details><summary>Re-evaluate the template-based rule abstraction</summary> ___ **The current template-based abstraction for Zen rules should be re-evaluated. A <br>design document included in the PR already proposes replacing this with a direct <br>JDM editor, so it would be more efficient to implement the final solution now <br>instead of building a temporary UI.** ### Examples: <details> <summary> <a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-ce489a06ca328b705897a7b71749c6519594920a192aa0d033944a046d743ef0R1-R2097">web-ng/lib/serviceradar_web_ng_web/live/settings/rules_live/index.ex [1-2097]</a> </summary></details> <details> <summary> <a href="https://github.com/carverauto/serviceradar/pull/2242/files#diff-42fa2772aef551acb72380ca7917b92d5ec240e919858b09ea18a173d0e637feR1-R38">openspec/changes/add-zen-jdm-editor/design.md [1-38]</a> </summary> ```markdown ## Context Zen rules are currently built via template + builder_config and a modal-driven UI. Users cannot see or edit the rule JSON (JDM) that powers GoRules, and “presets” are a separate editor that doesn’t represent the real rule. We want a direct editor for Zen rules with canvas + JSON, embedded in Phoenix LiveView via React. ## Goals / Non-Goals - Goals: - Provide a canvas + JSON editor for Zen rules that edits the actual JDM stored per rule. - Allow users to create new rules and new rule types (library items) from scratch. - Keep rules tenant-scoped and respect RBAC (operator/admin write access). - Preserve existing default rules and migrate them into editable JDM definitions. - Non-Goals: ... (clipped 28 lines) ``` </details> ### Solution Walkthrough: #### Before: ```markdown # elixir/serviceradar_core/lib/serviceradar/observability/zen_rule.ex defmodule ServiceRadar.Observability.ZenRule do use Ash.Resource # ... attributes do # ... attribute :template, :atom do constraints one_of: [:passthrough, :strip_full_message, ...] end attribute :builder_config, :map attribute :compiled_jdm, :map end actions do create :create do change ServiceRadar.Observability.Changes.CompileZenRule # ... end end end ``` #### After: ```markdown # elixir/serviceradar_core/lib/serviceradar/observability/zen_rule.ex defmodule ServiceRadar.Observability.ZenRule do use Ash.Resource # ... attributes do # ... # The :template and :builder_config attributes are removed # in favor of a direct JDM definition. attribute :jdm_definition, :map do allow_nil? false end # compiled_jdm might be kept for the synced payload or removed. attribute :compiled_jdm, :map end actions do create :create do # The CompileZenRule change is replaced with direct validation # and processing of the jdm_definition. change ServiceRadar.Observability.Changes.ProcessJdmDefinition # ... end end end ``` <details><summary>Suggestion importance[1-10]: 10</summary> __ Why: The suggestion correctly identifies a major strategic issue where the PR implements a complex, template-based UI that is already planned for replacement by a direct JDM editor, as documented in a new design file (`add-zen-jdm-editor/design.md`) within this same PR, thus preventing significant throwaway work. </details></details></td><td align=center>High </td></tr><tr><td rowspan=6>Possible issue</td> <td> <details><summary>Fix unreliable timestamp normalization logic</summary> ___ **Replace the brittle, magnitude-based timestamp normalization in <br><code>parse_timestamp_to_ms</code> with a more robust heuristic to avoid misinterpreting <br>timestamp units and prevent bugs in staleness detection.** [web-ng/lib/serviceradar_web_ng_web/live/settings/cluster_live/index.ex [915-932]](https://github.com/carverauto/serviceradar/pull/2242/files#diff-644e64ac49eaa128f9b429c8507d6a8a9ad820e2796721dd665caba8a154d24eR915-R932) ```diff defp parse_timestamp_to_ms(ts) when is_integer(ts) do cond do ts < 0 -> nil - ts > 10_000_000_000_000_000 -> - div(ts, 1_000_000) + true -> + # This is a heuristic. A better solution is to have consistent units from the source. + # We guess the unit based on the order of magnitude compared to current time. + now_ns = System.system_time(:nanosecond) + now_ms = div(now_ns, 1_000_000) - ts > 10_000_000_000_000 -> - div(ts, 1_000) - - ts > 10_000_000_000 -> - ts - - true -> - ts * 1_000 + cond do + # Assuming nanoseconds if it's much larger than current ms + ts > now_ms * 1000 -> div(ts, 1_000_000) + # Assuming microseconds + ts > now_ms * 10 -> div(ts, 1_000) + # Assuming seconds if it's much smaller + ts < div(now_ms, 10) -> ts * 1_000 + # Assume milliseconds + true -> ts + end end end ``` `[To ensure code accuracy, apply this suggestion manually]` <details><summary>Suggestion importance[1-10]: 9</summary> __ Why: The suggestion correctly identifies a significant bug in the timestamp normalization logic, which uses fragile magic numbers and can lead to incorrect staleness detection for cluster components. </details></details></td><td align=center>High </td></tr><tr><td> <details><summary>Refresh agents_cache on manual refresh</summary> ___ **Update the <code>agents_cache</code> in the <code>handle_event("refresh", ...)</code> handler to ensure <br>the agent list is refreshed along with the gateway list when the user triggers a <br>manual refresh.** [web-ng/lib/serviceradar_web_ng_web/live/settings/cluster_live/index.ex [262-289]](https://github.com/carverauto/serviceradar/pull/2242/files#diff-644e64ac49eaa128f9b429c8507d6a8a9ad820e2796721dd665caba8a154d24eR262-R289) ```diff def handle_event("refresh", _params, socket) do cluster_status = load_cluster_status() + + # Refresh both caches refreshed_gateways_cache = merge_gateways_cache(socket.assigns.gateways_cache, load_initial_gateways_cache()) + refreshed_agents_cache = + merge_agents_cache(socket.assigns.agents_cache, load_initial_agents_cache()) + gateways = compute_gateways(refreshed_gateways_cache) agents = compute_connected_agents( - socket.assigns.agents_cache, + refreshed_agents_cache, socket.assigns.is_platform_admin, socket.assigns.tenant_info ) cluster_health = build_cluster_health(gateways, agents) job_counts = load_job_counts(socket.assigns.current_scope) {:noreply, socket |> assign(:cluster_status, cluster_status) |> assign(:cluster_health, cluster_health) |> assign(:gateways_cache, refreshed_gateways_cache) + |> assign(:agents_cache, refreshed_agents_cache) |> assign(:gateways, gateways) |> assign(:agents, agents) |> assign(:job_counts, job_counts) |> assign(:oban_stats, load_oban_stats())} end ``` `[To ensure code accuracy, apply this suggestion manually]` <details><summary>Suggestion importance[1-10]: 8</summary> __ Why: The suggestion correctly identifies a bug where the manual refresh button does not update the agent list, leading to a stale UI display for agents. </details></details></td><td align=center>Medium </td></tr><tr><td> <details><summary>Handle seeder function return values</summary> ___ **Chain the seeder function calls using a <code>with</code> statement to handle potential <br>failures and prevent creating a tenant in an inconsistent state.** [elixir/serviceradar_core/lib/serviceradar/identity/changes/initialize_tenant_infrastructure.ex [97-103]](https://github.com/carverauto/serviceradar/pull/2242/files#diff-a9b0e6035eab1514edae7dc2f9bf0744176688cb167e1cc293d6687c72e88086R97-R103) ```diff -TemplateSeeder.seed_for_tenant(tenant) -Logger.debug("Seeded rule templates for tenant: #{tenant_slug}") +with {:ok, _} <- TemplateSeeder.seed_for_tenant(tenant), + {:ok, _} <- ZenRuleSeeder.seed_for_tenant(tenant) do + Logger.debug("Seeded rule templates and Zen rules for tenant: #{tenant_slug}") + {:ok, tenant} +else + {:error, reason} -> + Logger.error("Failed to seed rules for tenant #{tenant_slug}: #{inspect(reason)}") + {:error, {:rule_seeding_failed, reason}} +end -ZenRuleSeeder.seed_for_tenant(tenant) -Logger.debug("Seeded Zen rules for tenant: #{tenant_slug}") - -{:ok, tenant} - ``` - [ ] **Apply / Chat** <!-- /improve --apply_suggestion=3 --> <details><summary>Suggestion importance[1-10]: 8</summary> __ Why: The suggestion correctly identifies that failing seeder functions would lead to an inconsistent tenant state, and proposes an idiomatic `with` block to handle errors and ensure atomicity. </details></details></td><td align=center>Medium </td></tr><tr><td> <details><summary>Fetch records directly to prevent race conditions</summary> ___ **Refactor <code>edit_*</code> event handlers to fetch records directly from the database by <br>ID, preventing potential race conditions and ensuring users edit the most <br>up-to-date data.** [web-ng/lib/serviceradar_web_ng_web/live/settings/rules_live/index.ex [127-145]](https://github.com/carverauto/serviceradar/pull/2242/files#diff-ce489a06ca328b705897a7b71749c6519594920a192aa0d033944a046d743ef0R127-R145) ```diff def handle_event("edit_zen", %{"id" => id}, socket) do scope = socket.assigns.current_scope - id = to_string(id) - - case Enum.find(socket.assigns.zen_rules, &(to_string(&1.id) == id)) do - nil -> - {:noreply, socket} - - rule -> + case ZenRule.get_by_id(id, scope: scope) do + {:ok, rule} -> ash_form = build_zen_form(scope, rule) {:noreply, socket |> assign(:editing_zen_id, rule.id) |> assign(:zen_ash_form, ash_form) |> assign(:zen_form, to_form(ash_form))} + + {:error, _} -> + # Optionally, put a flash message informing the user the rule was not found + {:noreply, socket} end end ``` `[To ensure code accuracy, apply this suggestion manually]` <details><summary>Suggestion importance[1-10]: 7</summary> __ Why: The suggestion correctly identifies a potential race condition where editing is based on stale data from `socket.assigns`, and proposes fetching the latest data to ensure consistency. </details></details></td><td align=center>Medium </td></tr><tr><td> <details><summary>Fetch records before deletion for data consistency</summary> ___ **Refactor <code>delete_*</code> event handlers to fetch records directly from the database by <br>ID before deletion to ensure atomicity and prevent errors from stale data.** [web-ng/lib/serviceradar_web_ng_web/live/settings/rules_live/index.ex [151-169]](https://github.com/carverauto/serviceradar/pull/2242/files#diff-ce489a06ca328b705897a7b71749c6519594920a192aa0d033944a046d743ef0R151-R169) ```diff def handle_event("delete_zen", %{"id" => id}, socket) do scope = socket.assigns.current_scope - id = to_string(id) - - case Enum.find(socket.assigns.zen_rules, &(to_string(&1.id) == id)) do - nil -> - {:noreply, socket} - - rule -> + case ZenRule.get_by_id(id, scope: scope) do + {:ok, rule} -> case Ash.destroy(rule, scope: scope) do :ok -> {:noreply, assign(socket, :zen_rules, list_zen_rules(scope))} {:error, _} -> {:noreply, put_flash(socket, :error, "Failed to delete rule")} end + + {:error, _} -> + # The rule is already gone, so we can just refresh the list. + {:noreply, assign(socket, :zen_rules, list_zen_rules(scope))} end end ``` `[To ensure code accuracy, apply this suggestion manually]` <details><summary>Suggestion importance[1-10]: 7</summary> __ Why: The suggestion correctly points out that deleting based on a stale list from `socket.assigns` can lead to errors, and recommends fetching the record by ID before deletion for atomicity. </details></details></td><td align=center>Medium </td></tr><tr><td> <details><summary>Return a consistent {:ok, _} tuple</summary> ___ **Modify <code>reprovision_tenant</code> to ensure it always returns an <code>{:ok, _}</code> or <code>{:error, _}</code> <br>tuple by including the result of <br><code>ServiceRadar.NATS.Workers.CreateAccountWorker.enqueue/1</code> in the <code>with</code> statement.** [web-ng/lib/serviceradar_web_ng_web/live/admin/edge_sites_live/index.ex [709-714]](https://github.com/carverauto/serviceradar/pull/2242/files#diff-8b6f7b4c958acfb5ba26524427695d750cf7cf441e45ae449413c03b4a166fb1R709-R714) ```diff defp reprovision_tenant(tenant_id) do with {:ok, tenant} <- get_tenant(tenant_id), - :ok <- validate_retriable(tenant) do - ServiceRadar.NATS.Workers.CreateAccountWorker.enqueue(tenant_id) + :ok <- validate_retriable(tenant), + {:ok, job} <- ServiceRadar.NATS.Workers.CreateAccountWorker.enqueue(tenant_id) do + {:ok, job} end end ``` `[To ensure code accuracy, apply this suggestion manually]` <details><summary>Suggestion importance[1-10]: 7</summary> __ Why: The suggestion correctly points out that the `with` block might not return a tuple, which could cause a crash in the calling `case` statement, and proposes a valid fix. </details></details></td><td align=center>Medium </td></tr><tr><td rowspan=1>Security</td> <td> <details><summary>Prevent DoS with safe atom conversion</summary> ___ **Replace the unsafe <code>String.to_existing_atom/1</code> with a <code>case</code> statement to safely <br>convert user-provided filter status strings to atoms, preventing a potential DoS <br>vulnerability.** [web-ng/lib/serviceradar_web_ng_web/live/admin/edge_sites_live/index.ex [658-690]](https://github.com/carverauto/serviceradar/pull/2242/files#diff-8b6f7b4c958acfb5ba26524427695d750cf7cf441e45ae449413c03b4a166fb1R658-R690) ```diff defp load_tenant_accounts(socket) do filter_status = socket.assigns[:nats_filter_status] query = Tenant |> Ash.Query.for_read(:for_nats_provisioning) |> Ash.Query.select([ :id, :slug, :name, :nats_account_status, :nats_account_public_key, :nats_account_provisioned_at ]) |> Ash.Query.sort(inserted_at: :desc) |> Ash.Query.limit(100) query = - if filter_status do - status_atom = String.to_existing_atom(filter_status) - Ash.Query.filter(query, nats_account_status == ^status_atom) - else - query + case filter_status do + "pending" -> Ash.Query.filter(query, nats_account_status == :pending) + "ready" -> Ash.Query.filter(query, nats_account_status == :ready) + "error" -> Ash.Query.filter(query, nats_account_status == :error) + _ -> query end tenants = case Ash.read(query, authorize?: false) do {:ok, tenants} -> tenants {:error, _} -> [] end assign(socket, :tenants, tenants) end ``` `[To ensure code accuracy, apply this suggestion manually]` <details><summary>Suggestion importance[1-10]: 9</summary> __ Why: The suggestion correctly identifies a security vulnerability (DoS) from using `String.to_existing_atom/1` on user input and provides a robust fix using pattern matching. </details></details></td><td align=center>High </td></tr><tr><td rowspan=1>General</td> <td> <details><summary>Use assign_new for template assigns</summary> ___ **Replace <code>assign/3</code> with <code>assign_new/3</code> to avoid overwriting existing assigns and use <br>a multiline <code>~H</code> sigil for better readability.** [web-ng/lib/serviceradar_web_ng_web/live/alert_live/show.ex [192-196]](https://github.com/carverauto/serviceradar/pull/2242/files#diff-f26d33ba8e6bcd2325ae9f1d078284dc260c3709d017c962cfc7596ce9f22b34R192-R196) ```diff defp inline_value(%{value: value} = assigns) when is_boolean(value) do - assigns = assign(assigns, :value_text, to_string(value)) + assigns = assign_new(assigns, :value_text, fn -> to_string(value) end) - ~H|<span class="text-sm font-mono">{@value_text}</span>| + ~H""" + <span class="text-sm font-mono">{@value_text}</span> + """ end ``` `[To ensure code accuracy, apply this suggestion manually]` <details><summary>Suggestion importance[1-10]: 2</summary> __ Why: The suggestion to use `assign_new` is valid for preventing accidental overwrites, but in this specific context where `value_text` is derived directly from `value`, `assign` is sufficient and clear. The change offers a minor improvement in robustness. </details></details></td><td align=center>Low </td></tr> <tr><td align="center" colspan="2"> - [ ] More <!-- /improve --more_suggestions=true --> </td><td></td></tr></tbody></table>
qodo-code-review[bot] commented 2026-01-11 04:06:01 +00:00 (Migrated from github.com)
Author
Owner

Imported GitHub PR comment.

Original author: @qodo-code-review[bot]
Original URL: https://github.com/carverauto/serviceradar/pull/2242#issuecomment-3733974193
Original created: 2026-01-11T04:06:01Z

CI Feedback 🧐

A test triggered by this PR failed. Here is an AI-generated analysis of the failure:

Action: test-rust (rust/srql)

Failed stage: Run Tests []

Failed test name: srql_api_queries

Failure summary:

The action failed because the Rust integration test srql_api_queries failed while trying to connect
to the remote PostgreSQL fixture.
- The test panicked in rust/srql/tests/support/harness.rs:74:10
with: failed to acquire remote fixture lock: db error.
- The underlying DB error shows the
connection was rejected by Postgres pg_hba.conf: FATAL: pg_hba.conf rejects connection for host
"10.42.68.65", user "srql_hydra", database "postgres", no encryption.
- This indicates the CI
runner’s IP (or connection settings) is not allowed by the fixture’s pg_hba.conf, and/or the server
requires SSL but the client attempted an unencrypted connection (no encryption).

Relevant error logs:
1:  Runner name: 'arc-runner-set-hk6mk-runner-lhp92'
2:  Runner group name: 'Default'
...

126:  ^[[36;1mif command -v apt-get >/dev/null 2>&1; then^[[0m
127:  ^[[36;1m  sudo apt-get update^[[0m
128:  ^[[36;1m  sudo apt-get install -y build-essential pkg-config libssl-dev protobuf-compiler cmake flex bison^[[0m
129:  ^[[36;1melif command -v dnf >/dev/null 2>&1; then^[[0m
130:  ^[[36;1m  sudo dnf install -y gcc gcc-c++ make openssl-devel protobuf-compiler cmake flex bison^[[0m
131:  ^[[36;1melif command -v yum >/dev/null 2>&1; then^[[0m
132:  ^[[36;1m  sudo yum install -y gcc gcc-c++ make openssl-devel protobuf-compiler cmake flex bison^[[0m
133:  ^[[36;1melif command -v microdnf >/dev/null 2>&1; then^[[0m
134:  ^[[36;1m  sudo microdnf install -y gcc gcc-c++ make openssl-devel protobuf-compiler cmake flex bison^[[0m
135:  ^[[36;1melse^[[0m
136:  ^[[36;1m  echo "Unsupported package manager; please install gcc, g++ (or clang), make, OpenSSL headers, pkg-config, and protoc manually." >&2^[[0m
137:  ^[[36;1m  exit 1^[[0m
138:  ^[[36;1mfi^[[0m
139:  ^[[36;1m^[[0m
140:  ^[[36;1mensure_pkg_config^[[0m
141:  ^[[36;1mprotoc --version || (echo "protoc installation failed" && exit 1)^[[0m
142:  shell: /usr/bin/bash -e {0}
...

181:  libprotoc 3.12.4
182:  ##[group]Run bazelbuild/setup-bazelisk@v3
183:  with:
184:  bazelisk-version: 1.x
185:  token: ***
186:  env:
187:  BUILDBUDDY_ORG_API_KEY: ***
188:  SRQL_TEST_DATABASE_URL: ***
189:  SRQL_TEST_ADMIN_URL: ***
190:  ##[endgroup]
191:  Attempting to download 1.x...
192:  Acquiring v1.27.0 from https://github.com/bazelbuild/bazelisk/releases/download/v1.27.0/bazelisk-linux-amd64
193:  Adding to the cache ...
194:  Successfully cached bazelisk to /home/runner/_work/_tool/bazelisk/1.27.0/x64
195:  Added bazelisk to the path
196:  ##[warning]Failed to restore: Cache service responded with 400
197:  Restored bazelisk cache dir @ /home/runner/.cache/bazelisk
...

253:  targets: 
254:  components: clippy
255:  ##[endgroup]
256:  ##[group]Run : set $CARGO_HOME
257:  ^[[36;1m: set $CARGO_HOME^[[0m
258:  ^[[36;1mecho CARGO_HOME=${CARGO_HOME:-"$HOME/.cargo"} >> $GITHUB_ENV^[[0m
259:  shell: /usr/bin/bash --noprofile --norc -e -o pipefail {0}
260:  env:
261:  BUILDBUDDY_ORG_API_KEY: ***
262:  SRQL_TEST_DATABASE_URL: ***
263:  SRQL_TEST_ADMIN_URL: ***
264:  ##[endgroup]
265:  ##[group]Run : install rustup if needed
266:  ^[[36;1m: install rustup if needed^[[0m
267:  ^[[36;1mif ! command -v rustup &>/dev/null; then^[[0m
268:  ^[[36;1m  curl --proto '=https' --tlsv1.2 --retry 10 --retry-connrefused --location --silent --show-error --fail https://sh.rustup.rs | sh -s -- --default-toolchain none -y^[[0m
269:  ^[[36;1m  echo "$CARGO_HOME/bin" >> $GITHUB_PATH^[[0m
...

369:  ^[[36;1m    echo CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse >> $GITHUB_ENV^[[0m
370:  ^[[36;1m  elif rustc +stable --version --verbose | grep -q '^release: 1\.6[67]\.'; then^[[0m
371:  ^[[36;1m    touch "/home/runner/_work/_temp"/.implicit_cargo_registries_crates_io_protocol || true^[[0m
372:  ^[[36;1m    echo CARGO_REGISTRIES_CRATES_IO_PROTOCOL=git >> $GITHUB_ENV^[[0m
373:  ^[[36;1m  fi^[[0m
374:  ^[[36;1mfi^[[0m
375:  shell: /usr/bin/bash --noprofile --norc -e -o pipefail {0}
376:  env:
377:  BUILDBUDDY_ORG_API_KEY: ***
378:  SRQL_TEST_DATABASE_URL: ***
379:  SRQL_TEST_ADMIN_URL: ***
380:  CARGO_HOME: /home/runner/.cargo
381:  CARGO_INCREMENTAL: 0
382:  CARGO_TERM_COLOR: always
383:  ##[endgroup]
384:  ##[group]Run : work around spurious network errors in curl 8.0
385:  ^[[36;1m: work around spurious network errors in curl 8.0^[[0m
386:  ^[[36;1m# https://rust-lang.zulipchat.com/#narrow/stream/246057-t-cargo/topic/timeout.20investigation^[[0m
...

581:  ^[[1m^[[92m  Downloaded^[[0m tonic-build v0.14.2
582:  ^[[1m^[[92m  Downloaded^[[0m smallvec v1.15.1
583:  ^[[1m^[[92m  Downloaded^[[0m ryu v1.0.22
584:  ^[[1m^[[92m  Downloaded^[[0m chrono v0.4.42
585:  ^[[1m^[[92m  Downloaded^[[0m want v0.3.1
586:  ^[[1m^[[92m  Downloaded^[[0m utf8_iter v1.0.4
587:  ^[[1m^[[92m  Downloaded^[[0m tinystr v0.8.2
588:  ^[[1m^[[92m  Downloaded^[[0m linux-raw-sys v0.11.0
589:  ^[[1m^[[92m  Downloaded^[[0m shlex v1.3.0
590:  ^[[1m^[[92m  Downloaded^[[0m rustls v0.21.12
591:  ^[[1m^[[92m  Downloaded^[[0m serde_urlencoded v0.7.1
592:  ^[[1m^[[92m  Downloaded^[[0m rustls-webpki v0.101.7
593:  ^[[1m^[[92m  Downloaded^[[0m tower-service v0.3.3
594:  ^[[1m^[[92m  Downloaded^[[0m time-macros v0.2.24
595:  ^[[1m^[[92m  Downloaded^[[0m thread_local v1.1.9
596:  ^[[1m^[[92m  Downloaded^[[0m thiserror v1.0.69
597:  ^[[1m^[[92m  Downloaded^[[0m serde_path_to_error v0.1.20
598:  ^[[1m^[[92m  Downloaded^[[0m openssl-sys v0.9.111
599:  ^[[1m^[[92m  Downloaded^[[0m rustls-pki-types v1.13.2
600:  ^[[1m^[[92m  Downloaded^[[0m num-bigint v0.4.6
601:  ^[[1m^[[92m  Downloaded^[[0m thiserror-impl v2.0.17
602:  ^[[1m^[[92m  Downloaded^[[0m thiserror-impl v1.0.69
603:  ^[[1m^[[92m  Downloaded^[[0m thiserror v2.0.17
604:  ^[[1m^[[92m  Downloaded^[[0m rand_core v0.6.4
...

770:  ^[[1m^[[92m   Compiling^[[0m parking_lot_core v0.9.12
771:  ^[[1m^[[92m    Checking^[[0m log v0.4.29
772:  ^[[1m^[[92m    Checking^[[0m scopeguard v1.2.0
773:  ^[[1m^[[92m   Compiling^[[0m serde_core v1.0.228
774:  ^[[1m^[[92m    Checking^[[0m futures-sink v0.3.31
775:  ^[[1m^[[92m    Checking^[[0m once_cell v1.21.3
776:  ^[[1m^[[92m   Compiling^[[0m autocfg v1.5.0
777:  ^[[1m^[[92m    Checking^[[0m memchr v2.7.6
778:  ^[[1m^[[92m    Checking^[[0m pin-utils v0.1.0
779:  ^[[1m^[[92m   Compiling^[[0m fnv v1.0.7
780:  ^[[1m^[[92m    Checking^[[0m futures-task v0.3.31
781:  ^[[1m^[[92m   Compiling^[[0m regex-syntax v0.8.8
782:  ^[[1m^[[92m   Compiling^[[0m serde v1.0.228
783:  ^[[1m^[[92m    Checking^[[0m hashbrown v0.16.1
784:  ^[[1m^[[92m    Checking^[[0m equivalent v1.0.2
785:  ^[[1m^[[92m   Compiling^[[0m thiserror v2.0.17
786:  ^[[1m^[[92m    Checking^[[0m stable_deref_trait v1.2.1
...

885:  ^[[1m^[[92m    Checking^[[0m hashbrown v0.12.3
886:  ^[[1m^[[92m   Compiling^[[0m diesel_derives v2.3.6
887:  ^[[1m^[[92m    Checking^[[0m pem v3.0.6
888:  ^[[1m^[[92m    Checking^[[0m http-body-util v0.1.3
889:  ^[[1m^[[92m    Checking^[[0m rusticata-macros v4.1.0
890:  ^[[1m^[[92m    Checking^[[0m phf v0.13.1
891:  ^[[1m^[[92m    Checking^[[0m md-5 v0.10.6
892:  ^[[1m^[[92m    Checking^[[0m sha2 v0.10.9
893:  ^[[1m^[[92m    Checking^[[0m num-bigint v0.4.6
894:  ^[[1m^[[92m    Checking^[[0m hmac v0.12.1
895:  ^[[1m^[[92m    Checking^[[0m iana-time-zone v0.1.64
896:  ^[[1m^[[92m    Checking^[[0m winnow v0.7.14
897:  ^[[1m^[[92m    Checking^[[0m rand v0.8.5
898:  ^[[1m^[[92m   Compiling^[[0m rustls v0.21.12
899:  ^[[1m^[[92m    Checking^[[0m toml_write v0.1.2
900:  ^[[1m^[[92m   Compiling^[[0m thiserror v1.0.69
901:  ^[[1m^[[92m    Checking^[[0m data-encoding v2.9.0
...

905:  ^[[1m^[[92m    Checking^[[0m downcast-rs v2.0.2
906:  ^[[1m^[[92m    Checking^[[0m sharded-slab v0.1.7
907:  ^[[1m^[[92m    Checking^[[0m tracing-log v0.2.0
908:  ^[[1m^[[92m    Checking^[[0m scoped-futures v0.1.4
909:  ^[[1m^[[92m    Checking^[[0m thread_local v1.1.9
910:  ^[[1m^[[92m    Checking^[[0m nu-ansi-term v0.50.3
911:  ^[[1m^[[92m    Checking^[[0m time v0.3.44
912:  ^[[1m^[[92m    Checking^[[0m regex-automata v0.4.13
913:  ^[[1m^[[92m   Compiling^[[0m petgraph v0.7.1
914:  ^[[1m^[[92m   Compiling^[[0m ring v0.17.14
915:  ^[[1m^[[92m   Compiling^[[0m openssl-sys v0.9.111
916:  ^[[1m^[[92m   Compiling^[[0m pq-src v0.3.10+libpq-18.0
917:  ^[[1m^[[92m    Checking^[[0m rand v0.9.2
918:  ^[[1m^[[92m    Checking^[[0m axum-core v0.5.6
919:  ^[[1m^[[92m    Checking^[[0m stringprep v0.1.5
920:  ^[[1m^[[92m    Checking^[[0m serde_path_to_error v0.1.20
921:  ^[[1m^[[92m    Checking^[[0m uuid v1.19.0
...

925:  ^[[1m^[[92m    Checking^[[0m postgres-types v0.2.11
926:  ^[[1m^[[92m    Checking^[[0m matchers v0.2.0
927:  ^[[1m^[[92m   Compiling^[[0m pulldown-cmark-to-cmark v21.1.0
928:  ^[[1m^[[92m    Checking^[[0m sct v0.7.1
929:  ^[[1m^[[92m    Checking^[[0m rustls-webpki v0.101.7
930:  ^[[1m^[[92m   Compiling^[[0m synstructure v0.13.2
931:  ^[[1m^[[92m   Compiling^[[0m darling_core v0.21.3
932:  ^[[1m^[[92m   Compiling^[[0m diesel_table_macro_syntax v0.3.0
933:  ^[[1m^[[92m   Compiling^[[0m tonic-build v0.14.2
934:  ^[[1m^[[92m   Compiling^[[0m tokio-macros v2.6.0
935:  ^[[1m^[[92m   Compiling^[[0m tracing-attributes v0.1.31
936:  ^[[1m^[[92m   Compiling^[[0m displaydoc v0.2.5
937:  ^[[1m^[[92m   Compiling^[[0m zerofrom-derive v0.1.6
938:  ^[[1m^[[92m   Compiling^[[0m yoke-derive v0.8.1
939:  ^[[1m^[[92m   Compiling^[[0m serde_derive v1.0.228
940:  ^[[1m^[[92m   Compiling^[[0m thiserror-impl v2.0.17
941:  ^[[1m^[[92m   Compiling^[[0m prost-derive v0.14.1
942:  ^[[1m^[[92m   Compiling^[[0m zerovec-derive v0.11.2
943:  ^[[1m^[[92m   Compiling^[[0m async-trait v0.1.89
944:  ^[[1m^[[92m   Compiling^[[0m zeroize_derive v1.4.3
945:  ^[[1m^[[92m   Compiling^[[0m prost-derive v0.13.5
946:  ^[[1m^[[92m   Compiling^[[0m miette-derive v7.6.0
947:  ^[[1m^[[92m   Compiling^[[0m pin-project-internal v1.1.10
948:  ^[[1m^[[92m   Compiling^[[0m der_derive v0.7.3
949:  ^[[1m^[[92m   Compiling^[[0m asn1-rs-derive v0.6.0
950:  ^[[1m^[[92m   Compiling^[[0m asn1-rs-impl v0.2.0
951:  ^[[1m^[[92m   Compiling^[[0m async-stream-impl v0.3.6
952:  ^[[1m^[[92m   Compiling^[[0m axum-macros v0.4.2
953:  ^[[1m^[[92m   Compiling^[[0m thiserror-impl v1.0.69
954:  ^[[1m^[[92m   Compiling^[[0m regex v1.12.2
...

1075:  ^[[1m^[[92m   Compiling^[[0m pin-utils v0.1.0
1076:  ^[[1m^[[92m   Compiling^[[0m futures-task v0.3.31
1077:  ^[[1m^[[92m   Compiling^[[0m stable_deref_trait v1.2.1
1078:  ^[[1m^[[92m   Compiling^[[0m tower-service v0.3.3
1079:  ^[[1m^[[92m   Compiling^[[0m libc v0.2.180
1080:  ^[[1m^[[92m   Compiling^[[0m serde_core v1.0.228
1081:  ^[[1m^[[92m   Compiling^[[0m anyhow v1.0.100
1082:  ^[[1m^[[92m   Compiling^[[0m zerocopy v0.8.33
1083:  ^[[1m^[[92m   Compiling^[[0m lazy_static v1.5.0
1084:  ^[[1m^[[92m   Compiling^[[0m subtle v2.6.1
1085:  ^[[1m^[[92m   Compiling^[[0m percent-encoding v2.3.2
1086:  ^[[1m^[[92m   Compiling^[[0m lock_api v0.4.14
1087:  ^[[1m^[[92m   Compiling^[[0m num-traits v0.2.19
1088:  ^[[1m^[[92m   Compiling^[[0m futures-channel v0.3.31
1089:  ^[[1m^[[92m   Compiling^[[0m typenum v1.19.0
1090:  ^[[1m^[[92m   Compiling^[[0m thiserror v2.0.17
1091:  ^[[1m^[[92m   Compiling^[[0m base64 v0.22.1
...

1158:  ^[[1m^[[92m   Compiling^[[0m regex-syntax v0.8.8
1159:  ^[[1m^[[92m   Compiling^[[0m iana-time-zone v0.1.64
1160:  ^[[1m^[[92m   Compiling^[[0m winnow v0.7.14
1161:  ^[[1m^[[92m   Compiling^[[0m tinystr v0.8.2
1162:  ^[[1m^[[92m   Compiling^[[0m potential_utf v0.1.4
1163:  ^[[1m^[[92m   Compiling^[[0m toml_write v0.1.2
1164:  ^[[1m^[[92m   Compiling^[[0m async-stream v0.3.6
1165:  ^[[1m^[[92m   Compiling^[[0m icu_collections v2.1.1
1166:  ^[[1m^[[92m   Compiling^[[0m rustls-pemfile v2.2.0
1167:  ^[[1m^[[92m   Compiling^[[0m der v0.7.10
1168:  ^[[1m^[[92m   Compiling^[[0m icu_locale_core v2.1.1
1169:  ^[[1m^[[92m   Compiling^[[0m num-bigint v0.4.6
1170:  ^[[1m^[[92m   Compiling^[[0m stringprep v0.1.5
1171:  ^[[1m^[[92m   Compiling^[[0m indexmap v1.9.3
1172:  ^[[1m^[[92m   Compiling^[[0m downcast-rs v2.0.2
1173:  ^[[1m^[[92m   Compiling^[[0m thiserror v1.0.69
1174:  ^[[1m^[[92m   Compiling^[[0m tracing-log v0.2.0
...

1218:  ^[[1m^[[92m   Compiling^[[0m rand_chacha v0.3.1
1219:  ^[[1m^[[92m   Compiling^[[0m rusticata-macros v4.1.0
1220:  ^[[1m^[[92m   Compiling^[[0m logos v0.15.1
1221:  ^[[1m^[[92m   Compiling^[[0m icu_properties v2.1.2
1222:  ^[[1m^[[92m   Compiling^[[0m icu_normalizer v2.1.1
1223:  ^[[1m^[[92m   Compiling^[[0m spki v0.7.3
1224:  ^[[1m^[[92m   Compiling^[[0m pkcs8 v0.10.2
1225:  ^[[1m^[[92m   Compiling^[[0m x509-cert v0.2.5
1226:  ^[[1m^[[92m   Compiling^[[0m rand v0.9.2
1227:  ^[[1m^[[92m   Compiling^[[0m prost-types v0.14.1
1228:  ^[[1m^[[92m   Compiling^[[0m prost-types v0.13.5
1229:  ^[[1m^[[92m   Compiling^[[0m darling_macro v0.21.3
1230:  ^[[1m^[[92m   Compiling^[[0m rand v0.8.5
1231:  ^[[1m^[[92m   Compiling^[[0m serde v1.0.228
1232:  ^[[1m^[[92m   Compiling^[[0m serde_json v1.0.149
1233:  ^[[1m^[[92m   Compiling^[[0m serde_path_to_error v0.1.20
1234:  ^[[1m^[[92m   Compiling^[[0m uuid v1.19.0
...

1308:  test parser::tests::parses_multiple_stats ... ok
1309:  test parser::tests::parses_lists ... ok
1310:  test parser::tests::parses_list_values ... ok
1311:  test parser::tests::parses_rollup_stats_keyword ... ok
1312:  test parser::tests::parses_rollup_stats_with_filters ... ok
1313:  test parser::tests::parses_stats_expression ... ok
1314:  test parser::tests::parses_stats_with_field ... ok
1315:  test parser::tests::parses_time ... ok
1316:  test parser::tests::parses_unquoted_stats_alias ... ok
1317:  test parser::tests::rejects_empty_rollup_stats ... ok
1318:  test parser::tests::parses_unquoted_stats_alias_with_following_tokens ... ok
1319:  test parser::tests::rejects_stats_alias_missing_identifier ... ok
1320:  test query::agents::tests::builds_query_with_type_id_filter ... ok
1321:  test parser::tests::rejects_overly_long_stats_expression ... ok
1322:  test query::agents::tests::builds_query_with_gateway_filter ... ok
1323:  test query::agents::tests::unknown_filter_field_returns_error ... ok
1324:  test parser::tests::rejects_list_filters_over_limit ... ok
1325:  test query::cpu_metrics::tests::unknown_filter_field_returns_error ... ok
1326:  test query::gateways::tests::unknown_filter_field_returns_error ... ok
1327:  test query::interfaces::tests::stats_count_interfaces_emits_count_query ... ok
1328:  test query::disk_metrics::tests::unknown_filter_field_returns_error ... ok
1329:  test query::cpu_metrics::tests::stats_query_matches_cpu_language_reference ... ok
1330:  test query::logs::tests::rollup_stats_severity_builds_cagg_query ... ok
1331:  test query::logs::tests::rollup_stats_severity_with_service_filter ... ok
1332:  test query::logs::tests::rollup_stats_unknown_type_returns_error ... ok
1333:  test query::logs::tests::stats_query_counts_logs_for_service ... ok
1334:  test query::logs::tests::unknown_filter_field_returns_error ... ok
1335:  test query::logs::tests::unknown_stats_filter_field_returns_error ... ok
1336:  test query::memory_metrics::tests::unknown_filter_field_returns_error ... ok
1337:  test query::otel_metrics::tests::unknown_filter_field_returns_error ... ok
1338:  test query::process_metrics::tests::unknown_filter_field_returns_error ... ok
1339:  test query::services::tests::unknown_filter_field_returns_error ... ok
1340:  test query::tests::devices_docs_example_discovery_sources_contains_all ... ok
1341:  test query::tests::translate_downsample_emits_time_bucket_query ... ok
1342:  test query::tests::gateways_docs_example_health_and_status ... ok
1343:  test query::tests::services_docs_example_service_type_timeframe ... ok
1344:  test query::tests::devices_docs_example_available_true ... ok
1345:  test query::tests::devices_docs_example_available_false ... ok
1346:  test query::tests::interfaces_docs_example_ip_addresses_contains_any ... ok
1347:  test query::timeseries_metrics::tests::unknown_filter_field_returns_error ... ok
1348:  test time::tests::parses_absolute_range ... ok
1349:  test time::tests::parses_open_start_absolute_range ... ok
1350:  ^[[1m^[[92m     Running^[[0m unittests src/main.rs (/home/runner/_work/serviceradar/serviceradar/target/debug/deps/srql-37c8acb55abeef22)
1351:  ^[[1m^[[92m     Running^[[0m tests/api.rs (/home/runner/_work/serviceradar/serviceradar/target/debug/deps/api-a8d9dcc3b48816a3)
1352:  test query::tests::translate_graph_cypher_wraps_rows_as_topology_payload ... ok
1353:  test time::tests::parses_open_end_absolute_range ... ok
1354:  test query::traces::tests::unknown_filter_field_returns_error ... ok
1355:  test time::tests::serializes_today ... ok
1356:  test time::tests::parses_relative_days ... ok
1357:  test time::tests::rejects_absolute_range_exceeding_limit ... ok
1358:  test time::tests::rejects_open_end_range_exceeding_limit ... ok
1359:  test query::tests::translate_param_arity_matches_sql_placeholders ... ok
1360:  test query::tests::translate_graph_cypher_rejects_mutations ... ok
1361:  test time::tests::serializes_absolute_range ... ok
1362:  test time::tests::serializes_relative_days ... ok
1363:  test query::tests::translate_includes_visualization_metadata ... ok
1364:  test time::tests::serializes_relative_hours ... ok
1365:  test result: ok. 60 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
1366:  running 0 tests
1367:  test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
1368:  running 1 test
1369:  test srql_api_queries ... FAILED
1370:  failures:
1371:  ---- srql_api_queries stdout ----
1372:  [srql-test] SRQL_TEST_DATABASE_URL: user=srql db=srql_fixture hosts=["srql-fixture.serviceradar.cloud"] port=5432
1373:  [srql-test] SRQL_TEST_ADMIN_URL: user=srql_hydra db=postgres hosts=["srql-fixture.serviceradar.cloud"] port=5432
1374:  thread 'srql_api_queries' (9203) panicked at rust/srql/tests/support/harness.rs:74:10:
1375:  failed to acquire remote fixture lock: db error
1376:  Caused by:
1377:  FATAL: pg_hba.conf rejects connection for host "10.42.68.65", user "srql_hydra", database "postgres", no encryption
1378:  note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
1379:  failures:
1380:  srql_api_queries
1381:  test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.07s
1382:  ^[[1m^[[91merror^[[0m: test failed, to rerun pass `--test api`
1383:  ##[error]Process completed with exit code 101.
1384:  Post job cleanup.

Imported GitHub PR comment. Original author: @qodo-code-review[bot] Original URL: https://github.com/carverauto/serviceradar/pull/2242#issuecomment-3733974193 Original created: 2026-01-11T04:06:01Z --- ## CI Feedback 🧐 A test triggered by this PR failed. Here is an AI-generated analysis of the failure: <table><tr><td> **Action:** test-rust (rust/srql)</td></tr> <tr><td> **Failed stage:** [Run Tests](https://github.com/carverauto/serviceradar/actions/runs/20889153599/job/60017527821) [❌] </td></tr> <tr><td> **Failed test name:** srql_api_queries </td></tr> <tr><td> **Failure summary:** The action failed because the Rust integration test <code>srql_api_queries</code> failed while trying to connect <br>to the remote PostgreSQL fixture.<br> - The test panicked in <code>rust/srql/tests/support/harness.rs:74:10</code> <br>with: <code>failed to acquire remote fixture lock: db error</code>.<br> - The underlying DB error shows the <br>connection was rejected by Postgres <code>pg_hba.conf</code>: <code>FATAL: pg_hba.conf rejects connection for host </code><br><code>"10.42.68.65", user "srql_hydra", database "postgres", no encryption</code>.<br> - This indicates the CI <br>runner’s IP (or connection settings) is not allowed by the fixture’s <code>pg_hba.conf</code>, and/or the server <br>requires SSL but the client attempted an unencrypted connection (<code>no encryption</code>).<br> </td></tr> <tr><td> <details><summary>Relevant error logs:</summary> ```yaml 1: Runner name: 'arc-runner-set-hk6mk-runner-lhp92' 2: Runner group name: 'Default' ... 126: ^[[36;1mif command -v apt-get >/dev/null 2>&1; then^[[0m 127: ^[[36;1m sudo apt-get update^[[0m 128: ^[[36;1m sudo apt-get install -y build-essential pkg-config libssl-dev protobuf-compiler cmake flex bison^[[0m 129: ^[[36;1melif command -v dnf >/dev/null 2>&1; then^[[0m 130: ^[[36;1m sudo dnf install -y gcc gcc-c++ make openssl-devel protobuf-compiler cmake flex bison^[[0m 131: ^[[36;1melif command -v yum >/dev/null 2>&1; then^[[0m 132: ^[[36;1m sudo yum install -y gcc gcc-c++ make openssl-devel protobuf-compiler cmake flex bison^[[0m 133: ^[[36;1melif command -v microdnf >/dev/null 2>&1; then^[[0m 134: ^[[36;1m sudo microdnf install -y gcc gcc-c++ make openssl-devel protobuf-compiler cmake flex bison^[[0m 135: ^[[36;1melse^[[0m 136: ^[[36;1m echo "Unsupported package manager; please install gcc, g++ (or clang), make, OpenSSL headers, pkg-config, and protoc manually." >&2^[[0m 137: ^[[36;1m exit 1^[[0m 138: ^[[36;1mfi^[[0m 139: ^[[36;1m^[[0m 140: ^[[36;1mensure_pkg_config^[[0m 141: ^[[36;1mprotoc --version || (echo "protoc installation failed" && exit 1)^[[0m 142: shell: /usr/bin/bash -e {0} ... 181: libprotoc 3.12.4 182: ##[group]Run bazelbuild/setup-bazelisk@v3 183: with: 184: bazelisk-version: 1.x 185: token: *** 186: env: 187: BUILDBUDDY_ORG_API_KEY: *** 188: SRQL_TEST_DATABASE_URL: *** 189: SRQL_TEST_ADMIN_URL: *** 190: ##[endgroup] 191: Attempting to download 1.x... 192: Acquiring v1.27.0 from https://github.com/bazelbuild/bazelisk/releases/download/v1.27.0/bazelisk-linux-amd64 193: Adding to the cache ... 194: Successfully cached bazelisk to /home/runner/_work/_tool/bazelisk/1.27.0/x64 195: Added bazelisk to the path 196: ##[warning]Failed to restore: Cache service responded with 400 197: Restored bazelisk cache dir @ /home/runner/.cache/bazelisk ... 253: targets: 254: components: clippy 255: ##[endgroup] 256: ##[group]Run : set $CARGO_HOME 257: ^[[36;1m: set $CARGO_HOME^[[0m 258: ^[[36;1mecho CARGO_HOME=${CARGO_HOME:-"$HOME/.cargo"} >> $GITHUB_ENV^[[0m 259: shell: /usr/bin/bash --noprofile --norc -e -o pipefail {0} 260: env: 261: BUILDBUDDY_ORG_API_KEY: *** 262: SRQL_TEST_DATABASE_URL: *** 263: SRQL_TEST_ADMIN_URL: *** 264: ##[endgroup] 265: ##[group]Run : install rustup if needed 266: ^[[36;1m: install rustup if needed^[[0m 267: ^[[36;1mif ! command -v rustup &>/dev/null; then^[[0m 268: ^[[36;1m curl --proto '=https' --tlsv1.2 --retry 10 --retry-connrefused --location --silent --show-error --fail https://sh.rustup.rs | sh -s -- --default-toolchain none -y^[[0m 269: ^[[36;1m echo "$CARGO_HOME/bin" >> $GITHUB_PATH^[[0m ... 369: ^[[36;1m echo CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse >> $GITHUB_ENV^[[0m 370: ^[[36;1m elif rustc +stable --version --verbose | grep -q '^release: 1\.6[67]\.'; then^[[0m 371: ^[[36;1m touch "/home/runner/_work/_temp"/.implicit_cargo_registries_crates_io_protocol || true^[[0m 372: ^[[36;1m echo CARGO_REGISTRIES_CRATES_IO_PROTOCOL=git >> $GITHUB_ENV^[[0m 373: ^[[36;1m fi^[[0m 374: ^[[36;1mfi^[[0m 375: shell: /usr/bin/bash --noprofile --norc -e -o pipefail {0} 376: env: 377: BUILDBUDDY_ORG_API_KEY: *** 378: SRQL_TEST_DATABASE_URL: *** 379: SRQL_TEST_ADMIN_URL: *** 380: CARGO_HOME: /home/runner/.cargo 381: CARGO_INCREMENTAL: 0 382: CARGO_TERM_COLOR: always 383: ##[endgroup] 384: ##[group]Run : work around spurious network errors in curl 8.0 385: ^[[36;1m: work around spurious network errors in curl 8.0^[[0m 386: ^[[36;1m# https://rust-lang.zulipchat.com/#narrow/stream/246057-t-cargo/topic/timeout.20investigation^[[0m ... 581: ^[[1m^[[92m Downloaded^[[0m tonic-build v0.14.2 582: ^[[1m^[[92m Downloaded^[[0m smallvec v1.15.1 583: ^[[1m^[[92m Downloaded^[[0m ryu v1.0.22 584: ^[[1m^[[92m Downloaded^[[0m chrono v0.4.42 585: ^[[1m^[[92m Downloaded^[[0m want v0.3.1 586: ^[[1m^[[92m Downloaded^[[0m utf8_iter v1.0.4 587: ^[[1m^[[92m Downloaded^[[0m tinystr v0.8.2 588: ^[[1m^[[92m Downloaded^[[0m linux-raw-sys v0.11.0 589: ^[[1m^[[92m Downloaded^[[0m shlex v1.3.0 590: ^[[1m^[[92m Downloaded^[[0m rustls v0.21.12 591: ^[[1m^[[92m Downloaded^[[0m serde_urlencoded v0.7.1 592: ^[[1m^[[92m Downloaded^[[0m rustls-webpki v0.101.7 593: ^[[1m^[[92m Downloaded^[[0m tower-service v0.3.3 594: ^[[1m^[[92m Downloaded^[[0m time-macros v0.2.24 595: ^[[1m^[[92m Downloaded^[[0m thread_local v1.1.9 596: ^[[1m^[[92m Downloaded^[[0m thiserror v1.0.69 597: ^[[1m^[[92m Downloaded^[[0m serde_path_to_error v0.1.20 598: ^[[1m^[[92m Downloaded^[[0m openssl-sys v0.9.111 599: ^[[1m^[[92m Downloaded^[[0m rustls-pki-types v1.13.2 600: ^[[1m^[[92m Downloaded^[[0m num-bigint v0.4.6 601: ^[[1m^[[92m Downloaded^[[0m thiserror-impl v2.0.17 602: ^[[1m^[[92m Downloaded^[[0m thiserror-impl v1.0.69 603: ^[[1m^[[92m Downloaded^[[0m thiserror v2.0.17 604: ^[[1m^[[92m Downloaded^[[0m rand_core v0.6.4 ... 770: ^[[1m^[[92m Compiling^[[0m parking_lot_core v0.9.12 771: ^[[1m^[[92m Checking^[[0m log v0.4.29 772: ^[[1m^[[92m Checking^[[0m scopeguard v1.2.0 773: ^[[1m^[[92m Compiling^[[0m serde_core v1.0.228 774: ^[[1m^[[92m Checking^[[0m futures-sink v0.3.31 775: ^[[1m^[[92m Checking^[[0m once_cell v1.21.3 776: ^[[1m^[[92m Compiling^[[0m autocfg v1.5.0 777: ^[[1m^[[92m Checking^[[0m memchr v2.7.6 778: ^[[1m^[[92m Checking^[[0m pin-utils v0.1.0 779: ^[[1m^[[92m Compiling^[[0m fnv v1.0.7 780: ^[[1m^[[92m Checking^[[0m futures-task v0.3.31 781: ^[[1m^[[92m Compiling^[[0m regex-syntax v0.8.8 782: ^[[1m^[[92m Compiling^[[0m serde v1.0.228 783: ^[[1m^[[92m Checking^[[0m hashbrown v0.16.1 784: ^[[1m^[[92m Checking^[[0m equivalent v1.0.2 785: ^[[1m^[[92m Compiling^[[0m thiserror v2.0.17 786: ^[[1m^[[92m Checking^[[0m stable_deref_trait v1.2.1 ... 885: ^[[1m^[[92m Checking^[[0m hashbrown v0.12.3 886: ^[[1m^[[92m Compiling^[[0m diesel_derives v2.3.6 887: ^[[1m^[[92m Checking^[[0m pem v3.0.6 888: ^[[1m^[[92m Checking^[[0m http-body-util v0.1.3 889: ^[[1m^[[92m Checking^[[0m rusticata-macros v4.1.0 890: ^[[1m^[[92m Checking^[[0m phf v0.13.1 891: ^[[1m^[[92m Checking^[[0m md-5 v0.10.6 892: ^[[1m^[[92m Checking^[[0m sha2 v0.10.9 893: ^[[1m^[[92m Checking^[[0m num-bigint v0.4.6 894: ^[[1m^[[92m Checking^[[0m hmac v0.12.1 895: ^[[1m^[[92m Checking^[[0m iana-time-zone v0.1.64 896: ^[[1m^[[92m Checking^[[0m winnow v0.7.14 897: ^[[1m^[[92m Checking^[[0m rand v0.8.5 898: ^[[1m^[[92m Compiling^[[0m rustls v0.21.12 899: ^[[1m^[[92m Checking^[[0m toml_write v0.1.2 900: ^[[1m^[[92m Compiling^[[0m thiserror v1.0.69 901: ^[[1m^[[92m Checking^[[0m data-encoding v2.9.0 ... 905: ^[[1m^[[92m Checking^[[0m downcast-rs v2.0.2 906: ^[[1m^[[92m Checking^[[0m sharded-slab v0.1.7 907: ^[[1m^[[92m Checking^[[0m tracing-log v0.2.0 908: ^[[1m^[[92m Checking^[[0m scoped-futures v0.1.4 909: ^[[1m^[[92m Checking^[[0m thread_local v1.1.9 910: ^[[1m^[[92m Checking^[[0m nu-ansi-term v0.50.3 911: ^[[1m^[[92m Checking^[[0m time v0.3.44 912: ^[[1m^[[92m Checking^[[0m regex-automata v0.4.13 913: ^[[1m^[[92m Compiling^[[0m petgraph v0.7.1 914: ^[[1m^[[92m Compiling^[[0m ring v0.17.14 915: ^[[1m^[[92m Compiling^[[0m openssl-sys v0.9.111 916: ^[[1m^[[92m Compiling^[[0m pq-src v0.3.10+libpq-18.0 917: ^[[1m^[[92m Checking^[[0m rand v0.9.2 918: ^[[1m^[[92m Checking^[[0m axum-core v0.5.6 919: ^[[1m^[[92m Checking^[[0m stringprep v0.1.5 920: ^[[1m^[[92m Checking^[[0m serde_path_to_error v0.1.20 921: ^[[1m^[[92m Checking^[[0m uuid v1.19.0 ... 925: ^[[1m^[[92m Checking^[[0m postgres-types v0.2.11 926: ^[[1m^[[92m Checking^[[0m matchers v0.2.0 927: ^[[1m^[[92m Compiling^[[0m pulldown-cmark-to-cmark v21.1.0 928: ^[[1m^[[92m Checking^[[0m sct v0.7.1 929: ^[[1m^[[92m Checking^[[0m rustls-webpki v0.101.7 930: ^[[1m^[[92m Compiling^[[0m synstructure v0.13.2 931: ^[[1m^[[92m Compiling^[[0m darling_core v0.21.3 932: ^[[1m^[[92m Compiling^[[0m diesel_table_macro_syntax v0.3.0 933: ^[[1m^[[92m Compiling^[[0m tonic-build v0.14.2 934: ^[[1m^[[92m Compiling^[[0m tokio-macros v2.6.0 935: ^[[1m^[[92m Compiling^[[0m tracing-attributes v0.1.31 936: ^[[1m^[[92m Compiling^[[0m displaydoc v0.2.5 937: ^[[1m^[[92m Compiling^[[0m zerofrom-derive v0.1.6 938: ^[[1m^[[92m Compiling^[[0m yoke-derive v0.8.1 939: ^[[1m^[[92m Compiling^[[0m serde_derive v1.0.228 940: ^[[1m^[[92m Compiling^[[0m thiserror-impl v2.0.17 941: ^[[1m^[[92m Compiling^[[0m prost-derive v0.14.1 942: ^[[1m^[[92m Compiling^[[0m zerovec-derive v0.11.2 943: ^[[1m^[[92m Compiling^[[0m async-trait v0.1.89 944: ^[[1m^[[92m Compiling^[[0m zeroize_derive v1.4.3 945: ^[[1m^[[92m Compiling^[[0m prost-derive v0.13.5 946: ^[[1m^[[92m Compiling^[[0m miette-derive v7.6.0 947: ^[[1m^[[92m Compiling^[[0m pin-project-internal v1.1.10 948: ^[[1m^[[92m Compiling^[[0m der_derive v0.7.3 949: ^[[1m^[[92m Compiling^[[0m asn1-rs-derive v0.6.0 950: ^[[1m^[[92m Compiling^[[0m asn1-rs-impl v0.2.0 951: ^[[1m^[[92m Compiling^[[0m async-stream-impl v0.3.6 952: ^[[1m^[[92m Compiling^[[0m axum-macros v0.4.2 953: ^[[1m^[[92m Compiling^[[0m thiserror-impl v1.0.69 954: ^[[1m^[[92m Compiling^[[0m regex v1.12.2 ... 1075: ^[[1m^[[92m Compiling^[[0m pin-utils v0.1.0 1076: ^[[1m^[[92m Compiling^[[0m futures-task v0.3.31 1077: ^[[1m^[[92m Compiling^[[0m stable_deref_trait v1.2.1 1078: ^[[1m^[[92m Compiling^[[0m tower-service v0.3.3 1079: ^[[1m^[[92m Compiling^[[0m libc v0.2.180 1080: ^[[1m^[[92m Compiling^[[0m serde_core v1.0.228 1081: ^[[1m^[[92m Compiling^[[0m anyhow v1.0.100 1082: ^[[1m^[[92m Compiling^[[0m zerocopy v0.8.33 1083: ^[[1m^[[92m Compiling^[[0m lazy_static v1.5.0 1084: ^[[1m^[[92m Compiling^[[0m subtle v2.6.1 1085: ^[[1m^[[92m Compiling^[[0m percent-encoding v2.3.2 1086: ^[[1m^[[92m Compiling^[[0m lock_api v0.4.14 1087: ^[[1m^[[92m Compiling^[[0m num-traits v0.2.19 1088: ^[[1m^[[92m Compiling^[[0m futures-channel v0.3.31 1089: ^[[1m^[[92m Compiling^[[0m typenum v1.19.0 1090: ^[[1m^[[92m Compiling^[[0m thiserror v2.0.17 1091: ^[[1m^[[92m Compiling^[[0m base64 v0.22.1 ... 1158: ^[[1m^[[92m Compiling^[[0m regex-syntax v0.8.8 1159: ^[[1m^[[92m Compiling^[[0m iana-time-zone v0.1.64 1160: ^[[1m^[[92m Compiling^[[0m winnow v0.7.14 1161: ^[[1m^[[92m Compiling^[[0m tinystr v0.8.2 1162: ^[[1m^[[92m Compiling^[[0m potential_utf v0.1.4 1163: ^[[1m^[[92m Compiling^[[0m toml_write v0.1.2 1164: ^[[1m^[[92m Compiling^[[0m async-stream v0.3.6 1165: ^[[1m^[[92m Compiling^[[0m icu_collections v2.1.1 1166: ^[[1m^[[92m Compiling^[[0m rustls-pemfile v2.2.0 1167: ^[[1m^[[92m Compiling^[[0m der v0.7.10 1168: ^[[1m^[[92m Compiling^[[0m icu_locale_core v2.1.1 1169: ^[[1m^[[92m Compiling^[[0m num-bigint v0.4.6 1170: ^[[1m^[[92m Compiling^[[0m stringprep v0.1.5 1171: ^[[1m^[[92m Compiling^[[0m indexmap v1.9.3 1172: ^[[1m^[[92m Compiling^[[0m downcast-rs v2.0.2 1173: ^[[1m^[[92m Compiling^[[0m thiserror v1.0.69 1174: ^[[1m^[[92m Compiling^[[0m tracing-log v0.2.0 ... 1218: ^[[1m^[[92m Compiling^[[0m rand_chacha v0.3.1 1219: ^[[1m^[[92m Compiling^[[0m rusticata-macros v4.1.0 1220: ^[[1m^[[92m Compiling^[[0m logos v0.15.1 1221: ^[[1m^[[92m Compiling^[[0m icu_properties v2.1.2 1222: ^[[1m^[[92m Compiling^[[0m icu_normalizer v2.1.1 1223: ^[[1m^[[92m Compiling^[[0m spki v0.7.3 1224: ^[[1m^[[92m Compiling^[[0m pkcs8 v0.10.2 1225: ^[[1m^[[92m Compiling^[[0m x509-cert v0.2.5 1226: ^[[1m^[[92m Compiling^[[0m rand v0.9.2 1227: ^[[1m^[[92m Compiling^[[0m prost-types v0.14.1 1228: ^[[1m^[[92m Compiling^[[0m prost-types v0.13.5 1229: ^[[1m^[[92m Compiling^[[0m darling_macro v0.21.3 1230: ^[[1m^[[92m Compiling^[[0m rand v0.8.5 1231: ^[[1m^[[92m Compiling^[[0m serde v1.0.228 1232: ^[[1m^[[92m Compiling^[[0m serde_json v1.0.149 1233: ^[[1m^[[92m Compiling^[[0m serde_path_to_error v0.1.20 1234: ^[[1m^[[92m Compiling^[[0m uuid v1.19.0 ... 1308: test parser::tests::parses_multiple_stats ... ok 1309: test parser::tests::parses_lists ... ok 1310: test parser::tests::parses_list_values ... ok 1311: test parser::tests::parses_rollup_stats_keyword ... ok 1312: test parser::tests::parses_rollup_stats_with_filters ... ok 1313: test parser::tests::parses_stats_expression ... ok 1314: test parser::tests::parses_stats_with_field ... ok 1315: test parser::tests::parses_time ... ok 1316: test parser::tests::parses_unquoted_stats_alias ... ok 1317: test parser::tests::rejects_empty_rollup_stats ... ok 1318: test parser::tests::parses_unquoted_stats_alias_with_following_tokens ... ok 1319: test parser::tests::rejects_stats_alias_missing_identifier ... ok 1320: test query::agents::tests::builds_query_with_type_id_filter ... ok 1321: test parser::tests::rejects_overly_long_stats_expression ... ok 1322: test query::agents::tests::builds_query_with_gateway_filter ... ok 1323: test query::agents::tests::unknown_filter_field_returns_error ... ok 1324: test parser::tests::rejects_list_filters_over_limit ... ok 1325: test query::cpu_metrics::tests::unknown_filter_field_returns_error ... ok 1326: test query::gateways::tests::unknown_filter_field_returns_error ... ok 1327: test query::interfaces::tests::stats_count_interfaces_emits_count_query ... ok 1328: test query::disk_metrics::tests::unknown_filter_field_returns_error ... ok 1329: test query::cpu_metrics::tests::stats_query_matches_cpu_language_reference ... ok 1330: test query::logs::tests::rollup_stats_severity_builds_cagg_query ... ok 1331: test query::logs::tests::rollup_stats_severity_with_service_filter ... ok 1332: test query::logs::tests::rollup_stats_unknown_type_returns_error ... ok 1333: test query::logs::tests::stats_query_counts_logs_for_service ... ok 1334: test query::logs::tests::unknown_filter_field_returns_error ... ok 1335: test query::logs::tests::unknown_stats_filter_field_returns_error ... ok 1336: test query::memory_metrics::tests::unknown_filter_field_returns_error ... ok 1337: test query::otel_metrics::tests::unknown_filter_field_returns_error ... ok 1338: test query::process_metrics::tests::unknown_filter_field_returns_error ... ok 1339: test query::services::tests::unknown_filter_field_returns_error ... ok 1340: test query::tests::devices_docs_example_discovery_sources_contains_all ... ok 1341: test query::tests::translate_downsample_emits_time_bucket_query ... ok 1342: test query::tests::gateways_docs_example_health_and_status ... ok 1343: test query::tests::services_docs_example_service_type_timeframe ... ok 1344: test query::tests::devices_docs_example_available_true ... ok 1345: test query::tests::devices_docs_example_available_false ... ok 1346: test query::tests::interfaces_docs_example_ip_addresses_contains_any ... ok 1347: test query::timeseries_metrics::tests::unknown_filter_field_returns_error ... ok 1348: test time::tests::parses_absolute_range ... ok 1349: test time::tests::parses_open_start_absolute_range ... ok 1350: ^[[1m^[[92m Running^[[0m unittests src/main.rs (/home/runner/_work/serviceradar/serviceradar/target/debug/deps/srql-37c8acb55abeef22) 1351: ^[[1m^[[92m Running^[[0m tests/api.rs (/home/runner/_work/serviceradar/serviceradar/target/debug/deps/api-a8d9dcc3b48816a3) 1352: test query::tests::translate_graph_cypher_wraps_rows_as_topology_payload ... ok 1353: test time::tests::parses_open_end_absolute_range ... ok 1354: test query::traces::tests::unknown_filter_field_returns_error ... ok 1355: test time::tests::serializes_today ... ok 1356: test time::tests::parses_relative_days ... ok 1357: test time::tests::rejects_absolute_range_exceeding_limit ... ok 1358: test time::tests::rejects_open_end_range_exceeding_limit ... ok 1359: test query::tests::translate_param_arity_matches_sql_placeholders ... ok 1360: test query::tests::translate_graph_cypher_rejects_mutations ... ok 1361: test time::tests::serializes_absolute_range ... ok 1362: test time::tests::serializes_relative_days ... ok 1363: test query::tests::translate_includes_visualization_metadata ... ok 1364: test time::tests::serializes_relative_hours ... ok 1365: test result: ok. 60 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s 1366: running 0 tests 1367: test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s 1368: running 1 test 1369: test srql_api_queries ... FAILED 1370: failures: 1371: ---- srql_api_queries stdout ---- 1372: [srql-test] SRQL_TEST_DATABASE_URL: user=srql db=srql_fixture hosts=["srql-fixture.serviceradar.cloud"] port=5432 1373: [srql-test] SRQL_TEST_ADMIN_URL: user=srql_hydra db=postgres hosts=["srql-fixture.serviceradar.cloud"] port=5432 1374: thread 'srql_api_queries' (9203) panicked at rust/srql/tests/support/harness.rs:74:10: 1375: failed to acquire remote fixture lock: db error 1376: Caused by: 1377: FATAL: pg_hba.conf rejects connection for host "10.42.68.65", user "srql_hydra", database "postgres", no encryption 1378: note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace 1379: failures: 1380: srql_api_queries 1381: test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.07s 1382: ^[[1m^[[91merror^[[0m: test failed, to rerun pass `--test api` 1383: ##[error]Process completed with exit code 101. 1384: Post job cleanup. ``` </details></td></tr></table>
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
carverauto/serviceradar!2645
No description provided.