# Agent Rules **These rules are MANDATORY and MUST be followed without exception by all agents working on this codebase.** --- ## 1. Entity Versioning MUST Be Automatic > [!CAUTION] > **NON-NEGOTIABLE REQUIREMENT** All versioned entities (Parks, Rides, Companies, etc.) MUST have their version history created **automatically via Django signals**—NEVER manually in views, serializers, or management commands. ### Why This Rule Exists Manual version creation is fragile and error-prone. If any code path modifies a versioned entity without explicitly calling "create version," history is lost forever. Signal-based versioning guarantees that **every single modification** is captured, regardless of where the change originates. ### Required Implementation Pattern ```python # apps/{entity}/signals.py - REQUIRED for all versioned entities from django.db.models.signals import pre_save from django.dispatch import receiver from .models import Park, ParkVersion from .serializers import ParkSerializer @receiver(pre_save, sender=Park) def auto_create_park_version(sender, instance, **kwargs): """ Automatically snapshot the entity BEFORE any modification. This signal fires for ALL save operations, ensuring no history is ever lost. """ if not instance.pk: return # Skip for initial creation try: old_instance = sender.objects.get(pk=instance.pk) ParkVersion.objects.create( park=instance, data=ParkSerializer(old_instance).data, changed_by=getattr(instance, '_changed_by', None), change_summary=getattr(instance, '_change_summary', 'Auto-versioned') ) except sender.DoesNotExist: pass # Edge case: concurrent deletion ``` ### Passing Context to Signals When updating entities, attach metadata to the instance before saving: ```python # In views/serializers - attach context, DON'T create versions manually park._changed_by = request.user park._change_summary = submission_note or "Updated via API" park.save() # Signal handles versioning automatically ``` ### What Is FORBIDDEN The following patterns are **strictly prohibited** and will be flagged as non-compliant: ```python # ❌ FORBIDDEN: Manual version creation in views def update(self, request, slug=None): park = self.get_object() # ... update logic ... ParkVersion.objects.create(park=park, ...) # VIOLATION! park.save() # ❌ FORBIDDEN: Manual version creation in serializers def update(self, instance, validated_data): ParkVersion.objects.create(park=instance, ...) # VIOLATION! return super().update(instance, validated_data) # ❌ FORBIDDEN: Manual version creation in management commands def handle(self, *args, **options): for park in Park.objects.all(): ParkVersion.objects.create(park=park, ...) # VIOLATION! park.status = 'updated' park.save() ``` ### Compliance Checklist For every versioned entity, verify: - [ ] A `pre_save` signal receiver exists in `signals.py` - [ ] The signal is connected in `apps.py` via `ready()` method - [ ] No manual `{Entity}Version.objects.create()` calls exist in views - [ ] No manual `{Entity}Version.objects.create()` calls exist in serializers - [ ] No manual `{Entity}Version.objects.create()` calls exist in management commands --- ## 2. Error Handling MUST Use Capture Utilities > [!CAUTION] > **NON-NEGOTIABLE REQUIREMENT** All error-prone code on both backend AND frontend MUST use the error capture utilities. Errors should flow to the admin dashboard (`/admin/errors`) for monitoring. ### Backend Requirements Use the utilities from `apps.core.utils`: ```python from apps.core.utils import capture_errors, error_context, capture_and_log # ✅ REQUIRED: Decorator on views and critical functions @capture_errors(source='api', severity='high') def create_park(request, data): return ParkService.create(data) # ✅ REQUIRED: Context manager for critical operations with error_context('Processing payment', severity='critical'): process_payment() # ✅ ACCEPTABLE: Manual capture when you need the error ID except Exception as e: error_id = capture_and_log(e, 'Operation failed', severity='high') ``` ### Frontend Requirements Use the composables and utilities: ```typescript // ✅ REQUIRED: Component-level error boundary const { wrap, error, retry } = useErrorBoundary({ componentName: 'RideCard' }) const ride = await wrap(() => api.get('/rides/1'), 'Loading ride') // ✅ REQUIRED: tryCatch for async operations const [data, error] = await tryCatch(fetchData(), 'Fetching data') // ✅ REQUIRED: Report caught errors const { reportError } = useReportError() try { ... } catch (e) { reportError(e, { action: 'Operation' }) } ``` ### What Is FORBIDDEN ```python # ❌ FORBIDDEN: Silent exception swallowing except Exception: pass # VIOLATION! Error lost forever # ❌ FORBIDDEN: Logging without dashboard capture except Exception as e: logger.error(e) # VIOLATION! Not visible in dashboard ``` ```typescript // ❌ FORBIDDEN: Silent catch catch (e) { console.error(e) } // VIOLATION! Not in dashboard // ❌ FORBIDDEN: Uncaptured async errors await riskyOperation() // VIOLATION! No error handling ``` ### Compliance Checklist - [ ] All API views decorated with `@capture_errors` - [ ] All critical service methods use `error_context` - [ ] All frontend async operations use `tryCatch` or `useErrorBoundary` - [ ] No silent exception swallowing (`except: pass`) - [ ] All caught exceptions reported via utilities ### Documentation Full usage guide: [docs/ERROR_HANDLING.md](file:///Volumes/macminissd/Projects/thrillwiki_django_no_react/docs/ERROR_HANDLING.md) --- ## Document Authority This document has the same authority as all other `source_docs/` files. Per the `/comply` workflow, these specifications are **immutable law** and must be enforced immediately upon detection of any violation.