diff --git a/src/api/schedule.py b/src/api/schedule.py
index 104128c13ea107f3e86b6d766b4b7e9985986c6c..734c8bd22f10eb3bd73ef1c60de5cc830b650e1f 100644
--- a/src/api/schedule.py
+++ b/src/api/schedule.py
@@ -109,6 +109,7 @@ class ScheduleEncoder(json.JSONEncoder):
             return {'id': None, 'name': p, 'public_name': p}
 
         if isinstance(p, PlatformUser):
+            # TODO: Update after deciding oh one or more conferences in #648
             member: ConferenceMember = p.conferences.first()  # TODO search for correct conference
             name = p.get_display_name()
 
diff --git a/src/backoffice/views/assemblies/members.py b/src/backoffice/views/assemblies/members.py
index edd2866c2e43003a98651ccfbf672a175146f7a5..5b0e31a976cab65bc9023aa180341c2acd0bafef 100644
--- a/src/backoffice/views/assemblies/members.py
+++ b/src/backoffice/views/assemblies/members.py
@@ -69,6 +69,7 @@ class MemberListView(AssemblyMixin, ListView):
 
             elif k == 'show':
                 m = self.get_queryset().select_related('member', 'assembly').get(member_id=int(v))
+                # TODO: Update after deciding oh one or more conferences in #648
                 if not m.member.conferences.filter(conference_id=m.assembly.conference_id).exists():
                     messages.error(self.request, format_lazy(_('Assembly__members__no_member_cant_display'), user=m.member))
                     continue
diff --git a/src/core/models/assemblies.py b/src/core/models/assemblies.py
index 52d93e044c02a83ccda6023c5bb2db7bf52679b5..c3497ff6e3e1404f2523368483c376ee960a82c7 100644
--- a/src/core/models/assemblies.py
+++ b/src/core/models/assemblies.py
@@ -31,6 +31,7 @@ from core.models.invitation import Invitation
 from core.models.map import MapFloor
 from core.models.tags import TaggedItemMixin, TagItem
 from core.models.users import PlatformUser
+from core.predicates import has_perms
 from core.utils import render_markdown_as_text
 from core.validators import FileSizeValidator, ImageDimensionValidator
 
@@ -56,17 +57,6 @@ def is_habitat_manager(user: PlatformUser, assembly: 'Assembly') -> bool:
     return assembly.parent.user_can_manage(user)
 
 
-def perm_is(permission: str):
-    @rules.predicate
-    def user_has_permission(user, assembly: 'Assembly'):
-        if assembly is None:
-            return False
-        member = ConferenceMember.get_member(conference=assembly.conference, user=user)
-        return member.has_perms(permission, require_all=True, require_staff=True)
-
-    return user_has_permission
-
-
 class AssemblyManager(ConferenceManagerMixin['Assembly']):
     staff_permissions = ['core.assembly_team']
     assembly_filter = 'self'
@@ -95,9 +85,9 @@ class Assembly(TaggedItemMixin, ActivityLogMixin, RulesModel):
         rules_permissions = {
             'view': is_assembly_member,
             'add': is_authenticated,
-            'change': is_assembly_manager | perm_is('core.change_assembly'),
-            'delete': is_assembly_manager | perm_is('core.delete_assembly'),
-            'leave_habitat': is_assembly_manager | is_habitat_manager | perm_is('core.change_assembly'),
+            'change': is_assembly_manager | has_perms('core.change_assembly', require_staff=True),
+            'delete': is_assembly_manager | has_perms('core.delete_assembly', require_staff=True),
+            'leave_habitat': is_assembly_manager | is_habitat_manager | has_perms('core.change_assembly', require_staff=True),
         }
 
     class State(models.TextChoices):
@@ -420,7 +410,7 @@ class Assembly(TaggedItemMixin, ActivityLogMixin, RulesModel):
             self.logger.error('Failed to create a technical user "%s" for assembly <%s> (%s): %s', username, self.slug, self.id, err)
             raise Exception('Technical user for assembly could not be created.')
 
