Skip to content

fix(lint): 收敛 validate-rule-compilability 的 spec 不声明键 ?? 别名读法 (#5096) - #5121

Merged
xuyushun441-sys merged 1 commit into
mainfrom
claude/issue-5096-rule-compilability-alias
Aug 4, 2026
Merged

fix(lint): 收敛 validate-rule-compilability 的 spec 不声明键 ?? 别名读法 (#5096)#5121
xuyushun441-sys merged 1 commit into
mainfrom
claude/issue-5096-rule-compilability-alias

Conversation

@xuyushun441-sys

Copy link
Copy Markdown
Contributor

Fixes #5096

#4984#5009#5017/PR #5046 同族第八处,落在第三个文件。改动限于 packages/lint,未触碰 content/docs/releases/

一条读法,实测核实

validate-rule-compilability.ts:239:

const validations = obj.validations ?? obj.validationRules;

对着 live .shape + safeParse 实测(与 #5046 那条 414 行是同一份证据):

ObjectSchema.shape has `validations`?      true
ObjectSchema.shape has `validationRules`?  false

objects[].validationRules parses? false
  [unrecognized_keys] objects.0: Unrecognized key(s) on this object: `validationRules`. …
    Did you mean `validationRules` → `validations`?

objects[].validations parses? true

validateRuleCompilabilityinput: 'parsed' 注册(authoring-rules.ts:864),canonical 排首位 —— 别名 limb 对任何能解析的 stack 不可达。收敛为 obj.validations

真实元数据:零行为变化

三个 example 的对象面(该规则只读 stack.objects,所以对象 barrel 就是它的完整输入面):

stack objects validation rules AFTER BEFORE diff
app-crm 6 5 0 0 EMPTY(逐字节相同)
app-showcase 21 10 0 0 EMPTY
app-todo 1 2 0 0 EMPTY

合计 28 个对象、17 条验证规则,改动前后 findings 逐字相同。全包 grep validationRules 作对象键:真实元数据里零命中(仅剩 validate-expressions.test.ts#5017 刻意 pin 住 schema 拒绝的那条 fixture,以及 packages/spec/DEVELOPMENT_PLAN.mdFormViewSchema 的同名键 —— 另一个 surface,与本单无关)。

fixture 陷阱:这次是 1 → 0,不是 0 → N

议题提醒 validate-rule-compilability.test.ts:295validationRules: 拼法可能"绿但空转"(runtime-gate 的先例)。先测量再动手,结果是相反的方向:

findings
fixture :295 按原样(别名拼法),改动前 1
fixture :295 按原样,改动后 0
fixture :295 换 canonical,改动前后 1

所以它不是空转 —— 那条 it('reads \validationRules` too …')断言的正是本 PR 要删掉的那条 limb,它真的在跑。因此它被**替换**而不是改拼写:把 key 换成validations 只会留下一条主题(「本规则也读别名」)已不复存在的绿测试。替换成的三条断言分别 pin 住:schema 按名拒绝的原文、canonical 读法产出 1 条 / 别名拼法产出 0 条、以及重建的旧链(OLD_CHAIN`)与新读法只在 schema 已拒绝的那一种输入上不同。

反向验证的方向(#5018 的教训)

canonical 排首位,所以删掉别名 limb 在任何有效 stack 上都不会丢 finding;它只在一条 schema 已经按名拒绝的 stack 上不再判红 —— 而那里 schema 的具名拒绝本来就是更好的诊断。两边都写进了断言,而不是断言一边、假设另一边。

两层结构性 meta-guard(#4992 模式,#5017 形状)

  • declared-key guard —— 从 stack / obj / rule 上读的每个键 ⊆ 对应 surface 自己的 .shape,expected 精确匹配(改名 loop 变量会静默解除扫描),外加 "covers every receiver" 元测试与一条反陈旧断言(被豁免却已不再读任何东西的名字必须删掉 —— 否则豁免表迟早变成装饰)。
  • 计算属性专项 —— flattenRulesrule[branch] 下降,点号扫描看不见 then / otherwise,而那恰是本规则最有意思的读法。所以从源码里把字面量分支表读出来单独核对:漏掉它等于让这两个键坐在本文件所有 guard 之外。
  • reachability guard —— 两个 findings.push 落点都被一条 ObjectStackSchema 完整 parse 通过的 fixture 触达,且是把 parsed 产物喂给规则(它在 compile 路径上拿到的就是这个)。

判据这里用 #5018safeParse 全绿,比 validate-security-posture 只能要求"不报 unrecognized_keys"严一档,理由是这条规则判什么:在 spec 眼里 regex 是任意字符串、schema 是任意 record,所以编译不过的产物依然完全 spec 合法。这条 gate 存在的理由正是 zod 看不见该缺陷,因此它永远不需要一条 zod 会拒绝的 fixture。

顺带把源码扫描的字面量文本剥掉了:本规则的 message / hint 里全是 objects.< name >.validations.< rule >.regex 这类给作者看的配置路径rule-validator.ts 这类文件名,按字面扫会逼着把 objects / validations / validator 塞进"plumbing"豁免 —— 那正是让豁免表失去意义的那种填充。${…} 插值是真读法,保留。

变异测试

变异 转红
M1:别名 limb 加回(obj.validations ?? obj.validationRules) 2 条(reads the canonical key only …every key read off \obj` is declared by ObjectSchema`)
M2:改成纯别名读(obj.validationRules,#5026 那种形状) 11 条

全文件同族普查

全文件只有这一条 ??,别无同族链或纯别名读。其余 receiver(err / import / process)是 JS/Node 内建,已在 NOT_SCHEMA_RECEIVERS 里逐条写明理由。无新增 out-of-scope 发现,未新开 issue。

消费半径

validation-rule-regex-uncompilable / validation-rule-json-schema-uncompilable / validateRuleCompilabilitypackages/lint/src/ 之外零引用。跨包 fixture(#5046 返工的教训)packages/cli/test/authoring-rule-command-parity.test.tspackages/cli/src/utils/collect-docs.test.ts 都已是 canonical validations: 拼法(前者正是 #5046 顺手改的),实跑通过。

验证

pnpm --filter @objectstack/lint test        → 57 files, 1172 tests passed
pnpm --filter @objectstack/lint typecheck   → tsc --noEmit, clean
eslint(两个改动文件)                        → exit 0
packages/cli 上述两个 test 文件              → 2 files, 37 tests passed
真实元数据 before/after diff                 → 空(逐字节)
变异 M1 / M2                                 → 2 / 11 条红

已 merge origin/main(3 个提交,均未触及 packages/lint / packages/spec;pnpm-lock.yaml 动过,已 pnpm install --frozen-lockfile 后重跑全绿)。

⚠️ 一个新 worktree 的假红陷阱记录在此:packages/cli 的 parity 测试 import @objectstack/lint,在 lint 尚未 build 的新 worktree 里报的是 import 失败,与本改动无关 —— pnpm --filter @objectstack/lint build 后即绿。


Generated by Claude Code

#4984#5009#5017/PR #5046 同族第八处,落在第三个文件。
`validate-rule-compilability.ts:239` 读 `obj.validations ?? obj.validationRules`,
而 `ObjectSchema.shape` 只声明 `validations` 且 strict —— `validationRules` 被按名
拒绝("Did you mean `validationRules` → `validations`?",#4001)。该规则以
`input: 'parsed'` 注册,canonical 排首位,别名 limb 对任何能解析的 stack 不可达。

三个 example(crm / showcase / todo,28 个对象、17 条验证规则)上改动前后 findings
逐字相同,两侧均 0 条。代价从来不是漏报而是误导:consumer 里的别名 fallback 等于
向后来的读者和照着写的 AI 宣称 `objects[].validationRules` 是真实 authoring 面。

补两层结构性 meta-guard:declared-key(含 `rule[branch]` 计算属性读法的专项断言)
+ reachability(判据是 `safeParse` 全绿,比 #5046 严一档 —— 编不过的 regex/schema
在 spec 眼里依然完全合法)。原测试 `reads `validationRules` too` 断言的正是被删掉
的 limb(实测产出 1 条 finding,非空转),故替换而非改拼写。变异测试:别名 limb
加回 → 2 条红;改为纯别名读 → 11 条红。

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

vercel Bot commented Aug 4, 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)
objectstack Ignored Ignored Aug 4, 2026 5:25am

Request Review

@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tests tooling labels Aug 4, 2026
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/lint.

3 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/automation/hook-bodies.mdx (via @objectstack/lint)
  • content/docs/permissions/authorization.mdx (via @objectstack/lint)
  • content/docs/releases/v17.mdx (via @objectstack/lint)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@xuyushun441-sys
xuyushun441-sys marked this pull request as ready for review August 4, 2026 05:27
@xuyushun441-sys
xuyushun441-sys added this pull request to the merge queue Aug 4, 2026
Merged via the queue into main with commit f238970 Aug 4, 2026
24 checks passed
@xuyushun441-sys
xuyushun441-sys deleted the claude/issue-5096-rule-compilability-alias branch August 4, 2026 05:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

validate-rule-compilability.ts:239 也有同形的 obj.validations ?? obj.validationRules 别名读法(#5017 全包 grep 的第八处,第三个文件)

2 participants