Skip to content

Commit f679753

Browse files
committed
Merge remote-tracking branch 'origin/main' into tmp-24472
2 parents 5b8e5df + f4755a6 commit f679753

79 files changed

Lines changed: 1812 additions & 317 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: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
'@objectstack/objectql': minor
3+
---
4+
5+
feat(objectql)!: under the `single` posture, a system insert into a platform object that names no organization takes the Default Organization, or is refused; it no longer lands with `organization_id` NULL
6+
7+
Clause-②: no (narrowing)
8+
9+
<!-- adr-0087: not-required (no-migration-prescription) A validity narrowing at the engine's system-insert door: no key of any metadata schema is removed, renamed or re-shaped, so there is nothing for `objectstack migrate meta` to rewrite and no tombstone. No export, type or error code is added, removed or renamed: `PLATFORM_OBJECT_TENANCY`, `isPlatformObjectOutOfTenantAuditScope` and `ERR_SYSTEM_WRITE_ORGANIZATION_REQUIRED` keep their shapes. The remedy is a run-time one, stated below. -->
10+
11+
**BREAKING** accept-set narrowing, shipped as `minor` under the repo's launch-window convention for breaking changes (ADR-0131 D9, D11).
12+
13+
- **Before.** A system-context insert (`isSystem`, no `tenantId`) into a `sys_` / `cloud_` / `ai_` object that carries an organization column, but that the tenancy inventory had not admitted, skipped the organization rule entirely, under every posture. The row landed with `organization_id` NULL. Thirty-five platform objects in this repository were in that population, among them `sys_business_unit`, `sys_business_unit_member`, `sys_member`, `sys_team`, `sys_user_permission_set`, `sys_user_position`, `sys_sharing_rule`, `sys_share_link`, `sys_comment`, `sys_attachment`, `sys_activity`, `sys_email`, `sys_inbox_message`, `sys_secret`, `sys_setting`, `sys_setting_audit`, `sys_webhook` and `sys_flow_credential`.
14+
- **Now, under `single`.** That insert goes through the same decision every application object already gets. With exactly one organization (the Default Organization), the row is stamped with it. With none, or with several, the insert is refused with `ERR_SYSTEM_WRITE_ORGANIZATION_REQUIRED` (status 500), and the message names the missing organization. Nothing is written.
15+
- **Unchanged.** Under a walled posture (`group` / `isolated`) the per-object exclusion stands as before; the walled half of this change ships in a later 18.x release. A write that carries an organization, on the execution context or on the record, is never re-stamped or refused. Objects with no organization column, objects declaring `tenancy: { enabled: false }`, and federated objects are outside the rule. Updates are untouched. The engine's diagnostics-only tenant-audit mute is unchanged.
16+
17+
**The remedy.** If a refusal names your insert, carry the organization on the write: `ql.insert(object, { ...row, organization_id: orgId }, { context: { isSystem: true } })`, or `{ context: { isSystem: true, tenantId: orgId } }`. On a fresh `single` deployment the auth plugin creates the Default Organization at boot, so a refusal reading `no-organization` means the write ran before it, or in a composition that registers `sys_organization` but mounts no auth plugin to create the Default Organization.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
'@objectstack/skills': patch
3+
---
4+
5+
docs(skills): `objectstack-data` says which hook doors provide `ctx.title` — a `body` has it, a deprecated `handler` gets it at runtime without `HookContext` declaring it, and `ql.registerHook` and a bare `ObjectQL` `bindHooks` do not provide it (#24289)
6+
7+
Clause-②: no
8+
9+
`skills/objectstack-data/references/data-hooks.md` listed `ctx.title(field?)` in the sandbox `ctx` table with no qualifier, so it read as a member of every hook context. It is not: a sandboxed `body` has it (the recommended form); an inline or named `handler`, which is deprecated, is handed it at runtime when the runtime binds the stack's hooks, but `HookContext` does not declare it, so a handler annotated with that type fails to compile; a hook registered with `ql.registerHook(...)`, or bound through `ObjectQL.bindHooks` with no runtime, gets no `ctx.title` at all. The row now says so. Text only; nothing an author writes changes, and no hook context changes.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
"@objectstack/lint": patch
3+
"@objectstack/spec": patch
4+
---
5+
6+
fix(lint): the RLS predicate and org-axis rules judge an object's `defaultRowScope`
7+
8+
Clause-②: no
9+
10+
- **What changed.** `validateRlsPredicateEnforceability` now judges `objects[].defaultRowScope.using` and `.check` with the same passes it runs on a permission set's `rowLevelSecurity[]` policy, with the declaring object as the target. A default whose predicate does not lower, does not parse, overruns a parse bound, names a column the object does not declare, reads a `current_user` key nothing resolves, or (for a `select` / `all` read scope) is refused by the engine's filter admission is reported under the same id, at the same `error` severity, at `objects[N].defaultRowScope.<clause>`. `validateOrgAxisRedLines` rule ① (`org-axis-permission-inheritance`) also judges a default that reads `parent_organization_id`.
11+
- **A new authoring-time finding on a shape that was silent.** Until now these rules walked `permissions[].rowLevelSecurity[]` only, so `os validate`, `os build` and `os lint` passed such a default. The security plugin's row-policy collection hands an object's default to the same compiler an authored policy reaches, so a predicate that does not lower is dropped at request time and the object reads as zero rows for every caller the default applies to. A stack that declares such a default now fails these commands; the fix is the same rewrite the finding prescribes for a permission-set policy. A default that lowers, and an object with no default, report nothing new.
12+
- **How the finding reads.** `where` names the default and its object (`default row scope "NAME" of object "OBJECT"`), never a permission set. `os explain` for the RLS predicate ids now says what a dropped `using` does for both sources: a permission set's policy denies the holders of that set, and a default, applied only when the caller's sets carry no row policy for the object and operation, denies every caller it applies to.
13+
- **The key's own text.** `ObjectSchema.defaultRowScope`'s description said authoring checks do not judge its predicate; it now says `os validate`, `os build` and `os lint` judge it as they judge a permission set's policy, and that saving the object through the server does not run that check.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
Clause-②: no
6+
7+
docs(spec): the `GET /api/v1/ai/models` response JSDoc cites `cloud ADR-0028`, not a bare `ADR-0028`
8+
9+
- **What changed.** The JSDoc on `AiModelsResponseSchema` and on its `models` field now cites `cloud ADR-0028` (the plan-filtered AI model picker decision) where it cited a bare `ADR-0028`. This repository's own ADR-0028 is about metadata naming and namespace isolation; the AI service that serves this list is a cloud package. The client SDK TSDoc and the docs already cite it this way.
10+
- **Unchanged.** Every schema shape, `.describe()` string, generated JSON Schema and behaviour.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
'@objectstack/lint': minor
3+
---
4+
5+
fix(lint)!: the page save door refuses a page component `visibleWhen` that `os build` refuses (#24491)
6+
7+
Clause-②: no (narrowing)
8+
9+
A page component's `visibleWhen` was judged by `os validate`, `os build` and `os lint`, and not by the metadata save door. A page saved through Studio, REST `/meta` or an MCP/AI author answered `200` and was stored when a component predicate called a function nothing registers (such as `totallyBogusFn(1)`), read a bare field (`status == 'open'`), did not parse, or overran the CEL parse budget. At render such a predicate faults, the visibility fallback is `true`, and the component renders for everyone.
10+
11+
The visibility-predicate family (`validateVisibilityPredicates`, `validatePredicatePathRefs`) now runs on a `page` write as well as on a `view` write, on the principle of ruling B on #23388: a page with a finding is fixed, then saved. The door's verdict is the build's finding: the same rule id (`visibility-predicate-unknown-function`, `visibility-bare-identifier`, `visibility-predicate-syntax`, `visibility-predicate-over-budget`), location (`pages[N].regions[N].components[N]`, and the slot or nested path the build names), message and hint. `visibility-root-mislayered` (a `data.`-rooted predicate on a page) rides the save response as an advisory, as it is a warning at the build. `validatePredicatePathRefs` judges schema-bound form views only, so it adds no finding on a page.
12+
13+
**BREAKING — what moves for consumers.** This is an accept-set narrowing on the metadata save door, shipped as `minor` under the launch-window convention for accept-set narrowings (`.changeset/pre.json` is open in `next` pre mode).
14+
15+
- FROM: a page write in publish mode answered `200` for a component `visibleWhen` the build refuses. This covers `PUT /api/v1/meta/page/:name` (and `saveMetaItem` in publish mode), the promotion of a draft (`POST /api/v1/meta/page/:name/publish`, `publishMetaItem`), and a package draft publish (`publishPackageDrafts`).
16+
- TO: it answers `422 INVALID_METADATA`, with the build's issue located at that component.
17+
- Fix: correct the predicate the issue names, as `os build` already requires, and save again. Call a function `introspectScope` lists, read through a bound root (`record.FIELD`, `current_user.FIELD`, `page.VAR`), and fix the syntax the verdict quotes. Saving the page as a draft (`mode: 'draft'`) is still allowed, because drafts are never gated; publishing that draft is judged.
18+
19+
**The next save of a stored page whose component `visibleWhen` the build refuses is refused with the fix; stored pages keep serving until then.** The door judges writes only: a stored page row is not re-judged at boot, loads and is served as before.
20+
21+
**Unchanged.**
22+
23+
- Stored rows are not migrated, and they are not refused on read.
24+
- A `view` write is judged as before.
25+
- `OS_ALLOW_UNLINTED_METADATA_WRITES=1` still turns a refusal into a logged write.
26+
- Measured before crossing: the 36 pages this repository ships (the examples, `platform-objects`, `plugin-approvals`, `cloud-connection`, `mcp` and `qa/downstream-contract`) carry 8 component `visibleWhen` predicates. Neither rule finds anything in them, at the build or at the door, before this change or after it; deployed rows were not measured.
27+
- No public export or signature moves. The two registry entries gain `page` in `runtimeTypes`, and the gate itself is unchanged: `page` already mapped to `pages`.
28+
29+
<!-- adr-0087: not-required (no-migration-prescription) a refusal at the page save door of a page component visibleWhen that the published validator already refuses at `os build`: no authorable key, spelling, export or stored shape moves, and no stored row is read, rewritten or converted. A stored page whose component predicate the validator refuses keeps loading until it is next saved, and the repair is the author's edit of the predicate, which no ledger entry can derive. The other categories are closed on facts: the package publishes (not unpublished); no ADR-0087 id covers this door (not already-registered); and the change is a door verdict, not a declaration or an export (not runtime-interface-only, type-surface-only or runtime-export-retired). -->
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
'@objectstack/verify': patch
3+
---
4+
5+
fix(verify): the CRUD stage reads a refused create's whole answer before it calls the create a hard failure
6+
7+
Clause-②: no
8+
9+
- **A long validation message is `needs-fixture` again.** The stage used to cut a refused create's body to 280 characters and then look for the validation code in that cut. The `/data` door answers a validation refusal as `{ error, code, fields, object }`, with the app-authored message first, so a message long enough pushed `"code":"VALIDATION_FAILED"` past the cut. The object was then reported `create-failed`, a hard failure that makes `os verify` exit 1, although the app's own rule had refused the auto-derived record.
10+
- **The verdict reads the whole body.** When the body is JSON, the stage reads the envelope's `code`: `VALIDATION_FAILED` on a 400 is `needs-fixture`. A body that is not JSON, or has no string `code`, is matched against the full text with the stage's original pattern.
11+
- **Unchanged:** the stored `detail` still holds at most 280 characters, and the rule is the same: a 400 validation answer is `needs-fixture`, and anything else is `create-failed`.
12+
13+
A patch: the stage reads the same answer correctly. No export, accepted value or published shape changes.

‎.claude/skills/pm-dispatch/SKILL.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ PM 的工作是循环:选卡 → 认领 → 派发 → 收集 → 复核 → 报
5353
| `triage` | 以分诊座位身份运行(只扫/分类/路由,永不认领) | — |
5454
| `<domain>`(如 `spec`、`skills`) | 以该 `domain:*` 车道的执行座位身份运行 | — |
5555
| `epic:#<n>` | 以父单 #n 的 epic PM 身份运行(见 `references/seat-lifecycle.md` 〈Epic 子树车道〉) | — |
56-
| `director` | 以项目总监席身份运行(人工召唤;三职见 升级与决策 节与 `references/lanes/director.md`) | — |
56+
| `director` | 以项目总监席身份运行(人工召唤;三职与章程见 `references/lanes/director.md`) | — |
5757
| `label:<name>` | backlog 过滤标签;`label:all` = 全部 open 未认领 | `pm:queue` |
5858
| `repo:<owner/name>` | 扫哪个仓的 backlog(单 issue 的落地仓看它自己的 `repo:*` 标签) | `objectstack-ai/objectstack` |
5959
| `batch:<n>` | 席位同时在飞的子代理总上限,按 `Agent` 调用与 `create_session` 计:dev、复核、普查皆计,⛔ 只读不豁免 | 默认 `3`;`n` 的维护者天花板 `5` |
@@ -269,9 +269,9 @@ PM 的工作是循环:选卡 → 认领 → 派发 → 收集 → 复核 → 报
269269
- 决策卡带选项、推荐、证据、四棱块与工作量列;受阻执行卡同笔挂 `pm:blocked`+`Blocked-by:`。
270270
- `AskUserQuestion` 只是在场加速器:仅当维护者在本会话 ~30 分钟内有过人类输入才可发。
271271
- 每问必带推荐项,被 Skip 或长挂即转卡通道,⛔ 不重弹。
272+
- 推送维护者:受管 PR 待批合、决策卡待字、发版、仅其可解之阻 ⇒ 另发 `PushNotification`。
273+
- 一行 ≤200 字,先写动作与编号;每项恰一次,例行进展、刚问刚答与在场即见的不推。
272274
- 项目总监席:维护者在所在仓召唤 `/pm-dispatch director`;⛔ 无 Routine/cron;未回字 = 不在 = 停。
273-
- 三职 = 契约复核记录事后审计、决裁勤务、维护者动作台账。
274-
- 每场召唤把带席内 ACCEPT 的受管草稿呈为一批 ≤5 行决裁;批准与合并仍是维护者的点击。
275275
- 总监席档位由维护者按项目人工定,每场记入摘要台账;⛔ 无档位硬门、无免档整理态。
276276
- 总监席是唯一裁决者:决策箱、代裁与一类自裁只出自本席,⛔ 无子代理无自述。
277277

‎content/docs/deployment/validating-metadata.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,7 @@ orthogonal to both, and no cell here can carry it; it is written out in
484484
| View references — form targets, view-key collisions (#2554) | ✓ | ✓ | ✓ | — |
485485
| Flow authoring anti-patterns (#1874) | ✓ | ✓ | ✓ | ✓ᶠ |
486486
| Flow trigger readiness — a flow that looks armed and never launches (#5762) | ✓ | ✓ | ✓ | ✓ᶠ |
487-
| `views[]` conditional-visibility predicates — CEL syntax, parse budget, bare identifiers, binding-root layer, schema path refs (ADR-0089 D3b, #7010) | ✓ | ✓ | ✓ | ✓ᵛ |
487+
| `views[]` and page-component conditional-visibility predicates — CEL syntax, parse budget, bare identifiers, binding-root layer; schema path refs on `views[]` only (ADR-0089 D3b, #7010; page components at the save door #24491) | ✓ | ✓ | ✓ | ✓ᵛᵖ |
488488
| Advisory: record titles, semantic field pointers (ADR-0085), form-section layout, action placement, seed replay/state safety, capability references, liveness | ✓ | ✓ | ✓ | — |
489489
| Package docs — flatness, prefixes, links (ADR-0046) | ✓ | ✓ | ✓ | — |
490490
| Undeclared authoring keys — every metadata collection (#3786) and the stack's own top-level keys (#4167) | ✓ | ✓ | — | — |

0 commit comments

Comments
 (0)