perf(devex): parallel pnpm lint (169s → 50s), capped typecheck threads, fresh shard timings (#33817)

* perf(devex): parallel lint, capped typecheck threads, fresh shard timings

- pnpm lint runs its 37 steps in a bounded pool (run-lint.mjs).
- tsrs defaults to 8 checkers; check:changed --maxWorkers=N passes 2N.
- ci-shard-timings refreshed from an 8-vCPU PR run (old one lacked 1,976 files).
- tc/typecheck/dev/postinstall call node directly instead of nested pnpm run.

* fix(typecheck): keep resolveTypecheckCommand args inferred for mobile's strict test typecheck

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
OrcaWin
2026-10-26 00:39:55 -07:00
committed by GitHub
co-authored by OrcaWin
parent 83ca5b6b6f
commit a25a5f1fa7
8 changed files with 13686 additions and 12213 deletions
+3 -1
View File
@@ -134,7 +134,9 @@ function buildTasks(root, plan, { files, mergeBase, maxWorkers, bun, warmRecords
steps: [
step(node, [
'config/scripts/run-typecheck-projects-in-parallel.mjs',
...plan.typecheckProjects.map((config) => `--project=${config}`)
...plan.typecheckProjects.map((config) => `--project=${config}`),
// A worker cap means a shared host; 4 checkers cut typecheck CPU by about a third.
...(maxWorkers ? [`--checkers=${Math.max(2, Number(maxWorkers) * 2)}`] : [])
])
]
})
@@ -6,6 +6,7 @@ import { runInNewContext } from 'node:vm'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { parse } from 'yaml'
import { findBrokenReadmeLinks, main } from './check-readme-local-links.mjs'
import { LINT_STEPS } from './run-lint.mjs'
const projectDir = path.resolve(import.meta.dirname, '../..')
const tempDirs = []
@@ -175,6 +176,6 @@ describe('README local link check', () => {
expect(scripts['check:readme-local-links']).toBe(
'node config/scripts/check-readme-local-links.mjs'
)
expect(scripts.lint).toContain('pnpm run check:readme-local-links')
expect(LINT_STEPS).toContain('check:readme-local-links')
})
})
File diff suppressed because it is too large Load Diff
+20 -1
View File
@@ -46,7 +46,26 @@ age as specs change. Full CI runs on the existing runner classes are required to
measure elapsed-time and occupancy improvements, including discovery overhead.
No retries, assertions, coverage exclusions, runner classes or shard counts changed.
## Current unit refresh: October 14, 2026
## Current unit refresh: October 25, 2026
The baseline imports all five successful unit shards from
[run 40276310369, attempt 1](https://github.com/stablyai/orca/actions/runs/40276310369/attempts/1),
the runner class PR shards use: `blacksmith-8vcpu-ubuntu-2404`, eight workers, Node
24.22.1, Bun 1.4.4, source `085cfad2cb3b883c975d5630d94fb2900d0bed05`. The previous ARM
four-worker baseline lacked 1,976 current files and left PR shards 13–44% apart.
Balancing two later full runs (40276227094, 40275768715) with each baseline, the fresh one
cut the heaviest shard from 2,129 to 2,008 and from 2,564 to 2,134 worker-seconds, with a
spread of 3–4% instead of 13–44%.
| Shard | Job ID | Measured worker-seconds | Job seconds |
| ----- | ------------ | ----------------------: | ----------: |
| 1 | 121420433743 | 2,476.887 | 381 |
| 2 | 121420433755 | 2,069.007 | 329 |
| 3 | 121420433825 | 2,144.439 | 339 |
| 4 | 121420433749 | 2,050.086 | 322 |
| 5 | 121420433831 | 2,151.221 | 338 |
## Historical unit refresh: October 14, 2026
The baseline imports all five successful ARM unit shards from
[run 38646706136, attempt 1](https://github.com/stablyai/orca/actions/runs/38646706136/attempts/1).
+122
View File
@@ -0,0 +1,122 @@
// `pnpm lint`: runs the independent lint steps in a bounded pool, printing output only for failures.
import { spawn } from 'node:child_process'
import { readFileSync } from 'node:fs'
import { availableParallelism } from 'node:os'
import path from 'node:path'
import process from 'node:process'
import { pathToFileURL } from 'node:url'
// package.json script names, slowest first so short steps fill in around them.
export const LINT_STEPS = [
'check:process-host-imports',
'verify:localization-extraction',
'audit:anti-slop',
'audit:code-quality:type-aware',
'check:test-file-runners',
'check:compat-boundary-imports',
'verify:localization-catalogs',
'verify:localization-coverage',
'check:flaky-test-pattern-ratchet',
'lint:ast-grep',
'check:main-sync-spawn-ratchet',
'lint:js-plugins',
'oxlint',
'check:dead-classes',
'check:layout-writer-ratchets',
'check:e2e-typecheck-ratchet',
'check:focus-read-ratchet',
'audit:code-quality:native',
'check:legacy-relay-fence-ratchet',
'check:runtime-electron-ratchet',
'verify:rpc-params-catalog',
'check:repo-host-read-ratchet',
'test:ast-grep',
'check:max-lines-ratchet',
'check:mobile-shared-dependency-versions',
'check:ts-nocheck-ratchet',
'check:workspace-record-read-ratchet',
'check:unproven-pty-retirement-ratchet',
'check:node-runtime-pin',
'check:path-literal-ratchet',
'check:host-list-render-ratchet',
'verify:skill-bundle-manifest',
'check:reliability-gates',
'verify:bundled-skill-guides',
'check:readme-local-links',
'check:id-only-store-write-ratchet',
'verify:acp-protocol'
]
// Steps with no package script of their own run as the bare command.
const BARE_COMMANDS = { oxlint: 'oxlint' }
function parseMaxWorkers(argv) {
const argument = argv.find((value) => /^--max-?workers=\d+$/i.test(value))
const requested = argument ? Number(argument.split('=')[1]) : 0
// Most steps are multi-threaded themselves, so a quarter of the cores keeps the host responsive.
return requested > 0 ? requested : Math.max(2, Math.floor(availableParallelism() / 4))
}
export function resolveLintCommands(scripts, steps = LINT_STEPS) {
return steps.map((name) => {
const command = scripts[name] ?? BARE_COMMANDS[name]
if (!command) {
throw new Error(`Lint step ${name} has no package.json script.`)
}
return { name, command }
})
}
function runStep({ name, command }, root) {
const startedAt = performance.now()
return new Promise((resolve) => {
let output = ''
// Why a shell: package scripts are shell commands (`a && b`), as pnpm would run them.
const child = spawn(command, { cwd: root, shell: true, windowsHide: true })
child.stdout.on('data', (chunk) => (output += chunk))
child.stderr.on('data', (chunk) => (output += chunk))
const finish = (ok, extra = '') =>
resolve({
name,
ok,
output: output + extra,
elapsed: `${((performance.now() - startedAt) / 1000).toFixed(1)}s`
})
child.on('error', (error) => finish(false, String(error.message ?? error)))
child.on('close', (code) => finish(code === 0))
})
}
async function main(root, argv) {
const startedAt = performance.now()
const { scripts } = JSON.parse(readFileSync(path.join(root, 'package.json'), 'utf8'))
const steps = resolveLintCommands(scripts)
const concurrency = parseMaxWorkers(argv)
const results = []
let next = 0
await Promise.all(
Array.from({ length: Math.min(concurrency, steps.length) }, async () => {
while (next < steps.length) {
const result = await runStep(steps[next++], root)
if (!result.ok) {
console.error(`\n✗ ${result.name} (${result.elapsed})\n${result.output.trimEnd()}`)
}
results.push(result)
}
})
)
const failed = results.filter((result) => !result.ok)
const elapsed = `${((performance.now() - startedAt) / 1000).toFixed(1)}s`
if (failed.length > 0) {
console.error(
`\n✗ ${failed.length} of ${results.length} lint step(s) failed in ${elapsed}: ${failed.map((result) => result.name).join(', ')}`
)
return 1
}
console.log(`✓ ${results.length} lint step(s) passed in ${elapsed}.`)
return 0
}
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
process.exitCode = await main(path.resolve(import.meta.dirname, '../..'), process.argv.slice(2))
}
@@ -79,6 +79,7 @@ export function parseTypecheckOptions(args) {
compiler: 'auto',
bunConcurrency: 2,
bunThreads: 4,
checkers: null,
projects: TYPECHECK_PROJECTS
}
const selected = []
@@ -89,6 +90,8 @@ export function parseTypecheckOptions(args) {
options.bunConcurrency = Number(argument.slice('--bun-concurrency='.length))
} else if (argument.startsWith('--bun-threads=')) {
options.bunThreads = Number(argument.slice('--bun-threads='.length))
} else if (argument.startsWith('--checkers=')) {
options.checkers = Number(argument.slice('--checkers='.length))
} else if (argument.startsWith('--project=')) {
const config = argument.slice('--project='.length)
const project = [...TYPECHECK_PROJECTS, ...MOBILE_TYPECHECK_PROJECTS].find(
@@ -107,6 +110,9 @@ export function parseTypecheckOptions(args) {
throw new Error('Bun concurrency and threads must be integers from 1 to 4')
}
}
if (options.checkers !== null && (!Number.isInteger(options.checkers) || options.checkers < 1)) {
throw new Error('Checkers must be a positive integer')
}
if (selected.length > 0) {
options.projects = selected
}
@@ -152,9 +158,21 @@ export function typecheckBatches(projects, { compiler, bunConcurrency, memoryByt
)
}
export function resolveTypecheckCommand(config, { compiler, bunExecutable, bunThreads, root }) {
// Uncapped tsrs runs ~40 threads on 24 cores; 8 checkers matched its wall time with 16% less CPU under load.
export const TSRS_DEFAULT_CHECKERS = 8
const checkerArgs = (checkers) => (checkers ? ['--checkers', String(checkers)] : [])
export function resolveTypecheckCommand(
config,
{ compiler, bunExecutable, bunThreads, checkers = null, root }
) {
if (compiler === 'tsrs') {
return resolveTsrsTypecheckCommand(config, root)
const command = resolveTsrsTypecheckCommand(config, root)
return {
...command,
args: [...command.args, ...checkerArgs(checkers ?? TSRS_DEFAULT_CHECKERS)]
}
}
const installedCompiler = resolveProjectCompilerInfo(config, root)
if (compiler === 'bun' && installedCompiler.version !== BUN_TYPESCRIPT_VERSION) {
@@ -162,20 +180,27 @@ export function resolveTypecheckCommand(config, { compiler, bunExecutable, bunTh
`Bun CI compiler targets TypeScript ${BUN_TYPESCRIPT_VERSION}, but ${config} resolves ${installedCompiler.version}`
)
}
if (compiler === 'bun') {
return {
program: bunExecutable,
args: ['check', '-p', config, '--threads', String(bunThreads), '--no-pretty', '--all'],
cwd: root
}
}
return {
program: compiler === 'bun' ? bunExecutable : process.execPath,
args:
compiler === 'bun'
? ['check', '-p', config, '--threads', String(bunThreads), '--no-pretty', '--all']
: [installedCompiler.program, '--noEmit', '-p', config],
program: process.execPath,
args: [installedCompiler.program, '--noEmit', '-p', config, ...checkerArgs(checkers)],
cwd: root
}
}
function checkProject(config, { compiler, bunExecutable, bunThreads, root, spawnCompiler }) {
function checkProject(
config,
{ compiler, bunExecutable, bunThreads, checkers, root, spawnCompiler }
) {
return new Promise((resolve, reject) => {
const child = spawnCompiler({
...resolveTypecheckCommand(config, { compiler, bunExecutable, bunThreads, root }),
...resolveTypecheckCommand(config, { compiler, bunExecutable, bunThreads, checkers, root }),
stdio: 'inherit'
})
child.on('error', reject)
@@ -196,6 +221,7 @@ export async function runTypecheckProjects({
bunExecutable = 'bun',
bunConcurrency = 1,
bunThreads = 4,
checkers = null,
root = repoRoot,
projects = TYPECHECK_PROJECTS,
spawnCompiler = spawnProcess,
@@ -217,6 +243,7 @@ export async function runTypecheckProjects({
compiler,
bunExecutable,
bunThreads,
checkers,
root,
spawnCompiler
})
@@ -233,7 +260,14 @@ export async function runTypecheckProjects({
for (const batch of batches) {
const results = await Promise.allSettled(
batch.map((project) =>
checkProject(project.config, { compiler, bunExecutable, bunThreads, root, spawnCompiler })
checkProject(project.config, {
compiler,
bunExecutable,
bunThreads,
checkers,
root,
spawnCompiler
})
)
)
for (const result of results) {
@@ -18,6 +18,7 @@ import {
admissibleHeapGib,
planTypecheckBatches,
resolveProjectCompiler,
resolveTypecheckCommand,
parseTypecheckOptions,
selectTypecheckCompiler,
typecheckBatches,
@@ -230,11 +231,27 @@ describe('local and CI compiler selection', () => {
'--bun-concurrency=0',
'--bun-threads=NaN',
'--bun-concurrency=5',
'--checkers=0',
'--checkers=two',
'--project'
])('rejects unsafe or unknown options: %s', (argument) => {
expect(() => parseTypecheckOptions([argument])).toThrow()
})
it('passes a checker cap to the native compiler only when one is requested', () => {
const command = (checkers) =>
resolveTypecheckCommand('config/tsconfig.node.json', {
compiler: 'native',
checkers,
root: repoRoot
}).args
expect(command(parseTypecheckOptions(['--checkers=4']).checkers).slice(-2)).toEqual([
'--checkers',
'4'
])
expect(command(parseTypecheckOptions([]).checkers)).not.toContain('--checkers')
})
it('bounds Bun project concurrency and reserves memory for overlapping checks', () => {
const options = parseTypecheckOptions([
'--compiler=bun',
+12 -12
View File
@@ -16,7 +16,7 @@
"test:perf:contracts": "vitest run --config config/vitest.performance.config.ts",
"test:perf:benchmarks": "vitest bench --run --config config/vitest.performance.config.ts",
"format": "oxfmt --write .",
"lint": "oxlint && pnpm run lint:js-plugins && pnpm run lint:ast-grep && pnpm run test:ast-grep && pnpm run audit:anti-slop && pnpm run audit:code-quality:native && pnpm run audit:code-quality:type-aware && pnpm run check:reliability-gates && pnpm run check:dead-classes && pnpm run check:max-lines-ratchet && pnpm run check:ts-nocheck-ratchet && pnpm run check:e2e-typecheck-ratchet && pnpm run check:runtime-electron-ratchet && pnpm run check:focus-read-ratchet && pnpm run check:layout-writer-ratchets && pnpm run check:path-literal-ratchet && pnpm run check:legacy-relay-fence-ratchet && pnpm run check:unproven-pty-retirement-ratchet && pnpm run check:id-only-store-write-ratchet && pnpm run check:repo-host-read-ratchet && pnpm run check:workspace-record-read-ratchet && pnpm run check:host-list-render-ratchet && pnpm run check:flaky-test-pattern-ratchet && pnpm run check:readme-local-links && pnpm run check:node-runtime-pin && pnpm run check:test-file-runners && pnpm run check:mobile-shared-dependency-versions && pnpm run verify:rpc-params-catalog && pnpm run verify:acp-protocol && pnpm run verify:bundled-skill-guides && pnpm run verify:skill-bundle-manifest && pnpm run verify:localization-catalogs && pnpm run verify:localization-extraction && pnpm run verify:localization-coverage && pnpm run check:process-host-imports && pnpm run check:main-sync-spawn-ratchet && pnpm run check:compat-boundary-imports",
"lint": "node config/scripts/run-lint.mjs",
"audit:code-quality": "pnpm run audit:code-quality:native && pnpm run audit:code-quality:type-aware && pnpm run audit:react-doctor",
"audit:code-quality:native": "oxlint --config config/oxlint-code-quality-native-plugins.json src config tests mobile --deny-warnings",
"audit:code-quality:type-aware": "oxlint --type-aware --config config/oxlint-code-quality-type-aware.json src config tests --deny-warnings",
@@ -82,24 +82,24 @@
"verify:skill-bundle-manifest": "node config/scripts/generate-skill-bundle-manifest.mjs",
"verify:macos-entitlements": "node config/scripts/verify-macos-entitlements.mjs",
"vendor:feature-wall-assets": "node config/scripts/vendor-feature-wall-assets.mjs",
"tc:node": "pnpm run typecheck:node",
"tc:cli": "pnpm run typecheck:cli",
"tc:web": "pnpm run typecheck:web",
"tc": "pnpm run typecheck",
"typecheck:node": "pnpm run build:packages && node config/scripts/run-typecheck-projects-in-parallel.mjs --project=config/tsconfig.node.json",
"typecheck:cli": "pnpm run build:packages && node config/scripts/run-typecheck-projects-in-parallel.mjs --project=config/tsconfig.tc.cli.json",
"typecheck:web": "pnpm run build:packages && node config/scripts/run-typecheck-projects-in-parallel.mjs --project=config/tsconfig.tc.web.json",
"tc:node": "node config/scripts/build-workspace-packages.mjs && node config/scripts/run-typecheck-projects-in-parallel.mjs --project=config/tsconfig.node.json",
"tc:cli": "node config/scripts/build-workspace-packages.mjs && node config/scripts/run-typecheck-projects-in-parallel.mjs --project=config/tsconfig.tc.cli.json",
"tc:web": "node config/scripts/build-workspace-packages.mjs && node config/scripts/run-typecheck-projects-in-parallel.mjs --project=config/tsconfig.tc.web.json",
"tc": "node config/scripts/build-workspace-packages.mjs && node config/scripts/run-typecheck-projects-in-parallel.mjs",
"typecheck:node": "node config/scripts/build-workspace-packages.mjs && node config/scripts/run-typecheck-projects-in-parallel.mjs --project=config/tsconfig.node.json",
"typecheck:cli": "node config/scripts/build-workspace-packages.mjs && node config/scripts/run-typecheck-projects-in-parallel.mjs --project=config/tsconfig.tc.cli.json",
"typecheck:web": "node config/scripts/build-workspace-packages.mjs && node config/scripts/run-typecheck-projects-in-parallel.mjs --project=config/tsconfig.tc.web.json",
"typecheck:e2e": "pnpm run build:packages && node config/scripts/run-e2e-typecheck.mjs",
"typecheck:tsc:e2e": "pnpm run build:packages && node config/scripts/run-e2e-typecheck.mjs --compiler=native",
"typecheck": "pnpm run build:packages && node config/scripts/run-typecheck-projects-in-parallel.mjs",
"typecheck:ci": "pnpm run build:packages && node config/scripts/run-typecheck-projects-in-parallel.mjs --compiler=bun",
"typecheck": "node config/scripts/build-workspace-packages.mjs && node config/scripts/run-typecheck-projects-in-parallel.mjs",
"typecheck:ci": "node config/scripts/build-workspace-packages.mjs && node config/scripts/run-typecheck-projects-in-parallel.mjs --compiler=bun",
"typecheck:tsc:node": "pnpm run build:packages && node node_modules/typescript/bin/tsc --noEmit -p config/tsconfig.node.json --composite false",
"typecheck:tsc:cli": "pnpm run build:packages && tsc --noEmit -p config/tsconfig.cli.json --composite false",
"typecheck:tsc:web": "pnpm run build:packages && tsc --noEmit -p config/tsconfig.web.json --composite false",
"typecheck:tsc": "pnpm run build:packages && tsc --noEmit -p config/tsconfig.node.json --composite false && tsc --noEmit -p config/tsconfig.cli.json --composite false && tsc --noEmit -p config/tsconfig.web.json --composite false",
"ensure:electron-runtime": "node config/scripts/ensure-native-runtime.mjs --runtime=electron",
"start": "pnpm run build:packages && pnpm run ensure:electron-runtime && electron-vite preview",
"dev": "pnpm run build:packages && pnpm run ensure:electron-runtime && node config/scripts/run-electron-vite-dev.mjs",
"dev": "node config/scripts/build-workspace-packages.mjs && node config/scripts/ensure-native-runtime.mjs --runtime=electron && node config/scripts/run-electron-vite-dev.mjs",
"dev-stable-name": "pnpm run build:packages && pnpm run ensure:electron-runtime && node config/scripts/run-electron-vite-dev.mjs --stable-name",
"dev:fresh": "pnpm run build:packages && node config/scripts/dev-fresh-profile.mjs",
"dev:seed-profile": "node config/scripts/dev-profile-seed.mjs",
@@ -142,7 +142,7 @@
"build:release:javascript:parallel": "pnpm run build:cli && pnpm run build:electron-vite:parallel && pnpm run verify:built-skills-cli && pnpm run build:web-from-renderer && pnpm run build:mobile-web",
"build:release:host": "pnpm run build:wsl-guest && pnpm run build:native && pnpm run verify:computer-native && pnpm run verify:built-skills-cli",
"build:release:parallel": "pnpm run build:wsl-guest && pnpm run build:native && pnpm run verify:computer-native && pnpm run build:cli && pnpm run build:electron-vite:parallel && pnpm run verify:built-skills-cli && pnpm run build:web-from-renderer && pnpm run build:mobile-web",
"postinstall": "pnpm run build:packages && node config/scripts/rebuild-native-deps.mjs",
"postinstall": "node config/scripts/build-workspace-packages.mjs && node config/scripts/rebuild-native-deps.mjs",
"rebuild:electron": "node config/scripts/rebuild-native-deps.mjs",
"reclaim-space": "node config/scripts/reclaim-worktree-space.mjs",
"rebuild:node": "node config/scripts/ensure-native-runtime.mjs --runtime=node --force",