Skip to content

Feat/infrastructure refactor#13

Merged
saksham1991999 merged 6 commits into
mainfrom
feat/infrastructure-refactor
May 13, 2026
Merged

Feat/infrastructure refactor#13
saksham1991999 merged 6 commits into
mainfrom
feat/infrastructure-refactor

Conversation

@saksham1991999
Copy link
Copy Markdown
Contributor

@saksham1991999 saksham1991999 commented May 13, 2026


Open in Devin Review

Summary by cubic

Refactored the app factory into app/infrastructure and standardized config under app.config to simplify wiring and enable serverless scale‑to‑zero. Adds Design Studio image generation, blog SEO fields with a viral publisher flow, structured MCP responses with stricter CORS, flatmates admin moderation, cleaner SSE streaming, and modernized type hints.

  • New Features

    • Serverless mode: NullPool for Postgres and in‑memory cache when SERVERLESS_ENABLED=true; graceful scheduler shutdown.
    • Lightweight SSE event bus and safer streaming (release main DB session; use background pool).
    • Design Studio: image generation endpoint using Gemini 3 Pro Image Preview.
    • MCP consistency: wrap tool success with MCPResponse.success(), add structured error codes, code‑based error dispatch; validate CORS origins.
    • Flatmates admin moderation API (app/api/api_v1/endpoints/flatmates_admin.py) with listing/report actions.
    • Blog SEO: new fields (meta title/description, schema, sources), auto‑compute helpers, viral-blog-publisher skill, and publish_blog.py for direct inserts.
    • PM tools for ChatGPT split into focused modules (pm_dashboard_tools, pm_lease_tools, pm_rent_tools, pm_maintenance_tools, pm_tenant_tools) with widget mapping.
    • Refactor/cleanup: Python 3.10+ type hints and ruff lint fixes across endpoints, MCP, and core (no behavior change).
  • Migration

    • Import settings from app.config (e.g., from app.config import settings); legacy app.core.config imports were updated.
    • Environment selection via ENVIRONMENT.env.dev|.env.test|.env.prod. Updated AI vars: new AI_AGENT_* and GROQ_*; defaults set to GLM_MODEL=glm-5v-turbo and GEMINI_MODEL=gemini-3.1-flash-lite-preview.
    • Seed script path changed: uv run python seed_data/01_load_all.py (replace old populate_data commands).
    • Optional: enable serverless behavior by setting SERVERLESS_ENABLED=true to avoid persistent DB/cache connections.

Written for commit acef90d. Summary will update on new commits.

Extract app/infrastructure/ (lifespan, middleware, routing, MCP setup,
request context, errors), app/config/ (settings, constants), and
app/modules/ packages. Split monolithic pm_tools.py into per-domain
files (owner, tenant, lease, rent, maintenance, dashboard). Add
flatmates admin endpoints and schemas. Expand AI provider resilience
with retry backoff and SSE helper. Add social enum constraints
migration and comprehensive MCP user-tools tests.
…ler shutdown, MCP fixes, tests

- Convert SSE bus emit() to async across flatmates, visits, share preview
- Release main-pool DB session before streaming in agent_chat; use AsyncSessionLocalBG
- Use public shutdown APIs for schedulers instead of private _scheduler attr
- Add exc_info=True to scheduler error logs for full tracebacks
- Fix MCP lease tools: status filter, rent payment params, backward-compat message key
- Add lease termination fields + migration
- Escape redirect URL in tour share preview for JS safety
- Remove defensive getattr patterns in visits endpoint
- Add config_overrides ignored warning in AI provider cache
- Add process_result_value tests for EnumStringType
- Update repo-contract.json and ADRs
…e .gitignore

- Add blog SEO auto-compute fields (meta_title, meta_description, etc.)
- Refactor AI agent service with improved tool orchestration
- Remove obsolete populate_data scripts and seed_flatmates scripts
- Update .gitignore for .playwright-mcp, supabase/.temp, local screenshots
- Add supabase migrations for blog SEO and property location auto-populate
- Add blog image acquisition script and viral-blog-publisher skill
- Add blog model and schema unit tests
- Update config, SSE, MCP utils, PM authz across codebase
…isons, agent cleanup, migration fixes

- SSE: replace immediate reaping of full queues with a configurable
  full-cycle threshold before eviction
- Rent status: query each unpaid status separately instead of passing
  a list to list_rent_charges
- Conversations: use enum members directly instead of .value for
  status/source comparisons and assignments
- AI agent: remove unused agent cache and partial_text from
  _AgentRunError, emit conversation_info only on first fallback
- MCP servers: type _require_auth as NoReturn
- Migrations: validate enum constraints, fix trigger function
  volatility to VOLATILE
- Models: add PaymentMethod enum, fix EnumStringType fallback to
  return raw string
- Tests: update rent status tests for per-status query pattern
- Cleanup: remove unused imports and variables
@chatgpt-codex-connector
Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 13, 2026

Important

Review skipped

Too many files!

This PR contains 300 files, which is 150 over the limit of 150.

To get a review, narrow the scope:
• coderabbit review --type committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6a0e7000-4563-46d1-a9bd-8123a7323c30

📥 Commits

Reviewing files that changed from the base of the PR and between e47f817 and acef90d.

📒 Files selected for processing (300)
  • .env.example
  • .factory/skills/qa-backend/SKILL.md
  • .factory/skills/viral-blog-publisher/SKILL.md
  • .factory/skills/viral-blog-publisher/references/seo-playbook.md
  • .factory/skills/viral-blog-publisher/references/writing-style.md
  • .factory/skills/viral-blog-publisher/scripts/publish_blog.py
  • .gitignore
  • AGENTS.md
  • CLAUDE.md
  • README.md
  • app/api/api_v1/api.py
  • app/api/api_v1/dependencies/auth.py
  • app/api/api_v1/endpoints/agent_chat.py
  • app/api/api_v1/endpoints/agents.py
  • app/api/api_v1/endpoints/ai.py
  • app/api/api_v1/endpoints/amenities.py
  • app/api/api_v1/endpoints/blog.py
  • app/api/api_v1/endpoints/bookings.py
  • app/api/api_v1/endpoints/core.py
  • app/api/api_v1/endpoints/custom_domains.py
  • app/api/api_v1/endpoints/data_hub/bank_auctions.py
  • app/api/api_v1/endpoints/data_hub/circle_rates.py
  • app/api/api_v1/endpoints/data_hub/neighbourhood.py
  • app/api/api_v1/endpoints/data_hub/registry.py
  • app/api/api_v1/endpoints/data_hub/rera.py
  • app/api/api_v1/endpoints/design_studio.py
  • app/api/api_v1/endpoints/flatmates.py
  • app/api/api_v1/endpoints/flatmates_admin.py
  • app/api/api_v1/endpoints/floor_plans.py
  • app/api/api_v1/endpoints/notifications.py
  • app/api/api_v1/endpoints/oauth/authorization.py
  • app/api/api_v1/endpoints/oauth/discovery.py
  • app/api/api_v1/endpoints/oauth/helpers.py
  • app/api/api_v1/endpoints/oauth/pkce.py
  • app/api/api_v1/endpoints/oauth/registration.py
  • app/api/api_v1/endpoints/oauth/token.py
  • app/api/api_v1/endpoints/pm_applications.py
  • app/api/api_v1/endpoints/pm_assignments.py
  • app/api/api_v1/endpoints/pm_dashboard.py
  • app/api/api_v1/endpoints/pm_documents.py
  • app/api/api_v1/endpoints/pm_expenses.py
  • app/api/api_v1/endpoints/pm_inspections.py
  • app/api/api_v1/endpoints/pm_leases.py
  • app/api/api_v1/endpoints/pm_maintenance.py
  • app/api/api_v1/endpoints/pm_properties.py
  • app/api/api_v1/endpoints/pm_rent.py
  • app/api/api_v1/endpoints/pm_reports.py
  • app/api/api_v1/endpoints/pm_tenants.py
  • app/api/api_v1/endpoints/public.py
  • app/api/api_v1/endpoints/scenes.py
  • app/api/api_v1/endpoints/swipes.py
  • app/api/api_v1/endpoints/tours.py
  • app/api/api_v1/endpoints/upload.py
  • app/api/api_v1/endpoints/users.py
  • app/api/api_v1/endpoints/vastu.py
  • app/api/api_v1/endpoints/visits.py
  • app/api/api_v1/endpoints/websocket.py
  • app/api/share.py
  • app/config/__init__.py
  • app/config/constants.py
  • app/config/settings.py
  • app/core/auth.py
  • app/core/cache/__init__.py
  • app/core/cache/backends/memory.py
  • app/core/cache/backends/redis.py
  • app/core/cache/decorators.py
  • app/core/cache/interface.py
  • app/core/cache/keys.py
  • app/core/cache/manager.py
  • app/core/config.py
  • app/core/constants.py
  • app/core/constants/__init__.py
  • app/core/database.py
  • app/core/db_resilience.py
  • app/core/exceptions.py
  • app/core/logging.py
  • app/core/sse.py
  • app/core/utils.py
  • app/core/websocket.py
  • app/factory.py
  • app/infrastructure/__init__.py
  • app/infrastructure/errors.py
  • app/infrastructure/lifespan.py
  • app/infrastructure/mcp.py
  • app/infrastructure/middleware.py
  • app/infrastructure/request_context.py
  • app/infrastructure/routing.py
  • app/main.py
  • app/mcp/__init__.py
  • app/mcp/admin/admin.py
  • app/mcp/admin/agent.py
  • app/mcp/admin/agent_tools/bookings.py
  • app/mcp/admin/agent_tools/common.py
  • app/mcp/admin/agent_tools/dashboard.py
  • app/mcp/admin/agent_tools/leases.py
  • app/mcp/admin/agent_tools/maintenance.py
  • app/mcp/admin/agent_tools/properties.py
  • app/mcp/admin/agent_tools/rent.py
  • app/mcp/admin/server.py
  • app/mcp/apps_sdk.py
  • app/mcp/auth_provider.py
  • app/mcp/chatgpt/__init__.py
  • app/mcp/chatgpt/discovery_tools.py
  • app/mcp/chatgpt/pm_dashboard_tools.py
  • app/mcp/chatgpt/pm_lease_tools.py
  • app/mcp/chatgpt/pm_maintenance_tools.py
  • app/mcp/chatgpt/pm_owner_tools.py
  • app/mcp/chatgpt/pm_rent_tools.py
  • app/mcp/chatgpt/pm_shared.py
  • app/mcp/chatgpt/pm_tenant_tools.py
  • app/mcp/chatgpt/pm_tools.py
  • app/mcp/chatgpt/response_formatter.py
  • app/mcp/chatgpt/visit_tools.py
  • app/mcp/errors.py
  • app/mcp/tool_ops/__init__.py
  • app/mcp/tool_ops/bookings.py
  • app/mcp/tool_ops/dashboard.py
  • app/mcp/tool_ops/leases.py
  • app/mcp/tool_ops/maintenance.py
  • app/mcp/tool_ops/properties.py
  • app/mcp/tool_ops/rent.py
  • app/mcp/user/booking.py
  • app/mcp/user/owner.py
  • app/mcp/user/server.py
  • app/mcp/user/system.py
  • app/mcp/user/tenant.py
  • app/mcp/utils.py
  • app/middleware/security.py
  • app/middleware/trailing_slash.py
  • app/models/agents.py
  • app/models/ai_conversations.py
  • app/models/blogs.py
  • app/models/bookings.py
  • app/models/core.py
  • app/models/data_hub.py
  • app/models/enums.py
  • app/models/pm_documents.py
  • app/models/pm_finance.py
  • app/models/pm_inspections.py
  • app/models/pm_leases.py
  • app/models/pm_maintenance.py
  • app/models/pm_tenants.py
  • app/models/properties.py
  • app/models/social.py
  • app/models/tours.py
  • app/models/users.py
  • app/modules/__init__.py
  • app/repositories/base.py
  • app/repositories/property_query_builder.py
  • app/repositories/property_repository.py
  • app/schemas/agent.py
  • app/schemas/amenity.py
  • app/schemas/blog.py
  • app/schemas/booking.py
  • app/schemas/common.py
  • app/schemas/core.py
  • app/schemas/custom_domain.py
  • app/schemas/data_hub.py
  • app/schemas/flatmates.py
  • app/schemas/flatmates_admin.py
  • app/schemas/pm_application.py
  • app/schemas/pm_assignment.py
  • app/schemas/pm_dashboard.py
  • app/schemas/pm_document.py
  • app/schemas/pm_expense.py
  • app/schemas/pm_inspection.py
  • app/schemas/pm_lease.py
  • app/schemas/pm_maintenance.py
  • app/schemas/pm_property.py
  • app/schemas/pm_rent.py
  • app/schemas/pm_report.py
  • app/schemas/pm_tenant.py
  • app/schemas/storage.py
  • app/schemas/tour.py
  • app/schemas/user.py
  • app/schemas/visit.py
  • app/services/agent/__init__.py
  • app/services/agent/analytics.py
  • app/services/agent/crud.py
  • app/services/agent/helpers.py
  • app/services/agent/interactions.py
  • app/services/ai/__init__.py
  • app/services/ai/base.py
  • app/services/ai/image_gen/__init__.py
  • app/services/ai/image_gen/schemas.py
  • app/services/ai/image_gen/service.py
  • app/services/ai/providers/gemini.py
  • app/services/ai/providers/glm.py
  • app/services/ai/vastu/__init__.py
  • app/services/ai/vastu/analyzer.py
  • app/services/ai/vastu/prompts.py
  • app/services/ai/vastu/schemas.py
  • app/services/ai_agent/agent_service.py
  • app/services/ai_agent/conversation_store.py
  • app/services/ai_agent/tools/booking.py
  • app/services/ai_agent/tools/discovery.py
  • app/services/ai_agent/tools/helpers.py
  • app/services/ai_agent/tools/owner.py
  • app/services/ai_agent/tools/tenant.py
  • app/services/blog.py
  • app/services/blog_auto_publish.py
  • app/services/blog_auto_publish_scheduler.py
  • app/services/blog_service/generator.py
  • app/services/booking.py
  • app/services/core.py
  • app/services/custom_domain.py
  • app/services/data_hub/__init__.py
  • app/services/data_hub/aggregator_eauctions.py
  • app/services/data_hub/aggregator_misc.py
  • app/services/data_hub/alerts.py
  • app/services/data_hub/baanknet_auctions.py
  • app/services/data_hub/bank_auctions.py
  • app/services/data_hub/bank_rates.py
  • app/services/data_hub/bank_specific_auctions.py
  • app/services/data_hub/base_scraper.py
  • app/services/data_hub/circle_rates.py
  • app/services/data_hub/court_auctions.py
  • app/services/data_hub/dda_auctions.py
  • app/services/data_hub/dfc_delhi_auctions.py
  • app/services/data_hub/drt_auctions.py
  • app/services/data_hub/gazette.py
  • app/services/data_hub/hsvp_auctions.py
  • app/services/data_hub/hsvp_procure247_auctions.py
  • app/services/data_hub/ibbi_auctions.py
  • app/services/data_hub/jamabandi.py
  • app/services/data_hub/mda_auctions.py
  • app/services/data_hub/neighbourhood.py
  • app/services/data_hub/rera_complaints.py
  • app/services/data_hub/rera_projects.py
  • app/services/data_hub/utils.py
  • app/services/data_hub/yeida_auctions.py
  • app/services/data_hub/zoning.py
  • app/services/data_hub_scheduler.py
  • app/services/email.py
  • app/services/flatmates/__init__.py
  • app/services/flatmates/conversations.py
  • app/services/flatmates/helpers.py
  • app/services/flatmates/interactions.py
  • app/services/flatmates/matching.py
  • app/services/flatmates/moderation.py
  • app/services/flatmates/visits.py
  • app/services/image_processing.py
  • app/services/notification_dispatcher.py
  • app/services/notification_scheduler.py
  • app/services/notifications/crud.py
  • app/services/notifications/fcm.py
  • app/services/notifications/helpers.py
  • app/services/notifications/push.py
  • app/services/oauth_token_store.py
  • app/services/pm_applications.py
  • app/services/pm_assignments.py
  • app/services/pm_authz.py
  • app/services/pm_dashboard.py
  • app/services/pm_documents.py
  • app/services/pm_expenses.py
  • app/services/pm_inspections.py
  • app/services/pm_leases.py
  • app/services/pm_maintenance.py
  • app/services/pm_properties.py
  • app/services/pm_rent.py
  • app/services/pm_reports.py
  • app/services/pm_tenants.py
  • app/services/property/crud.py
  • app/services/property/search.py
  • app/services/push_notification.py
  • app/services/sms.py
  • app/services/storage/helpers.py
  • app/services/storage/processing.py
  • app/services/storage/service.py
  • app/services/storage_paths.py
  • app/services/swipe.py
  • app/services/tour/analytics.py
  • app/services/tour/floor_plans.py
  • app/services/tour/helpers.py
  • app/services/tour/hotspots.py
  • app/services/tour/scenes.py
  • app/services/tour/tours.py
  • app/services/tour_ai/background.py
  • app/services/tour_ai/helpers.py
  • app/services/tour_ai/hotspot_suggestions.py
  • app/services/tour_ai/scene_analysis.py
  • app/services/user.py
  • app/services/vector_sync_scheduler.py
  • app/services/visit.py
  • app/shared/__init__.py
  • app/utils/__init__.py
  • app/utils/distance.py
  • app/utils/validators.py
  • app/vector/backfill.py
  • app/vector/compose.py
  • app/vector/embedding_client.py
  • app/vector/store.py
  • app/vector/sync.py
  • docker-compose.yml
  • docs/adrs/001-domain-module-structure.md
  • docs/adrs/002-repository-protocol-interfaces.md
  • docs/adrs/003-event-driven-side-effects.md
  • docs/adrs/004-external-service-adapters.md
  • docs/architecture-contract.md
  • docs/contribution-contract.md

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/infrastructure-refactor

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@qodo-code-review
Copy link
Copy Markdown

