Skip to content

Commit 1161545

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/issue-19920-exported-types-remainder
2 parents 35094e8 + 5049a3c commit 1161545

320 files changed

Lines changed: 5172 additions & 577 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
---
2+
"@objectstack/spec": minor
3+
---
4+
5+
feat(spec)!: retire the `inline` and `grid` arms of form `layout` — every renderer folded both to `vertical`, and multi-column is `columns` (ADR-0049)
6+
7+
<!-- adr-0087: registered form-layout-inline-grid-to-vertical, ui-form-layout-inline-grid-retired -->
8+
9+
**BREAKING** — form `layout` accepts exactly `'vertical' | 'horizontal'` on both surfaces
10+
that declared the four-arm enum: the `object-form` page component
11+
(`ObjectFormPropsSchema.layout`) and the form view (`FormViewSchema.layout` — `view.form`,
12+
`view.formViews.*`, a form view item's `config`, and the flattened form overlay, which
13+
spreads the form view's shape). `'inline'` and `'grid'` are refused at parse, each with a
14+
prescription naming what to write instead.
15+
16+
| surface | before | after |
17+
|:--|:--|:--|
18+
| `object-form` `layout: 'grid'` | parsed clean, rendered as `vertical` | refused — write `'vertical'` (or omit `layout`); for multi-column set `columns` |
19+
| `object-form` `layout: 'inline'` | parsed clean, rendered as `vertical` | refused — write `'vertical'` (or omit `layout`) |
20+
| form view `layout: 'grid'` | parsed clean, rendered as `vertical` | refused — write `'vertical'` (or omit `layout`); for multi-column set `columns` |
21+
| form view `layout: 'inline'` | parsed clean, rendered as `vertical` | refused — write `'vertical'` (or omit `layout`) |
22+
| `layout: 'vertical'` / `'horizontal'` | accepted | **unchanged** |
23+
| `columns` | honoured under every layout | **unchanged** — the key multi-column always lived under |
24+
25+
**What was actually wrong.** No renderer ever gave either value a behaviour of its own.
26+
Measured at the `.objectui-sha` pin this repo builds against (`f8a9d0fb0`): the simple
27+
`object-form` arm folds both to `vertical` (`ObjectForm.tsx:1406-1410`, under the comment
28+
"Map 'grid' and 'inline' to 'vertical' as fallback"); the drawer and modal arms
29+
(`ObjectForm.tsx:463`, `:499`) and `DrawerForm.tsx:575` / `ModalForm.tsx:597` pass only
30+
`vertical` / `horizontal` through; `TabbedForm.tsx:556`, `SplitForm.tsx:445` and
31+
`WizardForm.tsx:1075` hard-code `vertical`. So both values were a green parse for a value the
32+
renderer threw away. The spec had admitted them from two declarations — the designer palette
33+
and the registry `inputs` offered all four — never from a read.
34+
35+
Under the maintainer's ADR-0049 family criterion (the capability exists on mainstream
36+
platforms ⇒ build the consumer; it does not ⇒ retire), multi-column — what `grid` would
37+
mean — already exists here under another key, `columns`, which the renderer honours under
38+
every arm; `inline` is a toolbar / filter-row pattern, not a record-form layout. The two arms
39+
are redundant vocabulary, retired with no alias window.
40+
41+
## What to write instead
42+
43+
```ts
44+
// before — parsed clean, rendered single-column 'vertical'
45+
{ type: 'object-form', properties: { objectName: 'crm_lead', layout: 'grid' } }
46+
// after — what it rendered; add `columns` if a multi-column form was the intent
47+
{ type: 'object-form', properties: { objectName: 'crm_lead', layout: 'vertical', columns: 2 } }
48+
```
49+
50+
`'inline'` → `'vertical'` (or delete `layout`: `'vertical'` is the renderer default). A form
51+
that wrote `'grid'` without `columns` always rendered single-column; only its author knows
52+
whether more columns were meant — set `columns` to the count you meant.
53+
54+
Existing sources: `os migrate meta --from 17` lists the mechanical edits; apply them by hand.
55+
56+
The retirement kit:
57+
58+
- both enums narrowed to `'vertical' | 'horizontal'`, each with a per-value error map keyed on
59+
the input (the `record:chatter` `position` precedent), so only a value that used to be legal
60+
is told it "was removed"; a never-vocabulary value keeps zod's own enum refusal
61+
- the D2 conversion `form-layout-inline-grid-to-vertical` (protocol 18, retired from the load
62+
path) rewrites both values to `'vertical'` and leaves `columns` untouched — on `object-form`
63+
page components, on every form payload a view carries, and on the assembled-manifest
64+
`viewItems` channel, so stored rows and assembled artifacts replay clean; wired into the
65+
protocol-18 chain step
66+
- one D3 entry for the family, `ui-form-layout-inline-grid-retired`, carrying the one judgement
67+
the chain cannot make: whether a form that said `grid` wanted columns it never declared
68+
- pin tests (`ui/form-layout-inline-grid-retired.test.ts`): both values refused with the
69+
prescription on five doors (`ObjectFormPropsSchema`, the `object-form` props-map row,
70+
`FormViewSchema`, a view container's `formViews`, the flattened form overlay), `vertical` /
71+
`horizontal` green on each as controls, the conversion's rewrite parsing green on the door
72+
that refused its input, and the chain registration
73+
- the `columns` descriptions no longer say "grid layout"; the generated references
74+
(`content/docs/references/**`, `skills/objectstack-ui/references/react-blocks.md`, the JSON
75+
Schema) print the two-arm enum; the hand-written `layout-dsl` page and the `form.layout`
76+
liveness row (still `live`) are updated
77+
- `api-surface/` and `authorable-surface/` are unchanged, correctly: they ratchet export and
78+
key existence, and no export or key leaves — `layout` is still declared, two values narrower
79+
80+
Clause-②: no (narrowing)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
'@objectstack/cli': minor
3+
'@objectstack/core': minor
4+
'@objectstack/spec': patch
5+
---
6+
7+
`os test` reports the suite and scenario names an author writes, and selects scenarios with `--tags` (#20289)
8+
9+
Clause-②: no
10+
11+
A Quality Protocol suite's `name`, each scenario's `name` and `description`, and scenario `tags` were parsed at load and then read by nothing: the report headed each suite with its file's basename, printed every scenario by its `id`, and `os test --tags critical` failed with `Nonexistent flag: --tags`.
12+
13+
- **Names in the report.** The suite heading is now the suite's `name` followed by its file — `📄 Running suite: Accounts smoke (accounts.test.json)` — and each scenario line is its `name` with the `id` in brackets — `✅ Scenario: An account can be created [acct-create] (12ms)` (the id alone when the two are equal). A failed scenario's `description` is printed under its line, before the error. A suite whose file fails to load is still headed by the file alone, since no name was parsed.
14+
- **`--tags TAG[,TAG...]`** runs only the scenarios carrying AT LEAST ONE of the listed tags (any-of, exact, case-sensitive) — the comma-list reading of Odoo's `--test-tags` and the everyday use of Playwright's `--grep @a|@b`. With the flag, an untagged scenario is left out. Left-out scenarios are **deselected**: not run, counted on the summary (`--tags smoke selected 1 of 4 scenarios; 3 deselected (not run, not counted as passed).`), never counted as passed. A requested tag that no loaded scenario carries is named on the summary. An empty entry (`--tags smoke,`) is refused before anything runs. Without the flag nothing changes: every scenario runs.
15+
- **Exit status.** A selection that matches no scenario takes the posture an empty pattern already has: exit `0` with `No scenario matched --tags …`, and exit `1` under `--fail-on-empty`, whose description now covers both cases. The `Found N test suites.` line and the `SUCCESS: All N scenarios passed.` / `FAILED: …` summary lines keep their spelling.
16+
- **`@objectstack/core`:** `QA.TestResult` gains `scenarioName` and `description` on every result, and `suiteName` on every result `runSuite` produces (absent only from a lone `runScenario` call, which has no suite).
17+
- **`@objectstack/spec`:** `TestScenario.requires` (`params`, `plugins`) is still checked by nothing — its describe() now says **NOT CHECKED** instead of reading as a guard, so a scenario that declares a plugin the target lacks still runs, and the unmet requirement surfaces only as whatever failure it causes, if any. The liveness ledger (`liveness/qa.json`) moves the four keys above to `live`, citing their readers.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
'@objectstack/formula': minor
3+
'@objectstack/spec': minor
4+
---
5+
6+
Expression refusals now carry a stable `code` and typed `params` beside their English `message`, so a localized author surface can render its own words: `validateExpression` (every entry in `errors[]` and `warnings[]`), `collectCelRootIdentifiers` (its `ok: false` arm), `predicateSlotRefusal` and `structuralConditionRefusal` (#20291)
7+
8+
Clause-②: yes
9+
10+
Until now the only thing a refusal said was an English sentence, and a designer running in another locale could only show it verbatim, beside its own translated headings. Each refusal now also names its code — one of a closed, kebab-case set — and the values its sentence interpolates, so a consumer keys a catalogue row to the code and fills it from the params. The `message` is the same sentence, byte for byte; nothing is removed or renamed, so no existing reader changes.
11+
12+
- `@objectstack/formula` exports `EXPRESSION_REFUSAL_CODES` (the closed set as a frozen list) and the types `ExpressionRefusalCode`, `ExpressionRefusalParams` (code → params), `ExpressionRefusal`, `ExprValidationCode`, `CelRootsRefusalCode`, `CelFieldRole`, `ExpressionSourceKind` and `CelRootIdentifiersResult`. `ExprValidationError` gains `code` and `params`.
13+
- `@objectstack/spec/automation` exports `FLOW_SLOT_REFUSAL_CODES` (the closed set of both flow-slot refusal producers, as a frozen list) and the types `FlowSlotRefusalCode`, `FlowSlotRefusalParams` (code → params), `PredicateSlotRefusalCode`, `PredicateSlotRefusal`, `PredicateSlotValueKind`, `StructuralConditionRefusalCode`, `StructuralConditionRefusal` and `StructuralConditionValueKind`. `predicateSlotRefusal` returns `PredicateSlotRefusal` and `structuralConditionRefusal` returns `StructuralConditionRefusal`; each is its previous `{ message, source }` plus `code` and `params`.
14+
- Narrowing on `code` narrows `params`. A code never changes once published: a reworded message keeps its code, and a new refusal gets a new one.
15+
- A `detail` param is the CEL or template engine's own diagnostic, in English, passed through as the message carries it.
16+
- These are authoring diagnostics returned as values, not ADR-0112 request error codes, which is why they are kebab-case.
17+
- The `validate_expression` MCP tool forwards `validateExpression`'s `errors` and `warnings` as they are, so each entry in its answer now also carries `code` and `params`.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
"@objectstack/rest": patch
3+
"@objectstack/runtime": patch
4+
---
5+
6+
**Pending metadata drafts are now served only to a caller with an authoring capability — the check `GET /api/v1/meta/_drafts` already made.** A pending draft is unpublished authoring work. Until now, every door that reads one, other than `/meta/_drafts`, served it to any signed-in caller who could open the item. The draft access that the `previewDrafts` / `state` request declarations, ADR-0106 D4 and ADR-0037 described as admin-gated upstream is now gated.
7+
8+
Clause-②: no
9+
10+
- **The doors:** `GET /api/v1/meta/:type/:name?state=draft` and `?preview=draft`, `GET /api/v1/meta/:type?preview=draft`, and `POST /api/v1/analytics/dataset/query` with `previewDrafts: true` or `?preview=draft` on `RestServer`, plus the runtime dispatcher's `/meta` item and list `?preview=draft`.
11+
- **Who may read drafts:** a system context, or a caller holding `studio.access`, `setup.access` or `manage_metadata`. This is the same predicate `/meta/_drafts` asks, not a second rule.
12+
- **Everyone else gets the read as if the draft switch were absent.** They receive the published version, pruned for them as the plain read prunes it. For a name that has nothing published, they receive that door's own absence answer: `404` on the item read, `404 NOT_FOUND` for a dataset by name, and the item simply missing from a list. The answer is byte-identical to the plain read, so it does not reveal whether a draft exists. For example, `?state=draft` on an app with no pending draft answers such a caller with the published app, not `404 NO_DRAFT`. A dataset preview run by such a caller uses live rows, never a pending seed draft's rows.
13+
- **Unchanged:** callers with an authoring capability read exactly what they read before. Whoever may save an app reads its `?state=draft` whole, and everyone else pruned per caller. `/meta/_drafts` still answers `403` to a caller without the capability, because it lists drafts and has no published answer to fall back to. `/diff` and `/history` are not changed by this release.

‎.claude/skills/spec-property-retirement/SKILL.md‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,11 @@ preview renderer 不算消费者)与 AGENTS.md §"Touched `packages/spec`?"(八
4040
`rls.enabled` 曾是「证据错误的 live」而实际**无人读取** —— disabled 的
4141
policy 仍在贡献它的授权。那个键最后是**被强制执行**,不是被删除。功能存在时,强制
4242
执行赢。
43-
- [ ] **它是文档形状的吗?** `hook.label`、`hook.description`、`flow.description`
44-
没有运行时消费者,但被**有意保留** —— 它们为下一个读者(按 ADR-0033,常是模
45-
型)记录意图。把豁免写进台账 `note`,下次审计不再重审。良性展示元数据
46-
(`description`、`tags`、`icon`)永远谈不上「误导」;不要标 `authorWarn`,也
47-
不要退役它。
43+
- [ ] **主流平台有没有这个能力?** 声明而未执行的键每族问这句,按维护者原话(裁决评论
44+
`5727134555`):「有 ⇒ 补消费端(一次做对);没有 ⇒ 退役,而不是看仓里有没有人读」。
45+
文档形状的键(`hook.label`、`flow.description`)为下一个读者记录意图(ADR-0033),是「有」的
46+
一例:补渲染、不退役,判定写进台账 `note`,下次审计不再重审。`description`、`tags`、`icon`
47+
不自动豁免:RLS 策略的 `tags` 主流没有 ⇒ 退役。`authorWarn` 另按 README 判。
4848
- [ ] **有已承诺的路线图吗?** 那就是 `experimental` + `.describe()` 里的
4949
`[EXPERIMENTAL — not enforced]` 标记,不是删除。
5050
- [ ] **同 major 记账。** 同一个*未发布 major* 里更早的 conversion 改名了你现在要删
@@ -58,7 +58,7 @@ preview renderer 不算消费者)与 AGENTS.md §"Touched `packages/spec`?"(八
5858
`live-elsewhere` 一节。
5959
- [ ] **零编写实例普查要并跑一个同族已知存活的键作对照**,两读数都报;同得零即没测出。
6060
- [ ] **拿 `cross-repo` 行当论据,就当刻重核路径与行号**:漂了读作重新取证,⛔ 不是 `dead`。
61-
- [ ] **「零编写实例」≠「没有代码读它」。** 前者答的是示例应用,后者才是退役的证据。
61+
- [ ] **「零编写实例」≠「没有代码读它」。** 前者答的是示例应用,后者才证 `dead`。
6262

6363
## 1. 裁判是构建,不是台账
6464

‎content/docs/api/declarative-endpoints.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: Custom endpoints declared as metadata
33
navTitle: Declarative Endpoints
4-
description: "Expose your app to systems outside the platform by declaring an apis: endpoint as metadata — which channel to pick, the four publish gates, and the obligation that comes with an anonymous endpoint."
4+
description: "Expose your app to outside systems by declaring an apis: endpoint as metadata — pick the channel, pass the four publish gates, secure anonymous calls."
55
---
66

77
A stack can publish an HTTP endpoint as **metadata** instead of writing a handler: a URL,

‎content/docs/api/plugin-endpoints.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: Plugin endpoints — add routes from a plugin
33
navTitle: Plugin Endpoints
4-
description: REST endpoints that become available when the corresponding plugin is installed — auth, workflow, automation, views, realtime, notifications, AI, i18n, and file storage.
4+
description: REST endpoints each installed plugin adds — auth, workflow, automation, views, realtime, notifications, AI, i18n and files — and how to discover them.
55
---
66

77
These REST endpoints are only available when the corresponding plugin is installed. Check the [discovery manifest](/docs/api#discovery) `services` map before calling them; all paths are relative to the base URL (defaults to `/api/v1`).

‎content/docs/automation/approvals.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: Approval chains — multi-step sign-off rules
33
navTitle: Approval workflow
4-
description: Route a record for sign-off — who can configure the automation, and the run-identity decision that keeps an approval flow from quietly bypassing row-level security.
4+
description: Route a record for sign-off with an approval flow — who may configure it, and the run-identity choice that keeps it from bypassing row-level security.
55
---
66

77
## Scenario

‎content/docs/automation/connectors.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: Connectors — call external systems safely
33
navTitle: Connectors
4-
description: Call external systems from flows — plugin-registered connectors, and declarative provider-bound instances (rest / openapi / mcp) authored as pure metadata with reference-based credentials.
4+
description: Call external systems from flows with plugin connectors or declarative rest, openapi and mcp instances — pure metadata with reference-based credentials.
55
---
66

77
> **Status:** Shipped · **Audience:** App authors (human and AI), integration

‎content/docs/capabilities/index.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: What can it do? — the capability overview
33
navTitle: What Can It Do?
4-
description: The platform's capabilities in business language — data, views, automation, approvals, permissions, analytics, AI, integrations — each with a real CRM as the running example
4+
description: What the platform can do, in business language — data, views, automation, approvals, permissions, analytics, AI and integrations, shown on a real CRM.
55
---
66

77

0 commit comments

Comments
 (0)