Skip to content

[spec] systemFields owner guidance misstates ownership: 'org' — tells authors org "chooses the principal" when it injects no owner_id at all #6365

Description

@hotlong

Found while doing the ADR-0117 D1 declaration sync (#5767 / PR #6364). Not fixed there — different fact, different defect class, and it predates #5677. Filed unassigned for triage.

The defect

packages/spec/src/data/object.zod.ts, in the systemFields strictObject's guidance block — the message an author gets when they write the non-existent systemFields: { owner: ... } key:

`owner` is not a `systemFields` key — `owner_id` injection is governed by the
object-level `ownership` property (`ownership: 'none'` skips it;
`'user'`/`'org'` choose the principal). `systemFields` controls only `tenant`
(organization_id) and `audit` (created_at/created_by/updated_at/updated_by).

'user'/'org' choose the principal is false. ownership: 'org' does not choose a different principal — it injects no owner_id at all. From packages/spec/src/data/injected-system-columns.ts (the authority applySystemFields consumes):

const owner = ownershipEligible && (ownership === undefined || ownership === 'user');

Only undefined and 'user' produce the column. The same file's own ownership JSDoc, ~90 lines above the guidance, already states it correctly:

- `org` / `none` — no per-record owner (Dataverse-style catalog / junction
  tables); `owner_id` is NOT injected.

So the guidance contradicts the JSDoc two stanzas away, and it is the guidance that is wrong.

Why it matters — the message is on a real authoring path

This is not dormant prose: it is the error text an author (or an AI writing metadata) is handed at the moment they are already confused about where owner injection is configured. It sends them to ownership: 'org' expecting an owner column keyed to the organization. They get no owner column, and every owner-keyed feature quietly does nothing — owner-scoped RLS, "My" views, owner reports, the first-admin bootstrap handoff. Silent, not loud: nothing rejects ownership: 'org', so the mistake ships.

The failure mode is exactly the one the surrounding guidance machinery exists to prevent, inverted — a wrong-key rescue that hands out a second wrong answer.

Second, smaller item in the same block (fix together)

The sibling guidance.ownership message is now incomplete rather than wrong:

`ownership` is a TOP-LEVEL object key, not a `systemFields` key — write it beside
`systemFields`. It, not this block, decides whether `owner_id` is injected.

Since #5677, ownership governs both ownership anchors — owner_id and owning_business_unit_id. PR #6364 deliberately left this alone (it is an error-message surface, not the injection-list description that issue named), but whoever fixes the 'org' claim should widen this sentence in the same pass, since both messages describe the same property.

Suggested fix

Text only, no schema change. Something like: ownership: 'user' (or omitted) injects the ownership anchors; 'org' and 'none' both skip them, 'org' meaning "org-wide catalog, no per-record owner". Keep 'none' and 'org' visibly distinct in intent so the message still explains why both exist.

Not in scope of this finding

The ownership enum's own error text and describe() (they enumerate the legal values verbatim and are #5678's surface), and adding the business_unit tier anywhere.

Provenance

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions