Copilot commented on code in PR #7160:
URL: https://github.com/apache/incubator-kie/pull/7160#discussion_r4409610839


##########
drools-model/drools-canonical-model/src/main/java/org/drools/model/impl/ViewPatternBuilder.java:
##########
@@ -194,6 +219,235 @@ public static Condition ruleItem2Condition(RuleItem 
ruleItem) {
         throw new UnsupportedOperationException( "Unknown " + ruleItem );
     }
 
+    private static boolean hasPositiveTrigger(SequenceStep step) {
+        if (step instanceof ExistentialExprViewItem) {
+            ExistentialExprViewItem ex = (ExistentialExprViewItem) step;
+            if (ex.getType() == Condition.Type.NOT) {
+                return false;
+            }
+            return true;
+        }
+        if (step instanceof CombinedExprViewItem) {
+            CombinedExprViewItem comb = (CombinedExprViewItem) step;
+            Condition.Type type = comb.getType();
+            if (type == Condition.Type.NOT || type == Condition.Type.NOR
+                    || type == Condition.Type.XOR || type == 
Condition.Type.XNOR) {
+                return false;
+            }
+            if (type == Condition.Type.AND) {
+                // In an AND composite, at least one child must be a positive 
trigger
+                for (org.drools.model.view.ViewItem expr : 
comb.getExpressions()) {
+                    if (expr instanceof SequenceStep) {
+                        if (hasPositiveTrigger((SequenceStep) expr)) {
+                            return true;
+                        }
+                    } else {
+                        return true;
+                    }
+                }
+                return false;
+            }
+            if (type == Condition.Type.OR) {
+                // In an OR composite, all branches must have a positive 
trigger
+                for (org.drools.model.view.ViewItem expr : 
comb.getExpressions()) {
+                    if (expr instanceof SequenceStep) {
+                        if (!hasPositiveTrigger((SequenceStep) expr)) {
+                            return false;
+                        }
+                    }
+                }
+                return true;
+            }
+        }
+        return true;
+    }
+
+    private static boolean isAbsenceStep(SequenceStep step) {
+        if (step instanceof ExistentialExprViewItem) {
+            return ((ExistentialExprViewItem) step).getType() == 
Condition.Type.NOT;
+        }
+        if (step instanceof CombinedExprViewItem) {
+            Condition.Type type = ((CombinedExprViewItem) step).getType();
+            return type == Condition.Type.NOT || type == Condition.Type.NOR;
+        }
+        throw new IllegalStateException("Unexpected SequenceStep type: " + 
step.getClass());
+    }
+
+    private static void 
validateActivationMatching(org.drools.model.view.ViewItem item) {
+        if (item instanceof CombinedExprViewItem) {
+            CombinedExprViewItem comb = (CombinedExprViewItem) item;
+            Condition.Type type = comb.getType();
+            for (org.drools.model.view.ViewItem expr : comb.getExpressions()) {
+                validateActivationMatching(expr);
+            }
+            if (type == Condition.Type.XOR) {
+                int matchedChildren = 0;
+                for (org.drools.model.view.ViewItem expr : 
comb.getExpressions()) {
+                    if (matchesOnActivation(expr)) {
+                        matchedChildren++;
+                    }
+                }
+                if (matchedChildren >= 2) {
+                    throw new IllegalArgumentException(
+                        "sequence(): oneOf() has " + matchedChildren + " 
children that match on step activation (0 inputs), " +
+                        "causing an immediate over-match veto upon activation. 
oneOf() requires exactly one child to match.");
+                }
+            }
+        }
+    }
+
+    private static boolean matchesOnActivation(org.drools.model.view.ViewItem 
item) {
+        if (item instanceof CombinedExprViewItem) {
+            CombinedExprViewItem comb = (CombinedExprViewItem) item;
+            Condition.Type type = comb.getType();
+            if (type == Condition.Type.XNOR) {
+                int matched = 0;
+                int total = comb.getExpressions().length;
+                for (org.drools.model.view.ViewItem expr : 
comb.getExpressions()) {
+                    if (matchesOnActivation(expr)) {
+                        matched++;
+                    }
+                }
+                return matched == 0 || matched == total;
+            }
+            if (type == Condition.Type.XOR) {
+                int matched = 0;
+                for (org.drools.model.view.ViewItem expr : 
comb.getExpressions()) {
+                    if (matchesOnActivation(expr)) {
+                        matched++;
+                    }
+                }
+                return matched == 1;
+            }
+            if (type == Condition.Type.AND) {
+                for (org.drools.model.view.ViewItem expr : 
comb.getExpressions()) {
+                    if (!matchesOnActivation(expr)) {
+                        return false;
+                    }
+                }
+                return comb.getExpressions().length > 0;
+            }
+            if (type == Condition.Type.OR) {
+                for (org.drools.model.view.ViewItem expr : 
comb.getExpressions()) {
+                    if (matchesOnActivation(expr)) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+            if (type == Condition.Type.NOT || type == Condition.Type.NOR) {
+                return false;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Rewrites runs of revertible gate steps (such as {@code oneOf()} / XOR 
and {@code noneOrAll()} / XNOR)
+     * by folding them together with their immediately following positive 
trigger into an {@code and(...)}
+     * composite step.
+     * <p>
+     * Standalone XOR/XNOR gates are non-propagating on their own within the 
sequence pipeline and require
+     * a following positive pattern step to act as the activation trigger. 
This transformation:
+     * <ul>
+     *   <li>Separates and emits any interleaved absence guards ({@code 
NOT}/{@code NOR}/{@code absent()})
+     *       before the folded composite so that compiler accumulation loops 
build them as independent
+     *       veto side-channels.</li>
+     *   <li>Folds consecutive XOR/XNOR gate steps and the subsequent positive 
trigger step into a single
+     *       {@link CombinedExprViewItem} of type {@link 
Condition.Type#AND}.</li>
+     * </ul>
+     *
+     * @param steps the original sequence steps
+     * @return the rewritten sequence steps with folded gate composites
+     */
+    private static SequenceStep[] rewriteRevertingGateSteps(SequenceStep[] 
steps) {
+        boolean hasXor = false;
+        for (int i = 0; i < steps.length; i++) {
+            SequenceStep step = steps[i];
+            if (step instanceof CombinedExprViewItem) {
+                Condition.Type t = ((CombinedExprViewItem) step).getType();
+                if ((t == Condition.Type.XOR || t == Condition.Type.XNOR) && i 
+ 1 < steps.length) {

Review Comment:
   Only bare XOR/XNOR steps are normalized, so `sequence(start, 
and(oneOf(a,b)), ack)` remains three runtime steps. Matching `a` completes the 
middle step and unregisters `b`, so `start, a, b, ack` fires instead of vetoing 
the attempt. Fold guard-only composites into their following positive trigger, 
or reject them at intermediate positions as well as at the end.



##########
drools-base/src/main/java/org/drools/base/reteoo/sequencing/steps/LogicCircuitStep.java:
##########
@@ -38,7 +38,7 @@ public LogicCircuit getCircuit() {
 
     public void activate(SequenceMemory sequenceMemory, ValueResolver 
valueResolver) {
         for (LogicGate gate : circuit.getGates()) {
-            gate.activate(sequenceMemory);
+            gate.activate(sequenceMemory, valueResolver);

Review Comment:
   `gate.activate()` can synchronously advance the sequence, but this loop then 
activates remaining gates from the consumed step. For `sequence(start, 
or(noneOrAll(a,b), other), ack)`, activation advances to `ack` and then 
re-registers `other`, allowing an `other` event to complete the sequence 
without an acknowledgement. Stop activating the old circuit when the current 
step changes.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to