This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Static website for the Contextual Dynamics Lab at Dartmouth College. Hosted on GitHub Pages at context-lab.com. Content pages (publications, people, software, news) are auto-generated from Excel spreadsheets via a Python build system — never edit the root HTML for those pages directly.
# Install dependencies
pip install -r requirements-build.txt
# Validate spreadsheet data
cd scripts && python validate_data.py
# Build all content pages (publications, people, software, news)
cd scripts && python build.py
# Build CV (requires XeLaTeX + Dartmouth Ruzicka fonts)
cd scripts && python build_cv.py
# Run full test suite
python -m pytest tests/ -v
# Run a single test file
python -m pytest tests/test_build_publications.py -v
# Lint and type check (both gate build-content.yml; run from repo root)
ruff check # config in ruff.toml
ruff check --fix # apply the auto-fixable subset
mypy # config in mypy.ini
# Pre-push validation (lint + type check + validation + build + tests)
cd scripts && python pre_push_check.py
# Local dev server
python3 -m http.server 8000
# Add hand-drawn borders to images
python scripts/add_borders.py image.png images/publications/
python scripts/add_borders.py photo.jpg images/people/ --face
# Onboard new member (from scripts/ dir)
python onboard_member.py "First Last" --rank "grad student" --photo headshot --skip-llm
# Offboard member to alumni (from scripts/ dir)
python offboard_member.py "member name" --end-year 2025
# Reconcile people across website, CV, and lab-manual
cd scripts && python reconcile_people.py --dry-run # report only
cd scripts && python reconcile_people.py # apply auto-fixes
# Initialize lab-manual submodule (required for reconciliation)
git submodule update --initdata/*.xlsx → scripts/build_*.py → root *.html (auto-generated)
↑
templates/*.html (markers like <!-- PUBLICATIONS_PAPERS -->)
Each build_*.py follows the same pattern:
- Load spreadsheet with
utils.load_spreadsheet_all_sheets() - Generate HTML fragments
- Inject into template via
utils.inject_content(template, output, {"MARKER": html})
| Directory | Purpose |
|---|---|
data/ |
Source spreadsheets (publications.xlsx, people.xlsx, software.xlsx, news.xlsx) + Dartmouth fonts |
templates/ |
HTML templates with <!-- MARKER --> injection points |
scripts/ |
Python build system, validation, onboarding/offboarding tools |
tests/ |
pytest suite — conftest.py adds scripts/ to sys.path |
documents/ |
CV source (JRM_CV.tex) and generated outputs (PDF, HTML) |
css/style.css |
Single stylesheet with CSS variables at top |
js/main.js |
All JS components (9 init functions) |
lab-manual/ |
Git submodule — ContextLab/lab-manual (init with git submodule update --init) |
documents/JRM_CV.tex → scripts/build_cv.py + scripts/extract_cv.py (custom LaTeX→HTML parser) → documents/JRM_CV.pdf + documents/JRM_CV.html
- build-content.yml: Triggers on changes to
data/,templates/,scripts/,tests/,requirements-build.txt,ruff.toml, ormypy.ini. Lints (ruff check), type checks (mypy), validates, builds, runs tests, auto-commits regenerated HTML. Lint and type check run before the build, so a script that fails them never generates a page. - build-cv.yml: Triggers on changes to
documents/JRM_CV.tex, CV scripts, orcss/cv.css. Compiles LaTeX, runs tests, auto-commits PDF+HTML.
- Never edit auto-generated root HTML (
publications.html,people.html,software.html,news.html) — editdata/*.xlsxortemplates/*.htmlinstead - Never edit the generated CV outputs (
documents/JRM_CV.html,documents/JRM_CV.pdf) — both are built fromdocuments/JRM_CV.texbyscripts/build_cv.py - Every generated file is produced by a build, never by hand. After changing a source (
data/*.xlsx,templates/*.html,documents/JRM_CV.tex), regenerate withcd scripts && python build.pyand/orpython build_cv.py, then commit the source and its regenerated output together. Any diff in a generated file must be reproducible by deleting it and re-running the build. - Never use
!importantin CSS without explicit justification - Never add inline styles to templates — use CSS classes
- Always run tests before pushing:
python -m pytest tests/ -v - All headings are lowercase via
text-transform: lowercasein CSS
- Colors:
--primary-green: rgb(0, 112, 60),--bg-green: rgba(0, 112, 60, 0.2),--dark-text: rgba(0, 0, 0, 0.7) - Font: Nunito Sans, 300 weight body, 14px base, 1.7 line-height
- Images: 500x500px with hand-drawn green borders (use
scripts/add_borders.py), face-detect cropping for people photos (--face) - Forms: Formspree backend — endpoint in form
actionattribute