diff --git a/LCS/PyCommon/test/postgres.py b/LCS/PyCommon/test/postgres.py
index 619104cc1d5a73ad928b249543a0ce0f60f978d9..f98092c3d7fd2a42f745c20ff59b2703bdb6cb43 100755
--- a/LCS/PyCommon/test/postgres.py
+++ b/LCS/PyCommon/test/postgres.py
@@ -39,7 +39,7 @@ class PostgresTestDatabaseInstance():
     Best used in a 'with'-context so the server is destroyed automagically.
     Derive your own sub-class and implement apply_database_schema with your own sql schema to setup your type of database.
     '''
-    _named_lock = NamedAtomicLock('PostgresTestDatabaseInstance', maxLockAge=60)
+    _named_lock = NamedAtomicLock('PostgresTestDatabaseInstance')
 
     def __init__(self, user: str = 'test_user', preferred_port: int=5444) -> None:
         self._postgresql = None
diff --git a/LCS/PyCommon/test/t_json_utils.py b/LCS/PyCommon/test/t_json_utils.py
index 0c4b97385b5a7cc8049b0c6805ae8ef9806dfe92..2237f0f8d68717fe304b4babb301887b6bf89546 100755
--- a/LCS/PyCommon/test/t_json_utils.py
+++ b/LCS/PyCommon/test/t_json_utils.py
@@ -23,7 +23,8 @@ logging.basicConfig(format='%(asctime)s %(process)s %(threadName)s %(levelname)s
 
 import unittest
 import threading
-from lofar.common.json_utils import *
+import json
+from lofar.common.json_utils import get_default_json_object_for_schema, replace_host_in_urls, resolved_refs
 
 class TestJSONUtils(unittest.TestCase):
     def test_empty_schema_yields_empty_object(self):
@@ -94,8 +95,14 @@ class TestJSONUtils(unittest.TestCase):
                                "type": "string",
                                "minLength": 2 },
                                 "email": {
-                                    "$ref": base_url + "/base_schema.json" + "#/definitions/email"  }
-                            } }
+                                    "$ref": base_url + "/base_schema.json" + "#/definitions/email"
+                                },
+                           "other_emails": {
+                               "type": "array",
+                               "items": {
+                                    "$ref": base_url + "/base_schema.json" + "#/definitions/email"
+                                }
+                           } } }
 
         class TestRequestHandler(http.server.BaseHTTPRequestHandler):
             '''helper class to serve the schemas via http. Needed for resolving the $ref URLs'''
@@ -146,7 +153,13 @@ class TestJSONUtils(unittest.TestCase):
                           "type": "string",
                           "minLength": 2 },
                       "email": {
-                          "$ref": base_host + path + "/base_schema.json" + "/#definitions/email"  }
+                          "$ref": base_host + path + "/base_schema.json" + "#/definitions/email"  },
+                       "other_emails": {
+                           "type": "array",
+                           "items": {
+                                "$ref": base_host + path + "/base_schema.json" + "#/definitions/email"
+                            }
+                       }
                   } }
 
         new_base_host = 'http://127.0.0.1'
@@ -156,7 +169,8 @@ class TestJSONUtils(unittest.TestCase):
         print('url_fixed_schema: ', json.dumps(url_fixed_schema, indent=2))
 
         self.assertEqual(new_base_host+path+"/user_schema.json", url_fixed_schema['$id'])
-        self.assertEqual(new_base_host+path+"/base_schema.json" + "/#definitions/email", url_fixed_schema['properties']['email']['$ref'])
+        self.assertEqual(new_base_host+path+"/base_schema.json" + "#/definitions/email", url_fixed_schema['properties']['email']['$ref'])
+        self.assertEqual(new_base_host+path+"/base_schema.json" + "#/definitions/email", url_fixed_schema['properties']['other_emails']['items']['$ref'])
         self.assertEqual("http://json-schema.org/draft-06/schema#", url_fixed_schema['$schema'])
         self.assertEqual(json.dumps(schema, indent=2).replace(base_host, new_base_host), json.dumps(url_fixed_schema, indent=2))
 
diff --git a/SAS/TMSS/src/tmss/settings.py b/SAS/TMSS/src/tmss/settings.py
index 8c836ccb568a4ca6975566669be68dc69632e0d8..db1f35dc924fac94922e19dfdd50268214daf5bb 100644
--- a/SAS/TMSS/src/tmss/settings.py
+++ b/SAS/TMSS/src/tmss/settings.py
@@ -70,7 +70,7 @@ LOGGING = {
         },
         'django.server': {
             'handlers': ['django.server'],
-            'level': 'DEBUG',
+            'level': 'INFO',
             'propagate': False,
         },
         'django_auth_ldap': {
diff --git a/SAS/TMSS/src/tmss/tmssapp/models/specification.py b/SAS/TMSS/src/tmss/tmssapp/models/specification.py
index 3dc05281234430e3e3d20546c271de7abcf6a9eb..357a1ae43ab549751af1e48c70c9872e974491e8 100644
--- a/SAS/TMSS/src/tmss/tmssapp/models/specification.py
+++ b/SAS/TMSS/src/tmss/tmssapp/models/specification.py
@@ -301,7 +301,7 @@ class Template(NamedCommon):
     def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
         self.auto_set_version_number()
         self.validate_and_annotate_schema()
-        super().save(force_insert, force_update, using, update_fields)
+        super().save(force_insert or self.pk is None, force_update, using, update_fields)
 
 
 # concrete models
diff --git a/SAS/TMSS/test/ldap_test_service.py b/SAS/TMSS/test/ldap_test_service.py
index a6fd0a68cda0cb59afd8ac951abadcd904c1a647..6cb6921e83745cedcff267a5e139b6669963a7a7 100644
--- a/SAS/TMSS/test/ldap_test_service.py
+++ b/SAS/TMSS/test/ldap_test_service.py
@@ -27,7 +27,7 @@ class TestLDAPServer():
     ''' A helper class which instantiates a running LDAP server (not interfering with any other test/production LDAP servers)
     Best used in a 'with'-context so the server is stoped automagically.
     '''
-    _named_lock = NamedAtomicLock('TestLDAPServer', maxLockAge=60)
+    _named_lock = NamedAtomicLock('TestLDAPServer')
 
     def __init__(self, user: str = 'test', password: str = 'test') -> None:
         self._tmp_creds = TemporaryCredentials(user=user, password=password)
diff --git a/SAS/TMSS/test/t_tmssapp_specification_django_API.py b/SAS/TMSS/test/t_tmssapp_specification_django_API.py
index 7b7db2f1cbf6fc142a0486431db926152ed649f3..593996d6242dde45a3ead1dd6b46f7328eba9c5a 100755
--- a/SAS/TMSS/test/t_tmssapp_specification_django_API.py
+++ b/SAS/TMSS/test/t_tmssapp_specification_django_API.py
@@ -40,7 +40,7 @@ from lofar.sas.tmss.test.tmss_test_data_django_models import *
 
 from django.db.utils import IntegrityError
 from django.core.exceptions import ValidationError
-
+from lofar.sas.tmss.tmss.exceptions import SchemaValidationException
 
 class GeneratorTemplateTest(unittest.TestCase):
     def test_GeneratorTemplate_gets_created_with_correct_creation_timestamp(self):
@@ -131,24 +131,83 @@ class TaskTemplateTest(unittest.TestCase):
         self.assertLess(before, entry.updated_at)
         self.assertGreater(after, entry.updated_at)
 
+    def test_TaskTemplate_incorrect_schema_raises(self):
+        with self.assertRaises(SchemaValidationException):
+            models.TaskTemplate.objects.create(**TaskTemplate_test_data(schema=""))
+
+        with self.assertRaises(SchemaValidationException) as context:
+            models.TaskTemplate.objects.create(**TaskTemplate_test_data(schema={}))
+        self.assertTrue(True)
+
+        with self.assertRaises(SchemaValidationException) as context:
+            schema = minimal_json_schema()
+            del schema['$schema']
+            models.TaskTemplate.objects.create(**TaskTemplate_test_data(schema=schema))
+        self.assertTrue("Missing required properties" in str(context.exception))
+
+        with self.assertRaises(SchemaValidationException) as context:
+            models.TaskTemplate.objects.create(**TaskTemplate_test_data(schema= minimal_json_schema(id="my id with no url")))
+        self.assertTrue("should contain a valid URL" in str(context.exception))
+
+    def test_TaskTemplate_annotated_schema(self):
+        schema = minimal_json_schema()
+        data = TaskTemplate_test_data(schema=schema, name="foo", description="bar")
+        template = models.TaskTemplate.objects.create(**data)
+        self.assertEqual("foo", template.name)
+        self.assertEqual("foo", template.schema['title'])
+        self.assertEqual("bar", template.description)
+        self.assertEqual("bar", template.schema['description'])
+
+
     def test_TaskTemplate_name_version_unique(self):
-        test_data = TaskTemplate_test_data(name=str(uuid.uuid4()))
+        name = str(uuid.uuid4())
+        self.assertEqual(0, models.TaskTemplate.objects.filter(name=name).count())
+        test_data = TaskTemplate_test_data(name=name)
         # save data twice
         entry1 = models.TaskTemplate.objects.create(**test_data)
         entry2 = models.TaskTemplate.objects.create(**test_data)
+        self.assertEqual(2, models.TaskTemplate.objects.filter(name=name).count())
 
         self.assertEqual(1, entry1.version)
         self.assertEqual(2, entry2.version) #version is autoincremented
 
         # try to modify version... should be allowed, cause the template is not used, but should raise IntegrityError (unique constraint)
+        self.assertFalse(entry2.is_used)
         with self.assertRaises(IntegrityError):
             entry2.version = 1
             entry2.save()
         entry2.refresh_from_db()
 
+        # versions still the same?
         self.assertEqual(1, entry1.version)
         self.assertEqual(2, entry2.version)
 
+        # let's use the template in a task
+        models.TaskDraft.objects.create(**TaskDraft_test_data(specifications_template=entry2))
+        self.assertTrue(entry2.is_used)
+
+        # there should still be only 2 templates with this name
+        self.assertEqual(2, models.TaskTemplate.objects.filter(name=name).count())
+
+        # now (try to) modify the template
+        org_pk = entry2.pk
+        org_schema = dict(entry2.schema)
+        new_schema = minimal_json_schema(properties={"new_prop":{"type":"string"}})
+        entry2.schema = new_schema
+        entry2.save()
+        #this should now be a NEW instance
+        self.assertNotEqual(org_pk, entry2.pk)
+        self.assertEqual(3, models.TaskTemplate.objects.filter(name=name).count())
+
+        # lets request the "old" entry2 via name and version, so we can check if it is unchanged
+        entry2 = models.TaskTemplate.objects.get(name=name, version=2)
+        self.assertEqual(org_schema, entry2.schema)
+
+        # instead there should be a new version of the template with the new schema
+        entry3 = models.TaskTemplate.objects.get(name=name, version=3)
+        self.assertEqual(3, entry3.version)
+        self.assertEqual(new_schema, entry3.schema)
+
 
 class TaskRelationSelectionTemplateTest(unittest.TestCase):
     def test_TaskRelationSelectionTemplate_gets_created_with_correct_creation_timestamp(self):
diff --git a/SAS/TMSS/test/tmss_test_data_django_models.py b/SAS/TMSS/test/tmss_test_data_django_models.py
index bf2c1edfa4531f319a6aa426acc5f2734b43d88e..d88e5a66a881ea77aa91efe061047029a38999f6 100644
--- a/SAS/TMSS/test/tmss_test_data_django_models.py
+++ b/SAS/TMSS/test/tmss_test_data_django_models.py
@@ -72,12 +72,15 @@ def SchedulingUnitObservingStrategyTemplate_test_data(name="my_SchedulingUnitObs
             "scheduling_unit_template": scheduling_unit_template,
             "tags": ["TMSS", "TESTING"]}
 
-def TaskTemplate_test_data(name="my TaskTemplate") -> dict:
+def TaskTemplate_test_data(name="my TaskTemplate", description:str=None, schema:dict=None) -> dict:
+    if schema is None:
+        schema = minimal_json_schema(properties={"mykey": {}})
+
     return {"type": models.TaskType.objects.get(value='observation'),
             "validation_code_js":"",
             "name": name,
-            "description": 'My TaskTemplate description',
-            "schema": minimal_json_schema(properties={"mykey": {}}),
+            "description": description or "<no description>",
+            "schema": schema,
             "tags": ["TMSS", "TESTING"]}
 
 def TaskRelationSelectionTemplate_test_data(name="my_TaskRelationSelectionTemplate") -> dict: