Skip to content

Commit 01ec8f2

Browse files
committed
Merge origin/main into claude/project-thread-l5hgva
Claude-Session: https://claude.ai/code/session_01VPkxx6ackwXchvtVNidtum Co-authored-by: Claude <noreply@anthropic.com>
2 parents e645ebb + 0453fab commit 01ec8f2

219 files changed

Lines changed: 4322 additions & 5358 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.

‎.changeset/25610-notify-email-field-recipient.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ A `notify` node's `recipients` takes a fourth form, as the whole value: `{ email
2222
- The form is the whole `recipients` value, never a list entry.
2323
- `@objectstack/lint` refuses, at `os validate`, `os lint`, `os build` and the runtime publish door for a flow, an email-field recipient whose field the trigger object does not declare (`notify-email-field-unknown`), one whose field is not `email`-typed (`notify-email-field-not-email`), and one in a flow that hands its run no record of a known object — a schedule, an inbound api call, a record trigger that names no object (`notify-email-field-no-trigger-record`). A trigger object another package declares is not judged.
2424

25-
This change is the protocol half. Delivery — resolving the field to an address, the outbound mail configuration, the per-address rate limit and the messaging-log entry — is the messaging service's half and lands separately; until it does, a run that reaches such a node fails at the node.
25+
This change is the protocol half. Delivery — resolving the field to an address, the outbound mail configuration, the per-address rate limit and the messaging-log entry — ships in the same release in `@objectstack/service-automation`.
2626

