Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions .changeset/field-presence-probe-stated-reason.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
"@objectstack/metadata-core": patch
---

docs(metadata-core): correct `createFieldPresenceProbe`'s stated reason — the `organization_id` column is provisioned unconditionally (#13416)

`createFieldPresenceProbe` is a published export, and its docstring is emitted
verbatim into the built declarations (`dist/index.d.ts` and `dist/index.d.cts`),
so it is what a consumer's editor shows on hover. That docstring recorded a
falsified fact as the probe's reason for existing: *"the SchemaRegistry
auto-injects `organization_id` only in multi-tenant mode … so on single-tenant
stacks the `sys_audit_log` / `sys_activity` tables have no such column."*

The column has since been decoupled from the posture flag. It is provisioned
**unconditionally**, subject only to the explicit opt-outs (`systemFields:
false`, `systemFields.tenant: false`, `managedBy: 'better-auth'`,
`tenancy.enabled: false`); the multi-tenant flag now governs only whether the
column is **INDEXED**, never whether it EXISTS. Three sources in the tree agree:
`applySystemFields` says so at the injection site (`objectql/src/registry.ts`),
the derivation it consumes (`resolveInjectedSystemColumns`,
`spec/src/data/injected-system-columns.ts`) accepts no `multiTenant` input to
decide with, and `objectql/src/registry-tenancy-posture.test.ts` pins it
executably. Both tables the old sentence named resolve the column on every
posture.

Read literally, the stale sentence invited two wrong moves — deleting the probe
as dead once someone checked that the column is always provisioned, or
hand-rolling a fresh posture-conditional probe elsewhere on the same false
premise. The sentence is therefore corrected and its history kept, rather than
dropped: the probe never read the posture flag, and what it still answers is
provenance, not posture (an ADR-0015 `external` object, the explicit opt-outs,
and an engine with no `getSchema`).

Prose only. No runtime behaviour, no exported signature and no accept/reject
decision moves; the probe's implementation is byte-identical.
39 changes: 33 additions & 6 deletions packages/metadata-core/src/record-organization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,41 @@ import { SystemFieldName } from '@objectstack/spec/system';
* columns on the same table, and a second hand-rolled probe would answer
* differently on the day one of them is fixed.
*
* Why the probe exists at all: the SchemaRegistry auto-injects
* `organization_id` only in multi-tenant mode (`applySystemFields({
* multiTenant })`), so on single-tenant stacks the `sys_audit_log` /
* `sys_activity` tables have no such column. Unconditionally stamping it there
* Why the probe exists at all — and what has changed under it. It was built
* for a posture-conditional `organization_id`: the SchemaRegistry used to
* auto-inject the column only in multi-tenant mode (`applySystemFields({
* multiTenant })`), so on a single-tenant stack the `sys_audit_log` /
* `sys_activity` tables had no such column. Unconditionally stamping it there
* made every audit INSERT fail with "table sys_audit_log has no column named
* organization_id" — and the error was swallowed, so audit logging was silently
* non-functional. Resolve the field set lazily from the engine schema and cache
* it; object schemas are static after registration.
* non-functional.
*
* ⚠️ That premise no longer holds. The `organization_id` COLUMN is provisioned
* UNCONDITIONALLY, subject only to the explicit opt-outs (`systemFields:
* false`, `systemFields.tenant: false`, `managedBy: 'better-auth'`,
* `tenancy.enabled: false`); the multi-tenant flag now governs only whether the
* column is INDEXED, never whether it EXISTS. Three sources agree:
* `applySystemFields` says so at the injection site
* (`objectql/src/registry.ts`); the derivation it consumes
* (`resolveInjectedSystemColumns`, `spec/src/data/injected-system-columns.ts`)
* takes no `multiTenant` input to decide with; and
* `objectql/src/registry-tenancy-posture.test.ts` pins it executably. Both
* tables named above resolve the column on every posture.
*
* The stale sentence is corrected rather than dropped, because it is the stated
* REASON for this probe and read literally it now invites two wrong moves:
* ⛔ deleting the probe as dead once someone checks the column is always
* provisioned, and ⛔ hand-rolling a fresh posture-conditional probe elsewhere
* on the premise it used to carry. (`sql-driver.ts`'s `applyTenantScope`
* docstring names the class: "which is exactly how a docstring becomes the last
* place a wrong fact survives.")
*
* The probe never read the flag, and it still has work. What it answers is
* PROVENANCE, not posture: the column is absent exactly where this process does
* not provision it — an ADR-0015 `external` object, the explicit opt-outs
* above, and (next paragraph) an engine with no `getSchema`. Resolve the field
* set lazily from the engine schema and cache it; object schemas are static
* after registration.
*
* Best-effort in both directions: an engine with no `getSchema` (an in-memory
* test double) reports every field absent, which skips the stamp rather than
Expand Down
Loading