Area
Multiple areas
What are you trying to accomplish?
Make the test suite navigable and CI faster without touching a single assertion.
tests/ holds 1061 *.test.ts files, 1045 of them flat at the root. 102 exceed 800 lines; the largest (codex-catalog.test.ts) is 6807. Ownership is by filename prefix only, so "the server tests" or "the Cursor adapter tests" are a grep, not a directory. Contributors touching one subsystem cannot see which tests own it, and reviewers cannot tell from a PR file list which area a moved test belongs to.
On CI the unsharded macOS job is the critical path on 10 of the last 10 green dev pushes (mean 14.9 min against a 4.7 min Linux shard maximum), so every PR waits ~15 minutes for one job.
What prevents this today?
- Bun discovery already recurses (
bunfig.toml root = "tests"; tests/images/, tests/videos/, tests/e2e-style/ are nested today), so the blocker is not the runner. It is the ~238 path literals in 104 files that name tests/<file> (ci.yml isolated-job lists, scripts/ci/run-bun-test-batches.sh root-anchored exclusions, scripts/test.ts serial lanes, scripts/release.ts, source-oracle tests that read import.meta.dir + "/../src", 15 child-process helpers located by join(import.meta.dir, "helpers", ...), and the CI contract tests that pin all of the above).
bun run test:changed follows only Bun's module graph, so a move that breaks a read-as-text or child-spawn path is invisible to it.
- macOS has no shard because it was deliberately kept as the "whole-pool control"; the comment justifying it as cheapest (5m23s) is stale.
What should OpenCodex do?
- Place tests in
tests/<domain>/ directories that mirror src/ (providers, codex-integration, server, adapters, responses, lab, cli, routing, gui, oauth, claude-integration, ci-workflows, usage, lib, clients, service, windows, storage, vision, config, web-search, update, plus the existing images/videos/e2e-style), two levels deep at most. Helpers and fixtures stay where they are.
- Provide the move as tooling, not a hand edit: a committed
scripts/test-layout/layout.json map, a mover that git mvs one domain slice per PR and rewrites relative imports, dynamic imports, new URL forms and child-helper joins by depth, and a tests/test-layout.test.ts guard that fails when a file lands outside its domain. Source-oracle tests resolve the repository through a tests/helpers/repo-root.ts helper instead of import.meta.dir + "/..".
- Keep
bun run test, bun run test:changed, scripts/ci/run-bun-test-batches.sh and every ci.yml job behaving as today (same files, same isolation groups). Nothing is deleted and no assertion changes.
- Shard the macOS CI job 2-way and keep the unsharded whole-pool run behind
workflow_dispatch (lane=macos-control). Linux stays at 4 shards; measured, going to 6 or 8 saves zero wall minutes while macOS is unsharded. The Windows leg stays dispatch-only and is not touched.
Example usage or interface
Before:
bun test tests/cursor-adapter.test.ts tests/cursor-blob.test.ts # find them by prefix first
After:
bun test tests/providers/cursor # everything that owns src/adapters/cursor
bun scripts/test-layout/plan.ts --domain server # what still needs moving
CI: a dev push today finishes in ~15.4 min (macos 14.9); after the 2-way shard, ~7.7 min with both macos 1/2 and macos 2/2 green and macos control available on dispatch.
Alternatives or workarounds
- Sharding by directory instead of by file: rejected, one fat domain (providers, 201 files) would dominate a shard; file-level
--shard stays.
- Moving
tests/helpers/remove-tree (405 importers) into each domain: rejected, it would touch every test in every PR for no gain.
- Linux 6/8 shards: measured to save nothing while macOS is the critical path.
- macOS 4-way: only ~3 more minutes than 2-way for two more 10x-billed jobs.
Additional context
Design and measurements: devlog/_plan/260905_test_modularization_and_windows/ (001 inventory, 002 codex-rs / hermes reference layouts, 003 CI timing baseline, 030 tooling design, 040 migration slices and the macOS shard). Landing as one tooling PR, six move PRs (one per domain group, git mv so history survives), and one CI PR, each linked here as it opens.
Checks
Area
Multiple areas
What are you trying to accomplish?
Make the test suite navigable and CI faster without touching a single assertion.
tests/holds 1061*.test.tsfiles, 1045 of them flat at the root. 102 exceed 800 lines; the largest (codex-catalog.test.ts) is 6807. Ownership is by filename prefix only, so "the server tests" or "the Cursor adapter tests" are a grep, not a directory. Contributors touching one subsystem cannot see which tests own it, and reviewers cannot tell from a PR file list which area a moved test belongs to.On CI the unsharded macOS job is the critical path on 10 of the last 10 green
devpushes (mean 14.9 min against a 4.7 min Linux shard maximum), so every PR waits ~15 minutes for one job.What prevents this today?
bunfig.tomlroot = "tests";tests/images/,tests/videos/,tests/e2e-style/are nested today), so the blocker is not the runner. It is the ~238 path literals in 104 files that nametests/<file>(ci.yml isolated-job lists,scripts/ci/run-bun-test-batches.shroot-anchored exclusions,scripts/test.tsserial lanes,scripts/release.ts, source-oracle tests that readimport.meta.dir + "/../src", 15 child-process helpers located byjoin(import.meta.dir, "helpers", ...), and the CI contract tests that pin all of the above).bun run test:changedfollows only Bun's module graph, so a move that breaks a read-as-text or child-spawn path is invisible to it.What should OpenCodex do?
tests/<domain>/directories that mirrorsrc/(providers, codex-integration, server, adapters, responses, lab, cli, routing, gui, oauth, claude-integration, ci-workflows, usage, lib, clients, service, windows, storage, vision, config, web-search, update, plus the existing images/videos/e2e-style), two levels deep at most. Helpers and fixtures stay where they are.scripts/test-layout/layout.jsonmap, a mover thatgit mvs one domain slice per PR and rewrites relative imports, dynamic imports,new URLforms and child-helper joins by depth, and atests/test-layout.test.tsguard that fails when a file lands outside its domain. Source-oracle tests resolve the repository through atests/helpers/repo-root.tshelper instead ofimport.meta.dir + "/..".bun run test,bun run test:changed,scripts/ci/run-bun-test-batches.shand every ci.yml job behaving as today (same files, same isolation groups). Nothing is deleted and no assertion changes.workflow_dispatch(lane=macos-control). Linux stays at 4 shards; measured, going to 6 or 8 saves zero wall minutes while macOS is unsharded. The Windows leg stays dispatch-only and is not touched.Example usage or interface
Before:
After:
CI: a
devpush today finishes in ~15.4 min (macos 14.9); after the 2-way shard, ~7.7 min with bothmacos 1/2andmacos 2/2green andmacos controlavailable on dispatch.Alternatives or workarounds
--shardstays.tests/helpers/remove-tree(405 importers) into each domain: rejected, it would touch every test in every PR for no gain.Additional context
Design and measurements:
devlog/_plan/260905_test_modularization_and_windows/(001 inventory, 002 codex-rs / hermes reference layouts, 003 CI timing baseline, 030 tooling design, 040 migration slices and the macOS shard). Landing as one tooling PR, six move PRs (one per domain group,git mvso history survives), and one CI PR, each linked here as it opens.Checks