AI Agent Instructions - Reveni Backend
Identity: You are an expert Django 5 / Python 3.13 developer working on a multi-tenant SaaS for e-commerce returns management. Objective: Maintain high code quality, follow strict architectural patterns, and leverage existing tooling.
1. Tech Stack & Structure
Tech Stack
| Layer | Technology |
|---|---|
| Language | Python 3.13 |
| Framework | Django 5.x, Django REST Framework |
| Async | Celery (Workers & Beat), Redis/Valkey |
| Database | PostgreSQL 17, django-safedelete, cacheops |
| Deployment | Docker/Docker Compose, Digital Ocean App Platform |
| Tooling | Poetry, uv, ruff, pre-commit, pytest, mypy |
Directory Structure
apps/
├── accounts/ # Merchants, Stores, Customers
├── orders/ # Orders, Returns, Line Items
├── deliveries/ # Shipping, Carriers, Labels
├── payments/ # Payment gateways (polymorphic)
├── integrations/ # E-commerce connectors (Shopify, Magento...)
├── billing/ # Fees, Invoicing
├── notifications/ # Email, SMS, Webhooks
├── events/ # Event tracking
├── metrics/ # Analytics
├── underwriting/ # Risk assessment
├── core/ # Shared utilities, base models
├── users/ # User roles, permissions
└── api/ # REST API (host-segregated)
├── merchants/ # Dashboard API
├── returns/ # Public Returns Portal
├── customers/ # Customer Dashboard
├── platforms/ # Webhook ingestion
├── public/ # Public API (docs: reveni.readme.io)
└── common/ # Shared mixins, generics
App File Patterns
| File | Purpose |
|---|---|
models.py |
Rich Models with Mixins. Core business logic lives here. |
managers.py |
Query logic, custom QuerySets. NO selectors.py. |
services.py |
Process orchestration (reports, complex workflows). |
connectors.py |
External API integrations. Inherit AbstractConnector. |
helpers.py |
Platform-specific logic (e.g., ReturnsHelper). |
choices.py |
Enums for model fields. Critical for migrations. |
tasks.py or tasks/ |
Celery async jobs. |
signals/ |
Django signals. Receivers in signals.py. |
tests/factories.py |
Factory Boy factories for tests. |
Nested Modules Pattern
Complex apps use modules/ subdirectories:
apps/orders/modules/
├── discounts/ # Discount logic
├── eligibility/ # Eligibility rules
├── inventory/ # Stock management
└── recoveries/ # Recovery workflows
Each module has its own models.py, managers.py, services.py, tests/.
2. Command Center (Makefile)
ALWAYS use these commands. Never run python manage.py directly on host.
| Goal | Command | Notes |
|---|---|---|
| Start Dev | make up |
Starts all services in background |
| Bash Shell | make shell |
Bash inside Django container |
| Django Shell | make sh |
shell_plus with models pre-loaded |
| Run Tests | make test ARGS="path" |
pytest with --reuse-db |
| Lint/Check | make pre-commit |
ruff, mypy. Run before review. |
| Migrations | make migrations |
Creates migrations inside docker |
3. Architectural Patterns
A. Rich Models & Mixins
Models contain business logic, split into mixins for manageability:
class Order(RejectionRulesMixin, WithUnderWritingEngine, AbstractTimeStampedUUID):
# Core fields + business methods
- Helpers: Platform-specific logic via
ReturnsHelper(handles Shopify vs PrestaShop nuances) - Configuration Sync:
Merchantsyncs settings down toStoremodels
B. Integration Layer (apps/integrations/)
AbstractConnectordefines ~20 abstract methods (create_order,calculate_refund)- Each platform (
shopify/,magento/) implements the connector - AI-powered features via
OpenAIConnectorfor address parsing
C. Payment Layer (apps/payments/)
- Polymorphic:
Paymentbase model, gateways are subclasses (StripeCheckoutPayment) - Direct SDK usage: Payments interact directly with vendor SDKs (no separate connector)
- Location:
apps/payments/gateways/[provider]/
D. Delivery Layer (apps/deliveries/)
- Carriers in
apps/deliveries/providers/[name]/ - Rich model in
models.py(handles shipping complexity)
E. API Layer (Host-Segregated)
APIs are organized by consumer, not resource:
| Directory | Host | Consumer |
|---|---|---|
api/merchants/ |
merchants-api | Merchant Dashboard |
api/returns/ |
returns-api | End-customer Portal |
api/customers/ |
customers-api | Customer Dashboard |
api/platforms/ |
platforms-api | Webhook ingestion |
api/public/ |
api | Third-party integrations |
Key Patterns:
- Forms as Validators:
apps/api/*/forms/classes inherit from serializers, act as input validators - Thin Views: Validate Form → delegate to Service/Model
- Context-Specific: Same entity, different validation per host
F. Services Layer
Reserved for process orchestration, not CRUD:
- Inherit
AbstractReportServicefor file processing - Use
@dataclassfor service parameters and DTOs
@dataclass
class ProcessReturnRequest:
order_id: str
items: list[ReturnableLineItem]
reason: str
G. Signals & Events
- Custom signals in
apps/[app]/signals/signals.py - Muted in tests by default via
conftest.py - Enable with
@pytest.mark.enable_signalswhen needed
4. Rules of Engagement (CRITICAL)
A. Migrations & Choices
ALWAYS import choices from choices.py. NEVER inline in migrations.
# CORRECT
from apps.accounts import choices
class Migration(migrations.Migration):
operations = [
migrations.AddField(
model_name="store",
name="status",
field=models.CharField(choices=choices.Merchant.Status.choices, max_length=50),
),
]
# WRONG - causes new migrations on every choice change
field=models.CharField(choices=[("active", "Active"), ("inactive", "Inactive")])
B. Testing Standards
| Rule | Details |
|---|---|
| Engine | pytest + pytest-xdist (parallel) |
| Factories | Use factory_boy in tests/factories.py. NO Model.objects.create() |
| Assertions | Use bare assert x == y. NO self.assertEqual() |
| Naming | Test classes use suffix: OrderTest, ReturnTest. NEVER TestOrder, TestReturn |
| Coverage | Strict 97.5% threshold. Do not break. |
| Signals | Muted by default. Use @pytest.mark.enable_signals when needed |
| Full Suite | Don't run unless told to (tests are slow) |
# Factory example
class StoreFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.Store
merchant = factory.SubFactory(MerchantFactory)
preferences__with_all_allowed_orders = True
C. Form Tests vs View Tests (API Layer)
Test validation logic in form tests, NOT view tests. Forms are DRF serializers that validate input and return typed objects (dataclasses).
| Test Type | Location | What to Test |
|---|---|---|
| Form tests | api/*/forms/tests/ |
Field validation, business rules, error messages, validated_data output |
| View tests | api/*/tests/ |
HTTP status codes, auth, response serialization, side effects |
# CORRECT - Form test (fast, isolated)
# apps/api/merchants/forms/tests/test_orders.py
def test_reject_without_reason(self):
form = self.form(self.ret, {"status": Status.REJECTED}, context=self.context)
assert not form.is_valid()
assert "reject_reason is required" in form.errors["non_field_errors"]
# CORRECT - View test (integration)
# apps/api/customers/tests/test_api.py
def test_ok(self):
response = self.client.post(self.url, data={"tracking_number": "test"})
assert response.status_code == status.HTTP_204_NO_CONTENT
# WRONG - Testing form validation in view test
def test_required_fields(self): # ← Should be in form test!
response = self.client.post(self.url, {})
assert response.json() == {"order_number": ["This field is required."]}
D. Type Safety
- Strict
mypywithmypy_django_pluginandmypy_drf_plugin - Avoid
Any. Use explicit types. - Use built-in generics:
list[str],dict[str, Any] - Use
TYPE_CHECKINGblocks for circular imports
E. Code Style
- Formatting:
ruff - Imports: sorted by
isort(via ruff) - Comments: Only when necessary for complex logic. Keep code self-documenting
- Docstrings: For complex logic in
services.pyandconnectors.py
5. Anti-Patterns (DO NOT)
| Pattern | Why Not | Instead |
|---|---|---|
selectors.py |
Not our pattern | Use Manager methods |
use_cases/ |
No clean architecture | Use services.py |
| Cross-host imports | Violates host segregation | Import from api/common/ only |
| Generic views for complex logic | Hard to maintain | Use APIView + Serializer |
| Hardcoded choices in migrations | Causes migration churn | Import from choices.py |
self.assertEqual() |
Not pytest style | Use bare assert |
TestXXX naming |
Wrong convention | Use XXXTest suffix |
| Excessive comments | Makes code noisy | Comment only when necessary |
| Running full test suite | Very slow | Run specific tests |
Co-Authored-By in commits |
Not wanted | Omit entirely |
| Form validation in view tests | Slow, duplicates coverage | Test in forms/tests/ |
6. Developer Cheatsheet
| If you want to... | Look in... |
|---|---|
| Add a field to Order | apps/orders/models.py (maybe a Mixin) |
| Add Store configuration | apps/accounts/models.py (Merchant & Store) |
| Integrate new e-commerce | apps/integrations/[name]/, implement AbstractConnector |
| Integrate new carrier | apps/deliveries/providers/[name]/ |
| Add API endpoint | apps/api/[host]/views.py AND forms/[name].py |
| Fix complex query | managers.py of relevant model |
| Debug payment error | apps/payments/gateways/[provider]/models.py |
| Add async task | apps/[app]/tasks.py, use @shared_task |
| Add custom signal | apps/[app]/signals/signals.py |
7. Environment & Setup
Required Files
.envs/.local/.django # Local Django env
.envs/.local/.postgres # Local Postgres env
.envs/.tests/.django # CI test env
Hosts File (/etc/hosts)
127.0.0.1 reveni.local api.reveni.local admin.reveni.local platforms.reveni.local customers-api.reveni.local returns-api.reveni.local merchants-api.reveni.local
Celery Notes
- Worker uses
watchfilesfor auto-reload (dev only) - Beat scheduler:
config/celery_scheduler.py - Fix stuck beat:
rm -f ./celerybeat.pid
8. Workflow: New Feature
- Plan: Identify which
apps/are involved - Model: Define data in
models.py(use Mixins if complex) - Migration:
make migrations- CHECK for hardcoded choices - Manager: Add query methods to
managers.pyif needed - Service: Complex logic in
services.py(use@dataclassfor params) - API: Expose via
api/[host]/views.py+forms/[name].py - Test: Write tests with factories, mirroring service logic
- Lint:
make pre-commitbefore requesting review
9. Key Configuration Files
| File | Purpose |
|---|---|
pyproject.toml |
Tool config (pytest, mypy, ruff, coverage) |
.pre-commit-config.yaml |
Pre-commit hooks |
Makefile |
Command interface |
docker/local.yml |
Docker Compose services |
config/celery_scheduler.py |
Celery Beat schedule |
config/hosts.py |
Django-hosts configuration |
conftest.py |
Pytest fixtures, signal muting |