Skip to content

Commit f308adf

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/project-thread-pxk0us
2 parents 33a1af0 + a485b5a commit f308adf

61 files changed

Lines changed: 3890 additions & 1086 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: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'@object-ui/plugin-detail': patch
3+
'@object-ui/plugin-kanban': patch
4+
---
5+
6+
fix(ui): the stage path and the kanban lay a select's options out by their declared lifecycle `kind` (#26612)
7+
8+
Clause-②: no
9+
Metadata-protocol: none
10+
11+
A select option may declare `kind: 'open' | 'won' | 'lost'` (`SelectOption.kind` in `@objectstack/spec`). Two readers now follow it instead of the option order or the value's spelling.
12+
13+
- **`record:path`.** Once the status field declares a kind on any option, an option without one reads as `open`, the spec's rule for an absent kind, and is never classified from a `closed_won` / `closed_lost` spelling. The stages are laid out by kind: the open options as the steps in their authored order, then every won option as the forward terminus, then the lost options apart from the rail. A lost option authored mid-list no longer pulls the options after it into the lost group. A field that declares no kind keeps the spelling fallback and its positional layout as before, and a dev build now warns once, naming the object, the field and the stages it guessed.
14+
- **Kanban.** Lanes the board derives from the lane field's options run open → won → lost when the field declares a kind, each class in its authored order, and every lost lane starts collapsed: a spine with its card count that the viewer can open. Authored `columns` and fields that declare no kind keep their order; the board guesses nothing from spelling.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
'@object-ui/plugin-list': patch
3+
'@object-ui/components': patch
4+
'@object-ui/i18n': patch
5+
---
6+
7+
feat(plugin-list): a list block on a page caps its rows and ends with a "View all N" link; a tabbed list block caps each tab and badges each tab with its count
8+
9+
Clause-②: yes
10+
Metadata-protocol: none
11+
12+
A `list-view` block in a page region half the page or wider used to draw the whole object list: every row of its query, the toolbar, the `#` column, every column of the view and a horizontal scrollbar. It now draws a capped list:
13+
14+
- **The rows.** At most `dataSource.limit` of them, the key every page element's data binding already declares, and 5 when the block states none. The cap is the query's own window (`$top`), so a capped block never fetches the rest.
15+
- **The columns.** The view's first four visible columns, in a table fixed to the block's width; the rest are dropped, not scrolled. A view that declares no columns draws the object's `highlightFields` led by its name field, else each record's display name.
16+
- **No object-list chrome.** No toolbar, view tabs, row-number column, pager or record-count bar. A click on a row opens the record, as a grid row does.
17+
- **The link.** Under the rows, 「View all N →」 (`list.viewAllCount`, new in all ten language packs) opens the object list on the block's bound view, the same destination the compact list's "View all" link opens.
18+
19+
A region narrower than half the page keeps the compact list, and a list outside any page region (the object list page, a block with no page around it) keeps the full list.
20+
21+
`page:tabs` now badges a tab holding `list-view` blocks with the number of records those blocks' own queries matched, summed with the `record:related_list` counts it already derives. Such a tab is mounted while inactive, hidden, so the badge is there before the tab is opened; a tab with an authored `count` keeps mounting on open only. Nothing an author writes changes. The published surface widens in three places, which is what `Clause-②: yes` declares: the i18n key `list.viewAllCount` in all ten packs; `usePageTabCountReporter`, a new export of `@object-ui/components`, the count channel a `page:tabs` panel opens to the blocks inside it; and `ListView`'s two new optional host props that use it, `capped` and `onTotalChange`.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'@object-ui/app-shell': patch
3+
'@object-ui/i18n': patch
4+
---
5+
6+
fix(app-shell,i18n): on a deployment with no AI agent, Home's build chip opens Studio's new-package flow as "New in Studio", and `/ai` says no assistant is enabled instead of redirecting home
7+
8+
Clause-②: yes
9+
Metadata-protocol: none
10+
11+
- **Home's build chip reads the agent catalog.** On a deployment whose catalog (`GET /api/v1/ai/agents`) is empty or absent, the greeting row's "Build an app" (「构建应用」) promised a build nothing there could start. There it now reads "New in Studio" (「在 Studio 中新建」) and opens Studio's new-package flow; a session that may not author gets no build chip at all, while "Start with a template" keeps the reason. The chip waits for the catalog's answer, so neither form flashes first. With an agent in the catalog nothing changes.
12+
- **`/ai` with no agent.** `RequireAiSurface` used to redirect a visit to `/ai` home with no word of why. It now shows the AI-unavailable screen in place (「AI 助手不可用」 · 「当前部署未启用 AI 助手。其他功能一切正常。」), with the way home and, for an admin who may author on a runtime that offers authoring, the same "New in Studio" link. The screen is the one `AiChatPage` already showed for an empty catalog, now its own module (`console/ai/AiUnavailable.tsx`) shared by both. `RequireAiSurface`'s `redirectTo` prop keeps its name and now sets where that screen's way home leads.
13+
- **Studio deep link.** `/studio?new=package` opens the Studio landing with the create-package dialog open, and drops the param from the address. `STUDIO_NEW_PACKAGE_PATH` (`views/studio-design/studioNewPackage.ts`) is its one spelling.
14+
- **i18n.** New key `home.build.inStudio` in all ten packs.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
'@object-ui/plugin-list': patch
3+
---
4+
5+
fix(plugin-list): the Display popover's Layout row folds its segments to icons when they would not fit
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
On a list that offers four layouts, the Layout row of the Display popover read 「布局 | 表格 | 看板 | 日历 | 时间线」 and broke each label onto two lines: the popover is a fixed width and the row shares it with its title. In English the labels are longer still.
11+
12+
What changes:
13+
14+
- The row now measures the width its segments need with every label shown against the width it has, in whatever locale is rendering. When they would not fit, every segment but the selected one shows its icon only. The selected segment keeps its label.
15+
- A folded segment keeps its accessible name and its tooltip (its `aria-label` and `title`), so it is still announced and still named on hover.
16+
- The fold is measured, not a count or a breakpoint: two layouts keep their labels, and the same row unfolds wherever it fits again.
17+
- Labels in the row no longer wrap, and the popover keeps its width.
18+
19+
What to do: nothing. Nothing a view or object declares changes.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@object-ui/i18n': patch
3+
---
4+
5+
fix(i18n): the zh chatbot build header separates the verb from the app name
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
- **zh `chatbot.build.built`** reads `已构建 · APP` instead of `已构建APP`, and **zh `chatbot.build.building`** reads `正在构建 · APP…` instead of `正在构建APP…`. A name pasted straight onto the verb ran on into one word, most visibly with a Latin-script name (「已构建Library Lending Management」). The separator is the ` · ` the sibling `chatbot.build.builtAndLive` (`已搭建并上线 · APP`) already used.
11+
- The unnamed-build stand-in goes through the same frame, so it now reads `正在构建 · 你的应用…`, the same shape `builtAndLive` already had for an unnamed build.
12+
- Values only: no key is added, removed or renamed. `ja` (name first, then a particle) and `ko` (already spaced) did not run on and are unchanged; the Latin-script packs are untouched.

