diff --git a/atdb/taskdatabase/migrations/0042_task_is_summary.py b/atdb/taskdatabase/migrations/0042_task_is_summary.py
new file mode 100644
index 0000000000000000000000000000000000000000..eae714e842c607213f3347d6e4666863706a668d
--- /dev/null
+++ b/atdb/taskdatabase/migrations/0042_task_is_summary.py
@@ -0,0 +1,18 @@
+# Generated by Django 5.0 on 2024-02-20 12:28
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('taskdatabase', '0041_alter_task_activity'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='task',
+            name='is_summary',
+            field=models.BooleanField(default=False),
+        ),
+    ]
diff --git a/atdb/taskdatabase/migrations/0043_activity_is_combined_activity_is_validated.py b/atdb/taskdatabase/migrations/0043_activity_is_combined_activity_is_validated.py
new file mode 100644
index 0000000000000000000000000000000000000000..6bbc07c6bf5f265616cb5d994e3acdcffa4a080d
--- /dev/null
+++ b/atdb/taskdatabase/migrations/0043_activity_is_combined_activity_is_validated.py
@@ -0,0 +1,23 @@
+# Generated by Django 5.0 on 2024-02-22 13:27
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('taskdatabase', '0042_task_is_summary'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='activity',
+            name='is_combined',
+            field=models.BooleanField(default=False),
+        ),
+        migrations.AddField(
+            model_name='activity',
+            name='is_validated',
+            field=models.BooleanField(default=False),
+        ),
+    ]
diff --git a/atdb/taskdatabase/models.py b/atdb/taskdatabase/models.py
index 8d9f1100d0f064cbd79f2bc3f73971b3a289a1c1..5f6e51cc8b849143fe3e65ab5b98c4b3e65160b4 100644
--- a/atdb/taskdatabase/models.py
+++ b/atdb/taskdatabase/models.py
@@ -7,7 +7,7 @@ import json
 import logging
 
 from .services import calculated_qualities as qualities
-from .services.common import State, verified_statusses
+from .services.common import State
 
 logger = logging.getLogger(__name__)
 
@@ -111,7 +111,15 @@ class Activity(models.Model):
     # this is the JSON blob that is filled in by the ldv_archiver during the ingest process
     archive = models.JSONField(null=True, blank=True)
 
-    is_verified = models.BooleanField(default=False)
+    # set by update_activity, used by Validation Page
+    is_verified  = models.BooleanField(default=False)
+
+    # TODO: flag set by the 'validate' step in ATDB, used by combine service
+    is_validated = models.BooleanField(default=False)
+
+    # TODO: flag set (and used) by the combine service, so that it doesn't do double work
+    is_combined = models.BooleanField(default=False)
+
     finished_fraction = models.FloatField(blank=True, null=True)
     ingested_fraction = models.FloatField(blank=True, null=True)
     total_size = models.FloatField(blank=True, null=True)
@@ -135,10 +143,29 @@ class Activity(models.Model):
         return str(self.sas_id)
 
 
+def check_if_summary(task):
+    """
+    check if this task is a summary task
+    for backward compatiblity reasons this is done very ugly, by looking if certain filenames contain the substring 'summary'
+    """
+    # look in the outputs.tar_archive
+    try:
+        tars = task.outputs['tar_archive']
+        for tar in tars:
+            if 'summary' in tar['basename']:
+                # a summary tarball was found, this task is a summary task
+                return True
+    except:
+        # no 'tar_archive' was found
+        return False
+
+    return False
+
 class Task(models.Model):
 
     # Task control properties
     task_type = models.CharField(max_length=20, default="regular")
+    is_summary = models.BooleanField(default=False)
     filter = models.CharField(max_length=30, blank=True, null=True)
     environment = models.CharField(max_length=255, blank=True, null=True)
     new_status = models.CharField(max_length=50, default="defining", null=True)
@@ -201,6 +228,15 @@ class Task(models.Model):
             tasks_for_this_sasid = Task.objects.filter(sas_id=self.sas_id)
             self.calculated_qualities = qualities.calculate_qualities(self, tasks_for_this_sasid, quality_thresholds)
 
+        # nv:20feb2024, check if this task is a summary task
+        if (self.status != State.STORED.value) & (self.new_status == State.STORED.value):
+            self.is_summary = check_if_summary(self)
+
+        # nv:20feb2024, same as above, but for backward compatibilty reasons.
+        # For tasks that are already beyond PROCESSED, but not yet ingested.
+        if (self.status != State.VALIDATED.value) & (self.new_status == State.VALIDATED.value):
+            self.is_summary = check_if_summary(self)
+
         # make sure that every task has an activity (also for backward compatibility)
         associate_task_with_activity(self)
 
diff --git a/atdb/taskdatabase/serializers.py b/atdb/taskdatabase/serializers.py
index 3adcc01c29f84f437b30aa2a4de0156ad8fdb6ca..f80c4eb13fcfc3e9c0d7ddf93963f69114b2da89 100644
--- a/atdb/taskdatabase/serializers.py
+++ b/atdb/taskdatabase/serializers.py
@@ -97,7 +97,7 @@ class TaskReadSerializer(serializers.ModelSerializer):
 
     class Meta:
         model = Task
