Skip to content

add criteria - #1382

Merged
iceljc merged 10 commits into
SciSharp:masterfrom
iceljc:features/add-rule-criteria
Aug 5, 2026
Merged

add criteria#1382
iceljc merged 10 commits into
SciSharp:masterfrom
iceljc:features/add-rule-criteria

Conversation

@iceljc

@iceljc iceljc commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

No description provided.

@iceljc
iceljc marked this pull request as draft July 17, 2026 16:56
@qodo-code-review

qodo-code-review Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Add pluggable rule criteria evaluators with code + LLM fallback

✨ Enhancement ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Add a DI-resolved criteria evaluation layer to gate rule triggering.
• Support code-script criteria with automatic fallback to LLM-based evaluation.
• Update agent/rule configs and code-generation prompts to align with new criteria flow.
Diagram

graph TD
  A["RuleEngine"] --> B["IRuleCriteriaEvaluator"] --> C["CodeCriteriaEvaluator"] --> D["ICodeProcessor"] --> E{{"Python runtime"}}
  B --> F["LlmCriteriaEvaluator"] --> G{{"Chat completion provider"}}
  H[("Agent rule config\ncriteria")] --> F
  H --> C
  subgraph Legend
    direction LR
    _svc["Service"] ~~~ _cfg[("Config/JSON")] ~~~ _ext{{"External"}}
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Embed criteria as rule-engine DSL (single evaluator)
  • ➕ Centralizes parsing/validation; fewer moving parts than DI-resolved evaluators
  • ➕ Can provide richer compile-time checks and better error messages
  • ➖ Harder to extend by plugins compared to DI-resolved evaluators
  • ➖ Requires designing/maintaining a DSL and migration path
2. Model criteria as a rule-flow/graph precondition node
  • ➕ Reuses existing rule-flow concepts and tooling
  • ➕ Keeps all conditional logic inside one execution model
  • ➖ Couples criteria evaluation back to graph infrastructure (which this PR is moving away from)
  • ➖ Harder to support multiple independent evaluation backends cleanly
3. Use a policy engine library (e.g., OPA/Rego-style)
  • ➕ Mature evaluation semantics; good for complex policy needs
  • ➕ Separates policy authoring from application code
  • ➖ Adds significant dependency/operational complexity
  • ➖ May be overkill for lightweight rule-trigger gating

Recommendation: The DI-resolved evaluator approach is the best fit for plugin extensibility and incremental adoption. The code-first evaluator with an LLM fallback provides a pragmatic reliability path (deterministic when possible, still functional when scripts are missing). Ensure downstream consumers are aware of the breaking config rename (TopologyName -> Criteria) and consider a compatibility shim if older stored rules exist.

Files changed (22) +1312 / -713

Enhancement (13) +1279 / -686
AgentRule.csRename rule config field to criteria +2/-2

Rename rule config field to criteria

• Replaces the rule config JSON field "topology_name" with "criteria". This shifts rule configuration from graph topology selection to a criteria string.

src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentRule.cs

BuiltInRuleCriteria.csAdd built-in criteria type constants +19/-0

Add built-in criteria type constants

• Introduces named string constants for criteria evaluator types ("code" and "llm"). These map to IRuleCriteriaEvaluator registrations.

src/Infrastructure/BotSharp.Abstraction/Rules/Constants/BuiltInRuleCriteria.cs

IRuleCriteriaEvaluator.csIntroduce criteria evaluator interface +29/-0

Introduce criteria evaluator interface

• Adds IRuleCriteriaEvaluator to allow pluggable rule applicability checks. Evaluators return bool? to enable fallback when they cannot produce an answer.

src/Infrastructure/BotSharp.Abstraction/Rules/IRuleCriteriaEvaluator.cs

RuleCriteriaContext.csAdd per-request criteria evaluation context +23/-0

Add per-request criteria evaluation context

• Introduces RuleCriteriaContext carrying trigger text, criteria options, and conversation states. This object is passed into evaluators for consistent inputs.

src/Infrastructure/BotSharp.Abstraction/Rules/Models/RuleCriteriaContext.cs

RuleTriggerOptions.csReplace JsonOptions/Flow with CriteriaOptions (typed + JSON data) +33/-5

Replace JsonOptions/Flow with CriteriaOptions (typed + JSON data)

• Replaces JsonSerializerOptions with CriteriaOptions, which selects an evaluator type and carries evaluator-specific JSON data. Adds a GetData<T>() helper for safe deserialization of JsonElement payloads.

src/Infrastructure/BotSharp.Abstraction/Rules/Options/RuleTriggerOptions.cs

JsonExtensions.csAdd System.Text.Json conversion helpers +71/-0

Add System.Text.Json conversion helpers

• Adds helpers to convert arbitrary objects into JsonDocument/JsonElement, treating strings as raw JSON when possible. Ensures returned JsonElement is cloned to outlive the backing document.

src/Infrastructure/BotSharp.Abstraction/Utilities/JsonExtensions.cs

CodeCriteriaEvaluator.csImplement code-script based criteria evaluator +134/-0

Implement code-script based criteria evaluator

• Introduces a criteria evaluator that runs an agent-owned code script (default: <trigger>_criteria.py) via an ICodeProcessor. Returns null on missing script/processor or execution failure to enable LLM fallback.

src/Infrastructure/BotSharp.Core.Rules/Criteria/Code/CodeCriteriaEvaluator.cs

CodeCriteriaSettings.csDefine JSON settings for code criteria evaluator +34/-0

Define JSON settings for code criteria evaluator

• Adds a settings model parsed from CriteriaOptions.Data, supporting processor selection, script naming, and JSON argument payload.

src/Infrastructure/BotSharp.Core.Rules/Criteria/Code/CodeCriteriaSettings.cs

LlmCriteriaEvaluator.csImplement LLM-based criteria evaluator using templates +168/-0

Implement LLM-based criteria evaluator using templates

• Adds an evaluator that renders a criteria-check template as the system prompt and asks a chat completion provider for a "1"/"0" decision. Fails closed (returns false) on errors/unparseable answers and never returns null.

src/Infrastructure/BotSharp.Core.Rules/Criteria/Llm/LlmCriteriaEvaluator.cs

LlmCriteriaSettings.csDefine JSON settings for LLM criteria evaluator +29/-0

Define JSON settings for LLM criteria evaluator

• Adds a settings model for selecting the template-hosting agent, template name, and JSON argument payload for the LLM input.

src/Infrastructure/BotSharp.Core.Rules/Criteria/Llm/LlmCriteriaSettings.cs

RuleEngine.csGate triggering via criteria evaluators with LLM fallback +686/-659

Gate triggering via criteria evaluators with LLM fallback

• Adds evaluator resolution by type from RuleTriggerOptions.Criteria and evaluates criteria per agent before triggering. When non-LLM evaluators return null, falls back to the LLM evaluator; large portions of the prior graph-flow execution path are now commented out.

src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs

AgentRuleMongoElement.csPersist rule criteria instead of topology name +3/-3

Persist rule criteria instead of topology name

• Updates MongoDB persistence models to store and restore RuleConfig.Criteria rather than RuleConfig.TopologyName. Aligns storage with the new rule config contract.

src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentRuleMongoElement.cs

PyCodeInterpreter.csRender code-generation templates with conversation states +48/-17

Render code-generation templates with conversation states

• Defaults agent/template for code generation, prefers template-specific LLM config, and renders instructions via ITemplateRender with merged request data + conversation states. Adds CollectRenderData helper and fails fast when instruction is missing.

src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyCodeInterpreter.cs

Bug fix (1) +0 / -2
AgentController.Coding.csStop writing code processor/language into conversation state +0/-2

Stop writing code processor/language into conversation state

• Removes setting "code_processor" and "programming_language" states from the code-generation controller path. This reduces side effects in state when generating scripts.

src/Infrastructure/BotSharp.OpenAPI/Controllers/Agent/AgentController.Coding.cs

Refactor (2) +11 / -14
CodeGenerationOptions.csDecouple code generation options from LlmConfigBase +1/-1

Decouple code generation options from LlmConfigBase

• Removes inheritance from LlmConfigBase, leaving CodeGenerationOptions as a standalone DTO. This narrows the option surface to agent/template/language/data.

src/Infrastructure/BotSharp.Abstraction/Coding/Options/CodeGenerationOptions.cs

IRuleEngine.csDisable graph-node execution contract +10/-13

Disable graph-node execution contract

• Removes/Comments the ExecuteGraphNode method from the public IRuleEngine surface. This aligns the engine API with the simplified trigger flow.

src/Infrastructure/BotSharp.Abstraction/Rules/IRuleEngine.cs

Documentation (1) +4 / -5
rule-trigger-code-generate_instruction.liquidRefine code-generation prompt for trigger criteria scripts +4/-5

Refine code-generation prompt for trigger criteria scripts

• Tightens wording to require strict adherence to user request and clarifies trigger_args parsing requirements. Removes markup wording and makes args example conditional.

src/Infrastructure/BotSharp.Core/data/agents/c2a2faf6-b8b5-47fe-807b-f4714cf25dd4/templates/rule-trigger-code-generate_instruction.liquid

Other (5) +18 / -6
BotSharp.Core.Rules.csprojAdd reference to BotSharp.Core +1/-0

Add reference to BotSharp.Core

• Adds BotSharp.Core project reference so rule evaluators can use core infrastructure components (e.g., completion provider utilities).

src/Infrastructure/BotSharp.Core.Rules/BotSharp.Core.Rules.csproj

RulesPlugin.csRegister criteria evaluators in DI +6/-0

Register criteria evaluators in DI

• Registers CodeCriteriaEvaluator and LlmCriteriaEvaluator as IRuleCriteriaEvaluator implementations. This enables runtime selection via CriteriaOptions.Type.

src/Infrastructure/BotSharp.Core.Rules/RulesPlugin.cs

agent.jsonAdd explicit LLM config for Rules interpreter agent +7/-1

Add explicit LLM config for Rules interpreter agent

• Adds an llmConfig block to the Rules interpreter agent JSON (OpenAI provider, gpt-5.4-mini model). This supports LLM-based criteria evaluation via templates.

src/Infrastructure/BotSharp.Core.Rules/data/agents/201e49a2-40b3-4ccd-b8cc-2476565a1b40/agent.json

agent.jsonUpdate agent model/version settings +2/-3

Update agent model/version settings

• Updates the agent's OpenAI model selection to gpt-5.4-mini and removes the reasoning_effort_level field. Keeps max recursion depth unchanged.

src/Infrastructure/BotSharp.Core/data/agents/01e2fc5c-2c89-4ec7-8470-7688608b496c/agent.json

agent.jsonUpdate agent model and reasoning effort level +2/-2

Update agent model and reasoning effort level

• Switches model to gpt-5.4-mini and changes reasoning_effort_level from minimal to low. Keeps recursion depth at 3.

src/Infrastructure/BotSharp.Core/data/agents/c2a2faf6-b8b5-47fe-807b-f4714cf25dd4/agent.json

@qodo-code-review

qodo-code-review Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (2) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. CodeCriteriaEvaluator fails open ✓ Resolved 📘 Rule violation ☼ Reliability
Description
CodeCriteriaEvaluator.EvaluateAsync returns true when it cannot evaluate criteria (missing code
processor or missing script), causing rules to execute even though criteria validation failed. This
violates the requirement to validate inputs and fail safely at integration boundaries.
Code

src/Infrastructure/BotSharp.Core.Rules/Criteria/Code/CodeCriteriaEvaluator.cs[R27-46]

