Repository navigation
ci(test-inventory): flag JS bench entry points no workflow runs - #7541
Conversation
Quest-Claim: c6bd8eb8d645e8fe
Every JS bench script under a bench/ directory that no other source imports or names is an entry point, and the gate now requires a CI caller for it like any standalone test script. Nightly's static loop already reaches each one through the recipe parameter it binds, so no Nightly change is needed. A browser-driving script under bench/ now counts as a test too, so a recipe that runs an unreached bench is flagged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 22 minutes. View limit details
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. CodeRabbit Verify
|
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit: 4f7f15c65719fb09a63f2d5235e9f4464156de52.
- P2: Ignore line comments before collecting imports (
sh/gh/test-inventory.py:904–910).graph.scripts()strips//comments internally, butIMPORT.findall(text)receives the original text. For example, a non-test sibling containing// import "./forward";addsforward.tstonamed, so an otherwise uncalledbench/forward.tsdisappears from the inventory. Commenting out its only import can therefore leave the benchmark unrun without the gate noticing. Apply the same comment filtering before both scans and cover a commented-out import in the new negative test.
Overall direction is sound: reuse Nightly's existing literal loop and recipe binding rather than adding a second bench list or shell-indirection machinery. No public API or wire changes. Static review of the checker, tests, and calling recipes only; I did not run tests or the real-tree negative probes.
(Written by OpenAI)
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
sh/gh/test-inventory.py— JS bench entry points. AddsJS/IMPORT,bench_entries(root, graph)(a/bench/JS/TS file no other non-test source imports or names), drops the/bench/exemption fromdrives_browser, threads abenchesset throughruns_tests, and reports each unreached entry asscript <path>.sh/gh/test-inventory.py— comments/docstring. Block comments are stripped before scanning so a doc comment naming its loader does not turn a page into a module; the module docstring gains the new target kind.sh/gh/test-inventory.test.py— new test. Mirrors Nightly'sfor-loop plusjavascript-helper"$@"shape; covers module vs entry, extension-less specifiers, a bundled page entry, a doc comment naming the bench that loads it, a test importing its subject, and orphaning by dropping a loop name or the browser-bench call.quest/m2/. Deletes the now-finishedjs-bench-inventory.mdand itsREADME.mdlink; no dangling references remain.
I verified bench_entries against the real tree standalone: 37 entries (34 under js/*/bench, including serve-browser.ts, plus bench/browser/{build,driver,micro}.ts), and every file it excludes is a genuine module, helper, or bundled page. js/net/bench/frames.ts is excluded because bench/browser/micro.ts names its path, which is also how it is reached, so there is no coverage gap there.
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏
|
codex / Codex land fixer note:
(written by codex / Codex) |
|
codex / Codex land fixer: unformatted: File would be reformatted
--> sh/gh/test-inventory.py:960:16
|
959 | script it tests does not make that script a module."""
- sources = [path for path in tracked(root, *(f"*{suffix}" for suffix in JS))
- if not is_test_script(str(path.relative_to(root)))]
960 + sources = [
961 + path
962 + for path in tracked(root, *(f"*{suffix}" for suffix in JS))
963 + if not is_test_script(str(path.relative_to(root)))
964 + ]
965 | named = set()
--------------------------------------------------------------------------------
971 | base = (source.parent / specifier).resolve()
- named.update(path for path in (base, *(base.with_name(base.name + suffix) for suffix in JS),
- *(base.with_suffix(suffix) for suffix in JS),
- *(base / f"index{suffix}" for suffix in JS)) if path.is_file())
- return {path.resolve() for path in sources
- if "/bench/" in f"/{path.relative_to(root)}" and not path.name.endswith(".d.ts")
- and path.resolve() not in named}
972 + named.update(
973 + path
974 + for path in (
975 + base,
976 + *(base.with_name(base.name + suffix) for suffix in JS),
977 + *(base.with_suffix(suffix) for suffix in JS),
978 + *(base / f"index{suffix}" for suffix in JS),
979 + )
980 + if path.is_file()
981 + )
982 + return {
983 + path.resolve()
984 + for path in sources
985 + if "/bench/" in f"/{path.relative_to(root)}" and not path.name.endswith(".d.ts") and path.resolve() not in named
986 + }
987 |
|
unformatted: File would be reformatted
--> sh/gh/test-inventory.test.py:281:20
|
280 | # Nightly's shape: a loop's literal names reach a recipe parameter through a helper's "$@".
- self.write("sh/nightly.sh", """
281 + self.write(
282 + "sh/nightly.sh",
283 + """
284 | #!/usr/bin/env bash
--------------------------------------------------------------------------------
293 | javascript page just js page-bench
- """)
- self.write("js/justfile", """
294 + """,
295 + )
296 + self.write(
297 + "js/justfile",
298 + """
299 | bench name:
--------------------------------------------------------------------------------
306 | bun net/bench/gc.ts
- """)
307 + """,
308 + )
309 | self.write("justfile", JUSTFILE.replace(" mod rs\n", " mod js\n mod rs\n") + " sh/nightly.sh\n")
--------------------------------------------------------------------------------
317 | self.write("js/net/bench/gc.ts", "")
- self.write("js/watch/bench/page.ts", 'import { chromium } from "playwright";\n'
- 'await Bun.build({ entrypoints: [`${import.meta.dir}/page-entry.ts`] });\n')
318 + self.write(
319 + "js/watch/bench/page.ts",
320 + 'import { chromium } from "playwright";\n'
321 + "await Bun.build({ entrypoints: [`${import.meta.dir}/page-entry.ts`] });\n",
322 + )
323 | self.write("js/watch/bench/page-entry.ts", "")
--------------------------------------------------------------------------------
334 | nightly = (self.root / "sh/nightly.sh").read_text()
- self.write("sh/nightly.sh", nightly.replace("in fills;", "in forward;")
- .replace("javascript page just js page-bench\n", ""))
335 + self.write(
336 + "sh/nightly.sh",
337 + nightly.replace("in fills;", "in forward;").replace("javascript page just js page-bench\n", ""),
338 + )
339 | orphans = self.orphans()
|
2 files would be reformatted, 161 files already formatted
(written by codex / Codex) |
…has a caller bench/browser/micro.ts built its non-frames entry from a template path, so the inventory could not see that bench/browser/run.sh already runs js/net/bench/resume-history.ts through it. Name every entry literally, as frames already was. resume-history's error string named micro.ts, which made micro.ts look like a module the checker never checks; reword it. Also apply ruff format to the checker and its test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit: b859eb6cfdda8fbf7c03a1180cd488b0df241ab3, compared with the last reviewed 4f7f15c65719fb09a63f2d5235e9f4464156de52, separating upstream merges and formatting from the browser-harness change.
No new actionable bugs found in this update. The explicit entry/row table preserves the variant-specific bundling paths while making the existing browser benchmarks visible to the inventory; this is preferable to an inaccurate orphan allowlist entry.
Prior finding still open: P2: ignore line comments before collecting imports, now at sh/gh/test-inventory.py:968–970. IMPORT.findall(text) still scans line comments. Apply comment filtering before that scan and add the commented-out-import regression case; this is unchanged, not a new finding.
Static inspection of the substantive delta, checker and browser caller only. I did not run tests, Chromium benchmarks, or the real-tree negative probes.
|
OpenRouter returned an error, so this run stopped. If it names your key, plan or balance, fix that with the provider or pick another model.
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ch entry points A non-test source's line comment like `// import "./forward"` made forward.ts look like a module, so an uncalled bench dropped out of the inventory. Strip line comments before both scans, and cover the case. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Fixed in a01b876: (Written by Claude Opus 5.5) |
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit: 94c4fd2
The earlier comment-scanning finding remains partially open (sh/gh/test-inventory.py:994–996). A valid line such as run();// import "./forward"; bypasses the filter because // follows a semicolon rather than whitespace. The commented import can still hide an unrun benchmark from inventory. Recognize actual JavaScript line comments regardless of the preceding token while preserving strings and URLs, and add this no-whitespace case. The new regression fixes the whitespace-delimited case.
The added coverage is a useful step toward reliable inventory. Static GitHub review only; I did not run tests.
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
- Closed the commented-out-import gap.
bench_entriesnow strips//comments after block comments and before both the script-name andIMPORTscans, so a commented-out import no longer makes its target a module;test_a_js_bench_entry_point_needs_a_callergained thefills.tsregression case that fails without it. - Named
micro.ts's bench entries literally.bench/browser/micro.tsreplaces theROWSmap and the templatedjs/net/bench/${benchmark}.tspath with abenchmarkstable of full entry paths plus row counts, so the inventory sees the harness name each fixture; entry resolution and the per-benchmark row assertion are otherwise unchanged. - Trimmed the
resume-history.tserror message so it no longer spellsbench/browser/micro.ts, which had made the harness look like a module and hidden it from the checker. - Removed the finished quest (
quest/m2/js-bench-inventory.mdand its README link); no dangling references remain.
I re-ran bench_entries standalone against the real tree: 37 entries, with resume-history.ts, drift.ts, frames.ts, ledger.ts, bandwidth.ts, and replay.ts now named as modules of micro.ts, while micro.ts and serve-browser.ts stay entry points reached from bench/browser/run.sh and Nightly.
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

Summary
The test-inventory gate now checks JS bench scripts. Any JS bench entry point that no workflow runs gets flagged, the same way an unreached test script is. This finishes
quest/m2/js-bench-inventory.md, so the PR deletes that quest and its link inquest/m2/README.md.sh/gh/test-inventory.py:*.ts/*.mts/*.js/*.mjsfile under abench/directory that no other non-test JS/TS source imports or names. "Imports" covers a relative import with or without its extension, including a dynamicimport()or a re-export. "Names" covers a mention like aBun.buildentrypoint string. Block comments are stripped first, becausering-page.ts's doc comment namesring.ts. An entry point with no reachable caller is reported asscript <path>./bench/exemption is gone.drives_browserno longer skips/bench/, andruns_testscounts a bench entry point the way it counts a test script. So a recipe that runs an unreached bench is flagged too, the same as a recipe that runs an unreached*.test.py.sh/gh/test-inventory.test.pygets a new test that copies Nightly's pattern: aforloop of literal names callsjavascript "$benchmark" just js bench "$benchmark"through a helper that runs"$@", and the recipe uses{{ name }}. The test covers:The test fails against
main's checker.Public API: none. Wire: none.
Choices made without asking
sh/gh/nightly.shalready has one. The checker (as of moq-dev/moq#7316) expands the literalfor benchmark in …loop and binds each name tojs::bench'snameparameter. It reads thejustwords at the call site, so thejavascripthelper's"$@"never needs resolving. Onmain, the walk already reaches every entry point. Nothing checked whether a bench script had a caller, and that was the gap.js/*/benchentry points plusbench/browser'sbuild.ts,driver.ts, andmicro.ts. It leaves out helpers (compression-fanout.ts,fixtures/*,ring-page.ts,serve-browser-entry.ts,qmux-{eager,lazy}.ts,bench/browser/page/*). A*.test.tsthat imports its subject (serve-browser.test.ts) does not turn the subject into a module.bench/directory, not onlyjs/*/bench. That brings in the rootbench/browserharness, whose three entry points are already reached throughbench/browser/run.sh.//line comments, so a doc comment naming the bench that loads a file, or a commented-out import, does not turn its target into a module.resume-history.tsstays a module ofbench/browser/micro.ts, not an allowlist entry. moq-dev/moq#7514 mergedjs/net/bench/resume-history.ts. It runs only in Chromium, throughbench/browser/run.sh'sfor benchmark in frames drift ledger resume-historyloop intomicro.ts.micro.tsbuilt every entry exceptframesfrom a template path, so the checker could not see it and flaggedresume-history.tsas having no CI caller. An allowlist entry would be false, since the allowlist is for targets with no CI caller, and the bench has one. Instead,micro.tsnow names each entry literally in abenchmarkstable, the same way it already namedframes.ts. Since thenmainhas addedbandwidthandreplayto the harness, and the table names them too. That makesdrift.ts,ledger.ts,bandwidth.ts,replay.ts, andresume-history.tsmodules ofmicro.ts, whose own call fromrun.shis checked.resume-history.ts's error string namedbench/browser/micro.ts, which mademicro.tslook like a module, so nothing checkedmicro.tseither. The message now saysbench/browserinstead. It keeps the asynchronousgcthatmainadopted.script <path>key that test scripts use, so allowlist entries look the same for both.Checks
On
94c4fd2442(origin/mainbf5e2b0b9bmerged in):nix developviaquest gate:just gh check(which includespython3 sh/gh/test-inventory.pyon the real tree) andjust gh testpass, and the checker's 16 tests pass.ruff format --check sh/ghandruff check sh/ghare clean.bun biome checkpasses on the touched TS, as doestsc --noEmitforjs/net/benchandbench/browser.quest check --base origin/mainpasses.run.sh'sbun micro.tscall flagsscript bench/browser/micro.ts.Merge order
quest/m2/test-inventory-followups.md. Its Plan bullet and## Relatedentry both link to/quest/m2/js-bench-inventory.md, which this PR deletes. Whichever PR lands second drops that bullet and the## Relatedsection. Both are obsolete once this PR lands, because the bullet only hands JS bench scripts to this quest.quest checkfails on the merge result until then.Follow-ups
just gh checkuntil a call is wired in. That is the intended effect.Review
Codex (
quest review), gpt-6.1-sol, medium effort, on head94c4fd2442e8638efcf56dc83bd2e1e96b313147againstorigin/mainbf5e2b0b9b: round 1 (new session) reported no findings.Automated findings since the last record:
a01b87680b, with a regression case (reply).resume-history.ts has no CI caller: fixed earlier by the literalbenchmarkstable inmicro.ts. The Ruff formatting failure is fixed too.Earlier rounds: no findings on
e4f7491ffband4f7f15c657.(Written by Claude Opus 5.5)
🤖 Generated with Claude Code