Getting Started
Conventions
Project folders, dot-notation names, and the naming rules Framework follows
Philosophy
Framework favors convention over configuration. Every project of a given type has the same folders, the same settings file, and the same entry point, so a person or an AI assistant who knows one project can find their way in another. Nearly every convention can be changed in the project's settings or in your global defaults.
This page describes the conventions that the rest of the documentation assumes.
One Entry Point
Every notebook and script starts with two lines.
import framework as fw
fw.scaffold()
scaffold() finds the project root, loads the settings and the .env file, sets the random seed when that is turned on, and puts the functions/ folder on the import path. The scaffold() guide describes each step.
Framework resolves project paths from the project root. The working directory is not changed, so a notebook in notebooks/ and a script in scripts/ refer to the same data by the same name.
Functions
Reusable code lives in the functions/ folder as ordinary Python modules. scaffold() adds the folder to sys.path, so notebooks and scripts import from it directly.
functions/
├── cleaning.py
└── plotting.py
import framework as fw
fw.scaffold()
from cleaning import standardize_names
Python imports run a module's top-level code once per session. Keep these modules to definitions, so that importing them has no side effects.
Inputs and Outputs
Framework separates the data a project reads from the files it produces.
Inputs
Data the project reads lives in inputs/, organized by stage.
inputs/
├── raw/ # data from outside sources, never modified
├── intermediate/ # cleaned or transformed data, mid-pipeline
└── final/ # analysis-ready datasets
Outputs
Files the project produces live in outputs/, organized by kind. Apart from outputs/notebooks/, which is created with the project, each folder is created the first time something is saved there.
outputs/
├── tables/ # save_table()
├── figures/ # save_figure()
├── models/ # save_model()
├── reports/ # save_report()
├── notebooks/ # rendered notebooks, from Quarto and save_notebook()
└── cache/ # cached values
The Saving Work guide describes the save_*() functions.
Work Folders
notebooks/holds Quarto (.qmd) and Jupyter (.ipynb) notebooks.scripts/holds standalone Python scripts.functions/holds reusable modules.scratch/holds throwaway files and quick captures.docs/holds project documentation.logs/holds run logs.
The standard .gitignore excludes scratch/, logs/, and every cache/ folder.
Dot Notation
Framework names data files with dot notation. A name such as inputs.raw.survey stands for a catalog entry, which records the file's path, type, and integrity settings. Code refers to the name, and the catalog maps it to a file.
survey = fw.data_read("inputs.raw.survey")
The catalog is the data section of the settings. Each part of a dot-notation name is usually one level of nesting, and a key may also contain dots, such as raw.survey: under inputs:.
data:
inputs:
raw:
survey:
path: inputs/raw/survey.csv
type: csv
locked: true
data_add() and data_save() write these entries for you. The Data Management guide covers the catalog.
Settings keys use the same notation. fw.settings("directories.notebooks") returns the notebooks entry of the directories section.
Naming
Framework applies a few naming rules consistently.
- Catalog names follow the folder they describe, so a file in
inputs/raw/is namedinputs.raw.<file>. Whendata_save()receives a name, its leading parts select the folder, using the longest run that names a directory key, and the rest becomes the file name. - Directory keys in the settings join the parts of a folder with underscores, such as
inputs_rawforinputs/rawandoutputs_tablesforoutputs/tables. - Notebook and script files are named from the title you give.
make_notebook("Data Cleaning")createsdata-cleaning.qmd. - Saved outputs are named by the name you pass plus the format's extension, such as
summary.csv. The results table inframework.dbrecords them by that file name. - Projects have a display name in
settings.yml. Thepyproject.tomlname and the workspace file use its kebab-case form.
Privacy-Sensitive Projects
Projects that hold restricted data split inputs and outputs into private and public folders.
inputs/
├── private/
│ ├── raw/
│ ├── intermediate/
│ └── final/
└── public/
├── raw/
├── intermediate/
└── final/
Everything under a private/ folder is ignored by git, and the publishing functions ask before they upload it. When Claude Code is one of the project's assistants, its settings deny it access to these folders. The Project Types guide compares the types.
Data Integrity
Framework records a SHA-256 hash of each catalog file in framework.db. A later read compares the file with the recorded hash. A changed file raises a warning, and a changed locked file raises an error.
IntegrityError: Hash mismatch for locked data 'inputs.raw.survey'
Expected hash: 71da0ff755e5
Current hash: ddaa40dcf34b
The hashes match the ones the R package records, so a file saved in one language verifies in the other.
One Language per Project
Each project has a single language, recorded in settings.yml. framework-py creates and works with Python projects. R projects are created by the R package, and the two kinds of project never mix. They share data through formats both languages read: CSV and Parquet files, the data catalog, and framework.db.
Messages
Framework reports progress through the framework logger. scaffold(), project creation, and other interactive functions print these messages to standard error. In library use the logger stays quiet unless you configure it with the logging module.