You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
|**tenantId**|`string`| optional | Tenant/Organization ID |
87
86
|**visibility**|`Enum<'global' \| 'organization' \| 'private'>`| ✅ |[EXPERIMENTAL — NOT ENFORCED, #1901] Intended listing scope. No runtime consumer yet; use access/permissions for real gating. |
|**reference**|`string`| optional | Target object name (snake_case) for lookup/master_detail fields. Required for relationship types. Used by $expand to resolve foreign key IDs into full objects. |
|**deleteBehavior**|`Enum<'set_null' \| 'cascade' \| 'restrict'>`| optional | What happens if referenced record is deleted |
121
119
|**inlineEdit**|`boolean \| Enum<'grid' \| 'form'>`| optional | Edit these child records inline within the parent's form (atomic master-detail). true = auto-pick grid/form by child shape; 'grid' = editable line-item grid; 'form' = list + per-row full form. |
122
120
|**inlineTitle**|`string`| optional | Title for the inline master-detail grid |
@@ -129,7 +127,7 @@ const result = Address.parse(data);
129
127
|**descriptionField**|`string`| optional | Secondary field shown under the label in the quick-select popover. |
130
128
|**lookupColumns**|`string \| { field: string; label?: string; width?: string; type?: string }[]`| optional | Explicit columns for the record-picker table; auto-derived from the referenced object when omitted. |
131
129
|**lookupPageSize**|`integer`| optional | Rows per page in the record-picker dialog (default 10). |
132
-
|**lookupFilters**|`{ field: string; operator: Enum<'eq' \| 'ne' \| 'gt' \| 'lt' \| 'gte' \| 'lte' \| 'contains' \| 'in' \| 'notIn'>; value: any }[]`| optional | Base filters restricting which records are selectable (e.g. only active). Structured, picker-honoured form of referenceFilters. |
130
+
|**lookupFilters**|`{ field: string; operator: Enum<'eq' \| 'ne' \| 'gt' \| 'lt' \| 'gte' \| 'lte' \| 'contains' \| 'in' \| 'notIn'>; value: any }[]`| optional | Base filters restricting which records are selectable (e.g. only active). The structured, picker-honoured lookup filter. |
133
131
|**dependsOn**|`string \| { field: string; param?: string }[]`| optional | Declares that this field's available values depend on the value of other field(s) on the same record — the form gates the field until they are set and re-evaluates as they change. For `lookup`/`master_detail` it scopes the candidate query (string = same local/remote key; `{field,param}` when the remote filter key differs — the `{field,param}` form is lookup-only). For `select`/`multiselect`/`radio` the actual per-option rule lives in each option's `visibleWhen`; list the referenced fields here (string form) so the option list gates and refreshes with the parent. |
134
132
|**allowCreate**|`boolean`| optional | Allow inline quick-create from the record picker: when no match exists the user can create a record from the typed text (optimistic dataSource.create with the display field). Best for simple objects whose only required field is the display field. |
|**active**|`boolean`| optional | Is the object active and usable |
105
-
|**isSystem**|`boolean`| optional | Is system object (protected from deletion) |
106
-
|**abstract**|`boolean`| optional | Is abstract base object (cannot be instantiated) |
103
+
|**isSystem**|`boolean`| optional | Is system object (protected from deletion; defaults its org-wide sharing to public when no sharingModel is set — plugin-sharing) |
| R10 |**`columnMap` ↔ `field.columnName` single source of truth**|❌ **gap**|two inverse mechanisms coexist|
34
+
| R10 |**`columnMap` ↔ `field.columnName` single source of truth**|✅ resolved|#2377 removed `field.columnName` (it was never applied by the driver); `external.columnMap` is now the sole physical-column mapping|
35
35
36
36
### The structural gap, precisely (R2)
37
37
@@ -83,6 +83,8 @@ The analytics native-SQL strategy compiles its own `FROM "<table>"` / column ref
83
83
84
84
`external.columnMap` ({ remoteColumn → localField }) is the supported way to map external columns (shipped #2149). `field.columnName` (localField → physicalColumn) is its inverse and is **not** applied by the driver's query pipeline for external objects. Decision: for external objects, `columnMap` is authoritative; `field.columnName` on an external object is rejected at validation (no silent dual-source) until a unified column-resolution model is designed. Managed objects' `field.columnName` semantics are untouched.
85
85
86
+
> **Resolution (#2377, R10 closed).**`field.columnName` was **removed from the spec entirely** (ADR-0049 enforce-or-remove): the SQL driver hardcodes the physical column = field key (`createColumn` never read `columnName`), so it was inert on *managed* objects too — not just external ones. With the field gone there is no dual-source ambiguity, so the build-time D7 rejection lint (`validateStackExpressions`) and the dead `StorageNameMapping.resolveColumnName`/`buildColumnMap`/`buildReverseColumnMap` helpers were removed with it. `external.columnMap` is the single, authoritative physical-column mechanism. Physical-column override for managed objects (e.g. legacy-DB adoption) — the only case with real value — should, if ever needed, be designed as a first-class driver feature rather than reintroduced as an unenforced field.
87
+
86
88
> **Phase 4 implementation note (#2163).***D7* is enforced at build time in `validateStackExpressions` (`os compile`/`build`): any object that declares an `external` binding and a field with `columnName` is an error with a corrective message (use `external.columnMap`). Managed objects are untouched. *D8*: `examples/app-showcase` now declares its external datasource with **no**`onEnable` driver registration — `onEnable` only provisions the "remote" fixture tables; the declared datasource auto-connects (D1). To exercise this in the dogfood gate, the `@objectstack/verify` harness now wires the datasource-admin plugin (hence the `'datasource-connection'` service) when an app declares datasources — so the harness matches `objectstack dev`/serve and the federated read is covered end-to-end (a new dogfood test reads `showcase_ext_customer`/`_order`, including the `remoteName` remap). `onEnable` + `ctx.drivers.register` remains documented as the escape hatch for drivers built dynamically at runtime.
87
89
88
90
### D8 — Drop the `onEnable` bridge from the canonical example; keep it as an escape hatch
0 commit comments