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
The vita page carried a hand-typed duplicate of education, publications,
presentations, and experience, and had drifted from the PDFs in about
fifteen places — Shu Lab still one entry titled "Undergraduate
Researcher" three months after the degree, Weenix at ~7,000 lines, CHASE
still "accepted" after being presented, a preprint missing entirely, and
two different code URLs for the same paper.
build/Vita.hs reads yaml-source/data/*.yml — the same files the PDF
pipeline builds from — and renders education, experience (keeping the
CV's research/industry split), publications, presentations, and contact.
Drift is now structurally impossible rather than merely discouraged.
Notes on the implementation:
- The YAML is LaTeX-flavoured because xelatex is its first consumer, so
values pass through a small closed converter (\textbf, \textit,
\texttt, \href, $\times$, $\delta$, \#, {,}, ~, --, ---). Escaping
happens before conversion; unhandled commands pass through visibly
rather than silently dropping their argument.
- Scalars are parsed loosely: the same logical field is sometimes a YAML
number and sometimes a string (`year: 2026` vs `year: "2026--2027"`),
and the PDF pipeline does not care, so this does not force it to.
- personal.yml's phone number is deliberately not read. It is printed on
the CV, which is handed to a chosen reader; this page is crawled.
- Contact chips carry labels (ORCID, Forgejo, GitHub) rather than the
CV's full display values, which exist because paper cannot be clicked.
The page keeps only what nothing else on the site owns: the pointers and
the four research threads. In-progress work belongs to /current, the
engineering index to /cv/projects/, and the narrative to /me/ — whose
abstract already described this page as the formal record.
vita.css composes on item-card.css and mirrors now.css's headings and
chip geometry, so /about and /current read as one system. Link chips get
the only interaction polish on the page, being the one affordance a
printed CV cannot offer.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SUGesXiMmACsLBTGG1xuEU
Dated facts:
- CCV affiliation ended July 2026; drop the "(HPC-supported)" role suffix
and the Phase 2/3 in-progress claim (also on the résumé's PQC entry).
- Weenix ended August 2025 at 10,000 lines; the site said ~7,000 and
January–May 2025.
- CHASE 2026 presented August 3; moved to now.yaml's shipped list and
marked Presented on the CV.
- Shu Lab split at the degree boundary: Undergraduate Researcher and
Technical Lead (Oct 2023 – May 2026), then Research Affiliate
(May 2026 – Present, bulletless — the work is described above it).
- DTU advisor/research-group TBD note removed.
Claims that had started to decay:
- Math preprints read "Preprint" rather than "arXiv (forthcoming)", and
now.yaml calls them revising rather than in-review.
- SIMD/PQC is paused pending a compute move to DTU.
- Semantic-embeddings work dropped; MTG no longer claims OSCAR.
Redundancy:
- NSF affiliation description reduced to the two qualifications its title
and role line don't already state; MARS V grant note to the funding
terms; DTU fellowship placeholder deleted; NeuroAI held to one line.
Structure:
- xAI toggled off on both documents (retained in the file, two flags away).
- Contract work renders inside Industry Experience above NeuroAI as
"Independent Research Contractor"; Selected Contract Work is now
conditional so the heading can't render empty.
- Templates: guard bulletless entries, and fix an unguarded exp.location
in the CV that printed a literal "None" for entries without one.
now.yaml: pmacs, levshell, and epiphany notes rewritten against the actual
repos (all three were badly out of date); epiphany added.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SUGesXiMmACsLBTGG1xuEU
Six follow-ups from the link-archive audit, all the items with a
code-shaped fix. Behaviour verified per feature (stubbed + live where a
network or browser was needed); ARCHIVE.md status notes updated alongside.
- suggest: `archive.py suggest` + `make archive-suggest` print works
cited in data/*.bib (url wins, DOI-only resolves to doi.org/...) but
absent from the manifest, as manifest-ready lines. Read-only, offline.
- aliases: optional `aliases:` manifest field for equivalent URLs no
normalisation can derive (DOI vs. landing URL). Enforced like canonical
URLs on both sides (archive.py pre-scan + Archive.hs validator);
ArchiveIndex drops alias keys matching a takedown. FIPS 203 now carries
its DOI form, so the simd paper's DOI citation resolves.
- check scheduling: systemd user timer (systemd/archive-check.{service,
timer}, symlink-installed) runs the rot scan daily. cmd_check gains an
offline canary guard so an unattended scan on an offline machine leaves
state untouched instead of mass-flipping entries to rotted.
- Wayback fallback: an original already dead at first fetch falls back to
its most recent existing Wayback capture (raw id_ bytes through the
normal pipeline), honouring a preserved X-Archive-Orig noarchive and
recording `fetched-from`. Dead-only, never during refresh.
- bibliography annotation: Filters.Archive exports annotateBlock;
Citations.hs applies it to each CSL-rendered entry, so bibliography
links get the same affordance / rotted-flip as body links.
- search-UI filter: archive "exclude/only" + "link status" filters on the
search page, backed by a new data/archive-meta.json. Scoped apart from
the epistemic `status` filter (own state, classes, labels) rather than
renamed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- import-photo.sh deletes the copied JPEG when EXIF stripping fails, so
the auto-commit can never publish GPS/serial metadata (AUDIT §4.11)
- pre-commit-marks hook: tab-aware path parsing, probes the staged blob
rather than the working tree (§4.11)
- preset-signing-passphrase uses printf; stamp-build-time writes via
temp + os.replace; archive.py passes -- to pdftotext and verifies the
vendored monolith binary against its recorded sha256 (mismatch is
fatal, consistent with the tool's integrity contract); extract-exif
./-prefixes relative paths (§4.11)
- blog-post.html: id="similar-links"/"backlinks" each appear once;
rendered output unchanged (§6.4)
- site.webmanifest: start_url/scope/description added, maskable icon
purpose restored alongside any (§9.3)
- Frontmatter cleanup: scaffold comments out of scaling_outage,
dangling null confidence-history keys removed (populated ones kept),
dead modified: key dropped from colophon (§6.4)
- canto31.jpg: 4.0 MB -> 1.9 MB (2400px, q80, grayscale — the source
is a monochrome Doré engraving, so single-channel is colorimetrically
lossless); webp sidecar regenerated (§6.4, prior-audit §6.1)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
.gitignore has declared content/drafts/ local-only working notes since
the rule was added, but four drafts were already tracked — ignore rules
don't untrack, so make build's auto-commit kept staging and deploy kept
pushing them (AUDIT §6.3). Untracked with --cached; the files remain on
disk and still build in dev. Also moved inclusionist-manifesto.md into
drafts/essays/ where the draft rule actually matches it (§6.1), and
un-shadowed the tracked .env.example from the credential patterns.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>