fix(platform-objects): organization_id optional so BU/Team create in single-tenant (ADR-0057)#2178
Merged
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
📓 Docs Drift CheckThis PR changes 2 package(s): 3 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
…d optional (single-tenant create) Single-tenant has no sys_organization row and no auto-stamp (org-scoping is multi-tenant-only), so a required organization_id made these objects uncreatable (VALIDATION_FAILED). Optional now: single-tenant = null; multi-tenant still auto-stamps via OrgScopingPlugin and tenant-isolation RLS hides null-org rows (fail-closed). +dogfood regression test. Verified live on os dev (BU + Team create 201 single-tenant). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
xuyushun441-sys
force-pushed
the
fix/adr-0057-org-id-optional
branch
from
June 22, 2026 06:11
b155607 to
27abccd
Compare
xuyushun441-sys
added a commit
that referenced
this pull request
Jun 22, 2026
…(ADR-0057) (#2182) 1) sys_member/sys_invitation organization_id -> optional (same class as #2178; single-tenant has no org/auto-stamp; multi-tenant still stamps + RLS hides null-org). 2) BusinessUnitGraphService.headOf() add missing orgScope() — it runs under SYSTEM_CTX so orgScope is the only isolation; without it headOf(buId) leaked a BU's manager_user_id across organizations (inconsistent with descendants()). +regression test (org1 service must not read an org2 BU's head). Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
xuyushun441-sys
added a commit
that referenced
this pull request
Jun 22, 2026
#2196) The metadata→DB sync was additive-only (create table / add column), so any non-additive metadata change — relax `required`, change type/length, drop or rename a field — silently diverged from an existing database. The physical column won at write time, surfacing a misleading `organization_id is required` 400 even though `/meta` reported the field optional. Root-cause note: that 400 is NOT a validator bug. `record-validator` already reads `required` from metadata and passes once the field is optional; the 400 is a DB NOT NULL violation that `rest-server` paraphrases as "is required". So the fix is reconciling the physical schema + de-misleading the message, not changing the validator's source of truth. P1 — detection + warnings (schema-drift.ts, wired into SqlDriver.initObjects): diff metadata vs physical columns, categorise safe / needs_confirm / destructive (reusing SchemaDiffEntry). Boot warns once per divergence with an actionable `os migrate` hint. rest-server's NOT NULL translation now carries a drift-aware `hint` (VALIDATION_FAILED / fields envelope unchanged for back-compat). P2 — dev auto-reconcile (SqlDriverConfig.autoMigrate:'safe', wired into serve/dev in dev only): auto-applies the loosening subset (relax NOT NULL, widen varchar) so an existing dev DB self-heals on restart. Never destructive; force-disabled under NODE_ENV=production. P3 — os migrate plan/apply (cli/commands/migrate/*): categorised dry-run + reconcile, --allow-destructive gate, confirm prompt, --json. SQLite reconciles via the official table-rebuild (copy→swap, data preserved); Postgres/MySQL alter in place. Only examines objects in the loaded artifact; never auto-drops a table absent from metadata. Tests: driver-sql +12 (incl. #2178 repro self-heal with data preserved, prod guard, destructive gating, PG/MySQL DDL-gen), cli integration (real createStandaloneStack boot → detect → apply → in-sync), rest hint. Full build green. Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
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.
You reported: creating a Business Unit in the Setup console errors.
Root cause
sys_business_unit.organization_id(andsys_team.organization_id) arerequired: true, but a single-tenant deployment has nosys_organizationrow and nothing auto-stamps one (OrgScopingPluginis multi-tenant-only). So every create fails:This made the whole org-scoped identity graph uncreatable single-tenant — directly contradicting "BU is usable in the open/single-tenant edition" (ADR-0057 addendum).
Fix
Make
organization_idoptional onsys_business_unitandsys_team:null(consistent with "no org concept");OrgScopingPlugin;tenant_isolationRLS hides any null-org row (fail-closed) → no cross-tenant exposure.Pure validation change (the columns are already nullable) — no migration.
sys_member/sys_invitationcarry the same flag but are created only via better-auth org flows (which always supply an org) and aren't directly creatable in single-tenant Setup — left unchanged.Verified (live
os dev, single-tenant)Before the fix both returned 400. Plus a dogfood regression test (boots single-tenant, creates both).
🤖 Generated with Claude Code