2727
Clause-②: yes (widening)
2828

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
'@objectstack/service-storage': patch
3+
---
4+
5+
fix(storage)!: a record write naming a file its caller may not read is refused at the field
6+
7+
A record write that names a file its caller may not read is now refused at that field, instead of saving the id as written. The answer is one `400 VALIDATION_FAILED` whose `fields[]` names each such field with the ADR-0114 code `invalid_value`, the same envelope the public form door gives an anonymous submission that carries a file. It names the field only. An id that names no file of the acting organization gets the same answer, so the write cannot tell the two apart. Nothing is claimed, copied or written. System writes and writes with no caller context reference files as before, and an authorization-store outage still fails the write with its declared envelope.
8+
9+
A file governed by a record's rule also gets the record-governed link lifetime at the download door.
10+
11+
Clause-②: no (narrowing) — a record write that names a file its caller may not read, or an id no file of the organization has, was saved and is now refused. Nothing that was refused becomes accepted.
12+
13+
Metadata-protocol: none (a write-time refusal on file fields; nothing a metadata author writes changes)
14+
15+
<!-- adr-0087: not-required (no-migration-prescription) No metadata author's surface moves: no spec key or authorable spelling is added, removed, renamed or re-shaped, and no exported declaration is removed or narrowed, so there is nothing for `objectstack migrate meta` to rewrite. What changes is which record writes the file-reference lifecycle accepts: a write naming a file its caller may not read is refused at the field. The remedy is naming a file the caller may read, or leaving the field empty, never a rewrite of a package source or a stored row. The other categories are closed on facts: the package publishes (not unpublished); no ADR-0087 id is named or touched (not registered or already-registered); and nothing exported is removed or narrowed (not runtime-interface-only, type-surface-only or runtime-export-retired). -->
16+
17+
**BREAKING** for a caller that writes a file id it may not read, shipped as `patch` because the metadata protocol does not move.
18+
19+
- **Before.** The write saved, and the field kept an id its readers could not use.
20+
- **Now.** The write is refused with `400 VALIDATION_FAILED`, naming the field with `invalid_value`.
21+
- **Fix.** Name a file the caller may read (upload it, or choose one they can open), or leave the field empty.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
'@objectstack/cli': patch
3+
---
4+
5+
fix(cli): `os dev` compiles the config before it boots, so the served connector `baseUrl` is the one dispatch calls
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
- **Before.** `os dev` booted whatever `dist/objectstack.json` already held, and compiled only when it was missing or `--compile` was passed. A host config's dev boot serves the stack from two copies: the config module the server evaluates (what a provider-bound connector is materialized from, so what dispatch calls) and the artifact (what `/api/v1/meta/<type>` serves). A config value read from the environment, such as `examples/app-showcase`'s self URL built from `OS_PORT`, then differed between them. With `OS_PORT=41302` over an artifact built without it, `GET /api/v1/meta/connector/showcase_status_api` served `http://127.0.0.1:3000` while the connector called port 41302.
11+
- **Now.** `--compile` is on by default: `os dev` compiles `objectstack.config.ts` into the artifact it serves, in its own environment, before the server starts, so both copies come from one evaluation. The watch loop already recompiled in that environment.
12+
- **`--no-compile`** boots the existing artifact as built, as a bare `os dev` did before (it is still compiled when missing, and the startup warning still says when it is older than the sources). A pinned artifact (`--artifact`, `OS_ARTIFACT_URL`, a remote `OS_ARTIFACT_PATH`) is never rebuilt. With no `objectstack.config.ts`, an existing artifact boots as before.
13+
- `os serve` and `os start` are unchanged.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'@objectstack/runtime': patch
3+
---
4+
5+
fix: a sandboxed hook's or action's business refusal is logged once, at info, without a stack
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
When a sandboxed hook or action body refuses on purpose, for example a state-machine hook answered as `422 INVALID_STATE`, the server log used to get two `ERROR [BodyRunner]` lines with full stacks: one at the hook and one again at the action that triggered it. An operator watching for errors could not tell a refused click from a crashed hook.
11+
12+
Now a throw that the HTTP doors answer in the 4xx band writes one `info` line, `[BodyRunner] sandboxed hook refused` (or `action refused`), naming the hook or action, its declared `code` and the status, with no stack. The action does not repeat a refusal its hook already logged. A throw that becomes a 5xx (a crash, a denied capability, a declared 5xx) still logs `error` with its stack, at both the hook and the action as before.
13+
14+
The refusal-or-fault split is read from the same error table the HTTP doors answer with (`mapDataError`), so the log level follows the status the caller saw. The HTTP status, body and message of every refusal are unchanged, and every throw is still rethrown.

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,7 @@ PM 的工作是循环:选卡 → 认领 → 派发 → 收集 → 复核 → 报
137137
- 一个标签存在当且仅当有具名读者;`pm:blocking` 即其推导缓存,读者以词表脚本注为权威。
138138
- 跨仓解锁判据是消费方可安装,⛔ 不是上游已合并;探针:装上的包是否接受新键/新行为。
139139
- 以发布包消费上游的仓,解锁放回前先验修复已进可安装的发行版。
140-
- 未发版 ⇒ 转 `pm:on-hold` + `Restart-when:` 加消费方安装面判据,⛔ 不回 `pm:queue`。
141-
- 该 hold 评论预写唤醒后的派发形状;pin 消费的仓不适用:pin 移动即可安装,走 pin 滞后读数。
140+
- 未发版 ⇒ 转 `pm:on-hold`;`Restart-when:` 写安装面判据与唤醒后派发形状,⛔ 不回 `pm:queue`。
142141
- 状态变更不过夜:标签无评论、assignee 无认领评论、结论只在 chat,都是半状态。
143142
- 半状态在结束会话(含限流悬挂)前补齐成对或回滚半边;report-only 巡查见守卫索引。
144143
- 等待他座位也是状态,写在卡上才存在(等谁、自何时),⛔ 不留在会话记忆。
@@ -162,8 +161,9 @@ PM 的工作是循环:选卡 → 认领 → 派发 → 收集 → 复核 → 报
162161
| `domain:devx` | `sdui-parser`、`packages/qa`、`content/docs`、`apps/docs`、`docs/qa`、`.githooks`、`docker/README.md`、`examples/**` 仅测试基建面;`packages/lint`、`scripts`(门禁类)、`.github/workflows`(接线)三者按锚定规则归 `domain:spec`,门禁按 SUBJECT:governed 面归 skills,代码/文档质量归本域 |
163162
| `domain:skills` | governed 面全量(含本文件;统一定义见「governed 面统一定义」行);非门禁的 `scripts/pm/**`(PM 循环工具);governed 面的治理执行文件:`.github/CODEOWNERS` + SUBJECT 是 governed 面本身的门禁/审计(现为 `scripts/pm/check-governed-merges.mjs`) |
164163
| `domain:spec` | `packages/spec` 整包:schema 形状、`contracts/**`、退役行为半边、strictness 台账;describe/JSDoc/墓碑散文/错误 guidance 与 alias 表;`packages/spec/scripts/**`、`packages/spec/docs/**` 及按锚定规则的例外归本域的工具链(域边界枚举与席内分派见 `references/lanes/spec.md`) |
164+
| `domain:ui` | `ui/**` 全部代码:`@object-ui/*` 库与 `ui/apps/*`、`ui/packages/types` 与 schema 语料、`ui/scripts` 与门禁工具、文档随所记录的面走;`packages/console`(dist 由 `pnpm console:build` 生成 ⛔ 不手改);协议恒归 `domain:spec`,受管面归 `domain:skills`;详见 `references/lanes/ui.md` |
165165
| `domain:cloud` | 云服务旅程,停放(加 `status:parked`,sweep 免扫)待 cloud PM 迁移,本仓不派 |
166-
| (无固定归属,按落点分诊) | `packages/apps/*`、`packages/console`(dist 由脚本生成 ⛔ 不手改;UI 缺陷走 `repo:objectui`)、`examples/*`(归它演练的子系统)、`docs/audits/**` |
166+
| (无固定归属,按落点分诊) | `packages/apps/*`、`examples/*`(归它演练的子系统)、`docs/audits/**` |
167167

