The split tools ran oxfmt in the checkout they rewrite. oxfmt looks up its configuration from that directory and from the formatted files' directories, and runs an oxfmt.config.ts it finds there, so a branch that rehome-split rewrites could run code with the maintainer's permissions. The tools now format through format.mjs, which passes the tools checkout's .oxfmtrc.jsonc with --config and disables the nested lookup, so no configuration file of the rewritten checkout is read or run. With no files to format it does nothing, where oxfmt would format the whole directory. The split-tools tests cover split-methods and move-state on a checkout whose root and src directory commit an oxfmt.config.ts that records it ran.
Split tools
These scripts split a large source file into smaller files without changing behavior, and carry open branches across the split. Each transform is deterministic: the same config gives the same result on any version of the tree, and rehoming depends on that.
Run them from a checkout with node_modules installed. That checkout is the tools directory (--tools) and supplies typescript and oxfmt.
Transforms
split-module.mjs plan|apply <config>moves top-level declarations into new files.plancomputes a manifest from seed declarations, andapplydoes the move.split-methods.mjs apply <config>moves class methods into exported functions that take the instance as a parameter (emitter). The class keeps a one-line stub with the same signature, so callers don't change.move-state.mjs apply <config>moves a group of class fields into a feature's state object, defined in the feature's file withdefineModuleState(packages/compiler/src/backend/llvm/emitter-state.ts). It rewritesthis.<field>andemitter.<field>toKEY.of(this).<field>and creates files from templates. It also applies the change's other edits as literal replacements, in order. A replacement whose text isn't found the expected number of times is reported and skipped.
Each transform takes --repo <dir> (the tree to rewrite) and --tools <dir>, and formats what it writes with the tools checkout's oxfmt and its .oxfmtrc.jsonc (format.mjs). The formatter reads no configuration from --repo, so an oxfmt.config.ts a branch commits is never run. The tools read and write the tree's files only at real paths inside --repo, and a config's templates and manifest only inside the config's directory. A path that resolves elsewhere, through a symlink or .., stops the tool with an error (confine.mjs). configs/ holds the configs of splits that have landed. For example, configs/loop-state/ moves the loop optimizations' state out of LlEmitter.
Rehoming an open branch
git fetch origin pull/<n>/head:pr-<n>
node scripts/split/rehome-split.mjs --branch pr-<n> --onto origin/main --config scripts/split/configs/<split>/<config>.json
The result is the branch pr-<n>-rehomed, a merge of origin/main into the branch.
The script applies the split's transform to the branch's merge base and to its tip, then cherry-picks the difference onto --onto. It records the result as a merge commit with parents (branch tip, --onto), so it can be pushed without rewriting the branch; --squash makes one ordinary commit instead. Conflicts made only of import lines in the split's files resolve to both sides' imports. Any other conflict stops the script, which lists the files and leaves its scratch worktree for you to resolve.