Rebuilding My Site from Gatsby to Astro with a Team of Very Fast Interns

If you’ve been here before, you might notice things look a little different. 👋

This site sat untouched for almost four years. The last real commit was in December 2022, and it was still running on Gatsby 3, React 17, Tailwind 2 and a node-canvas setup for social images that broke every time I looked at it funny. Even the package.json still said gatsby-starter-tailwind by Taylor Bryant.

The new homepage says I’m a “Fullstack developer, now managing a team of very fast interns.” This post is about the first project those interns shipped: a full rebuild of this site from Gatsby to Astro, using Claude Code and Matt Pocock’s skills.

I didn’t write the code. I wrote (well, approved) the tickets, picked the design, wrote the copy and pressed merge. Here’s how that went, including the parts that didn’t go to plan.

Table of Contents

Don’t start with code: get grilled

I opened Claude Code in the repo and ran /grill-me with one fuzzy prompt:

This repo is for my personal website, and I haven’t updated in a long time. I am looking to update everything: design, frameworks, etc., but I want to preserve as much of the content as possible, as well as the SEO / analytics. Help me figure it out the next steps

Before asking me a single question, it read the repo and pulled my Google Analytics data. That changed the conversation right away. Over the previous 12 months the site had about 820 sessions, and only 56 of them came from organic search. The homepage got more views than any post, and the best post was part 4 of my Craft + Gatsby series.

So the conclusion was: keep every URL exactly as it is, because that’s cheap insurance, but don’t let SEO limit the redesign. That framing ended up guiding the whole project.

It also caught the thing I would have completely forgotten: the old site used gatsby-plugin-offline, which installs a service worker. If you switch frameworks and just drop it, people who visited the old site can keep getting the old cached version forever. The new site had to ship a worker that removes itself.

The grilling went for 21 questions over three rounds: what the site is for now, which posts stay, whether to keep the URLs (including the “programatically” typo in two slugs!), GA4, hosting, Astro vs Next.js vs Eleventy, social images, launch scope, build order and the cutover checklist. Each question came with a recommendation and the reasoning behind it.

I’ll be honest: my answers were mostly “agree with all recommendations”. But that wasn’t rubber-stamping. I read every question and every recommendation, and I was pleasantly surprised by how good they were. Keeping all 12 posts and adding a “Written in 2021” notice instead of pruning them. Fixing the typo in titles but not in URLs. Building on a branch in the same repo, so the git history and the Netlify link stay. Those are the calls I’d have made myself, eventually, after forgetting half of them.

The only questions it pushed back to me were the ones only I could answer: my one-liner, and which socials I still use (just GitHub and LinkedIn).

Put the plan where the agents can read it

Next I ran /setup-matt-pocock-skills. It writes a small CLAUDE.md plus a few files in docs/agents/ that tell the other skills where things live: issues in GitHub (through the gh CLI), a set of triage labels, and where domain docs go. It’s the contract every other skill reads.

Then /to-tickets turned the plan into 12 vertical slices. Each one ends in something you can actually check, usually on a Netlify deploy preview. Each also got a label (ready-for-agent or ready-for-human) and native GitHub “blocked by” links:

# Ticket For Blocked by
#2 Astro skeleton serving one post at its old URL agent none
#3 Migrate posts, images, code highlighting agent #2
#4 Post content fixes (title typo, dated notices) agent #3
#5 Self-removing service worker agent #2
#6 SEO and analytics parity, RSS, JSON-LD agent #3
#7 Design directions: you pick one human none
#8 Chosen design applied to posts and post index agent #3, #7
#9 Homepage: About, Writing, Contact agent #8
#10 Share images at build time agent #6, #8
#11 Repo cleanup agent #2
#12 Verify the domain in Search Console human none
#13 Cutover human #4–#6, #9–#12

This turned out to be the most important step. From here on, the plan lived in the tracker, not in a chat window. Every later session started with “look at the open issues”, and the plan outlived every /clear and context compaction along the way.

“Review the open issues and implement them”

That was the whole prompt for the first build session. A little later, draft PR #14 had six issues done: the Astro skeleton, all 12 posts and their images, the dated-content notices, the self-destroying service worker, SEO and RSS, and the repo cleanup.

A few moments from that session that I liked:

  • Astro 7 shipped a new default markdown processor, Sätteri, which the model didn’t know about. Instead of reaching for rehype plugins from memory, it read the type definitions in node_modules and wrote a small custom plugin for heading anchors and external links.
  • The usual dependency friction: TypeScript 7 was too new for typescript-eslint, so it pinned TypeScript 6. pnpm 12 blocks package build scripts by default, so sharp and esbuild had to be allowed explicitly.
  • Heading anchors matched the old site except two headings with emoji. Before accepting the difference, it checked that nothing linked to them.

The part I appreciated most was that it verified its own work. It wrote two scripts, url-parity and meta-diff, and ran them against the real Netlify deploy preview, not just a local build. All 14 URLs from the live sitemap returned 200. The meta diff showed 19 differences, all intentional: new canonical tags and a couple of typo fixes (“Gastby” → “Gatsby”, “Thougthts” → “Thoughts”).

The human part: design

For #7, I asked for design mockups. Claude designed three directions and published them as a private artifact on a design canvas, where I could flip between them in the browser. Each direction had a homepage and a post page, in light and dark mode:

Three design directions: A Editorial, B Technical and C Quiet sans

The three homepage directions from round one, with my old photo and the placeholder copy.

I went with A, Editorial (Newsreader with IBM Plex Mono). We did two more rounds on it in the same canvas. Round two added phone frames at 390px (the header would have overflowed), an accent colour switcher with four contrast-checked light/dark pairs, and the /posts/ index and 404 page. In round three I picked oxblood as the accent and asked for previous/next links on series posts, since my Craft series is the most-read content on the site.

The Editorial direction on the design canvas: home, writings index, post and 404 pages in light and dark, plus phone frames

The Editorial direction on the design canvas artifact: every page in light and dark, plus phone frames at 390px.

This is where taste lives, and it’s the step I’d least want to delegate entirely. But having an agent produce real, clickable options in a few minutes made it a choice between three things, not a blank page. The final tokens were recorded on issue #7, so the implementation had a spec to follow.

Fanning out to subagents

Applying the design (#8) brought self-hosted fonts through Astro’s fonts API, a theme toggle that never flashes the wrong theme on load, and the series navigation. Claude also took headless Chrome screenshots to check its own work against the mockups.

For the last two agent tickets, I said “use subagents to work on the remaining issues”. #9 (the homepage) and #10 (the social images) each got their own subagent and their own git worktree, running in parallel.

That’s where my favourite moment of the project happened. A few minutes in, the #10 subagent sent a message to the lead agent:

Heads-up: issue-10 agent and issue-9-homepage agent seem to share one session-level worktree setting. Each EnterWorktree call by one flips the other’s isolation, so my writes to issue-10-og get rejected as “outside worktree issue-9-homepage” (and presumably vice versa).

The lead paused one agent and ran them one after the other. (The #9 agent had actually already finished by the time the pause landed.) Both branches merged cleanly into PR #14. The lesson for me: parallel agents are only as isolated as their shared state, and this setup had some I didn’t know about.

#10 replaced the old node-canvas images with Satori and sharp, with no native dependencies. Every post now gets its own share image in the new design:

The generated share image for part 4 of the Craft series

Checks, Search Console and the copy

Before merging, Claude ran the pre-merge checks from the PR description against the deploy preview. Two of them were good reminders that verification has limits:

  • GA4 Realtime showed nothing at first, because Google Analytics discards headless Chrome as a bot. A second attempt registered within a minute.
  • The service worker removal could only be simulated. The preview had never had the real Gatsby worker installed, so it faked an old cache and checked that the new worker cleared it. The real test had to wait for production, and Claude said so instead of ticking the box.

Issue #12 (verifying the domain in Search Console) was labelled for me, but I asked Claude to check it through Chrome DevTools. It turned out the domain had been verified since April 2021. It posted a baseline on the issue: 7 pages indexed, a sitemap Google last read in November 2024, and one click from 1,120 impressions over the previous three months. That’s a healthy dose of humility about what was actually at stake. 😅

The last piece was the homepage copy. Claude drafted a few options from my old bio, and when I told it I’m mostly focused on agentic coding these days and wanted it to be clever or funny, it came back with five more. It recommended “Building the web from Cape Cod, with a little help from my agents.” I went with the interns line instead. Then I swapped my old photo for an illustrated sticker version of me.

Review, merge, live

Before merging, I ran /code-review from Matt’s skills on PR #14. It runs two reviewers in parallel: one for Standards (does the code follow the repo’s own rules?) and one for Spec (does it do what the issues asked?). It found real things:

  • A bug: the browser’s theme-color follows the system setting, so it doesn’t match the page when you use the theme toggle.
  • Copy hardcoded in templates, breaking the one written rule in the repo (“change copy and links in site.ts, not in templates”).
  • Duplicated “Part X of Y” logic in three places, and a PR description that was out of date.

And then I said “merge it for now”. Agents make review cheap, but you still choose what to act on, and I wanted the site live. Those findings are next on the list, and I’ll write about cleaning up after the interns in a follow-up post.

Claude merged the PR and started a watcher for the Netlify deploy, polling GitHub for the commit status. Netlify never posted one, so the watcher stayed silent. In the end it was me who said “I think it landed”. It checked production right away: all 14 old URLs returned 200.

Here’s the before and after:

The old Gatsby homepage, with photo, social icons and a big Craft + Gatsby + Netlify cover image

Before: the Gatsby homepage, via the Wayback Machine.

The new Astro homepage in the Editorial design, with the interns one-liner and the sticker portrait

After: the new homepage.

The old post page for part 2 of the Craft series

Before: a post page. Note “Gastby” in the title.

The new post page with the Part 2 of 4 label and the Written in 2021 notice

After: the same post, with the series label, the “Written in 2021” notice and the typo fixed. Same URL.

What I’d tell you before you try this

  • Get grilled before you build. The service worker alone justified it. The questions surface decisions you’d otherwise make by accident, or not at all.
  • Put the plan in the tracker. GitHub issues outlived every session. The agents always knew what was next, and so did I.
  • Every ticket needs a checkable “done”. A deploy preview plus a script beats “looks good to me”.
  • Keep taste and the final calls human. Design, copy and the merge button were mine. Everything else was delegated.
  • Parallel agents need truly isolated state. Check what’s shared before you fan out.
  • Agents verify well when you give them tools, like URL diffs, GA Realtime and screenshots. They’re also honest when they can’t verify something, which matters just as much.

By the numbers

  • Planning: one grilling session (21 questions), then 12 tickets.
  • Building: from the first build commit on the afternoon of October 1 to the merge the next morning, about 21 hours of wall-clock time.
  • Diff: 85 files changed, +7,099 / −16,729 lines.
  • Skills used: /grill-me, /setup-matt-pocock-skills, /to-tickets and /code-review.

The interns did good work. I’m planning to write a lot more about working with them, and this post is the first one.