fix(cli): lint 的 action 去重改用复合注册键,不再按裸 name 误判 (#5510) - #5531
Merged
Conversation
…e name (#5510) `naming/namespace-prefix` deduplicated every `PREFIXED_TYPES` entry on `items[i].name` alone. For actions that is not the key they occupy: the engine registers under `objectName:name` (`ObjectQLPlugin.actionObjectKey`, falling back to the canonical object-less key `global`, #3913), so one package declaring one `log_call` per object holds one distinct key per object and nothing shadows anything. The bare-name dedup flagged that shape as an intra-package duplicate, with noise growing linearly in the object count — 12 fixed warnings per `objectstack lint` run on HotCRM (5 objects x 3 activity actions) — and its "rename one" prescription would have broken the shared i18n keys the #592 shape depends on. Actions now dedup on `objectName:name`; the other six types keep bare-name dedup and their message text byte-for-byte. Only `objectName` is read: `ActionSchema` rejects `object`/`entity` outright with a rename prescription, so a `??` chain here would only fossilize a spelling the contract already refuses (Prime Directive #12). Genuine shadowing still warns — same `objectName`, two object-less actions, and an action on an object literally named `global` meeting an object-less one (which is why the fallback is the real `global` literal rather than an inert sentinel). The action remedy now offers separating by `objectName` before renaming. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016FNvXhtSdnEGEfLEsMmvxh
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 21 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
baozhoutao
marked this pull request as ready for review
August 5, 2026 15:47
baozhoutao
enabled auto-merge
August 5, 2026 15:47
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 free
to 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 #5510
问题
naming/namespace-prefix规则对PREFIXED_TYPES里每一类都只按items[i].name去重。对 action 而言这不是它实际占用的键:引擎按objectName:name注册(ObjectQLPlugin.actionObjectKey,对象缺省时回落到 #3913 确立的规范无对象键global),所以一个包里「每个对象各带一个log_call」占的是互不相同的 N 个键,彼此不遮蔽。裸名去重把这个合法形状判成了包内重复,噪音随对象数线性增长 —— HotCRM(5 对象 x 3 个活动 action)每次
objectstack lint固定 12 条误报;而它给出的「rename one」处方,照做反而会破坏该形状赖以成立的 #592 共享 i18n 键设计。改动
PREFIXED_TYPES增加可选的registryKey,把去重问题从「裸名是否相同」改成「是否落到同一个注册键上」。只有actions声明了它,其余六类保持裸名去重,消息文案逐字不变(已用整句断言钉住)。两个刻意的取舍:
objectName,不做?? item.object兼容链。ActionSchema的严格形状会直接拒绝object/entity并给出改名到objectName的处方,这两种拼写根本到不了一个 spec-valid 的配置;在消费端加兼容链只会把契约已经拒绝的拼写固化下来(Prime Directive Add comprehensive test suite for Zod schema validation #12)。这一点由 schema 自身的判决钉住(断言该键报unrecognized_keys),而不是靠注释声明。global,而不是''之类的惰性哨兵。 issue 落点分析建议${objectName ?? ''};这里用了引擎真正注册的那个字面量,因为global是合法的对象名拼写 —— 一个声明在名字就叫global的对象上的 action,与一个无对象 action 同名时,在引擎的精确字符串 map 里是真冲突,必须报出来。''会漏掉这一对。PM 口径里「两个 objectless 同名仍告警」的不变量两种写法都满足,global额外多守住了这一格。真遮蔽依旧照报:同
objectName同名、两个 objectless 同名、以及上面那个global组合。action 的处方也做了校准 —— 先建议「用不同的objectName区分」,再退到改名(改名建议与fix字段保留)。反向验证(方向先判后跑,两次都与预判逐条吻合)
objectName那例(1 到 2,因为该 fixture 中间刻意插了一行不冲突的crm_contact)。实跑2 failed | 12 passed,红的正是这两条。注意global那例在 A 下保持绿,是因为两者裸名本就相同 —— 它绿得没有信息量,所以单靠 A 钉不住哨兵的选择。''—— 预判恰好 1 红,即global那例(1 到 0)。实跑1 failed | 13 passed,红的正是它。这也是为什么两次反向验证都做:A 证明复合键在起作用,B 证明哨兵取值在起作用,任何一次单独都留有一条绿得没有理由的用例。
测试
packages/cli全量:81 passed / 801 passed;pnpm --filter @objectstack/cli typecheck通过。新增用例覆盖 HotCRM 全尺寸形状、三类真冲突、action 文案、其余类型不受影响,以及走真实normalizeStackInput到lintConfig这条命令实际路径(钉住归一化既不搬走顶层actions数组,也不丢objectName)。changeset:
@objectstack/clipatch。🤖 Generated with Claude Code
https://claude.ai/code/session_016FNvXhtSdnEGEfLEsMmvxh
Generated by Claude Code