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.

@os-zhuang
os-zhuang marked this pull request as ready for review August 5, 2026 01:24
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 5, 2026
Merged via the queue into main with commit b4ad984 Aug 5, 2026
25 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-4990-camelcase-suggestions branch August 5, 2026 01:36
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Aug 6, 2026
…in 滞后、死代码删除复核 (objectstack-ai#5513) (objectstack-ai#5645)

2026-08-05 跑完一整条 filter 缺陷链(objectstack-ai#5363 / objectstack-ai#5366 / objectstack-ai#5368 / objectstack-ai#5375 / objectstack-ai#5431 / objectstack-ai#5445,
cloud#1117)后回看,六处在那一轮真实咬过人或真实救过场的规程,SKILL 里没有对应条目。
六条各落在 issue 指定的节内,**纯增补**:111 行插入、0 行删除,既有条目(objectstack-ai#5501 的接力
模式、objectstack-ai#5522 的座位模型、objectstack-ai#5630 的 assertEngineDeleteDispatch 条款)一字未动。

落点与要点:

1. **Multi-repo,rule 2 之后**「pin 滞后」——`Blocked-by:` 只保证上游已合并,姊妹仓还有
   第二个读数:本仓 pin 是否覆盖那个 commit。cloud#1116 的裁决落于 framework objectstack-ai#5368
   (`9c5abf4e9`),而 cloud 的 `.objectstack-sha` 未覆盖它,于是 `TursoDriver` 有一个
   方向反了的分叉窗口(fail-closed 一侧先到)。规程:派发前核祖先关系;未覆盖则 dev 在
   PR 正文留档窗口与方向,⛔ pin bump 不做 rider。
2. **step 3** 末「阻塞解除后重新定价」—— 前一单合入会改变后一单的成本模型,方向不止一个
   (本轮变便宜、没变、成本估计过期各有实例)。两个动作配对:派发前一单时带必答项
   「你的改动是否让 #X 变简单 / 变难 / 不必要 / 无影响」,派发被延后那单前用该回答重读
   其选项与成本估计。
3. **step 5** 派发令「多面组件的测试落点」—— 同一契约 ≥2 实现面时,新用例进共享一致性
   覆盖而非独立文件(原话照录)。附 objectstack-ai#5375 / objectstack-ai#5431 / objectstack-ai#5445 三条正交轴共用一条不变量。
4. **step 7 清单**「收益穿过它必经的那道边界之后还在吗」—— 判据是价值主张是否依赖下游
   如实转发;实例即 objectstack-ai#5423(4xx 直通曾整条替换 ≥500 字符正文,`code` 到了正文没到)。
5. **step 7 清单**「死代码删除的复核」——「这是死代码」是断言而非能从 diff 读出的事实,
   PM 在 origin/main 独立核一次引用面再 ACCEPT(查法用 Operational notes 6:notes 6 说
   怎么查不假阴性,本条说什么时候必须查)。
6. **step 8** 升级门槛之后「带前提的裁决」—— 分歧关键是可被代码证伪的事实时,第三档 =
   裁决 + 前提验证要求 + 「前提不成立报 fork,不许硬做也不许悄悄改选」禁令,三件缺一
   不可;缺第 3 条即退化为无人裁决且无读数显示。

实施时两处核实结果与 issue 正文不同,成文按核实后的事实写:

- issue 的附带论断「没有任何闸门在量这个 pin 滞后」**不成立** —— cloud 的
  `scripts/check-pin-staleness.sh`(test.yml 以 `continue-on-error` 跑)每次 CI 都报两个
  pin 各落后 main 多少 commit,advisory 是**有意设计**(`--max-behind` 需显式传)。它答
  的是「落后多少」,不是「是否覆盖我这条裁决 commit」;成文因此指向该脚本,并只把后一个
  问题留给派发前的祖先判断。据此**未**另立「无闸门」的发现单。
- 第 4 条的 rest-server 缺陷本身已由 objectstack-ai#5423 按「截断而非替换」修掉,成文改用过去时并注明,
  以免后来的读者去找一个已不存在的活 bug;该条要补的是**复核清单的缺口**,与代码是否已修
  无关。

第 1 / 3 条按 issue「未验证的部分」的克制写入适用判据(前后单共用同一契约或数据表示;
组件对同一契约有 ≥2 实现面),形态迥异的批次(纯 UI、纯文档)明确不强加。

验证:`node scripts/check-nul-bytes.mjs --self-test` + 全仓扫描绿(48 断言 / 5537 文件);
改动文件自扫 `grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]'` 零命中,并用邻近词反查证伪
「扫描器坏了」;`check:docs-audit-scope` 绿;markdown 结构核对(强调标记成对、代码围栏
16 个偶数、嵌套围栏缩进对齐)。

Claude-Session: https://claude.ai/code/session_01GX3sL71LFq8m2usg6VqTSE

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: os-zhuang <hr@objectstack.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

2 participants