project-layout
Propose and hold a canonical folder layout for a computational-science repository, and keep
a single layout document that says what every directory is for. The layout is a contract:
every artifact has exactly one home, bulk artifacts never enter version control, and
AI-session planning files never mix with project deliverables.
When to use
- Scaffolding a new research repository.
- Answering "where does this go?" for any new file.
- Auditing an existing repo whose layout document has drifted from reality.
When NOT to use
- Mid-project wholesale reorganization without an explicit decision-log entry — moving
directories breaks links, history, and muscle memory; propose, record the decision, then move.
- Adding one module to an existing, documented layout — just follow the map; do not re-derive it.
- Deciding project phase status — the layout document must never carry "current phase"
information (see Rules).
The canonical shape
Adapt names to the project; keep the separations. The copyable version lives in the pack's
templates/project-layout.md.
<project>/
├── README.md # summary, install, how to run
├── TODO.md # current-iteration execution buffer (ephemeral, rewritten per phase)
├── CLAUDE.md # operating rules + the Aletheia config block
├── <env manifest + lockfile> # pyproject.toml + lock, environment.yml, etc. (see run-provenance)
├── docs/
│ ├── README.md # authority map: which document is current / stale / archived
│ ├── decisions.md # ADR-lite decision chain (see decision-log)
│ ├── <spec>.md # implementation spec — the contract code is tested against
│ ├── design/ # full technical design ("why" documents)
│ ├── roadmap/ # long-horizon plan + phase/gate registry
│ ├── build_log/ # write-once phase/gate evidence entries (see build-log)
│ └── archive/ # retired documents — moved, never deleted
├── src/<package>/ # installable package (src-layout)
│ ├── common/ # shared library — imports nothing above it
│ └── <domain subpackages> # model / analysis / orchestration layers
├── configs/ # declarative run configurations (reviewer can re-run from these)
├── scripts/ # reusable CLI pipelines (run repeatedly, produce artifacts)
├── notebooks/ # numbered exploratory narratives + README manifest
├── tests/ # unit + integration; includes the named correctness gate
├── data/ # inputs — gitignored, README tracked (see run-provenance)
├── models/ # trained/fitted artifacts — gitignored
└── results/ # run evidence — gitignored, selected summaries force-added
# (see evidence-convention)
Runbook
- New repo: copy
templates/project-layout.md into docs/, prune directories the
project will not use, and create the tree. Create data/README.md, results/README.md
stating the gitignore-plus-tracked-README policy before the first run lands.
- Placement question: read the layout document, place the file, and if the answer was
not derivable from the document, add the missing one-line intent to it in the same change.
- Layout audit: diff the real tree (
find . -maxdepth 2 -type d) against the layout
document; report undocumented directories and documented-but-absent ones.
- Record any structural change as a decision-log entry before moving files.
Rules
- One home per artifact class. Exploratory one-offs →
notebooks/; reusable pipelines
→ scripts/; shared logic → src/<package>/common/; orchestration imports downward,
never sideways or up.
- Import direction is bottom-up.
common/ imports nothing from higher layers;
orchestration layers import everything below. A violated import direction is a layout bug.
- Bulk directories are gitignored, their READMEs are tracked.
data/, models/,
results/ accumulate gigabytes; version the policy and provenance (READMEs, meta files,
selected summaries via explicit force-add), not the bulk.
- The layout document never states current phase or status. Status is derived from git
history, the build log, and the roadmap's status board — a status line in a layout file is
a drift source by construction.
- Session artifacts are not deliverables. Plans, task breakdowns, and scratch specs
generated during AI-assisted sessions live under
.claude/plans/ (or the harness
equivalent), never in docs/, scripts/, or the repo root. docs/ holds only artifacts
the project's papers and reports derive from.
- Retired documents move to
docs/archive/, never get deleted. Superseded specs remain
citable; the docs authority map (docs/README.md) records what is current.
- Every directory earns a one-line intent in the layout document. A directory nobody can
describe in one line is a candidate for removal.
Placement + notebook discipline
Rule 1 states the routing (one-offs → notebooks/, pipelines → scripts/, shared logic →
the package); the discipline below is how each destination avoids its characteristic rot —
notebooks rot as un-diffable pipelines, scripts rot as unread one-offs, copy-pasted logic
rots everywhere at once.
- The notebook is the default; a script is earned by re-run demand, not granted by
optimism. Second manual re-run of a notebook for fresh outputs is the promotion signal:
move the logic into the package, wrap a CLI script around it, keep the notebook as the
narrative importing the same functions, and note the promotion in the manifest.
- Numbered series with a manifest.
NN_kebab-topic.ipynb, numbering append-only (reusing
a number breaks every citation to the old one); notebooks/README.md states each notebook's
role and rerun policy (live / frozen / archived).
- Execute top-to-bottom before commit. A committed notebook carries its outputs — run it
in place (e.g.
jupyter nbconvert --execute --inplace) so the repo version shows what the
code actually produces. A stale committed notebook is a bug: re-execute it or mark it
frozen/archived. Retired series move to notebooks/archive/, numbers never reused
(historical notebooks pinned to old APIs are narrative, not drift — layer-sync rule 7).
- Narrative is the point; no library logic in cells. Prose cells state the question,
method, and reading of each figure — a notebook that is only code should have been a script,
and the moment a function is worth reusing it moves to the package. Scripts orchestrate
(argument parsing + calls into
src/ + output writing); heavy logic inside a script is
library code in hiding. Never edit defaults to probe — a probe is a notebook cell or a
script flag (canonical-params).
Configuration
Reads from the Aletheia config block in the adopter's CLAUDE.md:
{{evidence_dir}} — the run-evidence root (default results/).
{{decision_log}} — the decision chain file (default docs/decisions.md).
{{build_log_dir}} — the build-log directory (default docs/build_log/).
Provenance & maintenance
Generalized from the working layout of a mature computational-research repository (src-layout
package, shared-library import discipline, gitignored bulk dirs with tracked READMEs,
docs authority map, session-artifact separation); see the pack's examples/ directory for
the worked exemplar mapping. The tree above is a verified pattern; directory names are
adaptable, the separations are the invariant. Inference is labeled where it occurs.
Re-verify in your project:
git check-ignore data results models — confirms bulk directories are ignored (paths that
print are ignored; adjust to your names).
test -f docs/README.md && test -f {{decision_log}} — confirms the authority map and
decision chain exist.
find . -maxdepth 2 -type d -not -path './.git*' — diff this against the layout document
for undocumented directories.
1---2name: project-layout3description: Use when starting a new computational-science project, restructuring an existing one, or deciding where a new module, script, notebook, dataset, or document belongs — including whether a piece of work is a notebook one-off or a reusable script, and maintaining the notebook series. Also use when reviewing whether a repository's layout still matches its documented map. Trigger phrases: "where should this file go", "set up the project structure", "scaffold the repo", "folder layout", "project organization", "does this belong in scripts or notebooks", "notebook or script", "should this be a notebook or a script", "make this rerunnable", "the notebook is stale", "promote this to a script".4---56# project-layout78Propose and hold a canonical folder layout for a computational-science repository, and keep9a single layout document that says what every directory is *for*. The layout is a contract:10every artifact has exactly one home, bulk artifacts never enter version control, and11AI-session planning files never mix with project deliverables.1213## When to use1415- Scaffolding a new research repository.16- Answering "where does this go?" for any new file.17- Auditing an existing repo whose layout document has drifted from reality.1819## When NOT to use2021- Mid-project wholesale reorganization without an explicit decision-log entry — moving22 directories breaks links, history, and muscle memory; propose, record the decision, then move.23- Adding one module to an existing, documented layout — just follow the map; do not re-derive it.24- Deciding project *phase status* — the layout document must never carry "current phase"25 information (see Rules).2627## The canonical shape2829Adapt names to the project; keep the separations. The copyable version lives in the pack's30`templates/project-layout.md`.3132```text33<project>/34├── README.md # summary, install, how to run35├── TODO.md # current-iteration execution buffer (ephemeral, rewritten per phase)36├── CLAUDE.md # operating rules + the Aletheia config block37├── <env manifest + lockfile> # pyproject.toml + lock, environment.yml, etc. (see run-provenance)38├── docs/39│ ├── README.md # authority map: which document is current / stale / archived40│ ├── decisions.md # ADR-lite decision chain (see decision-log)41│ ├── <spec>.md # implementation spec — the contract code is tested against42│ ├── design/ # full technical design ("why" documents)43│ ├── roadmap/ # long-horizon plan + phase/gate registry44│ ├── build_log/ # write-once phase/gate evidence entries (see build-log)45│ └── archive/ # retired documents — moved, never deleted46├── src/<package>/ # installable package (src-layout)47│ ├── common/ # shared library — imports nothing above it48│ └── <domain subpackages> # model / analysis / orchestration layers49├── configs/ # declarative run configurations (reviewer can re-run from these)50├── scripts/ # reusable CLI pipelines (run repeatedly, produce artifacts)51├── notebooks/ # numbered exploratory narratives + README manifest52├── tests/ # unit + integration; includes the named correctness gate53├── data/ # inputs — gitignored, README tracked (see run-provenance)54├── models/ # trained/fitted artifacts — gitignored55└── results/ # run evidence — gitignored, selected summaries force-added56 # (see evidence-convention)57```5859## Runbook60611. **New repo**: copy `templates/project-layout.md` into `docs/`, prune directories the62 project will not use, and create the tree. Create `data/README.md`, `results/README.md`63 stating the gitignore-plus-tracked-README policy before the first run lands.642. **Placement question**: read the layout document, place the file, and if the answer was65 not derivable from the document, add the missing one-line intent to it in the same change.663. **Layout audit**: diff the real tree (`find . -maxdepth 2 -type d`) against the layout67 document; report undocumented directories and documented-but-absent ones.684. Record any structural change as a decision-log entry before moving files.6970## Rules71721. **One home per artifact class.** Exploratory one-offs → `notebooks/`; reusable pipelines73 → `scripts/`; shared logic → `src/<package>/common/`; orchestration imports downward,74 never sideways or up.752. **Import direction is bottom-up.** `common/` imports nothing from higher layers;76 orchestration layers import everything below. A violated import direction is a layout bug.773. **Bulk directories are gitignored, their READMEs are tracked.** `data/`, `models/`,78 `results/` accumulate gigabytes; version the *policy and provenance* (READMEs, meta files,79 selected summaries via explicit force-add), not the bulk.804. **The layout document never states current phase or status.** Status is derived from git81 history, the build log, and the roadmap's status board — a status line in a layout file is82 a drift source by construction.835. **Session artifacts are not deliverables.** Plans, task breakdowns, and scratch specs84 generated during AI-assisted sessions live under `.claude/plans/` (or the harness85 equivalent), never in `docs/`, `scripts/`, or the repo root. `docs/` holds only artifacts86 the project's papers and reports derive from.876. **Retired documents move to `docs/archive/`, never get deleted.** Superseded specs remain88 citable; the docs authority map (`docs/README.md`) records what is current.897. **Every directory earns a one-line intent** in the layout document. A directory nobody can90 describe in one line is a candidate for removal.9192## Placement + notebook discipline9394Rule 1 states the routing (one-offs → `notebooks/`, pipelines → `scripts/`, shared logic →95the package); the discipline below is how each destination avoids its characteristic rot —96notebooks rot as un-diffable pipelines, scripts rot as unread one-offs, copy-pasted logic97rots everywhere at once.9899- **The notebook is the default**; a script is *earned* by re-run demand, not granted by100 optimism. Second manual re-run of a notebook for fresh outputs is the promotion signal:101 move the logic into the package, wrap a CLI script around it, keep the notebook as the102 narrative importing the same functions, and note the promotion in the manifest.103- **Numbered series with a manifest.** `NN_kebab-topic.ipynb`, numbering append-only (reusing104 a number breaks every citation to the old one); `notebooks/README.md` states each notebook's105 role and rerun policy (live / frozen / archived).106- **Execute top-to-bottom before commit.** A committed notebook carries its outputs — run it107 in place (e.g. `jupyter nbconvert --execute --inplace`) so the repo version shows what the108 code actually produces. A stale committed notebook is a bug: re-execute it or mark it109 frozen/archived. Retired series move to `notebooks/archive/`, numbers never reused110 (historical notebooks pinned to old APIs are narrative, not drift — `layer-sync` rule 7).111- **Narrative is the point; no library logic in cells.** Prose cells state the question,112 method, and reading of each figure — a notebook that is only code should have been a script,113 and the moment a function is worth reusing it moves to the package. Scripts orchestrate114 (argument parsing + calls into `src/` + output writing); heavy logic inside a script is115 library code in hiding. Never edit defaults to probe — a probe is a notebook cell or a116 script flag (`canonical-params`).117118## Configuration119120Reads from the Aletheia config block in the adopter's `CLAUDE.md`:121122- `{{evidence_dir}}` — the run-evidence root (default `results/`).123- `{{decision_log}}` — the decision chain file (default `docs/decisions.md`).124- `{{build_log_dir}}` — the build-log directory (default `docs/build_log/`).125126## Provenance & maintenance127128Generalized from the working layout of a mature computational-research repository (src-layout129package, shared-library import discipline, gitignored bulk dirs with tracked READMEs,130docs authority map, session-artifact separation); see the pack's `examples/` directory for131the worked exemplar mapping. The tree above is a *verified* pattern; directory names are132adaptable, the separations are the invariant. Inference is labeled where it occurs.133134Re-verify in your project:135136- `git check-ignore data results models` — confirms bulk directories are ignored (paths that137 print are ignored; adjust to your names).138- `test -f docs/README.md && test -f {{decision_log}}` — confirms the authority map and139 decision chain exist.140- `find . -maxdepth 2 -type d -not -path './.git*'` — diff this against the layout document141 for undocumented directories.