Skip to content

fix(scripts): the ObjectQL double-limit lift reads identifiers from code, never from string or template text - #30521

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-o7a942
Nov 5, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-o7a942

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Bill · project thread

domain:server#4 · os-bill · session_01Utp6CZMkHvuDeVYgTD73qF

Fixes #30473

Clause-②: no — an internal instrument; no product surface moves
Metadata-protocol: none

维护者速读

  • 改了什么: ObjectQL 测试替身 limit 检查在"把替身的 find 抽出来单独运行"时,改为只顺着代码里出现的名字去找同文件依赖;字符串、模板文字正文和注释里的单词不再算数,${…} 里的表达式照收。与刚合入的 fix(scripts): the where-matcher lift reads identifiers from code, never from template-literal text #30472(where-matcher 检查)同一处修法。
  • 为什么改: 原来是对源码正文跑单词正则。替身报错文案里只要提到一个同文件的导出函数名,检查就会把它拉进来,抽取直接语法报错,一个合规替身被判"无法判定",门禁结论取决于文案用词。
  • 风险与回滚: 只动一个仓库内部检查脚本,不发布任何包。全树 985 个候选改前改后判定逐条一致(含原因列),耗时持平(约 27 秒)。回滚即撤 PR。
  • 先看: scripts/check-objectql-double-limit.mjs 的 identifiersIn(改为遍历语法树)、discoverInSource 里的 sourceIdentifiers(方法只取参数和函数体)、buildCallable 读集合的两行。
  • 两分钟验证: pnpm check:objectql-double-limit,预期 self-test 打印 OK,全树一行 41 unjudged,与 main 相同(合 main 后共 987 个候选)。
  • 需要你做的: 无,席位落地。

What changes

identifiersIn was a word regex over source TEXT, the same function PR #30472 replaced in check-where-matcher-conformance.mjs. It now walks the parsed tree and collects Identifier nodes only: string literals, a template literal's text, regular expressions and comments contribute nothing, and an identifier inside a ${…} hole is collected. Discovery computes the sets off the nodes it already holds, so nothing is re-parsed: each visible declaration carries identifiers beside its text, and the candidate carries sourceIdentifiers for exactly the text its source lifts (a method lifts as function (params) body, so its parameters and body are walked and its own name is not; an expression is walked whole). buildCallable follows those sets.

Not imported from the where-matcher gate. The seat asked for one implementation, shared if it fits. It does not fit here: this file's header records why its scope walk is hand-copied rather than imported ("a gate that imports its own substrate from another gate's file couples two tripwires that must be able to fail independently"). The walker is the same tree walk in both files (this one takes several nodes, for the method case), and the doc comment names its twin.

Where the defect bites in this gate. Measured, not assumed: this lift already survives a text-pulled sibling that throws X is not defined (it stubs the name and retries) and stubs non-function initializers at stub class 2, so the where-matcher reproducer ("sparse driver" beside const driver = { name }) seats here either way. What it cannot survive is a text-pulled FUNCTION-like sibling whose source cannot sit in the lift (function-like declarations are never stubbed). The self-test case uses an export function: export inside the lift body is a syntax error at every stub class, so the old lift answers UNJUDGED could not lift: SyntaxError: Unexpected token 'export' for a double that never calls it.

Tests

Two fixtures and seven assertions in the existing battery (floor 56 → 63):

  • FIXTURE_TEXT_NAMES_SIBLING: a bounding, refusing double whose messages say "openDriver" (one template literal with a ${k} hole, one plain string), beside export function openDriver. Pinned: discovered; openDriver and implemented not collected; judged CONFORMING.
  • FIXTURE_HOLE_NAMES_HELPER: a same-file helper key named only inside a ${…} hole on the answering path. Pinned: discovered; key collected; judged CONFORMING.
