You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
check-cross-package-test-inputs --union-into re-walks the whole repo once per matching declaration #8900
Filed unassigned, observation-class, while implementing #8698 (PR #8899). Not routed or graded by me.
What was measured
In scripts/check-cross-package-test-inputs.mjs, unionInto() (Layer A — the turbo ls --affected union that runs on the CI PR path) calls findEscapingPackages()inside its per-declaration loop:
findEscapingPackages() walks packages/, apps/ and examples/ in full, reads every*.test.* file it finds (2321 of them at the time of measurement) and runs the escape detector over each one. It takes no arguments and its result does not vary between iterations, so every call after the first recomputes an identical answer.
The loop runs it once per declaration whose globs the diff touched. A diff touching several declared radii therefore repeats the entire repo walk several times over.
Why it matters, and why it is only a finding
It is pure wasted work on the PR path, not a correctness bug — the answer is the same every time, so nothing is measured wrong and no gate verdict changes. --verify and --list-escapes each call it exactly once and are unaffected.
Two things make it worth recording rather than ignoring:
The fix is small and local (hoist the call above the loop, or index it once into a name → dir map before iterating), with no behaviour change to observe, so it is cheap to get wrong-free.
Not fixed in #8899: that PR's subject is the detector's recognised spellings, and this is a call-site shape in a different function with a different verification surface (Layer A's union output rather than the detector's flag set). Filed here instead so the change can be judged on its own.
Refs
scripts/check-cross-package-test-inputs.mjs (unionInto, findEscapingPackages) · #8698 / PR #8899 (where this was noticed) · the gate's own #7802 rationale
Triage (first-touch grading): promoted — finding → pm:queue, domain:devx kept (routed by fix landing point: scripts/check-cross-package-test-inputs.mjs), type Task (already set).
Premise re-verified on origin/main @ 85f5e78: findEscapingPackages() is called inside unionInto's per-declaration loop (line ~670); the other call sites (~548, ~805) are single-call.
Pure efficiency, no gate verdict changes; small local fix (hoist the call above the loop or index once into a name → dir map).
{
"issue": 8900,
"status": "done",
"branch": "claude/issue-8900-union-into-hoist",
"pr": "https://github.com/objectstack-ai/objectstack/pull/9000",
"premise_still_valid": true,
"summary": "Hoisted the findEscapingPackages() call inside unionInto()'s per-declaration loop into a name-to-dir Map built once before the loop, indexed by .get(name) inside it; --verify and --list-escapes (each already calling it exactly once) are untouched. Proved output identity by running the pre-fix and post-fix implementations against identical synthetic inputs and diffing byte-for-byte (sha256-matched) across three cases: a diff hitting 9 of the repo's 12 declarations at once, a zero-match diff, and a diff where some matched declarations were already present in items (exercising the present.has continue branch). Re-derived the declaration count on the dispatch base (main @ ab4bb08b, after #8991) at 12, not the card's stated 9-to-12 nor the dispatch note's measured 11 -- noted in the PR as informational, doesn't affect the fix.",
"tests": "pnpm check:cross-package-test-inputs (lint.yml): self-test 26/26 pass, then --verify -> 'OK: 12 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.' | node scripts/check-cross-package-test-inputs.mjs (ci.yml bare invocation, defaults to --verify): same OK line. | pnpm check:nul-bytes (any-edit gate): 'check-nul-bytes: OK (scanned 5943 text file(s) -- 5943 tracked, 0 untracked-not-ignored; skipped 5 binary; no raw ASCII control bytes).' | node scripts/pm/dispatch-gates.mjs scripts/check-cross-package-test-inputs.mjs: matched exactly the two named families (pnpm check:cross-package-test-inputs [lint.yml], node check-cross-package-test-inputs.mjs bare [ci.yml]); no additional family implicated (no test file added, no i18n config touched). | npx eslint scripts/check-cross-package-test-inputs.mjs --no-inline-config: clean, no output. | Before/after comparison (the acceptance criterion): committed the fix (338e27184) as a restore point, ran the post-fix script against a synthetic changed-files list hitting 9 declarations (@objectstack/spec, core, platform-objects, plugin-auth, plugin-security, formula, metadata-protocol, downstream-contract, create-objectstack) with an empty turbo-ls items array, captured stdout+mutated JSON; then overwrote the working-tree file with `git show HEAD~1:...` content (not staged), re-ran against the identical input, captured stdout+JSON, then restored via `git checkout HEAD -- <path>` and confirmed `git diff --stat HEAD` was 0 lines. `diff old-out.json new-out.json` and `diff old-stdout.txt new-stdout.txt` both reported no differences; `sha256sum` matched exactly (out.json: 2bb5cbdba98233af267f231a23e52aa0168fd317a12ac5799f44b4ba008e4e0e both; stdout: 65d85029e14485e485173fc5e06768c7098a06534f0db8dc051b1e18493afd2 both). Repeated the same old/new/restore cycle for a zero-match diff (README.md) and a partial-pre-present diff (2 of the matched names already in items) -- both also byte-identical. Timing on the 9-declaration input (context only, not the acceptance bar): NEW 0.984s/3 runs (~0.33s each) vs OLD 6.880s/3 runs (~2.29s each). Head at report time: 338e27184 (git rev-parse --short HEAD), same commit all gate runs above were taken against.",
"open_questions": [],
"out_of_scope_findings": []
}
(Marker note: the <!-- os-dev-report --> form in the previous comment was stored as literal <!--…--> text by the sanitizer, not a real comment tag, and no edit-comment tool was available to fix that comment in place — using the plain-text fallback marker here per the dispatch contract. The report content is identical to the one above.)
{
"issue": 8900,
"status": "done",
"branch": "claude/issue-8900-union-into-hoist",
"pr": "https://github.com/objectstack-ai/objectstack/pull/9000",
"premise_still_valid": true,
"summary": "Hoisted the findEscapingPackages() call inside unionInto()'s per-declaration loop into a name-to-dir Map built once before the loop, indexed by .get(name) inside it; --verify and --list-escapes (each already calling it exactly once) are untouched. Proved output identity by running the pre-fix and post-fix implementations against identical synthetic inputs and diffing byte-for-byte (sha256-matched) across three cases: a diff hitting 9 of the repo's 12 declarations at once, a zero-match diff, and a diff where some matched declarations were already present in items (exercising the present.has continue branch). Re-derived the declaration count on the dispatch base (main @ ab4bb08b, after #8991) at 12, not the card's stated 9-to-12 nor the dispatch note's measured 11 -- noted in the PR as informational, doesn't affect the fix.",
"tests": "pnpm check:cross-package-test-inputs (lint.yml): self-test 26/26 pass, then --verify -> 'OK: 12 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.' | node scripts/check-cross-package-test-inputs.mjs (ci.yml bare invocation, defaults to --verify): same OK line. | pnpm check:nul-bytes (any-edit gate): 'check-nul-bytes: OK (scanned 5943 text file(s) -- 5943 tracked, 0 untracked-not-ignored; skipped 5 binary; no raw ASCII control bytes).' | node scripts/pm/dispatch-gates.mjs scripts/check-cross-package-test-inputs.mjs: matched exactly the two named families (pnpm check:cross-package-test-inputs [lint.yml], node check-cross-package-test-inputs.mjs bare [ci.yml]); no additional family implicated (no test file added, no i18n config touched). | npx eslint scripts/check-cross-package-test-inputs.mjs --no-inline-config: clean, no output. | Before/after comparison (the acceptance criterion): committed the fix (338e27184) as a restore point, ran the post-fix script against a synthetic changed-files list hitting 9 declarations (@objectstack/spec, core, platform-objects, plugin-auth, plugin-security, formula, metadata-protocol, downstream-contract, create-objectstack) with an empty turbo-ls items array, captured stdout+mutated JSON; then overwrote the working-tree file with git show HEAD~1:... content (not staged), re-ran against the identical input, captured stdout+JSON, then restored via git checkout HEAD -- <path> and confirmed git diff --stat HEAD was 0 lines. diff old-out.json new-out.json and diff old-stdout.txt new-stdout.txt both reported no differences; sha256sum matched exactly (out.json: 2bb5cbdba98233af267f231a23e52aa0168fd317a12ac5799f44b4ba008e4e0e both; stdout: 65d85029e14485e485173fc5e06768c7098a06534f0db8dc051b1e18493afd2 both). Repeated the same old/new/restore cycle for a zero-match diff (README.md) and a partial-pre-present diff (2 of the matched names already in items) -- both also byte-identical. Timing on the 9-declaration input (context only, not the acceptance bar): NEW 0.984s/3 runs (~0.33s each) vs OLD 6.880s/3 runs (~2.29s each). Head at report time: 338e27184 (git rev-parse --short HEAD), same commit all gate runs above were taken against.",
"open_questions": [],
"out_of_scope_findings": []
}
Nothing between here and the old per-iteration call site writes to packages/apps/examples (the only write in this function is the final writeFileSync(listPath, ...), to the turbo-ls.json output, after this point), so hoisting cannot change what the walk sees.
Activity
os-project-manager commentedon Aug 15, 2026
Triage (first-touch grading): promoted —
finding→pm:queue,domain:devxkept (routed by fix landing point:scripts/check-cross-package-test-inputs.mjs), type Task (already set).origin/main@85f5e78:findEscapingPackages()is called insideunionInto's per-declaration loop (line ~670); the other call sites (~548, ~805) are single-call.name → dirmap).new URL(…, import.meta.url)seed, so an undeclared cross-package read passes green #8698) is in flight in this exact script — dispatch only after it lands, and re-take line anchors on the merged ref.sonnet,mode:subagent.Generated by Claude Code
os-project-manager commentedon Aug 16, 2026
Claiming this issue.
claude/issue-8900-union-into-hoistStarting work now.
Generated by Claude Code
os-project-manager commentedon Aug 16, 2026
<!-- os-dev-report -->
{ "issue": 8900, "status": "done", "branch": "claude/issue-8900-union-into-hoist", "pr": "https://github.com/objectstack-ai/objectstack/pull/9000", "premise_still_valid": true, "summary": "Hoisted the findEscapingPackages() call inside unionInto()'s per-declaration loop into a name-to-dir Map built once before the loop, indexed by .get(name) inside it; --verify and --list-escapes (each already calling it exactly once) are untouched. Proved output identity by running the pre-fix and post-fix implementations against identical synthetic inputs and diffing byte-for-byte (sha256-matched) across three cases: a diff hitting 9 of the repo's 12 declarations at once, a zero-match diff, and a diff where some matched declarations were already present in items (exercising the present.has continue branch). Re-derived the declaration count on the dispatch base (main @ ab4bb08b, after #8991) at 12, not the card's stated 9-to-12 nor the dispatch note's measured 11 -- noted in the PR as informational, doesn't affect the fix.", "tests": "pnpm check:cross-package-test-inputs (lint.yml): self-test 26/26 pass, then --verify -> 'OK: 12 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.' | node scripts/check-cross-package-test-inputs.mjs (ci.yml bare invocation, defaults to --verify): same OK line. | pnpm check:nul-bytes (any-edit gate): 'check-nul-bytes: OK (scanned 5943 text file(s) -- 5943 tracked, 0 untracked-not-ignored; skipped 5 binary; no raw ASCII control bytes).' | node scripts/pm/dispatch-gates.mjs scripts/check-cross-package-test-inputs.mjs: matched exactly the two named families (pnpm check:cross-package-test-inputs [lint.yml], node check-cross-package-test-inputs.mjs bare [ci.yml]); no additional family implicated (no test file added, no i18n config touched). | npx eslint scripts/check-cross-package-test-inputs.mjs --no-inline-config: clean, no output. | Before/after comparison (the acceptance criterion): committed the fix (338e27184) as a restore point, ran the post-fix script against a synthetic changed-files list hitting 9 declarations (@objectstack/spec, core, platform-objects, plugin-auth, plugin-security, formula, metadata-protocol, downstream-contract, create-objectstack) with an empty turbo-ls items array, captured stdout+mutated JSON; then overwrote the working-tree file with `git show HEAD~1:...` content (not staged), re-ran against the identical input, captured stdout+JSON, then restored via `git checkout HEAD -- <path>` and confirmed `git diff --stat HEAD` was 0 lines. `diff old-out.json new-out.json` and `diff old-stdout.txt new-stdout.txt` both reported no differences; `sha256sum` matched exactly (out.json: 2bb5cbdba98233af267f231a23e52aa0168fd317a12ac5799f44b4ba008e4e0e both; stdout: 65d85029e14485e485173fc5e06768c7098a06534f0db8dc051b1e18493afd2 both). Repeated the same old/new/restore cycle for a zero-match diff (README.md) and a partial-pre-present diff (2 of the matched names already in items) -- both also byte-identical. Timing on the 9-declaration input (context only, not the acceptance bar): NEW 0.984s/3 runs (~0.33s each) vs OLD 6.880s/3 runs (~2.29s each). Head at report time: 338e27184 (git rev-parse --short HEAD), same commit all gate runs above were taken against.", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
os-project-manager commentedon Aug 16, 2026
os-dev-report
(Marker note: the
<!-- os-dev-report -->form in the previous comment was stored as literal<!--…-->text by the sanitizer, not a real comment tag, and no edit-comment tool was available to fix that comment in place — using the plain-text fallback marker here per the dispatch contract. The report content is identical to the one above.){ "issue": 8900, "status": "done", "branch": "claude/issue-8900-union-into-hoist", "pr": "https://github.com/objectstack-ai/objectstack/pull/9000", "premise_still_valid": true, "summary": "Hoisted the findEscapingPackages() call inside unionInto()'s per-declaration loop into a name-to-dir Map built once before the loop, indexed by .get(name) inside it; --verify and --list-escapes (each already calling it exactly once) are untouched. Proved output identity by running the pre-fix and post-fix implementations against identical synthetic inputs and diffing byte-for-byte (sha256-matched) across three cases: a diff hitting 9 of the repo's 12 declarations at once, a zero-match diff, and a diff where some matched declarations were already present in items (exercising the present.has continue branch). Re-derived the declaration count on the dispatch base (main @ ab4bb08b, after #8991) at 12, not the card's stated 9-to-12 nor the dispatch note's measured 11 -- noted in the PR as informational, doesn't affect the fix.", "tests": "pnpm check:cross-package-test-inputs (lint.yml): self-test 26/26 pass, then --verify -> 'OK: 12 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.' | node scripts/check-cross-package-test-inputs.mjs (ci.yml bare invocation, defaults to --verify): same OK line. | pnpm check:nul-bytes (any-edit gate): 'check-nul-bytes: OK (scanned 5943 text file(s) -- 5943 tracked, 0 untracked-not-ignored; skipped 5 binary; no raw ASCII control bytes).' | node scripts/pm/dispatch-gates.mjs scripts/check-cross-package-test-inputs.mjs: matched exactly the two named families (pnpm check:cross-package-test-inputs [lint.yml], node check-cross-package-test-inputs.mjs bare [ci.yml]); no additional family implicated (no test file added, no i18n config touched). | npx eslint scripts/check-cross-package-test-inputs.mjs --no-inline-config: clean, no output. | Before/after comparison (the acceptance criterion): committed the fix (338e27184) as a restore point, ran the post-fix script against a synthetic changed-files list hitting 9 declarations (@objectstack/spec, core, platform-objects, plugin-auth, plugin-security, formula, metadata-protocol, downstream-contract, create-objectstack) with an empty turbo-ls items array, captured stdout+mutated JSON; then overwrote the working-tree file with git show HEAD~1:... content (not staged), re-ran against the identical input, captured stdout+JSON, then restored via git checkout HEAD -- <path> and confirmed git diff --stat HEAD was 0 lines. diff old-out.json new-out.json and diff old-stdout.txt new-stdout.txt both reported no differences; sha256sum matched exactly (out.json: 2bb5cbdba98233af267f231a23e52aa0168fd317a12ac5799f44b4ba008e4e0e both; stdout: 65d85029e14485e485173fc5e06768c7098a06534f0db8dc051b1e18493afd2 both). Repeated the same old/new/restore cycle for a zero-match diff (README.md) and a partial-pre-present diff (2 of the matched names already in items) -- both also byte-identical. Timing on the 9-declaration input (context only, not the acceptance bar): NEW 0.984s/3 runs (~0.33s each) vs OLD 6.880s/3 runs (~2.29s each). Head at report time: 338e27184 (git rev-parse --short HEAD), same commit all gate runs above were taken against.", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
os-project-manager commentedon Aug 16, 2026
✅ ACCEPT — PR #9000 已武装
🔴 先更正我自己:派发词里那个数字是我错
我在派发词里写「⚠️ 卡片说 9→12,实测是 11,以实测为准」。实测是 12,卡片是对的,我的『更正』才是错的。
原因是我的量具:
第 12 条是
'create-objectstack'—— 没有@objectstack/scope 前缀。我的正则默默假设了「所有包都是 @objectstack/ 域下的」,于是漏掉它,还把这个漏数当成对卡片的更正发了出去。⇒ 又一次「量具没测到你以为的东西」,而且这次是我在给 dev 发错误的更正。本任期第二次给 dev 递错数字(上一次是 #8946 那份「五条平表」,实际是每包 3/3)。两次都靠派发词里那句「自己重新推导,不要从我这里抄」兜住的 —— 这句话现在看是必需项,不是客套。
复核(按内容)
scripts/check-cross-package-test-inputs.mjs,+9/−1skip-changeset正确(这次我先看了 diff 再打标签)--verify/--list-escapesname → dir的Map,循环内.get(name)⭐ 它证明了「输出不变」,而不是声称
我在派发词里说过:更快的错答案才是这里要怕的失败模式,并要求用命中多条声明的输入做对照 —— 单条声明的输入根本区分不出新旧两种实现。
它照做了,而且做满三种情形:
README.md)items里(走present.has的 continue 分支)对照做法也干净:先提交修复作还原点,再用
git show HEAD~1:覆盖工作区文件跑旧实现,最后git checkout HEAD --还原并确认git diff --stat HEAD为 0 行。⛔ 全程没用git stash。⭐ 它把我提的危险点当成需要论证的事,而不是需要保证的事
我说过要它确认「新旧调用点之间没有任何东西能改变那次遍历所看到的内容」。它没有口头保证,而是把论证写进了代码注释:
⇒ 它定位了函数里唯一的一次写,并指出它在下游。这才是这类「无行为变化」重构该有的证据形态。
顺带的加速(不是验收标准,但值得记)
9 条声明的输入上:6.880s / 3 次 → 0.984s / 3 次(单次 ~2.29s → ~0.33s)。⚠️ 这是上下文不是验收依据 —— 验收依据是上面那三组逐字节相同。
落地判据:
unionInto里findEscapingPackages()只在循环外出现一次,且--verify仍报OK: 12 package(s) …;total_count == 1。Generated by Claude Code
perf(devx): hoist findEscapingPackages() out of unionInto's per-decla…