levineuwirth.org/yaml-source
Levi Neuwirth 1f1969ab28
vita: generate the project index, and add a web_visible axis
The project index was the last hand-maintained duplicate of CV data, and it
had already drifted: a superseded Weenix line count and an end date the CV
disagreed with. It now renders from yaml-source/data/projects.yml, which
grows from three entries to nine and gains `group` (drives the index's
sections, first-appearance order, like now.yaml) and `links`.

Pmacs, Levshell, and Epiphany join under "Personal" — self-directed tools
rather than coursework or employment — written from the repos rather than
from memory. They are web-only: personal infrastructure is not academic
output, and the résumé is a full page. LeVCS keeps its essay; project
writeups stay essays, since a writeup is an informal presentation of a
project and not a record of it.

web_visible is a third visibility axis alongside cv_visible and
resume_visible, defaulting to cv_visible so nothing changes until it is
set. The file already knew how to let the CV and résumé disagree about an
entry; generating a third surface from the same data would otherwise have
collapsed that, and a document handed to a chosen reader is not a page that
gets crawled. This is what makes web-only projects expressible — and what
would let the xAI entry sit quietly on the CV while staying off the site,
if that is wanted later. It stays off both for now.

Two LaTeX conversions added while moving the markdown prose into the YAML:
$\rightarrow$ and $\sim$. The latter fixes a bug I introduced doing it —
"~10 crates" meant "about ten", but bare ~ is LaTeX's non-breaking space,
so it rendered "( 10 crates" and dropped the approximation.

Weenix's description now carries the site's fuller tail (the CS 169 origin
and the later pipes/preemption work) rather than the CV's terser "Brown CS
169." Single-sourcing means picking one, and this is the better one; the CV
grows by a clause and the résumé still fits on one page.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SUGesXiMmACsLBTGG1xuEU
2026-08-11 09:07:38 +02:00
..
data vita: generate the project index, and add a web_visible axis 2026-08-11 09:07:38 +02:00
templates chore: track CV/résumé pipeline source 2026-08-10 23:40:01 +02:00
Makefile chore: track CV/résumé pipeline source 2026-08-10 23:40:01 +02:00
README.md chore: track CV/résumé pipeline source 2026-08-10 23:40:01 +02:00
build.py chore: track CV/résumé pipeline source 2026-08-10 23:40:01 +02:00

README.md

Levi Neuwirth — CV and Résumé

Single-source pipeline producing two PDFs from a shared YAML content store:

  • output/cv.pdf — multi-page academic CV.
  • output/resume.pdf — one-page industry résumé.

How it works

Content lives in data/*.yml, one file per logical section. Each entry has cv_visible and resume_visible flags that control whether it appears in the CV, the résumé, or both. The two Jinja2 templates in templates/ render into LaTeX, which xelatex compiles.

Building

make all       # build both PDFs
make cv        # build CV only
make resume    # build résumé only
make clean     # remove build/ and output/
make install   # pip install pyyaml + jinja2 (user install)

Requires: Python 3.9+, xelatex (TeX Live, MacTeX, or equivalent), Source Serif Pro and Source Sans Pro fonts. On most Linux distributions these fonts are in the texlive-fonts-extra or equivalent package; on macOS they're available through Homebrew (brew install --cask font-source-serif-pro font-source-sans-pro) or directly from Adobe.

Maintenance patterns

Add a new publication: edit data/publications.yml, append an entry, set cv_visible and resume_visible as appropriate. Rebuild.

Update a role: edit data/experience.yml. The cv_order and resume_order fields let you reorder per document.

Change the ordering on the résumé without changing the CV: adjust resume_order in experience.yml. The CV uses cv_order independently.

Move a project from "in progress" to "published": move its entry from projects.yml into publications.yml.

Hide something temporarily: flip its visibility flag to false. The entry stays in source, preserving history.

Page-count notes

  • Résumé is a clean 1 page. It holds five Experience entries (MARS V, Shu Lab, NeuroAI, xAI, contracting), 3 projects, education, and header. The fit depends on two deliberate choices: xAI is held to a single line (scope line dropped, metrics bullets removed — see below), and the résumé-only spacing in resume.tex.j2 (\titlespacing, itemize spacing, \parskip) is tuned so the last education note lands on page 1. Adding a multi-line Experience or Projects entry will spill onto a second page; when that happens, either trim content or nudge \parskip down (0.1em is the current value; page 1 still reads with air at that level).
  • xAI is intentionally de-emphasized. It sits fourth in Experience and is one line that keeps only the grok-code-fast-1 credential. The old scope line and the two metrics bullets (tool-execution / API-usage percentages, hallucination-rate reduction) were removed on purpose to keep both documents' centre of gravity on research; do not re-add them without a reason.
  • CV at 4 pages is normal for this level of output. Page 4 is sparse (Languages, Skills, Research Interests); tightening \titlespacing and \parskip in preamble.tex can collapse to 3 pages but tends to reappear at 4 as soon as new content is added.

Design notes

  • Body: Source Serif Pro. Headers: Source Sans Pro. Fully black and white.
  • Templates use LaTeX-safe Jinja delimiters: %% ... %% for blocks, (( ... )) for variables, %# ... #% for comments. This avoids collisions with LaTeX's own % and {}.
  • \entryheader in templates/shared/commands.tex is the unified organization/role/date/location block used by both templates.
  • The MARS V and Shu Lab entries have cv_section: research set in experience.yml, which routes them into the CV's "Research Experience" section while they still sit under plain "Experience" on the résumé. Other routing flags can be added similarly.
  • Ordering is per-axis. The résumé sorts Experience by resume_order (research-first: MARS V, Shu Lab, NeuroAI, then xAI and contracting). The CV templates render in file order within each section — the cv_order field is documentation of intent, not a sort key, so keep the YAML file order and cv_order consistent when reordering.

File layout

cv/
├── data/              # YAML content (source of truth)
│   ├── affiliations.yml
│   ├── education.yml
│   ├── experience.yml
│   ├── grants.yml
│   ├── personal.yml
│   ├── presentations.yml
│   ├── projects.yml
│   ├── publications.yml
│   └── skills.yml
├── templates/
│   ├── cv.tex.j2
│   ├── resume.tex.j2
│   └── shared/
│       ├── commands.tex
│       └── preamble.tex
├── build.py           # renders + compiles
├── Makefile
├── build/             # intermediate (.aux, .log, rendered .tex)
└── output/
    ├── cv.pdf
    └── resume.pdf