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
{{ message }}
Repository navigation
Commit addbbf0
Browse filesBrowse the repository at this point in the historyBrowse files
feat(spec): the picklist metadata kind — a shared option list select fields reference by name (#19518) (#20823)
Part of #19518
Clause-②: yes
The spec layer of the shared picklist:
- the `picklist` kind: registered, loads before `object`, package-owned;
- `Field.select({ picklist })`, refused when `options` is also declared;
- the served shape `PicklistServedFieldSchema`;
- `picklistExtensions`;
- the `picklists.NAME` translation face and its resolvers.
Two gates this PR must keep green are also carried here:
- the published platform skill lists the two new top-level keys
(`check:skill-top-level-keys`);
- `os i18n extract` walks `picklists.NAME.{label, options.VALUE}`
(`check:i18n-walk-parity`).
Every pin that enumerates registered kinds or `FieldSchema` keys moves
with the kind. That includes `driver-sql`'s column-collision
classification.
Resolving the reference at runtime is #19519. Until then the liveness
ledger grades the new keys `planned`. Refusing a `select` / `radio` with
neither `options` nor `picklist` is #20827 (ruled A, after this PR). The
CLI compile / validate path is #20825. #19518 stays open for its Tier H
docs PR: the NORTH-STAR line and the two `records-forms` checklist
items.
## 维护者速读
**改了什么:** `skills/objectstack-platform/SKILL.md` 的顶层键清单加上
`picklists`、`picklistExtensions` 两个键(第 43 行起)。同一文件 CLI 小节的一句指路文字(第 453
行附近)删掉两处:
- 已失真的「below」:Part 3 已由 `595621d35c` 拆到
`references/operations.md`,不在本页下方;
- 与 cheat sheet 重复的「High-level」。
**读数:** 整文件 489 行不变;token 5829 → 5827,上限 5833;技能包总行数 4396
不变。`check-governed-merges`:75 个路径中 1 个命中登记(`skills/**`),所以是 Tier H;共
1564 行改动,低于 5000。
**为什么改:** 本 PR 给 stack 新增了这两个顶层键。必需门禁 `check:skill-top-level-keys`
要求技能页的清单与 schema 一致;页面上没列的键,AI 作者就不会写。
**风险与回滚:** 只改文字,链接目标不变,作者需要的内容一处没删。回滚办法是 revert `4049ae31ba`,但若不同时去掉这两个
stack 键,该门禁会重新变红。
**席位意见:** 建议批准。复核 PASS 后,席位在本 PR 上贴出记录。
**你要做的:** 看 SKILL.md 的两处改动,然后给出授权的 APPROVED 审阅,由席位落地;也可以亲手合并。
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1
---------
Co-authored-by: Claude <noreply@anthropic.com>
feat(spec): the `picklist` metadata kind — a shared option list that select fields reference by name (#19518)
9
+
10
+
Clause-②: yes (widening)
11
+
12
+
-**The kind.**`PicklistSchema` — `{ name, label, description?, options }`, where `options` is the field option shape (`SelectOptionSchema`) reused as is. Authored in a package as `*.picklist.ts` (`definePicklist`) or `defineStack({ picklists })`. It is a registered kind (`MetadataTypeSchema`, `DEFAULT_METADATA_TYPE_REGISTRY`, `getMetadataTypeSchema('picklist')`) that loads before `object`. It is package-owned, so a runtime create or a per-organization overlay is refused.
13
+
-**The reference.**`Field.select({ picklist: 'industry' })` adds a `picklist` key to `FieldSchema`. It is valid on the option types only (select, radio, multiselect, checkboxes, tags). A field that declares both `picklist` and `options` is refused at `options`, with a prescription. The functional-completeness predicate counts a `picklist` reference as the field's option source.
14
+
-**The served shape.**`PicklistServedFieldSchema` declares what a client reads for a picklist-bound field: the resolved `options` next to the `picklist` that names the list. This release does not resolve the reference. Until the runtime does, a picklist-bound field is served without options, and the liveness ledger grades the key `planned` and warns an author who writes it.
15
+
-**Extensions.**`defineStack({ picklistExtensions: [{ extend, options }] })` adds options to a picklist that another package owns. It can only add; removing or renaming a value stays with the owning package.
16
+
-**Translation.**`TranslationData` gains `picklists.<name>.{ label?, options: { value: label } }`. `translatePicklist` translates a served picklist item. `translateObject` gives a picklist-bound field the list's option labels, and a field-level `options` entry still wins over them.
17
+
-**Studio type label.**`@objectstack/platform-objects` carries the `picklist` type's label and description in its metadata-forms translation bundles (en, zh-CN, ja-JP, es-ES).
18
+
-**Extraction.**`os i18n extract` walks `picklists.NAME.{label, options.VALUE}`, including an extension's options under the list it extends, and `os lint` reports an untranslated option under its own rule, `i18n/missing-picklist`.
19
+
-**SQL driver.** The SQL driver classifies the `picklist` field key as presentation, so it adds no column.
Copy file name to clipboardExpand all lines: content/docs/concepts/metadata-lifecycle.mdx
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -115,7 +115,7 @@ In shared-database multi-tenancy, **most metadata types must not be per-org cust
115
115
|`datasource`| ❌ | Connection strings; multi-tenant isolation is enforced at a higher layer. (`allowRuntimeCreate: true` — the datasource wizard persists `origin: 'runtime'` rows.) |
116
116
|`job`| ❌ |**Also `allowRuntimeCreate: false` since protocol 17** (#4509). `JobSchema.handler` names a function in the compiled bundle's function table, which a runtime writer has no way to reach — so a job created in Studio or through `PUT /meta` parsed, saved, reported success and was never scheduled. The door is closed rather than bridged: `job` stays first-class through `*.job.ts` / `defineStack({ jobs, functions })`, where every schedule shape, `retryPolicy` and `timeout` does reach the scheduler. Existing rows are untouched — they were never scheduled — and `migrateStoredMetadata` reports them `skipped`. |
117
117
118
-
Those five are the **complete**`allowOrgOverride: true` set: of the 27 types in `DEFAULT_METADATA_TYPE_REGISTRY`, every other one is `false`. The ❌ rows above are the `false` types whose *second* tier (`allowRuntimeCreate`) is worth calling out; any type not listed is `allowOrgOverride: false`.
118
+
Those five are the **complete**`allowOrgOverride: true` set: of the 28 types in `DEFAULT_METADATA_TYPE_REGISTRY`, every other one is `false`. The ❌ rows above are the `false` types whose *second* tier (`allowRuntimeCreate`) is worth calling out; any type not listed is `allowOrgOverride: false`.
119
119
120
120
There is no `workflow` metadata type (per [ADR-0020](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0020-state-machine-converge-and-enforce.md), record state machines are a `state_machine` validation). Nor is there a standalone `validation` type any more — it was retired in protocol 17 under [ADR-0088](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0088-metadata-kind-admission-and-retirement.md) because `ValidationRuleSchema` carries no object-binding key, so a rule authored through that door could never say what it protected; author rules in the object's own `validations[]` instead. The runtime gate is implemented in `OVERLAY_ALLOWED_TYPES` (derived from the registry) and enforced by `SysMetadataRepository.put()`.
|**viewItems**|`never`| optional |[MACHINE-ASSEMBLED] Non-container view artifacts of a runtime-assembled manifest (standalone ViewItems, flattened overlays) — written by package export and artifact factories, refused in authored stack sources. |
@@ -352,8 +354,10 @@ Installed package row whose manifest is the assembled package body
352
354
|**integrity**|`Record<string, string>`| optional | Per-file content digests of the plugin artifact (ADR-0025 §3.2) |
353
355
|**functions**|`Record<string, string \| { handler?: string; effect?: Enum<'pure' \| 'writes'> }> \| { name: string; handler?: string; packageId?: string; effect?: Enum<'pure' \| 'writes'> }[]`| optional | Named handler functions, lowered to the refs a JSON document carries |
|**viewItems**|`never`| optional |[MACHINE-ASSEMBLED] Non-container view artifacts of a runtime-assembled manifest (standalone ViewItems, flattened overlays) — written by package export and artifact factories, refused in authored stack sources. |
Copy file name to clipboardExpand all lines: content/docs/references/data/field.mdx
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -75,6 +75,7 @@ const result = CurrencyConfigSchema.parse(data);
75
75
|**accept**|`string[]`| optional | Permitted upload types for media fields, as MIME types or extensions (e.g. ["image/*", ".pdf"]). Offered to the file picker AND enforced on write. |
76
76
|**maxSize**|`integer`| optional | Maximum permitted file size in BYTES for media fields. Enforced on write against the stored file size, not just checked in the browser. |
|**picklist**|`string`| optional | Name of a shared `picklist` whose options this field offers — instead of `options`, never with it. Option types only (select, radio, multiselect, checkboxes, tags). The server resolves the reference: the field clients read carries the resolved `options`. |
78
79
|**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. On a `tree` field it is optional and, if given, must be the declaring object's own name — the object schema refuses any other target. |
79
80
|**referenceVia**|`string`| optional | Declares this text field as the id half of a polymorphic pointer pair (ADR-0052 §5 ActivityPointer): the value is a record id of the object named by the SIBLING FIELD this key names — e.g. `record_id` with `referenceVia: 'object_name'`. The sibling must be a declared field on the same object holding an object machine name. Text fields only; mutually exclusive with `reference` (a static and a per-record target contradict). Enforced today at seed load: the value resolves as a natural key against the object the sibling column names, and an unresolvable pointer is refused loudly instead of stored verbatim. Adds no referential integrity or $expand behavior. |
80
81
|**deleteBehavior**|`Enum<'set_null' \| 'cascade' \| 'restrict'>`| optional (default: `"set_null"`) | What happens if referenced record is deleted |
Copy file name to clipboardExpand all lines: content/docs/references/data/index.mdx
+2-1Lines changed: 2 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,7 +1,7 @@
1
1
---
2
2
title: Data Protocol — complete schema reference
3
3
navTitle: Data Protocol
4
-
description: "The ObjectStack Data Protocol in 29 reference pages: every schema in @objectstack/spec with its properties, types, defaults and a TypeScript example."
4
+
description: "The ObjectStack Data Protocol in 30 reference pages: every schema in @objectstack/spec with its properties, types, defaults and a TypeScript example."
5
5
---
6
6
7
7
{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
@@ -34,6 +34,7 @@ This section contains all protocol schemas for the data layer of ObjectStack.
0 commit comments