‎.claude/skills/pm-dispatch/references/contract-review.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@
1919
出货字节,⛔ 不是已发布接受集,改它不触发 `Clause-②: yes`;legacy node10 深引用在契约之外。
2020
- 四面皆不碰 ⇒ CI 加席位自读 ⛔ 不起第二个 agent;谁跑 = 派发席,达档者席内审。
2121
- 复核记录 = 一条评论落 PR 或卡,席内与子代理同形;散文、dev 自评、`os-dev-report` 恒不算。
22-
- 同形 = `## Contract review` 题头、head sha 独占码段、①②③ 逐项(schema/governed 两面加 ④)、
22+
- 同形 = `## Contract review` 题头、head sha 独占码段、①②③ 逐项(申报面外三面加 ④)、
2323
独立性对、PASS/FAIL。
2424
- 同形含首行 `Served-tier:`:值写该面所定档的常量名 `CONTRACT_REVIEW_TIER` 或 `TIER_DEFAULT`;
2525
无此行不成裁决;模板 `node scripts/pm/record-recognisers.mjs --template`,申报面加 `--face declaration`。
2626
- 同形另带 `Local-runs:` 行:`none`,或 `probe — REASON` 具名唯一本地探针;
2727
有行必校,缺行旧记录不改判。
2828
- ① derived judgments 逐项:diff 引出的接受集与公开面变化逐条点名判对错。
2929
- ② semver 定级与 changeset 声明一致;③ 边界旗:dev 挂旗与 `open_questions` 逐旗答复或升级。
30-
- ④ 设计判断只欠 schema 与 governed 规则文本两面,spec、skills、ADR 皆已在内 ⛔ 不另立面:
30+
- ④ 设计判断欠 schema、governed 规则文本、收窄三面,spec、skills、ADR 皆已在内 ⛔ 不另立面:
3131
逐 `hits` 文件判改动对所引代码与验收场景是否成立,ADR 另判首个消费者与执行计划;
3232
被引代码读而不审;①–④ 合称完整复核,余面同形无 ④,⛔ 无第二条记录。
3333
- 实质 head 各欠一轮完整复核;只落发现的 commit 由同一复核者 delta 复核;无 must-fix 即止。

