diff --git a/CHANGELOG.md b/CHANGELOG.md
index b9d3b6a9..1f59fbae 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,41 @@
 
 ### Security
 
+## 0.9.0 - 2024-06-04
+
+### Added
+
+- Custom CoroutineContext during `Context` creation.
+
+### Changed
+
+- Kotlin 2.0
+- `MetaSpec` renamed to `MetaReader`. MetaSpec is now reserved for builder-based generation of meta descriptors.
+- Add self-type for Meta. Remove unsafe cast method for meta instances.
+
+### Removed
+
+- Automatic descriptors for schema. It is not possible to implement them without heavy reflection.
+
+## 0.8.2 - 2024-04-27
+
+### Added
+
+- Name index comparator
+- Specialized ByteArrayValue
+
+### Changed
+
+- DataSink `branch` is replaced with `putAll` to avoid confusion with DataTree methods
+- Meta delegate now uses a specific class that has a descriptor
+
+### Fixed
+
+- `listOfScheme` and `listOfConvertable` delegates provides correct items order.
+- Scheme meta setter works with proper sub-branch.
+- NameToken.parse improper work with indices.
+- Proper data handling for cache.
+
 ## 0.8.0 - 2024-02-03
 
 ### Added
diff --git a/build.gradle.kts b/build.gradle.kts
index b9349868..72f61abc 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -3,20 +3,21 @@ import space.kscience.gradle.useApache2Licence
 import space.kscience.gradle.useSPCTeam
 
 plugins {
-    id("space.kscience.gradle.project")
+    alias(spclibs.plugins.kscience.project)
+    alias(spclibs.plugins.kotlinx.kover)
 }
 
 allprojects {
     group = "space.kscience"
-    version = "0.8.0"
+    version = "0.9.0"
 }
 
 subprojects {
     apply(plugin = "maven-publish")
 
     tasks.withType<KotlinCompile> {
-        kotlinOptions {
-            freeCompilerArgs = freeCompilerArgs + "-Xcontext-receivers"
+        compilerOptions {
+            freeCompilerArgs.add("-Xcontext-receivers")
         }
     }
 }
@@ -30,7 +31,7 @@ ksciencePublish {
         useApache2Licence()
         useSPCTeam()
     }
-    repository("spc","https://maven.sciprog.center/kscience")
+    repository("spc", "https://maven.sciprog.center/kscience")
     sonatype("https://oss.sonatype.org")
 }
 
diff --git a/dataforge-context/README.md b/dataforge-context/README.md
index f0aff459..2cd53fd1 100644
--- a/dataforge-context/README.md
+++ b/dataforge-context/README.md
@@ -6,7 +6,7 @@ Context and provider definitions
 
 ## Artifact:
 
-The Maven coordinates of this project are `space.kscience:dataforge-context:0.8.0`.
+The Maven coordinates of this project are `space.kscience:dataforge-context:0.9.0-dev-1`.
 
 **Gradle Kotlin DSL:**
 ```kotlin
@@ -16,6 +16,6 @@ repositories {
 }
 
 dependencies {
-    implementation("space.kscience:dataforge-context:0.8.0")
+    implementation("space.kscience:dataforge-context:0.9.0-dev-1")
 }
 ```