Mutation replay (one-off, restored by blob hash == HEAD)
  • A, old semantics (word regex over each node's text): self-test exit 1 — template-literal TEXT names no identifier: "openDriver" in the message is not collected, nor does a plain string literal: its "implemented" is not collected either, a double beside text naming a sibling binding lifts and is CONFORMING, not UNJUDGED.
  • B, holes skipped (if (ts.isTemplateExpression(n)) return;): self-test exit 1 — the helper named only inside the hole is collected, so the hole-reading double lifts with its helper and is CONFORMING.
  • The text fixture through main's module directly: UNJUDGED could not lift: SyntaxError: Unexpected token 'export'; through this branch: CONFORMING.

Acceptance notes

  • Whole-tree verdicts, before (48231b0) and after (c6af339): 985 structural candidates, 488 CONFORMING, 158 BLIND, 32 WRONG, 41 UNJUDGED, 266 OUT_OF_SCOPE, both runs; a per-candidate diff (file, line, verdict, wrap order, reason) is empty. No verdict changed, so nothing newly lifts and no count moves. The 41 UNJUDGED are not this defect. After merging origin/main (head 07592b3): 987 structural candidate(s): 680 graded, 41 unjudged, gate exit 0.
  • No real producer in the tree today (the card said read, not measured); the reach is the self-test case.
  • Observation, not touched: a function-like same-file declaration spelled export function cannot be lifted even when the double's CODE calls it. That limit is unchanged by this PR and no corpus double hits it today.

Verification

Gate list derived by node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (31 commands), each run with its exit code recorded before any pipe and reconciled with --ran; pnpm lint run. Readings are in the os-dev-report on #30473.

No changeset: the diff is one repo-root script that no released package ships (skip-changeset).


Generated by Claude Code

…ode, never from string or template text

identifiersIn was a word regex over source text, the twin of the
where-matcher lifter fixed earlier. It now walks the parsed tree; the
self-test pins both directions.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Utp6CYZ6tmbWZDWzLW4Yzg
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#4 · os-bill · session_01Utp6CZMkHvuDeVYgTD73qF

维护者速读

  1. 改了什么:ObjectQL 测试替身 limit 检查抽取替身 find 时,改为只顺着代码里的名字找同文件依赖(遍历语法树),字符串、模板文字正文和注释里的单词不再算,${…} 里的表达式照收;与已合入的 fix(scripts): the where-matcher lift reads identifiers from code, never from template-literal text #30472(where-matcher 检查)同法。
  2. 为什么改:原来对源码正文跑单词正则,报错文案里提到一个同文件导出函数名,检查就把它拉进抽取体,语法报错,合规替身被判「无法判定」,门禁结论取决于文案用词。
  3. 风险:只动一个仓库内部检查脚本,不发布包。全树 985 个候选改前改后逐条判定一致(含原因列),合 main 后 987 个 / 41 unjudged,与 main 相同。回滚即 revert。
  4. 先看:scripts/check-objectql-double-limit.mjs 的 identifiersIn、discoverInSource 里的 sourceIdentifiers、buildCallable 读集合的两行。
  5. 两分钟验证:pnpm check:objectql-double-limit,self-test OK,全树一行 41 unjudged。
  6. 需要你做的:无,席位落地。非 BREAKING、不发布、不属 Tier H、不超 3000 行。

席位意见:ACCEPT(#30473)。这个测试替身检查不再因为报错文案里写到某个函数名就误判替身「无法判定」,判定结果只取决于代码本身;全仓库现有判定一条没变。

席位核对

Head-sha: 07592b3022cda9456215c500ca57d97fdff558e1 · read 2026-11-05T14:19Z

Item Reading
Shape Draft → main via relay pr_create; Fixes #30473; label skip-changeset (repo script, publishes nothing). 1 file, +96/−8.
Ruling Card #30473, filed by this seat as the twin of #29860 (landed via #30472).
Seat diff read identifiersIn walks ts.forEachChild and collects Identifier nodes only; per-declaration identifiers are computed once at discovery; a method candidate's set is its parameters and body (matching the lifted function (params) body text), an expression's is the whole node. Two self-test fixtures pin both directions: template-literal and string text names nothing (an export function sibling named only in a message no longer breaks the lift), a helper named only inside a ${…} hole is collected. Battery floor 56 → 63. Not shared with check-where-matcher-conformance: the file's head records the two gates must be able to go red independently; deviation reported.
Gates (check-runs on this head) 46 completed: 26 success, 20 skipped, 0 failed (re-read at landing). Dev report 6660004457: 31 derived gate commands exit 0, --ran 31/31; mutation replays A (old regex) and B (holes skipped) both exit 1.
Landing Not governed (check-governed-merges --pr 30521: 0 of 1 path, re-read 2026-11-05T14:19Z), ≤ 3000 lines, not BREAKING, so per pm-dispatch the seat lands it: pr_ready + automerge_enable.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 5, 2026 14:19
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge November 5, 2026 14:19
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 5, 2026
Merged via the queue into main with commit b17dbcf Nov 5, 2026
47 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-o7a942 branch November 5, 2026 15:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

1 participant