| name | cell-source-isolation |
| description | Use when writing or editing source files inside an `experiments/expNN_*/eNNx/` cell directory, or about to add a `_lib/` / `utils/` / `helpers/` directory under `experiments/` — enforces root-canonical filename rule (cell source files must mirror project-root filenames), forbids shared helper directories, and gates function reuse to two options: import unmodified from root, or copy the entire root file into the cell and edit the copy. |
Cell Source Isolation
Overview
A cell is a frozen snapshot of source files at the time of run. Once a cell has been run and its outputs / metrics committed, its source files are evidence, not editable — and duplication across cells is the point, because it guarantees each cell can be re-run independently without depending on shared state that some other cell may have quietly changed.
Violating the letter of the rules is violating the spirit of the rules.
When to Use
- Writing or editing source files inside a cell directory (
experiments/expNN_*/eNNx/)
- Writing the runner script for a new cell
- Deciding where a cache should live (cell-local vs shared at experiment level)
- Considering adding a shared helper directory (
_lib/, utils/, helpers/, common/) under experiments/
Terminology
- Root files: the canonical source files at the project root (e.g.
train.py, simulate.py, benchmark.py, pipeline.py, etc.) that
define the production / default pipeline.
- Runner script: whatever script actually launches the cell — e.g. a
SLURM
run.sbatch, a run.sh, a run.bat, a Makefile target, or a
docker-compose.yml. Use whatever is idiomatic for the project.
- Root-canonical filename: a source filename that exists at the project
root. Cell source files must mirror these names — a cell's
train.py is
a copy (possibly modified) of the project-root train.py, never a new
invented filename.
For the definitions of experiment (expNN_*/) and cell (eNNx/), and
for the rules about creating / numbering / not editing prior experiment
directories, see the sibling experiment-layout skill.
The Rules
1. Cell directories contain ONLY root-canonical filenames
The ONLY source files allowed in experiments/expNN_*/eNNx/:
| File | Purpose |
|---|
Copies of root files (e.g. train.py, simulate.py, pipeline.py, …) | Copied verbatim from the project root, then optionally modified for this variant. Only copy a root file if this cell needs to modify it. |
Runner script (e.g. run.sbatch, run.sh, Makefile) | Cell-specific job script that launches this cell. |
NOT allowed under any circumstance:
_lib/, lib/, helpers/, utils/, common/ — no shared-helper directories
utils.py, helpers.py, model.py, data.py, *_helpers.py, extra_*.py — no new top-level source files
- Any source filename that does not exist at the project root
Auto-generated artifacts that go in cell dirs are fine (and should be
gitignored). Typical examples include:
logs/, checkpoints/, outputs/, results/, _runtime/, .cache/
Use whatever names are idiomatic for the project — the rule is just that
source code must mirror root filenames; artifacts are unrestricted.
2. Function reuse — import or copy
Two and only two ways to use a root function in a cell:
| Need | Action |
|---|
| Use a root function unmodified | Import it from the root module (e.g. from pipeline import preprocess, from simulate import run_step). Root files are imported as-is. |
| Modify a root function for this variant | Copy the entire root file into the cell, edit the copy. The cell's local copy shadows the root for that cell. |
You never edit the root file from inside an experiment. Root is the
production pipeline; experiments cannot mutate it. New helpers needed by an
experiment go INSIDE the copied root file in the cell.
If multiple cells need the same new helper, each cell carries its own copy
of the modified root file. The duplication is the point — it guarantees
that re-running cell e04b a year from now does not depend on a _lib/
function that some other cell quietly changed.
3. Each cell has its own runner script
The runner script (whatever form it takes — sbatch, shell script, Makefile,
batch file, docker run invocation):
- Runs from the cell's directory (e.g.
cd …/experiments/expNN_*/eNNx or
the equivalent --chdir flag)
- Invokes the cell's local copies of source files where present, and root
files otherwise
- Writes outputs / logs into the cell's own artifact directories
4. Caches: cell-local or shared at experiment level
Allowed cache locations:
| Path | Scope |
|---|
eNNx/<artifact_dir>/ (logs, checkpoints, outputs, runtime, etc.) | One cell |
expNN_*/_shared_<name>_cache/ | All cells of this experiment that share the same input (dataset, simulation seed, fixture, etc.) |
Caches are auto-generated, not source. Add binary artifacts to .gitignore.
Source code never goes under _shared_*.
Quick Reference
What may live inside a cell directory:
experiments/expNN_*/eNNx/
├── train.py ← copy of root train.py (only if this cell modifies it)
├── simulate.py ← copy of root simulate.py (only if this cell modifies it)
├── run.sbatch ← runner script (sbatch / sh / Makefile / etc.)
├── logs/ ← auto-generated, gitignored
├── checkpoints/ ← auto-generated, gitignored
└── outputs/ ← auto-generated, gitignored
experiments/expNN_*/
└── _shared_<name>_cache/ ← optional, shared across cells of THIS experiment only
NOT allowed inside a cell directory:
eNNx/
├── <name>.py ✗ if <name>.py does NOT exist at the project root
├── extra_<name>.py ✗ invented filename, not root-canonical
├── <name>_v2.py ✗ ditto — modified copies keep the original name
├── lib/ ✗ no source-code subdirs, regardless of what's in root
├── helpers/ ✗ ditto
└── utils/ ✗ ditto (even if `utils/` exists at root — flatten into the copied root file)
The test for a source filename is: does this exact filename exist at the
project root (or src/)? If yes, it's allowed (as a verbatim or modified
copy). If no, it's forbidden — even if the name is conventional. So a
cell's utils.py is fine iff the project root has utils.py; otherwise
the helper goes inside the copied root file that needs it.
Artifact directories are unrestricted — name them whatever the project uses.
Decision Flow
Need a function from the root pipeline in this cell?
│
▼
Use it unmodified?
│
├── yes ──▶ Import it: `from <root_module> import <fn>`
│
└── no, need to modify it
│
▼
Copy the ENTIRE root file into the cell.
Edit the copy. The cell's local copy shadows
root for this cell.
│
▼
Other cells need the same change?
│
└── yes ──▶ Each cell carries its own copy.
Duplication is the point —
never extract into `_lib/`.
Common Mistakes & Rationalizations
| Excuse / mistake | Reality |
|---|
"I'll add a _lib/ for shared helpers across cells" | Forbidden. Helpers live inside the copied root file in each cell. The duplication ensures isolation. |
| "I'll edit the root pipeline file to add the new function this experiment needs" | Forbidden. Root is read-only from experiments. Copy the file into the cell and edit the copy. |
"I'll just put a utils.py in the cell, it's still inside the cell dir" | Forbidden. Source filenames inside a cell must match a root canonical file. |
Red Flags — STOP and Reset
If you find yourself about to do any of these, STOP, surface the situation to
the user, and propose a non-destructive alternative:
- Creating a source file in a cell whose name does not exist at the project root
- Creating a source-code subdirectory under a cell (artifact dirs like
logs/, outputs/, checkpoints/ are fine; lib/, utils/, helpers/ are not)
- Adding a new function to a root source file "to support an experiment"
Why These Rules Exist
The rules look pedantic. They exist because every weakening creates a path to
non-reproducible results.
_lib/ rule: A helper used by 5 cells, edited mid-experiment to fix
a bug for cell 5, silently changes the behavior of cells 1–4 if they're
ever re-run. Result: the metric you committed for cell 1 no longer matches
what re-running cell 1 produces.
- Copy-don't-share rule: Multiple cells modifying the same shared file
produces merge conflicts during parallel ablation, race conditions when
cells run concurrently, and silent regressions.
The duplication and rigidity are the point. They convert "an experiment
broke and I don't know why" into "experiment N is fine, this new experiment
M+1 is the one with the issue."
See also
experiment-layout — where the cell directory comes from (numbering, prior-experiments-read-only)
lab-journal-discipline — how to record the run after the cell finishes