168168
- 表未覆盖的包首次分诊时归类并走 PR 更新本表;新增或退役 `domain:*` 必须同批改本表。
169169
- 退役 `domain:*` 只退流通,对象保留;`domain:engine`/`domain:services`/`domain:cli` 读作 `domain:server`。

‎.claude/skills/pm-dispatch/references/cross-repo-coordination.md‎

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,31 @@
22

33
分诊席每次 fire 路由与查重前读本文;执行席跨仓认领、派发或转移时读本文。
44

5-
- 产品依赖方向固定:`objectstack`(后端,`packages/spec` 是唯一契约)→ `objectui`(前端)与 `cloud`。
6-
- 多车道仓 `objectstack`、`objectui`:中央分诊是 `domain:*`/type/定级的唯一生产者。
5+
- 产品依赖方向固定:`objectstack`(后端与 `ui/` 前端,`packages/spec` 唯一契约)→ `cloud`、`hotcrm`。
6+
- 多车道仓 `objectstack`:中央分诊是 `domain:*`/type/定级的唯一生产者。
77
- 单车道仓 `cloud`、`objectos`、`hotcrm`、`www.objectos.ai`;未来新仓默认此类。
88
- 单车道仓 `repo:*` 席自理机械三务,⛔ 不产 `domain:*`;决策卡入本仓收件箱,不落 objectstack。
99
- 机械三务 = 自扫 sweep、自打 `type`、自做首触(含 `finding`;业务理由归召唤的总监席)。
1010
- 新仓登记是一张清单:座位贴、标签、类别归属、门禁盘点、写身份锁移植(deny + hooks)。
1111
- 新仓三件:加入 `GOVERNED_REPOS`、安装车队 App、写 `lanes/<repo>.md` 宪章;应用仓无分诊席。
1212
- 新仓准入只问一句:真需要常设席位吗;hotcrm 宪章见 `references/lanes/hotcrm.md`。
13-
- objectui 卡按修复落点分两条车道:`domain:ui`(全部代码)与 `domain:skills`(仓根发布 `skills/**`)。
14-
- objectui 的 `domain:spec`、`domain:devx`、`domain:tooling` 已退役:⛔ 不贴、不认领、不再创建。
15-
- 协议恒归 objectstack `domain:spec`;跨层功能照规则 2,objectui 子单归 `domain:ui`。
13+
- objectui 仓已并入本仓 `ui/`:UI 卡住本仓,`ui/**` 代码归 `domain:ui`,受管面归 `domain:skills`。
14+
- 协议恒归 `domain:spec`;跨层功能照规则 2,`ui/` 子单归 `domain:ui`。
1615
- 车道可分席(席号 `· seat N`,或域×仓):忙时一车道开多席,闲时收归一席;每席独立座位贴。
1716
- 认领评论带 `Seat: domain:X#N` 行申报席号(缺省席 1);同席认领互斥,他席认领不触发自退。
1817
- 拆分触发预登记:单轮逼近 fire 周期,或某仓在全序下持续断粮 ⇒ 按仓拆回多席。
1918
- seam 卡单一归属:归修复落地仓的席,双仓皆动 objectstack 侧主导。
2019
- `scripts/pm/**` 等住 objectstack 的全板工具链单写手恒为 objectstack 侧席,他侧上游立卡回链。
2120
- 工装只答被告知的仓,缺 `--repo`/`PM_SWEEP_REPO` 即答 objectstack;⛔ 不答手上那张卡的仓。
2221
- 规则 1:issue 住在修复落地的仓,分诊时按判据严格执行。
23-
- 判据:正文抽掉 objectstack 还成立 ⇒ 当场转仓(console/UI 缺陷即转 objectui);不成立才是缝卡。
22+
- 判据:正文抽掉 objectstack 还成立 ⇒ 当场转仓(UI 缺陷不转,归 `domain:ui`);不成立才是缝卡。
2423
- 转仓走 `issue-transfer.mjs`;名册外或平台拒才重建:出处头 + 裸 `#N` 改全名 + 关源单为 moved。
2524
- 缝卡收窄为真协调卡:留 objectstack 带 `repo:*`,正文点名读者(哪个座位、哪一步)。
2625
- 决策卡留在立卡仓,总监席扫全部仓 `needs-user-decision` 呈批;在飞卡 ⛔ 不中途转仓。
27-
- 规则 2:跨层功能点或 objectui 消费的 `Seam:` 卡恒由分诊立父单 + 逐层子单,spec/后端先行。
26+
- 规则 2:跨层功能点或 `ui/` 消费的 `Seam:` 卡恒由分诊立父单 + 逐层子单,spec/后端先行。
2827
- `main` 有 `ui/` 起,新 spec 键不再先行、取代上句次序:与首个读者同 PR,hotcrm 读者同合并批。
2928
- 下游带 `Blocked-by: <owner/repo>#<n>`;`Blocked-by` 未关闭/未合并的不派发,对 GitHub 现验。
3029
- 被链接或同父的两单永不同批;`ui/` 起的新键与首个读者除外,⛔ `planned` 行不单独进 main。
31-
- pin 滞后是盲区:本仓 pin 是否已覆盖该 commit 是第二读数,派发前用 REST `compare` 核祖先。
32-
- 本地 `merge-base` 在浅检出上给假非祖先;未覆盖 ⇒ 派发令要求 PR 正文留档分叉窗口。
33-
- ⛔ pin bump 不做 rider:走专用 bump 脚本连带 override 与 lockfile。
3430
- 规则 3:联动杂事立单,不靠记忆。
3531
- 已验收 PR 的产物流向另一仓,接受该 PR 的执行席立即在消费仓立后续单,带 `Blocked-by:`。
3632
- 规则 4:纵向拆分,一个分诊 PM + N 个执行 PM;一人恰一车道,一车道可坐多席。
Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,23 @@
11
# 车道岗位说明:domain:ui
22