-        fields = ['id','task_type','creationTime','filter',
+        fields = ['id','task_type','is_summary','creationTime','filter',
                   'predecessor','predecessor_status','successors',
                   'joined_input_tasks','joined_output_task','joined_status',
                   'project','sas_id','priority','purge_policy','cleanup_policy','resume',
diff --git a/atdb/taskdatabase/services/algorithms.py b/atdb/taskdatabase/services/algorithms.py
index 42536608ddb2b12a276ba820009adedce6bf2861..23651ae0db817fc212fddd3b82bdae281881c78e 100644
--- a/atdb/taskdatabase/services/algorithms.py
+++ b/atdb/taskdatabase/services/algorithms.py
@@ -1144,4 +1144,4 @@ def construct_summary(task):
     elif summary_flavour == SummaryFlavour.LINC_TARGET.value:
         html = construct_linc_summary(task)
 
-    return html
\ No newline at end of file
+    return html
diff --git a/atdb/taskdatabase/templates/taskdatabase/index.html b/atdb/taskdatabase/templates/taskdatabase/index.html
index fc4fe7f33da131e313a1b83810dfa786f4e32ff6..35a46bfd0f2ad11b1652b01fa2cdb834d8545a12 100644
--- a/atdb/taskdatabase/templates/taskdatabase/index.html
+++ b/atdb/taskdatabase/templates/taskdatabase/index.html
@@ -31,7 +31,7 @@
             {% include 'taskdatabase/pagination.html' %}
         </div>
     </div>
-    <p class="footer"> Version 20 Feb 2024
+    <p class="footer"> Version 22 Feb 2024
 </div>
 
 {% include 'taskdatabase/refresh.html' %}
diff --git a/atdb/taskdatabase/tests/test_activities_associate.py b/atdb/taskdatabase/tests/test_activities_associate.py
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/atdb/taskdatabase/tests/test_summary_tasks.py b/atdb/taskdatabase/tests/test_summary_tasks.py
new file mode 100644
index 0000000000000000000000000000000000000000..bf33c9f75d68202b5ecfec0e0e801e81c50a1f40
--- /dev/null
+++ b/atdb/taskdatabase/tests/test_summary_tasks.py
@@ -0,0 +1,42 @@
+from django.test import TestCase
+import json
+from taskdatabase.models import Task, Workflow, Activity
+from taskdatabase.services.common import State
+class TestSummaryTasks(TestCase):
+    def setUp(self):
+        """
+        initialize test data
+        """
+        self.workflow_requantisation = Workflow(id=22, workflow_uri="psrfits_requantisation")
+        self.workflow_requantisation.save()
+
+        self.no_summary_task = Task.objects.create(sas_id=77777, new_status=State.DEFINED.value, workflow=self.workflow_requantisation,
+                                         outputs={"tar_archive": [{"size": 4885985280, "basename": "L621240_SAP002_B073_P000_bf.tar", "nameroot": "L621240_SAP002_B073_P000_bf"}]})
+        self.summary_task_defined = Task.objects.create(sas_id=77777, new_status=State.DEFINED.value, workflow=self.workflow_requantisation,
+                                         outputs={"tar_archive": [{"size": 4885985280, "basename": "L185619_summaryCS.tar", "nameroot": "L185619_summaryCS"}]})
+        self.summary_task_stored = Task.objects.create(sas_id=77777, new_status=State.STORED.value, workflow=self.workflow_requantisation,
+                                         outputs={"tar_archive": [{"size": 4885985280, "basename": "L185619_summaryCS.tar", "nameroot": "L185619_summaryCS"}]})
+
+    def test_no_summary_task(self):
+        """
+        test task that is not a summary task
+        """
+
+        actual = self.no_summary_task.is_summary
+        self.assertEqual(actual, False)
+
+    def test_summary_task_defined(self):
+        """
+        test summary task, but before it knows that it becomes a summary task (which it only knows when 'processed')
+        """
+
+        actual = self.summary_task_defined.is_summary
+        self.assertEqual(actual, False)
+
+    def test_summary_task_stored(self):
+        """
+        test summary task, at 'stored' it should know that it is a summary task and return True)
+        """
+        self.summary_task_stored.save()
+        actual = self.summary_task_stored.is_summary
+        self.assertEqual(actual, True)
diff --git a/atdb/taskdatabase/views.py b/atdb/taskdatabase/views.py
index eb462904c64a075019df95d1337a09913418f55d..0ff77a34177461537ab5f09977cd1e8685835af8 100644
--- a/atdb/taskdatabase/views.py
+++ b/atdb/taskdatabase/views.py
@@ -57,6 +57,7 @@ class TaskFilter(filters.FilterSet):
 
         fields = {
             'task_type': ['exact', 'icontains', 'in'],
+            'is_summary': ['exact'],
             'creationTime': ['icontains'],
             'filter': ['exact', 'icontains'],
             'workflow__id': ['exact', 'icontains'],