‎packages/drivers/driver-sql/src/sql-driver-compile-refusal-seam.test.ts‎

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@
2929
*
3030
* 1. **The enumeration** reads `sql-driver.ts` and its cross-field seam module
3131
* `sql-driver-cross-field-comparison.ts` (#25521 cut 13), plus the read-exit
32-
* fault module `sql-driver-read-exit-fault.ts` (#25521 cut 15), and lists every
33-
* function that builds an `INVALID_FILTER` error. Only three may call
32+
* fault module `sql-driver-read-exit-fault.ts` (#25521 cut 15) and the
33+
* predicate-emitter module `sql-driver-predicate-emitters.ts` (#25521 cut 16),
34+
* and lists every function that builds an `INVALID_FILTER` error. Only three may call
3435
* `unsupportedFilterError` directly (the seam itself, the `'author'`
3536
* re-issue, and the column refusal raised from a dialect error AFTER the
3637
* statement ran), and every caller of `withheldFilterError` must have a row
@@ -66,7 +67,7 @@ const SECRET_NUM = 7770123;
6667
const UNDECLARED_KEY = '$psecret_combinator';
6768

6869
type Door = {
69-
/** The builder in `sql-driver.ts` or its cross-field seam module this row drives — asserted by the error's stack. */
70+
/** The builder in `sql-driver.ts` or one of its seam modules this row drives — asserted by the error's stack. */
7071
readonly builder: string;
7172
readonly where: () => Record<string, unknown> | unknown[];
7273
readonly secrets: readonly string[];
@@ -273,8 +274,8 @@ const DOORS: readonly Door[] = [
273274
// ── Half 1: the enumeration ───────────────────────────────────────────────────
274275

275276
/**
276-
* Every function in `sql-driver.ts`, `sql-driver-cross-field-comparison.ts` and
277-
* `sql-driver-read-exit-fault.ts` that builds an `INVALID_FILTER` error, by the route it takes. Read with the TypeScript parser, not a regex, so a call
277+
* Every function in `sql-driver.ts`, `sql-driver-cross-field-comparison.ts`,
278+
* `sql-driver-read-exit-fault.ts` and `sql-driver-predicate-emitters.ts` that builds an `INVALID_FILTER` error, by the route it takes. Read with the TypeScript parser, not a regex, so a call
278279
* split across lines, an inline `throw`, or a builder written as a method or an
279280
* arrow cannot slip past the count.
280281
*/
@@ -287,10 +288,13 @@ function enumerateRefusalBuilders(): {
287288
// its own seam module with two of the builders below; the scan reads both.
288289
// [#25521 cut 15] The read exits' fault terminals moved to a third, with
289290
// `unresolvableFilterColumnError`; the scan reads it too.
291+
// [#25521 cut 16] The leaf predicate emitters moved to a fourth, with the two
292+
// `$empty` refusals; the scan reads it too.
290293
const sources = [
291294
['sql-driver.ts', readFileSync(new URL('./sql-driver.ts', import.meta.url), 'utf8')],
292295
['sql-driver-cross-field-comparison.ts', readFileSync(new URL('./sql-driver-cross-field-comparison.ts', import.meta.url), 'utf8')],
293296
['sql-driver-read-exit-fault.ts', readFileSync(new URL('./sql-driver-read-exit-fault.ts', import.meta.url), 'utf8')],
297+
['sql-driver-predicate-emitters.ts', readFileSync(new URL('./sql-driver-predicate-emitters.ts', import.meta.url), 'utf8')],
294298
] as const;
295299
const directCallers = new Set<string>();
296300
const seamCallers = new Set<string>();

0 commit comments

Comments
 (0)