Repository navigation
Fix #413–#424: tenancy API and Vary, outer-join tenant filter, i18n overrides, admin a11y - #425
Open
antosubash wants to merge 30 commits into
Open
antosubash wants to merge 30 commits into
antosubash wants to merge 30 commits into
Conversation
…enant (#423, #424) Also records the resolution's vary on request.state.tenant_vary and makes the claim path vary on Cookie, Authorization. Claude-Session: https://claude.ai/code/session_01M9neheZZEe3sVpDi2S3zT4
A child column used only inside a function (func.count(Child.id)) loses its ORM annotation, so _plain_tables took the outer-joined child for a Core table and added WHERE child.tenant_id = :t, turning the LEFT JOIN into an inner join. Exclude a columns-clause table from the WHERE candidates only when it is the target of an outer/full join in _setup_joins whose target is a plain ORM entity carrying loader criteria (they already sit in ON). Raw Model.__table__ targets keep the WHERE predicate; strict mode without a tenant still refuses. Pins the pre-existing Core outer join leak as a strict xfail. Claude-Session: https://claude.ai/code/session_01M9neheZZEe3sVpDi2S3zT4
A FULL join preserves its right side, and an ON predicate never removes a preserved row: with the child's WHERE gone, other tenants' (and trashed) child rows came back as unmatched rows. Exempt LEFT outer joins only, and pin it with FULL-join tests over a non-tenant left model. Claude-Session: https://claude.ai/code/session_01M9neheZZEe3sVpDi2S3zT4
host/locales/overrides/<locale>.json replaces existing keys by full dotted path after all catalogs; unknown keys are skipped and reported as SM027. Claude-Session: https://claude.ai/code/session_01M9neheZZEe3sVpDi2S3zT4
…lter (#417) .outerjoin(Parent.kids) and .outerjoin(Parent.kids.of_type(Kid)) record the relationship attribute as the join target, so the ON-covered exclusion never matched and func.count(Kid.id) still added WHERE kid.tenant_id = :t, dropping parents without children. Resolve the relationship's target mapper and apply the existing entity rules (LEFT only, covered class, single table, tenant bound). secondary relationships and aliased of_type keep their WHERE. Also: TenancyMode in simple_module_core.tenancy.__all__; mode_for dropped from simple_module_hosting.tenancy.__all__ (internal to create_app). Claude-Session: https://claude.ai/code/session_01M9neheZZEe3sVpDi2S3zT4
…#415) make doctor rebuilt the catalog from modules + host + ui but not the framework's own hosting namespace (setup wizard), so every valid hosting.* host override was reported as SM027. Locate simple_module_hosting/locales via importlib.util.find_spec (core must not import hosting) and add it like the running app's build_i18n_registry does. Claude-Session: https://claude.ai/code/session_01M9neheZZEe3sVpDi2S3zT4
attach_session_listeners guarded with event.contains, which SQLAlchemy keys on id(target): a new session class reusing a garbage-collected class's id read as already wired and got no tenant stamping, no tenant/soft-delete filter and no write marker. Guard with a marker in the class's own __dict__ instead. Found as an order-dependent test failure (a request's write never committed) that the new relationship outer-join tests shifted into view. Claude-Session: https://claude.ai/code/session_01M9neheZZEe3sVpDi2S3zT4
…d read_catalog Claude-Session: https://claude.ai/code/session_01M9neheZZEe3sVpDi2S3zT4
…d local import Claude-Session: https://claude.ai/code/session_01M9neheZZEe3sVpDi2S3zT4
- tenants resolver varies on Cookie whenever a signed-in user shaped the answer - session middleware no longer emits a duplicate Cookie in Vary - session listeners: a subclass of a wired class inherits its listeners once - doctor reports malformed override JSON as SM016 instead of crashing - gen-pages prunes node_modules/__pycache__ during the @source walk - settings: group headings are h3 under the module h2 Claude-Session: https://claude.ai/code/session_01M9neheZZEe3sVpDi2S3zT4
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. |
Deploying simple-module-python with
|
| Latest commit: |
6961aee
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://28671ac3.simple-module-python.pages.dev |
| Branch Preview URL: | https://feat-issue-batch-413-424.simple-module-python.pages.dev |
This branch has not been deployed
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.
Summary
Fixes twelve issues filed on 2026-10-08 in one batch. Design:
docs/superpowers/specs/2026-10-09-issue-batch-413-424-design.md. Plan:docs/superpowers/plans/2026-10-09-issue-batch-413-424.md. #317 is not included: it needs access to the external design file.Tenancy
TenantMiddlewaremerges everyVaryline and never dropsVary: *. Existing tokens are de-duplicated case-insensitively.tenant_sourceisNonewhenever no tenant is bound.simple_module_hosting.tenancy:TenancyMode,tenancy_mode,single_tenant_id,require_tenant()andtenant_vary.require_tenant()is an async yield dependency that binds the tenant through the commit. A test fails if that binding breaks.Varycompleteness:Cookiewhenever a signed-in user shaped the answer: session, header and subdomain sources.Cookie, Authorization.Cookietoken..outerjoin(Parent.children), with or withoutof_type). Unused parents are no longer dropped fromcount()queries. Leak guards check that another tenant's rows are never counted.attach_session_listenersnow marks each session class instead of usingevent.contains, which keys onid().Background tasks
mainby fix(migrations): make SQLite-autogenerated revisions portable to Postgres (#342) #406, which is not in a release yet. This PR only adds regression tests: the columns are timezone-aware, andsuccess_count_sincebinds an aware cutoff on Postgres.Auth
fetch()no longer overwrites it.i18n
host/locales/overrides/<lang>.json, keyed by full dotted path.make doctorand at dev boot.Build
@sourceper wheel-module subdirectory that holds.ts/.tsxfiles, so uv's.venv/.gitignoreno longer hides those subdirectories from Tailwind.UI and admin
--primary-foregroundand--sidebar-primary-foregroundare chosen by WCAG contrast against the brand colour. The active sidebar row uses them. The branding live preview now does too.#abcandF5F5F5to#rrggbb. Publish stays disabled while the value is invalid.NativeSelectgets awrapperClassNameprop.<Head title>, enforced by a guard test./admin/settings/has a visible h1.AdminLayoutfor signed-in admins; anonymous viewers and pre-shared-props errors still get the bare page.Deviations from the approved design (all reviewed):
Sec-Fetch-Deston subresources.BrandingMarklogo badge stays white. It sits on a gradient of fixed-lightness ramp steps, so the brand-colour ink made it worse.Vary: Cookie: extended from the session source to every signed-in branch (above).Verification
/admin/users/and the other touched pages, on port8201single-tenant and8202multi-tenant.QA Report
NativeSelectinvite/members pages andVaryheaders verbatim).?q=%00in users or background-tasks search returns 500 on Postgres. This predates the branch./admin/settings/nests two<main>landmarks./dashboard/.Test plan
/admin/settings/, sign in, and confirm you land back on/admin/settings/./admin/users/00000000-0000-0000-0000-000000000000as an admin and confirm the 404 renders inside the admin sidebar layout./admin/branding/, set a light colour (#62B8E2) and confirm the primary buttons and the active sidebar row use dark text./admin/background-tasks/loads.Closes #413, closes #414, closes #415, closes #416, closes #417, closes #418, closes #419, closes #420, closes #421, closes #422, closes #423, closes #424
https://claude.ai/code/session_01M9neheZZEe3sVpDi2S3zT4