Skip to content

Commit a1a42d4

Browse files
committed
Merge origin/main (2972097) into claude/issue-20334-aggregate-positions-parity
Claude-Session: https://claude.ai/code/session_01Bvd69VPa6puiNzzPUroDBx Co-authored-by: Claude <noreply@anthropic.com>
2 parents 93170f5 + 2972097 commit a1a42d4

277 files changed

Lines changed: 1381 additions & 336 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: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
"@objectstack/lint": minor
3+
---
4+
5+
A row-level-security predicate that compares a field with a `json` or `multiple` field is refused when it is authored, at `os validate` / `os build` / `os lint` and at the metadata save door, instead of only when it runs (#19886).
6+
7+
**BREAKING** — an accept-set narrowing, shipped by `@objectstack/lint` as `minor` under the repo's launch-window convention for accept-set narrowings. The hand-migration prescription is already registered under protocol major 18 as `cel-predicate-one-value-comparand-refused`, which names this class.
8+
9+
Clause-②: no (narrowing)
10+
11+
`record.status != record.tags`, with `tags` a `json` field or a `multiple` lookup, lowers to a legal filter shape, because the CEL lowering sees the predicate's text and not the object's field types, and the engine's filter admission does not judge a `{ $field }` reference against the referenced column's type either. Measured before this change: 400 cells (`==`, `!=`, `!(==)`, `>`, `<=`; a `json`, `address`, `multiselect`, `multiple` lookup and `multiple` user field; both operand orders; `using` on `select` / `all` / `update` / `delete` / `insert` and `check` on `insert` / `update` / `all`) were all accepted by the real `os validate` and by the save door. The runtime refused every one of them, measured through the real plugin-security on driver-sql: a read the `using` scopes answered `INVALID_FILTER` / 400, a by-id update or delete it scopes `PERMISSION_DENIED` / 403, and every insert or by-id update judged by the `check` (or by a `using` standing in as the check) `INVALID_FILTER` / 400, with nothing stored.
12+
13+
What changes:
14+
15+
- `@objectstack/lint`: `validateRlsPredicateEnforceability` reports `rls-predicate-unenforceable` for every lowered field-to-field comparison (`==`, `!=`, `>`, `>=`, `<`, `<=`, on either side, under `!` too) in which either column is DECLARED to hold a list or an object. The declaration is read from the stack's own objects through the spec's value-shape classes, the same two driver-sql refuses such a comparison by: a structured JSON type (`json`, `composite`, `repeater`, `record`, `location`, `address`, `vector`), or a multi-valued field (`multiselect`, `checkboxes`, `tags`, or `select` / `radio` / `lookup` / `user` / `file` / `image` with `multiple: true`). It judges `using` and `check` on every operation. The finding names each comparison and the declaration behind it, and states the clause's run-time consequence. A clause it refuses is not also handed to the engine's filter judge, so one defect earns one finding.
16+
- Both doors run this rule already, so both refuse: `os validate` / `os build` / `os lint` fail, and a publish through the metadata save door answers `422 INVALID_METADATA` with the same sentence in `issues[]`. `OS_ALLOW_UNLINTED_METADATA_WRITES=1` still turns the save-door refusal into a logged warning.
17+
18+
Not changed: a field compared with a single-valued field (`record.status != record.owner`, `record.amount > record.budget`), a `json` or `multiple` field compared with a literal or tested against `null`, and any column the stack does not declare (an object from another package, an external object with no field map), which the rule does not judge. The runtime refusals of stages 2d and 2e stay as the backstop. The stage 2d changeset's sentence that a field compared with a `json` or `multiple` field "is not reported at authoring time" no longer holds: it is now reported at both doors.
19+
20+
No shipped predicate moves: 0 of the 187 `using` / `check` / `condition` strings in this repository's packages and examples, and 0 of the 3 in the cloud repository, compare a field with a `json` or `multiple` field, and the real `os validate` over `app-crm`, `app-multi-package`, `app-showcase` and `app-todo` reports no `rls-predicate-*` finding.
21+
22+
**What to change.** A field compared with a `json` or `multiple` field has no row-filter form: compare with a single-valued column, or with a literal or a `current_user` value ("one of these values" is `record.status in ['open', 'pending']`, or `record.owner in current_user.org_user_ids`), or move the condition into a validation rule or a hook.
23+
24+
<!-- adr-0087: not-required (already-registered cel-predicate-one-value-comparand-refused) The entry already names this exact class in its surface ("a field compared with another field (==, !=, or an ordering operator) where either column holds a list or an object on the record, as a json column or a multiple lookup does") and carries its prescription in its replacement ("A field compared with a json or multiple field has no pushdown form: compare with a single-valued column, or move the condition into a validation rule or hook"). This change moves where that registered class is refused, from run time to authoring time; it adds no class and no prescription the entry does not already hold, and it rewrites no stored metadata. -->

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,8 @@ conversion 已不再覆盖的那位。
174174
`spec-changes.json`、生成的 upgrade guide 与 `spec_changes` MCP 工具才是主通道,不
175175
注册它们就一直空着。
176176

177-
⚠ **这是两个独立义务,其中只有一个是字符串匹配。** registry 条目是门禁读的;
178-
conversion 是消费者跟的。两个都要写。
177+
⚠ **这是三个独立义务,其中只有一个是字符串匹配。** registry 条目是门禁读的;
178+
conversion 是消费者跟的;D3 条目是升级方的 agent 读的。三个都要写。
179179

180180
- [ ] **一条 `MetadataConversion`**,在 `packages/spec/src/conversions/registry.ts`:
181181
kebab-case 的 `id` 以 `-removed` 结尾、`toMajor`、每键一条
@@ -203,9 +203,7 @@ conversion 是消费者跟的。两个都要写。
203203
- [ ] **`surface` 保持散文 —— 它不再被匹配。** 按作者写元数据的方式写
204204
(`flow.nodes[].outputSchema`),那也是 upgrade guide 打印的。多键 conversion
205205
仍用恰好 `' / '` 连接子句(tool 清扫以来的 house style)。下游不再有任何东西
206-
从它解析归属 —— 那个职责移给了上面的条目。#5898 起这对**每个**消费者都成立:门
207-
(c) 的 *aged-out tombstone* 证明曾是最后一个叶匹配者,现在也读同一张精确键
208-
表,再没有任何规则从 `surface` 解析归属。
206+
从它解析归属 —— 那个职责移给了上面的条目。
209207
- [ ] **`retiredFromLoadPath: true`** —— 退役恒真,但管辖权只有 authoring 漏斗
210208
`normalizeStackInput`;三处 data-at-rest seam 以 `includeRetired: true` 故意重放退役
211209
条目,它**一处也拦不住**:`applyConversionsToStoredItem`(钉死)、automation
@@ -231,8 +229,10 @@ conversion 是消费者跟的。两个都要写。
231229
`retiredFromLoadPath`。你的 transform 不属于这些形状,就自己证明幂等 —— CLI
232230
e2e(`packages/cli/test/migrate-meta.e2e.test.ts`)会 replay 迁移后的快照并断
233231
言 `applied` 为空。
234-
- [ ] **没有 source 可重写的响应面键**,改注册成 `SemanticMigration`(D3
235-
`semantic[]`),`reason` 与 `acceptanceCriteria` 非空 ——
232+
- [ ] **每个退役家族一条 D3 `semantic` 条目**(`SemanticMigration`;按家族,不按键),
233+
即使 D2 conversion 无损也欠 —— D2 只承载机械数据修复。`reason`(升级方仍欠的判断)
234+
与 `acceptanceCriteria` 非空;一条一个文件,见 `packages/spec/src/migrations/entries/`
235+
的 README。**没有 source 可重写的响应面键**写不出 conversion,只走这一条 ——
236236
`EnhancedApiError.fieldErrors` 是成品示例。
237237

238238
## 4. 面清单
@@ -248,7 +248,7 @@ conversion 是消费者跟的。两个都要写。
248248
窄全盲(#3883 从 `defineAction` 的输入删了三个键,快照没动)。它的门还住在另
249249
一个 workflow(`TypeScript Type Check`,不是 `Check Generated Artifacts`),读
250250
的是构建出的 `dist/*.d.ts`。
251-
- [ ] **Conversion + 链步 + 精确键 `RETIRED_KEYS_BY_MAJOR` 条目**(§3)。
251+
- [ ] **Conversion + 链步 + 精确键 `RETIRED_KEYS_BY_MAJOR` 条目 + 每家族一条 D3 `semantic` 条目**(§3)。
252252
- [ ] **Liveness 台账** —— 按 §2 的路线表,带 `verifiedAt`。更新 README 的按类型行
253253
**连同计数**(那张表狠狠漂过一次;用 README 里的 python 片段重新生成计数,不
254254
要手改)。

‎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

‎content/docs/concepts/metadata-lifecycle.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: Metadata lifecycle — load, publish and HMR
33
navTitle: Metadata Lifecycle & HMR
4-
description: How metadata flows through Repository → Change Log → Cache → Registry — the canonical event stream that powers Studio HMR, REST writes, and future cloud editing.
4+
description: How metadata flows through Repository, Change Log, Cache and Registry — the event stream behind Studio hot reload, REST writes and cloud editing.
55
---
66

77
This page documents the metadata data path introduced by [ADR-0008](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0008-metadata-repository-and-change-log.md) and refined by [ADR-0005](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0005-metadata-customization-overlay.md). It is the canonical event stream that powers Studio Hot Module Replacement (HMR), REST writes, and future cloud editing.

‎content/docs/concepts/north-star.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: North star — why this framework exists
33
navTitle: North Star
4-
description: ObjectStack's current product and architecture direction.
4+
description: "Where ObjectStack is headed: a metadata-native backend that humans and AI agents can both operate safely — its principles, runtime shape and non-goals."
55
---
66

77
ObjectStack is the metadata-native backend for business software that humans and

‎content/docs/data-modeling/drivers.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: Database drivers — Postgres, MySQL, Mongo
33
navTitle: Database Drivers
4-
description: Configuration reference for supported database drivers
4+
description: Connect ObjectStack to PostgreSQL, MySQL, SQLite, MongoDB, Turso or memory — URL-based driver inference, per-driver config keys and dialect caveats.
55
---
66

77
ObjectStack supports multiple database backends through a unified driver interface.

0 commit comments

Comments
 (0)