Review Summary by Qodo

Infrastructure refactor with admin moderation, PM tools consolidation, and AI provider fallback chain

✨ Enhancement 🧪 Tests

Grey Divider

Walkthroughs

Description
  **Major infrastructure refactoring with comprehensive feature additions and improvements:**
• **Infrastructure Modularization**: Extracted application factory concerns into dedicated modules
  (infrastructure.lifespan, infrastructure.middleware, infrastructure.errors,
  infrastructure.mcp, infrastructure.routing) for better separation of concerns and
  maintainability
• **Admin Moderation System**: Added comprehensive flatmates admin endpoints for listing
  approval/rejection workflows, user report moderation (suspension, warning, dismissal), and
  pre-screening with SSE real-time event support
• **Property Management Tools Expansion**: Split and refactored PM tools into specialized modules
  (pm_lease_tools, pm_rent_tools, pm_maintenance_tools, pm_tenant_tools) with centralized
  tool_ops layer for consistent error handling and response formatting
• **MCP User Tools Refactoring**: Migrated owner, booking, and tenant tools to use centralized
  tool_ops module, improving code reusability and reducing duplication
• **AI Provider Fallback Chain**: Implemented multi-provider fallback strategy (GLM → Gemini → Groq)
  with streaming refactor for robust AI agent operations
• **Authorization Centralization**: Extracted inline authorization logic into pm_authz service
  module for bookings and visits, reducing code duplication across endpoints
• **Database & Connection Pooling**: Added serverless mode support with NullPool, background pool
  sessions for long-running operations, and optimized connection lifecycle management
• **Blog System Enhancements**: Added SEO analytics (auto-generated titles, descriptions, reading
  time, word count), source citations, and blog publishing/acquisition scripts with image recovery
• **Image Processing Optimization**: Refactored to single-open pattern for thumbnails, WebP
  conversion, and metadata extraction; fixed deprecated PIL methods
• **Enum Type Safety**: Implemented EnumStringType SQLAlchemy decorator with database-level check
  constraints for type-safe enum storage
• **Conversation & Matching**: Added SSE event emissions for messages and conversation updates, Q&A
  persistence with user match creation
• **Configuration Updates**: Environment-based .env file selection, AI provider fallback
  configuration, image API keys (Pixabay, Pexels), serverless deployment flags
• **Security & Rate Limiting**: Added per-IP rate limiting for tour interactions, JSON escaping for
  redirect URLs, enhanced authentication logging
• **Comprehensive Test Coverage**: New unit tests for MCP user tools, flatmates admin endpoints,
  blog schemas, and social models with auth/error handling validation
• **Type Hint Modernization**: Updated codebase to PEP 585 style (dict/list instead of
  Dict/List, T | None instead of Optional[T])
Diagram
flowchart LR
  A["Factory"] -->|"extract concerns"| B["Infrastructure<br/>Modules"]
  B --> B1["lifespan"]
  B --> B2["middleware"]
  B --> B3["errors"]
  B --> B4["mcp"]
  B --> B5["routing"]
  
  C["PM Tools"] -->|"split & centralize"| D["tool_ops<br/>Layer"]
  D --> D1["lease_tools"]
  D --> D2["rent_tools"]
  D --> D3["maintenance_tools"]
  D --> D4["tenant_tools"]
  
  E["AI Agents"] -->|"implement fallback"| F["GLM → Gemini → Groq"]
  
  G["Endpoints"] -->|"centralize authz"| H["pm_authz<br/>Service"]
  
  I["Database"] -->|"optimize pooling"| J["Serverless +<br/>Background Pool"]
  
  K["Blog System"] -->|"enhance"| L["SEO Analytics +<br/>Image Acquisition"]
Loading

Grey Divider

File Changes

1. tests/unit/mcp/test_user_tools.py 🧪 Tests +962/-0

MCP user tools unit test suite with auth and error handling

• New comprehensive unit test suite for MCP user tools covering owner, booking, tenant, and system
 tools
• Tests verify correct delegation to tool_ops functions, proper argument passing, and MCPResponse
 formatting
• Includes authentication error handling, not-found errors, validation errors, and permission checks
• Mocks database sessions, user resolution, and tool operations throughout

tests/unit/mcp/test_user_tools.py


2. tests/unit/api/test_flatmates_admin.py 🧪 Tests +861/-0

Flatmates admin moderation endpoints authorization and logic tests

• New unit test suite for flatmates admin moderation endpoints with 861 lines of test coverage
• Tests authorization (403 for non-admin users) on all moderation endpoints
• Covers listing moderation actions (approve, reject, request_edit) with SSE event emission
• Tests report moderation actions (dismiss, warn_user, suspend_user, escalate) and pagination

tests/unit/api/test_flatmates_admin.py


3. scripts/blog_image_acquisition.py ✨ Enhancement +573/-0

Blog cover image acquisition and recovery script

• New script for recovering and acquiring blog cover images from hotlinks and external APIs
• Phase 1: Downloads existing hotlinked images and re-uploads to Supabase Storage
• Phase 2: Acquires new images from Pixabay/Pexels APIs with intelligent search query generation
• Includes image validation, deduplication, rate limiting, and database updates

scripts/blog_image_acquisition.py


View more (140)
4. app/api/api_v1/endpoints/flatmates.py ✨ Enhancement +42/-573

Refactor flatmates endpoints to separate admin moderation logic

• Refactored to remove inline moderation logic and helper functions
• Extracted moderation endpoints and helpers to separate flatmates_admin.py module
• Added new SSE endpoint for real-time flatmates events with keep-alive support
• Simplified endpoint to delegate to flatmates_ops service functions

app/api/api_v1/endpoints/flatmates.py


5. app/factory.py ✨ Enhancement +14/-395

Infrastructure refactor: extract factory concerns into modules

• Refactored application factory to extract infrastructure concerns into separate modules
• Moved lifespan management to infrastructure.lifespan module
• Moved middleware registration to infrastructure.middleware module
• Moved exception handlers to infrastructure.errors module
• Moved MCP app building to infrastructure.mcp module
• Moved routing registration to infrastructure.routing module

app/factory.py


6. app/api/api_v1/endpoints/oauth/discovery.py ⚙️ Configuration changes +1/-1

Update settings import path in OAuth discovery

• Updated import path for settings from app.core.config to app.config

app/api/api_v1/endpoints/oauth/discovery.py


7. app/mcp/user/owner.py ✨ Enhancement +96/-228

Refactor property management tools to use centralized tool operations

• Refactored to use new app.mcp.tool_ops module for property operations instead of direct service
 calls
• Updated type hints from Optional[T] to T | None and Dict/List to dict/list (PEP 585
 style)
• Simplified error handling by delegating business logic to centralized tool operation functions
• Removed inline validation and data transformation code, now handled by tool_ops layer

app/mcp/user/owner.py


8. app/mcp/user/tenant.py ✨ Enhancement +71/-238

Refactor tenant tools to use centralized tool operations module

• Migrated to new app.mcp.tool_ops module for tenant operations (lease, rent, maintenance)
• Updated type hints to modern Python style (str | None instead of Optional[str])
• Simplified error handling and response formatting using centralized functions
• Removed inline database queries and business logic, delegated to service layer

app/mcp/user/tenant.py


9. app/api/api_v1/endpoints/flatmates_admin.py ✨ Enhancement +371/-0

Add admin moderation endpoints for flatmates listings and reports

• New admin moderation endpoints for flatmates listings and user reports
• Implements listing approval/rejection workflow with boost notifications
• Provides user report moderation with suspension, warning, and dismissal actions
• Includes pre-screening endpoint for AI-based listing validation

app/api/api_v1/endpoints/flatmates_admin.py


10. app/services/ai_agent/agent_service.py ✨ Enhancement +151/-75

Implement AI agent provider fallback chain with streaming refactor

• Implemented fallback chain for AI providers (GLM → Gemini → Groq)
• Refactored streaming logic into _run_agent_stream() for reusability across fallback attempts
• Added _AgentRunError exception for internal error handling during fallback
• Updated configuration to support multiple provider API keys and base URLs

app/services/ai_agent/agent_service.py


11. app/mcp/user/booking.py ✨ Enhancement +92/-141

Refactor booking tools to use centralized tool operations

• Migrated to new app.mcp.tool_ops module for booking operations
• Updated type hints to modern Python style (PEP 585)
• Removed inline date parsing and validation, delegated to tool operations
• Simplified error handling with centralized response formatting

app/mcp/user/booking.py


12. app/mcp/chatgpt/pm_rent_tools.py ✨ Enhancement +311/-0

Add ChatGPT rent management tools for property owners

• New ChatGPT tools for owner rent management (status, payment recording, history)
• Implements rent charge listing with filtering by status (pending, partial, overdue)
• Provides payment recording with validation and Supabase storage integration
• Includes widget metadata for UI linkage and push notifications

app/mcp/chatgpt/pm_rent_tools.py


13. app/mcp/chatgpt/pm_maintenance_tools.py ✨ Enhancement +290/-0

Add ChatGPT maintenance management tools for property owners

• New ChatGPT tools for owner maintenance request management
• Implements listing with filtering by status, priority, and property
• Provides update functionality for status, scheduling, costs, and resolution notes
• Includes widget metadata and comprehensive error handling

app/mcp/chatgpt/pm_maintenance_tools.py


14. app/services/image_processing.py ✨ Enhancement +117/-31

Optimize image processing with single-open pattern and modern type hints

• Updated type hints to modern Python style (PEP 585)
• Refactored get_image_info() to accept either raw bytes or pre-opened Pillow Image to avoid
 redundant opens
• Optimized process_scene_image() to open image once and reuse for thumbnail, web conversion, and
 metadata
• Fixed deprecated img._getexif() to use img.getexif() method

app/services/image_processing.py


15. tests/unit/mcp/test_pm_tools_owner.py 🧪 Tests +42/-31

Update tests for split property management tools modules

• Updated imports to reflect split of pm_tools into pm_lease_tools and pm_rent_tools
• Updated patch paths to match new module structure
• Fixed test logic for owner_rent_status to handle per-status API calls instead of list parameter
• Added RentChargeStatus enum import for status filtering tests

tests/unit/mcp/test_pm_tools_owner.py


16. .factory/skills/viral-blog-publisher/scripts/publish_blog.py ✨ Enhancement +227/-0

Add blog publishing script with Supabase integration

• New script for publishing blog posts to Supabase via SQLAlchemy
• Supports JSON file input or CLI arguments for blog metadata
• Implements local image upload to Supabase Storage with automatic URL assignment
• Includes dry-run validation mode and admin user verification

.factory/skills/viral-blog-publisher/scripts/publish_blog.py


17. app/core/config.py ⚙️ Configuration changes +46/-9

Update configuration for environment-based settings and AI fallback chain

• Added environment-based .env file selection (.env.dev, .env.test, .env.prod)
• Added SERVERLESS_ENABLED flag for scale-to-zero deployment support
• Added CORS_ORIGINS_STR for environment-based CORS override
• Updated AI provider configurations with fallback chain (GLM → Gemini → Groq) and new API endpoints
• Added Groq and image API keys (Pixabay, Pexels) for blog cover acquisition
• Changed VECTOR_SYNC_INTERVAL_SECONDS from 300 to 86400 (daily)

app/core/config.py


18. app/api/api_v1/endpoints/vastu.py 📝 Documentation +2/-2

Update Vastu endpoint documentation for AI provider changes

• Updated AI provider documentation to reflect new defaults (GLM-5V-Turbo primary, Gemini 3.1
 fallback)
• Clarified provider options in docstring

app/api/api_v1/endpoints/vastu.py


19. app/infrastructure/__init__.py Miscellaneous +1/-0

Add infrastructure module initialization

• New infrastructure module for application wiring and adapters

app/infrastructure/init.py


20. app/mcp/chatgpt/pm_lease_tools.py ✨ Enhancement +260/-0

Owner lease management ChatGPT tools implementation

• New file implementing owner lease management tools for ChatGPT integration
• Provides three main tools: owner_leases_list, owner_leases_get, and owner_leases_terminate
• Includes authentication checks, error handling, and response formatting with widget metadata
• Serializes lease data and calculates lease statistics (active count, total monthly rent)

app/mcp/chatgpt/pm_lease_tools.py


21. app/services/booking.py ✨ Enhancement +83/-28

Booking service logging and import reorganization

• Reorganized imports for better clarity and consistency
• Added structured logging throughout booking operations (creation, cancellation, payment,
 availability checks)
• Enhanced logging with contextual metadata (user_id, property_id, dates, amounts)
• Updated type hints from Optional[T] to T | None syntax

app/services/booking.py


22. app/api/api_v1/endpoints/visits.py Refactoring +28/-49

Centralized visit authorization logic

• Removed inline authorization helper functions _agent_can_access_visit and _can_access_visit
• Centralized authorization logic by importing can_access_visit from app.services.pm_authz
• Updated all visit endpoint calls to use the centralized authorization function
• Improved type hints for VisitComplete payload and removed dict unpacking

app/api/api_v1/endpoints/visits.py


23. app/models/social.py ✨ Enhancement +86/-7

Type-safe enum storage with database constraints

• Added EnumStringType custom SQLAlchemy type decorator for storing enum values as strings with
 validation
• Implemented enum_check_constraint function to create database-level check constraints for enums
• Migrated enum columns to use EnumStringType with proper enum classes from app.models.enums
• Added check constraints to UserMatch, UserConversation, UserMessage, and UserReport tables

app/models/social.py


24. app/services/blog.py ✨ Enhancement +121/-2

Blog post SEO analytics and auto-generation

• Added helper functions for SEO analytics: _compute_word_count, _compute_reading_time,
 _auto_meta_title, _auto_meta_description
• Implemented serialization helpers for blog sources and SEO metadata
• Enhanced create_blog_post to auto-generate SEO fields and reading time metrics
• Updated update_blog_post to recalculate reading analytics and support SEO field updates

app/services/blog.py


25. app/api/api_v1/endpoints/agent_chat.py ✨ Enhancement +76/-49

Database connection pooling for streaming responses

• Refactored database session management to release main-pool connections before streaming
• Implemented background-pool sessions (AsyncSessionLocalBG) for long-running AI tool calls
• Added fresh session for persisting conversation messages after streaming completes
• Improved documentation explaining session lifecycle and pooling strategy

app/api/api_v1/endpoints/agent_chat.py


26. app/api/api_v1/endpoints/bookings.py Refactoring +26/-51

Centralized booking authorization logic

• Removed inline authorization helper functions _agent_can_access_booking and
 _can_access_booking
• Centralized authorization by importing can_access_booking from app.services.pm_authz
• Updated all booking endpoint calls to use the centralized authorization function
• Reorganized imports for consistency and clarity

app/api/api_v1/endpoints/bookings.py


27. app/services/flatmates/conversations.py ✨ Enhancement +127/-9

Conversation SSE events and Q&A persistence

• Updated enum comparisons to use enum objects directly instead of .value strings
• Added SSE event emissions for new messages and conversation updates
• Implemented mark_conversation_read function to mark peer messages as read with SSE notifications
• Added save_match_qna_answers function to persist Q&A answers and create user matches

app/services/flatmates/conversations.py


28. app/services/storage/processing.py ✨ Enhancement +75/-14

Image processing memory optimization

• Optimized image processing to open file once and reuse for all operations (thumbnail, WebP
 generation)
• Implemented _thumbnail_from_image and _webp_from_image helper functions for in-memory
 processing
• Added early memory release by deleting large buffers after upload
• Improved memory efficiency for 360 panorama image uploads

app/services/storage/processing.py


29. app/api/api_v1/endpoints/users.py Refactoring +14/-25

User endpoint schema consolidation

• Removed redundant UserSchemaModel imports by consolidating to single UserSchema import
• Updated assign_agent_to_specific_user endpoint to use typed AssignAgentPayload instead of dict
• Improved code consistency and reduced import duplication across multiple endpoints

app/api/api_v1/endpoints/users.py


30. app/infrastructure/errors.py ✨ Enhancement +182/-0

Centralized exception handling infrastructure

• New file implementing centralized exception handler registration for FastAPI
• Handles custom BaseAPIException, HTTPException, ValueError, and general exceptions
• Adds MCP-specific 401/403 handlers with OAuth resource metadata headers
• Provides standardized error response format with error codes and messages

app/infrastructure/errors.py


31. app/core/database.py ✨ Enhancement +69/-42

Serverless database pooling configuration

• Added serverless mode support with NullPool to prevent persistent connections
• Refactored engine creation to conditionally apply pooling based on SERVERLESS_ENABLED setting
• Improved slow-checkout logging with pool-specific labels
• Updated import from app.core.config to app.config

app/core/database.py


32. app/infrastructure/lifespan.py ✨ Enhancement +169/-0

