Paige Rattenberry

How this site was built

Claude Code wrote this site's code, tests and build logs, one session and one reviewed pull request at a time; Codex implemented one review follow-up. Paige made the decisions and merged every pull request. This page is the record: who did what, the plan, what went wrong and what caught it, and a log for every session.

Who did what

Generated by Claude Code

  • The planning documents, drafted with Claude Code in a planning session on 2026-09-07, before any code; the design was approved that day, and later amendments are dated in place.
  • The code, tests, CI workflow, build scripts and generators: one session and one pull request at a time, each session started from its prompt. The one exception is the September 13 review follow-up, which Codex implemented.
  • The wording of the pages, drafted from Paige's resume and source materials, with every number held in content/ next to its source. The headline on Home is Paige's own wording.
  • Every build log but that follow-up's, which Codex wrote, each with its record of what the AI got wrong; and this page, synthesis included.

Decided, reviewed or rejected by Paige

  • The decisions: the sections, the stack, the visual direction, which projects get deep pages and in what order, the headline on Home, and the changes of plan. DESIGN.md and the plan record each one with its date.
  • What may be published: she staged the source materials and decided what could appear on the site.
  • Choices among the options a session put to her, which meant turning the others down: the JavaScript budget in Session 7 (three ranked options), the footer screenshots in S0-G (four), and GitHub Pro, declined on 2026-09-23 in favour of branch protection on a public repository.
  • The review of every pull request, and every merge: no session merged its own work.
  • The setup outside the code: deploying StimMap3D and making its repository public, and connecting Vercel.

Claude Code throughout, with the models each log records: Claude Fable 5.1, Claude Opus 5 and Claude Opus 5.5, and Claude Sonnet 5.5 for read-only screenshot checks in S0-G. Codex (GPT-6) wrote the September 13 review and its follow-up. The logs of Sessions 1 and 2 name the tool but not the model.

The plan

The planning documents set the work before any code was written, and every session amended them in place when a decision changed.

DESIGN.md
The decisions, site map, content model, visual direction, architecture and quality bar, with every amendment dated and credited.
IMPLEMENTATION_PLAN.md
The sessions in order, each with its tasks and acceptance list, and the launch steps.
SESSION_PROMPTS.md
The prompt pasted into the coding agent at the start of each session.

What went wrong, and what caught it

Every log has a section called "What the AI got wrong and how it was caught" ("What Claude got wrong" in the earliest ones). Read together (the logs written before this page) and grouped by what caught each item, they show which checks did the catching, and which checks exist because something got through.

This is an honest record of one site's build, written as the work happened. It is not research, a benchmark, an evaluation or a dataset. The counts are of items written in these logs, the grouping is one reading of them, and none of it says anything about coding agents in general. The gates were engineering, not an experiment.

  1. A review pass115 items

    A separate review of the branch or pull request before Paige's review: Claude Code's code review, a review agent, or a second Claude Code session, and once a Claude Code review of Codex's work. Automated review, not Paige's.

    • The mobile contour was a crop of the desktop field again. Small review follow-up, 2026-09-13
    • The Session 1 and 2 prompts and tasks were rewritten, altering the record. Small review follow-up, 2026-09-13
    • Two build-log templates had diverged. Small review follow-up, 2026-09-13
    • The Resume link was indented at 390 px. Small review follow-up, 2026-09-13
    • A staged asset's digest was recorded but never checked. Small review follow-up, 2026-09-13
    • The headshot loader's error did not say the asset must be an image. Small review follow-up, 2026-09-13
    • Card copy dropped the capstone team size and the thesis method count. Small review follow-up, 2026-09-13
    • StimMap3D's public commit count was recorded wrong. Session 0-D
    • Checks stopped at the numbers: a block quote was attributed to a README section that does not exist. Session 0-D
    • The first publication gate had gaps: a term wrapped across a line, staged names outside a folder, home-folder shapes, silent skips. Session S0-G
    • The fixed gate still had gaps: long or bracketed old names, pattern terms across a line break, runs in a fork. Session S0-G
    • The scrub went past removal in four places, dropping credits and decisions. Session S0-G
    • Smaller scrub gaps: a dangling bullet, unmarked redactions, stale links. Session S0-G
    • The link-accent utility emitted no CSS. Session 1
    • The prose max-width token was never applied. Session 1
    • The mobile menu's open state was keyed to the pathname. Session 1
    • The contour's path data went over the wire twice. Session 1
    • The end-to-end specs could run against a development server. Session 1
    • outline-none removed the focus ring. Session 1
    • git log ran once per route. Session 1
    • Project names were typed into page copy. Session 1
    • The third ink colour failed AA contrast in both themes, and axe never saw it. Session 1
    • A fixed root font size overrode the reader's setting. Session 1
    • Nothing enforced Prettier. Session 1
    • A route count was off by one. Session 1
    • The stock favicon shipped. Session 1
    • A shell call through cmd.exe ate the git format string. Session 1
    • The footer date was a day ahead. Session 1
    • format:check would fail on a Windows clone with CRLF line endings. Session 1
    • The accessibility gate skipped the 404 page and the open mobile menu. Session 1
    • A stale production server could be reused by the specs. Session 1
    • Home's "Download resume" button led to a placeholder page. Session 1
    • The page scrolled sideways at phone width. Session 2
    • A hidden tooltip widened the page. Session 2
    • Bullets were not verbatim: dashes had changed. Session 2
    • Metric matching had no token boundary. Session 2
    • The GMLE certification printed twice. Session 2
    • A deprecated image prop. Session 2
    • A source label fell back to the raw source id. Session 2
    • Pages imported raw content modules, skipping validation. Session 2
    • StimMap3D appeared on About without its disclaimer. Session 2
    • A figure caption overprinted at 768 px. Session 2
    • A tooltip trigger was 11 px wide. Session 2
    • The contour weighed more than its budget. Session 2
    • Smaller cleanups from the review. Session 2
    • Tooltips ran off phone screens. Session 2
    • Tooltips failed WCAG 1.4.13 (dismissable, hoverable). Session 2
    • Hero contour lines stopped short of the right edge. Session 2
    • Splitting text by metrics could drop a metric. Session 2
    • A private staging path was served in the /projects page. Session 3a
    • Two metrics rendered without their source. Session 3a
    • The case studies were reordered. Session 3a
    • Three different definitions of a deep page. Session 3a
    • Nested complementary landmarks. Session 3a
    • A proof line carried numbers without metrics. Session 3a
    • A fact typed as a page literal. Session 3a
    • The EEG-TMS card named StimMap3D without its disclaimer. Session 3a
    • loading="lazy" on a click-mounted iframe could outlast the load timeout. Session 3a
    • Filter chips could be clicked before hydration. Session 3a
    • A malformed MetricStat tag passed silently. Session 3a
    • The claims gate over-counted a metric in a tagline. Session 3a
    • The embed frame lost keyboard focus after loading. Session 3a
    • The live region was inserted already filled. Session 3a
    • A whole asset record, staging path included, was serialized into a client island's props. Session 3b
    • The embed's two branches tested different conditions, so the disclaimer could fail open. Session 3b
    • The aside's links bypassed both link gates. Session 3b
    • A relaxed digest pattern turned a malformed revision into a skipped check. Session 3b
    • A dead fallback in the capture script. Session 3b
    • A cramped diagram at 390 px. Session 4
    • A caption fact typed in a component. Session 4
    • A figure could be numbered NaN or plot values out of scale. Session 4
    • Two figures were numbered 1. Session 4
    • Key collisions and long floats in the chart markup. Session 4
    • A test that could never fail. Session 4
    • A removed page was still inside the derived PDF. Session 4
    • Page labels were off by one. Session 4
    • A diagram note was not indented. Session 4
    • The log named a commit that no longer existed. Session 4
    • A margin utility did nothing. Session 4
    • An h3 rendered larger than its h2. Session 4
    • The StimMap3D timeline entry lacked its disclaimer. Session 5
    • The dimming rule recoloured hover and the tooltip. Session 5
    • The live region could announce a stale filter. Session 5
    • The Home teaser had no size cap. Session 5
    • CI never ran the explorer and projects specs. Session 5
    • Teaser labels were sized wrong. Session 5
    • prebuild rewrote the layout, so its guard could never fire. Session 5
    • Transitions ran under reduced motion. Session 5
    • Hovering under a filter dimmed the filter's own neighbourhood. Session 5
    • Environment files loaded after the site URL was read. Session 6
    • A stale overflow PDF survived a good build. Session 6
    • No predev hook rendered the resume. Session 6
    • Some strings bypassed the resume's font-run splitter. Session 6
    • A document exception was itself phone-shaped. Session 6
    • A NUL byte in the build log. Session 6
    • The site URL was not validated. Session 6
    • The resume's include flags were untested. Session 6
    • Typos in regular expressions. Session 6
    • Section headings were typed in the generator while AGENTS.md said no fact is typed. Session 6
    • The JSON-LD escape did nothing. Session 6
    • A trailing slash on the site URL failed the build. Session 6
    • The resume generator ignored the production environment file. Session 6
    • Definitions were attributed to the literature review's sources. Session 6b
    • An entry count was wrong. Plan amendment, 2026-09-20
    • A metric was sent to the wrong file. Plan amendment, 2026-09-20
    • A label that fits was listed as one that does not. Plan amendment, 2026-09-20
    • A change to Home's skills strip was called a reorder. Plan amendment, 2026-09-20
    • An acceptance item was too broad. Plan amendment, 2026-09-20
    • "Measured" covered one label, not two. Plan amendment, 2026-09-20
    • The font spec skipped the 404 page. Session 7
    • The local Lighthouse script could orphan its server. Session 7
    • The served font family took the loader's const name, a reserved font name. Session 7
    • The JS budget subtracted framework files the page did not load. Session 7
    • A content-visibility rule contradicted what shipped. Session 7
    • The plan called Session 7 shipped while it was in review. Session 7
  2. The agent's own check68 items

    Something the working agent did before committing: looked at a screenshot, measured, re-read a source or its own diff, or ran a scratch script.

  3. A unit or component test13 items

    A Vitest test other than the claims and privacy gates.

    • The contour grid overshot the view box. Session 1
    • A fallback waited for an iframe error event that never fires. Session 3a
    • A test fixture retyped research fields. Session 3a
    • Grid cards rendered only the tagline, so a ranking went unrendered while the claims gate passed. Session 3a
    • Heredocs halved the backslashes in a regular expression. Session 3b
    • MDX expression attributes were silently dropped. Session 4
    • A matcher was used where it cannot match. Session 4
    • A slug equal to a research id broke a test. Session 5
    • The literature review PDF had no title. Session 6
    • The resume failed the orphan-object check, twice. Session 6
    • A pairing was typed twice. Session 6b
    • The plan's task list was incomplete in three places. Session 6b
    • The font test expected a name record the subsetter drops. Session 7
  4. An end-to-end test11 items

    A Playwright test against the production build, other than axe.

    • Two concurrent Playwright runs shared one results folder. Small review follow-up, 2026-09-13
    • A test tried to focus a heading. Session 2
    • A locator counted nested list items. Session 3a
    • A live region sat in the server-rendered fallback. Session 5
    • A pattern lost its backslash, and the screenshots timed out. Session 5
    • A "flaky" tooltip test was smooth scrolling. Session 5
    • A metadata test over every page timed out. Session 6
    • A PDF check counted page objects in raw bytes. Session 6
    • Pages below the root lost their Open Graph image. Session 6
    • The tooltip test's wait raced smooth scrolling. Session 7
    • A Lighthouse flakiness estimate was wrong within the hour. Session 7
  5. The type checker or the build9 items

    tsc, next build, the resume generator's page guard, or a failed deployment.

    • The first Vercel build ran with no framework preset. Session 0-E
    • Typecheck failed on a clean tree. Session 1
    • Heredoc writes never landed. Session 2
    • A Required<> type broke. Session 2
    • __dirname pointed elsewhere under the bundler. Session 3a
    • A click handler sat in a server-rendered fallback. Session 3a
    • Git Bash's path rewriting broke the build. Session 5
    • The resume overflowed to three pages, then four. Session 6
    • The PDF renderer could not be required by the script runner. Session 6
  6. The privacy or publication gate7 items

    The phone pattern over every text file and shipped PDF, the staged-asset checks, and since S0-G the publication gate.

  7. The claims gate2 items

    The tests that every number is a sourced metric, present in its own text.

    • About's no-digits check tripped on real names. Session 2
    • Thesis metrics were declared but never rendered. Session 2
  8. A lint rule2 items

    ESLint, including the React Compiler rules.

  9. The axe check1 items

    Axe over every page in both themes.

    • Dimming entries by opacity failed contrast. Session 5
  10. Paige1 items

    Something Paige asked or noticed. Her review at merge is not written into the logs: only Session 1's log records her verdicts, so this group counts what the logs say, not what she caught.

    • The README's status had not changed since Session 2. Session 5
Fig. 1Items in the logs' "What the AI got wrong" sections, by what caught them (229 in all).

Gates added because of a failure

48 of the 229 items ended in a new test, check or rule, and 42 of those came out of a review pass: in each, the log says the gate was added because of that failure.

List them
  • The privacy test recomputes every staged digest. After: A staged asset's digest was recorded but never checked. Small review follow-up, 2026-09-13
  • Pattern, section and commit-message tests for the publication gate. After: The first publication gate had gaps: a term wrapped across a line, staged names outside a folder, home-folder shapes, silent skips. Session S0-G
  • A test per gap, and the old names moved into the private term list. After: The fixed gate still had gaps: long or bracketed old names, pattern terms across a line break, runs in a fork. Session S0-G
  • format:check as the first step of CI job 1. After: Nothing enforced Prettier. Session 1
  • Axe scans the 404 and the open menu. After: The accessibility gate skipped the 404 page and the open mobile menu. Session 1
  • An end-to-end overflow check at 360, 390 and 1280 px. After: The page scrolled sideways at phone width. Session 2
  • The same overflow check. After: A hidden tooltip widened the page. Session 2
  • The claims gate uses the renderer's matcher (in part). After: Metric matching had no token boundary. Session 2
  • The ESLint rule that forbids importing content/ outside the loaders. After: Pages imported raw content modules, skipping validation. Session 2
  • The path-data cap, lowered to 6.5 KB (in part). After: The contour weighed more than its budget. Session 2
  • An end-to-end check that every tooltip stays on screen at 360 px. After: Tooltips ran off phone screens. Session 2
  • A contour test that a line reaches the edge. After: Hero contour lines stopped short of the right edge. Session 2
  • A unit test for the split. After: Splitting text by metrics could drop a metric. Session 2
  • Unit and end-to-end checks that no staging path reaches served HTML. After: A private staging path was served in the /projects page. Session 3a
  • The claims gate counts only rendered text; a test gives every metric its source button. After: Two metrics rendered without their source. Session 3a
  • A test pins the featured order. After: The case studies were reordered. Session 3a
  • A no-JavaScript test that the live region is absent (in part). After: Filter chips could be clicked before hydration. Session 3a
  • A malformed tag fails the claims gate (in part). After: A malformed MetricStat tag passed silently. Session 3a
  • A test that no deep-page tagline holds a metric. After: The claims gate over-counted a metric in a tagline. Session 3a
  • A focus test. After: The embed frame lost keyboard focus after loading. Session 3a
  • A status-region test. After: The live region was inserted already filled. Session 3a
  • The privacy check runs on every HTML page. After: A whole asset record, staging path included, was serialized into a client island's props. Session 3b
  • The aside's links go through the publishable-link checks. After: The aside's links bypassed both link gates. Session 3b
  • A test that the digest was read. After: A relaxed digest pattern turned a malformed revision into a skipped check. Session 3b
  • The figure components throw at build (in part). After: A figure could be numbered NaN or plot values out of scale. Session 4
  • A content test on figure numbering. After: Two figures were numbered 1. Session 4
  • Orphan-object and page-count checks on every shipped PDF. After: A removed page was still inside the derived PDF. Session 4
  • A page-label assertion. After: Page labels were off by one. Session 4
  • Unit, component and end-to-end disclaimer tests. After: The StimMap3D timeline entry lacked its disclaimer. Session 5
  • An end-to-end test that hovers a dimmed link. After: The dimming rule recoloured hover and the tooltip. Session 5
  • A 16 KB unit cap on the teaser. After: The Home teaser had no size cap. Session 5
  • CI job 2 runs the feature specs. After: CI never ran the explorer and projects specs. Session 5
  • A label-fit test. After: Teaser labels were sized wrong. Session 5
  • prebuild only checks the committed layout. After: prebuild rewrote the layout, so its guard could never fire. Session 5
  • A reduced-motion test over every button. After: Transitions ran under reduced motion. Session 5
  • A unit test over every node and edge. After: Hovering under a filter dimmed the filter's own neighbourhood. Session 5
  • Excepted pages still reject four phone shapes. After: A document exception was itself phone-shaped. Session 6
  • Resume test assertions on them. After: The resume's include flags were untested. Session 6
  • A unit test of the escape. After: The JSON-LD escape did nothing. Session 6
  • Unit tests for the site URL. After: A trailing slash on the site URL failed the build. Session 6
  • The font spec covers the 404. After: The font spec skipped the 404 page. Session 7
  • A font test that fails on such a const. After: The served font family took the loader's const name, a reserved font name. Session 7
  • Trailer addresses are blanked before the term check, with a test. After: The commit-message check would have failed the first public commit. Session S0-G
  • A content test that every GitHub URL is exactly the cleared repository. After: Two tests used a real repository's URL as their refusal case. Session 3b
  • An end-to-end check at 360 px. After: Thesis chart labels ran together at 360 px. Session 7
  • An end-to-end check at 360 px. After: A video's title ran under its button at 360 px. Session 7
  • An anchor test in the explorer spec. After: content-visibility on the timeline nearly shipped, and broke anchor links. Session 7
  • A test pins the revision format. After: A staged revision broke the digest check. Session 3b

In the logs' words

Verifying the number in a claim is not the same as verifying the sentence around it.

The session's own aside link is a cleared-repository URL, so nothing shipped wrong; the gate was the defect.

Neither defect widened the page, so no existing check could see them; each has an e2e check that fails on the old markup.

What worked

  • A review pass before most merges. It caught more than any other check, and most of the gates added because of a failure came out of one. Session 1, Session 2, Session 3a, Session 5
  • Looking at screenshots in the session. Layout defects that no check could see were found in the captures, then pinned with a test. Session 1, Session 2, Session 7
  • Generated files committed, with a --check in prebuild: a deploy runs no generator, and CI proves the bytes reproduce. Session 5, Session 7
  • Open questions written as ranked options with the evidence, so Paige's answer is a choice, not a re-investigation. Session 1, Session 7

What did not

  • Checks that read text rather than the rendered page: the claims gate passed while a ranking went unrendered. Session 2, Session 3a
  • Shell heredocs and escapes on Windows: backslashes and bytes went missing in session after session. Session 2, Session 3b, Session 5, Session 6, Session S0-G
  • Estimates from too little data: a number put on the Lighthouse gate's flakiness was wrong within the hour. Session 7

The build log

The public history begins at launch; the counts below are a snapshot of the pre-launch history, and the build logs are the record of the build.

19 pull requests merged between and , and 249 commits on the main branch, 19 of them merges. Snapshot of , taken by hand during Session 9, before its own pull request merged.

Every entry below links to its full log: goal, what shipped, decisions, tools, and what the AI got wrong; most also record how the work was verified. Pull request numbers refer to the private pre-launch repository.

  1. 2026-09-08

    Session 1

    PR #1

    Scaffold, design system, shell

    Read the log: Session 1 — Scaffold, design system, shell

  2. 2026-09-09

    Session 0-E

    PR #2

    Connect Vercel

    Read the log: Session 0-E — Connect Vercel

  3. 2026-09-10

    Session 0

    PR #2

    spec amendments

    Read the log: Session 0 — spec amendments

  4. 2026-09-12

    Session 2

    PR #3

    Content model, Home, About, Experience

    Read the log: Session 2 — Content model, Home, About, Experience

  5. 2026-09-13

    Small review follow-up

    PR #4

    Small review follow-up

    Read the log: Small review follow-up — 2026-09-13

  6. 2026-09-13

    Plan amendment

    PR #5

    split Session 3 into 3a and 3b

    Read the log: Plan amendment — split Session 3 into 3a and 3b

  7. 2026-09-13

    Session 3a

    PR #6

    Projects index, MDX pipeline, EmbedFrame, StimMap3D page body

    Read the log: Session 3a — Projects index, MDX pipeline, EmbedFrame, StimMap3D page body

  8. 2026-09-13

    Session 4

    PR #7

    Capstone and thesis deep pages, Research, Leadership

    Read the log: Session 4 — Capstone and thesis deep pages, Research, Leadership

  9. 2026-09-15

    Plan amendment

    PR #8

    defer Session 3b behind Sessions 5 and 6

    Read the log: Plan amendment — defer Session 3b behind Sessions 5 and 6

  10. 2026-09-15

    Session 5

    PR #9

    Career timeline + skills explorer

    Read the log: Session 5 — Career timeline + skills explorer

  11. 2026-09-16

    Session 6

    PR #10

    Resume PDF from the single source of truth + SEO, Open Graph images, sitemap

    Read the log: Session 6 — Resume PDF from the single source of truth + SEO, Open Graph images, sitemap

  12. 2026-09-20

    Session 0-D

    PR #11

    Deploy StimMap3D and make it public

    Read the log: Session 0-D — Deploy StimMap3D and make it public

  13. 2026-09-20

    Session 3b

    PR #12

    StimMap3D live embed, public links and screenshots

    Read the log: Session 3b — StimMap3D live embed, public links and screenshots

  14. 2026-09-20

    Plan amendment

    PR #13

  15. 2026-09-20

    Session 6b

    PR #14

    The headline and the interpretability thread

    Read the log: Session 6b — The headline and the interpretability thread

  16. 2026-09-21

    Session 7

    PR #15

    Accessibility + performance gates, cross-device polish

    Read the log: Session 7 — Accessibility + performance gates, cross-device polish

  17. 2026-10-01

    Session S0-G

    PR #17

    Publication scrub

    Read the log: Session S0-G — Publication scrub

  18. 2026-10-01

    Session 8

    PR #18

    How This Site Was Built, and launch preparation

    Read the log: Session 8 — How This Site Was Built, and launch preparation

  19. 2026-10-06

    Session 9

    PR #19

    content update

    Read the log: Session 9 — content update

  20. 2026-10-07

    Session 9b

    PR #20

    pre-seed fixes

    Read the log: Session 9b — pre-seed fixes

Screenshots

Each session that changed the interface saved screenshots of its pages to docs/screenshots/. A few, cropped to the first screen, show how the site changed.

The site's home page after Session 1: the name in a serif headline over teal contour lines, a short introduction and three buttons, with the header navigation above.
Fig. 2Home after Session 1: the shell, the type and colour tokens and the contour motif, with content still to come.Source: docs/screenshots, Session 1
The home page after Session 2: the name, a one-paragraph headline, three buttons and the start of a three-card Selected work row, over faint contour lines.
Fig. 3Home after Session 2: the content model behind the hero and the first selected work.Source: docs/screenshots, Session 2
The projects index after Session 3a: three case-study cards, StimMap3D first with its disclaimer line, and the start of a filterable list below.
Fig. 4The projects index after Session 3a: case studies first, then a filterable grid.Source: docs/screenshots, Session 3a
The StimMap3D project page after Session 3b: the title and tagline, the boxed disclaimer Illustrative model, not for clinical use, and below it the poster of the embedded app.
Fig. 5The StimMap3D page after Session 3b: its disclaimer, then the poster of the click-to-load demo.Source: docs/screenshots, Session 3b
The career timeline and skills explorer on the Experience page after Session 5: a network of labelled skill nodes joined by lines, with Constellation and View as list buttons above it.
Fig. 6The skills constellation on Experience after Session 5, laid out once at build time.Source: docs/screenshots, Session 5
The home page after Session 6b: the headline and buttons on the left and the skills constellation teaser on the right, labelled Fig. 1.
Fig. 7Home after Session 6b: the headline beside the constellation teaser.Source: docs/screenshots, Session 6b

Case study 1: StimMap3D

The first project built this way, and the pattern this site follows. It has no session pack of its own: its repository's notes on AI-assisted development are the record.

Interactive 3D model of TMS coil placement and the cortical field it induces.

StimMap3D was built with Claude Code between June and August 2026. Its repository describes the workflow: work is split around observable contracts, acceptance criteria name the failure cases before implementation, and the maintainer's decisions set the product scope and the scientific boundaries.

Its design document states the honesty gates its tests enforce, and its agent instructions repeat them for every session: the disclaimer on every route and exported figure, synthetic trajectories labelled, relative field units. It is explicit that automated review is not independent human review or clinical validation.

This site is built the same way, with a build log and screenshots recorded as each session lands.