Skip to content

fix(console): un-invert the validation sample's condition and drop 4 silently-stripped keys - #3282

Merged
xuyushun441-sys merged 1 commit into
mainfrom
claude/issue-3276-validation-sample-semantics
Aug 3, 2026
Merged

fix(console): un-invert the validation sample's condition and drop 4 silently-stripped keys#3282
xuyushun441-sys merged 1 commit into
mainfrom
claude/issue-3276-validation-sample-semantics

Conversation

@xuyushun441-sys

Copy link
Copy Markdown
Contributor

Fixes #3276

1. condition 的方向(主要问题)

先按 dispatch 的要求核了契约再动手。node_modules/@objectstack/spec/src/data/validation.zod.ts:119:

condition: ExpressionInputSchema.describe('Predicate (CEL). If TRUE, validation fails. e.g. P`record.amount < 0`'),

确认无误:condition失败谓词,不是不变式。同文件顶部的 Salesforce 对照也是同一方向 —— condition: 'discount_percent > 0.40' 配 message「Discount cannot exceed 40%」,写的都是非法的情形。

样例原本是 condition: 'amount > 0',配 name: 'amount_positive' 和 message「Order amount must be greater than zero.」—— 按契约,它会在金额为正时判定失败,把每一条合法订单拦下、把每一条非法订单放行,和自己的名字与消息正好相反。改为 'amount <= 0'

这类错误 #3257 的守卫结构上抓不到(它只问「能不能 parse」),而危害恰恰更大:parse 不过的样例会被修,语义反了的样例会被照抄 —— 抄的人通常是模型。

2. 四个会被静默剥掉的键

validation 各分支与 SkillSchema 都不是 .strict(),所以这些键 parse 能过、解析结果里却不存在:

为什么不存在
validation.object 规则挂在宿主对象的 validations[] 上,宿主就是它的作用域
validation.field 只存在于 state_machine / format / json_schema 三支,script 没有
validation.expression 任何一支 validation 分支上都没有这个键
skill.type SkillSchemasurface(ask/build/both)分类,没有 type,更没有 'prompt'

field 是 issue 正文没列的第三个 —— 它是我按 dispatch 第 3 条机械审计所有样例时找出来的(见下)。它今天已经被剥掉,所以删除它在运行时零变化;只是不再教作者写一个没用的键。要把错误指到某个字段,spec 的正解是 type: 'cross_field' + fields: ['amount'](同一条求值路径,fields[0] 决定违规挂在哪个字段上)—— 这一点已写进文件注释。

⚠️ 提示,这四个键没有加进 RETIRED_KEYS:它们不是被退役的键,是在这些 schema 上从来没存在过的键,两件事不能混。

3. 全样例审计(dispatch 第 3 条)

把每个样例按守卫测试的方式嵌进 ObjectStackSchema,safeParse 后逐层比对输入/输出的 key 集合,输入有、输出没有的即为被剥掉的键。修完之后 validationskill 归零;其余发现不在本 PR 修,已按 Prime Directive #10 立为未指派 issue:

按 issue 第 3 条与 dispatch 的 ⛔,没有preview-samples-spec-valid.test.ts 里加任何语义断言。

验证

$ pnpm exec vitest run src/__tests__/preview-samples-spec-valid.test.ts --maxWorkers=2
 Test Files  1 passed (1)
      Tests  30 passed (30)

$ pnpm --filter @object-ui/console test -- --maxWorkers=2
 Test Files  20 passed (20)
      Tests  183 passed (183)

validationskill 都留在 SPEC_CLEAN 里,守卫全绿。

type-check:本 worktree 里 pnpm --filter @object-ui/console type-checkTS2307: Cannot find module '@object-ui/types' 等 —— 工作区包未构建所致,与本改动无关:git stash 后在干净树上重跑,报错完全相同(CI 会先构建)。改动文件单独过 tsc 干净:

$ pnpm exec tsc --noEmit --ignoreConfig --strict --target es2020 --moduleResolution bundler --module esnext src/preview-samples.ts
ISOLATED TYPECHECK: preview-samples.ts CLEAN (exit 0)

预览渲染的实际影响(已读 ValidationPreview.tsx 确认,无崩溃):object 芯片不再显示(规则本来就没有 object);field 只在非 script 分支被读,本样例零变化;condition 照常渲染。

不加 changeset(已复核)

preview-samples.ts 仅被 preview-gallery.tsx 引用,后者仅由 preview-gallery.html 引入;apps/console/vite.config.ts没有 rollupOptions.input(grep input: 命中 0 次),因此 Vite 的构建入口只有默认的 index.html,preview-gallery.html 不是构建输入。发布物 files: ["dist", …] 中的 dist 由该构建产出,样例不会进入 —— 对用户不可见。#3269 / #3277 的判断成立。


Generated by Claude Code

…stripped keys (#3276)

`ScriptValidationSchema.condition` is the FAILURE predicate — "Predicate
(CEL). If TRUE, validation fails." The `amount_positive` sample declared
`condition: 'amount > 0'`, i.e. it failed every order with a positive
amount and passed every non-positive one: the exact opposite of its own
name and message. Corrected to `amount <= 0`, matching the direction of
the spec's own examples (`record.amount < 0`, `discount_percent > 0.40`).

The spec guard added in #3257 structurally cannot catch this — it asks
whether a draft parses, never whether it means what its author intended —
and an inverted sample is worse than a broken one: the broken one gets
fixed, the inverted one gets copied, usually by a model.

Also removes four keys that parsed and were then silently dropped, since
neither the validation branches nor SkillSchema are `.strict()`:

  - validation.object     a rule's scope IS its host object's validations[]
  - validation.field      exists on state_machine/format/json_schema, not script
  - validation.expression not a key on any validation branch
  - skill.type            SkillSchema classifies by `surface`, not `type`

`field` was found by auditing every sample for input keys missing from
the parse result; the same audit's other findings are out of scope here
and filed as #3280 (view/job/email_template) and #3281 (ValidationPreview).

No changeset: `preview-samples.ts` is dev-only. It is reached only from
`preview-gallery.tsx` via `preview-gallery.html`, and `vite.config.ts`
declares no `rollupOptions.input`, so the build's sole entry stays
`index.html` — the samples never reach `dist/`, which is what the
published package ships.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NVPjPzmmAJ2Ngtvgg5MSRa
@vercel

vercel Bot commented Aug 3, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectui Ignored Ignored Aug 3, 2026 10:20am

Request Review

@github-actions github-actions Bot added the apps label Aug 3, 2026
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 28.1 KB 350 KB
Entry file index-jMXoCnx9.js
Status PASS

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 8.47KB 3.09KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 7.57KB 2.97KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 22.10KB 4.37KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.12KB 3.41KB
auth (LoginForm.js) 17.86KB 5.29KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.43KB 2.09KB
auth (SocialSignInButtons.js) 9.60KB 3.89KB
auth (UserMenu.js) 3.40KB 1.22KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 35.76KB 9.11KB
auth (createAuthenticatedFetch.js) 4.37KB 1.69KB
auth (index.js) 2.35KB 1.07KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 4.91KB 0.87KB
auth (useIsWorkspaceAdmin.js) 1.61KB 0.85KB
collaboration (CommentThread.js) 18.38KB 4.49KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 3.65KB 1.42KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.25KB 0.53KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 476.42KB 104.61KB
core (index.js) 2.25KB 0.80KB
create-plugin (index.js) 9.28KB 2.98KB
data-objectstack (index.js) 136.23KB 34.75KB
fields (index.js) 223.53KB 54.78KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.32KB 1.77KB
i18n (index.js) 2.46KB 0.96KB
i18n (pickLocalized.js) 1.70KB 0.83KB
i18n (provider.js) 5.37KB 1.72KB
i18n (useObjectLabel.js) 26.14KB 6.07KB
i18n (useSafeTranslation.js) 3.26KB 1.44KB
layout (index.js) 37.96KB 10.54KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.74KB
mobile (index.js) 1.50KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.71KB 0.42KB
mobile (useResponsiveConfig.js) 1.36KB 0.63KB
mobile (useSpecGesture.js) 4.05KB 1.53KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 8.75KB 3.06KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 3.67KB 1.12KB
permissions (evaluator.js) 4.41KB 1.44KB
permissions (index.js) 0.91KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.52KB
permissions (usePermissions.js) 1.55KB 0.71KB
plugin-ai (index.js) 15.71KB 3.79KB
plugin-calendar (index.js) 44.98KB 12.37KB
plugin-charts (index.js) 60.54KB 17.13KB
plugin-chatbot (index.js) 180.09KB 42.72KB
plugin-dashboard (index.js) 112.01KB 28.86KB
plugin-designer (index.js) 210.51KB 42.51KB
plugin-detail (index.js) 230.56KB 56.80KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 111.49KB 26.95KB
plugin-gantt (index.js) 162.25KB 39.55KB
plugin-grid (index.js) 185.08KB 49.04KB
plugin-kanban (index.js) 47.89KB 13.18KB
plugin-list (index.js) 104.94KB 25.32KB
plugin-map (index.js) 16.81KB 5.24KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 40.55KB 10.59KB
plugin-timeline (index.js) 25.76KB 7.33KB
plugin-tree (index.js) 8.34KB 2.82KB
plugin-view (index.js) 83.67KB 20.43KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.71KB 3.53KB
providers (index.js) 0.44KB 0.22KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.67KB 2.37KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 19.28KB 6.38KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.02KB 0.55KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 4.09KB 1.74KB
sdui-parser (index.js) 4.47KB 2.03KB
sdui-parser (parse.js) 10.04KB 2.82KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 4.69KB 1.48KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.87KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-retry.js) 4.32KB 2.02KB
types (index.js) 2.46KB 1.21KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 0.20KB 0.18KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (spec-report.js) 5.05KB 1.93KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 3.40KB 1.71KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

@xuyushun441-sys
xuyushun441-sys marked this pull request as ready for review August 3, 2026 10:26
@xuyushun441-sys
xuyushun441-sys added this pull request to the merge queue Aug 3, 2026
Merged via the queue into main with commit f673ada Aug 3, 2026
16 checks passed
@xuyushun441-sys
xuyushun441-sys deleted the claude/issue-3276-validation-sample-semantics branch August 3, 2026 10:27
Sign up for free to 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 validation 预览样例的 condition 语义写反了(spec 里 condition 为 TRUE 表示校验失败),另带两个会被静默剥掉的键

2 participants