+        var settings = context.Options.GetData<CodeCriteriaSettings>() ?? new();
+        var provider = settings.CodeProcessor ?? BuiltInCodeProcessor.PyInterpreter;
+        var processor = _services.GetServices<ICodeProcessor>().FirstOrDefault(x => x.Provider.IsEqualTo(provider));
+        if (processor == null)
+        {
+            _logger.LogWarning($"Unable to find code processor: {provider}.");
+            return true;
+        }
+
+        var agentService = _services.GetRequiredService<IAgentService>();
+        var scriptName = settings.CodeScriptName ?? $"{trigger.Name}_rule.py";
+        var codeScript = await agentService.GetAgentCodeScript(agent.Id, scriptName, scriptType: AgentCodeScriptType.Src);
+
+        var msg = $"rule trigger ({trigger.Name}) code script ({scriptName}) in agent ({agent.Name}) => args: {settings.ArgumentContent?.RootElement.GetRawText()}.";
+
+        if (codeScript == null || string.IsNullOrWhiteSpace(codeScript.Content))
+        {
+            _logger.LogWarning($"Unable to find {msg}.");
+            return true;
+        }
Evidence
Rule 2 requires safe failure behavior when required inputs/dependencies are missing/invalid. The
added code explicitly returns true (allow trigger) when processor == null and when codeScript
is missing/empty, which is fail-open.

src/Infrastructure/BotSharp.Core.Rules/Criteria/Code/CodeCriteriaEvaluator.cs[27-46]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`CodeCriteriaEvaluator.EvaluateAsync` currently returns `true` when the evaluator cannot run (e.g., no `ICodeProcessor` found or no script content). This is a fail-open behavior that can trigger rules when criteria evaluation is effectively unavailable.
## Issue Context
Compliance requires validating boundary inputs/dependencies and providing safe failure behavior when required data/services are missing or invalid.
## Fix Focus Areas
- src/Infrastructure/BotSharp.Core.Rules/Criteria/Code/CodeCriteriaEvaluator.cs[27-46]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. LlmCriteriaEvaluator fails open 📘 Rule violation ☼ Reliability
Description
LlmCriteriaEvaluator.EvaluateAsync claims to fail closed but returns true when it cannot
evaluate criteria due to a missing criteria agent or missing/empty template, allowing rules to
execute despite invalid or missing inputs. This violates the requirement to validate inputs and fail
safely at integration boundaries, making LLM-based criteria unreliable under configuration drift.
Code

src/Infrastructure/BotSharp.Core.Rules/Criteria/Llm/LlmCriteriaEvaluator.cs[R46-61]

+            var agentService = _services.GetRequiredService<IAgentService>();
+            var innerAgent = await agentService.GetAgent(agentId);
+            if (innerAgent == null)
+            {
+                _logger.LogWarning($"Unable to find agent for {msg}");
+                return true;
+            }
+
+            // Render the template as the system instruction, exposing the request states.
+            var render = _services.GetRequiredService<ITemplateRender>();
+            var template = innerAgent.Templates.FirstOrDefault(x => x.Name.IsEqualTo(templateName));
+            if (template == null || string.IsNullOrWhiteSpace(template.Content))
+            {
+                _logger.LogWarning($"Unable to find agent template for {msg}");
+                return true;
+            }
Evidence
Rule 2 requires boundary validation and safe failure, and the implementation in
LlmCriteriaEvaluator.EvaluateAsync explicitly returns true when innerAgent is null and when
the criteria template is missing/empty, which permits rule execution even though no LLM criteria
evaluation occurred. This behavior contradicts the file-level documented contract that LLM
evaluation fails closed and is also inconsistent with other branches in the same method that already
return false on missing completion provider, empty response, or exceptions, showing that only the
missing-agent/template paths are incorrectly fail-open.

src/Infrastructure/BotSharp.Core.Rules/Criteria/Llm/LlmCriteriaEvaluator.cs[46-61]
src/Infrastructure/BotSharp.Core.Rules/Criteria/Llm/LlmCriteriaEvaluator.cs[12-13]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`LlmCriteriaEvaluator.EvaluateAsync` currently returns `true` (pass) when required prerequisites for LLM criteria evaluation are missing (e.g., criteria agent not found, template missing/empty), which is a fail-open behavior. This contradicts the documented “fails closed” contract and violates compliance expectations for safe failure at integration boundaries.
## Issue Context
Compliance requires explicit failure when required inputs/dependencies are missing or invalid so rule execution does not proceed as if criteria passed. The evaluator already behaves fail-closed for other error paths (e.g., missing completion provider, empty LLM response, exceptions), so the missing-agent/template branches are inconsistent and should be aligned; warning messages can also be updated to clearly state evaluation failed and the rule will not execute.
## Fix Focus Areas
- src/Infrastructure/BotSharp.Core.Rules/Criteria/Llm/LlmCriteriaEvaluator.cs[6-61]
- src/Infrastructure/BotSharp.Core.Rules/Criteria/Llm/LlmCriteriaEvaluator.cs[46-61]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Missing criteria fail-safe in engine 📘 Rule violation ☼ Reliability
Description
When options.Criteria is provided but no matching IRuleCriteriaEvaluator is registered for
options.Criteria.Type, the engine only logs a warning and still triggers agents without applying
criteria filtering, causing criteria enforcement to fail-open. This violates the requirement to
safely fail when boundary configuration is invalid or misconfigured.
Code

src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs[R30-44]

+        // Resolve the criteria evaluator
+        IRuleCriteriaEvaluator? criteriaEvaluator = null;
+        if (options?.Criteria != null)
+        {
+            criteriaEvaluator = _services.GetServices<IRuleCriteriaEvaluator>()
+                .FirstOrDefault(x => x.Type.IsEqualTo(options.Criteria.Type));
+            if (criteriaEvaluator == null)
+            {
+                _logger.LogWarning($"Unable to find rule criteria evaluator for type ({options.Criteria.Type}).");
+            }
+        }
+
       // Trigger agents
       var filteredAgents = agents.Items.Where(x => x.Rules.Exists(r => r.TriggerName.IsEqualTo(trigger.Name) && !x.Disabled)).ToList();
       foreach (var agent in filteredAgents)
