CLAUDE.md
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Overview
Mike Dodds’ personal academic website (publications, talks, blog posts), built on the Academic Pages Jekyll template and hosted on GitHub Pages.
Toolchain & Local Development
Ruby version (important)
The site is pinned to Jekyll 3.10.x through the github-pages gem (see Gemfile). This gem deliberately locks Jekyll and all plugins (currently github-pages 232 → jekyll 3.10.0, liquid 4.0.4) to the exact versions GitHub Pages’ classic build runs, so a local build matches production.
The catch: this frozen stack does not run on modern Ruby. Two failure modes seen with recent Rubies:
- Ruby 3.4+ removed
csv,base64,logger, etc. from the standard library →LoadError: cannot load such file -- csv. - Ruby 3.2+ removed
Object#tainted?, which olderliquidcalls →NoMethodError: undefined method 'tainted?'. (Current liquid 4.0.4 is fine, but a staleGemfile.lockcan pin an older liquid that isn’t.)
Use Ruby 3.3.x, which is what GitHub Pages runs (3.3.4). Do not use Homebrew’s default ruby (4.x) directly. The idiomatic setup on macOS:
brew install rbenv
rbenv init # then follow its instruction to update ~/.zshrc and restart the shell
rbenv install 3.3.5
rbenv local 3.3.5 # writes .ruby-version, pinning just this project (gitignored)
ruby -v # confirm 3.3.x
Notes:
.ruby-versionis gitignored (local-only pin), as isGemfile.lock— sobundle installresolves fresh. If you ever hit thetainted?error, delete a staleGemfile.lockand re-runbundle installto pull current gems.bundle installneeds network + a writable Ruby env; it can fail under a restrictive sandbox.
Build and serve
# Install Ruby dependencies (needs network + writable Ruby env)
bundle install
# Install Node.js dependencies (only needed when rebuilding JS assets)
npm install
# Serve the site locally with live-reload; available at http://localhost:4000
bundle exec jekyll serve -l -H localhost
Always run Jekyll via bundle exec so the pinned (production-matching) version is used, not a stray global install.
Using Docker
Docker is an alternative that avoids the local Ruby setup. There’s a Dockerfile, a docker-compose.yaml, and a VS Code .devcontainer/. Compose is simplest:
docker compose up # builds and serves at http://localhost:4000
Compose uses _config_docker.yml as an overlay on _config.yml.
Rebuilding JavaScript assets
JS is pre-built and committed as assets/js/main.min.js. Only rebuild if you change source JS:
npm run build:js # uglify + minify into assets/js/main.min.js
npm run watch:js # rebuild on change
Site Architecture
Jekyll Collections
_posts/- Blog posts (dated)_publications/- Academic publications (hand-maintained markdown)_talks/- Talks and presentations (hand-maintained markdown)_teaching/- Teaching materials_pages/- Static pages (About, CV, publications/talks index pages)
Note: the portfolio collection exists in _config.yml but is unused — it has no entries and is commented out of _data/navigation.yml.
Theme Structure
_layouts/, _includes/, _sass/, and assets/ are stock Academic Pages template code with no local edits — refer to the upstream repo for their structure. _data/ holds YAML config; _data/navigation.yml controls the nav bar.
The template has a theme/skin system: _config.yml’s site_theme key selects a skin (default, air, sunrise, mint, dirt, contrast), each with light/dark variants under _sass/theme/. This site uses default.
The favicon is Mike’s custom headshot icon at the repo root (/favicon.ico), referenced from _includes/head/custom.html. It lives at root (not images/) so the browser’s implicit /favicon.ico probe resolves.
Configuration
_config.yml- Main Jekyll config (site settings, author info, collections,site_theme). Restart the Jekyll server after editing — it is not live-reloaded.Gemfile- Ruby gem dependencies (pins Jekyll viagithub-pages; plugins includejekyll-redirect-from, which powersredirect_from:front matter).package.json- Node dependencies for the JS asset build.
Content Management
Publications and talks are maintained by hand as individual markdown files in _publications/ and _talks/. Add or edit those files directly — do not use the markdown_generator/ scripts (see note below). The simplest way to add an entry is to copy an existing recent file and edit its front matter and body.
Adding a publication
- Create
_publications/YYYY-MM-DD-slug.md(date prefix = publication/submission date). - Front matter fields (match existing entries):
title:(double-quoted)collection: publicationscategory:— one ofconferences,preprints,manuscripts,miscpermalink: /publications/YYYY-MM-DD-slug(must match the filename, with date prefix and with a leading slash)date: YYYY-MM-DDvenue:(spelled-out venue name with acronym, e.g."Programming Language Design and Implementation (PLDI)")paperurl:(the canonical DOI/arXiv URL)
- Body: an
**Abstract:**paragraph, then a final footer line linking the DOI and any hosted PDF, e.g.:DOI: <https://doi.org/...>, Paper: [PDF](https://mikedodds.github.io/files/publications/YYYY-MM-DD-slug.pdf) - If hosting the PDF, place it at
files/publications/YYYY-MM-DD-slug.pdf(filename matching the markdown slug).
Adding a talk
Create _talks/YYYY-MM-DD-slug.md mirroring an existing talk’s front matter. Use zero-padded dates (YYYY-MM-DD) and a permalink of /talks/YYYY-MM-DD-slug with a leading slash.
Adding a blog post
Create _posts/YYYY-MM-DD-title.md:
---
title: 'Post Title'
date: YYYY-MM-DD
permalink: /posts/YYYY/MM/title/
tags:
- tag1
- tag2
---
File organization
- Hosted papers/PDFs go in
files/(e.g.files/publications/,files/talks/). - Images go in
images/.
About markdown_generator/ (stale — do not use)
This directory contains the template’s original Python/Jupyter scripts for generating publication and talk pages from CSV/TSV/BibTeX data. It is abandoned and not part of the build. The *.tsv/*.csv files contain only the template’s example rows and are wildly out of sync with the real, hand-maintained publications (~37 entries in _publications/). Nothing invokes these scripts at build time. Do not regenerate content from them — you would overwrite the real entries with placeholder data.
Deployment
The site deploys automatically to GitHub Pages on push to master (classic Pages build — there is no GitHub Actions deploy workflow). No manual deployment step.
Important — never break existing URLs. Changes to permalinks, filenames, or site structure must preserve existing page URLs (publications, talks, posts) to maintain link integrity and search rankings. Treat permalink/filename changes on already-published pages as needing explicit confirmation, even when they look like simple consistency fixes.
