> ## Documentation Index
> Fetch the complete documentation index at: https://starforge.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Recipes and version locking

> Method catalog, recipe.lock.json, and the framework version matrix

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
sf methods                    # what this deployment publishes
sf recipe status my-grpo      # what my experiment is pinned to
sf recipe upgrade my-grpo     # move it to the current catalog
```

A **recipe** is the full declaration of a post-training method: entrypoint, tunable hyperparameters
with their types and ranges, supported framework versions, image artifact, metrics contract,
artifacts contract. All of them together are the **catalog**.

The client and the server load the same files from `starforge-core`, so the two cannot disagree
about what a method is.

## Method ids

Two-part `<framework>/<method>`:

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
sf methods                  # list
sf methods nemo-rl/grpo     # hyperparameters for this method
```

Current catalog: **NeMo-RL**, **verl**, **TRL**, **OpenRLHF**, **evalkit** (benchmarks), **custom**. Full table: [Method catalog](/en/guides/methods).

## Lockfile: recipe.lock.json

`sf new` writes `recipe.lock.json`:

| Field                        | Meaning                                                                    |
| ---------------------------- | -------------------------------------------------------------------------- |
| `recipe.name` / `version`    | Method and recipe version                                                  |
| `recipe.digest`              | Digest of manifest + templates                                             |
| `framework.kind` / `version` | Exact framework version (`nemo-rl@0.7.0`, `verl@0.9.0`, `openrlhf@0.11.0`) |
| `framework.runtime_id`       | Key for the deployment artifact (OCI image / SIF / SQSH)                   |
| `requires.core`              | Compatible `starforge-core` range                                          |

At submit the CLI handshakes with the server catalog. The lock must match the published recipe exactly.

<Warning>
  After the platform publishes a new recipe version, an old lock is rejected. That is intentional. `sf recipe status` shows the diff; `sf recipe upgrade` applies it.
</Warning>

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
sf recipe status my-exp
sf recipe upgrade my-exp
sf recipe upgrade my-exp --framework-version 0.9.0
sf submit my-exp --upgrade-recipe
```

## Framework version matrix

One recipe can publish several framework versions at once (`verl/grpo` has `0.8.0` and `0.9.0`). Entrypoint changes, parameter-path moves, and images are declared on the version, not as branches in adapter code:

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
sf new my-verl --method verl/grpo --framework-version 0.9.0
```

* **Entrypoint override**: verl 0.9 dropped `main_ppo_sync` for a unified `main_ppo`.
* **Parameter path override**: `path_overrides` when a hyperparameter moved in the config tree.
* **Runtime artifact**: each version binds a `runtime_id`, resolved from a framework default, the deployment registry, or `--image`. Prefer a digest in production. Slurm wants SIF/SQSH.

<Tip>
  Adopting a new upstream version is usually YAML + an image, not a platform code change. [Upstream adoption](/en/ops/upgrades).
</Tip>

## Custom recipe

Frameworks that are not in the catalog use `custom/custom`. The platform runs `train.sh` in the experiment directory. It does not guess an entrypoint, and it does not fall back to custom after another adapter fails.

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
sf new my-custom --method custom/custom
sf submit my-custom --profile h200:8 \
  --image myregistry.io/my-train:v1
```

`--image` is required. Tags work; pin a digest in production. The registry must be on `FORGE_ALLOWED_IMAGE_REGISTRIES`.

The catalog custom recipe currently defaults to external observability, so those submits still need `--observability-url`. Logs follow stdout. Console curves need `starforge.report` in training code. Cookbook: [Custom training](/en/guides/custom-training). Image build: [Custom images](/en/guides/custom-images).

## Config layers

Effective config is four layers, later wins:

```text theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
recipe template config.yaml          # official base (e.g. the grpo_math_1B.yaml chain)
└─ experiment config.yaml            # your diffs (defaults + overrides)
   └─ hardware profile               # server registry: parallelism, memory knobs
      └─ sf submit --set k=v         # one-shot override, type-checked locally
```

`sf validate` walks the same stack locally. Struct mode rejects unknown keys.
