Skip to content

Commit 16cebeb

Browse files
os-zhuangclaude
andauthored
fix(spec): drop the dead systemFields.owner key (#3175 follow-up) (#3194)
`ObjectSchema.systemFields` advertised an `owner?: boolean` opt-out that nothing read — `applySystemFields` only consumes `systemFields.tenant` and `systemFields.audit`, and `owner_id` provisioning is governed by the object-level `ownership` property (made first-class in #3185). The key was declared but wired to nothing. - spec: remove `owner` from the `systemFields` object so it only advertises the two opt-outs it honors (`tenant`, `audit`). Runtime-compatible (the key was ignored; it is stripped now — both no-ops). A TS author who set it gets an excess-property error; the fix is to delete it or use `ownership: 'org' | 'none'`. - docs: regenerated the object reference; corrected the stale objectql/security note that called `audit` "reserved" (it is active). Claude-Session: https://claude.ai/code/session_014343Qv6DFAykuAJc9yjANb Co-authored-by: Claude <noreply@anthropic.com>
1 parent 1c7ab8b commit 16cebeb

4 files changed

Lines changed: 20 additions & 3 deletions

File tree

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
"@objectstack/spec": minor
3+
---
4+
5+
fix(spec): drop the dead `systemFields.owner` key (#3175 follow-up)
6+
7+
`ObjectSchema.systemFields` exposed an `owner?: boolean` opt-out key that nothing
8+
read — the registry (`applySystemFields`) only consumes `systemFields.tenant` and
9+
`systemFields.audit`, and `owner_id` provisioning is governed by the object-level
10+
`ownership` property (`'user' | 'org' | 'none'`, made first-class in #3185). The
11+
key was declared but wired to nothing.
12+
13+
Removed it so the schema only advertises the two opt-outs it actually honors
14+
(`tenant`, `audit`). Backward-compatible at runtime: the key was ignored before and
15+
is stripped now (both no-ops). A TypeScript author who set `systemFields.owner`
16+
will now see an excess-property error — the fix is to delete the key (it never did
17+
anything) or use `ownership: 'org' | 'none'` to skip `owner_id`. Also corrected the
18+
stale `objectql/security` doc that called `audit` "reserved" (it is active).

content/docs/protocol/objectql/security.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ Combine OWD (`sharingModel: private`), an RLS `using` policy, FLS (`readable: fa
405405

406406
### Security by Default
407407

408-
Leave registry-level `systemFields` injection enabled (the object default) so multi-tenant objects auto-stamp `organization_id` on create, and use field `defaultValue` to seed ownership fields — so records are never left unscoped. (`owner`/`audit` injection keys on `systemFields` are reserved for future expansion; only `tenant` is active today — see `packages/spec/src/data/object.zod.ts`.)
408+
Leave registry-level `systemFields` injection enabled (the object default) so multi-tenant objects auto-stamp `organization_id` on create, and use field `defaultValue` to seed ownership fields — so records are never left unscoped. (`systemFields` accepts two opt-out keys — `tenant` and `audit` — both active; `owner_id` provisioning is governed separately by the object-level `ownership` property. See `packages/spec/src/data/object.zod.ts`.)
409409

410410
---
411411

content/docs/references/data/object.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ const result = ApiMethod.parse(data);
107107
| **managedBy** | `Enum<'platform' \| 'config' \| 'system' \| 'append-only' \| 'better-auth'>` | optional | Lifecycle bucket — platform (user CRUD) \| config (admin authored) \| system (engine-managed) \| append-only (audit) \| better-auth (identity). UI clients honour the resolved affordance matrix. |
108108
| **ownership** | `Enum<'user' \| 'org' \| 'none'>` | optional | Record-ownership model: user (default — injects reassignable owner_id) \| org \| none (no per-record owner, skips owner_id). Distinct from the package own/extend contribution kind. |
109109
| **userActions** | `{ create?: boolean; import?: boolean; edit?: boolean \| { enabled?: boolean; visibleWhen?: string \| { dialect: Enum<'cel' \| 'js' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object }; disabledWhen?: string \| { dialect: Enum<'cel' \| 'js' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object } }; delete?: boolean \| { enabled?: boolean; visibleWhen?: string \| { dialect: Enum<'cel' \| 'js' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object }; disabledWhen?: string \| { dialect: Enum<'cel' \| 'js' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object } }; … }` | optional | Per-object override of the resolved CRUD affordance matrix. |
110-
| **systemFields** | `'false' \| { tenant?: boolean; owner?: boolean; audit?: boolean }` | optional | Opt out of, or selectively disable, registry-level system-field auto-injection. |
110+
| **systemFields** | `'false' \| { tenant?: boolean; audit?: boolean }` | optional | Opt out of, or selectively disable, registry-level system-field auto-injection. |
111111
| **datasource** | `string` | optional | Target Datasource ID. "default" is the primary DB. |
112112
| **external** | `{ remoteName?: string; remoteSchema?: string; writable?: boolean; columnMap?: Record<string, string>; … }` | optional | Remote table binding for federated (external) objects. |
113113
| **fields** | `Record<string, { name?: string; label?: string; type: Enum<'text' \| 'textarea' \| 'email' \| 'url' \| 'phone' \| 'password' \| 'secret' \| 'markdown' \| 'html' \| 'richtext' \| 'number' \| 'currency' \| 'percent' \| 'date' \| 'datetime' \| 'time' \| 'boolean' \| 'toggle' \| 'select' \| 'multiselect' \| 'radio' \| 'checkboxes' \| 'lookup' \| 'master_detail' \| 'tree' \| 'user' \| 'image' \| 'file' \| 'avatar' \| 'video' \| 'audio' \| 'formula' \| 'summary' \| 'autonumber' \| 'composite' \| 'repeater' \| 'record' \| 'location' \| 'address' \| 'code' \| 'json' \| 'color' \| 'rating' \| 'slider' \| 'signature' \| 'qrcode' \| 'progress' \| 'tags' \| 'vector'>; description?: string; … }>` || Field definitions map. Keys must be snake_case identifiers. |

packages/spec/src/data/object.zod.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -695,7 +695,6 @@ const ObjectSchemaBase = z.object({
695695
z.literal(false),
696696
z.object({
697697
tenant: z.boolean().optional().describe('Inject the organization_id column. Default true (the column is always provisioned; the multi-tenant flag governs only its index).'),
698-
owner: z.boolean().optional().describe('Unwired: owner_id provisioning is governed by the object-level `ownership` property (`user`/`org`/`none`), not this key. Reserved.'),
699698
audit: z.boolean().optional().describe('Inject the audit columns (created_at/created_by/updated_at/updated_by). Default true.'),
700699
}),
701700
])

0 commit comments

Comments
 (0)