A reproducible experiment framework for software engineering research. Manages experimental subjects as Git submodules and verifies baseline correctness — all through a single CLI.
- Python 3.12+
- uv
- Git
git clone <this-repo>
cd experiment-framework
uv sync# Add a subject (you must know its test and install commands)
uv run experiments add https://github.com/pallets/click \
--test-command "uv run --locked --no-default-groups --group dev tox run" \
--install-command "uv sync"
# Verify everything passes
uv run experiments baselineOutput:
── Processing click ──
Install: uv sync
Test: uv run --locked --no-default-groups --group dev tox run
Installing dependencies...
Running tests...
── Processing ultrajson ──
Install: uv sync && uv pip install pytest
Test: uv run python -m pytest
Installing dependencies...
Running tests...
====================================
Baseline Verification
✓ click
✓ ultrajson
2 passed
0 failed
====================================
Each subject is a Git submodule inside subjects/. You provide the exact shell commands for installing dependencies and running tests. There is no auto-discovery — you must know how the project is built and tested.
experiment-framework/
├── configs/subjects.yml # Subject metadata: name, path, commit, test_command, install_command
├── subjects/ # Git submodules directory (one per subject)
├── experiments/
│ ├── cli.py # CLI entry point (click)
│ ├── models.py # SubjectConfig, SubjectMap
│ ├── git.py # Submodule CRUD: add, init, update, checkout
│ ├── installer.py # Runs install commands in isolated environments
│ └── baseline.py # Orchestrates: init → checkout → install → test → report
├── pyproject.toml
└── README.md
Each subject runs in its own isolated environment. The framework's .venv is explicitly unset in subprocesses so tools like uv and poetry resolve project-local environments, not the framework's own.
Verify every subject passes its test suite:
- Initialize any missing submodules
- Checkout the pinned commit
- Install dependencies in an isolated environment
- Run the test suite (stdout/stderr streamed to terminal)
- Continue to next subject even if this one fails
- Print a summary of passes and failures
uv run experiments baselineRegister a new subject as a Git submodule. Both --test-command and --install-command are required.
uv run experiments add https://github.com/pallets/click \
--test-command "uv run --locked --no-default-groups --group dev tox run" \
--install-command "uv sync"Options:
| Option | Short | Required | Description |
|---|---|---|---|
--test-command |
-t |
yes | Shell command to run tests |
--install-command |
-i |
yes | Shell command to install dependencies |
--name |
-n |
no | Subject name (inferred from URL if omitted) |
Fetch the latest commit for every submodule and update subjects.yml:
uv run experiments updateList all registered subjects with their pinned commits and commands:
uv run experiments listEvery subject requires name, path, test_command, and install_command. commit is optional but recommended for reproducibility.
subjects:
- name: click
path: subjects/click
commit: 679a7a0eccbdded7a6e85680bdaaf08003765e01
test_command: uv run --locked --no-default-groups --group dev tox run
install_command: uv sync
- name: ultrajson
path: subjects/ultrajson
commit: 6f60807ae2da2ba5a6b6449e78b1033591eb4aa0
test_command: uv run python -m pytest
install_command: uv sync && uv pip install pytest| Field | Required | Purpose |
|---|---|---|
name |
yes | Short identifier for the subject |
path |
yes | Relative path from repo root to the submodule |
test_command |
yes | Shell command that runs the project's test suite |
install_command |
yes | Shell command that installs the project and its dependencies |
commit |
no | Pinned commit hash for reproducibility |
When adding a new subject, look at their CI configuration to find the right commands:
- Open
.github/workflows/in the project's repository - Find the main test workflow (usually
tests.ymlorci.yml) - Identify the install step that precedes the test step
- Copy those exact commands
For example, if a project's CI does:
- run: uv sync
- run: uv run pytestThen:
install_command: uv sync
test_command: uv run pytestYou can add entries directly to subjects.yml without using the CLI:
- Add a submodule manually:
git submodule add https://github.com/example/project subjects/project
- Add the entry to
configs/subjects.yml:- name: project path: subjects/project commit: abc1234 test_command: pytest install_command: pip install -e ".[test]"
- Explicit — test and install commands are always provided by the researcher; nothing is guessed
- Lightweight — no CI parsing, no file scanning, no auto-discovery
- Isolation — each subject runs in its own environment, no cross-contamination
- Resilience — one failing subject doesn't stop the rest
- Extensibility — research tools (PseudoSnake) can be inserted after baseline verification