-        cm = u.conferences.create(conference=self.conference)
+        cm = ConferenceMember(conference=self.conference, user=u)
         cm.save()
 
         self.technical_user = u
diff --git a/src/core/models/badges.py b/src/core/models/badges.py
index 1417a2d3a291a61ba4096f63a3cb73812c075261..16b07ac096233797c23ee5949668c2a7c200e6d4 100644
--- a/src/core/models/badges.py
+++ b/src/core/models/badges.py
@@ -339,6 +339,7 @@ class UserBadgeManager(models.Manager):
             defaults={
                 'accepted_by_user': not bool(issuer),
                 'token': token if token else None,
+                # TODO: Update after deciding oh one or more conferences in #648
                 'visibility': user.conferences.filter(conference=badge.conference).first().default_badge_visibility,
             },
         )
diff --git a/src/core/predicates.py b/src/core/predicates.py
new file mode 100644
index 0000000000000000000000000000000000000000..1e605f879f9ad56784b0d4e907c2c1833d400050
--- /dev/null
+++ b/src/core/predicates.py
@@ -0,0 +1,31 @@
+from typing import TYPE_CHECKING, Callable
+
+from rules.predicates import predicate
+
+if TYPE_CHECKING:
+    from src.core.models import PlatformUser
+
+
+def has_perms(
+    *required_permissions: str,
+    require_all: bool = True,
+    require_staff: bool = False,
+) -> Callable[['PlatformUser'], bool]:
+    @predicate
+    def test_team_perms(user: 'PlatformUser') -> bool:
+        # TODO: Update after deciding oh one or more conferences in #648
+        conference_member = user.conferences.first()
+        if conference_member is None:
+            return False
+        return conference_member.has_perms(*required_permissions, require_all=require_all, require_staff=require_staff)
+
+    return test_team_perms
+
+
+@predicate
+def is_conference_staff(user: 'PlatformUser') -> bool:
+    # TODO: Update after deciding oh one or more conferences in #648
+    conference_member = user.conferences.first()
+    if conference_member is None:
+        return False
+    return conference_member.is_authenticated and conference_member.is_staff
diff --git a/src/plainui/views/user_profile.py b/src/plainui/views/user_profile.py
index 12625588bb0fb37fa47823a7e7e521781eeb1585..2ba4f6639f95afdd467d5096e9c27499613882b2 100644
--- a/src/plainui/views/user_profile.py
+++ b/src/plainui/views/user_profile.py
@@ -63,6 +63,7 @@ class ProfileView(ConferenceRequiredMixin, UpdateView):
 
         form2_kwargs = super().get_form_kwargs()
         if hasattr(self, 'object'):
+            # TODO: Update after deciding oh one or more conferences in #648
             cm = self.object.conferences.filter(conference=self.conf).first()
             if cm:
                 form2_kwargs['instance'] = cm
@@ -119,6 +120,7 @@ class ProfileView(ConferenceRequiredMixin, UpdateView):
         form1.instance.timezone = form1.cleaned_data['timezone']
         form1.instance.save()
 
+        # TODO: Update after deciding oh one or more conferences in #648
         cm = form1.instance.conferences.filter(conference=self.conf).first()
         if cm:
             for lang in AVAILABLE_LANGUAGES:
diff --git a/src/plainui/views/users.py b/src/plainui/views/users.py
index 785e052416e2cc1e1ba9bac9141ec256702e2f74..a040f4b48e7e7417b58bd5d70394d3922ec04c36 100644
--- a/src/plainui/views/users.py
+++ b/src/plainui/views/users.py
@@ -33,6 +33,7 @@ class UserView(ConferenceRequiredMixin, TemplateView):
         context = super().get_context_data(**kwargs)
         context['conf'] = self.conf
         context['display_user'] = display_user = get_object_or_404(PlatformUser.objects.filter(slug=user_slug))
+        # TODO: Update after deciding oh one or more conferences in #648
         conference_member = display_user.conferences.filter(conference=self.conf).first()
         context['description_html'] = None if not conference_member else conference_member.description_html
         context['owned_badges'] = owned_badges