3-
见 cross-repo-coordination.md;本文是本席的岗位说明,现值状态恒在座位贴,⛔ 不迁入本文件。
3+
范围见 SKILL.md 域车道表 `domain:ui` 行;本文是岗位说明,现值状态恒在座位贴,⛔ 不迁入。
44

55
## 形态
66

7-
- 本席为姊妹仓执行座位:在 objectui 仓认领、派发、复核、落地。
7+
- 本席是本仓 `ui/` 树的执行座位:在 objectstack 认领、派发、复核、落地。
88
- ⛔ 不产 `domain:*`、type 与定级,中央分诊唯一生产;误标 ⛔ 不自改,挂 `pm:retriage`+异议同笔。
9-
- 维护者裁决例外于上条与 SKILL.md 无标签禁认领:main/队列 `Spec Main Shape Gate` 红即止血认领。
10-
11-
## 范围
12-
13-
- objectui 全部代码归本席:发布库 `@object-ui/*` 与 `apps/*`;`packages/types`、schema 语料与 spec pin;
14-
- `.github/`、`scripts/`、构建与发布管线、门禁工具;docs 随所记录的面走。
15-
- 协议恒归 objectstack `domain:spec`,新 `packages/spec` 工作同此;跨层功能的 objectui 子单归本席。
9+
- 协议恒归 `domain:spec`,新 `packages/spec` 工作同此;跨层功能的 `ui/` 子单归本席。
1610
- 落点不明留分诊首触,⛔ 不猜、不代判。
1711

1812
## 常设承诺
1913

