CLAUDE.md

Working notes for Claude (or any AI assistant) on this repo. Keep this scannable — add to it when conventions or gotchas crystallize.

Repo overview

  • Jekyll personal blog (“Wei’s Learning Notes”), deployed via GitHub Pages from the main branch.
  • Theme: minima, with local overrides in _layouts/, _includes/, and css/override.css.
  • Generated originally from chadbaldwin/simple-blog-bootstrap.
  • Live site: https://wwucla.github.io/

Sync before any change

The user also edits files directly via the GitHub web UI sometimes, so local main is often stale. Always start fresh work by pulling latest from origin:

git checkout main && git pull

Skip this and you’ll branch off old code and produce avoidable merge conflicts. Do this every session, every new branch — no exceptions.

PR workflow (preferred)

Always work on a feature branch and merge via gh — no clicking through the web UI for merges. The pattern that works reliably:

git checkout main && git pull                # see above — never skip
git checkout -b <type>/<short-name>          # feature/, fix/, demo/, chore/
# ... edits ...
git add <files>
git commit -m "<message>"
git push -u origin HEAD
gh pr create --fill --base main
gh pr merge --squash --delete-branch --auto

The --auto flag is the important one: it tells GitHub to merge the PR as soon as required checks pass, so we don’t need to foreground-watch with gh pr checks --watch (which has a race where it exits before the workflow has even registered).

“Allow auto-merge” is enabled in repo Settings → General. Leave it on.

After the auto-merge fires, sync local main:

git checkout main && git pull && git branch -d <branch-name>

Local dev (Jekyll)

GitHub Pages runs Ruby ~3.3 and a specific github-pages gem bundle. For a clean local match:

brew install ruby@3.1            # 3.1 is the sweet spot — newer Rubies (3.4+) break Liquid 4.0.3
echo 'export PATH="/opt/homebrew/opt/ruby@3.1/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
gem install --user-install bundler
bundle install
bundle exec jekyll serve --livereload     # http://127.0.0.1:4000

To preview future-dated posts (e.g., the demo post): add --future to jekyll serve.

A Gemfile exists locally for dev but is gitignored — keep it that way.

Site customizations (already in place)

  • Tag chips under each post title, linking to /archive.html#tag-<slug>. Implemented in _layouts/post.html. Per-tag sections live in archive.md with kramdown IAL anchors ({: id="tag-<slug>" }).
  • Reading time on each post: Liquid content | number_of_words | divided_by: 200 | plus: 1. In _layouts/post.html.
  • Auto table of contents (/js/toc.js): builds a nested TOC client-side from <h2>/<h3> headings in .post-content. Activation modes via data-toc-mode on #post-toc:
    • forcetoc: true in front-matter — render whenever ≥1 heading exists.
    • auto — default — render only when there are more than 3 headings.
    • To disable on a specific post: toc: false in front-matter.
  • CI build check: .github/workflows/jekyll-check.yml runs actions/jekyll-build-pages@v1 on every PR targeting main. Build-only; deploy still happens via Pages on main.

Posting conventions

Every post in _posts/ should have:

---
layout: post
title: "Descriptive Title"
date: 2026-05-11
categories: [Optional, Category]
tags: [Tag1, Tag2]              # used by tag chips + archive
description: One-sentence summary used by SEO.
---

Do not write hardcoded “Estimated reading time” lines or hand-rolled “Table of Contents” sections — both are auto-generated by the layout. Same for inline <a name="..."> anchor markers; kramdown auto-IDs headings.

Posts can opt out of the auto-TOC by setting toc: false. The Other tag is the default for posts that omit tags: (configured in _config.yml).

Gotchas / things to never do

  • Do not enable titles_from_headings.collections: true in _config.yml. The jekyll-titles-from-headings plugin (auto-loaded by github-pages) will then override the front-matter title with the first heading text whenever a post starts with a heading. Front-matter title must stay authoritative.
  • Do not use bitdowntoc or other manual-TOC generators in posts. The auto-TOC from /js/toc.js handles it.
  • Do not pin gem "jekyll" directly in a Gemfile. Always go through gem "github-pages", group: :jekyll_plugins so versions match production Pages.
  • Do not push directly to main. Always go through a PR (the auto-merge flow makes this near-zero overhead).
  • The _posts/2038-03-08-blog-post-title-from-file-name.md post is a future-dated rendering demo. Don’t backdate it — that would publish it to the live site.

When asked to add a new feature/page

  1. Branch off main.
  2. Check _layouts/ and _includes/ for existing partials before adding new ones — many extension points already exist (e.g., minima’s custom-head.html is honored).
  3. Append site-wide CSS to css/override.css (not a new stylesheet) so we keep one source of styling truth.
  4. Run jekyll build (or serve) locally if the change is non-trivial; the CI check catches breakage on PR anyway.
  5. PR via the workflow above.

Useful one-liners

  • See recent CI runs: gh run list --limit 5
  • Check PR status: gh pr view <num> --json statusCheckRollup
  • Open the live site: gh browse --no-browser then paste the URL, or just visit https://wwucla.github.io/

Avoid touching

  • images/ — only add new images, don’t reorganize.
  • js/highlightjs/ — vendored highlight.js. New languages require adding the matching module here AND a <script> tag in _includes/head.html.