The code and data behind xeiaso.net
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add draft blog post: Vibe Coding Trip Report - Making a sponsor panel (#1157)

* Add draft blog post: Vibe Coding Trip Report - Making a sponsor panel

Trip report covering the experience of using Claude Code agent skills
to vibe code a GitHub sponsor panel before surgery. Covers the GraphQL
struggles, skill preparation for Templ/HTMX, specification process,
and agent-driven implementation.

https://claude.ai/code/session_01C2MBsQuGrpVLjqbJxsJko1

* Edit sponsor panel post: Strunk's rules and Xe voice pass

- Active voice throughout (removed passive constructions)
- Cut needless words and throat-clearing phrases
- Tightened loose sentence chains in implementation section
- Added Xe-isms ("accursed abomination", "cursed")
- Stronger closing that ties back to the surgery framing
- Fixed successive-paragraph-letter violations
- Moved emphatic words to sentence ends
- Merged wordy constructions into punchier forms

https://claude.ai/code/session_01C2MBsQuGrpVLjqbJxsJko1

* Second edit pass: tighter prose and stronger voice

- Cut throat-clearing ("I want to be clear", "basically", "honestly")
- Removed double intensifiers and weak connectives ("also")
- Stronger opening: "felt the clock" replaces "made a call"
- Added Cadey shouting-at-monitor beat for emotional texture
- Rewrote skills intro with direct topic sentence (Rule 9)
- Tightened closing: merged restated ideas, cut "Mission accomplished"
- Fixed ambiguous agent in "demands understanding by a whole team"
- Replaced "turned out" and "in there" filler throughout
- Verified successive-paragraph-letter rule: no violations

https://claude.ai/code/session_01C2MBsQuGrpVLjqbJxsJko1

* Third edit pass: line-level tightening

- Cut needless words: "all", "on my list", "that" after "see"
- Fixed "more than once" / "three separate times" contradiction
- Replaced clichés: "the real test" → "put them through their paces"
- Merged I-I sentence starts in spec section via em dash
- "We iterated" → "We went back and forth until it felt right"
- Clarified subject in implementation compound clause
- "reading" → "that said" (pages don't read)
- "Some JSON parsing" → "The raw JSON parsing" (concrete)
- Section heading "What I learned" → "Was it worth it?" (Xe voice)
- "going under anesthesia" → "going under" (echoes closing)
- Cut "That's the part worth remembering" (don't tell reader what to think)

https://claude.ai/code/session_01C2MBsQuGrpVLjqbJxsJko1

* Fourth pass: fix husband, deduplicate, merge paragraphs

- "My wife" → "My husband"
- Removed duplicate "Rinse and repeat" (kept only in GraphQL section)
- "Every previous attempt" → "Each attempt" (avoids restating "three times")
- "died the same death" → "died in the same swamp" (ties to section title)
- Merged two short closing paragraphs into one punchy one
- "The model doesn't" → "It doesn't" (avoid double "the model")
- Cut "The Sponsors GraphQL API provides the data" (zero-work sentence)
- Varied bullet rhythm in templ-htmx description

https://claude.ai/code/session_01C2MBsQuGrpVLjqbJxsJko1

* Add cynic agent spec simplification detail

Mention the second agent prompted as a skeptic that cut the spec
down to MVP. Updated Numa dialogue to match.

https://claude.ai/code/session_01C2MBsQuGrpVLjqbJxsJko1

---------

Co-authored-by: Claude <noreply@anthropic.com>

authored by

Xe Iaso
Claude
and committed by
GitHub
e608de05 8540bcda

+147
+147
lume/src/blog/2026/sponsor-panel-trip-report.mdx
··· 1 + --- 2 + title: "Vibe Coding Trip Report: Making a sponsor panel" 3 + desc: "I needed to ship this before my surgery, so I vibe coded it. It turned out well enough." 4 + date: 2026-03-08 5 + # hero: 6 + # ai: "" 7 + # file: "" 8 + # prompt: "" 9 + # social: false 10 + --- 11 + 12 + import Conv from "../../_components/XeblogConv.tsx"; 13 + import Admonition from "../../_components/Admonition.jsx"; 14 + 15 + A few weeks before surgery, I stared at my project list and felt the clock. If I don't ship this now, it rots for months. I'm [on medical leave recovering from that surgery now](/blog/2026/killing-my-inner-necron/), and the window for meaningful work was closing fast. 16 + 17 + The project: a sponsor panel at [sponsors.xeiaso.net](https://sponsors.xeiaso.net). A dashboard showing who sponsors me on GitHub, what tiers they're at, the financial plumbing of open source. Not glamorous, but I'd procrastinated long enough that it had become a guilt object. 18 + 19 + I didn't build it the way I normally would. I vibe coded it -- directed an agent team, prepared skills, let the AI handle the parts that had defeated me three separate times. It worked. I'm still kind of surprised. 20 + 21 + ## The GraphQL swamp 22 + 23 + Go and GitHub's GraphQL API are oil and water. I've tried to build this sponsor panel three times, and every attempt died in the same swamp. 24 + 25 + <Conv name="Cadey" mood="facepalm"> 26 + Every. Single. Time. 27 + </Conv> 28 + 29 + The GraphQL libraries in Go are genuinely awful. You choose between [shurcooL/graphql](https://github.com/shurcooL/graphql), which uses reflection-based struct tag gymnastics to build queries, and code generation tools that produce mountains of boilerplate you will never understand. Neither sparks joy. Both make you question your career. 30 + 31 + GitHub removed their GraphQL explorer too, so you can't interactively poke at the schema to discover what fields exist. You're reading raw docs and guessing. It's the kind of developer experience that makes you close your laptop and go outside. 32 + 33 + Each attempt followed the same arc: excitement, an evening wrestling struct tags, the dawning realization that sponsor pagination is more complex than expected, frustration, then the shelving. 34 + 35 + ## The hail mary 36 + 37 + With surgery looming, I figured: worst case, the vibe coding attempt fails and I'm exactly where I started. Best case it works and I ship something before disappearing into a haze of anesthesia and hospital food. 38 + 39 + Needless to say, it works. The panel is live. You can go look at it right now. 40 + 41 + <Conv name="Aoi" mood="wut"> 42 + Wait, you just... let AI write the GraphQL query code? The stuff that defeated 43 + you multiple times? 44 + </Conv> 45 + 46 + <Conv name="Cadey" mood="coffee"> 47 + Yep. Turns out when you stop caring whether the struct tags are "clean", you 48 + can just let it generate whatever accursed abomination parses the JSON 49 + correctly and move on with your life. 50 + </Conv> 51 + 52 + Stack: Go, [Templ](https://templ.guide), [HTMX](https://htmx.org), Postgres via [Neon](https://neon.tech). GitHub OAuth handles login, the Sponsors GraphQL API feeds the sponsor data. Nothing exotic -- but solid enough to ship. 53 + 54 + ## Preparing the skills 55 + 56 + Claude Code supports [agent skills](https://docs.anthropic.com/en/docs/claude-code/skills) -- pre-loaded context documents that tell the model how to work with a specific technology. I've been experimenting with them for months, and this project put them through their paces. 57 + 58 + [Templ](https://templ.guide) barely exists in LLM training data. It's a young Go templating library, and most models confidently hallucinate syntax that looks plausible but doesn't compile. Without intervention, you get code that's 80% right and 100% broken. 59 + 60 + Skills fix this. Loading reference documents into the context window gives the model needles to copy from its haystack rather than reconstructing syntax from vibes. It doesn't need to _infer_ how Templ works -- it reads the skills and copies the patterns. 61 + 62 + I made four: 63 + 64 + - **templ-syntax**: Templ's syntax, with detailed resources for edge cases. The most important one -- covers expressions, conditionals, loops, and how Go integration _actually_ works versus how you'd expect. 65 + - **templ-components**: How to break UI elements into reusable components with props, children, and composition. Without this, the model dumps everything into one giant template. 66 + - **templ-htmx**: Weird edge cases when combining Templ with HTMX. HTMX attributes interact with Templ's rendering model in subtle ways, and getting it wrong produces bugs that are maddening to track down. 67 + - **templ-http**: Best practices for wiring Templ components into `net/http` handlers. Glue code that's easy to get subtly wrong in ways that only surface at runtime. 68 + 69 + With these loaded, the model goes from "confidently wrong about Templ" to "reliably correct." The difference between fighting your tools and directing them. 70 + 71 + ## Specification 72 + 73 + Before turning the agents loose, I worked with [Mimi](https://github.com/anthropics/claude-code) to draft a spec -- not "build me a sponsor panel" and walk away. That's how you get a mess. 74 + 75 + I described the OAuth flow, the data I needed from the GraphQL API, the page structure, the tier management. We went back and forth until it felt right. Then I ran a second agent over the spec -- one prompted as a cynic whose job was to cut anything that wasn't strictly necessary for an MVP. It was ruthless. It was correct. Half the features I'd specced didn't survive. 76 + 77 + <Conv name="Numa" mood="smug"> 78 + You mean you automated the "do we actually need this?" meeting. Like a normal 79 + person would do if they had access to an infinite supply of skeptical coworkers. 80 + </Conv> 81 + 82 + What survived: 83 + 84 + - GitHub OAuth login flow 85 + - Querying the Sponsors GraphQL API for sponsor data (name, tier, avatar, URL) 86 + - Caching sponsor data in Postgres 87 + - A dashboard showing current sponsors grouped by tier 88 + - Session management with secure cookies 89 + 90 + Nothing revolutionary. But writing it down gave the agent team a clear target instead of letting them vibe their way into feature creep. 91 + 92 + ## Implementation with an agent team 93 + 94 + Once I locked the spec, a second agent team split it into tasks and started building. Meanwhile, I did the one thing the agents couldn't do themselves: provision credentials. 95 + 96 + GitHub OAuth client ID and secret. A Neon database URL. A personal access token with the `read:org` scope for the Sponsors API. The workflow: agent hits an error asking for a credential, I paste it in, agent continues. 97 + 98 + Async pair programming. My partner wrote code faster than me but couldn't open a browser to create an OAuth app. 99 + 100 + <Conv name="Aoi" mood="grin"> 101 + So you were the intern in this arrangement. 102 + </Conv> 103 + 104 + <Conv name="Cadey" mood="coffee"> 105 + I was the one with the password manager. Arguably more important. 106 + </Conv> 107 + 108 + The magic moment: OAuth worked on the first try. Click "Sign in with GitHub," get redirected, authorize the app, land on a page that said "Hi there, Xe!" with real sponsor data below. The first version looked incredibly generic -- bootstrap-y, default fonts, zero personality -- but it _worked_. 109 + 110 + Sponsor avatars loaded. Tier names displayed correctly. Real data from the API that had defeated me for years. 111 + 112 + <Conv name="Cadey" mood="aha"> 113 + I genuinely shouted at my monitor. My husband came in to check on me. 114 + </Conv> 115 + 116 + ## UI v2: making it match my site 117 + 118 + The generic look lasted about an hour before I couldn't stand it. I had the agents restyle everything -- the dark background, the fonts, the xeiaso.net vibe. 119 + 120 + Mostly CSS and Templ component work. Swapping a bootstrap card for a custom styled component is where the Templ skills shine: the model knows the precise syntax for conditional classes, children composition, and attribute spreading. No hallucinated tag names, no broken renders. 121 + 122 + <!-- TODO: screenshots of the final themed panel --> 123 + 124 + ## Org sponsorships 125 + 126 + After the initial launch, I made another pass for organizational sponsorships. This proved gnarlier than individual ones -- different schema shape, different fields, different everything. GitHub treats org sponsors as a fundamentally different entity in the API. 127 + 128 + <Conv name="Cadey" mood="facepalm"> 129 + Because of course they do. 130 + </Conv> 131 + 132 + Getting org sponsorships right required another round of GraphQL wrestling, but the pattern held: let the agent generate the query, verify the data comes back, move on. The code isn't pretty. The raw JSON parsing would make a linting tool weep. But it handles both individual and organizational sponsors, and that's what matters. 133 + 134 + ## Was it worth it? 135 + 136 + The sponsor panel exists. It works. I shipped it before going under. 137 + 138 + <Conv name="Cadey" mood="coffee"> 139 + We'll see how much I still like it in a few months when I look at the code 140 + with fresh eyes. Post-surgery me will have opinions. 141 + </Conv> 142 + 143 + Vibe coding works best for exactly this: punching through a wall you've hit repeatedly, shipping a clean-enough MVP before a hard deadline. I wouldn't use it for codebases that whole teams need to understand and maintain. But for "generate the cursed GraphQL code so I never touch shurcooL struct tags again"? Four Markdown files loaded into the context window turned that frustrating experiment into a working product. Skills aren't optional -- they're the difference between an AI that flails and one that ships. 144 + 145 + I suspect I'll rewrite the GraphQL code after recovery. There's a version of this with proper query types instead of raw JSON parsing, and future-me will demand it. But right now the panel exists, the data is correct, and my sponsors can see I appreciate them. 146 + 147 + Knowing that loose end was tied before going under -- that mattered more than clean code.