Repository navigation
web-lint: hosts that run no heavy checks leave the type-aware lint to CI - #27177
Conversation
The pre-push web lint runs webviews' whole `check` script, whose `vp lint` and `vp check` start tsgolint (about 9 GB and 5 GB, both at once). On shared agent hosts that run no builds, that filled swap. A host that sets CMUX_LOCAL_HEAVY_CHECKS=0 now runs every other piece locally and leaves the heavy ones to CI's web lint job and the pull request's fast gates, saying so and naming cmux-ci test for a pre-push run. Wherever a heavy piece does run outside CI, it runs under nice with GOMAXPROCS, GOMEMLIMIT and Node's heap capped, each only when the caller set none. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-7a592ef92c66f080e3cc
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
web and size checks: green |
|
Owned by an agent session · session |
|
Bead |
Review of 6160990: with CMUX_LOCAL_HEAVY_CHECKS=0 the tui bindings were still installed and built (npm ci, npm run build: tsc) before the check pieces were filtered. Now such a host installs and builds nothing: light pieces run against the node_modules a package already has, and a package with none is left to CI with a named note. Regression test: a cmux-tui/frontends/web change with CMUX_LOCAL_HEAVY_CHECKS=0 spawns no npm, bun install, build, tsc or vp; it fails on 6160990 (npm ci and npm run build ran). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-7a592ef92c66f080e3cc
…stall without heavy checks Review of 5c38839: - the tui bindings build (npm ci, tsc) runs niced and capped outside CI, like the type check; - --new-only reruns a failed piece on the base through the same niced, capped job; - with CMUX_LOCAL_HEAVY_CHECKS=0, --new-only installs nothing in the base tree (no node_modules there: the red stays the change's), and a lint tree shares the checkout's node_modules for packages with local builds too, since nothing is built. Four regression tests; each fails on 5c38839. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-7a592ef92c66f080e3cc
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
|
Found 7 test failures on Blacksmith runners: Failures
|
…spawn for everything Five review rounds each found another path that still ran something heavy with CMUX_LOCAL_HEAVY_CHECKS=0, so the denylist is gone. Such a host now runs only LIGHT_COMMANDS: `vp fmt --check`, python3 source checks, and `bun scripts/<name>.ts` source checks. `bun run NAME` is resolved to NAME's pieces and only the allowlisted ones run, so check:effect-cleanup's `vp lint` is skipped. ESLint runs on changed web/ files (syntax only). It does no install, no build and no --new-only base comparison; every other piece, and a package with no node_modules, is named and left to CI or cmux-ci. Every tool this script starts (installs, builds, checks, base reruns) goes through spawn(), which outside CI runs it under nice with GOMAXPROCS, GOMEMLIMIT and Node's heap capped. Tests: with heavy=0, a subprocess spy sees only allowlisted commands across --head, a lint tree (another commit), --new-only, --all and --fix. On a build host every spawn is niced and capped. The allowlist is checked exactly. Each new test fails on f9399db. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-7a592ef92c66f080e3cc
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
…is never light Review of 5a4516a: an allowlisted prefix could carry another command (`python3 noop.py ; vp lint` ran the lint with CMUX_LOCAL_HEAVY_CHECKS=0), because accepted strings ran with shell=True. Now a piece holding any shell syntax (; | & $ backticks redirections globs quotes escapes newlines) is never light. An allowlisted piece is parsed with shlex.split, checked as an argv, and run as that argv with shell=False. Tests cover ;, &&, ||, |, &, $(...), backticks, newlines and the reviewer's repro through the full main(), and assert no light piece spawns through a shell; each fails on 5a4516a. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-7a592ef92c66f080e3cc
Review of 5eb2e77: `nice -n 10 CHECK_MODE=1 python3 check.py` ran CHECK_MODE=1 as the program (exit 127), and `nice -n 10 true; python3 x.py` niced only `true`. A shell string now runs as `nice -n 10 /bin/sh -c STRING`, so assignments and every command of a compound string keep their meaning and their priority. Regression test fails on 5eb2e77. The bindings-build test now clears CI and GITHUB_ACTIONS itself, since it asserts outside-CI behaviour (it failed on the CI runner). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-7a592ef92c66f080e3cc
…cript Review of 3811e05: any shell-free `python3 ...` counted as light, so `python3 -m pip install ..`, `python3 -m compileall` or a bare `python3` ran with CMUX_LOCAL_HEAVY_CHECKS=0. Now the allowlist is `vp fmt --check` exactly, plus `python3 [-I] PATH.py ARGS` or `bun PATH.ts ARGS`, where PATH resolves (relative to the package, no escape) into a scripts/ folder under one of the package's `executed` paths, which the hook already refuses to lint when a push changes them. No -m, -c, other flags, absolute paths or bare interpreter. webviews' real checks all still qualify. On 3811e05, is_light("python3 -m pip install requests") is True; the new test fails there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-7a592ef92c66f080e3cc
|
Review (codex gpt-6-astra high, next-review): SHIP at 91644bb No blocking findings. |
Review of 91644bb: with CMUX_LOCAL_HEAVY_CHECKS=0 the dry run skipped light_plan(), so it listed packages and ESLint areas a run drops for having no node_modules. A run and a dry run now build their commands with one function, check_jobs(), over the same light_plan(): the dry run checks the node_modules the run would see (in a lint tree only where shares_deps() holds). It prints one `<area>: <command>` line per command. A new test runs both in light mode, on the checked-out head and on another commit, and requires the dry run's list to equal the spawned list. It fails on 91644bb. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-7a592ef92c66f080e3cc
|
Review (codex-review-box/gpt-6-astra high, lane-place): BLOCK at c146f28 BLOCK
|
A dry run of a commit that runs in a fresh lint tree read the checkout's build stamps and node_modules, so it listed only the check while the run built the bindings and installed the frontend first. setup_steps() and plan_steps() now take a TreeState: the checkout as it is, or the lint tree tree_of() makes (no build output, node_modules only where it links the checkout's). Test: TuiBindingsTests.test_a_dry_run_for_a_lint_tree_lists_the_builds_and_installs_that_tree_needs fails on c146f28 (dry run lists only `sh check.sh`) and passes here; tests/test_web_lint_prepush.py: 48 tests OK. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-a7306ef145c005ad24ab
|
Review (codex gpt-6-astra high, fast, review-router; drain-other): SHIP at 1f157f0 [P3] Preserve Node heap limits using underscore syntax — scripts/web_lint_prepush.py:406. Node accepts |
Conflict: tests/test_web_lint_prepush.py imports (kept both io and json). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3aa8a14a36831eaec813
… NAME:pieces Since #24438 webviews' check is run-check-pieces.py over check:pieces. check_pieces() already reads SCRIPT:pieces; light_leaves() now does the same for `bun run NAME`, so a nested runner is never run whole as one (light-looking) python3 command. Tests: a heavy=0 push with the runner-shaped check runs only the allowlisted pieces, and a nested runner resolves to its pieces. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3aa8a14a36831eaec813
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
|
Review (codex gpt-6-astra high, fast, review-router; drain-other): BLOCK at 014ded9 [P2] [P3] |
…ive PATH entry Review of 014ded9: CMUX_LOCAL_HEAVY_CHECKS=0 inherited by a CI runner made CI run only the light allowlist. CI (CI or GITHUB_ACTIONS=true) now wins over the host setting. ensure_deps() refused any bun that shutil.which() found through a relative PATH entry (node_modules/.bin/bun) as "bun is not on PATH"; it now refuses only when which() finds none. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3aa8a14a36831eaec813
|
Review (codex gpt-6-astra high, fast, review-router; drain-other): BLOCK at 9fee512 [P2] |
Review of 9fee512: run_plan() ran prepare_builds() only when setup_steps() listed a spawn. With neither npm nor bun, a stale bindings build spawns nothing, and a frontend with node_modules and no bun.lock installs nothing, so the frontend check ran against the old bindings. With heavy checks on, run_plan() now always goes through prepare_builds() (ensure_build's failure surfaces), and setup_steps()/plan_steps() list that refusal and no checks after it, so --dry-run matches the run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3aa8a14a36831eaec813
|
Review (codex gpt-6-astra high, fast, review-router; drain-other): SHIP at 1b67ec3 No blocking findings. |
CI now always runs every piece (CI or GITHUB_ACTIONS=true wins over CMUX_LOCAL_HEAVY_CHECKS=0), so the bindings tests that mean "a host with no heavy checks, outside CI" inherited the runner's GITHUB_ACTIONS=true and ran the full install and build on CI. They now pass LIGHT_HOST, which clears both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3aa8a14a36831eaec813
|
Review (codex gpt-6-astra high, fast, review-router; drain-other): SHIP at 1e255ee [P3] [P3] No-heavy deferral output does not include the required build-box command for packages without |
|
Shipped in:
|
|
Shipped in:
|
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
Summary
The pre-push web lint runs webviews' whole
bun run check. Two of its pieces,vp lintandvp check --no-fmt --no-lint, are type-aware (config/vite-plus/check.ts:typeAware,typeCheck) and start tsgolint: about 9 GB and 5 GB, running at the same time. On hosts the fleet registry marks as no-build (shared agent hosts), every push of a webviews change ran both locally. That is a heavy local run on a host where they are banned, and it filled swap there about every three hours.After this change:
A host that sets
CMUX_LOCAL_HEAVY_CHECKS=0runs only an allowlist (LIGHT_COMMANDS), named exactly:vp fmt --check;bun scripts/<name>.tssource checks;web/files (syntax only).bun run NAMEis resolved to NAME's own pieces, and only the allowlisted ones run, socheck:effect-cleanup'svp lintis skipped. The host does no install, no build (the tui bindings build isnpm ciplus tsc) and no--new-onlybase comparison. Every skipped piece, and any package with no node_modules, is named in the output with thecmux-ci test -- bash -c 'cd webviews && bun install --frozen-lockfile && bun run check'command for a build box. A light failure still refuses the push.Everywhere else, every tool the script starts (installs, builds, checks, base reruns) goes through one
spawn(). Outside CI it runs the tool undernice -n 10withGOMAXPROCS(half the cores, at most 8),GOMEMLIMIT=6GiBand--max-old-space-size=4096, each set only when the caller did not set one. CI runs unchanged.The script reads only the environment variable and names no hosts. The fleet's own hook wrapper sets the variable from the registry's per-host build flag, so host lists stay out of this repo.
The
web lint (fast)job on every feat-cmux-next push and the PR fast gates still run the full check.Testing
python3 tests/test_web_lint_prepush.py: all 40 tests pass on the head. The new ones:test_no_mode_spawns_more_than_the_allowlist_on_a_host_that_runs_no_heavy_checks: with heavy=0, asubprocess.runspy sees only allowlisted commands in every mode:--headon the checkout, another commit (lint tree),--new-onlyon a red change,--alland--fix. No install, build, tsc or vp lint/check runs.test_a_host_that_runs_no_heavy_checks_runs_only_the_allowlistandtest_light_pieces_still_refuse_a_bad_push_...: a realgit pushthrough the installed hook. A nestedbun runscript with avp lintinside is skipped, and a light failure still refuses the push.test_a_host_that_runs_no_heavy_checks_installs_and_builds_nothingandtest_a_lint_tree_..._shares_the_frontend_node_modules: the tui frontend case.test_every_spawn_on_a_build_host_is_niced_and_capped,test_a_build_host_runs_every_piece_niced_and_capped,test_new_only_reruns_a_piece_on_the_base_niced_and_cappedandtest_the_bindings_build_runs_niced_and_capped_outside_ci: everything is niced and capped on a build host.test_the_allowlist_is_exactandtest_ci_spawns_as_is_and_a_caller_cap_is_kept.Observed / Not run
Observed: each new test fails on the previous heads (the allowlist and spawn tests on f9399db, the rest on fe31ba9 or 6160990). All 40 pass at the head. A dry run on the real packages with heavy=0 runs only the formatter check and the python3 and bun source checks; it names
vp lint(both),vp checkand the effect-cleanupvp lintas left to CI.Not run: a real
bun run checkon a build host. The stand-ins check niceness and env, not tsgolint's real peak memory.Changelog
none
Proof
No capture: pre-push tooling only, no UI.
Checklist
🤖 Generated with Claude Code
Agent session: agent session