Application lifespan and startup orchestration

• New file implementing FastAPI application lifespan management and startup orchestration
• Manages initialization of cache, schedulers, and AI providers
• Implements graceful shutdown for all background services
• Skips in-process schedulers in serverless mode to allow scale-to-zero

app/infrastructure/lifespan.py


33. app/mcp/chatgpt/pm_shared.py ✨ Enhancement +124/-0

Shared PM ChatGPT tool utilities

• New file with shared helpers for Property Management ChatGPT tools
• Implements serialization functions for leases, rent charges, and rent payments
• Provides natural language formatting helpers for lease and rent summaries
• Includes optional user retrieval from MCP context

app/mcp/chatgpt/pm_shared.py


34. app/api/api_v1/endpoints/public.py ✨ Enhancement +53/-10

Tour like/unlike rate limiting

• Added per-IP rate limiting for tour like/unlike endpoints to prevent abuse
• Implemented _is_like_rate_limited function with sliding window tracking
• Added periodic cleanup of stale IP entries to prevent unbounded memory growth
• Updated type hints from Optional[T] to T | None syntax

app/api/api_v1/endpoints/public.py


35. app/api/api_v1/dependencies/auth.py ✨ Enhancement +25/-12

Authentication logging and type hint modernization

• Updated type hints from Optional[T] to T | None syntax throughout
• Enhanced logging with structured context data (reason, scheme, token_suffix, error_type)
• Improved error messages with additional context in log entries
• Added optional auth resolution error logging

app/api/api_v1/dependencies/auth.py


36. app/mcp/utils.py ✨ Enhancement +36/-18

MCP database session background pool usage

• Updated get_db to use background pool (AsyncSessionLocalBG) for MCP tool calls
• Added error handling and Sentry context capture in database session management
• Removed string-based type hints in favor of direct type references
• Improved documentation explaining background pool usage for long-lived MCP operations

app/mcp/utils.py


37. app/api/share.py 🐞 Bug fix +16/-10

Tour share preview security and imports

• Updated import from app.core.config to app.config
• Added JSON escaping for redirect URL in script tag to prevent injection attacks
• Improved OG and Twitter meta tag generation with conditional formatting
• Updated type hints from Optional[T] to T | None syntax

app/api/share.py


38. app/schemas/blog.py ✨ Enhancement +51/-1

Blog post SEO and source schemas

• Added new BlogSource schema for citing sources with URL, name, type, and retrieval date
• Added BlogSEOMetadata schema for flexible SEO data (schema markup, keyword analysis, trending
 score)
• Extended BlogPostBase, BlogPostCreate, and BlogPostUpdate with SEO fields
• Added reading analytics fields (reading_time_minutes, word_count, published_at) to
 BlogPostInDB

app/schemas/blog.py


39. app/services/pm_authz.py ✨ Enhancement +83/-6

Centralized authorization functions for bookings and visits

• Added _Actor protocol for type-safe actor parameter handling
• Implemented can_access_booking function to centralize booking access control
• Implemented can_access_visit function to centralize visit access control
• Updated type hints from Optional[T] to T | None syntax

app/services/pm_authz.py


40. tests/unit/schemas/test_blog_schema.py 🧪 Tests +126/-0

Blog schema SEO fields unit tests

• New test file for blog schema SEO fields and validation
• Tests for BlogSource and BlogSEOMetadata schemas
• Tests for BlogPostCreate and BlogPostUpdate with SEO fields
• Validates field length constraints and optional field handling

tests/unit/schemas/test_blog_schema.py


41. app/mcp/chatgpt/pm_tenant_tools.py ✨ Enhancement +132/-0

Tenant rent dues ChatGPT tool

• New file implementing tenant-specific tools for ChatGPT integration
• Provides tenant_rent_dues tool to view outstanding rent charges
• Includes authentication checks and error handling with response formatting
• Calculates total outstanding rent and overdue charge counts

app/mcp/chatgpt/pm_tenant_tools.py


42. app/mcp/user/server.py Refactoring +18/-13

MCP server PM tools import reorganization

• Updated imports to use NoReturn type hint for _require_auth function
• Reorganized ChatGPT PM tool imports with explicit module names and noqa directives
• Split PM tools into separate imports: pm_dashboard_tools, pm_lease_tools,
 pm_maintenance_tools, pm_rent_tools, pm_tenant_tools
• Improved import organization with E402 and F401 noqa comments

app/mcp/user/server.py


43. .env.example Additional files +18/-3

...

.env.example


44. .factory/skills/qa-backend/SKILL.md Additional files +1/-1

...

.factory/skills/qa-backend/SKILL.md


45. .factory/skills/viral-blog-publisher/SKILL.md Additional files +224/-0

...

.factory/skills/viral-blog-publisher/SKILL.md


46. .factory/skills/viral-blog-publisher/references/seo-playbook.md Additional files +139/-0

...

.factory/skills/viral-blog-publisher/references/seo-playbook.md


47. .factory/skills/viral-blog-publisher/references/writing-style.md Additional files +123/-0

...

.factory/skills/viral-blog-publisher/references/writing-style.md


48. AGENTS.md Additional files +15/-5

...

AGENTS.md


49. CLAUDE.md Additional files +76/-32

...

CLAUDE.md


50. README.md Additional files +16/-7

...

README.md


51. app/api/api_v1/api.py Additional files +30/-27

...

app/api/api_v1/api.py


52. app/api/api_v1/endpoints/amenities.py Additional files +1/-1

...

app/api/api_v1/endpoints/amenities.py


53. app/api/api_v1/endpoints/blog.py Additional files +1/-1

...

app/api/api_v1/endpoints/blog.py


54. app/api/api_v1/endpoints/core.py Additional files +1/-1

...

app/api/api_v1/endpoints/core.py


55. app/api/api_v1/endpoints/oauth/authorization.py Additional files +1/-1

...

app/api/api_v1/endpoints/oauth/authorization.py


56. app/api/api_v1/endpoints/oauth/helpers.py Additional files +1/-1

...

app/api/api_v1/endpoints/oauth/helpers.py


57. app/api/api_v1/endpoints/tours.py Additional files +1/-1

...

app/api/api_v1/endpoints/tours.py


58. app/api/api_v1/endpoints/websocket.py Additional files +16/-8

...

app/api/api_v1/endpoints/websocket.py


59. app/config/__init__.py Additional files +5/-0

...

app/config/init.py


60. app/config/constants.py Additional files +5/-0

...

app/config/constants.py


61. app/config/settings.py Additional files +9/-0

...

app/config/settings.py


62. app/core/auth.py Additional files +21/-2

...

app/core/auth.py


63. app/core/cache/__init__.py Additional files +1/-1

...

app/core/cache/init.py


64. app/core/cache/manager.py Additional files +12/-0

...

app/core/cache/manager.py


65. app/core/constants.py Additional files +0/-25

...

app/core/constants.py


66. app/core/constants/__init__.py Additional files +3/-3

...

app/core/constants/init.py


67. app/core/db_resilience.py Additional files +2/-1

...

app/core/db_resilience.py


68. app/core/exceptions.py Additional files +6/-5

...

app/core/exceptions.py


69. app/core/logging.py Additional files +36/-3

...

app/core/logging.py


70. app/core/sse.py Additional files +107/-0

...

app/core/sse.py


71. app/infrastructure/mcp.py Additional files +67/-0

...

app/infrastructure/mcp.py


72. app/infrastructure/middleware.py Additional files +63/-0

...

app/infrastructure/middleware.py


73. app/infrastructure/request_context.py Additional files +14/-0

...

app/infrastructure/request_context.py


74. app/infrastructure/routing.py Additional files +28/-0

...

app/infrastructure/routing.py


75. app/main.py Additional files +2/-2

...

app/main.py


76. app/mcp/admin/admin.py Additional files +1/-1

...

app/mcp/admin/admin.py


77. app/mcp/admin/agent_tools/bookings.py Additional files +1/-1

...

app/mcp/admin/agent_tools/bookings.py


78. app/mcp/admin/agent_tools/dashboard.py Additional files +1/-1

...

app/mcp/admin/agent_tools/dashboard.py


79. app/mcp/admin/agent_tools/leases.py Additional files +1/-1

...

app/mcp/admin/agent_tools/leases.py


80. app/mcp/admin/agent_tools/maintenance.py Additional files +1/-1

...

app/mcp/admin/agent_tools/maintenance.py


81. app/mcp/admin/agent_tools/properties.py Additional files +1/-1

...

app/mcp/admin/agent_tools/properties.py


82. app/mcp/admin/agent_tools/rent.py Additional files +1/-1

...

app/mcp/admin/agent_tools/rent.py


83. app/mcp/admin/server.py Additional files +2/-2

...

app/mcp/admin/server.py


84. app/mcp/apps_sdk.py Additional files +1/-1

...

app/mcp/apps_sdk.py


85. app/mcp/auth_provider.py Additional files +1/-1

...

app/mcp/auth_provider.py


86. app/mcp/chatgpt/__init__.py Additional files +16/-10

...

app/mcp/chatgpt/init.py


87. app/mcp/chatgpt/pm_dashboard_tools.py Additional files +101/-0

...

app/mcp/chatgpt/pm_dashboard_tools.py


88. app/mcp/chatgpt/pm_owner_tools.py Additional files +16/-0

...

app/mcp/chatgpt/pm_owner_tools.py


89. app/mcp/chatgpt/pm_tools.py Additional files +0/-1211

...

app/mcp/chatgpt/pm_tools.py


90. app/mcp/errors.py Additional files +9/-2

...

app/mcp/errors.py


91. app/mcp/tool_ops/rent.py Additional files +12/-6

...

app/mcp/tool_ops/rent.py


92. app/mcp/user/system.py Additional files +1/-1

...

app/mcp/user/system.py


93. app/middleware/security.py Additional files +8/-31

...

app/middleware/security.py


94. app/models/blogs.py Additional files +28/-3

...

app/models/blogs.py


95. app/models/enums.py Additional files +74/-0

...

app/models/enums.py


96. app/models/pm_leases.py Additional files +3/-0

...

app/models/pm_leases.py


97. app/modules/__init__.py Additional files +11/-0

...

app/modules/init.py


98. app/schemas/common.py Additional files +21/-16

...

app/schemas/common.py


99. app/schemas/flatmates.py Additional files +12/-0

...

app/schemas/flatmates.py


100. app/schemas/flatmates_admin.py Additional files +114/-0

...

app/schemas/flatmates_admin.py


101. app/schemas/visit.py Additional files +44/-37

...

app/schemas/visit.py


102. app/services/ai/__init__.py Additional files +37/-12

...

app/services/ai/init.py


103. app/services/ai/providers/gemini.py Additional files +19/-2

...

app/services/ai/providers/gemini.py


104. app/services/ai/providers/glm.py Additional files +27/-7

...

app/services/ai/providers/glm.py


105. app/services/blog_auto_publish.py Additional files +35/-1

...

app/services/blog_auto_publish.py


106. app/services/blog_auto_publish_scheduler.py Additional files +9/-1

...

app/services/blog_auto_publish_scheduler.py


107. app/services/blog_service/generator.py Additional files +1/-1

...

app/services/blog_service/generator.py


108. app/services/data_hub/base_scraper.py Additional files +26/-8

...

app/services/data_hub/base_scraper.py


109. app/services/data_hub/jamabandi.py Additional files +1/-1

...

app/services/data_hub/jamabandi.py


110. app/services/data_hub/neighbourhood.py Additional files +1/-1

...

app/services/data_hub/neighbourhood.py


111. app/services/data_hub_scheduler.py Additional files +9/-1

...

app/services/data_hub_scheduler.py


112. app/services/email.py Additional files +1/-1

...

app/services/email.py


113. app/services/flatmates/__init__.py Additional files +4/-0

...

app/services/flatmates/init.py


114. app/services/flatmates/matching.py Additional files +12/-0

...

app/services/flatmates/matching.py


115. app/services/flatmates/visits.py Additional files +12/-0

...

app/services/flatmates/visits.py


116. app/services/notification_scheduler.py Additional files +10/-2

...

app/services/notification_scheduler.py


117. app/services/notifications/fcm.py Additional files +1/-1

...

app/services/notifications/fcm.py


118. app/services/notifications/helpers.py Additional files +5/-0

...

app/services/notifications/helpers.py


119. app/services/notifications/push.py Additional files +3/-1

...

app/services/notifications/push.py


120. app/services/pm_leases.py Additional files +6/-0

...

app/services/pm_leases.py


121. app/services/property/search.py Additional files +32/-1

...

app/services/property/search.py


122. app/services/push_notification.py Additional files +17/-0

...

app/services/push_notification.py


123. app/services/sms.py Additional files +1/-1

...

app/services/sms.py


124. app/services/storage/helpers.py Additional files +1/-1

...

app/services/storage/helpers.py


125. app/services/storage/service.py Additional files +1/-1

...

app/services/storage/service.py


126. app/services/storage_paths.py Additional files +3/-0

...

app/services/storage_paths.py


127. app/services/tour/scenes.py Additional files +1/-1

...

app/services/tour/scenes.py


128. app/services/tour_ai/background.py Additional files +6/-6

...

app/services/tour_ai/background.py


129. app/services/tour_ai/hotspot_suggestions.py Additional files +4/-4

...

app/services/tour_ai/hotspot_suggestions.py


130. app/services/tour_ai/scene_analysis.py Additional files +8/-8

...

app/services/tour_ai/scene_analysis.py


131. app/services/vector_sync_scheduler.py Additional files +9/-1

...

app/services/vector_sync_scheduler.py


132. app/shared/__init__.py Additional files +9/-0

...

app/shared/init.py


133. app/vector/compose.py Additional files +8/-8

...

app/vector/compose.py


134. app/vector/embedding_client.py Additional files +11/-11

...

app/vector/embedding_client.py

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review Bot commented May 13, 2026

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (1) 📎 Requirement gaps (0)

Grey Divider


Action required

1. app/modules placeholder package added 📘 Rule violation ⚙ Maintainability
Description
New placeholder packages were introduced under reserved directories (app/modules/ and
app/shared/) despite no actual domain migration or real usage occurring. This violates the
repository policy against shim/placeholder packages in reserved locations and risks architectural
drift and confusing/unused structure.
Code

app/modules/init.py[R1-11]

+"""Domain modules placeholder.
+
+Domain code will be migrated here as part of the long-term architecture
+evolution. Currently, domain code lives in the legacy locations:
+  - app/api/         — REST endpoints
+  - app/services/    — Business logic
+  - app/models/      — ORM models
+  - app/schemas/     — Pydantic schemas
+  - app/repositories/— Data access
+  - app/mcp/         — MCP tools
+"""
Evidence
PR Compliance ID 15 explicitly forbids introducing shim-only re-export packages or placeholder
domain entrypoints under reserved directories such as app/modules/ and app/shared/. The new
app/modules/__init__.py is described as a placeholder for future migration rather than containing
migrated domain code, and app/shared/__init__.py similarly acts as a placeholder/redirect to other
module locations instead of representing a genuinely migrated/shared package with real usage, which
directly matches the prohibited pattern.

AGENTS.md
app/modules/init.py[1-11]
app/shared/init.py[1-9]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The PR introduces placeholder/shim packages under reserved directories (`app/modules/` and `app/shared/`), which is not allowed unless there is an actual domain migration or concrete shared usage.
## Issue Context
PR Compliance ID 15 forbids using reserved directories like `app/modules/` and `app/shared/` for shim-only re-exports or placeholder entrypoints. The added `__init__.py` files are explicitly placeholders (and in the case of `app/shared/`, a redirect), rather than real migrated/shared code.
## Fix Focus Areas
- app/modules/__init__.py[1-11]
- app/shared/__init__.py[1-9]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Env file ignored 🐞 Bug ☼ Reliability
Description
SettingsConfigDict now loads env vars from .env.dev/.env.test/.env.prod based on the OS
ENVIRONMENT and no longer loads .env, but README/CLAUDE still instruct creating .env, causing
the app to start with missing/default config. Because the env-file choice is made before env-file
parsing, setting ENVIRONMENT inside .env cannot work (bootstrapping deadlock).
Code

app/core/config.py[R234-238]

  model_config = SettingsConfigDict(
-        env_file=str(BASE_DIR / ".env"),
+        env_file=str(BASE_DIR / _ENV_FILE),
      case_sensitive=True,
+        extra="ignore",
  )
Evidence
The settings loader now points at BASE_DIR / _ENV_FILE (derived from OS ENVIRONMENT), while
repository documentation instructs users to create .env; this mismatch means documented
configuration won’t be read and ENVIRONMENT can’t be set inside .env to influence _ENV_FILE.