Evidence
The cited code path logs a warning when it cannot resolve an evaluator for the requested criteria
type (src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs[30-39]), but later guards
criteria evaluation behind if (criteriaEvaluator != null)
(src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs[52-66]). As a result, when resolution
fails the criteria evaluation block is skipped and the engine continues sending/triggering messages
as if no criteria were provided, demonstrating a fail-open bypass on unknown or missing evaluator
types and conflicting with the safe-failure requirement for invalid configuration inputs.

src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs[30-66]
src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs[30-69]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
When `RuleTriggerOptions.Criteria` is specified but the corresponding `IRuleCriteriaEvaluator` cannot be resolved (e.g., `options.Criteria.Type` does not match any registered evaluator), `RuleEngine` currently logs a warning and proceeds to trigger agents without applying criteria filtering. This is a fail-open behavior that can bypass criteria enforcement and violates the requirement to fail safely on invalid boundary configuration.
## Issue Context
Compliance requires validating boundary configuration and behaving safely when inputs/configuration are invalid. `RuleEngine.Triggered` resolves `criteriaEvaluator` once; if it is null, the later `if (criteriaEvaluator != null)` gate skips criteria evaluation entirely, allowing unconditional triggering.
Desired behavior: when `options.Criteria` is present but no evaluator exists, do not trigger rules (fail-closed). Keep the warning log, but ensure misconfiguration is surfaced and cannot be used to bypass criteria.
Implementation options (choose one consistent with the system’s error-handling conventions):
- Return an empty result immediately.
- Treat all agents as not triggered (skip loop).
- Throw a controlled exception to surface misconfiguration.
## Fix Focus Areas
- src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs[30-66]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (2)
4. Rule flow path removed 🐞 Bug ≡ Correctness
Description
RuleEngine.Triggered no longer executes rule-flow graphs and instead always calls
SendMessageToAgent, breaking topology-based rule execution and skipping graph traversal/validation.
This is compounded by removing TopologyName from RuleConfig, leaving no per-rule way to select a
topology.
Code

src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs[R68-70]

+            var convId = await SendMessageToAgent(agent, trigger, text, states);
+            newConversationIds.Add(convId);
       }
Evidence
RuleEngine now only applies optional criteria and then always sends a message to the agent, while
rule-flow abstractions and flow options still exist, indicating an unintended behavior regression
for topology-based rules.

src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs[16-73]
src/Infrastructure/BotSharp.Abstraction/Rules/IRuleFlow.cs[5-26]
src/Infrastructure/BotSharp.Abstraction/Rules/Options/RuleFlowOptions.cs[3-40]
src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentRule.cs[16-20]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`RuleEngine.Triggered` previously supported executing rule-flow topologies (graphs) but now always sends a message directly to the agent. This breaks any rules relying on `IRuleFlow<T>`-based topology execution and removes schema/graph traversal behavior.
### Issue Context
The repository still defines rule-flow abstractions (`IRuleFlow<T>`) and flow options (`RuleFlowOptions` with `topology_name`), but `AgentRule.RuleConfig` no longer contains topology configuration. If flow execution is still a supported feature, the engine needs a way to choose and execute the topology.
### Fix Focus Areas
- src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs[16-129]
- src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentRule.cs[16-20]
- src/Infrastructure/BotSharp.Abstraction/Rules/Options/RuleTriggerOptions.cs[7-18]
- src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentRuleMongoElement.cs[33-62]
### What to change
- Decide the supported configuration source for topology selection (e.g., reintroduce `RuleConfig.TopologyName`, or add `RuleTriggerOptions.Flow` back, or both).
- Reintroduce (and update) the graph execution branch in `RuleEngine.Triggered`:
- If a topology name is present, load the graph via `IRuleFlow<FlowGraph>` and execute it.
- Otherwise fall back to `SendMessageToAgent`.
- If flow execution is intentionally deprecated, remove/retire the unused flow abstractions/options and storage fields explicitly and update docs/contracts accordingly (don’t leave large commented-out blocks).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Criteria deserialization uncaught 🐞 Bug ☼ Reliability
Description
CriteriaOptions.GetData<T>() can throw during JSON deserialization, and both LlmCriteriaEvaluator
and CodeCriteriaEvaluator call it before their try/catch blocks, allowing the exception to escape
and abort RuleEngine.Triggered. A malformed or type-incompatible Criteria.Data can therefore fail
the entire trigger operation.
Code

src/Infrastructure/BotSharp.Core.Rules/Criteria/Llm/LlmCriteriaEvaluator.cs[R32-45]

