sf submit packs a subset of files, uploads that bundle, and the server injects the Job Capsule on top.
After sf init my-lab --yes
starforge.yaml currently needs a name whose characters match [A-Za-z0-9._-]. That name is the console project. Do not pass --project on submit.
sf new, submit, ls, validate) walk up from the current directory looking for this file. CI can set SF_REPO_ROOT instead of cd. sf login / logout / status are global and do not need it.
One experiment directory
custom/custom also gets train.sh (the only entrypoint). You can add train.py next to it. Plugins add plugins.lock.json after sf plugin install … --exp.
Do not put a framework file in the experiment and expect the platform to notice. The lock file is the method.
What gets uploaded
The CLI packs the experiment,common/, and configs/. The same exclude list is shared with the server (PACKAGE_EXCLUDES in starforge.contract.env):
The JobSpec lands in the bundle at
.starforge/jobspec.json. That path is inside .starforge/, not .forge/. Mixing the two directories is a real footgun: .forge is excluded, so a spec written there never reaches the cluster.
What does not live in the lab
- Cluster kubeconfig, Slurm JWT, object-storage keys
- The Job Capsule (
runner.pex). The server injects it after admit. - Hardware parallelism. That comes from
--profileand the registry.
Git
Submit recordsNRL_GIT_COMMIT and NRL_GIT_DIRTY. A dirty tree is rejected unless you pass --allow-dirty. Untracked files are listed as warnings either way. Initialize git at sf init (the default) so the first commit is not an afterthought.