app/core/config.py[9-16]
app/core/config.py[234-238]
README.md[219-223]
CLAUDE.md[66-67]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`app/core/config.py` selects an environment-specific env file (`.env.dev`, `.env.test`, `.env.prod`) using `os.getenv("ENVIRONMENT")` and then configures Pydantic settings to load only that file. This breaks the documented setup (`cp .env.example .env`) and prevents `ENVIRONMENT` from being configured inside the env file itself.
## Issue Context
- Docs currently instruct copying `.env.example` to `.env`.
- The code defaults to `.env.dev` unless `ENVIRONMENT` is set in the OS environment, so `.env` is effectively ignored.
## Fix Focus Areas
- app/core/config.py[9-16]
- app/core/config.py[234-238]
- README.md[219-223]
- CLAUDE.md[66-67]
## Proposed fix
- Update settings loading to always include `.env` as a base file (backward compatible), and optionally overlay the env-specific file.
- Example: `env_file=(str(BASE_DIR / ".env"), str(BASE_DIR / _ENV_FILE))` (or a list/tuple depending on pydantic-settings support).
- Alternatively/additionally support an explicit `ENV_FILE` OS variable to override.
- Align docs with the intended behavior (either keep `.env` as the primary path, or update README/CLAUDE to instruct `.env.dev` etc., but avoid the bootstrapping trap where `ENVIRONMENT` must be set before env loading).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Loop-bound SSE lock 🐞 Bug ☼ Reliability
Description
app/core/sse.py creates a module-global SSEEventBus holding an asyncio.Lock, which becomes
bound to the first event loop that uses it and can raise at runtime if later awaited from a
different loop. This repo configures pytest to use a fresh event loop per test, increasing the
likelihood of “bound to a different event loop” crashes if SSE subscribe/emit is exercised across
tests or in multi-loop contexts.
Code

app/core/sse.py[R19-23]

+    def __init__(self) -> None:
+        self._queues: dict[int, list[asyncio.Queue[dict[str, Any]]]] = {}
+        self._full_counts: dict[int, int] = {}
+        self._lock = asyncio.Lock()
+        self._emit_count = 0
Evidence
The SSE bus is module-global and uses an asyncio.Lock; the repo’s test config explicitly uses a
new event loop per test, and the SSE endpoints/handlers call into this global bus, creating a
concrete cross-loop hazard.

app/core/sse.py[10-23]
pyproject.toml[69-72]
app/api/api_v1/endpoints/flatmates.py[69-103]
app/api/api_v1/endpoints/flatmates_admin.py[172-186]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A module-global `SSEEventBus` is instantiated with an `asyncio.Lock`. Asyncio synchronization primitives are loop-bound; if the same object is later used from a different event loop, it can raise runtime errors.
## Issue Context
- The repo’s pytest configuration sets `asyncio_default_fixture_loop_scope = "function"` (new loop per test).
- SSE endpoints and handlers import and use the module-global `sse_bus`.
## Fix Focus Areas
- app/core/sse.py[10-24]
- pyproject.toml[69-72]
- app/api/api_v1/endpoints/flatmates.py[69-103]
- app/api/api_v1/endpoints/flatmates_admin.py[172-186]
## Proposed fix
Choose one of:
1) **Per-app/per-lifespan bus**: create the `SSEEventBus` during app startup (lifespan) and store it on `app.state`, then access it via a dependency or request/app reference.
2) **Loop-aware lazy init** (minimal call-site change): do not create the lock at import time; instead create it lazily under `asyncio.get_running_loop()` and recreate/reset internal state when the running loop changes.
- e.g., keep `self._loop` and `self._lock: asyncio.Lock | None`, and in a private `_get_lock()` ensure the lock matches the current loop.
Ensure all call sites (`sse_bus.subscribe/emit/unsubscribe`) use the loop-safe lock/bus.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 183 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name=".factory/skills/qa-backend/SKILL.md">

<violation number="1" location=".factory/skills/qa-backend/SKILL.md:222">
P2: The new seeding command points to a non-existent script path, so the documented recovery step cannot be executed.</violation>
</file>

<file name="README.md">

<violation number="1" location="README.md:238">
P2: README setup commands reference missing `seed_data` scripts, so the documented data-loading flow will fail.</violation>
</file>

Note: This PR contains a large number of files. cubic only reviews up to 75 files per PR, so some files may not have been reviewed. cubic prioritizes the most important files to review.
On a pro plan you can use ultrareview for larger PRs.

5. **AI provider timeouts.** Vastu analysis and AI agent calls may take 10-30 seconds. Use `--max-time 60` with curl for these endpoints.

6. **Empty database.** Property search returns empty if no data is loaded. Run `uv run python populate_data/load_comprehensive_data.py --quick` to seed test data.
6. **Empty database.** Property search returns empty if no data is loaded. Run `uv run python seed_data/01_load_all.py --only hardcoded,seed` to seed test data.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The new seeding command points to a non-existent script path, so the documented recovery step cannot be executed.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .factory/skills/qa-backend/SKILL.md, line 222:

<comment>The new seeding command points to a non-existent script path, so the documented recovery step cannot be executed.</comment>

<file context>
@@ -219,7 +219,7 @@ Authorization: Bearer <supabase-jwt-token>
 5. **AI provider timeouts.** Vastu analysis and AI agent calls may take 10-30 seconds. Use `--max-time 60` with curl for these endpoints.
 
-6. **Empty database.** Property search returns empty if no data is loaded. Run `uv run python populate_data/load_comprehensive_data.py --quick` to seed test data.
+6. **Empty database.** Property search returns empty if no data is loaded. Run `uv run python seed_data/01_load_all.py --only hardcoded,seed` to seed test data.
 
 7. **MCP tool auth challenge format.** MCP tools requiring auth return a 401 with `WWW-Authenticate: Bearer resource_metadata="..."` header. This is expected -- not a failure.
</file context>

Comment thread README.md
# Quick load (~51 properties)
uv run python populate_data/load_comprehensive_data.py --quick
# Load all seed data (hardcoded + generated)
uv run python seed_data/01_load_all.py
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: README setup commands reference missing seed_data scripts, so the documented data-loading flow will fail.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At README.md, line 238:

<comment>README setup commands reference missing `seed_data` scripts, so the documented data-loading flow will fail.</comment>

<file context>
@@ -229,11 +234,15 @@ All endpoints are prefixed with `/api/v1`.
-   # Quick load (~51 properties)
-   uv run python populate_data/load_comprehensive_data.py --quick
+   # Load all seed data (hardcoded + generated)
+   uv run python seed_data/01_load_all.py
 
-   # Full load (~300 properties)
</file context>

@qodo-code-review
Copy link
Copy Markdown

CI Feedback 🧐

A test triggered by this PR failed. Here is an AI-generated analysis of the failure:

Action: test

Failed stage: Run tests [❌]

Failed test name: tests/api/test_blog_endpoints.py::TestBlogPostEndpoints::test_get_blog_post

Failure summary:

The action failed because the test suite exited with failures, setup errors, and insufficient
coverage:
- Coverage gate failed: total coverage was 45.40%, below --fail-under=90 (see ERROR:
Coverage failure: total of 45 is less than fail-under=90 and FAIL Required test coverage of 90% not
reached).
- 2 tests errored during setup due to a missing pytest fixture test_db:
-
tests/pm/test_authz.py::test_pm_authz_owner_agent_tenant_property_access
(/home/runner/work/backend/backend/tests/pm/test_authz.py:18) → fixture 'test_db' not found
-
tests/pm/test_rent.py::test_generate_rent_charges_idempotent_and_payment_status
(/home/runner/work/backend/backend/tests/pm/test_rent.py:14) → fixture 'test_db' not found
-
Multiple test failures indicate application/test breakages, including:
- API mismatch:
tests/api/test_blog_endpoints.py::TestBlogPostEndpoints::test_get_blog_post expected 200 but got
404.
- Mock/schema mismatch in bookings cancel endpoint:
-
app/api/api_v1/endpoints/bookings.py:167 raised AttributeError: Mock object has no attribute
'property_id' during
tests/e2e/test_booking_complete_flow.py::TestBookingManagementFlow::test_view_and_cancel_booking.

- Missing import in e2e test:
tests/e2e/test_pm_lifecycle_flow.py::TestPMLifecycleFlow::test_lease_to_rent_to_maintenance_flow
NameError: name 'Decimal' is not defined.
- Property details handler expecting an object but
received a dict:
- app/api/api_v1/endpoints/properties.py:350AttributeError: 'dict' object
has no attribute 'property_type' causing failures in test_view_property_details and
test_view_increments_counter.
- Integration API response missing pagination fields:
-
tests/integration/test_property_search.py::...::test_paginationKeyError: 'page'.
- MCP auth
behavior failing tests: many MCP admin tools raised app.mcp.apps_sdk.AuthRequiredError (“Please log
in...”), suggesting tests did not provide auth context expected by _require_auth.
- MCP unit tests
patching wrong/missing symbols:
- app.mcp.user.owner missing list_managed_properties /
create_managed_property
- app.mcp.user.booking missing booking_svc
- PM maintenance status
transition rules mismatch:
-
tests/unit/services/pm/test_pm_maintenance_service.py::...::test_update_request_status400: Cannot
transition from 'open' to 'in_review'. Allowed: {'in_progress', 'closed'}.
- Async mocking bug in
property creation unit test:
- app/services/property/crud.py:161 in create_propertyTypeError:
object MagicMock can't be used in 'await' expression.
- Import error in property deletion:
-
app/services/property/crud.py:380ModuleNotFoundError: No module named 'app.models.visits'.
-
Overall pytest result: 25 failed, 1352 passed, 2 errors, causing the job to exit with code 1.

Relevant error logs:
1:  ##[group]Runner Image Provisioner
2:  Hosted Compute Agent
...

386:  #6 0.347 Get:6 http://deb.debian.org/debian-security bullseye-security/main amd64 Packages [454 kB]
387:  #6 0.435 Get:7 http://deb.debian.org/debian bullseye-updates/main amd64 Packages [18.8 kB]
388:  #6 0.495 Get:8 http://apt.postgresql.org/pub/repos/apt bullseye-pgdg/main amd64 Packages [510 kB]
389:  #6 0.504 Get:9 http://apt.postgresql.org/pub/repos/apt bullseye-pgdg/15 amd64 Packages [4,653 B]
390:  #6 1.249 Fetched 9,380 kB in 1s (8,432 kB/s)
391:  #6 1.249 Reading package lists...
392:  #6 1.637 Reading package lists...
393:  #6 2.020 Building dependency tree...
394:  #6 2.106 Reading state information...
395:  #6 2.200 The following additional packages will be installed:
396:  #6 2.200   binutils binutils-common binutils-x86-64-linux-gnu bzip2 clang-16 cpp cpp-10
397:  #6 2.200   dpkg-dev g++ g++-10 gcc gcc-10 git-man icu-devtools lib32gcc-s1 lib32stdc++6
398:  #6 2.200   libasan6 libatomic1 libbinutils libc-dev-bin libc6 libc6-dev libc6-i386
399:  #6 2.200   libcc1-0 libclang-common-13-dev libclang-common-16-dev libclang-cpp13
400:  #6 2.200   libclang-cpp16 libclang1-13 libclang1-16 libcrypt-dev libctf-nobfd0 libctf0
401:  #6 2.200   libcurl4 libdpkg-perl liberror-perl libffi-dev libgc1 libgcc-10-dev libgomp1
402:  #6 2.201   libicu-dev libicu67 libisl23 libitm1 libllvm16 liblsan0 libmpc3 libmpdec3
...

419:  #6 2.202   diffutils-doc python3-doc python3-tk python3-venv python3-setuptools
420:  #6 2.202   python-pygments-doc ttf-bitstream-vera python3.9-venv python3.9-doc
421:  #6 2.202   binfmt-support
422:  #6 2.202 Recommended packages:
423:  #6 2.202   llvm-13-dev fakeroot libalgorithm-merge-perl ssh-client manpages
424:  #6 2.202   manpages-dev libc-devtools libnss-nis libnss-nisplus libclang-rt-16-dev
425:  #6 2.202   libfile-fcntllock-perl liblocale-gettext-perl libgpm2 binfmt-support
426:  #6 2.202   binfmt-support | systemd
427:  #6 2.501 The following NEW packages will be installed:
428:  #6 2.501   binutils binutils-common binutils-x86-64-linux-gnu build-essential bzip2
429:  #6 2.501   clang-13 clang-16 cpp cpp-10 dpkg-dev g++ g++-10 gcc gcc-10 git git-man
430:  #6 2.501   icu-devtools lib32gcc-s1 lib32stdc++6 libasan6 libatomic1 libbinutils
431:  #6 2.501   libc-dev-bin libc6-dev libc6-i386 libcc1-0 libclang-common-13-dev
432:  #6 2.501   libclang-common-16-dev libclang-cpp13 libclang-cpp16 libclang1-13
433:  #6 2.501   libclang1-16 libcrypt-dev libctf-nobfd0 libctf0 libcurl4 libdpkg-perl
434:  #6 2.501   liberror-perl libffi-dev libgc1 libgcc-10-dev libgomp1 libicu-dev libisl23
435:  #6 2.501   libitm1 libllvm16 liblsan0 libmpc3 libmpdec3 libncurses-dev libncurses6
...

503:  #6 2.975 Get:55 http://deb.debian.org/debian-security bullseye-security/main amd64 libc6-i386 amd64 2.31-13+deb11u13 [2,618 kB]
504:  #6 2.990 Get:56 http://deb.debian.org/debian bullseye/main amd64 lib32gcc-s1 amd64 10.2.1-6 [49.4 kB]
505:  #6 2.990 Get:57 http://deb.debian.org/debian bullseye/main amd64 lib32stdc++6 amd64 10.2.1-6 [510 kB]
506:  #6 2.994 Get:58 http://deb.debian.org/debian bullseye/main amd64 libclang-common-13-dev amd64 1:13.0.1-6~deb11u1 [5,602 kB]
507:  #6 3.027 Get:59 http://deb.debian.org/debian bullseye/main amd64 llvm-13-linker-tools amd64 1:13.0.1-6~deb11u1 [1,253 kB]
508:  #6 3.036 Get:60 http://deb.debian.org/debian bullseye/main amd64 libclang1-13 amd64 1:13.0.1-6~deb11u1 [6,187 kB]
509:  #6 3.069 Get:61 http://deb.debian.org/debian bullseye/main amd64 clang-13 amd64 1:13.0.1-6~deb11u1 [123 kB]
510:  #6 3.070 Get:62 http://deb.debian.org/debian-security bullseye-security/main amd64 libicu67 amd64 67.1-7+deb11u1 [8,624 kB]
511:  #6 3.116 Get:63 http://deb.debian.org/debian-security bullseye-security/main amd64 libxml2 amd64 2.9.10+dfsg-6.7+deb11u9 [694 kB]
512:  #6 3.122 Get:64 http://deb.debian.org/debian bullseye/main amd64 libllvm16 amd64 1:16.0.6-15~deb11u2 [23.0 MB]
513:  #6 3.246 Get:65 http://deb.debian.org/debian bullseye/main amd64 libclang-cpp16 amd64 1:16.0.6-15~deb11u2 [11.5 MB]
514:  #6 3.307 Get:66 http://deb.debian.org/debian bullseye/main amd64 libclang-common-16-dev all 1:16.0.6-15~deb11u2 [683 kB]
515:  #6 3.311 Get:67 http://deb.debian.org/debian bullseye/main amd64 llvm-16-linker-tools amd64 1:16.0.6-15~deb11u2 [1,258 kB]
516:  #6 3.318 Get:68 http://deb.debian.org/debian bullseye/main amd64 libclang1-16 amd64 1:16.0.6-15~deb11u2 [6,575 kB]
517:  #6 3.358 Get:69 http://deb.debian.org/debian bullseye/main amd64 clang-16 amd64 1:16.0.6-15~deb11u2 [137 kB]
518:  #6 3.360 Get:70 http://deb.debian.org/debian bullseye/main amd64 liberror-perl all 0.17029-1 [31.0 kB]
519:  #6 3.363 Get:71 http://deb.debian.org/debian-security bullseye-security/main amd64 git-man all 1:2.30.2-1+deb11u5 [1,831 kB]
...

849:  #6 17.41 Selecting previously unselected package libclang-cpp16.
850:  #6 17.41 Preparing to unpack .../51-libclang-cpp16_1%3a16.0.6-15~deb11u2_amd64.deb ...
851:  #6 17.41 Unpacking libclang-cpp16 (1:16.0.6-15~deb11u2) ...
852:  #6 18.26 Selecting previously unselected package libclang-common-16-dev.
853:  #6 18.26 Preparing to unpack .../52-libclang-common-16-dev_1%3a16.0.6-15~deb11u2_all.deb ...
854:  #6 18.26 Unpacking libclang-common-16-dev (1:16.0.6-15~deb11u2) ...
855:  #6 18.35 Selecting previously unselected package llvm-16-linker-tools.
856:  #6 18.35 Preparing to unpack .../53-llvm-16-linker-tools_1%3a16.0.6-15~deb11u2_amd64.deb ...
857:  #6 18.35 Unpacking llvm-16-linker-tools (1:16.0.6-15~deb11u2) ...
858:  #6 18.45 Selecting previously unselected package libclang1-16.
859:  #6 18.46 Preparing to unpack .../54-libclang1-16_1%3a16.0.6-15~deb11u2_amd64.deb ...
860:  #6 18.46 Unpacking libclang1-16 (1:16.0.6-15~deb11u2) ...
861:  #6 18.93 Selecting previously unselected package clang-16.
862:  #6 18.94 Preparing to unpack .../55-clang-16_1%3a16.0.6-15~deb11u2_amd64.deb ...
863:  #6 18.94 Unpacking clang-16 (1:16.0.6-15~deb11u2) ...
864:  #6 18.96 Selecting previously unselected package liberror-perl.
865:  #6 18.97 Preparing to unpack .../56-liberror-perl_0.17029-1_all.deb ...
866:  #6 18.97 Unpacking liberror-perl (0.17029-1) ...
867:  #6 18.98 Selecting previously unselected package git-man.
...

