Skip to content

Commit 07de412

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/project-thread-jfhqg2
2 parents 3ed572d + 365fe5e commit 07de412

155 files changed

Lines changed: 5583 additions & 2349 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: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
'@objectstack/objectql': patch
3+
'@objectstack/types': patch
4+
'@objectstack/rest': patch
5+
---
6+
7+
fix(objectql,types,rest)!: a database NOT NULL refusal on a declared field is the record validator's `ValidationError` at every door, named by the field's label
8+
9+
A field declared `storage: { notNull: true }` without `required` is ADR-0113's engine-populated column: the record validator judges `required` only, so a write with no value for it reaches the driver and the database refuses it. The engine let the driver's error out, and each HTTP door classified it from the driver's text its own way. For the same refused write, the runtime dispatcher's `/data` POST and PATCH answered `500 INTERNAL_ERROR`, the actions door answered `400 VALIDATION_ERROR` with the driver's text and `declaredCode: 'SQLITE_CONSTRAINT_NOTNULL'`, and `RestServer` answered `400 VALIDATION_FAILED` relabelled by a second object read on its own `POST` / `PATCH` only.
10+
11+
Clause-②: yes (narrowing: `@objectstack/types` adds the export `notNullViolationSite` and retires `notNullRefusalSite` and `relabelNotNullRefusal`; the HTTP doors' answer for this refusal moves to the record validator's envelope)
12+
Metadata-protocol: none
13+
14+
<!-- adr-0087: not-required (runtime-export-retired @objectstack/types#notNullRefusalSite, @objectstack/types#relabelNotNullRefusal) No metadata moves: no spec key, authorable spelling, object definition or stored shape is added, removed, renamed or re-shaped, so there is nothing for `objectstack migrate meta` to rewrite. What retires is two function exports of `@objectstack/types`' published entry, whose only consumer was `@objectstack/rest`'s door-side relabel, retired in this same change; no other consumer was found in this repository (its `ui/` and `examples/` included) or in hotcrm. The affected party is a TypeScript caller, and the compiler at that call site delivers the break. -->
15+
16+
**BREAKING** (an export retirement and a wire-answer change), shipped as `patch`: no metadata protocol kind moves.
17+
18+
- **`@objectstack/objectql`.** The write doors' driver exits (single-row and batch insert, the autonumber retry, by-id and predicate update) translate a driver NOT NULL refusal on a field the object's field map holds into the record validator's own `ValidationError` (`code: 'VALIDATION_FAILED'`), built by the validator's own builder with the validator's message context: one field error, `code: 'required'`, the field named by its label (translation bundle, then declared `label`, then API name) in the caller's locale, with the sentence the validator gives the same verb (`required` on insert, `required_cleared` on update). The driver's error rides `cause`, and the doors' log line still carries the database's own diagnosis. Left as the driver threw it: a column no field declares, a refusal the driver attributes to another table, and a NOT NULL code whose column the driver did not name. `required` and `storage.notNull` stay apart (ADR-0113 D1 / D2): no pre-write check reads `storage.notNull`.
19+
- **Every door answers it alike**, because every door already answers that class alike: the dispatcher `/data` POST and PATCH now answer `400 VALIDATION_FAILED` (was `500 INTERNAL_ERROR`), the actions door `400 VALIDATION_FAILED` with the field envelope (was `400 VALIDATION_ERROR` with the driver's text), and `RestServer`'s `POST` / `PATCH` and its `/data/:object/batch` rows answer the validator's envelope and sentence. Import rows, the public form submit and MCP write through the same engine doors and answer the same class through the same arms; they were not driven separately. On `RestServer` the body is now exactly the validator's `required` shape: `fields[0]` carries `label`, there is no `hint`, and a `PATCH` writing null reads the clear-out sentence.
20+
- **`@objectstack/types`.** New export `notNullViolationSite(error)`: the column (and table, where the dialect names it) of a driver's NOT NULL refusal, read from the error's codes (better-sqlite3 `SQLITE_CONSTRAINT_NOTNULL`; libsql `extendedCode` / `rawCode: 1299`; node-postgres `23502` with its `column` / `table` fields; mysql2 `ER_BAD_NULL_ERROR` / `errno: 1048`), then its text (all sql.js gives), then one step down its `cause` chain. An error whose code names another refusal is not read for NOT NULL text. `mapDataError`'s NOT NULL branch is unchanged, and answers the column no field declares.
21+
- **`@objectstack/rest`.** The `/data` create and update doors' NOT NULL relabel (`not-null-refusal-label.ts`) retires: every path it served is an engine write.
22+
23+
Migration, for a TypeScript caller of the retired exports:
24+
25+
- FROM `notNullRefusalSite(answer)` TO `notNullViolationSite(error)`: ask the driver's error, not `mapDataError`'s answer.
26+
- FROM `relabelNotNullRefusal(answer, { message, declaresNotNull })` TO nothing (no replacement): write through the engine, which answers a declared field's NOT NULL refusal as a labelled `ValidationError` itself.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
'@object-ui/plugin-detail': patch
3+
---
4+
5+
fix(plugin-detail): the record page's read mode wraps a text value instead of cutting it to one line, and a highlighted date keeps its day
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
- **Details body.** A `text` field in a `record:details` section, read mode, was drawn with the grid's one-line cell (one line, an ellipsis), so a case subject read "Data export timing out for large…" in a two-column section with room to spare. It now reads through the text field's own read display and wraps, as a `textarea` already did. The same holds for every type that used the grid's text cell (`code`, `time`, `auto_number`, …). A host that registered its own cell renderer for one of those types keeps it. Grid cells are unchanged and still truncate, with the whole value on hover.
11+
- **Highlights strip.** A `date` or `datetime` chip now takes the wide column (16rem floor) that email, phone and URL chips take. On the 9rem column the compact datetime face, and a date in Chinese or Japanese, overran the value box, so the ellipsis landed inside the day ("2026/10/2…"). A chip whose line runs out still truncates, with the whole value as its hover title.
12+
13+
What to do: nothing.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@object-ui/app-shell': patch
3+
---
4+
5+
fix(app-shell): the draft preview's failure pane is announced as an alert, drawn by the shared `DataErrorState`
6+
7+
Clause-②: no (one surface swaps its own block for the shared components; no exported type change. If the shared component needs a new prop, the thread reports first and the seat amends this line)
8+
Metadata-protocol: none
9+
10+
The pane a `?preview=draft` tree shows when it has nothing to draw (`PreviewDraftEmptyState`) now uses the shared view states from `@object-ui/components` instead of its own `Empty` block. When the draft overlay read fails, it is `DataErrorState` and is announced to assistive technology as `role="alert"`. When the app is not in the draft yet, or nothing is drafted, it is `DataEmptyState` with `role="status"`. Before, both were the same unlabelled node, so a screen reader said nothing when the preview failed to load. The text, the Retry button and the test ids are unchanged; the title and icon now use the shared components' smaller size.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@object-ui/plugin-form': patch
3+
---
4+
5+
fix(plugin-form): a `{ group }` form section no longer warns that it "also declares `collapsible`, `collapsed`" when its author wrote neither
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
`@objectstack/spec` gives a form section's `collapsible` and `collapsed` a default of `false`, so every parsed section carries both, and it refuses them beside `group` only when they are `true`. The console's form renderer reported any value at all, so every `{ group }` section of a parsed form logged the group-owned-key warning once per page and told the author to move keys they never wrote to the object's `fieldGroups`. The renderer now reads those two keys the way the spec does: `true` beside `group` is still reported (a programmatic caller that skips the spec's parse), while `false` or absent is not. `name`, `label`, `description` and `visibleWhen` are still reported on any value.
11+
12+
What to do: nothing. If you moved `collapsible` / `collapsed` off a `{ group }` section because of this warning, no metadata change was needed.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@object-ui/plugin-dashboard': patch
3+
---
4+
5+
fix(plugin-dashboard): a `compareTo` KPI tile shows its number when the reader picks All time, instead of the executor's "compareTo needs a dated window to shift" error
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
- **Before.** A dataset-bound widget with `compareTo` always sent `compareTo` to the dataset executor, but lowered a `timeDimensions` window only when its resolved filter held a closed `{ $gte, $lte }` date pair. Picking **All time** on the dashboard's date-range filter removes that pair, so the executor refused the query and the tile showed a 400 error where its number belonged. The same tile without `compareTo` showed its number.
11+
- **Now.** When the resolved filter carries no date condition at all on the compared field (on any field when `compareTo.dimension` is omitted), the widget asks for no comparison: the query is the one a widget without `compareTo` sends, and the tile shows its number with no comparison badge.
12+
- **Unchanged: an author-written window that is not closed stays an error.** A one-bound window (`{ $gte: '{current_year_start}' }`, a YTD tile), an exclusive bound or a single day is still sent with `compareTo`, and the executor still refuses it and names the fix. The executor and the spec define a comparison only against a two-bound window. Two dated fields with no `compareTo.dimension` stay the executor's ambiguity error.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@object-ui/app-shell': patch
3+
---
4+
5+
refactor(app-shell): the Console `ObjectView`'s module-level helpers move into sibling modules
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
Module-level helpers, types and constants of `views/ObjectView.tsx` (the record links the list publishes, the saved-view tab, override and column helpers, and the small toolbar and filter helpers) move verbatim into `objectViewRecordLinks.ts`, `objectViewTabs.ts` and `objectViewHelpers.ts` beside it. No logic, name or rendered output changes. `ObjectView.tsx` keeps the component and re-exports every name it exported before, and the package entry is unchanged.
11+
12+
What to do: nothing.
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 file its record lets go keeps that record's read rule
6+
7+
ADR-0104 D3 promises one file, one owning record and one access rule. That rule now holds after the owning record lets the file go: until a writer who may read that record claims the file again, or the platform collects it, the file is read by that record's rule. The download door and the record-write claim path ask the same verdict, so both follow it. A file no record has owned is read as before, within its organization. The uploader still reads their own file.
8+
9+
`sys_file` gains two nullable fields, `released_ref_object` and `released_ref_id`, naming the record that last owned the file. The platform writes them when a record lets a file go and clears them when a record claims it. Existing rows hold NULL and are read as before.
10+
11+
Clause-②: no (narrowing) — a file read that a record's rule refuses is now refused after the record lets the file go. Nothing that was refused becomes accepted.
12+
13+
Metadata-protocol: none (a file read rule and two platform-managed bookkeeping 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. The platform object `sys_file` gains two nullable bookkeeping fields that only the platform writes; schema sync adds the columns and existing rows read NULL, which is the shape every row had before. What changes is which callers a file read admits after the file's owning record lets it go. The remedy is reading the file as a reader of that record, 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 reader the owning record's rule refuses, shipped as `patch` because the metadata protocol does not move.
18+
19+
- **Before.** The owning record's rule did not hold for the file once the record let it go.
20+
- **Now.** It is read by the rule of the record that let it go, until it is claimed again or collected.
21+
- **Fix.** Read or claim such a file as a reader of that record.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@objectstack/plugin-security': patch
3+
---
4+
5+
The built-in permission sets' names are shown in Chinese, Japanese and Spanish
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
Setup → Permission Sets showed the platform's own permission sets with English names (*Administrator — Full Access*, *Member — Standard Access* …) under zh-CN, ja-JP and es-ES, next to each app's translated sets and next to their own translated descriptions. Each built-in set's label is now translated through the permission-set group of the translation bundle (`permissions.NAME.label`), beside its description, so a row reads in one language. For example, under zh-CN *Administrator — Full Access* reads 「管理员 — 完全访问」 and *Viewer — Read-Only* reads 「查看者 — 只读」.
11+
12+
No set's grants, name, English label or description changed. This changes only the text shown for each set, not its permissions.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ PM 的工作是循环:选卡 → 认领 → 派发 → 收集 → 复核 → 报
6868
- 批 = 一个上下文里读完的 ≤5 张同族卡,是质量单位,⛔ 不是预算。
6969
- 班 = 一个会话的任期,只以交接或惰性回收结束。
7070
- 席位两形:Routine 席与项目席(会话是车队项目的线程);细则见 `references/seat-lifecycle.md`。
71-
- 项目席唤醒 = PR 订阅事件 + 每项目一个巡查 routine;⛔ 无自绑 cron、无 `send_later` 链。
71+
- 项目席唤醒 = PR 订阅 + 每项目一个自建自绑的巡查 routine,默认每小时;⛔ 无 `send_later` 链。
7272
- 探针先行:fire 的第一动作是一次最便宜的车道盘点查询,⛔ 在它之前不读任何长文本。
7373
- 队列空 + 无在飞 + 无待复核 + 决策箱无新答复 ⇒ 自退。
7474
- 分诊席跑直连会话、默认判断档;直连会话按召唤跑,⛔ 无自醒不常驻;裁决归总监席。

‎.claude/skills/pm-dispatch/references/project-forms.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
- 项目聊天里的长句(「本账户坐 X;Y 的 PR 由本账户复审」)是等价写法。
2929
- 协调者每次开场把指令行与座位贴对照:逐席按 `seat-post-protocol.md` 坐席或接管。
3030
- 坐席即把本账号写进 🟢 行、每席开一条席位线程;⛔ 无花名册文件。
31-
- 首次坐席建本项目的 routine:巡逻每 2–4 小时;总监席加每日摘要与每小时收件箱扫描。
31+
- 首次坐席自建本项目的 routine:巡逻默认每小时;总监席加每日摘要与每小时收件箱扫描。
3232
- 分诊席加每小时裸卡轮;总监与分诊同在一个项目时两者可共用一个 routine。
3333
- 并发上限是维护者在该项目定的姿态;⛔ 不是协议常量,本文不写数字。
3434

0 commit comments

Comments
 (0)