Uh oh!
There was an error while loading. Please reload this page.
refactor(types,core)!: retire the inert validation action-param key (#3201) - #3283
Merged
xuyushun441-sys merged 1 commit intoAug 3, 2026
Merged
Conversation
…#3201) `validation?: string` was declared on BOTH halves of the action-param contract — the authoring `ActionParam` (`@object-ui/types`) and the resolved `ActionParamDef` (`@object-ui/core`) — read by neither, and rejected outright by the server. `ActionParamSchema` in `@objectstack/spec/ui` is `.strict()` and does not list `validation`, so an authored key is a hard parse rejection ("Unrecognized key(s) on this action param: `validation`") while `tsc` against the public type accepted it happily. Verified there is no consumer on the resolved side either: it was never a key of `resolveActionParams()`'s `RawActionParam`, `RuntimeField` has no `validation` for a field-backed param to inherit, and `paramToField()` never mapped it, so it could not reach the field widgets — whose rules `buildValidationRules()` builds from `required` / `minLength` / `maxLength` / `pattern`. Removed rather than implemented, on ADR-0049 enforce-or-remove: giving it meaning would mean first deciding what an "expression" is here (CEL? formula? regex?) and adding it to `@objectstack/spec`, which is where such a capability has to start. This retires the last named exception in #3174's drift guard, which carried `validation` as the one key `ActionParam` added on top of the spec's set. The rule it pins — the authoring type declares exactly the spec's authorable keys — is now literal: the guard asserts the local-only key set is empty (`[LocalOnlyParamKey] extends [never]`, tuple-wrapped to defeat distribution over the empty union) plus a `@ts-expect-error` on an authored `validation`. Sabotage-verified: re-adding `validation`, or any other undeclared local key, fails `tsc -p tsconfig.test.json`. Also corrects a stale line in `resolveActionParams.ts`'s module comment that claimed field-backed params inherit `validation` from the object field — they never did; there is no such key on `RuntimeField`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NVPjPzmmAJ2Ngtvgg5MSRa
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
xuyushun441-sys
marked this pull request as ready for review
August 3, 2026 10:42
Uh oh!
There was an error while loading. Please reload this page.
xuyushun441-sys
deleted the
claude/issue-3201-retire-actionparam-validation
branch
August 3, 2026 10:43
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes#3201
按 PM 裁定走选项 1(移除),不新增 spec 能力(选项 2 已否决,未触碰
packages/spec)。问题
validation?: string同时声明在 action-param 契约的两半上,两边都没人读,而服务端直接拒收:@object-ui/types的ActionParam(packages/types/src/ui-action.ts)@object-ui/core的ActionParamDef(packages/core/src/actions/ActionRunner.ts)@objectstack/spec/ui的ActionParamSchema是.strict()且ACTION_PARAM_KEYS不含它,所以作者写下它就是一次硬解析拒绝,而tsc对着公共类型却照单全收。本 PR 实测确认:这正是「类型说合法、运行时当场拒收」的 ADR-0049 enforce-or-remove 目标形态。
resolved 侧确实无消费者(逐一核实)
单子的判断成立,并且比单子说的还要彻底:
RawActionParam(resolveActionParams.ts)根本没有validation这个键;RuntimeField也没有validation,所以 field-backed param 从来无从继承;paramToField()/ActionParamDialog(app-shell 与 components 两处)/paramValueShape.ts/useConsoleActionRuntime.tsx/MetadataTypeActions.tsx—— 全部grep -c validation为 0;def.validation读取在packages/plugin-detail/src/RecordDetailDrawer.tsx:252,读的是schemaFields[name]这个对象字段 schema,与 action param 无关。一处顺带纠正:
resolveActionParams.ts模块注释原先声称 field-backed param 会从字段继承validation—— 这是错的(RuntimeField上没有该键)。仅改注释,无逻辑改动;留着它会在移除之后继续教错同一件事。漂移守卫回到「无例外」
packages/types/src/__tests__/page-nav-misc-spec-parity.test.ts原先把validation作为唯一具名例外携带,其注释自己预言过「retired 那天这行会编译失败,例外应被删除而不是被继承」—— 现在正是那天。新断言不再有任何例外:
刻意用
[ T ] extends [ never ]的元组包裹形式,而非裸的T extends never:裸形式会分配(distribute),对空联合求值得到never而非true,恰好在断言开始被满足的那一刻编译失败。另加一条@ts-expect-error钉死「作者写validation现在就是tsc错误」。type不构成例外:它是对 spec 已声明键的收窄(ResolvableParamFieldType),不是新增键。Sabotage 验证(守卫必须能红)
A —— 任意新增本地键(
sabotageOnlyKey?: string):B —— 把
validation原样加回,两条断言同时红:还原后
REAL EXIT CODE: 0。注意守卫的真实执行者是
tsc -p tsconfig.test.json(packages/types的type-check第三趟)—— 包自身的tsconfig.json把**/*.test.ts排除在外,build不覆盖测试文件,vitest 也不评估@ts-expect-error。验证
pnpm --filter @object-ui/types --filter @object-ui/core type-check→ 退出码 0(types 三趟全过,含tsconfig.test.json)pnpm exec vitest run packages/types packages/core/src/actions packages/app-shell/src/utils --maxWorkers=2→ 55 文件 / 914 用例全绿,退出码 0node scripts/check-changeset-no-major.mjs→✅ No changeset declares a 'major' bump.origin/main基线持平(未新增)origin/main(含 fix(console): un-invert the validation sample's condition and drop 4 silently-stripped keys #3282)之后重跑过Changeset
两个已发布类型移除键,按仓库版本策略标
minor(不是major—— fixed 组任一 major 会把 39 个包推离@objectstack的节奏),正文写清 FROM → TO 与迁移指引:若你声明过validation,它从未生效且会被服务端拒收,删掉即可。🤖 Generated with Claude Code
https://claude.ai/code/session_01NVPjPzmmAJ2Ngtvgg5MSRa
Generated by Claude Code