939:  #6 27.24 Preparing to unpack .../81-postgresql-server-dev-15_15.17-1.pgdg11+1_amd64.deb ...
940:  #6 27.24 Unpacking postgresql-server-dev-15 (15.17-1.pgdg11+1) ...
941:  #6 27.43 Setting up media-types (4.0.0) ...
942:  #6 27.44 Setting up libz3-dev:amd64 (4.8.10-1) ...
943:  #6 27.44 Setting up libicu67:amd64 (67.1-7+deb11u1) ...
944:  #6 27.44 Setting up libyaml-0-2:amd64 (0.2.2-1) ...
945:  #6 27.44 Setting up binutils-common:amd64 (2.35.2-2) ...
946:  #6 27.45 Setting up libpq5:amd64 (18.3-1.pgdg11+1) ...
947:  #6 27.45 Setting up linux-libc-dev:amd64 (5.10.251-4) ...
948:  #6 27.46 Setting up libctf-nobfd0:amd64 (2.35.2-2) ...
949:  #6 27.46 Setting up libgomp1:amd64 (10.2.1-6) ...
950:  #6 27.46 Setting up bzip2 (1.0.8-4) ...
951:  #6 27.47 Setting up libffi-dev:amd64 (3.3-6) ...
952:  #6 27.47 Setting up libasan6:amd64 (10.2.1-6) ...
953:  #6 27.47 Setting up llvm-13-linker-tools (1:13.0.1-6~deb11u1) ...
954:  #6 27.48 Setting up liberror-perl (0.17029-1) ...
955:  #6 27.48 Setting up libclang1-13 (1:13.0.1-6~deb11u1) ...
...

1035:  #6 29.46 You are in 'detached HEAD' state. You can look around, make experimental
1036:  #6 29.46 changes and commit them, and you can discard any commits you make in this
1037:  #6 29.46 state without impacting any branches by switching back to a branch.
1038:  #6 29.46 
1039:  #6 29.46 If you want to create a new branch to retain commits you create, you may
1040:  #6 29.46 do so (now or later) by using -c with the switch command. Example:
1041:  #6 29.46 
1042:  #6 29.46   git switch -c <new-branch-name>
1043:  #6 29.46 
1044:  #6 29.46 Or undo this operation with:
1045:  #6 29.46 
1046:  #6 29.46   git switch -
1047:  #6 29.46 
1048:  #6 29.46 Turn off this advice by setting config variable advice.detachedHead to false
1049:  #6 29.46 
1050:  #6 29.51 gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wimplicit-fallthrough=3 -Wcast-function-type -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno-format-truncation -Wno-stringop-truncation -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -fno-omit-frame-pointer -march=native -ftree-vectorize -fassociative-math -fno-signed-zeros -fno-trapping-math -fPIC -I. -I./ -I/usr/include/postgresql/15/server -I/usr/include/postgresql/internal  -Wdate-time -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2   -c -o src/bitutils.o src/bitutils.c
1051:  #6 29.72 gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wimplicit-fallthrough=3 -Wcast-function-type -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno-format-truncation -Wno-stringop-truncation -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -fno-omit-frame-pointer -march=native -ftree-vectorize -fassociative-math -fno-signed-zeros -fno-trapping-math -fPIC -I. -I./ -I/usr/include/postgresql/15/server -I/usr/include/postgresql/internal  -Wdate-time -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2   -c -o src/bitvec.o src/bitvec.c
1052:  #6 29.77 gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wimplicit-fallthrough=3 -Wcast-function-type -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno-format-truncation -Wno-stringop-truncation -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -fno-omit-frame-pointer -march=native -ftree-vectorize -fassociative-math -fno-signed-zeros -fno-trapping-math -fPIC -I. -I./ -I/usr/include/postgresql/15/server -I/usr/include/postgresql/internal  -Wdate-time -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2   -c -o src/halfutils.o src/halfutils.c
1053:  #6 30.02 gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wimplicit-fallthrough=3 -Wcast-function-type -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno-format-truncation -Wno-stringop-truncation -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -fno-omit-frame-pointer -march=native -ftree-vectorize -fassociative-math -fno-signed-zeros -fno-trapping-math -fPIC -I. -I./ -I/usr/include/postgresql/15/server -I/usr/include/postgresql/internal  -Wdate-time -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2   -c -o src/halfvec.o src/halfvec.c
1054:  #6 30.51 gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wimplicit-fallthrough=3 -Wcast-function-type -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno-format-truncation -Wno-stringop-truncation -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -fno-omit-frame-pointer -march=native -ftree-vectorize -fassociative-math -fno-signed-zeros -fno-trapping-math -fPIC -I. -I./ -I/usr/include/postgresql/15/server -I/usr/include/postgresql/internal  -Wdate-time -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2   -c -o src/hnsw.o src/hnsw.c
1055:  #6 30.62 gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wimplicit-fallthrough=3 -Wcast-function-type -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno-format-truncation -Wno-stringop-truncation -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -fno-omit-frame-pointer -march=native -ftree-vectorize -fassociative-math -fno-signed-zeros -fno-trapping-math -fPIC -I. -I./ -I/usr/include/postgresql/15/server -I/usr/include/postgresql/internal  -Wdate-time -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2   -c -o src/hnswbuild.o src/hnswbuild.c
1056:  #6 30.90 gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wimplicit-fallthrough=3 -Wcast-function-type -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno-format-truncation -Wno-stringop-truncation -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -fno-omit-frame-pointer -march=native -ftree-vectorize -fassociative-math -fno-signed-zeros -fno-trapping-math -fPIC -I. -I./ -I/usr/include/postgresql/15/server -I/usr/include/postgresql/internal  -Wdate-time -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2   -c -o src/hnswinsert.o src/hnswinsert.c
1057:  #6 31.10 gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wimplicit-fallthrough=3 -Wcast-function-type -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno-format-truncation -Wno-stringop-truncation -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -fno-omit-frame-pointer -march=native -ftree-vectorize -fassociative-math -fno-signed-zeros -fno-trapping-math -fPIC -I. -I./ -I/usr/include/postgresql/15/server -I/usr/include/postgresql/internal  -Wdate-time -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2   -c -o src/hnswscan.o src/hnswscan.c
1058:  #6 31.21 gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wimplicit-fallthrough=3 -Wcast-function-type -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno-format-truncation -Wno-stringop-truncation -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -fno-omit-frame-pointer -march=native -ftree-vectorize -fassociative-math -fno-signed-zeros -fno-trapping-math -fPIC -I. -I./ -I/usr/include/postgresql/15/server -I/usr/include/postgresql/internal  -Wdate-time -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2   -c -o src/hnswutils.o src/hnswutils.c
1059:  #6 31.87 gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wimplicit-fallthrough=3 -Wcast-function-type -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno-format-truncation -Wno-stringop-truncation -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -fno-omit-frame-pointer -march=native -ftree-vectorize -fassociative-math -fno-signed-zeros -fno-trapping-math -fPIC -I. -I./ -I/usr/include/postgresql/15/server -I/usr/include/postgresql/internal  -Wdate-time -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2   -c -o src/hnswvacuum.o src/hnswvacuum.c
1060:  #6 32.07 gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wimplicit-fallthrough=3 -Wcast-function-type -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno-format-truncation -Wno-stringop-truncation -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -fno-omit-frame-pointer -march=native -ftree-vectorize -fassociative-math -fno-signed-zeros -fno-trapping-math -fPIC -I. -I./ -I/usr/include/postgresql/15/server -I/usr/include/postgresql/internal  -Wdate-time -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2   -c -o src/ivfbuild.o src/ivfbuild.c
1061:  #6 32.31 gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wimplicit-fallthrough=3 -Wcast-function-type -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno-format-truncation -Wno-stringop-truncation -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -fno-omit-frame-pointer -march=native -ftree-vectorize -fassociative-math -fno-signed-zeros -fno-trapping-math -fPIC -I. -I./ -I/usr/include/postgresql/15/server -I/usr/include/postgresql/internal  -Wdate-time -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2   -c -o src/ivfflat.o src/ivfflat.c
1062:  #6 32.41 gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wimplicit-fallthrough=3 -Wcast-function-type -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno-format-truncation -Wno-stringop-truncation -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -fno-omit-frame-pointer -march=native -ftree-vectorize -fassociative-math -fno-signed-zeros -fno-trapping-math -fPIC -I. -I./ -I/usr/include/postgresql/15/server -I/usr/include/postgresql/internal  -Wdate-time -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2   -c -o src/ivfinsert.o src/ivfinsert.c
1063:  #6 32.50 gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wimplicit-fallthrough=3 -Wcast-function-type -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno-format-truncation -Wno-stringop-truncation -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -fno-omit-frame-pointer -march=native -ftree-vectorize -fassociative-math -fno-signed-zeros -fno-trapping-math -fPIC -I. -I./ -I/usr/include/postgresql/15/server -I/usr/include/postgresql/internal  -Wdate-time -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2   -c -o src/ivfkmeans.o src/ivfkmeans.c
1064:  #6 32.93 gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wimplicit-fallthrough=3 -Wcast-function-type -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno-format-truncation -Wno-stringop-truncation -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -fno-omit-frame-pointer -march=native -ftree-vectorize -fassociative-math -fno-signed-zeros -fno-trapping-math -fPIC -I. -I./ -I/usr/include/postgresql/15/server -I/usr/include/postgresql/internal  -Wdate-time -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2   -c -o src/ivfscan.o src/ivfscan.c
1065:  #6 33.07 gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wimplicit-fallthrough=3 -Wcast-function-type -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno-format-truncation -Wno-stringop-truncation -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -fno-omit-frame-pointer -march=native -ftree-vectorize -fassociative-math -fno-signed-zeros -fno-trapping-math -fPIC -I. -I./ -I/usr/include/postgresql/15/server -I/usr/include/postgresql/internal  -Wdate-time -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2   -c -o src/ivfutils.o src/ivfutils.c
1066:  #6 33.36 gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wimplicit-fallthrough=3 -Wcast-function-type -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno-format-truncation -Wno-stringop-truncation -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -fno-omit-frame-pointer -march=native -ftree-vectorize -fassociative-math -fno-signed-zeros -fno-trapping-math -fPIC -I. -I./ -I/usr/include/postgresql/15/server -I/usr/include/postgresql/internal  -Wdate-time -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2   -c -o src/ivfvacuum.o src/ivfvacuum.c
1067:  #6 33.45 gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wimplicit-fallthrough=3 -Wcast-function-type -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno-format-truncation -Wno-stringop-truncation -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -fno-omit-frame-pointer -march=native -ftree-vectorize -fassociative-math -fno-signed-zeros -fno-trapping-math -fPIC -I. -I./ -I/usr/include/postgresql/15/server -I/usr/include/postgresql/internal  -Wdate-time -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2   -c -o src/sparsevec.o src/sparsevec.c
1068:  #6 33.91 gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wimplicit-fallthrough=3 -Wcast-function-type -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno-format-truncation -Wno-stringop-truncation -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -fno-omit-frame-pointer -march=native -ftree-vectorize -fassociative-math -fno-signed-zeros -fno-trapping-math -fPIC -I. -I./ -I/usr/include/postgresql/15/server -I/usr/include/postgresql/internal  -Wdate-time -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2   -c -o src/vector.o src/vector.c
1069:  #6 34.52 gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wimplicit-fallthrough=3 -Wcast-function-type -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno-format-truncation -Wno-stringop-truncation -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -fno-omit-frame-pointer -march=native -ftree-vectorize -fassociative-math -fno-signed-zeros -fno-trapping-math -fPIC -shared -o vector.so src/bitutils.o src/bitvec.o src/halfutils.o src/halfvec.o src/hnsw.o src/hnswbuild.o src/hnswinsert.o src/hnswscan.o src/hnswutils.o src/hnswvacuum.o src/ivfbuild.o src/ivfflat.o src/ivfinsert.o src/ivfkmeans.o src/ivfscan.o src/ivfutils.o src/ivfvacuum.o src/sparsevec.o src/vector.o -L/usr/lib/x86_64-linux-gnu  -Wl,-z,relro -Wl,-z,now -L/usr/lib/llvm-13/lib  -Wl,--as-needed  
1070:  #6 34.55 cp sql/vector.sql sql/vector--0.7.0.sql
...

1139:  #6 38.18 /usr/bin/install -c -m 644 src/ivfscan.bc '/usr/lib/postgresql/15/lib/bitcode'/vector/src/
1140:  #6 38.18 /usr/bin/install -c -m 644 src/ivfutils.bc '/usr/lib/postgresql/15/lib/bitcode'/vector/src/
1141:  #6 38.19 /usr/bin/install -c -m 644 src/ivfvacuum.bc '/usr/lib/postgresql/15/lib/bitcode'/vector/src/
1142:  #6 38.19 /usr/bin/install -c -m 644 src/sparsevec.bc '/usr/lib/postgresql/15/lib/bitcode'/vector/src/
1143:  #6 38.19 /usr/bin/install -c -m 644 src/vector.bc '/usr/lib/postgresql/15/lib/bitcode'/vector/src/
1144:  #6 38.19 cd '/usr/lib/postgresql/15/lib/bitcode' && /usr/lib/llvm-13/bin/llvm-lto -thinlto -thinlto-action=thinlink -o vector.index.bc vector/src/bitutils.bc vector/src/bitvec.bc vector/src/halfutils.bc vector/src/halfvec.bc vector/src/hnsw.bc vector/src/hnswbuild.bc vector/src/hnswinsert.bc vector/src/hnswscan.bc vector/src/hnswutils.bc vector/src/hnswvacuum.bc vector/src/ivfbuild.bc vector/src/ivfflat.bc vector/src/ivfinsert.bc vector/src/ivfkmeans.bc vector/src/ivfscan.bc vector/src/ivfutils.bc vector/src/ivfvacuum.bc vector/src/sparsevec.bc vector/src/vector.bc
1145:  #6 38.21 Reading package lists...
1146:  #6 38.61 Building dependency tree...
1147:  #6 38.70 Reading state information...
1148:  #6 38.79 The following packages were automatically installed and are no longer required:
1149:  #6 38.79   binutils binutils-common binutils-x86-64-linux-gnu bzip2 clang-16 cpp cpp-10
1150:  #6 38.79   dpkg-dev g++ g++-10 gcc gcc-10 git-man icu-devtools lib32gcc-s1 lib32stdc++6
1151:  #6 38.79   libasan6 libatomic1 libbinutils libc-dev-bin libc6-dev libc6-i386 libcc1-0
1152:  #6 38.79   libclang-common-13-dev libclang-common-16-dev libclang-cpp13 libclang-cpp16
1153:  #6 38.79   libclang1-13 libclang1-16 libcrypt-dev libctf-nobfd0 libctf0 libcurl4
1154:  #6 38.79   libdpkg-perl liberror-perl libffi-dev libgc1 libgcc-10-dev libgomp1
1155:  #6 38.79   libicu-dev libisl23 libitm1 libllvm16 liblsan0 libmpc3 libmpdec3
...

1190:  #6 38.95 Removing build-essential (12.9) ...
1191:  #6 38.96 Removing clang-13 (1:13.0.1-6~deb11u1) ...
1192:  #6 38.97 Removing git (1:2.30.2-1+deb11u5) ...
1193:  #6 39.03 Removing llvm-13 (1:13.0.1-6~deb11u1) ...
1194:  #6 39.06 Removing postgresql-server-dev-15 (15.17-1.pgdg11+1) ...
1195:  #6 39.11 dpkg: warning: while removing postgresql-server-dev-15, directory '/usr/include/postgresql/15/server/extension' not empty so not removed
1196:  #6 39.14 Reading package lists...
1197:  #6 39.53 Building dependency tree...
1198:  #6 39.63 Reading state information...
1199:  #6 39.73 The following packages will be REMOVED:
1200:  #6 39.73   binutils binutils-common binutils-x86-64-linux-gnu bzip2 clang-16 cpp cpp-10
1201:  #6 39.73   dpkg-dev g++ g++-10 gcc gcc-10 git-man icu-devtools lib32gcc-s1 lib32stdc++6
1202:  #6 39.73   libasan6 libatomic1 libbinutils libc-dev-bin libc6-dev libc6-i386 libcc1-0
1203:  #6 39.73   libclang-common-13-dev libclang-common-16-dev libclang-cpp13 libclang-cpp16
1204:  #6 39.73   libclang1-13 libclang1-16 libcrypt-dev libctf-nobfd0 libctf0 libcurl4
1205:  #6 39.73   libdpkg-perl liberror-perl libffi-dev libgc1 libgcc-10-dev libgomp1
1206:  #6 39.73   libicu-dev libisl23 libitm1 libllvm16 liblsan0 libmpc3 libmpdec3
...