diff --git a/dataforge-context/api/dataforge-context.api b/dataforge-context/api/dataforge-context.api
index 12bca176..67cd0cba 100644
--- a/dataforge-context/api/dataforge-context.api
+++ b/dataforge-context/api/dataforge-context.api
@@ -57,6 +57,7 @@ public abstract interface class space/kscience/dataforge/context/ContextAware {
 
 public final class space/kscience/dataforge/context/ContextBuilder {
 	public final fun build ()Lspace/kscience/dataforge/context/Context;
+	public final fun coroutineContext (Lkotlin/coroutines/CoroutineContext;)V
 	public final fun getName ()Lspace/kscience/dataforge/names/Name;
 	public final fun plugin (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
 	public final fun plugin (Lspace/kscience/dataforge/context/Plugin;)V
@@ -69,9 +70,6 @@ public final class space/kscience/dataforge/context/ContextBuilder {
 	public final fun properties (Lkotlin/jvm/functions/Function1;)V
 }
 
-public final class space/kscience/dataforge/context/ContextBuilderKt {
-}
-
 public final class space/kscience/dataforge/context/DefaultLogManager : space/kscience/dataforge/context/AbstractPlugin, space/kscience/dataforge/context/LogManager {
 	public static final field Companion Lspace/kscience/dataforge/context/DefaultLogManager$Companion;
 	public fun <init> ()V
@@ -212,14 +210,14 @@ public final class space/kscience/dataforge/context/PluginTag : space/kscience/d
 	public fun toString ()Ljava/lang/String;
 }
 
-public final class space/kscience/dataforge/context/PluginTag$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
+public synthetic class space/kscience/dataforge/context/PluginTag$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
 	public static final field INSTANCE Lspace/kscience/dataforge/context/PluginTag$$serializer;
-	public fun childSerializers ()[Lkotlinx/serialization/KSerializer;
+	public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
 	public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
-	public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/dataforge/context/PluginTag;
-	public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+	public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/dataforge/context/PluginTag;
+	public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
 	public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
-	public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/dataforge/context/PluginTag;)V
+	public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/dataforge/context/PluginTag;)V
 	public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
 }
 
@@ -264,17 +262,6 @@ public abstract interface annotation class space/kscience/dataforge/descriptors/
 public abstract interface annotation class space/kscience/dataforge/descriptors/Multiple : java/lang/annotation/Annotation {
 }
 
-public final class space/kscience/dataforge/descriptors/ReflectiveDescriptorsKt {
-	public static final fun forClass (Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor$Companion;Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;
-	public static synthetic fun forClass$default (Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor$Companion;Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;
-}
-
-public final class space/kscience/dataforge/properties/MetaAsFlowKt {
-}
-
-public final class space/kscience/dataforge/provider/DfTypeKt {
-}
-
 public final class space/kscience/dataforge/provider/Path : java/lang/Iterable, kotlin/jvm/internal/markers/KMappedMarker {
 	public static final field Companion Lspace/kscience/dataforge/provider/Path$Companion;
 	public static final field PATH_SEGMENT_SEPARATOR Ljava/lang/String;
diff --git a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/Context.kt b/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/Context.kt
index bb74d605..c614598c 100644
--- a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/Context.kt
+++ b/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/Context.kt
@@ -10,6 +10,7 @@ import space.kscience.dataforge.misc.ThreadSafe
 import space.kscience.dataforge.names.Name
 import space.kscience.dataforge.provider.Provider
 import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.EmptyCoroutineContext
 
 /**
  * The local environment for anything being done in DataForge framework. Contexts are organized into tree structure with [Global] at the top.
@@ -26,6 +27,7 @@ public open class Context internal constructor(
     public val parent: Context?,
     plugins: Set<Plugin>, // set of unattached plugins
     meta: Meta,
+    coroutineContext: CoroutineContext = EmptyCoroutineContext,
 ) : Named, MetaRepr, Provider, CoroutineScope {
 
     /**
@@ -65,7 +67,7 @@ public open class Context internal constructor(
 
     override val coroutineContext: CoroutineContext by lazy {
         (parent ?: Global).coroutineContext.let { parenContext ->
-            parenContext + SupervisorJob(parenContext[Job])
+            parenContext + coroutineContext + SupervisorJob(parenContext[Job])
         }
     }
 
diff --git a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/ContextBuilder.kt b/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/ContextBuilder.kt
index c0db4314..894c5f15 100644
--- a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/ContextBuilder.kt
+++ b/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/context/ContextBuilder.kt
@@ -13,6 +13,8 @@ import space.kscience.dataforge.names.plus
 import kotlin.collections.component1
 import kotlin.collections.component2
 import kotlin.collections.set
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.EmptyCoroutineContext
 
 /**
  * A convenience builder for context
@@ -59,8 +61,15 @@ public class ContextBuilder internal constructor(
         plugin(DeFactoPluginFactory(plugin))
     }
 
+    private var coroutineContext: CoroutineContext = EmptyCoroutineContext
+
+    public fun coroutineContext(coroutineContext: CoroutineContext) {
+        this.coroutineContext = coroutineContext
+    }
+
+
     public fun build(): Context {
-        val contextName = name ?: NameToken("@auto",hashCode().toUInt().toString(16)).asName()
+        val contextName = name ?: NameToken("@auto", hashCode().toUInt().toString(16)).asName()
         val plugins = HashMap<PluginTag, Plugin>()
 
         fun addPlugin(factory: PluginFactory<*>, meta: Meta) {
@@ -86,7 +95,7 @@ public class ContextBuilder internal constructor(
             addPlugin(factory, meta)
         }
 
-        return Context(contextName, parent, plugins.values.toSet(), meta.seal())
+        return Context(contextName, parent, plugins.values.toSet(), meta.seal(), coroutineContext)
     }
 }
 
diff --git a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/properties/metaAsFlow.kt b/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/properties/metaAsFlow.kt
index 90fafc5e..da539fcb 100644
--- a/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/properties/metaAsFlow.kt
+++ b/dataforge-context/src/commonMain/kotlin/space/kscience/dataforge/properties/metaAsFlow.kt
@@ -10,7 +10,7 @@ import space.kscience.dataforge.meta.*
 import space.kscience.dataforge.misc.DFExperimental
 
 @DFExperimental
-public fun <T> ObservableMeta.asFlow(converter: MetaSpec<T>): Flow<T> = callbackFlow {
+public fun <T> ObservableMeta.asFlow(converter: MetaReader<T>): Flow<T> = callbackFlow {
     onChange(this){
         trySend(converter.read(this))
     }
diff --git a/dataforge-context/src/jvmMain/kotlin/space/kscience/dataforge/descriptors/reflectiveDescriptors.kt b/dataforge-context/src/jvmMain/kotlin/space/kscience/dataforge/descriptors/reflectiveDescriptors.kt
index 1f2db7fc..590324d7 100644
--- a/dataforge-context/src/jvmMain/kotlin/space/kscience/dataforge/descriptors/reflectiveDescriptors.kt
+++ b/dataforge-context/src/jvmMain/kotlin/space/kscience/dataforge/descriptors/reflectiveDescriptors.kt
@@ -4,18 +4,12 @@ import kotlinx.serialization.ExperimentalSerializationApi
 import kotlinx.serialization.json.Json
 import kotlinx.serialization.json.decodeFromStream
 import org.slf4j.LoggerFactory
-import space.kscience.dataforge.meta.Scheme
-import space.kscience.dataforge.meta.SchemeSpec
-import space.kscience.dataforge.meta.ValueType
 import space.kscience.dataforge.meta.descriptors.MetaDescriptor
 import space.kscience.dataforge.meta.descriptors.MetaDescriptorBuilder
-import space.kscience.dataforge.meta.descriptors.node
+import space.kscience.dataforge.misc.DFExperimental
 import java.net.URL
-import kotlin.reflect.KClass
-import kotlin.reflect.full.isSubclassOf
-import kotlin.reflect.full.memberProperties
-import kotlin.reflect.typeOf
-
+import kotlin.reflect.KAnnotatedElement
+import kotlin.reflect.KProperty
 
 /**
  * Description text for meta property, node or whole object
@@ -58,19 +52,9 @@ private fun MetaDescriptorBuilder.loadDescriptorFromResource(resource: Descripto
     }
 }
 
-
-public fun <T : Any> MetaDescriptor.Companion.forClass(
-    kClass: KClass<T>,
-    mod: MetaDescriptorBuilder.() -> Unit = {},
-): MetaDescriptor = MetaDescriptor {
-    when {
-        kClass.isSubclassOf(Number::class) -> valueType(ValueType.NUMBER)
-        kClass == String::class -> ValueType.STRING
-        kClass == Boolean::class -> ValueType.BOOLEAN
-        kClass == DoubleArray::class -> ValueType.LIST
-    }
-
-    kClass.annotations.forEach {
+@DFExperimental
+public fun MetaDescriptorBuilder.forAnnotatedElement(element: KAnnotatedElement) {
+    element.annotations.forEach {
         when (it) {
             is Description -> description = it.value
 
@@ -79,46 +63,70 @@ public fun <T : Any> MetaDescriptor.Companion.forClass(
             is DescriptorUrl -> loadDescriptorFromUrl(URL(it.url))
         }
     }
-    kClass.memberProperties.forEach { property ->
-
-        var flag = false
-
-        val descriptor = MetaDescriptor {
-            //use base type descriptor as a base
-            (property.returnType.classifier as? KClass<*>)?.let {
-                from(forClass(it))
-            }
-            property.annotations.forEach {
-                when (it) {
-                    is Description -> {
-                        description = it.value
-                        flag = true
-                    }
-
-                    is Multiple -> {
-                        multiple = true
-                        flag = true
-                    }
-
-                    is DescriptorResource -> {
-                        loadDescriptorFromResource(it)
-                        flag = true
-                    }
-
-                    is DescriptorUrl -> {
-                        loadDescriptorFromUrl(URL(it.url))
-                        flag = true
-                    }
-                }
-            }
-        }
-        if (flag) {
-            node(property.name, descriptor)
-        }
-    }
-    mod()
 }
 
-@Suppress("UNCHECKED_CAST")
-public inline fun <reified T : Scheme> SchemeSpec<T>.autoDescriptor( noinline mod: MetaDescriptorBuilder.() -> Unit = {}): MetaDescriptor =
-    MetaDescriptor.forClass(typeOf<T>().classifier as KClass<T>, mod)
\ No newline at end of file
+@DFExperimental
+public fun MetaDescriptorBuilder.forProperty(property: KProperty<*>) {
+    property.annotations.forEach {
+        when (it) {
+            is Description -> description = it.value
+
+            is DescriptorResource -> loadDescriptorFromResource(it)
+
+            is DescriptorUrl -> loadDescriptorFromUrl(URL(it.url))
+        }
+    }
+}
+//
+//@DFExperimental
+//public fun <T : Scheme> MetaDescriptor.Companion.forScheme(
+//    spec: SchemeSpec<T>,
+//    mod: MetaDescriptorBuilder.() -> Unit = {},
+//): MetaDescriptor = MetaDescriptor {
+//    val scheme = spec.empty()
+//    val kClass: KClass<T> = scheme::class as KClass<T>
+//    when {
+//        kClass.isSubclassOf(Number::class) -> valueType(ValueType.NUMBER)
+//        kClass == String::class -> ValueType.STRING
+//        kClass == Boolean::class -> ValueType.BOOLEAN
+//        kClass == DoubleArray::class -> ValueType.LIST
+//        kClass == ByteArray::class -> ValueType.LIST
+//    }
+//
+//    forAnnotatedElement(kClass)
+//    kClass.memberProperties.forEach { property ->
+//        node(property.name) {
+//
+//            (property.getDelegate(scheme) as? MetaDelegate<*>)?.descriptor?.let {
+//                from(it)
+//            }
+//
+//            property.annotations.forEach {
+//                when (it) {
+//                    is Description -> {
+//                        description = it.value
+//                    }
+//
+//                    is Multiple -> {
+//                        multiple = true
+//                    }
+//
+//                    is DescriptorResource -> {
+//                        loadDescriptorFromResource(it)
+//                    }
+//
+//                    is DescriptorUrl -> {
+//                        loadDescriptorFromUrl(URL(it.url))
+//                    }
+//                }
+//            }
+//        }
+//
+//    }
+//    mod()
+//}
+//
+//@DFExperimental
+//public inline fun <reified T : Scheme> SchemeSpec<T>.autoDescriptor(
+//    noinline mod: MetaDescriptorBuilder.() -> Unit = {},
+//): MetaDescriptor = MetaDescriptor.forScheme(this, mod)
\ No newline at end of file
diff --git a/dataforge-context/src/jvmTest/kotlin/space/kscience/dataforge/descriptors/TestAutoDescriptors.kt b/dataforge-context/src/jvmTest/kotlin/space/kscience/dataforge/descriptors/TestAutoDescriptors.kt
index 617e85cc..3b1fce3d 100644
--- a/dataforge-context/src/jvmTest/kotlin/space/kscience/dataforge/descriptors/TestAutoDescriptors.kt
+++ b/dataforge-context/src/jvmTest/kotlin/space/kscience/dataforge/descriptors/TestAutoDescriptors.kt
@@ -1,31 +1,29 @@
+@file:OptIn(DFExperimental::class)
+
 package space.kscience.dataforge.descriptors
 
-import kotlinx.serialization.encodeToString
-import kotlinx.serialization.json.Json
-import org.junit.jupiter.api.Test
-import space.kscience.dataforge.meta.Scheme
-import space.kscience.dataforge.meta.SchemeSpec
-import space.kscience.dataforge.meta.descriptors.MetaDescriptor
-import space.kscience.dataforge.meta.int
-import space.kscience.dataforge.meta.string
+import space.kscience.dataforge.misc.DFExperimental
 
-private class TestScheme: Scheme(){
-
-    @Description("A")
-    val a by string()
-
-    @Description("B")
-    val b by int()
-
-    companion object: SchemeSpec<TestScheme>(::TestScheme){
-        override val descriptor: MetaDescriptor = autoDescriptor()
-    }
-}
-
-class TestAutoDescriptors {
-    @Test
-    fun autoDescriptor(){
-        val autoDescriptor = MetaDescriptor.forClass(TestScheme::class)
-        println(Json{prettyPrint = true}.encodeToString(autoDescriptor))
-    }
-}
\ No newline at end of file
+//
+//class TestScheme : Scheme() {
+//
+//    @Description("A")
+//    val a by string()
+//
+//    @Description("B")
+//    val b by int()
+//
+//    val c by int()
+//
+//    companion object : SchemeSpec<TestScheme>(::TestScheme) {
+//        override val descriptor: MetaDescriptor = autoDescriptor()
+//    }
+//}
+//
+//class TestAutoDescriptors {
+//    @Test
+//    fun autoDescriptor() {
+//        val autoDescriptor = MetaDescriptor.forScheme(TestScheme)
+//        println(Json { prettyPrint = true }.encodeToString(autoDescriptor))
+//    }
+//}
\ No newline at end of file
diff --git a/dataforge-data/README.md b/dataforge-data/README.md
index d77ed1b9..35aaa4e6 100644
--- a/dataforge-data/README.md
+++ b/dataforge-data/README.md
@@ -6,7 +6,7 @@
 
 ## Artifact:
 
-The Maven coordinates of this project are `space.kscience:dataforge-data:0.8.0`.
+The Maven coordinates of this project are `space.kscience:dataforge-data:0.9.0-dev-1`.
 
 **Gradle Kotlin DSL:**
 ```kotlin
@@ -16,6 +16,6 @@ repositories {
 }
 
 dependencies {
-    implementation("space.kscience:dataforge-data:0.8.0")
+    implementation("space.kscience:dataforge-data:0.9.0-dev-1")
 }
 ```
diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/AbstractAction.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/AbstractAction.kt
index 7cd1ced5..4ed5f8df 100644
--- a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/AbstractAction.kt
+++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/AbstractAction.kt
@@ -1,11 +1,14 @@
 package space.kscience.dataforge.actions
 
-import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.launch
-import space.kscience.dataforge.data.*
+import space.kscience.dataforge.data.DataSink
+import space.kscience.dataforge.data.DataTree
+import space.kscience.dataforge.data.DataUpdate
+import space.kscience.dataforge.data.launchUpdate
 import space.kscience.dataforge.meta.Meta
-import space.kscience.dataforge.misc.DFInternal
+import space.kscience.dataforge.misc.UnsafeKType
 import space.kscience.dataforge.names.Name
 import space.kscience.dataforge.names.startsWith
 import kotlin.reflect.KType
@@ -21,7 +24,7 @@ internal fun MutableMap<Name, *>.removeWhatStartsWith(name: Name) {
 /**
  * An action that caches results on-demand and recalculates them on source push
  */
-public abstract class AbstractAction<T : Any, R : Any>(
+public abstract class AbstractAction<T, R>(
     public val outputType: KType,
 ) : Action<T, R> {
 
@@ -29,7 +32,7 @@ public abstract class AbstractAction<T : Any, R : Any>(
      * Generate initial content of the output
      */
     protected abstract fun DataSink<R>.generate(
-        data: DataTree<T>,
+        source: DataTree<T>,
         meta: Meta,
     )
 
@@ -37,36 +40,31 @@ public abstract class AbstractAction<T : Any, R : Any>(
      * Update part of the data set using provided data
      *
      * @param source the source data tree in case we need several data items to update
+     * @param meta the metadata used for the whole data tree
+     * @param updatedData an updated item
      */
-    protected open fun DataSink<R>.update(
+    protected open suspend fun DataSink<R>.update(
         source: DataTree<T>,
         meta: Meta,
-        namedData: NamedData<T>,
-    ){
+        updatedData: DataUpdate<T>,
+    ) {
         //by default regenerate the whole data set
-        generate(source,meta)
+        generate(source, meta)
     }
 
-    @OptIn(DFInternal::class)
+    @OptIn(UnsafeKType::class)
     override fun execute(
-        dataSet: DataTree<T>,
+        source: DataTree<T>,
         meta: Meta,
-    ): DataTree<R> = if(dataSet.isObservable()) {
-        MutableDataTree<R>(outputType, dataSet.updatesScope).apply {
-            generate(dataSet, meta)
-            dataSet.updates().onEach {
-                update(dataSet, meta, it)
-            }.launchIn(updatesScope)
-
-            //close updates when the source is closed
-            updatesScope.launch {
-                dataSet.awaitClose()
-                close()
-            }
-        }
-    } else{
-        DataTree(outputType){
-            generate(dataSet, meta)
+        updatesScope: CoroutineScope
+    ): DataTree<R> = DataTree(outputType) {
+        generate(source, meta)
+        //propagate updates
+        launchUpdate(updatesScope) {
+            source.updates.onEach { update ->
+                update(source, meta, update)
+            }.collect()
         }
     }
 }
+
diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/Action.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/Action.kt
index 5ed60db9..80898aa8 100644
--- a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/Action.kt
+++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/Action.kt
@@ -1,5 +1,8 @@
 package space.kscience.dataforge.actions
 
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.DelicateCoroutinesApi
+import kotlinx.coroutines.GlobalScope
 import space.kscience.dataforge.data.DataTree
 import space.kscience.dataforge.meta.Meta
 import space.kscience.dataforge.misc.DFExperimental
@@ -13,7 +16,7 @@ public fun interface Action<T, R> {
      * Transform the data in the node, producing a new node. By default, it is assumed that all calculations are lazy
      * so not actual computation is started at this moment.
      */
-    public fun execute(dataSet: DataTree<T>, meta: Meta): DataTree<R>
+    public fun execute(source: DataTree<T>, meta: Meta, updatesScope: CoroutineScope): DataTree<R>
 
     public companion object
 }
@@ -21,23 +24,27 @@ public fun interface Action<T, R> {
 /**
  * A convenience method to transform data using given [action]
  */
+@OptIn(DelicateCoroutinesApi::class)
 public fun <T, R> DataTree<T>.transform(
     action: Action<T, R>,
     meta: Meta = Meta.EMPTY,
-): DataTree<R> = action.execute(this, meta)
+    updateScope: CoroutineScope = GlobalScope,
+): DataTree<R> = action.execute(this, meta, updateScope)
 
 /**
  * Action composition. The result is terminal if one of its parts is terminal
  */
-public infix fun <T, I, R> Action<T, I>.then(action: Action<I, R>): Action<T, R> = Action { dataSet, meta ->
-    action.execute(this@then.execute(dataSet, meta), meta)
+public infix fun <T, I, R> Action<T, I>.then(action: Action<I, R>): Action<T, R> = Action { dataSet, meta, scope ->
+    action.execute(this@then.execute(dataSet, meta, scope), meta, scope)
 }
 
 @DFExperimental
+@OptIn(DelicateCoroutinesApi::class)
 public operator fun <T, R> Action<T, R>.invoke(
     dataSet: DataTree<T>,
     meta: Meta = Meta.EMPTY,
-): DataTree<R> = execute(dataSet, meta)
+    updateScope: CoroutineScope = GlobalScope,
+): DataTree<R> = execute(dataSet, meta, updateScope)
 
 
 
diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/MapAction.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/MapAction.kt
index 1f40ed73..08bf08e9 100644
--- a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/MapAction.kt
+++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/MapAction.kt
@@ -6,8 +6,7 @@ import space.kscience.dataforge.meta.MutableMeta
 import space.kscience.dataforge.meta.seal
 import space.kscience.dataforge.meta.toMutableMeta
 import space.kscience.dataforge.misc.DFBuilder
-import space.kscience.dataforge.misc.DFExperimental
-import space.kscience.dataforge.misc.DFInternal
+import space.kscience.dataforge.misc.UnsafeKType
 import space.kscience.dataforge.names.Name
 import kotlin.reflect.KType
 import kotlin.reflect.typeOf
@@ -49,13 +48,18 @@ public class MapActionBuilder<T, R>(
     public inline fun <reified R1 : R> result(noinline f: suspend ActionEnv.(T) -> R1): Unit = result(typeOf<R1>(), f)
 }
 
-@PublishedApi
-internal class MapAction<T : Any, R : Any>(
+@UnsafeKType
+public class MapAction<T, R>(
     outputType: KType,
     private val block: MapActionBuilder<T, R>.() -> Unit,
 ) : AbstractAction<T, R>(outputType) {
 
-    private fun DataSink<R>.mapOne(name: Name, data: Data<T>, meta: Meta) {
+    private fun DataSink<R>.mapOne(name: Name, data: Data<T>?, meta: Meta) {
+        //fast return for null data
+        if (data == null) {
+            put(name, null)
+            return
+        }
         // Creating a new environment for action using **old** name, old meta and task meta
         val env = ActionEnv(name, data.meta, meta)
 
@@ -74,7 +78,6 @@ internal class MapAction<T : Any, R : Any>(
         //getting new meta
         val newMeta = builder.meta.seal()
 
-        @OptIn(DFInternal::class)
         val newData = Data(builder.outputType, newMeta, dependencies = listOf(data)) {
             builder.result(env, data.await())
         }
@@ -82,12 +85,18 @@ internal class MapAction<T : Any, R : Any>(
         put(newName, newData)
     }
 
-    override fun DataSink<R>.generate(data: DataTree<T>, meta: Meta) {
-        data.forEach { mapOne(it.name, it.data, meta) }
+    override fun DataSink<R>.generate(source: DataTree<T>, meta: Meta) {
+        source.forEach { mapOne(it.name, it.data, meta) }
     }
 
-    override fun DataSink<R>.update(source: DataTree<T>, meta: Meta, namedData: NamedData<T>) {
-        mapOne(namedData.name, namedData.data, namedData.meta)
+
+
+    override suspend fun DataSink<R>.update(
+        source: DataTree<T>,
+        meta: Meta,
+        updatedData: DataUpdate<T>,
+    )  {
+        mapOne(updatedData.name, updatedData.data, meta)
     }
 }
 
@@ -95,8 +104,9 @@ internal class MapAction<T : Any, R : Any>(
 /**
  * A one-to-one mapping action
  */
-@DFExperimental
-public inline fun <T : Any, reified R : Any> Action.Companion.mapping(
+
+@OptIn(UnsafeKType::class)
+public inline fun <T, reified R> Action.Companion.mapping(
     noinline builder: MapActionBuilder<T, R>.() -> Unit,
 ): Action<T, R> = MapAction(typeOf<R>(), builder)
 
diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/ReduceAction.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/ReduceAction.kt
index 9440be55..93278442 100644
--- a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/ReduceAction.kt
+++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/ReduceAction.kt
@@ -4,15 +4,14 @@ import space.kscience.dataforge.data.*
 import space.kscience.dataforge.meta.Meta
 import space.kscience.dataforge.meta.MutableMeta
 import space.kscience.dataforge.misc.DFBuilder
-import space.kscience.dataforge.misc.DFExperimental
-import space.kscience.dataforge.misc.DFInternal
+import space.kscience.dataforge.misc.UnsafeKType
 import space.kscience.dataforge.names.Name
 import space.kscience.dataforge.names.parseAsName
 import kotlin.reflect.KType
 import kotlin.reflect.typeOf
 
 
-public class JoinGroup<T : Any, R : Any>(
+public class JoinGroup<T, R>(
     public var name: String,
     internal val set: DataTree<T>,
     @PublishedApi internal var outputType: KType,
@@ -22,12 +21,12 @@ public class JoinGroup<T : Any, R : Any>(
 
     public lateinit var result: suspend ActionEnv.(Map<Name, ValueWithMeta<T>>) -> R
 
-    internal fun <R1 : R> result(outputType: KType, f: suspend ActionEnv.(Map<Name,  ValueWithMeta<T>>) -> R1) {
+    internal fun <R1 : R> result(outputType: KType, f: suspend ActionEnv.(Map<Name, ValueWithMeta<T>>) -> R1) {
         this.outputType = outputType
         this.result = f;
     }
 
-    public inline fun <reified R1 : R> result(noinline f: suspend ActionEnv.(Map<Name,  ValueWithMeta<T>>) -> R1) {
+    public inline fun <reified R1 : R> result(noinline f: suspend ActionEnv.(Map<Name, ValueWithMeta<T>>) -> R1) {
         outputType = typeOf<R1>()
         this.result = f;
     }
@@ -35,7 +34,7 @@ public class JoinGroup<T : Any, R : Any>(
 }
 
 @DFBuilder
-public class ReduceGroupBuilder<T : Any, R : Any>(
+public class ReduceGroupBuilder<T, R>(
     public val actionMeta: Meta,
     private val outputType: KType,
 ) {
@@ -67,7 +66,7 @@ public class ReduceGroupBuilder<T : Any, R : Any>(
     /**
      * Apply transformation to the whole node
      */
-    public fun result(resultName: String, f: suspend ActionEnv.(Map<Name,  ValueWithMeta<T>>) -> R) {
+    public fun result(resultName: String, f: suspend ActionEnv.(Map<Name, ValueWithMeta<T>>) -> R) {
         groupRules += { node ->
             listOf(JoinGroup<T, R>(resultName, node, outputType).apply { result(outputType, f) })
         }
@@ -79,14 +78,14 @@ public class ReduceGroupBuilder<T : Any, R : Any>(
 }
 
 @PublishedApi
-internal class ReduceAction<T : Any, R : Any>(
+internal class ReduceAction<T, R>(
     outputType: KType,
     private val action: ReduceGroupBuilder<T, R>.() -> Unit,
 ) : AbstractAction<T, R>(outputType) {
     //TODO optimize reduction. Currently, the whole action recalculates on push
 
-    override fun DataSink<R>.generate(data: DataTree<T>, meta: Meta) {
-        ReduceGroupBuilder<T, R>(meta, outputType).apply(action).buildGroups(data).forEach { group ->
+    override fun DataSink<R>.generate(source: DataTree<T>, meta: Meta) {
+        ReduceGroupBuilder<T, R>(meta, outputType).apply(action).buildGroups(source).forEach { group ->
             val dataFlow: Map<Name, Data<T>> = group.set.asSequence().fold(HashMap()) { acc, value ->
                 acc.apply {
                     acc[value.name] = value.data
@@ -98,7 +97,7 @@ internal class ReduceAction<T : Any, R : Any>(
             val groupMeta = group.meta
 
             val env = ActionEnv(groupName.parseAsName(), groupMeta, meta)
-            @OptIn(DFInternal::class) val res: Data<R> = dataFlow.reduceToData(
+            @OptIn(UnsafeKType::class) val res: Data<R> = dataFlow.reduceToData(
                 group.outputType,
                 meta = groupMeta
             ) { group.result.invoke(env, it) }
@@ -111,7 +110,6 @@ internal class ReduceAction<T : Any, R : Any>(
 /**
  * A one-to-one mapping action
  */
-@DFExperimental
-public inline fun <reified T : Any, reified R : Any> Action.Companion.reducing(
+public inline fun <reified T, reified R> Action.Companion.reducing(
     noinline builder: ReduceGroupBuilder<T, R>.() -> Unit,
 ): Action<T, R> = ReduceAction(typeOf<R>(), builder)
diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/SplitAction.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/SplitAction.kt
index 057419a7..2268b0fa 100644
--- a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/SplitAction.kt
+++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/actions/SplitAction.kt
@@ -5,7 +5,6 @@ import space.kscience.dataforge.meta.Laminate
 import space.kscience.dataforge.meta.Meta
 import space.kscience.dataforge.meta.MutableMeta
 import space.kscience.dataforge.meta.toMutableMeta
-import space.kscience.dataforge.misc.DFExperimental
 import space.kscience.dataforge.names.Name
 import space.kscience.dataforge.names.parseAsName
 import kotlin.collections.set
@@ -13,9 +12,9 @@ import kotlin.reflect.KType
 import kotlin.reflect.typeOf
 
 
-public class SplitBuilder<T : Any, R : Any>(public val name: Name, public val meta: Meta) {
+public class SplitBuilder<T, R>(public val name: Name, public val meta: Meta) {
 
-    public class FragmentRule<T : Any, R : Any>(
+    public class FragmentRule<T, R>(
         public val name: Name,
         public var meta: MutableMeta,
         @PublishedApi internal var outputType: KType,
@@ -44,15 +43,15 @@ public class SplitBuilder<T : Any, R : Any>(public val name: Name, public val me
  * Action that splits each incoming element into a number of fragments defined in builder
  */
 @PublishedApi
-internal class SplitAction<T : Any, R : Any>(
+internal class SplitAction<T, R>(
     outputType: KType,
     private val action: SplitBuilder<T, R>.() -> Unit,
 ) : AbstractAction<T, R>(outputType) {
 
-    private fun DataSink<R>.splitOne(name: Name, data: Data<T>, meta: Meta) {
-        val laminate = Laminate(data.meta, meta)
+    private fun DataSink<R>.splitOne(name: Name, data: Data<T>?, meta: Meta) {
+        val laminate = Laminate(data?.meta, meta)
 
-        val split = SplitBuilder<T, R>(name, data.meta).apply(action)
+        val split = SplitBuilder<T, R>(name, data?.meta ?: Meta.EMPTY).apply(action)
 
 
         // apply individual fragment rules to result
@@ -64,28 +63,36 @@ internal class SplitAction<T : Any, R : Any>(
             ).apply(rule)
             //data.map<R>(outputType, meta = env.meta) { env.result(it) }.named(fragmentName)
 
-            put(
-                fragmentName,
-                @Suppress("OPT_IN_USAGE") Data(outputType, meta = env.meta, dependencies = listOf(data)) {
-                    env.result(data.await())
-                }
-            )
+            if (data == null) {
+                put(fragmentName, null)
+            } else {
+                put(
+                    fragmentName,
+                    @Suppress("OPT_IN_USAGE") Data(outputType, meta = env.meta, dependencies = listOf(data)) {
+                        env.result(data.await())
+                    }
+                )
+            }
         }
     }
 
-    override fun DataSink<R>.generate(data: DataTree<T>, meta: Meta) {
-        data.forEach { splitOne(it.name, it.data, meta) }
+    override fun DataSink<R>.generate(source: DataTree<T>, meta: Meta) {
+        source.forEach { splitOne(it.name, it.data, meta) }
     }
 
-    override fun DataSink<R>.update(source: DataTree<T>, meta: Meta, namedData: NamedData<T>) {
-        splitOne(namedData.name, namedData.data, namedData.meta)
+    override suspend fun DataSink<R>.update(
+        source: DataTree<T>,
+        meta: Meta,
+        updatedData: DataUpdate<T>,
+    )  {
+        splitOne(updatedData.name, updatedData.data, meta)
     }
 }
 
 /**
  * Action that splits each incoming element into a number of fragments defined in builder
  */
-@DFExperimental
-public inline fun <T : Any, reified R : Any> Action.Companion.splitting(
+
+public inline fun <T, reified R> Action.Companion.splitting(
     noinline builder: SplitBuilder<T, R>.() -> Unit,
 ): Action<T, R> = SplitAction(typeOf<R>(), builder)
\ No newline at end of file
diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/Data.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/Data.kt
index a0bb58ea..b9946a48 100644
--- a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/Data.kt
+++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/Data.kt
@@ -4,8 +4,8 @@ import kotlinx.coroutines.*
 import space.kscience.dataforge.meta.Meta
 import space.kscience.dataforge.meta.MetaRepr
 import space.kscience.dataforge.meta.isEmpty
-import space.kscience.dataforge.misc.DFInternal
 import space.kscience.dataforge.misc.DfType
+import space.kscience.dataforge.misc.UnsafeKType
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.EmptyCoroutineContext
 import kotlin.reflect.KType
@@ -41,7 +41,7 @@ public interface Data<out T> : Goal<T>, MetaRepr {
          */
         internal val TYPE_OF_NOTHING: KType = typeOf<Unit>()
 
-        public inline fun <reified T> static(
+        public inline fun <reified T> wrapValue(
             value: T,
             meta: Meta = Meta.EMPTY,
         ): Data<T> = StaticData(typeOf<T>(), value, meta)
@@ -50,10 +50,10 @@ public interface Data<out T> : Goal<T>, MetaRepr {
          * An empty data containing only meta
          */
         @OptIn(DelicateCoroutinesApi::class)
-        public fun empty(meta: Meta): Data<Nothing> = object : Data<Nothing> {
-            override val type: KType = TYPE_OF_NOTHING
+        public fun buildEmpty(meta: Meta): Data<Nothing> = object : Data<Nothing> {
+            override val type: KType get() = TYPE_OF_NOTHING
             override val meta: Meta = meta
-            override val dependencies: Collection<Goal<*>> = emptyList()
+            override val dependencies: Collection<Goal<*>> get() = emptyList()
             override val deferred: Deferred<Nothing>
                 get() = GlobalScope.async(start = CoroutineStart.LAZY) {
                     error("The Data is empty and could not be computed")
@@ -62,6 +62,8 @@ public interface Data<out T> : Goal<T>, MetaRepr {
             override fun async(coroutineScope: CoroutineScope): Deferred<Nothing> = deferred
             override fun reset() {}
         }
+
+        public val EMPTY: Data<Nothing> = buildEmpty(Meta.EMPTY)
     }
 }
 
@@ -87,7 +89,7 @@ public class StaticData<T>(
 public inline fun <reified T> Data(value: T, meta: Meta = Meta.EMPTY): StaticData<T> =
     StaticData(typeOf<T>(), value, meta)
 
-@DFInternal
+@UnsafeKType
 public fun <T> Data(
     type: KType,
     meta: Meta = Meta.EMPTY,
@@ -96,7 +98,7 @@ public fun <T> Data(
     block: suspend () -> T,
 ): Data<T> = LazyData(type, meta, context, dependencies, block)
 
-@OptIn(DFInternal::class)
+@OptIn(UnsafeKType::class)
 public inline fun <reified T> Data(
     meta: Meta = Meta.EMPTY,
     context: CoroutineContext = EmptyCoroutineContext,
diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataFilter.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataFilter.kt
index f45570ad..38174e50 100644
--- a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataFilter.kt
+++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataFilter.kt
@@ -1,28 +1,30 @@
 package space.kscience.dataforge.data
 
-import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.filter
 import space.kscience.dataforge.meta.Meta
 import space.kscience.dataforge.names.Name
+import space.kscience.dataforge.names.NameToken
+import space.kscience.dataforge.names.plus
 import kotlin.reflect.KType
 
 public fun interface DataFilter {
 
-    public fun accepts(name: Name, meta: Meta, type: KType): Boolean
+    public fun accepts(name: Name, meta: Meta?, type: KType): Boolean
 
     public companion object {
         public val EMPTY: DataFilter = DataFilter { _, _, _ -> true }
     }
 }
 
-public fun DataFilter.accepts(data: NamedData<*>): Boolean = accepts(data.name, data.meta, data.type)
 
-public fun <T> Sequence<NamedData<T>>.filterData(predicate: DataFilter): Sequence<NamedData<T>> = filter { data ->
+public fun DataFilter.accepts(update: DataUpdate<*>): Boolean = accepts(update.name, update.data?.meta, update.type)
+
+public fun <T, DU : DataUpdate<T>> Sequence<DU>.filterData(predicate: DataFilter): Sequence<DU> = filter { data ->
     predicate.accepts(data)
 }
 
-public fun <T> Flow<NamedData<T>>.filterData(predicate: DataFilter): Flow<NamedData<T>> = filter { data ->
+public fun <T, DU : DataUpdate<T>> Flow<DU>.filterData(predicate: DataFilter): Flow<DU> = filter { data ->
     predicate.accepts(data)
 }
 
@@ -41,7 +43,8 @@ public fun <T> DataSource<T>.filterData(
 public fun <T> ObservableDataSource<T>.filterData(
     predicate: DataFilter,
 ): ObservableDataSource<T> = object : ObservableDataSource<T> {
-    override fun updates(): Flow<NamedData<T>> = this@filterData.updates().filter { predicate.accepts(it) }
+    override val updates: Flow<DataUpdate<T>>
+        get() = this@filterData.updates.filter { predicate.accepts(it) }
 
     override val dataType: KType get() = this@filterData.dataType
 
@@ -49,14 +52,32 @@ public fun <T> ObservableDataSource<T>.filterData(
         this@filterData.read(name)?.takeIf { predicate.accepts(name, it.meta, it.type) }
 }
 
-public fun <T> GenericDataTree<T, *>.filterData(
-    predicate: DataFilter,
-): DataTree<T> = asSequence().filterData(predicate).toTree(dataType)
+internal class FilteredDataTree<T>(
+    val source: DataTree<T>,
+    val filter: DataFilter,
+    val branch: Name,
+    override val dataType: KType = source.dataType,
+) : DataTree<T> {
 
-public fun <T> GenericObservableDataTree<T, *>.filterData(
-    scope: CoroutineScope,
+    override val data: Data<T>?
+        get() = source[branch].takeIf {
+            filter.accepts(Name.EMPTY, it?.meta, it?.type ?: dataType)
+        }
+
+    override val items: Map<NameToken, DataTree<T>>
+        get() = source.branch(branch)?.items
+            ?.mapValues { FilteredDataTree(source, filter, branch + it.key) }
+            ?.filter { !it.value.isEmpty() }
+            ?: emptyMap()
+
+    override val updates: Flow<DataUpdate<T>>
+        get() = source.updates.filter { filter.accepts(it) }
+}
+
+
+public fun <T> DataTree<T>.filterData(
     predicate: DataFilter,
-): ObservableDataTree<T> = asSequence().filterData(predicate).toObservableTree(dataType, scope, updates().filterData(predicate))
+): DataTree<T> = FilteredDataTree(this, predicate, Name.EMPTY)
 
 
 ///**
diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataSink.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataSink.kt
new file mode 100644
index 00000000..6daeae98
--- /dev/null
+++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataSink.kt
@@ -0,0 +1,159 @@
+package space.kscience.dataforge.data
+
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.launch
+import space.kscience.dataforge.misc.UnsafeKType
+import space.kscience.dataforge.names.*
+import kotlin.reflect.KType
+import kotlin.reflect.typeOf
+
+public interface DataSink<in T> {
+    /**
+     * Put data without notification
+     */
+    public fun put(name: Name, data: Data<T>?)
+
+    /**
+     * Put data and propagate changes downstream
+     */
+    public suspend fun update(name: Name, data: Data<T>?)
+}
+
+/**
+ * Launch continuous update using
+ */
+public fun <T> DataSink<T>.launchUpdate(
+    scope: CoroutineScope,
+    updater: suspend DataSink<T>.() -> Unit,
+): Job = scope.launch {
+    object : DataSink<T> {
+        override fun put(name: Name, data: Data<T>?) {
+            launch {
+                this@launchUpdate.update(name, data)
+            }
+        }
+
+        override suspend fun update(name: Name, data: Data<T>?) {
+            this@launchUpdate.update(name, data)
+        }
+    }.updater()
+}
+
+/**
+ * A mutable version of [DataTree]
+ */
+public interface MutableDataTree<T> : DataTree<T>, DataSink<T> {
+    override var data: Data<T>?
+
+    override val items: Map<NameToken, MutableDataTree<T>>
+
+    public fun getOrCreateItem(token: NameToken): MutableDataTree<T>
+
+    public operator fun set(token: NameToken, data: Data<T>?)
+
+    override fun put(name: Name, data: Data<T>?): Unit = set(name, data)
+}
+
+public tailrec operator fun <T> MutableDataTree<T>.set(name: Name, data: Data<T>?): Unit {
+    when (name.length) {
+        0 -> this.data = data
+        1 -> set(name.first(), data)
+        else -> getOrCreateItem(name.first())[name.cutFirst()] = data
+    }
+}
+
+/**
+ * Provide a mutable subtree if it exists
+ */
+public tailrec fun <T> MutableDataTree<T>.branch(name: Name): MutableDataTree<T>? =
+    when (name.length) {
+        0 -> this
+        1 -> items[name.first()]
+        else -> items[name.first()]?.branch(name.cutFirst())
+    }
+
+private class MutableDataTreeRoot<T>(
+    override val dataType: KType,
+) : MutableDataTree<T> {
+
+    override val updates = MutableSharedFlow<DataUpdate<T>>(100, onBufferOverflow = BufferOverflow.DROP_LATEST)
+
+
+    inner class MutableDataTreeBranch(val branchName: Name) : MutableDataTree<T> {
+
+        override var data: Data<T>? = null
+
+        override val items = HashMap<NameToken, MutableDataTree<T>>()
+
+        override val updates: Flow<DataUpdate<T>> = this@MutableDataTreeRoot.updates.mapNotNull { update ->
+            update.name.removeFirstOrNull(branchName)?.let {
+                DataUpdate(update.data?.type ?: dataType, it, update.data)
+            }
+        }
+        override val dataType: KType get() = this@MutableDataTreeRoot.dataType
+
+
+        override fun getOrCreateItem(token: NameToken): MutableDataTree<T> =
+            items.getOrPut(token) { MutableDataTreeBranch(branchName + token) }
+
+
+        override fun set(token: NameToken, data: Data<T>?) {
+            val subTree = getOrCreateItem(token)
+            subTree.data = data
+        }
+
+        override suspend fun update(name: Name, data: Data<T>?) {
+            if (name.isEmpty()) {
+                this.data = data
+                this@MutableDataTreeRoot.updates.emit(DataUpdate(data?.type ?: dataType, branchName + name, data))
+            } else {
+                getOrCreateItem(name.first()).update(name.cutFirst(), data)
+            }
+        }
+
+    }
+
+
+    override var data: Data<T>? = null
+
+    override val items = HashMap<NameToken, MutableDataTree<T>>()
+
+    override fun getOrCreateItem(token: NameToken): MutableDataTree<T> = items.getOrPut(token) {
+        MutableDataTreeBranch(token.asName())
+    }
+
+    override fun set(token: NameToken, data: Data<T>?) {
+        val subTree = getOrCreateItem(token)
+        subTree.data = data
+    }
+
+    override suspend fun update(name: Name, data: Data<T>?) {
+        if (name.isEmpty()) {
+            this.data = data
+            updates.emit(DataUpdate(data?.type ?: dataType, name, data))
+        } else {
+            getOrCreateItem(name.first()).update(name.cutFirst(), data)
+        }
+    }
+}
+
+/**
+ * Create a new [MutableDataTree]
+ */
+@UnsafeKType
+public fun <T> MutableDataTree(
+    type: KType,
+): MutableDataTree<T> = MutableDataTreeRoot<T>(type)
+
+/**
+ * Create and initialize a observable mutable data tree.
+ */
+@OptIn(UnsafeKType::class)
+public inline fun <reified T> MutableDataTree(
+    generator: MutableDataTree<T>.() -> Unit = {},
+): MutableDataTree<T> = MutableDataTree<T>(typeOf<T>()).apply { generator() }
\ No newline at end of file
diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataSource.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataSource.kt
index d379d027..a9756be7 100644
--- a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataSource.kt
+++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/DataSource.kt
@@ -1,9 +1,8 @@
 package space.kscience.dataforge.data
 
-import kotlinx.coroutines.*
-import kotlinx.coroutines.flow.*
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
 import space.kscience.dataforge.meta.Meta
-import space.kscience.dataforge.misc.DFInternal
 import space.kscience.dataforge.names.*
 import kotlin.contracts.contract
 import kotlin.reflect.KType
@@ -33,46 +32,42 @@ public interface ObservableDataSource<out T> : DataSource<T> {
     /**
      * Flow updates made to the data
      */
-    public fun updates(): Flow<NamedData<T>>
+    public val updates: Flow<DataUpdate<T>>
 }
 
 /**
  * A tree like structure for data holding
  */
-public interface GenericDataTree<out T, out TR : GenericDataTree<T, TR>> : DataSource<T> {
-    public val self: TR
+public interface DataTree<out T> : ObservableDataSource<T> {
 
     public val data: Data<T>?
-    public val items: Map<NameToken, TR>
-
+    public val items: Map<NameToken, DataTree<T>>
 
     override fun read(name: Name): Data<T>? = when (name.length) {
         0 -> data
         else -> items[name.first()]?.read(name.cutFirst())
     }
 
+    /**
+     * Flow updates made to the data
+     */
+    override val updates: Flow<DataUpdate<T>>
+
     public companion object {
-        private object EmptyDataTree : GenericDataTree<Nothing, EmptyDataTree> {
-            override val self: EmptyDataTree get() = this
+        private object EmptyDataTree :
+            DataTree<Nothing> {
             override val data: Data<Nothing>? = null
             override val items: Map<NameToken, EmptyDataTree> = emptyMap()
             override val dataType: KType = typeOf<Unit>()
 
             override fun read(name: Name): Data<Nothing>? = null
-
+            override val updates: Flow<DataUpdate<Nothing>> get() = emptyFlow()
         }
 
-        public val EMPTY: GenericDataTree<Nothing, *> = EmptyDataTree
+        public val EMPTY: DataTree<Nothing> = EmptyDataTree
     }
 }
 
-public typealias DataTree<T> = GenericDataTree<T, GenericDataTree<T, *>>
-
-/**
- * Return a single data in this tree. Throw error if it is not single.
- */
-public fun <T> DataTree<T>.single(): NamedData<T> = asSequence().single()
-
 /**
  * An alias for easier access to tree values
  */
@@ -93,238 +88,44 @@ public fun <T> DataTree<T>.asSequence(
     }
 }
 
+/**
+ * Walk the data tree depth-first.
+ *
+ * @return a [Sequence] of pairs [Name]-[DataTree] for all nodes including the root one.
+ */
+public fun <T> DataTree<T>.walk(
+    namePrefix: Name = Name.EMPTY,
+): Sequence<Pair<Name, DataTree<T>>> = sequence {
+    yield(namePrefix to this@walk)
+    items.forEach { (token, tree) ->
+        yieldAll(tree.walk(namePrefix + token))
+    }
+}
+
 public val DataTree<*>.meta: Meta? get() = data?.meta
 
 /**
  * Provide subtree if it exists
  */
-public tailrec fun <T, TR : GenericDataTree<T, TR>> GenericDataTree<T, TR>.branch(name: Name): TR? =
+public tailrec fun <T> DataTree<T>.branch(name: Name): DataTree<T>? =
     when (name.length) {
-        0 -> self
+        0 -> this
         1 -> items[name.first()]
         else -> items[name.first()]?.branch(name.cutFirst())
     }
 
-public fun <T, TR : GenericDataTree<T, TR>> GenericDataTree<T, TR>.branch(name: String): TR? =
+public fun <T> DataTree<T>.branch(name: String): DataTree<T>? =
     branch(name.parseAsName())
 
-public fun GenericDataTree<*, *>.isEmpty(): Boolean = data == null && items.isEmpty()
-
-@PublishedApi
-internal class FlatDataTree<T>(
-    override val dataType: KType,
-    private val dataSet: Map<Name, Data<T>>,
-    private val prefix: Name,
-) : GenericDataTree<T, FlatDataTree<T>> {
-    override val self: FlatDataTree<T> get() = this
-    override val data: Data<T>? get() = dataSet[prefix]
-    override val items: Map<NameToken, FlatDataTree<T>>
-        get() = dataSet.keys
-            .filter { it.startsWith(prefix) && it.length > prefix.length }
-            .map { it.tokens[prefix.length] }
-            .associateWith { FlatDataTree(dataType, dataSet, prefix + it) }
-
-    override fun read(name: Name): Data<T>? = dataSet[prefix + name]
-}
-
-/**
- * Represent this flat data map as a [DataTree] without copying it
- */
-public inline fun <reified T> Map<Name, Data<T>>.asTree(): DataTree<T> = FlatDataTree(typeOf<T>(), this, Name.EMPTY)
-
-internal fun <T> Sequence<NamedData<T>>.toTree(type: KType): DataTree<T> =
-    FlatDataTree(type, associate { it.name to it.data }, Name.EMPTY)
-
-/**
- * Collect a sequence of [NamedData] to a [DataTree]
- */
-public inline fun <reified T> Sequence<NamedData<T>>.toTree(): DataTree<T> =
-    FlatDataTree(typeOf<T>(), associate { it.name to it.data }, Name.EMPTY)
-
-public interface GenericObservableDataTree<out T, out TR : GenericObservableDataTree<T, TR>> :
-    GenericDataTree<T, TR>, ObservableDataSource<T>, AutoCloseable {
-
-    /**
-     * A scope that is used to propagate updates. When this scope is closed, no new updates could arrive.
-     */
-    public val updatesScope: CoroutineScope
-
-    /**
-     * Close this data tree updates channel
-     */
-    override fun close() {
-        updatesScope.cancel()
-    }
-
-}
-
-public typealias ObservableDataTree<T> = GenericObservableDataTree<T, GenericObservableDataTree<T, *>>
+public fun DataTree<*>.isEmpty(): Boolean = data == null && items.isEmpty()
 
 /**
  * Check if the [DataTree] is observable
  */
-public fun <T> DataTree<T>.isObservable(): Boolean {
+public fun <T> DataSource<T>.isObservable(): Boolean {
     contract {
-        returns(true) implies (this@isObservable is GenericObservableDataTree<T, *>)
+        returns(true) implies (this@isObservable is ObservableDataSource<T>)
     }
-    return this is GenericObservableDataTree<T, *>
+    return this is ObservableDataSource<T>
 }
 
-/**
- * Wait for this data tree to stop spawning updates (updatesScope is closed).
- * If this [DataTree] is not observable, return immediately.
- */
-public suspend fun <T> DataTree<T>.awaitClose() {
-    if (isObservable()) {
-        updatesScope.coroutineContext[Job]?.join()
-    }
-}
-
-public fun <T> DataTree<T>.updates(): Flow<NamedData<T>> =
-    if (this is GenericObservableDataTree<T, *>) updates() else emptyFlow()
-
-public fun interface DataSink<in T> {
-    public fun put(name: Name, data: Data<T>?)
-}
-
-@DFInternal
-public class DataTreeBuilder<T>(private val type: KType) : DataSink<T> {
-    private val map = HashMap<Name, Data<T>>()
-    override fun put(name: Name, data: Data<T>?) {
-        if (data == null) {
-            map.remove(name)
-        } else {
-            map[name] = data
-        }
-    }
-
-    public fun build(): DataTree<T> = FlatDataTree(type, map, Name.EMPTY)
-}
-
-@DFInternal
-public inline fun <T> DataTree(
-    dataType: KType,
-    generator: DataSink<T>.() -> Unit,
-): DataTree<T> = DataTreeBuilder<T>(dataType).apply(generator).build()
-
-/**
- * Create and a data tree.
- */
-@OptIn(DFInternal::class)
-public inline fun <reified T> DataTree(
-    generator: DataSink<T>.() -> Unit,
-): DataTree<T> = DataTreeBuilder<T>(typeOf<T>()).apply(generator).build()
-
-/**
- * A mutable version of [GenericDataTree]
- */
-public interface MutableDataTree<T> : GenericObservableDataTree<T, MutableDataTree<T>>, DataSink<T> {
-    override var data: Data<T>?
-
-    override val items: Map<NameToken, MutableDataTree<T>>
-
-    public fun getOrCreateItem(token: NameToken): MutableDataTree<T>
-
-    public operator fun set(token: NameToken, data: Data<T>?)
-
-    override fun put(name: Name, data: Data<T>?): Unit = set(name, data)
-}
-
-public tailrec operator fun <T> MutableDataTree<T>.set(name: Name, data: Data<T>?): Unit {
-    when (name.length) {
-        0 -> this.data = data
-        1 -> set(name.first(), data)
-        else -> getOrCreateItem(name.first())[name.cutFirst()] = data
-    }
-}
-
-private class MutableDataTreeImpl<T>(
-    override val dataType: KType,
-    override val updatesScope: CoroutineScope,
-) : MutableDataTree<T> {
-
-
-    private val updates = MutableSharedFlow<NamedData<T>>()
-
-    private val children = HashMap<NameToken, MutableDataTree<T>>()
-
-    override var data: Data<T>? = null
-        set(value) {
-            if (!updatesScope.isActive) error("Can't send updates to closed MutableDataTree")
-            field = value
-            if (value != null) {
-                updatesScope.launch {
-                    updates.emit(value.named(Name.EMPTY))
-                }
-            }
-        }
-
-    override val items: Map<NameToken, MutableDataTree<T>> get() = children
-
-    override fun getOrCreateItem(token: NameToken): MutableDataTree<T> = children.getOrPut(token){
-        MutableDataTreeImpl(dataType, updatesScope)
-    }
-
-    override val self: MutableDataTree<T> get() = this
-
-    override fun set(token: NameToken, data: Data<T>?) {
-        if (!updatesScope.isActive) error("Can't send updates to closed MutableDataTree")
-        val subTree = getOrCreateItem(token)
-        subTree.updates().onEach {
-            updates.emit(it.named(token + it.name))
-        }.launchIn(updatesScope)
-        subTree.data = data
-    }
-
-    override fun updates(): Flow<NamedData<T>> = updates
-}
-
-/**
- * Create a new [MutableDataTree]
- *
- * @param parentScope a [CoroutineScope] to control data propagation. By default uses [GlobalScope]
- */
-@OptIn(DelicateCoroutinesApi::class)
-public fun <T> MutableDataTree(
-    type: KType,
-    parentScope: CoroutineScope = GlobalScope,
-): MutableDataTree<T> = MutableDataTreeImpl<T>(
-    type,
-    CoroutineScope(parentScope.coroutineContext + Job(parentScope.coroutineContext[Job]))
-)
-
-/**
- * Create and initialize a observable mutable data tree.
- */
-@OptIn(DelicateCoroutinesApi::class)
-public inline fun <reified T> MutableDataTree(
-    parentScope: CoroutineScope = GlobalScope,
-    generator: MutableDataTree<T>.() -> Unit = {},
-): MutableDataTree<T> = MutableDataTree<T>(typeOf<T>(), parentScope).apply { generator() }
-
-//@DFInternal
-//public fun <T> ObservableDataTree(
-//    type: KType,
-//    scope: CoroutineScope,
-//    generator: suspend MutableDataTree<T>.() -> Unit = {},
-//): ObservableDataTree<T> = MutableDataTree<T>(type, scope.coroutineContext).apply(generator)
-
-public inline fun <reified T> ObservableDataTree(
-    parentScope: CoroutineScope,
-    generator: MutableDataTree<T>.() -> Unit = {},
-): ObservableDataTree<T> = MutableDataTree<T>(typeOf<T>(), parentScope).apply(generator)
-
-
-/**
- * Collect a [Sequence] into an observable tree with additional [updates]
- */
-public fun <T> Sequence<NamedData<T>>.toObservableTree(
-    dataType: KType,
-    parentScope: CoroutineScope,
-    updates: Flow<NamedData<T>>,
-): ObservableDataTree<T> = MutableDataTree<T>(dataType, parentScope).apply {
-    this.putAll(this@toObservableTree)
-    updates.onEach {
-        put(it.name, it.data)
-    }.launchIn(updatesScope)
-}
diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/GroupRule.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/GroupRule.kt
index 90486d85..6dd3caa9 100644
--- a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/GroupRule.kt
+++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/GroupRule.kt
@@ -17,10 +17,10 @@ package space.kscience.dataforge.data
 
 import space.kscience.dataforge.meta.get
 import space.kscience.dataforge.meta.string
-import space.kscience.dataforge.misc.DFInternal
+import space.kscience.dataforge.misc.UnsafeKType
 
 public interface GroupRule {
-    public fun <T : Any> gather(set: DataTree<T>): Map<String, DataTree<T>>
+    public fun <T> gather(set: DataTree<T>): Map<String, DataTree<T>>
 
     public companion object {
         /**
@@ -31,24 +31,24 @@ public interface GroupRule {
          * @param defaultTagValue
          * @return
          */
-        @OptIn(DFInternal::class)
+        @OptIn(UnsafeKType::class)
         public fun byMetaValue(
             key: String,
             defaultTagValue: String,
         ): GroupRule = object : GroupRule {
 
-            override fun <T : Any> gather(
+            override fun <T> gather(
                 set: DataTree<T>,
             ): Map<String, DataTree<T>> {
-                val map = HashMap<String, DataTreeBuilder<T>>()
+                val map = HashMap<String, MutableDataTree<T>>()
 
                 set.forEach { data ->
                     val tagValue: String = data.meta[key]?.string ?: defaultTagValue
-                    map.getOrPut(tagValue) { DataTreeBuilder(set.dataType) }.put(data.name, data.data)
+                    map.getOrPut(tagValue) { MutableDataTree(set.dataType) }.put(data.name, data.data)
                 }
 
 
-                return map.mapValues { it.value.build() }
+                return map
             }
         }
     }
diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/MetaMaskData.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/MetaMaskData.kt
index acf2410d..85f0b2f9 100644
--- a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/MetaMaskData.kt
+++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/MetaMaskData.kt
@@ -20,4 +20,4 @@ public fun <T> Data<T>.withMeta(newMeta: Meta): Data<T> = if (this is MetaMaskDa
  * Create a new [Data] with the same computation, but different meta. The meta is created by applying [block] to
  * the existing data meta.
  */
-public inline fun <T> Data<T>.mapMeta(block: MutableMeta.() -> Unit): Data<T> = withMeta(meta.copy(block))
\ No newline at end of file
+public inline fun <T> Data<T>.withMeta(block: MutableMeta.() -> Unit): Data<T> = withMeta(meta.copy(block))
\ No newline at end of file
diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/NamedData.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/NamedData.kt
index 63e36a3f..9cb4c2d9 100644
--- a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/NamedData.kt
+++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/NamedData.kt
@@ -3,14 +3,34 @@ package space.kscience.dataforge.data
 import space.kscience.dataforge.meta.isEmpty
 import space.kscience.dataforge.misc.Named
 import space.kscience.dataforge.names.Name
+import kotlin.reflect.KType
 
-public interface NamedData<out T> : Named, Data<T> {
+/**
+ * An interface implementing a data update event.
+ *
+ * If [data] is null, then corresponding element should be removed.
+ */
+public interface DataUpdate<out T> : Named {
+    public val type: KType
     override val name: Name
-    public val data: Data<T>
+    public val data: Data<T>?
+}
+
+public fun <T> DataUpdate(type: KType, name: Name, data: Data<T>?): DataUpdate<T> = object : DataUpdate<T> {
+    override val type: KType = type
+    override val name: Name = name
+    override val data: Data<T>? = data
+}
+
+/**
+ * A data coupled to a name.
+ */
+public interface NamedData<out T> : DataUpdate<T>, Data<T> {
+    override val data: Data<T>
 }
 
 public operator fun NamedData<*>.component1(): Name = name
-public operator fun <T: Any> NamedData<T>.component2(): Data<T> = data
+public operator fun <T> NamedData<T>.component2(): Data<T> = data
 
 private class NamedDataImpl<T>(
     override val name: Name,
@@ -32,4 +52,6 @@ public fun <T> Data<T>.named(name: Name): NamedData<T> = if (this is NamedData)
     NamedDataImpl(name, this.data)
 } else {
     NamedDataImpl(name, this)
-}
\ No newline at end of file
+}
+
+public fun <T> NamedData(name: Name, data: Data<T>): NamedData<T> = data.named(name)
\ No newline at end of file
diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/dataBuilders.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/dataBuilders.kt
index 8e8b6eaa..649cfd19 100644
--- a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/dataBuilders.kt
+++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/dataBuilders.kt
@@ -1,62 +1,64 @@
 package space.kscience.dataforge.data
 
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
 import space.kscience.dataforge.meta.Meta
 import space.kscience.dataforge.meta.MutableMeta
-import space.kscience.dataforge.misc.DFExperimental
-import space.kscience.dataforge.names.Name
-import space.kscience.dataforge.names.asName
-import space.kscience.dataforge.names.isEmpty
-import space.kscience.dataforge.names.plus
+import space.kscience.dataforge.names.*
 
 
 public fun <T> DataSink<T>.put(value: NamedData<T>) {
     put(value.name, value.data)
 }
 
-public fun <T> DataSink<T>.branch(dataTree: DataTree<T>) {
-    putAll(dataTree.asSequence())
-}
-
-public inline fun <T> DataSink<T>.branch(
+public inline fun <T> DataSink<T>.putAll(
     prefix: Name,
     block: DataSink<T>.() -> Unit,
 ) {
     if (prefix.isEmpty()) {
         apply(block)
     } else {
-        val proxyDataSink = DataSink { nameWithoutPrefix, data ->
-            this.put(prefix + nameWithoutPrefix, data)
+        val proxyDataSink = object :DataSink<T>{
+            override fun put(name: Name, data: Data<T>?) {
+                this@putAll.put(prefix + name, data)
+            }
+
+            override suspend fun update(name: Name, data: Data<T>?) {
+                this@putAll.update(prefix + name, data)
+            }
+
         }
 
         proxyDataSink.apply(block)
     }
 }
 
-public inline fun <T> DataSink<T>.branch(
+
+public inline fun <T> DataSink<T>.putAll(
     prefix: String,
     block: DataSink<T>.() -> Unit,
-): Unit = branch(prefix.asName(), block)
+): Unit = putAll(prefix.asName(), block)
 
 
 public fun <T> DataSink<T>.put(name: String, value: Data<T>) {
     put(Name.parse(name), value)
 }
 
-public fun <T> DataSink<T>.branch(name: Name, set: DataTree<T>) {
-    branch(name) { putAll(set.asSequence()) }
+public fun <T> DataSink<T>.putAll(name: Name, tree: DataTree<T>) {
+    putAll(name) { putAll(tree.asSequence()) }
 }
 
-public fun <T> DataSink<T>.branch(name: String, set: DataTree<T>) {
-    branch(Name.parse(name)) { putAll(set.asSequence()) }
+
+public fun <T> DataSink<T>.putAll(name: String, tree: DataTree<T>) {
+    putAll(Name.parse(name)) { putAll(tree.asSequence()) }
 }
 
 /**
  * Produce lazy [Data] and emit it into the [MutableDataTree]
  */
-public inline fun <reified T> DataSink<T>.put(
+public inline fun <reified T> DataSink<T>.putValue(
     name: String,
     meta: Meta = Meta.EMPTY,
     noinline producer: suspend () -> T,
@@ -65,7 +67,7 @@ public inline fun <reified T> DataSink<T>.put(
     put(name, data)
 }
 
-public inline fun <reified T> DataSink<T>.put(
+public inline fun <reified T> DataSink<T>.putValue(
     name: Name,
     meta: Meta = Meta.EMPTY,
     noinline producer: suspend () -> T,
@@ -77,24 +79,35 @@ public inline fun <reified T> DataSink<T>.put(
 /**
  * Emit static data with the fixed value
  */
-public inline fun <reified T> DataSink<T>.wrap(
-    name: String,
-    data: T,
-    meta: Meta = Meta.EMPTY,
-): Unit = put(name, Data.static(data, meta))
-
-public inline fun <reified T> DataSink<T>.wrap(
+public inline fun <reified T> DataSink<T>.putValue(
     name: Name,
-    data: T,
+    value: T,
     meta: Meta = Meta.EMPTY,
-): Unit = put(name, Data.static(data, meta))
+): Unit = put(name, Data.wrapValue(value, meta))
 
-public inline fun <reified T> DataSink<T>.wrap(
+public inline fun <reified T> DataSink<T>.putValue(
     name: String,
-    data: T,
-    mutableMeta: MutableMeta.() -> Unit,
-): Unit = put(Name.parse(name), Data.static(data, Meta(mutableMeta)))
+    value: T,
+    meta: Meta = Meta.EMPTY,
+): Unit = put(name, Data.wrapValue(value, meta))
 
+public inline fun <reified T> DataSink<T>.putValue(
+    name: String,
+    value: T,
+    metaBuilder: MutableMeta.() -> Unit,
+): Unit = put(Name.parse(name), Data.wrapValue(value, Meta(metaBuilder)))
+
+public suspend inline fun <reified T> DataSink<T>.updateValue(
+    name: Name,
+    value: T,
+    meta: Meta = Meta.EMPTY,
+): Unit = update(name, Data.wrapValue(value, meta))
+
+public suspend inline fun <reified T> DataSink<T>.updateValue(
+    name: String,
+    value: T,
+    meta: Meta = Meta.EMPTY,
+): Unit = update(name.parseAsName(), Data.wrapValue(value, meta))
 
 public fun <T> DataSink<T>.putAll(sequence: Sequence<NamedData<T>>) {
     sequence.forEach {
@@ -103,30 +116,19 @@ public fun <T> DataSink<T>.putAll(sequence: Sequence<NamedData<T>>) {
 }
 
 public fun <T> DataSink<T>.putAll(tree: DataTree<T>) {
-    this.putAll(tree.asSequence())
-}
-
-
-/**
- * Update data with given node data and meta with node meta.
- */
-@DFExperimental
-public fun <T> MutableDataTree<T>.putAll(source: DataTree<T>) {
-    source.forEach {
-        put(it.name, it.data)
-    }
+    putAll(tree.asSequence())
 }
 
 /**
  * Copy given data set and mirror its changes to this [DataSink] in [this@setAndObserve]. Returns an update [Job]
  */
-public fun <T : Any> DataSink<T>.watchBranch(
-    name: Name,
-    dataSet: ObservableDataTree<T>,
+public fun <T : Any> DataSink<T>.putAllAndWatch(
+    scope: CoroutineScope,
+    branchName: Name = Name.EMPTY,
+    source: DataTree<T>,
 ): Job {
-    branch(name, dataSet)
-    return dataSet.updates().onEach {
-        put(name + it.name, it.data)
-    }.launchIn(dataSet.updatesScope)
-
+    putAll(branchName, source)
+    return source.updates.onEach {
+        update(branchName + it.name, it.data)
+    }.launchIn(scope)
 }
\ No newline at end of file
diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/dataTransform.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/dataTransform.kt
index 8c7ce70e..c0d92e9e 100644
--- a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/dataTransform.kt
+++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/dataTransform.kt
@@ -1,7 +1,7 @@
 package space.kscience.dataforge.data
 
 import space.kscience.dataforge.meta.*
-import space.kscience.dataforge.misc.DFInternal
+import space.kscience.dataforge.misc.UnsafeKType
 import space.kscience.dataforge.names.Name
 import space.kscience.dataforge.names.NameToken
 import kotlin.coroutines.CoroutineContext
@@ -18,6 +18,24 @@ public data class NamedValueWithMeta<T>(val name: Name, val value: T, val meta:
 public suspend fun <T> NamedData<T>.awaitWithMeta(): NamedValueWithMeta<T> =
     NamedValueWithMeta(name, await(), meta)
 
+/**
+ * Lazily transform this data to another data. By convention [block] should not use external data (be pure).
+ * @param type explicit type of the resulting [Data]
+ * @param coroutineContext additional [CoroutineContext] elements used for data computation.
+ * @param meta for the resulting data. By default equals input data.
+ * @param block the transformation itself
+ */
+@UnsafeKType
+public fun <T, R> Data<T>.transform(
+    type: KType,
+    meta: Meta = this.meta,
+    coroutineContext: CoroutineContext = EmptyCoroutineContext,
+    block: suspend (T) -> R,
+): Data<R> = Data(type, meta, coroutineContext, listOf(this)) {
+    block(await())
+}
+
+
 
 /**
  * Lazily transform this data to another data. By convention [block] should not use external data (be pure).
@@ -68,7 +86,7 @@ internal fun Map<*, Data<*>>.joinMeta(): Meta = Meta {
     }
 }
 
-@DFInternal
+@UnsafeKType
 public fun <K, T, R> Map<K, Data<T>>.reduceToData(
     outputType: KType,
     meta: Meta = joinMeta(),
@@ -103,7 +121,7 @@ public inline fun <K, T, reified R> Map<K, Data<T>>.reduceToData(
 
 //Iterable operations
 
-@DFInternal
+@UnsafeKType
 public inline fun <T, R> Iterable<Data<T>>.reduceToData(
     outputType: KType,
     meta: Meta = joinMeta(),
@@ -118,7 +136,7 @@ public inline fun <T, R> Iterable<Data<T>>.reduceToData(
     transformation(map { it.awaitWithMeta() })
 }
 
-@OptIn(DFInternal::class)
+@OptIn(UnsafeKType::class)
 public inline fun <T, reified R> Iterable<Data<T>>.reduceToData(
     meta: Meta = joinMeta(),
     coroutineContext: CoroutineContext = EmptyCoroutineContext,
@@ -141,7 +159,7 @@ public inline fun <T, reified R> Iterable<Data<T>>.foldToData(
 /**
  * Transform an [Iterable] of [NamedData] to a single [Data].
  */
-@DFInternal
+@UnsafeKType
 public inline fun <T, R> Iterable<NamedData<T>>.reduceNamedToData(
     outputType: KType,
     meta: Meta = joinMeta(),
@@ -156,7 +174,7 @@ public inline fun <T, R> Iterable<NamedData<T>>.reduceNamedToData(
     transformation(map { it.awaitWithMeta() })
 }
 
-@OptIn(DFInternal::class)
+@OptIn(UnsafeKType::class)
 public inline fun <T, reified R> Iterable<NamedData<T>>.reduceNamedToData(
     meta: Meta = joinMeta(),
     coroutineContext: CoroutineContext = EmptyCoroutineContext,
@@ -181,7 +199,8 @@ public inline fun <T, reified R> Iterable<NamedData<T>>.foldNamedToData(
 
 //DataSet operations
 
-@DFInternal
+
+@UnsafeKType
 public suspend fun <T, R> DataTree<T>.transform(
     outputType: KType,
     metaTransform: MutableMeta.() -> Unit = {},
@@ -198,7 +217,7 @@ public suspend fun <T, R> DataTree<T>.transform(
     }
 }
 
-@OptIn(DFInternal::class)
+@OptIn(UnsafeKType::class)
 public suspend inline fun <T, reified R> DataTree<T>.transform(
     noinline metaTransform: MutableMeta.() -> Unit = {},
     coroutineContext: CoroutineContext = EmptyCoroutineContext,
diff --git a/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/dataTreeBuilder.kt b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/dataTreeBuilder.kt
new file mode 100644
index 00000000..ac0492f0
--- /dev/null
+++ b/dataforge-data/src/commonMain/kotlin/space/kscience/dataforge/data/dataTreeBuilder.kt
@@ -0,0 +1,112 @@
+package space.kscience.dataforge.data
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
+import space.kscience.dataforge.misc.UnsafeKType
+import space.kscience.dataforge.names.*
+import kotlin.reflect.KType
+import kotlin.reflect.typeOf
+
+
+private class FlatDataTree<T>(
+    override val dataType: KType,
+    private val dataSet: Map<Name, Data<T>>,
+    private val sourceUpdates: Flow<DataUpdate<T>>,
+    private val prefix: Name,
+) : DataTree<T> {
+    override val data: Data<T>? get() = dataSet[prefix]
+    override val items: Map<NameToken, FlatDataTree<T>>
+        get() = dataSet.keys
+            .filter { it.startsWith(prefix) && it.length > prefix.length }
+            .map { it.tokens[prefix.length] }
+            .associateWith { FlatDataTree(dataType, dataSet, sourceUpdates, prefix + it) }
+
+    override fun read(name: Name): Data<T>? = dataSet[prefix + name]
+
+    override val updates: Flow<DataUpdate<T>> =
+        sourceUpdates.mapNotNull { update ->
+            update.name.removeFirstOrNull(prefix)?.let { DataUpdate(dataType, it, update.data) }
+        }
+}
+
+/**
+ * A builder for static [DataTree].
+ */
+private class DataTreeBuilder<T>(
+    private val type: KType,
+    initialData: Map<Name, Data<T>> = emptyMap(),
+) : DataSink<T> {
+
+    private val map = HashMap<Name, Data<T>>(initialData)
+
+    private val mutex = Mutex()
+
+    private val updatesFlow = MutableSharedFlow<DataUpdate<T>>()
+
+    override fun put(name: Name, data: Data<T>?) {
+        if (data == null) {
+            map.remove(name)
+        } else {
+            map[name] = data
+        }
+    }
+
+    override suspend fun update(name: Name, data: Data<T>?) {
+        mutex.withLock {
+            if (data == null) {
+                map.remove(name)
+            } else {
+                map.put(name, data)
+            }
+        }
+        updatesFlow.emit(DataUpdate(data?.type ?: type, name, data))
+    }
+
+    public fun build(): DataTree<T> = FlatDataTree(type, map, updatesFlow, Name.EMPTY)
+}
+
+/**
+ * Create a static [DataTree]
+ */
+@UnsafeKType
+public fun <T> DataTree(
+    dataType: KType,
+    generator: DataSink<T>.() -> Unit,
+): DataTree<T> = DataTreeBuilder<T>(dataType).apply(generator).build()
+
+/**
+ * Create and a data tree.
+ */
+@OptIn(UnsafeKType::class)
+public inline fun <reified T> DataTree(
+    noinline generator: DataSink<T>.() -> Unit,
+): DataTree<T> = DataTree(typeOf<T>(), generator)
+
+
+/**
+ * Represent this flat data map as a [DataTree] without copying it
+ */
+@UnsafeKType
+public fun <T> Map<Name, Data<T>>.asTree(type: KType): DataTree<T> =
+    DataTreeBuilder(type, this).build()
+
+/**
+ * Represent this flat data map as a [DataTree] without copying it
+ */
+@OptIn(UnsafeKType::class)
+public inline fun <reified T> Map<Name, Data<T>>.asTree(): DataTree<T> = asTree(typeOf<T>())
+
+
+@UnsafeKType
+public fun <T> Sequence<NamedData<T>>.toTree(type: KType): DataTree<T> =
+    DataTreeBuilder(type, associate { it.name to it.data }).build()
+
+
+/**
+ * Collect a sequence of [NamedData] to a [DataTree]
+ */
+@OptIn(UnsafeKType::class)
+public inline fun <reified T> Sequence<NamedData<T>>.toTree(): DataTree<T> = toTree(typeOf<T>())
diff --git a/dataforge-data/src/commonTest/kotlin/space/kscience/dataforge/data/DataTreeBuilderTest.kt b/dataforge-data/src/commonTest/kotlin/space/kscience/dataforge/data/DataTreeBuilderTest.kt
new file mode 100644
index 00000000..760aeec2
--- /dev/null
+++ b/dataforge-data/src/commonTest/kotlin/space/kscience/dataforge/data/DataTreeBuilderTest.kt
@@ -0,0 +1,73 @@
+package space.kscience.dataforge.data
+
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.take
+import kotlinx.coroutines.test.runTest
+import space.kscience.dataforge.names.asName
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.time.Duration.Companion.milliseconds
+
+
+internal class DataTreeBuilderTest {
+    @Test
+    fun testTreeBuild() = runTest(timeout = 500.milliseconds) {
+        val node = DataTree<Any> {
+            putAll("primary") {
+                putValue("a", "a")
+                putValue("b", "b")
+            }
+            putValue("c.d", "c.d")
+            putValue("c.f", "c.f")
+        }
+        assertEquals("a", node["primary.a"]?.await())
+        assertEquals("b", node["primary.b"]?.await())
+        assertEquals("c.d", node["c.d"]?.await())
+        assertEquals("c.f", node["c.f"]?.await())
+
+    }
+
+    @Test
+    fun testDataUpdate() = runTest(timeout = 500.milliseconds) {
+        val updateData = DataTree<Any> {
+            putAll("update") {
+                put("a", Data.wrapValue("a"))
+                put("b", Data.wrapValue("b"))
+            }
+        }
+
+        val node = DataTree<Any> {
+            putAll("primary") {
+                putValue("a", "a")
+                putValue("b", "b")
+            }
+            putValue("root", "root")
+            putAll(updateData)
+        }
+
+        assertEquals("a", node["update.a"]?.await())
+        assertEquals("a", node["primary.a"]?.await())
+    }
+
+    @Test
+    fun testDynamicUpdates() = runTest(timeout = 500.milliseconds) {
+        var job: Job? = null
+
+        val subNode = MutableDataTree<Int>()
+
+        val rootNode = MutableDataTree<Int>() {
+            job = putAllAndWatch(this@runTest, "sub".asName(), subNode)
+        }
+
+        repeat(10) {
+            subNode.updateValue("value[$it]", it)
+        }
+
+        rootNode.updates.take(10).collect()
+        assertEquals(9, rootNode["sub.value[9]"]?.await())
+        assertEquals(8, rootNode["sub.value[8]"]?.await())
+
+        job?.cancel()
+    }
+}
\ No newline at end of file
diff --git a/dataforge-data/src/jvmMain/kotlin/space/kscience/dataforge/data/dataFilterJvm.kt b/dataforge-data/src/jvmMain/kotlin/space/kscience/dataforge/data/dataFilterJvm.kt
index 48155595..0cc81f7a 100644
--- a/dataforge-data/src/jvmMain/kotlin/space/kscience/dataforge/data/dataFilterJvm.kt
+++ b/dataforge-data/src/jvmMain/kotlin/space/kscience/dataforge/data/dataFilterJvm.kt
@@ -1,6 +1,5 @@
 package space.kscience.dataforge.data
 
-import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.filter
 import space.kscience.dataforge.misc.DFInternal
@@ -25,32 +24,40 @@ private fun <R> Data<*>.castOrNull(type: KType): Data<R>? =
 
 @Suppress("UNCHECKED_CAST")
 @DFInternal
-public fun <R> Sequence<NamedData<*>>.filterByDataType(type: KType): Sequence<NamedData<R>> =
+public fun <R> Sequence<DataUpdate<*>>.filterByDataType(type: KType): Sequence<NamedData<R>> =
     filter { it.type.isSubtypeOf(type) } as Sequence<NamedData<R>>
 
 @Suppress("UNCHECKED_CAST")
 @DFInternal
-public fun <R> Flow<NamedData<*>>.filterByDataType(type: KType): Flow<NamedData<R>> =
+public fun <R> Flow<DataUpdate<*>>.filterByDataType(type: KType): Flow<NamedData<R>> =
     filter { it.type.isSubtypeOf(type) } as Flow<NamedData<R>>
 
 /**
  * Select all data matching given type and filters. Does not modify paths
  *
- * @param predicate additional filtering condition based on item name and meta. By default, accepts all
+ * @param filter additional filtering condition based on item name and meta. By default, accepts all
  */
+@Suppress("UNCHECKED_CAST")
 @DFInternal
 public fun <R> DataTree<*>.filterByType(
     type: KType,
-    predicate: DataFilter = DataFilter.EMPTY,
-): DataTree<R> = asSequence().filterByDataType<R>(type).filterData(predicate).toTree(type)
+    branch: Name = Name.EMPTY,
+    filter: DataFilter = DataFilter.EMPTY,
+): DataTree<R> {
+    val filterWithType = DataFilter { name, meta, dataType ->
+        filter.accepts(name, meta, dataType) && dataType.isSubtypeOf(type)
+    }
+    return FilteredDataTree(this, filterWithType, branch, type) as DataTree<R>
+}
 
 /**
  * Select a single datum of the appropriate type
  */
 @OptIn(DFInternal::class)
 public inline fun <reified R : Any> DataTree<*>.filterByType(
-    predicate: DataFilter = DataFilter.EMPTY,
-): DataTree<R> = filterByType(typeOf<R>(), predicate)
+    branch: Name = Name.EMPTY,
+    filter: DataFilter = DataFilter.EMPTY,
+): DataTree<R> = filterByType(typeOf<R>(), branch, filter = filter)
 
 /**
  * Select a single datum if it is present and of given [type]
@@ -63,25 +70,3 @@ public inline fun <reified R : Any> DataTree<*>.getByType(name: Name): NamedData
 
 public inline fun <reified R : Any> DataTree<*>.getByType(name: String): NamedData<R>? =
     this@getByType.getByType(typeOf<R>(), Name.parse(name))
-
-/**
- * Select all data matching given type and filters. Does not modify paths
- *
- * @param predicate additional filtering condition based on item name and meta. By default, accepts all
- */
-@DFInternal
-public fun <R> ObservableDataTree<*>.filterByType(
-    type: KType,
-    scope: CoroutineScope,
-    predicate: DataFilter = DataFilter.EMPTY,
-): ObservableDataTree<R> = asSequence()
-    .filterByDataType<R>(type)
-    .filterData(predicate)
-    .toObservableTree(type, scope, updates().filterByDataType<R>(type).filterData(predicate))
-
-
-@OptIn(DFInternal::class)
-public inline fun <reified R> ObservableDataTree<*>.filterByType(
-    scope: CoroutineScope,
-    predicate: DataFilter = DataFilter.EMPTY,
-): ObservableDataTree<R> = filterByType(typeOf<R>(),scope,predicate)
\ No newline at end of file
diff --git a/dataforge-data/src/jvmMain/kotlin/space/kscience/dataforge/data/dataSetBuilderInContext.kt b/dataforge-data/src/jvmMain/kotlin/space/kscience/dataforge/data/dataSetBuilderInContext.kt
index cfccb02b..8ce50a22 100644
--- a/dataforge-data/src/jvmMain/kotlin/space/kscience/dataforge/data/dataSetBuilderInContext.kt
+++ b/dataforge-data/src/jvmMain/kotlin/space/kscience/dataforge/data/dataSetBuilderInContext.kt
@@ -14,14 +14,14 @@ public infix fun <T : Any> String.put(data: Data<T>): Unit =
  * Append node
  */
 context(DataSink<T>)
-public infix fun <T : Any> String.put(dataSet: DataTree<T>): Unit =
-    branch(this, dataSet)
+public infix fun <T : Any> String.putAll(dataSet: DataTree<T>): Unit =
+    putAll(this, dataSet)
 
 /**
  * Build and append node
  */
 context(DataSink<T>)
-public infix fun <T : Any> String.put(
+public infix fun <T : Any> String.putAll(
     block: DataSink<T>.() -> Unit,
-): Unit = branch(Name.parse(this), block)
+): Unit = putAll(Name.parse(this), block)
 
diff --git a/dataforge-data/src/jvmTest/kotlin/space/kscience/dataforge/data/ActionsTest.kt b/dataforge-data/src/jvmTest/kotlin/space/kscience/dataforge/data/ActionsTest.kt
index 4aa6e6d4..13660eee 100644
--- a/dataforge-data/src/jvmTest/kotlin/space/kscience/dataforge/data/ActionsTest.kt
+++ b/dataforge-data/src/jvmTest/kotlin/space/kscience/dataforge/data/ActionsTest.kt
@@ -1,22 +1,24 @@
 package space.kscience.dataforge.data
 
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.take
 import kotlinx.coroutines.test.runTest
-import org.junit.jupiter.api.Test
 import space.kscience.dataforge.actions.Action
 import space.kscience.dataforge.actions.invoke
 import space.kscience.dataforge.actions.mapping
 import space.kscience.dataforge.misc.DFExperimental
+import kotlin.test.Test
 import kotlin.test.assertEquals
+import kotlin.time.Duration.Companion.milliseconds
 
 @OptIn(DFExperimental::class)
 internal class ActionsTest {
     @Test
-    fun testStaticMapAction() = runTest {
+    fun testStaticMapAction() = runTest(timeout = 500.milliseconds) {
         val data: DataTree<Int> = DataTree {
             repeat(10) {
-                wrap(it.toString(), it)
+                putValue(it.toString(), it)
             }
         }
 
@@ -28,7 +30,7 @@ internal class ActionsTest {
     }
 
     @Test
-    fun testDynamicMapAction() = runBlocking {
+    fun testDynamicMapAction() = runTest(timeout = 500.milliseconds) {
         val source: MutableDataTree<Int> = MutableDataTree()
 
         val plusOne = Action.mapping<Int, Int> {
@@ -39,13 +41,10 @@ internal class ActionsTest {
 
 
         repeat(10) {
-            source.wrap(it.toString(), it)
+            source.updateValue(it.toString(), it)
         }
 
-        delay(10)
-
-        source.close()
-        result.awaitClose()
+        result.updates.take(10).onEach { println(it.name) }.collect()
 
         assertEquals(2, result["1"]?.await())
     }
diff --git a/dataforge-data/src/jvmTest/kotlin/space/kscience/dataforge/data/DataTreeBuilderTest.kt b/dataforge-data/src/jvmTest/kotlin/space/kscience/dataforge/data/DataTreeBuilderTest.kt
deleted file mode 100644
index 95b7a7bd..00000000
--- a/dataforge-data/src/jvmTest/kotlin/space/kscience/dataforge/data/DataTreeBuilderTest.kt
+++ /dev/null
@@ -1,68 +0,0 @@
-package space.kscience.dataforge.data
-
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.test.runTest
-import space.kscience.dataforge.misc.DFExperimental
-import space.kscience.dataforge.names.asName
-import kotlin.test.Test
-import kotlin.test.assertEquals
-
-
-internal class DataTreeBuilderTest {
-    @Test
-    fun testTreeBuild() = runTest {
-        val node = DataTree<Any> {
-            "primary" put {
-                wrap("a", "a")
-                wrap("b", "b")
-            }
-            wrap("c.d", "c.d")
-            wrap("c.f", "c.f")
-        }
-        assertEquals("a", node["primary.a"]?.await())
-        assertEquals("b", node["primary.b"]?.await())
-        assertEquals("c.d", node["c.d"]?.await())
-        assertEquals("c.f", node["c.f"]?.await())
-
-    }
-
-    @OptIn(DFExperimental::class)
-    @Test
-    fun testDataUpdate() = runTest {
-        val updateData = DataTree<Any> {
-            "update" put {
-                "a" put Data.static("a")
-                "b" put Data.static("b")
-            }
-        }
-
-        val node = DataTree<Any> {
-            "primary" put {
-                wrap("a", "a")
-                wrap("b", "b")
-            }
-            wrap("root", "root")
-            putAll(updateData)
-        }
-
-        assertEquals("a", node["update.a"]?.await())
-        assertEquals("a", node["primary.a"]?.await())
-    }
-
-    @Test
-    fun testDynamicUpdates() = runBlocking {
-        val subNode = MutableDataTree<Int>()
-
-        val rootNode = MutableDataTree<Int> {
-            watchBranch("sub".asName(), subNode)
-        }
-
-        repeat(10) {
-            subNode.wrap("value[$it]", it)
-        }
-
-        delay(20)
-        assertEquals(9, rootNode["sub.value[9]"]?.await())
-    }
-}
\ No newline at end of file
diff --git a/dataforge-io/README.md b/dataforge-io/README.md
index 85e49e5a..5a9979a9 100644
--- a/dataforge-io/README.md
+++ b/dataforge-io/README.md
@@ -6,7 +6,7 @@ IO module
 
 ## Artifact:
 
-The Maven coordinates of this project are `space.kscience:dataforge-io:0.8.0`.
+The Maven coordinates of this project are `space.kscience:dataforge-io:0.9.0-dev-1`.
 
 **Gradle Kotlin DSL:**
 ```kotlin
@@ -16,6 +16,6 @@ repositories {
 }
 
 dependencies {
-    implementation("space.kscience:dataforge-io:0.8.0")
+    implementation("space.kscience:dataforge-io:0.9.0-dev-1")
 }
 ```
diff --git a/dataforge-io/dataforge-io-yaml/README.md b/dataforge-io/dataforge-io-yaml/README.md
index 20f5b4f6..f70a1490 100644
--- a/dataforge-io/dataforge-io-yaml/README.md
+++ b/dataforge-io/dataforge-io-yaml/README.md
@@ -6,7 +6,7 @@ YAML meta IO
 
 ## Artifact:
 
-The Maven coordinates of this project are `space.kscience:dataforge-io-yaml:0.8.0`.
+The Maven coordinates of this project are `space.kscience:dataforge-io-yaml:0.9.0-dev-1`.
 
 **Gradle Kotlin DSL:**
 ```kotlin
@@ -16,6 +16,6 @@ repositories {
 }
 
 dependencies {
-    implementation("space.kscience:dataforge-io-yaml:0.8.0")
+    implementation("space.kscience:dataforge-io-yaml:0.9.0-dev-1")
 }
 ```
diff --git a/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/EnvelopeParts.kt b/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/EnvelopeParts.kt
index 183e7b03..88231899 100644
--- a/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/EnvelopeParts.kt
+++ b/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/EnvelopeParts.kt
@@ -11,6 +11,7 @@ import space.kscience.dataforge.io.PartDescriptor.Companion.PARTS_KEY
 import space.kscience.dataforge.io.PartDescriptor.Companion.SEPARATOR_KEY
 import space.kscience.dataforge.meta.*
 import space.kscience.dataforge.names.asName
+import space.kscience.dataforge.names.getIndexedList
 import space.kscience.dataforge.names.plus
 
 private class PartDescriptor : Scheme() {
@@ -84,7 +85,7 @@ public fun EnvelopeBuilder.envelopes(
 public fun Envelope.parts(): EnvelopeParts {
     if (data == null) return emptyList()
     //TODO add zip folder reader
-    val parts = meta.getIndexed(PARTS_KEY).values.map {
+    val parts = meta.getIndexedList(PARTS_KEY).map {
         PartDescriptor.read(it)
     }
     return if (parts.isEmpty()) {
diff --git a/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/IOPlugin.kt b/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/IOPlugin.kt
index 0d79da4d..f431a731 100644
--- a/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/IOPlugin.kt
+++ b/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/IOPlugin.kt
@@ -6,7 +6,7 @@ import space.kscience.dataforge.io.IOFormatFactory.Companion.IO_FORMAT_TYPE
 import space.kscience.dataforge.io.MetaFormatFactory.Companion.META_FORMAT_TYPE
 import space.kscience.dataforge.meta.Meta
 import space.kscience.dataforge.meta.string
-import space.kscience.dataforge.misc.DFInternal
+import space.kscience.dataforge.misc.UnsafeKType
 import space.kscience.dataforge.names.Name
 import kotlin.reflect.KType
 import kotlin.reflect.typeOf
@@ -19,11 +19,11 @@ public class IOPlugin(meta: Meta) : AbstractPlugin(meta) {
     }
 
     @Suppress("UNCHECKED_CAST")
-    @DFInternal
+    @UnsafeKType
     public fun <T> resolveIOFormat(type: KType, meta: Meta): IOFormat<T>? =
         ioFormatFactories.singleOrNull { it.type == type }?.build(context, meta) as? IOFormat<T>
 
-    @OptIn(DFInternal::class)
+    @OptIn(UnsafeKType::class)
     public inline fun <reified T> resolveIOFormat(meta: Meta = Meta.EMPTY): IOFormat<T>? =
         resolveIOFormat(typeOf<T>(), meta)
 
diff --git a/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/ioMisc.kt b/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/ioMisc.kt
index 93fd210a..b5e56dfe 100644
--- a/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/ioMisc.kt
+++ b/dataforge-io/src/commonMain/kotlin/space/kscience/dataforge/io/ioMisc.kt
@@ -5,7 +5,6 @@ import kotlinx.io.bytestring.ByteString
 import kotlinx.io.bytestring.decodeToString
 import kotlinx.io.bytestring.encodeToByteString
 import space.kscience.dataforge.meta.Meta
-import space.kscience.dataforge.misc.DFExperimental
 import kotlin.math.min
 
 /**
@@ -52,7 +51,6 @@ public fun IOPlugin.peekBinaryEnvelopeFormat(binary: Binary): EnvelopeFormat? {
 /**
  * A zero-copy read from
  */
-@DFExperimental
 public fun IOPlugin.readEnvelope(
     binary: Binary,
     readNonEnvelopes: Boolean = false,
@@ -62,7 +60,6 @@ public fun IOPlugin.readEnvelope(
     Envelope(Meta.EMPTY, binary)
 } else error("Can't infer format for $binary")
 
-@DFExperimental
 public fun IOPlugin.readEnvelope(
     string: String,
     readNonEnvelopes: Boolean = false,
diff --git a/dataforge-io/src/jvmMain/kotlin/space/kscience/dataforge/io/fileIO.kt b/dataforge-io/src/jvmMain/kotlin/space/kscience/dataforge/io/fileIO.kt
index c15280f3..7df23eb5 100644
--- a/dataforge-io/src/jvmMain/kotlin/space/kscience/dataforge/io/fileIO.kt
+++ b/dataforge-io/src/jvmMain/kotlin/space/kscience/dataforge/io/fileIO.kt
@@ -77,14 +77,12 @@ public fun Path.rewrite(block: Sink.() -> Unit): Unit {
     stream.asSink().buffered().use(block)
 }
 
-@DFExperimental
 public fun EnvelopeFormat.readFile(path: Path): Envelope = readFrom(path.asBinary())
 
 /**
  * Resolve IOFormat based on type
  */
 @Suppress("UNCHECKED_CAST")
-@DFExperimental
 public inline fun <reified T : Any> IOPlugin.resolveIOFormat(): IOFormat<T>? =
     ioFormatFactories.find { it.type.isSupertypeOf(typeOf<T>()) } as IOFormat<T>?
 
@@ -192,7 +190,6 @@ public fun IOPlugin.peekFileEnvelopeFormat(path: Path): EnvelopeFormat? {
  *
  * Return null otherwise.
  */
-@DFExperimental
 public fun IOPlugin.readEnvelopeFile(
     path: Path,
     readNonEnvelopes: Boolean = false,
diff --git a/dataforge-meta/README.md b/dataforge-meta/README.md
index bd11ebf1..e4fcacb1 100644
--- a/dataforge-meta/README.md
+++ b/dataforge-meta/README.md
@@ -6,7 +6,7 @@ Meta definition and basic operations on meta
 
 ## Artifact:
 
-The Maven coordinates of this project are `space.kscience:dataforge-meta:0.8.0`.
+The Maven coordinates of this project are `space.kscience:dataforge-meta:0.9.0-dev-1`.
 
 **Gradle Kotlin DSL:**
 ```kotlin
@@ -16,6 +16,6 @@ repositories {
 }
 
 dependencies {
-    implementation("space.kscience:dataforge-meta:0.8.0")
+    implementation("space.kscience:dataforge-meta:0.9.0-dev-1")
 }
 ```
diff --git a/dataforge-meta/api/dataforge-meta.api b/dataforge-meta/api/dataforge-meta.api
index 49f28259..0bb4a908 100644
--- a/dataforge-meta/api/dataforge-meta.api
+++ b/dataforge-meta/api/dataforge-meta.api
@@ -1,3 +1,15 @@
+public final class space/kscience/dataforge/meta/ByteArrayValue : java/lang/Iterable, kotlin/jvm/internal/markers/KMappedMarker, space/kscience/dataforge/meta/Value {
+	public fun <init> ([B)V
+	public fun equals (Ljava/lang/Object;)Z
+	public fun getList ()Ljava/util/List;
+	public fun getType ()Lspace/kscience/dataforge/meta/ValueType;
+	public synthetic fun getValue ()Ljava/lang/Object;
+	public fun getValue ()[B
+	public fun hashCode ()I
+	public fun iterator ()Ljava/util/Iterator;
+	public fun toString ()Ljava/lang/String;
+}
+
 public abstract interface class space/kscience/dataforge/meta/Configurable {
 	public abstract fun getMeta ()Lspace/kscience/dataforge/meta/MutableMeta;
 }
@@ -30,7 +42,20 @@ public final class space/kscience/dataforge/meta/EnumValue : space/kscience/data
 }
 
 public final class space/kscience/dataforge/meta/ExoticValuesKt {
+	public static final fun asValue ([B)Lspace/kscience/dataforge/meta/Value;
 	public static final fun asValue ([D)Lspace/kscience/dataforge/meta/Value;
+	public static final fun byteArray (Lspace/kscience/dataforge/meta/MetaProvider;[BLspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty;
+	public static final fun byteArray (Lspace/kscience/dataforge/meta/MutableMetaProvider;[BLspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty;
+	public static synthetic fun byteArray$default (Lspace/kscience/dataforge/meta/MetaProvider;[BLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty;
+	public static synthetic fun byteArray$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;[BLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty;
+	public static final fun doubleArray (Lspace/kscience/dataforge/meta/MetaProvider;[DLspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty;
+	public static final fun doubleArray (Lspace/kscience/dataforge/meta/MutableMetaProvider;[DLspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty;
+	public static synthetic fun doubleArray$default (Lspace/kscience/dataforge/meta/MetaProvider;[DLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty;
+	public static synthetic fun doubleArray$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;[DLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty;
+	public static final fun getByteArray (Lspace/kscience/dataforge/meta/Meta;)[B
+	public static final fun getByteArray (Lspace/kscience/dataforge/meta/Value;)[B
+	public static final fun getDoubleArray (Lspace/kscience/dataforge/meta/Meta;)[D
+	public static final fun getDoubleArray (Lspace/kscience/dataforge/meta/Value;)[D
 	public static final fun lazyParseValue (Ljava/lang/String;)Lspace/kscience/dataforge/meta/LazyParsedValue;
 }
 
@@ -78,6 +103,8 @@ public final class space/kscience/dataforge/meta/Laminate : space/kscience/dataf
 	public synthetic fun get (Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/TypedMeta;
 	public fun getItems ()Ljava/util/Map;
 	public final fun getLayers ()Ljava/util/List;
+	public fun getSelf ()Lspace/kscience/dataforge/meta/Laminate;
+	public synthetic fun getSelf ()Lspace/kscience/dataforge/meta/TypedMeta;
 	public fun getValue ()Lspace/kscience/dataforge/meta/Value;
 	public fun hashCode ()I
 	public final fun merge ()Lspace/kscience/dataforge/meta/SealedMeta;
@@ -173,7 +200,7 @@ public final class space/kscience/dataforge/meta/MetaBuilder : space/kscience/da
 public abstract interface annotation class space/kscience/dataforge/meta/MetaBuilderMarker : java/lang/annotation/Annotation {
 }
 
-public abstract interface class space/kscience/dataforge/meta/MetaConverter : space/kscience/dataforge/meta/MetaSpec {
+public abstract interface class space/kscience/dataforge/meta/MetaConverter : space/kscience/dataforge/meta/MetaReader {
 	public static final field Companion Lspace/kscience/dataforge/meta/MetaConverter$Companion;
 	public abstract fun convert (Ljava/lang/Object;)Lspace/kscience/dataforge/meta/Meta;
 	public fun getDescriptor ()Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;
@@ -199,53 +226,56 @@ public final class space/kscience/dataforge/meta/MetaConverterKt {
 	public static final fun convertNullable (Lspace/kscience/dataforge/meta/MetaConverter;Ljava/lang/Object;)Lspace/kscience/dataforge/meta/Meta;
 }
 
+public abstract interface class space/kscience/dataforge/meta/MetaDelegate : kotlin/properties/ReadOnlyProperty, space/kscience/dataforge/meta/descriptors/Described {
+}
+
 public final class space/kscience/dataforge/meta/MetaDelegateKt {
-	public static final fun boolean (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty;
-	public static final fun boolean (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)Lkotlin/properties/ReadOnlyProperty;
-	public static final fun boolean (Lspace/kscience/dataforge/meta/MetaProvider;ZLspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty;
-	public static synthetic fun boolean$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty;
-	public static synthetic fun boolean$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty;
-	public static synthetic fun boolean$default (Lspace/kscience/dataforge/meta/MetaProvider;ZLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty;
-	public static final fun double (Lspace/kscience/dataforge/meta/MetaProvider;DLspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty;
-	public static final fun double (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty;
-	public static synthetic fun double$default (Lspace/kscience/dataforge/meta/MetaProvider;DLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty;
-	public static synthetic fun double$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty;
-	public static final fun float (Lspace/kscience/dataforge/meta/MetaProvider;FLspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty;
-	public static final fun float (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty;
-	public static synthetic fun float$default (Lspace/kscience/dataforge/meta/MetaProvider;FLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty;
-	public static synthetic fun float$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty;
-	public static final fun int (Lspace/kscience/dataforge/meta/MetaProvider;ILspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty;
-	public static final fun int (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty;
-	public static synthetic fun int$default (Lspace/kscience/dataforge/meta/MetaProvider;ILspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty;
-	public static synthetic fun int$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty;
-	public static final fun listOfSpec (Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/meta/MetaSpec;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty;
-	public static synthetic fun listOfSpec$default (Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/meta/MetaSpec;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty;
-	public static final fun long (Lspace/kscience/dataforge/meta/MetaProvider;JLspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty;
-	public static final fun long (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty;
-	public static synthetic fun long$default (Lspace/kscience/dataforge/meta/MetaProvider;JLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty;
-	public static synthetic fun long$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty;
-	public static final fun node (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty;
-	public static final fun node (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/MetaSpec;)Lkotlin/properties/ReadOnlyProperty;
-	public static synthetic fun node$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty;
-	public static synthetic fun node$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/MetaSpec;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty;
-	public static final fun number (Lspace/kscience/dataforge/meta/MetaProvider;Ljava/lang/Number;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty;
-	public static final fun number (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty;
-	public static final fun number (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)Lkotlin/properties/ReadOnlyProperty;
-	public static synthetic fun number$default (Lspace/kscience/dataforge/meta/MetaProvider;Ljava/lang/Number;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty;
-	public static synthetic fun number$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty;
-	public static synthetic fun number$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty;
-	public static final fun spec (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/meta/MetaSpec;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty;
-	public static synthetic fun spec$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/meta/MetaSpec;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty;
-	public static final fun string (Lspace/kscience/dataforge/meta/MetaProvider;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty;
-	public static final fun string (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty;
-	public static final fun string (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)Lkotlin/properties/ReadOnlyProperty;
-	public static synthetic fun string$default (Lspace/kscience/dataforge/meta/MetaProvider;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty;
-	public static synthetic fun string$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty;
-	public static synthetic fun string$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty;
-	public static final fun value (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadOnlyProperty;
-	public static final fun value (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function1;)Lkotlin/properties/ReadOnlyProperty;
-	public static synthetic fun value$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty;
-	public static synthetic fun value$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty;
+	public static final fun boolean (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static final fun boolean (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static final fun boolean (Lspace/kscience/dataforge/meta/MetaProvider;ZLspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static synthetic fun boolean$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static synthetic fun boolean$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static synthetic fun boolean$default (Lspace/kscience/dataforge/meta/MetaProvider;ZLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static final fun double (Lspace/kscience/dataforge/meta/MetaProvider;DLspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static final fun double (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static synthetic fun double$default (Lspace/kscience/dataforge/meta/MetaProvider;DLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static synthetic fun double$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static final fun float (Lspace/kscience/dataforge/meta/MetaProvider;FLspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static final fun float (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static synthetic fun float$default (Lspace/kscience/dataforge/meta/MetaProvider;FLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static synthetic fun float$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static final fun int (Lspace/kscience/dataforge/meta/MetaProvider;ILspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static final fun int (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static synthetic fun int$default (Lspace/kscience/dataforge/meta/MetaProvider;ILspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static synthetic fun int$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static final fun listOfSpec (Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/meta/MetaReader;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static synthetic fun listOfSpec$default (Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/meta/MetaReader;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static final fun long (Lspace/kscience/dataforge/meta/MetaProvider;JLspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static final fun long (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static synthetic fun long$default (Lspace/kscience/dataforge/meta/MetaProvider;JLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static synthetic fun long$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static final fun node (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/MetaReader;)Lkotlin/properties/ReadOnlyProperty;
+	public static final fun node (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static synthetic fun node$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/MetaReader;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty;
+	public static synthetic fun node$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static final fun number (Lspace/kscience/dataforge/meta/MetaProvider;Ljava/lang/Number;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static final fun number (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static final fun number (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static synthetic fun number$default (Lspace/kscience/dataforge/meta/MetaProvider;Ljava/lang/Number;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static synthetic fun number$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static synthetic fun number$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static final fun spec (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/meta/MetaReader;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static synthetic fun spec$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/meta/MetaReader;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static final fun string (Lspace/kscience/dataforge/meta/MetaProvider;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static final fun string (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static final fun string (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static synthetic fun string$default (Lspace/kscience/dataforge/meta/MetaProvider;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static synthetic fun string$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static synthetic fun string$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static final fun value (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static final fun value (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static synthetic fun value$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate;
+	public static synthetic fun value$default (Lspace/kscience/dataforge/meta/MetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MetaDelegate;
 }
 
 public final class space/kscience/dataforge/meta/MetaKt {
@@ -266,7 +296,6 @@ public final class space/kscience/dataforge/meta/MetaKt {
 	public static final fun getLong (Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Long;
 	public static final synthetic fun getNonNullable (Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/names/NameToken;)Lspace/kscience/dataforge/meta/Meta;
 	public static final fun getNumber (Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Number;
-	public static final fun getSelf (Lspace/kscience/dataforge/meta/TypedMeta;)Lspace/kscience/dataforge/meta/TypedMeta;
 	public static final fun getShort (Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Short;
 	public static final fun getString (Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/String;
 	public static final fun getStringList (Lspace/kscience/dataforge/meta/Meta;)Ljava/util/List;
@@ -283,6 +312,16 @@ public abstract interface class space/kscience/dataforge/meta/MetaProvider : spa
 	public fun getValue (Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/Value;
 }
 
+public abstract interface class space/kscience/dataforge/meta/MetaReader : space/kscience/dataforge/meta/descriptors/Described {
+	public fun read (Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object;
+	public abstract fun readOrNull (Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object;
+}
+
+public final class space/kscience/dataforge/meta/MetaReaderKt {
+	public static final fun readNullable (Lspace/kscience/dataforge/meta/MetaReader;Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object;
+	public static final fun readValue (Lspace/kscience/dataforge/meta/MetaReader;Lspace/kscience/dataforge/meta/Value;)Ljava/lang/Object;
+}
+
 public abstract interface class space/kscience/dataforge/meta/MetaRepr {
 	public abstract fun toMeta ()Lspace/kscience/dataforge/meta/Meta;
 }
@@ -296,16 +335,6 @@ public final class space/kscience/dataforge/meta/MetaSerializer : kotlinx/serial
 	public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/dataforge/meta/Meta;)V
 }
 
-public abstract interface class space/kscience/dataforge/meta/MetaSpec : space/kscience/dataforge/meta/descriptors/Described {
-	public fun read (Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object;
-	public abstract fun readOrNull (Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object;
-}
-
-public final class space/kscience/dataforge/meta/MetaSpecKt {
-	public static final fun readNullable (Lspace/kscience/dataforge/meta/MetaSpec;Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object;
-	public static final fun readValue (Lspace/kscience/dataforge/meta/MetaSpec;Lspace/kscience/dataforge/meta/Value;)Ljava/lang/Object;
-}
-
 public final class space/kscience/dataforge/meta/MetaTransformation {
 	public static final field Companion Lspace/kscience/dataforge/meta/MetaTransformation$Companion;
 	public static final fun apply-impl (Ljava/util/Collection;Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/dataforge/meta/Meta;
@@ -352,6 +381,7 @@ public abstract interface class space/kscience/dataforge/meta/MutableMeta : spac
 	public fun put (Ljava/lang/String;Lspace/kscience/dataforge/meta/MetaRepr;)V
 	public fun put (Ljava/lang/String;Lspace/kscience/dataforge/meta/Value;)V
 	public fun put (Ljava/lang/String;Z)V
+	public fun put (Ljava/lang/String;[B)V
 	public fun put (Ljava/lang/String;[D)V
 	public fun put (Lspace/kscience/dataforge/names/Name;Ljava/lang/Enum;)V
 	public fun put (Lspace/kscience/dataforge/names/Name;Ljava/lang/Number;)V
@@ -371,68 +401,70 @@ public final class space/kscience/dataforge/meta/MutableMeta$Companion {
 	public final fun serializer ()Lkotlinx/serialization/KSerializer;
 }
 
+public abstract interface class space/kscience/dataforge/meta/MutableMetaDelegate : kotlin/properties/ReadWriteProperty, space/kscience/dataforge/meta/descriptors/Described {
+}
+
 public final class space/kscience/dataforge/meta/MutableMetaDelegateKt {
-	public static final fun boolean (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty;
-	public static final fun boolean (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)Lkotlin/properties/ReadWriteProperty;
-	public static final fun boolean (Lspace/kscience/dataforge/meta/MutableMetaProvider;ZLspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty;
-	public static synthetic fun boolean$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty;
-	public static synthetic fun boolean$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty;
-	public static synthetic fun boolean$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;ZLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty;
-	public static final fun convertable (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/meta/MetaConverter;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty;
-	public static synthetic fun convertable$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/meta/MetaConverter;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty;
-	public static final fun double (Lspace/kscience/dataforge/meta/MutableMetaProvider;DLspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty;
-	public static final fun double (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty;
-	public static synthetic fun double$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;DLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty;
-	public static synthetic fun double$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty;
-	public static final fun doubleArray (Lspace/kscience/dataforge/meta/MutableMetaProvider;[DLspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty;
-	public static synthetic fun doubleArray$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;[DLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty;
-	public static final fun float (Lspace/kscience/dataforge/meta/MutableMetaProvider;FLspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty;
-	public static final fun float (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty;
-	public static synthetic fun float$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;FLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty;
-	public static synthetic fun float$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty;
-	public static final fun int (Lspace/kscience/dataforge/meta/MutableMetaProvider;ILspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty;
-	public static final fun int (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty;
-	public static synthetic fun int$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;ILspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty;
-	public static synthetic fun int$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty;
-	public static final fun listOfConvertable (Lspace/kscience/dataforge/meta/MutableMeta;Lspace/kscience/dataforge/meta/MetaConverter;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty;
-	public static synthetic fun listOfConvertable$default (Lspace/kscience/dataforge/meta/MutableMeta;Lspace/kscience/dataforge/meta/MetaConverter;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty;
-	public static final fun listValue (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lkotlin/properties/ReadWriteProperty;
-	public static synthetic fun listValue$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty;
-	public static final fun long (Lspace/kscience/dataforge/meta/MutableMetaProvider;JLspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty;
-	public static final fun long (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty;
-	public static synthetic fun long$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;JLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty;
-	public static synthetic fun long$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty;
-	public static final fun node (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty;
-	public static final fun node (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/MetaConverter;)Lkotlin/properties/ReadWriteProperty;
-	public static synthetic fun node$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty;
-	public static synthetic fun node$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/MetaConverter;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty;
-	public static final fun number (Lspace/kscience/dataforge/meta/MutableMetaProvider;Ljava/lang/Number;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty;
-	public static final fun number (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty;
-	public static final fun number (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)Lkotlin/properties/ReadWriteProperty;
-	public static synthetic fun number$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Ljava/lang/Number;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty;
-	public static synthetic fun number$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty;
-	public static synthetic fun number$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty;
-	public static final fun numberList (Lspace/kscience/dataforge/meta/MutableMetaProvider;[Ljava/lang/Number;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty;
-	public static synthetic fun numberList$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;[Ljava/lang/Number;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty;
-	public static final fun string (Lspace/kscience/dataforge/meta/MutableMetaProvider;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty;
-	public static final fun string (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty;
-	public static final fun string (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)Lkotlin/properties/ReadWriteProperty;
-	public static synthetic fun string$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty;
-	public static synthetic fun string$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty;
-	public static synthetic fun string$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty;
-	public static final fun stringList (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty;
-	public static final fun stringList (Lspace/kscience/dataforge/meta/MutableMetaProvider;[Ljava/lang/String;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty;
-	public static synthetic fun stringList$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty;
-	public static synthetic fun stringList$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;[Ljava/lang/String;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty;
-	public static final fun value (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty;
-	public static final fun value (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lkotlin/properties/ReadWriteProperty;
-	public static synthetic fun value$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty;
-	public static synthetic fun value$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty;
+	public static final fun boolean (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static final fun boolean (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static final fun boolean (Lspace/kscience/dataforge/meta/MutableMetaProvider;ZLspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static synthetic fun boolean$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static synthetic fun boolean$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static synthetic fun boolean$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;ZLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static final fun convertable (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/meta/MetaConverter;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static synthetic fun convertable$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/meta/MetaConverter;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static final fun double (Lspace/kscience/dataforge/meta/MutableMetaProvider;DLspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static final fun double (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static synthetic fun double$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;DLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static synthetic fun double$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static final fun float (Lspace/kscience/dataforge/meta/MutableMetaProvider;FLspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static final fun float (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static synthetic fun float$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;FLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static synthetic fun float$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static final fun int (Lspace/kscience/dataforge/meta/MutableMetaProvider;ILspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static final fun int (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static synthetic fun int$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;ILspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static synthetic fun int$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static final fun listOfConvertable (Lspace/kscience/dataforge/meta/MutableMeta;Lspace/kscience/dataforge/meta/MetaConverter;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static synthetic fun listOfConvertable$default (Lspace/kscience/dataforge/meta/MutableMeta;Lspace/kscience/dataforge/meta/MetaConverter;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static final fun listValue (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static synthetic fun listValue$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static final fun long (Lspace/kscience/dataforge/meta/MutableMetaProvider;JLspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static final fun long (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static synthetic fun long$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;JLspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static synthetic fun long$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static final fun node (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/MetaConverter;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static final fun node (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static synthetic fun node$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/MetaConverter;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static synthetic fun node$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static final fun number (Lspace/kscience/dataforge/meta/MutableMetaProvider;Ljava/lang/Number;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static final fun number (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static final fun number (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static synthetic fun number$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Ljava/lang/Number;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static synthetic fun number$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static synthetic fun number$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static final fun numberList (Lspace/kscience/dataforge/meta/MutableMetaProvider;[Ljava/lang/Number;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static synthetic fun numberList$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;[Ljava/lang/Number;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static final fun string (Lspace/kscience/dataforge/meta/MutableMetaProvider;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static final fun string (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static final fun string (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static synthetic fun string$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static synthetic fun string$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static synthetic fun string$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static final fun stringList (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static final fun stringList (Lspace/kscience/dataforge/meta/MutableMetaProvider;[Ljava/lang/String;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static synthetic fun stringList$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static synthetic fun stringList$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;[Ljava/lang/String;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static final fun value (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function1;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static final fun value (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static synthetic fun value$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function1;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
+	public static synthetic fun value$default (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/MutableMetaDelegate;
 }
 
 public final class space/kscience/dataforge/meta/MutableMetaKt {
 	public static final fun ObservableMutableMeta ()Lspace/kscience/dataforge/meta/ObservableMutableMeta;
 	public static final fun ObservableMutableMeta (Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/meta/ObservableMutableMeta;
+	public static final fun ObservableMutableMeta (Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/dataforge/meta/ObservableMutableMeta;
 	public static synthetic fun ObservableMutableMeta$default (Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/ObservableMutableMeta;
 	public static final fun append (Lspace/kscience/dataforge/meta/MutableMeta;Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;)V
 	public static final fun append (Lspace/kscience/dataforge/meta/MutableMeta;Ljava/lang/String;Lspace/kscience/dataforge/meta/Value;)V
@@ -446,6 +478,7 @@ public final class space/kscience/dataforge/meta/MutableMetaKt {
 	public static final fun getOrCreate (Lspace/kscience/dataforge/meta/MutableTypedMeta;Ljava/lang/String;)Lspace/kscience/dataforge/meta/MutableTypedMeta;
 	public static final fun remove (Lspace/kscience/dataforge/meta/MutableMetaProvider;Ljava/lang/String;)V
 	public static final fun remove (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;)V
+	public static final fun reset (Lspace/kscience/dataforge/meta/MutableMeta;Lspace/kscience/dataforge/meta/Meta;)V
 	public static final fun set (Lspace/kscience/dataforge/meta/MutableMetaProvider;Ljava/lang/String;Ljava/lang/Iterable;)V
 	public static final fun set (Lspace/kscience/dataforge/meta/MutableMetaProvider;Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;)V
 	public static final fun set (Lspace/kscience/dataforge/meta/MutableMetaProvider;Lspace/kscience/dataforge/names/Name;Ljava/lang/Iterable;)V
@@ -519,6 +552,8 @@ public abstract interface class space/kscience/dataforge/meta/ObservableMutableM
 	public fun get (Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/ObservableMutableMeta;
 	public synthetic fun get (Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/TypedMeta;
 	public abstract fun getOrCreate (Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/ObservableMutableMeta;
+	public fun getSelf ()Lspace/kscience/dataforge/meta/ObservableMutableMeta;
+	public synthetic fun getSelf ()Lspace/kscience/dataforge/meta/TypedMeta;
 }
 
 public final class space/kscience/dataforge/meta/RegexItemTransformationRule : space/kscience/dataforge/meta/TransformationRule {
@@ -538,6 +573,8 @@ public final class space/kscience/dataforge/meta/RegexItemTransformationRule : s
 
 public class space/kscience/dataforge/meta/Scheme : space/kscience/dataforge/meta/Configurable, space/kscience/dataforge/meta/MetaRepr, space/kscience/dataforge/meta/MutableMetaProvider, space/kscience/dataforge/meta/descriptors/Described {
 	public fun <init> ()V
+	public fun <init> (Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;)V
+	public synthetic fun <init> (Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
 	public synthetic fun get (Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/Meta;
 	public fun get (Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/MutableMeta;
 	public final fun getDescriptor ()Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;
@@ -555,6 +592,10 @@ public final class space/kscience/dataforge/meta/SchemeKt {
 	public static final fun copy (Lspace/kscience/dataforge/meta/Scheme;Lspace/kscience/dataforge/meta/SchemeSpec;Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/meta/Scheme;
 	public static synthetic fun copy$default (Lspace/kscience/dataforge/meta/Scheme;Lspace/kscience/dataforge/meta/SchemeSpec;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/dataforge/meta/Scheme;
 	public static final fun invoke (Lspace/kscience/dataforge/meta/Scheme;Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/meta/Scheme;
+	public static final fun listOfScheme (Lspace/kscience/dataforge/meta/MutableMeta;Lspace/kscience/dataforge/meta/SchemeSpec;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty;
+	public static final fun listOfScheme (Lspace/kscience/dataforge/meta/Scheme;Lspace/kscience/dataforge/meta/SchemeSpec;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty;
+	public static synthetic fun listOfScheme$default (Lspace/kscience/dataforge/meta/MutableMeta;Lspace/kscience/dataforge/meta/SchemeSpec;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty;
+	public static synthetic fun listOfScheme$default (Lspace/kscience/dataforge/meta/Scheme;Lspace/kscience/dataforge/meta/SchemeSpec;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty;
 	public static final fun scheme (Lspace/kscience/dataforge/meta/MutableMeta;Lspace/kscience/dataforge/meta/SchemeSpec;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty;
 	public static final fun scheme (Lspace/kscience/dataforge/meta/Scheme;Lspace/kscience/dataforge/meta/SchemeSpec;Lspace/kscience/dataforge/names/Name;)Lkotlin/properties/ReadWriteProperty;
 	public static synthetic fun scheme$default (Lspace/kscience/dataforge/meta/MutableMeta;Lspace/kscience/dataforge/meta/SchemeSpec;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty;
@@ -586,19 +627,21 @@ public final class space/kscience/dataforge/meta/SealedMeta : space/kscience/dat
 	public fun <init> (Lspace/kscience/dataforge/meta/Value;Ljava/util/Map;)V
 	public fun equals (Ljava/lang/Object;)Z
 	public fun getItems ()Ljava/util/Map;
+	public fun getSelf ()Lspace/kscience/dataforge/meta/SealedMeta;
+	public synthetic fun getSelf ()Lspace/kscience/dataforge/meta/TypedMeta;
 	public fun getValue ()Lspace/kscience/dataforge/meta/Value;
 	public fun hashCode ()I
 	public fun toString ()Ljava/lang/String;
 }
 
-public final class space/kscience/dataforge/meta/SealedMeta$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
+public synthetic class space/kscience/dataforge/meta/SealedMeta$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
 	public static final field INSTANCE Lspace/kscience/dataforge/meta/SealedMeta$$serializer;
-	public fun childSerializers ()[Lkotlinx/serialization/KSerializer;
+	public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
 	public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
-	public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/dataforge/meta/SealedMeta;
-	public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+	public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/dataforge/meta/SealedMeta;
+	public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
 	public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
-	public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/dataforge/meta/SealedMeta;)V
+	public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/dataforge/meta/SealedMeta;)V
 	public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
 }
 
@@ -672,6 +715,7 @@ public abstract interface class space/kscience/dataforge/meta/TypedMeta : space/
 	public synthetic fun get (Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/Meta;
 	public fun get (Lspace/kscience/dataforge/names/Name;)Lspace/kscience/dataforge/meta/TypedMeta;
 	public abstract fun getItems ()Ljava/util/Map;
+	public abstract fun getSelf ()Lspace/kscience/dataforge/meta/TypedMeta;
 	public fun toMeta ()Lspace/kscience/dataforge/meta/Meta;
 }
 
@@ -696,7 +740,6 @@ public final class space/kscience/dataforge/meta/Value$Companion {
 public final class space/kscience/dataforge/meta/ValueExtensionsKt {
 	public static final fun getBoolean (Lspace/kscience/dataforge/meta/Value;)Z
 	public static final fun getDouble (Lspace/kscience/dataforge/meta/Value;)D
-	public static final fun getDoubleArray (Lspace/kscience/dataforge/meta/Value;)[D
 	public static final fun getFloat (Lspace/kscience/dataforge/meta/Value;)F
 	public static final fun getInt (Lspace/kscience/dataforge/meta/Value;)I
 	public static final fun getLong (Lspace/kscience/dataforge/meta/Value;)J
@@ -715,7 +758,6 @@ public final class space/kscience/dataforge/meta/ValueKt {
 	public static final fun asValue (Ljava/lang/Number;)Lspace/kscience/dataforge/meta/Value;
 	public static final fun asValue (Ljava/lang/String;)Lspace/kscience/dataforge/meta/Value;
 	public static final fun asValue (Z)Lspace/kscience/dataforge/meta/Value;
-	public static final fun asValue ([B)Lspace/kscience/dataforge/meta/Value;
 	public static final fun asValue ([F)Lspace/kscience/dataforge/meta/Value;
 	public static final fun asValue ([I)Lspace/kscience/dataforge/meta/Value;
 	public static final fun asValue ([J)Lspace/kscience/dataforge/meta/Value;
@@ -808,14 +850,14 @@ public final class space/kscience/dataforge/meta/descriptors/MetaDescriptor {
 	public fun toString ()Ljava/lang/String;
 }
 
-public final class space/kscience/dataforge/meta/descriptors/MetaDescriptor$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
+public synthetic class space/kscience/dataforge/meta/descriptors/MetaDescriptor$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
 	public static final field INSTANCE Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor$$serializer;
-	public fun childSerializers ()[Lkotlinx/serialization/KSerializer;
+	public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
 	public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
-	public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;
-	public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+	public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;
+	public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
 	public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
-	public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;)V
+	public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;)V
 	public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
 }
 
@@ -837,17 +879,16 @@ public final class space/kscience/dataforge/meta/descriptors/MetaDescriptorBuild
 	public final fun getDefault ()Lspace/kscience/dataforge/meta/Value;
 	public final fun getDescription ()Ljava/lang/String;
 	public final fun getIndexKey ()Ljava/lang/String;
-	public final fun getInfo ()Ljava/lang/String;
 	public final fun getMultiple ()Z
 	public final fun getValueRestriction ()Lspace/kscience/dataforge/meta/descriptors/ValueRestriction;
 	public final fun getValueTypes ()Ljava/util/List;
+	public final fun node (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;)V
 	public final fun setAllowedValues (Ljava/util/List;)V
 	public final fun setAttributes (Lspace/kscience/dataforge/meta/MutableMeta;)V
 	public final fun setChildren (Ljava/util/Map;)V
 	public final fun setDefault (Lspace/kscience/dataforge/meta/Value;)V
 	public final fun setDescription (Ljava/lang/String;)V
 	public final fun setIndexKey (Ljava/lang/String;)V
-	public final fun setInfo (Ljava/lang/String;)V
 	public final fun setMultiple (Z)V
 	public final fun setValueRestriction (Lspace/kscience/dataforge/meta/descriptors/ValueRestriction;)V
 	public final fun setValueTypes (Ljava/util/List;)V
@@ -893,10 +934,6 @@ public final class space/kscience/dataforge/meta/descriptors/ValueRestriction$Co
 	public final fun serializer ()Lkotlinx/serialization/KSerializer;
 }
 
-public final class space/kscience/dataforge/misc/CastJvmKt {
-	public static final fun unsafeCast (Ljava/lang/Object;)Ljava/lang/Object;
-}
-
 public abstract interface annotation class space/kscience/dataforge/misc/DFBuilder : java/lang/annotation/Annotation {
 }
 
@@ -923,6 +960,9 @@ public final class space/kscience/dataforge/misc/NamedKt {
 	public static final fun isAnonymous (Lspace/kscience/dataforge/misc/Named;)Z
 }
 
+public abstract interface annotation class space/kscience/dataforge/misc/UnsafeKType : java/lang/annotation/Annotation {
+}
+
 public final class space/kscience/dataforge/names/Name {
 	public static final field Companion Lspace/kscience/dataforge/names/Name$Companion;
 	public static final field NAME_SEPARATOR Ljava/lang/String;
@@ -940,6 +980,16 @@ public final class space/kscience/dataforge/names/Name$Companion {
 	public final fun serializer ()Lkotlinx/serialization/KSerializer;
 }
 
+public final class space/kscience/dataforge/names/NameIndexComparator : java/util/Comparator {
+	public static final field INSTANCE Lspace/kscience/dataforge/names/NameIndexComparator;
+	public synthetic fun compare (Ljava/lang/Object;Ljava/lang/Object;)I
+	public fun compare (Ljava/lang/String;Ljava/lang/String;)I
+}
+
+public final class space/kscience/dataforge/names/NameIndexComparatorKt {
+	public static final fun getIndexedList (Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/names/Name;)Ljava/util/List;
+}
+
 public final class space/kscience/dataforge/names/NameKt {
 	public static final fun appendFirst (Lspace/kscience/dataforge/names/Name;Ljava/lang/String;)Lspace/kscience/dataforge/names/Name;
 	public static final fun appendLeft (Lspace/kscience/dataforge/names/Name;Ljava/lang/String;)Lspace/kscience/dataforge/names/Name;
diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Laminate.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Laminate.kt
index 87284107..0ae84bb6 100644
--- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Laminate.kt
+++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Laminate.kt
@@ -9,6 +9,8 @@ import space.kscience.dataforge.names.NameToken
  */
 public class Laminate internal constructor(public val layers: List<Meta>) : TypedMeta<Laminate> {
 
+    override val self: Laminate get() = this
+
     override val value: Value? = layers.firstNotNullOfOrNull { it.value }
 
     override val items: Map<NameToken, Laminate> by lazy {
diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Meta.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Meta.kt
index 5cf53c75..fd953085 100644
--- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Meta.kt
+++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Meta.kt
@@ -3,7 +3,6 @@ package space.kscience.dataforge.meta
 import kotlinx.serialization.Serializable
 import kotlinx.serialization.json.Json
 import space.kscience.dataforge.misc.DfType
-import space.kscience.dataforge.misc.unsafeCast
 import space.kscience.dataforge.names.*
 import kotlin.jvm.JvmName
 
@@ -151,6 +150,8 @@ public interface TypedMeta<out M : TypedMeta<M>> : Meta {
 
     override val items: Map<NameToken, M>
 
+    public val self: M
+
     override fun get(name: Name): M? {
         tailrec fun M.find(name: Name): M? = if (name.isEmpty()) {
             this
@@ -164,11 +165,6 @@ public interface TypedMeta<out M : TypedMeta<M>> : Meta {
     override fun toMeta(): Meta = this
 }
 
-/**
- * Access self as a recursive type instance
- */
-public inline val <M : TypedMeta<M>> TypedMeta<M>.self: M get() = unsafeCast()
-
 //public typealias Meta = TypedMeta<*>
 
 public operator fun <M : TypedMeta<M>> TypedMeta<M>?.get(token: NameToken): M? = this?.items?.get(token)
diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaConverter.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaConverter.kt
index 8959ae4a..9baf0087 100644
--- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaConverter.kt
+++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaConverter.kt
@@ -11,7 +11,7 @@ import space.kscience.dataforge.misc.DFExperimental
 /**
  * A converter of generic object to and from [Meta]
  */
-public interface MetaConverter<T>: MetaSpec<T> {
+public interface MetaConverter<T>: MetaReader<T> {
 
     /**
      * A descriptor for resulting meta
@@ -135,16 +135,17 @@ public interface MetaConverter<T>: MetaSpec<T> {
         @DFExperimental
         public inline fun <reified T> serializable(
             descriptor: MetaDescriptor? = null,
+            jsonEncoder: Json = Json,
         ): MetaConverter<T> = object : MetaConverter<T> {
             private val serializer: KSerializer<T> = serializer()
 
             override fun readOrNull(source: Meta): T? {
                 val json = source.toJson(descriptor)
-                return Json.decodeFromJsonElement(serializer, json)
+                return jsonEncoder.decodeFromJsonElement(serializer, json)
             }
 
             override fun convert(obj: T): Meta {
-                val json = Json.encodeToJsonElement(obj)
+                val json = jsonEncoder.encodeToJsonElement(obj)
                 return json.toMeta(descriptor)
             }
 
diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaDelegate.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaDelegate.kt
index a24f7371..1b506b44 100644
--- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaDelegate.kt
+++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaDelegate.kt
@@ -1,25 +1,41 @@
 package space.kscience.dataforge.meta
 
+import space.kscience.dataforge.meta.descriptors.Described
 import space.kscience.dataforge.meta.descriptors.MetaDescriptor
 import space.kscience.dataforge.misc.DFExperimental
 import space.kscience.dataforge.names.Name
 import space.kscience.dataforge.names.asName
 import kotlin.properties.ReadOnlyProperty
+import kotlin.reflect.KProperty
 
 /* Meta delegates */
 
-public fun MetaProvider.node(key: Name? = null): ReadOnlyProperty<Any?, Meta?> = ReadOnlyProperty { _, property ->
-    get(key ?: property.name.asName())
+public interface MetaDelegate<T> : ReadOnlyProperty<Any?, T>, Described
+
+
+public fun MetaProvider.node(
+    key: Name? = null,
+    descriptor: MetaDescriptor? = null,
+): MetaDelegate<Meta?> = object : MetaDelegate<Meta?> {
+    override val descriptor: MetaDescriptor? = descriptor
+
+    override fun getValue(thisRef: Any?, property: KProperty<*>): Meta? {
+        return get(key ?: property.name.asName())
+    }
 }
 
 /**
- * Use [metaSpec] to read the Meta node
+ * Use [metaReader] to read the Meta node
  */
 public fun <T> MetaProvider.spec(
-    metaSpec: MetaSpec<T>,
+    metaReader: MetaReader<T>,
     key: Name? = null,
-): ReadOnlyProperty<Any?, T?> = ReadOnlyProperty { _, property ->
-    get(key ?: property.name.asName())?.let { metaSpec.read(it) }
+): MetaDelegate<T?> = object : MetaDelegate<T?> {
+    override val descriptor: MetaDescriptor? get() = metaReader.descriptor
+
+    override fun getValue(thisRef: Any?, property: KProperty<*>): T? {
+        return get(key ?: property.name.asName())?.let { metaReader.read(it) }
+    }
 }
 
 /**
@@ -27,94 +43,106 @@ public fun <T> MetaProvider.spec(
  */
 @DFExperimental
 public inline fun <reified T> MetaProvider.serializable(
-    descriptor: MetaDescriptor? = null,
     key: Name? = null,
-): ReadOnlyProperty<Any?, T?> = spec(MetaConverter.serializable(descriptor), key)
+    descriptor: MetaDescriptor? = null,
+): MetaDelegate<T?> = spec(MetaConverter.serializable(descriptor), key)
 
 @Deprecated("Use convertable", ReplaceWith("convertable(converter, key)"))
 public fun <T> MetaProvider.node(
     key: Name? = null,
-    converter: MetaSpec<T>,
+    converter: MetaReader<T>,
 ): ReadOnlyProperty<Any?, T?> = spec(converter, key)
 
 /**
  * Use [converter] to convert a list of same name siblings meta to object
  */
 public fun <T> Meta.listOfSpec(
-    converter: MetaSpec<T>,
+    converter: MetaReader<T>,
     key: Name? = null,
-): ReadOnlyProperty<Any?, List<T>> = ReadOnlyProperty{_, property ->
-    val name = key ?: property.name.asName()
-    getIndexed(name).values.map { converter.read(it) }
+): MetaDelegate<List<T>> = object : MetaDelegate<List<T>> {
+    override fun getValue(thisRef: Any?, property: KProperty<*>): List<T> {
+        val name = key ?: property.name.asName()
+        return getIndexed(name).values.map { converter.read(it) }
+    }
+
+    override val descriptor: MetaDescriptor? = converter.descriptor?.copy(multiple = true)
 }
 
 @DFExperimental
 public inline fun <reified T> Meta.listOfSerializable(
-    descriptor: MetaDescriptor? = null,
     key: Name? = null,
-): ReadOnlyProperty<Any?, List<T>> = listOfSpec(MetaConverter.serializable(descriptor), key)
+    descriptor: MetaDescriptor? = null,
+): MetaDelegate<List<T>> = listOfSpec(MetaConverter.serializable(descriptor), key)
 
 /**
  * A property delegate that uses custom key
  */
-public fun MetaProvider.value(key: Name? = null): ReadOnlyProperty<Any?, Value?> = ReadOnlyProperty { _, property ->
-    get(key ?: property.name.asName())?.value
+public fun MetaProvider.value(
+    key: Name? = null,
+    descriptor: MetaDescriptor? = null,
+): MetaDelegate<Value?> = object : MetaDelegate<Value?> {
+    override fun getValue(thisRef: Any?, property: KProperty<*>): Value? = get(key ?: property.name.asName())?.value
+
+    override val descriptor: MetaDescriptor? = descriptor
 }
 
 public fun <R> MetaProvider.value(
     key: Name? = null,
+    descriptor: MetaDescriptor? = null,
     reader: (Value?) -> R,
-): ReadOnlyProperty<Any?, R> = ReadOnlyProperty { _, property ->
-    reader(get(key ?: property.name.asName())?.value)
+): MetaDelegate<R> = object : MetaDelegate<R> {
+    override fun getValue(thisRef: Any?, property: KProperty<*>): R = reader(get(key ?: property.name.asName())?.value)
+
+    override val descriptor: MetaDescriptor? = descriptor
 }
 
 //TODO add caching for sealed nodes
 
 /* Read-only delegates for [Meta] */
 
-public fun MetaProvider.string(key: Name? = null): ReadOnlyProperty<Any?, String?> = value(key) { it?.string }
+public fun MetaProvider.string(key: Name? = null): MetaDelegate<String?> = value(key = key) { it?.string }
 
-public fun MetaProvider.boolean(key: Name? = null): ReadOnlyProperty<Any?, Boolean?> = value(key) { it?.boolean }
+public fun MetaProvider.boolean(key: Name? = null): MetaDelegate<Boolean?> = value(key = key) { it?.boolean }
 
-public fun MetaProvider.number(key: Name? = null): ReadOnlyProperty<Any?, Number?> = value(key) { it?.numberOrNull }
+public fun MetaProvider.number(key: Name? = null): MetaDelegate<Number?> = value(key = key) { it?.numberOrNull }
 
-public fun MetaProvider.double(key: Name? = null): ReadOnlyProperty<Any?, Double?> = value(key) { it?.double }
+public fun MetaProvider.double(key: Name? = null): MetaDelegate<Double?> = value(key = key) { it?.double }
 
-public fun MetaProvider.float(key: Name? = null): ReadOnlyProperty<Any?, Float?> = value(key) { it?.float }
+public fun MetaProvider.float(key: Name? = null): MetaDelegate<Float?> = value(key = key) { it?.float }
 
-public fun MetaProvider.int(key: Name? = null): ReadOnlyProperty<Any?, Int?> = value(key) { it?.int }
+public fun MetaProvider.int(key: Name? = null): MetaDelegate<Int?> = value(key = key) { it?.int }
 
-public fun MetaProvider.long(key: Name? = null): ReadOnlyProperty<Any?, Long?> = value(key) { it?.long }
+public fun MetaProvider.long(key: Name? = null): MetaDelegate<Long?> = value(key = key) { it?.long }
 
-public fun MetaProvider.string(default: String, key: Name? = null): ReadOnlyProperty<Any?, String> =
-    value(key) { it?.string ?: default }
+public fun MetaProvider.string(default: String, key: Name? = null): MetaDelegate<String> =
+    value(key = key) { it?.string ?: default }
 
-public fun MetaProvider.boolean(default: Boolean, key: Name? = null): ReadOnlyProperty<Any?, Boolean> =
-    value(key) { it?.boolean ?: default }
+public fun MetaProvider.boolean(default: Boolean, key: Name? = null): MetaDelegate<Boolean> =
+    value(key = key) { it?.boolean ?: default }
 
-public fun MetaProvider.number(default: Number, key: Name? = null): ReadOnlyProperty<Any?, Number> =
-    value(key) { it?.numberOrNull ?: default }
+public fun MetaProvider.number(default: Number, key: Name? = null): MetaDelegate<Number> =
+    value(key = key) { it?.numberOrNull ?: default }
 
-public fun MetaProvider.double(default: Double, key: Name? = null): ReadOnlyProperty<Any?, Double> =
-    value(key) { it?.double ?: default }
+public fun MetaProvider.double(default: Double, key: Name? = null): MetaDelegate<Double> =
+    value(key = key) { it?.double ?: default }
 
-public fun MetaProvider.float(default: Float, key: Name? = null): ReadOnlyProperty<Any?, Float> =
-    value(key) { it?.float ?: default }
+public fun MetaProvider.float(default: Float, key: Name? = null): MetaDelegate<Float> =
+    value(key = key) { it?.float ?: default }
 
-public fun MetaProvider.int(default: Int, key: Name? = null): ReadOnlyProperty<Any?, Int> =
-    value(key) { it?.int ?: default }
+public fun MetaProvider.int(default: Int, key: Name? = null): MetaDelegate<Int> =
+    value(key = key) { it?.int ?: default }
 
-public fun MetaProvider.long(default: Long, key: Name? = null): ReadOnlyProperty<Any?, Long> =
-    value(key) { it?.long ?: default }
+public fun MetaProvider.long(default: Long, key: Name? = null): MetaDelegate<Long> =
+    value(key = key) { it?.long ?: default }
 
-public inline fun <reified E : Enum<E>> MetaProvider.enum(default: E, key: Name? = null): ReadOnlyProperty<Any?, E> =
-    value<E>(key) { it?.enum<E>() ?: default }
+public inline fun <reified E : Enum<E>> MetaProvider.enum(default: E, key: Name? = null): MetaDelegate<E> =
+    value<E>(key = key) { it?.enum<E>() ?: default }
 
-public fun MetaProvider.string(key: Name? = null, default: () -> String): ReadOnlyProperty<Any?, String> =
-    value(key) { it?.string ?: default() }
+public fun MetaProvider.string(key: Name? = null, default: () -> String): MetaDelegate<String> =
+    value(key = key) { it?.string ?: default() }
 
-public fun MetaProvider.boolean(key: Name? = null, default: () -> Boolean): ReadOnlyProperty<Any?, Boolean> =
-    value(key) { it?.boolean ?: default() }
+public fun MetaProvider.boolean(key: Name? = null, default: () -> Boolean): MetaDelegate<Boolean> =
+    value(key = key) { it?.boolean ?: default() }
 
-public fun MetaProvider.number(key: Name? = null, default: () -> Number): ReadOnlyProperty<Any?, Number> =
-    value(key) { it?.numberOrNull ?: default() }
+public fun MetaProvider.number(key: Name? = null, default: () -> Number): MetaDelegate<Number> =
+    value(key = key) { it?.numberOrNull ?: default() }
diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaSpec.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaReader.kt
similarity index 59%
rename from dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaSpec.kt
rename to dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaReader.kt
index 9918d504..a8514d63 100644
--- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaSpec.kt
+++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaReader.kt
@@ -2,7 +2,7 @@ package space.kscience.dataforge.meta
 
 import space.kscience.dataforge.meta.descriptors.Described
 
-public interface MetaSpec<out T> : Described {
+public interface MetaReader<out T> : Described {
 
     /**
      * Read the source meta into an object and return null if Meta could not be interpreted as a target type
@@ -10,12 +10,12 @@ public interface MetaSpec<out T> : Described {
     public fun readOrNull(source: Meta): T?
 
     /**
-     * Read generic read-only meta with this [MetaSpec] producing instance of the desired type.
+     * Read generic read-only meta with this [MetaReader] producing instance of the desired type.
      * Throws an error if conversion could not be done.
      */
     public fun read(source: Meta): T = readOrNull(source) ?: error("Meta $source could not be interpreted by $this")
 }
 
 
-public fun <T : Any> MetaSpec<T>.readNullable(item: Meta?): T? = item?.let { read(it) }
-public fun <T> MetaSpec<T>.readValue(value: Value): T? = read(Meta(value))
+public fun <T : Any> MetaReader<T>.readNullable(item: Meta?): T? = item?.let { read(it) }
+public fun <T> MetaReader<T>.readValue(value: Value): T? = read(Meta(value))
diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaRef.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaRef.kt
new file mode 100644
index 00000000..413fe404
--- /dev/null
+++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MetaRef.kt
@@ -0,0 +1,64 @@
+package space.kscience.dataforge.meta
+
+import space.kscience.dataforge.meta.descriptors.Described
+import space.kscience.dataforge.meta.descriptors.MetaDescriptor
+import space.kscience.dataforge.meta.descriptors.MetaDescriptorBuilder
+import space.kscience.dataforge.misc.DFExperimental
+import space.kscience.dataforge.names.Name
+import space.kscience.dataforge.names.asName
+import kotlin.properties.PropertyDelegateProvider
+import kotlin.properties.ReadOnlyProperty
+
+
+/**
+ * A reference to a read-only value of type [T] inside [MetaProvider]
+ */
+@DFExperimental
+public data class MetaRef<T>(
+    public val name: Name,
+    public val converter: MetaConverter<T>,
+    override val descriptor: MetaDescriptor? = converter.descriptor,
+) : Described
+
+@DFExperimental
+public operator fun <T> MetaProvider.get(ref: MetaRef<T>): T? = get(ref.name)?.let { ref.converter.readOrNull(it) }
+
+@DFExperimental
+public operator fun <T> MutableMetaProvider.set(ref: MetaRef<T>, value: T) {
+    set(ref.name, ref.converter.convert(value))
+}
+
+@DFExperimental
+public class MetaSpec(
+    private val configuration: MetaDescriptorBuilder.() -> Unit = {},
+) : Described {
+    private val refs: MutableList<MetaRef<*>> = mutableListOf()
+
+    private fun registerRef(ref: MetaRef<*>) {
+        refs.add(ref)
+    }
+
+    public fun <T> item(
+        converter: MetaConverter<T>,
+        descriptor: MetaDescriptor? = converter.descriptor,
+        key: Name? = null,
+    ): PropertyDelegateProvider<MetaSpec, ReadOnlyProperty<MetaSpec, MetaRef<T>>> =
+        PropertyDelegateProvider { _, property ->
+            val ref = MetaRef(key ?: property.name.asName(), converter, descriptor)
+            registerRef(ref)
+            ReadOnlyProperty { _, _ ->
+                ref
+            }
+        }
+
+    override val descriptor: MetaDescriptor by lazy {
+        MetaDescriptor {
+            refs.forEach { ref ->
+                ref.descriptor?.let {
+                    node(ref.name, ref.descriptor)
+                }
+            }
+            configuration()
+        }
+    }
+}
\ No newline at end of file
diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMeta.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMeta.kt
index 231f9e54..7be16cc9 100644
--- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMeta.kt
+++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMeta.kt
@@ -19,6 +19,10 @@ public annotation class MetaBuilderMarker
 public interface MutableMetaProvider : MetaProvider, MutableValueProvider {
     override fun get(name: Name): MutableMeta?
     public operator fun set(name: Name, node: Meta?)
+
+    /**
+     * Set value with the given name. Does nothing if value is not changed.
+     */
     override fun setValue(name: Name, value: Value?)
 }
 
@@ -48,11 +52,13 @@ public interface MutableMeta : Meta, MutableMetaProvider {
     }
 
     override fun setValue(name: Name, value: Value?) {
-        getOrCreate(name).value = value
+        if (value != getValue(name)) {
+            getOrCreate(name).value = value
+        }
     }
 
     /**
-     * Get existing node or create a new one
+     * Get an existing node or create a new one
      */
     public fun getOrCreate(name: Name): MutableMeta
 
@@ -122,6 +128,10 @@ public interface MutableMeta : Meta, MutableMetaProvider {
         setValue(Name.parse(this), array.asValue())
     }
 
+    public infix fun String.put(array: ByteArray) {
+        setValue(Name.parse(this), array.asValue())
+    }
+
     public infix fun String.put(repr: MetaRepr) {
         set(Name.parse(this), repr.toMeta())
     }
@@ -198,10 +208,8 @@ public operator fun MutableMetaProvider.set(key: String, metas: Iterable<Meta>):
 
 
 /**
- * Update existing mutable node with another node. The rules are following:
- *  * value replaces anything
- *  * node updates node and replaces anything but node
- *  * node list updates node list if number of nodes in the list is the same and replaces anything otherwise
+ * Update the existing mutable node with another node.
+ * Values that are present in the current provider and are missing in [meta] are kept.
  */
 public fun MutableMetaProvider.update(meta: Meta) {
     meta.valueSequence().forEach { (name, value) ->
@@ -222,7 +230,7 @@ public fun <M : MutableTypedMeta<M>> MutableTypedMeta<M>.edit(name: Name, builde
     getOrCreate(name).apply(builder)
 
 /**
- * Set a value at a given [name]. If node does not exist, create it.
+ * Set a value at a given [name]. If a node does not exist, create it.
  */
 public operator fun <M : MutableTypedMeta<M>> MutableTypedMeta<M>.set(name: Name, value: Value?) {
     edit(name) {
@@ -245,6 +253,9 @@ private class MutableMetaImpl(
     value: Value?,
     children: Map<NameToken, Meta> = emptyMap(),
 ) : AbstractObservableMeta(), ObservableMutableMeta {
+
+    override val self get() = this
+
     override var value = value
         @ThreadSafe set(value) {
             val oldValue = field
@@ -367,6 +378,21 @@ public fun MutableMeta.append(name: Name, value: Value): Unit = append(name, Met
 
 public fun MutableMeta.append(key: String, value: Value): Unit = append(Name.parse(key), value)
 
+/**
+ * Update all items that exist in the [newMeta] and remove existing items that are missing in [newMeta].
+ * This produces the same result as clearing all items and updating blank meta with a [newMeta], but does not
+ * produce unnecessary invalidation events (if they are supported).
+ */
+public fun MutableMeta.reset(newMeta: Meta) {
+    //remove old items
+    (items.keys - newMeta.items.keys).forEach {
+        remove(it.asName())
+    }
+    newMeta.items.forEach { (token, item)->
+        set(token, item)
+    }
+}
+
 /**
  * Create a mutable copy of this meta. The copy is created even if the Meta is already mutable
  */
@@ -378,6 +404,11 @@ public fun Meta.asMutableMeta(): MutableMeta = (this as? MutableMeta) ?: toMutab
 @JsName("newObservableMutableMeta")
 public fun ObservableMutableMeta(): ObservableMutableMeta = MutableMetaImpl(null)
 
+/**
+ * Create a pre-filled [ObservableMutableMeta]
+ */
+public fun ObservableMutableMeta(content: Meta): ObservableMutableMeta = ObservableMutableMeta { update(content) }
+
 /**
  * Build a [MutableMeta] using given transformation
  */
diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMetaDelegate.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMetaDelegate.kt
index f6e96109..37140c6f 100644
--- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMetaDelegate.kt
+++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/MutableMetaDelegate.kt
@@ -1,26 +1,36 @@
 package space.kscience.dataforge.meta
 
+import space.kscience.dataforge.meta.descriptors.Described
 import space.kscience.dataforge.meta.descriptors.MetaDescriptor
 import space.kscience.dataforge.misc.DFExperimental
 import space.kscience.dataforge.names.Name
 import space.kscience.dataforge.names.asName
+import space.kscience.dataforge.names.getIndexedList
 import kotlin.properties.ReadWriteProperty
 import kotlin.reflect.KProperty
 
+
 /* Read-write delegates */
 
-public fun MutableMetaProvider.node(key: Name? = null): ReadWriteProperty<Any?, Meta?> =
-    object : ReadWriteProperty<Any?, Meta?> {
-        override fun getValue(thisRef: Any?, property: KProperty<*>): Meta? {
-            return get(key ?: property.name.asName())
-        }
+public interface MutableMetaDelegate<T> : ReadWriteProperty<Any?, T>, Described
 
-        override fun setValue(thisRef: Any?, property: KProperty<*>, value: Meta?) {
-            val name = key ?: property.name.asName()
-            set(name, value)
-        }
+public fun MutableMetaProvider.node(
+    key: Name? = null,
+    descriptor: MetaDescriptor? = null,
+): MutableMetaDelegate<Meta?> = object : MutableMetaDelegate<Meta?> {
+
+    override val descriptor: MetaDescriptor? = descriptor
+
+    override fun getValue(thisRef: Any?, property: KProperty<*>): Meta? {
+        return get(key ?: property.name.asName())
     }
 
+    override fun setValue(thisRef: Any?, property: KProperty<*>, value: Meta?) {
+        val name = key ?: property.name.asName()
+        set(name, value)
+    }
+}
+
 /**
  * Use [converter] to transform an object to Meta and back.
  * Note that mutation of the object does not change Meta.
@@ -28,21 +38,24 @@ public fun MutableMetaProvider.node(key: Name? = null): ReadWriteProperty<Any?,
 public fun <T> MutableMetaProvider.convertable(
     converter: MetaConverter<T>,
     key: Name? = null,
-): ReadWriteProperty<Any?, T?> =
-    object : ReadWriteProperty<Any?, T?> {
-        override fun getValue(thisRef: Any?, property: KProperty<*>): T? {
-            val name = key ?: property.name.asName()
-            return get(name)?.let { converter.read(it) }
-        }
+): MutableMetaDelegate<T?> = object : MutableMetaDelegate<T?> {
 
-        override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
-            val name = key ?: property.name.asName()
-            set(name, value?.let { converter.convert(it) })
-        }
+    override val descriptor: MetaDescriptor? get() = converter.descriptor
+
+
+    override fun getValue(thisRef: Any?, property: KProperty<*>): T? {
+        val name = key ?: property.name.asName()
+        return get(name)?.let { converter.read(it) }
     }
 
+    override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
+        val name = key ?: property.name.asName()
+        set(name, value?.let { converter.convert(it) })
+    }
+}
+
 @Deprecated("Use convertable", ReplaceWith("convertable(converter, key)"))
-public fun <T> MutableMetaProvider.node(key: Name? = null, converter: MetaConverter<T>): ReadWriteProperty<Any?, T?> =
+public fun <T> MutableMetaProvider.node(key: Name? = null, converter: MetaConverter<T>): MutableMetaDelegate<T?> =
     convertable(converter, key)
 
 /**
@@ -53,7 +66,7 @@ public fun <T> MutableMetaProvider.node(key: Name? = null, converter: MetaConver
 public inline fun <reified T> MutableMetaProvider.serializable(
     descriptor: MetaDescriptor? = null,
     key: Name? = null,
-): ReadWriteProperty<Any?, T?> = convertable(MetaConverter.serializable(descriptor), key)
+): MutableMetaDelegate<T?> = convertable(MetaConverter.serializable(descriptor), key)
 
 /**
  * Use [converter] to convert a list of same name siblings meta to object and back.
@@ -62,10 +75,12 @@ public inline fun <reified T> MutableMetaProvider.serializable(
 public fun <T> MutableMeta.listOfConvertable(
     converter: MetaConverter<T>,
     key: Name? = null,
-): ReadWriteProperty<Any?, List<T>> = object : ReadWriteProperty<Any?, List<T>> {
+): MutableMetaDelegate<List<T>> = object : MutableMetaDelegate<List<T>> {
+    override val descriptor: MetaDescriptor? = converter.descriptor?.copy(multiple = true)
+
     override fun getValue(thisRef: Any?, property: KProperty<*>): List<T> {
         val name = key ?: property.name.asName()
-        return getIndexed(name).values.map { converter.read(it) }
+        return getIndexedList(name).map { converter.read(it) }
     }
 
     override fun setValue(thisRef: Any?, property: KProperty<*>, value: List<T>) {
@@ -76,26 +91,33 @@ public fun <T> MutableMeta.listOfConvertable(
 
 @DFExperimental
 public inline fun <reified T> MutableMeta.listOfSerializable(
-    descriptor: MetaDescriptor? = null,
     key: Name? = null,
-): ReadWriteProperty<Any?, List<T>> = listOfConvertable(MetaConverter.serializable(descriptor), key)
+    descriptor: MetaDescriptor? = null,
+): MutableMetaDelegate<List<T>> = listOfConvertable(MetaConverter.serializable(descriptor), key)
 
 
-public fun MutableMetaProvider.value(key: Name? = null): ReadWriteProperty<Any?, Value?> =
-    object : ReadWriteProperty<Any?, Value?> {
-        override fun getValue(thisRef: Any?, property: KProperty<*>): Value? =
-            get(key ?: property.name.asName())?.value
+public fun MutableMetaProvider.value(
+    key: Name? = null,
+    descriptor: MetaDescriptor? = null,
+): MutableMetaDelegate<Value?> = object : MutableMetaDelegate<Value?> {
+    override val descriptor: MetaDescriptor? = descriptor
 
-        override fun setValue(thisRef: Any?, property: KProperty<*>, value: Value?) {
-            setValue(key ?: property.name.asName(), value)
-        }
+    override fun getValue(thisRef: Any?, property: KProperty<*>): Value? =
+        get(key ?: property.name.asName())?.value
+
+    override fun setValue(thisRef: Any?, property: KProperty<*>, value: Value?) {
+        setValue(key ?: property.name.asName(), value)
     }
+}
 
 public fun <T> MutableMetaProvider.value(
     key: Name? = null,
     writer: (T) -> Value? = { Value.of(it) },
+    descriptor: MetaDescriptor? = null,
     reader: (Value?) -> T,
-): ReadWriteProperty<Any?, T> = object : ReadWriteProperty<Any?, T> {
+): MutableMetaDelegate<T> = object : MutableMetaDelegate<T> {
+    override val descriptor: MetaDescriptor? = descriptor
+
     override fun getValue(thisRef: Any?, property: KProperty<*>): T =
         reader(get(key ?: property.name.asName())?.value)
 
@@ -106,65 +128,65 @@ public fun <T> MutableMetaProvider.value(
 
 /* Read-write delegates for [MutableItemProvider] */
 
-public fun MutableMetaProvider.string(key: Name? = null): ReadWriteProperty<Any?, String?> =
+public fun MutableMetaProvider.string(key: Name? = null): MutableMetaDelegate<String?> =
     value(key) { it?.string }
 
-public fun MutableMetaProvider.boolean(key: Name? = null): ReadWriteProperty<Any?, Boolean?> =
+public fun MutableMetaProvider.boolean(key: Name? = null): MutableMetaDelegate<Boolean?> =
     value(key) { it?.boolean }
 
-public fun MutableMetaProvider.number(key: Name? = null): ReadWriteProperty<Any?, Number?> =
+public fun MutableMetaProvider.number(key: Name? = null): MutableMetaDelegate<Number?> =
     value(key) { it?.number }
 
-public fun MutableMetaProvider.string(default: String, key: Name? = null): ReadWriteProperty<Any?, String> =
+public fun MutableMetaProvider.string(default: String, key: Name? = null): MutableMetaDelegate<String> =
     value(key) { it?.string ?: default }
 
-public fun MutableMetaProvider.boolean(default: Boolean, key: Name? = null): ReadWriteProperty<Any?, Boolean> =
+public fun MutableMetaProvider.boolean(default: Boolean, key: Name? = null): MutableMetaDelegate<Boolean> =
     value(key) { it?.boolean ?: default }
 
-public fun MutableMetaProvider.number(default: Number, key: Name? = null): ReadWriteProperty<Any?, Number> =
+public fun MutableMetaProvider.number(default: Number, key: Name? = null): MutableMetaDelegate<Number> =
     value(key) { it?.number ?: default }
 
-public fun MutableMetaProvider.string(key: Name? = null, default: () -> String): ReadWriteProperty<Any?, String> =
+public fun MutableMetaProvider.string(key: Name? = null, default: () -> String): MutableMetaDelegate<String> =
     value(key) { it?.string ?: default() }
 
-public fun MutableMetaProvider.boolean(key: Name? = null, default: () -> Boolean): ReadWriteProperty<Any?, Boolean> =
+public fun MutableMetaProvider.boolean(key: Name? = null, default: () -> Boolean): MutableMetaDelegate<Boolean> =
     value(key) { it?.boolean ?: default() }
 
-public fun MutableMetaProvider.number(key: Name? = null, default: () -> Number): ReadWriteProperty<Any?, Number> =
+public fun MutableMetaProvider.number(key: Name? = null, default: () -> Number): MutableMetaDelegate<Number> =
     value(key) { it?.number ?: default() }
 
 public inline fun <reified E : Enum<E>> MutableMetaProvider.enum(
     default: E,
     key: Name? = null,
-): ReadWriteProperty<Any?, E> = value(key) { value -> value?.string?.let { enumValueOf<E>(it) } ?: default }
+): MutableMetaDelegate<E> = value(key) { value -> value?.string?.let { enumValueOf<E>(it) } ?: default }
 
 /* Number delegates */
 
-public fun MutableMetaProvider.int(key: Name? = null): ReadWriteProperty<Any?, Int?> =
+public fun MutableMetaProvider.int(key: Name? = null): MutableMetaDelegate<Int?> =
     value(key) { it?.int }
 
-public fun MutableMetaProvider.double(key: Name? = null): ReadWriteProperty<Any?, Double?> =
+public fun MutableMetaProvider.double(key: Name? = null): MutableMetaDelegate<Double?> =
     value(key) { it?.double }
 
-public fun MutableMetaProvider.long(key: Name? = null): ReadWriteProperty<Any?, Long?> =
+public fun MutableMetaProvider.long(key: Name? = null): MutableMetaDelegate<Long?> =
     value(key) { it?.long }
 
-public fun MutableMetaProvider.float(key: Name? = null): ReadWriteProperty<Any?, Float?> =
+public fun MutableMetaProvider.float(key: Name? = null): MutableMetaDelegate<Float?> =
     value(key) { it?.float }
 
 
 /* Safe number delegates*/
 
-public fun MutableMetaProvider.int(default: Int, key: Name? = null): ReadWriteProperty<Any?, Int> =
+public fun MutableMetaProvider.int(default: Int, key: Name? = null): MutableMetaDelegate<Int> =
     value(key) { it?.int ?: default }
 
-public fun MutableMetaProvider.double(default: Double, key: Name? = null): ReadWriteProperty<Any?, Double> =
+public fun MutableMetaProvider.double(default: Double, key: Name? = null): MutableMetaDelegate<Double> =
     value(key) { it?.double ?: default }
 
-public fun MutableMetaProvider.long(default: Long, key: Name? = null): ReadWriteProperty<Any?, Long> =
+public fun MutableMetaProvider.long(default: Long, key: Name? = null): MutableMetaDelegate<Long> =
     value(key) { it?.long ?: default }
 
-public fun MutableMetaProvider.float(default: Float, key: Name? = null): ReadWriteProperty<Any?, Float> =
+public fun MutableMetaProvider.float(default: Float, key: Name? = null): MutableMetaDelegate<Float> =
     value(key) { it?.float ?: default }
 
 
@@ -173,7 +195,7 @@ public fun MutableMetaProvider.float(default: Float, key: Name? = null): ReadWri
 public fun MutableMetaProvider.stringList(
     vararg default: String,
     key: Name? = null,
-): ReadWriteProperty<Any?, List<String>> = value(
+): MutableMetaDelegate<List<String>> = value(
     key,
     writer = { list -> list.map { str -> str.asValue() }.asValue() },
     reader = { it?.stringList ?: listOf(*default) },
@@ -181,7 +203,7 @@ public fun MutableMetaProvider.stringList(
 
 public fun MutableMetaProvider.stringList(
     key: Name? = null,
-): ReadWriteProperty<Any?, List<String>?> = value(
+): MutableMetaDelegate<List<String>?> = value(
     key,
     writer = { it -> it?.map { str -> str.asValue() }?.asValue() },
     reader = { it?.stringList },
@@ -190,29 +212,18 @@ public fun MutableMetaProvider.stringList(
 public fun MutableMetaProvider.numberList(
     vararg default: Number,
     key: Name? = null,
-): ReadWriteProperty<Any?, List<Number>> = value(
+): MutableMetaDelegate<List<Number>> = value(
     key,
     writer = { it.map { num -> num.asValue() }.asValue() },
     reader = { it?.list?.map { value -> value.numberOrNull ?: Double.NaN } ?: listOf(*default) },
 )
 
-/* A special delegate for double arrays */
-
-
-public fun MutableMetaProvider.doubleArray(
-    vararg default: Double,
-    key: Name? = null,
-): ReadWriteProperty<Any?, DoubleArray> = value(
-    key,
-    writer = { DoubleArrayValue(it) },
-    reader = { it?.doubleArray ?: doubleArrayOf(*default) },
-)
 
 public fun <T> MutableMetaProvider.listValue(
     key: Name? = null,
     writer: (T) -> Value = { Value.of(it) },
     reader: (Value) -> T,
-): ReadWriteProperty<Any?, List<T>?> = value(
+): MutableMetaDelegate<List<T>?> = value(
     key,
     writer = { it?.map(writer)?.asValue() },
     reader = { it?.list?.map(reader) }
diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMeta.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMeta.kt
index b481962e..7cd28746 100644
--- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMeta.kt
+++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMeta.kt
@@ -39,6 +39,9 @@ public interface ObservableMeta : Meta {
  * A [Meta] which is both observable and mutable
  */
 public interface ObservableMutableMeta : ObservableMeta, MutableMeta, MutableTypedMeta<ObservableMutableMeta> {
+
+    override val self: ObservableMutableMeta get() = this
+
     override fun getOrCreate(name: Name): ObservableMutableMeta
 
     override fun get(name: Name): ObservableMutableMeta? {
diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMetaWrapper.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMetaWrapper.kt
index 71e15aa9..7c62f692 100644
--- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMetaWrapper.kt
+++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/ObservableMetaWrapper.kt
@@ -14,10 +14,13 @@ private class ObservableMetaWrapper(
     val nodeName: Name,
     val listeners: MutableSet<MetaListener>,
 ) : ObservableMutableMeta {
+
+    override val self get() = this
+
     override val items: Map<NameToken, ObservableMutableMeta>
-        get() = root.items.keys.associateWith {
+        get() = root[nodeName]?.items?.keys?.associateWith {
             ObservableMetaWrapper(root, nodeName + it, listeners)
-        }
+        } ?: emptyMap()
 
     override fun get(name: Name): ObservableMutableMeta? = if (root[nodeName + name] == null) {
         null
diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Scheme.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Scheme.kt
index 2e9edc1d..fe121c42 100644
--- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Scheme.kt
+++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Scheme.kt
@@ -12,33 +12,29 @@ import kotlin.reflect.KProperty
 import kotlin.reflect.KProperty1
 
 /**
- * A base for delegate-based or descriptor-based scheme. [Scheme] has an empty constructor to simplify usage from [MetaSpec].
- * Default item provider and [MetaDescriptor] are optional
+ * A base for delegate-based or descriptor-based scheme. [Scheme] has an empty constructor to simplify usage from [MetaReader].
+ *
+ * @param prototype default values provided by this scheme
  */
-public open class Scheme : Described, MetaRepr, MutableMetaProvider, Configurable {
+public open class Scheme(
+    private var prototype: Meta? = null,
+    descriptor: MetaDescriptor? = null,
+) : Described, MetaRepr, MutableMetaProvider, Configurable {
 
     /**
      * Meta to be mutated by this scheme
      */
-    private var target: MutableMeta? = null
-        get() {
-            // automatic initialization of target if it is missing
-            if (field == null) {
-                field = MutableMeta()
-            }
-            return field
-        }
+    internal var target: MutableMeta = MutableMeta()
 
     /**
-     * Default values provided by this scheme
+     * A descriptor of this scheme
      */
-    private var prototype: Meta? = null
+    final override var descriptor: MetaDescriptor? = descriptor
+        private set
+
 
     final override val meta: ObservableMutableMeta = SchemeMeta(Name.EMPTY)
 
-    final override var descriptor: MetaDescriptor? = null
-        private set
-
     /**
      * This method must be called before the scheme could be used
      */
@@ -84,13 +80,16 @@ public open class Scheme : Described, MetaRepr, MutableMetaProvider, Configurabl
     override fun toString(): String = meta.toString()
 
     private inner class SchemeMeta(val pathName: Name) : ObservableMutableMeta {
+
+        override val self get() = this
+
         override var value: Value?
             get() = target[pathName]?.value
                 ?: prototype?.get(pathName)?.value
                 ?: descriptor?.get(pathName)?.defaultValue
             set(value) {
                 val oldValue = target[pathName]?.value
-                target!![pathName] = value
+                target[pathName] = value
                 if (oldValue != value) {
                     invalidate(Name.EMPTY)
                 }
@@ -126,7 +125,7 @@ public open class Scheme : Described, MetaRepr, MutableMetaProvider, Configurabl
         override fun hashCode(): Int = Meta.hashCode(this)
 
         override fun set(name: Name, node: Meta?) {
-            target!![name] = node
+            target[pathName + name] = node
             invalidate(name)
         }
 
@@ -176,18 +175,22 @@ public open class SchemeSpec<T : Scheme>(
         it.initialize(MutableMeta(), source, descriptor)
     }
 
+    /**
+     * Write changes made to the [Scheme] to target [MutableMeta]. If the empty [Scheme] contains any data it is copied to the target.
+     */
     public fun write(target: MutableMeta): T = empty().also {
+        target.update(it.meta)
         it.initialize(target, Meta.EMPTY, descriptor)
     }
 
     /**
-     * Generate an empty object
+     * Generate a blank object. The object could contain some elements if they are defined in a constructor
      */
     public fun empty(): T = builder().also {
-        it.initialize(MutableMeta(), Meta.EMPTY, descriptor)
+        it.initialize(MutableMeta(), it.target, descriptor)
     }
 
-    override fun convert(obj: T): Meta  = obj.meta
+    override fun convert(obj: T): Meta = obj.meta
 
     /**
      * A convenience method to use specifications in builders
@@ -197,7 +200,6 @@ public open class SchemeSpec<T : Scheme>(
 }
 
 
-
 /**
  * Update a [MutableMeta] using given specification
  */
@@ -217,7 +219,7 @@ public fun <T : Scheme> Configurable.updateWith(
 
 
 /**
- * A delegate that uses a [MetaSpec] to wrap a child of this provider
+ * A delegate that uses a [MetaReader] to wrap a child of this provider
  */
 public fun <T : Scheme> MutableMeta.scheme(
     spec: SchemeSpec<T>,
@@ -240,7 +242,7 @@ public fun <T : Scheme> Scheme.scheme(
 ): ReadWriteProperty<Any?, T> = meta.scheme(spec, key)
 
 /**
- * A delegate that uses a [MetaSpec] to wrap a child of this provider.
+ * A delegate that uses a [MetaReader] to wrap a child of this provider.
  * Returns null if meta with given name does not exist.
  */
 public fun <T : Scheme> MutableMeta.schemeOrNull(
@@ -265,18 +267,17 @@ public fun <T : Scheme> Scheme.schemeOrNull(
 ): ReadWriteProperty<Any?, T?> = meta.schemeOrNull(spec, key)
 
 /**
- * A delegate that uses a [MetaSpec] to wrap a list of child providers.
+ * A delegate that uses a [MetaReader] to wrap a list of child providers.
  * If children are mutable, the changes in list elements are reflected on them.
  * The list is a snapshot of children state, so change in structure is not reflected on its composition.
  */
-@DFExperimental
 public fun <T : Scheme> MutableMeta.listOfScheme(
     spec: SchemeSpec<T>,
     key: Name? = null,
 ): ReadWriteProperty<Any?, List<T>> = object : ReadWriteProperty<Any?, List<T>> {
     override fun getValue(thisRef: Any?, property: KProperty<*>): List<T> {
         val name = key ?: property.name.asName()
-        return getIndexed(name).values.map { spec.write(it as MutableMeta) }
+        return getIndexedList(name).map { spec.write(it as MutableMeta) }
     }
 
     override fun setValue(thisRef: Any?, property: KProperty<*>, value: List<T>) {
@@ -286,7 +287,6 @@ public fun <T : Scheme> MutableMeta.listOfScheme(
 }
 
 
-@DFExperimental
 public fun <T : Scheme> Scheme.listOfScheme(
     spec: SchemeSpec<T>,
     key: Name? = null,
diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/SealedMeta.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/SealedMeta.kt
index e842b990..b218fad6 100644
--- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/SealedMeta.kt
+++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/SealedMeta.kt
@@ -13,6 +13,9 @@ public class SealedMeta(
     override val value: Value?,
     override val items: Map<NameToken, SealedMeta>,
 ) : TypedMeta<SealedMeta> {
+
+    override val self: SealedMeta get() = this
+
     override fun toString(): String = Meta.toString(this)
     override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta)
 
@@ -76,6 +79,8 @@ internal class MetaBuilder(
 
 
     override fun set(name: Name, node: Meta?) {
+        //skip setting if value has not changed
+        if(node == get(name)) return
         when (name.length) {
             0 -> error("Can't set a meta with empty name")
             1 -> {
@@ -89,7 +94,7 @@ internal class MetaBuilder(
             }
 
             else -> {
-                getOrCreate(name.first().asName()).set(name.cutFirst(), node)
+                getOrCreate(name.first().asName())[name.cutFirst()] = node
             }
         }
     }
diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Value.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Value.kt
index 66e14c86..2ab7b9ee 100644
--- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Value.kt
+++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/Value.kt
@@ -256,8 +256,6 @@ public fun ShortArray.asValue(): Value = if (isEmpty()) Null else ListValue(map
 
 public fun FloatArray.asValue(): Value = if (isEmpty()) Null else ListValue(map { NumberValue(it) })
 
-public fun ByteArray.asValue(): Value = if (isEmpty()) Null else ListValue(map { NumberValue(it) })
-
 public fun <E : Enum<E>> E.asValue(): Value = EnumValue(this)
 
 
diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder.kt
index 5d4d81ad..2590273e 100644
--- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder.kt
+++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/descriptors/MetaDescriptorBuilder.kt
@@ -11,9 +11,6 @@ import kotlin.collections.set
 public class MetaDescriptorBuilder @PublishedApi internal constructor() {
     public var description: String? = null
 
-    @Deprecated("Replace by description", ReplaceWith("description"))
-    public var info: String? by ::description
-
     public var children: MutableMap<String, MetaDescriptorBuilder> = linkedMapOf()
     public var multiple: Boolean = false
     public var valueRestriction: ValueRestriction = ValueRestriction.NONE
@@ -50,6 +47,7 @@ public class MetaDescriptorBuilder @PublishedApi internal constructor() {
     ): Unit {
         when (name.length) {
             0 -> error("Can't set descriptor to root")
+
             1 -> {
                 children[name.first().body] = descriptorBuilder
             }
@@ -60,7 +58,7 @@ public class MetaDescriptorBuilder @PublishedApi internal constructor() {
         }
     }
 
-    internal fun node(
+    public fun node(
         name: Name,
         descriptorBuilder: MetaDescriptor,
     ): Unit {
diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/descriptors/schemeDescriptor.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/descriptors/schemeDescriptor.kt
index a2a77182..c7b73508 100644
--- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/descriptors/schemeDescriptor.kt
+++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/descriptors/schemeDescriptor.kt
@@ -3,11 +3,12 @@ package space.kscience.dataforge.meta.descriptors
 import space.kscience.dataforge.meta.Scheme
 import space.kscience.dataforge.meta.SchemeSpec
 import space.kscience.dataforge.meta.ValueType
-import space.kscience.dataforge.misc.DFExperimental
 import kotlin.reflect.KProperty1
 import kotlin.reflect.typeOf
 
-@DFExperimental
+/**
+ * Add a value item to a [MetaDescriptor] inferring some of its properties from the type
+ */
 public inline fun <S : Scheme, reified T> MetaDescriptorBuilder.value(
     property: KProperty1<S, T>,
     noinline block: MetaDescriptorBuilder.() -> Unit = {},
@@ -39,7 +40,9 @@ public inline fun <S : Scheme, reified T> MetaDescriptorBuilder.value(
     else -> node(property.name, block)
 }
 
-@DFExperimental
+/**
+ * Add a schem-based branch to a [MetaDescriptor]
+ */
 public inline fun <S : Scheme, reified T : Scheme> MetaDescriptorBuilder.scheme(
     property: KProperty1<S, T>,
     spec: SchemeSpec<T>,
diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/exoticValues.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/exoticValues.kt
index 74952053..eb39e985 100644
--- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/exoticValues.kt
+++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/exoticValues.kt
@@ -1,5 +1,9 @@
 package space.kscience.dataforge.meta
 
+import space.kscience.dataforge.names.Name
+import kotlin.properties.ReadOnlyProperty
+import kotlin.properties.ReadWriteProperty
+
 
 /**
  * A value built from string which content and type are parsed on-demand
@@ -44,3 +48,79 @@ public class DoubleArrayValue(override val value: DoubleArray) : Value, Iterable
 }
 
 public fun DoubleArray.asValue(): Value = if (isEmpty()) Null else DoubleArrayValue(this)
+
+public val Value.doubleArray: DoubleArray
+    get() = if (this is DoubleArrayValue) {
+        value
+    } else {
+        DoubleArray(list.size) { list[it].double }
+    }
+
+public val Meta?.doubleArray: DoubleArray? get() = this?.value?.doubleArray
+
+public fun MetaProvider.doubleArray(
+    vararg default: Double,
+    key: Name? = null,
+): ReadOnlyProperty<Any?, DoubleArray> = value(
+    key,
+    reader = { it?.doubleArray ?: doubleArrayOf(*default) },
+)
+
+public fun MutableMetaProvider.doubleArray(
+    vararg default: Double,
+    key: Name? = null,
+): ReadWriteProperty<Any?, DoubleArray> = value(
+    key,
+    writer = { DoubleArrayValue(it) },
+    reader = { it?.doubleArray ?: doubleArrayOf(*default) },
+)
+
+
+public class ByteArrayValue(override val value: ByteArray) : Value, Iterable<Byte> {
+    override val type: ValueType get() = ValueType.LIST
+    override val list: List<Value> get() = value.map { NumberValue(it) }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is Value) return false
+
+        return when (other) {
+            is ByteArrayValue -> value.contentEquals(other.value)
+            else -> list == other.list
+        }
+    }
+
+    override fun hashCode(): Int = value.contentHashCode()
+
+    override fun toString(): String = list.joinToString(prefix = "[", postfix = "]")
+
+    override fun iterator(): Iterator<Byte> = value.iterator()
+}
+
+public fun ByteArray.asValue(): Value = ByteArrayValue(this)
+
+public val Value.byteArray: ByteArray
+    get() = if (this is ByteArrayValue) {
+        value
+    } else {
+        ByteArray(list.size) { list[it].number.toByte() }
+    }
+
+public val Meta?.byteArray: ByteArray? get() = this?.value?.byteArray
+
+public fun MetaProvider.byteArray(
+    vararg default: Byte,
+    key: Name? = null,
+): ReadOnlyProperty<Any?, ByteArray> = value(
+    key,
+    reader = { it?.byteArray ?: byteArrayOf(*default) },
+)
+
+public fun MutableMetaProvider.byteArray(
+    vararg default: Byte,
+    key: Name? = null,
+): ReadWriteProperty<Any?, ByteArray> = value(
+    key,
+    writer = { ByteArrayValue(it) },
+    reader = { it?.byteArray ?: byteArrayOf(*default) },
+)
\ No newline at end of file
diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/valueExtensions.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/valueExtensions.kt
index e6b622ff..73464305 100644
--- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/valueExtensions.kt
+++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/meta/valueExtensions.kt
@@ -31,12 +31,5 @@ public inline fun <reified E : Enum<E>> Value.enum(): E = if (this is EnumValue<
 
 public val Value.stringList: List<String> get() = list.map { it.string }
 
-public val Value.doubleArray: DoubleArray
-    get() = if (this is DoubleArrayValue) {
-        value
-    } else {
-        DoubleArray(list.size) { list[it].double }
-    }
-
 
 public fun Value.toMeta(): Meta = Meta(this)
\ No newline at end of file
diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/misc/annotations.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/misc/annotations.kt
index 3c9d6ac3..29568403 100644
--- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/misc/annotations.kt
+++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/misc/annotations.kt
@@ -18,4 +18,11 @@ public annotation class DFExperimental
  */
 @RequiresOptIn(level = RequiresOptIn.Level.WARNING)
 @Retention(AnnotationRetention.BINARY)
-public annotation class DFInternal
\ No newline at end of file
+public annotation class DFInternal
+
+/**
+ * Annotation marks methods that explicitly use KType without checking that it corresponds to the type parameter
+ */
+@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
+@Retention(AnnotationRetention.BINARY)
+public annotation class UnsafeKType
\ No newline at end of file
diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/misc/cast.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/misc/cast.kt
deleted file mode 100644
index e714d596..00000000
--- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/misc/cast.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-package space.kscience.dataforge.misc
-
-public expect inline fun <T> Any?.unsafeCast(): T
\ No newline at end of file
diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/names/Name.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/names/Name.kt
index b46a3507..7867330c 100644
--- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/names/Name.kt
+++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/names/Name.kt
@@ -58,7 +58,7 @@ public class Name(public val tokens: List<NameToken>) {
          */
         public fun parse(string: String): Name {
             if (string.isBlank()) return EMPTY
-            val tokens = sequence {
+            val tokens = buildList<NameToken> {
                 var bodyBuilder = StringBuilder()
                 var queryBuilder = StringBuilder()
                 var bracketCount = 0
@@ -91,7 +91,7 @@ public class Name(public val tokens: List<NameToken>) {
                         else -> when (it) {
                             '.' -> {
                                 val query = if (queryBuilder.isEmpty()) null else queryBuilder.toString()
-                                yield(NameToken(bodyBuilder.toString(), query))
+                                add(NameToken(bodyBuilder.toString(), query))
                                 bodyBuilder = StringBuilder()
                                 queryBuilder = StringBuilder()
                             }
@@ -106,9 +106,9 @@ public class Name(public val tokens: List<NameToken>) {
                     }
                 }
                 val query = if (queryBuilder.isEmpty()) null else queryBuilder.toString()
-                yield(NameToken(bodyBuilder.toString(), query))
+                add(NameToken(bodyBuilder.toString(), query))
             }
-            return Name(tokens.toList())
+            return Name(tokens)
         }
     }
 }
diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/names/NameIndexComparator.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/names/NameIndexComparator.kt
new file mode 100644
index 00000000..742f8ebb
--- /dev/null
+++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/names/NameIndexComparator.kt
@@ -0,0 +1,30 @@
+package space.kscience.dataforge.names
+
+import space.kscience.dataforge.meta.Meta
+import space.kscience.dataforge.meta.getIndexed
+
+
+/**
+ * A comparator for indices in a [Name]. If both indices are integers, compare them as integers.
+ * Null always stays "before" non-null index.
+ */
+public object NameIndexComparator : Comparator<String?> {
+    override fun compare(a: String?, b: String?): Int {
+        if (a == b) return 0
+        if (a == null) return 1
+        if (b == null) return -1
+        val aInt = a.toIntOrNull()
+        val bInt = b.toIntOrNull()
+        return if (aInt != null && bInt != null) {
+            aInt.compareTo(bInt)
+        } else {
+            a.compareTo(b)
+        }
+    }
+
+}
+
+public fun Meta.getIndexedList(name: Name): List<Meta> = getIndexed(name).entries.sortedWith(
+    //sort by index
+    compareBy(space.kscience.dataforge.names.NameIndexComparator) { it.key }
+).map{it.value}
\ No newline at end of file
diff --git a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/names/NameToken.kt b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/names/NameToken.kt
index 0dc83c57..83752b9a 100644
--- a/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/names/NameToken.kt
+++ b/dataforge-meta/src/commonMain/kotlin/space/kscience/dataforge/names/NameToken.kt
@@ -69,7 +69,7 @@ public class NameToken(public val body: String, public val index: String? = null
         public fun parse(string: String): NameToken {
             val body = string.substringBefore('[')
             val index = string.substringAfter('[', "")
-            if (index.isNotEmpty() && index.endsWith(']')) error("NameToken with index must end with ']'")
+            if (index.isNotEmpty() && !index.endsWith(']')) error("NameToken with index must end with ']'")
             return NameToken(body, index.removeSuffix("]"))
         }
     }
diff --git a/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/MetaTest.kt b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/MetaTest.kt
index 85db7bd6..78b5cdb4 100644
--- a/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/MetaTest.kt
+++ b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/MetaTest.kt
@@ -61,4 +61,33 @@ class MetaTest {
         assertEquals(null, indexed["8"])
         assertEquals(12, indexed["12"].int)
     }
+
+    @Test
+    fun reset() {
+        val oldMeta = MutableMeta {
+            "a" put {
+                "value" put "aValue"
+            }
+            "b" put {
+                "value" put "bValue"
+            }
+            "c" put {
+                "value" put "cValue"
+            }
+        }
+        val newMeta = Meta {
+            "a" put {
+                "value" put "aValue"
+            }
+            "b" put {
+                "value" put "bValue"
+            }
+            "d" put {
+                "value" put "dValue"
+            }
+        }
+        oldMeta.reset(newMeta)
+        println(oldMeta)
+        assertEquals(setOf("a", "b", "d"), oldMeta.items.keys.map { it.toString() }.toSet())
+    }
 }
\ No newline at end of file
diff --git a/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/SchemeTest.kt b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/SchemeTest.kt
deleted file mode 100644
index bb2736ce..00000000
--- a/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/SchemeTest.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-package space.kscience.dataforge.meta
-
-import space.kscience.dataforge.misc.DFExperimental
-import kotlin.test.Test
-import kotlin.test.assertEquals
-import kotlin.test.assertNotNull
-
-@DFExperimental
-class SchemeTest {
-    @Test
-    fun testSchemeWrappingBeforeEdit() {
-        val config = MutableMeta()
-        val scheme = TestScheme.write(config)
-        scheme.a = 29
-        assertEquals(29, config["a"].int)
-    }
-
-    @Test
-    fun testSchemeWrappingAfterEdit() {
-        val scheme = TestScheme.empty()
-        scheme.a = 29
-        val config = MutableMeta()
-        scheme.retarget(config)
-        assertEquals(29, scheme.a)
-    }
-
-    @Test
-    fun testSchemeSubscription() {
-        val scheme = TestScheme.empty()
-        var flag: Int? = null
-        scheme.useProperty(TestScheme::a) { a ->
-            flag = a
-        }
-        scheme.a = 2
-        assertEquals(2, flag)
-    }
-
-    @Test
-    fun testListSubscription(){
-        val scheme = TestScheme.empty()
-        var value: Value? = null
-        scheme.v = ListValue(0.0,0.0,0.0)
-        scheme.useProperty(TestScheme::v){
-            value = it
-        }
-        scheme.v = ListValue(1.0, 2.0, 3.0)
-        assertNotNull(value)
-    }
-}
\ No newline at end of file
diff --git a/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/SpecificationTest.kt b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/SpecificationTest.kt
index 8d4d3537..dc9b9d64 100644
--- a/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/SpecificationTest.kt
+++ b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/meta/SpecificationTest.kt
@@ -1,7 +1,17 @@
 package space.kscience.dataforge.meta
 
+import space.kscience.dataforge.misc.DFExperimental
 import kotlin.test.Test
 import kotlin.test.assertEquals
+import kotlin.test.assertNotNull
+
+
+internal class SubScheme : Scheme() {
+
+    var subValue by string()
+
+    companion object : SchemeSpec<SubScheme>(::SubScheme)
+}
 
 internal class TestScheme : Scheme() {
     var list by numberList(1, 2, 3)
@@ -11,9 +21,23 @@ internal class TestScheme : Scheme() {
 
     var v by value()
 
+    var sub by scheme(SubScheme)
+
     companion object : SchemeSpec<TestScheme>(::TestScheme)
 }
 
+private class SchemeWithInit: Scheme(){
+    init {
+        set("initial", "initialValue")
+    }
+
+    var initial by string()
+
+    companion object: SchemeSpec<SchemeWithInit>(::SchemeWithInit)
+}
+
+
+
 class SpecificationTest {
 
 //    @Test
@@ -71,4 +95,64 @@ class SpecificationTest {
         assertEquals(22, config["child.a"].int)
         assertEquals("test", config["child.b"].string)
     }
+
+    @Test
+    fun testSchemeWrappingBeforeEdit() {
+        val config = MutableMeta()
+        val scheme = TestScheme.write(config)
+        scheme.a = 29
+        assertEquals(29, config["a"].int)
+    }
+
+    @OptIn(DFExperimental::class)
+    @Test
+    fun testSchemeWrappingAfterEdit() {
+        val scheme = TestScheme.empty()
+        scheme.a = 29
+        val config = MutableMeta()
+        scheme.retarget(config)
+        assertEquals(29, scheme.a)
+    }
+
+    @Test
+    fun testSchemeSubscription() {
+        val scheme = TestScheme.empty()
+        var flag: Int? = null
+        scheme.useProperty(TestScheme::a) { a ->
+            flag = a
+        }
+        scheme.a = 2
+        assertEquals(2, flag)
+    }
+
+    @Test
+    fun testListSubscription(){
+        val scheme = TestScheme.empty()
+        var value: Value? = null
+        scheme.v = ListValue(0.0,0.0,0.0)
+        scheme.useProperty(TestScheme::v){
+            value = it
+        }
+        scheme.v = ListValue(1.0, 2.0, 3.0)
+        assertNotNull(value)
+    }
+
+    @Test
+    fun testSubScheme(){
+        val scheme = TestScheme.empty()
+
+        scheme.sub.subValue = "aaa"
+
+        assertEquals("aaa",scheme.sub.subValue)
+    }
+
+
+    @Test
+    fun testSchemeWithInit(){
+        val scheme = SchemeWithInit()
+        assertEquals("initialValue", scheme.initial)
+        scheme.initial = "none"
+        assertEquals("none", scheme.initial)
+    }
+
 }
\ No newline at end of file
diff --git a/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/names/NameTest.kt b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/names/NameTest.kt
index a5bdf3fc..db630487 100644
--- a/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/names/NameTest.kt
+++ b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/names/NameTest.kt
@@ -1,9 +1,6 @@
 package space.kscience.dataforge.names
 
-import kotlin.test.Test
-import kotlin.test.assertEquals
-import kotlin.test.assertFalse
-import kotlin.test.assertTrue
+import kotlin.test.*
 
 class NameTest {
     @Test
@@ -50,4 +47,19 @@ class NameTest {
         val name = Name.parse("a.b.c")
         assertEquals("a.b".parseAsName(), name.cutLast())
     }
+
+    @Test
+    fun tokenParseTest(){
+        val token1 = NameToken.parse("token[index]")
+        assertEquals("token", token1.body)
+        assertEquals("index", token1.index)
+
+        val token2 = NameToken.parse("token-body")
+        assertEquals("token-body", token2.body)
+        assertEquals("", token2.index)
+
+        assertFails {
+            NameToken.parse("token[22")
+        }
+    }
 }
\ No newline at end of file
diff --git a/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/values/DoubleArrayValue.kt b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/values/DoubleArrayValue.kt
new file mode 100644
index 00000000..30bc2adc
--- /dev/null
+++ b/dataforge-meta/src/commonTest/kotlin/space/kscience/dataforge/values/DoubleArrayValue.kt
@@ -0,0 +1,24 @@
+package space.kscience.dataforge.values
+
+import space.kscience.dataforge.meta.DoubleArrayValue
+import space.kscience.dataforge.meta.Meta
+import space.kscience.dataforge.meta.doubleArray
+import space.kscience.dataforge.meta.get
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
+
+class DoubleArrayValue {
+    @Test
+    fun doubleArrayWriteRead(){
+        val meta = Meta{
+            "doubleArray" put doubleArrayOf(1.0,2.0,3.0)
+        }
+
+        assertTrue {
+            meta["doubleArray"]?.value is DoubleArrayValue
+        }
+
+        assertEquals(2.0, meta["doubleArray"].doubleArray?.get(1))
+    }
+}
\ No newline at end of file
diff --git a/dataforge-meta/src/jsMain/kotlin/space/kscience/dataforge/meta/DynamicMeta.kt b/dataforge-meta/src/jsMain/kotlin/space/kscience/dataforge/meta/DynamicMeta.kt
index b38d5891..57d324c7 100644
--- a/dataforge-meta/src/jsMain/kotlin/space/kscience/dataforge/meta/DynamicMeta.kt
+++ b/dataforge-meta/src/jsMain/kotlin/space/kscience/dataforge/meta/DynamicMeta.kt
@@ -31,6 +31,9 @@ public fun Meta.toDynamic(): dynamic {
 }
 
 public class DynamicMeta(internal val obj: dynamic) : TypedMeta<DynamicMeta> {
+
+    override val self: DynamicMeta get() = this
+
     private fun keys(): Array<String> = js("Object").keys(obj) as Array<String>
 
     private fun isArray(obj: dynamic): Boolean =
diff --git a/dataforge-meta/src/jsMain/kotlin/space/kscience/dataforge/misc/castJs.kt b/dataforge-meta/src/jsMain/kotlin/space/kscience/dataforge/misc/castJs.kt
deleted file mode 100644
index b057bcbe..00000000
--- a/dataforge-meta/src/jsMain/kotlin/space/kscience/dataforge/misc/castJs.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package space.kscience.dataforge.misc
-import kotlin.js.unsafeCast as unsafeCastJs
-
-@Suppress("NOTHING_TO_INLINE")
-public actual inline fun <T> Any?.unsafeCast(): T = unsafeCastJs<T>()
\ No newline at end of file
diff --git a/dataforge-meta/src/jvmMain/kotlin/space/kscience/dataforge/misc/castJvm.kt b/dataforge-meta/src/jvmMain/kotlin/space/kscience/dataforge/misc/castJvm.kt
deleted file mode 100644
index 27d399fe..00000000
--- a/dataforge-meta/src/jvmMain/kotlin/space/kscience/dataforge/misc/castJvm.kt
+++ /dev/null
@@ -1,4 +0,0 @@
-package space.kscience.dataforge.misc
-
-@Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE")
-public actual inline fun <T> Any?.unsafeCast(): T = this as T
\ No newline at end of file
diff --git a/dataforge-meta/src/nativeMain/kotlin/space/kscience/dataforge/misc/castNative.kt b/dataforge-meta/src/nativeMain/kotlin/space/kscience/dataforge/misc/castNative.kt
deleted file mode 100644
index 4d9aa758..00000000
--- a/dataforge-meta/src/nativeMain/kotlin/space/kscience/dataforge/misc/castNative.kt
+++ /dev/null
@@ -1,4 +0,0 @@
-package space.kscience.dataforge.misc
-
-@Suppress("UNCHECKED_CAST")
-public actual inline fun <T> Any?.unsafeCast(): T = this as T
\ No newline at end of file
diff --git a/dataforge-meta/src/wasmJsMain/kotlin/space/kscience/dataforge/misc/castWasm.kt b/dataforge-meta/src/wasmJsMain/kotlin/space/kscience/dataforge/misc/castWasm.kt
deleted file mode 100644
index 27d399fe..00000000
--- a/dataforge-meta/src/wasmJsMain/kotlin/space/kscience/dataforge/misc/castWasm.kt
+++ /dev/null
@@ -1,4 +0,0 @@
-package space.kscience.dataforge.misc
-
-@Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE")
-public actual inline fun <T> Any?.unsafeCast(): T = this as T
\ No newline at end of file
diff --git a/dataforge-scripting/README.md b/dataforge-scripting/README.md
index fbc5cb69..1f650bea 100644
--- a/dataforge-scripting/README.md
+++ b/dataforge-scripting/README.md
@@ -6,7 +6,7 @@
 
 ## Artifact:
 
-The Maven coordinates of this project are `space.kscience:dataforge-scripting:0.8.0`.
+The Maven coordinates of this project are `space.kscience:dataforge-scripting:0.9.0-dev-1`.
 
 **Gradle Kotlin DSL:**
 ```kotlin
@@ -16,6 +16,6 @@ repositories {
 }
 
 dependencies {
-    implementation("space.kscience:dataforge-scripting:0.8.0")
+    implementation("space.kscience:dataforge-scripting:0.9.0-dev-1")
 }
 ```
diff --git a/dataforge-workspace/README.md b/dataforge-workspace/README.md
index cea37368..87b38c6e 100644
--- a/dataforge-workspace/README.md
+++ b/dataforge-workspace/README.md
@@ -6,7 +6,7 @@
 
 ## Artifact:
 
-The Maven coordinates of this project are `space.kscience:dataforge-workspace:0.8.0`.
+The Maven coordinates of this project are `space.kscience:dataforge-workspace:0.9.0-dev-1`.
 
 **Gradle Kotlin DSL:**
 ```kotlin
@@ -16,6 +16,6 @@ repositories {
 }
 
 dependencies {
-    implementation("space.kscience:dataforge-workspace:0.8.0")
+    implementation("space.kscience:dataforge-workspace:0.9.0-dev-1")
 }
 ```
diff --git a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/Task.kt b/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/Task.kt
index e636de49..a1a754a4 100644
--- a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/Task.kt
+++ b/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/Task.kt
@@ -5,11 +5,12 @@ import space.kscience.dataforge.data.DataSink
 import space.kscience.dataforge.data.GoalExecutionRestriction
 import space.kscience.dataforge.data.MutableDataTree
 import space.kscience.dataforge.meta.Meta
+import space.kscience.dataforge.meta.MetaReader
 import space.kscience.dataforge.meta.MetaRepr
-import space.kscience.dataforge.meta.MetaSpec
 import space.kscience.dataforge.meta.descriptors.Described
 import space.kscience.dataforge.meta.descriptors.MetaDescriptor
 import space.kscience.dataforge.misc.DfType
+import space.kscience.dataforge.misc.UnsafeKType
 import space.kscience.dataforge.names.Name
 import space.kscience.dataforge.workspace.Task.Companion.TYPE
 import kotlin.reflect.KType
@@ -43,10 +44,10 @@ public interface Task<T> : Described {
 }
 
 /**
- * A [Task] with [MetaSpec] for wrapping and unwrapping task configuration
+ * A [Task] with [MetaReader] for wrapping and unwrapping task configuration
  */
 public interface TaskWithSpec<T, C : Any> : Task<T> {
-    public val spec: MetaSpec<C>
+    public val spec: MetaReader<C>
     override val descriptor: MetaDescriptor? get() = spec.descriptor
 
     public suspend fun execute(workspace: Workspace, taskName: Name, configuration: C): TaskResult<T>
@@ -90,7 +91,8 @@ public fun <T : Any> Task(
         taskMeta: Meta,
     ): TaskResult<T> {
         //TODO use safe builder and check for external data on add and detects cycles
-        val dataset = MutableDataTree<T>(resultType, workspace.context).apply {
+        @OptIn(UnsafeKType::class)
+        val dataset = MutableDataTree<T>(resultType).apply {
             TaskResultBuilder(workspace, taskName, taskMeta, this).apply {
                 withContext(GoalExecutionRestriction() + workspace.goalLogger) {
                     builder()
@@ -98,7 +100,6 @@ public fun <T : Any> Task(
             }
         }
         return workspace.wrapResult(dataset, taskName, taskMeta)
-
     }
 }
 
@@ -117,13 +118,14 @@ public inline fun <reified T : Any> Task(
  * @param builder for resulting data set
  */
 
+
 @Suppress("FunctionName")
 public fun <T : Any, C : MetaRepr> Task(
     resultType: KType,
-    specification: MetaSpec<C>,
+    specification: MetaReader<C>,
     builder: suspend TaskResultBuilder<T>.(C) -> Unit,
 ): TaskWithSpec<T, C> = object : TaskWithSpec<T, C> {
-    override val spec: MetaSpec<C> = specification
+    override val spec: MetaReader<C> = specification
 
     override suspend fun execute(
         workspace: Workspace,
@@ -132,7 +134,8 @@ public fun <T : Any, C : MetaRepr> Task(
     ): TaskResult<T> = withContext(GoalExecutionRestriction() + workspace.goalLogger) {
         //TODO use safe builder and check for external data on add and detects cycles
         val taskMeta = configuration.toMeta()
-        val dataset = MutableDataTree<T>(resultType, this).apply {
+        @OptIn(UnsafeKType::class)
+        val dataset = MutableDataTree<T>(resultType).apply {
             TaskResultBuilder(workspace, taskName, taskMeta, this).apply { builder(configuration) }
         }
         workspace.wrapResult(dataset, taskName, taskMeta)
@@ -140,6 +143,6 @@ public fun <T : Any, C : MetaRepr> Task(
 }
 
 public inline fun <reified T : Any, C : MetaRepr> Task(
-    specification: MetaSpec<C>,
+    specification: MetaReader<C>,
     noinline builder: suspend TaskResultBuilder<T>.(C) -> Unit,
 ): Task<T> = Task(typeOf<T>(), specification, builder)
\ No newline at end of file
diff --git a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/TaskResult.kt b/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/TaskResult.kt
index 7aa94101..d4d4291a 100644
--- a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/TaskResult.kt
+++ b/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/TaskResult.kt
@@ -4,7 +4,7 @@ import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.joinAll
 import kotlinx.coroutines.launch
-import space.kscience.dataforge.data.ObservableDataTree
+import space.kscience.dataforge.data.DataTree
 import space.kscience.dataforge.data.asSequence
 import space.kscience.dataforge.data.launch
 import space.kscience.dataforge.meta.Meta
@@ -17,16 +17,16 @@ import space.kscience.dataforge.names.Name
  * @param taskMeta The configuration of the task that produced the result
  */
 public data class TaskResult<T>(
-    public val content: ObservableDataTree<T>,
+    public val content: DataTree<T>,
     public val workspace: Workspace,
     public val taskName: Name,
     public val taskMeta: Meta,
-) : ObservableDataTree<T> by content
+) : DataTree<T> by content
 
 /**
  * Wrap data into [TaskResult]
  */
-public fun <T> Workspace.wrapResult(data: ObservableDataTree<T>, taskName: Name, taskMeta: Meta): TaskResult<T> =
+public fun <T> Workspace.wrapResult(data: DataTree<T>, taskName: Name, taskMeta: Meta): TaskResult<T> =
     TaskResult(data, this, taskName, taskMeta)
 
 /**
diff --git a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/Workspace.kt b/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/Workspace.kt
index f3ea322c..7247240b 100644
--- a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/Workspace.kt
+++ b/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/Workspace.kt
@@ -2,7 +2,10 @@ package space.kscience.dataforge.workspace
 
 import kotlinx.coroutines.CoroutineScope
 import space.kscience.dataforge.context.ContextAware
-import space.kscience.dataforge.data.*
+import space.kscience.dataforge.data.Data
+import space.kscience.dataforge.data.DataTree
+import space.kscience.dataforge.data.asSequence
+import space.kscience.dataforge.data.get
 import space.kscience.dataforge.meta.Meta
 import space.kscience.dataforge.meta.MutableMeta
 import space.kscience.dataforge.misc.DfType
@@ -11,7 +14,7 @@ import space.kscience.dataforge.provider.Provider
 import kotlin.coroutines.CoroutineContext
 
 
-public fun interface DataSelector<T> {
+public fun interface DataSelector<out T> {
     public suspend fun select(workspace: Workspace, meta: Meta): DataTree<T>
 }
 
@@ -26,7 +29,7 @@ public interface Workspace : ContextAware, Provider, CoroutineScope {
     /**
      * The whole data node for current workspace
      */
-    public val data: ObservableDataTree<*>
+    public val data: DataTree<*>
 
     /**
      * All targets associated with the workspace
diff --git a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/WorkspaceBuilder.kt b/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/WorkspaceBuilder.kt
index 4705c3b0..013c0171 100644
--- a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/WorkspaceBuilder.kt
+++ b/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/WorkspaceBuilder.kt
@@ -1,6 +1,5 @@
 package space.kscience.dataforge.workspace
 
-import kotlinx.coroutines.CoroutineScope
 import space.kscience.dataforge.actions.Action
 import space.kscience.dataforge.context.Context
 import space.kscience.dataforge.context.ContextBuilder
@@ -12,6 +11,7 @@ import space.kscience.dataforge.meta.*
 import space.kscience.dataforge.meta.descriptors.MetaDescriptor
 import space.kscience.dataforge.meta.descriptors.MetaDescriptorBuilder
 import space.kscience.dataforge.misc.DFBuilder
+import space.kscience.dataforge.misc.UnsafeKType
 import space.kscience.dataforge.names.Name
 import space.kscience.dataforge.names.asName
 import kotlin.collections.set
@@ -71,10 +71,10 @@ public inline fun <reified T : Any> TaskContainer.task(
 }
 
 /**
- * Create a task based on [MetaSpec]
+ * Create a task based on [MetaReader]
  */
 public inline fun <reified T : Any, C : MetaRepr> TaskContainer.task(
-    specification: MetaSpec<C>,
+    specification: MetaReader<C>,
     noinline builder: suspend TaskResultBuilder<T>.(C) -> Unit,
 ): PropertyDelegateProvider<Any?, ReadOnlyProperty<Any?, TaskReference<T>>> = PropertyDelegateProvider { _, property ->
     val taskName = Name.parse(property.name)
@@ -98,19 +98,19 @@ public inline fun <reified T : Any> TaskContainer.task(
 public inline fun <T : Any, reified R : Any> TaskContainer.action(
     selector: DataSelector<T>,
     action: Action<T, R>,
-    noinline metaTransform: MutableMeta.()-> Unit = {},
+    noinline metaTransform: MutableMeta.() -> Unit = {},
     noinline descriptorBuilder: MetaDescriptorBuilder.() -> Unit = {},
 ): PropertyDelegateProvider<Any?, ReadOnlyProperty<Any?, TaskReference<R>>> =
     task(MetaDescriptor(descriptorBuilder)) {
-        result(action.execute(from(selector), taskMeta.copy(metaTransform)))
+        result(action.execute(from(selector), taskMeta.copy(metaTransform), workspace))
     }
 
 public class WorkspaceBuilder(
     private val parentContext: Context = Global,
-    private val coroutineScope: CoroutineScope = parentContext,
 ) : TaskContainer {
     private var context: Context? = null
-    private val data = MutableDataTree<Any?>(typeOf<Any?>(), coroutineScope)
+    @OptIn(UnsafeKType::class)
+    private val data = MutableDataTree<Any?>(typeOf<Any?>())
     private val targets: HashMap<String, Meta> = HashMap()
     private val tasks = HashMap<Name, Task<*>>()
     private var cache: WorkspaceCache? = null
diff --git a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/WorkspaceImpl.kt b/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/WorkspaceImpl.kt
index 21c5e8c2..94839d62 100644
--- a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/WorkspaceImpl.kt
+++ b/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/WorkspaceImpl.kt
@@ -2,14 +2,14 @@ package space.kscience.dataforge.workspace
 
 import space.kscience.dataforge.context.Context
 import space.kscience.dataforge.context.gather
-import space.kscience.dataforge.data.ObservableDataTree
+import space.kscience.dataforge.data.DataTree
 import space.kscience.dataforge.meta.Meta
 import space.kscience.dataforge.names.Name
 
 
 internal class WorkspaceImpl internal constructor(
     override val context: Context,
-    override val data: ObservableDataTree<*>,
+    override val data: DataTree<*>,
     override val targets: Map<String, Meta>,
     tasks: Map<Name, Task<*>>,
     private val postProcess: suspend (TaskResult<*>) -> TaskResult<*>,
diff --git a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/envelopeData.kt b/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/envelopeData.kt
index d54ff510..a74f8f05 100644
--- a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/envelopeData.kt
+++ b/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/envelopeData.kt
@@ -3,14 +3,14 @@ package space.kscience.dataforge.workspace
 import space.kscience.dataforge.data.Data
 import space.kscience.dataforge.data.await
 import space.kscience.dataforge.io.*
-import space.kscience.dataforge.misc.DFInternal
+import space.kscience.dataforge.misc.UnsafeKType
 import kotlin.reflect.typeOf
 
 
 /**
  * Convert an [Envelope] to a data via given format. The actual parsing is done lazily.
  */
-@OptIn(DFInternal::class)
+@OptIn(UnsafeKType::class)
 public inline fun <reified T : Any> Envelope.toData(format: IOReader<T>): Data<T> = Data(typeOf<T>(), meta) {
     data?.readWith(format) ?: error("Can't convert envelope without data to Data")
 }
diff --git a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/taskBuilders.kt b/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/taskBuilders.kt
index 1900ff23..15565995 100644
--- a/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/taskBuilders.kt
+++ b/dataforge-workspace/src/commonMain/kotlin/space/kscience/dataforge/workspace/taskBuilders.kt
@@ -3,8 +3,8 @@ package space.kscience.dataforge.workspace
 import space.kscience.dataforge.actions.Action
 import space.kscience.dataforge.context.PluginFactory
 import space.kscience.dataforge.data.DataTree
-import space.kscience.dataforge.data.branch
 import space.kscience.dataforge.data.forEach
+import space.kscience.dataforge.data.putAll
 import space.kscience.dataforge.data.transform
 import space.kscience.dataforge.meta.*
 import space.kscience.dataforge.misc.DFExperimental
@@ -101,7 +101,7 @@ public suspend inline fun <T, reified R> TaskResultBuilder<R>.transformEach(
  * Set given [dataSet] as a task result.
  */
 public fun <T> TaskResultBuilder<T>.result(dataSet: DataTree<T>) {
-    branch(dataSet)
+    this.putAll(dataSet)
 }
 
 /**
@@ -113,7 +113,7 @@ public suspend inline fun <T, reified R> TaskResultBuilder<R>.actionFrom(
     action: Action<T, R>,
     dependencyMeta: Meta = defaultDependencyMeta,
 ) {
-    branch(action.execute(from(selector, dependencyMeta), dependencyMeta))
+    putAll(action.execute(from(selector, dependencyMeta), dependencyMeta, workspace))
 }
 
 
diff --git a/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/CachingAction.kt b/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/CachingAction.kt
new file mode 100644
index 00000000..5f88ab74
--- /dev/null
+++ b/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/CachingAction.kt
@@ -0,0 +1,19 @@
+package space.kscience.dataforge.workspace
+
+import space.kscience.dataforge.actions.AbstractAction
+import space.kscience.dataforge.data.*
+import space.kscience.dataforge.meta.Meta
+import kotlin.reflect.KType
+
+internal class CachingAction<T>(type: KType, private val caching: (NamedData<T>) -> NamedData<T>) :
+    AbstractAction<T, T>(type) {
+    override fun DataSink<T>.generate(source: DataTree<T>, meta: Meta) {
+        source.forEach {
+            put(caching(it))
+        }
+    }
+
+    override suspend fun DataSink<T>.update(source: DataTree<T>, meta: Meta, updatedData: DataUpdate<T>) {
+        put(updatedData.name, updatedData.data?.named(updatedData.name)?.let(caching))
+    }
+}
\ No newline at end of file
diff --git a/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/FileWorkspaceCache.kt b/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/FileWorkspaceCache.kt
index a43657f8..4d2578e5 100644
--- a/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/FileWorkspaceCache.kt
+++ b/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/FileWorkspaceCache.kt
@@ -1,19 +1,22 @@
 package space.kscience.dataforge.workspace
 
-import kotlinx.coroutines.flow.map
 import kotlinx.io.*
 import kotlinx.serialization.ExperimentalSerializationApi
 import kotlinx.serialization.KSerializer
 import kotlinx.serialization.json.Json
 import kotlinx.serialization.protobuf.ProtoBuf
 import kotlinx.serialization.serializer
+import space.kscience.dataforge.actions.Action
+import space.kscience.dataforge.actions.invoke
 import space.kscience.dataforge.context.error
 import space.kscience.dataforge.context.logger
 import space.kscience.dataforge.context.request
-import space.kscience.dataforge.data.*
+import space.kscience.dataforge.data.Data
+import space.kscience.dataforge.data.await
+import space.kscience.dataforge.data.named
 import space.kscience.dataforge.io.*
 import space.kscience.dataforge.misc.DFExperimental
-import space.kscience.dataforge.misc.DFInternal
+import space.kscience.dataforge.misc.UnsafeKType
 import space.kscience.dataforge.names.withIndex
 import java.nio.file.Path
 import kotlin.io.path.deleteIfExists
@@ -51,7 +54,8 @@ public class FileWorkspaceCache(public val cacheDirectory: Path) : WorkspaceCach
 
     //    private fun <T : Any> TaskData<*>.checkType(taskType: KType): TaskData<T> = this as TaskData<T>
 
-    @OptIn(DFExperimental::class, DFInternal::class)
+
+    @OptIn(DFExperimental::class, UnsafeKType::class)
     override suspend fun <T> cache(result: TaskResult<T>): TaskResult<T> {
         val io = result.workspace.context.request(IOPlugin)
 
@@ -59,8 +63,8 @@ public class FileWorkspaceCache(public val cacheDirectory: Path) : WorkspaceCach
             ?: ProtobufIOFormat(result.dataType)
             ?: error("Can't resolve IOFormat for ${result.dataType}")
 
-        fun cacheOne(data: NamedData<T>): NamedData<T> {
 
+        val cachingAction: Action<T, T> = CachingAction(result.dataType) { data ->
             val path = cacheDirectory /
                     result.taskName.withIndex(result.taskMeta.hashCode().toString(16)).toString() /
                     data.name.toString()
@@ -79,7 +83,7 @@ public class FileWorkspaceCache(public val cacheDirectory: Path) : WorkspaceCach
                     }
                 }
 
-                //waiting for data in current scope because Envelope is synchronous
+                //waiting for data in the current scope because Envelope is synchronous
                 return@Data data.await().also { result ->
                     val envelope = Envelope {
                         meta = data.meta
@@ -91,12 +95,10 @@ public class FileWorkspaceCache(public val cacheDirectory: Path) : WorkspaceCach
                 }
 
             }
-            return datum.named(data.name)
+            datum.named(data.name)
         }
 
-
-        val cachedTree = result.asSequence().map { cacheOne(it) }
-            .toObservableTree(result.dataType, result.workspace, result.updates().map { cacheOne(it) })
+        val cachedTree = cachingAction(result)
 
         return result.workspace.wrapResult(cachedTree, result.taskName, result.taskMeta)
     }
diff --git a/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/InMemoryWorkspaceCache.kt b/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/InMemoryWorkspaceCache.kt
index a3792231..8ba39ec1 100644
--- a/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/InMemoryWorkspaceCache.kt
+++ b/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/InMemoryWorkspaceCache.kt
@@ -1,8 +1,11 @@
 package space.kscience.dataforge.workspace
 
-import kotlinx.coroutines.flow.map
-import space.kscience.dataforge.data.*
+import space.kscience.dataforge.actions.Action
+import space.kscience.dataforge.actions.invoke
+import space.kscience.dataforge.data.Data
+import space.kscience.dataforge.data.named
 import space.kscience.dataforge.meta.Meta
+import space.kscience.dataforge.misc.DFExperimental
 import space.kscience.dataforge.names.Name
 import kotlin.reflect.KType
 import kotlin.reflect.full.isSubtypeOf
@@ -19,19 +22,18 @@ public class InMemoryWorkspaceCache : WorkspaceCache {
         if (type.isSubtypeOf(taskType)) this as Data<T>
         else error("Cached data type mismatch: expected $taskType but got $type")
 
+    @OptIn(DFExperimental::class)
     override suspend fun <T> cache(result: TaskResult<T>): TaskResult<T> {
-        fun cacheOne(data: NamedData<T>): NamedData<T> {
+        val cachingAction: Action<T, T> = CachingAction(result.dataType) { data ->
             val cachedData =  cache.getOrPut(TaskResultId(result.taskName, result.taskMeta)){
                 HashMap()
             }.getOrPut(data.name){
                 data.data
             }
-            return cachedData.checkType<T>(result.dataType).named(data.name)
+            cachedData.checkType<T>(result.dataType).named(data.name)
         }
 
-
-        val cachedTree = result.asSequence().map { cacheOne(it) }
-            .toObservableTree(result.dataType, result.workspace, result.updates().map { cacheOne(it) })
+        val cachedTree = cachingAction(result)
 
         return result.workspace.wrapResult(cachedTree, result.taskName, result.taskMeta)
     }
diff --git a/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/readFileData.kt b/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/readFileData.kt
index 1815c3e4..37dafab9 100644
--- a/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/readFileData.kt
+++ b/dataforge-workspace/src/jvmMain/kotlin/space/kscience/dataforge/workspace/readFileData.kt
@@ -8,15 +8,11 @@ import space.kscience.dataforge.io.*
 import space.kscience.dataforge.meta.Meta
 import space.kscience.dataforge.meta.copy
 import space.kscience.dataforge.misc.DFExperimental
-import space.kscience.dataforge.misc.DFInternal
 import space.kscience.dataforge.names.Name
 import space.kscience.dataforge.names.NameToken
 import space.kscience.dataforge.names.asName
 import space.kscience.dataforge.names.plus
-import java.nio.file.Files
-import java.nio.file.Path
-import java.nio.file.StandardWatchEventKinds
-import java.nio.file.WatchEvent
+import java.nio.file.*
 import java.nio.file.attribute.BasicFileAttributes
 import java.nio.file.spi.FileSystemProvider
 import kotlin.io.path.*
@@ -39,7 +35,6 @@ public object FileData {
  * Read data with supported envelope format and binary format. If the envelope format is null, then read binary directly from file.
  * The operation is blocking since it must read the meta header. The reading of envelope body is lazy
  */
-@OptIn(DFExperimental::class)
 public fun IOPlugin.readFileData(
     path: Path,
 ): Data<Binary> {
@@ -127,8 +122,6 @@ public fun DataSink<Binary>.files(
 
 private fun Path.toName() = Name(map { NameToken.parse(it.nameWithoutExtension) })
 
-@DFInternal
-@DFExperimental
 public fun DataSink<Binary>.monitorFiles(
     io: IOPlugin,
     name: Name,
@@ -174,13 +167,22 @@ public fun DataSink<Binary>.monitorFiles(
 @DFExperimental
 public fun DataSink<Binary>.resources(
     io: IOPlugin,
-    vararg resources: String,
+    resource: String,
+    vararg otherResources: String,
     classLoader: ClassLoader = Thread.currentThread().contextClassLoader,
 ) {
-    resources.forEach { resource ->
-        val path = classLoader.getResource(resource)?.toURI()?.toPath() ?: error(
-            "Resource with name $resource is not resolved"
+    //create a file system if necessary
+    val uri = Thread.currentThread().contextClassLoader.getResource("common")!!.toURI()
+    try {
+        uri.toPath()
+    } catch (e: FileSystemNotFoundException) {
+        FileSystems.newFileSystem(uri, mapOf("create" to "true"))
+    }
+
+    listOf(resource,*otherResources).forEach { r ->
+        val path = classLoader.getResource(r)?.toURI()?.toPath() ?: error(
+            "Resource with name $r is not resolved"
         )
-        files(io, resource.asName(), path)
+        files(io, r.asName(), path)
     }
 }
diff --git a/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/CachingWorkspaceTest.kt b/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/CachingWorkspaceTest.kt
index e5c2c230..7a6a8202 100644
--- a/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/CachingWorkspaceTest.kt
+++ b/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/CachingWorkspaceTest.kt
@@ -3,7 +3,7 @@ package space.kscience.dataforge.workspace
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.test.runTest
 import org.junit.jupiter.api.Test
-import space.kscience.dataforge.data.wrap
+import space.kscience.dataforge.data.putValue
 import space.kscience.dataforge.meta.Meta
 import space.kscience.dataforge.meta.boolean
 import space.kscience.dataforge.meta.get
@@ -22,7 +22,7 @@ internal class CachingWorkspaceTest {
             data {
                 //statically initialize data
                 repeat(5) {
-                    wrap("myData[$it]", it)
+                    putValue("myData[$it]", it)
                 }
             }
 
diff --git a/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/DataPropagationTest.kt b/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/DataPropagationTest.kt
index d611b1c8..cd38f809 100644
--- a/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/DataPropagationTest.kt
+++ b/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/DataPropagationTest.kt
@@ -47,7 +47,7 @@ class DataPropagationTest {
         }
         data {
             repeat(100) {
-                wrap("myData[$it]", it)
+                putValue("myData[$it]", it)
             }
         }
     }
diff --git a/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/FileDataTest.kt b/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/FileDataTest.kt
index 10a1c268..5466da76 100644
--- a/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/FileDataTest.kt
+++ b/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/FileDataTest.kt
@@ -23,12 +23,12 @@ import kotlin.test.assertEquals
 
 class FileDataTest {
     val dataNode = DataTree<String> {
-        branch("dir") {
-            wrap("a", "Some string") {
+        putAll("dir") {
+            putValue("a", "Some string") {
                 "content" put "Some string"
             }
         }
-        wrap("b", "root data")
+        putValue("b", "root data")
 //        meta {
 //            "content" put "This is root meta node"
 //        }
diff --git a/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/FileWorkspaceCacheTest.kt b/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/FileWorkspaceCacheTest.kt
index 0f16b1c8..0cf4f401 100644
--- a/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/FileWorkspaceCacheTest.kt
+++ b/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/FileWorkspaceCacheTest.kt
@@ -3,11 +3,11 @@ package space.kscience.dataforge.workspace
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runTest
 import org.junit.jupiter.api.Test
-import space.kscience.dataforge.data.wrap
+import space.kscience.dataforge.data.putValue
 import space.kscience.dataforge.misc.DFExperimental
 import java.nio.file.Files
 
-@OptIn(ExperimentalCoroutinesApi::class,DFExperimental::class)
+@OptIn(ExperimentalCoroutinesApi::class, DFExperimental::class)
 class FileWorkspaceCacheTest {
 
     @Test
@@ -16,7 +16,7 @@ class FileWorkspaceCacheTest {
             data {
                 //statically initialize data
                 repeat(5) {
-                    wrap("myData[$it]", it)
+                    putValue("myData[$it]", it)
                 }
             }
             fileCache(Files.createTempDirectory("dataforge-temporary-cache"))
diff --git a/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/SimpleWorkspaceTest.kt b/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/SimpleWorkspaceTest.kt
index b49b9d54..39837c15 100644
--- a/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/SimpleWorkspaceTest.kt
+++ b/dataforge-workspace/src/jvmTest/kotlin/space/kscience/dataforge/workspace/SimpleWorkspaceTest.kt
@@ -6,7 +6,6 @@ package space.kscience.dataforge.workspace
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.test.runTest
-import org.junit.jupiter.api.Timeout
 import space.kscience.dataforge.context.*
 import space.kscience.dataforge.data.*
 import space.kscience.dataforge.meta.*
@@ -16,6 +15,7 @@ import space.kscience.dataforge.names.plus
 import kotlin.test.Test
 import kotlin.test.assertEquals
 import kotlin.test.assertTrue
+import kotlin.time.Duration.Companion.milliseconds
 
 
 /**
@@ -62,7 +62,7 @@ internal class SimpleWorkspaceTest {
         data {
             //statically initialize data
             repeat(100) {
-                wrap("myData[$it]", it)
+                putValue("myData[$it]", it)
             }
         }
 
@@ -148,18 +148,16 @@ internal class SimpleWorkspaceTest {
     }
 
     @Test
-    @Timeout(1)
-    fun testWorkspace() = runTest {
+    fun testWorkspace() = runTest(timeout = 100.milliseconds) {
         val node = workspace.produce("sum")
         val res = node.asSequence().single()
         assertEquals(328350, res.await())
     }
 
     @Test
-    @Timeout(1)
-    fun testMetaPropagation() = runTest {
+    fun testMetaPropagation() = runTest(timeout = 100.milliseconds) {
         val node = workspace.produce("sum") { "testFlag" put true }
-        val res = node.single().await()
+        val res = node["sum"]!!.await()
     }
 
     @Test
@@ -188,7 +186,7 @@ internal class SimpleWorkspaceTest {
         val node = workspace.produce("filterOne") {
             "name" put "myData[12]"
         }
-        assertEquals(12, node.single().await())
+        assertEquals(12, node.asSequence().first().await())
     }
 
 }
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index 3734d13e..ffc318d9 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,10 +1,9 @@
+kotlin.code.style=official
+
 org.gradle.parallel=true
 org.gradle.jvmargs=-Xmx4096m
 
-kotlin.code.style=official
 kotlin.mpp.stability.nowarn=true
-kotlin.incremental.js.ir=true
 kotlin.native.ignoreDisabledTargets=true
 
-toolsVersion=0.15.2-kotlin-1.9.21
-#kotlin.experimental.tryK2=true
\ No newline at end of file
+toolsVersion=0.15.4-kotlin-2.0.0
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index e411586a..17655d0e 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists