Skip to main content
patch.py
plugin.yaml
That is a complete algorithm plugin. The launcher imports patch, calls install, and your change is in effect for the rest of the job.

When your function is called

Two load modes, and the choice is about one thing: whether your patch needs the training context.
Called by the launcher before the training entrypoint runs, with the job’s hyperparameters.
params is spec.hyperparams — the flattened --set overrides and the resolved config values for this submission. Use eager when your patch only needs to replace a function.
The name passed to install_deferred is the manifest’s name, not the full <owner>/<name>. Calling it with an unregistered name raises and lists what is registered.

What the launcher does

1

Verifies the injected package

Recomputes the digest of forge_plugins/<name>/ and compares it to the JobSpec. A mismatch stops the job.
2

Checks SDK compatibility

If the manifest declares requires.core, the running starforge-core must satisfy it.
3

Puts the package root on sys.path and imports

Which is why top-level names that shadow real dependencies are refused at publish time.
4

Calls or registers

eager → calls install(params) immediately. deferred → registers it under the manifest name for the training entrypoint to call.
Only kind: algorithm is loaded on the cluster. Other executable kinds are skipped with a log line — an environment plugin is loaded by the environment machinery, and a data-prep plugin never leaves your laptop.

Writing the function

Rules that are worth stating because breaking them fails in confusing places:
  • Raise on a real problem. An exception during install stops the job. That is correct: a patch that silently did not apply produces a run whose numbers mean something other than what the experiment claims.
  • Be idempotent. With multi-node or restart-based executors your module can be imported more than once. Guard against double-wrapping a function you already wrapped.
  • Do not import the training framework at module import time unless you are certain it is present. Import inside install, where you know the container is the training container.
  • Read configuration from params, not from the environment. params is recorded with the job, so a reader can see what your patch was told. An environment variable is not.

Why monkey-patching is a supported mechanism here

For a cluster on an isolated network that cannot install new dependencies, a zero-dependency patch applied at runtime is a decisive advantage over shipping a forked framework image.What makes it acceptable rather than reckless is that the patch is declared, versioned and digest pinned: the platform can say which patch, at which version, ran in which job. An undeclared monkey-patch buried in a training script has all the same risks and none of that record.

Two sources, one resolution order

A job can carry patches from two places: When both name the same patch, the plugin package wins. An explicitly locked version has to beat an implicit built-in.

Confirm it worked

The job log shows one line per plugin:
If you see neither, the experiment does not reference the plugin — check plugins.lock.json and resubmit.