Skip to content

Forking Policy

When Marin needs a modified version of an upstream package, we maintain a fork under the marin-community GitHub organization rather than vendoring code into the Marin monorepo. Each fork carries Marin's patches on top of an upstream base, and an automated weekly process keeps it close to upstream. This page describes how Marin pins a fork and how the refresh runs.

How Marin pins a fork

Every fork is pinned under config/external/. The pins feed config/update-external.py, which regenerates lib/marin/src/marin/external_dependencies.py; nothing else imports a fork revision directly. There are three pin kinds:

  • Isolated uv lock (evalchemy, harbor, MarinSkyRL): the fork is a git dependency in config/external/<fork>/uv.lock. uv run config/update-external.py <fork> advances the lock and regenerates the pins.
  • Descriptor SHA (vllm TPU source, tpu-inference): the exact pair lives in config/external/vllm/tpu.toml. Its vLLM commit comes from the same maintained main lineage as GPU releases. The stack runs from an isolated uvx environment, so its jax/jaxlib/libtpu/torch come from the forks' own dependencies and there is no workspace lock change.
  • Release wheel (vllm GPU): config/external/vllm/gpu.toml records a promoted, immutable wheel per architecture. See the GPU release pipeline below.
  • Pinned wheel URL (xla): the gpu extra's jax-cuda13-pjrt source in lib/marin/pyproject.toml points at a promoted wheel from the fork's release, and uv.lock records its hash. This fork sits outside the weekly refresh; see the XLA fork section below.

The weekly refresh

.github/workflows/ops-fork-ferry.yaml runs every Monday at 08:00 UTC. It has one matrix leg per fork unit (tpu-vllm, vllm-gpu, evalchemy, harbor). Each leg mints a GitHub OIDC token, exchanges it for a short-lived Loom token, and launches one Weaver session for that unit. The fork-ferry Loom profile admits all four scheduled units; a test keeps its concurrency limit at or above the matrix size. There is no stored PAT.

MarinSkyRL is pinned and refreshable through the skill on demand, but is not yet in the weekly rotation. A human runs the skill for a single fork the same way.

The session runs the refresh-fork skill (.agents/skills/refresh-fork/SKILL.md), which owns the migration procedure: select a new upstream base or existing main-line source, replay Marin's overlays for overlay forks, stage rebases on main-next, re-pin Marin, run the fork's declared end-to-end test, and on green open one draft Marin PR requesting the descriptor's reviewer. On an unresolved external blocker it files a "can't migrate" issue instead of a PR.

config/external/migration.toml is the per-fork descriptor. It records the upstream repository, the pin kind, the fork branch the pin tracks, how to select a new base, the validating e2e, the blocker assignee, and the nuances a refresh must respect (a held torch revision or a CUDA/torch/stable-ABI boundary that turns a bump into a migration).

Validation

Each descriptor names one required e2e that runs before the PR opens:

Fork End-to-end
vllm TPU source, tpu-inference marin-community/vllm:.github/workflows/marin-gpu-release.yaml#tpu (Qwen3-0.6B TP8 on v6e-8)
vllm (GPU) tests/cluster/vllm/test_snowball_backend_parity.py
evalchemy experiments/evaluation/configs/evalchemy/gsm8k-smoke.yaml
harbor experiments/evaluation/configs/harbor/aime-smoke.yaml
MarinSkyRL experiments/post_training/iceball_micro.py

When an e2e fails, the refresh reruns the same workload against Marin's current pins on the old fork stack. It fixes only failures that pass on the old stack and regress on the refreshed one. A workload already broken on the old stack is recorded as a baseline failure and left for its own fix.

The vLLM GPU release pipeline

The GPU pin resolves to a prebuilt wheel. The current marin-community/vllm pipeline builds an immutable CUDA 13.2 x86_64 wheel for H100, validates the exact wheel bytes on real GPUs, and publishes a GitHub release carrying marin-vllm-gpu-manifest.json. The GPU overlay lives on the fork's main, which the candidate build triggers on.

A refresh dispatches those workflows against the staged main-next branch, waits for the promoted release, downloads the manifest, and re-pins with:

uv run config/update-external.py --promote-gpu-release marin-vllm-gpu-manifest.json

That command writes gpu.toml (release tag, source commit, version, torch backend, and each arch's wheel URL and SHA-256) and regenerates the pins. Do not hand-edit gpu.toml; the helper re-encodes the wheel URLs the way the pin loader validates.

Promotion

The refresh never force-moves a fork's stable branch. A rebase stages on main-next and leaves the protected stable branch at the old tip; the draft Marin PR names the main-next to main hard swap an admin performs after review. Because the staged tip and the eventual stable tip are the same commit, the pins need no change after promotion.

The vLLM TPU selector is the exception: it reuses an exact commit already on the fork's main lineage, so it has no vLLM staging branch or protected-branch promotion. Its paired tpu-inference refresh still follows the normal main-next promotion protocol.

Existing forks

Fork Repository Tracks upstream Pin
vLLM TPU source marin-community/vllm vllm-project/vllm exact main-line SHA (tpu.toml)
vllm (GPU) marin-community/vllm vllm-project/vllm release wheel from main (gpu.toml)
tpu-inference marin-community/tpu-inference vllm-project/tpu-inference descriptor SHA (tpu.toml)
evalchemy marin-community/evalchemy mlfoundations/evalchemy isolated uv lock
harbor marin-community/harbor harbor-framework/harbor isolated uv lock
MarinSkyRL marin-community/MarinSkyRL NovaSky-AI/SkyRL isolated uv lock
xla marin-community/xla openxla/xla wheel URL in lib/marin/pyproject.toml (gpu extra)

The XLA fork

The fork exists to carry two ragged all-to-all device-kernel patches on the GPU PJRT plugin, which only that plugin binary embeds. It is not in config/external/migration.toml and the weekly refresh never advances it, because its base is not a refreshable choice: jax pins an exact XLA revision by integrity hash, so the fork must sit on the XLA commit that the workspace's pinned jax[cuda13] release names, and it moves only when that pin moves. Every sibling package (jax, jaxlib, jax-cuda13-plugin) installs stock at the pin, and the wheel's +marin.<sha> local version still satisfies the stock jax-cuda13-pjrt== requirement.

A refresh happens only after a jax bump; the step-by-step rebuild-and-re-pin procedure is the refresh-fork skill's .agents/skills/refresh-fork/docs/xla.md. The pin gate is policy: do not pin a wheel whose manifest's validation block is not status: passed. A patch can compile in and still be inert, and an inert patch passes ordinary correctness tests, including the e2e (tests/cluster/grug/test_ragged_ep_check.py, cluster-marked), so the promote job's validation proves the patched behaviors are live, and a patch that adds another required behavior adds its own check there.

When to fork

Fork only when upstream has not accepted patches you need, the upstream release cadence is too slow for a fix you need now, or you need a custom build such as TPU-specific or GPU-specific wheels. Prefer upstreaming changes. A fork is ongoing maintenance overhead even with the weekly refresh, and every retained patch needs a reason and a drop condition so the overlay shrinks as upstream absorbs it.