name: benchmark description: Benchmark runner, fixture repos, and publication. Read before running, modifying, or publishing benchmark results.
Benchmark
What it measures
ttsc + @ttsc/lint + ttsc format versus the legacy tsc + eslint + prettier toolchain, on seven real-world TypeScript projects. experimental/benchmark/performance.mjs clones each fixture's three branches into experimental/benchmark/.work/, replays each cell RUNS times, and writes the medians to website/public/benchmark/performance.json for the public dashboard at https://ttsc.dev/benchmark.
experimental/benchmark/graph.mjs is a separate AI-token benchmark for @ttsc/graph and the codegraph comparator. It reuses the same fixture clones and setup path, runs projects sequentially, fixes reasoning effort to high, and upserts its own cells into website/public/benchmark/graph.json. The codegraph arm runs codegraph init before the agent cells, records that setup as toolSetupMs, local-ignores .codegraph/, and removes the index unless --keep-codegraph-index is passed.
Cell ID = project:branch:op:threading.
| Axis | Values |
|---|---|
| Project | vue, rxjs, typeorm, zod, nestjs, vscode, shopping-backend |
| Branch | legacy, ttsc, ttsc-lint |
| Op | build, noEmit, eslint (legacy only), format |
| Threading | single, checkers2, checkers4, checkers8 (format keeps single + default multi) |
Methodology, per-axis interpretation, and dashboard tabs are documented in website/src/content/docs/benchmark/performance.mdx. Flags and env vars are tabled in experimental/benchmark/README.md. This skill covers the higher-level rules that those references assume.
Running
node experimental/benchmark/performance.mjs # full matrix sweep
node experimental/benchmark/performance.mjs --project=vue --no-website # one fixture, do not touch dashboard
node experimental/benchmark/performance.mjs --verify-only # one pass per cell, no timing
node experimental/benchmark/performance.mjs --list # print resolved grid
node experimental/benchmark/performance.mjs --sequential # clone, measure, delete one fixture at a time (low-disk)
node experimental/benchmark/graph.mjs --project=typeorm --tools=ttsc-graph,codegraph # one graph AI-token fixture
Option families:
- Scope:
--project,--cell-filter,--lint-only,--format-only,--ttsc-build-only. Every targeted run pairs with--no-websiteso the dashboard stays consistent. - Setup:
--setup-only,--no-setup,--no-install,--no-pack(orTTSC_BENCH_SKIP_PACK=1),--force-install. Setup packs the localttscworkspace into tarballs, clones the fixtures, installs the tarballs, and runsttsc prepareso plugin binaries are warm before any measured cell. - Sampling:
TTSC_BENCH_RUNS(5),TTSC_BENCH_WARMUP(1),TTSC_BENCH_RETRIES(2, applied only torace-classified failures). The reported number is the median;minand the full sample list stay inreport.jsonfor audit. - Host gate:
TTSC_BENCH_REQUIRE_QUIET=1upgrades the load-average warning into a hard error and is set for every publication run;TTSC_BENCH_SKIP_LOAD_CHECK=1silences it for development iterations. - Output:
--no-websiteskips merging intowebsite/public/benchmark/performance.json;--resetdiscards prior measurements instead of merging in place;TTSC_BENCH_OUTredirects the local report;--verbosetees child stdio with[cmd]/[step]/[timer]traces (default is milestone-only). - Disk-cheap mode:
--sequential(envTTSC_BENCH_SEQUENTIAL=1) holds only one(project, branch)clone in.work/at a time: clone, measure, delete, next. Incompatible with--setup-only/--no-setup. - Publication host: run publication sweeps on a quiet external host, then commit the resulting
website/public/benchmark/performance.json.merge-website.mjsremains available when merging partialreport.jsonfiles by cell id.
Publication sweep:
TTSC_BENCH_REQUIRE_QUIET=1 node experimental/benchmark/performance.mjs
After the sweep, inspect the diff against website/public/benchmark/performance.json: every fixture row present, row order preserved, host panel reflects the machine that produced the numbers.
Fixture repositories
Each fixture is a forked GitHub repo at samchon/ttsc-benchmark-<name>, plus samchon/shopping-backend for the plugin-heavy case. Every fixture carries three independent branches:
legacy: upstream source with stocktsc,eslint, andprettier. TypeScript pinned to the Legacy TypeScript version shown on the dashboard host panel (currentlyv6.0.3).ttsc: same source aslegacy, withtscswapped forttscon the pinned TypeScript-Gotypescript@rcruntime and the workspace configured to installttscfrom the tarball the runner packs.ttsc-lint: same source asttsc, with@ttsc/lintfolded into the compile pass so theeslintstep is no longer invoked.
Source parity
The three branches differ only in tooling, package.json, tsconfig*.json, eslint.config.*, .prettierrc*, lockfile, ttsc plugin descriptors. Application source is identical across legacy, ttsc, and ttsc-lint, so a cell delta reflects tool cost rather than workload drift.
Lint and format scope is the tsconfig program
Across legacy and ttsc-lint, the lint and format cells process exactly the file set tsconfig.json compiles for that fixture. eslint and @ttsc/lint lint that set; prettier --check and ttsc format format that set.
Nothing inside the program is excluded; nothing outside it is targeted. Carve-outs via --ignore-pattern, eslint.config ignores, .prettierignore, or extra files/exclude entries are rejected because they make the cells incomparable.
Editing workflow
Edits go to the fixture repo on GitHub, not to the local clone. Setup runs fetch + reset --hard to the upstream branch tip on every run, so local changes in .work/ disappear immediately.
Finish every fixture-branch edit by running the branch's own build, format, and lint commands (e.g. pnpm build, prettier --write or ttsc format, eslint --fix) until the tree is green, then commit and push. A half-finished tip pollutes every later run because the runner pulls upstream every setup.
READMEs and prose docs inside a fixture repo follow the same writing rules as ttsc itself. See AGENTS.md ## Maintenance 짠 Writing style and .codex/skills/documentation/SKILL.md 짠 READMEs.
Other rules
- No tarball or built artifact in the fixture. The runner packs and installs the local
ttscworkspace during setup. The fixture repo must not carry pre-baked tarball paths, vendoredttscbuilds, or staledist/output. - Pin major TypeScript in lockstep. When the Legacy TypeScript headline version bumps, update every fixture's
legacybranch in the same release so thetscbaseline stays one major across the matrix. - Add fixtures by adding repos. A new fixture means a new
samchon/ttsc-benchmark-<name>repo carrying all three branches, plus aPACKAGE_CONFIGSentry at the top ofperformance.mjs. Multi-fixture tricks inside one repo are out of scope. - Removed comparisons stay removed. The
type-festfixture was dropped deliberately. Thetsgorows (raw TypeScript-Go measured on the samettscclone forbuild/noEmit, cell IDproject:ttsc:tsgo:op:threading) are a kept reference for launcher overhead, not eligible for the headline winner.