Skip to content

fix(objectql): 悬空引用巡检为「预算没轮到」的对象建诚实桶 unscannedObjects (#5718) - #5760

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-5718-audit-unscanned-bucket
Aug 6, 2026
Merged

fix(objectql): 悬空引用巡检为「预算没轮到」的对象建诚实桶 unscannedObjects (#5718)#5760
os-zhuang merged 1 commit into
mainfrom
claude/issue-5718-audit-unscanned-bucket

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes#5718

问题

packages/objectql/src/integrity/dangling-reference-audit.ts 主循环在
report.scanned >= maxRows(默认 DEFAULT_MAX_ROWS = 5_000)时直接 break,
其后所有对象既没被读、也没进任何桶。它们的 dangling: [] 与「真的读过且
干净」的表在报告里完全无法区分 —— 而这正是本文件所有诚实桶存在的理由:

已有桶覆盖的不完整路径
truncatedObjects某张表内部的行预算用完,只看了样本
unreadableObjects试着读了,数据源不给
aborted(#4747)运行被叫停

「整张表没轮到」是唯一没有对应桶的一条 —— 模块头注释原话:Together they are
what stops 0 dangling from ever being read as everything is fine

改动

新增 unscannedObjects: string[],在主循环每一处提前退出前把未轮到的对象名记下:

#5719 的关系:#4743 不是成因,只是把这条路径抬到「容易踩到」—— 溯源族入巡检
后几乎每个对象都有可审计字段,同样的预算摊到多得多的表上,提前 break 的概率显著
上升。三档 prioritise 决定拿到有限预算,本桶报告谁一点也没拿到,两件
事独立:扫描顺序无法让有界运行变完整,只有说出漏了什么才能让它诚实。

反向验证(方向在跑之前就预测好了)

预测:删掉预算处的 fileUnscanned(i) → 依赖预算停的用例转红;不依赖它的用例
(显式 []、被叫停、枚举前叫停)保持绿。实测完全一致:

× the objects behind the stop are listed, in the order the run would have read them
× an object with NO reference field is not listed — its silence was already proven
× an object the CALLER excluded is not listed — that narrowing is not the audit missing it
× a zero budget reads nothing and names EVERYTHING — the whole report is "not looked at"
× a truncated run and an unscanned run are DIFFERENT reports
× rides along in the warning payload, and does NOT raise the line on its own
AssertionError: expected [] to deeply equal [ 'showcase_task', 'showcase_note' ]
Tests 6 failed | 3 passed | 29 skipped (38)

第二处限位单独验:只删 abort 分支的 fileUnscanned(i)
a called-off run names what it never reached too 单独转红
(expected [] to deeply equal [ 'showcase_task', 'showcase_note' ]),其余保持绿。

注意这里断言的选择是关键:只断言 danglingscanned 的测试在旧的静默
break 下同样是绿的
—— 发现数一个都不变。名字本身才是被测行为。

测试证据

pnpm --filter @objectstack/objectql exec vitest run \
src/integrity/dangling-reference-audit.test.ts \
src/engine-dangling-reference-audit.test.ts --maxWorkers=2
Test Files 2 passed (2)
Tests 45 passed (45)
# 新增 [#5718] 块 10 例全绿
Tests 10 passed | 29 skipped (39)
pnpm --filter @objectstack/objectql test # 全包
Test Files 123 passed (123)
Tests 2031 passed (2031)
pnpm --filter @objectstack/objectql typecheck # tsc --noEmit,无输出即通过

门禁:

check:nul-bytes OK (5641 tracked text files, no raw control bytes)
check:query-options-erasure ratchet holds, baseline verified against 5e3c83b
check:engine-double-contract OK — 27 pinned, 65 DEBT, 1 exempt
check:type-check-coverage OK — 63/78 packages
check:durability-log-level OK — 24 seams, all loud
eslint (两个改动文件, --no-inline-config) clean

改动面

  • packages/objectql/src/integrity/dangling-reference-audit.ts(主循环 + 报告类型)
  • packages/objectql/src/integrity/dangling-reference-audit.test.ts(新增 [#5718] 块 10 例)
  • .changeset/dangling-audit-unscanned-bucket.md(patch)

未碰 engine.ts / lifecycle-service.ts(有其他 dev 在飞),未碰
content/docs/releases/


Generated by Claude Code

)
悬空引用巡检的主循环在 `report.scanned >= maxRows` 时直接 break,其后所有
对象既没被读、也没进任何桶 —— 它们的 `dangling: []` 与「真的读过且干净」的
表完全无法区分,而这正是本文件所有诚实桶存在的理由(`truncatedObjects` /
`unreadableObjects` / `aborted` 已分别覆盖表内预算、数据源拒答、运行被叫停,
唯独「整张表没轮到」没有对应桶)。
新增 `unscannedObjects: string[]`:按扫描顺序(prioritise 三档)记下未轮到
的对象名,可直接回喂 `options.objects` 续跑。类型上 optional(同 `aborted`
与 #4743 的 `provenance`,手写 report 字面量仍可编译),运行时本模块产出的
每一份报告都显式赋值 —— 走到底就是 `[]`,永不缺席,`undefined` 只可能表示
「旧报告形状」。
不入桶的两类:无任何引用字段的对象(prioritise 已在循环前丢弃,其沉默是被
证明的而非假设的),以及调用方用 `options.objects` 排除的对象(那是调用方
自己的收窄,不是巡检漏看)。与 `truncatedObjects` 同族:不单独抬告警(大库
每次健康运行都会耗尽预算,常亮告警就是 #4747 的坏警报),只在告警载荷里逐项
带出,并始终留在返回的报告里。
被叫停的运行同样记录未轮到的对象,否则 `unscannedObjects: []` 与
`aborted: true` 并排会读成「早停了但什么都没漏」;唯一边界是在枚举注册表之前
就被叫停 —— 那时没有名单可给,由 `aborted` 承担,已用测试钉住。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V7WetGmnfoXNn8cLieKKmx
@vercel

vercelBot commented Aug 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 6, 2026 4:05am

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/concepts/metadata-lifecycle.mdx(via @objectstack/objectql)
  • content/docs/data-modeling/formulas.mdx(via packages/objectql)
  • content/docs/deployment/migration-from-objectql.mdx(via @objectstack/objectql)
  • content/docs/deployment/vercel.mdx(via @objectstack/objectql)
  • content/docs/kernel/services-checklist.mdx(via @objectstack/objectql)
  • content/docs/kernel/services.mdx(via @objectstack/objectql)
  • content/docs/permissions/authentication.mdx(via @objectstack/objectql)
  • content/docs/plugins/index.mdx(via @objectstack/objectql)
  • content/docs/plugins/packages.mdx(via @objectstack/objectql)
  • content/docs/protocol/kernel/index.mdx(via @objectstack/objectql)
  • content/docs/protocol/objectql/query-syntax.mdx(via packages/objectql)
  • content/docs/protocol/objectql/state-machine.mdx(via @objectstack/objectql)
  • content/docs/releases/implementation-status.mdx(via @objectstack/objectql)

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 freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

悬空引用巡检:行预算耗尽后剩余对象被静默丢弃 —— 报告里没有任何桶说「这些对象没被看过」

2 participants

@os-zhuang@claude