Skip to content
Always Shippable

Agent-Built · Part 4 of 4

How I Code with Agents Today

The process I use to turn an idea into application code with agents, shown end to end on a real case study: this blog.

24 min read
A tangled idea unwinds through conversation and a plan into ordered repository blocks behind a check
On this page

TL;DR#

Agentic coding isn’t “write a giant prompt and hope.” It’s an engineering process for moving ambiguity out of my head and into plans, code, validation, and repository rules. This process helps me start a project in good state.

The main shift isn’t that Claude writes more code. It’s that I’m designing a system in which future agent sessions are less likely to do the wrong thing, because the repo itself pushes back.

The rest of this post walks that process end to end on one real case study: this blog, the site you are reading. The process has three beats: decide before you prompt, scaffold and then plan against it, and treat validation as the real output. The result is a static site where, on every push, CI proves that:

  • Lighthouse scores 100/100/100/100 on representative pages
  • every page passes an axe WCAG 2.2 AA audit and the W3C Nu markup validator
  • prose pages ship zero JavaScript
  • the build fails on a broken link, a missing alt, an unknown tag, or any admin output in dist/

Those are not goals. They are a floor the repo enforces, and a red check blocks the deploy.

Each section opens with this rail of the process: the three beats, the eleven stages inside them, and the bold stage is the one we’re in.

  1. Decide before you prompt
    1. fuzzy idea
    2. reason about product + architecture
    3. surface the assumptions I didn't specify
    4. bootstrap prompt
  2. Scaffold, then plan against it
    1. working repository scaffold
    2. plans with validation baked into every stage
    3. plan review / steering
  3. Validation is the real output
    1. agent execution
    2. quality becomes a floor
    3. every issue becomes a permanent check
    4. extend it in short sessions, each one shippable

The idea for the case study#

The concrete example is a new static technical blog. The app is intentionally modest; the workflow is the interesting part. The core constraints were a GitHub Pages project site served under /blog/, static pages, MDX posts, React only for interactive islands, search with no external service, a local-only admin, and no analytics, comments, CMS, or backend, all while meeting accessibility, SEO, performance, and responsive-behavior targets. That is not a huge application, but routing, static generation, MDX, React islands, client-side search, SEO, accessibility, dev-only admin tooling, and GitHub Pages deployment constraints are enough moving parts to exercise the process.

The blog was just a convenient product. The real object being built was an agent-maintainable codebase, and agentic coding raises the value of executable constraints. “Agent-maintainable” meant explicit contracts, typed schemas, deterministic file organization, validation, reusable primitives, build-time failures for invalid states, straightforward conventions, low ambiguity, and clear instructions for future agents. Instead of relying on future agents to remember instructions, I want the repo itself to reject bad states. Each human convention should become a repository contract, and each contract should become a machine-checkable invariant. In practice that means an unknown tag, a broken internal link, and admin content in dist/ are all build failures.

  1. Decide before you prompt
    1. fuzzy idea
    2. reason about product + architecture
    3. surface the assumptions I didn't specify
    4. bootstrap prompt
  2. Scaffold, then plan against it
    1. working repository scaffold
    2. plans with validation baked into every stage
    3. plan review / steering
  3. Validation is the real output
    1. agent execution
    2. quality becomes a floor
    3. every issue becomes a permanent check
    4. extend it in short sessions, each one shippable

I talked to ChatGPT before Claude#

Before asking Claude to code, I used ChatGPT to interrogate the idea and expose hidden architectural decisions. The danger with AI-assisted coding is invisible decisions: they get baked into the first code pass before you notice them. The reusable pattern is simple: do not ask for the implementation prompt until the model has asked you what decisions are missing. My starting point was intentionally messy, a bag of requirements like TypeScript, React, GitHub Pages, a landing page, search, series navigation, a local admin, light/dark mode, accessibility, etc.

I did not ask for the final Claude prompt immediately. Instead I asked ChatGPT, before writing the actual startup prompt, to ask me the questions that would be useful for making the architecture explicit. That changed the task from “write a prompt” to “surface the decisions this prompt needs to encode.” ChatGPT came back with 45 questions, and about ten of them were decisions I did not know I needed to make.

Actual conversation: my opening prompt

Me:
I want to write a project setup prompt for Claude, but I want to discuss it before writing. I want to specify things like TypeScript, MUI, React, Vite, ESLint or whatever Vite uses. What else do we need to specify?

It will be GitHub Pages. This will be a blog: a landing page with index and featured content, a search page, individual blog pages, series that posts belong to and navigation to the next one, and a series page.

Blog writing will all be done in code, with no live editing. We will need a logo, excellent SEO, responsive behavior, accessibility, correct contrast, light and dark mode, favicon, personal links, series definitions and descriptions, my profile, and a local-only blog admin for adjusting configuration.

Posts will be statically runnable React pages that are agentically written as Markdown with React, SVG, or images inside them.

First, I want you to ask more questions that may be helpful in crafting the startup project prompt for Claude.

  1. Decide before you prompt
    1. fuzzy idea
    2. reason about product + architecture
    3. surface the assumptions I didn't specify
    4. bootstrap prompt
  2. Scaffold, then plan against it
    1. working repository scaffold
    2. plans with validation baked into every stage
    3. plan review / steering
  3. Validation is the real output
    1. agent execution
    2. quality becomes a floor
    3. every issue becomes a permanent check
    4. extend it in short sessions, each one shippable

The first model exposed the real forks#

ChatGPT turned the bag of requirements into specific architectural choices. These were the forks that mattered:

  • Markdown vs MDX
  • SPA routing vs emitted static HTML
  • pure React/Vite vs Astro + React
  • MUI vs publication-oriented design primitives
  • centralized metadata vs free-form frontmatter
  • global components vs post-local components
  • client-side search vs hosted search
  • local-only drafts vs draft visibility in production
  • config-driven featured content vs post metadata
  • static site vs runtime backend
  • how much validation belongs at build time

If I had skipped this step, Claude would have silently made those choices, some fine, some wrong. The problem is not just wrong decisions; it is invisible ones. An invisible architecture decision becomes expensive to reverse because it gets baked into the first code generation pass, and by the time you notice it, half the codebase assumes it.

Actual conversation: ChatGPT's first response

ChatGPT:
Before writing the Claude startup prompt, I would lock down a few architectural decisions. The biggest one is probably content format. What you described sounds much more like MDX than plain Markdown, because posts need to embed React components, SVGs, and other interactive content.

Here are the questions I would answer first:

  1. Are you good with MDX for posts?
  2. Since this is GitHub Pages, do you want clean client-side routes, or actual static HTML emitted for every route?
  3. Is React + Vite mandatory, or is React-based Astro acceptable?
  4. Do you specifically want Material UI, or just a serious component library?
  5. Do you want Claude to establish a complete design system from the start?
  6. Besides text, images, SVG, and React components, what should posts support?
  7. Should MDX be allowed to import arbitrary React components, or should there be a curated blog-component library?
  8. What metadata should every article require?
  9. Should unpublished posts exist in the repo and appear locally while being excluded from production?
  10. Can a post belong to only one series or multiple series?
  11. How should series ordering work?
  12. Should series metadata be centralized?
  13. Do you want both tags and series?
  14. Should search be entirely static and client-side?
  15. What should search cover?
  16. What should dominate the landing page?
  17. Is the article index separate from the home page?
  18. What belongs in the profile?
  19. Should personal links be generic config objects?
  20. Should /admin exist only in local development?
  21. Are you okay with a tiny local Node/Vite mechanism that writes config files?
  22. Should admin manage posts? I would keep it out.
  23. Should we establish an explicit contract such as posts/<slug>/index.mdx plus assets?
  24. Should the repo contain CLAUDE.md?
  25. Should article-local images live next to each post?
  26. Should agents be encouraged to produce inline SVG/React visualizations?
  27. Do you care about responsive image optimization?
  28. Should SEO explicitly include canonical URLs, OpenGraph, JSON-LD, sitemap, robots, RSS, etc.?
  29. Should posts have generated fallback OG images?
  30. RSS?
  31. Analytics?
  32. Comments?
  33. Newsletter?
  34. Should we explicitly require WCAG 2.2 AA?
  35. Should we put hard Lighthouse goals in the project contract?
  36. What testing stack do you want?
  37. Should GitHub Actions run checks and deploy?
  38. npm, pnpm, or something else?
  39. Should we pin Node?
  40. Will this use a custom domain or a GitHub Pages project path?
  41. Should direct navigation and refresh be an explicit requirement?
  42. Should we prohibit secrets, runtime credentials, and unnecessary third-party scripts?
  43. Modern evergreen browsers only?
  44. System default theme with persistent light/dark override?
  45. Do you want strong architectural constraints specifically because agents will maintain the repo?

I already see one architectural choice I would seriously consider changing:

Instead of React + Vite + MUI + Markdown, I would consider Astro + React + MDX + MUI where useful.

