-
Notifications
You must be signed in to change notification settings - Fork 1
how to contribute patterns and conventions
Coding patterns the 360 Ghar backend enforces and how to follow them. Most are encoded in CLAUDE.md and AGENTS.md at the repo root and enforced by ruff in CI.
All database operations and services use async/await. Services inject an AsyncSession via FastAPI dependencies and never block on I/O. The service-class pattern is class XService: def __init__(self, db: AsyncSession), with async methods returning ORM models or 3-tuples. Sync helpers (UTC, timezone) live in app/core/utils.py.
Endpoints in app/api/api_v1/endpoints/ are thin controllers: validate input, enforce auth, delegate to app/services/. Business rules belong in services. REST, MCP, and the AI agent all call the same service functions instead of re-implementing them. Complex queries factor out into app/repositories/ (BaseRepository, PropertyRepository, PropertyQueryBuilder).
MCP tool business logic lives in app/mcp/tool_ops/. Both MCP servers (app/mcp/user_server.py, app/mcp/admin/server.py) and the AI agent (app/services/ai_agent/tool_bridge.py) call into tool_ops. Never duplicate tool behavior in the server or the bridge. When adding a new MCP tool, implement the logic in tool_ops/ first, then wire it through both surfaces. Multi-client wrappers in app/mcp/chatgpt/ format responses and bind widgets but still call tool_ops for the actual work.
Four httpx.AsyncClient singletons live in app/core/http.py:
| Client | Timeout | Used by |
|---|---|---|
get_scraper_client() |
30s | Data hub scrapers, jamabandi, gazette |
get_blog_client() |
120s | Perplexity blog, SerpAPI image search |
get_general_client() |
30s | Image downloads, geocoding, image gen |
get_supabase_auth_http_client() |
10s | Supabase Auth JWKS / metadata |
Never create an ephemeral async with httpx.AsyncClient() per request. Use the shared clients and pass a per-request timeout= override when needed.
One AsyncIOScheduler from app/infrastructure/scheduler.py is registered in app/infrastructure/lifespan.py. Four job families attach to it: blog auto-publish, notifications, vector sync, and data hub scraping. Do not create per-module scheduler instances; add jobs via get_scheduler(). When SERVERLESS_ENABLED=True, both DB engines switch to NullPool (no persistent connections), schedulers are skipped, and the cache falls back to in-memory so the app can scale to zero behind PgBouncer. The trade-off is roughly 10-50ms added latency per request; move cron work to Railway cron jobs in that mode.
SSEEventBus in app/core/sse.py provides per-user pub/sub. Services call await sse_bus.emit(user_id, event_dict) after a DB commit, never before. The endpoint GET /api/v1/flatmates/sse consumes from the queue with a 30-second keepalive; the bus drops the oldest event on a full queue. Event types are listed in the glossary; adding a new type requires updating CLAUDE.md and AGENTS.md. Streaming endpoints release the main-pool session before streaming and use the background pool (get_bg_db) for tool calls.
As of June 2026, paginated list endpoints return (items, next_cursor, has_more) tuples instead of bare lists, with keyset cursors on created_at replacing offset pagination. This applies across properties, users, agents, bookings, visits, and blog. New list endpoints should return the 3-tuple and accept a cursor query parameter.
The same property can be booked by multiple people for the same or overlapping dates. Do not add date-overlap conflict checks, double-booking guards, or DB exclusion constraints. check_availability in app/services/booking.py only validates that the property exists and the guest count fits max_occupancy.
Clients authenticate directly with Supabase Auth and send a bearer access token. get_current_user (app/api/api_v1/dependencies/auth.py) verifies the JWT and syncs the local user row. Phone is the primary identifier. There are no /api/v1/auth/* session endpoints; clients own login, refresh, and logout via the Supabase SDK. When Supabase is unreachable, the dep returns PROVIDER_UNREACHABLE and the endpoint responds HTTP 503 with Retry-After: 5.
Ruff runs in CI (uv run ruff check app/) and fails on any violation. Target is Python 3.10, line length 100. Key rules:
-
I001, UP035, F401, E402 —
from __future__ import annotationsas the first import in every.pyfile. Uselist/dict/set/tuple/typenottyping.*. ImportCallable,Awaitable,AsyncIterator,Sequencefromcollections.abc. Remove unused imports. All imports at file top;# noqa: E402only for unavoidable circular imports with a comment. -
UP045, UP006, UP007, UP037 —
X | NonenotOptional[X],X | YnotUnion[X, Y],list[X]notList[X]. Remove unnecessary quotes in annotations. For forward references, addfrom __future__ import annotationsand import the type underTYPE_CHECKING. -
B904 — Always chain exceptions in
except.from ewhen the original aids debugging;from Nonewhen logging the original and raising a user-facing exception. -
E712 — Never
== True/== False. Use the column directly (Model.is_active) or bitwise negation (~Model.is_active,not_(...)). -
B905, C401, F541, F811, E741, F841, W291/W292/W293 —
zip(a, b, strict=True); set comprehensions notset(gen); no placeholder-free f-strings; no redefining imported names; no single-letterl; no unused assignments (use_); no trailing whitespace, clean blank lines, files end with a newline.
Custom exceptions live in app/core/exceptions.py; always chain per B904.
Never delete real user data without explicit confirmation. seed_data/02_clear_data.py filters by WHERE is_seed_data = true on users, agents, and properties, and deletes child records via subquery joins to seed parents. Never run it against production. New seeded models need either an is_seed_data column (server_default=text("false")) or a FK cascade to an existing seeded parent.
Update docs/ and docs/repo-contract.json when adding any public surface: endpoint, service module, MCP tool, widget, scheduler, shared httpx client domain, flatmates/social feature, notification type, AI provider, SSE event type, infrastructure module, seed data, storage bucket, or is_seed_data column.
- Features overview
- Ghar Core (marketplace)
- 360 Stays (bookings)
- 360 Flatmates
- Property Management
- 360 Virtual Tours
- 360 Data Hub
- MCP servers and widgets
- AI agent
- Blog and SEO
- Notifications
- Vastu analyzer