Skip to content

test(spec): 门测量 BFS 只留一份实现,并给仪器本身补上对照 (#5056) - #5833

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-5056-bfs-overlap-ratio
Aug 6, 2026
Merged

test(spec): 门测量 BFS 只留一份实现,并给仪器本身补上对照 (#5056)#5833
baozhoutao merged 1 commit into
mainfrom
claude/issue-5056-bfs-overlap-ratio

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes #5056

前提核对(先说结论:issue 仍然成立,但清单跟 issue 写的不一样)

Issue 是 08-04 写的,#4001 战役跑得快。对着 origin/main(28f2e04)重新点了一遍拷贝清单:

位置 状态
packages/spec/src/ui/door-reachability.testkit.ts 已存在,已是重合度(≥ 0.5)版本,已带 extraRoots 翻转支持和 cloneOverlap
packages/spec/src/ui/widget.test.ts 已 import 该工具,三组对照齐
packages/spec/src/ui/i18n.test.ts 已 import 该工具,三组对照齐
packages/spec/src/ui/chart.test.ts 仍带内联拷贝,而且是缺陷版 —— 停在「任意单属性命中即判可达」

也就是说 issue 建议 1 做了一半:共享工具已经抽出来了,但 issue 点名的引入处 chart.test.ts 自己没迁移,留下的那份恰好是有 bug 的那份。全仓再无第四份(visitedDefs / childrenOf 全仓搜过,另一处命中是无关的 packages/lint/src/validate-responsive-styles.ts)。

所以本 PR 的实际工作是「迁移最后一份拷贝」,不是「新建共享工具」。

改动

  1. chart.test.ts 删掉 100 行内联 BFS,改 import measureDoors()。全仓只剩一份实现。
  2. chart.test.ts 补齐缺的对照。它原本只有正对照,现在补上负对照和合成载体翻转对照(issue 建议 2 要求的三者)。
  3. 新增 door-reachability.testkit.test.ts —— 仪器自己的对照。三个消费者各自为自己的形状带对照,却没人为走图器带,于是它的两条腿都可能在一片绿里烂掉。

两处必须说明的实测修正

A. issue body 里那行 .describe() 复现是错的

Issue 写:

defOf(z.string().describe('x')) === defOf(z.string())  →  true

本 build 实测为 false。 两次独立的 z.string() 调用建两个独立的 def,压根没有东西可共享。真正成立的是:

const base = z.string();
defOf(base.describe('x')) === defOf(base)              →  true   ← 共享跟着「接收者实例」走
defOf(base.describe('x')) === defOf(base.describe('y'))→  true
defOf(SnakeCaseIdentifierSchema.describe('a'))
  === defOf(SnakeCaseIdentifierSchema.describe('b'))   →  true   ← 真正咬到本仓的那条
defOf(base.optional()) / .extend() / .strip()          →  false  ← 与 issue 一致

机制和结论都对,一行复现写反了。共享跟着接收者实例走,不跟着形状走 —— 而这恰恰是 bug 咬本仓的原因:spec 把几十个形状都灌进少数几个共享叶子实例,于是每个 SnakeCaseIdentifierSchema.describe(…) 彼此 def-同一。

按派发要求把这条事实钉成了测试,但钉的是修正后的说法 —— 照 issue 原文钉会钉住一个假命题。zod 升级改了 clone() 语义,这组会响。

B. bridge 那条腿,全仓此前没有任何测试走到

Issue 和派发都建议拿 ChartConfigSchemaReportChartSchema.extend() 当 derived-clone 正对照。实测:

ChartConfigSchema   →  direct   (重合度 1.0000)
ReportChartSchema   →  direct

它本来就在图里,判定是 direct 而不是 derived-clone。三个消费者断言可达的形状全部direct。也就是说:把 bridge 收紧到「永不触发」,三个文件依然全绿。

所以正对照另找了真派生场景,并钉进测试:

PageSchema.extend({ osDoorProbeExtra })  →  derived-clone  (重合度 0.9600)
ObjectListViewSchema.strip()             →  derived-clone  (重合度 1.0000)

阈值:保持 0.5,对照没有推翻它

形状 重合度 判定
ObjectListViewSchema.strip()(真派生) 1.0000 derived-clone
PageSchema.extend(…)(真派生) 0.9600 derived-clone
WidgetManifestSchema(批 16 假可达) 0.1053(2/19) unreachable ✅
z.object({ name: z.string(), label: z.string() })(issue 的负对照) 0.0000 unreachable ✅

两端离 0.5 都很远。

反向验证(方向是事先定的:标准红向)

DERIVED_CLONE_MIN_OVERLAP 临时改回「任意单属性命中」,预测 → 实测:

文件 预测 实测
door-reachability.testkit.test.ts 红(WidgetManifestSchema) ✅ 红:expected 'derived-clone' to be 'unreachable'
widget.test.ts 红(WidgetManifestSchema) ✅ 红:WidgetManifestSchema must have no door
chart.test.ts 绿(不变) ✅ 绿
i18n.test.ts 绿 ✅ 绿
负对照 / KNOWN BOUND 绿 ✅ 绿

chart.test.ts 预测为绿并且实测为绿,这条值得单独讲:迁移它并不改变它自己的任何判定(它那几个形状重合度是 0.0000,老 bridge 在那儿根本不触发)。迁移的价值是「杀掉真相的第二份拷贝」,不是「改一个答案」—— 那份拷贝的危害在于它是批 17-22 复制粘贴时会拿到的那一份。

一个残余边界:钉住了,没有顺手改仪器

重合度的分母是候选形状自己的键数,于是一个键很少、且每个键都是共享叶子的形状恒为 1.0:

z.object({
  name:  SnakeCaseIdentifierSchema.describe('Machine name'),
  label: I18nLabelSchema.describe('Display label'),
})

实测重合度 1.0000 → 判 derived-clone → 可达。任何阈值都排不掉:真 .strip() 也是 1.0000 且必须保持可达,两者在「重合度」这一个维度上不可分。

今天代价为零(真实 no door 形状都远低于阈值),而且修它需要另一个判据(例如要求共享属性里至少有一个是结构性节点),那是对仪器契约的改动 —— 不该塞进一个测试层 PR。所以:作为具名 pin 钉住(用例名以 KNOWN BOUND 开头),并作为观察类发现另开 #5828,让后续批次遇到小形状时知道去读那条 pin。

验证

pnpm --filter @objectstack/spec test        →  320 files / 8176 tests passed   (基线 8164,本 PR +12)
pnpm --filter @objectstack/spec typecheck   →  tsc --noEmit 通过;check:test-typecheck OK
pnpm --filter @objectstack/spec check:generated → 10/10 artifacts up to date
npx eslint (三个改动文件)                    →  无输出
node scripts/check-nul-bytes.mjs            →  OK (5687 files)

范围

严格测试层:仅 chart.test.tsdoor-reachability.testkit.ts、新增 door-reachability.testkit.test.ts。未碰任何 *.zod.ts、任何 schema 语义、任何 scripts/。逐文件核对过 git status,authorable-surface.base.json 不在 diff 中。

Changeset

skip-changeset —— 纯测试层改动,不发布任何东西,对使用者无可见变化(*.testkit.ts 不从 ui/index.ts 导出,也不是 tsup entry,check:api-surface 已验证导出面无变化)。

🤖 Generated with Claude Code

https://claude.ai/code/session_01559M8FVm6W6vDLABL3jvdW


Generated by Claude Code

`chart.test.ts` 仍带着 `reachableFromMetadataRoots()` 的内联拷贝,而且是
**缺陷版**:共享测试工具 `door-reachability.testkit.ts` 早已换成整形状重合度
(≥ 0.5)的 derived-clone bridge,这份拷贝还停在「任意单属性命中即判可达」。
删掉拷贝,改为 import `measureDoors()`,全仓只剩一份实现。

同时补齐两处此前没人拥有的东西:

1. `chart.test.ts` 的门测量原本只有正对照,现在补上负对照与合成载体翻转对照。
2. 新增 `door-reachability.testkit.test.ts` —— 仪器自己的对照。三个消费者各自
   为自己的形状带对照,却没人为走图器带,于是它的两条腿都可能在一片绿里烂掉:
   - `.describe()` 共享 def 这条**前提**被钉住(zod 升级改了 clone 语义要响);
   - derived-clone bridge 的**正对照**被钉住 —— 实测所有消费者断言可达的形状
     都是 `direct`,这条腿在全仓没有任何测试走到,把它收紧到「永不触发」全绿。

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

vercel Bot commented Aug 6, 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 6, 2026 7:20am

Request Review

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

110 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/apps.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/field-grouping-and-order.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.

@baozhoutao baozhoutao added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 6, 2026 — with Claude
@baozhoutao
baozhoutao marked this pull request as ready for review August 6, 2026 07:34
@baozhoutao
baozhoutao added this pull request to the merge queue Aug 6, 2026
Merged via the queue into main with commit 9a15446 Aug 6, 2026
27 of 28 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-5056-bfs-overlap-ratio branch August 6, 2026 07:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

protocol:ui size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

#4001 战役的门测量 BFS 会误报「可达」:zod .describe() 共享 def 对象,任意单属性 bridge 把无关形状连起来

2 participants