You would still be writing React. Astro itself uses Vite. But you get static HTML per blog post, filesystem/content routing, MDX support, very little client JavaScript, stronger SEO defaults, and the ability to embed genuinely interactive React components only where needed.

I answered decisions, not trivia#

I made product and architecture calls but avoided over-prescribing implementation details. The division of labor is that the human decides invariants and the agent decides implementation mechanics while executing. The decisions I locked looked like this:

MDX: yes
Static HTML: yes
React islands inside MDX: yes
Astro: yes
External search service: no
Drafts in production: no
One series per post: yes
Series order: explicit, default max + 1
Admin edits posts: no
Admin edits config: yes
Analytics/comments/newsletter: no
WCAG 2.2 AA: yes
npm: yes

I did not need to dictate the exact Node version, the Astro APIs, the component implementation, the shape of test files, etc. The invariant mattered more than the implementation. “/admin/ must not exist in production output” is an invariant; the precise Astro/Vite mechanism that guarantees it is an implementation detail, and the agent is better placed than I am to pick it once it can see the real toolchain.

By the end of the conversation, the fuzzy idea was a decision list, and a decision list is something I can build prompts from. It became two: one prompt to build the scaffold, and one to build the implementation plan for the product.

Actual conversation: my answers to the first round

Me:

  1. MDX, perfect.
  2. Emit actual static HTML, however we still want client-side MDX charts, etc.
  3. Let’s try Astro.
  4. Skip MUI, but I want a serious library.
  5. Yes, we can base the design system on strong standards for now: Medium, Hashnode, etc.
  6. Conceptually yes, it’s MDX. Downloadable files are a no.
  7. A curated list is fine, but if I want something bespoke Claude will just add it.
  8. The metadata list makes sense; most of it will be agentically filled in.
  9. Whatever is simpler. Local-only drafts are fine.
  10. Zero or one series.
  11. Posts should have an order flag, defaulted to one more than the last published post in the series.
  12. Yes, central series definitions.
  13. Yes, tags too.
  14. Yes, no external search service.
  15. Search all sensible content without going crazy.
  16. Title/logo, featured post, recent posts, footer, menu.
  17. A separate index page can have all posts and could be combined with search.
  18. The proposed profile fields are fine for now.
  19. Yes, generic links.
  20. Yes, local-only admin with direct editing of config.
  21. Yes, keep the write mechanism simple.
  22. No post editing in admin.
  23. Yes, explicit post directory contract. There will also be skills.
  24. Yes, CLAUDE.md, skills, and README.
  25. Images should live with the post.
  26. Yes, there will be skills for SVG/visuals too.
  27. Yes, do responsive image optimization.
  28. Yes to the SEO pieces that make sense.
  29. Yes to generated fallback OG images.
  30. One main RSS feed.
  31. No analytics.
  32. No comments.
  33. No newsletter.
  34. WCAG 2.2 AA.
  35. Yes to performance targets.
  36. Yes to the proposed testing stack.
  37. Yes to CI and deploy.
  38. npm.
  39. Yes, pin Node.
  40. No custom domain.
  41. Do what is correct for direct navigation.
  42. Yes, no secrets/runtime assumptions.
  43. Modern evergreen browsers.
  44. Yes, system/light/dark with persistence.
  45. Yes, strong architecture constraints; there will be skills.

We converged on a stack#

The stack became Astro + React + MDX + TypeScript + Tailwind + shadcn/ui + Radix + npm, which gives future agents a crisp architecture boundary to preserve. Astro owns pages, routing, layouts, static rendering, etc. React owns interactive islands: visualizations, demos, bespoke MDX components, etc. The non-goals were just as important and were written down explicitly: do not turn the site into a React SPA, do not introduce React Router, do not hydrate the entire page tree, and keep static content as static HTML wherever possible.

Actual conversation: ChatGPT narrows the stack

ChatGPT:
Good. Based on your answers, I would now lock the base stack as:

Astro + React + MDX + TypeScript + Tailwind CSS + shadcn/ui/Radix + npm

I would not use MUI. You said “skip MUI, but I want a serious lib,” and shadcn/ui + Radix fits this project better.

Astro content collections are also exactly what I would use for posts because they give us typed, validated frontmatter and are designed for collections of Markdown/MDX content.

Before we write the actual Claude project prompt, I have one more round of questions. These are much narrower.

We locked the URL model#

The /blog/ deployment path was a major constraint. We handled it as a deployment base rather than a source-folder structure, and it later became a validation target. The final public routes were:

https://jonborchardt.github.io/blog/
https://jonborchardt.github.io/blog/all-posts/
https://jonborchardt.github.io/blog/<post-slug>/
https://jonborchardt.github.io/blog/series/
https://jonborchardt.github.io/blog/series/<series-slug>/
https://jonborchardt.github.io/blog/about/

The rule that came out of this was that Astro gets base: "/blog", but nobody hardcodes /blog/ into application routes, nobody creates src/pages/blog, and every link goes through Astro’s base/site URL utilities. Base-path handling is exactly the kind of thing agents get wrong, because the site works locally and breaks in production. So I wanted it to be a first-class invariant from the start, not a late deployment fix.

We decided how posts live in the repo#

Posts are self-contained MDX directories with colocated assets and optional post-specific components. The principle is to allow local weirdness and keep shared abstractions clean. A post looks like this on disk:

src/content/posts/
  some-post/
    index.mdx
    hero.webp
    architecture.svg
    demo-data.json
    components/
      SomeVisualization.tsx

The important component rule is not to force bespoke MDX React components into the global reusable library. Reusable article primitives live in src/components/blog/, but a weird visualization that belongs to one article stays with that article, and if it later proves generally useful it gets promoted. This matters for agent-authored content specifically. Agents are likely to create unusual diagrams, visualizations, and demos for specific posts, and if every one-off artifact lands in the shared component library, the design system becomes a junk drawer. Post-local components let agents be creative without degrading the global architecture.

Actual conversation: the second, narrower round

ChatGPT:
What URL structure do you prefer? My default would be /, /blog, /blog/my-post, /series, /series/agentic-dev, /about.

Should article headers show Part 3 of 7 · Series Name and previous/next navigation?

Should tags be centrally defined?

Should every post be self-contained, with index.mdx, local assets, and local components?

Should the global component library contain primitives like Callout, Figure, Tabs, Steps, Quote, Aside, Mermaid, Math, Video, and Details?

Should we avoid a chart library initially and prefer SVG or DOM?

Should /admin exist only locally and directly edit source files?

Since this will be a GitHub Pages project site, is the real deployment shape jonborchardt.github.io/blog/?

If so, Astro should use base: "/blog" rather than putting blog into the source route hierarchy.

Actual conversation: my second-round decisions

Me:
Flat post URLs.
Lock Tailwind + shadcn/ui + Radix.
Use MDX for the actual About page and config for short author metadata.
I will migrate old posts myself and do not care about preserving old URLs.

The site is Always Shippable.

The author is Jonathan Borchardt, but the blog branding should be primary.

One featured post should live in config, falling back to the most recent post.

GitHub and LinkedIn should live on the About page.

/admin/ should be local-only.

Saving in admin should immediately update source files; source control is the undo mechanism.

I will provide the logo and favicon.

One main RSS feed.

No drafts in production.

No general charting library for now. Prefer SVG or DOM.

Do not force bespoke MDX React components into the global curated library.

  1. Decide before you prompt
    1. fuzzy idea
    2. reason about product + architecture
    3. surface the assumptions I didn't specify
    4. bootstrap prompt
  2. Scaffold, then plan against it
    1. working repository scaffold
    2. plans with validation baked into every stage
    3. plan review / steering
  3. Validation is the real output
    1. agent execution
    2. quality becomes a floor
    3. every issue becomes a permanent check
    4. extend it in short sessions, each one shippable

Prompt 1: build the skeleton, not the product#

Now that we had some idea of the architecture, it was time to start building, but not the features. For projects like this, I split project setup from implementation. The first prompt on any project is about the skeleton only: architecture, dependencies, file structure, contracts, tooling, and validation, with no features. Features come later, in separate prompts, once the foundation is in place and the agent has something solid to build on. So the first Claude prompt explicitly said the goal was not to build the finished blog. The opening constraint read:

The goal of this task is not to build the finished blog.

The goal is to establish the correct architecture, dependencies, file structure, content contracts, routing model, development environment, validation foundation, and deployment setup so that subsequent work can be done cleanly and incrementally.

The kinds of things that had to be true at the end:

  • the project installs, runs locally, and builds
  • GitHub Pages deployment is configured
  • the major routes exist, with placeholder UI proving the architecture
  • MDX posts statically render
  • React islands inside MDX work
  • the repo structure is ready for future Claude skills and agent-authored posts, etc.

Explicitly out of scope were polished visual design, sophisticated search, the full admin UI, rich blog components, generated social images, and finished product behavior in general. Written non-goals matter because agents drift toward completing things that look incomplete.

Actual artifact: full Prompt 1 sent to Claude

Build the initial scaffold for Always Shippable#

Create the initial working repository and architectural scaffold for a personal technical blog called Always Shippable.

The goal of this task is not to build the finished blog.

The goal is to establish the correct architecture, dependencies, file structure, content contracts, routing model, development environment, validation foundation, and deployment setup so that subsequent work can be done cleanly and incrementally.

By the end of this task:

  • the project must install successfully
  • the project must run locally
  • the project must build successfully
  • GitHub Pages deployment must be configured correctly
  • the major routes and content structures must exist
  • there should be minimal placeholder UI proving that the architecture works
  • MDX posts must statically render
  • React islands inside MDX must work
  • production URLs must work under /blog/
  • the repository structure must be ready for future Claude skills and agent-authored posts

Do not spend significant effort yet on visual design, polished page composition, sophisticated search, the admin UI, rich blog components, generated social images, or other later functionality.

Build the foundation. Do not prematurely build the rest of the product.


Product identity#

Site name:

Always Shippable

Site description:

Thoughts on engineering systems, product design, AI tooling, and keeping things always shippable.

Author:

Jonathan Borchardt

Author tagline:

Always shippable, always improving

Initial author bio:

I’ve spent 25 years turning complex data into things people can actually use and understand, from large-scale search at Microsoft to interactive research demos at Allen Institute for AI. These days I lead engineering teams focused on making cutting-edge AI work accessible, not just impressive. My bias is speed with structure: move fast, but in a way that holds up. I’ve built UI platforms, visualization systems, and front-end architecture that help teams ship continuously without chaos. I care about clean systems, good design, and making work something people actually enjoy. People do better work when they like what they’re building.

External author links:

  • GitHub: https://github.com/jonborchardt
  • LinkedIn: https://www.linkedin.com/in/borchardt/

Do not design or invent a logo or favicon. Those will be provided later.


Deployment model#

This will be deployed as a GitHub Pages project site at:

https://jonborchardt.github.io/blog/

The GitHub repository will therefore be named:

blog

Configure Astro correctly for this deployment model.

The public site root is conceptually /, but production URLs are served beneath GitHub Pages’ /blog/ base path.

Do not bake /blog/ manually into application routes or create source folders such as src/pages/blog.

Use Astro’s site/base configuration and URL utilities correctly.

The architecture must work both:

  • locally at the normal Astro development origin
  • in production beneath /blog/

Avoid fragile absolute paths.


Core technology#

Use:

  • Astro
  • React
  • MDX
  • TypeScript
  • strict TypeScript
  • Tailwind CSS
  • shadcn/ui where useful
  • Radix primitives underlying shadcn where useful
  • npm
  • ESLint
  • Prettier
  • Vitest
  • React Testing Library where appropriate
  • Playwright
  • axe-based accessibility testing where appropriate

Use current mutually compatible stable versions.

Pin and configure the Node environment appropriately so the project behaves consistently across machines and CI.

Use Astro’s native capabilities before adding dependencies.

Avoid dependency proliferation.

Do not add a library merely because it might be useful later.


Architectural principle#

This is primarily a statically generated publishing system.

Astro owns:

  • pages
  • routing
  • layouts
  • content loading
  • static rendering
  • metadata
  • build-time behavior

React is for:

  • genuinely interactive islands
  • interactive visualizations
  • interactive demos
  • selected client-side tools such as archive search/filtering
  • bespoke interactive components embedded in MDX

Do not turn the site into a React SPA.

Do not introduce React Router.

Do not hydrate the entire page tree.

Static content should remain static HTML wherever possible.

A normal article containing prose, images, code, and SVG should require little or no client JavaScript.

An MDX article must nevertheless be able to embed a React component and selectively hydrate it when necessary.


Content model#

Use Astro content collections for blog content.

Each post should be self-contained in a directory.

Use approximately this structure:

src/
  content/
    posts/
      example-post/
        index.mdx
        example.svg
        components/
          ExampleInteractive.tsx

Post-specific assets belong with the post.

Post-specific components also belong with the post.

Do not force bespoke article components into the global reusable component library.

If a component later proves generally useful, it can be promoted into the shared blog component library.

Create the appropriate Astro content collection schema and validate frontmatter.

The initial post schema should support at least:

title
slug
description
publishedAt
updatedAt
series
seriesOrder
tags
draft
hero
ogImage

Use sensible optionality.

Rules:

  • series is optional
  • a post belongs to zero or one series
  • seriesOrder is relevant only when the post belongs to a series
  • updatedAt is optional
  • draft should default appropriately
  • hero is optional
  • ogImage is optional
  • slug can normally be derived from the post but must support explicit control
  • malformed metadata must fail validation

The eventual authoring workflow will fill most of this metadata agentically.


Draft behavior#

Drafts are development-only.

In local development, drafts should be available and clearly identifiable.

In production:

  • draft pages must not be generated
  • drafts must not appear in listings
  • drafts must not appear in search data
  • drafts must not appear in RSS
  • drafts must not appear in the sitemap
  • drafts must not emit public metadata

Do not implement a production ?includedrafts mechanism.


Series#

Create a central typed series registry.

A series definition should contain at least:

id
title
description

Do not add series lifecycle states or planned posts.

Posts reference their series by ID.

Eventually, when an agent creates a post in a series without an explicit series order, the authoring workflow will assign:

max(existing seriesOrder) + 1

You do not need to build that complete workflow yet, but structure the project so it is straightforward.

The build should eventually be able to validate:

  • unknown series IDs
  • duplicate ordering within a series

Establish whatever foundation is sensible now.


Tags#

Create a central typed tag registry rather than allowing uncontrolled free-form tags forever.

Posts reference tags from this registry.

Later Claude skills or validation utilities may automatically extend or validate this registry.

Establish the contract now without building a complex tag-management system.


Public routes#

The production site will ultimately expose routes conceptually equivalent to:

/                       home
/archive/                all posts + search/filter UI
/<post-slug>/            individual post
/series/                 all series
/series/<series-slug>/   individual series
/about/                  about page

Because this is deployed as a GitHub Pages project site, production URLs will naturally be:

/blog/
/blog/archive/
/blog/<post-slug>/
/blog/series/
/blog/series/<series-slug>/
/blog/about/

Post URLs should therefore be flat.

Do not use:

/posts/<slug>/

Reserve route names such as:

archive
series
about
admin

so posts cannot conflict with application routes.

Create route scaffolding and enough minimal output to prove routing works.

Do not build the finished page designs yet.


About content#

Use two layers:

  1. typed author/site configuration for short metadata such as name, tagline, social links, and SEO identity
  2. MDX for the actual /about/ page content

Set up both.


Shared components#

Establish a sensible location for reusable blog components, for example:

src/components/blog/

Do not build the complete library yet.

A future shared library is expected to contain primitives such as:

  • Callout
  • Figure
  • Image
  • CodeBlock
  • Tabs
  • Steps
  • Quote
  • Aside
  • Comparison
  • Mermaid
  • Math
  • Video
  • expandable details

For now, create only enough shared structure and perhaps one or two trivial examples to prove MDX integration.

Do not create speculative abstractions.


SVG and visualization philosophy#

Prefer:

  1. semantic HTML and CSS
  2. SVG
  3. React DOM-based interactive visualization

Do not introduce a charting library now.

A future post may use a dedicated charting library if a concrete need justifies it.

Agent-authored SVG visualizations are expected to be common.


Styling foundation#

Set up:

  • Tailwind
  • shadcn/ui
  • Radix where relevant
  • typography support appropriate for long-form technical writing
  • CSS variables/design tokens
  • responsive foundations
  • light mode
  • dark mode
  • system preference
  • persistent user override

Do not design the finished visual system yet.

The eventual visual direction is a blend of:

  • serious technical publication
  • developer-oriented site

Think along the quality level of strong technical publishing platforms such as Medium or Hashnode, but do not imitate them.

For now, establish a clean neutral foundation only.


Accessibility#

The finished site will target WCAG 2.2 AA.

Establish the project so accessibility is treated as a build-time and design constraint rather than a cleanup task.

Use semantic HTML.

Do not hand-roll interactive accessibility primitives when Radix already solves the problem.

Set up appropriate accessibility testing infrastructure.

Do not attempt a comprehensive accessibility implementation of unfinished pages yet.


Performance#

The finished site should target Lighthouse scores of at least approximately:

  • Performance: 95+
  • Accessibility: 95+
  • Best Practices: 95+
  • SEO: 95+

Architecture should support those goals.

Avoid unnecessary JavaScript and hydration.

Do not optimize prematurely beyond establishing good defaults.


SEO foundation#

The finished site will require:

  • page titles
  • meta descriptions
  • canonical URLs
  • OpenGraph metadata
  • social metadata
  • JSON-LD
  • article structured data
  • Person structured data
  • sitemap
  • robots.txt
  • RSS
  • default/generated OG images
  • correct semantic headings
  • good internal linking

