Search/Build a Multi-Tenant Automation Platform Without Mixing Tenants or Editions

Build a Multi-Tenant Automation Platform Without Mixing Tenants or Editions

For coding a multi-tenant automation platform with community, enterprise, and hosted editions. Keeps each query in its project, routes outbound HTTP through the safe client to block SSRF, declares dependencies where they are used, and tests every edition before finishing.

To use this AI rule
## Keep tenant data separate and paid code out of the community edition Treat tenant scope, product editions, feature access, and branding as security and behavior contracts. ### Enforce isolation - Model ownership explicitly across platform, project, and user levels. - Scope every database query by the correct project or platform identifier; never rely on a caller to filter results later. - Require an access-control declaration on every endpoint. - Apply backend authorization and feature middleware before protected behavior, return the standard 402 response for unavailable paid features, and mirror the gate in the frontend guard and query enablement. ### Keep editions directional - Let premium or hosted editions extend the community core through registered hooks or interfaces. - Never import premium-only application code into community code. - Keep community defaults unbranded, apply configured branding in hosted environments, and gate self-hosted enterprise branding by the appropriate plan capability. - Verify customer-facing behavior across 5 paths: community self-hosted, enterprise self-hosted, hosted free, hosted paid, and hosted enterprise. - Keep all customer-facing names, colors, logos, sign-in screens, and messages driven by configured appearance rather than hardcoded product branding. ## Declare dependencies per workspace and lock data shared across servers Make dependency ownership and concurrent data behavior explicit so local success matches production deployment. ### Maintain package boundaries - Order shared libraries from thin, framework-independent foundations to thick application-level packages. - Allow reusable integrations and execution engines to depend only on thin packages; expose required application behavior through a narrow framework layer. - Declare each runtime dependency in the workspace that imports it and each test or tooling dependency in that workspace’s development dependencies. - Reserve the root manifest for repository-level tooling or true bootstrap requirements. - Pin versions consistently and update the lockfile with the canonical installer after dependency changes. ### Protect persistence and concurrency - Register new entities explicitly when the object mapper does not auto-discover them. - Separate side effects into dedicated modules and invoke them explicitly after successful mutations. - For multi-server work, use a distributed lock, queue deduplication, or row locking with skip-locked semantics. - Avoid database extensions unavailable on managed services. - Sanitize externally supplied objects before storing them in the database. ## Avoid the `any` type and forced type casts, and use the project's shared helpers Use predictable interfaces, data flow, file organization, and shared utilities. ### Preserve type integrity - Do not use `any`; use accurate types or `unknown` with type guards. - Do not force values through type casts. Fix the source type, add validation, or narrow the value. - Check library documentation before using an API and replace anything marked deprecated. - Use a single destructured object parameter for every function with more than 1 parameter. - Prefer immutable data flow across function boundaries; build and return new collections instead of mutating caller-owned objects. ### Keep conventions explicit - Use the project’s shared result helpers for synchronous and asynchronous error handling. - Make user-facing validation messages localization keys, with shared keys for common form errors and new keys when needed. - Bump a shared package’s patch version for compatible fixes or additions and its minor version for new exports or behavior, after checking the current branch. - Define non-exported helpers as standalone functions, keep executable logic first, and group exported constants and types at the end instead of interleaving them with logic. - Comment why a decision exists, not what self-explanatory code does. - Export multiple plain utilities through 1 named object, while keeping React components as named exports. ## Route outbound HTTP through the safe client to block SSRF, and follow API rules Apply explicit request semantics and safe network clients at every untrusted boundary. ### Standardize endpoints - Use `POST` for all create and update mutations and `DELETE` for deletion; never use `PUT` or `PATCH` when the API contract excludes them. - Give every endpoint an explicit security-access configuration. - Use canonical identifiers, error types, schema bases, collection helpers, and database sanitizers instead of reimplementing them. ### Prevent server-side request forgery - Route outbound HTTP from server components through the approved safe client. - Never use raw fetch or an unrestricted client factory for URLs originating from user input, administrator configuration, OAuth endpoints, or third-party integrations. - Preserve filtering for private, loopback, and cloud-metadata addresses. - Keep type coercion, URL validation, and external-object sanitation at the boundary. ### Treat migrations as controlled changes - Read the repository’s migration playbook before creating or modifying a migration. - Follow the documented generation and structure process rather than hand-inventing migration layout. - Keep migrations compatible with the supported managed database environment. ## Show the global error dialog only when a page's main data fails to load Show global query failures only when the failed data is essential to the current page. ### Classify queries by impact - Set the query metadata flag that triggers the global error dialog for primary page data such as table rows or main lists. - Do not enable it for auxiliary data such as feature flags, metadata, individual lookups, filter options, or user details. - Use this decision rule: if failure would otherwise leave an unexplained blank page or empty primary view, show the dialog. - Let minor queries fail quietly when the surrounding interface remains useful. - Keep feature-gated queries disabled until the applicable plan capability is available. ## Test tenant isolation and every edition with the monorepo's task runner Run focused tests through the monorepo’s canonical task runner and verify the paths that differ by edition. ### Exercise code and data - Run unit tests for shared and execution code and integration tests for API behavior. - Use the standard test-environment and request-context helpers so authentication and routing match production behavior. - Ensure test data is isolated and cleaned between tests. - Add concurrency tests when code uses locks, deduplication, or skip-locked database selection. - Test tenant isolation by proving data from another project or platform cannot be returned. ### Use the supported commands - Run the repository’s required lint-and-fix command before considering the task complete. - Use the configured monorepo task runner and filtered package commands; do not invoke an unrelated build system. - When a hosted frontend points at a remote backend, avoid OAuth flows that redirect to the hosted domain; use non-redirecting credentials or a fully local backend. - Verify edition gates, branding, limits, and feature availability across every supported path. ## Label the change and finish only after the required checks pass Use feature documentation, labels, and final checks to make the change understandable and releasable. ### Prepare the implementation - Before modifying a feature, read its focused documentation for entities, services, data flows, and integration constraints. - Keep critical cross-cutting safety rules concise and separate from step-by-step feature workflows. - Review dependency manifests, entity registration, tenant filters, edition imports, access declarations, and side-effect ordering. ### Prepare the contribution - Apply exactly 1 primary change label that distinguishes a feature, bug fix, or changelog-exempt internal change. - Add a separate integration-area label when the change affects a third-party or core integration. - Confirm the lockfile, package version, localization keys, tests, and generated artifacts match the implementation. - Run the required lint, test, and security checks and report any check that could not run. - Do not declare completion until the final diff and verification results support it.