Skip to content

Commit ba42fbd

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/project-thread-sfz9hi
2 parents 40aace6 + 0ea90c7 commit ba42fbd

68 files changed

Lines changed: 1768 additions & 2074 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: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
fix(spec): `os lint` tells an author that a field's `externalId: true` does nothing
6+
7+
Clause-②: no (a liveness-ledger re-grade: no schema, accept set, served body or error code moves; only the advisory `liveness-dead-property` warning on an authored `externalId: true` starts, and the flag's description is reworded)
8+
Metadata-protocol: none
9+
10+
The shipped liveness ledger (`liveness/field.json`) graded a field's `externalId` flag `live` on the strength of one renderer module that was deleted for having no importers. Re-measured across `packages/**` and `ui/**`, nothing reads the flag: the Studio field inspector edits it and a metadata summary copies it, and no upsert matches on it. The row is now `dead`, so `os lint` warns on a field that sets `externalId: true` (the default `false` is not warned). The flag's schema description, and the reference pages generated from it, no longer say it drives upserts.
11+
12+
- **What to do.** Remove the flag. To match seed records on a field, name the field in the seed dataset's `externalId` (`name` when it is left out). For an import, name it in the mapping's `upsertKey`.
13+
- **Unchanged.** Parsing (the key is still accepted, default `false`), seed loading, imports and every upsert.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
'@objectstack/spec': minor
3+
'@objectstack/platform-objects': minor
4+
'@objectstack/service-messaging': minor
5+
---
6+
7+
feat(spec)!: a single-word language name (`Chinese`, `English`, `Japanese`) is refused as a locale
8+
9+
The 18.x locale shape refused `zh_CN`, a padded value and a multi-word name such as `Chinese (Simplified)`, but its 2–8 letter language subtag still admitted any single word of that length. An author who wrote `locale: 'Chinese'` got a green save, and the strings were stored under a locale no session ever asks for, so they never rendered. The refusal text already said a language name is refused.
10+
11+
The language subtag is now 2–3 letters, the ISO 639 codes every tag in use starts with (BCP-47 reserves 4 letters and registers no 5–8 letter language). Everything else in the shape is unchanged: hyphen-separated 1–8 letter-or-digit subtags (`en`, `zh-CN`, `zh-Hans-CN`, `es-419`, `yue-HK`), and case is not judged. The one shape moves in all three places that hold it:
12+
13+
- `LocaleSchema` (`@objectstack/spec/system`): a `translation` item's `locale`, translation bundle keys, `i18n.defaultLocale` / `fallbackLocale` / `supportedLocales`, a seed dataset's `locale` scope and the seed loader's `locale`.
14+
- `SYS_USER_LOCALE_TAG_PATTERN` (`@objectstack/platform-objects/identity`): the `locale_bcp47_shape` rule on a user's own `sys_user.locale` column.
15+
- `LOCALE_TAG_SHAPE` (`@objectstack/service-messaging`): the read side that picks a recipient's notification locale, byte-identical to the column rule.
16+
17+
Clause-②: no (narrowing) — the locale shape refuses values it used to accept. Nothing is widened.
18+
19+
Metadata-protocol: narrowed (a locale whose language subtag is 4–8 letters, in practice a language name, is refused on translation items, bundle keys, the i18n configuration and seed datasets)
20+
21+
<!-- adr-0087: not-required (already-registered translation-locale-non-tag-refused) the 18.x D3 entry already prescribes rewriting a language name by hand, and its conversion already lists such a stored row as a TODO through LocaleSchema; this diff narrows the same shape and corrects that entry's subtag length -->
22+
23+
**BREAKING** for an author or user who wrote a language name as a locale.
24+
25+
- **Before.** `defineTranslation({ name: 'qa_x', locale: 'Chinese', … })` parsed and its strings never rendered; `PATCH` of a user's `locale` to `Japanese` was stored, and that user's notifications fell to the delivery ladder's floor.
26+
- **Now.** The value is refused at `locale` (or at the bundle key) with a message naming the tag shape, and the user's write is refused by the column's `locale_bcp47_shape` rule.
27+
- **Fix.** Write the tag, not the name: `Chinese` → `zh-CN`, `English` → `en`, `Japanese` → `ja-JP`. A language name has no single tag it can have meant, so `os migrate meta --stored` lists a stored translation row as a TODO (D3 entry `translation-locale-non-tag-refused`) and you rewrite it by hand. A user whose stored `locale` is already a name such as `Japanese` keeps loading: their notifications fall to the deployment default, and saving that value again is refused until it is replaced with a tag.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
'@objectstack/cli': patch
3+
---
4+
5+
fix(cli): `os dev`'s in-place organization-ownership upgrade names what stopped it, and the exact command that carries on
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
When the organization-ownership boot guard refuses a development database, `os dev` copies it and runs the ceremony on it in place. When that upgrade did not finish, it could exit with 「The upgrade did not run to a report」 and no cause.
11+
12+
What changes:
13+
14+
- The ceremony's own refusal is printed in its own words, also when the terminal forces colour (`FORCE_COLOR`): the refusal lines are read with colour stripped, and the `✗` glyph is no longer printed twice.
15+
- An apply that stopped with no refusal of its own (an uncaught error, a crash, a signal) says how it ended and prints the end of its log, without the kernel's boot narration.
16+
- A report file still holding the apply's reservation text, or one that does not parse, is read as "no report", with the cause, never as "a blocker (above)" with nothing above. A blocked report that names no blocker prints the log's cause too.
17+
- A stale `organization_id` the plan gives the column-drop fate is upgraded as before, but no longer silently: before the apply runs, `os dev` prints per table how many stamped values the drop discards, and names the copy that keeps them.
18+
- Each failed upgrade ends on the exact command that carries on: `os migrate organization-ownership --dev --database-url URL --apply --plan-file PLAN --backup-taken`, with the database and the plan the upgrade wrote.
19+
20+
The copy taken before the upgrade is kept, as before. A development database with a stale `organization_id` column on `sys_flow_credential` and `sys_secret` (objects that no longer declare it) is upgraded and starts under `os dev`, now pinned by a test. The production ceremony is unchanged.
21+
22+
What to do: nothing.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
'@objectstack/metadata-protocol': patch
3+
---
4+
5+
fix(metadata-protocol): on PostgreSQL, the NULL-safe row identity of `sys_setting` and `sys_notification_preference` now lands, and a tightening that did not land is reported instead of answered `created`
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
On PostgreSQL the declared unique both tables start with is a UNIQUE CONSTRAINT (the driver builds it with knex `table.unique()`), not a bare index. The boot migration that replaces it with the NULL-safe key ran `DROP INDEX` on the constraint's index, which PostgreSQL refuses (2BP01, printed on every boot), and then `CREATE UNIQUE INDEX IF NOT EXISTS` under the name the constraint still held, which succeeds as a no-op. Both migrations reported `created` while the plain, NULL-distinct key stayed: two tenant-scope or global-scope settings rows for one (organization, namespace, key) could coexist.
11+
12+
- **The drop.** On PostgreSQL the replace step now frees the name the way the driver's own `dropIndexIfExists` does: `ALTER TABLE … DROP CONSTRAINT IF EXISTS`, then `DROP INDEX IF EXISTS`. No `CASCADE`.
13+
- **The verdict.** On PostgreSQL `created` is answered only after the definition under the real name reads back (`pg_get_indexdef`) equal to the one the probe built. When it does not (something depends on the old constraint and the drop is refused), the boot prints the existing `[metadata-protocol] could not rebuild …` line naming what is not enforced, with the refusal, and keeps running.
14+
- **Existing duplicates.** When rows already break the NULL-safe key, nothing is deleted or merged and the old key stays, as before. The `cannot tighten …` line now also says how many keys are duplicated and how many rows hold them, on every dialect. It prints no row values.
15+
- SQLite and MySQL run the same probe, drop and create statements as before, with the same outcomes. The only new statement there is the count, and it runs only on a conflict.

‎.claude/agents/os-dev.md‎

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ model: opus
7070
- 优先扩展一个守卫关掉整个类;任一条不成立 ⇒ 回默认:报告列出交席位立单、不碰。
7171
- 本轮改动令其变假或触碰的已发布缺陷必修;其余立卡并记明已发布面,PR 照常落地。
7272
4. ⛔ 永不推 `main`、合并任何 PR、在代码 PR 里改 `content/docs/releases/`;改错另开 docs-only PR。
73-
- force-push 按落地仓 AGENTS.md:objectui/cloud 绝对禁;objectstack §3 五条全立才 `--force-with-lease`。
73+
- force-push 按落地仓 AGENTS.md:cloud 绝对禁;objectstack §3 五条全立才 `--force-with-lease`。
7474
5. **Contract-first。** 修复若诱使你在消费端加宽容回退(`??` 别名、宽松解析),缺陷在上游。
7575
- 去生产者或 spec 修,或返回 `needs_decision`。
7676
6. **issue 正文是线索,不是规格。** 动手前对 `origin/main` 核验其前提。
@@ -122,7 +122,7 @@ model: opus
122122
- 从门禁绿跑清单里挑一个已 pin 的 fake 照抄。
123123
- 缺一种该文件没有的 double 时,覆写文件里已有的 double 优于 pin 新的:不动台账。
124124
- 匹配到零个脚本的 `pnpm --filter` 运行以 0 退出:什么都没跑,读上去却是通过。
125-
- objectui 把 typecheck 拼作 `type-check`(连字符);核对输出里确实回显了脚本名。
125+
- `ui/` 的包把 typecheck 拼作 `type-check`(连字符);核对输出里确实回显了脚本名。
126126
- 浏览器验证:Chromium 已预装,`PLAYWRIGHT_BROWSERS_PATH` 指向 `/opt/pw-browsers`。
127127
- launch 传 `executablePath: '/opt/pw-browsers/chromium'`;⛔ 永不跑 `playwright install`。
128128

@@ -154,7 +154,7 @@ model: opus
154154
- 派生答哪些门禁读你改的文件,不答哪些测试测这个脚本。
155155
- 跑同目录点名它的 `*.test.ts`,加同包 tests 下 `git grep` 该脚本文件名的全部命中。
156156
- 该脚本只住在 objectstack,答案只关于它所在的树:每次推导第一行(stderr)点名仓与 commit。
157-
- 读之前先核对;姊妹仓未必有 `scripts/pm/dispatch-gates.mjs`:objectui 只有 `check-half-states.mjs`。
157+
- 读之前先核对;姊妹仓未必有 `scripts/pm/dispatch-gates.mjs`:hotcrm 就没有。
158158
- 把它们的路径喂进 objectstack 检出,得到的是 objectstack 的门禁族:形态完整、退出 0、全错。
159159
- 那边的清单从该仓自己的 `package.json` 与 `.github/workflows/` 手工推导。
160160
- 加 `--repo <owner>/<name>` 申报本次答案该属于哪个仓,不匹配即拒绝并同时点名两个仓。
@@ -306,8 +306,6 @@ model: opus
306306
- 关此步骤的是读回不是写入;读回只验写落了,验不出有门在读:幻影门标签读回照样成功。
307307
- 被拒 ⇒ 报端点与状态码、席位代挂,⛔ 不报 `blocked`、不走 MCP;收尾再读一次。
308308
- 分类器拒外部写 ⇒ 停手,`deviations` 记命令与拒因,席位只转维护者定夺;⛔ 不换路重发。
309-
- objectui:路径标签归 `labeler.yml`,逐 push 同步;无门禁读你打的标签 ⇒ 派发词未点名即零写。
310-
- objectui 的 `skip-changeset` 零读者;空 frontmatter changeset 即声明,门禁判定行为准,⛔ 永不施加。
311309
- 报告在本地验证走完时交付,CI 收敛等待归 PM 不归你:⛔ 不为等 CI 结论推迟报告。
312310
- 门禁状态如实记录,`in_progress` 是诚实值;PR 开出后 ⛔ 永不 sleep、定时等待或空转轮询 CI。
313311
- 你报告之后才转红的门禁,会作为同一认领上的补丁轮回来。

‎.claude/settings.json‎

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
"Bash(agent-browser snapshot *)",
3030
"Bash(agent-browser skills *)",
3131
"Bash(curl -sS -X POST https://api.github.com/repos/objectstack-ai/objectstack/issues/*/labels *)",
32-
"Bash(curl -sS -X POST https://api.github.com/repos/objectstack-ai/objectui/issues/*/labels *)",
3332
"Bash(curl -sS -X POST https://api.github.com/repos/objectstack-ai/hotcrm/issues/*/labels *)",
3433
"Bash(curl -sS -X GET https://api.github.com/rate_limit *)",
3534
"Bash(curl -sS -X GET https://api.github.com/repos/objectstack-ai/objectstack/issues?* *)",
@@ -57,13 +56,10 @@
5756
"Bash(curl -sS -X GET https://api.github.com/repos/objectstack-ai/objectui/commits/*/check-runs* *)",
5857
"Bash(curl -sS -X GET https://api.github.com/repos/objectstack-ai/hotcrm/commits/*/check-runs* *)",
5958
"Bash(curl -sS -X PATCH https://api.github.com/repos/objectstack-ai/objectstack/pulls/* *)",
60-
"Bash(curl -sS -X PATCH https://api.github.com/repos/objectstack-ai/objectui/pulls/* *)",
6159
"Bash(curl -sS -X PATCH https://api.github.com/repos/objectstack-ai/hotcrm/pulls/* *)",
6260
"Bash(curl -sS -X POST https://api.github.com/repos/objectstack-ai/objectstack/pulls/*/ccr/ready_for_review *)",
6361
"Bash(curl -sS -X PUT https://api.github.com/repos/objectstack-ai/objectstack/pulls/*/ccr/auto_merge *)",
64-
"Bash(curl -sS -X POST https://api.github.com/repos/objectstack-ai/objectui/pulls/*/ccr/ready_for_review *)",
6562
"Bash(curl -sS -X POST https://api.github.com/repos/objectstack-ai/hotcrm/pulls/*/ccr/ready_for_review *)",
66-
"Bash(curl -sS -X PUT https://api.github.com/repos/objectstack-ai/objectui/pulls/*/ccr/auto_merge *)",
6763
"Bash(curl -sS -X PUT https://api.github.com/repos/objectstack-ai/hotcrm/pulls/*/ccr/auto_merge *)",
6864
"Bash(node --use-env-proxy scripts/pm/post-stamped.mjs *)",
6965
"Bash(node --use-env-proxy scripts/pm/label-write.mjs *)",
@@ -78,7 +74,6 @@
7874
"Bash(node scripts/pm/issue-transfer.mjs *)",
7975
"Bash(node --use-env-proxy scripts/pm/issue-transfer.mjs *)",
8076
"Bash(curl -sS -X PATCH https://api.github.com/repos/objectstack-ai/objectstack/issues/* *)",
81-
"Bash(curl -sS -X PATCH https://api.github.com/repos/objectstack-ai/objectui/issues/* *)",
8277
"Bash(curl -sS -X PATCH https://api.github.com/repos/objectstack-ai/hotcrm/issues/* *)"
8378
],
8479
"deny": [

‎.claude/skills/checklist-test/SKILL.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ GitHub 写入只走 pm-dispatch 写入门:仓根直调 `scripts/pm/issue-create.
137137
是协议载体、不入分诊 sweep,⛔ 不是可派发单元;工作标签随抽取出的缺陷卡走(RUNNER 抽取
138138
义务条款)。**这条在记录的整个生命周期成立** —— 收口与关闭时同样不补挂。
139139
- **正文**,按此顺序:
140-
- **环境指纹** —— framework sha、`.objectui-sha`、端口、db、seed、时间戳。
140+
- **环境指纹** —— framework sha(`ui/` 在内)、端口、db、seed、时间戳。
141141
- **范围** —— 选择器 + 每项所对的 `revision`。
142142
- **逐条款判定表** —— 项 · 条款 · 判定 · 一行**文本** oracle 证据
143143
(API/网络/构建/测试结果 —— 服务端真相,永不是像素)。

‎.claude/skills/dogfood-verification/SKILL.md‎

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -73,24 +73,17 @@ dev 工作树、dev-server 端口、preview 浏览器全是**共享的**:并行
7373
- [ ] ⚠️ **消融的 mutate 腿与 restore 腿都要重建 —— 但那条规则不是 dogfood 专属**:
7474
判据、preflight 与两次实测住在 `.claude/agents/os-dev.md`;⛔ 这里不留第二份拷贝。
7575
- [ ] `dist/` 已 gitignore —— 安全;永不提交构建产物。
76-
- [ ] **`/_console` UI 是 *vendored objectui 构建*,与框架 `dist` 是两回事。** 它由
77-
`.objectui-sha` 钉住、按预构建 bundle 提供。已合并的 objectui 修复 —— *甚至
78-
bump 过的 `.objectui-sha`* —— 在 vendored console 重建之前**不会活在 :3000
79-
上**:运行中的服务器继续提供旧构建产物(BUILD 陈旧 ≠ PIN 陈旧)。所以**在宣布
80-
:3000/_console 上发现 console/Studio UI 缺陷之前,先对当前 objectui 源码或全新
81-
构建核一遍** —— vendored bundle 可能是旧的,缺陷可能上游已修。最快的权威核验 =
82-
objectui 的 HMR console 指向你的服务器:
76+
- [ ] **`/_console` UI 是 *Console 构建*,与框架包的 `dist` 是两回事。** 它由
77+
仓库根目录的 `pnpm console:build` 从 `ui/apps/console` 构建,拷进 `packages/console/dist`。
78+
`ui/` 下已合并的修复在 Console 重建之前**不会活在 :3000 上**:运行中的服务器继续
79+
提供旧构建产物。所以**在宣布 :3000/_console 上发现 console/Studio UI 缺陷之前,先
80+
对当前 `ui/` 源码或全新构建核一遍** —— 构建可能是旧的,缺陷可能已修。最快的权威
81+
核验 = `ui/` 的 HMR console 指向你的服务器:
8382
`VITE_SERVER_URL=http://localhost:<port> DEV_PROXY_TARGET=http://localhost:<port> pnpm --filter @object-ui/console dev`
8483
(独立 origin → 需要它自己的 `admin@objectos.ai/admin123` 登录)。跳过这步曾在
85-
一个 objectui main 已修掉的 action-create 死路上白付一整圈 spawn-a-fix。
86-
- [ ] **漂移的构建不再静默启动(#7752)。** `build-console.sh` 把它构建所用的 SHA 盖
87-
进 `packages/console/dist/.objectui-sha`;当这个章与仓库的 `.objectui-sha` pin
88-
不一致时,`os dev` **拒绝挂载 `/_console`** 并打印补救 —— `/_console/` 404,横
89-
幅不再宣传 console URL。那是要修的前置条件,不是产品缺陷:跑
90-
**`pnpm console:build`**(在仓库根目录跑,从本仓 `ui/` 下的 Console 源码重建;
91-
改 Console 就改 `ui/` 下的源码)再重启。`pnpm check:console-sha`
92-
不启动也能报同一比较。`OS_ALLOW_CONSOLE_DRIFT=1` 是故意启动陈旧 bundle —— 那之
93-
后你做的每个 console 观察,描述的都是一个仓库没有钉住的 commit。
84+
一个上游已修掉的 action-create 死路上白付一整圈 spawn-a-fix。
85+
- [ ] **构建新鲜度由你自核。** `pnpm console:build` 产出的 dist 不盖 SHA 章,`os dev`
86+
无从判定它是否陈旧,照常挂载 `/_console`;改过 `ui/` 就先在仓库根目录重建再观察。
9487

9588
## 3. 核验 —— 视觉 / API 先行,DOM 殿后(防误报规则)
9689

@@ -104,8 +97,7 @@ dev 工作树、dev-server 端口、preview 浏览器全是**共享的**:并行
10497
- [ ] **门的两侧都要测**:`requiresService`/`requiresObject`/权限门要在依赖存在*与*
10598
缺席两种状态下各验一次。
10699
- [ ] 服务端是权威可见性门(ADR-0124 D1)—— 客户端过滤只是「礼貌」。元数据开关不改
107-
UI 时,先查强制在服务端(框架,可在本仓修)还是客户端(objectui console,另一
108-
个仓)。
100+
UI 时,先查强制在服务端(框架)还是客户端(`ui/` 的 console);两边都在本仓修。
109101
- [ ] 用 `preview_screenshot`(API 改动用 `preview_network`)向用户证明;loading
110102
spinner / 异步数据状态要显式写明,而不是声称你没等到的最终值。
111103

‎.claude/skills/pm-dispatch/references/decision-analysis.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343

4444
## 应用仓推荐序特例
4545

46-
- 能力先建在平台(spec、objectstack 运行时、objectui 渲染器);应用仓(cloud、hotcrm)不自建替代。
46+
- 能力先建在平台(spec、objectstack 运行时与其 `ui/` 渲染器);应用仓(cloud、hotcrm)不自建替代。
4747
- 阻塞源是平台缺陷 ⇒ 恒荐等待;⛔ 不荐绕行(形状容错、复刻平台规则)、不荐劈半落地。
4848
- 过渡唯维护者明裁;应用仓临时扩展平台契约,仅当平台卡与移除卡同一裁决立出。
4949
- 应用仓裁决立出的平台卡取被裁卡优先级,⛔ 不降档。推论出处:cloud 2932 A、cloud 2924 A。

0 commit comments

Comments
 (0)