You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kie.apache.org by ri...@apache.org on 2026/09/14 07:36:19 UTC

(incubator-kie) branch main updated: feat(sequencing): introduce sequence() DSL and Phreak SequenceNode (#6909)

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

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


The following commit(s) were added to refs/heads/main by this push:
     new d97dd8bd20b feat(sequencing): introduce sequence() DSL and Phreak SequenceNode (#6909)
d97dd8bd20b is described below

commit d97dd8bd20b5caf046939dd58ec3307b9b2b77d7
Author: Toni Rikkola <to...@rikkola.net>
AuthorDate: Mon Sep 14 10:36:14 2026 +0300

    feat(sequencing): introduce sequence() DSL and Phreak SequenceNode (#6909)
    
    Adds a new rule condition type that matches an ordered sequence of events.
    
    New runtime infrastructure (drools-base, drools-core):
    - Sequence / Sequencer / SequencerMemory: data model for a named sequence
      of steps; each step is a LogicCircuit of AND/OR gates over alpha-filtered
      event slots
    - SignalProcessor hierarchy (LogicGate, TerminatingSignalProcessor, etc.):
      bit-mask signal propagation that advances or terminates a sequence on
      each matching fact
    - DynamicFilter / DynamicFilterProto / SignalAdapter: per-session filters
      that attach signal adapters to the active alpha network while a step is
      live
    - SequenceNode (LeftTupleSource): new Rete/Phreak node that hosts the
      sequencer; SequencerMemoryImpl holds per-left-tuple sequencer state
    - SequenceBuilder / PhreakNodeFactory: build-time wiring of SequenceNode
      into the Rete network; CombinedAlphaConstraint aggregates per-step
      alpha constraints into a single filter slot
    
    Canonical model DSL (drools-canonical-model):
    - PatternDSL.sequence() entry point; SequenceViewItem and SequenceStep
      marker interface; SequenceConditionImpl (Condition.Type.SEQUENCE)
    - PatternDSL.or() / and() overloads for use inside sequence steps
    - PatternDef now extends SequenceStep so patterns compose directly as steps
    
    Compiler integration (drools-model-compiler):
    - KiePackagesBuilder: translate SequenceConditionImpl → Sequence/Sequencer
      and wire the SequenceNode during rule compilation
    - LambdaConsequence: supply sequencer fact-handles to rule consequences
      so matched facts are accessible from RHS code
    
    Fixes: apache/incubator-kie#7059
    
    Co-authored-by: Toni Rikkola <to...@ibm.com>
---
 .../base/definitions/rule/impl/RuleImpl.java       |   6 +
 .../java/org/drools/base/reteoo/DynamicFilter.java |  87 ++++
 .../org/drools/base/reteoo/DynamicFilterProto.java |  33 +-
 .../java/org/drools/base/reteoo/NodeTypeEnums.java |   1 +
 .../java/org/drools/base/reteoo/SignalAdapter.java |  48 ++
 .../drools/base/reteoo/sequencing/Sequence.java    | 328 ++++++++++++
 .../drools/base/reteoo/sequencing/Sequencer.java   |  70 +++
 .../base/reteoo/sequencing/SequencerMemory.java    |  47 ++
 .../reteoo/sequencing/signalprocessors/Gates.java  |  30 +-
 .../sequencing/signalprocessors/LogicCircuit.java  |  29 +-
 .../sequencing/signalprocessors/LogicGate.java     | 123 +++++
 .../LogicGateOutputSignalProcessor.java            |  49 ++
 .../sequencing/signalprocessors/SignalIndex.java   |  33 +-
 .../signalprocessors/SignalProcessor.java          |  31 +-
 .../TerminatingSignalProcessor.java                |  46 +-
 .../base/reteoo/sequencing/steps/AbstractStep.java |  30 +-
 .../reteoo/sequencing/steps/LogicCircuitStep.java  |  50 ++
 .../drools/base/reteoo/sequencing/steps/Step.java  |  49 ++
 .../rule/constraint/CombinedAlphaConstraint.java   | 110 +++++
 .../org/drools/base/util/CircularArrayList.java    | 128 +++++
 .../java/org/drools/base}/util/LinkedListTest.java |  11 +-
 .../org/drools/core/common/SuperCacheFixer.java    |   5 +
 .../core/impl/WorkingMemoryReteExpireAction.java   |   6 +-
 .../core/phreak/BuildtimeSegmentUtilities.java     |  11 +
 .../core/phreak/PhreakNetworkNodeFactory.java      |   3 +
 .../core/phreak/PhreakNetworkNodeFactoryImpl.java  |   7 +
 .../org/drools/core/phreak/PhreakTimerNode.java    |   1 -
 .../core/phreak/RuleNetworkEvaluatorImpl.java      |  10 +
 .../drools/core/reteoo/RightInputAdapterNode.java  |   5 +-
 .../java/org/drools/core/reteoo/SegmentMemory.java |  13 +
 .../java/org/drools/core/reteoo/SequenceNode.java  | 550 +++++++++++++++++++++
 .../java/org/drools/core/reteoo/TupleFactory.java  |   6 +
 .../drools/core/reteoo/builder/NodeFactory.java    |   6 +
 .../core/reteoo/builder/PhreakNodeFactory.java     |  94 +++-
 .../core/reteoo/builder/ReteooRuleBuilder.java     |   3 +
 .../core/reteoo/builder/SequenceBuilder.java       |  55 +++
 .../reteoo/sequencing/SequencerMemoryImpl.java     | 152 ++++++
 drools-metric/pom.xml                              |   5 +
 .../phreak/MetricPhreakNetworkNodeFactoryImpl.java |  10 +
 .../metric/phreak/PhreakSequenceNodeMetric.java    |  51 ++
 .../org/drools/metric/SequenceNodeMetricTest.java  | 126 +++++
 .../src/main/java/org/drools/model/Condition.java  |   2 +-
 .../src/main/java/org/drools/model/PatternDSL.java |  47 +-
 ...stentialExprViewItem.java => SequenceStep.java} |  34 +-
 .../org/drools/model/impl/ViewPatternBuilder.java  |  11 +
 .../SequenceConditionImpl.java}                    |  28 +-
 .../drools/model/view/CombinedExprViewItem.java    |   2 +-
 .../drools/model/view/ExistentialExprViewItem.java |   2 +-
 .../model/codegen/execmodel/ReteDumperTest.java    |   4 +-
 .../drools/modelcompiler/KiePackagesBuilder.java   | 129 ++++-
 .../java/org/drools/modelcompiler/RuleContext.java |  28 ++
 .../consequence/LambdaConsequence.java             | 146 +++++-
 .../PatternDSLSequenceAdvancedTest.java            | 518 +++++++++++++++++++
 .../PatternDSLSequenceCompositeTest.java           | 335 +++++++++++++
 .../PatternDSLSequenceConsequenceTest.java         | 452 +++++++++++++++++
 .../PatternDSLSequenceEdgeCaseTest.java            | 514 +++++++++++++++++++
 .../PatternDSLSequenceLifecycleTest.java           | 298 +++++++++++
 .../modelcompiler/PatternDSLSequenceTest.java      | 157 ++++++
 .../modelcompiler/SequenceReviewProbeTest.java     | 288 +++++++++++
 .../drools/modelcompiler/domain/SensorEvents.java  | 120 +++++
 .../impl/sessions/RuleUnitExecutorImpl.java        |   2 +-
 .../AbstractPhreakSequencerSubsequenceTest.java    | 183 +++++++
 .../phreak/sequencing/AlphaConstraint.java         |  73 +++
 .../integrationtests/phreak/sequencing/BEvent.java |  33 +-
 .../integrationtests/phreak/sequencing/CEvent.java |  33 +-
 .../phreak/sequencing/CircularArrayListTest.java   |  91 ++++
 .../integrationtests/phreak/sequencing/DEvent.java |  80 +++
 .../integrationtests/phreak/sequencing/EEvent.java |  33 +-
 .../phreak/sequencing/GatesTest.java               |  67 +++
 .../PhreakSequencerActivateDeactivateStepTest.java | 161 ++++++
 .../sequencing/PhreakSequencerLogicGateTest.java   | 156 ++++++
 .../phreak/sequencing/Predicate1.java              |  31 +-
 .../phreak/sequencing/SequenceCloneTest.java       |  65 +++
 .../phreak/sequencing/TestMemories.java            | 109 ++++
 74 files changed, 6367 insertions(+), 323 deletions(-)

diff --git a/drools-base/src/main/java/org/drools/base/definitions/rule/impl/RuleImpl.java b/drools-base/src/main/java/org/drools/base/definitions/rule/impl/RuleImpl.java
index 1fb0fe6aed8..f290d39f7b2 100644
--- a/drools-base/src/main/java/org/drools/base/definitions/rule/impl/RuleImpl.java
+++ b/drools-base/src/main/java/org/drools/base/definitions/rule/impl/RuleImpl.java
@@ -37,6 +37,7 @@ import org.drools.base.base.EnabledBoolean;
 import org.drools.base.base.SalienceInteger;
 import org.drools.base.base.ValueResolver;
 import org.drools.base.reteoo.BaseTuple;
+import org.drools.base.reteoo.sequencing.Sequence;
 import org.drools.base.rule.ConsequenceMetaData;
 import org.drools.base.rule.Declaration;
 import org.drools.base.rule.Dialectable;
@@ -545,6 +546,11 @@ public class RuleImpl implements Externalizable,
         this.lhsRoot.addChild( element );
     }
 
+    public void addSequence(final Sequence element) {
+        this.dirty = true;
+        this.lhsRoot.addChild( element );
+    }
+
     /**
      * Retrieve the <code>List</code> of <code>Conditions</code> for this
      * rule.
diff --git a/drools-base/src/main/java/org/drools/base/reteoo/DynamicFilter.java b/drools-base/src/main/java/org/drools/base/reteoo/DynamicFilter.java
new file mode 100644
index 00000000000..498fc9faa87
--- /dev/null
+++ b/drools-base/src/main/java/org/drools/base/reteoo/DynamicFilter.java
@@ -0,0 +1,87 @@
+/*
+ * 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.base.reteoo;
+
+import org.drools.base.base.ValueResolver;
+import org.drools.base.rule.ContextEntry;
+import org.drools.base.rule.constraint.AlphaNodeFieldConstraint;
+import org.drools.base.rule.constraint.BetaConstraint;
+import org.drools.base.util.AbstractLinkedListNode;
+import org.drools.base.util.LinkedList;
+import org.kie.api.runtime.rule.FactHandle;
+
+public class DynamicFilter extends AbstractLinkedListNode<DynamicFilter> {
+    private AlphaNodeFieldConstraint  constraint;
+    private LinkedList<SignalAdapter> signalAdapters;
+    private int                       adapterIndex;
+
+    public DynamicFilter(DynamicFilterProto proto) {
+        this.constraint    = proto.getConstraint();
+        this.adapterIndex  = proto.getAdapterIndex();
+        this.signalAdapters = new LinkedList<>();
+    }
+
+    public int getAdapterIndex() {
+        return adapterIndex;
+    }
+
+    public void addSignalAdapter(SignalAdapter signalAdapter) {
+        signalAdapters.add(signalAdapter);
+    }
+
+    public void removeSignalAdapter(SignalAdapter signalAdapter) {
+        signalAdapters.remove(signalAdapter);
+    }
+
+    public LinkedList<SignalAdapter> getSignalAdapters() {
+        return signalAdapters;
+    }
+
+    public void assertObject(final FactHandle factHandle,
+                             final ValueResolver valueResolver) {
+
+        if (constraint.getRequiredDeclarations().length == 0) {
+            if (constraint.isAllowed(factHandle, valueResolver)) {
+                for (SignalAdapter current = signalAdapters.getFirst(); current != null; ) {
+                    SignalAdapter next = current.getNext();
+                    current.receive(valueResolver, factHandle);
+                    current = next;
+                }
+            }
+        } else {
+            for (SignalAdapter current = signalAdapters.getFirst(); current != null; ) {
+                SignalAdapter next = current.getNext();
+                if (test(factHandle, valueResolver, current.getAnchorTuple())) {
+                    current.receive(valueResolver, factHandle);
+                }
+                current = next;
+            }
+        }
+    }
+
+    private boolean test(FactHandle factHandle, ValueResolver valueResolver, BaseTuple tuple) {
+        if (constraint instanceof BetaConstraint) {
+            BetaConstraint<ContextEntry> betaConstraint = (BetaConstraint<ContextEntry>) constraint;
+            ContextEntry ctx = betaConstraint.createContext();
+            ctx.updateFromTuple(valueResolver, tuple);
+            return betaConstraint.isAllowedCachedLeft(ctx, factHandle);
+        }
+        throw new IllegalStateException("test() called with a non-beta constraint: " + constraint.getClass());
+    }
+}
diff --git a/drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java b/drools-base/src/main/java/org/drools/base/reteoo/DynamicFilterProto.java
similarity index 54%
copy from drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java
copy to drools-base/src/main/java/org/drools/base/reteoo/DynamicFilterProto.java
index d74396d1070..76737d347d7 100644
--- a/drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java
+++ b/drools-base/src/main/java/org/drools/base/reteoo/DynamicFilterProto.java
@@ -16,33 +16,24 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.drools.model.view;
+package org.drools.base.reteoo;
 
-import org.drools.model.Condition;
-import org.drools.model.Variable;
+import org.drools.base.rule.constraint.AlphaNodeFieldConstraint;
 
-public class ExistentialExprViewItem<T> extends AbstractExprViewItem<T> {
+public class DynamicFilterProto {
+    private AlphaNodeFieldConstraint constraint;
+    private int                      adapterIndex;
 
-    private final Condition.Type type;
-    private final ViewItem expression;
-
-    public ExistentialExprViewItem(Condition.Type type, ViewItem expression) {
-        super(expression.getFirstVariable());
-        this.type = type;
-        this.expression = expression;
-    }
-
-    public ViewItem getExpression() {
-        return expression;
+    public DynamicFilterProto(AlphaNodeFieldConstraint constraint, int adapterIndex) {
+        this.constraint   = constraint;
+        this.adapterIndex = adapterIndex;
     }
 
-    @Override
-    public Variable<?>[] getVariables() {
-        return expression.getVariables();
+    public AlphaNodeFieldConstraint getConstraint() {
+        return constraint;
     }
 
-    @Override
-    public Condition.Type getType() {
-        return type;
+    public int getAdapterIndex() {
+        return adapterIndex;
     }
 }
diff --git a/drools-base/src/main/java/org/drools/base/reteoo/NodeTypeEnums.java b/drools-base/src/main/java/org/drools/base/reteoo/NodeTypeEnums.java
index 19bc533214a..67afcb4e260 100644
--- a/drools-base/src/main/java/org/drools/base/reteoo/NodeTypeEnums.java
+++ b/drools-base/src/main/java/org/drools/base/reteoo/NodeTypeEnums.java
@@ -75,6 +75,7 @@ public class NodeTypeEnums {
     public static final int AsyncReceiveNode        = (260 << shift) | TupleSourceMask | TupleSinkMask | TupleNodeMask | MemoryFactoryMask;
     public static final int FromNode                = (270 << shift) | TupleSourceMask | TupleSinkMask | TupleNodeMask | MemoryFactoryMask;
     public static final int ReactiveFromNode        = (280 << shift) | TupleSourceMask | TupleSinkMask | TupleNodeMask | MemoryFactoryMask;
+    public static final int SequenceNode            = (290 << shift) | TupleSourceMask | TupleSinkMask | TupleNodeMask | MemoryFactoryMask;
 
     public static final int QueryElementNode        = (300 << shift) | TupleSourceMask | TupleSinkMask | TupleNodeMask | MemoryFactoryMask;
     public static final int ConditionalBranchNode   = (310 << shift) | TupleSourceMask | TupleSinkMask | TupleNodeMask | MemoryFactoryMask;
diff --git a/drools-base/src/main/java/org/drools/base/reteoo/SignalAdapter.java b/drools-base/src/main/java/org/drools/base/reteoo/SignalAdapter.java
new file mode 100644
index 00000000000..fef0f26f3a1
--- /dev/null
+++ b/drools-base/src/main/java/org/drools/base/reteoo/SignalAdapter.java
@@ -0,0 +1,48 @@
+/*
+ * 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.base.reteoo;
+
+import org.drools.base.base.ValueResolver;
+import org.drools.base.reteoo.sequencing.Sequence.SequenceMemory;
+import org.drools.base.reteoo.sequencing.signalprocessors.SignalProcessor;
+import org.drools.base.util.AbstractLinkedListNode;
+import org.kie.api.runtime.rule.FactHandle;
+
+public class SignalAdapter extends AbstractLinkedListNode<SignalAdapter> {
+    private SignalProcessor output;
+    private int             signalBitIndex;
+    private int             filterIndex;
+    private SequenceMemory  memory;
+
+    public SignalAdapter(SignalProcessor output, int signalBitIndex, int filterIndex, SequenceMemory memory) {
+        this.output         = output;
+        this.signalBitIndex = signalBitIndex;
+        this.filterIndex    = filterIndex;
+        this.memory         = memory;
+    }
+
+    public BaseTuple getAnchorTuple() {
+        return memory.getSequencerMemory().getLeftTuple();
+    }
+
+    public void receive(ValueResolver reteEvaluator, FactHandle factHandle) {
+        memory.setData(filterIndex, factHandle);
+        output.consume(signalBitIndex, memory, reteEvaluator);
+    }
+}
diff --git a/drools-base/src/main/java/org/drools/base/reteoo/sequencing/Sequence.java b/drools-base/src/main/java/org/drools/base/reteoo/sequencing/Sequence.java
new file mode 100644
index 00000000000..e9595e2c731
--- /dev/null
+++ b/drools-base/src/main/java/org/drools/base/reteoo/sequencing/Sequence.java
@@ -0,0 +1,328 @@
+/*
+ * 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.base.reteoo.sequencing;
+
+import org.drools.base.base.ValueResolver;
+import org.drools.base.reteoo.DynamicFilter;
+import org.drools.base.reteoo.sequencing.signalprocessors.LogicGate;
+import org.drools.base.reteoo.sequencing.steps.LogicCircuitStep;
+import org.drools.base.reteoo.sequencing.steps.Step;
+import org.drools.base.reteoo.sequencing.steps.Step.StepFactory;
+import org.drools.base.rule.Declaration;
+import org.drools.base.rule.Pattern;
+import org.drools.base.rule.RuleConditionElement;
+import org.drools.base.reteoo.SignalAdapter;
+import org.drools.base.util.CircularArrayList;
+import org.kie.api.runtime.rule.FactHandle;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+public class Sequence implements RuleConditionElement {
+    private final int sequenceIndex;
+
+    private Pattern[] filters;
+
+    private Step[] steps;
+
+    private LogicGate[] gates;
+
+    private SequenceController controller;
+
+    public Sequence(int sequenceIndex, StepFactory... stepFactories) {
+        this.steps = new Step[stepFactories.length];
+        this.sequenceIndex = sequenceIndex;
+        this.controller = DefaultController.getInstance();
+
+        for ( int i = 0; i < steps.length; i++ ) {
+            steps[i] = stepFactories[i].createStep(this);
+        }
+        populateLogicGates();
+    }
+
+    public Pattern[] getFilters() {
+        return filters;
+    }
+
+    public void setFilters(Pattern[] filters) {
+        this.filters = filters;
+    }
+
+    public void populateLogicGates() {
+        List<LogicGate> list = new ArrayList<>();
+        for (Step step : getSteps()) {
+            if (step instanceof LogicCircuitStep) {
+                LogicGate[] circuitGates = ((LogicCircuitStep) step).getCircuit().getGates();
+                for (int i = 0; i < circuitGates.length; i++) {
+                    list.add(circuitGates[i]);
+                }
+            }
+        }
+        gates = list.toArray(new LogicGate[0]);
+    }
+
+    public int getSequenceIndex() {
+        return sequenceIndex;
+    }
+
+    public Step[] getSteps() {
+        return steps;
+    }
+
+    public LogicGate[] getGates() {
+        return gates;
+    }
+
+    @Override
+    public Map<String, Declaration> getInnerDeclarations() {
+        return Map.of();
+    }
+
+    @Override
+    public Map<String, Declaration> getOuterDeclarations() {
+        return Map.of();
+    }
+
+    @Override
+    public Declaration resolveDeclaration(String identifier) {
+        return null;
+    }
+
+    @Override
+    public RuleConditionElement clone() {
+        Sequence clone = new Sequence(sequenceIndex);
+        clone.steps      = this.steps;
+        clone.gates      = this.gates;
+        clone.filters    = this.filters;
+        clone.controller = this.controller;
+        return clone;
+    }
+
+    @Override
+    public List<? extends RuleConditionElement> getNestedElements() {
+        return List.of();
+    }
+
+    @Override
+    public boolean isPatternScopeDelimiter() {
+        return false;
+    }
+
+    @Override
+    public void writeExternal(ObjectOutput out) throws IOException {
+        throw new UnsupportedOperationException("Sequence does not support serialization");
+    }
+
+    @Override
+    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+        throw new UnsupportedOperationException("Sequence does not support serialization");
+    }
+
+    public void start(SequenceMemory memory, ValueResolver valueResolver) {
+        controller.start(memory, valueResolver);
+    }
+
+    public void next(SequenceMemory sequenceMemory, ValueResolver valueResolver) {
+         controller.next(sequenceMemory, valueResolver);
+    }
+
+    public interface SequenceController {
+        default void start(SequenceMemory sequenceMemory, ValueResolver valueResolver) {
+            sequenceMemory.setStep(0);
+            sequenceMemory.sequence.steps[0].activate(sequenceMemory, valueResolver);
+        }
+
+        default void next(SequenceMemory memory, ValueResolver valueResolver) {
+
+        }
+
+        void end(SequenceMemory memory, ValueResolver valueResolver);
+    }
+
+    public static class DefaultController implements SequenceController {
+        private static final DefaultController INSTANCE = new DefaultController();
+
+        private DefaultController() {}
+
+        public static DefaultController getInstance() {
+            return INSTANCE;
+        }
+
+        public void next(SequenceMemory sequenceMemory, ValueResolver valueResolver) {
+            Sequence sequence = sequenceMemory.getSequence();
+            int step = sequenceMemory.getStep();
+
+            sequence.steps[step].deactivate(sequenceMemory, valueResolver);
+            step = sequenceMemory.incrementStep();
+
+            if (step < sequenceMemory.getSequence().getSteps().length) {
+                sequence.steps[step].activate(sequenceMemory, valueResolver);
+            } else {
+                end(sequenceMemory, valueResolver);
+            }
+        }
+
+        @Override
+        public void end(SequenceMemory sequenceMemory, ValueResolver valueResolver)  {
+            sequenceMemory.getSequencerMemory().match(valueResolver);
+        }
+
+        @Override
+        public String toString() {
+            return "DefaultController{}";
+        }
+    }
+
+    public static class SequenceMemory {
+        private final Sequence sequence;
+
+        private int step;
+
+        private final SequencerMemory sequencerMemory;
+
+        private final SignalAdapter[] signalAdapters;
+
+        private final SignalAdapter[] activeSignalAdapters;
+
+        private final long[] gateMemory;
+
+        private final boolean[] gateMatched;
+
+        private CircularArrayList<Object> data;
+
+        public SequenceMemory(SequencerMemory sequencerMemory, Sequence sequence, CircularArrayList<Object> data,
+                              SignalAdapter[] signalAdapters, SignalAdapter[] activeSignalAdapters,
+                              long[] gateMemory, long[] counterMemories) {
+            this.sequencerMemory      = sequencerMemory;
+            this.data                 = data;
+            this.sequence             = sequence;
+            this.signalAdapters       = signalAdapters;
+            this.activeSignalAdapters = activeSignalAdapters;
+            this.gateMemory           = gateMemory;
+            this.gateMatched          = new boolean[gateMemory.length + counterMemories.length];
+        }
+
+        public Sequence getSequence() {
+            return sequence;
+        }
+
+        public SequencerMemory getSequencerMemory() {
+            return sequencerMemory;
+        }
+
+        public boolean isLogicGateMatched(int index) {
+            return gateMatched[index];
+        }
+
+        public void setLogicGateMatched(int index, boolean matched) {
+            gateMatched[index] = matched;
+        }
+
+        public SignalAdapter[] getSignalAdapters() {
+            return signalAdapters;
+        }
+
+        public SignalAdapter[] getActiveSignalAdapters() {
+            return activeSignalAdapters;
+        }
+
+        public long[] getLogicGateMemory() {
+            return gateMemory;
+        }
+
+        public int incrementStep() {
+            return ++step;
+        }
+
+        public int getStep() {
+            return step;
+        }
+
+        public void setStep(int step) {
+            this.step = step;
+        }
+
+        public void setData(int filterIndex, FactHandle handle) {
+            data.put(filterIndex, handle);
+        }
+
+        public SignalAdapter activateSignalAdapter(int filterIndex, LogicGate gate, int signalAdapterIndex, int signalBitIndex) {
+            if (activeSignalAdapters[signalAdapterIndex] != null) {
+                throw new RuntimeException("Defensive coding, this should not be re-entrant");
+            }
+
+            SignalAdapter signalAdapter = signalAdapters[signalAdapterIndex];
+
+            if (signalAdapter == null) {
+                signalAdapter = new SignalAdapter(gate, signalBitIndex, filterIndex, this);
+                signalAdapters[signalAdapterIndex] = signalAdapter;
+            }
+
+            activeSignalAdapters[signalAdapterIndex] = signalAdapter;
+
+            DynamicFilter filter = sequencerMemory.getActiveDynamicFilter(filterIndex);
+            filter.addSignalAdapter(signalAdapter);
+
+            return signalAdapter;
+        }
+
+        public void deactivateSignalAdapter(int filterIndex, int signalAdapterIndex) {
+            SignalAdapter signalAdapter = activeSignalAdapters[signalAdapterIndex];
+            if (signalAdapter == null) {
+                // Already deactivated — no-op to prevent a double-remove from the linked filter list.
+                return;
+            }
+            activeSignalAdapters[signalAdapterIndex] = null;
+
+            DynamicFilter filter = sequencerMemory.getActiveDynamicFilter(filterIndex);
+            filter.removeSignalAdapter(signalAdapter);
+
+            if (filter.getSignalAdapters().isEmpty()) {
+                sequencerMemory.removeActiveFilter(filter);
+            }
+        }
+
+        public void resetLogicGateMemory(int gateIndex, ValueResolver valueResolver) {
+            gateMemory[gateIndex]     = 0;
+            gateMatched[gateIndex]    = false;
+        }
+
+        @Override
+        public String toString() {
+            return "SequenceMemory{" +
+                   "sequence=" + sequence.getSequenceIndex() +
+                   ", step=" + step +
+                   '}';
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "Sequence{" +
+               "sequenceIndex=" + sequenceIndex +
+               ", steps=" + Arrays.toString(steps) +
+               ", controller=" + controller +
+               '}';
+    }
+}
diff --git a/drools-base/src/main/java/org/drools/base/reteoo/sequencing/Sequencer.java b/drools-base/src/main/java/org/drools/base/reteoo/sequencing/Sequencer.java
new file mode 100644
index 00000000000..e1aeb4420fa
--- /dev/null
+++ b/drools-base/src/main/java/org/drools/base/reteoo/sequencing/Sequencer.java
@@ -0,0 +1,70 @@
+/*
+ * 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.base.reteoo.sequencing;
+
+import org.drools.base.base.ValueResolver;
+import org.drools.base.reteoo.sequencing.Sequence.SequenceMemory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Sequencer {
+
+    private final Sequence     sequence;
+
+    private final Sequence[] sequences;
+
+    public Sequencer(Sequence sequence) {
+        this.sequence = sequence;
+        this.sequences = populateSequences(sequence, new ArrayList<>()).stream().toArray(Sequence[]::new);
+    }
+
+    public static List<Sequence> populateSequences(Sequence sequence, List<Sequence> list) {
+        list.add(sequence);
+        return list;
+    }
+
+    public Sequence[] getSequences() {
+        return sequences;
+    }
+
+    public void start(SequencerMemory memory, ValueResolver valueResolver) {
+        SequenceMemory sequenceMemory = memory.getOrCreateSequenceMemory(sequence, memory.getData());
+        memory.setChildSequenceMemory(sequenceMemory);
+        sequence.start(sequenceMemory, valueResolver);
+    }
+
+    public void stop(SequenceMemory memory, ValueResolver valueResolver) {
+        if (memory != null) {
+            Sequence sequence = memory.getSequence();
+            int step = memory.getStep();
+            if (step < sequence.getSteps().length) {
+                sequence.getSteps()[step].deactivate(memory, valueResolver);
+            }
+            // Mark terminal so a subsequent stop() (e.g. retract after completion) is a no-op.
+            memory.setStep(sequence.getSteps().length);
+        }
+    }
+
+    public Sequence getSequence() {
+        return sequence;
+    }
+
+
+}
diff --git a/drools-base/src/main/java/org/drools/base/reteoo/sequencing/SequencerMemory.java b/drools-base/src/main/java/org/drools/base/reteoo/sequencing/SequencerMemory.java
new file mode 100644
index 00000000000..aa48e2dbb05
--- /dev/null
+++ b/drools-base/src/main/java/org/drools/base/reteoo/sequencing/SequencerMemory.java
@@ -0,0 +1,47 @@
+/*
+ * 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.base.reteoo.sequencing;
+
+import org.drools.base.base.ValueResolver;
+import org.drools.base.reteoo.BaseTuple;
+import org.drools.base.reteoo.DynamicFilter;
+import org.drools.base.reteoo.sequencing.Sequence.SequenceMemory;
+import org.drools.base.util.CircularArrayList;
+
+public interface SequencerMemory {
+    BaseTuple getLeftTuple();
+
+    CircularArrayList<Object> getData();
+
+    Sequencer getSequencer();
+
+    SequenceMemory getChildSequenceMemory();
+
+    void setChildSequenceMemory(SequenceMemory childSequenceMemory);
+
+    SequenceMemory getOrCreateSequenceMemory(Sequence sequence, CircularArrayList<Object> newData);
+
+    SequenceMemory getSequenceMemory(Sequence sequence);
+
+    void match(ValueResolver valueResolver);
+
+    DynamicFilter getActiveDynamicFilter(int filterIndex);
+
+    void removeActiveFilter(DynamicFilter filter);
+}
diff --git a/drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java b/drools-base/src/main/java/org/drools/base/reteoo/sequencing/signalprocessors/Gates.java
similarity index 53%
copy from drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java
copy to drools-base/src/main/java/org/drools/base/reteoo/sequencing/signalprocessors/Gates.java
index d74396d1070..0a7acdea225 100644
--- a/drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java
+++ b/drools-base/src/main/java/org/drools/base/reteoo/sequencing/signalprocessors/Gates.java
@@ -16,33 +16,11 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.drools.model.view;
+package org.drools.base.reteoo.sequencing.signalprocessors;
 
-import org.drools.model.Condition;
-import org.drools.model.Variable;
+public class Gates {
+    public static boolean and(long a, long b) {return a == b;}
 
-public class ExistentialExprViewItem<T> extends AbstractExprViewItem<T> {
+    public static boolean or(long a, long b)  {return (a & b) != 0;}
 
-    private final Condition.Type type;
-    private final ViewItem expression;
-
-    public ExistentialExprViewItem(Condition.Type type, ViewItem expression) {
-        super(expression.getFirstVariable());
-        this.type = type;
-        this.expression = expression;
-    }
-
-    public ViewItem getExpression() {
-        return expression;
-    }
-
-    @Override
-    public Variable<?>[] getVariables() {
-        return expression.getVariables();
-    }
-
-    @Override
-    public Condition.Type getType() {
-        return type;
-    }
 }
diff --git a/drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java b/drools-base/src/main/java/org/drools/base/reteoo/sequencing/signalprocessors/LogicCircuit.java
similarity index 54%
copy from drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java
copy to drools-base/src/main/java/org/drools/base/reteoo/sequencing/signalprocessors/LogicCircuit.java
index d74396d1070..4772da69d6e 100644
--- a/drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java
+++ b/drools-base/src/main/java/org/drools/base/reteoo/sequencing/signalprocessors/LogicCircuit.java
@@ -16,33 +16,22 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.drools.model.view;
+package org.drools.base.reteoo.sequencing.signalprocessors;
 
-import org.drools.model.Condition;
-import org.drools.model.Variable;
+public class LogicCircuit {
 
-public class ExistentialExprViewItem<T> extends AbstractExprViewItem<T> {
+    private final LogicGate[] gates;
 
-    private final Condition.Type type;
-    private final ViewItem expression;
-
-    public ExistentialExprViewItem(Condition.Type type, ViewItem expression) {
-        super(expression.getFirstVariable());
-        this.type = type;
-        this.expression = expression;
+    public LogicCircuit(LogicGate... gates) {
+        this.gates = gates;
     }
 
-    public ViewItem getExpression() {
-        return expression;
+    public LogicGate[] getGates() {
+        return gates;
     }
 
-    @Override
-    public Variable<?>[] getVariables() {
-        return expression.getVariables();
+    public interface LongBiPredicate {
+        boolean test(long a, long b);
     }
 
-    @Override
-    public Condition.Type getType() {
-        return type;
-    }
 }
diff --git a/drools-base/src/main/java/org/drools/base/reteoo/sequencing/signalprocessors/LogicGate.java b/drools-base/src/main/java/org/drools/base/reteoo/sequencing/signalprocessors/LogicGate.java
new file mode 100644
index 00000000000..a3e94f71a36
--- /dev/null
+++ b/drools-base/src/main/java/org/drools/base/reteoo/sequencing/signalprocessors/LogicGate.java
@@ -0,0 +1,123 @@
+/*
+ * 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.base.reteoo.sequencing.signalprocessors;
+
+import org.drools.base.base.ValueResolver;
+import org.drools.base.reteoo.sequencing.signalprocessors.LogicCircuit.LongBiPredicate;
+import org.drools.base.reteoo.sequencing.Sequence.SequenceMemory;
+
+public class LogicGate extends SignalProcessor {
+    protected long allMatched;
+
+    private SignalProcessor output;
+
+    private final LongBiPredicate predicate;
+
+    private final int gateIndex;
+
+    private LogicGate[] inputGates = EMPTY_INPUT_GATES;
+
+    private final int[] filterIndexes;
+
+    private int[] signalAdapterIndexes;
+
+    private static final LogicGate[] EMPTY_INPUT_GATES = new LogicGate[0];
+
+    public LogicGate(LongBiPredicate predicate, int gateIndex, int[] filterIndexes, int[] signalAdapterIndexes, int nbrOfInputGates) {
+        this.predicate = predicate;
+
+        this.filterIndexes        = filterIndexes;
+        this.signalAdapterIndexes = signalAdapterIndexes;
+
+        int totalInputs = signalAdapterIndexes.length + nbrOfInputGates;
+        if (totalInputs > Long.SIZE) {
+            throw new IllegalArgumentException(
+                    "LogicGate supports at most " + Long.SIZE + " inputs; got " + totalInputs);
+        }
+        for (int i = 0; i < totalInputs; i++) {
+            allMatched = allMatched | (1L << i);
+        }
+
+        this.gateIndex = gateIndex;
+    }
+
+    public int[] getSignalAdapterIndexes() {
+        return signalAdapterIndexes;
+    }
+
+    public void setInputGates(LogicGate... inputGates) {
+        this.inputGates = inputGates;
+    }
+
+    public void setOutput(SignalProcessor output) {
+        this.output = output;
+    }
+
+    @Override
+    public void consume(SequenceMemory memory, ValueResolver valueResolver) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void consume(int signalBitIndex, SequenceMemory memory, ValueResolver valueResolver) {
+        long currentMatched = memory.getLogicGateMemory()[gateIndex] | (1L << (signalBitIndex - 1));
+        memory.getLogicGateMemory()[gateIndex] = currentMatched;
+
+        boolean matched = predicate.test(currentMatched, allMatched);
+
+        if (matched && !memory.isLogicGateMatched(gateIndex)) {
+            memory.setLogicGateMatched(gateIndex, true);
+            propagate(memory, valueResolver);
+        }
+    }
+
+    public void propagate(SequenceMemory memory, ValueResolver valueResolver) {
+        resetPrior(memory, valueResolver);
+        output.consume(memory, valueResolver);
+    }
+
+    public void resetPrior(SequenceMemory memory, ValueResolver valueResolver) {
+        for (LogicGate gate : inputGates) {
+            gate.reset(memory, valueResolver);
+        }
+
+        memory.resetLogicGateMemory(gateIndex, valueResolver);
+    }
+
+    public void reset(SequenceMemory memory, ValueResolver valueResolver) {
+        resetPrior(memory, valueResolver);
+        output.reset(memory, valueResolver);
+    }
+
+    public void activate(SequenceMemory memory) {
+        for (int i = 0; i < filterIndexes.length; i++) {
+            memory.activateSignalAdapter(filterIndexes[i], this, signalAdapterIndexes[i], i + 1); // bit indexes start at 1
+        }
+
+    }
+
+    public void deactivate(SequenceMemory memory, ValueResolver valueResolver) {
+        for (int i = 0; i < filterIndexes.length; i++) {
+            memory.deactivateSignalAdapter(filterIndexes[i], signalAdapterIndexes[i]);
+        }
+
+        memory.resetLogicGateMemory(gateIndex, valueResolver);
+    }
+
+}
diff --git a/drools-base/src/main/java/org/drools/base/reteoo/sequencing/signalprocessors/LogicGateOutputSignalProcessor.java b/drools-base/src/main/java/org/drools/base/reteoo/sequencing/signalprocessors/LogicGateOutputSignalProcessor.java
new file mode 100644
index 00000000000..c7546b4a8ab
--- /dev/null
+++ b/drools-base/src/main/java/org/drools/base/reteoo/sequencing/signalprocessors/LogicGateOutputSignalProcessor.java
@@ -0,0 +1,49 @@
+/*
+ * 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.base.reteoo.sequencing.signalprocessors;
+
+import org.drools.base.base.ValueResolver;
+import org.drools.base.reteoo.sequencing.Sequence.SequenceMemory;
+
+public class LogicGateOutputSignalProcessor extends SignalProcessor {
+    private final LogicGate gate;
+    private final int       index;
+
+    public LogicGateOutputSignalProcessor(SignalIndex signalIndex) {
+        this.gate  = signalIndex.getGate();
+        this.index = signalIndex.getBitIndex();
+    }
+
+    @Override
+    public void consume(SequenceMemory memory, ValueResolver valueResolver) {
+        gate.consume(index, memory, valueResolver);
+    }
+
+    @Override
+    public void consume(int signalBitIndex, SequenceMemory memory, ValueResolver valueResolver) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected void reset(SequenceMemory memory, ValueResolver valueResolver) {
+        // No-op: LogicGateOutputSignalProcessor is a fan-out node whose reset is driven
+        // by each downstream LogicGate resetting itself via resetPrior(). There is no
+        // state held here that needs clearing.
+    }
+}
diff --git a/drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java b/drools-base/src/main/java/org/drools/base/reteoo/sequencing/signalprocessors/SignalIndex.java
similarity index 55%
copy from drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java
copy to drools-base/src/main/java/org/drools/base/reteoo/sequencing/signalprocessors/SignalIndex.java
index d74396d1070..e92efd30328 100644
--- a/drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java
+++ b/drools-base/src/main/java/org/drools/base/reteoo/sequencing/signalprocessors/SignalIndex.java
@@ -16,33 +16,26 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.drools.model.view;
+package org.drools.base.reteoo.sequencing.signalprocessors;
 
-import org.drools.model.Condition;
-import org.drools.model.Variable;
+public class SignalIndex {
+    private final LogicGate gate;
+    private final int       bitIndex;
 
-public class ExistentialExprViewItem<T> extends AbstractExprViewItem<T> {
-
-    private final Condition.Type type;
-    private final ViewItem expression;
-
-    public ExistentialExprViewItem(Condition.Type type, ViewItem expression) {
-        super(expression.getFirstVariable());
-        this.type = type;
-        this.expression = expression;
+    public static SignalIndex of(LogicGate gate, int bitIndex) {
+        return new SignalIndex(gate, bitIndex);
     }
 
-    public ViewItem getExpression() {
-        return expression;
+    public SignalIndex(LogicGate gate, int bitIndex) {
+        this.gate     = gate;
+        this.bitIndex = bitIndex;
     }
 
-    @Override
-    public Variable<?>[] getVariables() {
-        return expression.getVariables();
+    public LogicGate getGate() {
+        return gate;
     }
 
-    @Override
-    public Condition.Type getType() {
-        return type;
+    public int getBitIndex() {
+        return bitIndex;
     }
 }
diff --git a/drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java b/drools-base/src/main/java/org/drools/base/reteoo/sequencing/signalprocessors/SignalProcessor.java
similarity index 53%
copy from drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java
copy to drools-base/src/main/java/org/drools/base/reteoo/sequencing/signalprocessors/SignalProcessor.java
index d74396d1070..b0f3c32d78b 100644
--- a/drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java
+++ b/drools-base/src/main/java/org/drools/base/reteoo/sequencing/signalprocessors/SignalProcessor.java
@@ -16,33 +16,16 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.drools.model.view;
+package org.drools.base.reteoo.sequencing.signalprocessors;
 
-import org.drools.model.Condition;
-import org.drools.model.Variable;
+import org.drools.base.base.ValueResolver;
+import org.drools.base.reteoo.sequencing.Sequence.SequenceMemory;
 
-public class ExistentialExprViewItem<T> extends AbstractExprViewItem<T> {
+public abstract class SignalProcessor {
 
-    private final Condition.Type type;
-    private final ViewItem expression;
+    public abstract void consume(SequenceMemory memory, ValueResolver valueResolver);
 
-    public ExistentialExprViewItem(Condition.Type type, ViewItem expression) {
-        super(expression.getFirstVariable());
-        this.type = type;
-        this.expression = expression;
-    }
+    public abstract void consume(int signalBitIndex, SequenceMemory memory, ValueResolver valueResolver);
 
-    public ViewItem getExpression() {
-        return expression;
-    }
-
-    @Override
-    public Variable<?>[] getVariables() {
-        return expression.getVariables();
-    }
-
-    @Override
-    public Condition.Type getType() {
-        return type;
-    }
+    protected abstract void reset(SequenceMemory memory, ValueResolver valueResolver);
 }
diff --git a/drools-model/drools-canonical-model/src/main/java/org/drools/model/Condition.java b/drools-base/src/main/java/org/drools/base/reteoo/sequencing/signalprocessors/TerminatingSignalProcessor.java
similarity index 50%
copy from drools-model/drools-canonical-model/src/main/java/org/drools/model/Condition.java
copy to drools-base/src/main/java/org/drools/base/reteoo/sequencing/signalprocessors/TerminatingSignalProcessor.java
index 6897ad2478f..f3477a5a2ee 100644
--- a/drools-model/drools-canonical-model/src/main/java/org/drools/model/Condition.java
+++ b/drools-base/src/main/java/org/drools/base/reteoo/sequencing/signalprocessors/TerminatingSignalProcessor.java
@@ -16,43 +16,33 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.drools.model;
+package org.drools.base.reteoo.sequencing.signalprocessors;
 
-import java.util.Collections;
-import java.util.List;
+import org.drools.base.base.ValueResolver;
+import org.drools.base.reteoo.sequencing.Sequence.SequenceMemory;
 
-public interface Condition {
+public class TerminatingSignalProcessor extends SignalProcessor {
+    private static final TerminatingSignalProcessor MATCH = new TerminatingSignalProcessor();
 
-    default List<Condition> getSubConditions() {
-        return Collections.emptyList();
+    private TerminatingSignalProcessor() {
     }
 
-    Type getType();
+    public static TerminatingSignalProcessor get() {
+        return MATCH;
+    }
 
-    Variable<?>[] getBoundVariables();
+    @Override
+    public void consume(SequenceMemory memory, ValueResolver valueResolver) {
+        memory.getSequence().next(memory, valueResolver);
+    }
 
-    default Condition cloneCondition() {
+    @Override
+    public void consume(int signalBitIndex, SequenceMemory memory, ValueResolver valueResolver) {
         throw new UnsupportedOperationException();
     }
 
-    enum Type {
-        PATTERN( false ), EVAL( false ), QUERY( false ),
-        ACCUMULATE( false ), GROUP_BY( false ), TEMPORAL( false ),
-        RECEIVER( false ), SENDER( false ),
-        OR( true ), AND( true ), NOT( false ), EXISTS( false ), FORALL( false ), CONSEQUENCE( false );
-
-        private final boolean composite;
-
-        Type( boolean composite ) {
-            this.composite = composite;
-        }
-
-        public boolean isComposite() {
-            return composite;
-        }
-
-        public boolean createsScope() {
-            return this == OR;
-        }
+    @Override
+    protected void reset(SequenceMemory memory, ValueResolver valueResolver) {
+        throw new UnsupportedOperationException();
     }
 }
diff --git a/drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java b/drools-base/src/main/java/org/drools/base/reteoo/sequencing/steps/AbstractStep.java
similarity index 54%
copy from drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java
copy to drools-base/src/main/java/org/drools/base/reteoo/sequencing/steps/AbstractStep.java
index d74396d1070..4634421f62b 100644
--- a/drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java
+++ b/drools-base/src/main/java/org/drools/base/reteoo/sequencing/steps/AbstractStep.java
@@ -16,33 +16,15 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.drools.model.view;
+package org.drools.base.reteoo.sequencing.steps;
 
-import org.drools.model.Condition;
-import org.drools.model.Variable;
+import org.drools.base.reteoo.sequencing.Sequence;
 
-public class ExistentialExprViewItem<T> extends AbstractExprViewItem<T> {
+public abstract class AbstractStep implements Step {
 
-    private final Condition.Type type;
-    private final ViewItem expression;
+    protected final Sequence sequence; // the sequence the step is in
 
-    public ExistentialExprViewItem(Condition.Type type, ViewItem expression) {
-        super(expression.getFirstVariable());
-        this.type = type;
-        this.expression = expression;
-    }
-
-    public ViewItem getExpression() {
-        return expression;
-    }
-
-    @Override
-    public Variable<?>[] getVariables() {
-        return expression.getVariables();
-    }
-
-    @Override
-    public Condition.Type getType() {
-        return type;
+    public AbstractStep(Sequence sequence) {
+        this.sequence = sequence;
     }
 }
diff --git a/drools-base/src/main/java/org/drools/base/reteoo/sequencing/steps/LogicCircuitStep.java b/drools-base/src/main/java/org/drools/base/reteoo/sequencing/steps/LogicCircuitStep.java
new file mode 100644
index 00000000000..07010de2370
--- /dev/null
+++ b/drools-base/src/main/java/org/drools/base/reteoo/sequencing/steps/LogicCircuitStep.java
@@ -0,0 +1,50 @@
+/*
+ * 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.base.reteoo.sequencing.steps;
+
+import org.drools.base.base.ValueResolver;
+import org.drools.base.reteoo.sequencing.signalprocessors.LogicCircuit;
+import org.drools.base.reteoo.sequencing.signalprocessors.LogicGate;
+import org.drools.base.reteoo.sequencing.Sequence;
+import org.drools.base.reteoo.sequencing.Sequence.SequenceMemory;
+
+public class LogicCircuitStep extends AbstractStep implements Step {
+    private final LogicCircuit circuit;
+
+    public LogicCircuitStep(Sequence sequence, LogicCircuit circuit) {
+        super(sequence);
+        this.circuit = circuit;
+    }
+
+    public LogicCircuit getCircuit() {
+        return circuit;
+    }
+
+    public void activate(SequenceMemory sequenceMemory, ValueResolver valueResolver) {
+        for (LogicGate gate : circuit.getGates()) {
+            gate.activate(sequenceMemory);
+        }
+    }
+
+    public void deactivate(SequenceMemory sequenceMemory, ValueResolver valueResolver) {
+        for (LogicGate gate : circuit.getGates()) {
+            gate.deactivate(sequenceMemory, valueResolver);
+        }
+    }
+}
diff --git a/drools-base/src/main/java/org/drools/base/reteoo/sequencing/steps/Step.java b/drools-base/src/main/java/org/drools/base/reteoo/sequencing/steps/Step.java
new file mode 100644
index 00000000000..b2348fefb05
--- /dev/null
+++ b/drools-base/src/main/java/org/drools/base/reteoo/sequencing/steps/Step.java
@@ -0,0 +1,49 @@
+/*
+ * 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.base.reteoo.sequencing.steps;
+
+import org.drools.base.base.ValueResolver;
+import org.drools.base.reteoo.sequencing.signalprocessors.LogicCircuit;
+import org.drools.base.reteoo.sequencing.Sequence;
+import org.drools.base.reteoo.sequencing.Sequence.SequenceMemory;
+
+public interface Step {
+    void activate(SequenceMemory memory, ValueResolver valueResolver);
+
+    void deactivate(SequenceMemory memory, ValueResolver valueResolver);
+
+    static StepFactory of(LogicCircuit circuit) {
+        StepFactory factory = new StepFactory();
+        factory.setCircuit(circuit);
+        return factory;
+    }
+
+    class StepFactory {
+        private       LogicCircuit circuit;
+
+        public void setCircuit(LogicCircuit circuit) {
+            this.circuit = circuit;
+        }
+
+        public Step createStep(Sequence sequence) {
+            return new LogicCircuitStep(sequence, circuit);
+        }
+    }
+
+}
diff --git a/drools-base/src/main/java/org/drools/base/rule/constraint/CombinedAlphaConstraint.java b/drools-base/src/main/java/org/drools/base/rule/constraint/CombinedAlphaConstraint.java
new file mode 100644
index 00000000000..5344678ea3b
--- /dev/null
+++ b/drools-base/src/main/java/org/drools/base/rule/constraint/CombinedAlphaConstraint.java
@@ -0,0 +1,110 @@
+/*
+ * 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.base.rule.constraint;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.Arrays;
+
+import org.drools.base.base.ValueResolver;
+import org.drools.base.rule.Declaration;
+import org.kie.api.runtime.rule.FactHandle;
+
+/**
+ * ANDs two or more {@link AlphaNodeFieldConstraint}s into a single constraint.
+ * Used by the sequencing runtime when a sequence step carries multiple expressions:
+ * every expression must be satisfied for the step to match.
+ */
+public class CombinedAlphaConstraint implements AlphaNodeFieldConstraint {
+
+    private AlphaNodeFieldConstraint[] constraints;
+
+    public CombinedAlphaConstraint(AlphaNodeFieldConstraint[] constraints) {
+        this.constraints = constraints;
+    }
+
+    @Override
+    public boolean isAllowed(FactHandle handle, ValueResolver valueResolver) {
+        for (AlphaNodeFieldConstraint c : constraints) {
+            if (!c.isAllowed(handle, valueResolver)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public Declaration[] getRequiredDeclarations() {
+        return new Declaration[0];
+    }
+
+    @Override
+    public void replaceDeclaration(Declaration oldDecl, Declaration newDecl) {
+        // Sequence step constraints are always self-contained alpha constraints —
+        // they carry no cross-pattern declaration references, so getRequiredDeclarations()
+        // returns empty and LogicTransformer never calls this method.
+    }
+
+    @Override
+    public CombinedAlphaConstraint clone() {
+        AlphaNodeFieldConstraint[] cloned = new AlphaNodeFieldConstraint[constraints.length];
+        for (int i = 0; i < constraints.length; i++) {
+            cloned[i] = (AlphaNodeFieldConstraint) constraints[i].clone();
+        }
+        return new CombinedAlphaConstraint(cloned);
+    }
+
+    @Override
+    public AlphaNodeFieldConstraint cloneIfInUse() {
+        return clone();
+    }
+
+    @Override
+    public ConstraintType getType() {
+        return ConstraintType.ALPHA;
+    }
+
+    @Override
+    public boolean isTemporal() {
+        return false;
+    }
+
+    @Override
+    public void writeExternal(ObjectOutput out) throws IOException {
+        out.writeObject(constraints);
+    }
+
+    @Override
+    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+        constraints = (AlphaNodeFieldConstraint[]) in.readObject();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof CombinedAlphaConstraint)) return false;
+        return Arrays.equals(constraints, ((CombinedAlphaConstraint) o).constraints);
+    }
+
+    @Override
+    public int hashCode() {
+        return Arrays.hashCode(constraints);
+    }
+}
diff --git a/drools-base/src/main/java/org/drools/base/util/CircularArrayList.java b/drools-base/src/main/java/org/drools/base/util/CircularArrayList.java
new file mode 100644
index 00000000000..0739de1fc21
--- /dev/null
+++ b/drools-base/src/main/java/org/drools/base/util/CircularArrayList.java
@@ -0,0 +1,128 @@
+/*
+ * 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.base.util;
+
+import java.lang.reflect.Array;
+import java.util.Arrays;
+
+public class CircularArrayList<E> {
+    private E[] array;
+    private int head = 0;
+    // windowSize is the logical max-kept capacity (as requested by the caller).
+    // arrayCapacity is rounded up to the nearest power of two for fast modulo via mask.
+    private final int windowSize;
+    private final int arrayCapacity;
+    private final int mask;
+    private Class cls;
+
+    public CircularArrayList(int capacity) {
+        this(Object.class, capacity);
+    }
+
+    public CircularArrayList(Class cls, int capacity) {
+        // Round up to the nearest power of two (same strategy as HashMap).
+        int cap = capacity <= 1 ? 1 : Integer.highestOneBit(capacity - 1) << 1;
+        this.windowSize    = capacity;
+        this.arrayCapacity = cap;
+        this.mask          = cap - 1;
+        this.array         = (E[]) new Object[cap];
+        this.cls           = cls;
+    }
+
+    public boolean set(int index, E e) {
+        array[index & mask] = e;
+        return true;
+    }
+
+    /**
+     * Write {@code e} at the given logical {@code index} and advance {@code head}
+     * to {@code index + 1} if it has not reached that point yet. This allows
+     * writing to an arbitrary slot (e.g. by filterIndex) while keeping the
+     * {@code get()} range check valid.
+     */
+    public void put(int index, E e) {
+        array[index & mask] = e;
+        if (index >= head) {
+            head = index + 1;
+        }
+    }
+
+    public boolean add(E e) {
+            array[head++ & mask] = e;
+            return true;
+    }
+
+    public E getHead() {
+        return array[(head-1) & mask];
+    }
+
+    public E getHeadMinus(int i) {
+        return array[(head-1-i) & mask];
+    }
+
+    public E get(int index) {
+        if (index < 0 || index < head - windowSize || index >= head) {
+            throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + windowSize);
+        }
+
+        return array[index & mask];
+    }
+
+    public int size() {
+        return head;
+    }
+
+    public boolean isEmpty() {
+        return size() == 0;
+    }
+
+    public E[] toArray() {
+        int headIndex = head & mask;
+        E[] trg;
+        if (head <= windowSize) {
+            // Buffer has not yet wrapped around the logical window.
+            trg = (E[]) Array.newInstance(cls, head);
+            System.arraycopy(array, 0, trg, 0, head);
+        } else {
+            // Buffer is full: oldest entry starts at headIndex (mod arrayCapacity).
+            trg = (E[]) Array.newInstance(cls, windowSize);
+            // headIndex is where the *next* write goes, which is also the oldest slot.
+            // We need to copy windowSize elements starting from that position.
+            int oldestIndex = (head - windowSize) & mask;
+            if (oldestIndex + windowSize <= arrayCapacity) {
+                System.arraycopy(array, oldestIndex, trg, 0, windowSize);
+            } else {
+                int firstPart = arrayCapacity - oldestIndex;
+                System.arraycopy(array, oldestIndex, trg, 0, firstPart);
+                System.arraycopy(array, 0, trg, firstPart, windowSize - firstPart);
+            }
+        }
+
+        return trg;
+    }
+
+    @Override
+    public String toString() {
+        return "CircularArrayList{" +
+               "array=" + Arrays.toString(array) +
+               ", head=" + head +
+               ", windowSize=" + windowSize +
+               '}';
+    }
+}
diff --git a/drools-core/src/test/java/org/drools/core/util/LinkedListTest.java b/drools-base/src/test/java/org/drools/base/util/LinkedListTest.java
similarity index 97%
rename from drools-core/src/test/java/org/drools/core/util/LinkedListTest.java
rename to drools-base/src/test/java/org/drools/base/util/LinkedListTest.java
index 28a5f26318b..746993ec2a7 100644
--- a/drools-core/src/test/java/org/drools/core/util/LinkedListTest.java
+++ b/drools-base/src/test/java/org/drools/base/util/LinkedListTest.java
@@ -16,13 +16,10 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.drools.core.util;
+package org.drools.base.util;
 
-import org.drools.base.util.AbstractLinkedListNode;
-import org.drools.base.util.DoubleLinkedEntry;
-import org.drools.base.util.LinkedList;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
+import org.junit.Before;
+import org.junit.Test;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.fail;
@@ -35,7 +32,7 @@ public class LinkedListTest {
     DoubleLinkedEntry node2 = null;
     DoubleLinkedEntry node3 = null;
 
-    @BeforeEach
+    @Before
     public void setUp() throws Exception {
         this.list = new LinkedList();
         this.node1 = new AbstractBaseLinkedListNodeMock();
diff --git a/drools-core/src/main/java/org/drools/core/common/SuperCacheFixer.java b/drools-core/src/main/java/org/drools/core/common/SuperCacheFixer.java
index 2ea4040660c..a9208e092e8 100644
--- a/drools-core/src/main/java/org/drools/core/common/SuperCacheFixer.java
+++ b/drools-core/src/main/java/org/drools/core/common/SuperCacheFixer.java
@@ -42,6 +42,7 @@ import org.drools.core.reteoo.ObjectTypeNodeId;
 import org.drools.core.reteoo.QueryElementNode;
 import org.drools.core.reteoo.QueryTerminalNode;
 import org.drools.core.reteoo.ReactiveFromNode;
+import org.drools.core.reteoo.SequenceNode;
 import org.drools.core.reteoo.TupleToObjectNode;
 import org.drools.core.reteoo.RightTuple;
 import org.drools.core.reteoo.RightTupleSink;
@@ -77,6 +78,7 @@ public class SuperCacheFixer {
             case NodeTypeEnums.QueryElementNode: return (QueryElementNode) s;
             case NodeTypeEnums.TimerConditionNode: return (TimerNode) s;
             case NodeTypeEnums.MockBetaNode: return (LeftTupleNode) s;
+            case NodeTypeEnums.SequenceNode: return (SequenceNode) s;
             default:
                 throw new UnsupportedOperationException("Cannot be cast to LeftTupleNode: " + s);
         }
@@ -115,6 +117,7 @@ public class SuperCacheFixer {
             case NodeTypeEnums.QueryElementNode: return (QueryElementNode) n;
             case NodeTypeEnums.TimerConditionNode: return (TimerNode) n;
             case NodeTypeEnums.MockBetaNode: return (LeftTupleSinkNode) n;
+            case NodeTypeEnums.SequenceNode: return (SequenceNode) n;
             default:
                 throw new UnsupportedOperationException("Cannot be cast to LeftTupleNode: " + n);
         }
@@ -139,6 +142,7 @@ public class SuperCacheFixer {
             case NodeTypeEnums.QueryElementNode: return ((QueryElementNode) s).getInputOtnId();
             case NodeTypeEnums.TimerConditionNode: return ((TimerNode) s).getInputOtnId();
             case NodeTypeEnums.MockBetaNode: return ((LeftTupleSource)s).getInputOtnId();
+            case NodeTypeEnums.SequenceNode: return ((SequenceNode)s).getInputOtnId();
             default:
                 throw new UnsupportedOperationException("Node does not have an LeftInputOtnId: " + s);
         }
@@ -175,6 +179,7 @@ public class SuperCacheFixer {
             case NodeTypeEnums.ConditionalBranchNode: return  ((ConditionalBranchNode) s).getLeftTupleSource();
             case NodeTypeEnums.QueryElementNode: return  ((QueryElementNode) s).getLeftTupleSource();
             case NodeTypeEnums.TimerConditionNode: return  ((TimerNode) s).getLeftTupleSource();
+            case NodeTypeEnums.SequenceNode: return ((SequenceNode)s).getLeftTupleSource();
             default:
                 throw new UnsupportedOperationException("Does not have a LeftTupleSource: " + s);
         }
diff --git a/drools-core/src/main/java/org/drools/core/impl/WorkingMemoryReteExpireAction.java b/drools-core/src/main/java/org/drools/core/impl/WorkingMemoryReteExpireAction.java
index 5841d09dfdc..db424cbd2e0 100644
--- a/drools-core/src/main/java/org/drools/core/impl/WorkingMemoryReteExpireAction.java
+++ b/drools-core/src/main/java/org/drools/core/impl/WorkingMemoryReteExpireAction.java
@@ -23,14 +23,14 @@ import java.io.IOException;
 import java.io.ObjectInput;
 import java.io.ObjectOutput;
 
+import org.drools.base.phreak.PropagationEntry;
+import org.drools.base.phreak.actions.AbstractPropagationEntry;
 import org.drools.core.common.DefaultEventHandle;
 import org.drools.core.common.DefaultFactHandle;
 import org.drools.core.common.PropagationContext;
 import org.drools.core.common.ReteEvaluator;
 import org.drools.core.common.WorkingMemoryAction;
 import org.drools.core.marshalling.MarshallerReaderContext;
-import org.drools.base.phreak.PropagationEntry;
-import org.drools.base.phreak.actions.AbstractPropagationEntry;
 import org.drools.core.phreak.actions.AbstractPartitionedPropagationEntry;
 import org.drools.core.reteoo.ObjectTypeNode;
 import org.drools.core.reteoo.RightTuple;
@@ -110,7 +110,7 @@ public class WorkingMemoryReteExpireAction
     }
 
     @Override
-    public PropagationEntry getSplitForPartition( int partitionNr ) {
+    public PropagationEntry<ReteEvaluator> getSplitForPartition( int partitionNr ) {
         return new PartitionAwareWorkingMemoryReteExpireAction( factHandle, node, partitionNr );
     }
 
diff --git a/drools-core/src/main/java/org/drools/core/phreak/BuildtimeSegmentUtilities.java b/drools-core/src/main/java/org/drools/core/phreak/BuildtimeSegmentUtilities.java
index 7d06a644862..90322386dbc 100644
--- a/drools-core/src/main/java/org/drools/core/phreak/BuildtimeSegmentUtilities.java
+++ b/drools-core/src/main/java/org/drools/core/phreak/BuildtimeSegmentUtilities.java
@@ -40,6 +40,8 @@ import org.drools.core.reteoo.PathEndNode;
 import org.drools.core.reteoo.PathEndNode.PathMemSpec;
 import org.drools.core.reteoo.QueryElementNode;
 import org.drools.core.reteoo.ReactiveFromNode;
+import org.drools.core.reteoo.SegmentMemory.SequenceMemoryPrototype;
+import org.drools.core.reteoo.SequenceNode;
 import org.drools.core.reteoo.TupleToObjectNode;
 import org.drools.core.reteoo.SegmentMemory.AccumulateMemoryPrototype;
 import org.drools.core.reteoo.SegmentMemory.AsyncReceiveMemoryPrototype;
@@ -240,6 +242,9 @@ public class BuildtimeSegmentUtilities {
                     case NodeTypeEnums.TimerConditionNode:
                         processTimerNode((TimerNode) node, memories, nodes, nodePosMask);
                         break;
+                    case NodeTypeEnums.SequenceNode:
+                        processSequenceNode((SequenceNode) node, memories, nodes, nodePosMask);
+                        break;
                     case NodeTypeEnums.AsyncSendNode:
                         processAsyncSendNode((AsyncSendNode) node, memories, nodes);
                         break;
@@ -379,6 +384,12 @@ public class BuildtimeSegmentUtilities {
         nodes.add(tupleSource);
     }
 
+    private static void processSequenceNode(SequenceNode tupleSource, List<MemoryPrototype> memories, List<LeftTupleNode> nodes, long nodePosMask) {
+        SequenceMemoryPrototype seqMem = new SequenceMemoryPrototype(nodePosMask);
+        memories.add(seqMem);
+        nodes.add(tupleSource);
+    }
+
     private static long processLiaNode(LeftInputAdapterNode tupleSource,
                                        List<MemoryPrototype> memories,
                                        List<LeftTupleNode> nodes,
diff --git a/drools-core/src/main/java/org/drools/core/phreak/PhreakNetworkNodeFactory.java b/drools-core/src/main/java/org/drools/core/phreak/PhreakNetworkNodeFactory.java
index 44413871198..6933b15e092 100644
--- a/drools-core/src/main/java/org/drools/core/phreak/PhreakNetworkNodeFactory.java
+++ b/drools-core/src/main/java/org/drools/core/phreak/PhreakNetworkNodeFactory.java
@@ -19,6 +19,7 @@
 package org.drools.core.phreak;
 
 import org.drools.core.common.ReteEvaluator;
+import org.drools.core.reteoo.SequenceNode.PhreakSequenceNode;
 import org.kie.api.internal.utils.KieService;
 
 public interface PhreakNetworkNodeFactory extends KieService {
@@ -47,6 +48,8 @@ public interface PhreakNetworkNodeFactory extends KieService {
 
     PhreakAsyncSendNode createPhreakAsyncSendNode(ReteEvaluator reteEvaluator);
 
+    PhreakSequenceNode createPhreakSequenceNode(ReteEvaluator reteEvaluator);
+
     PhreakAsyncReceiveNode createPhreakAsyncReceiveNode(ReteEvaluator reteEvaluator);
 
     PhreakRuleTerminalNode createPhreakRuleTerminalNode(ReteEvaluator reteEvaluator);
diff --git a/drools-core/src/main/java/org/drools/core/phreak/PhreakNetworkNodeFactoryImpl.java b/drools-core/src/main/java/org/drools/core/phreak/PhreakNetworkNodeFactoryImpl.java
index 0c5f117db52..f7a3c56ef49 100644
--- a/drools-core/src/main/java/org/drools/core/phreak/PhreakNetworkNodeFactoryImpl.java
+++ b/drools-core/src/main/java/org/drools/core/phreak/PhreakNetworkNodeFactoryImpl.java
@@ -18,6 +18,8 @@
  */
 package org.drools.core.phreak;
 
+import org.drools.core.reteoo.SequenceNode.PhreakSequenceNode;
+
 import org.drools.core.common.ReteEvaluator;
 
 public class PhreakNetworkNodeFactoryImpl implements PhreakNetworkNodeFactory {
@@ -77,6 +79,11 @@ public class PhreakNetworkNodeFactoryImpl implements PhreakNetworkNodeFactory {
         return new PhreakTimerNode(reteEvaluator);
     }
 
+    @Override
+    public PhreakSequenceNode createPhreakSequenceNode(ReteEvaluator reteEvaluator) {
+        return new PhreakSequenceNode(reteEvaluator);
+    }
+
     @Override
     public PhreakAsyncSendNode createPhreakAsyncSendNode(ReteEvaluator reteEvaluator) {
         return new PhreakAsyncSendNode(reteEvaluator);
diff --git a/drools-core/src/main/java/org/drools/core/phreak/PhreakTimerNode.java b/drools-core/src/main/java/org/drools/core/phreak/PhreakTimerNode.java
index 481c136a1d5..a520e6b65fb 100644
--- a/drools-core/src/main/java/org/drools/core/phreak/PhreakTimerNode.java
+++ b/drools-core/src/main/java/org/drools/core/phreak/PhreakTimerNode.java
@@ -41,7 +41,6 @@ import org.drools.core.reteoo.TupleFactory;
 import org.drools.core.reteoo.TupleImpl;
 import org.drools.core.time.Job;
 import org.drools.core.time.JobContext;
-import org.drools.base.phreak.PropagationEntry;
 import org.drools.base.phreak.actions.AbstractPropagationEntry;
 import org.drools.core.time.TimerService;
 import org.drools.core.time.impl.DefaultJobHandle;
diff --git a/drools-core/src/main/java/org/drools/core/phreak/RuleNetworkEvaluatorImpl.java b/drools-core/src/main/java/org/drools/core/phreak/RuleNetworkEvaluatorImpl.java
index c6a4759f3b8..9e6c2b6eb32 100644
--- a/drools-core/src/main/java/org/drools/core/phreak/RuleNetworkEvaluatorImpl.java
+++ b/drools-core/src/main/java/org/drools/core/phreak/RuleNetworkEvaluatorImpl.java
@@ -26,6 +26,7 @@ import java.util.List;
 
 import org.drools.base.common.NetworkNode;
 import org.drools.base.reteoo.NodeTypeEnums;
+import org.drools.base.util.LinkedList;
 import org.drools.core.common.ActivationsManager;
 import org.drools.core.common.Memory;
 import org.drools.core.common.NodeMemories;
@@ -63,6 +64,9 @@ import org.drools.core.reteoo.QueryTerminalNode;
 import org.drools.core.reteoo.ReactiveFromNode;
 import org.drools.core.reteoo.RightInputAdapterNode;
 import org.drools.core.reteoo.SegmentMemory;
+import org.drools.core.reteoo.SequenceNode;
+import org.drools.core.reteoo.SequenceNode.PhreakSequenceNode;
+import org.drools.core.reteoo.SequenceNode.SequenceNodeMemory;
 import org.drools.core.reteoo.SubnetworkTuple;
 import org.drools.core.reteoo.TimerNode;
 import org.drools.core.reteoo.TimerNode.TimerNodeMemory;
@@ -88,6 +92,7 @@ public class RuleNetworkEvaluatorImpl implements RuleNetworkEvaluator {
     private final PhreakBranchNode       pBranchNode;
     private final PhreakQueryNode        pQueryNode;
     private final PhreakTimerNode        pTimerNode;
+    private final PhreakSequenceNode     pSequenceNode;
     private final PhreakAsyncSendNode    pSendNode;
     private final PhreakAsyncReceiveNode pReceiveNode;
     private final PhreakRuleTerminalNode pRtNode;
@@ -113,6 +118,7 @@ public class RuleNetworkEvaluatorImpl implements RuleNetworkEvaluator {
         pBranchNode = PhreakNetworkNodeFactory.Factory.get().createPhreakBranchNode(reteEvaluator);
         pQueryNode  = PhreakNetworkNodeFactory.Factory.get().createPhreakQueryNode(reteEvaluator);
         pTimerNode  = PhreakNetworkNodeFactory.Factory.get().createPhreakTimerNode(reteEvaluator);
+        pSequenceNode = PhreakNetworkNodeFactory.Factory.get().createPhreakSequenceNode(reteEvaluator);
         pSendNode   = PhreakNetworkNodeFactory.Factory.get().createPhreakAsyncSendNode(reteEvaluator);
         pReceiveNode = PhreakNetworkNodeFactory.Factory.get().createPhreakAsyncReceiveNode(reteEvaluator);
         pRtNode     = PhreakNetworkNodeFactory.Factory.get().createPhreakRuleTerminalNode(reteEvaluator);
@@ -432,6 +438,10 @@ public class RuleNetworkEvaluatorImpl implements RuleNetworkEvaluator {
                 pReceiveNode.doNode((AsyncReceiveNode) sc.getCurrentNode(), (AsyncReceiveMemory) sc.getCurrentNodeMemory(), sink, sc.getSourceTuples(), trgTuples);
                 break;
             }
+            case NodeTypeEnums.SequenceNode: {
+                pSequenceNode.doNode((SequenceNode) sc.getCurrentNode(), (SequenceNodeMemory) sc.getCurrentNodeMemory(), sink, sc.getSourceTuples(), trgTuples, sc.getStagedLeftTuples());
+                break;
+            }
         }
         if (exitInnerEval && trgTuples.isEmpty()) {
             return null;
diff --git a/drools-core/src/main/java/org/drools/core/reteoo/RightInputAdapterNode.java b/drools-core/src/main/java/org/drools/core/reteoo/RightInputAdapterNode.java
index 11337ad668e..f817f43ee5f 100644
--- a/drools-core/src/main/java/org/drools/core/reteoo/RightInputAdapterNode.java
+++ b/drools-core/src/main/java/org/drools/core/reteoo/RightInputAdapterNode.java
@@ -1,4 +1,4 @@
-/**
+/*
  * 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
@@ -406,7 +406,8 @@ public abstract class RightInputAdapterNode<T extends  BetaNode> extends BaseNod
 
     @Override
     public String toString() {
-        return "[RightInputAdapterNode(" + id + ")]";
+        return "RightInputAdapterNode(" + id + ")[ source="
+               + source + ", associations=" + associations + ", partitionId=" + partitionId + "]";
     }
 
     public void networkUpdated(UpdateContext updateContext) {
diff --git a/drools-core/src/main/java/org/drools/core/reteoo/SegmentMemory.java b/drools-core/src/main/java/org/drools/core/reteoo/SegmentMemory.java
index 26fb206a0b9..2d2c82f4ddd 100644
--- a/drools-core/src/main/java/org/drools/core/reteoo/SegmentMemory.java
+++ b/drools-core/src/main/java/org/drools/core/reteoo/SegmentMemory.java
@@ -1162,6 +1162,19 @@ public class SegmentMemory extends LinkedList<SegmentMemory>
         }
     }
 
+    public static class SequenceMemoryPrototype extends MemoryPrototype {
+
+        public SequenceMemoryPrototype(long nodePosMaskBit) {
+            this.nodePosMaskBit = nodePosMaskBit;
+        }
+
+        @Override
+        public void populateMemory(NodeMemories nodeMemories, SegmentMemorySupport segmentMemorySupport, Memory mem) {
+            SequenceNode.SequenceNodeMemory seqmem = (SequenceNode.SequenceNodeMemory)  mem;
+            seqmem.setNodePosMaskBit(nodePosMaskBit);
+        }
+    }
+
     public static class AsyncSendMemoryPrototype extends MemoryPrototype {
 
         public AsyncSendMemoryPrototype() {}
diff --git a/drools-core/src/main/java/org/drools/core/reteoo/SequenceNode.java b/drools-core/src/main/java/org/drools/core/reteoo/SequenceNode.java
new file mode 100644
index 00000000000..d6c78c139af
--- /dev/null
+++ b/drools-core/src/main/java/org/drools/core/reteoo/SequenceNode.java
@@ -0,0 +1,550 @@
+/*
+ * 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.core.reteoo;
+
+import org.drools.base.base.ObjectType;
+import org.drools.base.common.RuleBasePartitionId;
+import org.drools.base.reteoo.DynamicFilter;
+import org.drools.base.reteoo.DynamicFilterProto;
+import org.drools.base.reteoo.NodeTypeEnums;
+import org.drools.base.reteoo.sequencing.Sequence.SequenceMemory;
+import org.drools.base.rule.Pattern;
+import org.drools.core.RuleBaseConfiguration;
+import org.drools.core.common.InternalFactHandle;
+import org.drools.core.common.InternalWorkingMemory;
+import org.drools.core.common.Memory;
+import org.drools.core.common.MemoryFactory;
+import org.drools.core.common.PropagationContext;
+import org.drools.core.common.ReteEvaluator;
+import org.drools.core.common.TupleSets;
+import org.drools.core.common.TupleSetsImpl;
+import org.drools.core.common.UpdateContext;
+import org.drools.core.reteoo.SequenceNode.SequenceNodeMemory;
+import org.drools.core.reteoo.builder.BuildContext;
+import org.drools.base.reteoo.sequencing.Sequencer;
+import org.drools.core.reteoo.sequencing.SequencerMemoryImpl;
+import org.drools.base.reteoo.sequencing.SequencerMemory;
+import org.drools.base.util.AbstractLinkedListNode;
+import org.drools.base.util.LinkedList;
+import org.drools.core.util.index.TupleList;
+import org.drools.util.bitmask.BitMask;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.List;
+
+public class SequenceNode extends LeftTupleSource
+    implements
+    LeftTupleNode,
+    LeftTupleSinkNode,
+    MemoryFactory<SequenceNodeMemory> {
+
+    private static final long      serialVersionUID = 510l;
+
+    protected boolean              tupleMemoryEnabled;
+
+    private Sequencer sequencer;
+
+    private AlphaAdapter[] alphaAdapters;
+
+    private DynamicFilterProto[] dynamicFilters;
+
+    private LeftTupleSinkNode      previousTupleSinkNode;
+
+    private LeftTupleSinkNode      nextTupleSinkNode;
+
+    public SequenceNode() {
+
+    }
+
+    public SequenceNode(final int id,
+                        final LeftTupleSource tupleSource,
+                        final BuildContext context) {
+        super(id, context);
+        setLeftTupleSource(tupleSource);
+        this.setObjectCount(leftInput.getObjectCount()); // 'sequence' nodes do not increase the object count
+        this.tupleMemoryEnabled = context.isTupleMemoryEnabled();
+
+        initMasks(context);
+
+        hashcode = calculateHashCode();
+        setStreamMode(true);
+    }
+
+    public void setAlphaAdapters(AlphaAdapter[] adapters) {
+        this.alphaAdapters = adapters;
+    }
+
+    public DynamicFilterProto[] getDynamicFilters() {
+        return dynamicFilters;
+    }
+
+    public void setDynamicFilters(DynamicFilterProto[] dynamicFilters) {
+        this.dynamicFilters = dynamicFilters;
+    }
+
+    public void doAttach(BuildContext context) {
+        super.doAttach(context);
+        this.leftInput.addTupleSink( this, context );
+    }
+
+    public void networkUpdated(UpdateContext updateContext) {
+        this.leftInput.networkUpdated(updateContext);
+    }
+
+    public String toString() {
+        return "[SequenceNode(" + this.id + ")]]";
+    }
+
+    private int calculateHashCode() {
+        int result = this.leftInput.hashCode();
+        if (this.sequencer != null) {
+            result = 31 * result + System.identityHashCode(this.sequencer.getSequence());
+        }
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object object) {
+        if (this == object) {
+            return true;
+        }
+
+        if ( object == null || !(object instanceof SequenceNode) || this.hashCode() != object.hashCode() ) {
+            return false;
+        }
+
+        SequenceNode other = (SequenceNode) object;
+        return this.leftInput.getId() == other.leftInput.getId()
+            && this.sequencer.getSequence() == other.sequencer.getSequence();
+    }
+
+    public SequenceNodeMemory createMemory(final RuleBaseConfiguration config, ReteEvaluator reteEvaluator) {
+        LinkedList<DynamicFilter>[] activeFilters = new LinkedList[alphaAdapters.length];
+        SequenceNodeMemory          memory  = new SequenceNodeMemory(this, activeFilters, new DynamicFilter[dynamicFilters.length]);
+        return memory;
+    }
+
+    public SequencerMemory createSequencerMemory(TupleImpl lt, LeftTupleSink sink, SequenceNodeMemory nodeMemory) {
+        SequencerMemory sequencerMemory = new SequencerMemoryImpl(sequencer, lt, sink, nodeMemory);
+
+        return sequencerMemory;
+    }
+
+    public Sequencer getSequencer() {
+        return sequencer;
+    }
+
+    public void setSequencer(Sequencer sequencer) {
+        this.sequencer = sequencer;
+        this.hashcode = calculateHashCode();
+    }
+
+    public boolean isLeftTupleMemoryEnabled() {
+        return tupleMemoryEnabled;
+    }
+
+    public LeftTupleSinkNode getNextLeftTupleSinkNode() {
+        return this.nextTupleSinkNode;
+    }
+
+    public void setNextLeftTupleSinkNode(final LeftTupleSinkNode next) {
+        this.nextTupleSinkNode = next;
+    }
+
+    public LeftTupleSinkNode getPreviousLeftTupleSinkNode() {
+        return this.previousTupleSinkNode;
+    }
+
+    public void setPreviousLeftTupleSinkNode(final LeftTupleSinkNode previous) {
+        this.previousTupleSinkNode = previous;
+    }
+
+    public int getType() {
+        return NodeTypeEnums.SequenceNode;
+    }
+
+    public static class SequenceNodeMemory extends AbstractLinkedListNode<Memory>
+        implements
+        Externalizable,
+        Memory {
+
+        private static final long serialVersionUID = 510l;
+
+        private TupleMemory       leftTupleMemory;
+
+        private TupleSets         stagedChildTuples;
+
+        private SegmentMemory     memory;
+
+        private LinkedList<DynamicFilter>[] activeFilters;
+
+        private DynamicFilter[] filters;
+
+        private SequenceNode node;
+
+        private long          nodePosMaskBit;
+
+        public SequenceNodeMemory(SequenceNode node, LinkedList<DynamicFilter>[] activeFilters, DynamicFilter[] filters) {
+            this.node = node;
+            this.stagedChildTuples = new TupleSetsImpl();
+            this.leftTupleMemory = new TupleList();
+            this.activeFilters = activeFilters;
+            this.filters = filters;
+        }
+
+        public void addActiveFilter(DynamicFilter filter) {
+            int adapterIdx = filter.getAdapterIndex();
+            if (this.activeFilters[adapterIdx] == null) {
+                this.activeFilters[adapterIdx] = new LinkedList<>();
+            }
+            this.activeFilters[adapterIdx].add(filter);
+        }
+
+        public void removeActiveFilter(DynamicFilter filter) {
+            this.activeFilters[filter.getAdapterIndex()].remove(filter);
+        }
+
+        public LinkedList<DynamicFilter>[] getActiveFilters() {
+            return this.activeFilters;
+        }
+
+        public DynamicFilter[] getFilters() {
+            return filters;
+        }
+
+        public void setFilters(DynamicFilter[] filters) {
+            this.filters = filters;
+        }
+
+        public TupleMemory getLeftTupleMemory() {
+            return leftTupleMemory;
+        }
+
+        public TupleSets getStagedChildTuples() {
+            return stagedChildTuples;
+        }
+
+        public void readExternal(ObjectInput in) throws IOException,
+                                                ClassNotFoundException {
+            throw new UnsupportedOperationException("SequenceNodeMemory does not support serialization");
+        }
+
+        public void writeExternal(ObjectOutput out) throws IOException {
+            throw new UnsupportedOperationException("SequenceNodeMemory does not support serialization");
+        }
+
+        public int getNodeType() {
+            return NodeTypeEnums.SequenceNode;
+        }
+
+        public void setSegmentMemory(SegmentMemory smem) {
+            this.memory = smem;
+        }
+
+        public SegmentMemory getSegmentMemory() {
+            return this.memory;
+        }
+
+        public void reset() {
+            leftTupleMemory.clear();
+            stagedChildTuples.resetAll();
+            for (int i = 0; i < activeFilters.length; i++) {
+                if (activeFilters[i] != null) {
+                    activeFilters[i].clear();
+                }
+            }
+            java.util.Arrays.fill(filters, null);
+        }
+
+        public DynamicFilter getActiveDynamicFilter(int filterIndex) {
+            DynamicFilter filter = filters[filterIndex];
+
+            if (filter == null) {
+                DynamicFilterProto proto = node.getDynamicFilters()[filterIndex];
+                filter = new DynamicFilter(proto);
+                filters[filterIndex] = filter;
+                addActiveFilter(filter);
+            } else if (filter.getSignalAdapters().isEmpty()) {
+                addActiveFilter(filter);
+            }
+
+            return filter;
+        }
+
+        public void setNodePosMaskBit(long nodePosMaskBit) {
+            this.nodePosMaskBit = nodePosMaskBit;
+        }
+
+        public long getNodePosMaskBit() {
+            return nodePosMaskBit;
+        }
+    }
+
+    @Override
+    public ObjectTypeNode getObjectTypeNode() {
+        return leftInput.getObjectTypeNode();
+    }
+
+    protected boolean doRemove(final RuleRemovalContext context,
+                               final ReteooBuilder builder) {
+        if (!this.isInUse()) {
+            getLeftTupleSource().removeTupleSink(this);
+
+            if (alphaAdapters != null) {
+                for (AlphaAdapter adapter : alphaAdapters) {
+                    ((ObjectSource) adapter.getParent()).removeObjectSink(adapter);
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+
+    public static class AlphaAdapter extends ObjectSource
+            implements
+            ObjectSinkNode,
+            RightTupleSink {
+        private int          adapterIndex;
+
+        private ObjectTypeNodeId otnId;
+
+        private SequenceNode node;
+
+
+        public AlphaAdapter(int id, ObjectSource source, RuleBasePartitionId partitionId, SequenceNode node, int adapterIndex) {
+            super(id, source, partitionId);
+            this.adapterIndex = adapterIndex;
+            this.node = node;
+        }
+
+        public void assertObject(final InternalFactHandle factHandle,
+                                 final PropagationContext pctx,
+                                 final ReteEvaluator reteEvaluator) {
+            SequenceNodeMemory memory = reteEvaluator.getNodeMemory(node);
+
+            LinkedList<DynamicFilter> filters = memory.getActiveFilters()[adapterIndex];
+            if (filters != null) {
+                for (DynamicFilter f = filters.getFirst(); f != null; ) {
+                    DynamicFilter next = f.getNext();
+                    f.assertObject(factHandle, reteEvaluator);
+                    f = next;
+                }
+            }
+        }
+
+        public void propagate() {
+
+        }
+
+        public void modifyObject(InternalFactHandle factHandle,
+                                 ModifyPreviousTuples modifyPreviousTuples,
+                                 PropagationContext context,
+                                 ReteEvaluator reteEvaluator) {
+
+        }
+
+        @Override
+        public void retractRightTuple(TupleImpl rightTuple, PropagationContext context, ReteEvaluator reteEvaluator) {
+            // for now assuming we don't have any deletes, but we could add this later.
+        }
+
+        @Override
+        public void modifyRightTuple(TupleImpl rightTuple, PropagationContext context, ReteEvaluator reteEvaluator) {
+            // for now assuming we don't have any modifies, but we could add this later.
+            // typically just gets treated as another add, as the processor is not that stateful (for this use case) - to keep it fast.
+        }
+
+        public int getId() {
+            return super.getId();
+        }
+
+        public void byPassModifyToBetaNode(InternalFactHandle factHandle,
+                                           ModifyPreviousTuples modifyPreviousTuples,
+                                           PropagationContext context,
+                                           ReteEvaluator reteEvaluator) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public ObjectSinkNode getNextObjectSinkNode() {
+            return null;
+        }
+
+        @Override
+        public void setNextObjectSinkNode(ObjectSinkNode next) {
+            // not used — AlphaAdapter is not part of the sink propagation chain
+        }
+
+        @Override
+        public ObjectSinkNode getPreviousObjectSinkNode() {
+            return null;
+        }
+
+        @Override
+        public void setPreviousObjectSinkNode(ObjectSinkNode previous) {
+            // not used — AlphaAdapter is not part of the sink propagation chain
+        }
+
+        @Override
+        public BitMask calculateDeclaredMask(Pattern pattern, ObjectType modifiedType, List<String> settableProperties) {
+            return null;
+        }
+
+        @Override
+        public void updateSink(ObjectSink sink, PropagationContext context, InternalWorkingMemory workingMemory) {
+
+        }
+
+        @Override
+        public ObjectTypeNodeId getInputOtnId() {
+            return otnId;
+        }
+
+        public int getType() {
+            return NodeTypeEnums.SequenceNode;
+        }
+
+        public void doAttach(BuildContext context) {
+            super.doAttach(context);
+            this.source.addObjectSink(this);
+        }
+    }
+
+    public static class PhreakSequenceNode {
+        private final ReteEvaluator reteEvaluator;
+
+        public PhreakSequenceNode(ReteEvaluator reteEvaluator) {
+            this.reteEvaluator = reteEvaluator;
+        }
+
+        public void doNode(SequenceNode node,
+                           SequenceNodeMemory memory,
+                           LeftTupleSink sink,
+                           TupleSets srcLeftTuples,
+                           TupleSets trgLeftTuples,
+                           TupleSets stagedLeftTuples) {
+            if (srcLeftTuples.getDeleteFirst() != null) {
+                doLeftDeletes(node, srcLeftTuples, trgLeftTuples);
+            }
+
+            if (srcLeftTuples.getUpdateFirst() != null) {
+                doLeftUpdates(node, srcLeftTuples, trgLeftTuples);
+            }
+
+            if (srcLeftTuples.getInsertFirst() != null) {
+                doLeftInserts(node, memory, sink, srcLeftTuples);
+            }
+
+            srcLeftTuples.resetAll();
+
+            if (memory.getStagedChildTuples().getInsertFirst() != null) {
+                for (TupleImpl tuple = memory.getStagedChildTuples().getInsertFirst(); tuple != null; ) {
+                    TupleImpl next = tuple.getStagedNext();
+                    trgLeftTuples.addInsert(tuple);
+                    tuple.clearStaged();
+                    tuple = next;
+                }
+                
+                memory.getStagedChildTuples().resetAll();
+            }
+
+            memory.getSegmentMemory().updateCleanNodeMask(memory.getNodePosMaskBit());
+        }
+
+        private void doLeftUpdates(SequenceNode node,
+                                   TupleSets srcLeftTuples,
+                                   TupleSets trgLeftTuples) {
+            for (TupleImpl leftTuple = srcLeftTuples.getUpdateFirst(); leftTuple != null; ) {
+                TupleImpl next = leftTuple.getStagedNext();
+
+                SequencerMemory sequencerMemory = (SequencerMemory) leftTuple.getContextObject();
+                SequenceMemory sequenceMemory = sequencerMemory.getSequenceMemory(sequencerMemory.getSequencer().getSequence());
+                node.getSequencer().stop(sequenceMemory, reteEvaluator);
+                SequenceNodeMemory nodeMemoryForUpdate = reteEvaluator.getNodeMemory(node);
+                deleteChildren(nodeMemoryForUpdate.getStagedChildTuples(), trgLeftTuples, leftTuple);
+
+                node.getSequencer().start(sequencerMemory, reteEvaluator);
+                leftTuple.getMemory().removeAdd(leftTuple);
+
+                leftTuple.clearStaged();
+                leftTuple = next;
+            }
+        }
+
+        private void doLeftDeletes(SequenceNode node,
+                                   TupleSets srcLeftTuples,
+                                   TupleSets trgLeftTuples) {
+            for (TupleImpl leftTuple = srcLeftTuples.getDeleteFirst(); leftTuple != null; ) {
+                TupleImpl next = leftTuple.getStagedNext();
+
+                SequencerMemory sequencerMemory = (SequencerMemory) leftTuple.getContextObject();
+                SequenceNodeMemory nodeMemory = reteEvaluator.getNodeMemory(node);
+
+                SequenceMemory sequenceMemory = sequencerMemory.getSequenceMemory(sequencerMemory.getSequencer().getSequence());
+                node.getSequencer().stop(sequenceMemory, reteEvaluator);
+                leftTuple.getMemory().remove(leftTuple);
+                leftTuple.setContextObject(null);
+
+                deleteChildren(nodeMemory.getStagedChildTuples(), trgLeftTuples, leftTuple);
+
+                leftTuple.clearStaged();
+                leftTuple = next;
+            }
+        }
+
+        private static void deleteChildren(TupleSets stagedChildTuples, TupleSets trgLeftTuples, TupleImpl leftTuple) {
+            if (leftTuple.getFirstChild() != null) {
+                TupleImpl childLeftTuple = leftTuple.getFirstChild();
+                while (childLeftTuple != null) {
+                    TupleImpl nextChild = childLeftTuple.getHandleNext();
+                    childLeftTuple.unlinkFromLeftParent();
+                    if (childLeftTuple.getStagedType() == Tuple.INSERT) {
+                        stagedChildTuples.removeInsert(childLeftTuple);
+                    } else {
+                        trgLeftTuples.addDelete(childLeftTuple);
+                    }
+                    childLeftTuple = nextChild;
+                }
+            }
+        }
+
+        private void doLeftInserts(SequenceNode node,
+                                   SequenceNodeMemory memory,
+                                   LeftTupleSink sink,
+                                   TupleSets srcLeftTuples) {
+            for (TupleImpl leftTuple = srcLeftTuples.getInsertFirst(); leftTuple != null; ) {
+                TupleImpl next = leftTuple.getStagedNext();
+
+                memory.getLeftTupleMemory().add(leftTuple);
+
+                SequencerMemory sequencerMemory = memory.node.createSequencerMemory(leftTuple, sink, memory);
+                leftTuple.setContextObject(sequencerMemory);
+                node.getSequencer().start(sequencerMemory, reteEvaluator);
+
+                leftTuple.clearStaged();
+                leftTuple = next;
+            }
+        }
+
+    }
+}
diff --git a/drools-core/src/main/java/org/drools/core/reteoo/TupleFactory.java b/drools-core/src/main/java/org/drools/core/reteoo/TupleFactory.java
index adcfae21ec7..a41588da264 100644
--- a/drools-core/src/main/java/org/drools/core/reteoo/TupleFactory.java
+++ b/drools-core/src/main/java/org/drools/core/reteoo/TupleFactory.java
@@ -40,6 +40,7 @@ public class TupleFactory {
             case NodeTypeEnums.ConditionalBranchNode:
             case NodeTypeEnums.EvalConditionNode:
             case NodeTypeEnums.TimerConditionNode:
+            case NodeTypeEnums.SequenceNode:
                 peer = new EvalNodeLeftTuple();
                 break;
             case NodeTypeEnums.ReactiveFromNode:
@@ -78,6 +79,7 @@ public class TupleFactory {
             case NodeTypeEnums.AsyncReceiveNode:
             case NodeTypeEnums.ConditionalBranchNode:
             case NodeTypeEnums.EvalConditionNode:
+            case NodeTypeEnums.SequenceNode:
             case NodeTypeEnums.TimerConditionNode:
                 return new EvalNodeLeftTuple(factHandle, s, leftTupleMemoryEnabled);
             case NodeTypeEnums.ReactiveFromNode:
@@ -109,6 +111,7 @@ public class TupleFactory {
             case NodeTypeEnums.ConditionalBranchNode:
             case NodeTypeEnums.EvalConditionNode:
             case NodeTypeEnums.TimerConditionNode:
+            case NodeTypeEnums.SequenceNode:
                 return new EvalNodeLeftTuple(factHandle, leftTuple, s);
             case NodeTypeEnums.ReactiveFromNode:
                 return new ReactiveFromNodeLeftTuple(factHandle, leftTuple, s);
@@ -140,6 +143,7 @@ public class TupleFactory {
             case NodeTypeEnums.ConditionalBranchNode:
             case NodeTypeEnums.EvalConditionNode:
             case NodeTypeEnums.TimerConditionNode:
+            case NodeTypeEnums.SequenceNode:
                 return new EvalNodeLeftTuple(leftTuple, s, pctx, leftTupleMemoryEnabled);
             case NodeTypeEnums.ReactiveFromNode:
                 throw new IllegalStateException("ReactFromNode does not implement this constructor.");
@@ -170,6 +174,7 @@ public class TupleFactory {
             case NodeTypeEnums.ConditionalBranchNode:
             case NodeTypeEnums.EvalConditionNode:
             case NodeTypeEnums.TimerConditionNode:
+            case NodeTypeEnums.SequenceNode:
                 return new EvalNodeLeftTuple(leftTuple, rightTuple, s);
             case NodeTypeEnums.ReactiveFromNode:
                 throw new IllegalStateException("ReactFromNode does not implement this constructor.");
@@ -203,6 +208,7 @@ public class TupleFactory {
             case NodeTypeEnums.ConditionalBranchNode:
             case NodeTypeEnums.EvalConditionNode:
             case NodeTypeEnums.TimerConditionNode:
+            case NodeTypeEnums.SequenceNode:
                 return new EvalNodeLeftTuple(leftTuple, rightTuple, currentLeftChild, currentRightChild, s, leftTupleMemoryEnabled);
             case NodeTypeEnums.ReactiveFromNode:
                 return new ReactiveFromNodeLeftTuple(leftTuple, rightTuple, currentLeftChild, currentRightChild, s, leftTupleMemoryEnabled);
diff --git a/drools-core/src/main/java/org/drools/core/reteoo/builder/NodeFactory.java b/drools-core/src/main/java/org/drools/core/reteoo/builder/NodeFactory.java
index 74936b36ff1..cd63e6ae2b5 100644
--- a/drools-core/src/main/java/org/drools/core/reteoo/builder/NodeFactory.java
+++ b/drools-core/src/main/java/org/drools/core/reteoo/builder/NodeFactory.java
@@ -24,6 +24,7 @@ import java.util.List;
 import org.drools.base.base.ObjectType;
 import org.drools.base.common.RuleBasePartitionId;
 import org.drools.base.definitions.rule.impl.RuleImpl;
+import org.drools.base.reteoo.sequencing.Sequence;
 import org.drools.base.rule.Accumulate;
 import org.drools.base.rule.AsyncReceive;
 import org.drools.base.rule.AsyncSend;
@@ -56,6 +57,7 @@ import org.drools.core.reteoo.ObjectTypeNode;
 import org.drools.core.reteoo.QueryElementNode;
 import org.drools.core.reteoo.ReactiveFromNode;
 import org.drools.core.reteoo.TupleToObjectNode;
+import org.drools.core.reteoo.SequenceNode;
 import org.drools.core.reteoo.TerminalNode;
 import org.drools.core.reteoo.TimerNode;
 import org.drools.core.reteoo.WindowNode;
@@ -95,6 +97,10 @@ public interface NodeFactory {
                                      EvalCondition eval,
                                      BuildContext context);
 
+    SequenceNode buildSequenceNode(int id,
+                                   LeftTupleSource tupleSource,
+                                   Sequence seq,
+                                   BuildContext context);
 
     TupleToObjectNode buildRightInputNode(int id,
                                           LeftTupleSource leftInput,
diff --git a/drools-core/src/main/java/org/drools/core/reteoo/builder/PhreakNodeFactory.java b/drools-core/src/main/java/org/drools/core/reteoo/builder/PhreakNodeFactory.java
index 3087733befd..394434acc54 100644
--- a/drools-core/src/main/java/org/drools/core/reteoo/builder/PhreakNodeFactory.java
+++ b/drools-core/src/main/java/org/drools/core/reteoo/builder/PhreakNodeFactory.java
@@ -20,11 +20,24 @@ package org.drools.core.reteoo.builder;
 
 
 import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 import org.drools.base.base.ObjectType;
 import org.drools.base.common.RuleBasePartitionId;
 import org.drools.base.definitions.rule.impl.RuleImpl;
+import org.drools.base.reteoo.DynamicFilterProto;
+import org.drools.base.rule.Pattern;
+import org.drools.base.rule.constraint.AlphaNodeFieldConstraint;
+import org.drools.base.rule.constraint.CombinedAlphaConstraint;
+import org.drools.base.rule.constraint.Constraint;
+import org.drools.base.reteoo.sequencing.Sequence;
+import org.drools.base.reteoo.sequencing.Sequencer;
 import org.drools.base.rule.Accumulate;
 import org.drools.base.rule.AsyncReceive;
 import org.drools.base.rule.AsyncSend;
@@ -35,7 +48,6 @@ import org.drools.base.rule.From;
 import org.drools.base.rule.GroupElement;
 import org.drools.base.rule.QueryElement;
 import org.drools.base.rule.accessor.DataProvider;
-import org.drools.base.rule.constraint.AlphaNodeFieldConstraint;
 import org.drools.base.time.impl.Timer;
 import org.drools.core.common.BetaConstraints;
 import org.drools.core.reteoo.AccumulateNode;
@@ -46,6 +58,7 @@ import org.drools.core.reteoo.AsyncReceiveNode;
 import org.drools.core.reteoo.AsyncSendNode;
 import org.drools.core.reteoo.ConditionalBranchEvaluator;
 import org.drools.core.reteoo.ConditionalBranchNode;
+import org.drools.core.reteoo.CoreComponentFactory;
 import org.drools.core.reteoo.EntryPointNode;
 import org.drools.core.reteoo.EvalConditionNode;
 import org.drools.core.reteoo.ExistsNode;
@@ -63,6 +76,8 @@ import org.drools.core.reteoo.QueryElementNode;
 import org.drools.core.reteoo.QueryTerminalNode;
 import org.drools.core.reteoo.ReactiveFromNode;
 import org.drools.core.reteoo.TupleToObjectNode;
+import org.drools.core.reteoo.SequenceNode;
+import org.drools.core.reteoo.SequenceNode.AlphaAdapter;
 import org.drools.core.reteoo.RuleTerminalNode;
 import org.drools.core.reteoo.TerminalNode;
 import org.drools.core.reteoo.TimerNode;
@@ -113,6 +128,83 @@ public class PhreakNodeFactory implements NodeFactory, Serializable {
         }
         return new TupleToObjectNode(id, leftInput, startTupleSource, context );
     }
+    public SequenceNode buildSequenceNode(final int id,
+                                          final LeftTupleSource tupleSource,
+                                          final Sequence seq,
+                                          final BuildContext context) {
+        SequenceNode node = new SequenceNode(id, tupleSource, context);
+        node.setSequencer(new Sequencer(seq));
+
+        NodeFactory nFactory = CoreComponentFactory.get().getNodeFactoryService();
+
+        Pattern[] patterns =  seq.getFilters();
+
+        // first attach the OTN sources in Rete.
+        // Use LinkedHashSet to preserve insertion order (first-seen per type),
+        // so objectTypeList index aligns with the first pattern index for that type.
+        Set<ObjectType>  objectTypeSet  = new LinkedHashSet<>();
+        Arrays.stream(patterns).forEach(p -> objectTypeSet.add(p.getObjectType()));
+        List<ObjectType> objectTypeList = new ArrayList<>(objectTypeSet);
+
+        Map<ObjectType, Integer> objectTypeIndex = new HashMap<>(objectTypeList.size() * 2);
+        ObjectSource[] otns = new ObjectSource[objectTypeList.size()];
+        EntryPointNode epn = context.getRuleBase().getRete().getEntryPointNode(context.getCurrentEntryPoint());
+        Map<ObjectType, ObjectTypeNode> existingOtns = epn.getObjectTypeNodes();
+        for ( int i = 0; i < objectTypeList.size(); i++ ) {
+            ObjectType objectType = objectTypeList.get(i);
+            objectTypeIndex.put(objectType, i);
+            ObjectTypeNode existing = existingOtns.get(objectType);
+            if (existing != null) {
+                // Reuse the existing OTN — building a fresh one and calling attach()
+                // would call EntryPointNode.addObjectSink which is a Map.put, silently
+                // replacing the existing OTN and cutting off all rules already wired to it.
+                otns[i] = existing;
+            } else {
+                otns[i] = nFactory.buildObjectTypeNode(context.getNextNodeId(), epn, objectType, context);
+                otns[i].attach(context);
+            }
+        }
+
+        // For each source create an alpha adapter, which will map it to any filters on the stack.
+        AlphaAdapter[] adapters = new AlphaAdapter[objectTypeList.size()];
+        for ( int i = 0; i < objectTypeList.size(); i++ ) {
+            adapters[i] = new AlphaAdapter(context.getNextNodeId(), otns[i],
+                    context.getPartitionId(), node, i);
+
+            adapters[i].attach(context);
+        }
+        node.setAlphaAdapters(adapters);
+
+
+        // Create all the dynamic filters, these are added on demand to the stack.
+        // filterIndex (i) is the pattern index — one per step.
+        // adapterIndex is the index of this pattern's type in objectTypeList — one per distinct type.
+        DynamicFilterProto[] filters = new DynamicFilterProto[patterns.length];
+        for ( int i = 0; i < patterns.length; i++ ) {
+            List<Constraint> constraints = patterns[i].getConstraints();
+            if (constraints.isEmpty()) {
+                throw new IllegalArgumentException(
+                        "sequence step " + i + " has no constraint expression. " +
+                        "Every sequence step must have at least one expr(). " +
+                        "Add an expr() to the pattern, or use a dedicated fact type per step.");
+            }
+            AlphaNodeFieldConstraint combined;
+            if (constraints.size() == 1) {
+                combined = (AlphaNodeFieldConstraint) constraints.get(0);
+            } else {
+                AlphaNodeFieldConstraint[] all = constraints.stream()
+                        .map(c -> (AlphaNodeFieldConstraint) c)
+                        .toArray(AlphaNodeFieldConstraint[]::new);
+                combined = new CombinedAlphaConstraint(all);
+            }
+            int adapterIndex = objectTypeIndex.get(patterns[i].getObjectType());
+            filters[i] = new DynamicFilterProto(combined, adapterIndex);
+        }
+
+        node.setDynamicFilters( filters);
+
+        return node;
+    }
 
     public JoinNode buildJoinNode( int id, LeftTupleSource leftInput, ObjectSource rightInput, BetaConstraints binder, BuildContext context ) {
         JoinRightAdapterNode joinRight = new JoinRightAdapterNode(id, rightInput, context);
diff --git a/drools-core/src/main/java/org/drools/core/reteoo/builder/ReteooRuleBuilder.java b/drools-core/src/main/java/org/drools/core/reteoo/builder/ReteooRuleBuilder.java
index 6403061d31f..e6f47d468a2 100755
--- a/drools-core/src/main/java/org/drools/core/reteoo/builder/ReteooRuleBuilder.java
+++ b/drools-core/src/main/java/org/drools/core/reteoo/builder/ReteooRuleBuilder.java
@@ -25,6 +25,7 @@ import java.util.List;
 import org.drools.base.base.ClassObjectType;
 import org.drools.base.definitions.rule.impl.RuleImpl;
 import org.drools.base.reteoo.NodeTypeEnums;
+import org.drools.base.reteoo.sequencing.Sequence;
 import org.drools.base.rule.Accumulate;
 import org.drools.base.rule.AsyncReceive;
 import org.drools.base.rule.AsyncSend;
@@ -70,6 +71,8 @@ public class ReteooRuleBuilder implements RuleBuilder {
                                new PatternBuilder() );
         this.utils.addBuilder( EvalCondition.class,
                                new EvalBuilder() );
+        this.utils.addBuilder( Sequence.class,
+                               new SequenceBuilder() );
         this.utils.addBuilder( QueryElement.class,
                                new QueryElementBuilder() );
         this.utils.addBuilder( From.class,
diff --git a/drools-core/src/main/java/org/drools/core/reteoo/builder/SequenceBuilder.java b/drools-core/src/main/java/org/drools/core/reteoo/builder/SequenceBuilder.java
new file mode 100755
index 00000000000..bd5d2edaa33
--- /dev/null
+++ b/drools-core/src/main/java/org/drools/core/reteoo/builder/SequenceBuilder.java
@@ -0,0 +1,55 @@
+/*
+ * 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.core.reteoo.builder;
+
+import org.drools.base.reteoo.sequencing.Sequence;
+import org.drools.base.rule.RuleConditionElement;
+import org.drools.core.reteoo.CoreComponentFactory;
+
+public class SequenceBuilder
+    implements
+    ReteooComponentBuilder {
+
+    /**
+     * @inheritDoc
+     */
+    public void build(final BuildContext context,
+                      final BuildUtils utils,
+                      final RuleConditionElement rce) {
+        final Sequence seq = (Sequence) rce;
+        context.pushRuleComponent( rce );
+        context.setTupleSource( utils.attachNode( context,
+                                                  CoreComponentFactory.get()
+                                                                      .getNodeFactoryService().buildSequenceNode(context.getNextNodeId(),
+                                                                                                                 context.getTupleSource(),
+                                                                                                                 seq,
+                                                                                                                 context) ) );
+
+        context.popRuleComponent();
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public boolean requiresLeftActivation(final BuildUtils utils,
+                                          final RuleConditionElement rce) {
+        return true;
+    }
+
+}
diff --git a/drools-core/src/main/java/org/drools/core/reteoo/sequencing/SequencerMemoryImpl.java b/drools-core/src/main/java/org/drools/core/reteoo/sequencing/SequencerMemoryImpl.java
new file mode 100644
index 00000000000..26b663e92d6
--- /dev/null
+++ b/drools-core/src/main/java/org/drools/core/reteoo/sequencing/SequencerMemoryImpl.java
@@ -0,0 +1,152 @@
+/*
+ * 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.core.reteoo.sequencing;
+
+import org.drools.base.base.ValueResolver;
+import org.drools.base.reteoo.DynamicFilter;
+import org.drools.base.reteoo.sequencing.Sequence;
+import org.drools.base.reteoo.sequencing.Sequencer;
+import org.drools.base.reteoo.sequencing.SequencerMemory;
+import org.drools.core.common.ReteEvaluator;
+import org.drools.core.common.TupleSetsImpl;
+import org.drools.core.reteoo.LeftTupleSink;
+import org.drools.core.reteoo.PathMemory;
+import org.drools.core.reteoo.SegmentMemory;
+import org.drools.core.reteoo.SequenceNode;
+import org.drools.core.reteoo.SequenceNode.SequenceNodeMemory;
+import org.drools.base.reteoo.SignalAdapter;
+import org.drools.core.reteoo.TupleFactory;
+import org.drools.core.reteoo.TupleImpl;
+import org.drools.base.reteoo.sequencing.Sequence.SequenceMemory;
+import org.drools.base.reteoo.sequencing.signalprocessors.LogicGate;
+import org.drools.base.util.CircularArrayList;
+
+
+public class SequencerMemoryImpl implements SequencerMemory {
+
+    private final TupleImpl lt;
+
+    private final CircularArrayList<Object> events;
+
+    private final SequenceMemory[] sequenceMemories;
+
+    private final LeftTupleSink sink;
+
+    private Sequencer sequencer;
+
+    private SequenceNodeMemory nodeMemory;
+
+    private SequenceMemory childSequenceMemory;
+
+    public SequencerMemoryImpl(Sequencer sequencer, TupleImpl lt, LeftTupleSink sink, SequenceNodeMemory nodeMemory) {
+        this.sequencer        = sequencer;
+        this.lt               = lt;
+        int filterCount = sequencer.getSequence().getFilters().length;
+        this.events           = new CircularArrayList<>(Object.class, Math.max(filterCount, 4));
+        this.sink             = sink;
+        this.sequenceMemories = new SequenceMemory[sequencer.getSequences().length];
+        this.nodeMemory = nodeMemory;
+    }
+
+    @Override
+    public TupleImpl getLeftTuple() {
+        return lt;
+    }
+
+    @Override
+    public CircularArrayList<Object> getData() {
+        return events;
+    }
+
+    @Override
+    public Sequencer getSequencer() {
+        return sequencer;
+    }
+
+    @Override
+    public SequenceMemory getChildSequenceMemory() {
+        return childSequenceMemory;
+    }
+
+    @Override
+    public void setChildSequenceMemory(SequenceMemory childSequenceMemory) {
+        this.childSequenceMemory = childSequenceMemory;
+    }
+
+    public SequenceMemory getOrCreateSequenceMemory(Sequence sequence, CircularArrayList<Object> newData) {
+        SequenceMemory sequenceMemory = sequenceMemories[sequence.getSequenceIndex()];
+        if (sequenceMemory == null) {
+
+            int signalAdapters = 0;
+
+            for (LogicGate gate : sequence.getGates()) {
+                signalAdapters = signalAdapters + gate.getSignalAdapterIndexes().length;
+            }
+
+            long[] gateMemory    = new long[sequence.getGates().length];
+            long[] counterMemory = new long[0];
+
+            sequenceMemory  = new SequenceMemory(this, sequence, newData,
+                                                  new SignalAdapter[signalAdapters], new SignalAdapter[signalAdapters],
+                                                  gateMemory, counterMemory);
+
+            sequenceMemories[sequence.getSequenceIndex()] = sequenceMemory;
+        }
+
+        return sequenceMemory;
+    }
+
+    @Override
+    public SequenceMemory getSequenceMemory(Sequence sequence) {
+        SequenceMemory sequenceMemory = sequenceMemories[sequence.getSequenceIndex()];
+        return sequenceMemory;
+    }
+
+
+    @Override
+    public void match(ValueResolver valueResolver) {
+        boolean wasEmpty = nodeMemory.getStagedChildTuples().isEmpty();
+        TupleImpl child = TupleFactory.createLeftTuple(lt, sink, lt.getPropagationContext(), true);
+        nodeMemory.getStagedChildTuples().addInsert(child);
+
+        long          nodePosMaskBit = nodeMemory.getNodePosMaskBit();
+        SegmentMemory smem           = nodeMemory.getSegmentMemory();
+
+        if (wasEmpty) {
+            smem.notifyRuleLinkSegment(nodePosMaskBit);
+        } else {
+            smem.linkSegmentWithoutRuleNotify(nodePosMaskBit);
+        }
+
+        ReteEvaluator reteEvaluator = (ReteEvaluator) valueResolver;
+        PathMemory pmem = smem.getPathMemories().get(0);
+        reteEvaluator.getRuleNetworkEvaluator().forceFlushLeftTuple(pmem, smem, new TupleSetsImpl());
+        reteEvaluator.getRuleNetworkEvaluator().forceFlushWhenSubnetwork(pmem);
+    }
+
+    @Override
+    public DynamicFilter getActiveDynamicFilter(int filterIndex) {
+        return nodeMemory.getActiveDynamicFilter(filterIndex);
+    }
+
+    @Override
+    public void removeActiveFilter(DynamicFilter filter) {
+        nodeMemory.removeActiveFilter(filter);
+    }
+}
diff --git a/drools-metric/pom.xml b/drools-metric/pom.xml
index 3e66126705d..dfcd0e89d8a 100644
--- a/drools-metric/pom.xml
+++ b/drools-metric/pom.xml
@@ -73,6 +73,11 @@
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.drools</groupId>
+      <artifactId>drools-model-compiler</artifactId>
+      <scope>test</scope>
+    </dependency>
     <dependency>
       <groupId>org.assertj</groupId>
       <artifactId>assertj-core</artifactId>
diff --git a/drools-metric/src/main/java/org/drools/metric/phreak/MetricPhreakNetworkNodeFactoryImpl.java b/drools-metric/src/main/java/org/drools/metric/phreak/MetricPhreakNetworkNodeFactoryImpl.java
index 67475c99204..82cd1461618 100644
--- a/drools-metric/src/main/java/org/drools/metric/phreak/MetricPhreakNetworkNodeFactoryImpl.java
+++ b/drools-metric/src/main/java/org/drools/metric/phreak/MetricPhreakNetworkNodeFactoryImpl.java
@@ -35,6 +35,7 @@ import org.drools.core.phreak.PhreakQueryTerminalNode;
 import org.drools.core.phreak.PhreakReactiveFromNode;
 import org.drools.core.phreak.PhreakRuleTerminalNode;
 import org.drools.core.phreak.PhreakTimerNode;
+import org.drools.core.reteoo.SequenceNode.PhreakSequenceNode;
 import org.drools.metric.util.MetricLogUtils;
 
 public class MetricPhreakNetworkNodeFactoryImpl implements PhreakNetworkNodeFactory {
@@ -138,6 +139,15 @@ public class MetricPhreakNetworkNodeFactoryImpl implements PhreakNetworkNodeFact
         }
     }
 
+    @Override
+    public PhreakSequenceNode createPhreakSequenceNode(ReteEvaluator reteEvaluator) {
+        if (MetricLogUtils.getInstance().isEnabled()) {
+            return new PhreakSequenceNodeMetric(reteEvaluator);
+        } else {
+            return new PhreakSequenceNode(reteEvaluator);
+        }
+    }
+
     @Override
     public PhreakAsyncSendNode createPhreakAsyncSendNode(ReteEvaluator reteEvaluator) {
         if (MetricLogUtils.getInstance().isEnabled()) {
diff --git a/drools-metric/src/main/java/org/drools/metric/phreak/PhreakSequenceNodeMetric.java b/drools-metric/src/main/java/org/drools/metric/phreak/PhreakSequenceNodeMetric.java
new file mode 100644
index 00000000000..ab458fa98b8
--- /dev/null
+++ b/drools-metric/src/main/java/org/drools/metric/phreak/PhreakSequenceNodeMetric.java
@@ -0,0 +1,51 @@
+/*
+ * 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.metric.phreak;
+
+import org.drools.core.common.ReteEvaluator;
+import org.drools.core.common.TupleSets;
+import org.drools.core.reteoo.LeftTupleSink;
+import org.drools.core.reteoo.SequenceNode;
+import org.drools.core.reteoo.SequenceNode.SequenceNodeMemory;
+import org.drools.metric.util.MetricLogUtils;
+
+public class PhreakSequenceNodeMetric extends SequenceNode.PhreakSequenceNode {
+
+    public PhreakSequenceNodeMetric(ReteEvaluator reteEvaluator) {
+        super(reteEvaluator);
+    }
+
+    @Override
+    public void doNode(SequenceNode node,
+                       SequenceNodeMemory memory,
+                       LeftTupleSink sink,
+                       TupleSets srcLeftTuples,
+                       TupleSets trgLeftTuples,
+                       TupleSets stagedLeftTuples) {
+        try {
+            MetricLogUtils.getInstance().startMetrics(node);
+            MetricLogUtils.getInstance().incrementEvalCount();
+
+            super.doNode(node, memory, sink, srcLeftTuples, trgLeftTuples, stagedLeftTuples);
+
+        } finally {
+            MetricLogUtils.getInstance().logAndEndMetrics();
+        }
+    }
+}
diff --git a/drools-metric/src/test/java/org/drools/metric/SequenceNodeMetricTest.java b/drools-metric/src/test/java/org/drools/metric/SequenceNodeMetricTest.java
new file mode 100644
index 00000000000..370ec14a4f3
--- /dev/null
+++ b/drools-metric/src/test/java/org/drools/metric/SequenceNodeMetricTest.java
@@ -0,0 +1,126 @@
+/*
+ * 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.metric;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import io.micrometer.core.instrument.Timer;
+import io.micrometer.core.instrument.search.Search;
+import org.drools.model.Model;
+import org.drools.model.Rule;
+import org.drools.model.Variable;
+import org.drools.model.impl.ModelImpl;
+import org.drools.modelcompiler.KieBaseBuilder;
+import org.junit.jupiter.api.Test;
+import org.kie.api.KieBase;
+import org.kie.api.runtime.KieSession;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.drools.model.DSL.declarationOf;
+import static org.drools.model.DSL.execute;
+import static org.drools.model.PatternDSL.pattern;
+import static org.drools.model.PatternDSL.rule;
+import static org.drools.model.PatternDSL.sequence;
+
+public class SequenceNodeMetricTest extends AbstractMetricTest {
+
+    /**
+     * Builds a KieBase with a single rule whose LHS is: String() then sequence(Integer(), Long()).
+     * Inserting a String activates the sequencer; inserting an Integer then a Long fires the rule.
+     */
+    private KieSession buildSequenceSession(List<String> results) {
+        Variable<String>  anchor  = declarationOf(String.class);
+        Variable<Integer> step1   = declarationOf(Integer.class);
+        Variable<Long>    step2   = declarationOf(Long.class);
+
+        Rule r = rule("sequence-metric-rule").build(
+                pattern(anchor),
+                sequence(
+                        pattern(step1).expr("step1-expr", i -> i > 0),
+                        pattern(step2).expr("step2-expr", l -> l > 0)
+                ),
+                execute(() -> results.add("fired"))
+        );
+
+        Model model = new ModelImpl().addRule(r);
+        KieBase kieBase = KieBaseBuilder.createKieBaseFromModel(model);
+        return kieBase.newKieSession();
+    }
+
+    @Test
+    public void sequenceNodeMetricIsRecordedWhenMetricsEnabled() {
+        List<String> results = new ArrayList<>();
+        KieSession ksession = buildSequenceSession(results);
+
+        // Activate sequencer
+        ksession.insert("anchor");
+        ksession.fireAllRules();
+
+        // Advance sequence: step 1
+        ksession.insert(1);
+        ksession.fireAllRules();
+
+        // Advance sequence: step 2 — rule fires
+        ksession.insert(1L);
+        ksession.fireAllRules();
+
+        ksession.dispose();
+
+        assertThat(results).containsExactly("fired");
+
+        // Metrics must have been recorded for the SequenceNode
+        Collection<Timer> timers = Search.in(registry)
+                .name("org.drools.metric.elapsed.time.per.evaluation")
+                .timers();
+        assertThat(timers).isNotEmpty();
+    }
+
+    @Test
+    public void sequenceNodeMetricIsNotRecordedWhenMetricsDisabled() {
+        // Override: disable metrics for this test
+        System.setProperty("drools.metric.logger.enabled", "false");
+        org.drools.metric.util.MetricLogUtils.recreateInstance();
+
+        try {
+            List<String> results = new ArrayList<>();
+            KieSession ksession = buildSequenceSession(results);
+
+            ksession.insert("anchor");
+            ksession.fireAllRules();
+            ksession.insert(1);
+            ksession.fireAllRules();
+            ksession.insert(1L);
+            ksession.fireAllRules();
+            ksession.dispose();
+
+            assertThat(results).containsExactly("fired");
+
+            // No timers should be registered when metrics are disabled
+            Collection<Timer> timers = Search.in(registry)
+                    .name("org.drools.metric.elapsed.time.per.evaluation")
+                    .timers();
+            assertThat(timers).isEmpty();
+        } finally {
+            System.setProperty("drools.metric.logger.enabled", "true");
+            org.drools.metric.util.MetricLogUtils.recreateInstance();
+        }
+    }
+}
diff --git a/drools-model/drools-canonical-model/src/main/java/org/drools/model/Condition.java b/drools-model/drools-canonical-model/src/main/java/org/drools/model/Condition.java
index 6897ad2478f..21e85fc9fc2 100644
--- a/drools-model/drools-canonical-model/src/main/java/org/drools/model/Condition.java
+++ b/drools-model/drools-canonical-model/src/main/java/org/drools/model/Condition.java
@@ -39,7 +39,7 @@ public interface Condition {
         PATTERN( false ), EVAL( false ), QUERY( false ),
         ACCUMULATE( false ), GROUP_BY( false ), TEMPORAL( false ),
         RECEIVER( false ), SENDER( false ),
-        OR( true ), AND( true ), NOT( false ), EXISTS( false ), FORALL( false ), CONSEQUENCE( false );
+        OR( true ), AND( true ), NOT( false ), EXISTS( false ), FORALL( false ), SEQUENCE( false ), CONSEQUENCE( false );
 
         private final boolean composite;
 
diff --git a/drools-model/drools-canonical-model/src/main/java/org/drools/model/PatternDSL.java b/drools-model/drools-canonical-model/src/main/java/org/drools/model/PatternDSL.java
index d49629f1862..1023077e5e6 100644
--- a/drools-model/drools-canonical-model/src/main/java/org/drools/model/PatternDSL.java
+++ b/drools-model/drools-canonical-model/src/main/java/org/drools/model/PatternDSL.java
@@ -93,8 +93,10 @@ import org.drools.model.view.BindViewItem1;
 import org.drools.model.view.BindViewItem2;
 import org.drools.model.view.BindViewItem3;
 import org.drools.model.view.BindViewItem4;
+import org.drools.model.view.CombinedExprViewItem;
 import org.drools.model.view.ExprViewItem;
 import org.drools.model.view.ViewItem;
+import org.drools.model.view.ViewItemBuilder;
 
 
 import static java.util.UUID.randomUUID;
@@ -142,7 +144,7 @@ public class PatternDSL extends DSL {
         return new ReactOn( reactOn );
     }
 
-    public interface PatternDef<T> extends ViewItem<T> {
+    public interface PatternDef<T> extends ViewItem<T>, SequenceStep {
         PatternDef<T> and();
         PatternDef<T> or();
         PatternDef<T> endAnd();
@@ -1775,6 +1777,23 @@ public class PatternDSL extends DSL {
         }
     }
 
+    public static class SequenceViewItem implements RuleItemBuilder<SequenceViewItem>, RuleItem {
+        private final SequenceStep[] steps;
+
+        public SequenceViewItem(SequenceStep... steps) {
+            this.steps = steps;
+        }
+
+        public SequenceStep[] getSteps() {
+            return steps;
+        }
+
+        @Override
+        public SequenceViewItem get() {
+            return this;
+        }
+    }
+
     public static abstract class PatternBindingImpl<T, A> implements PatternItem<T> {
         protected final Variable<A> boundVar;
         protected final ReactOn reactOn;
@@ -1887,6 +1906,32 @@ public class PatternDSL extends DSL {
 
     // -- rule --
 
+    public static SequenceViewItem sequence(SequenceStep... steps) {
+        if (steps == null || steps.length == 0) {
+            throw new IllegalArgumentException("sequence() requires at least one step");
+        }
+        return new SequenceViewItem(steps);
+    }
+
+
+    public static CombinedExprViewItem or(ViewItemBuilder<?> first, ViewItemBuilder<?>... rest) {
+        return new CombinedExprViewItem(Condition.Type.OR, viewItems(first, rest));
+    }
+
+    public static CombinedExprViewItem and(ViewItemBuilder<?> first, ViewItemBuilder<?>... rest) {
+        return new CombinedExprViewItem(Condition.Type.AND, viewItems(first, rest));
+    }
+
+    private static ViewItem[] viewItems(ViewItemBuilder<?> first, ViewItemBuilder<?>[] rest) {
+        ViewItem[] all = new ViewItem[1 + rest.length];
+        all[0] = first.get();
+        for (int i = 0; i < rest.length; i++) {
+            all[1 + i] = rest[i].get();
+        }
+        return all;
+    }
+
+
     public static RuleBuilder rule( String name ) {
         return new RuleBuilder( VIEW_BUILDER, name );
     }
diff --git a/drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java b/drools-model/drools-canonical-model/src/main/java/org/drools/model/SequenceStep.java
similarity index 53%
copy from drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java
copy to drools-model/drools-canonical-model/src/main/java/org/drools/model/SequenceStep.java
index d74396d1070..c785029648d 100644
--- a/drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java
+++ b/drools-model/drools-canonical-model/src/main/java/org/drools/model/SequenceStep.java
@@ -16,33 +16,11 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.drools.model.view;
+package org.drools.model;
 
-import org.drools.model.Condition;
-import org.drools.model.Variable;
-
-public class ExistentialExprViewItem<T> extends AbstractExprViewItem<T> {
-
-    private final Condition.Type type;
-    private final ViewItem expression;
-
-    public ExistentialExprViewItem(Condition.Type type, ViewItem expression) {
-        super(expression.getFirstVariable());
-        this.type = type;
-        this.expression = expression;
-    }
-
-    public ViewItem getExpression() {
-        return expression;
-    }
-
-    @Override
-    public Variable<?>[] getVariables() {
-        return expression.getVariables();
-    }
-
-    @Override
-    public Condition.Type getType() {
-        return type;
-    }
+/**
+ * Marker for items that may appear as a step inside {@code sequence(...)}.
+ * Stamped on PatternDef, CombinedExprViewItem, and ExistentialExprViewItem.
+ */
+public interface SequenceStep {
 }
diff --git a/drools-model/drools-canonical-model/src/main/java/org/drools/model/impl/ViewPatternBuilder.java b/drools-model/drools-canonical-model/src/main/java/org/drools/model/impl/ViewPatternBuilder.java
index f9d1cb7190d..c16411e3b71 100644
--- a/drools-model/drools-canonical-model/src/main/java/org/drools/model/impl/ViewPatternBuilder.java
+++ b/drools-model/drools-canonical-model/src/main/java/org/drools/model/impl/ViewPatternBuilder.java
@@ -19,6 +19,7 @@
 package org.drools.model.impl;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -33,6 +34,7 @@ import org.drools.model.PatternDSL.PatternBindingImpl;
 import org.drools.model.PatternDSL.PatternDefImpl;
 import org.drools.model.PatternDSL.PatternExprImpl;
 import org.drools.model.PatternDSL.PatternItem;
+import org.drools.model.PatternDSL.SequenceViewItem;
 import org.drools.model.RuleItem;
 import org.drools.model.RuleItemBuilder;
 import org.drools.model.SingleConstraint;
@@ -59,6 +61,7 @@ import org.drools.model.patterns.ExistentialPatternImpl;
 import org.drools.model.patterns.GroupByPatternImpl;
 import org.drools.model.patterns.PatternImpl;
 import org.drools.model.patterns.QueryCallPattern;
+import org.drools.model.patterns.SequenceConditionImpl;
 import org.drools.model.view.AccumulateExprViewItem;
 import org.drools.model.view.CombinedExprViewItem;
 import org.drools.model.view.ExistentialExprViewItem;
@@ -180,6 +183,14 @@ public class ViewPatternBuilder implements ViewBuilder {
             return new PatternImpl( exchangeDef.getFirstVariable(), Condition.Type.SENDER );
         }
 
+        if (ruleItem instanceof SequenceViewItem) {
+            SequenceViewItem sv = (SequenceViewItem) ruleItem;
+            List<Condition> steps = Arrays.stream(sv.getSteps())
+                    .map(s -> ruleItem2Condition((RuleItem) s))
+                    .collect(toList());
+            return new SequenceConditionImpl(steps);
+        }
+
         throw new UnsupportedOperationException( "Unknown " + ruleItem );
     }
 
diff --git a/drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java b/drools-model/drools-canonical-model/src/main/java/org/drools/model/patterns/SequenceConditionImpl.java
similarity index 61%
copy from drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java
copy to drools-model/drools-canonical-model/src/main/java/org/drools/model/patterns/SequenceConditionImpl.java
index d74396d1070..3e938431e41 100644
--- a/drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java
+++ b/drools-model/drools-canonical-model/src/main/java/org/drools/model/patterns/SequenceConditionImpl.java
@@ -16,33 +16,33 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.drools.model.view;
+package org.drools.model.patterns;
+
+import java.util.List;
 
 import org.drools.model.Condition;
 import org.drools.model.Variable;
 
-public class ExistentialExprViewItem<T> extends AbstractExprViewItem<T> {
+public class SequenceConditionImpl implements Condition {
 
-    private final Condition.Type type;
-    private final ViewItem expression;
+    private final List<Condition> steps;
 
-    public ExistentialExprViewItem(Condition.Type type, ViewItem expression) {
-        super(expression.getFirstVariable());
-        this.type = type;
-        this.expression = expression;
+    public SequenceConditionImpl(List<Condition> steps) {
+        this.steps = steps;
     }
 
-    public ViewItem getExpression() {
-        return expression;
+    @Override
+    public Type getType() {
+        return Type.SEQUENCE;
     }
 
     @Override
-    public Variable<?>[] getVariables() {
-        return expression.getVariables();
+    public List<Condition> getSubConditions() {
+        return steps;
     }
 
     @Override
-    public Condition.Type getType() {
-        return type;
+    public Variable<?>[] getBoundVariables() {
+        return new Variable[0];
     }
 }
diff --git a/drools-model/drools-canonical-model/src/main/java/org/drools/model/view/CombinedExprViewItem.java b/drools-model/drools-canonical-model/src/main/java/org/drools/model/view/CombinedExprViewItem.java
index 08cbf80fb3f..91d6aa4c099 100644
--- a/drools-model/drools-canonical-model/src/main/java/org/drools/model/view/CombinedExprViewItem.java
+++ b/drools-model/drools-canonical-model/src/main/java/org/drools/model/view/CombinedExprViewItem.java
@@ -23,7 +23,7 @@ import java.util.stream.Stream;
 import org.drools.model.Condition;
 import org.drools.model.Variable;
 
-public class CombinedExprViewItem<T> extends AbstractExprViewItem<T> {
+public class CombinedExprViewItem<T> extends AbstractExprViewItem<T> implements org.drools.model.SequenceStep {
 
     private final Condition.Type type;
     private final ViewItem[] expressions;
diff --git a/drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java b/drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java
index d74396d1070..23fd4ff3c3b 100644
--- a/drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java
+++ b/drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java
@@ -21,7 +21,7 @@ package org.drools.model.view;
 import org.drools.model.Condition;
 import org.drools.model.Variable;
 
-public class ExistentialExprViewItem<T> extends AbstractExprViewItem<T> {
+public class ExistentialExprViewItem<T> extends AbstractExprViewItem<T> implements org.drools.model.SequenceStep {
 
     private final Condition.Type type;
     private final ViewItem expression;
diff --git a/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/ReteDumperTest.java b/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/ReteDumperTest.java
index da9cad76a27..606c5879eaa 100644
--- a/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/ReteDumperTest.java
+++ b/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/ReteDumperTest.java
@@ -58,7 +58,7 @@ class ReteDumperTest extends BaseModelTest {
         List<String> lines = getLinesByCoreReteDumper(kbase);
 
         // The first RightInputAdapterNode should be followed by ExistsNode
-        assertSinkOrder(lines, "[RightInputAdapterNode", "[ExistsNode");
+        assertSinkOrder(lines, "RightInputAdapterNode", "[ExistsNode");
     }
 
     @ParameterizedTest
@@ -70,7 +70,7 @@ class ReteDumperTest extends BaseModelTest {
         List<String> lines = getLinesByExecmodelReteDumper(kbase);
 
         // The first RightInputAdapterNode should be followed by ExistsNode
-        assertSinkOrder(lines, "[RightInputAdapterNode", "[ExistsNode");
+        assertSinkOrder(lines, "RightInputAdapterNode", "[ExistsNode");
     }
 
     private static void assertSinkOrder(List<String> lines, String targetNodePrefix, String expectedNextNodePrefix) {
diff --git a/drools-model/drools-model-compiler/src/main/java/org/drools/modelcompiler/KiePackagesBuilder.java b/drools-model/drools-model-compiler/src/main/java/org/drools/modelcompiler/KiePackagesBuilder.java
index 332cc0387b6..d80d740f481 100644
--- a/drools-model/drools-model-compiler/src/main/java/org/drools/modelcompiler/KiePackagesBuilder.java
+++ b/drools-model/drools-model-compiler/src/main/java/org/drools/modelcompiler/KiePackagesBuilder.java
@@ -131,6 +131,15 @@ import org.drools.model.patterns.ExistentialPatternImpl;
 import org.drools.model.patterns.GroupByPatternImpl;
 import org.drools.model.patterns.PatternImpl;
 import org.drools.model.patterns.QueryCallPattern;
+import org.drools.model.patterns.SequenceConditionImpl;
+import org.drools.base.reteoo.sequencing.Sequence;
+import org.drools.base.reteoo.sequencing.signalprocessors.Gates;
+import org.drools.base.reteoo.sequencing.signalprocessors.LogicCircuit;
+import org.drools.base.reteoo.sequencing.signalprocessors.LogicGate;
+import org.drools.base.reteoo.sequencing.signalprocessors.LogicGateOutputSignalProcessor;
+import org.drools.base.reteoo.sequencing.signalprocessors.SignalIndex;
+import org.drools.base.reteoo.sequencing.signalprocessors.TerminatingSignalProcessor;
+import org.drools.base.reteoo.sequencing.steps.Step;
 import org.drools.model.view.SelfPatternBiding;
 import org.drools.modelcompiler.attributes.LambdaEnabled;
 import org.drools.modelcompiler.attributes.LambdaSalience;
@@ -398,15 +407,41 @@ public class KiePackagesBuilder {
         Declaration[] requiredDeclarations = getRequiredDeclarationsIfPossible( ctx, consequence, name );
         boolean enabledTupleOptimization = requiredDeclarations != null && requiredDeclarations.length > 0;
 
+        Map<String, int[]> sequenceVarIndexes = buildSequenceVarIndexesForConsequence( ctx, consequence );
+
         if ( name.equals( RuleImpl.DEFAULT_CONSEQUENCE_NAME ) ) {
             if ("java".equals(consequence.getLanguage())) {
-                ctx.getRule().setConsequence( new LambdaConsequence( consequence, enabledTupleOptimization ) );
+                ctx.getRule().setConsequence( new LambdaConsequence( consequence, enabledTupleOptimization, sequenceVarIndexes ) );
             } else {
                 throw new UnsupportedOperationException("Unknown script language for consequence: " + consequence.getLanguage());
             }
         } else {
-            ctx.getRule().addNamedConsequence( name, new LambdaConsequence( consequence, enabledTupleOptimization ) );
+            ctx.getRule().addNamedConsequence( name, new LambdaConsequence( consequence, enabledTupleOptimization, sequenceVarIndexes ) );
+        }
+    }
+
+    /**
+     * Builds a map of variable-name → sequencer-data-index for any consequence variables
+     * that were bound by a sequence step pattern (rather than a normal LHS pattern).
+     * These variables are not accessible via the activation tuple; they must be resolved
+     * from SequencerMemory.getData() at consequence evaluation time.
+     */
+    private Map<String, int[]> buildSequenceVarIndexesForConsequence( RuleContext ctx, Consequence consequence ) {
+        Map<Variable, int[]> seqIndexes = ctx.getSequenceVarIndexes();
+        if (seqIndexes.isEmpty()) {
+            return Map.of();
+        }
+        int totalSeqs = ctx.getSequenceCount();
+        Map<String, int[]> result = null;
+        for (Variable var : consequence.getDeclarations()) {
+            int[] entry = seqIndexes.get(var);
+            if (entry != null) {
+                if (result == null) result = new java.util.HashMap<>();
+                int skip = totalSeqs - 1 - entry[RuleContext.SEQ_VAR_SEQ_IDX];
+                result.put(var.getName(), new int[]{ skip, entry[RuleContext.SEQ_VAR_FILTER_IDX] });
+            }
         }
+        return result == null ? Map.of() : result;
     }
 
     private Declaration[] getRequiredDeclarationsIfPossible( RuleContext ctx, Consequence consequence, String name ) {
@@ -499,6 +534,27 @@ public class KiePackagesBuilder {
             case FORALL: {
                 return buildForAll( ctx, group, condition );
             }
+            case SEQUENCE: {
+                int seqIdx = ctx.nextSeqIndex();
+                SequenceConditionImpl sc = (SequenceConditionImpl) condition;
+                List<Condition> steps = sc.getSubConditions();
+                int n = steps.size();
+                List<Pattern> filters = new ArrayList<>();
+                Step.StepFactory[] stepFactories = new Step.StepFactory[n];
+                int[] gateCounter = new int[]{0};
+
+                for (int i = 0; i < n; i++) {
+                    List<LogicGate> stepGates = new ArrayList<>();
+                    LogicGate root = buildStepGate(ctx, group, steps.get(i), filters, stepGates, gateCounter, seqIdx);
+                    root.setOutput(TerminatingSignalProcessor.get());
+                    stepFactories[i] = Step.of(new LogicCircuit(stepGates.toArray(new LogicGate[0])));
+                }
+
+                Sequence seq = new Sequence(0, stepFactories);
+                seq.setFilters(filters.toArray(new Pattern[0]));
+                ctx.getRule().addSequence(seq);
+                return null;
+            }
             case CONSEQUENCE:
                 if (condition instanceof NamedConsequenceImpl) {
                     NamedConsequenceImpl consequence = (NamedConsequenceImpl) condition;
@@ -549,6 +605,75 @@ public class KiePackagesBuilder {
         return transformedForall;
     }
 
+    private static final String DEFERRED_GATE_ERROR =
+            "sequence(...) does not yet support condition type %s. Absence-based " +
+            "gates (nor, nand, xor, xnor, not) and other composites are deferred ";
+
+    private LogicGate buildStepGate(RuleContext ctx, GroupElement group,
+                                    Condition node, List<Pattern> filters,
+                                    List<LogicGate> stepGates, int[] gateCounter,
+                                    int seqIdx) {
+        Condition.Type type = node.getType();
+
+        if (type == Condition.Type.PATTERN) {
+            int idx = filters.size();
+            PatternImpl patternImpl = (PatternImpl) node;
+            RuleConditionElement built = buildPattern(ctx, group, patternImpl);
+            if (!(built instanceof Pattern)) {
+                throw new IllegalStateException(
+                        "SEQUENCE leaf must compile to a simple Pattern, got " + built);
+            }
+            filters.add((Pattern) built);
+            // Record variable → (sequence, filter index) so the consequence can look up the matched
+            // fact from SequencerMemory.getData() at rule-fire time.
+            Variable stepVar = patternImpl.getPatternVariable();
+            if (stepVar != null) {
+                ctx.addSequenceVarIndex(stepVar, seqIdx, idx);
+            }
+            LogicGate leaf = new LogicGate(
+                    Gates::and,
+                    gateCounter[0]++,
+                    new int[]{idx},
+                    new int[]{idx},
+                    0);
+            stepGates.add(leaf);
+            return leaf;
+        }
+
+        LogicCircuit.LongBiPredicate pred = predicateFor(type);
+        List<Condition> children = node.getSubConditions();
+        if (children.isEmpty()) {
+            throw new IllegalStateException(
+                    "SEQUENCE composite node has no children: " + type);
+        }
+        LogicGate[] inputs = new LogicGate[children.size()];
+        for (int i = 0; i < children.size(); i++) {
+            inputs[i] = buildStepGate(ctx, group, children.get(i), filters, stepGates, gateCounter, seqIdx);
+        }
+        LogicGate parent = new LogicGate(
+                pred,
+                gateCounter[0]++,
+                new int[0],
+                new int[0],
+                inputs.length);
+        parent.setInputGates(inputs);
+        for (int k = 0; k < inputs.length; k++) {
+            inputs[k].setOutput(new LogicGateOutputSignalProcessor(SignalIndex.of(parent, k + 1)));
+        }
+        stepGates.add(parent);
+        return parent;
+    }
+
+    private static LogicCircuit.LongBiPredicate predicateFor(Condition.Type t) {
+        switch (t) {
+            case AND: return Gates::and;
+            case OR:  return Gates::or;
+            default:
+                throw new UnsupportedOperationException(
+                        String.format(DEFERRED_GATE_ERROR, t));
+        }
+    }
+
     private RuleConditionElement buildAccumulate( RuleContext ctx, GroupElement group, AccumulatePattern accumulatePattern ) {
         Pattern pattern = null;
         boolean isGroupBy = accumulatePattern instanceof GroupByPattern;
diff --git a/drools-model/drools-model-compiler/src/main/java/org/drools/modelcompiler/RuleContext.java b/drools-model/drools-model-compiler/src/main/java/org/drools/modelcompiler/RuleContext.java
index a1c826a3994..3bf255f8231 100644
--- a/drools-model/drools-model-compiler/src/main/java/org/drools/modelcompiler/RuleContext.java
+++ b/drools-model/drools-model-compiler/src/main/java/org/drools/modelcompiler/RuleContext.java
@@ -39,6 +39,12 @@ import org.drools.model.Variable;
 
 public class RuleContext {
 
+    /** Index into the {@code int[]} stored in {@link #sequenceVarIndexes}: the sequence index (raw, before skip conversion). */
+    static final int SEQ_VAR_SEQ_IDX    = 0;
+    /** Index into the {@code int[]} stored in {@link #sequenceVarIndexes}: the filter/step index within that sequence. */
+    static final int SEQ_VAR_FILTER_IDX = 1;
+
+
     private final KiePackagesBuilder builder;
     private final InternalKnowledgePackage pkg;
     private final RuleImpl rule;
@@ -48,7 +54,10 @@ public class RuleContext {
     private Map<Variable, Declaration> queryDeclarations;
     private Map<Variable, Accumulate> accumulateSource;
 
+    private Map<Variable, int[]> sequenceVarIndexes;
+
     private int patternIndex = -1;
+    private int seqIndex = -1;
     private boolean needStreamMode = false;
 
     private Deque<Set<Variable>> variablesInOrCondition;
@@ -78,6 +87,14 @@ public class RuleContext {
         return ++patternIndex;
     }
 
+    int nextSeqIndex() {
+        return ++seqIndex;
+    }
+
+    int getSequenceCount() {
+        return seqIndex + 1;
+    }
+
     void startOrCondition() {
         if (variablesInOrCondition == null) {
             variablesInOrCondition = new ArrayDeque<>();
@@ -136,6 +153,17 @@ public class RuleContext {
         declarations.put( variable, declaration );
     }
 
+    void addSequenceVarIndex( Variable variable, int seqIndex, int filterIndex ) {
+        if (sequenceVarIndexes == null) {
+            sequenceVarIndexes = new HashMap<>();
+        }
+        sequenceVarIndexes.put( variable, new int[]{ seqIndex, filterIndex } );
+    }
+
+    Map<Variable, int[]> getSequenceVarIndexes() {
+        return sequenceVarIndexes == null ? Map.of() : sequenceVarIndexes;
+    }
+
     void addGroupByDeclaration( Variable groupKeyVar, Declaration declaration ) {
         addDeclaration( groupKeyVar, declaration );
     }
diff --git a/drools-model/drools-model-compiler/src/main/java/org/drools/modelcompiler/consequence/LambdaConsequence.java b/drools-model/drools-model-compiler/src/main/java/org/drools/modelcompiler/consequence/LambdaConsequence.java
index 92773c249d2..49921d719da 100644
--- a/drools-model/drools-model-compiler/src/main/java/org/drools/modelcompiler/consequence/LambdaConsequence.java
+++ b/drools-model/drools-model-compiler/src/main/java/org/drools/modelcompiler/consequence/LambdaConsequence.java
@@ -21,11 +21,13 @@ package org.drools.modelcompiler.consequence;
 import org.drools.base.base.ValueResolver;
 import org.drools.base.definitions.rule.impl.RuleImpl;
 import org.drools.base.reteoo.BaseTuple;
+import org.drools.base.reteoo.sequencing.SequencerMemory;
 import org.drools.base.rule.Declaration;
 import org.drools.base.rule.consequence.Consequence;
 import org.drools.core.common.DefaultEventHandle;
 import org.drools.core.common.ReteEvaluator;
 import org.drools.core.reteoo.RuleTerminalNode;
+import org.drools.core.reteoo.TupleImpl;
 import org.drools.core.rule.consequence.KnowledgeHelper;
 import org.drools.model.Variable;
 import org.kie.api.runtime.rule.FactHandle;
@@ -33,6 +35,7 @@ import org.kie.api.runtime.rule.FactHandle;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 
 public class LambdaConsequence implements Consequence<KnowledgeHelper> {
 
@@ -40,21 +43,34 @@ public class LambdaConsequence implements Consequence<KnowledgeHelper> {
     // consequence in linear time by traversing the tuple only once.
     private static final boolean ENABLE_LINEARIZED_ARGUMENTS_RETRIEVAL_OPTIMIZATION = true;
 
+    /** Index into the {@code int[]} values of {@link #sequenceVarIndexes}: tuple-anchor skip count. */
+    private static final int SEQ_ENTRY_SKIP_IDX   = 0;
+    /** Index into the {@code int[]} values of {@link #sequenceVarIndexes}: filter/step index within the sequence. */
+    private static final int SEQ_ENTRY_FILTER_IDX = 1;
+
     private final org.drools.model.Consequence consequence;
     private final int factsNr;
     private final boolean enabledTupleOptimization;
     private Declaration[] requiredDeclarations;
 
+    // variable name → [skip, filterIndex] for step-pattern variables.
+    // Non-empty only when the rule contains a sequence() and the consequence references step-bound vars.
+    private final Map<String, int[]> sequenceVarIndexes;
+
     private TupleFactSupplier[] factSuppliers;
+    private SequencerFactSupplier[] sequencerFactSuppliers;
     private GlobalSupplier[] globalSuppliers;
     private Object[] facts;
 
     private FactHandleLookup fhLookup;
 
-    public LambdaConsequence( org.drools.model.Consequence consequence, boolean enabledTupleOptimization) {
+    public LambdaConsequence( org.drools.model.Consequence consequence,
+                              boolean enabledTupleOptimization,
+                              Map<String, int[]> sequenceVarIndexes) {
         this.consequence = consequence;
         this.enabledTupleOptimization = ENABLE_LINEARIZED_ARGUMENTS_RETRIEVAL_OPTIMIZATION & enabledTupleOptimization;
         this.factsNr = consequence.getVariables().length + ( consequence.isUsingDrools() ? 1 : 0 );
+        this.sequenceVarIndexes = sequenceVarIndexes;
     }
 
     @Override
@@ -69,7 +85,7 @@ public class LambdaConsequence implements Consequence<KnowledgeHelper> {
             if (enabledTupleOptimization) {
                 this.requiredDeclarations = declarations;
             } else {
-                Object[] facts = declarationsToFacts( knowledgeHelper, valueResolver, knowledgeHelper.getTuple(), declarations, consequence.getVariables(), consequence.isUsingDrools() );
+                Object[] facts = declarationsToFacts( knowledgeHelper, valueResolver, knowledgeHelper.getTuple(), declarations, consequence.getVariables(), consequence.isUsingDrools(), sequenceVarIndexes );
                 consequence.getBlock().execute( facts );
                 return;
             }
@@ -84,6 +100,10 @@ public class LambdaConsequence implements Consequence<KnowledgeHelper> {
     }
 
     private static Object[] declarationsToFacts( KnowledgeHelper knowledgeHelper, ValueResolver valueResolver, BaseTuple tuple, Declaration[] declarations, Variable[] vars, boolean useDrools ) {
+        return declarationsToFacts( knowledgeHelper, valueResolver, tuple, declarations, vars, useDrools, Map.of() );
+    }
+
+    private static Object[] declarationsToFacts( KnowledgeHelper knowledgeHelper, ValueResolver valueResolver, BaseTuple tuple, Declaration[] declarations, Variable[] vars, boolean useDrools, Map<String, int[]> sequenceVarIndexes ) {
         Object[] objects;
         FactHandleLookup fhLookup = useDrools ? new FactHandleLookup.Multi() : null;
 
@@ -99,12 +119,31 @@ public class LambdaConsequence implements Consequence<KnowledgeHelper> {
         int declrCounter = 0;
         for (Variable var : vars) {
             if ( var.isFact() ) {
-                Declaration declaration = declarations[declrCounter++];
-                FactHandle fh = getOriginalFactHandle(tuple.get(declaration));
-                if ( useDrools ) {
-                    fhLookup.put( fh.getObject(), fh );
+                int[] seqEntry = sequenceVarIndexes.get( var.getName() );
+                if ( seqEntry != null ) {
+                    // Sequence-step variable: not in the activation tuple — resolve from SequencerMemory.
+                    BaseTuple anchor = findSequencerAnchor( tuple, seqEntry[SEQ_ENTRY_SKIP_IDX] );
+                    if ( anchor == null ) {
+                        throw new IllegalStateException(
+                                "declarationsToFacts: no SequencerMemory anchor found at skip=" + seqEntry[SEQ_ENTRY_SKIP_IDX] +
+                                " for variable '" + var.getName() + "'. " +
+                                "This indicates a compile-time skip-count miscalculation in KiePackagesBuilder." );
+                    }
+                    SequencerMemory sequencerMemory = (SequencerMemory) anchor.getContextObject();
+                    FactHandle fh = getOriginalFactHandle( (FactHandle) sequencerMemory.getData().get( seqEntry[SEQ_ENTRY_FILTER_IDX] ) );
+                    if ( useDrools && fhLookup != null ) {
+                        fhLookup.put( fh.getObject(), fh );
+                    }
+                    objects[index++] = fh.getObject();
+                    declrCounter++; // consume the declaration slot (which is null for sequence vars)
+                } else {
+                    Declaration declaration = declarations[declrCounter++];
+                    FactHandle fh = getOriginalFactHandle(tuple.get(declaration));
+                    if ( useDrools ) {
+                        fhLookup.put( fh.getObject(), fh );
+                    }
+                    objects[index++] = declaration.getValue( valueResolver, fh );
                 }
-                objects[index++] = declaration.getValue( valueResolver, fh );
             } else {
                 objects[index++] = valueResolver.getGlobal( var.getName() );
             }
@@ -112,6 +151,28 @@ public class LambdaConsequence implements Consequence<KnowledgeHelper> {
         return objects;
     }
 
+    /**
+     * Walks up the tuple parent chain counting {@link SequencerMemory} anchors.
+     * Returns the {@code skip}-th anchor found (0 = innermost/closest to terminal).
+     *
+     * Must use {@link TupleImpl#getLeftParent()} rather than {@link BaseTuple#getParent()}:
+     * in subnetwork tuples the two fields diverge — getParent() skips the SequencerMemory
+     * anchor tuples that getLeftParent() traverses.
+     */
+    private static TupleImpl findSequencerAnchor(BaseTuple tuple, int skip) {
+        TupleImpl t = ((TupleImpl) tuple).getLeftParent();
+        while (t != null) {
+            if (t.getContextObject() instanceof SequencerMemory) {
+                if (skip == 0) {
+                    return t;
+                }
+                skip--;
+            }
+            t = t.getLeftParent();
+        }
+        return null;
+    }
+
     private static FactHandle getOriginalFactHandle( FactHandle handle ) {
         if ( !handle.isEvent() ) {
             return handle;
@@ -148,6 +209,12 @@ public class LambdaConsequence implements Consequence<KnowledgeHelper> {
             tuple = factSuppliers[j].resolveAndStore(facts, reteEvaluator, tuple, fhLookup);
         }
 
+        if (sequencerFactSuppliers != null) {
+            for (int j = 0; j < sequencerFactSuppliers.length; j++) {
+                sequencerFactSuppliers[j].resolveAndStore(facts, reteEvaluator, knowledgeHelper.getTuple(), fhLookup);
+            }
+        }
+
         if (globalSuppliers != null) {
             for (int j = 0; j < globalSuppliers.length; j++) {
                 globalSuppliers[j].resolveAndStore(facts, reteEvaluator);
@@ -164,7 +231,8 @@ public class LambdaConsequence implements Consequence<KnowledgeHelper> {
         }
 
         BaseTuple tuple = knowledgeHelper.getTuple();
-        List<TupleFactSupplier> factSuppliers = new ArrayList<>();
+        List<TupleFactSupplier> tupleFactSuppliers = new ArrayList<>();
+        List<SequencerFactSupplier> seqFactSuppliers = new ArrayList<>();
         List<GlobalSupplier> globalSuppliers = new ArrayList<>();
 
         Object[] facts;
@@ -180,7 +248,14 @@ public class LambdaConsequence implements Consequence<KnowledgeHelper> {
         int declrCounter = 0;
         for (Variable var : vars) {
             if ( var.isFact() ) {
-                factSuppliers.add( new TupleFactSupplier( supplierIndex, requiredDeclarations[declrCounter++], consequence.isUsingDrools() ) );
+                int[] entry = sequenceVarIndexes.get(var.getName());
+                if (entry != null) {
+                    // Step-pattern variable: resolved from SequencerMemory, not from the tuple.
+                    seqFactSuppliers.add( new SequencerFactSupplier( supplierIndex, entry[SEQ_ENTRY_SKIP_IDX], entry[SEQ_ENTRY_FILTER_IDX], consequence.isUsingDrools() ) );
+                    declrCounter++; // still consume the declaration slot
+                } else {
+                    tupleFactSuppliers.add( new TupleFactSupplier( supplierIndex, requiredDeclarations[declrCounter++], consequence.isUsingDrools() ) );
+                }
             } else {
                 facts[supplierIndex] = reteEvaluator.getGlobal( var.getName() );
                 globalSuppliers.add( new GlobalSupplier( supplierIndex, var.getName() ) );
@@ -190,16 +265,16 @@ public class LambdaConsequence implements Consequence<KnowledgeHelper> {
 
         FactHandleLookup fhLookup = null;
         if ( consequence.isUsingDrools() ) {
-            fhLookup = FactHandleLookup.create( factSuppliers.size() );
+            fhLookup = FactHandleLookup.create( tupleFactSuppliers.size() + seqFactSuppliers.size() );
             facts[0] = new DroolsImpl( knowledgeHelper, reteEvaluator, fhLookup );
         }
 
-        Collections.sort( factSuppliers );
+        Collections.sort( tupleFactSuppliers );
         Collections.sort( globalSuppliers );
 
         BaseTuple current = tuple;
         boolean first = true;
-        for (TupleFactSupplier tupleFactSupplier : factSuppliers) {
+        for (TupleFactSupplier tupleFactSupplier : tupleFactSuppliers) {
             int targetTupleIndex = tupleFactSupplier.declarationTupleIndex;
 
             tupleFactSupplier.offsetFromPrior = 0;
@@ -214,9 +289,15 @@ public class LambdaConsequence implements Consequence<KnowledgeHelper> {
             tupleFactSupplier.resolveAndStore(facts, reteEvaluator, current.getFactHandle(), fhLookup);
         }
 
+        // Resolve sequence-step variables immediately (they are always stable once the sequence fired).
+        for (SequencerFactSupplier seqSupplier : seqFactSuppliers) {
+            seqSupplier.resolveAndStore(facts, reteEvaluator, tuple, fhLookup);
+        }
+
         // factSuppliers has to be last because factSuppliers is used as an initialization flag in fetchFacts(). See DROOLS-6961
         this.globalSuppliers = globalSuppliers.isEmpty() ? null : globalSuppliers.toArray( new GlobalSupplier[globalSuppliers.size()] );
-        this.factSuppliers = factSuppliers.toArray( new TupleFactSupplier[factSuppliers.size()] );
+        this.sequencerFactSuppliers = seqFactSuppliers.isEmpty() ? null : seqFactSuppliers.toArray( new SequencerFactSupplier[seqFactSuppliers.size()] );
+        this.factSuppliers = tupleFactSuppliers.toArray( new TupleFactSupplier[tupleFactSuppliers.size()] );
 
         if (!reteEvaluator.getRuleSessionConfiguration().isThreadSafe()) {
             this.facts = facts;
@@ -290,4 +371,43 @@ public class LambdaConsequence implements Consequence<KnowledgeHelper> {
             return o.declarationTupleIndex - declarationTupleIndex;
         }
     }
+
+    /**
+     * Resolves a fact that was matched by a sequence step pattern.
+     * Instead of reading from the activation tuple (where step-matched facts do not appear),
+     * it reads from SequencerMemory.getData() on the anchor left-tuple's context object.
+     *
+     * The activation tuple chain is: child (fired) → anchor (SequencerMemory as context).
+     * SequencerMemory.getData() is a CircularArrayList indexed by filter/step index.
+     */
+    private static class SequencerFactSupplier {
+        private final int supplierIndex;
+        private final int skip;
+        private final int filterIndex;
+        private boolean useDrools;
+
+        private SequencerFactSupplier( int supplierIndex, int skip, int filterIndex, boolean useDrools ) {
+            this.supplierIndex = supplierIndex;
+            this.skip          = skip;
+            this.filterIndex   = filterIndex;
+            this.useDrools     = useDrools;
+        }
+
+        public void resolveAndStore(Object[] facts, ValueResolver reteEvaluator, BaseTuple tuple, FactHandleLookup fhLookup) {
+            // Walk up to the anchor tuple — counting past `skip` SequencerMemory anchors.
+            BaseTuple anchor = findSequencerAnchor(tuple, skip);
+            if (anchor == null) {
+                throw new IllegalStateException(
+                        "SequencerFactSupplier: no SequencerMemory anchor found at skip=" + skip +
+                        " (supplierIndex=" + supplierIndex + ", filterIndex=" + filterIndex +
+                        "). This indicates a compile-time skip-count miscalculation in KiePackagesBuilder.");
+            }
+            SequencerMemory sequencerMemory = (SequencerMemory) anchor.getContextObject();
+            FactHandle fh = getOriginalFactHandle( (FactHandle) sequencerMemory.getData().get(filterIndex) );
+            if ( useDrools && fhLookup != null ) {
+                fhLookup.put( fh.getObject(), fh );
+            }
+            facts[supplierIndex] = fh.getObject();
+        }
+    }
 }
\ No newline at end of file
diff --git a/drools-model/drools-model-compiler/src/test/java/org/drools/modelcompiler/PatternDSLSequenceAdvancedTest.java b/drools-model/drools-model-compiler/src/test/java/org/drools/modelcompiler/PatternDSLSequenceAdvancedTest.java
new file mode 100644
index 00000000000..6714f3a3c3f
--- /dev/null
+++ b/drools-model/drools-model-compiler/src/test/java/org/drools/modelcompiler/PatternDSLSequenceAdvancedTest.java
@@ -0,0 +1,518 @@
+/*
+ * 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.modelcompiler;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.drools.model.Rule;
+import org.drools.model.Variable;
+import org.drools.model.impl.ModelImpl;
+import org.drools.modelcompiler.domain.Person;
+import org.drools.modelcompiler.domain.Relationship;
+import org.drools.modelcompiler.domain.SensorEvents.AlarmRaised;
+import org.drools.modelcompiler.domain.SensorEvents.CalibrationPassed;
+import org.drools.modelcompiler.domain.SensorEvents.HeartbeatOk;
+import org.drools.modelcompiler.domain.SensorEvents.MaintenanceScheduled;
+import org.drools.modelcompiler.domain.SensorEvents.MonitoringStation;
+import org.drools.modelcompiler.domain.SensorEvents.OperatorAcknowledged;
+import org.drools.modelcompiler.domain.SensorEvents.SensorActivated;
+import org.drools.modelcompiler.domain.Toy;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.kie.api.runtime.KieSession;
+import org.kie.api.runtime.rule.FactHandle;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.drools.model.DSL.declarationOf;
+import static org.drools.model.DSL.execute;
+import static org.drools.model.PatternDSL.and;
+import static org.drools.model.PatternDSL.or;
+import static org.drools.model.PatternDSL.pattern;
+import static org.drools.model.PatternDSL.rule;
+import static org.drools.model.PatternDSL.sequence;
+
+public class PatternDSLSequenceAdvancedTest {
+
+    private final List<String> results = new ArrayList<>();
+    private KieSession ksession;
+
+    @AfterEach
+    public void tearDown() {
+        results.clear();
+        if (ksession != null) {
+            ksession.dispose();
+            ksession = null;
+        }
+    }
+
+    private void insertAndFire(Object... facts) {
+        for (Object f : facts) { ksession.insert(f); }
+        ksession.fireAllRules();
+    }
+
+    @Test
+    public void noiseEventsBetweenStepsAreIgnored() {
+        Variable<Person>       personV = declarationOf(Person.class);
+        Variable<Toy>          toyV    = declarationOf(Toy.class);
+        Variable<Relationship> relV    = declarationOf(Relationship.class);
+
+        Rule rule = rule("noise-rule").build(
+                pattern(personV),
+                sequence(
+                        pattern(toyV).expr("is-ball", t -> t.getName().equals("ball")),
+                        pattern(relV).expr("is-go",   r -> r.getStart().equals("go"))
+                ),
+                execute(() -> results.add("fired"))
+        );
+
+        ksession = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule)).newKieSession();
+
+        insertAndFire(new Person("anchor"));
+
+        insertAndFire(new Toy("ball"));               // step 1 satisfied
+
+        // Noise: wrong toy name (must not re-satisfy step 1 or advance step 2)
+        insertAndFire(new Toy("other"));
+        assertThat(results).isEmpty();
+
+        // Noise: wrong relationship start (must not fire rule)
+        insertAndFire(new Relationship("nope", "x"));
+        assertThat(results).isEmpty();
+
+        // Correct step 2
+        insertAndFire(new Relationship("go", "done"));
+        assertThat(results).containsExactly("fired");
+    }
+
+    @Test
+    public void eventBeforeAnchorIsNotCaptured() {
+        Variable<Person>       personV = declarationOf(Person.class);
+        Variable<Toy>          toyV    = declarationOf(Toy.class);
+        Variable<Relationship> relV    = declarationOf(Relationship.class);
+
+        Rule rule = rule("pre-anchor-rule").build(
+                pattern(personV),
+                sequence(
+                        pattern(toyV).expr("is-ball", t -> t.getName().equals("ball")),
+                        pattern(relV).expr("is-go",   r -> r.getStart().equals("go"))
+                ),
+                execute(() -> results.add("fired"))
+        );
+
+        ksession = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule)).newKieSession();
+
+        // Step-1 event arrives BEFORE the anchor
+        insertAndFire(new Toy("ball"));
+
+        // Anchor inserted — sequencer starts, but the ball is already in the past
+        insertAndFire(new Person("anchor"));
+
+        // Step-2 event: the sequencer is at step 0 (ball not yet seen), must NOT fire
+        insertAndFire(new Relationship("go", "done"));
+
+        assertThat(results).isEmpty();
+    }
+
+    @Test
+    public void orSecondBranchAloneFires() {
+        Variable<MonitoringStation>    stationV   = declarationOf(MonitoringStation.class);
+        Variable<SensorActivated>      activatedV = declarationOf(SensorActivated.class);
+        Variable<HeartbeatOk>          heartbeatV = declarationOf(HeartbeatOk.class);
+        Variable<AlarmRaised>          alarmV     = declarationOf(AlarmRaised.class);
+        Variable<OperatorAcknowledged> ackV       = declarationOf(OperatorAcknowledged.class);
+
+        Rule rule = rule("or-second-branch").build(
+                pattern(stationV),
+                sequence(
+                        pattern(activatedV).expr("s1", a -> a.getSensorId().equals("s1")),
+                        or(
+                                pattern(heartbeatV).expr("hb",  h  -> h.getSensorId().equals("s1")),
+                                pattern(alarmV).expr("al",      al -> al.getSeverity().equals("high"))
+                        ),
+                        pattern(ackV).expr("ack", a -> a.getOperator().equals("alice"))
+                ),
+                execute(() -> results.add("fired"))
+        );
+
+        ksession = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule)).newKieSession();
+
+        insertAndFire(new MonitoringStation("station-1"));
+        insertAndFire(new SensorActivated("s1"));
+
+        // Only the alarm branch — no heartbeat
+        insertAndFire(new AlarmRaised("s1", "high"));
+        insertAndFire(new OperatorAcknowledged("s1", "alice"));
+
+        assertThat(results).containsExactly("fired");
+    }
+
+    @Test
+    public void orBothBranchesArriveRuleFiresOnce() {
+        Variable<MonitoringStation>    stationV   = declarationOf(MonitoringStation.class);
+        Variable<SensorActivated>      activatedV = declarationOf(SensorActivated.class);
+        Variable<HeartbeatOk>          heartbeatV = declarationOf(HeartbeatOk.class);
+        Variable<AlarmRaised>          alarmV     = declarationOf(AlarmRaised.class);
+        Variable<OperatorAcknowledged> ackV       = declarationOf(OperatorAcknowledged.class);
+
+        Rule rule = rule("or-both-branches").build(
+                pattern(stationV),
+                sequence(
+                        pattern(activatedV).expr("s1", a -> a.getSensorId().equals("s1")),
+                        or(
+                                pattern(heartbeatV).expr("hb",  h  -> h.getSensorId().equals("s1")),
+                                pattern(alarmV).expr("al",      al -> al.getSeverity().equals("high"))
+                        ),
+                        pattern(ackV).expr("ack", a -> a.getOperator().equals("alice"))
+                ),
+                execute(() -> results.add("fired"))
+        );
+
+        ksession = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule)).newKieSession();
+
+        insertAndFire(new MonitoringStation("station-1"));
+        insertAndFire(new SensorActivated("s1"));
+
+        // First branch fires — OR step advances, gate deactivates
+        insertAndFire(new HeartbeatOk("s1"));
+
+        // Second branch arrives AFTER step has advanced — must be ignored
+        insertAndFire(new AlarmRaised("s1", "high"));
+
+        insertAndFire(new OperatorAcknowledged("s1", "alice"));
+
+        // Rule must have fired exactly once
+        assertThat(results).containsExactly("fired");
+    }
+
+    @Test
+    public void andGateWithInterleavedNoiseWaitsForBothSignals() {
+        Variable<MonitoringStation>    stationV   = declarationOf(MonitoringStation.class);
+        Variable<SensorActivated>      activatedV = declarationOf(SensorActivated.class);
+        Variable<HeartbeatOk>          heartbeatV = declarationOf(HeartbeatOk.class);
+        Variable<AlarmRaised>          alarmV     = declarationOf(AlarmRaised.class);
+        Variable<OperatorAcknowledged> ackV       = declarationOf(OperatorAcknowledged.class);
+
+        Rule rule = rule("and-noise-rule").build(
+                pattern(stationV),
+                sequence(
+                        pattern(activatedV).expr("s1", a -> a.getSensorId().equals("s1")),
+                        and(
+                                pattern(heartbeatV).expr("hb", h  -> h.getSensorId().equals("s1")),
+                                pattern(alarmV).expr("al",     al -> al.getSeverity().equals("high"))
+                        ),
+                        pattern(ackV).expr("ack", a -> a.getOperator().equals("alice"))
+                ),
+                execute(() -> results.add("fired"))
+        );
+
+        ksession = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule)).newKieSession();
+
+        insertAndFire(new MonitoringStation("station-1"));
+        insertAndFire(new SensorActivated("s1"));
+
+        // AND child #1 (heartbeat for s1)
+        insertAndFire(new HeartbeatOk("s1"));
+        assertThat(results).isEmpty();                              // still waiting for alarm
+
+        // Noise: heartbeat for a different sensor — must not count as s1's signal
+        insertAndFire(new HeartbeatOk("s2"));
+        assertThat(results).isEmpty();
+
+        // Noise: low-severity alarm — doesn't satisfy the "high" constraint
+        insertAndFire(new AlarmRaised("s1", "low"));
+        assertThat(results).isEmpty();
+
+        // AND child #2 (high alarm for s1) — both gates now satisfied
+        insertAndFire(new AlarmRaised("s1", "high"));
+
+        insertAndFire(new OperatorAcknowledged("s1", "alice"));
+        assertThat(results).containsExactly("fired");
+    }
+
+    @Test
+    public void threeStepSequenceFiresInOrder() {
+        Variable<Person>       personV = declarationOf(Person.class);
+        Variable<Toy>          toyV    = declarationOf(Toy.class);
+        Variable<Relationship> relV    = declarationOf(Relationship.class);
+        Variable<SensorActivated> sensorV = declarationOf(SensorActivated.class);
+
+        Rule rule = rule("three-step-rule").build(
+                pattern(personV),
+                sequence(
+                        pattern(toyV).expr("is-ball",   t -> t.getName().equals("ball")),
+                        pattern(relV).expr("is-go",     r -> r.getStart().equals("go")),
+                        pattern(sensorV).expr("is-s1",  s -> s.getSensorId().equals("s1"))
+                ),
+                execute(() -> results.add("fired"))
+        );
+
+        ksession = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule)).newKieSession();
+
+        insertAndFire(new Person("anchor"));
+
+        insertAndFire(new Toy("ball"));
+        assertThat(results).isEmpty();
+
+        insertAndFire(new Relationship("go", "done"));
+        assertThat(results).isEmpty();
+
+        insertAndFire(new SensorActivated("s1"));
+        assertThat(results).containsExactly("fired");
+    }
+
+    @Test
+    public void threeStepSequenceOutOfOrderDoesNotFire() {
+        Variable<Person>       personV = declarationOf(Person.class);
+        Variable<Toy>          toyV    = declarationOf(Toy.class);
+        Variable<Relationship> relV    = declarationOf(Relationship.class);
+        Variable<SensorActivated> sensorV = declarationOf(SensorActivated.class);
+
+        Rule rule = rule("three-step-oo-rule").build(
+                pattern(personV),
+                sequence(
+                        pattern(toyV).expr("is-ball",   t -> t.getName().equals("ball")),
+                        pattern(relV).expr("is-go",     r -> r.getStart().equals("go")),
+                        pattern(sensorV).expr("is-s1",  s -> s.getSensorId().equals("s1"))
+                ),
+                execute(() -> results.add("fired"))
+        );
+
+        ksession = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule)).newKieSession();
+
+        insertAndFire(new Person("anchor"));
+
+        insertAndFire(new Toy("ball"));                         // step 1 done
+
+        // Step 3 event arrives before step 2 — must not skip step 2
+        insertAndFire(new SensorActivated("s1"));
+        assertThat(results).isEmpty();
+
+        // Step 2 now — but step 3 event in the past, so still at step 2 waiting for step 3 again
+        insertAndFire(new Relationship("go", "done"));          // step 2 done
+        assertThat(results).isEmpty();
+
+        // Step 3 again — now fires
+        insertAndFire(new SensorActivated("s1"));
+        assertThat(results).containsExactly("fired");
+    }
+
+    @Test
+    public void retractAnchorAfterCompletionIsNoOp() {
+        Variable<Person>       personV = declarationOf(Person.class);
+        Variable<Toy>          toyV    = declarationOf(Toy.class);
+        Variable<Relationship> relV    = declarationOf(Relationship.class);
+
+        Rule rule = rule("retract-after-rule").build(
+                pattern(personV),
+                sequence(
+                        pattern(toyV).expr("is-ball", t -> t.getName().equals("ball")),
+                        pattern(relV).expr("is-go",   r -> r.getStart().equals("go"))
+                ),
+                execute(() -> results.add("fired"))
+        );
+
+        ksession = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule)).newKieSession();
+
+        FactHandle anchorHandle = ksession.insert(new Person("anchor"));
+        ksession.fireAllRules();
+
+        insertAndFire(new Toy("ball"));
+        insertAndFire(new Relationship("go", "done"));          // sequence complete
+        assertThat(results).containsExactly("fired");
+
+        // Retract after completion — must not throw, must not fire again
+        ksession.retract(anchorHandle);
+        ksession.fireAllRules();
+
+        assertThat(results).hasSize(1);
+    }
+
+    @Test
+    public void firstAnchorCompletesWhileSecondIsMidSequence() {
+        Variable<Person>       personV = declarationOf(Person.class);
+        Variable<Toy>          toyV    = declarationOf(Toy.class);
+        Variable<Relationship> relV    = declarationOf(Relationship.class);
+
+        Rule rule = rule("concurrent-anchor-rule").build(
+                pattern(personV),
+                sequence(
+                        pattern(toyV).expr("is-ball", t -> t.getName().equals("ball")),
+                        pattern(relV).expr("is-go",   r -> r.getStart().equals("go"))
+                ),
+                execute(() -> results.add("fired"))
+        );
+
+        ksession = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule)).newKieSession();
+
+        FactHandle anchorAHandle = ksession.insert(new Person("anchor-A"));
+        FactHandle anchorBHandle = ksession.insert(new Person("anchor-B"));
+        ksession.fireAllRules();                                // both sequencers start
+
+        // Both anchors consume step 1
+        insertAndFire(new Toy("ball"));
+
+        // Step 2 arrives — both sequences should be able to complete
+        insertAndFire(new Relationship("go", "done"));
+
+        // TODO: once per-tuple DynamicFilter isolation is fixed, change to hasSize(2).
+        // Current known limitation: only one sequencer advances due to shared filter slots.
+        assertThat(results).hasSizeGreaterThanOrEqualTo(1);
+    }
+
+    @Test
+    public void sameTypeInFirstAndThirdStep() {
+        Variable<Person>       personV = declarationOf(Person.class);
+        Variable<Toy>          toyV1   = declarationOf(Toy.class);
+        Variable<Relationship> relV    = declarationOf(Relationship.class);
+        Variable<Toy>          toyV2   = declarationOf(Toy.class);
+
+        Rule rule = rule("type-reuse-rule").build(
+                pattern(personV),
+                sequence(
+                        pattern(toyV1).expr("is-ball",  t -> t.getName().equals("ball")),
+                        pattern(relV).expr("is-go",     r -> r.getStart().equals("go")),
+                        pattern(toyV2).expr("is-doll",  t -> t.getName().equals("doll"))
+                ),
+                execute(() -> results.add("fired"))
+        );
+
+        ksession = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule)).newKieSession();
+
+        insertAndFire(new Person("anchor"));
+
+        // Step 1: Toy("ball")
+        insertAndFire(new Toy("ball"));
+        assertThat(results).isEmpty();
+
+        // Wrong Toy during step 2 wait — must not advance step 3
+        insertAndFire(new Toy("doll"));
+        assertThat(results).isEmpty();
+
+        // Step 2: Relationship("go")
+        insertAndFire(new Relationship("go", "done"));
+        assertThat(results).isEmpty();
+
+        // Step 3: Toy("doll") — same type as step 1, different filter
+        insertAndFire(new Toy("doll"));
+        assertThat(results).containsExactly("fired");
+    }
+
+    @Test
+    public void secondSequenceEventBeforeFirstSequenceCompletesIsIgnored() {
+        Variable<Person>       personV = declarationOf(Person.class);
+        Variable<Toy>          toyV    = declarationOf(Toy.class);
+        Variable<Relationship> relV    = declarationOf(Relationship.class);
+
+        Rule rule = rule("chained-seq-order-rule").build(
+                pattern(personV),
+                sequence(
+                        pattern(toyV).expr("is-ball", t -> t.getName().equals("ball"))
+                ),
+                sequence(
+                        pattern(relV).expr("is-go", r -> r.getStart().equals("go"))
+                ),
+                execute(() -> results.add("fired"))
+        );
+
+        ksession = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule)).newKieSession();
+
+        insertAndFire(new Person("anchor"));
+
+        // Relationship arrives before Toy — second SequenceNode not yet active
+        insertAndFire(new Relationship("go", "done"));
+        assertThat(results).isEmpty();
+
+        // Now Toy arrives — first sequence completes, second SequenceNode activates
+        insertAndFire(new Toy("ball"));
+        assertThat(results).isEmpty();                          // second step not yet seen
+
+        // Relationship again — now the second SequenceNode is listening
+        insertAndFire(new Relationship("go", "done"));
+        assertThat(results).containsExactly("fired");
+    }
+
+    @Test
+    public void batchInsertBeforeFireDoesNotFire() {
+        Variable<Person>       personV = declarationOf(Person.class);
+        Variable<Toy>          toyV    = declarationOf(Toy.class);
+        Variable<Relationship> relV    = declarationOf(Relationship.class);
+
+        Rule rule = rule("batch-insert-rule").build(
+                pattern(personV),
+                sequence(
+                        pattern(toyV).expr("is-ball", t -> t.getName().equals("ball")),
+                        pattern(relV).expr("is-go", r -> r.getStart().equals("go"))
+                ),
+                execute(() -> results.add("fired"))
+        );
+
+        ksession = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule)).newKieSession();
+
+        // Insert all facts before any fireAllRules() — step signals are lost
+        // because the sequencer hasn't started yet
+        ksession.insert(new Person("anchor"));
+        ksession.insert(new Toy("ball"));
+        ksession.insert(new Relationship("go", "done"));
+
+        ksession.fireAllRules();
+
+        // Sequence does not fire: step facts were inserted before the sequencer
+        // started. This is a known limitation of the current AlphaAdapter design.
+        assertThat(results).isEmpty();
+    }
+
+    @Test
+    public void orGateWithFiveInputsFiresOnAnyBranch() {
+
+        final Variable<MonitoringStation>    stationV     = declarationOf(MonitoringStation.class);
+        final Variable<SensorActivated>      activatedV   = declarationOf(SensorActivated.class);
+        final Variable<HeartbeatOk>          heartbeatV   = declarationOf(HeartbeatOk.class);
+        final Variable<AlarmRaised>          alarmV       = declarationOf(AlarmRaised.class);
+        final Variable<CalibrationPassed>    calibV       = declarationOf(CalibrationPassed.class);
+        final Variable<OperatorAcknowledged> ackV         = declarationOf(OperatorAcknowledged.class);
+        final Variable<MaintenanceScheduled> maintenanceV = declarationOf(MaintenanceScheduled.class);
+
+        Rule fiveInputRule =
+                rule("five-input-or").build(
+                        pattern(stationV),
+                        sequence(
+                                pattern(activatedV).expr("anchor", a -> a.getSensorId().equals("sensor-1")),
+                                or(
+                                        pattern(heartbeatV).expr("hb",   h -> h.getSensorId().equals("sensor-1")),
+                                        pattern(alarmV).expr("alarm",    al -> al.getSeverity().equals("high")),
+                                        pattern(calibV).expr("calib",    c -> c.getSensorId().equals("sensor-1")),
+                                        pattern(ackV).expr("ack",        a -> a.getOperator().equals("alice")),
+                                        pattern(maintenanceV).expr("maint", m -> m.getSensorId().equals("sensor-1"))
+                                ),
+                                pattern(activatedV).expr("final", a -> a.getSensorId().equals("sensor-2"))
+                        ),
+                        execute(() -> results.add("done"))
+                );
+
+        ksession = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(fiveInputRule)).newKieSession();
+
+        insertAndFire(new MonitoringStation("station-1"));
+        insertAndFire(new SensorActivated("sensor-1"));             // anchor fires; OR step activates
+        insertAndFire(new MaintenanceScheduled("sensor-1"));        // 5th OR branch triggers default loop path
+        insertAndFire(new SensorActivated("sensor-2"));             // final step; consequence fires
+
+        assertThat(results).containsExactly("done");
+    }
+}
diff --git a/drools-model/drools-model-compiler/src/test/java/org/drools/modelcompiler/PatternDSLSequenceCompositeTest.java b/drools-model/drools-model-compiler/src/test/java/org/drools/modelcompiler/PatternDSLSequenceCompositeTest.java
new file mode 100644
index 00000000000..93f4e18f843
--- /dev/null
+++ b/drools-model/drools-model-compiler/src/test/java/org/drools/modelcompiler/PatternDSLSequenceCompositeTest.java
@@ -0,0 +1,335 @@
+/*
+ * 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.modelcompiler;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.drools.model.Rule;
+import org.drools.model.Variable;
+import org.drools.model.impl.ModelImpl;
+import org.drools.modelcompiler.domain.SensorEvents.AlarmRaised;
+import org.drools.modelcompiler.domain.SensorEvents.CalibrationPassed;
+import org.drools.modelcompiler.domain.SensorEvents.HeartbeatOk;
+import org.drools.modelcompiler.domain.SensorEvents.MonitoringStation;
+import org.drools.modelcompiler.domain.SensorEvents.OperatorAcknowledged;
+import org.drools.modelcompiler.domain.SensorEvents.SensorActivated;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.kie.api.runtime.KieSession;
+import org.kie.api.runtime.rule.FactHandle;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.drools.model.DSL.declarationOf;
+import static org.drools.model.DSL.execute;
+import static org.drools.model.PatternDSL.and;
+import static org.drools.model.PatternDSL.or;
+import static org.drools.model.PatternDSL.pattern;
+import static org.drools.model.PatternDSL.rule;
+import static org.drools.model.PatternDSL.sequence;
+
+public class PatternDSLSequenceCompositeTest {
+
+    private final Variable<MonitoringStation>     station         = declarationOf(MonitoringStation.class);
+    private final Variable<SensorActivated>       sensorActivated = declarationOf(SensorActivated.class);
+    private final Variable<HeartbeatOk>           heartbeat       = declarationOf(HeartbeatOk.class);
+    private final Variable<AlarmRaised>           alarm           = declarationOf(AlarmRaised.class);
+    private final Variable<CalibrationPassed>     calibration     = declarationOf(CalibrationPassed.class);
+    private final Variable<OperatorAcknowledged>  ack             = declarationOf(OperatorAcknowledged.class);
+
+    private KieSession ksession;
+
+    @AfterEach
+    public void tearDown() {
+        if (ksession != null) {
+            ksession.dispose();
+        }
+    }
+
+    private void insertAndFire(Object... facts) {
+        for (Object fact : facts) {
+            ksession.insert(fact);
+        }
+        ksession.fireAllRules();
+    }
+
+    @Test
+
+    public void sequenceFiresWithOrOfTwo() {
+        // Step 1 is or(heartbeat, alarm). Either signal is enough to advance.
+        final List<String> results = new ArrayList<>();
+
+        Rule orRule =
+                rule("or-rule").build(
+                        pattern(station),
+                        sequence(
+                                pattern(sensorActivated).expr("anchor", a -> a.getSensorId().equals("sensor-1")),
+                                or(
+                                        pattern(heartbeat).expr("ok", h -> h.getSensorId().equals("sensor-1")),
+                                        pattern(alarm).expr("hi", al -> al.getSeverity().equals("high"))
+                                ),
+                                pattern(ack).expr("ack", a -> a.getOperator().equals("alice"))
+                        ),
+                        execute(() -> results.add("acknowledged"))
+                );
+
+        ksession = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(orRule)).newKieSession();
+
+        insertAndFire(new MonitoringStation("station-1"));
+        insertAndFire(new SensorActivated("sensor-1"));
+        insertAndFire(new HeartbeatOk("sensor-1"));            // OR child #1 fires → step advances
+        insertAndFire(new OperatorAcknowledged("sensor-1", "alice"));
+
+        assertThat(results).containsExactly("acknowledged");
+    }
+
+    @Test
+    public void sequenceFiresWithAndOfTwo() {
+        // Step 1 is and(heartbeat, alarm). Both signals must arrive before step advances.
+        final List<String> results = new ArrayList<>();
+
+        Rule andRule =
+                rule("and-rule").build(
+                        pattern(station),
+                        sequence(
+                                pattern(sensorActivated).expr("anchor", a -> a.getSensorId().equals("sensor-1")),
+                                and(
+                                        pattern(heartbeat).expr("ok", h -> h.getSensorId().equals("sensor-1")),
+                                        pattern(alarm).expr("hi", al -> al.getSeverity().equals("high"))
+                                ),
+                                pattern(ack).expr("ack", a -> a.getOperator().equals("alice"))
+                        ),
+                        execute(() -> results.add("acknowledged"))
+                );
+
+        ksession = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(andRule)).newKieSession();
+
+        insertAndFire(new MonitoringStation("station-1"));
+        insertAndFire(new SensorActivated("sensor-1"));
+        insertAndFire(new HeartbeatOk("sensor-1"));               // AND child #1 fires → step still waiting
+        insertAndFire(new AlarmRaised("sensor-1", "high"));       // AND child #2 fires → step advances
+        insertAndFire(new OperatorAcknowledged("sensor-1", "alice"));
+
+        assertThat(results).containsExactly("acknowledged");
+    }
+
+    @Test
+    public void sequenceFiresWithNestedAndInsideOr() {
+        // Step 1 is or(heartbeat, and(calibration, alarm)).
+        // Either branch suffices: a heartbeat alone, or calibration + alarm together.
+        final List<String> results = new ArrayList<>();
+
+        Rule nestedRule =
+                rule("nested-rule").build(
+                        pattern(station),
+                        sequence(
+                                pattern(sensorActivated).expr("anchor", a -> a.getSensorId().equals("sensor-1")),
+                                or(
+                                        pattern(heartbeat).expr("ok", h -> h.getSensorId().equals("sensor-1")),
+                                        and(
+                                                pattern(calibration).expr("calibrated", c -> c.getSensorId().equals("sensor-1")),
+                                                pattern(alarm).expr("hi", al -> al.getSeverity().equals("high"))
+                                        )
+                                ),
+                                pattern(ack).expr("ack", a -> a.getOperator().equals("alice"))
+                        ),
+                        execute(() -> results.add("acknowledged"))
+                );
+
+        ksession = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(nestedRule)).newKieSession();
+
+        insertAndFire(new MonitoringStation("station-1"));
+        insertAndFire(new SensorActivated("sensor-1"));
+        // The AND branch matches: calibration + alarm together, no heartbeat needed.
+        insertAndFire(new CalibrationPassed("sensor-1"));
+        insertAndFire(new AlarmRaised("sensor-1", "high"));
+        insertAndFire(new OperatorAcknowledged("sensor-1", "alice"));
+
+        assertThat(results).containsExactly("acknowledged");
+    }
+
+    @Test
+    public void sequenceDoesNotFireWhenAndPartial() {
+        // Same shape as sequenceFiresWithAndOfTwo, but only one of the AND children fires.
+        // AND requires every child matched; one match is not enough; step never advances.
+        final List<String> results = new ArrayList<>();
+
+        Rule andRule =
+                rule("and-partial-rule").build(
+                        pattern(station),
+                        sequence(
+                                pattern(sensorActivated).expr("anchor", a -> a.getSensorId().equals("sensor-1")),
+                                and(
+                                        pattern(heartbeat).expr("ok", h -> h.getSensorId().equals("sensor-1")),
+                                        pattern(alarm).expr("hi", al -> al.getSeverity().equals("high"))
+                                ),
+                                pattern(ack).expr("ack", a -> a.getOperator().equals("alice"))
+                        ),
+                        execute(() -> results.add("acknowledged"))
+                );
+
+        ksession = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(andRule)).newKieSession();
+
+        insertAndFire(new MonitoringStation("station-1"));
+        insertAndFire(new SensorActivated("sensor-1"));
+        insertAndFire(new HeartbeatOk("sensor-1"));               // AND child #1 fires; child #2 never does.
+        insertAndFire(new OperatorAcknowledged("sensor-1", "alice"));   // Step 1 still active; ack is ignored.
+
+        assertThat(results).isEmpty();
+    }
+
+    @Test
+    public void orBranchRetractAfterStepConsumedIsNoOp() {
+        final List<String> localResults = new ArrayList<>();
+
+        Rule orRule =
+                rule("or-retract-noop").build(
+                        pattern(station),
+                        sequence(
+                                pattern(sensorActivated).expr("anchor", a -> a.getSensorId().equals("sensor-1")),
+                                or(
+                                        pattern(heartbeat).expr("ok", h -> h.getSensorId().equals("sensor-1")),
+                                        pattern(alarm).expr("hi", al -> al.getSeverity().equals("high"))
+                                ),
+                                pattern(ack).expr("ack", a -> a.getOperator().equals("alice"))
+                        ),
+                        execute(() -> localResults.add("acknowledged"))
+                );
+
+        ksession = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(orRule)).newKieSession();
+
+        insertAndFire(new MonitoringStation("station-1"));
+        insertAndFire(new SensorActivated("sensor-1"));                   // anchor fires; OR step activates
+
+        FactHandle hbHandle = ksession.insert(new HeartbeatOk("sensor-1"));
+        ksession.fireAllRules();                                          // OR branch fires; step deactivates, ack step activates
+
+        ksession.retract(hbHandle);
+        ksession.fireAllRules();                                          // no-op: OR step already deactivated
+
+        insertAndFire(new AlarmRaised("sensor-1", "high"));               // no-op: OR step gone, no listener
+        insertAndFire(new OperatorAcknowledged("sensor-1", "alice"));     // ack step matches; rule fires
+
+        assertThat(localResults).containsExactly("acknowledged");
+    }
+
+    @Test
+    public void orBranchRetractAndReinsertAfterStepConsumedIsNoOp() {
+        final List<String> localResults = new ArrayList<>();
+
+        Rule orRule =
+                rule("or-reinsert-noop").build(
+                        pattern(station),
+                        sequence(
+                                pattern(sensorActivated).expr("anchor", a -> a.getSensorId().equals("sensor-1")),
+                                or(
+                                        pattern(heartbeat).expr("ok", h -> h.getSensorId().equals("sensor-1")),
+                                        pattern(alarm).expr("hi", al -> al.getSeverity().equals("high"))
+                                ),
+                                pattern(ack).expr("ack", a -> a.getOperator().equals("alice"))
+                        ),
+                        execute(() -> localResults.add("acknowledged"))
+                );
+
+        ksession = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(orRule)).newKieSession();
+
+        insertAndFire(new MonitoringStation("station-1"));
+        insertAndFire(new SensorActivated("sensor-1"));                   // anchor fires; OR step activates
+
+        FactHandle hbHandle = ksession.insert(new HeartbeatOk("sensor-1"));
+        ksession.fireAllRules();                                          // OR branch fires; step deactivated, ack step activates
+
+        ksession.retract(hbHandle);
+        ksession.fireAllRules();                                          // no-op: OR step already deactivated
+
+        insertAndFire(new HeartbeatOk("sensor-1"));                       // no-op: no listener for OR branch any more
+        insertAndFire(new OperatorAcknowledged("sensor-1", "alice"));     // ack step matches; rule fires
+
+        assertThat(localResults).containsExactly("acknowledged");
+    }
+
+    @Test
+    public void nestedOrInsideOrAdvancesOnAnyLeaf() {
+        final List<String> results = new ArrayList<>();
+
+        Rule rule =
+                rule("nested-or-rule").build(
+                        pattern(station),
+                        sequence(
+                                pattern(sensorActivated).expr("anchor", a -> a.getSensorId().equals("sensor-1")),
+                                or(
+                                        or(
+                                                pattern(heartbeat).expr("hb", h -> h.getSensorId().equals("sensor-1")),
+                                                pattern(alarm).expr("hi", al -> al.getSeverity().equals("high"))
+                                        ),
+                                        pattern(calibration).expr("cal", c -> c.getSensorId().equals("sensor-1"))
+                                ),
+                                pattern(ack).expr("ack", a -> a.getOperator().equals("alice"))
+                        ),
+                        execute(() -> results.add("acknowledged"))
+                );
+
+        ksession = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule)).newKieSession();
+
+        insertAndFire(new MonitoringStation("station-1"));
+        insertAndFire(new SensorActivated("sensor-1"));
+        // Trigger the inner-OR branch (alarm), which feeds the outer OR → step advances
+        insertAndFire(new AlarmRaised("sensor-1", "high"));
+        insertAndFire(new OperatorAcknowledged("sensor-1", "alice"));
+
+        assertThat(results).containsExactly("acknowledged");
+    }
+
+    @Test
+    public void andChildReceivesDuplicateSignalDoesNotDoubleCount() {
+        final List<String> results = new ArrayList<>();
+
+        Rule rule =
+                rule("and-dup-rule").build(
+                        pattern(station),
+                        sequence(
+                                pattern(sensorActivated).expr("anchor", a -> a.getSensorId().equals("sensor-1")),
+                                and(
+                                        pattern(heartbeat).expr("hb", h -> h.getSensorId().equals("sensor-1")),
+                                        pattern(alarm).expr("hi", al -> al.getSeverity().equals("high"))
+                                ),
+                                pattern(ack).expr("ack", a -> a.getOperator().equals("alice"))
+                        ),
+                        execute(() -> results.add("acknowledged"))
+                );
+
+        ksession = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule)).newKieSession();
+
+        insertAndFire(new MonitoringStation("station-1"));
+        insertAndFire(new SensorActivated("sensor-1"));
+
+        // AND child #1 arrives twice — must not advance step on second arrival
+        insertAndFire(new HeartbeatOk("sensor-1"));
+        assertThat(results).isEmpty();                              // still waiting for alarm
+
+        insertAndFire(new HeartbeatOk("sensor-1"));                 // duplicate — no-op for the gate
+        assertThat(results).isEmpty();
+
+        // AND child #2 arrives — both distinct children now satisfied
+        insertAndFire(new AlarmRaised("sensor-1", "high"));
+        insertAndFire(new OperatorAcknowledged("sensor-1", "alice"));
+
+        assertThat(results).containsExactly("acknowledged");
+    }
+}
diff --git a/drools-model/drools-model-compiler/src/test/java/org/drools/modelcompiler/PatternDSLSequenceConsequenceTest.java b/drools-model/drools-model-compiler/src/test/java/org/drools/modelcompiler/PatternDSLSequenceConsequenceTest.java
new file mode 100644
index 00000000000..3931065c2c8
--- /dev/null
+++ b/drools-model/drools-model-compiler/src/test/java/org/drools/modelcompiler/PatternDSLSequenceConsequenceTest.java
@@ -0,0 +1,452 @@
+/*
+ * 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.modelcompiler;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.drools.model.Rule;
+import org.drools.model.Variable;
+import org.drools.model.impl.ModelImpl;
+import org.drools.modelcompiler.domain.Person;
+import org.drools.modelcompiler.domain.Relationship;
+import org.drools.modelcompiler.domain.SensorEvents.AlarmRaised;
+import org.drools.modelcompiler.domain.SensorEvents.HeartbeatOk;
+import org.drools.modelcompiler.domain.SensorEvents.MonitoringStation;
+import org.drools.modelcompiler.domain.SensorEvents.SensorActivated;
+import org.drools.modelcompiler.domain.Toy;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.kie.api.KieBase;
+import org.kie.api.runtime.KieSession;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.drools.model.DSL.declarationOf;
+import static org.drools.model.PatternDSL.on;
+import static org.drools.model.PatternDSL.or;
+import static org.drools.model.PatternDSL.pattern;
+import static org.drools.model.PatternDSL.rule;
+import static org.drools.model.PatternDSL.sequence;
+import static org.drools.model.PatternDSL.when;
+
+public class PatternDSLSequenceConsequenceTest {
+
+    private final List<String> results = new ArrayList<>();
+    private KieSession ksession;
+
+    @AfterEach
+    public void tearDown() {
+        results.clear();
+        if (ksession != null) {
+            ksession.dispose();
+            ksession = null;
+        }
+    }
+
+    private void insertAndFire(Object... facts) {
+        for (Object f : facts) { ksession.insert(f); }
+        ksession.fireAllRules();
+    }
+
+    @Test
+    public void anchorVariableBoundInConsequence() {
+        Variable<Person>       personV = declarationOf(Person.class);
+        Variable<Toy>          toyV    = declarationOf(Toy.class);
+        Variable<Relationship> relV    = declarationOf(Relationship.class);
+
+        Rule rule = rule("anchor-binding-rule").build(
+                pattern(personV),
+                sequence(
+                        pattern(toyV).expr("is-ball", t -> t.getName().equals("ball")),
+                        pattern(relV).expr("is-go",   r -> r.getStart().equals("go"))
+                ),
+                on(personV).execute(p -> results.add("fired:" + p.getName()))
+        );
+
+        KieBase kbase = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule));
+        ksession = kbase.newKieSession();
+
+        insertAndFire(new Person("alice"));
+        insertAndFire(new Toy("ball"));
+        insertAndFire(new Relationship("go", "done"));
+
+        assertThat(results).containsExactly("fired:alice");
+    }
+
+    @Test
+    public void stepVariableBoundInConsequence() {
+        Variable<Person>       personV = declarationOf(Person.class);
+        Variable<Toy>          toyV    = declarationOf(Toy.class);
+        Variable<Relationship> relV    = declarationOf(Relationship.class);
+
+        Rule rule = rule("step-binding-rule").build(
+                pattern(personV),
+                sequence(
+                        pattern(toyV).expr("is-ball", t -> t.getName().equals("ball")),
+                        pattern(relV).expr("is-go",   r -> r.getStart().equals("go"))
+                ),
+                on(toyV, relV).execute((t, r) -> results.add(t.getName() + "+" + r.getStart()))
+        );
+
+        KieBase kbase = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule));
+        ksession = kbase.newKieSession();
+
+        insertAndFire(new Person("anchor"));
+        insertAndFire(new Toy("ball"));
+        insertAndFire(new Relationship("go", "done"));
+
+        assertThat(results).containsExactly("ball+go");
+    }
+
+    @Test
+    public void namedConsequenceAnchorVariableIsAccessible() {
+        Variable<Person>       personV = declarationOf(Person.class);
+        Variable<Toy>          toyV    = declarationOf(Toy.class);
+        Variable<Relationship> relV    = declarationOf(Relationship.class);
+
+        Rule rule = rule("named-consequence-anchor").build(
+                pattern(personV),
+                sequence(
+                        pattern(toyV).expr("is-ball", t -> t.getName().equals("ball")),
+                        pattern(relV).expr("is-go",   r -> r.getStart().equals("go"))
+                ),
+                when("cond", personV, p -> p.getAge() < 50).then(
+                        on(personV).breaking().execute(p -> results.add("young:" + p.getName()))
+                ).elseWhen().then(
+                        on(personV).breaking().execute(p -> results.add("old:" + p.getName()))
+                )
+        );
+
+        KieBase kbase = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule));
+        ksession = kbase.newKieSession();
+
+        insertAndFire(new Person("alice", 25));
+        insertAndFire(new Toy("ball"));
+        insertAndFire(new Relationship("go", "done"));
+
+        assertThat(results).containsExactly("young:alice");
+    }
+
+    @Test
+    public void namedConsequenceStepVariablesAreAccessible() {
+        Variable<Person>       personV = declarationOf(Person.class);
+        Variable<Toy>          toyV    = declarationOf(Toy.class);
+        Variable<Relationship> relV    = declarationOf(Relationship.class);
+
+        Rule rule = rule("named-consequence-step-vars").build(
+                pattern(personV),
+                sequence(
+                        pattern(toyV).expr("is-ball", t -> t.getName().equals("ball")),
+                        pattern(relV).expr("is-go",   r -> r.getStart().equals("go"))
+                ),
+                when("cond", personV, p -> p.getAge() < 50).then(
+                        on(toyV, relV).breaking().execute((t, r) ->
+                                results.add("named:" + t.getName() + "+" + r.getStart()))
+                ).elseWhen().then(
+                        on(toyV, relV).breaking().execute((t, r) ->
+                                results.add("named-old:" + t.getName() + "+" + r.getStart()))
+                )
+        );
+
+        KieBase kbase = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule));
+        ksession = kbase.newKieSession();
+
+        insertAndFire(new Person("alice", 25));
+        insertAndFire(new Toy("ball"));
+        insertAndFire(new Relationship("go", "done"));
+
+        assertThat(results).containsExactly("named:ball+go");
+    }
+
+    @Test
+    public void stepVarsResolvedWhenPatternChainsAfterSequence() {
+        Variable<Person>       personV  = declarationOf(Person.class);
+        Variable<Toy>          toyV     = declarationOf(Toy.class);
+        Variable<Relationship> relV     = declarationOf(Relationship.class);
+        Variable<Person>       extra    = declarationOf(Person.class);
+
+        Rule rule = rule("step-vars-after-chain").build(
+                pattern(personV).expr("is-anchor", p -> p.getName().equals("anchor")),
+                sequence(
+                        pattern(toyV).expr("is-ball", t -> t.getName().equals("ball")),
+                        pattern(relV).expr("is-go",   r -> r.getStart().equals("go"))
+                ),
+                // extra pattern chained AFTER the sequence
+                pattern(extra).expr("is-observer", p -> p.getName().equals("observer")),
+                on(toyV, relV, extra).execute((t, r, p) ->
+                        results.add(t.getName() + "+" + r.getStart() + "+" + p.getName()))
+        );
+
+        KieBase kbase = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule));
+        ksession = kbase.newKieSession();
+
+        // Insert the extra fact first so it is available when the sequence completes
+        insertAndFire(new Person("observer"));
+        insertAndFire(new Person("anchor"));
+        insertAndFire(new Toy("ball"));
+        insertAndFire(new Relationship("go", "done"));
+
+        assertThat(results).containsExactly("ball+go+observer");
+    }
+
+    @Test
+    public void stepVariableConsequenceFiredTwice() {
+        Variable<Person>       personV = declarationOf(Person.class);
+        Variable<Toy>          toyV    = declarationOf(Toy.class);
+        Variable<Relationship> relV    = declarationOf(Relationship.class);
+
+        Rule rule = rule("step-binding-fires-twice").build(
+                pattern(personV),
+                sequence(
+                        pattern(toyV).expr("is-ball", t -> t.getName().equals("ball")),
+                        pattern(relV).expr("is-go",   r -> r.getStart().equals("go"))
+                ),
+                on(toyV, relV).execute((t, r) -> results.add(t.getName() + "+" + r.getStart()))
+        );
+
+        KieBase kbase = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule));
+        ksession = kbase.newKieSession();
+
+        // Two anchors — rule fires once per anchor, second fire hits fetchFacts()
+        insertAndFire(new Person("alice"));
+        insertAndFire(new Person("bob"));
+        insertAndFire(new Toy("ball"));
+        insertAndFire(new Relationship("go", "done"));
+
+        assertThat(results).containsExactlyInAnyOrder("ball+go", "ball+go");
+    }
+
+    @Test
+    public void stepVariableWithDroolsInConsequence() {
+        Variable<Person>       personV = declarationOf(Person.class);
+        Variable<Toy>          toyV    = declarationOf(Toy.class);
+        Variable<Relationship> relV    = declarationOf(Relationship.class);
+
+        Rule rule = rule("step-binding-with-drools").build(
+                pattern(personV),
+                sequence(
+                        pattern(toyV).expr("is-ball", t -> t.getName().equals("ball")),
+                        pattern(relV).expr("is-go",   r -> r.getStart().equals("go"))
+                ),
+                on(toyV).execute((drools, t) -> results.add(t.getName()))
+        );
+
+        KieBase kbase = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule));
+        ksession = kbase.newKieSession();
+
+        insertAndFire(new Person("anchor"));
+        insertAndFire(new Toy("ball"));
+        insertAndFire(new Relationship("go", "done"));
+
+        assertThat(results).containsExactly("ball");
+    }
+
+    @Test
+    public void orStepSecondBranchVariableIsAccessibleInConsequence() {
+        Variable<MonitoringStation> stationV   = declarationOf(MonitoringStation.class);
+        Variable<SensorActivated>   activatedV = declarationOf(SensorActivated.class);
+        Variable<HeartbeatOk>       heartbeatV = declarationOf(HeartbeatOk.class);
+        Variable<AlarmRaised>       alarmV     = declarationOf(AlarmRaised.class);
+
+        Rule rule = rule("or-step-second-branch").build(
+                pattern(stationV),
+                sequence(
+                        pattern(activatedV).expr("act", a -> a.getSensorId().equals("s1")),
+                        or(
+                                pattern(heartbeatV).expr("hb", h -> h.getSensorId().equals("s1")),
+                                pattern(alarmV).expr("hi",     al -> al.getSeverity().equals("high"))
+                        )
+                ),
+                on(alarmV).execute(al -> results.add("alarm:" + al.getSeverity()))
+        );
+
+        KieBase kbase = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule));
+        ksession = kbase.newKieSession();
+
+        insertAndFire(new MonitoringStation("s1"));
+        insertAndFire(new SensorActivated("s1"));
+        insertAndFire(new AlarmRaised("s1", "high"));
+
+        assertThat(results).containsExactly("alarm:high");
+    }
+
+    @Test
+    public void orStepFirstBranchVariableIsAccessibleInConsequence() {
+        Variable<MonitoringStation> stationV   = declarationOf(MonitoringStation.class);
+        Variable<SensorActivated>   activatedV = declarationOf(SensorActivated.class);
+        Variable<HeartbeatOk>       heartbeatV = declarationOf(HeartbeatOk.class);
+        Variable<AlarmRaised>       alarmV     = declarationOf(AlarmRaised.class);
+
+        Rule rule = rule("or-step-first-branch").build(
+                pattern(stationV),
+                sequence(
+                        pattern(activatedV).expr("act", a -> a.getSensorId().equals("s1")),
+                        or(
+                                pattern(heartbeatV).expr("hb", h -> h.getSensorId().equals("s1")),
+                                pattern(alarmV).expr("hi",     al -> al.getSeverity().equals("high"))
+                        )
+                ),
+                on(heartbeatV).execute(h -> results.add("heartbeat:" + h.getSensorId()))
+        );
+
+        KieBase kbase = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule));
+        ksession = kbase.newKieSession();
+
+        insertAndFire(new MonitoringStation("s1"));
+        insertAndFire(new SensorActivated("s1"));
+        insertAndFire(new HeartbeatOk("s1"));   // first OR child — filterIndex 1, appended at position 1
+
+        assertThat(results).containsExactly("heartbeat:s1");
+    }
+
+    @Test
+    public void twoSequencesInOneRuleStepVariablesResolveFromCorrectMemory() {
+        Variable<Person>       personV = declarationOf(Person.class);
+        Variable<Toy>          toyV    = declarationOf(Toy.class);     // seq1, filterIndex=0
+        Variable<Relationship> relV    = declarationOf(Relationship.class); // seq2, filterIndex=0 (collision)
+
+        Rule rule = rule("two-sequences-variable-collision").build(
+                pattern(personV),
+                sequence(
+                        pattern(toyV).expr("is-ball", t -> t.getName().equals("ball"))
+                ),
+                sequence(
+                        pattern(relV).expr("is-go", r -> r.getStart().equals("go"))
+                ),
+                on(toyV, relV).execute((t, r) -> results.add(t.getName() + "+" + r.getStart()))
+        );
+
+        KieBase kbase = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule));
+        ksession = kbase.newKieSession();
+
+        insertAndFire(new Person("anchor"));
+        insertAndFire(new Toy("ball"));
+        insertAndFire(new Relationship("go", "done"));
+
+        assertThat(results).containsExactly("ball+go");
+    }
+
+    @Test
+    public void twoSequencesInOneRuleMultipleAnchorsBothFire() {
+        Variable<Person>       personV = declarationOf(Person.class);
+        Variable<Toy>          toyV    = declarationOf(Toy.class);
+        Variable<Relationship> relV    = declarationOf(Relationship.class);
+
+        Rule rule = rule("two-sequences-multiple-anchors").build(
+                pattern(personV),
+                sequence(
+                        pattern(toyV).expr("is-ball", t -> t.getName().equals("ball"))
+                ),
+                sequence(
+                        pattern(relV).expr("is-go", r -> r.getStart().equals("go"))
+                ),
+                on(personV, toyV, relV).execute((p, t, r) -> results.add(p.getName() + ":" + t.getName() + "+" + r.getStart()))
+        );
+
+        KieBase kbase = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule));
+        ksession = kbase.newKieSession();
+
+        // Multiple anchors (Person)
+        insertAndFire(new Person("alice"));
+        insertAndFire(new Person("bob"));
+        insertAndFire(new Toy("ball"));
+        insertAndFire(new Relationship("go", "done"));
+
+        assertThat(results).containsExactlyInAnyOrder(
+                "alice:ball+go",
+                "bob:ball+go"
+        );
+    }
+
+    @ParameterizedTest
+    @ValueSource(strings = {"single-anchor", "two-anchors", "three-anchors"})
+    public void twoSequencesInOneRuleMultipleAnchorsAndBindings(String mode) {
+        Variable<Person>       personV = declarationOf(Person.class);
+        Variable<Toy>          toyV    = declarationOf(Toy.class);
+        Variable<Relationship> relV    = declarationOf(Relationship.class);
+
+        Rule rule = rule("two-sequences-" + mode).build(
+                pattern(personV),
+                sequence(
+                        pattern(toyV).expr("is-ball", t -> t.getName().equals("ball"))
+                ),
+                sequence(
+                        pattern(relV).expr("is-go", r -> r.getStart().equals("go"))
+                ),
+                on(personV, toyV, relV).execute((p, t, r) -> results.add(p.getName() + ":" + t.getName() + "+" + r.getStart()))
+        );
+
+        KieBase kbase = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule));
+        ksession = kbase.newKieSession();
+
+        switch (mode) {
+            case "single-anchor":
+                insertAndFire(new Person("alice"));
+                insertAndFire(new Toy("ball"));
+                insertAndFire(new Relationship("go", "done"));
+                assertThat(results).containsExactly("alice:ball+go");
+                break;
+            case "two-anchors":
+                insertAndFire(new Person("alice"));
+                insertAndFire(new Person("bob"));
+                insertAndFire(new Toy("ball"));
+                insertAndFire(new Relationship("go", "done"));
+                assertThat(results).containsExactlyInAnyOrder("alice:ball+go", "bob:ball+go");
+                break;
+            case "three-anchors":
+                insertAndFire(new Person("alice"));
+                insertAndFire(new Person("bob"));
+                insertAndFire(new Person("charlie"));
+                insertAndFire(new Toy("ball"));
+                insertAndFire(new Relationship("go", "done"));
+                assertThat(results).containsExactlyInAnyOrder("alice:ball+go", "bob:ball+go", "charlie:ball+go");
+                break;
+        }
+    }
+
+    @Test
+    public void stepVariableResolvedWhenLhsHasFirstLevelOr() {
+        Variable<Person>       personV  = declarationOf(Person.class);
+        Variable<Toy>          toyV     = declarationOf(Toy.class);
+        Variable<Relationship> relV     = declarationOf(Relationship.class);
+
+        Rule rule = rule("step-var-with-first-level-or").build(
+                or(
+                        pattern(personV).expr("is-alice", p -> p.getName().equals("alice")),
+                        pattern(personV).expr("is-bob",   p -> p.getName().equals("bob"))
+                ),
+                sequence(
+                        pattern(toyV).expr("is-ball", t -> t.getName().equals("ball")),
+                        pattern(relV).expr("is-go",   r -> r.getStart().equals("go"))
+                ),
+                on(toyV, relV).execute((t, r) -> results.add(t.getName() + "+" + r.getStart()))
+        );
+
+        KieBase kbase = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule));
+        ksession = kbase.newKieSession();
+
+        insertAndFire(new Person("alice"));
+        insertAndFire(new Toy("ball"));
+        insertAndFire(new Relationship("go", "done"));
+
+        assertThat(results).containsExactly("ball+go");
+    }
+}
diff --git a/drools-model/drools-model-compiler/src/test/java/org/drools/modelcompiler/PatternDSLSequenceEdgeCaseTest.java b/drools-model/drools-model-compiler/src/test/java/org/drools/modelcompiler/PatternDSLSequenceEdgeCaseTest.java
new file mode 100644
index 00000000000..a499e0dd2c8
--- /dev/null
+++ b/drools-model/drools-model-compiler/src/test/java/org/drools/modelcompiler/PatternDSLSequenceEdgeCaseTest.java
@@ -0,0 +1,514 @@
+/*
+ * 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.modelcompiler;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.drools.model.Rule;
+import org.drools.model.Variable;
+import org.drools.model.impl.ModelImpl;
+import org.drools.modelcompiler.domain.Person;
+import org.drools.modelcompiler.domain.Relationship;
+import org.drools.modelcompiler.domain.Result;
+import org.drools.modelcompiler.domain.SensorEvents.AlarmRaised;
+import org.drools.modelcompiler.domain.SensorEvents.HeartbeatOk;
+import org.drools.modelcompiler.domain.SensorEvents.MonitoringStation;
+import org.drools.modelcompiler.domain.SensorEvents.SensorActivated;
+import org.drools.modelcompiler.domain.Toy;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.kie.api.KieBase;
+import org.kie.api.runtime.KieSession;
+import org.kie.api.runtime.rule.FactHandle;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.drools.model.DSL.declarationOf;
+import static org.drools.model.DSL.execute;
+import static org.drools.model.PatternDSL.and;
+import static org.drools.model.PatternDSL.on;
+import static org.drools.model.PatternDSL.or;
+import static org.drools.model.PatternDSL.pattern;
+import static org.drools.model.PatternDSL.rule;
+import static org.drools.model.PatternDSL.sequence;
+import static org.drools.model.PatternDSL.when;
+
+public class PatternDSLSequenceEdgeCaseTest {
+
+    private final List<String> results = new ArrayList<>();
+    private KieSession ksession;
+
+    @AfterEach
+    public void tearDown() {
+        results.clear();
+        if (ksession != null) {
+            ksession.dispose();
+            ksession = null;
+        }
+    }
+
+    @Test
+    public void consequenceBranchDoesNotFallThroughToSequence() {
+        Variable<Result>       resultV = declarationOf(Result.class);
+        Variable<Person>       personV = declarationOf(Person.class);
+        Variable<Toy>          toyV    = declarationOf(Toy.class);
+        Variable<Relationship> relV    = declarationOf(Relationship.class);
+
+        Rule rule = rule("c1-rule").build(
+                pattern(resultV),
+                pattern(personV),
+                when("cond", personV, p -> p.getAge() < 30).then(
+                        on(personV, resultV).breaking().execute((p, r) -> r.setValue("young"))
+                ).elseWhen().then(
+                        on(personV, resultV).breaking().execute((p, r) -> r.setValue("other"))
+                ),
+                sequence(
+                        pattern(toyV).expr("is-ball", t -> t.getName().equals("ball")),
+                        pattern(relV).expr("is-go",   r -> r.getStart().equals("go"))
+                ),
+                execute(() -> results.add("seq-fired"))
+        );
+
+        KieBase kieBase = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule));
+        assertThat(kieBase).isNotNull();
+    }
+
+    @Test
+    public void longSequenceDoesNotOverflowCircularBuffer() {
+        Variable<Person> personV = declarationOf(Person.class);
+        Variable<Toy>    toyV    = declarationOf(Toy.class);
+
+        Rule rule = rule("c2-rule").build(
+                pattern(personV),
+                sequence(
+                        pattern(toyV).expr("is-ball", t -> t.getName().equals("ball"))
+                ),
+                execute(() -> results.add("fired"))
+        );
+
+        KieBase kbase = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule));
+        ksession = kbase.newKieSession();
+
+        Person anchor = new Person("anchor");
+        FactHandle anchorHandle = ksession.insert(anchor);
+        ksession.fireAllRules();
+
+        for (int i = 0; i < 110; i++) {
+            ksession.insert(new Toy("ball"));
+            ksession.fireAllRules();                          // sequence completes
+            ksession.update(anchorHandle, anchor);            // restart sequencer
+            ksession.fireAllRules();
+        }
+
+        assertThat(results).hasSize(110);
+    }
+
+    @Test
+    public void retractAnchorWithOrStepDoesNotLeakSiblingAdapters() {
+        Variable<MonitoringStation> stationV  = declarationOf(MonitoringStation.class);
+        Variable<SensorActivated>   activatedV = declarationOf(SensorActivated.class);
+        Variable<HeartbeatOk>       heartbeatV = declarationOf(HeartbeatOk.class);
+        Variable<AlarmRaised>       alarmV     = declarationOf(AlarmRaised.class);
+
+        Rule rule = rule("c3-rule").build(
+                pattern(stationV),
+                sequence(
+                        pattern(activatedV).expr("s1", a -> a.getSensorId().equals("s1")),
+                        or(
+                                pattern(heartbeatV).expr("hb", h -> h.getSensorId().equals("s1")),
+                                pattern(alarmV).expr("al", al -> al.getSeverity().equals("high"))
+                        )
+                ),
+                execute(() -> results.add("seq-fired"))
+        );
+
+        KieBase kbase = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule));
+        ksession = kbase.newKieSession();
+
+        FactHandle stationHandle = ksession.insert(new MonitoringStation("station-1"));
+        ksession.fireAllRules();                              // sequencer starts
+
+        ksession.insert(new SensorActivated("s1"));
+        ksession.fireAllRules();                              // step-1 consumed; OR step now active
+
+        // Retract the anchor — stop() must deactivate BOTH OR siblings
+        ksession.retract(stationHandle);
+        ksession.fireAllRules();
+
+        // Insert a heartbeat — the leaked OR sibling adapter must NOT respond
+        ksession.insert(new HeartbeatOk("s1"));
+        ksession.fireAllRules();
+
+        assertThat(results).isEmpty();
+    }
+
+    @Test
+    public void retractAnchorMidAndStepDoesNotLeakChildAdapters() {
+        Variable<MonitoringStation> stationV   = declarationOf(MonitoringStation.class);
+        Variable<SensorActivated>   activatedV = declarationOf(SensorActivated.class);
+        Variable<HeartbeatOk>       heartbeatV = declarationOf(HeartbeatOk.class);
+        Variable<AlarmRaised>       alarmV     = declarationOf(AlarmRaised.class);
+
+        Rule rule = rule("c3b-rule").build(
+                pattern(stationV),
+                sequence(
+                        pattern(activatedV).expr("s1", a -> a.getSensorId().equals("s1")),
+                        and(
+                                pattern(heartbeatV).expr("hb", h -> h.getSensorId().equals("s1")),
+                                pattern(alarmV).expr("al", al -> al.getSeverity().equals("high"))
+                        )
+                ),
+                execute(() -> results.add("seq-fired"))
+        );
+
+        KieBase kbase = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule));
+        ksession = kbase.newKieSession();
+
+        FactHandle stationHandle = ksession.insert(new MonitoringStation("station-1"));
+        ksession.fireAllRules();                              // sequencer starts
+
+        ksession.insert(new SensorActivated("s1"));
+        ksession.fireAllRules();                              // step-1 consumed; AND step now active
+
+        // Satisfy the first AND child only — AND step is now partially satisfied
+        ksession.insert(new HeartbeatOk("s1"));
+        ksession.fireAllRules();
+        assertThat(results).isEmpty();                        // still waiting for alarm
+
+        // Retract the anchor — stop() must deactivate BOTH AND children
+        ksession.retract(stationHandle);
+        ksession.fireAllRules();
+
+        // Insert the second AND child — the AND adapter must NOT respond
+        ksession.insert(new AlarmRaised("s1", "high"));
+        ksession.fireAllRules();
+
+        assertThat(results).isEmpty();
+    }
+
+    @Test
+    public void twoAnchorsOrStepOnlyOneAnchorSatisfied() {
+        Variable<MonitoringStation> stationV   = declarationOf(MonitoringStation.class);
+        Variable<SensorActivated>   activatedAV = declarationOf(SensorActivated.class);
+        Variable<SensorActivated>   activatedBV = declarationOf(SensorActivated.class);
+        Variable<HeartbeatOk>       heartbeatV  = declarationOf(HeartbeatOk.class);
+        Variable<AlarmRaised>       alarmV      = declarationOf(AlarmRaised.class);
+
+        // Rule anchored on MonitoringStation: sequence is SensorActivated → or(Heartbeat, Alarm)
+        Rule rule = rule("c5-rule").build(
+                pattern(stationV),
+                sequence(
+                        pattern(activatedAV).expr("s1-act", a -> a.getSensorId().equals("s1")),
+                        or(
+                                pattern(heartbeatV).expr("hb", h -> h.getSensorId().equals("s1")),
+                                pattern(alarmV).expr("al", al -> al.getSeverity().equals("high"))
+                        )
+                ),
+                execute(() -> results.add("seq-fired"))
+        );
+
+        KieBase kbase = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule));
+        ksession = kbase.newKieSession();
+
+        // Start two independent sequencers via two separate anchor tuples
+        ksession.insert(new MonitoringStation("station-A"));
+        ksession.insert(new MonitoringStation("station-B"));
+        ksession.fireAllRules();
+
+        // Both sequencers consume step-1 (SensorActivated s1); both are now at the OR step
+        ksession.insert(new SensorActivated("s1"));
+        ksession.fireAllRules();
+        assertThat(results).isEmpty();                        // OR step not yet satisfied for either
+
+        // OR step satisfied — both sequencers complete; each must fire exactly once
+        ksession.insert(new HeartbeatOk("s1"));
+        ksession.fireAllRules();
+
+        assertThat(results).hasSize(2);
+    }
+
+    @Test
+    public void twoSequencesInOneRuleBothComplete() {
+        Variable<Person>       personV = declarationOf(Person.class);
+        Variable<Toy>          toyV    = declarationOf(Toy.class);
+        Variable<Relationship> relV    = declarationOf(Relationship.class);
+
+        Rule rule = rule("c4-two-seq-rule").build(
+                pattern(personV),
+                sequence(
+                        pattern(toyV).expr("is-ball", t -> t.getName().equals("ball"))
+                ),
+                sequence(
+                        pattern(relV).expr("is-go", r -> r.getStart().equals("go"))
+                ),
+                execute(() -> results.add("fired"))
+        );
+
+        KieBase kbase = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule));
+        ksession = kbase.newKieSession();
+
+        ksession.insert(new Person("anchor"));
+        ksession.fireAllRules();                        // both sequencers start
+
+        ksession.insert(new Toy("ball"));
+        ksession.fireAllRules();                        // first sequence completes
+
+        // Both SequenceNodes have independent state. Completing the first sequence
+        // must not affect the second; the rule fires only after both complete.
+        ksession.insert(new Relationship("go", "done"));
+        ksession.fireAllRules();                        // second sequence completes
+
+        assertThat(results).containsExactly("fired");
+    }
+
+    @Test
+    public void twoRulesWithSequencesTrackIndependently() {
+        Variable<MonitoringStation> stationAV   = declarationOf(MonitoringStation.class);
+        Variable<MonitoringStation> stationBV   = declarationOf(MonitoringStation.class);
+        Variable<SensorActivated>   activated1V = declarationOf(SensorActivated.class);
+        Variable<SensorActivated>   activated2V = declarationOf(SensorActivated.class);
+        Variable<HeartbeatOk>       heartbeat1V = declarationOf(HeartbeatOk.class);
+        Variable<HeartbeatOk>       heartbeat2V = declarationOf(HeartbeatOk.class);
+
+        // Rule 1: station-A → sequence(sensorActivated(s1) → heartbeat(s1))
+        Rule rule1 = rule("c4-rule-1").build(
+                pattern(stationAV).expr("is-a", s -> s.getId().equals("station-A")),
+                sequence(
+                        pattern(activated1V).expr("s1-act", a -> a.getSensorId().equals("s1")),
+                        pattern(heartbeat1V).expr("s1-hb",  h -> h.getSensorId().equals("s1"))
+                ),
+                execute(() -> results.add("rule-1-fired"))
+        );
+
+        // Rule 2: station-B → sequence(sensorActivated(s2) → heartbeat(s2))
+        Rule rule2 = rule("c4-rule-2").build(
+                pattern(stationBV).expr("is-b", s -> s.getId().equals("station-B")),
+                sequence(
+                        pattern(activated2V).expr("s2-act", a -> a.getSensorId().equals("s2")),
+                        pattern(heartbeat2V).expr("s2-hb",  h -> h.getSensorId().equals("s2"))
+                ),
+                execute(() -> results.add("rule-2-fired"))
+        );
+
+        KieBase kbase = KieBaseBuilder.createKieBaseFromModel(
+                new ModelImpl().addRule(rule1).addRule(rule2));
+        ksession = kbase.newKieSession();
+
+        // Start both sequencers
+        ksession.insert(new MonitoringStation("station-A"));
+        ksession.insert(new MonitoringStation("station-B"));
+        ksession.fireAllRules();
+
+        // Complete rule-1's sequence
+        ksession.insert(new SensorActivated("s1"));
+        ksession.fireAllRules();
+        ksession.insert(new HeartbeatOk("s1"));
+        ksession.fireAllRules();
+
+        // Complete rule-2's sequence
+        ksession.insert(new SensorActivated("s2"));
+        ksession.fireAllRules();
+        ksession.insert(new HeartbeatOk("s2"));
+        ksession.fireAllRules();
+
+        // Both rules must have fired exactly once
+        assertThat(results).containsExactlyInAnyOrder("rule-1-fired", "rule-2-fired");
+    }
+
+    @Test
+    public void twoRulesWithSharedAnchorAndDifferentSequencesFireIndependently() {
+        // Both rules anchor on an undecorated pattern(person) — they will share the same
+        // LeftInputAdapterNode, which is the minimal condition to trigger the node-reuse bug.
+        Variable<Person>       personV1 = declarationOf(Person.class);
+        Variable<Person>       personV2 = declarationOf(Person.class);
+        Variable<Toy>          toyV1    = declarationOf(Toy.class);
+        Variable<Toy>          toyV2    = declarationOf(Toy.class);
+        Variable<Relationship> relV1    = declarationOf(Relationship.class);
+        Variable<Relationship> relV2    = declarationOf(Relationship.class);
+
+        // Rule 1: sequence(toy named "ball" → relationship starting "go")
+        Rule rule1 = rule("shared-anchor-seq-1").build(
+                pattern(personV1),
+                sequence(
+                        pattern(toyV1).expr("is-ball", t -> t.getName().equals("ball")),
+                        pattern(relV1).expr("is-go",   r -> r.getStart().equals("go"))
+                ),
+                execute(() -> results.add("rule-1-fired"))
+        );
+
+        // Rule 2: same anchor type, completely different step predicates.
+        // With the bug, this SequenceNode is discarded and rule-2 is wired to rule-1's
+        // Sequencer, so it never fires its own consequence.
+        Rule rule2 = rule("shared-anchor-seq-2").build(
+                pattern(personV2),
+                sequence(
+                        pattern(toyV2).expr("is-robot", t -> t.getName().equals("robot")),
+                        pattern(relV2).expr("is-stop",  r -> r.getStart().equals("stop"))
+                ),
+                execute(() -> results.add("rule-2-fired"))
+        );
+
+        KieBase kbase = KieBaseBuilder.createKieBaseFromModel(
+                new ModelImpl().addRule(rule1).addRule(rule2));
+        ksession = kbase.newKieSession();
+
+        // Insert the shared anchor — starts both sequencers
+        ksession.insert(new Person("anchor"));
+        ksession.fireAllRules();
+
+        // Complete rule-1's sequence only
+        ksession.insert(new Toy("ball"));
+        ksession.fireAllRules();
+        ksession.insert(new Relationship("go", "done"));
+        ksession.fireAllRules();
+
+        assertThat(results)
+                .as("rule-1 must fire after its own sequence completes")
+                .contains("rule-1-fired");
+        assertThat(results)
+                .as("rule-2 must not fire — its step predicates were not satisfied")
+                .doesNotContain("rule-2-fired");
+
+        // Now complete rule-2's sequence
+        ksession.insert(new Toy("robot"));
+        ksession.fireAllRules();
+        ksession.insert(new Relationship("stop", "done"));
+        ksession.fireAllRules();
+
+        assertThat(results)
+                .as("rule-2 must fire after its own sequence completes")
+                .containsExactlyInAnyOrder("rule-1-fired", "rule-2-fired");
+    }
+
+    @Test
+    public void betaExprInSequenceStepFiltersAgainstAnchor() {
+        Variable<Person> personV = declarationOf(Person.class);
+        Variable<Toy>    toyV    = declarationOf(Toy.class);
+
+        Rule rule = rule("beta-in-seq").build(
+                pattern(personV),
+                sequence(
+                        pattern(toyV).expr("name-matches-anchor", personV,
+                                (t, p) -> t.getName().equals(p.getName()))
+                ),
+                on(personV).execute(p -> results.add("fired:" + p.getName()))
+        );
+
+        KieBase kbase = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule));
+        ksession = kbase.newKieSession();
+
+        ksession.insert(new Person("alice"));
+        ksession.fireAllRules();
+
+        ksession.insert(new Toy("bob"));     // name does NOT match — must not fire
+        ksession.fireAllRules();
+        assertThat(results).isEmpty();
+
+        ksession.insert(new Toy("alice"));   // name matches — must fire
+        ksession.fireAllRules();
+        assertThat(results).containsExactly("fired:alice");
+    }
+
+    @Test
+    public void betaExprInSequenceStepOnlyMatchesOwnAnchor() {
+        Variable<Person> personV = declarationOf(Person.class);
+        Variable<Toy>    toyV    = declarationOf(Toy.class);
+
+        Rule rule = rule("beta-in-seq-multi-anchor").build(
+                pattern(personV),
+                sequence(
+                        pattern(toyV).expr("name-matches-anchor", personV,
+                                (t, p) -> t.getName().equals(p.getName()))
+                ),
+                on(personV).execute(p -> results.add("fired:" + p.getName()))
+        );
+
+        KieBase kbase = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule));
+        ksession = kbase.newKieSession();
+
+        ksession.insert(new Person("alice"));
+        ksession.insert(new Person("bob"));
+        ksession.fireAllRules();   // both sequencers start
+
+        ksession.insert(new Toy("alice"));   // only alice's sequencer should advance
+        ksession.fireAllRules();
+        assertThat(results).containsExactly("fired:alice");
+
+        ksession.insert(new Toy("bob"));     // only bob's sequencer should advance
+        ksession.fireAllRules();
+        assertThat(results).containsExactlyInAnyOrder("fired:alice", "fired:bob");
+    }
+
+    @Test
+    public void twoRulesWithIdenticalSequenceSegmentBothFire() {
+        // Two rules share the exact same anchor type and identical step predicates.
+        // When the sequence fires, smem.getPathMemories() contains two PathMemory entries
+        // (one per rule). SequencerMemoryImpl.match() flushes only get(0), so only
+        // rule-1's consequence is triggered — rule-2 is silently dropped.
+        Variable<Person>       personV1 = declarationOf(Person.class);
+        Variable<Person>       personV2 = declarationOf(Person.class);
+        Variable<Toy>          toyV1    = declarationOf(Toy.class);
+        Variable<Toy>          toyV2    = declarationOf(Toy.class);
+
+        Rule rule1 = rule("shared-seg-1").build(
+                pattern(personV1),
+                sequence(
+                        pattern(toyV1).expr("is-ball-1", t -> t.getName().equals("ball"))
+                ),
+                execute(() -> results.add("rule-1-fired"))
+        );
+
+        Rule rule2 = rule("shared-seg-2").build(
+                pattern(personV2),
+                sequence(
+                        pattern(toyV2).expr("is-ball-2", t -> t.getName().equals("ball"))
+                ),
+                execute(() -> results.add("rule-2-fired"))
+        );
+
+        KieBase kbase = KieBaseBuilder.createKieBaseFromModel(
+                new ModelImpl().addRule(rule1).addRule(rule2));
+        ksession = kbase.newKieSession();
+
+        ksession.insert(new Person("anchor"));
+        ksession.fireAllRules();           // both sequencers start
+
+        ksession.insert(new Toy("ball"));
+        ksession.fireAllRules();           // sequence completes — both rules must fire
+
+        assertThat(results)
+                .as("both rules whose sequences completed must fire")
+                .containsExactlyInAnyOrder("rule-1-fired", "rule-2-fired");
+    }
+
+    @Test
+    public void emptySequenceThrowsIllegalArgument() {
+        assertThatThrownBy(() -> sequence())
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("at least one step");
+    }
+
+    @Test
+    public void nullStepsThrowsIllegalArgument() {
+        assertThatThrownBy(() -> sequence((org.drools.model.SequenceStep[]) null))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("at least one step");
+    }
+}
diff --git a/drools-model/drools-model-compiler/src/test/java/org/drools/modelcompiler/PatternDSLSequenceLifecycleTest.java b/drools-model/drools-model-compiler/src/test/java/org/drools/modelcompiler/PatternDSLSequenceLifecycleTest.java
new file mode 100644
index 00000000000..3493fdb9d42
--- /dev/null
+++ b/drools-model/drools-model-compiler/src/test/java/org/drools/modelcompiler/PatternDSLSequenceLifecycleTest.java
@@ -0,0 +1,298 @@
+/*
+ * 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.modelcompiler;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.drools.model.Model;
+import org.drools.model.Rule;
+import org.drools.model.Variable;
+import org.drools.model.impl.ModelImpl;
+import org.drools.modelcompiler.domain.Person;
+import org.drools.modelcompiler.domain.Relationship;
+import org.drools.modelcompiler.domain.Toy;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.kie.api.KieBase;
+import org.kie.api.runtime.KieSession;
+import org.kie.api.runtime.rule.FactHandle;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.drools.model.DSL.declarationOf;
+import static org.drools.model.DSL.execute;
+import static org.drools.model.PatternDSL.pattern;
+import static org.drools.model.PatternDSL.rule;
+import static org.drools.model.PatternDSL.sequence;
+
+public class PatternDSLSequenceLifecycleTest {
+
+    private final Variable<Person>       person       = declarationOf(Person.class);
+    private final Variable<Toy>          toy          = declarationOf(Toy.class);
+    private final Variable<Relationship> relationship = declarationOf(Relationship.class);
+
+    private final List<String> results = new ArrayList<>();
+    private KieSession ksession;
+
+    @AfterEach
+    public void tearDown() {
+        results.clear();
+        if (ksession != null) {
+            ksession.dispose();
+            ksession = null;
+        }
+    }
+
+    @Test
+    public void retractAnchorStopsSequencer() {
+        ksession = makeKSession();
+
+        FactHandle anchorHandle = ksession.insert(new Person("anchor"));
+        ksession.fireAllRules();                    // activates sequencer
+
+        ksession.retract(anchorHandle);
+        ksession.fireAllRules();                    // stop() must be called
+
+        ksession.insert(new Toy("ball"));
+        ksession.fireAllRules();                    // step-1 — sequencer gone, must not fire
+
+        assertThat(results).isEmpty();
+    }
+
+    @Test
+    public void retractAnchorMidSequenceStopped() {
+        ksession = makeKSession();
+
+        FactHandle anchorHandle = ksession.insert(new Person("anchor"));
+        ksession.fireAllRules();
+
+        ksession.insert(new Toy("ball"));
+        ksession.fireAllRules();                    // step 1 consumed, step 2 waiting
+
+        ksession.retract(anchorHandle);
+        ksession.fireAllRules();
+
+        ksession.insert(new Relationship("go", "done"));
+        ksession.fireAllRules();                    // step-2 — sequencer stopped, must not fire
+
+        assertThat(results).isEmpty();
+    }
+
+    @Test
+    public void twoAnchorsConcurrentlyBothFire() {
+        ksession = makeKSession();
+
+        ksession.insert(new Person("anchor-A"));
+        ksession.insert(new Person("anchor-B"));
+        ksession.fireAllRules();                    // both sequencers start
+
+        ksession.insert(new Toy("ball"));
+        ksession.fireAllRules();                    // step-1 consumed by BOTH sequencers
+
+        ksession.insert(new Relationship("go", "done"));
+        ksession.fireAllRules();                    // step-2 consumed by BOTH sequencers
+
+        assertThat(results).hasSize(2);
+    }
+
+    @Test
+    public void twoAnchorsSingleStepSequenceBothFire() {
+        ksession = makeKSessionSingleStep();
+
+        ksession.insert(new Person("anchor-A"));
+        ksession.insert(new Person("anchor-B"));
+        ksession.fireAllRules();                    // both single-step sequencers start
+
+        ksession.insert(new Toy("ball"));
+        ksession.fireAllRules();
+
+        assertThat(results)
+                .as("each anchor's sequence must complete and fire the rule independently")
+                .hasSize(2);
+    }
+
+    @Test
+    public void sequenceRestartAfterUpdate() {
+        ksession = makeKSession();
+
+        Person anchor = new Person("anchor");
+        FactHandle anchorHandle = ksession.insert(anchor);
+        ksession.fireAllRules();
+
+        ksession.insert(new Toy("ball"));
+        ksession.fireAllRules();                    // step 1 consumed
+
+        ksession.update(anchorHandle, anchor);      // update restarts the sequence
+        ksession.fireAllRules();
+
+        ksession.insert(new Relationship("go", "done"));
+        ksession.fireAllRules();                    // step-2 without a new step-1 — must NOT fire
+
+        assertThat(results).isEmpty();
+    }
+
+    @Test
+    public void sequenceDoesNotFireAgainAfterCompletion() {
+        ksession = makeKSession();
+
+        ksession.insert(new Person("anchor"));
+        ksession.fireAllRules();
+
+        ksession.insert(new Toy("ball"));
+        ksession.fireAllRules();
+
+        ksession.insert(new Relationship("go", "done"));
+        ksession.fireAllRules();                    // sequence complete — fired once
+
+        assertThat(results).containsExactly("fired");
+
+        // A second step-1 event after completion must not restart the sequence
+        ksession.insert(new Toy("ball"));
+        ksession.fireAllRules();
+
+        assertThat(results).hasSize(1);             // still only one firing
+    }
+
+    @Test
+    public void sequenceRefirableAfterUpdateFollowingCompletion() {
+        ksession = makeKSession();
+
+        Person anchor = new Person("anchor");
+        FactHandle anchorHandle = ksession.insert(anchor);
+        ksession.fireAllRules();
+
+        // First run: complete the sequence
+        ksession.insert(new Toy("ball"));
+        ksession.fireAllRules();
+        ksession.insert(new Relationship("go", "done"));
+        ksession.fireAllRules();
+        assertThat(results).containsExactly("fired");   // fired once
+
+        // Restart: update resets the sequencer
+        ksession.update(anchorHandle, anchor);
+        ksession.fireAllRules();
+
+        // Second run: complete the sequence again
+        ksession.insert(new Toy("ball"));
+        ksession.fireAllRules();
+        ksession.insert(new Relationship("go", "done"));
+        ksession.fireAllRules();
+
+        assertThat(results).hasSize(2);                 // fired a second time
+    }
+
+    @Test
+    public void retractStepEventDoesNotUndoStep() {
+        ksession = makeKSession();
+
+        ksession.insert(new Person("anchor"));
+        ksession.fireAllRules();
+
+        FactHandle toyHandle = ksession.insert(new Toy("ball"));
+        ksession.fireAllRules();                    // step-1 consumed; sequence now at step-2
+
+        ksession.retract(toyHandle);               // retract the step-1 fact — no-op for the sequencer
+        ksession.fireAllRules();
+
+        ksession.insert(new Relationship("go", "done"));
+        ksession.fireAllRules();                    // step-2 arrives — sequence is still at step-2, fires
+
+        assertThat(results).containsExactly("fired");
+    }
+
+    @Test
+    public void updateStepEventDoesNotUndoStep() {
+        ksession = makeKSession();
+
+        ksession.insert(new Person("anchor"));
+        ksession.fireAllRules();
+
+        Toy toy = new Toy("ball");
+        FactHandle toyHandle = ksession.insert(toy);
+        ksession.fireAllRules();                    // step-1 consumed; sequence now at step-2
+
+        ksession.update(toyHandle, toy);            // update the step-1 fact — no-op for the sequencer
+        ksession.fireAllRules();
+
+        ksession.insert(new Relationship("go", "done"));
+        ksession.fireAllRules();                    // step-2 arrives — sequence fires
+
+        assertThat(results).containsExactly("fired");
+    }
+
+    @Test
+    public void removedRuleNoLongerReceivesStepEvents() {
+        Variable<Person> personV = declarationOf(Person.class);
+        Variable<Toy>    toyV    = declarationOf(Toy.class);
+
+        Rule rule = rule("seq-remove-rule").build(
+                pattern(personV),
+                sequence(
+                        pattern(toyV).expr("is-ball", t -> t.getName().equals("ball"))
+                ),
+                execute(() -> results.add("fired"))
+        );
+
+        Model model = new ModelImpl().addRule(rule);
+        KieBase kieBase = KieBaseBuilder.createKieBaseFromModel(model);
+        ksession = kieBase.newKieSession();
+
+        // Start the sequencer
+        ksession.insert(new Person("anchor"));
+        ksession.fireAllRules();
+
+        // Remove the rule from the KieBase while the session is active
+        kieBase.removeRule("defaultpkg", "seq-remove-rule");
+
+        // Insert the step event — the removed rule must NOT fire
+        ksession.insert(new Toy("ball"));
+        ksession.fireAllRules();
+
+        assertThat(results)
+                .as("no rule should fire after it has been removed from the KieBase")
+                .isEmpty();
+    }
+
+    private KieSession makeKSession() {
+        Rule rule = rule("lifecycle-rule").build(
+                pattern(person),
+                sequence(
+                        pattern(toy).expr("is-ball", t -> t.getName().equals("ball")),
+                        pattern(relationship).expr("is-go", r -> r.getStart().equals("go"))
+                ),
+                execute(() -> results.add("fired"))
+        );
+        Model model = new ModelImpl().addRule(rule);
+        KieBase kieBase = KieBaseBuilder.createKieBaseFromModel(model);
+        return kieBase.newKieSession();
+    }
+
+    private KieSession makeKSessionSingleStep() {
+        Rule rule = rule("lifecycle-single-step").build(
+                pattern(person),
+                sequence(
+                        pattern(toy).expr("is-ball", t -> t.getName().equals("ball"))
+                ),
+                execute(() -> results.add("fired"))
+        );
+        Model model = new ModelImpl().addRule(rule);
+        KieBase kieBase = KieBaseBuilder.createKieBaseFromModel(model);
+        return kieBase.newKieSession();
+    }
+}
diff --git a/drools-model/drools-model-compiler/src/test/java/org/drools/modelcompiler/PatternDSLSequenceTest.java b/drools-model/drools-model-compiler/src/test/java/org/drools/modelcompiler/PatternDSLSequenceTest.java
new file mode 100644
index 00000000000..dae95a6afab
--- /dev/null
+++ b/drools-model/drools-model-compiler/src/test/java/org/drools/modelcompiler/PatternDSLSequenceTest.java
@@ -0,0 +1,157 @@
+/*
+ * 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.modelcompiler;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.drools.model.Model;
+import org.drools.model.Rule;
+import org.drools.model.Variable;
+import org.drools.model.impl.ModelImpl;
+import org.drools.modelcompiler.domain.Person;
+import org.drools.modelcompiler.domain.Relationship;
+import org.drools.modelcompiler.domain.Toy;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.kie.api.KieBase;
+import org.kie.api.runtime.KieSession;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.drools.model.DSL.declarationOf;
+import static org.drools.model.DSL.execute;
+import static org.drools.model.PatternDSL.pattern;
+import static org.drools.model.PatternDSL.rule;
+import static org.drools.model.PatternDSL.sequence;
+
+public class PatternDSLSequenceTest {
+
+    private final Variable<Person>       person = declarationOf(Person.class);
+    private final Variable<Toy>          toy = declarationOf(Toy.class);
+    private final Variable<Relationship> relationship = declarationOf(Relationship.class);
+    private final List<String>           results = new ArrayList<>();
+    private KieSession                   ksession;
+
+    private final Rule rule =
+            rule("seq-rule").build(
+                pattern(person),
+                sequence(
+                    pattern(toy).expr("toy-filter",
+                            t -> t.getName().equals("ball")),
+                    pattern(relationship).expr("rel-filter",
+                            r -> r.getStart().equals("go"))
+                ),
+                execute(() -> results.add("fired"))
+    );
+
+    @Test
+    public void sequenceFiresWhenToyThenRelationship() {
+        ksession = makeKSession();
+
+        // LHS anchor — activates the sequencer
+        insertAndFire(
+           new Person("anchor")
+        );
+
+        // Step 1, then step 2 — rule should fire
+        insertAndFire(
+           new Toy("ball"),
+           new Relationship("go", "done")
+        );
+
+        assertThat(results).containsExactly("fired");
+    }
+
+    @Test
+    public void sequenceFiresWithSeparateInserts() {
+        ksession = makeKSession();
+
+        // LHS anchor — activates the sequencer
+        insertAndFire(
+                new Person("anchor")
+        );
+
+        // Step 1, then fireAll() — rule should NOT fire
+        insertAndFire(
+                new Toy("ball")
+        );
+
+        assertThat(results).isEmpty();
+
+        // Then step 2 — rule should fire
+        insertAndFire(
+                new Relationship("go", "done")
+        );
+
+        assertThat(results).containsExactly("fired");
+    }
+
+    @Test
+    public void sequenceDoesNotFireWithoutCorrectOrder() {
+        ksession = makeKSession();
+
+        // LHS anchor — activates the sequencer
+        insertAndFire(
+           new Person("anchor")
+        );
+
+        // Step 1, then step 2 — sequence expects Toy then Relationship. We provide Relationship then Toy
+        insertAndFire(
+            new Relationship("go", "done"),
+            new Toy("ball"));
+
+        assertThat(results).isEmpty();
+    }
+
+    @Test
+    public void sequenceDoesNotFireWithoutToy() {
+        ksession = makeKSession();
+
+        insertAndFire(
+            new Person("anchor")
+        );
+
+        // Step 2 arrives without step 1 — rule must NOT fire
+        insertAndFire(
+            new Relationship("go", "done")
+        );
+
+        assertThat(results).isEmpty();
+    }
+
+    @AfterEach
+    public void tearDown() {
+        results.clear();
+        ksession.dispose();
+    }
+
+    private void insertAndFire(Object... facts) {
+        for (Object fact : facts) {
+            ksession.insert(fact);
+        }
+        ksession.fireAllRules();
+    }
+
+    private KieSession makeKSession() {
+        final Model model = new ModelImpl().addRule(rule);
+        final KieBase kieBase = KieBaseBuilder.createKieBaseFromModel(model);
+        return kieBase.newKieSession();
+    }
+
+}
diff --git a/drools-model/drools-model-compiler/src/test/java/org/drools/modelcompiler/SequenceReviewProbeTest.java b/drools-model/drools-model-compiler/src/test/java/org/drools/modelcompiler/SequenceReviewProbeTest.java
new file mode 100644
index 00000000000..091f2a6b6b6
--- /dev/null
+++ b/drools-model/drools-model-compiler/src/test/java/org/drools/modelcompiler/SequenceReviewProbeTest.java
@@ -0,0 +1,288 @@
+/*
+ * 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.modelcompiler;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.drools.kiesession.rulebase.InternalKnowledgeBase;
+import org.drools.model.Model;
+import org.drools.model.Rule;
+import org.drools.model.Variable;
+import org.drools.model.impl.ModelImpl;
+import org.drools.modelcompiler.domain.Person;
+import org.drools.modelcompiler.domain.Toy;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.kie.api.KieBase;
+import org.kie.api.event.rule.DefaultAgendaEventListener;
+import org.kie.api.event.rule.MatchCreatedEvent;
+import org.kie.api.runtime.KieSession;
+import org.kie.api.runtime.KieSessionsPool;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.drools.model.DSL.declarationOf;
+import static org.drools.model.DSL.execute;
+import static org.drools.model.PatternDSL.pattern;
+import static org.drools.model.PatternDSL.rule;
+import static org.drools.model.PatternDSL.sequence;
+
+public class SequenceReviewProbeTest {
+
+    private final List<String> results = new ArrayList<>();
+    private KieSession ksession;
+
+    @AfterEach
+    public void tearDown() {
+        results.clear();
+        if (ksession != null) {
+            ksession.dispose();
+        }
+    }
+
+    @Test
+    public void plainRuleAndSequenceRuleShareAnEventType() {
+        Variable<Toy> toy = declarationOf(Toy.class);
+        Variable<Person> person = declarationOf(Person.class);
+
+        Rule plainRule = rule("plain-rule").build(
+                pattern(toy).expr("is-ball", t -> t.getName().equals("ball")),
+                execute(() -> results.add("plain:ball"))
+        );
+
+        Rule seqRule = rule("seq-rule").build(
+                pattern(person),
+                sequence(
+                        pattern(toy).expr("seq-is-ball", t -> t.getName().equals("ball"))
+                ),
+                execute(() -> results.add("seq:ball"))
+        );
+
+        Model model = new ModelImpl().addRule(plainRule).addRule(seqRule);
+        KieBase kieBase = KieBaseBuilder.createKieBaseFromModel(model);
+        ksession = kieBase.newKieSession();
+
+        ksession.insert(new Person("anchor"));
+        ksession.fireAllRules();
+
+        ksession.insert(new Toy("ball"));
+        ksession.fireAllRules();
+
+        // Both the plain rule and the sequence rule must fire.
+        assertThat(results).contains("plain:ball");
+        assertThat(results).contains("seq:ball");
+    }
+
+    @Test
+    public void sameTypeInTwoSteps() {
+        Variable<Toy>    toy    = declarationOf(Toy.class);
+        Variable<Person> person = declarationOf(Person.class);
+
+        Rule rule = rule("ball-then-doll").build(
+                pattern(person),
+                sequence(
+                        pattern(toy).expr("is-ball", t -> t.getName().equals("ball")),
+                        pattern(toy).expr("is-doll", t -> t.getName().equals("doll"))
+                ),
+                execute(() -> results.add("fired"))
+        );
+
+        ksession = KieBaseBuilder.createKieBaseFromModel(
+                new org.drools.model.impl.ModelImpl().addRule(rule))
+                .newKieSession();
+
+        ksession.insert(new Person("anchor"));
+        ksession.fireAllRules();
+
+        ksession.insert(new Toy("ball"));
+        ksession.fireAllRules();
+
+        ksession.insert(new Toy("doll"));
+        ksession.fireAllRules();
+
+        assertThat(results).containsExactly("fired");
+    }
+
+    @Test
+    public void stepWithTwoConstraintsIsUnsatisfiable() {
+        Variable<Toy>    toy    = declarationOf(Toy.class);
+        Variable<Person> person = declarationOf(Person.class);
+
+        // A step that requires name == "ball" AND name == "doll" simultaneously —
+        // logically impossible, must never match any real Toy.
+        Rule rule = rule("impossible-step").build(
+                pattern(person),
+                sequence(
+                        pattern(toy)
+                                .expr("is-ball", t -> t.getName().equals("ball"))
+                                .expr("is-doll", t -> t.getName().equals("doll"))
+                ),
+                execute(() -> results.add("fired"))
+        );
+
+        ksession = KieBaseBuilder.createKieBaseFromModel(
+                new org.drools.model.impl.ModelImpl().addRule(rule))
+                .newKieSession();
+
+        ksession.insert(new Person("anchor"));
+        ksession.fireAllRules();
+
+        // A Toy("ball") satisfies "is-ball" but not "is-doll" — must NOT fire.
+        ksession.insert(new Toy("ball"));
+        ksession.fireAllRules();
+
+        assertThat(results).isEmpty();
+    }
+
+    @Test
+    public void stepWithTwoConstraintsBothSatisfiedFires() {
+        Variable<Toy>    toy    = declarationOf(Toy.class);
+        Variable<Person> person = declarationOf(Person.class);
+
+        // A step that requires name starts with "t" AND length > 3.
+        // Toy("toy-car") satisfies both — CombinedAlphaConstraint.isAllowed()
+        // must return true (the all-pass branch at the end of the loop).
+        Rule rule = rule("two-constraint-step").build(
+                pattern(person),
+                sequence(
+                        pattern(toy)
+                                .expr("starts-with-t", t -> t.getName().startsWith("t"))
+                                .expr("longer-than-3",  t -> t.getName().length() > 3)
+                ),
+                execute(() -> results.add("fired"))
+        );
+
+        ksession = KieBaseBuilder.createKieBaseFromModel(
+                new org.drools.model.impl.ModelImpl().addRule(rule))
+                .newKieSession();
+
+        ksession.insert(new Person("anchor"));
+        ksession.fireAllRules();
+
+        // Toy("toy-car"): starts with "t" ✓, length 7 > 3 ✓ — both constraints pass.
+        ksession.insert(new Toy("toy-car"));
+        ksession.fireAllRules();
+
+        assertThat(results).containsExactly("fired");
+    }
+
+    @Test
+    public void joinAfterSequenceHasCorrectTupleWidth() {
+        Variable<Person> anchorV   = declarationOf(Person.class);
+        Variable<Toy>    toyV      = declarationOf(Toy.class);
+        Variable<Person> observerV = declarationOf(Person.class);
+
+        Rule rule = rule("join-after-sequence").build(
+                pattern(anchorV).expr("is-anchor",   p -> p.getName().equals("anchor")),
+                sequence(
+                        pattern(toyV).expr("is-ball", t -> t.getName().equals("ball"))
+                ),
+                pattern(observerV).expr("is-observer", p -> p.getName().equals("observer")),
+                execute(() -> results.add("fired"))
+        );
+
+        KieBase kbase = KieBaseBuilder.createKieBaseFromModel(new ModelImpl().addRule(rule));
+        ksession = kbase.newKieSession();
+
+        AtomicInteger capturedHandleCount = new AtomicInteger(-1);
+        ksession.addEventListener(new DefaultAgendaEventListener() {
+            @Override
+            public void matchCreated(MatchCreatedEvent event) {
+                capturedHandleCount.set(event.getMatch().getFactHandles().size());
+            }
+        });
+
+        ksession.insert(new Person("anchor"));
+        ksession.insert(new Person("observer"));
+        ksession.fireAllRules();
+
+        ksession.insert(new Toy("ball"));
+        ksession.fireAllRules();
+
+        assertThat(results).containsExactly("fired");
+        assertThat(capturedHandleCount.get())
+                .as("match must contain both anchor and observer fact handles")
+                .isEqualTo(2);
+    }
+
+    @Test
+    public void stepWithNoConstraintThrowsDescriptiveError() {
+        Variable<Toy>    toy    = declarationOf(Toy.class);
+        Variable<Person> person = declarationOf(Person.class);
+
+        Rule rule = rule("unconstrained-step").build(
+                pattern(person),
+                sequence(
+                        pattern(toy)
+                ),
+                execute(() -> results.add("fired"))
+        );
+
+        assertThat(org.assertj.core.api.Assertions.catchThrowableOfType(() ->
+                KieBaseBuilder.createKieBaseFromModel(
+                        new org.drools.model.impl.ModelImpl().addRule(rule)),
+                Exception.class))
+                .as("step with no constraint must throw a descriptive build error")
+                .isNotNull()
+                .isNotInstanceOf(IndexOutOfBoundsException.class)
+                .hasMessageContaining("sequence step");
+    }
+
+    @Test
+    public void pooledSessionReuseShouldNotLeakSequenceState() {
+        Variable<Toy>    toy    = declarationOf(Toy.class);
+        Variable<Person> person = declarationOf(Person.class);
+
+        Rule rule = rule("seq-rule").build(
+                pattern(person),
+                sequence(
+                        pattern(toy).expr("is-ball", t -> t.getName().equals("ball"))
+                ),
+                execute(() -> results.add("fired"))
+        );
+
+        InternalKnowledgeBase kbase = KieBaseBuilder.createKieBaseFromModel(
+                new ModelImpl().addRule(rule));
+        KieSessionsPool pool = kbase.newKieSessionsPool(1);
+
+        KieSession s1 = pool.newKieSession();
+        s1.insert(new Person("anchor"));
+        s1.fireAllRules();
+        // Do NOT complete the sequence — dispose mid-flight.
+        s1.dispose();
+
+        KieSession s2 = pool.newKieSession();
+        assertThat(s2).as("pool of 1 must return the same physical session").isSameAs(s1);
+
+        s2.insert(new Person("anchor"));
+        s2.fireAllRules();
+        s2.insert(new Toy("ball"));
+        s2.fireAllRules();
+        int secondFires = results.size();
+
+        pool.shutdown();
+
+        assertThat(secondFires)
+                .as("sequence must fire exactly once in session #2 — " +
+                    "stale DynamicFilter/SignalAdapter state from session #1 must not cause extra or missing fires")
+                .isEqualTo(1);
+    }
+
+}
diff --git a/drools-model/drools-model-compiler/src/test/java/org/drools/modelcompiler/domain/SensorEvents.java b/drools-model/drools-model-compiler/src/test/java/org/drools/modelcompiler/domain/SensorEvents.java
new file mode 100644
index 00000000000..cac2b2dfa18
--- /dev/null
+++ b/drools-model/drools-model-compiler/src/test/java/org/drools/modelcompiler/domain/SensorEvents.java
@@ -0,0 +1,120 @@
+/*
+ * 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.modelcompiler.domain;
+
+public final class SensorEvents {
+
+    private SensorEvents() { }
+
+    public static final class MonitoringStation {
+        private final String id;
+
+        public MonitoringStation(String id) {
+            this.id = id;
+        }
+
+        public String getId() {
+            return id;
+        }
+    }
+
+    public static final class SensorActivated {
+        private final String sensorId;
+
+        public SensorActivated(String sensorId) {
+            this.sensorId = sensorId;
+        }
+
+        public String getSensorId() {
+            return sensorId;
+        }
+    }
+
+    public static final class HeartbeatOk {
+        private final String sensorId;
+
+        public HeartbeatOk(String sensorId) {
+            this.sensorId = sensorId;
+        }
+
+        public String getSensorId() {
+            return sensorId;
+        }
+    }
+
+    public static final class AlarmRaised {
+        private final String sensorId;
+        private final String severity;
+
+        public AlarmRaised(String sensorId, String severity) {
+            this.sensorId = sensorId;
+            this.severity = severity;
+        }
+
+        public String getSensorId() {
+            return sensorId;
+        }
+
+        public String getSeverity() {
+            return severity;
+        }
+    }
+
+    public static final class CalibrationPassed {
+        private final String sensorId;
+
+        public CalibrationPassed(String sensorId) {
+            this.sensorId = sensorId;
+        }
+
+        public String getSensorId() {
+            return sensorId;
+        }
+    }
+
+    public static final class MaintenanceScheduled {
+        private final String sensorId;
+
+        public MaintenanceScheduled(String sensorId) {
+            this.sensorId = sensorId;
+        }
+
+        public String getSensorId() {
+            return sensorId;
+        }
+    }
+
+    public static final class OperatorAcknowledged {
+        private final String sensorId;
+        private final String operator;
+
+        public OperatorAcknowledged(String sensorId, String operator) {
+            this.sensorId = sensorId;
+            this.operator = operator;
+        }
+
+        public String getSensorId() {
+            return sensorId;
+        }
+
+        public String getOperator() {
+            return operator;
+        }
+    }
+}
diff --git a/drools-ruleunits/drools-ruleunits-impl/src/main/java/org/drools/ruleunits/impl/sessions/RuleUnitExecutorImpl.java b/drools-ruleunits/drools-ruleunits-impl/src/main/java/org/drools/ruleunits/impl/sessions/RuleUnitExecutorImpl.java
index 72308a32138..47c8d9f7ec7 100644
--- a/drools-ruleunits/drools-ruleunits-impl/src/main/java/org/drools/ruleunits/impl/sessions/RuleUnitExecutorImpl.java
+++ b/drools-ruleunits/drools-ruleunits-impl/src/main/java/org/drools/ruleunits/impl/sessions/RuleUnitExecutorImpl.java
@@ -22,6 +22,7 @@ import org.drools.base.beliefsystem.Mode;
 import org.drools.base.definitions.rule.impl.RuleImpl;
 import org.drools.base.factmodel.traits.Thing;
 import org.drools.base.factmodel.traits.TraitableBean;
+import org.drools.base.phreak.PropagationEntry;
 import org.drools.base.rule.Declaration;
 import org.drools.base.rule.accessor.GlobalResolver;
 import org.drools.core.EntryPointsManager;
@@ -53,7 +54,6 @@ import org.drools.core.event.RuleEventListenerSupport;
 import org.drools.core.event.RuleRuntimeEventSupport;
 import org.drools.core.impl.ActivationsManagerImpl;
 import org.drools.core.impl.InternalRuleBase;
-import org.drools.base.phreak.PropagationEntry;
 import org.drools.core.phreak.actions.ExecuteQuery;
 import org.drools.core.phreak.RuleNetworkEvaluator;
 import org.drools.core.phreak.RuleNetworkEvaluatorImpl;
diff --git a/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/AbstractPhreakSequencerSubsequenceTest.java b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/AbstractPhreakSequencerSubsequenceTest.java
new file mode 100644
index 00000000000..e234210ddd7
--- /dev/null
+++ b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/AbstractPhreakSequencerSubsequenceTest.java
@@ -0,0 +1,183 @@
+/*
+ * 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.mvel.integrationtests.phreak.sequencing;
+
+import org.drools.base.base.ClassObjectType;
+import org.drools.base.base.ObjectType;
+import org.drools.base.base.ValueResolver;
+import org.drools.base.definitions.InternalKnowledgePackage;
+import org.drools.base.definitions.rule.impl.RuleImpl;
+import org.drools.base.reteoo.sequencing.Sequence.SequenceMemory;
+import org.drools.base.rule.EntryPointId;
+import org.drools.base.rule.Pattern;
+import org.drools.base.rule.consequence.Consequence;
+import org.drools.base.rule.consequence.ConsequenceContext;
+import org.drools.core.ClockType;
+import org.drools.core.SessionConfiguration;
+import org.drools.core.common.InternalFactHandle;
+import org.drools.core.impl.KnowledgeBaseImpl;
+import org.drools.core.impl.RuleBaseFactory;
+import org.drools.core.reteoo.CoreComponentFactory;
+import org.drools.core.reteoo.ObjectTypeNode;
+import org.drools.core.reteoo.PathMemory;
+import org.drools.core.reteoo.SequenceNode;
+import org.drools.core.reteoo.SequenceNode.SequenceNodeMemory;
+import org.drools.core.reteoo.builder.BuildContext;
+import org.drools.base.reteoo.sequencing.Sequence;
+import org.drools.base.reteoo.sequencing.SequencerMemory;
+import org.drools.core.rule.JavaDialectRuntimeData;
+import org.drools.kiesession.rulebase.SessionsAwareKnowledgeBase;
+import org.drools.kiesession.session.StatefulKnowledgeSessionImpl;
+import org.drools.mvel.integrationtests.phreak.A;
+import org.kie.api.conf.EventProcessingOption;
+import org.kie.api.runtime.conf.ThreadSafeOption;
+import org.kie.internal.conf.CompositeBaseConfiguration;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class AbstractPhreakSequencerSubsequenceTest {
+    StatefulKnowledgeSessionImpl session;
+    SequenceNodeMemory nodeMemory;
+    PathMemory         pmem;
+    SequencerMemory    sequencerMemory;
+    InternalFactHandle fhA0;
+    BuildContext       buildContext;
+    Sequence           seq0;
+    SequenceNode       snode;
+
+    Pattern bpattern;
+    Pattern cpattern;
+    Pattern dpattern;
+    Pattern epattern;
+
+    RuleImpl                   rule;
+    InternalKnowledgePackage   pkg;
+    SessionsAwareKnowledgeBase kbase;
+
+    public void initKBaseWithEmptyRule() {
+        CompositeBaseConfiguration conf = (CompositeBaseConfiguration) RuleBaseFactory.newKnowledgeBaseConfiguration();
+        conf.setOption(EventProcessingOption.STREAM);
+
+        KnowledgeBaseImpl rbase = new KnowledgeBaseImpl("ID", conf );
+
+        kbase = new SessionsAwareKnowledgeBase(rbase);
+
+        rule = new RuleImpl("rule1").setPackage("org.pkg1");
+        rule.setConsequence(new Consequence() {
+            @Override
+            public String getName() {
+                return "r1";
+            }
+
+            @Override
+            public void evaluate(ConsequenceContext knowledgeHelper, ValueResolver valueResolver) throws Exception {
+            }
+        });
+        pkg  = CoreComponentFactory.get().createKnowledgePackage("org.pkg1");
+        pkg.getDialectRuntimeRegistry().setDialectData( "java", new JavaDialectRuntimeData());
+        pkg.addRule( rule );
+
+        final ObjectType aObjectType = new ClassObjectType(A.class);
+        final ObjectType bObjectType = new ClassObjectType(BEvent.class);
+        final ObjectType cObjectType = new ClassObjectType(CEvent.class);
+        final ObjectType dObjectType = new ClassObjectType(DEvent.class);
+        final ObjectType eObjectType = new ClassObjectType(EEvent.class);
+
+        Pattern aPattern = new Pattern(0, aObjectType);
+        rule.addPattern(aPattern);
+
+        bpattern = new Pattern(0, bObjectType, "b" );
+        bpattern.addConstraint(new AlphaConstraint((Predicate1<BEvent>) b -> b.getText().equals("b")));
+
+        cpattern = new Pattern(0, cObjectType, "c" );
+        cpattern.addConstraint(new AlphaConstraint( (Predicate1<CEvent>) c -> c.getText().equals("c")));
+
+        dpattern = new Pattern(0, dObjectType, "d" );
+        dpattern.addConstraint(new AlphaConstraint( (Predicate1<DEvent>) d -> d.getText().equals("d")));
+
+        epattern = new Pattern(0, eObjectType, "e" );
+        epattern.addConstraint(new AlphaConstraint( (Predicate1<EEvent>) e -> e.getText().equals("e")));
+    }
+
+    public static BuildContext createContext() {
+
+        CompositeBaseConfiguration conf = (CompositeBaseConfiguration) RuleBaseFactory.newKnowledgeBaseConfiguration();
+
+        KnowledgeBaseImpl rbase = new KnowledgeBaseImpl("ID",
+                                                        conf );
+        BuildContext buildContext = new BuildContext(rbase, Collections.emptyList() );
+
+        RuleImpl                 rule = new RuleImpl("rule1").setPackage("org.pkg1");
+        InternalKnowledgePackage pkg  = CoreComponentFactory.get().createKnowledgePackage("org.pkg1");
+        pkg.getDialectRuntimeRegistry().setDialectData( "java", new JavaDialectRuntimeData());
+
+        pkg.addRule( rule );
+        buildContext.setRule( rule );
+
+        return buildContext;
+    }
+
+    void createSession() {
+        SessionConfiguration sessionConf = kbase.getSessionConfiguration();
+        sessionConf.setOption(ThreadSafeOption.NO);
+        sessionConf.setClockType(ClockType.PSEUDO_CLOCK);
+
+        if (snode == null) {
+            ObjectTypeNode aNode = kbase.getRete().getEntryPointNode(EntryPointId.DEFAULT).getObjectTypeNodes().get(new ClassObjectType(A.class));
+            snode = (SequenceNode) aNode.getSinks()[0].getSinks()[0];
+        }
+
+        if (session != null) {
+            nodeMemory      = null;
+            sequencerMemory = null;
+            session.dispose();
+            session = null;
+        }
+
+        session = (StatefulKnowledgeSessionImpl) kbase.newKieSession(sessionConf, null);
+
+        fhA0 = (InternalFactHandle) session.insert(new A(0));
+        session.fireAllRules();
+        nodeMemory = session.getNodeMemory(snode);
+        pmem = nodeMemory.getSegmentMemory().getPathMemories().get(0);
+        sequencerMemory = (SequencerMemory) fhA0.getFirstLeftTuple().getContextObject();
+    }
+
+    public int getCurrentStep(SequencerMemory sqncrMemory) {
+        SequenceMemory sqncMemory = sqncrMemory.getChildSequenceMemory();
+
+        List<SequenceMemory> leafSequences = new ArrayList<>();
+        getLeafSequences(sqncrMemory, sqncMemory, leafSequences);
+
+        if (leafSequences.isEmpty()) {
+            return -1;
+        }
+        return leafSequences.get(0).getStep();
+    }
+
+    public void getLeafSequences(SequencerMemory sqncrMemory, SequenceMemory sqncMemory, List<SequenceMemory> leafSequences) {
+        Sequence sqnc = sqncMemory.getSequence();
+        if (sqncMemory.getStep() >= sqnc.getSteps().length ) {
+            return;
+        }
+        leafSequences.add(sqncMemory);
+    }
+}
diff --git a/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/AlphaConstraint.java b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/AlphaConstraint.java
new file mode 100644
index 00000000000..7139a937064
--- /dev/null
+++ b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/AlphaConstraint.java
@@ -0,0 +1,73 @@
+/*
+ * 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.mvel.integrationtests.phreak.sequencing;
+
+import org.drools.base.base.ValueResolver;
+import org.drools.base.reteoo.BaseTuple;
+import org.drools.base.rule.Declaration;
+import org.drools.base.rule.MutableTypeConstraint;
+import org.kie.api.runtime.rule.FactHandle;
+
+public class AlphaConstraint extends MutableTypeConstraint {
+    private Predicate1 predicate1;
+
+    public AlphaConstraint(Predicate1 predicate1) {
+        this.predicate1 = predicate1;
+    }
+
+    @Override
+    public Declaration[] getRequiredDeclarations() {
+        return new Declaration[0];
+    }
+
+    @Override
+    public void replaceDeclaration(Declaration oldDecl, Declaration newDecl) {
+
+    }
+
+    @Override
+    public MutableTypeConstraint clone() {
+        return new AlphaConstraint(predicate1);
+    }
+
+    @Override
+    public boolean isTemporal() {
+        return false;
+    }
+
+    @Override
+    public boolean isAllowed(FactHandle handle, ValueResolver valueResolver) {
+        return predicate1.test(handle.getObject());
+    }
+
+    @Override
+    public boolean isAllowedCachedLeft(Object context, FactHandle handle) {
+        throw new UnsupportedOperationException("AlphaConstraint does not support cached context evaluation");
+    }
+
+    @Override
+    public boolean isAllowedCachedRight(BaseTuple tuple, Object context) {
+        throw new UnsupportedOperationException("AlphaConstraint does not support cached context evaluation");
+    }
+
+    @Override
+    public Object createContext() {
+        throw new UnsupportedOperationException("AlphaConstraint does not support cached context evaluation");
+    }
+}
diff --git a/drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/BEvent.java
similarity index 55%
copy from drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java
copy to drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/BEvent.java
index d74396d1070..49aede24dec 100644
--- a/drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java
+++ b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/BEvent.java
@@ -16,33 +16,30 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.drools.model.view;
+package org.drools.mvel.integrationtests.phreak.sequencing;
 
-import org.drools.model.Condition;
-import org.drools.model.Variable;
+import org.drools.mvel.integrationtests.phreak.B;
+import org.kie.api.definition.type.Position;
 
-public class ExistentialExprViewItem<T> extends AbstractExprViewItem<T> {
+public class BEvent extends B {
 
-    private final Condition.Type type;
-    private final ViewItem expression;
+    @Position(1)
+    private String text;
 
-    public ExistentialExprViewItem(Condition.Type type, ViewItem expression) {
-        super(expression.getFirstVariable());
-        this.type = type;
-        this.expression = expression;
+    public BEvent(Object object) {
+        super(object);
     }
 
-    public ViewItem getExpression() {
-        return expression;
+    public BEvent(Object object, String text) {
+        super(object);
+        this.text = text;
     }
 
-    @Override
-    public Variable<?>[] getVariables() {
-        return expression.getVariables();
+    public String getText() {
+        return text;
     }
 
-    @Override
-    public Condition.Type getType() {
-        return type;
+    public void setText(String text) {
+        this.text = text;
     }
 }
diff --git a/drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/CEvent.java
similarity index 55%
copy from drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java
copy to drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/CEvent.java
index d74396d1070..96b46df04d7 100644
--- a/drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java
+++ b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/CEvent.java
@@ -16,33 +16,30 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.drools.model.view;
+package org.drools.mvel.integrationtests.phreak.sequencing;
 
-import org.drools.model.Condition;
-import org.drools.model.Variable;
+import org.drools.mvel.integrationtests.phreak.C;
+import org.kie.api.definition.type.Position;
 
-public class ExistentialExprViewItem<T> extends AbstractExprViewItem<T> {
+public class CEvent extends C {
 
-    private final Condition.Type type;
-    private final ViewItem expression;
+    @Position(1)
+    private String text;
 
-    public ExistentialExprViewItem(Condition.Type type, ViewItem expression) {
-        super(expression.getFirstVariable());
-        this.type = type;
-        this.expression = expression;
+    public CEvent(Object object) {
+        super(object);
     }
 
-    public ViewItem getExpression() {
-        return expression;
+    public CEvent(Object object, String text) {
+        super(object);
+        this.text = text;
     }
 
-    @Override
-    public Variable<?>[] getVariables() {
-        return expression.getVariables();
+    public String getText() {
+        return text;
     }
 
-    @Override
-    public Condition.Type getType() {
-        return type;
+    public void setText(String text) {
+        this.text = text;
     }
 }
diff --git a/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/CircularArrayListTest.java b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/CircularArrayListTest.java
new file mode 100644
index 00000000000..4e6d1bb46cc
--- /dev/null
+++ b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/CircularArrayListTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.mvel.integrationtests.phreak.sequencing;
+
+import org.drools.base.util.CircularArrayList;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+public class CircularArrayListTest {
+
+    @Test
+    public void testAddingWithWrap() {
+        CircularArrayList<Integer> list = new CircularArrayList<>(10);
+
+        for (int i = 0; i < 15; i++) {
+            list.add(i);
+        }
+
+        assertThat(list.size()).isEqualTo(15);
+        assertThat(list.get(list.size()-1)).isEqualTo(14);
+        assertThat(list.get(14)).isEqualTo(14);
+        assertThat(list.getHead()).isEqualTo(14);
+        assertThat(list.getHeadMinus(3)).isEqualTo(11);
+
+        assertThatThrownBy(() -> list.get(-1)).isInstanceOf(IndexOutOfBoundsException.class);
+        assertThatThrownBy(() -> list.get(0)).isInstanceOf(IndexOutOfBoundsException.class);
+        assertThatThrownBy(() -> list.get(4)).isInstanceOf(IndexOutOfBoundsException.class);
+        assertThat(list.get(5)).isEqualTo(5);
+    }
+
+    @Test
+    public void testNegativeIndexBeforeBufferFills() {
+        // When head < windowSize, head - windowSize is negative.
+        // A negative index must still be rejected even though it satisfies
+        // index >= (head - windowSize) when that difference is negative.
+        CircularArrayList<Integer> list = new CircularArrayList<>(10);
+        list.add(0);
+        list.add(1);
+        list.add(2);
+
+        assertThatThrownBy(() -> list.get(-1)).isInstanceOf(IndexOutOfBoundsException.class);
+        assertThatThrownBy(() -> list.get(-7)).isInstanceOf(IndexOutOfBoundsException.class);
+    }
+
+    @Test
+    public void testToArray() {
+        CircularArrayList<Object> list = new CircularArrayList<>(10);
+        assertThat(list.toArray().length).isEqualTo(0);
+        list.add(0);
+        assertThat(list.toArray()).isEqualTo(new Object[] {0});
+        list.add(1);
+        assertThat(list.toArray()).isEqualTo(new Object[] {0, 1});
+        list.add(2);
+        list.add(3);
+        list.add(4);
+        list.add(5);
+        list.add(6);
+        list.add(7);
+        list.add(8);
+        assertThat(list.toArray()).isEqualTo(new Object[] {0, 1, 2, 3, 4, 5, 6, 7, 8});
+        list.add(9);
+        assertThat(list.toArray()).isEqualTo(new Object[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
+        list.add(10);
+        assertThat(list.toArray()).isEqualTo(new Object[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
+        list.add(11);
+        list.add(12);
+        list.add(13);
+        list.add(14);
+        list.add(15);
+        assertThat(list.toArray()).isEqualTo(new Object[] {6, 7, 8, 9, 10, 11, 12, 13, 14, 15});
+    }
+
+}
diff --git a/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/DEvent.java b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/DEvent.java
new file mode 100644
index 00000000000..34905d6b02f
--- /dev/null
+++ b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/DEvent.java
@@ -0,0 +1,80 @@
+/*
+ * 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.mvel.integrationtests.phreak.sequencing;
+
+import org.kie.api.definition.type.Position;
+
+public class DEvent {
+
+    @Position(0)
+    private Object object;
+
+    @Position(1)
+    private String text;
+
+    public DEvent(Object object) {
+        this.object = object;
+    }
+
+    public DEvent(Object object, String text) {
+        this.object = object;
+        this.text = text;
+    }
+
+    public Object getObject() {
+        return object;
+    }
+
+    public void setObject(Object object) {
+        this.object = object;
+    }
+
+    public String getText() {
+        return text;
+    }
+
+    public void setText(String text) {
+        this.text = text;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((object == null) ? 0 : object.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if ( this == obj ) return true;
+        if ( obj == null ) return false;
+        if ( getClass() != obj.getClass() ) return false;
+        DEvent other = (DEvent) obj;
+        if ( object == null ) {
+            if ( other.object != null ) return false;
+        } else if ( !object.equals( other.object ) ) return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "D [" + object + "]";
+    }
+}
diff --git a/drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/EEvent.java
similarity index 55%
copy from drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java
copy to drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/EEvent.java
index d74396d1070..3145f0ec630 100644
--- a/drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java
+++ b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/EEvent.java
@@ -16,33 +16,30 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.drools.model.view;
+package org.drools.mvel.integrationtests.phreak.sequencing;
 
-import org.drools.model.Condition;
-import org.drools.model.Variable;
+import org.drools.mvel.integrationtests.phreak.E;
+import org.kie.api.definition.type.Position;
 
-public class ExistentialExprViewItem<T> extends AbstractExprViewItem<T> {
+public class EEvent extends E {
 
-    private final Condition.Type type;
-    private final ViewItem expression;
+    @Position(1)
+    private String text;
 
-    public ExistentialExprViewItem(Condition.Type type, ViewItem expression) {
-        super(expression.getFirstVariable());
-        this.type = type;
-        this.expression = expression;
+    public EEvent(Object object) {
+        super(object);
     }
 
-    public ViewItem getExpression() {
-        return expression;
+    public EEvent(Object object, String text) {
+        super(object);
+        this.text = text;
     }
 
-    @Override
-    public Variable<?>[] getVariables() {
-        return expression.getVariables();
+    public String getText() {
+        return text;
     }
 
-    @Override
-    public Condition.Type getType() {
-        return type;
+    public void setText(String text) {
+        this.text = text;
     }
 }
diff --git a/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/GatesTest.java b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/GatesTest.java
new file mode 100644
index 00000000000..8d6fb31a57b
--- /dev/null
+++ b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/GatesTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.mvel.integrationtests.phreak.sequencing;
+
+import org.drools.base.reteoo.sequencing.signalprocessors.Gates;
+import org.drools.base.reteoo.sequencing.signalprocessors.LogicGate;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+public class GatesTest {
+
+    // AND gate: matched when current == allMatched (all bits set)
+    @Test
+    public void testAndAllSet() {
+        assertThat(Gates.and(0b111L, 0b111L)).isTrue();
+        assertThat(Gates.and(0b110L, 0b111L)).isFalse();
+    }
+
+    // OR gate: matched when at least one bit of allMatched is set in current
+    @Test
+    public void testOrBasic() {
+        assertThat(Gates.or(0b001L, 0b111L)).isTrue();
+        assertThat(Gates.or(0b100L, 0b111L)).isTrue();
+        assertThat(Gates.or(0b000L, 0b111L)).isFalse();
+    }
+
+    @Test
+    public void testOrBit63() {
+        long bit63 = Long.MIN_VALUE; // 0x8000_0000_0000_0000 — only sign bit set
+        // OR gate: allMatched = bit63, current = bit63 → should match
+        assertThat(Gates.or(bit63, bit63)).isTrue();
+    }
+
+    @Test
+    public void testLogicGateTooManyInputsThrows() {
+        // Build an int[] of 65 filterIndexes/signalAdapterIndexes to exceed the limit.
+        int[] indexes = new int[65];
+        assertThatThrownBy(() -> new LogicGate((a, b) -> a == b, 0, indexes, indexes, 0))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("64");
+    }
+
+    @Test
+    public void testLogicGateExactly64InputsAllowed() {
+        int[] indexes = new int[32];
+        // 32 signal adapters + 32 input gates = 64 total — should not throw.
+        new LogicGate((a, b) -> a == b, 0, indexes, indexes, 32);
+    }
+}
diff --git a/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/PhreakSequencerActivateDeactivateStepTest.java b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/PhreakSequencerActivateDeactivateStepTest.java
new file mode 100644
index 00000000000..9841f4741f6
--- /dev/null
+++ b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/PhreakSequencerActivateDeactivateStepTest.java
@@ -0,0 +1,161 @@
+/**
+ * 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.mvel.integrationtests.phreak.sequencing;
+
+import org.drools.base.rule.Pattern;
+import org.drools.base.reteoo.DynamicFilter;
+import org.drools.base.reteoo.sequencing.Sequence.SequenceMemory;
+import org.drools.base.reteoo.SignalAdapter;
+import org.drools.base.reteoo.sequencing.signalprocessors.LogicCircuit;
+import org.drools.base.reteoo.sequencing.signalprocessors.Gates;
+import org.drools.base.reteoo.sequencing.signalprocessors.LogicGate;
+import org.drools.base.reteoo.sequencing.Sequence;
+import org.drools.base.reteoo.sequencing.steps.Step;
+import org.drools.base.reteoo.sequencing.signalprocessors.TerminatingSignalProcessor;
+import org.drools.mvel.integrationtests.phreak.A;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class PhreakSequencerActivateDeactivateStepTest extends AbstractPhreakSequencerSubsequenceTest {
+
+    private SequenceMemory sequenceMemory;
+
+    @BeforeEach
+    public void setup() {
+        initKBaseWithEmptyRule();
+
+        LogicGate gate1 = new LogicGate((inputMask, sourceMask) -> Gates.and(inputMask, sourceMask),0,
+                                        new int[] {0, 1}, // B and C
+                                        new int[] {0, 1}, // Each SignalAdapter must be in a unique index  for the Sequence
+                                        0);
+        gate1.setOutput(TerminatingSignalProcessor.get());
+
+        LogicCircuit circuit1 = new LogicCircuit(gate1);
+
+        LogicGate gate2 = new LogicGate((inputMask, sourceMask) -> Gates.and(inputMask, sourceMask),1,
+                                        new int[] {1, 2}, // C and D, C Filter is re-used
+                                        new int[] {2, 3}, // Each SignalAdapter must be in a unique index  for the Sequence
+                                        0);
+        gate2.setOutput(TerminatingSignalProcessor.get());
+
+        LogicCircuit circuit2 = new LogicCircuit(gate2);
+
+        seq0 = new Sequence(0, Step.of(circuit1), Step.of(circuit2));
+        seq0.setFilters(new Pattern[]{bpattern, cpattern, dpattern});
+        rule.addSequence(seq0);
+        kbase.addPackage(pkg);
+
+        createSession();
+        sequenceMemory = sequencerMemory.getSequenceMemory(seq0);
+    }
+
+    @Test
+    public void testActivateAfterFirstCircuitStep() {
+        // activate LogicCircuit1 and check filters are adapters are created and made active
+        DynamicFilter filter0 = nodeMemory.getFilters()[0]; // B
+        DynamicFilter filter1 = nodeMemory.getFilters()[1]; // C
+        // [2] D is null
+
+        // DynamicFilters are created on demand from Protos
+        assertThat(nodeMemory.getFilters()).usingRecursiveComparison().isEqualTo(new DynamicFilter[] {filter0, filter1, null });
+
+        // Check correct activation of DynamicFilters
+        assertThat(nodeMemory.getActiveFilters()[0].size()).isEqualTo(1); // B is active
+        assertThat(nodeMemory.getActiveFilters()[0].getFirst()).isSameAs(filter0);
+        assertThat(nodeMemory.getActiveFilters()[1].size()).isEqualTo(1); // C is active
+        assertThat(nodeMemory.getActiveFilters()[1].getFirst()).isSameAs(filter1);
+        assertThat(nodeMemory.getActiveFilters()[2]).isNull(); // D is not yet active
+
+        // Check correct activation of SignalAdapters
+        SignalAdapter signal0 = sequenceMemory.getSignalAdapters()[0];
+        SignalAdapter signal1 = sequenceMemory.getSignalAdapters()[1];
+        assertThat(sequenceMemory.getSignalAdapters()).usingRecursiveComparison().isEqualTo(new SignalAdapter[] {signal0, signal1, null, null });
+        assertThat(sequenceMemory.getActiveSignalAdapters()).usingRecursiveComparison().isEqualTo(new SignalAdapter[] {signal0, signal1, null, null });
+    }
+
+    @Test
+    public void testDeactivateAndActivateAfterSecondCircuitStep() {
+        DynamicFilter filter0 = nodeMemory.getFilters()[0]; // B
+        DynamicFilter filter1 = nodeMemory.getFilters()[1]; // C
+
+        SignalAdapter signal0 = sequenceMemory.getSignalAdapters()[0];
+        SignalAdapter signal1 = sequenceMemory.getSignalAdapters()[1];
+
+        session.insert(new BEvent(0, "b"));
+        assertThat(getCurrentStep(sequencerMemory)).isEqualTo(0); // still step 0
+
+        session.insert(new CEvent(0, "c"));
+        // Next step, check everything was for LogicCircuit was de-activated and LogicCircuit1 was activated;
+        assertThat(getCurrentStep(sequencerMemory)).isEqualTo(1); // Now step 1
+
+        DynamicFilter filter2 = nodeMemory.getFilters()[2]; // D was created from its Proto
+
+        assertThat(nodeMemory.getFilters()).usingRecursiveComparison().isEqualTo(new DynamicFilter[] {filter0, filter1, filter2 }); // all three now created
+
+        // Check correct activation of DynamicFilters
+        assertThat(nodeMemory.getActiveFilters()[0].size()).isEqualTo(0); // B is deactived and now empty
+        assertThat(nodeMemory.getActiveFilters()[1].size()).isEqualTo(1); // C remains active
+        assertThat(nodeMemory.getActiveFilters()[1].getFirst()).isSameAs(filter1);
+        assertThat(nodeMemory.getActiveFilters()[2].size()).isEqualTo(1); // D is now active
+        assertThat(nodeMemory.getActiveFilters()[2].getFirst()).isSameAs(filter2);
+
+        // Check correct activation of SignalAdapters
+        SignalAdapter signal2 = sequenceMemory.getSignalAdapters()[2];
+        SignalAdapter signal3 = sequenceMemory.getSignalAdapters()[3];
+
+        assertThat(sequenceMemory.getSignalAdapters()).usingRecursiveComparison().isEqualTo(new SignalAdapter[] {signal0, signal1, signal2, signal3 });
+        assertThat(sequenceMemory.getActiveSignalAdapters()).usingRecursiveComparison().isEqualTo(new SignalAdapter[] {null, null, signal2, signal3 }); // 0 and 1 are no longer active
+    }
+
+    @Test
+    public void testDeactivateAfterEndStep() {
+        assertThat(getCurrentStep(sequencerMemory)).isEqualTo(0); // step 0
+        session.insert(new BEvent(0, "b"));
+        session.insert(new CEvent(0, "c"));
+        assertThat(getCurrentStep(sequencerMemory)).isEqualTo(1); // step 1
+        session.insert(new CEvent(0, "c"));
+        session.insert(new DEvent(0, "d"));
+
+        assertThat(getCurrentStep(sequencerMemory)).isEqualTo(-1); // Now step -1, which means it's finished.
+
+        // make sure there are no active SignalAapters or active Filters.
+        assertThat(sequenceMemory.getActiveSignalAdapters()).usingRecursiveComparison().isEqualTo(new SignalAdapter[] {null, null, null, null }); // 0 and 1 are no longer active
+        assertThat(nodeMemory.getActiveFilters()[0].size()).isEqualTo(0);
+        assertThat(nodeMemory.getActiveFilters()[1].size()).isEqualTo(0);
+        assertThat(nodeMemory.getActiveFilters()[2].size()).isEqualTo(0);
+    }
+
+    @Test
+    public void testUpdateOnDrivingTupleRestartsSequence() {
+        // Advance to step 1: insert B then C to satisfy gate 1
+        session.insert(new BEvent(0, "b"));
+        session.insert(new CEvent(0, "c"));
+        assertThat(getCurrentStep(sequencerMemory)).isEqualTo(1); // sequence is at step 1
+
+        // Update A — should stop the running sequence and restart it at step 0.
+        // fireAllRules() is required to flush the Phreak left-tuple update queue.
+        A a = (A) fhA0.getObject();
+        session.update(fhA0, a);
+        session.fireAllRules();
+
+        assertThat(getCurrentStep(sequencerMemory)).isEqualTo(0);
+    }
+}
diff --git a/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/PhreakSequencerLogicGateTest.java b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/PhreakSequencerLogicGateTest.java
new file mode 100644
index 00000000000..5160798b577
--- /dev/null
+++ b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/PhreakSequencerLogicGateTest.java
@@ -0,0 +1,156 @@
+/**
+ * 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.mvel.integrationtests.phreak.sequencing;
+
+import org.drools.base.rule.Pattern;
+import org.drools.core.common.InternalFactHandle;
+import org.drools.base.reteoo.sequencing.signalprocessors.LogicCircuit;
+import org.drools.base.reteoo.sequencing.signalprocessors.Gates;
+import org.drools.base.reteoo.sequencing.signalprocessors.LogicGate;
+import org.drools.base.reteoo.sequencing.signalprocessors.LogicGateOutputSignalProcessor;
+import org.drools.base.reteoo.sequencing.Sequence;
+import org.drools.base.reteoo.sequencing.signalprocessors.SignalIndex;
+import org.drools.base.reteoo.sequencing.steps.Step;
+import org.drools.base.reteoo.sequencing.signalprocessors.TerminatingSignalProcessor;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class PhreakSequencerLogicGateTest extends AbstractPhreakSequencerSubsequenceTest {
+
+    @BeforeEach
+    public void setup() {
+        initKBaseWithEmptyRule();
+    }
+
+    @Test
+    public void testAnd() {
+        LogicGate gate1 = new LogicGate((inputMask, sourceMask) -> Gates.and(inputMask, sourceMask),0,
+                                        new int[] {0, 1}, // B and C
+                                        new int[] {0, 1}, // Each SignalAdapter must be in a unique index  for the Sequence
+                                        0);
+
+        gate1.setOutput(TerminatingSignalProcessor.get());
+
+        LogicCircuit circuit1 = new LogicCircuit(gate1);
+
+        Sequence seq = new Sequence(0, Step.of(circuit1));
+        seq.setFilters(new Pattern[]{bpattern, cpattern, dpattern, epattern});
+        rule.addSequence(seq);
+        kbase.addPackage(pkg);
+
+        createSession();
+        assertThat(getCurrentStep(sequencerMemory)).isEqualTo(0); // step 0
+        session.insert(new BEvent(0, "b"));
+        assertThat(getCurrentStep(sequencerMemory)).isEqualTo(0); // step 0
+         session.insert(new CEvent(0, "c"));
+        assertThat(getCurrentStep(sequencerMemory)).isEqualTo(-1); // terminated
+
+        // reverse B and C
+        createSession();
+        assertThat(getCurrentStep(sequencerMemory)).isEqualTo(0); // step 0
+        session.insert(new CEvent(0, "c"));
+
+        assertThat(getCurrentStep(sequencerMemory)).isEqualTo(0); // step 0
+        session.insert(new BEvent(0, "b"));
+        assertThat(getCurrentStep(sequencerMemory)).isEqualTo(-1); // terminated
+    }
+
+    @Test
+    public void testOr() {
+        LogicGate gate1 = new LogicGate((inputMask, sourceMask) -> Gates.or(inputMask, sourceMask),0,
+                                        new int[] {0, 1}, // B and C
+                                        new int[] {0, 1}, // Each SignalAdapter must be in a unique index  for the Sequence
+                                        0);
+
+        gate1.setOutput(TerminatingSignalProcessor.get());
+
+        LogicCircuit circuit1 = new LogicCircuit(gate1);
+
+        Sequence seq = new Sequence(0, Step.of(circuit1));
+        seq.setFilters(new Pattern[]{bpattern, cpattern, dpattern, epattern});
+        rule.addSequence(seq);
+        kbase.addPackage(pkg);
+
+        createSession();
+        assertThat(getCurrentStep(sequencerMemory)).isEqualTo(0); // step 0
+        session.insert(new BEvent(0, "b"));
+
+        assertThat(getCurrentStep(sequencerMemory)).isEqualTo(-1); // terminated
+
+        // reverse B and C
+        createSession();
+        assertThat(getCurrentStep(sequencerMemory)).isEqualTo(0); // step 0
+        InternalFactHandle fhC0 = (InternalFactHandle) session.insert(new CEvent(0, "c"));
+
+        assertThat(getCurrentStep(sequencerMemory)).isEqualTo(-1); // terminated
+    }
+
+
+    @Test
+    public void testComposite() {
+        LogicGate gate1 = new LogicGate((inputMask, sourceMask) -> Gates.and(inputMask, sourceMask),0,
+                                        new int[] {0, 1}, // B and C
+                                        new int[] {0, 1}, // Each SignalAdapter must be in a unique index  for the Sequence
+                                        0);
+
+        LogicGate gate2 = new LogicGate((inputMask, sourceMask) -> Gates.and(inputMask, sourceMask),1,
+                                        new int[] {2}, // D
+                                        new int[] {2}, // D
+                                        1);
+
+        gate1.setOutput(new LogicGateOutputSignalProcessor(SignalIndex.of(gate2, 2))); // gate bits must come after signals
+        gate2.setOutput(TerminatingSignalProcessor.get());
+        gate2.setInputGates(gate1);
+
+        LogicCircuit circuit1 = new LogicCircuit(gate1, gate2);
+
+        Sequence seq = new Sequence(0, Step.of(circuit1));
+        seq.setFilters(new Pattern[]{bpattern, cpattern, dpattern, epattern});
+        rule.addSequence(seq);
+        kbase.addPackage(pkg);
+
+        // D last
+        createSession();
+        assertThat(getCurrentStep(sequencerMemory)).isEqualTo(0); // step 0
+        session.insert(new BEvent(0, "b"));
+        assertThat(getCurrentStep(sequencerMemory)).isEqualTo(0); // step 0
+        session.insert(new CEvent(0, "c"));
+        assertThat(getCurrentStep(sequencerMemory)).isEqualTo(0); // step 0
+
+        // now it'll transition
+        session.insert(new DEvent(0, "d"));
+        assertThat(getCurrentStep(sequencerMemory)).isEqualTo(-1); // terminated
+
+        // change order, D first
+        createSession();
+        session.insert(new DEvent(0, "d"));
+        assertThat(getCurrentStep(sequencerMemory)).isEqualTo(0); // step 0
+
+        assertThat(getCurrentStep(sequencerMemory)).isEqualTo(0); // step 0
+        session.insert(new BEvent(0, "b"));
+        assertThat(getCurrentStep(sequencerMemory)).isEqualTo(0); // step 0
+
+        // now it'll transition
+        session.insert(new CEvent(0, "c"));
+        assertThat(getCurrentStep(sequencerMemory)).isEqualTo(-1); // terminated
+    }
+
+}
diff --git a/drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/Predicate1.java
similarity index 53%
copy from drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java
copy to drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/Predicate1.java
index d74396d1070..07ea3962eda 100644
--- a/drools-model/drools-canonical-model/src/main/java/org/drools/model/view/ExistentialExprViewItem.java
+++ b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/Predicate1.java
@@ -16,33 +16,8 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.drools.model.view;
+package org.drools.mvel.integrationtests.phreak.sequencing;
 
-import org.drools.model.Condition;
-import org.drools.model.Variable;
-
-public class ExistentialExprViewItem<T> extends AbstractExprViewItem<T> {
-
-    private final Condition.Type type;
-    private final ViewItem expression;
-
-    public ExistentialExprViewItem(Condition.Type type, ViewItem expression) {
-        super(expression.getFirstVariable());
-        this.type = type;
-        this.expression = expression;
-    }
-
-    public ViewItem getExpression() {
-        return expression;
-    }
-
-    @Override
-    public Variable<?>[] getVariables() {
-        return expression.getVariables();
-    }
-
-    @Override
-    public Condition.Type getType() {
-        return type;
-    }
+interface Predicate1<A> {
+    boolean test(A a);
 }
diff --git a/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/SequenceCloneTest.java b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/SequenceCloneTest.java
new file mode 100644
index 00000000000..4ef4c697cc1
--- /dev/null
+++ b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/SequenceCloneTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.mvel.integrationtests.phreak.sequencing;
+
+import org.drools.base.reteoo.sequencing.Sequence;
+import org.drools.base.reteoo.sequencing.signalprocessors.Gates;
+import org.drools.base.reteoo.sequencing.signalprocessors.LogicCircuit;
+import org.drools.base.reteoo.sequencing.signalprocessors.LogicGate;
+import org.drools.base.reteoo.sequencing.signalprocessors.TerminatingSignalProcessor;
+import org.drools.base.reteoo.sequencing.steps.Step;
+import org.drools.base.rule.RuleConditionElement;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class SequenceCloneTest {
+
+    @Test
+    public void cloneDoesNotThrow() {
+        LogicGate gate = new LogicGate(
+                (a, b) -> Gates.and(a, b), 0,
+                new int[]{0}, new int[]{0}, 0);
+        gate.setOutput(TerminatingSignalProcessor.get());
+
+        Sequence original = new Sequence(0, Step.of(new LogicCircuit(gate)));
+
+        RuleConditionElement clone = original.clone();
+
+        assertThat(clone).isNotNull();
+        assertThat(clone).isNotSameAs(original);
+    }
+
+    @Test
+    public void clonePreservesDescriptors() {
+        LogicGate gate = new LogicGate(
+                (a, b) -> Gates.and(a, b), 0,
+                new int[]{0}, new int[]{0}, 0);
+        gate.setOutput(TerminatingSignalProcessor.get());
+
+        Sequence original = new Sequence(0, Step.of(new LogicCircuit(gate)));
+
+        Sequence clone = (Sequence) original.clone();
+
+        assertThat(clone.getSteps()).isSameAs(original.getSteps());
+        assertThat(clone.getGates()).isSameAs(original.getGates());
+        assertThat(clone.getFilters()).isSameAs(original.getFilters());
+        assertThat(clone).isNotSameAs(original);
+    }
+}
diff --git a/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/TestMemories.java b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/TestMemories.java
new file mode 100644
index 00000000000..3ea3204e9a4
--- /dev/null
+++ b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/phreak/sequencing/TestMemories.java
@@ -0,0 +1,109 @@
+/*
+ * 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.mvel.integrationtests.phreak.sequencing;
+
+import org.drools.base.base.ClassObjectType;
+import org.drools.base.reteoo.DynamicFilter;
+import org.drools.base.reteoo.SignalAdapter;
+import org.drools.base.reteoo.sequencing.Sequence;
+import org.drools.base.reteoo.sequencing.Sequence.SequenceMemory;
+import org.drools.base.reteoo.sequencing.SequencerMemory;
+import org.drools.base.reteoo.sequencing.signalprocessors.Gates;
+import org.drools.base.reteoo.sequencing.signalprocessors.LogicCircuit;
+import org.drools.base.reteoo.sequencing.signalprocessors.LogicGate;
+import org.drools.base.reteoo.sequencing.signalprocessors.TerminatingSignalProcessor;
+import org.drools.base.reteoo.sequencing.steps.Step;
+import org.drools.base.rule.EntryPointId;
+import org.drools.base.rule.Pattern;
+import org.drools.base.util.LinkedList;
+import org.drools.core.ClockType;
+import org.drools.core.SessionConfiguration;
+import org.drools.core.common.InternalFactHandle;
+import org.drools.core.reteoo.LeftTuple;
+import org.drools.core.reteoo.MockLeftTupleSink;
+import org.drools.core.reteoo.ObjectTypeNode;
+import org.drools.core.reteoo.SequenceNode;
+import org.drools.core.reteoo.builder.BuildContext;
+import org.drools.kiesession.session.StatefulKnowledgeSessionImpl;
+import org.drools.mvel.integrationtests.phreak.A;
+import org.junit.jupiter.api.Test;
+import org.kie.api.runtime.conf.ThreadSafeOption;
+
+import java.util.Collections;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class TestMemories extends AbstractPhreakSequencerSubsequenceTest {
+
+    @Test
+    public void testInitialisedMemoryBeforeStart() {
+        initKBaseWithEmptyRule();
+        buildContext = new BuildContext(kbase, Collections.emptyList() );
+        buildContext.setRule(rule);
+
+        LogicGate gate1 = new LogicGate((inputMask, sourceMask) -> Gates.and(inputMask, sourceMask), 0,
+                                        new int[] {0, 1}, // B and C
+                                        new int[] {0, 1}, // Each SignalAdapter must be in a unique index  for the Sequence
+                                        0);
+        gate1.setOutput(TerminatingSignalProcessor.get());
+
+        LogicCircuit circuit1 = new LogicCircuit(gate1);
+
+        LogicGate gate2 = new LogicGate((inputMask, sourceMask) -> Gates.and(inputMask, sourceMask),1,
+                                        new int[] {1, 2}, // C and D, C Filter is re-used
+                                        new int[] {2, 3}, // Each SignalAdapter must be in a unique index  for the Sequence
+                                        0);
+        gate2.setOutput(TerminatingSignalProcessor.get());
+
+        LogicCircuit circuit2 = new LogicCircuit(gate2);
+
+        seq0 = new Sequence(0, Step.of(circuit1), Step.of(circuit2));
+        seq0.setFilters(new Pattern[]{bpattern, cpattern, dpattern});
+        rule.addSequence(seq0);
+        kbase.addPackage(pkg);
+
+        ObjectTypeNode aNode = kbase.getRete().getEntryPointNode(EntryPointId.DEFAULT).getObjectTypeNodes().get(new ClassObjectType(A.class));
+        snode = (SequenceNode) aNode.getSinks()[0].getSinks()[0];
+
+        SessionConfiguration sessionConf = kbase.getSessionConfiguration();
+        sessionConf.setOption(ThreadSafeOption.NO);
+        sessionConf.setClockType(ClockType.PSEUDO_CLOCK);
+
+        session = (StatefulKnowledgeSessionImpl) kbase.newKieSession(sessionConf, null);
+
+        InternalFactHandle fhA0 = (InternalFactHandle) session.insert(new A(0));
+        nodeMemory = session.getNodeMemory(snode);
+        LeftTuple lt = new LeftTuple(fhA0, snode, true);
+        lt.setContextObject(snode.createSequencerMemory(lt, new MockLeftTupleSink(buildContext.getNextNodeId(), buildContext), nodeMemory));
+        nodeMemory.getLeftTupleMemory().add(lt);
+        sequencerMemory = (SequencerMemory) lt.getContextObject();
+
+        SequenceMemory sequenceMemory = sequencerMemory.getOrCreateSequenceMemory(seq0, sequencerMemory.getData());
+        sequencerMemory.setChildSequenceMemory(sequenceMemory);
+
+        // make sure these are empty
+        assertThat(nodeMemory.getFilters()).usingRecursiveComparison().isEqualTo(new DynamicFilter[3]);
+        assertThat(nodeMemory.getActiveFilters()).usingRecursiveComparison().isEqualTo(new LinkedList[3]);
+        assertThat(nodeMemory.getActiveFilters()).usingRecursiveComparison().isEqualTo(new LinkedList[3]);
+        assertThat(sequenceMemory.getActiveSignalAdapters()).usingRecursiveComparison().isEqualTo(new SignalAdapter[4]);
+        assertThat(sequenceMemory.getSignalAdapters()).usingRecursiveComparison().isEqualTo(new SignalAdapter[4]);
+        assertThat(getCurrentStep(sequencerMemory)).isEqualTo(0);
+    }
+
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@kie.apache.org
For additional commands, e-mail: commits-help@kie.apache.org