feat(tenancy): adoption prerequisites — default tenant id, tenant-role vocabulary, tenant_client (#380) [stack 1/11] - #385
Merged
Merged
Conversation
Groundwork so core modules can adopt MultiTenantMixin without breaking single-tenant installs or leaving tenant members without permissions. - DEFAULT_TENANT_ID = "default" (simple_module_db). With tenancy not strict and no tenant bound, MultiTenantMixin inserts (session.add, ORM/Core insert, multi-VALUES, and inside all_tenants()) are stamped with it instead of hitting the NOT NULL constraint. Strict mode is unchanged: an unbound insert still raises MissingTenantError. HostSettings.default_tenant is untouched. Reads stay unfiltered when unbound and not strict: on a single-tenant install every row belongs to the install whatever tenant_id it carries, and narrowing to "default" would hide rows written under default_tenant or while multi_tenant was on. - Tenant-role vocabulary in core (simple_module_core.tenancy): TenantRole (owner/admin/member), TENANT_ROLE_PREFIX, tenant_role(), is_tenant_role(). tenants re-exports them (MembershipRole is TenantRole). - tenant_client fixture in simple_module_test: factory tenant_client(role="owner", *, tenant_id=None, email=None) yielding TenantClient(client, tenant_id, user_id); lazily imports users/tenants. tenants tests use it where it was a straight swap. - SM025: multi_tenant on with no app.state.tenant_resolver, reported at boot after module registration (needs the built app, so not in the doctor CLI) in every environment, and folded into the Doctor screen's results. - tenants: depends_on uses _MODULE_* constants (ci-check-hardcoded-strings). - Docs: multi-tenancy (default tenant, roles, testing), diagnostic codes, CLAUDE.md. Claude-Session: https://claude.ai/code/session_01F8RiTBUJQnZmSq56qReZeV
Deploying simple-module-python with
|
| Latest commit: |
7ad5870
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://c5735544.simple-module-python.pages.dev |
| Branch Preview URL: | https://tenancy-01-prereqs.simple-module-python.pages.dev |
…n unscoped writes (review of #380) Non-strict bypass and unbound writes always fell back to DEFAULT_TENANT_ID: an insert with execution_options(all_tenants=True) inside a request dropped the request's tenant, and on a default_tenant install every CLI/all_tenants() write landed in "default", invisible to the install's own scoped requests. Both guards now stamp current_tenant_id if bound, else the engine policy's default_tenant_id, which the host publishes from Settings.default_tenant (single-tenant installs only). Strict mode is unchanged. Claude-Session: https://claude.ai/code/session_01F8RiTBUJQnZmSq56qReZeV
#380) create_user looked the 'user' Role up and silently skipped the UserRole when the row was absent, which it always is under create_all. Extract users.bootstrap.ensure_user_role (shared with create_standard_user) and use it so the fixture user really holds the platform role. Claude-Session: https://claude.ai/code/session_01F8RiTBUJQnZmSq56qReZeV
…address in the DB (qa BUG-003, BUG-004) Claude-Session: https://claude.ai/code/session_01F8RiTBUJQnZmSq56qReZeV
antosubash
marked this pull request as ready for review
October 1, 2026 16:14
antosubash
added this pull request to stack #397
October 1, 2026 16:14
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #380. Stack 1/11 of the tenancy-adoption series (base:
main).What
DEFAULT_TENANT_ID = "default"(simple_module_db). On a non-strict install with no tenant bound,MultiTenantMixininserts are stamped with it:session.add, ORM and Coreinsert, and multi-row.values(). A tenant id the caller sets explicitly is kept. Strict mode is unchanged and an unbound insert still raises. Reads on non-strict installs stay unfiltered, because every row belongs to the install. Without this, the first module to adopt the mixin would break writes on every single-tenant install.simple_module_core.tenancy):TENANT_ROLE_PREFIX,TenantRole,tenant_role()andis_tenant_role(). Modules can nowmap_role(tenant_role(TenantRole.MEMBER), …)without importingtenants, andtenantsre-exports these names.tenant_clientfixture insimple_module_test: a real user plus a tenant and membership, with that tenant set active in the session.multi_tenantis on but notenant_resolveris registered. It also shows on the in-app Doctor screen.ci-check-hardcoded-stringsfailure already on main intenants/module.py(depends_onnow uses constants).multi-tenancy.md(default tenant, backfill migrations, tenant roles, testing), the diagnostic codes page and CLAUDE.md.Tests
make test-py: 3319 passed, 9 skipped.make lint: clean.test_default_tenant.py,test_diagnostics_tenancy.py,test_tenant_resolver_diagnostic.py,test_tenant_client_fixture.py.https://claude.ai/code/session_01F8RiTBUJQnZmSq56qReZeV