This is an automated email from the ASF dual-hosted git repository.

tkobayas pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-kie-drools.git


The following commit(s) were added to refs/heads/main by this push:
     new bf78d7c0bf [incubator-kie-drools-6421] Rules in agenda group with 
auto-focus may… (#6447)
bf78d7c0bf is described below

commit bf78d7c0bf05076f24f2f14af2970a629288fee7
Author: Toshiya Kobayashi <[email protected]>
AuthorDate: Wed Sep 10 16:18:46 2025 +0900

    [incubator-kie-drools-6421] Rules in agenda group with auto-focus may… 
(#6447)
    
    * [incubator-kie-drools-6421] Rules in agenda group with auto-focus may not 
fire immediately (or at all) after activation
    - Insert case
    
    * cleanup
---
 .../org/drools/core/reteoo/AlphaTerminalNode.java  |   4 +-
 .../model/codegen/execmodel/FiringOrderTest.java   | 141 +++++++++++++++++++++
 2 files changed, 144 insertions(+), 1 deletion(-)

diff --git 
a/drools-core/src/main/java/org/drools/core/reteoo/AlphaTerminalNode.java 
b/drools-core/src/main/java/org/drools/core/reteoo/AlphaTerminalNode.java
index b78b559677..0d2241fd80 100644
--- a/drools-core/src/main/java/org/drools/core/reteoo/AlphaTerminalNode.java
+++ b/drools-core/src/main/java/org/drools/core/reteoo/AlphaTerminalNode.java
@@ -51,7 +51,9 @@ public class AlphaTerminalNode extends LeftInputAdapterNode {
             leftTuple.setPropagationContext( propagationContext );
 
             if ( rtn.getRule().getAutoFocus() && 
!agendaItem.getAgendaGroup().isActive() ) {
-                activationsManager.getAgendaGroupsManager().setFocus( 
agendaItem.getAgendaGroup() );
+                if 
(activationsManager.getAgendaGroupsManager().setFocus(agendaItem.getAgendaGroup()))
 {
+                    activationsManager.haltGroupEvaluation();
+                }
             }
 
             PhreakRuleTerminalNode.doLeftTupleInsert( rtn, 
agendaItem.getRuleExecutor(), activationsManager, agendaItem, leftTuple );
diff --git 
a/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/FiringOrderTest.java
 
b/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/FiringOrderTest.java
new file mode 100644
index 0000000000..c77872b5de
--- /dev/null
+++ 
b/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/FiringOrderTest.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.drools.model.codegen.execmodel;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.kie.api.event.rule.AgendaEventListener;
+import org.kie.api.event.rule.BeforeMatchFiredEvent;
+import org.kie.api.event.rule.DefaultAgendaEventListener;
+import org.kie.api.runtime.KieSession;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class FiringOrderTest extends BaseModelTest {
+
+    private static final org.slf4j.Logger log = 
org.slf4j.LoggerFactory.getLogger(FiringOrderTest.class);
+
+    public static class State {
+
+        private int value;
+        private boolean aFired;
+
+        public State(int value, boolean aFired) {
+            this.value = value;
+            this.aFired = aFired;
+        }
+
+        public int getValue() {
+            return value;
+        }
+
+        public void setValue(int value) {
+            this.value = value;
+        }
+
+        public boolean isAFired() {
+            return aFired;
+        }
+
+        public void setAFired(boolean aFired) {
+            this.aFired = aFired;
+        }
+    }
+
+    private static final String DRL_INSERT_FACT = """
+                                      package com.example.drools
+                                      
+                                      import %s.State;
+                                      
+                                      global java.util.List firingOrder;
+                                      
+                                      // Rule A: in ruleflow-group XGroup. It 
modifies State so that B and C become eligible.
+                                      rule "A"
+                                          ruleflow-group "XGroup"
+                                      when
+                                          $s : State(aFired == false, value == 
0)
+                                      then
+                                          // Mark A as fired and set value to 
1 so that B condition is met
+                                          modify($s) { setAFired(true), 
setValue(1) };
+                                      
+                                          // Insert new fact to make C 
eligible (modification of existing fact does not necessarily trigger auto-focus)
+                                          insert(new State(2, false));
+                                      end
+                                      
+                                      // Rule B: in ruleflow-group XGroup 
(same as A).
+                                      rule "B"
+                                          ruleflow-group "XGroup"
+                                      when
+                                          State(value == 1, aFired == true)
+                                      then
+                                      end
+                                      
+                                      // Rule C: in ruleflow-group YGroup with 
auto-focus true.
+                                      // When this rule becomes active, it 
will push its ruleflow group to the focus stack.
+                                      rule "C"
+                                          ruleflow-group "YGroup"
+                                          auto-focus true
+                                      when
+                                          State(value == 2, aFired == false)
+                                      then
+                                      end
+                                      
""".formatted(FiringOrderTest.class.getName());
+
+    @ParameterizedTest
+    @MethodSource("parameters")
+    void ruleCActivatesBeforeRuleBInsertTest(RUN_TYPE runType) {
+        final KieSession kieSession = getKieSession(runType, DRL_INSERT_FACT);
+
+        try {
+            final List<String> firingOrder = new ArrayList<>();
+
+            // Listener to capture firing order for B and C
+            final AgendaEventListener listener = new 
DefaultAgendaEventListener() {
+                @Override
+                public void beforeMatchFired(BeforeMatchFiredEvent event) {
+                    firingOrder.add(event.getMatch().getRule().getName());
+                }
+            };
+            kieSession.addEventListener(listener);
+
+            // Global for firing order from the DRL RHS for additional 
verification
+            kieSession.setGlobal("firingOrder", firingOrder);
+
+            // Initial fact to trigger A
+            State state = new State(0, false);
+            kieSession.insert(state);
+
+            // Ensure we start in ruleflow-group X so A fires first
+            kieSession.getAgenda().getAgendaGroup("XGroup").setFocus();
+
+            final int fired = kieSession.fireAllRules();
+
+            // We expect all three to have fired
+            assertThat(fired).as("Expected three rules to fire: A, C, 
B").isEqualTo(3);
+
+            // Verify firing order: A will first, then C (due to auto-focus to 
ruleflow-group Y), then B
+            assertThat(firingOrder).as("Firing order should be [A, C, 
B]").containsExactly("A", "C", "B");
+        } finally {
+            kieSession.dispose();
+        }
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to