Skip to content

Retire tenancy.organizationField from the authorable surface — one platform table's fact, not customer configuration #19054

Description

@hotlong

Ruling (maintainer, 2026-09-18, live chat, verbatim): 「organizationField 撤出可授权面 同意你的建议」 — filed on that word. The recommendation it accepts: take the key off the authorable surface and keep its one real use as a platform-internal fact, but do it on its own card, AFTER #18420 lands.

What is wrong today

tenancy.organizationField is an authorable key — packages/spec/authorable-surface/data.json carries data/TenancyConfig:organizationField, so any app author can declare it on any object. That means every future piece of organization-related logic has to consider "what if somebody set this?".

It does not earn that reach. Measured on main, the whole repository declares it once:

packages/platform-objects/src/identity/sys-api-key.object.ts:69
  tenancy: { enabled: false, organizationField: 'active_organization_id' },

Every other hit is a test fixture, a spec docblock, a generated JSON-schema, or a comment. Zero business objects declare it, and the spec's own docblock says why: "For ordinary objects the two coincide and organizationField is never needed."

Why sys_api_key needs it at all (⛔ do not "just use organization_id")

Two facts make this table genuinely different, and both must survive the change:

  1. It is managedBy: 'better-auth' (sys-api-key.object.ts:22). resolveInjectedSystemColumns bails on managedBy before tenancy is consulted, so the platform never injects organization_id here. The column that exists is better-auth's active_organization_id (:231).
  2. The table must not be walled. In this platform "has an organization_id column" IS the wall — computeTenantField / resolveTenantFieldName fall back to it and the Layer-0 wall exempts objects that lack it. Naming the column organization_id would wall the credential table on an equality that excludes NULL, and every pre-[finding] API keys carry no organization — under the isolated posture a minted key reads no org data at all (no leak, but the key surface is inert) #8287 key would vanish from its own owner's "My Keys" list. That is the defect [finding] API keys carry no organization — under the isolated posture a minted key reads no org data at all (no leak, but the key surface is inert) #8287 exists to have removed, and the object's own comment forbids the move in as many words.

So the key's whole job is: one unwalled table whose audit/approval/run rows still stamp the organization the row is about. That is a fact about a platform table we ship — not a knob customers need.

Proposed change

  1. Remove organizationField from TenancyConfigSchema (packages/spec/src/data/object.zod.ts) and add its row to TENANCY_RETIRED_KEY_GUIDANCE beside the two existing precedents (tenancy.strategy, tenancy.crossTenantAccess, both removed after v15.0 with a prescription each).
  2. Replace limb 0 with a platform-internal constant in packages/metadata-core/src/record-organization.ts — one row, sys_api_key → active_organization_id, read by the stamp face only. The three sanctioned platform-row writers (audit stamping, the approval-row writer, the automation-run recorder) keep their behaviour byte for byte; their pins should stay green untouched.
  3. ADR-0087 conversion entry — retiring an authorable key is a protocol change, and an app that declared it needs a rewrite or a named refusal rather than a silent drop.
  4. Regenerate: json-schema/**, authorable-surface/data.json, liveness/object.json (its organizationField evidence row moves or retires), the generated reference page.
  5. The wall face added by [Decision] group posture is an on-premise shape — should package-authored scheduled flows run under group with the switch on, and which organization do their writes carry? (ruling G item 3, reopened by the maintainer) #18378 (resolveRecordWallOrganizationField) is unaffected — it never read the key.

The playbook for all of this already exists in-repo: .claude/skills/spec-property-retirement.

Sequencing and risk

Acceptance criteria

Dedup terms

organizationField · TenancyConfig · stamp-only · sys_api_key active_organization_id · authorable surface retirement


Filed from the #18378 / PR #18420 work, where the key's scope-pin was the finding that blocked the PR until the design was changed to stop reading it.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions