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

# What tp init creates

> What tp init creates, what the CLI uploads, and what never leaves your laptop

A TunePlane **lab** is a normal git repository on your machine. The console never clones it. `tp submit` packs a subset of files, uploads that bundle, and the server injects the Job Capsule on top.

## After `tp init my-lab --yes`

```text theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
my-lab/
├── tuneplane.yaml      # repo marker + project name (only signal `tuneplane` uses to find the root)
├── experiments/        # tp new creates directories here
├── configs/            # official bases + model fragments; yours to edit or pin
├── common/             # shared code: data scripts, environments, rewards
├── .gitignore
└── README.md
```

`tuneplane.yaml` currently needs a `name` whose characters match `[A-Za-z0-9._-]`. That name is the console project. Do not pass `--project` on submit.

```yaml theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
name: my-lab
```

Commands that need a project (`tp new`, `submit`, `ls`, `validate`) walk up from the current directory looking for this file. CI can set `TUNEPLANE_CLIENT_REPO_ROOT` instead of `cd`. `tp login` / `logout` / `status` are global and do not need it.

## One experiment directory

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
tp new my-grpo --method nemo-rl/grpo
```

```text theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
experiments/my-grpo/
├── config.yaml           # your diffs; inherits via defaults
├── README.md             # scaffold notes, not executed
└── recipe.lock.json      # method + framework pin
```

`custom/custom` also gets `train.sh` (the only entrypoint). You can add `train.py` next to it. Plugins add `plugins.lock.json` after `tp 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 `tuneplane.contract.env`):

| Pattern                                     | Why it is dropped                                                               |
| ------------------------------------------- | ------------------------------------------------------------------------------- |
| `.tuneplane-data/**`                        | Local credentials. Putting this in the bundle would ship tokens to the cluster. |
| `.git/**`                                   | History is recorded as commit SHA in provenance, not as a clone.                |
| `**/outputs/**`                             | Local leftover artifacts. Cluster output is `$TUNEPLANE_OUT_DIR`.               |
| `datasets/**/raw/**`, `datasets/**/data/**` | Raw data volume. Use a [platform dataset](/en/guides/datasets) or an HF id.     |
| `**/__pycache__/**`                         | Bytecode.                                                                       |
| `**/*.key`, `**/secrets.env`                | Secrets belong on the server, not in the job package.                           |

The JobSpec lands in the bundle at `.tuneplane/jobspec.json`. That path is inside `.tuneplane/`, not `.tuneplane/`. Mixing the two directories is a real footgun: `.tuneplane` 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 `--profile` and the registry.

## Git

Submit records `NRL_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 `tp init` (the default) so the first commit is not an afterthought.

Git is how a run becomes reproducible, not a requirement for submitting. A lab that is not a repository — `tp init --no-git`, a directory copied from somewhere, a machine with no git installed — submits with no commit recorded, and the console shows the run without provenance. A lab that *is* a repository but has no commit yet keeps its `.gitignore`: git still lists the files, only the commit is missing. What changes when there is no repository at all is the file list: instead of git applying `.gitignore`, the bundle is the same three directories walked against the platform's built-in exclusions (`outputs/`, `tuneplane_plugins/`, `__pycache__/`, `.venv/`, the caches, and anything that looks like a key), and it is refused above 64 MB so an artifact nobody meant to ship cannot quietly become an upload. Raise that ceiling with `TUNEPLANE_CLIENT_MAX_PACKAGE_MB` if a lab really does carry something large.

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
git add -A && git commit -m "first grpo config"
tp submit my-grpo --profile h200:8
```
