Skip to content

fix(console): make 8 more preview samples parse against @objectstack/spec - #3277

Merged
xuyushun441-sys merged 1 commit into
mainfrom
claude/issue-3266-preview-samples-spec-conformance
Aug 3, 2026
Merged

fix(console): make 8 more preview samples parse against @objectstack/spec#3277
xuyushun441-sys merged 1 commit into
mainfrom
claude/issue-3266-preview-samples-spec-conformance

Conversation

@xuyushun441-sys

Copy link
Copy Markdown
Contributor

Fixes#3266

预览画廊里的样例不是测试夹具,是每种元数据类型给作者(多数情况下是模型)照抄的那份范例。范例过期不会只错一次,它会扩散。objectui#3257 / PR #3269 建好了度量它的守卫;本 PR 把那份 KNOWN_STALE 台账里机械可修的那一半清空

  • 修好并提升进 SPEC_CLEAN:8 个(actionagentskillflowreportvalidationdatasourceapp)
  • 按派发要求留在台账里、附理由:4 个(objectpagedashboardtranslation)
  • 守卫的判定标准一处未放宽:没有豁免、没有挪进 NO_AUTHORING_SCHEMA、没有降低严格度。退役键的钉定测试反而被加强了(见下)。

一、4 个带已退役键的(与 #3257 同性质、同裁决依据)

样例改动依据
actionbulkEnabled;type: 'server''script' 并补 target: 'closeOrder';variant: 'default''primary';locations: ['record','list']['record_header','list_toolbar']objectstack#3896
agenttoolsknowledgeobjectstack#3894 / #3896
skilltriggerPhrases;triggerConditions 改写成真正参与路由的 field/operator/value 形态objectstack#3896
flowwaitEventConfig.onTimeout;type: 'scheduled''schedule';删 variables[].description(FlowVariableSchema.strict());13 条 edge 补上必需的 idobjectstack#4158

actiontarget 不是自由发挥:ActionSchema 有一条 refine —— type: 'script' 而既无 body 又无 target 会被拒(那是 #2169 的「注册不上 handler、运行时才炸」)。原样例 type: 'server' 表达的就是服务端处理器,script + 具名 target 是它在 17 里的写法。

二、4 个形态漂移

  • report —— 本单原描述是「columns 改成列名字符串」,实际不止:ADR-0021 单一形态下报表必须绑 dataset + values。样例用的是 9.0 之前的内联查询(object + columns: [{field,label}] + groupBy),ReportSchema.strict() 会把 object/groupBy 静默剥掉,然后因为「没有 dataset」失败 —— 只改列名字符串修不好。已重写为 dataset: 'sales_orders' / rows: ['status'] / values: ['order_count','total_amount'] / order
  • validation —— events写入上下文(insert/update),不是生命周期钩子名。
  • datasource —— type/isDefault 不是 datasource 的键(路由在 stack 级的 datasourceMapping);sslcapabilities 是配置对象;intervalintervalMs,且单位是毫秒(原来的 60 拼对键之后是 60ms,不是 60 秒)。
  • app —— navigation 是按 type 的判别联合且每支 .strict():app 不用手写 path 路由,而是点名要打开的元数据记录(objectName / pageName / dashboardName / url),路由由 shell 推导;id 必填;landing 已被 objectstack#4001 移除,替代是 homePageId(它是 nav item 的 id,不是路由字符串)。顺带让样例互相引用起来(crm_welcome 页、open_orders 视图、sales_overview 仪表盘)。

三、⚠️ 画廊(共用浏览器验证台)的可见变化,逐条

以下都是在 preview-gallery.html?only=<type>实测的改前 → 改后,不是推断。

designer改前改后
actiontype: server / variant: default,Locations 显示 recordlist,On click 显示 "No handler bound yet.",「WHERE IT APPEARS」标题下一片空白(placement mock 认不出 record/list)type: script / variant: primary,Locations 显示 record_headerlist_toolbar,On click 显示 "Run named script closeOrder",「WHERE IT APPEARS」真的画出 record_header 与 list_toolbar 两块占位模拟 —— 本 PR 里画廊变丰富的一处
agentTOOLS 区显示 query_orders (OBJECTQL) / lookup_company (HTTP),并有 KNOWLEDGE (RAG): sales_playbook 区块TOOLS 显示空态提示 "No direct tools (skills can provide them)",KNOWLEDGE 区块消失。SKILLS 芯片不变
skillTRIGGER PHRASES (2) 区块("draft an email" / "follow up");trigger condition 一行显示 record.status == 'Open'TRIGGER PHRASES 区块消失;trigger condition 显示 COND | sales_order(渲染器只读 expression ?? value,看不见 field/operator —— 已另立单)
flow顶栏 Trigger: scheduled顶栏 Trigger: schedule画布节点/连线渲染完全不变(edge 的 id 只影响 React key,原本回落 ${source}-${target}-${i})
report空态卡片 "Bind a dataset to preview this report"走 dataset 绑定分支,显示内联错误 "This data source does not support dataset queries."(画廊无后端;页头本来就写着 view/dashboard/report 需要活的 data adapter)。这是本 PR 里画廊唯一变「难看」的一处 —— 但任何 spec 合规的报表都必须绑 dataset,所以修 report 就一定会走到这条分支;换来的是验证台从此覆盖的是真实设计器走的那条路,而不是空态
validationon beforeInsert, beforeUpdateon insert, update
datasourceSSL 区显示 enabled: true;Health Check 显示 interval: 60;有 CAPABILITIES: read / aggregate 区块SSL 区显示 enabled: true + rejectUnauthorized;Health Check 显示 intervalMs: 60000;CAPABILITIES 区块消失(渲染器 Array.isArray(d.capabilities) 只认数组,对象形态读不到 —— 已另立单)
app每项下面有一行路径(/apps/crm/accounts 等);Dashboard 的 kind 徽标是 dashboard;顶部 Landing: /apps/crm/home路径行消失(spec 里没有 path);Dashboard 徽标退化成 item;顶部 Landing: /(渲染器不读 homePageId)。层级、分组、外链项照常

object / page / dashboard / translation / view / job / tool / permission / position / email_template / workflow / approval 的样例一个字没动,画廊对应的卡片与 main 完全一致。

其中 agent / skill / datasource / app 的「变少」不是本 PR 造成的退化,而是暴露出来的既有问题:那几块内容原本是靠不合规的样例喂出来的 —— 渲染器在读 spec 现在明确拒收的键。按 AGENTS.md #0.1(修生产者、不在消费者加宽容回退)和 objectui#3236 / PR #3258 的先例,该修的是渲染器,那在 packages/app-shell,不在本轮 scope fence(apps/console)内,已另立 #3275

四、守卫本体的改动(只增不减)

  • SPEC_CLEAN 增加 8 项;KNOWN_STALE 只剩 4 项,每项理由保留并写清「为什么需要先裁决」而不是「还没轮到」。
  • 台账注释明确写上:样例只能靠被修好离开台账;为了买一次绿而放宽守卫、豁免 schema 或把行挪进 NO_AUTHORING_SCHEMA,会把这个文件唯一的价值删掉。
  • 退役键钉定测试从「只钉 tool 的 3 个键」扩成 RETIRED_KEYS 表(9 条,每条带裁决出处),并且改为任意深度匹配 —— waitEventConfig.onTimeout 挂在 flow 节点上,原来的 not.toHaveProperty 够不着。这是加强,不是放宽。
  • 文件头补上一句 LIMIT 的推论:report 是靠绑 dataset 才进的 SPEC_CLEAN,它被剥掉的 object/groupBy 自己不会让任何断言失败 —— 这正是 RETIRED_KEYS 存在的理由。

五、留在台账里的 4 个,以及理由

样例为什么不动
objectfields 的数组形态是 packages/app-shell/.../object-fields-io.tsreadFields()有意支持的分支(它按 shape: 'array' | 'record' 分流并原样保留)。改成 record 会让画廊不再覆盖 array 那条路,而真正该裁的是「设计器要不要继续编辑一个 ObjectSchema 拒收的形态」(AGENTS.md #0.1)—— 那是 app-shell 那头的决定
pageregion 里的 component 带 props,PageComponentSchema 按 ADR-0089 D3a 拒收;改法会改变画廊渲染结果,需先裁决
dashboardwidget 缺 dataset/values、带已退役的 value/formatchart 不是合法 widget type;同样改变渲染结果
translationObjectStackSchema.translationsArray< Record< locale, TranslationData > >,而样例是 console 实际在编辑的元数据记录形态。更像是映射选错而不是样例过期 —— 先定这个样例到底对应哪个契约,再动

六、changeset

不需要,#3269 的判断依然成立并已复核:apps/console/vite.config.ts没有rollupOptions.input,默认入口只有 index.html,preview-gallery.html 及其引用的 preview-samples.ts 不是生产构建输入(文件头也自述 DEV-ONLY)。另外 AGENTS.md 明确「纯 bug 修复不需要 changeset」,本 PR 两条都占。

七、验证

$ pnpm --filter @object-ui/console test
Test Files 20 passed (20)
Tests 183 passed (183)
$ vitest run src/__tests__/preview-samples-spec-valid.test.ts
Test Files 1 passed (1)
Tests 30 passed (30) # 含 8 条新的 SPEC_CLEAN、4 条仍如实失败、9 条退役键钉定
$ eslint src/preview-samples.ts src/__tests__/preview-samples-spec-valid.test.ts
(exit 0)

pnpm --filter @object-ui/console type-check 在本地 worktree 报 306 个 Cannot find module '@object-ui/*' —— 这是工作区包未构建导致的既有状态,改动前后逐字一致(306 → 306),且没有一条落在本 PR 改的两个文件上(CI 先构建,不会命中)。

浏览器验证走 verify skill(自建 :5233 vite,收工按 PID 停,未碰 :3000/:5180),上表每一行都是实测。

八、越界发现(Prime Directive #10,均未指派、不在本 PR 修)


Generated by Claude Code

…spec (#3266)
The preview gallery's samples are the worked EXAMPLE an author (usually a
model) copies for each metadata type, so a stale one does not fail once — it
propagates. objectui#3257 shipped the guard that measures this; this change
empties the mechanically-fixable half of its KNOWN_STALE ledger.
Four carried RETIRED keys, adjudicated exactly like #3257's:
- action: `bulkEnabled` (objectstack#3896); `type: server` / `variant:
default` / `locations: [record, list]` are pre-17 enum values.
`script` needs a handler, so `target: 'closeOrder'` is declared.
- agent: `tools` (objectstack#3894 — an agent reaches the tools its skills
declare) and `knowledge` (objectstack#3896 — it never scoped
retrieval).
- skill: `triggerPhrases` (objectstack#3896); `triggerConditions` restated
in the field/operator/value form that actually routes.
- flow: `waitEventConfig.onTimeout` (objectstack#4158); `scheduled` →
`schedule`; the strict FlowVariableSchema rejects `description`;
every edge now carries the required `id`.
Four were shape drift:
- report: rewritten to the ADR-0021 single form (`dataset` + `values`
grouped by `rows`). The pre-9.0 inline query it used was
silently stripped by the non-strict schema, then rejected for
having no dataset — column-name strings alone would not fix it.
- validation: `events` is the write CONTEXT (`insert`/`update`), not a
lifecycle-hook name.
- datasource: `type`/`isDefault` are not datasource keys; `ssl` and
`capabilities` are config objects; `interval` → `intervalMs`
(and the unit is milliseconds).
- app: navigation rewritten against the spec-17 discriminated union
(`type` + the named metadata record, not a hand-written
`path`); `landing` → `homePageId` (objectstack#4001).
All eight move KNOWN_STALE → SPEC_CLEAN, as the ledger's reverse assertion
demands. `object`, `page`, `dashboard` and `translation` stay quarantined with
their reasons — each needs an adjudication that does not belong in this file.
The guard itself is untouched in strictness: nothing was exempted, relaxed or
moved into NO_AUTHORING_SCHEMA. The retired-key pin is widened instead, and now
matches at any depth so a nested tombstone like `onTimeout` is covered.
Refs #3257, #3269
@vercel

vercelBot commented Aug 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectuiIgnoredIgnoredAug 3, 2026 10:00am

Request Review

@xuyushun441-sysClaude

Copy link
Copy Markdown
ContributorAuthor

正文第三节里那句「在 preview-gallery.html?only= 上实测」被 body sanitizer 吃掉了尖括号占位符,原文是 preview-gallery.html?only=&lt;type&gt; —— 即每个 designer 都用 ?only=action?only=report 这样单独隔离渲染后截的图/取的文本,不是在混排页面上目测的。


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Main entry (gzip)28.1 KB350 KB
Entry fileindex-D7Oj1KNN.js
StatusPASS

📦 Bundle Size Report

PackageSizeGzipped
app-shell (index.js)8.47KB3.09KB
app-shell (runtime-config.js)7.42KB2.32KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)7.57KB2.97KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)1.17KB0.53KB
auth (AuthProvider.js)22.10KB4.37KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.12KB3.41KB
auth (LoginForm.js)17.86KB5.29KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.43KB2.09KB
auth (SocialSignInButtons.js)9.60KB3.89KB
auth (UserMenu.js)3.40KB1.22KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)35.76KB9.11KB
auth (createAuthenticatedFetch.js)4.37KB1.69KB
auth (index.js)2.35KB1.07KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)4.91KB0.87KB
auth (useIsWorkspaceAdmin.js)1.61KB0.85KB
collaboration (CommentThread.js)18.38KB4.49KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)3.65KB1.42KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.25KB0.53KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)476.24KB104.56KB
core (index.js)2.25KB0.80KB
create-plugin (index.js)9.28KB2.98KB
data-objectstack (index.js)136.23KB34.75KB
fields (index.js)223.53KB54.78KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (i18n.js)4.32KB1.77KB
i18n (index.js)2.46KB0.96KB
i18n (pickLocalized.js)1.70KB0.83KB
i18n (provider.js)5.37KB1.72KB
i18n (useObjectLabel.js)26.14KB6.07KB
i18n (useSafeTranslation.js)3.26KB1.44KB
layout (index.js)37.96KB10.54KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.74KB
mobile (index.js)1.50KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.71KB0.42KB
mobile (useResponsiveConfig.js)1.36KB0.63KB
mobile (useSpecGesture.js)4.05KB1.53KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)8.75KB3.06KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)3.67KB1.12KB
permissions (evaluator.js)4.41KB1.44KB
permissions (index.js)0.91KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.52KB
permissions (usePermissions.js)1.55KB0.71KB
plugin-ai (index.js)15.71KB3.79KB
plugin-calendar (index.js)44.98KB12.37KB
plugin-charts (index.js)60.54KB17.13KB
plugin-chatbot (index.js)180.09KB42.72KB
plugin-dashboard (index.js)112.01KB28.86KB
plugin-designer (index.js)210.51KB42.51KB
plugin-detail (index.js)230.56KB56.80KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)111.49KB26.95KB
plugin-gantt (index.js)162.25KB39.55KB
plugin-grid (index.js)185.08KB49.04KB
plugin-kanban (index.js)47.89KB13.18KB
plugin-list (index.js)104.94KB25.32KB
plugin-map (index.js)16.81KB5.24KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)40.55KB10.59KB
plugin-timeline (index.js)25.76KB7.33KB
plugin-tree (index.js)8.34KB2.82KB
plugin-view (index.js)83.67KB20.43KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.71KB3.53KB
providers (index.js)0.44KB0.22KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.67KB2.37KB
react (LazyPluginLoader.js)3.77KB1.33KB
react (SchemaRenderer.js)19.28KB6.38KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)1.02KB0.55KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)4.09KB1.74KB
sdui-parser (index.js)4.47KB2.03KB
sdui-parser (parse.js)10.04KB2.82KB
sdui-parser (types.js)0.29KB0.24KB
sdui-parser (validate.js)4.69KB1.48KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)0.20KB0.18KB
types (crud.js)0.20KB0.18KB
types (data-display.js)0.20KB0.18KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.87KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-retry.js)4.32KB2.02KB
types (index.js)2.46KB1.21KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)0.20KB0.18KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)0.20KB0.18KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

console preview-samples: 除 tool 外还有 11 个样例过不了对应的 spec schema(其中 4 个同样带已退役的键)

2 participants

@xuyushun441-sys@claude