1259:  #6 40.38 Removing libatomic1:amd64 (10.2.1-6) ...
1260:  #6 40.39 Removing libncurses-dev:amd64 (6.2+20201114-2+deb11u2) ...
1261:  #6 40.40 Removing libc6-dev:amd64 (2.31-13+deb11u13) ...
1262:  #6 40.44 Removing libc-dev-bin (2.31-13+deb11u13) ...
1263:  #6 40.45 Removing libc6-i386 (2.31-13+deb11u13) ...
1264:  #6 40.48 Removing libcc1-0:amd64 (10.2.1-6) ...
1265:  #6 40.49 Removing libclang-common-16-dev (1:16.0.6-15~deb11u2) ...
1266:  #6 40.51 Removing libclang-cpp13 (1:13.0.1-6~deb11u1) ...
1267:  #6 40.53 Removing libclang-cpp16 (1:16.0.6-15~deb11u2) ...
1268:  #6 40.54 Removing libclang1-13 (1:13.0.1-6~deb11u1) ...
1269:  #6 40.56 Removing libclang1-16 (1:16.0.6-15~deb11u2) ...
1270:  #6 40.57 Removing libcrypt-dev:amd64 (1:4.4.18-4) ...
1271:  #6 40.58 Removing llvm-16 (1:16.0.6-15~deb11u2) ...
1272:  #6 40.61 Removing libcurl4:amd64 (7.74.0-1.3+deb11u16) ...
1273:  #6 40.63 Removing libdpkg-perl (1.20.13) ...
1274:  #6 40.65 Removing liberror-perl (0.17029-1) ...
1275:  #6 40.66 Removing libffi-dev:amd64 (3.3-6) ...
...