20-
- 构建产物经 `pnpm objectui:refresh` 回流,见 AGENTS.md。
21-
- 回流后的 console pin bump 是 objectstack 的单张卡,⛔ 永不做本仓 PR 的 rider。
22-
- `scripts/pm/**` 单写手恒在 objectstack 侧 ⇒ 本席的工具需求走上游卡带 `Blocked-by:` 回链。
23-
- 本仓标签/assignee 写走 objectstack `label-write.mjs --repo`,⛔ 不 MCP `issue_write`;被拒转有通道席。
24-
- 受管面五项含仓根发布 `skills/**`,清单、判据与禁令见 objectui AGENTS.md §9 受管面段。
25-
- ⇒ 命中即停 draft;⛔ 未获授权批准不 ready 不入队不自合、永不批准,获批后认领席落地。
26-
- 未命中的 PR 按同节走合并队列落地;changeset 与版本纪律见 objectui AGENTS.md §9。
27-
- 半状态巡查在本仓有载体:定时 workflow 与 sweeper 皆在,自 objectstack 移植。
28-
- 锚 issue 未配置 ⇒ 写锚步骤 skip,发现只落 run summary,绿不等于已交付读锚腿。
29-
- ⇒ 手工半边照 `hotcrm.md` 那条办,并每轮实测锚是否已配,⛔ 不沿用本行读数。
14+
- `ui/` 的规则在 `ui/AGENTS.md`,只补充根 `AGENTS.md`;受管面、合并队列与 changeset 一律按根文件。
15+
- `scripts/pm/**` 不归本席 ⇒ 工具需求立卡给归属车道并带 `Blocked-by:` 回链,⛔ 不顺手改。
3016

3117
## 席内判断
3218

3319
- 卡正文是快照不是状态:⛔ 不凭正文单读裁决或派发,读全线程并对 `origin/main` 重测。
34-
- `pm:queue` 在本仓兼指已分诊与可派发 ⇒ 逐张重验可派性,⛔ 不按标签直派。
35-
- 零读数恒配点亮的正控,且正控须经被测机制本身生效;本仓 search 假零,查重只引命中。
20+
- 零读数恒配点亮的正控,且正控须经被测机制本身生效;查重只引命中。
3621
- 脚本能滚动不证明用户能滚动:可达性断言必须驱动真实输入管道。
3722
- 落地验证按内容,正控须在合并前证明会响,⛔ 不按 sha 判已落地。
3823
- 契约三面复核本席适用:按面欠达档记录落 PR 或卡,散文面归 ACCEPT,见 `../contract-review.md`。

‎.github/workflows/ci.yml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,8 @@ jobs:
277277
- 'package.json'
278278
- 'pnpm-lock.yaml'
279279
- 'tsconfig.json'
280+
- 'tsconfig.base.json'
281+
- 'tsconfig.test.base.json'
280282
- '.github/workflows/ci.yml'
281283
# BUILD INPUTS outside every package (#21202): turbo.json itself,
282284
# its `globalDependencies`, and each `$TURBO_ROOT$/…` input of a

‎.github/workflows/lint.yml‎

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -984,29 +984,6 @@ jobs:
984984
continue-on-error: true
985985
run: pnpm check:widget-option-census
986986

987-
# The sdui-parser LOCKSTEP invariant (#12719 / #13150). Two copies of the
988-
# constrained-JSX parser exist: objectui's runs in the RENDERER, this one
989-
# runs in the SAVE GATE, and #12719 states that they must byte-agree on
990-
# the accepted grammar and on the diagnostic codes — an invariant that had
991-
# been enforced by NOTHING through three manual ports, two of which landed
992-
# only because someone happened to file a card naming the drift.
993-
#
994-
# Compared against a VENDORED RECORD of objectui's side, so this step
995-
# needs no network, no build and no objectui checkout. Read the gate's
996-
# header for what that buys and what it costs: it sees this copy moving
997-
# away from the recorded objectui state; it does NOT see objectui moving
998-
# afterwards, and only a `.objectui-sha` bump — the moment a divergence
999-
# starts SHIPPING — brings that direction back into view.
1000-
#
1001-
# Unfiltered on purpose. The record lives under packages/sdui-parser/, the
1002-
# pin is a root dotfile, and this workflow declares no paths: filtering it
1003-
# to the parser subtree would go dormant on exactly the PR that moves the
1004-
# pin. Cost is one TypeScript parse of seven small files, ~0.4 s.
1005-
- name: sdui-parser stays in lockstep with objectui's copy
1006-
id: sdui-parser-stays-in-lockstep-with-objectui
1007-
continue-on-error: true
1008-
run: pnpm check:sdui-lockstep
1009-
1010987
# The checked-in SDUI public-tier manifest (#12924, ruled 2026-08-29: wire
1011988
# it). `resolveSduiManifest()` degrades to parse-only SILENTLY when the
1012989
# repo-root `sdui.manifest.json` is absent or unreadable, so nothing in

0 commit comments

Comments
 (0)