Skip to content

fix(spec): 未知键建议对两侧折叠大小写 —— camelCase 键不再白扣编辑距离 (#4990) - #5363

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-4990-camelcase-suggestions
Aug 5, 2026
Merged

fix(spec): 未知键建议对两侧折叠大小写 —— camelCase 键不再白扣编辑距离 (#4990)#5363
os-zhuang merged 1 commit into
mainfrom
claude/issue-4990-camelcase-suggestions

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #4990

前提复核

issue 的实测表来自 fe83042,main 已走远。在 553a47fda 上重跑,四行逐字复现:

输入 预算 findClosestMatches
hideOn 2 []
hiddenon 2 ["hiddenOn"]
hiddenOnn 3 ["hiddenOn"]
maxLenght 3 ["maxLength"]

第二行仍是完整刻画:把键写成全小写的作者,拿到的建议比写对大小写、只错一两个字母的作者更好。

改动

打分对两侧做同样归一化(大小写 + -/空格 → _),回显仍用候选原始拼写。另有两处附带修正,都是同一处过滤器的连带:

  1. 折叠后距离为 0 的候选不再被丢掉。distance > 0 过滤的本意是「别把作者写过的字符串原样回显」,但折叠后「只差大小写」也变成距离 0 —— 而那恰恰是兜底能给出的最有把握的一条建议(hiddenonhiddenOn)。过滤改为只排除作者逐字写过的那个字符串。

    这里顺带修掉一个 issue 未记录的同源缺陷,是反向验证时才浮出来的:旧实现在 maxLength 这种键上会把作者写对的键原样回显成「你是不是想写 maxLength」,因为输入被小写化后与候选距离是 1 而不是 0,过滤器根本没拦住。

  2. 折叠后打平时,以作者自己的大小写作次级排序。 yxAis 折叠后同距 yAxisxAxis(均为 2),作者写下的大写 A 是仅存的证据。严格次级,不可能复活「大小写压过真实编辑」的原缺陷。

影响面实测

全部 325 组真实候选集(从 src/index 递归采集每个 ZodObject 的 shape)上,按单字符删除/相邻换位/双字符删除生成 16734 个 camelCase 笔误探针,逐个对比新旧:

数量
结果不变 16374
新增建议(原本没有) 329(正确 328 / 错误 1)
失去建议 0
改变选中项 31(更准 30 / 变差 1)

两处代价如实记录,均为折叠后的真实平局、非排序质量退化:

  • 变差 1 例:yxAis 旧给 yAxis(对)、新给 xAxis(错)。折叠距离与次级 cased 距离双双打平,由 shape 声明顺序决定。再加启发式(如「首字母优先」)缺乏原则依据,不做。
  • 新增里错误 1 例:mxaRowsminRows(应为 maxRows),同为双双平局。注意这不是退化:改动前该输入没有任何建议

测试

packages/spec 全套 308 files / 7903 tests 全绿(改动前 7895,新增 8 条);pnpm typecheck 干净。

按消费半径外扩(PR #5046 的教训:规则在哪里被调用,fixture 就可能在哪里):findClosestMatches 还被 packages/lint 三个 write-validator 调用,packages/services/service-automation 断言 spec 产出的报错文案。两者分别跑过:lint 57 files / 1194 tests 全绿,service-automation 相关三个文件 149 tests 全绿

自证测试(issue 称之为本单实质)

「同一个键的全小写形式不应比正确大小写形式得到更好的建议」。这条的编码方式改过一次,值得记录:

issue 字面的写法 —— 比较 suggest(T)suggest(T.toLowerCase()) —— 对旧代码是空转的。旧实现第一步就把输入小写化,两次调用坍缩成同一次,462 个探针上「全部通过」,而缺陷完好无损。

真正的不对称不在输入的两种拼法之间,而在声明键的两种拼法之间:同一个笔误,对着 hiddenOn 判一个结果,对着 hiddenon 判另一个,因为只有候选保留了大写、每个大写都向作者收费。这才是标题里的「对 camelCase 键系统性偏弱」。改后的测试锁的是这个:声明键的大小写不得改变对一个笔误的判决。同一语料在旧实现上破坏 462 例中的 55 例,且双向破坏(bordeRradius 对着驼峰 borderRadius 能解析、对着扁平 borderradius 反而不能 —— 大写恰好帮了忙)。大小写两个方向都不该说了算。

反向验证

先定方向再跑。第一次预测部分落空,如实记录:预测「表格测试 + 不变量测试转红,其余为回归护栏」,实际把源文件换回 origin/main 版本后 4 条转红,多出来的一条是上文第 1 点里那个未记录的「原样回显」缺陷 —— 它是被测试意外揭露的,不是被预期的。修复后 26/26 全绿。

关于两处「同病」排查

  • data/object.zod.tssuggestKey(issue 注 1):不同病。 它本来就对两侧都做小写化(editDistance(unknown.toLowerCase(), key.toLowerCase())),声明键的大写从未向作者收费。已补一条测试锁定该性质,防止两个 suggester 日后分叉。
  • ui/responsive.zod.ts 批 13 的逐例 hideOn: 'hiddenOn' alias:已退役。 对真实完整键集(breakpoint, hiddenOn, columns, order)复测,hideOn 现在靠距离本身即可解析。该 alias 的注释记录了实测值,按要求未删除而是转为断言存进 responsive.test.ts,并补了 hiddenon 一例 —— 即当年让旧行为站不住脚的那个对照。注释里那句「fallback 仍够不着」已成假命题,留着比删掉更有害。hidden: 'hiddenOn' 保留:它是 objectui 解析结果的拼写,是另一个,任何编辑距离都够不着。

未动的东西

packages/spec/authorable-surface.base.json 会被 pnpm buildgen:schema 步骤重写(改 baseRev 并净删 110 个 key)。这与本单无关,提交前已 git checkout -- 还原,不在本 PR 内。该行为已有 #5358 在跟,我在那边补了本次的复现数据而没有另开单。


Generated by Claude Code

`findClosestMatches()` 只把输入小写化、候选不做同样处理,于是候选键里每一个
大写字母都要额外付一次编辑距离。叠加 `strictUnknownKeyError` 长度相对的预算
(短键为 2),短 camelCase 键上一个普通笔误就够不着建议:`hideOn` 对 `hiddenOn`
真实距离 2、加大写罚分后 3、超预算返回空;而同一个词写成全小写的 `hiddenon`
反而拿得到建议。

现在打分对两侧做同样的归一化,回显仍用候选原始拼写。另两处附带修正:

- 折叠后距离为 0 的候选(只差大小写)不再被 `distance > 0` 丢掉 —— 那是最有
  把握的一条建议。过滤改为只排除作者逐字写过的字符串,顺带修掉一个未记录的
  同源缺陷:旧实现会把作者写对的键原样回显成「你是不是想写」。
- 折叠后打平时以作者自己的大小写作次级排序(`yxAis` 同距 `yAxis`/`xAxis`)。

325 组真实候选集实测:329 例从「没有建议」变为有建议(328 例正确),0 例失去
建议,31 例改变选中项(30 例更准)。批 13 的逐例 `hideOn` alias 随之退役,实测
值改由 responsive.test.ts 断言保存。`data/object.zod.ts` 的 `suggestKey` 经核查
不同病(本就对两侧小写化),补测试锁定防止分叉。

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

vercel Bot commented Aug 5, 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 5, 2026 1:08am

Request Review

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via @objectstack/spec)
  • content/docs/automation/connectors.mdx (via @objectstack/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via @objectstack/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/tenancy-modes.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/spec)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/http-protocol.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/spec)
  • content/docs/releases/v17.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

2 participants