1336:  �[36;1m  -e POSTGRES_PASSWORD=test_password \�[0m
1337:  �[36;1m  -e POSTGRES_DB=test_db \�[0m
1338:  �[36;1m  -p 5432:5432 \�[0m
1339:  �[36;1m  test-postgres�[0m
1340:  �[36;1m�[0m
1341:  �[36;1m# Wait for PostgreSQL to accept connections FROM THE HOST�[0m
1342:  �[36;1mecho "Waiting for PostgreSQL to accept host connections..."�[0m
1343:  �[36;1mfor i in {1..30}; do�[0m
1344:  �[36;1m  if pg_isready -h localhost -p 5432 -U test_user; then�[0m
1345:  �[36;1m    echo "PostgreSQL is ready and accepting host connections"�[0m
1346:  �[36;1m    exit 0�[0m
1347:  �[36;1m  fi�[0m
1348:  �[36;1m  echo "Waiting for PostgreSQL... ($i/30)"�[0m
1349:  �[36;1m  sleep 2�[0m
1350:  �[36;1mdone�[0m
1351:  �[36;1mecho "ERROR: PostgreSQL failed to become ready"�[0m
1352:  �[36;1mdocker logs test-postgres�[0m
...

1782:  tests/api/test_agent_endpoints.py::TestUpdateAgentAvailabilityEndpoint::test_update_availability PASSED [  1%]
1783:  tests/api/test_agent_endpoints.py::TestUpdateAgentAvailabilityEndpoint::test_update_availability_not_found PASSED [  1%]
1784:  tests/api/test_agent_endpoints.py::TestGetAgentMeEndpoint::test_get_agent_profile PASSED [  2%]
1785:  tests/api/test_amenity_endpoints.py::TestListAmenitiesEndpoint::test_list_amenities PASSED [  2%]
1786:  tests/api/test_amenity_endpoints.py::TestListAmenitiesEndpoint::test_list_amenities_empty PASSED [  2%]
1787:  tests/api/test_auth_endpoints.py::test_legacy_auth_endpoint_returns_not_found[/api/v1/auth/login/-payload0] PASSED [  2%]
1788:  tests/api/test_auth_endpoints.py::test_legacy_auth_endpoint_returns_not_found[/api/v1/auth/register/-payload1] PASSED [  2%]
1789:  tests/api/test_auth_endpoints.py::test_legacy_auth_endpoint_returns_not_found[/api/v1/auth/otp/request-payload2] PASSED [  2%]
1790:  tests/api/test_auth_endpoints.py::test_legacy_auth_endpoint_returns_not_found[/api/v1/auth/otp/verify-payload3] PASSED [  2%]
1791:  tests/api/test_auth_endpoints.py::test_legacy_auth_endpoint_returns_not_found[/api/v1/auth/refresh-payload4] PASSED [  2%]
1792:  tests/api/test_auth_endpoints.py::test_legacy_auth_endpoint_returns_not_found[/api/v1/auth/logout-payload5] PASSED [  2%]
1793:  tests/api/test_auth_endpoints.py::test_legacy_auth_endpoint_returns_not_found[/api/v1/auth/forgot-password-payload6] PASSED [  2%]
1794:  tests/api/test_auth_endpoints.py::test_legacy_auth_endpoint_returns_not_found[/api/v1/auth/verify-payload7] PASSED [  2%]
1795:  tests/api/test_blog_endpoints.py::TestBlogPostEndpoints::test_list_blog_posts PASSED [  2%]
1796:  tests/api/test_blog_endpoints.py::TestBlogPostEndpoints::test_list_blog_posts_with_filters PASSED [  2%]
1797:  tests/api/test_blog_endpoints.py::TestBlogPostEndpoints::test_list_blog_posts_returns_503_for_transient_db_error PASSED [  2%]
1798:  tests/api/test_blog_endpoints.py::TestBlogPostEndpoints::test_get_blog_post FAILED [  3%]
1799:  tests/api/test_blog_endpoints.py::TestBlogPostEndpoints::test_get_blog_post_not_found PASSED [  3%]
...

1873:  tests/api/test_oauth_endpoints.py::TestTokenResponseResource::test_token_response_omits_resource_when_absent PASSED [  8%]
1874:  tests/api/test_oauth_endpoints.py::TestTokenResponseResource::test_refresh_token_response_includes_resource PASSED [  8%]
1875:  tests/api/test_oauth_endpoints.py::TestWellKnownEndpoints::test_protected_resource_metadata_root PASSED [  8%]
1876:  tests/api/test_oauth_endpoints.py::TestWellKnownEndpoints::test_protected_resource_metadata_mcp PASSED [  8%]
1877:  tests/api/test_oauth_endpoints.py::TestWellKnownEndpoints::test_authorization_server_metadata_root PASSED [  8%]
1878:  tests/api/test_oauth_endpoints.py::TestWellKnownEndpoints::test_authorization_server_metadata_mcp_oauth PASSED [  8%]
1879:  tests/api/test_oauth_endpoints.py::TestWellKnownEndpoints::test_root_and_mcp_oauth_metadata_are_consistent PASSED [  8%]
1880:  tests/api/test_property_endpoints.py::TestCreateProperty::test_create_property_success PASSED [  8%]
1881:  tests/api/test_property_endpoints.py::TestCreateProperty::test_create_pg_property_rejects_non_rent_purpose PASSED [  9%]
1882:  tests/api/test_property_endpoints.py::TestCreateProperty::test_create_property_unauthenticated PASSED [  9%]
1883:  tests/api/test_property_endpoints.py::TestListProperties::test_list_properties_public PASSED [  9%]
1884:  tests/api/test_property_endpoints.py::TestListProperties::test_list_properties_with_filters PASSED [  9%]
1885:  tests/api/test_property_endpoints.py::TestListProperties::test_list_properties_with_location PASSED [  9%]
1886:  tests/api/test_property_endpoints.py::TestListProperties::test_list_properties_with_ids_filter PASSED [  9%]
1887:  tests/api/test_property_endpoints.py::TestListProperties::test_list_properties_with_listing_preference_filters PASSED [  9%]
1888:  tests/api/test_property_endpoints.py::TestListProperties::test_list_properties_returns_503_for_transient_db_error PASSED [  9%]
1889:  tests/api/test_property_endpoints.py::TestRecommendations::test_recommendations_returns_503_for_transient_db_error PASSED [  9%]
1890:  tests/api/test_property_endpoints.py::TestGetProperty::test_get_property_success PASSED [  9%]
...

1952:  tests/api/test_vastu_endpoints.py::TestVastuAnalyzeEndpoint::test_analyze_with_glm_provider PASSED [ 14%]
1953:  tests/api/test_vastu_endpoints.py::TestVastuHealthEndpoint::test_vastu_health_check PASSED [ 14%]
1954:  tests/api/test_visit_endpoints.py::TestCreateVisitEndpoint::test_create_visit_success PASSED [ 14%]
1955:  tests/api/test_visit_endpoints.py::TestCreateVisitEndpoint::test_create_visit_unauthorized PASSED [ 14%]
1956:  tests/api/test_visit_endpoints.py::TestGetVisitEndpoint::test_get_visit_success PASSED [ 14%]
1957:  tests/api/test_visit_endpoints.py::TestGetVisitEndpoint::test_get_visit_not_found PASSED [ 14%]
1958:  tests/api/test_visit_endpoints.py::TestGetUserVisitsEndpoint::test_get_user_visits PASSED [ 14%]
1959:  tests/api/test_visit_endpoints.py::TestGetUpcomingVisitsEndpoint::test_get_upcoming_visits PASSED [ 14%]
1960:  tests/api/test_visit_endpoints.py::TestGetPastVisitsEndpoint::test_get_past_visits PASSED [ 14%]
1961:  tests/api/test_visit_endpoints.py::TestCancelVisitEndpoint::test_cancel_visit_success PASSED [ 14%]
1962:  tests/api/test_visit_endpoints.py::TestCancelVisitEndpoint::test_cancel_visit_not_found PASSED [ 14%]
1963:  tests/api/test_visit_endpoints.py::TestRescheduleVisitEndpoint::test_reschedule_visit_success PASSED [ 15%]
1964:  tests/api/test_visit_endpoints.py::TestMarkVisitCompletedEndpoint::test_mark_visit_completed PASSED [ 15%]
1965:  tests/api/test_visit_endpoints.py::TestMarkVisitCompletedEndpoint::test_mark_visit_completed_forbidden PASSED [ 15%]
1966:  tests/e2e/test_booking_complete_flow.py::TestBookingCompleteFlow::test_search_check_book_flow PASSED [ 15%]
1967:  tests/e2e/test_booking_complete_flow.py::TestBookingManagementFlow::test_view_and_cancel_booking FAILED [ 15%]
1968:  tests/e2e/test_booking_complete_flow.py::TestBookingListingFlow::test_list_all_bookings PASSED [ 15%]
1969:  tests/e2e/test_booking_complete_flow.py::TestBookingListingFlow::test_list_upcoming_bookings PASSED [ 15%]
1970:  tests/e2e/test_booking_complete_flow.py::TestBookingStatusTransitions::test_confirm_booking PASSED [ 15%]
1971:  tests/e2e/test_booking_complete_flow.py::TestBookingStatusTransitions::test_check_in_booking PASSED [ 15%]
1972:  tests/e2e/test_pm_lifecycle_flow.py::TestPMLifecycleFlow::test_lease_to_rent_to_maintenance_flow FAILED [ 15%]
1973:  tests/e2e/test_property_listing_flow.py::TestPropertyListingFlow::test_create_and_list_property PASSED [ 15%]
1974:  tests/e2e/test_property_listing_flow.py::TestPropertySearchFlow::test_search_properties_by_location PASSED [ 15%]
1975:  tests/e2e/test_property_listing_flow.py::TestPropertySearchFlow::test_search_properties_by_filters PASSED [ 15%]
1976:  tests/e2e/test_property_listing_flow.py::TestPropertyViewFlow::test_view_property_details FAILED [ 15%]
1977:  tests/e2e/test_property_listing_flow.py::TestPropertyViewFlow::test_view_increments_counter FAILED [ 16%]
1978:  tests/e2e/test_property_listing_flow.py::TestPropertySwipeFlow::test_swipe_like_and_view_likes PASSED [ 16%]
...

1992:  tests/integration/test_full_text_search.py::TestSearchFilters::test_search_with_price_filter PASSED [ 17%]
1993:  tests/integration/test_full_text_search.py::TestSearchFilters::test_search_with_property_type_filter PASSED [ 17%]
1994:  tests/integration/test_geospatial_queries.py::TestPostGISRadiusSearch::test_properties_within_radius PASSED [ 17%]
1995:  tests/integration/test_geospatial_queries.py::TestPostGISRadiusSearch::test_properties_sorted_by_distance PASSED [ 17%]
1996:  tests/integration/test_geospatial_queries.py::TestPostGISRadiusSearch::test_no_properties_outside_radius PASSED [ 17%]
1997:  tests/integration/test_geospatial_queries.py::TestPostGISBoundingBox::test_properties_in_bounding_box PASSED [ 17%]
1998:  tests/integration/test_geospatial_queries.py::TestPostGISDistanceCalculation::test_st_distance_calculation PASSED [ 17%]
1999:  tests/integration/test_geospatial_queries.py::TestPostGISDistanceCalculation::test_st_dwithin_filter PASSED [ 17%]
2000:  tests/integration/test_geospatial_queries.py::TestLocationIndexing::test_spatial_index_exists PASSED [ 17%]
2001:  tests/integration/test_property_search.py::TestPropertySearchCombinations::test_city_and_purpose_filter PASSED [ 17%]
2002:  tests/integration/test_property_search.py::TestPropertySearchCombinations::test_city_and_type_filter PASSED [ 17%]
2003:  tests/integration/test_property_search.py::TestPropertySearchCombinations::test_purpose_and_type_filter PASSED [ 17%]
2004:  tests/integration/test_property_search.py::TestPropertySearchCombinations::test_price_range_filter PASSED [ 17%]
2005:  tests/integration/test_property_search.py::TestPropertySearchCombinations::test_bedroom_filter PASSED [ 18%]
2006:  tests/integration/test_property_search.py::TestPropertySearchCombinations::test_empty_results PASSED [ 18%]
2007:  tests/integration/test_property_search.py::TestPropertySearchCombinations::test_pagination FAILED [ 18%]
2008:  tests/integration/test_property_search.py::TestPropertySearchCombinations::test_special_listing_filters PASSED [ 18%]
2009:  tests/mcp/test_admin_mcp_server.py::TestAgentPropertyTools::test_agent_properties_list_authenticated FAILED [ 18%]
2010:  tests/mcp/test_admin_mcp_server.py::TestAgentPropertyTools::test_agent_properties_list_unauthorized FAILED [ 18%]
2011:  tests/mcp/test_admin_mcp_server.py::TestAgentLeaseTools::test_agent_leases_list FAILED [ 18%]
2012:  tests/mcp/test_admin_mcp_server.py::TestAgentLeaseTools::test_agent_leases_create FAILED [ 18%]
2013:  tests/mcp/test_admin_mcp_server.py::TestAgentRentTools::test_agent_rent_list_due FAILED [ 18%]
2014:  tests/mcp/test_admin_mcp_server.py::TestAgentRentTools::test_agent_rent_record_payment FAILED [ 18%]
2015:  tests/mcp/test_admin_mcp_server.py::TestAgentMaintenanceTools::test_agent_maintenance_list FAILED [ 18%]
2016:  tests/mcp/test_admin_mcp_server.py::TestAgentMaintenanceTools::test_agent_maintenance_update_status FAILED [ 18%]
2017:  tests/mcp/test_admin_mcp_server.py::TestAgentDashboardTools::test_agent_dashboard_overview FAILED [ 18%]
2018:  tests/mcp/test_admin_mcp_server.py::TestAdminTools::test_admin_system_status PASSED [ 18%]
2019:  tests/mcp/test_admin_mcp_server.py::TestAdminTools::test_admin_system_status_non_admin PASSED [ 19%]
2020:  tests/mcp/test_admin_mcp_server.py::TestAgentBookingTools::test_agent_bookings_list_all FAILED [ 19%]
2021:  tests/mcp/test_admin_mcp_server.py::TestAgentBookingTools::test_agent_bookings_update_status FAILED [ 19%]
2022:  tests/mcp/test_mcp_integration.py::TestMCPServerIntegration::test_mcp_endpoint_mounted PASSED [ 19%]
...

2029:  tests/mcp/test_mcp_integration.py::TestMCPToolsIntegration::test_discovery_amenities PASSED [ 19%]
2030:  tests/mcp/test_mcp_integration.py::TestMCPAuthIntegration::test_token_verifier_valid_oauth_token PASSED [ 19%]
2031:  tests/mcp/test_mcp_integration.py::TestMCPAuthIntegration::test_token_verifier_invalid_token_format PASSED [ 19%]
2032:  tests/mcp/test_mcp_integration.py::TestMCPAuthIntegration::test_token_verifier_expired_token PASSED [ 20%]
2033:  tests/mcp/test_mcp_integration.py::TestMCPAuthIntegration::test_token_verifier_audience_mismatch PASSED [ 20%]
2034:  tests/mcp/test_mcp_integration.py::TestMCPAuthIntegration::test_token_verifier_missing_scope PASSED [ 20%]
2035:  tests/mcp/test_mcp_integration.py::TestMCPOAuthTokenStore::test_store_and_retrieve_auth_code PASSED [ 20%]
2036:  tests/mcp/test_mcp_integration.py::TestMCPOAuthTokenStore::test_store_and_retrieve_tokens PASSED [ 20%]
2037:  tests/mcp/test_mcp_integration.py::TestMCPOAuthTokenStore::test_revoke_token PASSED [ 20%]
2038:  tests/mcp/test_mcp_integration.py::TestMCPOAuthTokenStore::test_refresh_token_rotation PASSED [ 20%]
2039:  tests/mcp/test_mcp_integration.py::TestMCPWidgetIntegration::test_widget_registration PASSED [ 20%]
2040:  tests/mcp/test_mcp_integration.py::TestMCPWidgetIntegration::test_widget_tool_mapping PASSED [ 20%]
2041:  tests/mcp/test_mcp_integration.py::TestMCPWidgetIntegration::test_get_widget_for_tool PASSED [ 20%]
2042:  tests/mcp/test_mcp_integration.py::TestMCPWidgetIntegration::test_all_discovery_tools_have_widgets PASSED [ 20%]
2043:  tests/mcp/test_mcp_integration.py::TestMCPEndToEnd::test_complete_property_workflow PASSED [ 20%]
2044:  tests/mcp/test_mcp_integration.py::TestMCPEndToEnd::test_agent_property_management_workflow FAILED [ 20%]
2045:  tests/mcp/test_mcp_integration.py::TestMCPEndToEnd::test_owner_property_workflow PASSED [ 20%]
2046:  tests/mcp/test_mcp_integration.py::TestMCPEndToEnd::test_tenant_workflow PASSED [ 21%]
2047:  tests/mcp/test_user_mcp_server.py::TestOwnerPropertyTools::test_owner_properties_list_authenticated FAILED [ 21%]
2048:  tests/mcp/test_user_mcp_server.py::TestOwnerPropertyTools::test_owner_properties_list_unauthenticated PASSED [ 21%]
2049:  tests/mcp/test_user_mcp_server.py::TestOwnerPropertyTools::test_owner_properties_list_www_authenticate_meta PASSED [ 21%]
2050:  tests/mcp/test_user_mcp_server.py::TestOwnerPropertyTools::test_tools_list_includes_security_schemes_and_template PASSED [ 21%]
2051:  tests/mcp/test_user_mcp_server.py::TestOwnerPropertyCreate::test_create_property_success FAILED [ 21%]
2052:  tests/mcp/test_user_mcp_server.py::TestTenantTools::test_tenant_lease_current PASSED [ 21%]
2053:  tests/mcp/test_user_mcp_server.py::TestTenantTools::test_tenant_rent_history PASSED [ 21%]
2054:  tests/mcp/test_user_mcp_server.py::TestBookingTools::test_bookings_list FAILED [ 21%]
2055:  tests/mcp/test_user_mcp_server.py::TestBookingTools::test_bookings_check_availability FAILED [ 21%]
2056:  tests/mcp/test_user_mcp_server.py::TestMCPErrorResponses::test_unauthorized_response PASSED [ 21%]
2057:  tests/mcp/test_user_mcp_server.py::TestMCPErrorResponses::test_not_found_response PASSED [ 21%]
2058:  tests/mcp/test_user_mcp_server.py::TestMCPErrorResponses::test_invalid_input_response PASSED [ 21%]
2059:  tests/middleware/test_rate_limit_middleware.py::TestRateLimitMiddleware::test_middleware_initialization PASSED [ 21%]
...

2072:  tests/middleware/test_security_middleware.py::TestXSSProtection::test_xss_protection_header PASSED [ 22%]
2073:  tests/middleware/test_security_middleware.py::TestContentTypeOptions::test_x_content_type_options_header PASSED [ 22%]
2074:  tests/middleware/test_security_middleware.py::TestFrameOptions::test_x_frame_options_header PASSED [ 23%]
2075:  tests/middleware/test_security_middleware.py::TestContentSecurityPolicy::test_csp_header PASSED [ 23%]
2076:  tests/middleware/test_security_middleware.py::TestHSTS::test_hsts_header_in_production PASSED [ 23%]
2077:  tests/middleware/test_trailing_slash_middleware.py::TestMCPMountPaths::test_mcp_mount_paths_do_not_include_sse PASSED [ 23%]
2078:  tests/middleware/test_trailing_slash_middleware.py::TestTrailingSlashAddition::test_adds_trailing_slash_for_mcp PASSED [ 23%]
2079:  tests/middleware/test_trailing_slash_middleware.py::TestTrailingSlashAddition::test_adds_trailing_slash_for_mcp_admin PASSED [ 23%]
2080:  tests/middleware/test_trailing_slash_middleware.py::TestTrailingSlashAddition::test_mcp_subpaths_unchanged PASSED [ 23%]
2081:  tests/middleware/test_trailing_slash_middleware.py::TestTrailingSlashStripping::test_strips_trailing_slash_for_api_routes PASSED [ 23%]
2082:  tests/middleware/test_trailing_slash_middleware.py::TestTrailingSlashStripping::test_preserves_api_root PASSED [ 23%]
2083:  tests/middleware/test_trailing_slash_middleware.py::TestTrailingSlashStripping::test_no_trailing_slash_unchanged PASSED [ 23%]
2084:  tests/middleware/test_trailing_slash_middleware.py::TestTrailingSlashStripping::test_deep_api_path_stripped PASSED [ 23%]
2085:  tests/middleware/test_trailing_slash_middleware.py::TestNonHTTPScopes::test_websocket_scope_unchanged PASSED [ 23%]
2086:  tests/middleware/test_trailing_slash_middleware.py::TestNonHTTPScopes::test_lifespan_scope_unchanged PASSED [ 23%]
2087:  tests/pm/test_authz.py::test_pm_authz_owner_agent_tenant_property_access ERROR [ 24%]
2088:  tests/pm/test_rent.py::test_generate_rent_charges_idempotent_and_payment_status ERROR [ 24%]
2089:  tests/unit/api/test_agent_chat_endpoint.py::test_agent_chat_persists_widget_and_assistant_messages PASSED [ 24%]
2090:  tests/unit/api/test_agent_chat_endpoint.py::test_agent_chat_persists_empty_assistant_when_only_widget_event PASSED [ 24%]
2091:  tests/unit/api/test_agent_chat_endpoint.py::test_agent_chat_ignores_malformed_widget_event PASSED [ 24%]
2092:  tests/unit/api/test_agent_chat_endpoint.py::test_agent_chat_stream_error_emits_error_event PASSED [ 24%]
2093:  tests/unit/api/test_agent_chat_endpoint.py::test_list_conversations_delegates_to_store_and_respects_pagination PASSED [ 24%]
...

2126:  tests/unit/api/test_flatmates_admin.py::TestGetPendingListingsPagination::test_returns_listings_with_pagination PASSED [ 26%]
2127:  tests/unit/api/test_flatmates_admin.py::TestGetPendingReportsPagination::test_returns_reports_with_pagination PASSED [ 26%]
2128:  tests/unit/api/test_flatmates_admin.py::TestGetPendingReportsPagination::test_empty_reports PASSED [ 26%]
2129:  tests/unit/api/test_flatmates_admin.py::TestPrescreenListing::test_admin_can_prescreen PASSED [ 27%]
2130:  tests/unit/api/test_flatmates_admin.py::TestPrescreenListing::test_non_admin_gets_403 PASSED [ 27%]
2131:  tests/unit/app/test_app_composition.py::test_openapi_paths_match_refactor_baseline PASSED [ 27%]
2132:  tests/unit/app/test_app_composition.py::test_mcp_mount_paths_are_registered PASSED [ 27%]
2133:  tests/unit/app/test_app_composition.py::test_base_api_exception_envelope_is_preserved PASSED [ 27%]
2134:  tests/unit/app/test_app_composition.py::test_legacy_request_id_filter_import_remains_compatible PASSED [ 27%]
2135:  tests/unit/app/test_app_composition.py::test_canonical_domain_modules_expose_existing_property_service PASSED [ 27%]
2136:  tests/unit/core/test_auth.py::TestGetSupabaseClients::test_get_supabase_auth_client_creates_singleton PASSED [ 27%]
2137:  tests/unit/core/test_auth.py::TestGetSupabaseClients::test_get_supabase_auth_client_requires_publishable_key PASSED [ 27%]
2138:  tests/unit/core/test_auth.py::TestGetSupabaseClients::test_get_supabase_service_client_creates_singleton PASSED [ 27%]
2139:  tests/unit/core/test_auth.py::TestGetSupabaseClients::test_get_supabase_storage_client_creates_singleton PASSED [ 27%]
2140:  tests/unit/core/test_auth.py::TestVerifySupabaseToken::test_verify_token_success PASSED [ 27%]
2141:  tests/unit/core/test_auth.py::TestVerifySupabaseToken::test_verify_token_failure_returns_none PASSED [ 27%]
2142:  tests/unit/core/test_auth.py::TestVerifySupabaseToken::test_verify_token_missing_id_returns_none PASSED [ 27%]
2143:  tests/unit/core/test_auth.py::TestVerifySupabaseToken::test_verify_token_network_error_returns_none PASSED [ 28%]
2144:  tests/unit/core/test_auth.py::TestAdminFindUserByPhone::test_find_user_success PASSED [ 28%]
...

2163:  tests/unit/core/test_cache_manager.py::TestNullCacheBackend::test_connect_is_noop PASSED [ 29%]
2164:  tests/unit/core/test_cache_manager.py::TestNullCacheBackend::test_disconnect_is_noop PASSED [ 29%]
2165:  tests/unit/core/test_cache_manager.py::TestNullCacheBackend::test_is_available_returns_false PASSED [ 29%]
2166:  tests/unit/core/test_cache_manager.py::TestCacheBackendType::test_memory_backend_type PASSED [ 29%]
2167:  tests/unit/core/test_cache_manager.py::TestCacheBackendType::test_redis_backend_type PASSED [ 29%]
2168:  tests/unit/core/test_cache_manager.py::TestCacheManager::test_create_from_config_memory PASSED [ 29%]
2169:  tests/unit/core/test_cache_manager.py::TestCacheManager::test_create_from_config_redis PASSED [ 29%]
2170:  tests/unit/core/test_cache_manager.py::TestCacheManager::test_get_delegates_to_backend PASSED [ 30%]
2171:  tests/unit/core/test_cache_manager.py::TestCacheManager::test_set_delegates_to_backend PASSED [ 30%]
2172:  tests/unit/core/test_cache_manager.py::TestCacheManager::test_delete_delegates_to_backend PASSED [ 30%]
2173:  tests/unit/core/test_cache_manager.py::TestCacheManager::test_delete_pattern_delegates_to_backend PASSED [ 30%]
2174:  tests/unit/core/test_cache_manager.py::TestCacheManager::test_exists_delegates_to_backend PASSED [ 30%]
2175:  tests/unit/core/test_cache_manager.py::TestCacheManager::test_clear_delegates_to_backend PASSED [ 30%]
2176:  tests/unit/core/test_cache_manager.py::TestCacheManager::test_is_available_checks_backend PASSED [ 30%]
2177:  tests/unit/core/test_cache_manager.py::TestCacheManager::test_uses_fallback_when_primary_unavailable PASSED [ 30%]
2178:  tests/unit/core/test_cache_manager.py::TestCacheManager::test_connect_sets_fallback_on_primary_failure PASSED [ 30%]
2179:  tests/unit/core/test_cache_manager.py::TestCacheManager::test_disconnect_closes_all_backends PASSED [ 30%]
...

2199:  tests/unit/core/test_config.py::TestSettings::test_vastu_default_provider PASSED [ 32%]
2200:  tests/unit/core/test_config.py::TestSettings::test_supabase_client_key_returns_publishable_key PASSED [ 32%]
2201:  tests/unit/core/test_config.py::TestSettings::test_auto_blog_defaults PASSED [ 32%]
2202:  tests/unit/core/test_config.py::TestSettings::test_auto_blog_publisher_user_id_blank_string_is_treated_as_none PASSED [ 32%]
2203:  tests/unit/core/test_constants.py::TestVisionConstants::test_valid_vision_providers PASSED [ 32%]
2204:  tests/unit/core/test_constants.py::TestVisionConstants::test_default_vision_provider_is_glm PASSED [ 32%]
2205:  tests/unit/core/test_constants.py::TestVisionConstants::test_default_vision_model_gemini PASSED [ 32%]
2206:  tests/unit/core/test_constants.py::TestVisionConstants::test_default_vision_model_glm PASSED [ 32%]
2207:  tests/unit/core/test_constants.py::TestVisionConstants::test_valid_providers_are_tuple PASSED [ 32%]
2208:  tests/unit/core/test_constants.py::TestVisionConstants::test_fallback_provider_default PASSED [ 32%]
2209:  tests/unit/core/test_constants.py::TestConstantsDeriveFromSettings::test_gemini_model_fallback_when_settings_none PASSED [ 32%]
2210:  tests/unit/core/test_constants.py::TestConstantsDeriveFromSettings::test_glm_model_fallback_when_settings_none PASSED [ 32%]
2211:  tests/unit/core/test_constants.py::TestConstantsDeriveFromSettings::test_fallback_provider_empty_when_not_set PASSED [ 32%]
2212:  tests/unit/core/test_db_resilience.py::test_execute_with_transient_retry_succeeds_on_second_attempt PASSED [ 33%]
2213:  tests/unit/core/test_db_resilience.py::test_execute_with_transient_retry_does_not_retry_non_transient PASSED [ 33%]
2214:  tests/unit/core/test_db_resilience.py::test_transient_db_error_detection_and_code_extraction PASSED [ 33%]
2215:  tests/unit/core/test_docs_contracts.py::test_docs_contract_validation_passes_for_current_repo PASSED [ 33%]
...

2217:  tests/unit/core/test_exceptions.py::TestBaseAPIException::test_default_status_code PASSED [ 33%]
2218:  tests/unit/core/test_exceptions.py::TestBaseAPIException::test_default_detail PASSED [ 33%]
2219:  tests/unit/core/test_exceptions.py::TestBaseAPIException::test_custom_detail PASSED [ 33%]
2220:  tests/unit/core/test_exceptions.py::TestBaseAPIException::test_custom_headers PASSED [ 33%]
2221:  tests/unit/core/test_exceptions.py::TestBaseAPIException::test_extra_kwargs PASSED [ 33%]
2222:  tests/unit/core/test_exceptions.py::TestNotFoundExceptions::test_not_found_exception PASSED [ 33%]
2223:  tests/unit/core/test_exceptions.py::TestNotFoundExceptions::test_property_not_found PASSED [ 33%]
2224:  tests/unit/core/test_exceptions.py::TestNotFoundExceptions::test_property_not_found_custom_message PASSED [ 33%]
2225:  tests/unit/core/test_exceptions.py::TestNotFoundExceptions::test_user_not_found PASSED [ 34%]
2226:  tests/unit/core/test_exceptions.py::TestNotFoundExceptions::test_agent_not_found PASSED [ 34%]
2227:  tests/unit/core/test_exceptions.py::TestNotFoundExceptions::test_booking_not_found PASSED [ 34%]
2228:  tests/unit/core/test_exceptions.py::TestNotFoundExceptions::test_visit_not_found PASSED [ 34%]
2229:  tests/unit/core/test_exceptions.py::TestAuthExceptions::test_unauthorized_exception PASSED [ 34%]
2230:  tests/unit/core/test_exceptions.py::TestAuthExceptions::test_forbidden_exception PASSED [ 34%]
2231:  tests/unit/core/test_exceptions.py::TestAuthExceptions::test_insufficient_permissions PASSED [ 34%]
2232:  tests/unit/core/test_exceptions.py::TestAuthExceptions::test_property_ownership_error PASSED [ 34%]
2233:  tests/unit/core/test_exceptions.py::TestValidationExceptions::test_validation_exception PASSED [ 34%]
2234:  tests/unit/core/test_exceptions.py::TestValidationExceptions::test_bad_request_exception PASSED [ 34%]
2235:  tests/unit/core/test_exceptions.py::TestConflictExceptions::test_conflict_exception PASSED [ 34%]
2236:  tests/unit/core/test_exceptions.py::TestConflictExceptions::test_booking_conflict_error PASSED [ 34%]
2237:  tests/unit/core/test_exceptions.py::TestConflictExceptions::test_duplicate_swipe_error PASSED [ 34%]
2238:  tests/unit/core/test_exceptions.py::TestRateLimitException::test_rate_limit_exception PASSED [ 34%]
2239:  tests/unit/core/test_exceptions.py::TestServiceUnavailableException::test_service_unavailable_exception PASSED [ 35%]
2240:  tests/unit/core/test_exceptions.py::TestPayloadExceptions::test_file_too_large_exception PASSED [ 35%]
2241:  tests/unit/core/test_exceptions.py::TestPayloadExceptions::test_invalid_file_exception PASSED [ 35%]
2242:  tests/unit/core/test_exceptions.py::TestExternalAndStorageExceptions::test_external_service_error PASSED [ 35%]
2243:  tests/unit/core/test_exceptions.py::TestExternalAndStorageExceptions::test_storage_exception PASSED [ 35%]
...

2247:  tests/unit/core/test_exceptions.py::TestBlogExceptions::test_blog_not_found PASSED [ 35%]
2248:  tests/unit/core/test_exceptions.py::TestBlogExceptions::test_category_not_found PASSED [ 35%]
2249:  tests/unit/core/test_exceptions.py::TestBlogExceptions::test_tag_not_found PASSED [ 35%]
2250:  tests/unit/core/test_exceptions.py::TestPMExceptions::test_lease_not_found PASSED [ 35%]
2251:  tests/unit/core/test_exceptions.py::TestPMExceptions::test_maintenance_request_not_found PASSED [ 35%]
2252:  tests/unit/core/test_exceptions.py::TestExceptionInheritance::test_domain_exceptions_inherit_from_base PASSED [ 35%]
2253:  tests/unit/core/test_exceptions.py::TestExceptionInheritance::test_exceptions_are_http_exceptions PASSED [ 36%]
2254:  tests/unit/core/test_logging.py::TestColorFormatter::test_format_returns_string PASSED [ 36%]
2255:  tests/unit/core/test_logging.py::TestColorFormatter::test_format_with_colors_enabled PASSED [ 36%]
2256:  tests/unit/core/test_logging.py::TestColorFormatter::test_format_without_colors PASSED [ 36%]
2257:  tests/unit/core/test_logging.py::TestColorFormatter::test_name_map_cleans_logger_names PASSED [ 36%]
2258:  tests/unit/core/test_logging.py::TestColorFormatter::test_extras_appended_as_key_value PASSED [ 36%]
2259:  tests/unit/core/test_logging.py::TestColorFormatter::test_level_color_mapping[DEBUG-\x1b[36m] PASSED [ 36%]
2260:  tests/unit/core/test_logging.py::TestColorFormatter::test_level_color_mapping[INFO-\x1b[32m] PASSED [ 36%]
2261:  tests/unit/core/test_logging.py::TestColorFormatter::test_level_color_mapping[WARNING-\x1b[33m] PASSED [ 36%]
2262:  tests/unit/core/test_logging.py::TestColorFormatter::test_level_color_mapping[ERROR-\x1b[31m] PASSED [ 36%]
2263:  tests/unit/core/test_logging.py::TestStructuredFormatter::test_format_returns_valid_json PASSED [ 36%]
...

2281:  tests/unit/core/test_utils.py::TestUtcNow::test_returns_timezone_aware_datetime PASSED [ 38%]
2282:  tests/unit/core/test_utils.py::TestUtcNow::test_returns_utc_timezone PASSED [ 38%]
2283:  tests/unit/core/test_utils.py::TestUtcNow::test_returns_recent_time PASSED [ 38%]
2284:  tests/unit/core/test_utils.py::TestUtcNowIso::test_returns_string PASSED [ 38%]
2285:  tests/unit/core/test_utils.py::TestUtcNowIso::test_is_parseable_as_iso8601 PASSED [ 38%]
2286:  tests/unit/core/test_utils.py::TestMakeTzAware::test_none_returns_none PASSED [ 38%]
2287:  tests/unit/core/test_utils.py::TestMakeTzAware::test_naive_datetime_gets_utc PASSED [ 38%]
2288:  tests/unit/core/test_utils.py::TestMakeTzAware::test_aware_datetime_stays_in_utc PASSED [ 38%]
2289:  tests/unit/core/test_utils.py::TestMakeTzAware::test_non_utc_timezone_converted_to_utc PASSED [ 38%]
2290:  tests/unit/core/test_utils.py::TestMakeTzAware::test_various_naive_dates[2024-1-1-0] PASSED [ 38%]
2291:  tests/unit/core/test_utils.py::TestMakeTzAware::test_various_naive_dates[2025-6-15-12] PASSED [ 38%]
2292:  tests/unit/core/test_utils.py::TestMakeTzAware::test_various_naive_dates[2025-12-31-23] PASSED [ 38%]
2293:  tests/unit/core/test_websocket.py::TestJobConnections::test_connect_job PASSED [ 38%]
2294:  tests/unit/core/test_websocket.py::TestJobConnections::test_disconnect_job PASSED [ 39%]
2295:  tests/unit/core/test_websocket.py::TestJobConnections::test_disconnect_job_removes_empty_set PASSED [ 39%]
2296:  tests/unit/core/test_websocket.py::TestJobConnections::test_disconnect_nonexistent_job_no_error PASSED [ 39%]
2297:  tests/unit/core/test_websocket.py::TestJobConnections::test_multiple_connections_same_job PASSED [ 39%]
2298:  tests/unit/core/test_websocket.py::TestJobConnections::test_send_job_update PASSED [ 39%]
2299:  tests/unit/core/test_websocket.py::TestJobConnections::test_send_job_update_no_connections PASSED [ 39%]
2300:  tests/unit/core/test_websocket.py::TestJobConnections::test_send_job_update_dead_connection_cleaned_up PASSED [ 39%]
2301:  tests/unit/core/test_websocket.py::TestJobConnections::test_get_job_connection_count PASSED [ 39%]
2302:  tests/unit/core/test_websocket.py::TestUserConnections::test_connect_user PASSED [ 39%]
2303:  tests/unit/core/test_websocket.py::TestUserConnections::test_disconnect_user PASSED [ 39%]
2304:  tests/unit/core/test_websocket.py::TestUserConnections::test_send_user_notification PASSED [ 39%]
2305:  tests/unit/core/test_websocket.py::TestUserConnections::test_send_user_notification_no_connections PASSED [ 39%]
2306:  tests/unit/core/test_websocket.py::TestUserConnections::test_get_user_connection_count PASSED [ 39%]
2307:  tests/unit/core/test_websocket.py::TestBroadcasts::test_broadcast_job_completion PASSED [ 39%]
2308:  tests/unit/core/test_websocket.py::TestBroadcasts::test_broadcast_job_error PASSED [ 40%]
2309:  tests/unit/mcp/test_apps_sdk.py::TestResourceMimeType::test_mime_type_format PASSED [ 40%]
2310:  tests/unit/mcp/test_apps_sdk.py::TestResourceMimeType::test_mime_type_contains_html PASSED [ 40%]
2311:  tests/unit/mcp/test_apps_sdk.py::TestResourceMimeType::test_mime_type_contains_profile PASSED [ 40%]
2312:  tests/unit/mcp/test_apps_sdk.py::TestSecuritySchemes::test_mixed_schemes_includes_noauth PASSED [ 40%]
2313:  tests/unit/mcp/test_apps_sdk.py::TestSecuritySchemes::test_mixed_schemes_includes_oauth2 PASSED [ 40%]
2314:  tests/unit/mcp/test_apps_sdk.py::TestSecuritySchemes::test_oauth2_only_has_no_noauth PASSED [ 40%]
2315:  tests/unit/mcp/test_apps_sdk.py::TestSecuritySchemes::test_oauth2_only_has_oauth2 PASSED [ 40%]
2316:  tests/unit/mcp/test_apps_sdk.py::TestSecuritySchemes::test_oauth2_scopes_present PASSED [ 40%]
2317:  tests/unit/mcp/test_apps_sdk.py::TestAppsSDKToolResult::test_default_not_error PASSED [ 40%]
2318:  tests/unit/mcp/test_apps_sdk.py::TestAppsSDKToolResult::test_error_result PASSED [ 40%]
2319:  tests/unit/mcp/test_apps_sdk.py::TestAppsSDKToolResult::test_structured_content PASSED [ 40%]
2320:  tests/unit/mcp/test_apps_sdk.py::TestAppsSDKToolResult::test_meta_content PASSED [ 40%]
2321:  tests/unit/mcp/test_apps_sdk.py::TestBuildWidgetToolMeta::test_returns_dict PASSED [ 40%]
2322:  tests/unit/mcp/test_apps_sdk.py::TestBuildWidgetToolMeta::test_includes_ui_resource_uri PASSED [ 41%]
2323:  tests/unit/mcp/test_apps_sdk.py::TestBuildWidgetToolMeta::test_includes_openai_compat_keys PASSED [ 41%]
2324:  tests/unit/mcp/test_apps_sdk.py::TestBuildWidgetToolMeta::test_empty_widget_uri PASSED [ 41%]
2325:  tests/unit/mcp/test_apps_sdk.py::TestBuildWidgetToolMeta::test_custom_visibility PASSED [ 41%]
2326:  tests/unit/mcp/test_errors.py::TestMCPErrorCode::test_auth_error_codes PASSED [ 41%]
2327:  tests/unit/mcp/test_errors.py::TestMCPErrorCode::test_validation_error_codes PASSED [ 41%]
2328:  tests/unit/mcp/test_errors.py::TestMCPErrorCode::test_resource_error_codes PASSED [ 41%]
2329:  tests/unit/mcp/test_errors.py::TestMCPErrorCode::test_system_error_codes PASSED [ 41%]
2330:  tests/unit/mcp/test_errors.py::TestMCPError::test_create_error PASSED    [ 41%]
2331:  tests/unit/mcp/test_errors.py::TestMCPError::test_error_with_details PASSED [ 41%]
2332:  tests/unit/mcp/test_errors.py::TestMCPResponse::test_success_response PASSED [ 41%]
2333:  tests/unit/mcp/test_errors.py::TestMCPResponse::test_failure_response PASSED [ 41%]
2334:  tests/unit/mcp/test_errors.py::TestMCPResponse::test_model_dump_excludes_none PASSED [ 41%]
2335:  tests/unit/mcp/test_errors.py::TestMCPResponse::test_failure_model_dump PASSED [ 41%]
2336:  tests/unit/mcp/test_errors.py::TestHelperFunctions::test_invalid_input_response PASSED [ 42%]
2337:  tests/unit/mcp/test_errors.py::TestHel...

Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

View 11 additional findings in Devin Review.

Open in Devin Review

Comment on lines +139 to +143
# Snapshot IDs needed after the session is released
conversation_id = conversation.id

# Release the main-pool session — streaming may take minutes
await db.close()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 User message and new conversation rolled back by premature db.close() in authenticated chat endpoint

In the authenticated /chat endpoint, conversation_store.add_message(db, ...) flushes the user's message to the main-pool session at line 127, and get_or_create_conversation(db, ...) may create a new conversation at line 116. Then at line 143, await db.close() is called, which closes the session and causes the connection pool to roll back the uncommitted transaction — discarding both the user message and any newly created conversation.

After streaming completes, the persistence code at app/api/api_v1/endpoints/agent_chat.py:183 opens a fresh_db session but only persists widget events and the assistant response — the user message is never re-persisted.

Impact by scenario

Existing conversation (body.conversation_id provided): The conversation row exists in the DB, so conversation_id is valid. The assistant response is persisted, but the user's question is permanently lost. The conversation history is incomplete.

New conversation (no body.conversation_id): The conversation was created on db and rolled back. conversation_id references a non-existent row. The add_message(fresh_db, conversation_id=conversation_id, ...) call at line 195 fails with a foreign key violation, which is caught and logged at line 200. Neither the user message nor the assistant response is persisted.

The old code (before this PR) kept db open throughout streaming and committed everything (user message + assistant response) together in the finally block. The new code splits sessions but fails to commit or re-persist the user message before closing the first session.

Prompt for agents
The bug is that the user message and (potentially new) conversation are flushed to the main-pool DB session but never committed before db.close() rolls them back at line 143. The post-streaming persistence code on fresh_db only persists widget events and assistant response, not the user message.

To fix this, you need to commit the main-pool session before closing it, so the user message and conversation are persisted. Add await db.commit() before await db.close() at line 143 in app/api/api_v1/endpoints/agent_chat.py. This ensures the user message (added at line 127-131) and the conversation (created or updated at line 116-121) are committed to the database before the session is released.

Alternatively, you could re-persist the user message on fresh_db after streaming, but committing before close is simpler and preserves the original semantics where the user message is guaranteed to be stored even if streaming fails.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread app/modules/__init__.py
Comment on lines +1 to +11
"""Domain modules placeholder.

Domain code will be migrated here as part of the long-term architecture
evolution. Currently, domain code lives in the legacy locations:
- app/api/ — REST endpoints
- app/services/ — Business logic
- app/models/ — ORM models
- app/schemas/ — Pydantic schemas
- app/repositories/— Data access
- app/mcp/ — MCP tools
"""
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. app/modules placeholder package added 📘 Rule violation ⚙ Maintainability

New placeholder packages were introduced under reserved directories (app/modules/ and
app/shared/) despite no actual domain migration or real usage occurring. This violates the
repository policy against shim/placeholder packages in reserved locations and risks architectural
drift and confusing/unused structure.
Agent Prompt
## Issue description
The PR introduces placeholder/shim packages under reserved directories (`app/modules/` and `app/shared/`), which is not allowed unless there is an actual domain migration or concrete shared usage.

## Issue Context
PR Compliance ID 15 forbids using reserved directories like `app/modules/` and `app/shared/` for shim-only re-exports or placeholder entrypoints. The added `__init__.py` files are explicitly placeholders (and in the case of `app/shared/`, a redirect), rather than real migrated/shared code.

## Fix Focus Areas
- app/modules/__init__.py[1-11]
- app/shared/__init__.py[1-9]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread app/core/config.py
Comment on lines 234 to 238
model_config = SettingsConfigDict(
env_file=str(BASE_DIR / ".env"),
env_file=str(BASE_DIR / _ENV_FILE),
case_sensitive=True,
extra="ignore",
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Env file ignored 🐞 Bug ☼ Reliability

SettingsConfigDict now loads env vars from .env.dev/.env.test/.env.prod based on the OS
ENVIRONMENT and no longer loads .env, but README/CLAUDE still instruct creating .env, causing
the app to start with missing/default config. Because the env-file choice is made before env-file
parsing, setting ENVIRONMENT inside .env cannot work (bootstrapping deadlock).
Agent Prompt
## Issue description
`app/core/config.py` selects an environment-specific env file (`.env.dev`, `.env.test`, `.env.prod`) using `os.getenv("ENVIRONMENT")` and then configures Pydantic settings to load only that file. This breaks the documented setup (`cp .env.example .env`) and prevents `ENVIRONMENT` from being configured inside the env file itself.

## Issue Context
- Docs currently instruct copying `.env.example` to `.env`.
- The code defaults to `.env.dev` unless `ENVIRONMENT` is set in the OS environment, so `.env` is effectively ignored.

## Fix Focus Areas
- app/core/config.py[9-16]
- app/core/config.py[234-238]
- README.md[219-223]
- CLAUDE.md[66-67]

## Proposed fix
- Update settings loading to always include `.env` as a base file (backward compatible), and optionally overlay the env-specific file.
  - Example: `env_file=(str(BASE_DIR / ".env"), str(BASE_DIR / _ENV_FILE))` (or a list/tuple depending on pydantic-settings support).
  - Alternatively/additionally support an explicit `ENV_FILE` OS variable to override.
- Align docs with the intended behavior (either keep `.env` as the primary path, or update README/CLAUDE to instruct `.env.dev` etc., but avoid the bootstrapping trap where `ENVIRONMENT` must be set before env loading).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 327 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="app/api/api_v1/endpoints/public.py">

<violation number="1" location="app/api/api_v1/endpoints/public.py:48">
P2: The periodic cleanup only deletes empty buckets, so inactive IP entries with expired timestamps are retained indefinitely and the rate-limit map can grow without bound.</violation>

<violation number="2" location="app/api/api_v1/endpoints/public.py:380">
P1: Rate limiting uses a spoofable IP source (`x-forwarded-for`), allowing clients to evade the new like/unlike limits by sending forged headers.</violation>
</file>

Note: This PR contains a large number of files. cubic only reviews up to 75 files per PR, so some files may not have been reviewed. cubic prioritizes the most important files to review.
On a pro plan you can use ultrareview for larger PRs.

For production, consider storing likes per user/session to prevent abuse.
Rate-limited per IP to prevent count manipulation.
"""
client_ip = get_client_ip(request)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Rate limiting uses a spoofable IP source (x-forwarded-for), allowing clients to evade the new like/unlike limits by sending forged headers.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/api/api_v1/endpoints/public.py, line 380:

<comment>Rate limiting uses a spoofable IP source (`x-forwarded-for`), allowing clients to evade the new like/unlike limits by sending forged headers.</comment>

<file context>
@@ -345,9 +375,14 @@ async def like_tour(
-    For production, consider storing likes per user/session to prevent abuse.
+    Rate-limited per IP to prevent count manipulation.
     """
+    client_ip = get_client_ip(request)
+    if _is_like_rate_limited(client_ip):
+        raise HTTPException(
</file context>

# Periodically reap keys with empty timestamp lists to prevent unbounded dict growth
if now - _last_like_reap > _LIKE_REAP_INTERVAL_S:
_last_like_reap = now
stale = [k for k, v in _ip_like_timestamps.items() if not v]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The periodic cleanup only deletes empty buckets, so inactive IP entries with expired timestamps are retained indefinitely and the rate-limit map can grow without bound.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/api/api_v1/endpoints/public.py, line 48:

<comment>The periodic cleanup only deletes empty buckets, so inactive IP entries with expired timestamps are retained indefinitely and the rate-limit map can grow without bound.</comment>

<file context>
@@ -21,6 +22,35 @@
+    # Periodically reap keys with empty timestamp lists to prevent unbounded dict growth
+    if now - _last_like_reap > _LIKE_REAP_INTERVAL_S:
+        _last_like_reap = now
+        stale = [k for k, v in _ip_like_timestamps.items() if not v]
+        for k in stale:
+            del _ip_like_timestamps[k]
</file context>
Suggested change
stale = [k for k, v in _ip_like_timestamps.items() if not v]
stale = [k for k, v in _ip_like_timestamps.items() if not v or v[-1] <= window_start]

…ing, specific exception handling, CORS validation

- Wrap owner_properties_list success response in MCPResponse.success() for consistent shape
- Add structured error codes (NOT_FOUND, FORBIDDEN, OPERATION_FAILED, INVALID_INPUT) to tool_ops returns
- Replace fragile string matching in MCP user layer with code-based error dispatch
- Catch PropertyNotFoundException/InsufficientPermissionsError specifically in get_property_detail
- Validate CORS_ORIGINS_STR entries must start with http:// or https://
- Add *.skill to .gitignore
@saksham1991999 saksham1991999 merged commit 84703b4 into main May 13, 2026
2 of 5 checks passed
@RaviSahu1520 RaviSahu1520 deleted the feat/infrastructure-refactor branch May 17, 2026 13:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant