feat(core): 混合键响亮化 — 说清谁赢了 (#3104 PR3,收官) - #3124
Merged
Merged
Conversation
Closes the battle opened in #3104. PR1 (#3119) put the canonicalizing fold at ingestion; PR2 (#3122) converged all 22 read sites onto columnIdentity(). This is the audible half. A column carrying two identity keys that DISAGREE now logs a one-time dev-mode warning naming which key won and what to change. The fold making the two halves agree is what stops the bug, but silently rewriting `name` to match `field` also hides that the producer is emitting a contradiction. The renderer recovering is not the same as the metadata being right, so the recovery says so. Deliberately narrow: - Only contradictions. `{name:'stage'}` is legacy, not conflicting — stamped without noise. - Warn once per (identity, conflicting spelling). Columns are re-normalized on every render, and a warning that floods the console is one that gets muted. Keyed by the pair rather than the identity alone, so a column carrying two different stale spellings reports both. - Silent under NODE_ENV=production, and the fold still runs there. No lint rule, and that is a measured decision. #3104 asked for no-restricted-syntax on `.field ?? .name` to be evaluated on its false-positive rate first. With the family at zero, all 12 remaining scanner hits are legitimate — a syntactic rule cannot tell a two-layer join from a dual read, because the distinction is what the keys MEAN in that layer, not how the expression is spelled. Adopting it would mean 12 inline disables on correct code, which trains the next author to reach for the disable. The ratchet carries a verdict and a why per site instead. The evaluation is written into its header. Ledger item resolved with no change needed: #3104 flagged ListColumn for disposition under objectstack#4115 (spec-named symbols must be imports, not declarations). ListColumnSchema is already a by-reference re-export of @objectstack/spec/ui, and spec-subschema-parity.test.ts already pins it by reference identity — the only check that distinguishes a re-export from a faithful fork. Already compliant. Verified: M5 (drop the warn call) turns 4 of the new tests red. vitest core+list+grid+detail+tree+view+types -> 188 files / 2663 tests green vitest app-shell -> 250 files / 2089 tests green turbo type-check, eslint -> green Refs: objectstack#4115, #3090 (playbook), #3119 (PR1), #3122 (PR2) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C2pdPmf2yZSd4wFDs1NHY5
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
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.
#3104 PR3(闸门 + 响亮化),本战役收官。接 #3119(PR1)、#3122(PR2),均已合。
PR1 把归一挂在 ingestion,PR2 把 22 处读点收敛到
columnIdentity()。这一件是能听见的那一半。响亮化:renderer 恢复了,不等于元数据是对的
fold 让一列的两半达成一致 —— 这是止血的部分。但悄悄地把
name改写成field的值,同时也把「生产方正在发出自相矛盾的元数据」这件事藏了起来。所以这次恢复自己会开口:作者不用读源码就能照着改:谁赢、谁输、两边的值、以及下一步动什么。
刻意收窄的三点:
{ name: 'stage' }是遗留,不是矛盾 —— 照常 stamp,不出声NODE_ENV=production下静默,且 fold 照常运行resetColumnIdentityWarnings()导出给测试用(沿用actionKeys.ts:213已有的 warn-once 先例)。不加 lint 规则 —— 这是量出来的,不是拍的
issue 要求「评估
no-restricted-syntax匹配.field ?? .name的误报率后决定是否叠 lint」。评估做了,结论很干脆:家族归零之后,扫描器剩下的 12 处命中,12 处都是合法的。 误报率 100%,真阳性 0。
因为区别在于这两个键在那一层里意味着什么,而不在于表达式怎么写 —— 语法规则看不见这个。叠上去就是在 12 处正确代码上写 inline disable,而那恰好训练下一个作者去伸手拿 disable,正是让真的漏网的那个反射。
ratchet 能做 lint 做不到的事:每个点位带
verdict和why,新命中必须被分流而不是被消音。「家族 = 0」这条断言,本身就等于「今天 lint 会报的每一处都是误报」。评估过程写进了 ratchet 的 header。台账条目:查证后无需改动
#3104 要求「台账相关条目处置(ListColumn 等),同步 objectstack#4115」。查了 —— objectstack#4115 是「与 spec 同名的符号必须是 import 而不是本地声明」这条规则。
ListColumn的实况:而且
spec-subschema-parity.test.ts:154已经有toBe(SpecListColumnSchema)的 identity pin —— 也就是 #4115 特别强调的那条(「忠实的副本能通过所有值比较,引用同一性是唯一能区分 re-export 和 fork 的检查」)。本来就合规,无需改动。 记在这里而不是去 #4115 下面刷一条评论。
mutation-test
warnOnConflictingIdentity调用(M1–M4 分别在 PR1 / PR2 验过,闸门未变。)
验证
我没做 fold 的「镜像 → 删除」
我在 PR1 描述里把它列成了 PR3 的活,但 issue 的 PR3 清单里没有这一项,而且我当时就写清了它的风险:
columns条目会越过包边界进入宿主应用和第三方 renderer,把name从它们脚下抽掉是我们没有清单的破坏性变更。PR2 收敛的是仓内消费者,这个前提只覆盖仓内。真要删,需要单独一件:major/minor + 迁移说明 + 宿主侧的排查。现在这个状态是完整且自洽的 —— 缺陷已闭合(家族归零 + ratchet 守住),矛盾元数据会自己喊出来。
关联:#3119(PR1)、#3122(PR2)、objectstack#4115、#3090(判别法)、#2598(chokepoint 先例)。
Generated by Claude Code