diff --git a/dist b/dist
index 3f60690880908e7f9a92642e37253f1ac84db3f6..e2f93af86e9dd207cb57d313c6ac305cd69e34d1 160000
--- a/dist
+++ b/dist
@@ -1 +1 @@
-Subproject commit 3f60690880908e7f9a92642e37253f1ac84db3f6
+Subproject commit e2f93af86e9dd207cb57d313c6ac305cd69e34d1
diff --git a/src/Database.js b/src/Database.js
index 8246e3fc90fe3cb76c4386c48854d85bb11cc1ae..7810c1784c0bfe8320f16d5c0d559df07057abab 100644
--- a/src/Database.js
+++ b/src/Database.js
@@ -413,7 +413,7 @@ module.exports = function (Y /* :any */) {
 
       // notify parent, if it was instanciated as a custom type
       if (t != null) {
-        let o = Y.utils.copyObject(op)
+        let o = Y.utils.copyOperation(op)
         yield* t._changed(transaction, o)
       }
       if (!op.deleted) {
diff --git a/src/SpecHelper.js b/src/SpecHelper.js
index 5683962d2e4517b8e322fd5226f8b0b898e13fb4..47ce153f0f8f12e4d445dc3672037a6dd72cb78c 100644
--- a/src/SpecHelper.js
+++ b/src/SpecHelper.js
@@ -86,10 +86,6 @@ function getRandomString () {
 g.getRandomString = getRandomString
 
 function * applyTransactions (relAmount, numberOfTransactions, objects, users, transactions, noReconnect) {
-  function randomTransaction (root) {
-    var f = getRandom(transactions)
-    f(root)
-  }
   for (var i = 0; i < numberOfTransactions * relAmount + 1; i++) {
     var r = Math.random()
     if (r >= 0.5) {
@@ -97,44 +93,76 @@ function * applyTransactions (relAmount, numberOfTransactions, objects, users, t
       yield Y.utils.globalRoom.flushOne() // flushes for some user.. (not necessarily 0)
     } else if (noReconnect || r >= 0.05) {
       // 45% chance to create operation
-      randomTransaction(getRandom(objects))
+      var done = getRandom(transactions)(getRandom(objects))
+      if (done != null) {
+        yield done
+      } else {
+        yield wait()
+      }
       yield Y.utils.globalRoom.whenTransactionsFinished()
     } else {
       // 5% chance to disconnect/reconnect
       var u = getRandom(users)
+      yield Promise.all(objects.map(fixAwaitingInType))
       if (u.connector.isDisconnected()) {
         yield u.reconnect()
       } else {
         yield u.disconnect()
       }
+      yield Promise.all(objects.map(fixAwaitingInType))
     }
   }
 }
 
+function fixAwaitingInType (type) {
+  return new Promise(function (resolve) {
+    type.os.whenTransactionsFinished().then(function () {
+      // _debuggingAwaiting artificially increases the awaiting property. We need to make sure that we only do that once / reverse the effect once
+      type.os.requestTransaction(function * () {
+        if (type.eventHandler.awaiting > 0 && type.eventHandler._debuggingAwaiting === true) {
+          type.eventHandler._debuggingAwaiting = false
+          yield* type.eventHandler.awaitedOps(this, 0)
+        }
+        wait(50).then(type.os.whenTransactionsFinished()).then(wait(50)).then(resolve)
+      })
+    })
+  })
+}
+g.fixAwaitingInType = fixAwaitingInType
+
 g.applyRandomTransactionsNoGCNoDisconnect = async(function * applyRandomTransactions (users, objects, transactions, numberOfTransactions) {
   yield* applyTransactions(1, numberOfTransactions, objects, users, transactions, true)
   yield Y.utils.globalRoom.flushAll()
+  yield Promise.all(objects.map(fixAwaitingInType))
 })
 
 g.applyRandomTransactionsAllRejoinNoGC = async(function * applyRandomTransactions (users, objects, transactions, numberOfTransactions) {
   yield* applyTransactions(1, numberOfTransactions, objects, users, transactions)
+  yield Promise.all(objects.map(fixAwaitingInType))
   yield Y.utils.globalRoom.flushAll()
+  yield Promise.all(objects.map(fixAwaitingInType))
   for (var u in users) {
+    yield Promise.all(objects.map(fixAwaitingInType))
     yield users[u].reconnect()
+    yield Promise.all(objects.map(fixAwaitingInType))
   }
+  yield Promise.all(objects.map(fixAwaitingInType))
   yield Y.utils.globalRoom.flushAll()
+  yield Promise.all(objects.map(fixAwaitingInType))
   yield g.garbageCollectAllUsers(users)
 })
 
 g.applyRandomTransactionsWithGC = async(function * applyRandomTransactions (users, objects, transactions, numberOfTransactions) {
   yield* applyTransactions(1, numberOfTransactions, objects, users.slice(1), transactions)
   yield Y.utils.globalRoom.flushAll()
+  yield Promise.all(objects.map(fixAwaitingInType))
   for (var u in users) {
     // TODO: here, we enforce that two users never sync at the same time with u[0]
     //       enforce that in the connector itself!
     yield users[u].reconnect()
   }
   yield Y.utils.globalRoom.flushAll()
+  yield Promise.all(objects.map(fixAwaitingInType))
   yield g.garbageCollectAllUsers(users)
 })
 
@@ -222,7 +250,6 @@ g.compareAllUsers = async(function * compareAllUsers (users) {
         })
       })
     } else {
-      // TODO: make requestTransaction return a promise..
       u.db.requestTransaction(function * () {
         yield* t2.call(this)
         var db2 = []
diff --git a/src/Transaction.js b/src/Transaction.js
index 7f0275bf09e811815a00780a00f9018a9618c66c..d32112b3f6a6c4d82f42d8e673291c74444e013a 100644
--- a/src/Transaction.js
+++ b/src/Transaction.js
@@ -1068,7 +1068,7 @@ module.exports = function (Y/* :any */) {
     /* this is what we used before.. use this as a reference..
     * makeOperationReady (startSS, op) {
       op = Y.Struct[op.struct].encode(op)
-      op = Y.utils.copyObject(op)
+      op = Y.utils.copyObject(op) -- use copyoperation instead now!
       var o = op
       var ids = [op.id]
       // search for the new op.right
diff --git a/src/Utils.js b/src/Utils.js
index 759ccd15858e7d25dc55fd8315daa73cf97cb80b..6214fcb59fe1eaa9de786e5e269c385d44765103 100644
--- a/src/Utils.js
+++ b/src/Utils.js
@@ -102,9 +102,41 @@ module.exports = function (Y /* : any*/) {
       prematurely called operations are executed
     */
     awaitAndPrematurelyCall (ops) {
-      this.awaiting += ops.length
-      ops.forEach(this.onevent)
+      this.awaiting++
+      ops.map(Y.utils.copyOperation).forEach(this.onevent)
     }
+    * awaitedOps (transaction, n) {
+      // remove awaited ops
+      this.waiting.splice(this.waiting.length - n)
+      // update all waiting ops
+      for (let i = 0; i < this.waiting.length; i++) {
+        var o = this.waiting[i]
+        if (o.struct === 'Insert') {
+          var _o = yield* transaction.getInsertion(o.id)
+          if (!Y.utils.compareIds(_o.id, o.id)) {
+            // o got extended
+            o.left = [o.id[0], o.id[1] - 1]
+          } else if (_o.left == null) {
+            o.left = null
+          } else {
+            // find next undeleted op
+            var left = yield* transaction.getInsertion(_o.left)
+            while (left.deleted != null) {
+              if (left.left != null) {
+                left = yield* transaction.getInsertion(left.left)
+              } else {
+                left = null
+                break
+              }
+            }
+            o.left = left != null ? Y.utils.getLastId(left) : null
+          }
+        }
+      }
+      this._tryCallEvents()
+    }
+    // TODO: Remove awaitedInserts and awaitedDeletes in favor of awaitedOps, as they are deprecated and do not always work
+    // Do this in one of the coming releases that are breaking anyway
     /*
       Call this when you successfully awaited the execution of n Insert operations
     */
@@ -162,12 +194,42 @@ module.exports = function (Y /* : any*/) {
     /* (private)
       Try to execute the events for the waiting operations
     */
-    _tryCallEvents (n) {
-      this.awaiting -= n
+    _tryCallEvents () {
+      function notSoSmartSort (array) {
+        var result = []
+        while (array.length > 0) {
+          for (var i = 0; i < array.length; i++) {
+            var independent = true
+            for (var j = 0; j < array.length; j++) {
+              if (Y.utils.matchesId(array[j], array[i].left)) {
+                // array[i] depends on array[j]
+                independent = false
+                break
+              }
+            }
+            if (independent) {
+              result.push(array.splice(i, 1)[0])
+              i--
+            }
+          }
+        }
+        return result
+      }
+      if (this.awaiting > 0) this.awaiting--
       if (this.awaiting === 0 && this.waiting.length > 0) {
-        var ops = this.waiting
+        var ins = []
+        var dels = []
+        this.waiting.forEach(function (o) {
+          if (o.struct === 'Delete') {
+            dels.push(o)
+          } else {
+            ins.push(o)
+          }
+        })
+        ins = notSoSmartSort(ins)
+        ins.forEach(this.onevent)
+        dels.forEach(this.onevent)
         this.waiting = []
-        ops.forEach(this.onevent)
       }
     }
   }
@@ -236,6 +298,20 @@ module.exports = function (Y /* : any*/) {
   }
   Y.utils.copyObject = copyObject
 
+  /*
+    Copy an operation, so that it can be manipulated.
+    Note: You must not change subproperties (except o.content)!
+  */
+  function copyOperation (o) {
+    o = copyObject(o)
+    if (o.content != null) {
+      o.content = o.content.map(function (c) { return c })
+    }
+    return o
+  }
+
+  Y.utils.copyOperation = copyOperation
+
   /*
     Defines a smaller relation on Id's
   */
@@ -244,6 +320,11 @@ module.exports = function (Y /* : any*/) {
   }
   Y.utils.smaller = smaller
 
+  function inDeletionRange (del, ins) {
+    return del.target[0] === ins[0] && del.target[1] <= ins[1] && ins[1] < del.target[1] + (del.length || 1)
+  }
+  Y.utils.inDeletionRange = inDeletionRange
+
   function compareIds (id1, id2) {
     if (id1 == null || id2 == null) {
       return id1 === id2