Set up the architecture and reusable metadata model now.

Do not fully implement all sophisticated SEO behavior yet unless trivial.

There will eventually be one main RSS feed.

Do not create per-tag or per-series feeds.


Homepage behavior#

The eventual homepage should contain:

  • blog branding/title/logo
  • navigation
  • one featured post
  • recent posts
  • footer

Featured post selection will eventually come from site configuration.

If none is explicitly configured, the newest published post is the fallback.

Do not build the polished homepage yet.

Create enough minimal UI to prove the data flow.


Archive#

/archive/ will eventually combine:

  • all posts
  • text search
  • series filtering
  • tag filtering
  • sorting

Search will be local/client-side.

There will be no external search service.

Search state should eventually be representable in URL query parameters.

Do not build the sophisticated search implementation during this scaffold task.

Create the route and architecture needed for it.


Local admin#

There will eventually be a local-development-only admin interface at:

/admin/

It will allow direct editing of configuration such as:

  • site identity
  • author profile
  • navigation
  • external links
  • theme settings
  • series
  • tags
  • featured post
  • SEO defaults

It will not edit blog posts.

It may use a very small local Node/Vite/Astro development-only mechanism to write source configuration files.

Saving should directly update files.

No undo system is needed because the repository has source control.

Important invariant:

The admin must not exist in the production output at all.

Do not build the admin UI during this task unless a tiny placeholder is useful to prove a development-only route can exist.

Do not accidentally ship /admin/.


Repository as an agent-authored publishing system#

This repository is not merely a blog codebase.

It is intended to become an agent-authored publishing system.

Future Claude workflows will create sophisticated posts containing:

  • MDX prose
  • code
  • SVG
  • images
  • React components
  • interactive demonstrations

Optimize the repository for agents to add content safely without slowly degrading architecture or consistency.

This means favoring:

  • explicit contracts
  • typed schemas
  • deterministic file organization
  • validation
  • reusable primitives
  • build-time failures for invalid states
  • straightforward conventions
  • low ambiguity

Avoid clever architecture that an agent will have difficulty maintaining.


Claude repository structure#

Create:

CLAUDE.md
README.md

.claude/
  skills/

The eventual skill set is expected to include workflows approximately like:

write-post
create-visual
create-series
review-post
publish-post

Do not implement the full skills during this task.

Leave a clean structure for them.

Responsibility should be:

README.md#

Documentation for humans:

  • what the project is
  • requirements
  • install
  • local development
  • build
  • test
  • deployment
  • high-level architecture
CLAUDE.md#

Repository invariants and rules for coding agents:

  • architectural boundaries
  • content organization
  • Astro vs React responsibilities
  • static-first behavior
  • content contracts
  • validation expectations
  • dependency philosophy
  • accessibility expectations
  • agent maintenance rules

Keep CLAUDE.md concise enough to remain useful as persistent agent context.

Procedural workflows belong in skills rather than growing CLAUDE.md indefinitely.


Testing and quality#

Set up the basic quality toolchain.

At minimum provide working commands for:

dev
build
preview
typecheck
lint
format
test
test:e2e

Names can vary slightly if ecosystem conventions strongly justify it.

Configure:

  • strict TypeScript
  • ESLint
  • Prettier
  • unit testing
  • basic Playwright infrastructure
  • accessibility test infrastructure

Add only a few representative tests proving the scaffold works.

Do not build an enormous test suite around placeholder functionality.


Build validation#

Design the validation layer with the expectation that production builds should ultimately fail for:

  • invalid frontmatter
  • duplicate slugs
  • reserved slugs
  • duplicate series order
  • unknown series
  • unknown tags
  • broken internal links
  • required image alt-text violations
  • missing required SEO metadata
  • TypeScript errors

Implement the straightforward high-value validations now.

If some validations would require disproportionate implementation during scaffolding, document them clearly as planned work rather than building an elaborate system prematurely.


GitHub Actions and Pages#

Configure GitHub Actions to:

  1. install dependencies reproducibly
  2. typecheck
  3. lint
  4. test
  5. build
  6. deploy the static Astro build to GitHub Pages

Make the workflow correct for a repository named blog deployed at:

jonborchardt.github.io/blog/

Do not require any backend service.

Do not require secrets unless GitHub Pages itself genuinely requires one.


Security and privacy constraints#

This is a static site.

Do not introduce:

  • runtime secrets
  • API keys
  • databases
  • authentication
  • server-side application infrastructure
  • analytics
  • comments
  • newsletter infrastructure
  • tracking scripts
  • unnecessary third-party code

No downloadable-file publishing feature is required.

Support modern evergreen browsers.


Initial example content#

Create a minimal example series and a minimal example post.

The example post should prove:

  • frontmatter validation
  • MDX rendering
  • colocated asset loading
  • series association
  • tags
  • static article generation
  • one small React island embedded inside MDX

Keep the content obviously temporary and small.

Do not spend time writing a real article.


Expected repository shape#

You may refine this structure to follow current Astro conventions, but the architecture should remain recognizable:

/
├── .claude/
│   └── skills/
├── .github/
│   └── workflows/
├── public/
├── src/
│   ├── components/
│   │   ├── blog/
│   │   └── ui/
│   ├── config/
│   │   ├── site.ts
│   │   ├── author.ts
│   │   ├── series.ts
│   │   └── tags.ts
│   ├── content/
│   │   └── posts/
│   │       └── example-post/
│   │           ├── index.mdx
│   │           ├── example.svg
│   │           └── components/
│   │               └── ExampleInteractive.tsx
│   ├── layouts/
│   ├── pages/
│   │   ├── index.astro
│   │   ├── archive/
│   │   │   └── index.astro
│   │   ├── series/
│   │   │   ├── index.astro
│   │   │   └── [slug].astro
│   │   ├── about/
│   │   │   └── index.mdx
│   │   └── [slug].astro
│   ├── styles/
│   └── utils/
├── astro.config.*
├── package.json
├── tsconfig.json
├── CLAUDE.md
└── README.md

Do not follow this mechanically if Astro’s current recommended structure suggests a better implementation.

Preserve the architectural intent.


Implementation approach#

Before changing files:

  1. inspect the existing repository
  2. determine whether anything useful already exists
  3. prefer preserving useful existing work over replacing it
  4. use current recommended Astro integrations and configuration
  5. create the smallest coherent implementation that establishes this architecture

Then implement it.

Run the relevant commands yourself.

Fix errors you encounter.

Do not stop after generating files.

Verify at minimum that:

  • dependencies install
  • development server can start
  • TypeScript passes
  • lint passes
  • tests pass
  • production build succeeds
  • expected static pages are generated
  • the example MDX article renders
  • its React island works
  • URLs/assets respect the /blog/ deployment base

At the end, summarize:

  • what you created
  • major architectural decisions actually implemented
  • commands available
  • anything intentionally left for later
  • any differences from this requested structure and why

Do not proceed into the later feature implementation merely because you have time.

Stop once the scaffold is sound, runnable, documented, and verified.

Why “must be true” beats “build something great”#

Checkable acceptance criteria cause the agent to verify; vague quality goals cause it to guess. This applies to any prompt that hands work to an agent. From the agent’s side, “build me a great blog” forces it to silently make dozens of decisions and hope, whereas “MDX must statically render, a React island must hydrate, URLs must work under /blog/, the build must pass” gives it checkable truths. It can run commands and know whether it is done.

  1. Decide before you prompt
    1. fuzzy idea
    2. reason about product + architecture
    3. surface the assumptions I didn't specify
    4. bootstrap prompt
  2. Scaffold, then plan against it
    1. working repository scaffold
    2. plans with validation baked into every stage
    3. plan review / steering
  3. Validation is the real output
    1. agent execution
    2. quality becomes a floor
    3. every issue becomes a permanent check
    4. extend it in short sessions, each one shippable

Running the scaffold prompt, and reading the report#

With the prompt written, I handed it to Claude and let it run. It installed Astro, wrangled the dependency conflicts, set up the routes, tooling, and deployment config, ran the build and checks, and came back with the scaffold working and a report of what it had done.

The final report mattered because it became input to the next reasoning step, and the next model needs reasons, not just a status. The report had three useful categories. Verified facts: which commands ran, what passed, what was generated. Deviations with reasons: Node 24 because Astro 7 required newer Node, no jsx-a11y because of the ESLint 10 incompatibility, a system font stack instead of Geist, and a repo that was initialized but uncommitted. Explicitly deferred work: search, the full admin, OG images, rich primitives, and polished design.

The reasons are what matter. “Skipped jsx-a11y” is ambiguous. “Skipped jsx-a11y because it does not support ESLint 10 yet, while axe e2e covers rendered accessibility” tells the next agent when that decision should be revisited.

Actual conversation: Claude's scaffold report

Claude:
Notes / deviations

  • Node: Astro 7 requires Node >=22.12; the machine default was Node 20.19. I installed Node 24.19 and pinned .nvmrc to 24.
  • Dev origin is localhost:4321/blog/, because Astro applies base in dev too. No source path hardcodes it.
  • eslint-plugin-jsx-a11y was skipped because it does not support ESLint 10 yet; axe e2e covers accessibility.
  • shadcn’s default Geist webfont was removed in favor of a system stack.
  • Astro 7 auto-backgrounds astro dev/preview when it detects an agent environment.
  • Git repo initialized on main, nothing committed.
Actual conversation: my check back with ChatGPT

Me:
Do you need to change your last prompt based on this? I am hoping no.

ChatGPT:
No. I would keep the second prompt essentially unchanged.

Those are implementation notes from the scaffold, and the second prompt already explicitly tells Claude to inspect the actual repo and treat it as the source of truth.

I would only add the notes as a short preface so Claude does not waste time rediscovering them.

Treat these as current repository facts, not necessarily permanent architectural requirements. Change them later only if there is a concrete reason.

That last sentence matters. For example, Node 24 is an implementation choice, not a product requirement.

Fresh planning found real bugs#

Prompt 2 did not go to the session that built the scaffold. I closed that one and started a new Claude session with an empty context, so the only things it knew were the prompt and whatever it could read from the repo. That was deliberate: the scaffold session had just spent its whole context building this thing, and asking it to audit its own work would mostly get back its own assumptions.

The clean inspection pass found silent defects that green tests did not catch. @tailwindcss/typography was installed but not registered, the Shiki dark-mode CSS hook did not exist, UTC dates rendered one day early in local time, etc.

This worked because the fresh session had no memory of building the scaffold, an explicit instruction to distrust it, permission to inspect the real repo, and a mandate to put foundational fixes early. Those issues became Plan 1: Foundation fixes instead of getting buried under feature work. Fresh sessions are not a formality; they are a review mechanism that reduces the chance the agent rationalizes or overlooks its own prior work. The scaffold session had shipped all of those defects, not because it was careless but because each one was a silent gap: everything green, output subtly wrong.

  1. Decide before you prompt
    1. fuzzy idea
    2. reason about product + architecture
    3. surface the assumptions I didn't specify
    4. bootstrap prompt
  2. Scaffold, then plan against it
    1. working repository scaffold
    2. plans with validation baked into every stage
    3. plan review / steering
  3. Validation is the real output
    1. agent execution
    2. quality becomes a floor
    3. every issue becomes a permanent check
    4. extend it in short sessions, each one shippable

Prompt 2 produced plans, not code#

The second prompt turned the real repo into an ordered sequence of eleven bounded implementation plans. It told Claude to inspect the repo, find the gaps between scaffold and product, and turn them into implementation plans that kept the repo shippable at every stage. Every plan had to carry the same sections:

Goal
Why this comes now
Scope
Key files/areas
Important implementation constraints
Acceptance criteria
Validation
Explicitly out of scope
Actual artifact: full Prompt 2 sent to a fresh Claude session

Plan the remaining implementation of Always Shippable#

The initial architectural scaffold for Always Shippable has already been built.

Do not begin implementing the remaining product yet.

Your job in this task is to:

  1. deeply inspect the existing repository
  2. understand what is actually implemented
  3. identify gaps between the scaffold and the intended finished product
  4. determine dependencies between the remaining pieces
  5. produce a set of concrete implementation plans in a sensible execution order

The plans should be suitable for giving to coding agents one at a time.

We want a sequence that keeps the repository working and shippable after every meaningful stage.


First: inspect, do not assume#

Read:

  • README.md
  • CLAUDE.md
  • package.json
  • Astro configuration
  • TypeScript configuration
  • source directories
  • content collection definitions
  • site/author/series/tag config
  • layouts
  • pages
  • shared components
  • sample MDX
  • tests
  • GitHub Actions
  • anything under .claude/

Run appropriate validation commands if useful.

Do not assume the first scaffold task implemented everything perfectly.

Treat the repository itself as the source of truth.

Identify:

  • what exists
  • what works
  • what is placeholder
  • what is missing
  • what is architecturally questionable
  • what should be corrected before layering features on top

If foundational changes are necessary, put them early in the plan.

Do not casually rewrite sound architecture merely because you would personally structure it differently.


Product we’re ultimately building#

The finished site is:

Always Shippable#

Thoughts on engineering systems, product design, AI tooling, and keeping things always shippable.

Author:

Jonathan Borchardt

Tagline:

Always shippable, always improving

The site is a serious technical publication/personal engineering blog with a developer-oriented visual identity.

It is statically generated and hosted at:

https://jonborchardt.github.io/blog/

It should feel carefully designed, fast, readable, accessible, and technically credible.


Core architectural constraints#

These are intentional and should remain true:

  • Astro owns routing, content, layouts, metadata, and static generation.
  • React is used only where interactivity warrants hydration.
  • Do not turn the site into an SPA.
  • Posts are MDX.
  • Posts produce actual static HTML.
  • MDX may embed React islands.
  • Post-specific components remain colocated with their posts.
  • Reusable primitives live in a shared blog component library.
  • Prefer semantic HTML, CSS, and SVG before adding JavaScript-heavy solutions.
  • No backend service.
  • No database.
  • No live CMS.
  • No analytics.
  • No comments.
  • No newsletter.
  • No external search service.
  • No production admin system.
  • Drafts are local-development-only.
  • GitHub Pages /blog/ base-path behavior must remain correct.
  • npm is the package manager.
  • TypeScript remains strict.
  • Dependencies should remain disciplined and minimal.

Final route model#

Conceptual application routes:

/                       homepage
/archive/                archive/search/filter
/<post-slug>/            article
/series/                 series directory
/series/<series-slug>/   series landing page
/about/                  About

Production URLs are beneath:

https://jonborchardt.github.io/blog/

Post URLs remain flat.

Do not add /posts/.


Homepage#

The finished homepage needs:

  • Always Shippable branding
  • supplied logo
  • primary navigation
  • one featured article
  • recent articles
  • footer

Featured article behavior:

  • site configuration may explicitly specify one post
  • otherwise use the newest published post

Keep the homepage editorial and restrained rather than dashboard-like.


/archive/ is the central content discovery experience.

It should contain:

  • all published posts
  • local client-side full-text search
  • tag filtering
  • series filtering
  • sorting
  • useful result summaries
  • empty states
  • responsive behavior

Search should cover sensible article information such as:

  • title
  • description
  • body content
  • tags
  • series
  • headings where practical

Do this without an external service.

Avoid overengineering fuzzy search unless the improvement is worthwhile.

Search/filter state should be representable in URL query parameters when practical so filtered searches are linkable.


Posts#

Posts live in colocated directories and may contain:

  • MDX
  • images
  • SVG
  • data
  • post-specific React components

Posts should support high-quality technical writing including:

  • prose
  • headings
  • links
  • images
  • responsive figures
  • code
  • syntax highlighting
  • tables
  • quotes
  • callouts
  • asides
  • steps
  • tabs when appropriate
  • expandable details
  • Mermaid diagrams
  • math
  • video embeds
  • SVG
  • bespoke DOM visualizations
  • selectively hydrated React interactions

Do not introduce a general charting library until an actual post needs one.

Prefer SVG or DOM visualizations.

Do not require every bespoke post component to enter the global component library.


Shared article primitives#

Create a coherent reusable component vocabulary for agent-authored MDX.

Likely primitives include:

Callout
Figure
Image
CodeBlock
Tabs
Steps
Quote
Aside
Comparison
Mermaid
Math
Video
Details

This list is a direction, not an instruction to mechanically create abstractions that are not useful.

The resulting MDX authoring experience should be simple and predictable for Claude.

Shared components should:

  • look coherent
  • work in light and dark themes
  • be responsive
  • be accessible
  • compose cleanly
  • avoid requiring repetitive per-post styling

Series#

A post belongs to zero or one series.

Series definitions contain:

  • ID
  • title
  • description

Posts contain:

  • series reference
  • explicit series order

When an authoring workflow creates a post in a series and no order is explicitly supplied:

seriesOrder = max(existing published seriesOrder) + 1

Series pages should provide:

  • title
  • description
  • ordered article list

An article belonging to a series should clearly show something equivalent to:

Part 3 of 7 · Series Name

and should provide useful previous/next-series navigation near the end of the article.

Do not support:

  • planned future articles
  • series lifecycle status
  • posts belonging to multiple series

Tags#

Tags come from a central registry.

The system should prevent gradual vocabulary fragmentation such as:

AI
ai
artificial-intelligence
Artificial Intelligence

The eventual authoring skill may add tags to the registry deliberately.

Tags support:

  • metadata
  • archive filtering
  • article discovery
  • SEO where sensible

Drafts#

Drafts:

  • appear locally
  • are visually identified locally
  • never generate production article pages
  • never appear in production archive data
  • never appear in production search
  • never appear in RSS
  • never appear in sitemap
  • never appear in structured metadata

There is no production draft query flag.


About#

Short author metadata belongs in typed configuration.

Long-form About content belongs in MDX.

About page should include the author’s GitHub and LinkedIn links.

Current profile copy:

Jonathan Borchardt#

Always shippable, always improving

I’ve spent 25 years turning complex data into things people can actually use and understand, from large-scale search at Microsoft to interactive research demos at Allen Institute for AI. These days I lead engineering teams focused on making cutting-edge AI work accessible, not just impressive. My bias is speed with structure: move fast, but in a way that holds up. I’ve built UI platforms, visualization systems, and front-end architecture that help teams ship continuously without chaos. I care about clean systems, good design, and making work something people actually enjoy. People do better work when they like what they’re building.

Links:

  • https://github.com/jonborchardt
  • https://www.linkedin.com/in/borchardt/

The user will provide the real logo and favicon separately.


Visual system#

Use the existing Tailwind + shadcn/ui + Radix foundation.

The target is a blend of:

  • high-quality technical publication
  • developer-oriented personal site

It may take cues from the clarity and typography of products such as Medium and Hashnode, but should develop its own restrained visual identity.

Develop a coherent design system covering:

  • typography
  • long-form article typography
  • content widths
  • spacing
  • responsive layout
  • color
  • borders
  • radii
  • interaction states
  • code presentation
  • figures
  • metadata
  • navigation
  • cards where useful
  • light theme
  • dark theme

Avoid making the site look like a generic SaaS dashboard.


Theme#

Support:

  • system preference
  • explicit light mode
  • explicit dark mode
  • persistent user selection

Avoid flashes of incorrect theme during page load.

All site and article primitives must work correctly in both themes.


Accessibility#

Target WCAG 2.2 AA.

This includes:

  • semantic HTML
  • keyboard navigation
  • visible focus
  • correct labeling
  • usable heading hierarchy
  • image alt text
  • reduced-motion behavior
  • sufficient contrast
  • accessible interactive components
  • accessible code/figure treatment where relevant

Use automated testing where useful, but do not treat automated accessibility tests as sufficient by themselves.


Responsive behavior#

The site must work intentionally across:

  • small mobile
  • large mobile
  • tablet
  • laptop
  • wide desktop

Article typography and interactive visualizations should remain usable on narrow screens.

Do not simply stack desktop layouts and call them responsive.


SEO#

The finished site should have excellent static SEO.

Implement appropriately:

  • unique titles
  • meta descriptions
  • canonical URLs
  • OpenGraph
  • social metadata
  • article structured data
  • Person structured data
  • JSON-LD
  • sitemap
  • robots.txt
  • RSS
  • semantic document structure
  • sensible internal linking
  • dates
  • updated dates where present
  • tags
  • series metadata where useful

The deployment base path must be handled correctly in all absolute/canonical URLs.


Social preview images#

A post may provide a bespoke OG image.

If it does not, generate a polished fallback social card using:

  • article title
  • series if applicable
  • date
  • Always Shippable branding

This should happen at build time.

Do not require a runtime service.


RSS#

Provide one primary RSS feed containing published posts.

Do not add per-series or per-tag feeds.


Image handling#

Post images should remain authored next to the post.

The build may transform/output assets as needed.

Implement sensible:

  • resizing
  • modern formats
  • responsive sources
  • dimensions
  • lazy loading
  • alt-text requirements
  • performance behavior

Avoid making authoring cumbersome.


404#

Provide a custom 404 page consistent with the rest of the site.

It should help recovery with useful navigation and potentially recent content or search.


Local admin#

Build a useful development-only /admin/ UI.

It must not exist in production output.

It should edit source configuration directly for things such as:

  • site identity
  • author profile
  • navigation
  • theme settings
  • series
  • tags
  • featured article
  • SEO defaults
  • personal links

It must not edit posts.

A minimal development-only Node/Astro/Vite writing mechanism is acceptable.

Changes should be written immediately to source files.

No undo system is required.

Keep this intentionally small.

It is a convenience interface over configuration, not a CMS product.


Validation#

Production validation should fail for important invalid states, including where appropriate:

  • invalid frontmatter
  • duplicate slugs
  • reserved slugs
  • unknown series
  • duplicate series order
  • unknown tags
  • broken internal links
  • missing required alt text
  • required SEO metadata problems
  • TypeScript errors

Prefer failing early and clearly.

Validation messages should tell an agent exactly how to correct the problem.


Agent-authored repository#

A major design requirement is that Claude will create and maintain much of this content.

The repository should make correct behavior easier than incorrect behavior.

Improve:

  • explicit contracts
  • schemas
  • conventions
  • reusable components
  • validation
  • documentation
  • predictable locations
  • good error messages
  • low ambiguity

Avoid relying on unwritten human knowledge.


Claude skills#

We expect to create workflows approximately corresponding to:

write-post
create-visual
create-series
review-post
publish-post

Part of your planning task is to decide:

  • what each skill should own
  • what should remain ordinary repository behavior
  • what validations/utilities those skills should invoke
  • what order the skills should be created in
  • what duplication between CLAUDE.md and skills should be avoided

Do not write all of those skills during this planning task.

Plan them.


Documentation responsibilities#

Maintain the intended separation:

README.md#

Human-facing operational information.

CLAUDE.md#

Persistent architectural rules and invariants for coding agents.

.claude/skills/*#

Procedural workflows.

Avoid duplicating the same large instructions in all three.


Performance#

Target representative Lighthouse scores around 95+ across:

  • Performance
  • Accessibility
  • Best Practices
  • SEO

The more important architectural requirement is:

Do not ship unnecessary JavaScript.

Interactive posts may legitimately have more JavaScript than normal articles.

The rest of the site should remain lightweight.


Planning methodology#

Do not organize the work simply according to the order features appear in this prompt.

Determine dependencies.

Look for opportunities where one piece enables several later pieces.

For example, content contracts and article layout may need to stabilize before sophisticated search or agent skills should be built.

The site should remain buildable and useful after every phase.

Prefer vertical slices where they produce useful working software, but do not force vertical slicing when foundational work clearly needs to precede it.


Produce implementation plans#

Create a sequence of implementation plans.

Each plan should represent a coherent unit of work that can reasonably be given to a coding agent.

Aim for plans that are substantial enough to produce meaningful progress but small enough to review.

For every plan include:

Goal#

What becomes possible or materially better after this plan.

Why this comes now#

Its dependency relationship to earlier and later work.

Keep this brief.

Scope#

Specific things to implement.

Key files/areas#

Likely areas of the current repository that will change.

Base this on what actually exists, not merely on the hypothetical structure above.

Important implementation constraints#

Architecture or product rules the implementing agent must preserve.

Acceptance criteria#

Concrete observable conditions establishing that the plan is complete.

Prefer things that can be tested.

Validation#

Commands, tests, or manual checks the implementation agent should run.

Explicitly out of scope#

Prevent the agent from wandering into later plans.


Also identify cross-cutting risks#

Before presenting the plan sequence, identify a small number of meaningful risks discovered from inspecting the repository.

Examples might include:

  • base-path URL handling
  • Astro/MDX integration choices
  • content-schema deficiencies
  • excessive hydration
  • asset handling
  • admin accidentally entering production
  • duplicated design systems
  • fragile content validation
  • difficulty testing generated pages

Do not manufacture risks merely to fill a section.


Planning standard#

The resulting sequence should be good enough that we can subsequently say:

Implement Plan 4.

and another Claude session can execute Plan 4 without needing to redesign the whole product.

Plans should therefore include enough technical specificity to constrain implementation, but they should not contain huge speculative code dumps.

Do not implement the plans during this task.

Do not make broad unrelated refactors.

Do not create placeholder files merely because the future plan mentions them.

Inspect, reason, and produce the ordered implementation program only.

The plan used an existing skill system#

Claude did not invent the planning methodology from scratch; it used the plugins I already had installed. Superpowers supplied the writing-plans skill, which prescribed that plans assume the executor has zero context, that file responsibilities are mapped before tasks, that tasks are sized to carry their own test cycle, that commits are frequent, and that plans are saved in a standard location. Ponytail pushed the DRY/YAGNI side: the laziest solution that works, stdlib and native features before dependencies, no abstractions for imagined future needs (I wrote about it in Ponytail: making the AI a lazy senior dev). Reusable process assets compound. Skills are process memory.

The project goal and the process skills aligned neatly. The product wanted an agent-authored repository with explicit contracts and validation, and the planning skill produced explicit contracts for future agent sessions. It is the same philosophy at two levels: repo contracts help agents maintain code, and plan contracts help agents execute work.

  1. Decide before you prompt
    1. fuzzy idea
    2. reason about product + architecture
    3. surface the assumptions I didn't specify
    4. bootstrap prompt
  2. Scaffold, then plan against it
    1. working repository scaffold
    2. plans with validation baked into every stage
    3. plan review / steering
  3. Validation is the real output
    1. agent execution
    2. quality becomes a floor
    3. every issue becomes a permanent check
    4. extend it in short sessions, each one shippable

The plan had to survive beyond the chat#

Anything future agents need has to move out of ephemeral chat context and into durable repository or memory artifacts. So before closing the planning session, I asked it directly: do the plans you generated contain everything they need from this conversation, so that I can run each one in a brand new context? That question is a standard step for me at the end of any planning session, because the plans will be executed by sessions that never saw the chat that produced them.

Claude found a gap: the plan referenced the planning prompt itself, which was unsafe because a future session might receive only “Implement Plan 4” and never see the original product prompt.

The fix was to add a condensed product spec into the plan document itself, plus a subagent briefing rule that subagent prompts must quote, not summarize, the global constraints, execution notes, product spec, and full plan text. Quote, not summarize. Summarization is where invariants are quietly lost.

I reviewed the plan with ChatGPT#

Before running the plans, I brought them back to ChatGPT for an independent review, as a reviewer and not as an infinite redesign loop. The review kept the plan intact and added execution guardrails: no opportunistic refactors, validation against a known production preview, one commit per plan. A second model is useful for catching overengineering, detecting architecture drift, challenging speculative fallbacks, strengthening task boundaries, and checking sequencing. But once the repository-aware plan is coherent, wholesale replanning is usually counterproductive.

Actual conversation: ChatGPT's steering note before implementation

ChatGPT:
This plan looks good. A few execution notes before you start:

  • Treat the implementation program as the default sequence, but do not follow it mechanically if the repo reveals a better small adjustment while implementing a plan.
  • Keep each plan independently shippable. Do not pull substantial work forward from later plans just because it is nearby.
  • Fix regressions and foundational issues immediately when discovered, but avoid opportunistic refactors unrelated to the current plan.
  • Preserve the static-first Astro architecture and keep client JavaScript narrowly justified.
  • Keep /blog/ base-path correctness as an invariant.
  • Make validation failures actionable for future coding agents.
  • Before finishing each plan, run the relevant validation/build/e2e checks against a known production preview rather than reusing an ambiguous dev server.
  • Commit each completed plan separately with a clear commit message.

Two clarifications:

  1. Published meta/reference posts are fine for now, but make them genuinely useful posts rather than obvious test fixtures.
  2. Build-time Mermaid is the preferred direction. Do not add a client-side fallback preemptively.
  1. Decide before you prompt
    1. fuzzy idea
    2. reason about product + architecture
    3. surface the assumptions I didn't specify
    4. bootstrap prompt
  2. Scaffold, then plan against it
    1. working repository scaffold
    2. plans with validation baked into every stage
    3. plan review / steering
  3. Validation is the real output
    1. agent execution
    2. quality becomes a floor
    3. every issue becomes a permanent check
    4. extend it in short sessions, each one shippable

Claude builds and reports#

With the plan reviewed, I opened a fresh Claude session and pointed it at the repo. Then it ran, largely unattended, for about 80 minutes. It read the plan, inspected the repo, and worked through the tasks: the foundation fixes, the design system, the article page, the MDX primitives, build-time diagrams, OG cards, validation checks, search, the dev-only admin, etc. Each stage ran the validation and build before moving on, so the repo stayed shippable the whole way through. The git log has the receipts; An Agent Built This Blog walks through them commit by commit. The execution itself was boring, and that is the point: if coding is the exciting part of your agent story, the preparation was too thin.

Commit timeline of the plan runEleven plan commits land between 12:45 and 14:04, five to thirteen minutes apart, after a 12:35 scaffold commit.
Plan commits, 2026-08-17
TimeCommit
12:35Scaffold + implementation program
12:45Plan 1: foundation fixes, trustworthy tests
12:53Plan 2: design tokens, chrome, homepage, 404
12:58Plan 3: article page, images, series nav
13:11Plan 4: shared MDX primitives, reference posts
13:16Plan 5: build-time diagrams and math
13:24Plan 6: SEO, social cards, feeds
13:30Plan 7: build-output validation
13:41Plan 8: archive explorer island
13:52Plan 9: dev-only admin
13:57Plan 10: agent skills and docs
14:04Plan 11: launch QA and performance

When it finished, I reviewed the resulting diff, architecture, behavior, and validation results myself. Claude also produced the same kind of report as the scaffold session: what it verified, where it deviated from the plan and why, and what it marked as a deliberate shortcut.

ChatGPT reviews the report#

I did not read that report alone. I pasted it back into the ChatGPT thread that had reviewed the plans, so the model that knew the product intent could judge the outcome against it. That is the same loop as before, just one turn later: Claude does the work, the report carries the reasons, and a second model with product context checks whether the work still matches the plan.

Actual conversation: ChatGPT's review of the result

ChatGPT:
This is very good. I would not reopen architecture at this point.

The implementation sequence paid off. The result reads like a coherent publishing system, not a pile of blog features.

The static-first constraint held.

The validation layer is probably the highest-leverage piece long term.

100/100/100/100 Lighthouse across representative page types is a strong baseline.

The deviations sound reasonable.

The only two things I would inspect before pushing are the two explicitly marked shortcuts, but I would not automatically fix them.

I would also do one quick real-content smoke test before launch.

What the run delivered#

The resulting repo was close to complete and had a strong validation posture. Here is what each plan delivered:

PlanResult
FoundationsTypography, Shiki dark CSS, UTC dates, deterministic e2e preview, axe over every sitemap URL
Design systemOKLCH palette, contrast validation, tokens, header/logo slot, theme toggle, footer, home, 404
Article pageSeries context, reading time, hero, TOC, anchors, tables, prev/next, responsive images
Primitives + postsMDX primitives, two real posts, dev-only draft fixture, base-aware MDX links
Diagrams/mathBuild-time Mermaid + KaTeX, zero client JS
SEO/OGSatori/resvg cards, OG/Twitter metadata, JSON-LD, RSS polish
Build validationLinks, fragments, base paths, alt, dimensions, SEO, accidental admin output
ArchiveFull-text search, lazy index, tag/series/sort filters, URL state, one island
AdminDev-only config editor via Vite middleware, refuses invalid deletes
SkillsNew-post scaffold, five skills, README/CLAUDE.md synced
QALighthouse 100/100/100/100 on representative pages, JS budget, keyboard e2e

The deviations were the interesting part. Prettier ignores *.mdx because the MDX formatter mangled JSX-embedded lists and fences. Markdown plugins moved to Astro 7’s actual processor mechanism. Admin routing stayed structured to guarantee no production output. And additional support dependencies were added where required.

  1. Decide before you prompt
    1. fuzzy idea
    2. reason about product + architecture
    3. surface the assumptions I didn't specify
    4. bootstrap prompt
  2. Scaffold, then plan against it
    1. working repository scaffold
    2. plans with validation baked into every stage
    3. plan review / steering
  3. Validation is the real output
    1. agent execution
    2. quality becomes a floor
    3. every issue becomes a permanent check
    4. extend it in short sessions, each one shippable

Validation became the highest-leverage output#

Validation turns architectural and content rules into build failures future agents cannot ignore. Of everything here, this is probably the most reusable lesson for a team. Each gate sits at a different stage and fails on a different class of mistake:

Six validation gates between authoring and deployA post flows through scaffold checks, the type system, the content-load schema, cross-post validation, built-HTML checks, and end-to-end tests before deploy; each gate fails the process on a different class of mistake.

Build failures now catch:

  • invalid frontmatter
  • duplicate slugs
  • reserved slugs
  • unknown series
  • duplicate series order
  • unknown tags
  • broken internal links
  • broken fragments
  • missing required alt text
  • missing image dimensions
  • required SEO metadata problems
  • incorrect base paths
  • accidental admin output in dist/
  • TypeScript errors

The tag registry is the clearest example of why. Tags and series compile to literal TypeScript unions, so an unknown tag isn’t a typo that ages into data drift; it’s a type error. That is what kills the entropy many-session authorship otherwise guarantees: the drift toward ai, AI, and artificial-intelligence as three different tags.

A validation error should be an instruction, not just a complaint: it should tell the next agent exactly how to fix the problem. As agent autonomy increases, executable constraints become more valuable than review comments, because review catches bad states after they exist and validation prevents them from shipping.

The repo ended with its own skills#

The process skills used to build the repo were transformed into repo-specific content skills, so the repo now has operating procedures, not just code:

SkillDoes
write-postDraft a complete post: prose, diagrams, hero, validated build
create-hero1500×600 hero contract, SVG-first, rendered via script
create-visualDiagram design language + accessibility verification
create-seriesRegistry-first series creation
review-postContracts, accessibility, links, SEO, both themes, three widths
publish-postFlip draft, validation/build/e2e, commit, deploy, verify live
wrapupWhole-site audit before shipping a batch

The layered model is that CLAUDE.md invariants say what must stay true, skills say how recurring work is done, and validation says what cannot be wrong. This is the end state I want in agent-maintained repos; Repos as Experts explores it further.

Quality became a floor, not a target#

A green build now implies that accessibility, SEO, links, metadata, base paths, and production-safety checks all passed. Early on, quality depended on the model reading a prompt, remembering it, caring about it, and a human noticing if it forgot. By the end, quality depended on the schema rejecting invalid content, validation rejecting broken output, e2e checking rendered behavior, and skills routing work through the right sequence. Claude’s own framing of it was:

Every session I worked on this repo made the next session’s mistakes harder to commit.

That is the target property. Concretely, here is what the site passes on every push, and the numbers as they stood at launch:

  • Lighthouse 100/100/100/100 (Performance, Accessibility, Best Practices, SEO) on the home page, a prose post, an interactive post, and the archive, mobile preset, against the production preview.
  • Accessibility: an axe WCAG 2.2 AA audit of every URL in the sitemap, with zero violations; a keyboard tour (skip link, header, theme toggle, tabs, details, copy button, heading anchors) and a reduced-motion check.
  • Markup: every built HTML page runs through the W3C Nu validator; markup errors fail the build.
  • JavaScript budget: prose pages load zero script files; the archive loads only its React island, under 120 KB gzipped; an interactive post loads only the chunks for its own island.
  • Responsive: no horizontal overflow at 360 px on every sitemap page, including pages with wide tables and code blocks; the archive is usable and axe-clean at 360 px.
  • SEO: every page has a unique title, a description, a canonical URL that matches its sitemap entry, og:url, a reachable 1200×630 og:image, a summary_large_image Twitter card, and JSON-LD; articles carry article:* tags and BlogPosting data; RSS, sitemap, and robots.txt all resolve under /blog/.
  • Links and assets: every internal link and #fragment resolves; nothing links outside the base path; every image has alt text and dimensions; external links that return 404 or 410 fail the build.
  • Production safety: /admin/ returns 404 in the build, asserted twice (dist check and e2e); drafts never appear in pages, listings, RSS, or the sitemap.
  • Theme and dates: the theme toggle cycles system → light → dark and persists; code blocks switch to the dark Shiki theme; dates render as UTC calendar dates.

None of that is aspirational. Each line is a test or a build check that runs in CI on every push, and a red one blocks the deploy.

What I actually review after an agent run#

My review target moves up from local code style to architecture, scope, validation, and maintainability. The first question is whether the architecture survived: static content stayed static, React remained narrow, the /blog/ base path stayed correct, no backend crept in, etc. Then, did validation get stronger? If the agent added features but weakened validation, that is a bad trade. Did JavaScript stay justified, with normal articles not turning into interactive applications? Were the deviations reasonable, meaning they preserved invariants while adapting to reality? And did scope stay bounded? Correct code outside the requested boundary is still a process failure.

  1. Decide before you prompt
    1. fuzzy idea
    2. reason about product + architecture
    3. surface the assumptions I didn't specify
    4. bootstrap prompt
  2. Scaffold, then plan against it
    1. working repository scaffold
    2. plans with validation baked into every stage
    3. plan review / steering
  3. Validation is the real output
    1. agent execution
    2. quality becomes a floor
    3. every issue becomes a permanent check
    4. extend it in short sessions, each one shippable

Make future mistakes harder to commit#

Every agent session should leave behind stronger constraints, not just more code. In this run, the Bash tool mangling $ and backticks in file writes became a CLAUDE.md rule, the port collision became a preview rule, base-path issues became dist checks, tag/series consistency became schema validation, the content workflow became skills, etc.

When an agent discovers a class of mistake, do not just fix the instance. Ask whether it can become a test, a validation rule, a repo instruction, a skill step, or a generator default.

  1. Decide before you prompt
    1. fuzzy idea
    2. reason about product + architecture
    3. surface the assumptions I didn't specify
    4. bootstrap prompt
  2. Scaffold, then plan against it
    1. working repository scaffold
    2. plans with validation baked into every stage
    3. plan review / steering
  3. Validation is the real output
    1. agent execution
    2. quality becomes a floor
    3. every issue becomes a permanent check
    4. extend it in short sessions, each one shippable

The initial run was not the end#

The plans did not produce a finished site, and I did not expect them to. Over the next several sessions I went back in and added what the program had not covered: a redesigned logo, skill authority and sourcing rules, next-reading suggestions, a table of contents, read time, this post’s process rail, an llms.txt index, an external link checker, an image lightbox, git-derived last-updated dates, a print stylesheet, a legal page, a WCAG target-size fix for tag pills, abandonment of Mermaid, etc.

These additions were comparatively routine because the repository already encoded most of the constraints they needed to preserve. That is the point. Each feature was a short session against a repository that already knew how to reject a broken state: the schema caught bad frontmatter, the dist checks caught a missing alt or a link outside the base path, the e2e suite caught a layout regression, and the skills routed each change through the same validate-and-build sequence as the original plans. The cost of the validation work in the first session was paid back on every one of these additions, because I could focus my review on the important architectural and behavioral changes, while relying on the validation suite to cover the mechanical invariants it was designed to enforce.

None of them needed a long prompt either. Each was one or two sentences against a repo that already knew its own rules. That is not an argument for a bigger Prompt 1; it is the opposite. Once the constraints live in the repo, when a feature gets asked for stops mattering, because the same schema, checks, and skills police it whether it arrives in the first plan or the fortieth session.

The prompts, one per feature

Suggested next reading

At the end of every post, show one ‘read next’ card. The series nav already owns same-series posts, so exclude those and the post itself; pick the post with the most shared tags, tie-break by newest then slug so builds are deterministic, and fall back to the newest eligible post so it is never empty. Put the picker in src/lib/posts.ts with a unit test.

llms.txt and raw markdown

Serve each post as raw markdown at /<slug>/index.md and add /llms.txt listing the site name, description, and one line per post linking to its .md. Strip MDX imports and components to plain markdown. Both go through getPosts so drafts are excluded exactly as in HTML; add llms.txt to the reserved slugs. Pure builders in src/lib/llms.ts with tests; endpoints under src/pages/.

External link checker

After the HTML dist checks, fetch every external link once. Only 404 and 410 fail the build; timeouts, 5xx, 403 bot blocks and 429 are warnings, because those are mostly not our fault. Cache reachable URLs for seven days in node_modules/.cache, throttle requests, use AbortSignal.timeout, and honour SKIP_EXTERNAL_LINKS=1 for offline builds. Inject fetch so it is unit-testable.

Lightbox

Give Figure a zoom prop that wraps the image in a button (aria-haspopup=dialog) and opens a native <dialog> with a close button and the caption as its label. One small inline script in the post layout, delegated by class, so pages without zoomable figures ship no extra JavaScript. Hide the dialog in print and make sure it passes axe.

Git last-updated

Show an ‘Updated’ date on posts from git log -1 of the post file, build-time only, memoised per file. Render it only when it is at least seven days after the publish date so launch-week fixes stay quiet. Return nothing outside a checkout or for uncommitted files, and set fetch-depth: 0 in CI or every file reports the same shallow date.

Print styles

Add print rules in global.css: hide chrome per element with print:hidden, force the light theme, append external link URLs after the link text, avoid page breaks inside code, figures, tables and blockquotes, wrap long code lines, and hide the lightbox and heading anchors.

Table of contents

Add a left-rail table of contents on posts at desktop widths. Collapsed, it is one bar per heading, bar width proportional to the title, with the current section marked by a scroll listener; on hover or keyboard focus it expands into the clickable ‘On this page’ list. Hidden in print. No framework, a few lines of inline script, and the anchors must resolve in the dist checks.

Tag pills

Tag pills and nav links must meet the WCAG 2.2 24 px target size.

Legal page

Add a /legal/ page (privacy, disclaimer, terms) as static MDX under src/pages/ using the page layout, pulling name and site details from the author and site configs. Link it from the footer as ‘Legal & Privacy’ and add legal to the reserved slugs.

Read time

Show reading time in post metadata: whole minutes at about 230 words per minute, minimum one, counted from the post body with collapsed <Details> blocks removed since readers skip those. Pure function with a unit test.

Navigation cues

This post walks a nine-stage process. Add a small ‘you are here’ rail above each stage’s section, listing every stage with the current one filled in, so a reader who scrolls in from the middle knows where they are. Post-local component, static, no script. Also make the table of contents keep the current link scrolled into view when the panel is long.

Every one of those ran through the same skill, the same validation, and the same build, and every one landed green.

Final takeaway#

Use conversation to reduce ambiguity, use a scaffold to discover reality, then plan from the repository instead of the original idea. The useful pattern is not a better giant prompt. The first prompt establishes architecture; the repository then replaces that prompt as the source of truth; the second planning pass turns the real repository into bounded, independently shippable changes. Over time, the important knowledge should leave chat and become code, schemas, tests, validation, repository instructions, and skills. That is what makes this feel less like prompting and more like software engineering.