How I built this
This site displays both my professional content (resume, portfolio, blog, and so on) and the testing apparatus behind it. It is my solution to the problem of how technical communicators can display both their documentation skills and their AI expertise. This site does that in the form of a documentation pipeline.
My vision
Most of a technical writer's best work lives inside a company, locked behind confidentiality agreements. How can tech writers (or context engineers or knowledge engineers or whatever we end up calling ourselves) display the full breadth of our skills?
My answer was to build a portfolio site that parallels a documentation website. This proof of concept shows the finished work and the documentation system behind it. Anyone can see both the frontend, where the writer's words live, and the backend, where the testing and pipeline run.
That only works if the process is visible too. This page gives the overall picture of how I built the site, while the blog dives into specific problems and solutions. The blog also provides continuing content that the pipeline can run against, allowing me to continue iterating and improving it.
What I built
I built this site with Claude Code as my
day-to-day collaborator. Three custom skills live in .claude/commands/:
- One starts the local dev server.
- One runs the same style, spelling, and link checks that CI runs.
- One scaffolds a new blog post with the right frontmatter.
A fourth mechanism, CLAUDE.md, isn't a skill I invoke. Claude Code reads
it automatically at the start of every session and follows it as standing
context. For more information on what each one does, see Skills and
and CLAUDE.md.
Every content change, mine or Claude's, passes through Vale, codespell, Lychee, Doc Detective, and docmeta before it reaches the published site. For the full technical breakdown, see this site's documentation pipeline overview and this repository's README on GitHub.
What I learned
None of that is the interesting part, though. Tooling is easy to copy. What isn't easy to copy is judgment, and judgment only shows up in the moments where I disagreed with what Claude did and said so.
Claude converted two pages from .tsx to .mdx without being asked, and it
broke the home page. I'd noticed Vale wasn't linting my home page or
portfolio page, and asked why. Claude explained that Vale skips .tsx files,
then suggested converting both pages to .mdx so they'd get linted too.
I agreed to that change. What I didn't agree to was the side effect.
Docusaurus treats top-level .mdx files differently, and my home page
quietly grew a sidebar that didn't belong there.
Claude never flagged that risk, and never noticed the sidebar afterward. I caught it immediately, because a stray sidebar looks wrong to a human at a glance. It doesn't register as wrong to a model that never looks at rendered output and lacks human judgment.
I had Claude revert the home page to .tsx and left the portfolio page as
.mdx, since that one rendered fine. Same fix, applied selectively, because
the failure only showed up in one of the two cases.
Claude built tests against an outdated Doc Detective schema, and I didn't catch it until CI failed. I hadn't told Claude to check the current version before writing against its schema. It defaulted to whatever it already knew, instead of confirming.
The tests failed once I ran them for real. Since then, the standing rule between us is explicit. Verify the installed or current version before writing code against a library's API or schema, every time, not just when something breaks.
That rule now applies to every tool in this pipeline, not just Doc Detective.
I wrote my first Doc Detective test the way I would explain the steps to a person, not the way the testing framework needed them. My instructions read like "navigate to google.com" and "observe the doodle." That's how you'd write steps for a human.
Doc Detective's testing agent doesn't parse prose that way. Claude started writing the actual test logic into a separate JSON file to work around the gap. That matched a pattern I recognized from a demo I'd seen before, though I hadn't asked for it here.
I pushed back and asked Claude to rewrite the instructions so the testing agent could act on them directly. That closed the gap and let the test live inline, which was the structure I actually wanted for that use case.
The lesson wasn't about JSON versus inline files. It was that I was writing for the wrong reader, and Claude adapted to my mistake instead of naming it.
Conclusion
None of these were dramatic failures. They were the ordinary kind: a side effect that went unmentioned, a version check that got skipped, a wrong assumption about the audience.
What made them recoverable was that I read Claude's output closely. I noticed when something didn't match what I'd actually asked for. And I was willing to reject a working answer that solved the wrong problem.
This page is the summary in between: not the log, not the schematic, just the judgment calls.
Further reading
For the day-by-day account, including the incidents that took longer to untangle, my blog documents each one as it happened. For the technical mechanics, this site's documentation pipeline overview and this repository's README on GitHub describe how each tool is wired together.