+    public async Task<bool> EvaluateAsync(Agent agent, IRuleTrigger trigger, RuleCriteriaContext context)
+    {
+        var settings = context.Options.GetData<LlmCriteriaSettings>() ?? new();
+        var rule = agent.Rules.FirstOrDefault(x => x.TriggerName.IsEqualTo(trigger.Name));
+
+        // The Rules agent hosts the criteria-check template by default.
+        var agentId = !string.IsNullOrWhiteSpace(settings.AgentId) ? settings.AgentId! : BuiltInAgentId.RulesInterpreter;
+        var templateName = !string.IsNullOrWhiteSpace(settings.TemplateName) ? settings.TemplateName! : DefaultTemplateName;
+
+        var input = BuildInput(rule?.Config, settings);
+        var msg = $"rule trigger ({trigger.Name}) llm criteria (agent {agentId}, template {templateName}).";
+
+        try
+        {
Evidence
GetData<T>() performs unchecked deserialization, and the LLM/Code evaluators call it before entering
their exception handling, so a JsonException can propagate to the engine call site.

src/Infrastructure/BotSharp.Abstraction/Rules/Options/RuleTriggerOptions.cs[39-47]
src/Infrastructure/BotSharp.Core.Rules/Criteria/Llm/LlmCriteriaEvaluator.cs[32-45]
src/Infrastructure/BotSharp.Core.Rules/Criteria/Code/CodeCriteriaEvaluator.cs[25-28]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`CriteriaOptions.GetData<T>()` deserializes raw JSON without error handling. Evaluators call it outside their try/catch, so deserialization failures escape and can abort the whole trigger request.
### Issue Context
- `CriteriaOptions.GetData<T>()` uses `JsonElement.Deserialize<T>()`, which can throw (e.g., when JSON types don’t match the target settings model).
- `LlmCriteriaEvaluator` and `CodeCriteriaEvaluator` call `GetData<...>()` before their `try` blocks.
### Fix Focus Areas
- src/Infrastructure/BotSharp.Abstraction/Rules/Options/RuleTriggerOptions.cs[39-47]
- src/Infrastructure/BotSharp.Core.Rules/Criteria/Llm/LlmCriteriaEvaluator.cs[32-45]
- src/Infrastructure/BotSharp.Core.Rules/Criteria/Code/CodeCriteriaEvaluator.cs[25-28]
### What to change
- Make deserialization safe:
- Option A (preferred): wrap `Data.Value.Deserialize<T>` in a try/catch inside `GetData<T>()` and return `default` on failure (optionally log via a passed-in logger or rethrow a controlled domain exception).
- Option B: move `GetData<...>()` calls inside each evaluator’s existing try/catch and treat deserialization failure as a fail-closed result (`false`).
- Ensure behavior is consistent with your intended fail-open/fail-closed semantics for criteria evaluation.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


To customize comments, go to the Qodo configuration screen, or learn more in the docs.

ⓘ You are approaching your monthly quota for Qodo. Upgrade your plan

Qodo Logo

Comment on lines +27 to +46
var settings = context.Options.GetData<CodeCriteriaSettings>() ?? new();
var provider = settings.CodeProcessor ?? BuiltInCodeProcessor.PyInterpreter;
var processor = _services.GetServices<ICodeProcessor>().FirstOrDefault(x => x.Provider.IsEqualTo(provider));
if (processor == null)
{
_logger.LogWarning($"Unable to find code processor: {provider}.");
return true;
}

var agentService = _services.GetRequiredService<IAgentService>();
var scriptName = settings.CodeScriptName ?? $"{trigger.Name}_rule.py";
var codeScript = await agentService.GetAgentCodeScript(agent.Id, scriptName, scriptType: AgentCodeScriptType.Src);

var msg = $"rule trigger ({trigger.Name}) code script ({scriptName}) in agent ({agent.Name}) => args: {settings.ArgumentContent?.RootElement.GetRawText()}.";

if (codeScript == null || string.IsNullOrWhiteSpace(codeScript.Content))
{
_logger.LogWarning($"Unable to find {msg}.");
return true;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Action required

1. codecriteriaevaluator fails open 📘 Rule violation ☼ Reliability

CodeCriteriaEvaluator.EvaluateAsync returns true when it cannot evaluate criteria (missing code
processor or missing script), causing rules to execute even though criteria validation failed. This
violates the requirement to validate inputs and fail safely at integration boundaries.
Agent Prompt
## Issue description
`CodeCriteriaEvaluator.EvaluateAsync` currently returns `true` when the evaluator cannot run (e.g., no `ICodeProcessor` found or no script content). This is a fail-open behavior that can trigger rules when criteria evaluation is effectively unavailable.

## Issue Context
Compliance requires validating boundary inputs/dependencies and providing safe failure behavior when required data/services are missing or invalid.

## Fix Focus Areas
- src/Infrastructure/BotSharp.Core.Rules/Criteria/Code/CodeCriteriaEvaluator.cs[27-46]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +46 to +61
var agentService = _services.GetRequiredService<IAgentService>();
var innerAgent = await agentService.GetAgent(agentId);
if (innerAgent == null)
{
_logger.LogWarning($"Unable to find agent for {msg}");
return true;
}

// Render the template as the system instruction, exposing the request states.
var render = _services.GetRequiredService<ITemplateRender>();
var template = innerAgent.Templates.FirstOrDefault(x => x.Name.IsEqualTo(templateName));
if (template == null || string.IsNullOrWhiteSpace(template.Content))
{
_logger.LogWarning($"Unable to find agent template for {msg}");
return true;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Action required

2. llmcriteriaevaluator fails open 📘 Rule violation ☼ Reliability

LlmCriteriaEvaluator.EvaluateAsync claims to fail closed but returns true when it cannot
evaluate criteria due to a missing criteria agent or missing/empty template, allowing rules to
execute despite invalid or missing inputs. This violates the requirement to validate inputs and fail
safely at integration boundaries, making LLM-based criteria unreliable under configuration drift.
Agent Prompt
## Issue description
`LlmCriteriaEvaluator.EvaluateAsync` currently returns `true` (pass) when required prerequisites for LLM criteria evaluation are missing (e.g., criteria agent not found, template missing/empty), which is a fail-open behavior. This contradicts the documented “fails closed” contract and violates compliance expectations for safe failure at integration boundaries.

## Issue Context
Compliance requires explicit failure when required inputs/dependencies are missing or invalid so rule execution does not proceed as if criteria passed. The evaluator already behaves fail-closed for other error paths (e.g., missing completion provider, empty LLM response, exceptions), so the missing-agent/template branches are inconsistent and should be aligned; warning messages can also be updated to clearly state evaluation failed and the rule will not execute.

## Fix Focus Areas
- src/Infrastructure/BotSharp.Core.Rules/Criteria/Llm/LlmCriteriaEvaluator.cs[6-61]
- src/Infrastructure/BotSharp.Core.Rules/Criteria/Llm/LlmCriteriaEvaluator.cs[46-61]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +30 to 44
// Resolve the criteria evaluator
IRuleCriteriaEvaluator? criteriaEvaluator = null;
if (options?.Criteria != null)
{
criteriaEvaluator = _services.GetServices<IRuleCriteriaEvaluator>()
.FirstOrDefault(x => x.Type.IsEqualTo(options.Criteria.Type));
if (criteriaEvaluator == null)
{
_logger.LogWarning($"Unable to find rule criteria evaluator for type ({options.Criteria.Type}).");
}
}

// Trigger agents
var filteredAgents = agents.Items.Where(x => x.Rules.Exists(r => r.TriggerName.IsEqualTo(trigger.Name) && !x.Disabled)).ToList();
foreach (var agent in filteredAgents)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Action required

3. Missing criteria fail-safe in engine 📘 Rule violation ☼ Reliability

When options.Criteria is provided but no matching IRuleCriteriaEvaluator is registered for
options.Criteria.Type, the engine only logs a warning and still triggers agents without applying
criteria filtering, causing criteria enforcement to fail-open. This violates the requirement to
safely fail when boundary configuration is invalid or misconfigured.
Agent Prompt
## Issue description
When `RuleTriggerOptions.Criteria` is specified but the corresponding `IRuleCriteriaEvaluator` cannot be resolved (e.g., `options.Criteria.Type` does not match any registered evaluator), `RuleEngine` currently logs a warning and proceeds to trigger agents without applying criteria filtering. This is a fail-open behavior that can bypass criteria enforcement and violates the requirement to fail safely on invalid boundary configuration.

## Issue Context
Compliance requires validating boundary configuration and behaving safely when inputs/configuration are invalid. `RuleEngine.Triggered` resolves `criteriaEvaluator` once; if it is null, the later `if (criteriaEvaluator != null)` gate skips criteria evaluation entirely, allowing unconditional triggering.

Desired behavior: when `options.Criteria` is present but no evaluator exists, do not trigger rules (fail-closed). Keep the warning log, but ensure misconfiguration is surfaced and cannot be used to bypass criteria.

Implementation options (choose one consistent with the system’s error-handling conventions):
- Return an empty result immediately.
- Treat all agents as not triggered (skip loop).
- Throw a controlled exception to surface misconfiguration.

## Fix Focus Areas
- src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs[30-66]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +68 to 70
var convId = await SendMessageToAgent(agent, trigger, text, states);
newConversationIds.Add(convId);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Action required

4. Rule flow path removed 🐞 Bug ≡ Correctness

RuleEngine.Triggered no longer executes rule-flow graphs and instead always calls
SendMessageToAgent, breaking topology-based rule execution and skipping graph traversal/validation.
This is compounded by removing TopologyName from RuleConfig, leaving no per-rule way to select a
topology.
Agent Prompt
### Issue description
`RuleEngine.Triggered` previously supported executing rule-flow topologies (graphs) but now always sends a message directly to the agent. This breaks any rules relying on `IRuleFlow<T>`-based topology execution and removes schema/graph traversal behavior.

### Issue Context
The repository still defines rule-flow abstractions (`IRuleFlow<T>`) and flow options (`RuleFlowOptions` with `topology_name`), but `AgentRule.RuleConfig` no longer contains topology configuration. If flow execution is still a supported feature, the engine needs a way to choose and execute the topology.

### Fix Focus Areas
- src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs[16-129]
- src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentRule.cs[16-20]
- src/Infrastructure/BotSharp.Abstraction/Rules/Options/RuleTriggerOptions.cs[7-18]
- src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentRuleMongoElement.cs[33-62]

### What to change
- Decide the supported configuration source for topology selection (e.g., reintroduce `RuleConfig.TopologyName`, or add `RuleTriggerOptions.Flow` back, or both).
- Reintroduce (and update) the graph execution branch in `RuleEngine.Triggered`:
  - If a topology name is present, load the graph via `IRuleFlow<FlowGraph>` and execute it.
  - Otherwise fall back to `SendMessageToAgent`.
- If flow execution is intentionally deprecated, remove/retire the unused flow abstractions/options and storage fields explicitly and update docs/contracts accordingly (don’t leave large commented-out blocks).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +32 to +45
public async Task<bool> EvaluateAsync(Agent agent, IRuleTrigger trigger, RuleCriteriaContext context)
{
var settings = context.Options.GetData<LlmCriteriaSettings>() ?? new();
var rule = agent.Rules.FirstOrDefault(x => x.TriggerName.IsEqualTo(trigger.Name));

// The Rules agent hosts the criteria-check template by default.
var agentId = !string.IsNullOrWhiteSpace(settings.AgentId) ? settings.AgentId! : BuiltInAgentId.RulesInterpreter;
var templateName = !string.IsNullOrWhiteSpace(settings.TemplateName) ? settings.TemplateName! : DefaultTemplateName;

var input = BuildInput(rule?.Config, settings);
var msg = $"rule trigger ({trigger.Name}) llm criteria (agent {agentId}, template {templateName}).";

try
{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Action required

5. Criteria deserialization uncaught 🐞 Bug ☼ Reliability

CriteriaOptions.GetData<T>() can throw during JSON deserialization, and both LlmCriteriaEvaluator
and CodeCriteriaEvaluator call it before their try/catch blocks, allowing the exception to escape
and abort RuleEngine.Triggered. A malformed or type-incompatible Criteria.Data can therefore fail
the entire trigger operation.
Agent Prompt
### Issue description
`CriteriaOptions.GetData<T>()` deserializes raw JSON without error handling. Evaluators call it outside their try/catch, so deserialization failures escape and can abort the whole trigger request.

### Issue Context
- `CriteriaOptions.GetData<T>()` uses `JsonElement.Deserialize<T>()`, which can throw (e.g., when JSON types don’t match the target settings model).
- `LlmCriteriaEvaluator` and `CodeCriteriaEvaluator` call `GetData<...>()` before their `try` blocks.

### Fix Focus Areas
- src/Infrastructure/BotSharp.Abstraction/Rules/Options/RuleTriggerOptions.cs[39-47]
- src/Infrastructure/BotSharp.Core.Rules/Criteria/Llm/LlmCriteriaEvaluator.cs[32-45]
- src/Infrastructure/BotSharp.Core.Rules/Criteria/Code/CodeCriteriaEvaluator.cs[25-28]

### What to change
- Make deserialization safe:
  - Option A (preferred): wrap `Data.Value.Deserialize<T>` in a try/catch inside `GetData<T>()` and return `default` on failure (optionally log via a passed-in logger or rethrow a controlled domain exception).
  - Option B: move `GetData<...>()` calls inside each evaluator’s existing try/catch and treat deserialization failure as a fail-closed result (`false`).
- Ensure behavior is consistent with your intended fail-open/fail-closed semantics for criteria evaluation.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@iceljc
iceljc marked this pull request as ready for review August 5, 2026 14:47
@iceljc
iceljc merged commit e2f09ce into SciSharp:master Aug 5, 2026
4 checks passed
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. LLM criteria fails open 🐞 Bug ≡ Correctness
Description
LlmCriteriaEvaluator returns true when the criteria agent or template is missing, causing rules to
execute even though criteria was not evaluated (contradicts its own fail-closed contract). This can
unintentionally bypass rule gating for any trigger using LLM criteria or LLM fallback.
Code

src/Infrastructure/BotSharp.Core.Rules/Criteria/Llm/LlmCriteriaEvaluator.cs[R50-53]

+            if (innerAgent == null)
+            {
+                _logger.LogWarning($"Unable to find agent for {msg}");
+                return true;
Evidence
The class documentation states it fails closed (returns false), but the implementation returns true
when required resources are missing, which makes the rule engine execute the rule despite inability
to evaluate criteria.

src/Infrastructure/BotSharp.Core.Rules/Criteria/Llm/LlmCriteriaEvaluator.cs[12-15]
src/Infrastructure/BotSharp.Core.Rules/Criteria/Llm/LlmCriteriaEvaluator.cs[48-63]
src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs[51-69]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`LlmCriteriaEvaluator` is documented to fail closed (return `false`) but it currently returns `true` when it cannot find the inner criteria agent or the requested template. This makes the rule engine treat the criteria as satisfied and execute the rule.

### Issue Context
The rule engine uses the evaluator's boolean result to decide whether to skip or execute the agent rule. Returning `true` on missing dependencies effectively bypasses criteria enforcement.

### Fix Focus Areas
- src/Infrastructure/BotSharp.Core.Rules/Criteria/Llm/LlmCriteriaEvaluator.cs[12-15]
- src/Infrastructure/BotSharp.Core.Rules/Criteria/Llm/LlmCriteriaEvaluator.cs[48-63]

### Proposed fix
- Change both `return true;` branches (missing agent, missing template/content) to `return false;` (or `return null;` if you want the engine to treat it as “could not evaluate”, but since this is the fallback evaluator, `false` is the safer/consistent option).
- Keep behavior consistent with the class doc: other error paths already return `false`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Unknown type bypasses criteria 🐞 Bug ⛨ Security
Description
If options.Criteria.Type doesn't match any registered IRuleCriteriaEvaluator, RuleEngine only logs a
warning and continues without evaluating criteria, so rules execute unconditionally. This can
unintentionally bypass criteria enforcement on misconfiguration/typos.
Code

src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs[R34-37]

+            criteriaEvaluator = ResolveCriteriaEvaluator(options.Criteria.Type);
+            if (criteriaEvaluator == null)
+            {
+                _logger.LogWarning($"Unable to find rule criteria evaluator for type ({options.Criteria.Type}).");
Evidence
RuleEngine resolves the evaluator and only logs if it is missing; later it performs the criteria
check only when criteriaEvaluator != null, otherwise it still triggers agents via
SendMessageToAgent.

src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs[30-39]
src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs[51-69]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
When `options.Criteria` is provided but `ResolveCriteriaEvaluator(type)` returns null, the engine logs a warning and proceeds to send messages to agents without any criteria check.

### Issue Context
The criteria check is only performed under `if (criteriaEvaluator != null)`. A misspelled/unsupported type therefore disables criteria gating.

### Fix Focus Areas
- src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs[30-69]

### Proposed fix
Implement fail-closed behavior when criteria is explicitly requested but cannot be evaluated:
- Option A (recommended): if evaluator not found, skip triggering (return empty list / continue) and log warning.
- Option B: fall back to the built-in LLM evaluator when the requested evaluator type is unknown (similar to the existing null-result fallback path).

Make sure the engine does not execute rules when a criteria type is specified but no evaluator exists.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Criteria data can throw 🐞 Bug ☼ Reliability
Description
CriteriaOptions.GetData<T>() deserializes JsonElement without handling JsonException; malformed or
non-convertible Criteria.Data can throw and abort the Triggered request. This exception occurs
before evaluator try/catch blocks (e.g., CodeCriteriaEvaluator/LlmCriteriaEvaluator) so it can
propagate out of the rule engine.
Code

src/Infrastructure/BotSharp.Abstraction/Rules/Options/RuleTriggerOptions.cs[R43-46]

+            return default;
+        }
+
+        return Data.Value.Deserialize<T>(options ?? _webJsonOptions);
Evidence
GetData<T>() has no exception handling, and evaluators call it outside their try/catch blocks;
RuleEngine doesn't guard evaluator invocation either, so deserialization errors can escape and fail
the Triggered call.

src/Infrastructure/BotSharp.Abstraction/Rules/Options/RuleTriggerOptions.cs[39-47]
src/Infrastructure/BotSharp.Core.Rules/Criteria/Code/CodeCriteriaEvaluator.cs[28-33]
src/Infrastructure/BotSharp.Core.Rules/Criteria/Llm/LlmCriteriaEvaluator.cs[33-36]
src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs[86-96]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`CriteriaOptions.GetData<T>()` calls `JsonElement.Deserialize<T>()` directly. If `Criteria.Data` is malformed or incompatible with `T`, deserialization can throw and fail the entire rule-trigger request.

### Issue Context
Both `CodeCriteriaEvaluator` and `LlmCriteriaEvaluator` call `context.Options.GetData<...>()` before their `try {}` blocks, and `RuleEngine.EvaluateCriteria` does not guard `EvaluateAsync`, so exceptions from `GetData<T>()` can propagate.

### Fix Focus Areas
- src/Infrastructure/BotSharp.Abstraction/Rules/Options/RuleTriggerOptions.cs[39-47]
- src/Infrastructure/BotSharp.Core.Rules/Criteria/Code/CodeCriteriaEvaluator.cs[28-33]
- src/Infrastructure/BotSharp.Core.Rules/Criteria/Llm/LlmCriteriaEvaluator.cs[33-36]
- src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs[86-96]

### Proposed fix
- Wrap `Data.Value.Deserialize<T>(...)` in a `try/catch (JsonException)` (and optionally `NotSupportedException`) inside `GetData<T>()` and return `default` on failure.
- Optionally add a safe log point (if you don't want logging in Abstraction): catch in evaluators or `RuleEngine.EvaluateCriteria` and treat it as `null`/`false` so criteria enforcement doesn't crash the request.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment on lines +50 to +53
if (innerAgent == null)
{
_logger.LogWarning($"Unable to find agent for {msg}");
return true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Action required

1. Llm criteria fails open 🐞 Bug ≡ Correctness

LlmCriteriaEvaluator returns true when the criteria agent or template is missing, causing rules to
execute even though criteria was not evaluated (contradicts its own fail-closed contract). This can
unintentionally bypass rule gating for any trigger using LLM criteria or LLM fallback.
Agent Prompt
### Issue description
`LlmCriteriaEvaluator` is documented to fail closed (return `false`) but it currently returns `true` when it cannot find the inner criteria agent or the requested template. This makes the rule engine treat the criteria as satisfied and execute the rule.

### Issue Context
The rule engine uses the evaluator's boolean result to decide whether to skip or execute the agent rule. Returning `true` on missing dependencies effectively bypasses criteria enforcement.

### Fix Focus Areas
- src/Infrastructure/BotSharp.Core.Rules/Criteria/Llm/LlmCriteriaEvaluator.cs[12-15]
- src/Infrastructure/BotSharp.Core.Rules/Criteria/Llm/LlmCriteriaEvaluator.cs[48-63]

### Proposed fix
- Change both `return true;` branches (missing agent, missing template/content) to `return false;` (or `return null;` if you want the engine to treat it as “could not evaluate”, but since this is the fallback evaluator, `false` is the safer/consistent option).
- Keep behavior consistent with the class doc: other error paths already return `false`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +43 to +46
return default;
}

return Data.Value.Deserialize<T>(options ?? _webJsonOptions);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Remediation recommended

2. Criteria data can throw 🐞 Bug ☼ Reliability

CriteriaOptions.GetData<T>() deserializes JsonElement without handling JsonException; malformed or
non-convertible Criteria.Data can throw and abort the Triggered request. This exception occurs
before evaluator try/catch blocks (e.g., CodeCriteriaEvaluator/LlmCriteriaEvaluator) so it can
propagate out of the rule engine.
Agent Prompt
### Issue description
`CriteriaOptions.GetData<T>()` calls `JsonElement.Deserialize<T>()` directly. If `Criteria.Data` is malformed or incompatible with `T`, deserialization can throw and fail the entire rule-trigger request.

### Issue Context
Both `CodeCriteriaEvaluator` and `LlmCriteriaEvaluator` call `context.Options.GetData<...>()` before their `try {}` blocks, and `RuleEngine.EvaluateCriteria` does not guard `EvaluateAsync`, so exceptions from `GetData<T>()` can propagate.

### Fix Focus Areas
- src/Infrastructure/BotSharp.Abstraction/Rules/Options/RuleTriggerOptions.cs[39-47]
- src/Infrastructure/BotSharp.Core.Rules/Criteria/Code/CodeCriteriaEvaluator.cs[28-33]
- src/Infrastructure/BotSharp.Core.Rules/Criteria/Llm/LlmCriteriaEvaluator.cs[33-36]
- src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs[86-96]

### Proposed fix
- Wrap `Data.Value.Deserialize<T>(...)` in a `try/catch (JsonException)` (and optionally `NotSupportedException`) inside `GetData<T>()` and return `default` on failure.
- Optionally add a safe log point (if you don't want logging in Abstraction): catch in evaluators or `RuleEngine.EvaluateCriteria` and treat it as `null`/`false` so criteria enforcement doesn't crash the request.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +34 to +37
criteriaEvaluator = ResolveCriteriaEvaluator(options.Criteria.Type);
if (criteriaEvaluator == null)
{
_logger.LogWarning($"Unable to find rule criteria evaluator for type ({options.Criteria.Type}).");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Action required

3. Unknown type bypasses criteria 🐞 Bug ⛨ Security

If options.Criteria.Type doesn't match any registered IRuleCriteriaEvaluator, RuleEngine only logs a
warning and continues without evaluating criteria, so rules execute unconditionally. This can
unintentionally bypass criteria enforcement on misconfiguration/typos.
Agent Prompt
### Issue description
When `options.Criteria` is provided but `ResolveCriteriaEvaluator(type)` returns null, the engine logs a warning and proceeds to send messages to agents without any criteria check.

### Issue Context
The criteria check is only performed under `if (criteriaEvaluator != null)`. A misspelled/unsupported type therefore disables criteria gating.

### Fix Focus Areas
- src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs[30-69]

### Proposed fix
Implement fail-closed behavior when criteria is explicitly requested but cannot be evaluated:
- Option A (recommended): if evaluator not found, skip triggering (return empty list / continue) and log warning.
- Option B: fall back to the built-in LLM evaluator when the requested evaluator type is unknown (similar to the existing null-result fallback path).

Make sure the engine does not execute rules when a criteria type is specified but no evaluator exists.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant