diff --git a/libs/jastadd2.jar b/libs/jastadd2.jar
index d615b895453d660f0e7397fffad58a05029169fd..3c952d30318ddc23d38d7ccb277ee8431fe6b000 100644
Binary files a/libs/jastadd2.jar and b/libs/jastadd2.jar differ
diff --git a/pages/docs/dsl.md b/pages/docs/dsl.md
index 3f83c719934a1eebb6a35978fe55c4c01f65c9be..19125a893a07aa3dda53a34ddd7f46537675bbb8 100644
--- a/pages/docs/dsl.md
+++ b/pages/docs/dsl.md
@@ -38,9 +38,6 @@ A breakdown of the parts of that syntax:
 
 ### Context-Free Endpoints
 
-!!! attention
-    Context-Free endpoints are currently only supported for receiving endpoints.
-
 An endpoint with only a non-terminal and without a target is called context-free endpoint.
 Specifying such an endpoint has several consequences:
 
diff --git a/pages/docs/inner-workings.md b/pages/docs/inner-workings.md
index a36a62654511e88a4063a52d356f69fc9125a27f..bd9986a9c7c81c034e30e57e364d243393a8e39a 100644
--- a/pages/docs/inner-workings.md
+++ b/pages/docs/inner-workings.md
@@ -19,3 +19,17 @@ One of the main aspects is `Intermediate` containing all attributes consumed by
 The other main aspect (which is currently not really used) is `IntermediateToYAML` containing the transformation from a `RagConnect` subtree to a `Document` subtree defined by `Mustache.relast` (located in `relast-preprocessor` submodule).
 This is used to generate a YAML file containing the data used by mustache.
 It can be used by the default mustache implementation together with the templates.
+
+# Implementation details
+
+In the following, details for special implementation topics are discussed.
+
+## forwarding
+
+When a nonterminal is used in a send endpoints, it needs an implicit forwarding attribute to work, because only _computed elements_ can be sent.
+Since the nonterminal itself should be sent, the generated attribute simply returns this nonterminal.
+
+However, changing any token within the whole subtree or changing the structure of the subtree must trigger a new message, upon computation of the forwarding attribute, all tokens are "touched" (their getter is called).
+This way, the dependency tracking registers a dependency between structure and tokens to the attribute.
+
+The attribute (as well as any other generated element) is prefixed with `_ragconnect_` to avoid potential name conflicts with user-specified elements.
diff --git a/pages/docs/using.md b/pages/docs/using.md
index ee52eedbbc66bcc37f5e080776be7faee5014d56..d4d2a30bea5b060e1ccd071b5b2b9e0730c41357 100644
--- a/pages/docs/using.md
+++ b/pages/docs/using.md
@@ -4,14 +4,14 @@ The full example is available at <https://git-st.inf.tu-dresden.de/jastadd/ragco
 
 ## Preparation and Specification
 
-The following examples are inspired by the real test case [read1write2](https://git-st.inf.tu-dresden.de/jastadd/ragconnect-tests/-/tree/master/ragconnect.tests/src/test/01-input/read1write2)
+The following examples are inspired by real [test cases](https://git-st.inf.tu-dresden.de/jastadd/ragconnect-tests/-/tree/master/ragconnect.tests/src/test/01-input) read1write2 and tokenValueSend.
 The idea is to have two non-terminals, where input information is received on one of them, and - after transformation - is sent out by both.
 
 Let's use the following grammar:
 
 ```
 A ::= <Input:String> /<OutputOnA:String>/ B* ;
-B ::= /<OutputOnB:String>/ ;
+B ::= <OutputOnB:String> ;
 ```
 
 To declare receiving and sending tokens, a dedicated DSL is used:
@@ -35,28 +35,15 @@ Such mapping definitions can be defined for receiving tokens as well.
 In this case, they are applied before the value is set.
 If no mapping definition is given, or if the required type (depending on the communication protocol, see later) does not match, a "default mapping definition" is used to avoid boilerplate code converting from or to primitive types.
 
-Furthermore, let the following attribute definitions be given:
+Furthermore, let the following attribute definition be given:
 
 ```java
 syn String A.getOutputOnA() = "a" + getInput();
-
-syn String B.getOutputOnB() = "b" + input();
-inh String B.input();
-eq A.getB().input() = getInput();
 ```
 
-In other words, `OutputOnA` depends on `Input` of the same node, and `OutputOnB` depends on `Input` of its parent node.
-Currently, those dependencies can be explicitly written down, or incremental evaluation can be used.
-
-### Dependency tracking: Manually specified
-
-This specification happens also in the DSL (dependencies have to be named to uniquely identify them):
-
-```java
-// dependency definitions
-A.OutputOnA canDependOn A.Input as dependencyA ;
-B.OutputOnB canDependOn A.Input as dependencyB ;
-```
+In other words, `OutputOnA` depends on `Input` of the same node.
+This dependency is automatically inferred, if incremental evaluation is used.
+Otherwise, the deprecated manual dependencies must be used.
 
 ### Dependency tracking: Automatically derived
 
@@ -68,6 +55,15 @@ The value for `trace` can include other values besides `flush`.
 An experimental, optimized version can be selected using `--experimental-jastadd-329` reducing the risk of conflicts between concurrent attribute evaluations.
 However, this requires a version of JastAdd that resolved the [issue 329](https://bitbucket.org/jastadd/jastadd2/issues/329/add-event-for-completion-of-flush).
 
+### Deprecated Manual Dependency Specification
+
+Specification happens also in the DSL (dependencies have to be named to uniquely identify them):
+
+```java
+// dependency definition
+A.OutputOnA canDependOn A.Input as dependencyA ;
+```
+
 ## Using generated code
 
 After specifying everything, code will be generated if [setup properly](/adding).
@@ -83,15 +79,11 @@ a.addB(b1);
 a.addB(b2);
 ```
 
-If necessary, we have to set the dependencies as [described earlier](#dependency-tracking-manually-specified).
+If necessary, we have to set the dependencies as [described earlier](#deprecated-manual-dependency-specification).
 
 ```java
 // a.OutputOnA -> a.Input
 a.addDependencyA(a);
-// b1.OutputOnB -> a.Input
-b1.addDependencyB(a);
-// b2.OutputOnB -> a.Input
-b2.addDependencyB(a);
 ```
 
 Finally, we can actually _connect_ the tokens.
@@ -144,24 +136,24 @@ Non-terminal children can also be selected as endpoints (not only tokens).
 Receiving normal non-terminal children and optionals means to replace them with a new node deserialized from the received message.
 Sending them involves serializing a node, and sending this representation in a message.
 
-Suppose, the following (shortened) grammar is used (inspired from the testcase [tree](https://git-st.inf.tu-dresden.de/jastadd/ragconnect-tests/-/tree/master/ragconnect.tests/src/test/01-input/tree))
+Suppose, the following (shortened) grammar is used (inspired from the testcase tree and forwarding)
 
 ```
 Root ::= SenderRoot ReceiverRoot ;
-SenderRoot ::= <Input:int> /Alfa/ ;
-ReceiverRoot ::= Alfa ;
-Alfa ::= // some content ...
+SenderRoot ::= <Input:int> /A/ B ;
+ReceiverRoot ::= A ;
+A ::= // some content ...
+B ::= <Value> ;
 ```
 
-Now, the complete node of type `Alfa` can be sent, and received again using the following connect specification:
+Now, the complete node of types `A` and `B` can be sent, and received again using the following connect specification:
 
 ```
-send tree SenderRoot.Alfa ;
-receive tree ReceiverRoot.Alfa ;
+send SenderRoot.A ;
+send SenderRoot.B ;
+receive ReceiverRoot.A ;
 ```
 
-Currently, receiving and sending trees requires the explicit demarcation from tokens using the keyword `tree`.
-
 To process non-terminals, default mappings are provided for every non-terminal type of the used grammar.
 They use the JSON serialization offered by the RelAST compiler, i.e., interpret the message as a `String`, deserialize the content reading the message as JSON, or vice versa.
 Additional dependencies are required to use this feature, as detailed in [the compiler section](/compiler#treelist-endpoints).
@@ -173,82 +165,82 @@ When receiving list children, there are a few more options to match the connecti
 Suppose we use a similar grammar as above, i.e.:
 
 ```
-SenderRoot ::= /AlfaList:Alfa*/ /SingleAlfa:Alfa/;
-ReceiverRoot ::= Alfa* ;
+SenderRoot ::= /AList:A*/ /SingleA:A/;
+ReceiverRoot ::= A* ;
 ```
 
-Several options are possible:
+Several options are possible (please also refer to the specification of the [connect DSL](/dsl):
 
-### list
+### (empty)
 
-A message for a list endpoint can be interpreted as a complete list (a sequence of nodes of type `Alfa`) by using the `list` keyword instead of `tree`:
+A message for a list endpoint can be interpreted as a complete list (a sequence of nodes of type `A`) by not specifying any special keyword:
 
 ```
-receive list ReceiverRoot.Alfa ;
+receive ReceiverRoot.A ;
 ```
 
-### list + with add
+### with add
 
 Upon receiving the message, the deserialized list can also be appended to the existing list instead of replace the latter.
-This can be achieved using the keyword `with add` in addition to the keyword `list`:
+This can be achieved using the keyword `with add` :
 
 ```
-receive list with add ReceiverRoot.Alfa ;
+receive with add ReceiverRoot.Alfa ;
 ```
 
-### tree (indexed)
+### indexed
 
 A message for a list endpoint can also be interpreted as an element of this list.
 
 ```
-receive tree ReceiverRoot.Alfa ;
+receive tree ReceiverRoot.A ;
 ```
 
 Upon connection, the index of the deserialized element to set, has to be passed (`1` in the example below).
 The list must have enough elements once a message is received.
 
 ```java
-receiverRoot.connectAlfa("<some-url>", 1);
+receiverRoot.connectA("<some-url>", 1);
 ```
 
-### tree (wildcard)
+### indexed (wildcard)
 
-Similar to the `tree (indexed)` case above, messages are interpreted as an element of the list, but the connection can also be made using a "wildcard topic" and without an index.
+Similar to the `indexed` case above, messages are interpreted as an element of the list, but the connection can also be made using a "wildcard topic" and without an index.
 Then, once a message is received from a new concrete topic, the deserialized element will be appended to the list and this topic is associated with the index of the newly added element.
 Any further message from that topic will replace the element at the associated index.
 In the short example below, MQTT is used to with a wildcard topic, as `#` matches every sub-topic.
 
 ```java
-receiverRoot.connectAlfa("mqtt://<broker>/some/topic/#");
+receiverRoot.connectA("mqtt://<broker>/some/topic/#");
 
 // list is initially empty
-assertEquals(receiverRoot.getAlfaList(), list());
+assertEquals(receiverRoot.getAList(), list());
 // after receiving "1" on new topic "some/topic/one" (index 0)
-assertEquals(receiverRoot.getAlfaList(), list("1"));
+assertEquals(receiverRoot.getAList(), list("1"));
 // after receiving "other" on new topic "some/topic/two" (index 1)
-assertEquals(receiverRoot.getAlfaList(), list("1", "other"));
+assertEquals(receiverRoot.getAList(), list("1", "other"));
 // after receiving "new" on existing topic "some/topic/one" (index 0)
-assertEquals(receiverRoot.getAlfaList(), list("new", "other"));
+assertEquals(receiverRoot.getAList(), list("new", "other"));
 ```
 
-### tree (indexed/wildcard) + with add
+### indexed + with add
 
-Combining `tree` and `with add` results in a connection, where messages are interpreted as elements of the list, and new elements are appended to the existing list.
+Combining `indexed` and `with add` results in a connection, where messages are interpreted as elements of the list, and new elements are appended to the existing list.
 In that case, wildcard and non-wildcard connections behave in the same way, as no index has to be passed, and the element is always append at the end.
 Reusing the example from above, the following observations can be made.
 
 ```java
-receiverRoot.connectAlfa("mqtt://<broker>/some/topic/#");
+receiverRoot.connectA("mqtt://<broker>/some/topic/#");
 // or
-receiverRoot.connectAlfa("mqtt://<broker>/some/topic/one");
-receiverRoot.connectAlfa("mqtt://<broker>/some/topic/two");
+receiverRoot.connectA("mqtt://<broker>/some/topic/one");
+receiverRoot.connectA("mqtt://<broker>/some/topic/two");
 
 // list is initially empty
-assertEquals(receiverRoot.getAlfaList(), list());
+assertEquals(receiverRoot.getAList(), list());
 // after receiving "1" on topic "some/topic/one"
-assertEquals(receiverRoot.getAlfaList(), list("1"));
+assertEquals(receiverRoot.getAList(), list("1"));
 // after receiving "other" on topic "some/topic/two"
-assertEquals(receiverRoot.getAlfaList(), list("1", "other"));
+assertEquals(receiverRoot.getAList(), list("1", "other"));
 // after receiving "new" on topic "some/topic/one"
-assertEquals(receiverRoot.getAlfaList(), list("1", "other", "new"));
+assertEquals(receiverRoot.getAList(), list("1", "other", "new"));
 ```
diff --git a/ragconnect.base/build.gradle b/ragconnect.base/build.gradle
index f039c8bc129e8f2ca58ed389c1cc5bf5cd8e92e9..7275d3ba1afc3347e047f840ad3188e3b6048c6c 100644
--- a/ragconnect.base/build.gradle
+++ b/ragconnect.base/build.gradle
@@ -22,7 +22,10 @@ mainClassName = 'org.jastadd.ragconnect.compiler.Compiler'
 
 repositories {
     mavenCentral()
-    jcenter()
+    maven {
+        name "gitlab-maven"
+        url "https://git-st.inf.tu-dresden.de/api/v4/groups/jastadd/-/packages/maven"
+    }
 }
 tasks.compileJava {
     options.release.set(11)
@@ -31,8 +34,8 @@ tasks.compileJava {
 dependencies {
     implementation project(':relast-preprocessor')
     implementation group: 'com.github.spullara.mustache.java', name: 'compiler', version: "${mustache_java_version}"
-//    runtimeOnly group: 'org.jastadd', name: 'jastadd', version: '2.3.5'
-    runtimeOnly fileTree(include: ['jastadd2.jar'], dir: '../libs')
+    runtimeOnly group: 'org.jastadd', name: 'jastadd2', version: '2.3.5-dresden'
+//    runtimeOnly fileTree(include: ['jastadd2.jar'], dir: '../libs')
     api group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11'
 }
 
diff --git a/ragconnect.base/src/main/jastadd/Analysis.jrag b/ragconnect.base/src/main/jastadd/Analysis.jrag
index 5659c9321a0016162ee5ca9715d6a38770c2a693..85ff38d40d88ddbc415d3bd3ea39867c00d1e2fd 100644
--- a/ragconnect.base/src/main/jastadd/Analysis.jrag
+++ b/ragconnect.base/src/main/jastadd/Analysis.jrag
@@ -8,7 +8,7 @@ aspect Analysis {
         .count() > 1;
   }
   eq TypeEndpointTarget.isAlreadyDefined() {
-    return lookupTypeEndpointDefinitions(getType()).stream()
+    return lookupGivenTypeEndpointDefinitions(getType()).stream()
         .filter(containingEndpointDefinition()::matchesType)
         .count() > 1;
   }
diff --git a/ragconnect.base/src/main/jastadd/Errors.jrag b/ragconnect.base/src/main/jastadd/Errors.jrag
index f1924e72f4b5944ee8897390f3db494bfd48c4c9..51f8b03b18e9d1eb18079fbaff0ae27632d17267 100644
--- a/ragconnect.base/src/main/jastadd/Errors.jrag
+++ b/ragconnect.base/src/main/jastadd/Errors.jrag
@@ -11,6 +11,10 @@ aspect Errors {
     when !getSend() && getEndpointTarget().isTokenEndpointTarget() && token().getNTA()
     to RagConnect.errors();
 
+  EndpointDefinition contributes error("Indexed based list access may only be used for type endpoint targets!")
+    when getIndexBasedListAccess() && !getEndpointTarget().isTypeEndpointTarget()
+    to RagConnect.errors();
+
   // if first mapping is null, then suitableDefaultMapping() == null
   EndpointDefinition contributes error("No suitable default mapping found for type " +
       ((getMappingList().isEmpty())
@@ -27,6 +31,9 @@ aspect Errors {
           token().effectiveJavaTypeUse())
     to RagConnect.errors();
 
+  UntypedEndpointTarget contributes error("Could not resolve endpoint target " + getTypeName() + "." + getChildName())
+    to RagConnect.errors();
+
   ContextFreeTypeEndpointTarget contributes error("Context-Free endpoint not allowed for root node " +
       getTypeDecl().getName() + "!")
     when getTypeDecl().occurencesInProductionRules().isEmpty()
@@ -34,7 +41,7 @@ aspect Errors {
 
   EndpointDefinition contributes error("Clash with implied, indexed endpoint definition of context-free endpoint in line " +
           clashingContextFreeEndpointDefinition().getStartLine() + "!")
-    when !getSend() && clashingContextFreeEndpointDefinition() != null
+    when clashingContextFreeEndpointDefinition() != null && clashingContextFreeEndpointDefinition().matchesType(this)
     to RagConnect.errors();
 
   DependencyDefinition contributes error("Dependency definition already defined for " + getSource().containingTypeDecl().getName() + " with name " + getID())
diff --git a/ragconnect.base/src/main/jastadd/Intermediate.jadd b/ragconnect.base/src/main/jastadd/Intermediate.jadd
index 0bc8cc208035afa153d13752fb2db1a18bd7a366..bcd4cc4da4242ccd317f3d23bb66bb70a62f3599 100644
--- a/ragconnect.base/src/main/jastadd/Intermediate.jadd
+++ b/ragconnect.base/src/main/jastadd/Intermediate.jadd
@@ -7,16 +7,38 @@ aspect SharedMustache {
   // === RagConnect ===
   syn boolean RagConnect.configIncrementalOptionActive() = getConfiguration().getIncrementalOptionActive();
 
+  syn String RagConnect.configJastAddList() = getConfiguration().getJastAddList();
+
+  syn String RagConnect.configJastAddOpt() = getConfiguration().getJastAddOpt();
+
   syn boolean RagConnect.configLoggingEnabledForIncremental() = getConfiguration().getLoggingEnabledForIncremental();
 
   syn boolean RagConnect.configExperimentalJastAdd329() = getConfiguration().getExperimentalJastAdd329();
 
   syn String RagConnect.internalRagConnectPrefix() = "_ragconnect_";
 
+  syn String RagConnect.observerInstanceSingletonMethodName() = internalRagConnectPrefix() + "Observer";
+  syn String RagConnect.observerInstanceResetMethodName() = internalRagConnectPrefix() + "resetObserver";
+
   syn String RagConnect.rootNodeName() = getConfiguration().getRootNode().getName();
 
   // === EndpointDefinition ===
   syn String EndpointDefinition.lastResult() = lastDefinition().outputVarName();
+
+  syn boolean EndpointDefinition.typeIsList() = getEndpointTarget().typeIsList();
+
+  syn boolean EndpointDefinition.typeIsOpt() = getEndpointTarget().typeIsOpt();
+
+  // === attributes needed for computing above ones ===
+  syn boolean EndpointTarget.typeIsList() = false;
+  eq TypeEndpointTarget.typeIsList() {
+    return getType().isListComponent();
+  }
+
+  syn boolean EndpointTarget.typeIsOpt() = false;
+  eq TypeEndpointTarget.typeIsOpt() {
+    return getType().isOptComponent();
+  }
 }
 
 aspect MustacheDependencyDefinition {
@@ -60,8 +82,6 @@ aspect MustacheHandleUri { /* empty */ }
 
 aspect MustacheListAspect {
   // === RagConnect ===
-  syn String RagConnect.configJastAddList() = getConfiguration().getJastAddList();
-
   syn boolean RagConnect.hasTreeListEndpoints() {
     for (EndpointDefinition endpointDef : allEndpointDefinitionList()) {
       if (endpointDef.typeIsList()) {
@@ -96,14 +116,19 @@ aspect MustacheMappingApplicationAndDefinition {
       // only check if received list is not null
       return lastResult() + " == null";
     }
+    String toPrepend = "";
+    if (!getSend() && getIndexBasedListAccess() && typeIsList() && !getWithAdd()) {
+      // for wildcard (list) receive connect
+      toPrepend = "index >= 0 && ";
+    }
     if (getEndpointTarget().isTypeEndpointTarget() && type().isOptComponent()) {
       // use "hasX()" instead of "getX() != null" for optionals
-      return "has" + typeName() + "()" + " && " + preemptiveExpectedValue() + ".equals(" + lastResult() + ")";
+      return toPrepend + "has" + typeName() + "()" + " && " + preemptiveExpectedValue() + ".equals(" + lastResult() + ")";
     }
     if (lastDefinition().getMappingDefinition().getToType().isPrimitiveType() || lastDefinition().getMappingDefinition().isDefaultMappingDefinition()) {
-      return preemptiveExpectedValue() + " != null && " + preemptiveExpectedValue() + ".equals(" + lastResult() + ")";
+      return toPrepend + preemptiveExpectedValue() + " != null && " + preemptiveExpectedValue() + ".equals(" + lastResult() + ")";
     }
-    return preemptiveExpectedValue() + " != null ? " + preemptiveExpectedValue() + ".equals(" + lastResult() + ") : " + lastResult() + " == null";
+    return toPrepend + preemptiveExpectedValue() + " != null ? " + preemptiveExpectedValue() + ".equals(" + lastResult() + ") : " + lastResult() + " == null";
   }
 
   syn JastAddList<MInnerMappingDefinition> EndpointDefinition.innerMappingDefinitions() = toMustache().getInnerMappingDefinitionList();
@@ -144,19 +169,19 @@ aspect MustacheMappingApplicationAndDefinition {
   syn String MEndpointDefinition.firstInputVarName();
 
   eq MTokenReceiveDefinition.firstInputVarName() = "message";
-  eq MTokenReceiveDefinition.preemptiveExpectedValue() = getterMethodName() + "()";
+  eq MTokenReceiveDefinition.preemptiveExpectedValue() = getterMethodCall();
   eq MTokenReceiveDefinition.preemptiveReturn() = "return;";
 
-  eq MTokenSendDefinition.firstInputVarName() = getterMethodName() + "()";
-  eq MTokenSendDefinition.preemptiveExpectedValue() = lastValue();
+  eq MTokenSendDefinition.firstInputVarName() = getterMethodCall();
+  eq MTokenSendDefinition.preemptiveExpectedValue() = lastValueGetterCall();
   eq MTokenSendDefinition.preemptiveReturn() = "return false;";
 
   eq MTypeReceiveDefinition.firstInputVarName() = "message";
-  eq MTypeReceiveDefinition.preemptiveExpectedValue() = getterMethodName() + "()";
+  eq MTypeReceiveDefinition.preemptiveExpectedValue() = getterMethodCall();
   eq MTypeReceiveDefinition.preemptiveReturn() = "return;";
 
-  eq MTypeSendDefinition.firstInputVarName() = getterMethodName() + "()";
-  eq MTypeSendDefinition.preemptiveExpectedValue() = lastValue();
+  eq MTypeSendDefinition.firstInputVarName() = getterMethodCall();
+  eq MTypeSendDefinition.preemptiveExpectedValue() = lastValueGetterCall();
   eq MTypeSendDefinition.preemptiveReturn() = "return false;";
 
   eq MContextFreeTypeReceiveDefinition.firstInputVarName() = "message";
@@ -169,7 +194,7 @@ aspect MustacheMappingApplicationAndDefinition {
 
   syn String MEndpointDefinition.parentTypeName() = getEndpointDefinition().parentTypeName();
 
-  syn String MEndpointDefinition.getterMethodName() = getEndpointDefinition().getterMethodName();
+  syn String MEndpointDefinition.getterMethodCall() = getEndpointDefinition().getterMethodCall();
 }
 
 aspect MustacheRagConnect {
@@ -198,8 +223,26 @@ aspect MustacheRagConnect {
     return result;
   }
 
+  syn List<TypeDecl> RagConnect.allTypeDecls() {
+    return getProgram().typeDecls().stream()
+            .sorted(java.util.Comparator.comparing(TypeDecl::getName))
+            .collect(java.util.stream.Collectors.toList());
+  }
+
   // > allMappingDefinitions in Mappings.jrag
 
+  syn String RagConnect.observerInstanceFieldName() = internalRagConnectPrefix() + "ObserverInstance";
+
+  syn java.util.List<Component> TypeDecl.tokenComponents() {
+    return filteredComponents(Component::isTokenComponent);
+  }
+  syn java.util.List<Component> TypeDecl.normalComponents() {
+    return filteredComponents(comp -> comp.isTypeComponent() && !comp.isListComponent());
+  }
+  syn java.util.List<Component> TypeDecl.listComponents() {
+    return filteredComponents(Component::isListComponent);
+  }
+
   syn List<TokenComponent> RagConnect.tokenComponentsThatNeedProxy() {
     List<TokenComponent> result = new ArrayList<>();
     for (TokenComponent token : getProgram().allTokenComponents()) {
@@ -210,6 +253,8 @@ aspect MustacheRagConnect {
     return result;
   }
 
+  syn String RagConnect.touchedTerminalsMethodName() = internalRagConnectPrefix() + "touchedTerminals";
+
   syn List<TypeDecl> RagConnect.typeDeclsOfContextFreeEndpointTargets() {
     List<TypeDecl> result = new ArrayList<>();
     for (EndpointTarget target : givenEndpointTargetList()) {
@@ -233,11 +278,18 @@ aspect MustacheRagConnect {
   }
 
   syn nta JastAddList<EndpointDefinition> EndpointTarget.impliedEndpointDefinitions() = new JastAddList<>();
+//  eq TypeEndpointTarget.impliedEndpointDefinitions() {
+//    JastAddList<EndpointDefinition> result = super.impliedEndpointDefinitions();
+//    if (!getSend() || !typeIsList() || !getIndexBasedListAccess()) {
+//      return result;
+//    }
+//    // create a new endpoint
+//  }
   eq ContextFreeTypeEndpointTarget.impliedEndpointDefinitions() {
     JastAddList<EndpointDefinition> result = super.impliedEndpointDefinitions();
     EndpointDefinition containingDef = containingEndpointDefinition();
     for (TypeComponent typeComponent : getTypeDecl().occurencesInProductionRules()) {
-      List<EndpointDefinition> defsForTypeComponent = lookupTypeEndpointDefinitions(typeComponent);
+      List<EndpointDefinition> defsForTypeComponent = lookupGivenTypeEndpointDefinitions(typeComponent);
       if (!defsForTypeComponent.stream().anyMatch(containingDef::matchesType)) {
         // there is no user-defined endpoint definition for this typeComponent yet
         // -> create a new endpoint definition with the same options and mappings as the context-free def
@@ -258,6 +310,15 @@ aspect MustacheRagConnect {
     return result;
   }
 
+  private java.util.List<Component> TypeDecl.filteredComponents(java.util.function.Predicate<Component> filter) {
+    java.util.List<Component> result = new java.util.ArrayList<>();
+    for (Component comp : getComponentList()) {
+      if (filter.test(comp)) {
+        result.add(comp);
+      }
+    }
+    return result;
+  }
 }
 
 aspect MustacheReceiveAndSendAndHandleUri {
@@ -272,7 +333,18 @@ aspect MustacheReceiveAndSendAndHandleUri {
     if (getEndpointTarget().isTokenEndpointTarget()) {
       extra = lookupTokenEndpointDefinitions(token()).size() > 1 ? uniqueSuffix() : "";
     } else if (getEndpointTarget().isTypeEndpointTarget()) {
-      extra = lookupTypeEndpointDefinitions(type()).size() > 1 ? uniqueSuffix() : "";
+      // here it has to be checked if there are ANY typeEndpointDefinitions within all endpoints (including implied)
+      extra = lookupAllTypeEndpointDefinitions(type()).size() > 1 ? uniqueSuffix() : "";
+    } else if (getEndpointTarget().isContextFreeTypeEndpointTarget()) {
+      // here it has to be checked if there are ANY typeEndpointDefinitions within all endpoints (including implied)
+      boolean needExtra = false;
+      for (TypeComponent typeComponent : getEndpointTarget().asContextFreeTypeEndpointTarget().getTypeDecl().occurencesInProductionRules()) {
+        if (lookupAllTypeEndpointDefinitions(typeComponent).size() > 1) {
+          needExtra = true;
+          break;
+        }
+      }
+      extra = needExtra ? uniqueSuffix() : "";
     } else {
       extra = "";
     }
@@ -281,12 +353,21 @@ aspect MustacheReceiveAndSendAndHandleUri {
 
   syn String EndpointDefinition.entityName() = getEndpointTarget().entityName();
 
+  syn boolean EndpointDefinition.indexedSend() = getSend() && indexedList();
+  syn boolean EndpointDefinition.indexedList() = typeIsList() && getIndexBasedListAccess();
+
   syn String EndpointDefinition.getterMethodName() = getEndpointTarget().getterMethodName();
+  syn String EndpointDefinition.getterMethodCall() = getEndpointTarget().getterMethodCall();
+  syn String EndpointDefinition.realGetterMethodName() = getEndpointTarget().realGetterMethodName();
+  syn String EndpointDefinition.realGetterMethodCall() = getEndpointTarget().realGetterMethodCall();
 
   syn String EndpointDefinition.parentTypeName() = getEndpointTarget().parentTypeName();
 
   // === attributes needed for computing above ones ===
   syn String EndpointTarget.getterMethodName();
+  syn String EndpointTarget.getterMethodCall() = getterMethodName() + "()";
+  syn String EndpointTarget.realGetterMethodName() = getterMethodName() + "NoTransform";
+  syn String EndpointTarget.realGetterMethodCall() = realGetterMethodName() + "()";
   syn String EndpointTarget.parentTypeName();
   syn String EndpointTarget.entityName();
 
@@ -294,14 +375,23 @@ aspect MustacheReceiveAndSendAndHandleUri {
   eq TokenEndpointTarget.parentTypeName() = getToken().containingTypeDecl().getName();
   eq TokenEndpointTarget.entityName() = getToken().getName();
 
-  eq TypeEndpointTarget.getterMethodName() = "get" + getType().getName() + (typeIsList() ? "List" : "");
+  eq TypeEndpointTarget.getterMethodName() = getterMethodeNameHelper(true);
+  eq TypeEndpointTarget.getterMethodCall() = getterMethodName() + (containingEndpointDefinition().indexedList() && !containingEndpointDefinition().getWithAdd() ? "(index)" : "()") + (typeIsOpt() ? ".getChild(0)" : "");
+  eq TypeEndpointTarget.realGetterMethodName() = getterMethodeNameHelper(false);
+  eq TypeEndpointTarget.realGetterMethodCall() = realGetterMethodName() + (containingEndpointDefinition().indexedSend() ? "(index)" : "()");
   eq TypeEndpointTarget.parentTypeName() = getType().containingTypeDecl().getName();
   eq TypeEndpointTarget.entityName() = getType().getName() + (typeIsList() && !containingEndpointDefinition().getIndexBasedListAccess() ? "List" : "");
 
+  private String TypeEndpointTarget.getterMethodeNameHelper(boolean useForwarding) {
+    return (useForwarding && needForwardingNTA() ? forwardingNTA_Name() :
+            "get" + getType().getName() + (typeIsList() && (!containingEndpointDefinition().getIndexBasedListAccess() ||
+                    containingEndpointDefinition().getWithAdd()) ? "List" : "") + (typeIsOpt() ? "Opt" : "")
+                    + (needForwardingNTA() ? "NoTransform" : ""));
+  }
+
   eq ContextFreeTypeEndpointTarget.getterMethodName() = null;
   eq ContextFreeTypeEndpointTarget.parentTypeName() = getTypeDecl().getName();
   eq ContextFreeTypeEndpointTarget.entityName() = "";
-
 }
 
 aspect MustacheReceiveDefinition {
@@ -319,14 +409,7 @@ aspect MustacheReceiveDefinition {
 
   syn String EndpointDefinition.resolveInListMethodName() = ragconnect().internalRagConnectPrefix() + "_resolve" + entityName() + "InList";
 
-  syn boolean EndpointDefinition.typeIsList() = getEndpointTarget().typeIsList();
-
   // === attributes needed for computing above ones ===
-  syn boolean EndpointTarget.typeIsList() = false;
-  eq TypeEndpointTarget.typeIsList() {
-    return getType().isListComponent();
-  }
-
   syn String EndpointDefinition.uniqueSuffix() = getSend() ? "Send" : "Receive";
 }
 
@@ -335,7 +418,14 @@ aspect MustacheSendDefinition {
   syn boolean RagConnect.configLoggingEnabledForWrites() = getConfiguration().getLoggingEnabledForWrites();
 
   // === EndpointDefinition ===
-  syn String EndpointDefinition.lastValue() = senderName() + ".lastValue";
+  syn String EndpointDefinition.lastValueGetterCall() = senderName() + ".getLastValue(" +
+    (getIndexBasedListAccess() ? "index" : "") + ")";
+
+  syn String EndpointDefinition.lastValueSetter() = senderName() + ".setLastValue";
+
+  syn boolean EndpointDefinition.needForwardingNTA() = getEndpointTarget().needForwardingNTA();
+  syn String EndpointDefinition.forwardingNTA_Name() = getEndpointTarget().forwardingNTA_Name();
+  syn String EndpointDefinition.forwardingNTA_Type() = getEndpointTarget().forwardingNTA_Type();
 
   syn String EndpointDefinition.senderName() = getEndpointTarget().senderName();
 
@@ -348,6 +438,24 @@ aspect MustacheSendDefinition {
   syn String EndpointDefinition.writeMethodName() = toMustache().writeMethodName();
 
   // === attributes needed for computing above ones ===
+  syn boolean EndpointTarget.needForwardingNTA() = false;
+  eq TypeEndpointTarget.needForwardingNTA() = containingEndpointDefinition().getSend() && !getType().getNTA();
+
+  syn String EndpointTarget.forwardingNTA_Name() = null;
+  eq TypeEndpointTarget.forwardingNTA_Name() = ragconnect().internalRagConnectPrefix() + getType().getName();
+
+  syn String EndpointTarget.forwardingNTA_Type() = null;
+  eq TypeEndpointTarget.forwardingNTA_Type() = getType().forwardingNTA_Type(
+          containingEndpointDefinition().getIndexBasedListAccess());
+
+  syn String TypeComponent.forwardingNTA_Type(boolean indexBasedListAccess);
+  eq NormalComponent.forwardingNTA_Type(boolean indexBasedListAccess) = getTypeDecl().getName();
+  eq OptComponent.forwardingNTA_Type(boolean indexBasedListAccess) =
+          ragconnect().configJastAddOpt() + "<" + getTypeDecl().getName() + ">";
+  eq ListComponent.forwardingNTA_Type(boolean indexBasedListAccess) = indexBasedListAccess ?
+          getTypeDecl().getName() :
+          ragconnect().configJastAddList() + "<" + getTypeDecl().getName() + ">";
+
   syn String EndpointTarget.senderName();
   eq TokenEndpointTarget.senderName() = ragconnect().internalRagConnectPrefix() + "_sender_" + getToken().getName();
   eq TypeEndpointTarget.senderName() = ragconnect().internalRagConnectPrefix() + "_sender_" + getType().getName();
@@ -426,6 +534,14 @@ aspect MustacheTokenComponent {
 aspect MustacheTypeDecl {
   // === TypeComponent ===
   syn String TypeComponent.parentTypeName() = containingTypeDecl().getName();
+  syn String TypeComponent.disconnectMethodName() {
+    List<TypeEndpointTarget> typeEndpointTargets = getTypeEndpointTargets();
+    if (typeEndpointTargets.isEmpty()) {
+      return "MISSING_ENDPOINT";
+    } else {
+      return typeEndpointTargets.get(0).containingEndpointDefinition().disconnectMethodName();
+    }
+  }
 
   // === TypeDecl ===
   syn List<TypeComponent> TypeDecl.occurencesInProductionRules() {
@@ -443,7 +559,8 @@ aspect MustacheTypeDecl {
 }
 
 aspect AttributesForMustache {
-  syn String MEndpointDefinition.lastValue() = getEndpointDefinition().lastValue();
+  syn String MEndpointDefinition.lastValueGetterCall() = getEndpointDefinition().lastValueGetterCall();
+  syn String MEndpointDefinition.lastValueSetter() = getEndpointDefinition().lastValueSetter();
 
   // token and type are potentially dangerous because asXEndpointTarget can return null
   syn TokenComponent EndpointDefinition.token() = getEndpointTarget().asTokenEndpointTarget().getToken();
@@ -473,8 +590,8 @@ aspect AttributesForMustache {
     return isSend ? new MContextFreeTypeSendDefinition() : new MContextFreeTypeReceiveDefinition();
   }
   MEndpointDefinition UntypedEndpointTarget.createMEndpointDefinition(boolean isSend) {
-    throw new RuntimeException("Untyped endpoint target type, typeName= " +
-        getTypeName() + ", childName=" + getChildName());
+    throw new RuntimeException("Could not resolve endpoint target '" +
+        getTypeName() + "." + getChildName() + "'");
   }
 }
 
diff --git a/ragconnect.base/src/main/jastadd/IntermediateToYAML.jrag b/ragconnect.base/src/main/jastadd/IntermediateToYAML.jrag
index 867d3beedd3cef611ec6721a7365bcda365c3635..fb198ce9cfe34db3234cc5a7feff5ddfb210dc91 100644
--- a/ragconnect.base/src/main/jastadd/IntermediateToYAML.jrag
+++ b/ragconnect.base/src/main/jastadd/IntermediateToYAML.jrag
@@ -100,7 +100,8 @@ aspect IntermediateToYAML {
     result.put("parentTypeName" , parentTypeName());
 
     if (getSend()) {
-      result.put("lastValue" , lastValue());
+      result.put("lastValueGetterCall" , lastValueGetterCall());
+      result.put("lastValueSetter" , lastValueSetter());
       result.put("senderName" , senderName());
       result.put("shouldSendValue" , shouldSendValue());
       result.put("tokenResetMethodName" , tokenResetMethodName());
diff --git a/ragconnect.base/src/main/jastadd/Mappings.jrag b/ragconnect.base/src/main/jastadd/Mappings.jrag
index 45d28e8d99c02292780fed005642e72489a1c58e..738028b3a264a0c7dcf976618aa0b63f35be62c5 100644
--- a/ragconnect.base/src/main/jastadd/Mappings.jrag
+++ b/ragconnect.base/src/main/jastadd/Mappings.jrag
@@ -236,12 +236,8 @@ aspect Mappings {
 
   // --- suitableSendDefaultMapping ---
   syn DefaultMappingDefinition EndpointDefinition.suitableSendDefaultMapping() {
-    if (getEndpointTarget().isTypeEndpointTarget()) {
-      try {
-        TypeDecl typeDecl = program().resolveTypeDecl(targetTypeName());
-        return typeIsList() && !getIndexBasedListAccess() ? ragconnect().defaultListTreeToBytesMapping() : ragconnect().defaultTreeToBytesMapping(typeDecl.getName());
-      } catch (Exception ignore) {
-      }
+    if (getEndpointTarget().isTypeEndpointTarget() && typeIsList() && !getIndexBasedListAccess()) {
+      return ragconnect().defaultListTreeToBytesMapping();
     }
     switch (targetTypeName()) {
       case "boolean":
@@ -270,9 +266,9 @@ aspect Mappings {
       default:
         try {
           TypeDecl typeDecl = program().resolveTypeDecl(targetTypeName());
-          // TODO: also support list-types, if list is last type
           return ragconnect().defaultTreeToBytesMapping(typeDecl.getName());
         } catch (Exception ignore) {
+          // exception should be logged to debug/fine
         }
         System.err.println("Could not find suitable default mapping for " + targetTypeName() + " on " + this);
         return null;
diff --git a/ragconnect.base/src/main/jastadd/NameResolution.jrag b/ragconnect.base/src/main/jastadd/NameResolution.jrag
index cf29d470e5734409b9cc8debd48ef70c3e281638..6d9b1a78abb66f3628d66c259d92f1e90dce94b4 100644
--- a/ragconnect.base/src/main/jastadd/NameResolution.jrag
+++ b/ragconnect.base/src/main/jastadd/NameResolution.jrag
@@ -13,15 +13,22 @@ aspect RagConnectNameResolution {
     return result;
   }
 
-  // --- lookupTypeEndpointDefinition ---
-  inh java.util.List<EndpointDefinition> EndpointDefinition.lookupTypeEndpointDefinitions(TypeComponent type);
-  inh java.util.List<EndpointDefinition> EndpointTarget.lookupTypeEndpointDefinitions(TypeComponent type);
-  eq RagConnect.getConnectSpecificationFile().lookupTypeEndpointDefinitions(TypeComponent type) = lookupTypeEndpointDefinitions(type);
-  syn java.util.List<EndpointDefinition> RagConnect.lookupTypeEndpointDefinitions(TypeComponent type) {
+  // --- lookupGivenTypeEndpointDefinition ---
+  inh java.util.List<EndpointDefinition> EndpointDefinition.lookupGivenTypeEndpointDefinitions(TypeComponent type);
+  inh java.util.List<EndpointDefinition> EndpointTarget.lookupGivenTypeEndpointDefinitions(TypeComponent type);
+  eq RagConnect.getConnectSpecificationFile().lookupGivenTypeEndpointDefinitions(TypeComponent type) = lookupTypeEndpointDefinitions(type, true);
+
+  // --- lookupAllTypeEndpointDefinition ---
+  inh java.util.List<EndpointDefinition> EndpointDefinition.lookupAllTypeEndpointDefinitions(TypeComponent type);
+  inh java.util.List<EndpointDefinition> EndpointTarget.lookupAllTypeEndpointDefinitions(TypeComponent type);
+  eq RagConnect.getConnectSpecificationFile().lookupAllTypeEndpointDefinitions(TypeComponent type) = lookupTypeEndpointDefinitions(type, false);
+
+  syn java.util.List<EndpointDefinition> RagConnect.lookupTypeEndpointDefinitions(TypeComponent type, boolean onlyGiven) {
     java.util.List<EndpointDefinition> result = new java.util.ArrayList<>();
-    for (EndpointTarget target : givenEndpointTargetList()) {
+    for (EndpointDefinition def : onlyGiven ? givenEndpointDefinitionList() : allEndpointDefinitionList()) {
+      EndpointTarget target = def.getEndpointTarget();
       if (target.isTypeEndpointTarget() && target.asTypeEndpointTarget().getType().equals(type)) {
-        result.add(target.containingEndpointDefinition());
+        result.add(def);
       }
     }
     return result;
diff --git a/ragconnect.base/src/main/jastadd/Printing.jrag b/ragconnect.base/src/main/jastadd/Printing.jrag
index 89b9b82a9abea9786979b3864449f529b57bbd96..bcc7cfb231c1a4e4117c634ff082f78d5fd26320 100644
--- a/ragconnect.base/src/main/jastadd/Printing.jrag
+++ b/ragconnect.base/src/main/jastadd/Printing.jrag
@@ -1,7 +1,7 @@
 aspect Printing {
   syn String MappingDefinitionType.prettyPrint();
-  eq JavaMappingDefinitionType.prettyPrint() = getType().getName();
-  eq JavaArrayMappingDefinitionType.prettyPrint() = getType().getName() + "[]";
+  eq JavaMappingDefinitionType.prettyPrint() = getType().prettyPrint();
+  eq JavaArrayMappingDefinitionType.prettyPrint() = getType().prettyPrint() + "[]";
 
   syn String JavaTypeUse.prettyPrint() {
     StringBuilder sb = new StringBuilder();
diff --git a/ragconnect.base/src/main/jastadd/RagConnect.relast b/ragconnect.base/src/main/jastadd/RagConnect.relast
index 37ec5feaecbff82163d7ca1360c9916abfaed36e..01a0f2f884a2f0824bf2787eaf42d7d2d5797c41 100644
--- a/ragconnect.base/src/main/jastadd/RagConnect.relast
+++ b/ragconnect.base/src/main/jastadd/RagConnect.relast
@@ -12,7 +12,7 @@ rel TokenEndpointTarget.Token <-> TokenComponent.TokenEndpointTarget*;
 TypeEndpointTarget : EndpointTarget;
 rel TypeEndpointTarget.Type <-> TypeComponent.TypeEndpointTarget*;
 ContextFreeTypeEndpointTarget : EndpointTarget;
-rel ContextFreeTypeEndpointTarget.TypeDecl <-> TypeDecl.ContextFreeTypeEndpointTarget?;
+rel ContextFreeTypeEndpointTarget.TypeDecl <-> TypeDecl.ContextFreeTypeEndpointTarget*;
 UntypedEndpointTarget : EndpointTarget ::= <TypeName> <ChildName>;  // only used by parser
 // to be integrated:
 //AttributeEndpointTarget : EndpointTarget ::= <Name> ;
@@ -36,6 +36,7 @@ Configuration ::=
 <LoggingEnabledForWrites:boolean>
 <LoggingEnabledForIncremental:boolean>
 <JastAddList:String>
+<JastAddOpt:String>
 <IncrementalOptionActive:boolean>
 <ExperimentalJastAdd329:boolean>;
 rel Configuration.RootNode -> TypeDecl ;
diff --git a/ragconnect.base/src/main/jastadd/parser/ParserRewrites.jrag b/ragconnect.base/src/main/jastadd/parser/ParserRewrites.jrag
index 23dbabaef4ce3d4e0ce114591d1c4ee61937b878..f4a8912fe7c2affa7034d3eb8651f9d210bbba54 100644
--- a/ragconnect.base/src/main/jastadd/parser/ParserRewrites.jrag
+++ b/ragconnect.base/src/main/jastadd/parser/ParserRewrites.jrag
@@ -39,4 +39,5 @@ aspect ParserRewrites {
   eq UntypedEndpointTarget.isAlreadyDefined() = false;
   eq UntypedEndpointTarget.entityIsNormalAttribute() = false;
   eq UntypedEndpointTarget.targetTypeName() = "<untyped.targetTypeName>";
+  eq UntypedEndpointTarget.isTypeEndpointTarget() = false;
 }
diff --git a/ragconnect.base/src/main/jastadd/parser/RagConnect.parser b/ragconnect.base/src/main/jastadd/parser/RagConnect.parser
index 2c326763856f437e770e9bf5ba1566c86f2a2fdb..54a388e51d35bc52296fd4f60e220d325be00674 100644
--- a/ragconnect.base/src/main/jastadd/parser/RagConnect.parser
+++ b/ragconnect.base/src/main/jastadd/parser/RagConnect.parser
@@ -54,6 +54,7 @@ EndpointDefinition endpoint_definition
 
 EndpointDefinition endpoint_definition_type
   = SEND endpoint_target.t                        {: return createEndpointDefinition(t, true,  false, false); :}
+  | SEND INDEXED endpoint_target.t                {: return createEndpointDefinition(t, true,  true,  false); :}
   | RECEIVE endpoint_target.t                     {: return createEndpointDefinition(t, false, false, false); :}
   | RECEIVE INDEXED endpoint_target.t             {: return createEndpointDefinition(t, false, true,  false); :}
   | RECEIVE WITH ADD endpoint_target.t            {: return createEndpointDefinition(t, false, false, true ); :}
diff --git a/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java b/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java
index b94e0ddf2f97b18721084ae1c26c4687b53b3a3e..d8c9c550d183416e353900e97b6629383b31016e 100644
--- a/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java
+++ b/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java
@@ -41,13 +41,22 @@ public class Compiler extends AbstractCompiler {
 
   @Override
   protected int compile() throws CompilerException {
+    compile0();
+    return 0;
+  }
+
+  /**
+   * Compiles with given options. Either successful, or throws exception upon failure.
+   * @throws CompilerException if something went wrong
+   */
+  private void compile0() throws CompilerException {
     if (getConfiguration().shouldPrintVersion()) {
       System.out.println(readVersion());
-      return 0;
+      return;
     }
     if (getConfiguration().shouldPrintHelp()) {
       getConfiguration().printHelp(System.out);
-      return 0;
+      return;
     }
     if (optionVerbose.value()) {
       LOGGER.setLevel(Level.FINE);
@@ -64,11 +73,15 @@ public class Compiler extends AbstractCompiler {
     }
 
     if (!optionRootNode.isMatched()) {
-      return error("Root node not specified");
+      throw new CompilerException("Root node not specified");
     }
 
     RagConnect ragConnect = parseProgram(getConfiguration().getFiles());
-    setConfiguration(ragConnect);
+    try {
+      setConfiguration(ragConnect);
+    } catch (RuntimeException re) {
+      throw new CompilerException("Failed to parse all files", re);
+    }
 
     if (!ragConnect.errors().isEmpty()) {
       StringBuilder sb = new StringBuilder("Errors:\n");
@@ -83,7 +96,7 @@ public class Compiler extends AbstractCompiler {
       String yamlContent = ragConnect.toYAML().prettyPrint();
       LOGGER.fine(yamlContent);
       writeToFile(getConfiguration().outputDir().toPath().resolve("RagConnect.yml"), yamlContent);
-      return 0;
+      return;
     }
 
     LOGGER.fine("Writing output files");
@@ -109,9 +122,13 @@ public class Compiler extends AbstractCompiler {
       Path outputFile = getConfiguration().outputDir().toPath().resolve(grammarFile.getFileName());
       writeToFile(outputFile, grammarFile.generateAbstractGrammar());
     }
-    writeToFile(getConfiguration().outputDir().toPath().resolve("RagConnect.jadd"),
-        generateAspect(ragConnect));
-    return 0;
+    String aspectCode;
+    try {
+      aspectCode = generateAspect(ragConnect);
+    } catch (RuntimeException re) {
+      throw new CompilerException("Could not generate RagConnect aspect", re);
+    }
+    writeToFile(getConfiguration().outputDir().toPath().resolve("RagConnect.jadd"), aspectCode);
   }
 
   public static void main(String[] args) {
@@ -272,6 +289,10 @@ public class Compiler extends AbstractCompiler {
     return new File(filename).getName();
   }
 
+  /**
+   * Set all configuration values.
+   * @param ragConnect the RagConnect instance to set configuration values
+   */
   private void setConfiguration(RagConnect ragConnect) {
     ragConnect.setConfiguration(new Configuration());
     ragConnect.getConfiguration().setLoggingEnabledForReads(optionLogReads.value());
@@ -284,10 +305,18 @@ public class Compiler extends AbstractCompiler {
     ragConnect.getConfiguration().setIncrementalOptionActive(incrementalOptionActive);
     LOGGER.fine(() -> "ragConnect.getConfiguration().IncrementalOptionActive = " + incrementalOptionActive);
 
-    // reuse "--List" option of JastAdd
+    // reuse "--List" and "--Opt" options of JastAdd
     ragConnect.getConfiguration().setJastAddList(this.getConfiguration().listType());
+    ragConnect.getConfiguration().setJastAddOpt(this.getConfiguration().optType());
 
-    ragConnect.getConfiguration().setRootNode(ragConnect.getProgram().resolveTypeDecl(optionRootNode.value()));
+    final TypeDecl rootNode;
+    try {
+      rootNode = ragConnect.getProgram().resolveTypeDecl(optionRootNode.value());
+    } catch (RuntimeException re) {
+      // root node was not found
+      throw new RuntimeException("Could not resolve root node '" + optionRootNode.value() + "'!", re);
+    }
+    ragConnect.getConfiguration().setRootNode(rootNode);
 
     // Handler ::= <ClassName> <UniqueName> <InUse:boolean>;
     ragConnect.addHandler(new Handler("MqttHandler.jadd", "MqttServerHandler", "mqtt", optionProtocols.hasValue(OPTION_PROTOCOL_MQTT)));
@@ -317,4 +346,11 @@ public class Compiler extends AbstractCompiler {
     m.execute(new java.io.PrintWriter(new org.jastadd.ragconnect.compiler.AppendableWriter(sb)), ragConnect);
     return sb.toString();
   }
+
+  @Override
+  protected int error(String message) {
+    LOGGER.log(Level.SEVERE, message);
+    return 1;
+  }
+
 }
diff --git a/ragconnect.base/src/main/resources/handler.mustache b/ragconnect.base/src/main/resources/handler.mustache
index 37923f4419371cea7bbafe2eab9ad7cb745fe425..a7ef5977683a236275a14eb37b9775d24f31526d 100644
--- a/ragconnect.base/src/main/resources/handler.mustache
+++ b/ragconnect.base/src/main/resources/handler.mustache
@@ -13,6 +13,10 @@ aspect RagConnectHandler {
     {{#Handlers}}
     {{#InUse}}{{fieldName}}.close();{{/InUse}}
     {{/Handlers}}
+    {{#configIncrementalOptionActive}}
+    trace().setReceiver({{observerInstanceSingletonMethodName}}().oldReceiver);
+    {{observerInstanceResetMethodName}}();
+    {{/configIncrementalOptionActive}}
   }
 
 {{#mqttHandler}}
@@ -62,7 +66,7 @@ aspect RagConnectHandler {
   class RagConnectPublisher {
     java.util.List<Runnable> senders = new java.util.ArrayList<>();
     java.util.Map<RagConnectToken, Runnable> tokenToSender;
-    byte[] lastValue;
+    private byte[] lastValue;
 
     void add(Runnable sender, RagConnectToken connectToken) {
       if (tokenToSender == null) {
@@ -73,24 +77,89 @@ aspect RagConnectHandler {
     }
 
     boolean remove(RagConnectToken token) {
-      if (tokenToSender == null) {
-        System.err.println("Removing sender before first addition for " + token.entityName + " at " + token.uri);
+      String errorMessage = internal_remove(token);
+      if (errorMessage == null) {
+        return true;
+      } else {
+        System.err.println(errorMessage);
         return false;
       }
+    }
+
+    /**
+    * (internal) Removes the token, returning an error message if there is one.
+    * @param token the token to be removed
+    * @return an error message (upon error), or null (upon success)
+    */
+    String internal_remove(RagConnectToken token) {
+      if (tokenToSender == null) {
+        return "Removing sender before first addition for " + token.entityName + " at " + token.uri;
+      }
       Runnable sender = tokenToSender.remove(token);
       if (sender == null) {
-        System.err.println("Could not find connected sender for " + token.entityName + " at " + token.uri);
-        return false;
+        return "Could not find connected sender for " + token.entityName + " at " + token.uri;
       }
       boolean success = senders.remove(sender);
       if (senders.isEmpty()) {
         lastValue = null;
       }
-      return success;
+      return success ? null : "Could not remove sender for " + token.entityName + " at " + token.uri;
     }
 
     void run() {
       senders.forEach(Runnable::run);
     }
+
+    byte[] getLastValue() {
+      return lastValue;
+    }
+
+    void setLastValue(byte[] value) {
+      this.lastValue = value;
+    }
+  }
+
+  class RagConnectMappingPublisher {
+    java.util.Map<Integer, RagConnectPublisher> publishers = new java.util.HashMap<>();
+
+    void add(Runnable sender, int index, RagConnectToken connectToken) {
+      publishers.computeIfAbsent(index, ignoredIndex -> new RagConnectPublisher()).add(sender, connectToken);
+    }
+
+    boolean remove(RagConnectToken token) {
+      // publishers.forEach((index, publisher) -> publisher.remove(token));
+      // remove token from each publisher, at least one has to successfully remove the token to make this call a success
+      boolean result = false;
+      java.util.List<String> errorMessages = new java.util.ArrayList<>();
+      for (RagConnectPublisher publisher : publishers.values()) {
+        String errorMessage = publisher.internal_remove(token);
+        if (errorMessage == null) {
+          result = true;
+        } else {
+          errorMessages.add(errorMessage);
+        }
+      }
+      if (!result) {
+        // only print error message, if all publishers failed to remove the token
+        errorMessages.stream().forEachOrdered(System.err::println);
+      }
+      return result;
+    }
+
+    void run(int index) {
+      java.util.Optional.ofNullable(publishers.get(index)).ifPresent(RagConnectPublisher::run);
+    }
+
+    byte[] getLastValue(int index) {
+      RagConnectPublisher publisher = publishers.get(index);
+      if (publisher == null) {
+        return null;
+      }
+      return publisher.getLastValue();
+    }
+
+    void setLastValue(int index, final byte[] value) {
+      java.util.Optional.ofNullable(publishers.get(index)).ifPresent(publisher -> publisher.setLastValue(value));
+    }
   }
 }
diff --git a/ragconnect.base/src/main/resources/ragconnect.mustache b/ragconnect.base/src/main/resources/ragconnect.mustache
index 6917f46e42437b58cdcd168c31c0a06c647e99f9..fbe3dd572676b60fdb826ec01b098d8a6aa7252a 100644
--- a/ragconnect.base/src/main/resources/ragconnect.mustache
+++ b/ragconnect.base/src/main/resources/ragconnect.mustache
@@ -32,6 +32,39 @@ aspect RagConnect {
   {{> typeDecl}}
   {{/typeDeclsOfContextFreeEndpointTargets}}
 
+  {{! --- touchedTerminals ---}}
+  {{#allTypeDecls}}
+    {{Name}} {{Name}}.{{touchedTerminalsMethodName}}() {
+    {{#tokenComponents}}
+      get{{Name}}();
+    {{/tokenComponents}}
+    {{#normalComponents}}
+      get{{Name}}().{{touchedTerminalsMethodName}}();
+    {{/normalComponents}}
+    {{#listComponents}}
+      for ({{#TypeDecl}}{{Name}}{{/TypeDecl}} element : get{{Name}}List()) {
+        element.{{touchedTerminalsMethodName}}();
+      }
+    {{/listComponents}}
+    return this;
+  }
+  {{/allTypeDecls}}
+  ASTNode ASTNode.{{touchedTerminalsMethodName}}() {
+    return this;
+  }
+  {{configJastAddList}}<T> {{configJastAddList}}.{{touchedTerminalsMethodName}}() {
+    for (T child : this) {
+      child.{{touchedTerminalsMethodName}}();
+    }
+    return this;
+  }
+  {{configJastAddOpt}}<T> {{configJastAddOpt}}.{{touchedTerminalsMethodName}}() {
+    if (getChild(0) != null) {
+      getChild(0).{{touchedTerminalsMethodName}}();
+    }
+    return this;
+  }
+
   {{> ListAspect}}
 
   public void {{rootNodeName}}.ragconnectCheckIncremental() {
@@ -57,12 +90,17 @@ aspect RagConnectObserver {
       final RagConnectToken connectToken;
       final ASTNode node;
       final String attributeString;
+      final boolean compareParams;
+      final Object params;
       final Runnable attributeCall;
 
-      RagConnectObserverEntry(RagConnectToken connectToken, ASTNode node, String attributeString, Runnable attributeCall) {
+      RagConnectObserverEntry(RagConnectToken connectToken, ASTNode node, String attributeString,
+                              boolean compareParams, Object params, Runnable attributeCall) {
         this.connectToken = connectToken;
         this.node = node;
         this.attributeString = attributeString;
+        this.compareParams = compareParams;
+        this.params = params;
         this.attributeCall = attributeCall;
       }
     }
@@ -96,11 +134,21 @@ aspect RagConnectObserver {
     }
 
     void add(RagConnectToken connectToken, ASTNode node, String attributeString, Runnable attributeCall) {
+      internal_add(connectToken, node, attributeString, false, null, attributeCall);
+    }
+    void add(RagConnectToken connectToken, ASTNode node, String attributeString, Object params, Runnable attributeCall) {
+      internal_add(connectToken, node, attributeString, true, params, attributeCall);
+    }
+
+    private void internal_add(RagConnectToken connectToken, ASTNode node, String attributeString,
+        boolean compareParams, Object params, Runnable attributeCall) {
       {{#configLoggingEnabledForIncremental}}
-      System.out.println("** observer add: " + node + " on " + attributeString);
+      System.out.println("** observer add: " + node + " on " + attributeString + (compareParams ? " (parameterized)" : ""));
       {{/configLoggingEnabledForIncremental}}
-      observedNodes.add(new RagConnectObserverEntry(connectToken, node, attributeString, attributeCall));
+      observedNodes.add(new RagConnectObserverEntry(connectToken, node, attributeString,
+                                                    compareParams, params, attributeCall));
     }
+
     void remove(RagConnectToken connectToken) {
       observedNodes.removeIf(entry -> entry.connectToken.equals(connectToken));
     }
@@ -127,7 +175,7 @@ aspect RagConnectObserver {
         entryQueue.clear();
         startEntry = null;
         {{#configLoggingEnabledForIncremental}}
-        System.out.println("** observer process (" + entriesToProcess.length + "): " + node + " on " + attribute);
+        System.out.println("** observer process (entries: " + entriesToProcess.length + "): " + node + " on " + attribute);
         {{/configLoggingEnabledForIncremental}}
         for (RagConnectObserverEntry entry : entriesToProcess) {
           entry.attributeCall.run();
@@ -146,7 +194,7 @@ aspect RagConnectObserver {
       {{/configLoggingEnabledForIncremental}}
       // iterate through list, if matching pair. could maybe be more efficient.
       for (RagConnectObserverEntry entry : observedNodes) {
-        if (entry.node.equals(node) && entry.attributeString.equals(attribute)) {
+        if (entry.node.equals(node) && entry.attributeString.equals(attribute) && (!entry.compareParams || java.util.Objects.equals(entry.params, params))) {
           // hit. call the attribute/nta-token
           {{#configLoggingEnabledForIncremental}}
           System.out.println("** observer hit: " + entry.node + " on " + entry.attributeString);
@@ -162,13 +210,16 @@ aspect RagConnectObserver {
     }
   }
 
-  private static RagConnectObserver ASTNode.{{internalRagConnectPrefix}}ObserverInstance;
-  RagConnectObserver ASTNode.{{internalRagConnectPrefix}}Observer() {
-    if ({{internalRagConnectPrefix}}ObserverInstance == null) {
+  private static RagConnectObserver ASTNode.{{observerInstanceFieldName}};
+  RagConnectObserver ASTNode.{{observerInstanceSingletonMethodName}}() {
+    if ({{observerInstanceFieldName}} == null) {
       // does not matter, which node is used to create the observer as ASTState/tracing is also static
-    {{internalRagConnectPrefix}}ObserverInstance = new RagConnectObserver(this);
+      {{observerInstanceFieldName}} = new RagConnectObserver(this);
     }
-    return {{internalRagConnectPrefix}}ObserverInstance;
+    return {{observerInstanceFieldName}};
+  }
+  void ASTNode.{{observerInstanceResetMethodName}}() {
+    {{observerInstanceFieldName}} = null;
   }
 }
 {{/configIncrementalOptionActive}}
diff --git a/ragconnect.base/src/main/resources/receiveDefinition.mustache b/ragconnect.base/src/main/resources/receiveDefinition.mustache
index 25dd2a484abd288091f4a501f6fe8e06a90c1f95..3bd20363ab557af22e937f7cbc35a869df3dc075 100644
--- a/ragconnect.base/src/main/resources/receiveDefinition.mustache
+++ b/ragconnect.base/src/main/resources/receiveDefinition.mustache
@@ -34,7 +34,7 @@ public boolean {{parentTypeName}}.{{connectMethodName}}(String {{connectParamete
  {{#typeIsList}}
   {{^IndexBasedListAccess}}
    {{#WithAdd}}
-    {{getterMethodName}}().addAll({{lastResult}});
+    {{getterMethodCall}}.addAll({{lastResult}});
    {{/WithAdd}}
    {{^WithAdd}}
     set{{entityName}}({{lastResult}});
@@ -43,7 +43,7 @@ public boolean {{parentTypeName}}.{{connectMethodName}}(String {{connectParamete
   {{#IndexBasedListAccess}}
     {{lastResult}}.set{{idTokenName}}(topic);
    {{#WithAdd}}
-    {{getterMethodName}}().add({{lastResult}});
+    {{getterMethodCall}}.add({{lastResult}});
    {{/WithAdd}}
    {{^WithAdd}}
     set{{entityName}}({{lastResult}}, index);
@@ -70,16 +70,16 @@ public boolean {{parentTypeName}}.{{connectMethodName}}(String {{connectParamete
 */
 public boolean {{parentTypeName}}.{{connectMethodName}}(String {{connectParameterName}}) throws java.io.IOException {
   java.util.function.BiConsumer<String, byte[]> consumer = (topic, message) -> {
+    int index = {{resolveInListMethodName}}(topic);
     {{> mappingApplication}}
 {{#configLoggingEnabledForReads}}
     System.out.println("[Receive] " + {{connectParameterName}} + " (" + topic + ") -> {{entityName}} = " + {{lastResult}});
 {{/configLoggingEnabledForReads}}
     {{lastResult}}.set{{idTokenName}}(topic);
-    int resolvedIndex = {{resolveInListMethodName}}(topic);
-    if (resolvedIndex == -1) {
+    if (index == -1) {
       add{{entityName}}({{lastResult}});
     } else {
-      set{{entityName}}({{lastResult}}, resolvedIndex);
+      set{{entityName}}({{lastResult}}, index);
     }
   };
   return {{internalConnectMethodName}}({{connectParameterName}}, consumer);
diff --git a/ragconnect.base/src/main/resources/sendDefinition.mustache b/ragconnect.base/src/main/resources/sendDefinition.mustache
index 9b80bcdef5966f011c29f1b891b17337e33ff91d..293b37c65f2159687fbbe81c8eb112789dd952e9 100644
--- a/ragconnect.base/src/main/resources/sendDefinition.mustache
+++ b/ragconnect.base/src/main/resources/sendDefinition.mustache
@@ -1,6 +1,6 @@
-private RagConnectPublisher {{parentTypeName}}.{{senderName}} = new RagConnectPublisher();
+private RagConnect{{#IndexBasedListAccess}}Mapping{{/IndexBasedListAccess}}Publisher {{parentTypeName}}.{{senderName}} = new RagConnect{{#IndexBasedListAccess}}Mapping{{/IndexBasedListAccess}}Publisher();
 
-public boolean {{parentTypeName}}.{{connectMethodName}}(String {{connectParameterName}}, boolean writeCurrentValue) throws java.io.IOException {
+public boolean {{parentTypeName}}.{{connectMethodName}}(String {{connectParameterName}}{{#IndexBasedListAccess}}, int index{{/IndexBasedListAccess}}, boolean writeCurrentValue) throws java.io.IOException {
   {{>handleUri}}
   RagConnectToken connectToken = new RagConnectToken(uri, "{{entityName}}");
   boolean success;
@@ -12,13 +12,13 @@ public boolean {{parentTypeName}}.{{connectMethodName}}(String {{connectParamete
       final String topic = {{attributeName}}().extractTopic(uri);
       {{senderName}}.add(() -> {
         {{#configLoggingEnabledForWrites}}
-        System.out.println("[Send] {{entityName}} = " + {{getterMethodName}}() + " -> " + {{connectParameterName}});
+        System.out.println("[Send] {{entityName}} = " + {{getterMethodCall}} + " -> " + {{connectParameterName}});
         {{/configLoggingEnabledForWrites}}
-        handler.publish(topic, {{lastValue}});
-        }, connectToken);
-      {{updateMethodName}}();
+        handler.publish(topic, {{lastValueGetterCall}});
+        }{{#IndexBasedListAccess}}, index{{/IndexBasedListAccess}}, connectToken);
+      {{updateMethodName}}({{#IndexBasedListAccess}}index{{/IndexBasedListAccess}});
       if (writeCurrentValue) {
-        {{writeMethodName}}();
+        {{writeMethodName}}({{#IndexBasedListAccess}}index{{/IndexBasedListAccess}});
       }
       success = true;
       break;
@@ -28,8 +28,8 @@ public boolean {{parentTypeName}}.{{connectMethodName}}(String {{connectParamete
   {{#InUse}}
     case "rest":
       success = {{attributeName}}().newGETConnection(connectToken, () -> {
-        {{updateMethodName}}();
-        return new String({{lastValue}});
+        {{updateMethodName}}({{#IndexBasedListAccess}}index{{/IndexBasedListAccess}});
+        return new String({{lastValueGetterCall}});
       });
       break;
   {{/InUse}}
@@ -41,16 +41,24 @@ public boolean {{parentTypeName}}.{{connectMethodName}}(String {{connectParamete
   if (success) {
     connectTokenMap.add(this, false, connectToken);
     {{#configIncrementalOptionActive}}
-    {{internalRagConnectPrefix}}Observer().add(connectToken, this, "{{getterMethodName}}", () -> {
-      if (this.{{updateMethodName}}()) {
-        this.{{writeMethodName}}();
+        {{!todo maybe getterMethodName needs to be change for indexed send}}
+      {{observerInstanceSingletonMethodName}}().add(
+      connectToken,
+      this,
+      "{{getterMethodName}}{{#IndexBasedListAccess}}_int{{/IndexBasedListAccess}}",
+      {{#IndexBasedListAccess}}index,{{/IndexBasedListAccess}}
+      () -> {
+        if (this.{{updateMethodName}}({{#IndexBasedListAccess}}index{{/IndexBasedListAccess}})) {
+          this.{{writeMethodName}}({{#IndexBasedListAccess}}index{{/IndexBasedListAccess}});
+        }
       }
-    });
+    );
     {{/configIncrementalOptionActive}}
   }
   return success;
 }
 
+{{!todo check if index parameter is needed here for typeIsList and indexBasedListAccess}}
 public boolean {{parentTypeName}}.{{disconnectMethodName}}(String {{connectParameterName}}) throws java.io.IOException {
   {{>handleUri}}
   java.util.List<RagConnectToken> connectTokens = connectTokenMap.removeAll(this, false, uri, "{{entityName}}");
@@ -59,7 +67,7 @@ public boolean {{parentTypeName}}.{{disconnectMethodName}}(String {{connectParam
     return false;
   }
   {{#configIncrementalOptionActive}}
-  connectTokens.forEach(token -> {{internalRagConnectPrefix}}Observer().remove(token));
+  connectTokens.forEach(token -> {{observerInstanceSingletonMethodName}}().remove(token));
   {{/configIncrementalOptionActive}}
   RagConnectDisconnectHandlerMethod disconnectingMethod;
   switch (scheme) {
@@ -88,16 +96,20 @@ public boolean {{parentTypeName}}.{{disconnectMethodName}}(String {{connectParam
   return success;
 }
 
-protected boolean {{parentTypeName}}.{{updateMethodName}}() {
+protected boolean {{parentTypeName}}.{{updateMethodName}}({{#IndexBasedListAccess}}int index{{/IndexBasedListAccess}}) {
   {{^shouldSendValue}}
   {{tokenResetMethodName}}();
   {{/shouldSendValue}}
   {{> mappingApplication}}
-  {{lastValue}} = {{lastResult}};
+  {{lastValueSetter}}({{#IndexBasedListAccess}}index, {{/IndexBasedListAccess}}{{lastResult}});
   // normally we would return true here. unless no connect method was called so far to initialize {{senderName}} yet
   return {{senderName}} != null;
 }
 
-protected void {{parentTypeName}}.{{writeMethodName}}() {
-  {{senderName}}.run();
+protected void {{parentTypeName}}.{{writeMethodName}}({{#IndexBasedListAccess}}int index{{/IndexBasedListAccess}}) {
+  {{senderName}}.run({{#IndexBasedListAccess}}index{{/IndexBasedListAccess}});
 }
+
+{{#needForwardingNTA}}
+syn {{{forwardingNTA_Type}}} {{parentTypeName}}.{{forwardingNTA_Name}}({{#IndexBasedListAccess}}int index{{/IndexBasedListAccess}}) = {{realGetterMethodCall}}.{{touchedTerminalsMethodName}}();
+{{/needForwardingNTA}}
diff --git a/ragconnect.base/src/main/resources/typeDecl.mustache b/ragconnect.base/src/main/resources/typeDecl.mustache
index 643fbc80a4eccfa50da5d48650e03d4380badb62..e37001ee779e6d5fcf471a80085a6a2817b7882b 100644
--- a/ragconnect.base/src/main/resources/typeDecl.mustache
+++ b/ragconnect.base/src/main/resources/typeDecl.mustache
@@ -14,16 +14,16 @@ inh int {{Name}}._ragconnect_myIndexInList();
   {{/isListComponent}}
 {{/occurencesInProductionRules}}
 
-{{#ContextFreeTypeEndpointTarget}}{{#containingEndpointDefinition}}
-public boolean {{Name}}.{{connectMethodName}}(String {{connectParameterName}}) throws java.io.IOException {
+{{#ContextFreeTypeEndpointTargets}}{{#containingEndpointDefinition}}
+public boolean {{Name}}.{{connectMethodName}}(String {{connectParameterName}}{{#Send}}, boolean writeCurrentValue{{/Send}}) throws java.io.IOException {
   switch (_ragconnect_myContext()) {
   {{#occurencesInProductionRules}}
-    {{!only using "connectMethodName" is not correct, since the actual name might be different, e.g., if both send and receive are defined. need a reference to the actual endpoint-definition here}}
+    {{!TODO only using "connectMethodName" is not correct, since the actual name might be different, e.g., if both send and receive are defined. need a reference to the actual endpoint-definition here}}
     {{^isListComponent}}
-    case "{{parentTypeName}}.{{Name}}": return (({{parentTypeName}}) _ragconnect_myParent()).{{connectMethodName}}{{Name}}({{connectParameterName}});
+    case "{{parentTypeName}}.{{Name}}": return (({{parentTypeName}}) _ragconnect_myParent()).{{connectMethodName}}{{Name}}({{connectParameterName}}{{#Send}}, writeCurrentValue{{/Send}});
     {{/isListComponent}}
     {{#isListComponent}}
-    case "{{parentTypeName}}.{{Name}}": return (({{parentTypeName}}) _ragconnect_myParent()).{{connectMethodName}}{{Name}}({{connectParameterName}}, _ragconnect_myIndexInList());
+    case "{{parentTypeName}}.{{Name}}": return (({{parentTypeName}}) _ragconnect_myParent()).{{connectMethodName}}{{Name}}({{connectParameterName}}, _ragconnect_myIndexInList(){{#Send}}, writeCurrentValue{{/Send}});
     {{/isListComponent}}
   {{/occurencesInProductionRules}}
     default:
@@ -35,11 +35,11 @@ public boolean {{Name}}.{{connectMethodName}}(String {{connectParameterName}}) t
 public boolean {{Name}}.{{disconnectMethodName}}(String {{connectParameterName}}) throws java.io.IOException {
   switch (_ragconnect_myContext()) {
 {{#occurencesInProductionRules}}
-  case "{{parentTypeName}}.{{Name}}": return (({{parentTypeName}}) _ragconnect_myParent()).{{disconnectMethodName}}{{Name}}({{connectParameterName}});
+  case "{{parentTypeName}}.{{Name}}": return (({{parentTypeName}}) _ragconnect_myParent()).{{disconnectMethodName}}({{connectParameterName}});
 {{/occurencesInProductionRules}}
     default:
       System.err.println("No matching context while disconnecting for " + this + " from " + {{connectParameterName}});
       return false;
   }
 }
-{{/containingEndpointDefinition}}{{/ContextFreeTypeEndpointTarget}}
+{{/containingEndpointDefinition}}{{/ContextFreeTypeEndpointTargets}}
diff --git a/ragconnect.tests/build.gradle b/ragconnect.tests/build.gradle
index 865abd9e99d657441dde62ba38112f0e3b18604f..8d073b55ac6b14ebdf6b295bd993dda64dbbd701 100644
--- a/ragconnect.tests/build.gradle
+++ b/ragconnect.tests/build.gradle
@@ -31,6 +31,10 @@ group = 'de.tudresden.inf.st'
 
 repositories {
     mavenCentral()
+    maven {
+        name "gitlab-maven"
+        url "https://git-st.inf.tu-dresden.de/api/v4/groups/jastadd/-/packages/maven"
+    }
 }
 tasks.compileTestJava {
     options.release.set(11)
@@ -39,12 +43,13 @@ tasks.compileTestJava {
 dependencies {
     implementation project(':ragconnect.base')
 
-    runtimeOnly group: 'org.jastadd', name: 'jastadd', version: '2.3.5-dresden'
-//    runtimeOnly fileTree(include: ['jastadd2.jar'], dir: '../libs')
+//    runtimeOnly group: 'org.jastadd', name: 'jastadd', version: '2.3.5-dresden'
+    runtimeOnly fileTree(include: ['jastadd2.jar'], dir: '../libs')
 
     testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.4.0'
     testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.4.0'
     testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.12.1'
+    testImplementation group: 'org.awaitility', name: 'awaitility', version: '4.1.1'
 
     // jackson (for serialization of types)
     implementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.12.1'
@@ -137,6 +142,15 @@ clean {
     delete fileTree(dir: 'src/test/java-gen/', exclude: '.gitignore')
 }
 
+def JASTADD_INCREMENTAL_OPTIONS = ['--tracing=cache,flush',
+                               '--incremental=param',
+                               '--cache=all',
+                               '--rewrite=cnta',
+                               '--flush=full']
+def JASTADD_INCREMENTAL_OPTIONS_TRACING_FULL = JASTADD_INCREMENTAL_OPTIONS.clone()
+JASTADD_INCREMENTAL_OPTIONS_TRACING_FULL.set(0, '--tracing=all')
+JASTADD_INCREMENTAL_OPTIONS_TRACING_FULL.set(1, '--incremental=param,debug')
+
 // --- Test: Example ---
 task compileExampleTest(type: RagConnectTest) {
     ragconnect {
@@ -305,11 +319,7 @@ task compileIncremental(type: RagConnectTest) {
         jastAddList = 'JastAddList'
         packageName = 'incremental.ast'
         inputFiles = [file('src/test/01-input/incremental/Test.jadd')]
-        extraOptions = ['--tracing=cache,flush',
-                        '--incremental=param',
-                        '--cache=all',
-                        '--rewrite=cnta',
-                        '--flush=full']
+        extraOptions = JASTADD_INCREMENTAL_OPTIONS
     }
 }
 
@@ -371,11 +381,7 @@ task compileTreeIncremental(type: RagConnectTest) {
         jastAddList = 'JastAddList'
         packageName = 'treeInc.ast'
         inputFiles = [file('src/test/01-input/tree/Test.jadd')]
-        extraOptions = ['--tracing=cache,flush',
-                        '--incremental=param',
-                        '--cache=all',
-                        '--rewrite=cnta',
-                        '--flush=full']
+        extraOptions = JASTADD_INCREMENTAL_OPTIONS
     }
 }
 
@@ -418,11 +424,7 @@ task compileTreeAllowedTokensIncremental(type: RagConnectTest) {
         jastAddList = 'JastAddList'
         packageName = 'treeAllowedTokensInc.ast'
         inputFiles = [file('src/test/01-input/treeAllowedTokens/Test.jadd')]
-        extraOptions = ['--tracing=cache,flush',
-                        '--incremental=param',
-                        '--cache=all',
-                        '--rewrite=cnta',
-                        '--flush=full']
+        extraOptions = JASTADD_INCREMENTAL_OPTIONS
     }
 }
 
@@ -464,11 +466,7 @@ task compileListIncremental(type: RagConnectTest, dependsOn: ':ragconnect.base:j
         jastAddList = 'JastAddList'
         packageName = 'listInc.ast'
         inputFiles = [file('src/test/01-input/list/Test.jadd')]
-        extraOptions = ['--tracing=cache,flush',
-                        '--incremental=param',
-                        '--cache=all',
-                        '--rewrite=cnta',
-                        '--flush=full']
+        extraOptions = JASTADD_INCREMENTAL_OPTIONS
     }
 }
 
@@ -510,11 +508,7 @@ task compileSingleListIncremental(type: RagConnectTest, dependsOn: ':ragconnect.
         jastAddList = 'JastAddList'
         packageName = 'singleListInc.ast'
         inputFiles = [file('src/test/01-input/singleList/Test.jadd')]
-        extraOptions = ['--tracing=cache,flush',
-                        '--incremental=param',
-                        '--cache=all',
-                        '--rewrite=cnta',
-                        '--flush=full']
+        extraOptions = JASTADD_INCREMENTAL_OPTIONS
     }
 }
 
@@ -557,11 +551,7 @@ task compileSingleListVariantIncremental(type: RagConnectTest, dependsOn: ':ragc
         jastAddList = 'JastAddList'
         packageName = 'singleListVariantInc.ast'
         inputFiles = [file('src/test/01-input/singleListVariant/Test.jadd')]
-        extraOptions = ['--tracing=cache,flush',
-                        '--incremental=param',
-                        '--cache=all',
-                        '--rewrite=cnta',
-                        '--flush=full']
+        extraOptions = JASTADD_INCREMENTAL_OPTIONS
     }
 }
 
@@ -582,10 +572,52 @@ task compileContextFreeSimpleIncremental(type: RagConnectTest, dependsOn: ':ragc
     jastadd {
         jastAddList = 'JastAddList'
         packageName = 'contextFreeSimpleInc.ast'
-        extraOptions = ['--tracing=cache,flush',
-                        '--incremental=param',
-                        '--cache=all',
-                        '--rewrite=cnta',
-                        '--flush=full']
+        extraOptions = JASTADD_INCREMENTAL_OPTIONS
+    }
+}
+
+// --- Test: forwarding-incremental ---
+task compileForwardingIncremental(type: RagConnectTest, dependsOn: ':ragconnect.base:jar') {
+    ragconnect {
+        outputDir = file('src/test/02-after-ragconnect/forwardingInc')
+        inputFiles = [file('src/test/01-input/forwarding/Test.relast'),
+                      file('src/test/01-input/forwarding/Test.connect')]
+        rootNode = 'Root'
+        extraOptions = ['--experimental-jastadd-329']
+    }
+    relast {
+        useJastAddNames = true
+        grammarName = 'src/test/03-after-relast/forwardingInc/forwardingInc'
+        serializer = 'jackson'
+    }
+    jastadd {
+        jastAddList = 'JastAddList'
+        packageName = 'forwardingInc.ast'
+        extraOptions = JASTADD_INCREMENTAL_OPTIONS
+    }
+}
+
+// --- Test: indexed-send-incremental ---
+task compileIndexedSendIncremental(type: RagConnectTest, dependsOn: ':ragconnect.base:jar') {
+    ragconnect {
+        outputDir = file('src/test/02-after-ragconnect/indexedSendInc')
+        inputFiles = [file('src/test/01-input/indexedSend/Test.relast'),
+                      file('src/test/01-input/indexedSend/Test.connect')]
+        rootNode = 'Root'
+        logWrites = true
+        logReads = true
+        logIncremental = true
+        extraOptions = ['--experimental-jastadd-329']
+    }
+    relast {
+        useJastAddNames = true
+        grammarName = 'src/test/03-after-relast/indexedSendInc/indexedSendInc'
+        serializer = 'jackson'
+    }
+    jastadd {
+        jastAddList = 'JastAddList'
+        packageName = 'indexedSendInc.ast'
+        inputFiles = [file('src/test/01-input/indexedSend/Test.jadd')]
+        extraOptions = JASTADD_INCREMENTAL_OPTIONS_TRACING_FULL
     }
 }
diff --git a/ragconnect.tests/src/test/01-input/errors/Errors.relast b/ragconnect.tests/src/test/01-input/errors/Errors.relast
index f700f39abdcac18174e192bfc6098d95e4720573..dd3fc0faf5bea8d4718f4cc429605758fb2bd972 100644
--- a/ragconnect.tests/src/test/01-input/errors/Errors.relast
+++ b/ragconnect.tests/src/test/01-input/errors/Errors.relast
@@ -1,7 +1,7 @@
 A ::= B C D E* ;
 
 // read definitions
-B ::= /<ErrorNTA:String>/ <ErrorTypeOfFirstMapping:String> <ErrorTypeOfLastMapping:String> <DoubledValue:int> <ErrorTypeMismatch:String> ;
+B ::= /<ErrorNTA:String>/ <ErrorTypeOfFirstMapping:java.util.List<String>> <ErrorTypeOfLastMapping:String> <DoubledValue:int> <ErrorTypeMismatch:String> ;
 
 // write definitions
 C ::= /<ErrorTypeOfFirstMapping:String>/ /<ErrorTypeOfLastMapping1:String>/ /<ErrorTypeOfLastMapping2:List<String>>/ /<ErrorTypeMismatch:String>/ /<DoubledValue:int>/ ;
diff --git a/ragconnect.tests/src/test/01-input/errors/Part.expected b/ragconnect.tests/src/test/01-input/errors/Part.expected
index 86bb6dd0d9cc23865031bdcf89ba71c441f85a7f..d5e2f0db2b1f5a20af7e4ceb50741c6d8e4dce45 100644
--- a/ragconnect.tests/src/test/01-input/errors/Part.expected
+++ b/ragconnect.tests/src/test/01-input/errors/Part.expected
@@ -1,10 +1,10 @@
 Part1.connect Line 3, column 1: Endpoint definition already defined for B.DoubledValue
 Part1.connect Line 4, column 1: Endpoint definition already defined for B.DoubledValue
 Part1.connect Line 10, column 1: Receiving target token must not be an NTA token!
-Part1.connect Line 13, column 1: No suitable default mapping found for type java.util.List
-Part1.connect Line 13, column 1: to-type of last mapping (java.util.List) not assignable to type of the token (String)!
-Part1.connect Line 16, column 1: to-type of last mapping (List) not assignable to type of the token (String)!
+Part1.connect Line 13, column 1: No suitable default mapping found for type java.util.List<String>
+Part1.connect Line 16, column 1: to-type of last mapping (java.util.List<String>) not assignable to type of the token (String)!
 Part1.connect Line 19, column 1: to-type of last mapping (int) not assignable to type of the token (String)!
+Part1.connect Line 26, column 6: Could not resolve endpoint target C.ErrorNotResolved
 Part2.connect Line 5, column 1: Endpoint definition already defined for C.DoubledValue
 Part2.connect Line 6, column 1: Endpoint definition already defined for C.DoubledValue
 Part2.connect Line 17, column 1: The name of a dependency definition must not be equal to a list-node on the source
diff --git a/ragconnect.tests/src/test/01-input/errors/Part1.connect b/ragconnect.tests/src/test/01-input/errors/Part1.connect
index a1be3918b36ee19561a278639555db14efb8da06..d6594a6d84861a9bd846886c58806c1be468539e 100644
--- a/ragconnect.tests/src/test/01-input/errors/Part1.connect
+++ b/ragconnect.tests/src/test/01-input/errors/Part1.connect
@@ -22,8 +22,8 @@ receive B.ErrorTypeMismatch using StringToList, IntToInt ;
 // NOT HANDLED \\ Error: the token must be resolvable within the parent type
 // NOT HANDLED \\ receive C.NonExisting ;
 
-// Error: Token must be a TokenNTA (i.e., check for Token.getNTA())
-send C.ErrorNotNTA ;
+// Error: type not resolved
+send C.ErrorNotResolved ;
 
 // Error: from-type of first mapping must be type of Token
 send C.ErrorTypeOfFirstMapping using IntToInt ;
diff --git a/ragconnect.tests/src/test/01-input/errors/Part2.connect b/ragconnect.tests/src/test/01-input/errors/Part2.connect
index 4d1148260a36855528b37f8b45020e076ff9ff1e..617c595dafc45ba4b9d8fe915eea377c54f4aa79 100644
--- a/ragconnect.tests/src/test/01-input/errors/Part2.connect
+++ b/ragconnect.tests/src/test/01-input/errors/Part2.connect
@@ -27,7 +27,7 @@ ListToList maps java.util.List<String> list to java.util.List<String> {:
   return list;
 :}
 
-StringToList maps String s to List<String> {:
+StringToList maps String s to java.util.List<String> {:
   java.util.List<String> result = new java.util.ArrayList<>();
   result.add(s);
   return result;
diff --git a/ragconnect.tests/src/test/01-input/errors/Standard.connect b/ragconnect.tests/src/test/01-input/errors/Standard.connect
index 844c2e97b4f6e32e0e1f3f1392065a2f0ea3efbc..6224df4184a2f7cc21d913934785cd022b14f5b0 100644
--- a/ragconnect.tests/src/test/01-input/errors/Standard.connect
+++ b/ragconnect.tests/src/test/01-input/errors/Standard.connect
@@ -10,10 +10,10 @@ receive B.DoubledValue using IntToInt ;
 receive B.ErrorNTA ;
 
 // Error: from-type of first mapping must be byte[] or a supported primitive type
-receive B.ErrorTypeOfFirstMapping using ListToList ;
+receive B.ErrorTypeOfFirstMapping using ListToList, ListToList ;
 
 // Error: to-type of last mapping must be type of the Token
-receive B.ErrorTypeOfLastMapping using StringToList ;
+receive B.ErrorTypeOfLastMapping using StringToString, StringToList ;
 
 // Error: types of mappings must match (modulo inheritance)
 receive B.ErrorTypeMismatch using StringToList, IntToInt ;
@@ -26,8 +26,8 @@ receive E;
 receive A.E;
 
 // --- send definitions ---
-// NOT HANDLED \\ Error: the token must be resolvable within the parent type
-// NOT HANDLED \\ receive C.NonExisting ;
+// Error: the token must be resolvable within the parent type
+receive C.NonExisting ;
 
 // NOT HANDLED \\ // Error: from-type of first mapping must be type of Token
 // NOT HANDLED \\ send C.ErrorTypeOfFirstMapping using IntToInt ;
@@ -65,12 +65,16 @@ ListToList maps java.util.List<String> list to java.util.List<String> {:
   return list;
 :}
 
-StringToList maps String s to List<String> {:
+StringToList maps String s to java.util.List<String> {:
   java.util.List<String> result = new java.util.ArrayList<>();
   result.add(s);
   return result;
 :}
 
+StringToString maps String s to String {:
+  return s + "1";
+:}
+
 IntToInt maps int number to int {:
   return number + 1;
 :}
diff --git a/ragconnect.tests/src/test/01-input/errors/Standard.expected b/ragconnect.tests/src/test/01-input/errors/Standard.expected
index d258c2c599b54523d4ecff92eb95af8f419a9ec9..9b3d23ac98118e0857ffb9964e1a04d592b1dea1 100644
--- a/ragconnect.tests/src/test/01-input/errors/Standard.expected
+++ b/ragconnect.tests/src/test/01-input/errors/Standard.expected
@@ -1,12 +1,12 @@
 Standard.connect Line 3, column 1: Endpoint definition already defined for B.DoubledValue
 Standard.connect Line 4, column 1: Endpoint definition already defined for B.DoubledValue
 Standard.connect Line 10, column 1: Receiving target token must not be an NTA token!
-Standard.connect Line 13, column 1: No suitable default mapping found for type java.util.List
-Standard.connect Line 13, column 1: to-type of last mapping (java.util.List) not assignable to type of the token (String)!
-Standard.connect Line 16, column 1: to-type of last mapping (List) not assignable to type of the token (String)!
+Standard.connect Line 13, column 1: No suitable default mapping found for type java.util.List<String>
+Standard.connect Line 16, column 1: to-type of last mapping (java.util.List<String>) not assignable to type of the token (String)!
 Standard.connect Line 19, column 1: to-type of last mapping (int) not assignable to type of the token (String)!
 Standard.connect Line 22, column 9: Context-Free endpoint not allowed for root node A!
 Standard.connect Line 26, column 1: Clash with implied, indexed endpoint definition of context-free endpoint in line 25!
+Standard.connect Line 30, column 9: Could not resolve endpoint target C.NonExisting
 Standard.connect Line 43, column 1: Endpoint definition already defined for C.DoubledValue
 Standard.connect Line 44, column 1: Endpoint definition already defined for C.DoubledValue
 Standard.connect Line 55, column 1: The name of a dependency definition must not be equal to a list-node on the source
diff --git a/ragconnect.tests/src/test/01-input/forwarding/README.md b/ragconnect.tests/src/test/01-input/forwarding/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..522815706491182dbd0766ee6b2e83e6a5e5d64a
--- /dev/null
+++ b/ragconnect.tests/src/test/01-input/forwarding/README.md
@@ -0,0 +1,6 @@
+# Forwarding
+
+Idea: Use send definitions targeting only non-NTAs (thus, requiring the creation of implicit NTAs).
+Also test context-free endpoint definitions targeting non-NTAs to test compatibility of those two features.
+
+Note: When a type occurs in a list, the context-free endpoint definition will force an (implicit) indexed send for that context.
diff --git a/ragconnect.tests/src/test/01-input/forwarding/Test.connect b/ragconnect.tests/src/test/01-input/forwarding/Test.connect
new file mode 100644
index 0000000000000000000000000000000000000000..066623e6330e807c058384a4172df7d7f59de594
--- /dev/null
+++ b/ragconnect.tests/src/test/01-input/forwarding/Test.connect
@@ -0,0 +1,54 @@
+send SenderRoot.A;
+send SenderRoot.SingleA;
+send SenderRoot.MaybeA;
+send SenderRoot.MultipleA;
+
+send B;
+send indexed SenderRoot.MultipleB using AddOtherSuffixForB;
+
+send SenderRoot.C using PrependPrefix;
+send SenderRoot.SingleC using PrependPrefix;
+send SenderRoot.MaybeC using PrependPrefix;
+send SenderRoot.MultipleC using ListPrependPrefix;
+
+send D using AddSuffix;
+send indexed SenderRoot.MultipleD using AddOtherSuffixForD;
+
+AddOtherSuffixForB maps B b to B {:
+  B result = new B();
+  result.setValue(b.getValue() + "-other");
+  return result;
+:}
+
+PrependPrefix maps C c to C {:
+  C result = new C();
+  result.setValue("pre-" + c.getValue());
+  return result;
+:}
+
+ListPrependPrefix maps JastAddList<C> cList to JastAddList<C> {:
+  JastAddList<C> result = new JastAddList<C>();
+  for (C c : cList) {
+    result.add(c.treeCopy().setValue("pre-" + c.getValue()));
+  }
+  return result;
+:}
+
+AddSuffix maps D d to D {:
+  D result = new D();
+  result.setValue(d.getValue() + "-post");
+  return result;
+:}
+
+AddOtherSuffixForD maps D d to D {:
+  D result = new D();
+  result.setValue(d.getValue() + "-other");
+  return result;
+:}
+
+receive ReceiverRoot.A;
+receive ReceiverRoot.ManyA;
+receive ReceiverRoot.B;
+receive ReceiverRoot.C;
+receive ReceiverRoot.ManyC;
+receive ReceiverRoot.D;
diff --git a/ragconnect.tests/src/test/01-input/forwarding/Test.relast b/ragconnect.tests/src/test/01-input/forwarding/Test.relast
new file mode 100644
index 0000000000000000000000000000000000000000..058aaf00e4a3ca7d36734ac924ec110486820ce9
--- /dev/null
+++ b/ragconnect.tests/src/test/01-input/forwarding/Test.relast
@@ -0,0 +1,14 @@
+Root ::= SenderRoot ReceiverRoot;
+
+SenderRoot ::= A SingleA:A [MaybeA:A] MultipleA:A*
+         B SingleB:B [MaybeB:B] MultipleB:B*
+         C SingleC:C [MaybeC:C] MultipleC:C*
+         D SingleD:D [MaybeD:D] MultipleD:D*
+;
+
+ReceiverRoot ::= A ManyA:A* B C ManyC:C* D ;
+
+A ::= <Value> ;
+B ::= <Value> ;
+C ::= <Value> ;
+D ::= <Value> ;
diff --git a/ragconnect.tests/src/test/01-input/indexedSend/README.md b/ragconnect.tests/src/test/01-input/indexedSend/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..0de8c51d137e617dc8a8a5f6234c93a89b4b6fd5
--- /dev/null
+++ b/ragconnect.tests/src/test/01-input/indexedSend/README.md
@@ -0,0 +1,3 @@
+# Indexed Send
+
+Idea: Use send definitions on (parts of) a list to send only some of its elements, similar to `receive indexed`.
diff --git a/ragconnect.tests/src/test/01-input/indexedSend/Test.connect b/ragconnect.tests/src/test/01-input/indexedSend/Test.connect
new file mode 100644
index 0000000000000000000000000000000000000000..3ff49e174a5afddac148e0a99374255ec0b61646
--- /dev/null
+++ b/ragconnect.tests/src/test/01-input/indexedSend/Test.connect
@@ -0,0 +1,14 @@
+send indexed SenderRoot.MultipleA;
+
+send indexed SenderRoot.MultipleAWithSuffix using AddSuffix;
+
+AddSuffix maps A a to A {:
+  A result = new A();
+  String changedValue = a.getValue() + "post";
+  result.setValue(changedValue);
+  result.setInner(new Inner("inner" + changedValue));
+  return result;
+:}
+
+receive indexed ReceiverRoot.ManyA;
+receive indexed ReceiverRoot.ManyAWithSuffix;
diff --git a/ragconnect.tests/src/test/01-input/indexedSend/Test.jadd b/ragconnect.tests/src/test/01-input/indexedSend/Test.jadd
new file mode 100644
index 0000000000000000000000000000000000000000..d37dd045dbf11754fbd9066faae1613ea2a95e97
--- /dev/null
+++ b/ragconnect.tests/src/test/01-input/indexedSend/Test.jadd
@@ -0,0 +1,12 @@
+aspect NameResolution {
+  // overriding customID guarantees to produce the same JSON representation for equal lists
+  // otherwise, the value for id is different each time
+  @Override
+  protected String A.customID() {
+    return getClass().getSimpleName() + getValue();
+  }
+  @Override
+  protected String Inner.customID() {
+    return getClass().getSimpleName() + getInnerValue();
+  }
+}
diff --git a/ragconnect.tests/src/test/01-input/indexedSend/Test.relast b/ragconnect.tests/src/test/01-input/indexedSend/Test.relast
new file mode 100644
index 0000000000000000000000000000000000000000..9480f18d9efcd27452e548044f4caca335d4312a
--- /dev/null
+++ b/ragconnect.tests/src/test/01-input/indexedSend/Test.relast
@@ -0,0 +1,5 @@
+Root ::= SenderRoot ReceiverRoot;
+SenderRoot ::= MultipleA:A* MultipleAWithSuffix:A* ;
+ReceiverRoot ::= ManyA:A* ManyAWithSuffix:A* ;
+A ::= <Value> Inner ;
+Inner ::= <InnerValue> ;
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AbstractMqttTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AbstractMqttTest.java
index db48fdf9a525e242bed1c39f7adf54260668a08a..648e6d300b06b72721efd2d75ba5134723aaee16 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AbstractMqttTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AbstractMqttTest.java
@@ -1,6 +1,8 @@
 package org.jastadd.ragconnect.tests;
 
 import defaultOnlyRead.ast.MqttHandler;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
 import org.junit.jupiter.api.*;
 
 import java.io.IOException;
@@ -15,10 +17,22 @@ import java.util.concurrent.TimeUnit;
 public abstract class AbstractMqttTest {
 
   private static boolean checkDone = false;
+
   protected static MqttHandler publisher;
 
+  protected Logger logger = LogManager.getLogger(getClass());
+
+  /**
+   * if the initial/current value shall be sent upon connecting
+   */
+  private boolean writeCurrentValue;
+
+  public boolean isWriteCurrentValue() {
+    return writeCurrentValue;
+  }
+
   @BeforeAll
-  public static void createPublishAndOnceCheckMqttConnection() {
+  public static void createPublisherAndCheckMqttConnectionOnce() {
     boolean checkResult;
     try {
       publisher = new MqttHandler("Publisher")
@@ -45,9 +59,13 @@ public abstract class AbstractMqttTest {
   @Tag("mqtt")
   @Test
   public final void testCommunicateSendInitialValue() throws IOException, InterruptedException {
+    this.writeCurrentValue = true;
+
+    logger.debug("Start testCommunicateSendInitialValue");
     createModel();
-    setupReceiverAndConnect(true);
+    setupReceiverAndConnect();
 
+    logger.debug("Calling communicateSendInitialValue");
     communicateSendInitialValue();
   }
 
@@ -60,9 +78,13 @@ public abstract class AbstractMqttTest {
   @Tag("mqtt")
   @Test
   public final void testCommunicateOnlyUpdatedValue() throws IOException, InterruptedException {
+    this.writeCurrentValue = false;
+
+    logger.debug("Start testCommunicateOnlyUpdatedValue");
     createModel();
-    setupReceiverAndConnect(false);
+    setupReceiverAndConnect();
 
+    logger.debug("Calling communicateOnlyUpdatedValue");
     communicateOnlyUpdatedValue();
   }
 
@@ -80,32 +102,36 @@ public abstract class AbstractMqttTest {
   /**
    * Begin with this snippet
    * <pre>
-   *     model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
+   * {@code
+   * model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
    *
-   *     handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
-   *     assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS));
+   * handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
+   * assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS));
+   * }
    * </pre>
    *
    * And then add dependencies, initialise receiver, add connections to those receivers,
    * and finally call generated connect* methods on model elements.
-   * @param writeCurrentValue if the initial/current value shall be sent upon connecting
    */
-  protected abstract void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException, InterruptedException;
+  protected abstract void setupReceiverAndConnect() throws IOException, InterruptedException;
 
   @AfterEach
   public void alwaysCloseConnections() {
+    logger.debug("Closing connections");
     closeConnections();
   }
 
   /**
    * Write the following snippet (using your correct handler and model):
    * <pre>
+   * {@code
    * if (handler != null) {
    *    handler.close();
    * }
    * if (model != null) {
    *   model.ragconnectCloseConnections();
    * }
+   * }
    * </pre>
    */
   protected abstract void closeConnections();
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ContextFreeSimpleTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ContextFreeSimpleTest.java
index afd6c59ed3a506dd5944f02b986bb6bbc6f32c4b..ee32fbf61208b93ab0934167cfb7b276099a5079 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ContextFreeSimpleTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ContextFreeSimpleTest.java
@@ -54,7 +54,7 @@ public class ContextFreeSimpleTest extends AbstractMqttTest {
   }
 
   @Override
-  protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException, InterruptedException {
+  protected void setupReceiverAndConnect() throws IOException, InterruptedException {
     model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
 
     assertTrue(unnamedA.connect(mqttUri(TOPIC_UNNAMED)));
@@ -163,6 +163,6 @@ public class ContextFreeSimpleTest extends AbstractMqttTest {
 
   @Override
   protected void closeConnections() {
-
+    model.ragconnectCloseConnections();
   }
 }
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyReadTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyReadTest.java
index d9cf9d92a456a02cd2acba9e5f8990527d5842e9..356f2b139107223cecde7645f2e4908824aa4355 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyReadTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyReadTest.java
@@ -65,7 +65,7 @@ public class DefaultOnlyReadTest extends AbstractMqttTest {
   }
 
   @Override
-  protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException {
+  protected void setupReceiverAndConnect() throws IOException {
     model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
 
     assertTrue(integers.connectBooleanValue(mqttUri(TOPIC_NATIVE_BOOLEAN)));
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyWriteTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyWriteTest.java
index d8ce0c083148b671590c6129d16f8d3ed071e313..d30912c88f4f49ad9dc776aecabf492ae4a77bec 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyWriteTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyWriteTest.java
@@ -94,7 +94,7 @@ public class DefaultOnlyWriteTest extends AbstractMqttTest {
   }
 
   @Override
-  protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException {
+  protected void setupReceiverAndConnect() throws IOException {
     model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
 
     receiver = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
@@ -137,39 +137,39 @@ public class DefaultOnlyWriteTest extends AbstractMqttTest {
     dataNormal = createReceiver(false);
     dataTransformed = createReceiver(true);
 
-    assertTrue(nativeIntegers.connectBooleanValue(mqttUri(TOPIC_NATIVE_BOOLEAN), writeCurrentValue));
-    assertTrue(nativeIntegers.connectIntValue(mqttUri(TOPIC_NATIVE_INT), writeCurrentValue));
-    assertTrue(nativeIntegers.connectShortValue(mqttUri(TOPIC_NATIVE_SHORT), writeCurrentValue));
-    assertTrue(nativeIntegers.connectLongValue(mqttUri(TOPIC_NATIVE_LONG), writeCurrentValue));
-    assertTrue(nativeFloats.connectFloatValue(mqttUri(TOPIC_NATIVE_FLOAT), writeCurrentValue));
-    assertTrue(nativeFloats.connectDoubleValue(mqttUri(TOPIC_NATIVE_DOUBLE), writeCurrentValue));
-    assertTrue(nativeChars.connectCharValue(mqttUri(TOPIC_NATIVE_CHAR), writeCurrentValue));
-    assertTrue(nativeChars.connectStringValue(mqttUri(TOPIC_NATIVE_STRING), writeCurrentValue));
-
-    assertTrue(nativeIntegers.connectBooleanValueTransformed(mqttUri(TOPIC_NATIVE_BOOLEAN_TRANSFORMED), writeCurrentValue));
-    assertTrue(nativeIntegers.connectIntValueTransformed(mqttUri(TOPIC_NATIVE_INT_TRANSFORMED), writeCurrentValue));
-    assertTrue(nativeIntegers.connectShortValueTransformed(mqttUri(TOPIC_NATIVE_SHORT_TRANSFORMED), writeCurrentValue));
-    assertTrue(nativeIntegers.connectLongValueTransformed(mqttUri(TOPIC_NATIVE_LONG_TRANSFORMED), writeCurrentValue));
-    assertTrue(nativeFloats.connectFloatValueTransformed(mqttUri(TOPIC_NATIVE_FLOAT_TRANSFORMED), writeCurrentValue));
-    assertTrue(nativeFloats.connectDoubleValueTransformed(mqttUri(TOPIC_NATIVE_DOUBLE_TRANSFORMED), writeCurrentValue));
-    assertTrue(nativeChars.connectCharValueTransformed(mqttUri(TOPIC_NATIVE_CHAR_TRANSFORMED), writeCurrentValue));
-    assertTrue(nativeChars.connectStringValueTransformed(mqttUri(TOPIC_NATIVE_STRING_TRANSFORMED), writeCurrentValue));
-
-    assertTrue(boxedIntegers.connectBooleanValue(mqttUri(TOPIC_BOXED_BOOLEAN), writeCurrentValue));
-    assertTrue(boxedIntegers.connectIntValue(mqttUri(TOPIC_BOXED_INTEGER), writeCurrentValue));
-    assertTrue(boxedIntegers.connectShortValue(mqttUri(TOPIC_BOXED_SHORT), writeCurrentValue));
-    assertTrue(boxedIntegers.connectLongValue(mqttUri(TOPIC_BOXED_LONG), writeCurrentValue));
-    assertTrue(boxedFloats.connectFloatValue(mqttUri(TOPIC_BOXED_FLOAT), writeCurrentValue));
-    assertTrue(boxedFloats.connectDoubleValue(mqttUri(TOPIC_BOXED_DOUBLE), writeCurrentValue));
-    assertTrue(boxedChars.connectCharValue(mqttUri(TOPIC_BOXED_CHARACTER), writeCurrentValue));
-
-    assertTrue(boxedIntegers.connectBooleanValueTransformed(mqttUri(TOPIC_BOXED_BOOLEAN_TRANSFORMED), writeCurrentValue));
-    assertTrue(boxedIntegers.connectIntValueTransformed(mqttUri(TOPIC_BOXED_INTEGER_TRANSFORMED), writeCurrentValue));
-    assertTrue(boxedIntegers.connectShortValueTransformed(mqttUri(TOPIC_BOXED_SHORT_TRANSFORMED), writeCurrentValue));
-    assertTrue(boxedIntegers.connectLongValueTransformed(mqttUri(TOPIC_BOXED_LONG_TRANSFORMED), writeCurrentValue));
-    assertTrue(boxedFloats.connectFloatValueTransformed(mqttUri(TOPIC_BOXED_FLOAT_TRANSFORMED), writeCurrentValue));
-    assertTrue(boxedFloats.connectDoubleValueTransformed(mqttUri(TOPIC_BOXED_DOUBLE_TRANSFORMED), writeCurrentValue));
-    assertTrue(boxedChars.connectCharValueTransformed(mqttUri(TOPIC_BOXED_CHARACTER_TRANSFORMED), writeCurrentValue));
+    assertTrue(nativeIntegers.connectBooleanValue(mqttUri(TOPIC_NATIVE_BOOLEAN), isWriteCurrentValue()));
+    assertTrue(nativeIntegers.connectIntValue(mqttUri(TOPIC_NATIVE_INT), isWriteCurrentValue()));
+    assertTrue(nativeIntegers.connectShortValue(mqttUri(TOPIC_NATIVE_SHORT), isWriteCurrentValue()));
+    assertTrue(nativeIntegers.connectLongValue(mqttUri(TOPIC_NATIVE_LONG), isWriteCurrentValue()));
+    assertTrue(nativeFloats.connectFloatValue(mqttUri(TOPIC_NATIVE_FLOAT), isWriteCurrentValue()));
+    assertTrue(nativeFloats.connectDoubleValue(mqttUri(TOPIC_NATIVE_DOUBLE), isWriteCurrentValue()));
+    assertTrue(nativeChars.connectCharValue(mqttUri(TOPIC_NATIVE_CHAR), isWriteCurrentValue()));
+    assertTrue(nativeChars.connectStringValue(mqttUri(TOPIC_NATIVE_STRING), isWriteCurrentValue()));
+
+    assertTrue(nativeIntegers.connectBooleanValueTransformed(mqttUri(TOPIC_NATIVE_BOOLEAN_TRANSFORMED), isWriteCurrentValue()));
+    assertTrue(nativeIntegers.connectIntValueTransformed(mqttUri(TOPIC_NATIVE_INT_TRANSFORMED), isWriteCurrentValue()));
+    assertTrue(nativeIntegers.connectShortValueTransformed(mqttUri(TOPIC_NATIVE_SHORT_TRANSFORMED), isWriteCurrentValue()));
+    assertTrue(nativeIntegers.connectLongValueTransformed(mqttUri(TOPIC_NATIVE_LONG_TRANSFORMED), isWriteCurrentValue()));
+    assertTrue(nativeFloats.connectFloatValueTransformed(mqttUri(TOPIC_NATIVE_FLOAT_TRANSFORMED), isWriteCurrentValue()));
+    assertTrue(nativeFloats.connectDoubleValueTransformed(mqttUri(TOPIC_NATIVE_DOUBLE_TRANSFORMED), isWriteCurrentValue()));
+    assertTrue(nativeChars.connectCharValueTransformed(mqttUri(TOPIC_NATIVE_CHAR_TRANSFORMED), isWriteCurrentValue()));
+    assertTrue(nativeChars.connectStringValueTransformed(mqttUri(TOPIC_NATIVE_STRING_TRANSFORMED), isWriteCurrentValue()));
+
+    assertTrue(boxedIntegers.connectBooleanValue(mqttUri(TOPIC_BOXED_BOOLEAN), isWriteCurrentValue()));
+    assertTrue(boxedIntegers.connectIntValue(mqttUri(TOPIC_BOXED_INTEGER), isWriteCurrentValue()));
+    assertTrue(boxedIntegers.connectShortValue(mqttUri(TOPIC_BOXED_SHORT), isWriteCurrentValue()));
+    assertTrue(boxedIntegers.connectLongValue(mqttUri(TOPIC_BOXED_LONG), isWriteCurrentValue()));
+    assertTrue(boxedFloats.connectFloatValue(mqttUri(TOPIC_BOXED_FLOAT), isWriteCurrentValue()));
+    assertTrue(boxedFloats.connectDoubleValue(mqttUri(TOPIC_BOXED_DOUBLE), isWriteCurrentValue()));
+    assertTrue(boxedChars.connectCharValue(mqttUri(TOPIC_BOXED_CHARACTER), isWriteCurrentValue()));
+
+    assertTrue(boxedIntegers.connectBooleanValueTransformed(mqttUri(TOPIC_BOXED_BOOLEAN_TRANSFORMED), isWriteCurrentValue()));
+    assertTrue(boxedIntegers.connectIntValueTransformed(mqttUri(TOPIC_BOXED_INTEGER_TRANSFORMED), isWriteCurrentValue()));
+    assertTrue(boxedIntegers.connectShortValueTransformed(mqttUri(TOPIC_BOXED_SHORT_TRANSFORMED), isWriteCurrentValue()));
+    assertTrue(boxedIntegers.connectLongValueTransformed(mqttUri(TOPIC_BOXED_LONG_TRANSFORMED), isWriteCurrentValue()));
+    assertTrue(boxedFloats.connectFloatValueTransformed(mqttUri(TOPIC_BOXED_FLOAT_TRANSFORMED), isWriteCurrentValue()));
+    assertTrue(boxedFloats.connectDoubleValueTransformed(mqttUri(TOPIC_BOXED_DOUBLE_TRANSFORMED), isWriteCurrentValue()));
+    assertTrue(boxedChars.connectCharValueTransformed(mqttUri(TOPIC_BOXED_CHARACTER_TRANSFORMED), isWriteCurrentValue()));
   }
 
   private ReceiverData createReceiver(boolean transformed) {
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ExampleTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ExampleTest.java
index 5ad824bcd8de42d998d7825c6930892ecc57e37d..655adf5776c632be7f3c9e8d91efa11dc2d5eba6 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ExampleTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ExampleTest.java
@@ -74,7 +74,7 @@ public class ExampleTest extends AbstractMqttTest {
   }
 
   @Override
-  protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException {
+  protected void setupReceiverAndConnect() throws IOException {
     model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
 
     handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
@@ -97,7 +97,7 @@ public class ExampleTest extends AbstractMqttTest {
       }
     });
 
-    assertTrue(robotArm.connectAppropriateSpeed(mqttUri(TOPIC_CONFIG), writeCurrentValue));
+    assertTrue(robotArm.connectAppropriateSpeed(mqttUri(TOPIC_CONFIG), isWriteCurrentValue()));
     assertTrue(link1.connectCurrentPosition(mqttUri(TOPIC_JOINT1)));
     assertTrue(link2.connectCurrentPosition(mqttUri(TOPIC_JOINT2)));
   }
@@ -249,10 +249,11 @@ public class ExampleTest extends AbstractMqttTest {
   @Test
   public void testFailedConversion() throws IOException {
     createModel();
-    setupReceiverAndConnect(false);
+    setupReceiverAndConnect();
+    int numberOfPreviousConfigs = data.numberOfConfigs;
 
     publisher.publish(TOPIC_JOINT1, "not-a-pandaLinkState".getBytes());
-    assertEquals(0, data.numberOfConfigs);
+    assertEquals(numberOfPreviousConfigs, data.numberOfConfigs);
     assertTrue(data.failedLastConversion);
   }
 
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ForwardingTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ForwardingTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..64430eb40800d7bea28e38e63d0cb73728f5856f
--- /dev/null
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ForwardingTest.java
@@ -0,0 +1,544 @@
+package org.jastadd.ragconnect.tests;
+
+import forwardingInc.ast.*;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.assertj.core.groups.Tuple;
+import org.junit.jupiter.api.Tag;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.groups.Tuple.tuple;
+import static org.awaitility.Awaitility.await;
+import static org.jastadd.ragconnect.tests.TestUtils.mqttUri;
+import static org.jastadd.ragconnect.tests.TestUtils.waitForMqtt;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Test case "forwarding".
+ *
+ * @author rschoene - Initial contribution
+ */
+@Tag("Incremental")
+public class ForwardingTest extends AbstractMqttTest {
+
+  private static final String TOPIC_A_SINGLE = "a-single";
+  private static final String TOPIC_A_MANY = "a-many";
+  private static final String TOPIC_B_SINGLE = "b-single";
+  private static final String TOPIC_C_SINGLE = "c-single";
+  private static final String TOPIC_C_MANY = "c-many";
+  private static final String TOPIC_D_SINGLE = "d-single";
+
+  private static final Random rand = new Random();
+
+  /** Use initially created members as values in {@link #check} method */
+  private static final String INITIAL_VALUE = "initial" + rand.nextInt(100);
+
+  private Root model;
+  private SenderRoot senderRoot;
+  private ReceiverRoot receiverRoot;
+  private MqttHandler handler;
+
+  private ReceiverData data;
+  private final TestUtils.ChangeObserver observer = new TestUtils.ChangeObserver();
+
+  private final Values<A> valuesA = new Values<>();
+  private final Values<B> valuesB = new Values<>();
+  private final Values<C> valuesC = new Values<>();
+  private final Values<D> valuesD = new Values<>();
+
+  @Override
+  protected void createModel() {
+    model = new Root();
+//    model.trace().setReceiver(TestUtils::logEvent);
+    senderRoot = new SenderRoot();
+    model.setSenderRoot(senderRoot);
+
+    valuesA.unnamed = new A().setValue("as1");
+    valuesA.single = new A().setValue("as1");
+    valuesA.opt = new A().setValue("as1");
+    valuesA.list1 = new A().setValue("am1");
+    valuesA.list2 = new A().setValue("am2");
+
+    senderRoot.setA(valuesA.unnamed);
+    senderRoot.setSingleA(valuesA.single);
+    senderRoot.setMaybeA(valuesA.opt);
+    senderRoot.addMultipleA(valuesA.list1);
+    senderRoot.addMultipleA(valuesA.list2);
+
+    valuesB.unnamed = new B().setValue("bs1");
+    valuesB.single = new B().setValue("bs1");
+    valuesB.opt = new B().setValue("bs1");
+    valuesB.list1 = new B().setValue("bs1");
+    valuesB.list2 = new B().setValue("bs2");
+
+    senderRoot.setB(valuesB.unnamed);
+    senderRoot.setSingleB(valuesB.single);
+    senderRoot.setMaybeB(valuesB.opt);
+    senderRoot.addMultipleB(valuesB.list1);
+    senderRoot.addMultipleB(valuesB.list2);
+
+    valuesC.unnamed = new C().setValue("cs1");
+    valuesC.single = new C().setValue("cs1");
+    valuesC.opt = new C().setValue("cs1");
+    valuesC.list1 = new C().setValue("cm1");
+    valuesC.list2 = new C().setValue("cm2");
+
+    senderRoot.setC(valuesC.unnamed);
+    senderRoot.setSingleC(valuesC.single);
+    senderRoot.setMaybeC(valuesC.opt);
+    senderRoot.addMultipleC(valuesC.list1);
+    senderRoot.addMultipleC(valuesC.list2);
+
+    valuesD.unnamed = new D().setValue("ds1");
+    valuesD.single = new D().setValue("ds1");
+    valuesD.opt = new D().setValue("ds1");
+    valuesD.list1 = new D().setValue("ds1");
+    valuesD.list2 = new D().setValue("ds2");
+
+    senderRoot.setD(valuesD.unnamed);
+    senderRoot.setSingleD(valuesD.single);
+    senderRoot.setMaybeD(valuesD.opt);
+    senderRoot.addMultipleD(valuesD.list1);
+    senderRoot.addMultipleD(valuesD.list2);
+
+    receiverRoot = new ReceiverRoot();
+    model.setReceiverRoot(receiverRoot);
+
+    receiverRoot.setA(new A().setValue(INITIAL_VALUE));
+    receiverRoot.setB(new B().setValue(INITIAL_VALUE));
+    receiverRoot.setC(new C().setValue(INITIAL_VALUE));
+    receiverRoot.setD(new D().setValue(INITIAL_VALUE));
+
+    data = new ReceiverData();
+    observer.init(
+            () -> receiverRoot.getA().getValue(),
+            () -> receiverRoot.getNumManyA() > 0 ? receiverRoot.getManyA(0).getValue() : INITIAL_VALUE,
+            () -> receiverRoot.getNumManyA() > 1 ? receiverRoot.getManyA(1).getValue() : INITIAL_VALUE,
+            () -> receiverRoot.getNumManyA() > 2 ? receiverRoot.getManyA(2).getValue() : INITIAL_VALUE,
+            () -> receiverRoot.getB().getValue(),
+            () -> receiverRoot.getC().getValue(),
+            () -> receiverRoot.getNumManyC() > 0 ? receiverRoot.getManyC(0).getValue() : INITIAL_VALUE,
+            () -> receiverRoot.getNumManyC() > 1 ? receiverRoot.getManyC(1).getValue() : INITIAL_VALUE,
+            () -> receiverRoot.getNumManyC() > 2 ? receiverRoot.getManyC(2).getValue() : INITIAL_VALUE,
+            () -> receiverRoot.getD().getValue()
+    );
+  }
+
+  @Override
+  protected void setupReceiverAndConnect() throws IOException, InterruptedException {
+    model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
+
+    // connect receive
+    assertTrue(receiverRoot.connectA(mqttUri(TOPIC_A_SINGLE)));
+    assertTrue(receiverRoot.connectManyAList(mqttUri(TOPIC_A_MANY)));
+    assertTrue(receiverRoot.connectB(mqttUri(TOPIC_B_SINGLE)));
+    assertTrue(receiverRoot.connectC(mqttUri(TOPIC_C_SINGLE)));
+    assertTrue(receiverRoot.connectManyCList(mqttUri(TOPIC_C_MANY)));
+    assertTrue(receiverRoot.connectD(mqttUri(TOPIC_D_SINGLE)));
+
+    handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
+    assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS));
+
+    handler.publish("test", Boolean.toString(isWriteCurrentValue()).getBytes(StandardCharsets.UTF_8));
+
+    handler.newConnection("#", (topic, bytes) -> {
+      data.valuesSent += 1;
+      data.valueSentSinceLastCheck.set(true);
+    });
+
+    // connect send
+    assertTrueAndWaitForValue(senderRoot.connectA(mqttUri(TOPIC_A_SINGLE), isWriteCurrentValue()));
+    assertTrueAndWaitForValue(senderRoot.connectSingleA(mqttUri(TOPIC_A_SINGLE), isWriteCurrentValue()));
+    assertTrueAndWaitForValue(senderRoot.connectMaybeA(mqttUri(TOPIC_A_SINGLE), isWriteCurrentValue()));
+    assertTrueAndWaitForValue(senderRoot.connectMultipleAList(mqttUri(TOPIC_A_MANY), isWriteCurrentValue()));
+
+    assertTrueAndWaitForValue(valuesB.unnamed.connect(mqttUri(TOPIC_B_SINGLE), isWriteCurrentValue()));
+    assertTrueAndWaitForValue(valuesB.single.connect(mqttUri(TOPIC_B_SINGLE), isWriteCurrentValue()));
+    assertTrueAndWaitForValue(valuesB.opt.connect(mqttUri(TOPIC_B_SINGLE), isWriteCurrentValue()));
+    assertTrueAndWaitForValue(valuesB.list1.connect(mqttUri(TOPIC_B_SINGLE), isWriteCurrentValue()));
+    assertTrueAndWaitForValue(valuesB.list2.connect(mqttUri(TOPIC_B_SINGLE), isWriteCurrentValue()));
+
+    assertTrueAndWaitForValue(senderRoot.connectC(mqttUri(TOPIC_C_SINGLE), isWriteCurrentValue()));
+    assertTrueAndWaitForValue(senderRoot.connectSingleC(mqttUri(TOPIC_C_SINGLE), isWriteCurrentValue()));
+    assertTrueAndWaitForValue(senderRoot.connectMaybeC(mqttUri(TOPIC_C_SINGLE), isWriteCurrentValue()));
+    assertTrueAndWaitForValue(senderRoot.connectMultipleCList(mqttUri(TOPIC_C_MANY), isWriteCurrentValue()));
+
+    assertTrueAndWaitForValue(valuesD.unnamed.connect(mqttUri(TOPIC_D_SINGLE), isWriteCurrentValue()));
+    assertTrueAndWaitForValue(valuesD.single.connect(mqttUri(TOPIC_D_SINGLE), isWriteCurrentValue()));
+    assertTrueAndWaitForValue(valuesD.opt.connect(mqttUri(TOPIC_D_SINGLE), isWriteCurrentValue()));
+    assertTrueAndWaitForValue(valuesD.list1.connect(mqttUri(TOPIC_D_SINGLE), isWriteCurrentValue()));
+    assertTrueAndWaitForValue(valuesD.list2.connect(mqttUri(TOPIC_D_SINGLE), isWriteCurrentValue()));
+  }
+
+  private void assertTrueAndWaitForValue(boolean actual) {
+    assertTrue(actual);
+    waitForValue();
+  }
+
+  private void waitForValue() {
+    if (isWriteCurrentValue()) {
+      await().until(() -> data.valueSentSinceLastCheck.getAndSet(false));
+    }
+  }
+
+  @Override
+  protected void communicateSendInitialValue() throws IOException, InterruptedException {
+    // Sink.A     <-- Root.A, Root.SingleA, Root.MaybeA (and C)
+    // Sink.ManyA <-- Root.MultipleA (and C)
+    // Sink.B     <-- Root.B, Root.SingleB, Root.MaybeB, indexed Root.MultipleB (and D)
+    // MultipleB += "-other", all C = "pre-" + value, almost all D += "-post", MultipleD += "-other"
+
+    observer.start();
+
+    // last sent value is always list2
+    check(18, "as1", tuple("am1", "am2"), "bs2-other",
+            "pre-cs1", tuple("pre-cm1", "pre-cm2"), "ds2-other", false);
+
+    // --- A ---
+    senderRoot.getA().setValue("test-3");
+    check(19, "test-3", tuple("am1", "am2"), "bs2-other",
+            "pre-cs1", tuple("pre-cm1", "pre-cm2"), "ds2-other");
+
+    senderRoot.getSingleA().setValue("test-4");
+    check(20, "test-4", tuple("am1", "am2"), "bs2-other",
+            "pre-cs1", tuple("pre-cm1", "pre-cm2"), "ds2-other");
+
+    senderRoot.getMaybeA().setValue("test-5");
+    check(21, "test-5", tuple("am1", "am2"), "bs2-other",
+            "pre-cs1", tuple("pre-cm1", "pre-cm2"), "ds2-other");
+
+    senderRoot.getMultipleA(0).setValue("test-6");
+    check(22, "test-5", tuple("test-6", "am2"), "bs2-other",
+            "pre-cs1", tuple("pre-cm1", "pre-cm2"), "ds2-other");
+
+    senderRoot.addMultipleA(new A().setValue("test-7"));
+    check(23, "test-5", tuple("test-6", "am2", "test-7"), "bs2-other",
+            "pre-cs1", tuple("pre-cm1", "pre-cm2"), "ds2-other");
+
+    // --- B ---
+    senderRoot.getB().setValue("test-8");
+    check(24, "test-5", tuple("test-6", "am2", "test-7"), "test-8",
+            "pre-cs1", tuple("pre-cm1", "pre-cm2"), "ds2-other");
+
+    senderRoot.getSingleB().setValue("test-9");
+    check(25, "test-5", tuple("test-6", "am2", "test-7"), "test-9",
+            "pre-cs1", tuple("pre-cm1", "pre-cm2"), "ds2-other");
+
+    senderRoot.getMaybeB().setValue("test-10");
+    check(26, "test-5", tuple("test-6", "am2", "test-7"), "test-10",
+            "pre-cs1", tuple("pre-cm1", "pre-cm2"), "ds2-other");
+
+    senderRoot.getMultipleB(0).setValue("test-11");
+    check(27, "test-5", tuple("test-6", "am2", "test-7"), "test-11-other",
+            "pre-cs1", tuple("pre-cm1", "pre-cm2"), "ds2-other");
+
+    // not connected, so no value is sent (manually wait)
+    senderRoot.addMultipleB(new B().setValue("test-12"));
+    waitForMqtt();
+    check(27, "test-5", tuple("test-6", "am2", "test-7"), "test-11-other",
+            "pre-cs1", tuple("pre-cm1", "pre-cm2"), "ds2-other", false);
+
+    assertTrue(senderRoot.getMultipleB(2).connect(mqttUri(TOPIC_B_SINGLE), true));
+    check(28, "test-5", tuple("test-6", "am2", "test-7"), "test-12-other",
+            "pre-cs1", tuple("pre-cm1", "pre-cm2"), "ds2-other");
+
+    // --- C ---
+    senderRoot.getC().setValue("test-13");
+    check(29, "test-5", tuple("test-6", "am2", "test-7"), "test-12-other",
+            "pre-test-13", tuple("pre-cm1", "pre-cm2"), "ds2-other");
+
+    senderRoot.getSingleC().setValue("test-14");
+    check(30, "test-5", tuple("test-6", "am2", "test-7"), "test-12-other",
+            "pre-test-14", tuple("pre-cm1", "pre-cm2"), "ds2-other");
+
+    senderRoot.getMaybeC().setValue("test-15");
+    check(31, "test-5", tuple("test-6", "am2", "test-7"), "test-12-other",
+            "pre-test-15", tuple("pre-cm1", "pre-cm2"), "ds2-other");
+
+    senderRoot.getMultipleC(1).setValue("test-16");
+    check(32, "test-5", tuple("test-6", "am2", "test-7"), "test-12-other",
+            "pre-test-15", tuple("pre-cm1", "pre-test-16"), "ds2-other");
+
+    senderRoot.addMultipleC(new C().setValue("test-17"));
+    check(33, "test-5", tuple("test-6", "am2", "test-7"), "test-12-other",
+            "pre-test-15", tuple("pre-cm1", "pre-test-16", "pre-test-17"), "ds2-other");
+
+    // --- D ---
+    senderRoot.getD().setValue("test-18");
+    check(34, "test-5", tuple("test-6", "am2", "test-7"), "test-12-other",
+            "pre-test-15", tuple("pre-cm1", "pre-test-16", "pre-test-17"), "test-18-post");
+
+    senderRoot.getSingleD().setValue("test-19");
+    check(35, "test-5", tuple("test-6", "am2", "test-7"), "test-12-other",
+            "pre-test-15", tuple("pre-cm1", "pre-test-16", "pre-test-17"), "test-19-post");
+
+    senderRoot.getMaybeD().setValue("test-20");
+    check(36, "test-5", tuple("test-6", "am2", "test-7"), "test-12-other",
+            "pre-test-15", tuple("pre-cm1", "pre-test-16", "pre-test-17"), "test-20-post");
+
+    senderRoot.getMultipleD(0).setValue("test-21");
+    check(37, "test-5", tuple("test-6", "am2", "test-7"), "test-12-other",
+            "pre-test-15", tuple("pre-cm1", "pre-test-16", "pre-test-17"), "test-21-other");
+
+    // not connected, so no value is sent (manually wait)
+    senderRoot.addMultipleD(new D().setValue("test-22"));
+    waitForMqtt();
+    check(37, "test-5", tuple("test-6", "am2", "test-7"), "test-12-other",
+            "pre-test-15", tuple("pre-cm1", "pre-test-16", "pre-test-17"), "test-21-other", false);
+
+    assertTrue(senderRoot.getMultipleD(2).connect(mqttUri(TOPIC_D_SINGLE), true));
+    check(38, "test-5", tuple("test-6", "am2", "test-7"), "test-12-other",
+            "pre-test-15", tuple("pre-cm1", "pre-test-16", "pre-test-17"), "test-22-other");
+
+    senderRoot.getMultipleD(2).setValue("test-23");
+    check(39, "test-5", tuple("test-6", "am2", "test-7"), "test-12-other",
+            "pre-test-15", tuple("pre-cm1", "pre-test-16", "pre-test-17"), "test-23-other");
+
+    // disconnecting elements, changing values must not send any message (thus not change anything on the receiver)
+    assertTrue(senderRoot.getMultipleB(1).disconnectSend(mqttUri(TOPIC_B_SINGLE)));
+    senderRoot.getMultipleB(1).setValue("test-23-ignored");
+
+    // disconnect affects complete list
+    senderRoot.getMultipleB(0).setValue("test-24-ignored");
+    senderRoot.getMultipleB(2).setValue("test-25-ignored");
+
+    assertTrue(senderRoot.getMaybeB().disconnectSend(mqttUri(TOPIC_B_SINGLE)));
+    senderRoot.getMaybeB().setValue("test-26-ignored");
+
+    assertTrue(senderRoot.getB().disconnectSend(mqttUri(TOPIC_B_SINGLE)));
+    senderRoot.getB().setValue("test-27-ignored");
+
+    waitForMqtt();
+    check(39, "test-5", tuple("test-6", "am2", "test-7"), "test-12-other",
+            "pre-test-15", tuple("pre-cm1", "pre-test-16", "pre-test-17"), "test-23-other", false);
+
+    // same for A
+    assertTrue(senderRoot.disconnectMultipleAList(mqttUri(TOPIC_A_MANY)));
+    senderRoot.getMultipleA(0).setValue("test-28-ignored");
+    senderRoot.getMultipleA(2).setValue("test-29-ignored");
+
+    assertTrue(senderRoot.disconnectMaybeA(mqttUri(TOPIC_A_SINGLE)));
+    senderRoot.getMaybeA().setValue("test-30-ignored");
+
+    assertTrue(senderRoot.disconnectA(mqttUri(TOPIC_A_SINGLE)));
+    senderRoot.getA().setValue("test-31-ignored");
+
+    waitForMqtt();
+    check(39, "test-5", tuple("test-6", "am2", "test-7"), "test-12-other",
+            "pre-test-15", tuple("pre-cm1", "pre-test-16", "pre-test-17"), "test-23-other", false);
+  }
+
+  @Override
+  protected void communicateOnlyUpdatedValue() throws IOException, InterruptedException {
+    // Sink.A     <-- Root.A, Root.SingleA, Root.MaybeA (and C)
+    // Sink.ManyA <-- Root.MultipleA (and C)
+    // Sink.B     <-- Root.B, Root.SingleB, Root.MaybeB, indexed Root.MultipleB (and D)
+    // MultipleB += "-other", all C = "pre-" + value, almost all D += "-post", MultipleD += "-other"
+
+    observer.start();
+    check(0, INITIAL_VALUE, tuple(), INITIAL_VALUE,
+            INITIAL_VALUE, tuple(), INITIAL_VALUE, false);
+
+    // --- A ---
+    senderRoot.getA().setValue("test-3");
+    check(1, "test-3", tuple(), INITIAL_VALUE,
+            INITIAL_VALUE, tuple(), INITIAL_VALUE);
+
+    senderRoot.getSingleA().setValue("test-4");
+    check(2, "test-4", tuple(), INITIAL_VALUE,
+            INITIAL_VALUE, tuple(), INITIAL_VALUE);
+
+    senderRoot.getMaybeA().setValue("test-5");
+    check(3, "test-5", tuple(), INITIAL_VALUE,
+            INITIAL_VALUE, tuple(), INITIAL_VALUE);
+
+    // whole list is updated after change of one element
+    senderRoot.getMultipleA(0).setValue("test-6");
+    check(4, "test-5", tuple("test-6", "am2"), INITIAL_VALUE,
+            INITIAL_VALUE, tuple(), INITIAL_VALUE);
+
+    senderRoot.addMultipleA(new A().setValue("test-7"));
+    check(5, "test-5", tuple("test-6", "am2", "test-7"), INITIAL_VALUE,
+            INITIAL_VALUE, tuple(), INITIAL_VALUE);
+
+    // --- B ---
+    senderRoot.getB().setValue("test-8");
+    check(6, "test-5", tuple("test-6", "am2", "test-7"), "test-8",
+            INITIAL_VALUE, tuple(), INITIAL_VALUE);
+
+    senderRoot.getSingleB().setValue("test-9");
+    check(7, "test-5", tuple("test-6", "am2", "test-7"), "test-9",
+            INITIAL_VALUE, tuple(), INITIAL_VALUE);
+
+    senderRoot.getMaybeB().setValue("test-10");
+    check(8, "test-5", tuple("test-6", "am2", "test-7"), "test-10",
+            INITIAL_VALUE, tuple(), INITIAL_VALUE);
+
+    senderRoot.getMultipleB(0).setValue("test-11");
+    check(9, "test-5", tuple("test-6", "am2", "test-7"), "test-11-other",
+            INITIAL_VALUE, tuple(), INITIAL_VALUE);
+
+    // not connected, so no value is sent (manually wait)
+    senderRoot.addMultipleB(new B().setValue("test-12-ignored"));
+    waitForMqtt();
+    check(9, "test-5", tuple("test-6", "am2", "test-7"), "test-11-other",
+            INITIAL_VALUE, tuple(), INITIAL_VALUE, false);
+
+    // connected, but current value is not sent (manually wait)
+    assertTrue(senderRoot.getMultipleB(2).connect(mqttUri(TOPIC_B_SINGLE), false));
+    waitForMqtt();
+    check(9, "test-5", tuple("test-6", "am2", "test-7"), "test-11-other",
+            INITIAL_VALUE, tuple(), INITIAL_VALUE, false);
+
+    senderRoot.getMultipleB(2).setValue("test-12");
+    check(10, "test-5", tuple("test-6", "am2", "test-7"), "test-12-other",
+            INITIAL_VALUE, tuple(), INITIAL_VALUE);
+
+    // --- C ---
+    senderRoot.getC().setValue("test-13");
+    check(11, "test-5", tuple("test-6", "am2", "test-7"), "test-12-other",
+            "pre-test-13", tuple(), INITIAL_VALUE);
+
+    senderRoot.getSingleC().setValue("test-14");
+    check(12, "test-5", tuple("test-6", "am2", "test-7"), "test-12-other",
+            "pre-test-14", tuple(), INITIAL_VALUE);
+
+    senderRoot.getMaybeC().setValue("test-15");
+    check(13, "test-5", tuple("test-6", "am2", "test-7"), "test-12-other",
+            "pre-test-15", tuple(), INITIAL_VALUE);
+
+    senderRoot.getMultipleC(1).setValue("test-16");
+    check(14, "test-5", tuple("test-6", "am2", "test-7"), "test-12-other",
+            "pre-test-15", tuple("pre-cm1", "pre-test-16"), INITIAL_VALUE);
+
+    senderRoot.addMultipleC(new C().setValue("test-17"));
+    check(15, "test-5", tuple("test-6", "am2", "test-7"), "test-12-other",
+            "pre-test-15", tuple("pre-cm1", "pre-test-16", "pre-test-17"), INITIAL_VALUE);
+
+    // --- D ---
+    senderRoot.getD().setValue("test-18");
+    check(16, "test-5", tuple("test-6", "am2", "test-7"), "test-12-other",
+            "pre-test-15", tuple("pre-cm1", "pre-test-16", "pre-test-17"), "test-18-post");
+
+    senderRoot.getSingleD().setValue("test-19");
+    check(17, "test-5", tuple("test-6", "am2", "test-7"), "test-12-other",
+            "pre-test-15", tuple("pre-cm1", "pre-test-16", "pre-test-17"), "test-19-post");
+
+    senderRoot.getMaybeD().setValue("test-20");
+    check(18, "test-5", tuple("test-6", "am2", "test-7"), "test-12-other",
+            "pre-test-15", tuple("pre-cm1", "pre-test-16", "pre-test-17"), "test-20-post");
+
+    senderRoot.getMultipleD(0).setValue("test-21");
+    check(19, "test-5", tuple("test-6", "am2", "test-7"), "test-12-other",
+            "pre-test-15", tuple("pre-cm1", "pre-test-16", "pre-test-17"), "test-21-other");
+
+    // not connected, so no value is sent (manually wait)
+    senderRoot.addMultipleD(new D().setValue("test-22-ignored"));
+    waitForMqtt();
+    check(19, "test-5", tuple("test-6", "am2", "test-7"), "test-12-other",
+            "pre-test-15", tuple("pre-cm1", "pre-test-16", "pre-test-17"), "test-21-other", false);
+
+    // connected, but current value is not sent (manually wait)
+    assertTrue(senderRoot.getMultipleD(2).connect(mqttUri(TOPIC_D_SINGLE), false));
+    waitForMqtt();
+    check(19, "test-5", tuple("test-6", "am2", "test-7"), "test-12-other",
+            "pre-test-15", tuple("pre-cm1", "pre-test-16", "pre-test-17"), "test-21-other", false);
+
+    senderRoot.getMultipleD(2).setValue("test-22");
+    check(20, "test-5", tuple("test-6", "am2", "test-7"), "test-12-other",
+            "pre-test-15", tuple("pre-cm1", "pre-test-16", "pre-test-17"), "test-22-other");
+
+
+    assertTrue(senderRoot.getMultipleB(1).disconnectSend(mqttUri(TOPIC_B_SINGLE)));
+    senderRoot.getMultipleB(1).setValue("test-23-ignored");
+
+    // disconnect affects complete list
+    senderRoot.getMultipleB(0).setValue("test-24-ignored");
+    senderRoot.getMultipleB(2).setValue("test-25-ignored");
+
+    assertTrue(senderRoot.getMaybeB().disconnectSend(mqttUri(TOPIC_B_SINGLE)));
+    senderRoot.getMaybeB().setValue("test-26-ignored");
+
+    assertTrue(senderRoot.getB().disconnectSend(mqttUri(TOPIC_B_SINGLE)));
+    senderRoot.getB().setValue("test-27-ignored");
+
+    waitForMqtt();
+    check(20, "test-5", tuple("test-6", "am2", "test-7"), "test-12-other",
+            "pre-test-15", tuple("pre-cm1", "pre-test-16", "pre-test-17"), "test-22-other", false);
+
+    // same for A
+    assertTrue(senderRoot.disconnectMultipleAList(mqttUri(TOPIC_A_MANY)));
+    senderRoot.getMultipleA(0).setValue("test-28-ignored");
+    senderRoot.getMultipleA(2).setValue("test-29-ignored");
+
+    assertTrue(senderRoot.disconnectMaybeA(mqttUri(TOPIC_A_SINGLE)));
+    senderRoot.getMaybeA().setValue("test-30-ignored");
+
+    assertTrue(senderRoot.disconnectA(mqttUri(TOPIC_A_SINGLE)));
+    senderRoot.getA().setValue("test-31-ignored");
+
+    waitForMqtt();
+    check(20, "test-5", tuple("test-6", "am2", "test-7"), "test-12-other",
+            "pre-test-15", tuple("pre-cm1", "pre-test-16", "pre-test-17"), "test-22-other", false);
+  }
+
+  private void check(int valuesSent, String valueSingleA, Tuple valueManyA, String valueSingleB,
+                     String valueSingleC, Tuple valueManyC, String valueSingleD) {
+    check(valuesSent, valueSingleA, valueManyA, valueSingleB, valueSingleC, valueManyC, valueSingleD, true);
+  }
+
+  private void check(int valuesSent, String valueSingleA, Tuple valueManyA, String valueSingleB,
+                     String valueSingleC, Tuple valueManyC, String valueSingleD, boolean wait) {
+    if (wait) {
+      observer.awaitChange();
+    } else {
+      assertFalse(observer.hasChanged());
+    }
+
+    assertEquals(valuesSent, data.valuesSent);
+
+    assertThat(receiverRoot.getA().getValue()).as("A").isEqualTo(valueSingleA);
+    assertThat(receiverRoot.getManyAList()).extracting("Value").as("a-many")
+            .containsExactlyElementsOf(valueManyA.toList());
+
+    assertThat(receiverRoot.getB().getValue()).as("B").isEqualTo(valueSingleB);
+
+    assertThat(receiverRoot.getC().getValue()).as("C").isEqualTo(valueSingleC);
+    assertThat(receiverRoot.getManyCList()).extracting("Value").as("c-many")
+            .containsExactlyElementsOf(valueManyC.toList());
+
+    assertThat(receiverRoot.getD().getValue()).as("D").isEqualTo(valueSingleD);
+  }
+
+  @Override
+  protected void closeConnections() {
+    if (handler != null) {
+      handler.close();
+    }
+    if (model != null) {
+      model.ragconnectCloseConnections();
+    }
+    if (observer != null) {
+      observer.init();
+    }
+  }
+
+  static class Values<T> {
+    T unnamed;
+    T single;
+    T opt;
+    T list1;
+    T list2;
+  }
+
+  static class ReceiverData {
+    int valuesSent = 0;
+    AtomicBoolean valueSentSinceLastCheck = new AtomicBoolean(false);
+  }
+}
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IncrementalDependencyTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IncrementalDependencyTest.java
index 132317701c73cfa9a06fa136b985393c065823ba..ce6bb40861239ce921f62714bba54937c9ad820e 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IncrementalDependencyTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IncrementalDependencyTest.java
@@ -45,7 +45,7 @@ public class IncrementalDependencyTest extends AbstractMqttTest {
   }
 
   @Override
-  protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException {
+  protected void setupReceiverAndConnect() throws IOException {
     model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
 
     handler = new MqttHandler("TestHandler")
@@ -73,9 +73,9 @@ public class IncrementalDependencyTest extends AbstractMqttTest {
     });
 
     assertTrue(model.connectInput(mqttUri(TOPIC_IN)));
-    assertTrue(model.connectOutputOnA(mqttUri(TOPIC_OUT_A), writeCurrentValue));
-    assertTrue(b1.connectOutputOnB(mqttUri(TOPIC_OUT_B1), writeCurrentValue));
-    assertTrue(b2.connectOutputOnB(mqttUri(TOPIC_OUT_B2), writeCurrentValue));
+    assertTrue(model.connectOutputOnA(mqttUri(TOPIC_OUT_A), isWriteCurrentValue()));
+    assertTrue(b1.connectOutputOnB(mqttUri(TOPIC_OUT_B1), isWriteCurrentValue()));
+    assertTrue(b2.connectOutputOnB(mqttUri(TOPIC_OUT_B2), isWriteCurrentValue()));
   }
 
   @Override
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IndexedSendTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IndexedSendTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a4575feafc716fe6dbd95f43f2b2212436a843d0
--- /dev/null
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IndexedSendTest.java
@@ -0,0 +1,207 @@
+package org.jastadd.ragconnect.tests;
+
+import indexedSendInc.ast.*;
+import org.assertj.core.api.Assertions;
+import org.assertj.core.groups.Tuple;
+import org.junit.jupiter.api.Tag;
+
+import java.io.IOException;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Predicate;
+
+import static org.assertj.core.groups.Tuple.tuple;
+import static org.awaitility.Awaitility.await;
+import static org.jastadd.ragconnect.tests.TestUtils.mqttUri;
+import static org.jastadd.ragconnect.tests.TestUtils.waitForMqtt;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Test case "indexedSend (Incremental)".
+ *
+ * @author rschoene - Initial contribution
+ */
+@Tag("Incremental")
+public class IndexedSendTest extends AbstractMqttTest {
+
+  private static final String TOPIC_A_MANY_NORMAL_WILDCARD = "a-many/#";
+  private static final String TOPIC_A_MANY_NORMAL_0 = "a-many/0";
+  private static final String TOPIC_A_MANY_NORMAL_1 = "a-many/1";
+  private static final String TOPIC_A_MANY_SUFFIX_WILDCARD = "a-many-suffix/#";
+  private static final String TOPIC_A_MANY_SUFFIX_0 = "a-many-suffix/0";
+  private static final String TOPIC_A_MANY_SUFFIX_1 = "a-many-suffix/1";
+  private static final String TOPIC_A_MANY_SUFFIX_2 = "a-many-suffix/2";
+
+  private MqttHandler handler;
+  private ReceiverData data;
+
+  private Root model;
+  private SenderRoot senderRoot;
+  private ReceiverRoot receiverRoot;
+
+  private A listA0;
+  private A listA1;
+  private A listA0InSuffix;
+  private A listA1InSuffix;
+
+  @Override
+  protected void createModel() {
+    model = new Root();
+    // model.trace().setReceiver(TestUtils::logEvent);
+    senderRoot = new SenderRoot();
+    receiverRoot = new ReceiverRoot();
+    model.setSenderRoot(senderRoot);
+    model.setReceiverRoot(receiverRoot);
+
+    listA0 = createA("am0");
+    listA1 = createA("am1");
+    listA0InSuffix = createA("am0");
+    listA1InSuffix = createA("am1");
+
+    senderRoot.addMultipleA(listA0);
+    senderRoot.addMultipleA(listA1);
+    senderRoot.addMultipleAWithSuffix(listA0InSuffix);
+    senderRoot.addMultipleAWithSuffix(listA1InSuffix);
+  }
+
+  private A createA(String value) {
+    return new A().setValue(value).setInner(new Inner("inner" + value));
+  }
+
+  @Override
+  protected void setupReceiverAndConnect() throws IOException, InterruptedException {
+    model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
+    handler = new MqttHandler().setHost(TestUtils.getMqttHost()).dontSendWelcomeMessage();
+    assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS));
+
+    data = new ReceiverData();
+    assertTrue(handler.newConnection(TOPIC_A_MANY_NORMAL_WILDCARD, bytes -> data.numberOfValues += 1));
+    assertTrue(handler.newConnection(TOPIC_A_MANY_SUFFIX_WILDCARD, bytes -> data.numberOfValues += 1));
+
+    // connect receive
+    assertTrue(receiverRoot.connectManyA(mqttUri(TOPIC_A_MANY_NORMAL_WILDCARD)));
+    assertTrue(receiverRoot.connectManyAWithSuffix(mqttUri(TOPIC_A_MANY_SUFFIX_WILDCARD)));
+
+    // connect send, and wait to receive (if writeCurrentValue is set)
+    assertTrue(senderRoot.connectMultipleA(mqttUri(TOPIC_A_MANY_NORMAL_0), 0, isWriteCurrentValue()));
+    waitForValue(receiverRoot::getNumManyA, 1);
+
+    assertTrue(senderRoot.connectMultipleA(mqttUri(TOPIC_A_MANY_NORMAL_1), 1, isWriteCurrentValue()));
+    waitForValue(receiverRoot::getNumManyA, 2);
+
+    assertTrue(senderRoot.connectMultipleAWithSuffix(mqttUri(TOPIC_A_MANY_SUFFIX_0), 0, isWriteCurrentValue()));
+    waitForValue(receiverRoot::getNumManyAWithSuffix, 1);
+
+    assertTrue(senderRoot.connectMultipleAWithSuffix(mqttUri(TOPIC_A_MANY_SUFFIX_1), 1, isWriteCurrentValue()));
+    waitForValue(receiverRoot::getNumManyAWithSuffix, 2);
+  }
+
+  private void waitForValue(Callable<Integer> callable, int expectedValue) {
+    if (isWriteCurrentValue()) {
+      await().until(callable, Predicate.isEqual(expectedValue));
+    }
+  }
+
+  @Override
+  protected void communicateSendInitialValue() throws IOException, InterruptedException {
+    // Sink.ManyA           <-- Root.MultipleA
+    // Sink.ManyAWithSuffix <-- Root.MultipleAWithSuffix
+    check(4, tuple("am0", "am1"), tuple("am0post", "am1post"), false);
+
+    listA0.setValue("changedValue");
+    check(5, tuple("changedValue", "am1"), tuple("am0post", "am1post"));
+
+    // setting same value must not change data, and must not trigger a new sent message
+    listA0.setValue("changedValue");
+    check(5, tuple("changedValue", "am1"), tuple("am0post", "am1post"));
+
+    listA1.setValue("");
+    check(6, tuple("changedValue", ""), tuple("am0post", "am1post"));
+
+    listA1InSuffix.setValue("re");
+    check(7, tuple("changedValue", ""), tuple("am0post", "repost"));
+
+    // adding a new element does not automatically send it
+    A listA3InSuffix = createA("out");
+    senderRoot.addMultipleAWithSuffix(listA3InSuffix);
+    check(7, tuple("changedValue", ""), tuple("am0post", "repost"));
+
+    // only after connecting it, the element gets sent
+    assertTrue(senderRoot.connectMultipleAWithSuffix(mqttUri(TOPIC_A_MANY_SUFFIX_2), 2, true));
+    check(8, tuple("changedValue", ""), tuple("am0post", "repost", "outpost"));
+
+    // after successful disconnect, no messages will be sent
+    assertTrue(senderRoot.disconnectMultipleAWithSuffix(mqttUri(TOPIC_A_MANY_SUFFIX_0)));
+    listA0InSuffix.setValue("willBeIgnored");
+    check(8, tuple("changedValue", ""), tuple("am0post", "repost", "outpost"));
+  }
+
+  @Override
+  protected void communicateOnlyUpdatedValue() throws IOException, InterruptedException {
+    check(0, tuple(), tuple(), false);
+
+    assertEquals(listA0.getValue(), senderRoot._ragconnect_MultipleA(0).getValue());
+    listA0.setValue("changedValue");
+    assertEquals(listA0.getValue(), senderRoot._ragconnect_MultipleA(0).getValue());
+    check(1, tuple("changedValue"), tuple());
+
+    // setting same value must not change data, and must not trigger a new sent message
+    listA0.setValue("changedValue");
+    check(1, tuple("changedValue"), tuple());
+
+    listA1.setValue("");
+    check(2, tuple("changedValue", ""), tuple());
+
+    // first element in suffix-list
+    listA1InSuffix.setValue("re");
+    check(3, tuple("changedValue", ""), tuple("repost"));
+
+    // adding a new element does not automatically send it
+    A listA3InSuffix = createA("out");
+    senderRoot.addMultipleAWithSuffix(listA3InSuffix);
+    check(3, tuple("changedValue", ""), tuple("repost"));
+
+    // only after connecting it, the element gets sent
+    assertTrue(senderRoot.connectMultipleAWithSuffix(mqttUri(TOPIC_A_MANY_SUFFIX_2), 2, true));
+    check(4, tuple("changedValue", ""), tuple("repost", "outpost"));
+
+    // after successful disconnect, no messages will be sent
+    assertTrue(senderRoot.disconnectMultipleAWithSuffix(mqttUri(TOPIC_A_MANY_SUFFIX_0)));
+    listA0InSuffix.setValue("willBeIgnored");
+    check(4, tuple("changedValue", ""), tuple("repost", "outpost"));
+  }
+
+  private void check(int expectedNumberOfSentValues, Tuple valueManyA, Tuple valueManyAWithSuffix)
+          throws InterruptedException {
+    check(expectedNumberOfSentValues, valueManyA, valueManyAWithSuffix, true);
+  }
+
+  private void check(int expectedNumberOfSentValues, Tuple valueManyA, Tuple valueManyAWithSuffix, boolean wait)
+          throws InterruptedException {
+    if (wait) {
+      waitForMqtt();
+    }
+    assertEquals(expectedNumberOfSentValues, data.numberOfValues, "expectedNumberOfSentValues");
+    Assertions.assertThat(receiverRoot.getManyAList()).extracting("Value")
+            .as("many-a")
+            .containsExactlyElementsOf(valueManyA.toList());
+    Assertions.assertThat(receiverRoot.getManyAWithSuffixList()).extracting("Value")
+            .as("many-a-with-suffix")
+            .containsExactlyElementsOf(valueManyAWithSuffix.toList());
+  }
+
+  @Override
+  protected void closeConnections() {
+    if (handler != null) {
+      handler.close();
+    }
+    if (model != null) {
+      model.ragconnectCloseConnections();
+    }
+  }
+
+  private static class ReceiverData {
+    int numberOfValues = 0;
+  }
+}
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/MappingTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/MappingTest.java
index 52d750223efb102f73e5f63d83aaf8c85ae6847f..c8ac3b889d41dafbbae1717a78f7066f70ad21b7 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/MappingTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/MappingTest.java
@@ -45,7 +45,7 @@ public class MappingTest extends AbstractMqttTest {
   }
 
   @Override
-  protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException {
+  protected void setupReceiverAndConnect() throws IOException {
     model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
 
     handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
@@ -89,13 +89,13 @@ public class MappingTest extends AbstractMqttTest {
       data.lastNativeBooleanValue = TestUtils.DefaultMappings.BytesToBool(bytes);
     });
 
-    assertTrue(natives.connectWriteIntValue(mqttUri(TOPIC_WRITE_NATIVE_INT), writeCurrentValue));
-    assertTrue(natives.connectWriteShortValue(mqttUri(TOPIC_WRITE_NATIVE_SHORT), writeCurrentValue));
-    assertTrue(natives.connectWriteLongValue(mqttUri(TOPIC_WRITE_NATIVE_LONG), writeCurrentValue));
-    assertTrue(natives.connectWriteFloatValue(mqttUri(TOPIC_WRITE_NATIVE_FLOAT), writeCurrentValue));
-    assertTrue(natives.connectWriteDoubleValue(mqttUri(TOPIC_WRITE_NATIVE_DOUBLE), writeCurrentValue));
-    assertTrue(natives.connectWriteCharValue(mqttUri(TOPIC_WRITE_NATIVE_CHAR), writeCurrentValue));
-    assertTrue(natives.connectWriteBooleanValue(mqttUri(TOPIC_WRITE_NATIVE_BOOLEAN), writeCurrentValue));
+    assertTrue(natives.connectWriteIntValue(mqttUri(TOPIC_WRITE_NATIVE_INT), isWriteCurrentValue()));
+    assertTrue(natives.connectWriteShortValue(mqttUri(TOPIC_WRITE_NATIVE_SHORT), isWriteCurrentValue()));
+    assertTrue(natives.connectWriteLongValue(mqttUri(TOPIC_WRITE_NATIVE_LONG), isWriteCurrentValue()));
+    assertTrue(natives.connectWriteFloatValue(mqttUri(TOPIC_WRITE_NATIVE_FLOAT), isWriteCurrentValue()));
+    assertTrue(natives.connectWriteDoubleValue(mqttUri(TOPIC_WRITE_NATIVE_DOUBLE), isWriteCurrentValue()));
+    assertTrue(natives.connectWriteCharValue(mqttUri(TOPIC_WRITE_NATIVE_CHAR), isWriteCurrentValue()));
+    assertTrue(natives.connectWriteBooleanValue(mqttUri(TOPIC_WRITE_NATIVE_BOOLEAN), isWriteCurrentValue()));
 
     assertTrue(natives.connectIntValue(mqttUri(TOPIC_INPUT)));
     assertTrue(natives.connectShortValue(mqttUri(TOPIC_INPUT)));
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read1Write2Test.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read1Write2Test.java
index 76eac958448c69e0ac1158074a4ddac12f4027f9..11bbd8f632870ba1d41394e9c5775f39995fb62c 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read1Write2Test.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read1Write2Test.java
@@ -56,7 +56,7 @@ public class Read1Write2Test extends AbstractMqttTest {
   }
 
   @Override
-  protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException {
+  protected void setupReceiverAndConnect() throws IOException {
     model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
 
     handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
@@ -101,14 +101,14 @@ public class Read1Write2Test extends AbstractMqttTest {
     });
 
     assertTrue(onSameNonterminal.connectInput(mqttUri(TOPIC_SAME_READ)));
-    assertTrue(onSameNonterminal.connectOutInteger(mqttUri(TOPIC_SAME_WRITE_INT), writeCurrentValue));
-    assertTrue(onSameNonterminal.connectOutString(mqttUri(TOPIC_SAME_WRITE_STRING), writeCurrentValue));
+    assertTrue(onSameNonterminal.connectOutInteger(mqttUri(TOPIC_SAME_WRITE_INT), isWriteCurrentValue()));
+    assertTrue(onSameNonterminal.connectOutString(mqttUri(TOPIC_SAME_WRITE_STRING), isWriteCurrentValue()));
 
     assertTrue(onDifferentNonterminal.connectInput(mqttUri(TOPIC_DIFFERENT_READ)));
-    assertTrue(other1.connectOutInteger(mqttUri(TOPIC_DIFFERENT_WRITE1_INT), writeCurrentValue));
-    assertTrue(other1.connectOutString(mqttUri(TOPIC_DIFFERENT_WRITE1_STRING), writeCurrentValue));
-    assertTrue(other2.connectOutInteger(mqttUri(TOPIC_DIFFERENT_WRITE2_INT), writeCurrentValue));
-    assertTrue(other2.connectOutString(mqttUri(TOPIC_DIFFERENT_WRITE2_STRING), writeCurrentValue));
+    assertTrue(other1.connectOutInteger(mqttUri(TOPIC_DIFFERENT_WRITE1_INT), isWriteCurrentValue()));
+    assertTrue(other1.connectOutString(mqttUri(TOPIC_DIFFERENT_WRITE1_STRING), isWriteCurrentValue()));
+    assertTrue(other2.connectOutInteger(mqttUri(TOPIC_DIFFERENT_WRITE2_INT), isWriteCurrentValue()));
+    assertTrue(other2.connectOutString(mqttUri(TOPIC_DIFFERENT_WRITE2_STRING), isWriteCurrentValue()));
   }
 
   @Override
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read2Write1Test.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read2Write1Test.java
index f6a0d33a4b70689a1c883f866d33442defd2b786..7d9742b864daf44eb95fa934b81dc0963c2450e5 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read2Write1Test.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/Read2Write1Test.java
@@ -57,7 +57,7 @@ public class Read2Write1Test extends AbstractMqttTest {
   }
 
   @Override
-  protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException {
+  protected void setupReceiverAndConnect() throws IOException {
     model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
 
     handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
@@ -91,12 +91,12 @@ public class Read2Write1Test extends AbstractMqttTest {
 
     assertTrue(onSameNonterminal.connectInput1(mqttUri(TOPIC_SAME_READ1)));
     assertTrue(onSameNonterminal.connectInput2(mqttUri(TOPIC_SAME_READ2)));
-    assertTrue(onSameNonterminal.connectOutInteger(mqttUri(TOPIC_SAME_WRITE_INT), writeCurrentValue));
+    assertTrue(onSameNonterminal.connectOutInteger(mqttUri(TOPIC_SAME_WRITE_INT), isWriteCurrentValue()));
 
     assertTrue(onDifferentNonterminal.connectInput1(mqttUri(TOPIC_DIFFERENT_READ1)));
     assertTrue(onDifferentNonterminal.connectInput2(mqttUri(TOPIC_DIFFERENT_READ2)));
-    assertTrue(other1.connectOutInteger(mqttUri(TOPIC_DIFFERENT_WRITE1_INT), writeCurrentValue));
-    assertTrue(other2.connectOutInteger(mqttUri(TOPIC_DIFFERENT_WRITE2_INT), writeCurrentValue));
+    assertTrue(other1.connectOutInteger(mqttUri(TOPIC_DIFFERENT_WRITE1_INT), isWriteCurrentValue()));
+    assertTrue(other2.connectOutInteger(mqttUri(TOPIC_DIFFERENT_WRITE2_INT), isWriteCurrentValue()));
   }
 
   @Override
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TestUtils.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TestUtils.java
index 55e4420e34964d72f484768df0d1ee9a602638c5..937c930d4b4a9e75003dbf74cdc42f12bc85ca0e 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TestUtils.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TestUtils.java
@@ -15,16 +15,16 @@ import java.nio.charset.Charset;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.*;
+import java.util.concurrent.Callable;
 import java.util.concurrent.TimeUnit;
-import java.util.function.BiConsumer;
+import java.util.function.Supplier;
 import java.util.stream.Collectors;
 
-import static java.lang.Math.abs;
 import static java.util.Collections.addAll;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.util.Lists.newArrayList;
+import static org.awaitility.Awaitility.await;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.fail;
 
@@ -145,6 +145,11 @@ public class TestUtils {
     TimeUnit.MILLISECONDS.sleep(1500);
   }
 
+  static <T_Event, T_ASTNode> void logEvent(T_Event event, T_ASTNode node, String attribute, Object params, Object value) {
+    logger.info("event: {}, node: {}, attribute: {}, params: {}, value: {}",
+            event, node, attribute, params, value);
+  }
+
   public static class IntList {
     private final List<Integer> integers = newArrayList();
     public IntList(Integer... values) {
@@ -361,4 +366,39 @@ public class TestUtils {
       return outputStream.toString().getBytes();
     }
   }
+
+  static class ChangeObserver {
+    Map<Supplier<String>, String> callableToPrevious = new HashMap<>();
+    private Callable<Boolean> hasChanged;
+
+    @SafeVarargs
+    final void init(Supplier<String>... suppliers) {
+      callableToPrevious.clear();
+      Arrays.stream(suppliers).forEach(callable -> callableToPrevious.put(callable, callable.get()));
+      hasChanged = () -> callableToPrevious.entrySet().stream()
+              .anyMatch(entry -> !entry.getKey().get().equals(entry.getValue()));
+    }
+
+    void start() {
+      updatePrevious();
+    }
+
+    private void updatePrevious() {
+      callableToPrevious.keySet().forEach(callable -> callableToPrevious.put(callable, callable.get()));
+    }
+
+    void awaitChange() {
+      await().until(hasChanged);
+      updatePrevious();
+    }
+
+    boolean hasChanged() {
+      try {
+        return hasChanged.call();
+      } catch (Exception e) {
+        fail(e);
+        return false;
+      }
+    }
+  }
 }
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TokenValueSendTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TokenValueSendTest.java
index da4d70308afd1efc12e10d4dbfa1a84c04852628..1a29e11885757db528f2d6da2b37297119b751a1 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TokenValueSendTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TokenValueSendTest.java
@@ -56,7 +56,8 @@ public class TokenValueSendTest extends AbstractMqttTest {
     model.setReceiveSendAndDepend(three);
   }
 
-  protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException {
+  @Override
+  protected void setupReceiverAndConnect() throws IOException {
     model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
 
     handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
@@ -87,12 +88,12 @@ public class TokenValueSendTest extends AbstractMqttTest {
       dataThreeOther.lastStringValue = TestUtils.DefaultMappings.BytesToString(bytes);
     });
 
-    assertTrue(one.connectValue(mqttUri(TOPIC_SEND_ONE), writeCurrentValue));
+    assertTrue(one.connectValue(mqttUri(TOPIC_SEND_ONE), isWriteCurrentValue()));
     assertTrue(two.connectValue(mqttUri(TOPIC_RECEIVE_TWO)));
-    assertTrue(two.connectValue(mqttUri(TOPIC_SEND_TWO), writeCurrentValue));
+    assertTrue(two.connectValue(mqttUri(TOPIC_SEND_TWO), isWriteCurrentValue()));
     assertTrue(three.connectValue(mqttUri(TOPIC_RECEIVE_THREE_VALUE)));
-    assertTrue(three.connectValue(mqttUri(TOPIC_SEND_THREE_VALUE), writeCurrentValue));
-    assertTrue(three.connectOtherOutput(mqttUri(TOPIC_SEND_THREE_OTHER), writeCurrentValue));
+    assertTrue(three.connectValue(mqttUri(TOPIC_SEND_THREE_VALUE), isWriteCurrentValue()));
+    assertTrue(three.connectOtherOutput(mqttUri(TOPIC_SEND_THREE_OTHER), isWriteCurrentValue()));
   }
 
   @Override
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TutorialTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TutorialTest.java
index 64ad5fbd147ba6c7e22d24ba7f92fe8d7114ced7..27edceebbfe77fc165d2f8bb19b0ff37c8e21a8c 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TutorialTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TutorialTest.java
@@ -31,7 +31,7 @@ public class TutorialTest extends AbstractMqttTest {
   }
 
   @Override
-  protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException {
+  protected void setupReceiverAndConnect() throws IOException {
     // a.OutputOnA -> a.Input
     a.addDependencyA(a);
     // b1.OutputOnB -> a.Input
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ViaTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ViaTest.java
index ded2990b5e997a18ff834379258689c359e4a465..d630f5eb46b0d2cfe525647a9061725b7297ff49 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ViaTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ViaTest.java
@@ -70,7 +70,7 @@ public class ViaTest extends AbstractMqttTest {
   }
 
   @Override
-  protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException {
+  protected void setupReceiverAndConnect() throws IOException {
     model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
 
     handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
@@ -109,17 +109,17 @@ public class ViaTest extends AbstractMqttTest {
     senderBoth2Rest = client.target(REST_SERVER_BASE_URL + PATH_BOTH_REST_RECEIVE);
 
     assertTrue(model.connectMqtt2MqttInput(mqttUri(TOPIC_MQTT_2_MQTT_RECEIVE)));
-    assertTrue(model.connectMqtt2MqttOutput(mqttUri(TOPIC_MQTT_2_MQTT_SEND), writeCurrentValue));
+    assertTrue(model.connectMqtt2MqttOutput(mqttUri(TOPIC_MQTT_2_MQTT_SEND), isWriteCurrentValue()));
     assertTrue(model.connectMqtt2RestInput(mqttUri(TOPIC_MQTT_2_REST_RECEIVE)));
-    assertTrue(model.connectMqtt2RestOutput(restUri(PATH_MQTT_2_REST_SEND, REST_PORT), writeCurrentValue));
+    assertTrue(model.connectMqtt2RestOutput(restUri(PATH_MQTT_2_REST_SEND, REST_PORT), isWriteCurrentValue()));
     assertTrue(model.connectRest2MqttInput(restUri(PATH_REST_2_MQTT_RECEIVE, REST_PORT)));
-    assertTrue(model.connectRest2MqttOutput(mqttUri(TOPIC_REST_2_MQTT_SEND), writeCurrentValue));
+    assertTrue(model.connectRest2MqttOutput(mqttUri(TOPIC_REST_2_MQTT_SEND), isWriteCurrentValue()));
     assertTrue(model.connectRest2RestInput(restUri(PATH_REST_2_REST_RECEIVE, REST_PORT)));
-    assertTrue(model.connectRest2RestOutput(restUri(PATH_REST_2_REST_SEND, REST_PORT), writeCurrentValue));
+    assertTrue(model.connectRest2RestOutput(restUri(PATH_REST_2_REST_SEND, REST_PORT), isWriteCurrentValue()));
     assertTrue(model.connectBoth2BothInput(mqttUri(TOPIC_BOTH_MQTT_RECEIVE)));
     assertTrue(model.connectBoth2BothInput(restUri(PATH_BOTH_REST_RECEIVE, REST_PORT)));
-    assertTrue(model.connectBoth2MqttOutput(mqttUri(TOPIC_BOTH_2_MQTT_SEND), writeCurrentValue));
-    assertTrue(model.connectBoth2RestOutput(restUri(PATH_BOTH_2_REST_SEND, REST_PORT), writeCurrentValue));
+    assertTrue(model.connectBoth2MqttOutput(mqttUri(TOPIC_BOTH_2_MQTT_SEND), isWriteCurrentValue()));
+    assertTrue(model.connectBoth2RestOutput(restUri(PATH_BOTH_2_REST_SEND, REST_PORT), isWriteCurrentValue()));
   }
 
   @Override
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/ListIncrementalTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/ListIncrementalTest.java
index ac8b9415523c2a1e9121b5647e1d886fd2efba09..75010c83a554a7bbd0955a1574fab5d676bf7d30 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/ListIncrementalTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/ListIncrementalTest.java
@@ -39,7 +39,7 @@ public class ListIncrementalTest extends AbstractListTest {
   }
 
   @Override
-  protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException {
+  protected void setupReceiverAndConnect() throws IOException {
     model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
 
     handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
@@ -58,8 +58,8 @@ public class ListIncrementalTest extends AbstractListTest {
     assertTrue(receiverRoot.connectWithAddFromAList(mqttUri(TOPIC_A)));
     assertTrue(receiverRoot.connectWithAddFromSingleAList(mqttUri(TOPIC_SINGLE_A)));
 
-    assertTrue(senderRoot.connectAList(mqttUri(TOPIC_A), writeCurrentValue));
-    assertTrue(senderRoot.connectSingleAList(mqttUri(TOPIC_SINGLE_A), writeCurrentValue));
+    assertTrue(senderRoot.connectAList(mqttUri(TOPIC_A), isWriteCurrentValue()));
+    assertTrue(senderRoot.connectSingleAList(mqttUri(TOPIC_SINGLE_A), isWriteCurrentValue()));
   }
 
   @Override
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/ListManualTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/ListManualTest.java
index 978359f772ba91187cae829ea0952807e1892ce2..a0e9aa85586b2cc5196f526781d33879145f96b2 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/ListManualTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/list/ListManualTest.java
@@ -40,7 +40,7 @@ public class ListManualTest extends AbstractListTest {
   }
 
   @Override
-  protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException {
+  protected void setupReceiverAndConnect() throws IOException {
     model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
 
     handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
@@ -61,8 +61,8 @@ public class ListManualTest extends AbstractListTest {
     assertTrue(receiverRoot.connectWithAddFromAList(mqttUri(TOPIC_A)));
     assertTrue(receiverRoot.connectWithAddFromSingleAList(mqttUri(TOPIC_SINGLE_A)));
 
-    assertTrue(senderRoot.connectAList(mqttUri(TOPIC_A), writeCurrentValue));
-    assertTrue(senderRoot.connectSingleAList(mqttUri(TOPIC_SINGLE_A), writeCurrentValue));
+    assertTrue(senderRoot.connectAList(mqttUri(TOPIC_A), isWriteCurrentValue()));
+    assertTrue(senderRoot.connectSingleAList(mqttUri(TOPIC_SINGLE_A), isWriteCurrentValue()));
   }
 
   @Override
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleList/AbstractSingleListTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleList/AbstractSingleListTest.java
index ff1e655370b85e3c75a69d7b5b4cab0a44fb8329..832ba76d7df935164b7ebd5ac90af1bbb3e854e9 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleList/AbstractSingleListTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleList/AbstractSingleListTest.java
@@ -9,14 +9,12 @@ import singleList.ast.MqttHandler;
 
 import java.io.IOException;
 import java.nio.file.Paths;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Function;
 
+import static org.awaitility.Awaitility.await;
 import static org.jastadd.ragconnect.tests.TestUtils.IntList.list;
 import static org.jastadd.ragconnect.tests.TestUtils.mqttUri;
 import static org.jastadd.ragconnect.tests.TestUtils.testJaddContainReferenceToJackson;
@@ -31,6 +29,8 @@ import static org.junit.jupiter.api.Assertions.*;
 @Tag("SingleList")
 public abstract class AbstractSingleListTest extends AbstractMqttTest {
 
+  protected MqttHandler handler;
+
   public interface TestWrapperJastAddList<T> extends Iterable<T> {
     int getNumChild();
   }
@@ -111,7 +111,7 @@ public abstract class AbstractSingleListTest extends AbstractMqttTest {
   }
 
   @Override
-  protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException, InterruptedException {
+  protected void setupReceiverAndConnect() throws IOException, InterruptedException {
     // late model initialization
     senderRoot.setInput1(0);
     senderRoot.setInput2(0);
@@ -141,40 +141,34 @@ public abstract class AbstractSingleListTest extends AbstractMqttTest {
     assertTrue(receiverRoot.connectUsingWildcardWithAddA(mqttUri(TOPIC_A_WILDCARD)));
 
     // send: explicit topics, wait between connections to ensure correct arrival at receiver
-    MqttHandler checkArrivalHandler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
-    Map<String, CountDownLatch> arrived = new HashMap<>() {{
-      put(TOPIC_A_1, new CountDownLatch(1));
-      put(TOPIC_A_2, new CountDownLatch(1));
-      put(TOPIC_A_3, new CountDownLatch(1));
-      put(TOPIC_A_4, new CountDownLatch(1));
-    }};
-    checkArrivalHandler.waitUntilReady(2, TimeUnit.SECONDS);
-    checkArrivalHandler.newConnection("#", (topic, bytes) ->
-        Optional.ofNullable(arrived.get(topic)).ifPresent(CountDownLatch::countDown));
-
-    assertTrue(senderRoot.connectA4(mqttUri(TOPIC_A_4), writeCurrentValue));
-    if (writeCurrentValue) {
-      assertTrue(arrived.get(TOPIC_A_4).await(2, TimeUnit.SECONDS));
-    }
+    handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
+    data = new ReceiverData();
+    handler.waitUntilReady(2, TimeUnit.SECONDS);
+    handler.newConnection("#", bytes -> {
+      data.valueSentSinceLastCheck.set(true);
+      data.numberOfElements += 1;
+    });
 
-    assertTrue(senderRoot.connectA3(mqttUri(TOPIC_A_3), writeCurrentValue));
-    if (writeCurrentValue) {
-      assertTrue(arrived.get(TOPIC_A_3).await(2, TimeUnit.SECONDS));
-    }
+    assertTrue(senderRoot.connectA4(mqttUri(TOPIC_A_4), isWriteCurrentValue()));
+    waitForValue();
 
-    assertTrue(senderRoot.connectA2(mqttUri(TOPIC_A_2), writeCurrentValue));
-    if (writeCurrentValue) {
-      assertTrue(arrived.get(TOPIC_A_2).await(2, TimeUnit.SECONDS));
-    }
+    assertTrue(senderRoot.connectA3(mqttUri(TOPIC_A_3), isWriteCurrentValue()));
+    waitForValue();
 
-    assertTrue(senderRoot.connectA1(mqttUri(TOPIC_A_1), writeCurrentValue));
-    if (writeCurrentValue) {
-      assertTrue(arrived.get(TOPIC_A_1).await(2, TimeUnit.SECONDS));
-    }
+    assertTrue(senderRoot.connectA2(mqttUri(TOPIC_A_2), isWriteCurrentValue()));
+    waitForValue();
 
-    assertTrue(senderRoot.connectInOutput(mqttUri(TOPIC_A_5_INOUT), writeCurrentValue));
+    assertTrue(senderRoot.connectA1(mqttUri(TOPIC_A_1), isWriteCurrentValue()));
+    waitForValue();
+
+    assertTrue(senderRoot.connectInOutput(mqttUri(TOPIC_A_5_INOUT), isWriteCurrentValue()));
     // no need to wait here, because first "checkTree" will wait anyway
-    checkArrivalHandler.close();
+  }
+
+  private void waitForValue() {
+    if (isWriteCurrentValue()) {
+      await().until(() -> data.valueSentSinceLastCheck.getAndSet(false));
+    }
   }
 
   abstract protected void setupReceiverAndConnectPart() throws IOException;
@@ -337,6 +331,7 @@ public abstract class AbstractSingleListTest extends AbstractMqttTest {
 
   protected static class ReceiverData {
     int numberOfElements = 0;
+    AtomicBoolean valueSentSinceLastCheck = new AtomicBoolean(false);
   }
 
 }
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleList/SingleListIncrementalTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleList/SingleListIncrementalTest.java
index 54bbf675b2ad4c594a5c001cfaca7342248fbfbc..71280afde1ad015736ea518a161090e795a526ab 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleList/SingleListIncrementalTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleList/SingleListIncrementalTest.java
@@ -1,14 +1,14 @@
 package org.jastadd.ragconnect.tests.singleList;
 
-import org.jastadd.ragconnect.tests.TestUtils;
 import org.junit.jupiter.api.Tag;
-import singleListInc.ast.*;
+import singleListInc.ast.A;
+import singleListInc.ast.ReceiverRoot;
+import singleListInc.ast.Root;
+import singleListInc.ast.SenderRoot;
 
-import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
  * Test case "single list incremental".
@@ -19,7 +19,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 public class SingleListIncrementalTest extends AbstractSingleListTest {
 
   private Root model;
-  private MqttHandler handler;
 
   SingleListIncrementalTest() {
     super("singleListInc");
@@ -43,16 +42,10 @@ public class SingleListIncrementalTest extends AbstractSingleListTest {
   }
 
   @Override
-  protected void setupReceiverAndConnectPart() throws IOException {
+  protected void setupReceiverAndConnectPart() {
     model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
 
-    handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
-    assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS));
-
     // no dependencies
-
-    data = new ReceiverData();
-    handler.newConnection(TOPIC_A_WILDCARD, bytes -> data.numberOfElements += 1);
   }
 
   @Override
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleList/SingleListManualTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleList/SingleListManualTest.java
index 9a8bf0a0d36d5a2f6e370bb7563453f00e0b5600..39443220a84728f5dcf746775d525534ce8f8a65 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleList/SingleListManualTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleList/SingleListManualTest.java
@@ -1,13 +1,13 @@
 package org.jastadd.ragconnect.tests.singleList;
 
-import org.jastadd.ragconnect.tests.TestUtils;
-import singleList.ast.*;
+import singleList.ast.A;
+import singleList.ast.ReceiverRoot;
+import singleList.ast.Root;
+import singleList.ast.SenderRoot;
 
-import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
  * Test case "single list manual".
@@ -17,7 +17,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 public class SingleListManualTest extends AbstractSingleListTest {
 
   private Root model;
-  private MqttHandler handler;
 
   SingleListManualTest() {
     super("singleList");
@@ -41,20 +40,14 @@ public class SingleListManualTest extends AbstractSingleListTest {
   }
 
   @Override
-  protected void setupReceiverAndConnectPart() throws IOException {
+  protected void setupReceiverAndConnectPart() {
     model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
 
-    handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
-    assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS));
-
     // add dependencies
     ((SenderRoot) senderRoot).addInputDependencyToA1((SenderRoot) senderRoot);
     ((SenderRoot) senderRoot).addInputDependencyToA2((SenderRoot) senderRoot);
     ((SenderRoot) senderRoot).addInputDependencyToA3((SenderRoot) senderRoot);
     ((SenderRoot) senderRoot).addInputDependencyToA4((SenderRoot) senderRoot);
-
-    data = new ReceiverData();
-    handler.newConnection(TOPIC_A_WILDCARD, bytes -> data.numberOfElements += 1);
   }
 
   @Override
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/AbstractSingleListVariantTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/AbstractSingleListVariantTest.java
index d4d5f8397b9d8478435c2c8431dd6069800e5a36..34846a3a425d0254797b4502f754d28ddac06f8c 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/AbstractSingleListVariantTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleListVariant/AbstractSingleListVariantTest.java
@@ -181,7 +181,7 @@ public abstract class AbstractSingleListVariantTest extends AbstractMqttTest {
   }
 
   @Override
-  protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException, InterruptedException {
+  protected void setupReceiverAndConnect() throws IOException, InterruptedException {
     // late model initialization
     setInput(0);
     setShouldSetOptAndList(false);
@@ -214,14 +214,14 @@ public abstract class AbstractSingleListVariantTest extends AbstractMqttTest {
     assertTrue(receiverRoot.connectAbstractWithAdd(mqttUri(TOPIC_T_Abstract)));
 
     // send
-    assertTrue(senderRoot.connectT_Empty(mqttUri(TOPIC_T_Empty), writeCurrentValue));
-    assertTrue(senderRoot.connectT_Token(mqttUri(TOPIC_T_Token), writeCurrentValue));
-    assertTrue(senderRoot.connectT_OneChild(mqttUri(TOPIC_T_OneChild), writeCurrentValue));
-    assertTrue(senderRoot.connectT_OneOpt(mqttUri(TOPIC_T_OneOpt), writeCurrentValue));
-    assertTrue(senderRoot.connectT_OneList(mqttUri(TOPIC_T_OneList), writeCurrentValue));
-    assertTrue(senderRoot.connectT_TwoChildren(mqttUri(TOPIC_T_TwoChildren), writeCurrentValue));
-    assertTrue(senderRoot.connectT_OneOfEach(mqttUri(TOPIC_T_OneOfEach), writeCurrentValue));
-    assertTrue(senderRoot.connectT_Abstract(mqttUri(TOPIC_T_Abstract), writeCurrentValue));
+    assertTrue(senderRoot.connectT_Empty(mqttUri(TOPIC_T_Empty), isWriteCurrentValue()));
+    assertTrue(senderRoot.connectT_Token(mqttUri(TOPIC_T_Token), isWriteCurrentValue()));
+    assertTrue(senderRoot.connectT_OneChild(mqttUri(TOPIC_T_OneChild), isWriteCurrentValue()));
+    assertTrue(senderRoot.connectT_OneOpt(mqttUri(TOPIC_T_OneOpt), isWriteCurrentValue()));
+    assertTrue(senderRoot.connectT_OneList(mqttUri(TOPIC_T_OneList), isWriteCurrentValue()));
+    assertTrue(senderRoot.connectT_TwoChildren(mqttUri(TOPIC_T_TwoChildren), isWriteCurrentValue()));
+    assertTrue(senderRoot.connectT_OneOfEach(mqttUri(TOPIC_T_OneOfEach), isWriteCurrentValue()));
+    assertTrue(senderRoot.connectT_Abstract(mqttUri(TOPIC_T_Abstract), isWriteCurrentValue()));
   }
 
   abstract protected void setupReceiverAndConnectPart() throws IOException;
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/TreeIncrementalTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/TreeIncrementalTest.java
index 96cbadc4fbfabed7685c6a5d4c0f0ff827c4f9b1..542d4a3ed9ce1ec0e22f41dfc7e7b5fe495ad6de 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/TreeIncrementalTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/TreeIncrementalTest.java
@@ -44,7 +44,7 @@ public class TreeIncrementalTest extends AbstractTreeTest {
   }
 
   @Override
-  protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException {
+  protected void setupReceiverAndConnect() throws IOException {
     model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
 
     handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
@@ -57,7 +57,7 @@ public class TreeIncrementalTest extends AbstractTreeTest {
 
     // connect. important: first receiver, then sender. to not miss initial value.
     assertTrue(receiverRoot.connectAlfa(mqttUri(TOPIC_ALFA)));
-    assertTrue(senderRoot.connectAlfa(mqttUri(TOPIC_ALFA), writeCurrentValue));
+    assertTrue(senderRoot.connectAlfa(mqttUri(TOPIC_ALFA), isWriteCurrentValue()));
   }
 
   protected void setInput(int input) {
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/TreeManualTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/TreeManualTest.java
index e557c9442be50d04d4b9d1bb2de7d003f749d4f0..c48335ffb07699cc22e7327943c6c2fc89341213 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/TreeManualTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/tree/TreeManualTest.java
@@ -45,7 +45,7 @@ public class TreeManualTest extends AbstractTreeTest {
   }
 
   @Override
-  protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException {
+  protected void setupReceiverAndConnect() throws IOException {
     model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
 
     handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
@@ -59,7 +59,7 @@ public class TreeManualTest extends AbstractTreeTest {
 
     // connect. important: first receiver, then sender. to not miss initial value.
     assertTrue(receiverRoot.connectAlfa(mqttUri(TOPIC_ALFA)));
-    assertTrue(senderRoot.connectAlfa(mqttUri(TOPIC_ALFA), writeCurrentValue));
+    assertTrue(senderRoot.connectAlfa(mqttUri(TOPIC_ALFA), isWriteCurrentValue()));
   }
 
   protected void setInput(int input) {
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/TreeAllowedTokensIncrementalTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/TreeAllowedTokensIncrementalTest.java
index 851565047c4c8a95284d9b8d58e158863596467f..d4cc6d5b76e510ad504b30489217f37bd0f0b834 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/TreeAllowedTokensIncrementalTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/TreeAllowedTokensIncrementalTest.java
@@ -48,7 +48,7 @@ public class TreeAllowedTokensIncrementalTest extends AbstractTreeAllowedTokensT
   }
 
   @Override
-  protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException {
+  protected void setupReceiverAndConnect() throws IOException {
     model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
 
     handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
@@ -67,8 +67,8 @@ public class TreeAllowedTokensIncrementalTest extends AbstractTreeAllowedTokensT
     assertTrue(senderRoot.connectInput3(mqttUri(TOPIC_INPUT3)));
     assertTrue(receiverRoot.connectAlfa(mqttUri(TOPIC_ALFA)));
     assertTrue(receiverRoot.connectAlfaPrimitive(mqttUri(TOPIC_ALFA_PRIMITIVE)));
-    assertTrue(senderRoot.connectAlfa(mqttUri(TOPIC_ALFA), writeCurrentValue));
-    assertTrue(senderRoot.connectAlfaPrimitive(mqttUri(TOPIC_ALFA_PRIMITIVE), writeCurrentValue));
+    assertTrue(senderRoot.connectAlfa(mqttUri(TOPIC_ALFA), isWriteCurrentValue()));
+    assertTrue(senderRoot.connectAlfaPrimitive(mqttUri(TOPIC_ALFA_PRIMITIVE), isWriteCurrentValue()));
   }
 
   protected void setFlag(boolean value) {
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/TreeAllowedTokensManualTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/TreeAllowedTokensManualTest.java
index 8563e0b04808f939cf1070f08c5666f9de2fef00..96e0080ae07c462f2b7902c7be9e56b417ac4ed4 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/TreeAllowedTokensManualTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/treeAllowedTokens/TreeAllowedTokensManualTest.java
@@ -44,7 +44,7 @@ public class TreeAllowedTokensManualTest extends AbstractTreeAllowedTokensTest {
   }
 
   @Override
-  protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException {
+  protected void setupReceiverAndConnect() throws IOException {
     model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
 
     handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
@@ -69,8 +69,8 @@ public class TreeAllowedTokensManualTest extends AbstractTreeAllowedTokensTest {
     assertTrue(senderRoot.connectInput3(mqttUri(TOPIC_INPUT3)));
     assertTrue(receiverRoot.connectAlfa(mqttUri(TOPIC_ALFA)));
     assertTrue(receiverRoot.connectAlfaPrimitive(mqttUri(TOPIC_ALFA_PRIMITIVE)));
-    assertTrue(senderRoot.connectAlfa(mqttUri(TOPIC_ALFA), writeCurrentValue));
-    assertTrue(senderRoot.connectAlfaPrimitive(mqttUri(TOPIC_ALFA_PRIMITIVE), writeCurrentValue));
+    assertTrue(senderRoot.connectAlfa(mqttUri(TOPIC_ALFA), isWriteCurrentValue()));
+    assertTrue(senderRoot.connectAlfaPrimitive(mqttUri(TOPIC_ALFA_PRIMITIVE), isWriteCurrentValue()));
   }
 
   protected void setFlag(boolean value) {
diff --git a/ragconnect.tests/src/test/resources/log4j2.xml b/ragconnect.tests/src/test/resources/log4j2.xml
index 4c0d4548c61b23abad6aabc6811e68cd8a928871..653d6c357cef6a51c43a2fe9e54d4a3e86148abf 100644
--- a/ragconnect.tests/src/test/resources/log4j2.xml
+++ b/ragconnect.tests/src/test/resources/log4j2.xml
@@ -2,7 +2,7 @@
 <Configuration status="INFO">
     <Appenders>
         <Console name="Console" target="SYSTEM_OUT">
-            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
+            <PatternLayout pattern="%highlight{%d{HH:mm:ss.SSS} %-5level [%t] %logger{20} - %msg%n}" disableAnsi="false"/>
         </Console>
     </Appenders>
     <Loggers>
diff --git a/relast-preprocessor b/relast-preprocessor
index 02f8e35993dc3f62ab49e94f69a6dc27170660da..cee30f5c8a1b356118f5ed78302a1df64fe76897 160000
--- a/relast-preprocessor
+++ b/relast-preprocessor
@@ -1 +1 @@
-Subproject commit 02f8e35993dc3f62ab49e94f69a6dc27170660da
+Subproject commit cee30f5c8a1b356118f5ed78302a1df64fe76897