<rss version="2.0">
  <channel>
    <title>Projects on Numeric Citizen I/O — A Blog About Blogging</title>
    <link>https://meta.numericcitizen.me/categories/projects/</link>
    <description></description>
    
    <language>en-us</language>
    
    <lastBuildDate>Sun, 06 Sep 2026 10:20:14 -0400</lastBuildDate>
    
    <item>
      <title>Making Eight Years of Blogging Searchable</title>
      <link>https://meta.numericcitizen.me/2026/09/06/making-eight-years-of-blogging.html</link>
      <pubDate>Sun, 06 Sep 2026 10:20:14 -0400</pubDate>
      
      <guid>http://numericcitizen-meta.micro.blog/2026/09/06/making-eight-years-of-blogging.html</guid>
      <description>&lt;p&gt;I have been publishing on Ghost since October 2018. That is 677 posts, and until this week I had no good way to ask questions about any of it. I could search my own site, which matches titles. I could remember roughly what I wrote and when, which works for the last few months and fails completely for 2019.&lt;/p&gt;
&lt;p&gt;What I wanted was to ask &amp;ldquo;what have I written about X&amp;rdquo; and get a real answer drawn from the full text of every post. Ghost offers an API but no MCP endpoint, so the obvious path was not available. Here is what it actually took, including the parts I got wrong.&lt;/p&gt;
&lt;h2 id=&#34;starting-with-what-already-exists&#34;&gt;Starting with what already exists&lt;/h2&gt;
&lt;p&gt;There are several community-built Ghost MCP servers. I tried one that supports the read-only Content API, and it worked immediately. Posts, pages, tags, authors, all queryable in conversation.&lt;/p&gt;
&lt;p&gt;For about an hour I thought I was done.&lt;/p&gt;
&lt;h2 id=&#34;the-thing-that-quietly-breaks-everything&#34;&gt;The thing that quietly breaks everything&lt;/h2&gt;
&lt;p&gt;Ghost 6.0 removed support for &lt;code&gt;?limit=all&lt;/code&gt; and imposed a maximum page size of 100 on every API endpoint. That change is documented and reasonable, since unbounded queries hurt large sites.&lt;/p&gt;
&lt;p&gt;The problem is how it fails. Requesting &lt;code&gt;?limit=all&lt;/code&gt; does not error. It returns 100 items and says nothing about the other 577.&lt;/p&gt;
&lt;p&gt;I found this by running a &lt;code&gt;curl&lt;/code&gt; command to measure my archive size and getting back exactly 100 posts. A suspiciously round number for a blog running since 2018. The same cap applies to any MCP server built on the Content API, which means every answer I had been getting was drawn from my most recent fourteen months of writing while presenting itself as complete.&lt;/p&gt;
&lt;p&gt;That is worse than an error. An error tells you to fix something. This just quietly narrows your world.&lt;/p&gt;
&lt;p&gt;There is a second limitation underneath it. Ghost&amp;rsquo;s NQL filter language has no &lt;code&gt;like&lt;/code&gt; operator and no partial matching, and content fields are not filterable at all. You can retrieve &lt;code&gt;plaintext&lt;/code&gt; in a response, but you cannot query against it. So even with correct pagination, there is no way to search article bodies through the API.&lt;/p&gt;
&lt;p&gt;Ghost&amp;rsquo;s own site search works around this the same way I ended up doing: download the corpus and search it locally.&lt;/p&gt;
&lt;h2 id=&#34;the-local-copy&#34;&gt;The local copy&lt;/h2&gt;
&lt;p&gt;The design that followed is unglamorous and took an afternoon.&lt;/p&gt;
&lt;p&gt;A Postgres table holding one row per post, with the full body in a &lt;code&gt;plaintext&lt;/code&gt; column and a generated &lt;code&gt;tsvector&lt;/code&gt; column indexed with GIN. An n8n workflow pulls the archive weekly with explicit pagination, seven pages at 100 posts each with a one second delay between them, and upserts everything in a single round trip.&lt;/p&gt;
&lt;p&gt;The schema is boring on purpose:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;create&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;table&lt;/span&gt; ghost.posts (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  id            text &lt;span style=&#34;color:#66d9ef&#34;&gt;primary&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;key&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  slug          text &lt;span style=&#34;color:#66d9ef&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;unique&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  title         text,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  plaintext     text,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  url           text,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  tags          text[],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  published_at  timestamptz,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  updated_at    timestamptz,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  synced_at     timestamptz &lt;span style=&#34;color:#66d9ef&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  fts tsvector &lt;span style=&#34;color:#66d9ef&#34;&gt;generated&lt;/span&gt; always &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    setweight(to_tsvector(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;english&amp;#39;&lt;/span&gt;::regconfig, coalesce(title, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;)),     &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;A&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    setweight(to_tsvector(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;english&amp;#39;&lt;/span&gt;::regconfig, coalesce(plaintext, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;)), &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;B&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ) stored
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;setweight&lt;/code&gt; calls mean a title match outranks a body match. Without them, a post that mentions a term once in passing can outrank the post named after it.&lt;/p&gt;
&lt;p&gt;Two details in the sync are worth stealing if you build something similar.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Full reconcile, not incremental.&lt;/strong&gt; My first instinct was a watermark on &lt;code&gt;updated_at&lt;/code&gt;, which is what I use for feed-based syncs. It is the wrong tool here. It would save six HTTP calls a week and would never detect a deleted or unpublished post. Pulling everything self-heals.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A guard on the delete step.&lt;/strong&gt; Every row gets stamped with the run&amp;rsquo;s timestamp, and anything older gets pruned, which is how deletions propagate. But the prune only runs if this pass fetched at least 80% of the rows already in the table. Without that check, one bad fetch silently wipes eight years of archive. This is the kind of thing you want in place before you need it.&lt;/p&gt;
&lt;h2 id=&#34;i-was-wrong-about-the-size&#34;&gt;I was wrong about the size&lt;/h2&gt;
&lt;p&gt;I nearly did not build this because I assumed the archive would blow past my database provider&amp;rsquo;s free tier. That assumption was off by roughly two orders of magnitude.&lt;/p&gt;
&lt;p&gt;677 posts come to 3.2 million characters of body text. The table with its full-text index totals 11 MB against a 512 MB limit. I would need something like 32,000 posts to be in trouble.&lt;/p&gt;
&lt;p&gt;A related surprise: my recent posts average about 8,400 characters while the archive average is 4,700. My writing has roughly doubled in length over eight years. I did not know that about myself before running the query.&lt;/p&gt;
&lt;p&gt;The more useful lesson is that on serverless Postgres, storage is almost never the binding constraint for text. Compute hours are. Every query wakes the database for a minimum billing interval regardless of how fast it runs, so a scattered handful of ad-hoc searches costs more than the weekly bulk write does. Worth knowing before you optimise the wrong thing, which I did for a while.&lt;/p&gt;
&lt;h2 id=&#34;the-part-i-did-not-anticipate&#34;&gt;The part I did not anticipate&lt;/h2&gt;
&lt;p&gt;With the table populated, I assumed I was finished. I was not, and the gap here is the least obvious thing in this whole project.&lt;/p&gt;
&lt;p&gt;Nothing routes the question.&lt;/p&gt;
&lt;p&gt;Each time you ask something, the assistant picks tools based on their names and descriptions. It sees a Ghost MCP whose tools are explicitly labelled as browsing and reading blog posts. It also sees a generic SQL tool pointed at a database it knows nothing about. Ask &amp;ldquo;what have I written about X&amp;rdquo; and the match is overwhelmingly toward the Ghost tools, which return the 100 most recent posts and no warning.&lt;/p&gt;
&lt;p&gt;Building the better data source does not cause it to be used. You have to say so, explicitly, somewhere persistent.&lt;/p&gt;
&lt;p&gt;I solved this with a Skill: a set of instructions that triggers on phrasings like &amp;ldquo;what did I write about X&amp;rdquo; and states the routing rule as a prohibition rather than a preference. Do not use the Ghost browse tools for search, here is why they fail, here is the table and the query shape to use instead.&lt;/p&gt;
&lt;p&gt;The skill also pins the retrieval pattern, which matters more than I expected. Search first with ranked snippets, then fetch full bodies for only the three to five posts that are actually relevant. And read whole posts, never fragments. A paragraph mentioning a product without the surrounding argument is exactly how you end up having your own past position mischaracterised back to you.&lt;/p&gt;
&lt;h2 id=&#34;full-text-is-not-semantic-search&#34;&gt;Full-text is not semantic search&lt;/h2&gt;
&lt;p&gt;I conflated these two for a while, and the distinction is worth being precise about.&lt;/p&gt;
&lt;p&gt;What I built matches words and their grammatical variants. Ask about &amp;ldquo;Apple Intelligence&amp;rdquo; and it finds posts containing those words. Ask &amp;ldquo;how has my thinking about AI subscriptions evolved&amp;rdquo; and it will find something, because the words appear somewhere, but it has no way to surface a post that discusses the same idea in entirely different vocabulary.&lt;/p&gt;
&lt;p&gt;Semantic search needs embeddings, a vector column, and a model called at both index and query time. That is a real layer of work.&lt;/p&gt;
&lt;p&gt;I deliberately did not build it. Full-text over 677 posts with English stemming handles the lookup questions well, and lookup is most of what I actually ask. If the thematic questions come back consistently thin, the embeddings layer drops into the same table without rearchitecting anything. Building it up front would have been solving a problem I had not yet confirmed I have.&lt;/p&gt;
&lt;h2 id=&#34;what-it-does-not-do&#34;&gt;What it does not do&lt;/h2&gt;
&lt;p&gt;Four posts have empty bodies. The unauthenticated Content API cannot see members-only content, so gated posts arrive with a title and nothing else. They are findable by name but their text is not indexed. Fixing that means switching to the Admin API with JWT signing, which is not worth it for four posts.&lt;/p&gt;
&lt;p&gt;The copy is up to seven days stale. Irrelevant for archive questions, relevant if I ask about something published yesterday.&lt;/p&gt;
&lt;p&gt;And the whole thing is lexical. When a thematic question comes back thin, the honest answer is that the tool is not built for it, not that the archive is silent.&lt;/p&gt;
&lt;h2 id=&#34;was-it-worth-it&#34;&gt;Was it worth it&lt;/h2&gt;
&lt;p&gt;An afternoon, one database table, one scheduled workflow, and one skill file.&lt;/p&gt;
&lt;p&gt;The first real test was asking what I had written about GuruShots, a photography game I covered in late 2018. Two posts came back with dates and links. Through the Ghost API alone, that question has no answer at all.&lt;/p&gt;
&lt;p&gt;That is the whole point. Eight years of writing is only an archive if you can reach into it.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>The Second Half of 2026</title>
      <link>https://meta.numericcitizen.me/2026/06/16/the-second-half-of.html</link>
      <pubDate>Tue, 16 Jun 2026 21:53:16 -0400</pubDate>
      
      <guid>http://numericcitizen-meta.micro.blog/2026/06/16/the-second-half-of.html</guid>
      <description>&lt;p&gt;After spending the &lt;a href=&#34;https://meta.numericcitizen.me/2026/06/15/reach-is-the-right-word.html&#34;&gt;first half of 2026&lt;/a&gt; vibe-coding web applications, now that they are mature and operational, I&amp;rsquo;m thinking about how I&amp;rsquo;m going to spend the second half: setting up and finding the right use cases for running agentic AI locally on my dormant M4 Mac mini. Just another digital world open for exploration.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Reach is the right word</title>
      <link>https://meta.numericcitizen.me/2026/06/15/reach-is-the-right-word.html</link>
      <pubDate>Mon, 15 Jun 2026 21:46:59 -0400</pubDate>
      
      <guid>http://numericcitizen-meta.micro.blog/2026/06/15/reach-is-the-right-word.html</guid>
      <description>&lt;p&gt;I was exploring the GitHub website, my profile page to be exact. Here&amp;rsquo;s my GitHub activity panel for 2026 so far. Apart from the commits from the Micro.blog auto-archiving process, this is what it shows, with everything beginning on January 1st: the contributions coming from my vibe activities. This represents nearly 8 commits per day, every day, resulting in seven custom-built web applications.&lt;/p&gt;
&lt;p&gt;Looking back on the past six months, I cannot believe how far I&amp;rsquo;ve come with all this. If I could choose one word to summarize what generative AI and vibe coding have done for me, it would be &amp;ldquo;reach&amp;rdquo;. I have achieved goals that would otherwise have required years of coding experience. The only thing I had was years of end-user experience and a clear idea of what would be fulfilling.&lt;/p&gt;
&lt;p&gt;Excited to see what the next six months of 2026 have in store.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/143495/2026/github-activity-first-half-2026.png&#34; width=&#34;600&#34; height=&#34;205&#34; alt=&#34;&#34;&gt;
</description>
    </item>
    
    <item>
      <title>The Numeric Citizen Analytics Dashboard Experiment</title>
      <link>https://meta.numericcitizen.me/2026/06/06/the-numeric-citizen-analytics-dashboard.html</link>
      <pubDate>Sat, 06 Jun 2026 08:49:07 -0400</pubDate>
      
      <guid>http://numericcitizen-meta.micro.blog/2026/06/06/the-numeric-citizen-analytics-dashboard.html</guid>
      <description>&lt;p&gt;This morning, I delved into integrating Craft with Claude Cowork. Using the Craft MCP connector, I built a dashboard that sources data directly from Craft tables. This setup generates a Live Artifact in Claude Cowork, which dynamically updates as more data is added. Although Lovable could have been used to achieve this, the absence of custom domain support in its free tier led me to choose Claude Cowork, which offers more flexibility for my needs.&lt;/p&gt;
&lt;p&gt;&lt;video controls=&#34;controls&#34; playsinline=&#34;playsinline&#34; preload=&#34;none&#34; width=&#34;2140&#34; height=&#34;1440&#34; poster=&#34;https://cdn.uploads.micro.blog/143495/2026/frames/1775364-0-8bb8a5.jpg&#34; src=&#34;https://cdn.uploads.micro.mov/143495/2026/numericcitizenanalyticsdashboard/playlist.m3u8&#34;&gt;&lt;/video&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://meta.numericcitizen.me/2026/04/23/im-not-planning-to-go.html</link>
      <pubDate>Thu, 23 Apr 2026 22:18:53 -0400</pubDate>
      
      <guid>http://numericcitizen-meta.micro.blog/2026/04/23/im-not-planning-to-go.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;m not planning to go any further with &lt;a href=&#34;https://meta.numericcitizen.me/2026/04/19/just-testing-something-for-my.html&#34;&gt;my Lovable experimentation&lt;/a&gt;. The reason? On the free plan, custom domains are not supported. It&amp;rsquo;s a complete deal-breaker. Plus, the free tier doesn&amp;rsquo;t provide enough credits to build something meaningful, and you have to wait 24 hours before getting more. Too bad, it was somewhat promising. Pass. 😔 The good thing is that I&amp;rsquo;m focusing on Vercel for hosting web apps like mine.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://meta.numericcitizen.me/2026/04/19/just-testing-something-for-my.html</link>
      <pubDate>Sun, 19 Apr 2026 17:14:13 -0400</pubDate>
      
      <guid>http://numericcitizen-meta.micro.blog/2026/04/19/just-testing-something-for-my.html</guid>
      <description>&lt;p&gt;Just testing something for my next project&amp;hellip; for that one, I might try Lovable instead of Vercel&amp;hellip; because I want to try different services.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/143495/2026/testing-lovable-things3-duplicate.png&#34; width=&#34;600&#34; height=&#34;345&#34; alt=&#34;&#34;&gt;
</description>
    </item>
    
    <item>
      <title>Coming Soon </title>
      <link>https://meta.numericcitizen.me/2026/04/09/coming-soon.html</link>
      <pubDate>Thu, 09 Apr 2026 19:20:16 -0400</pubDate>
      
      <guid>http://numericcitizen-meta.micro.blog/2026/04/09/coming-soon.html</guid>
      <description>&lt;p&gt;One of the first things I’m going to do when I come back home is to deploy my new blog visual theme built entirely with Claude Code. It’s ready. I’m anxiously awaiting for the moment I put that project behind. Next, I’ll spend some time on &lt;a href=&#34;https://whois.numericcitizen.me&#34;&gt;Who Is Numeric Citizen website&lt;/a&gt; design and hosting solution (I’m thinking of leaving Chillidog Hosting hosting service to go with Elements’ built in solution instead).&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://meta.numericcitizen.me/2026/03/30/i-realized-i-forgot-to.html</link>
      <pubDate>Mon, 30 Mar 2026 20:42:52 -0400</pubDate>
      
      <guid>http://numericcitizen-meta.micro.blog/2026/03/30/i-realized-i-forgot-to.html</guid>
      <description>&lt;p&gt;I realized I forgot to clearly state the design goals before starting to build this custom theme for Micro.blog. They became clearer as I progressed and explored how Hugo and Micro.blog work, especially with assistance from Claude AI, and as I encountered various challenges. Here are the goals: a) I want a theme that stands out and doesn&amp;rsquo;t resemble typical Micro.blog blogs. b) I aim to minimize the use of external plugins, ensuring that all functionality is integrated within the custom theme. c) I want the same theme to be usable on more than one blog (I have two). Stay tuned for more news.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Takeaways From Building a Custom Micro.blog Theme Using Claude Code and Claude AI</title>
      <link>https://meta.numericcitizen.me/2026/03/29/takeaways-from-building-a-custom.html</link>
      <pubDate>Sun, 29 Mar 2026 19:14:16 -0400</pubDate>
      
      <guid>http://numericcitizen-meta.micro.blog/2026/03/29/takeaways-from-building-a-custom.html</guid>
      <description>&lt;img src=&#34;https://cdn.uploads.micro.blog/143495/2026/numericcitizen-beta-theme-small.png&#34; width=&#34;600&#34; height=&#34;677&#34; alt=&#34;&#34;&gt;
&lt;p&gt;Building a custom visual theme for Micro.blog presents unique challenges at the intersection of web design, static site generation, and AI-assisted development. This blog post captures practical insights and lessons learned from creating a custom theme with Claude Code and Claude AI, including key considerations on Hugo compatibility, plugin conflicts, and theme architecture.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Claude AI isn&amp;rsquo;t well-trained on Micro.blog architecture and dependency on Hugo static website generator. Claude doesn’t make a clear distinction between Hugo’s capabilities and Micro.blog’s unique features.&lt;/li&gt;
&lt;li&gt;Plugins are a challenge because they might inject conflicting formatting instructions into your custom-built theme. It’s hard to debug.&lt;/li&gt;
&lt;li&gt;Plugins can conflict with each other.&lt;/li&gt;
&lt;li&gt;Don’t use the latest Hugo version (0.158) and stay on 0.117 if you use many plugins. By removing low-value plugins, you increase your chance of using version 0.158.&lt;/li&gt;
&lt;li&gt;The workflow for updating a GitHub repo hosting your custom theme to the deployment on The process of Micro.blog is tedious because it’s manual and slow, especially for large websites and Hugo rendering engine. Hugo 0.158 could help, unless it conflicts with one plugin that you need.&lt;/li&gt;
&lt;li&gt;More than ever: less is more. Keep it simple, and problems will be kept at bay: removing low-value plugins can make a big difference.&lt;/li&gt;
&lt;li&gt;Test the website after each plugin removal, force a rebuild of the entire site to cleanup things. It’s slow but it might help debug later.&lt;/li&gt;
&lt;li&gt;If a plugin introduces support for smart code in blog posts, after removing that plugin, Hugo will generate errors. These blog posts need to be either updated or deleted.&lt;/li&gt;
&lt;li&gt;If the custom-built theme covers a specific design area, like a /photos page, don’t use a plugin touching the same area, prioritize the custom-built theme and skip the plugin. The idea is to have a self-contained custom theme.&lt;/li&gt;
&lt;li&gt;Stay away from abandoned-ware plugins.&lt;/li&gt;
&lt;li&gt;Prefer adding stuff in the custom theme instead of relying on an external plugin, so the theme is self-contained.&lt;/li&gt;
&lt;li&gt;The lower the number of plugins, the better the chance you can use the latest version of Hugo.&lt;/li&gt;
&lt;li&gt;Using the custom.css functionality, which helps externalize and override the custom-theme’s styling, speeds up testing different styling options.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Creating a custom Micro.blog theme using Claude AI revealed both the power and limitations of AI-assisted development in specialized ecosystems. While the tools provided invaluable support, the real breakthroughs came from hands-on debugging and understanding the unique interactions between Hugo, Micro.blog, and plugins. These takeaways form the foundation for a more robust theme architecture—one that will continue to improve with future iterations.&lt;/p&gt;
&lt;p&gt;I’m still not done with my design. As I progress, you can visit &lt;a href=&#34;https://numericcitizen-test.micro.blog&#34;&gt;my test blog&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://meta.numericcitizen.me/2026/03/28/one-positive-side-effect-of.html</link>
      <pubDate>Sat, 28 Mar 2026 14:36:45 -0400</pubDate>
      
      <guid>http://numericcitizen-meta.micro.blog/2026/03/28/one-positive-side-effect-of.html</guid>
      <description>&lt;p&gt;One positive side effect of this custom visual theme that I&amp;rsquo;m working on is that I&amp;rsquo;m cleaning up this plugin&amp;rsquo;s mess on my blog. I have way too many with little added value for the readers.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Analyzing My Installed Plugins</title>
      <link>https://meta.numericcitizen.me/2026/03/28/just-finished-compiling-the-list.html</link>
      <pubDate>Sat, 28 Mar 2026 13:52:08 -0400</pubDate>
      
      <guid>http://numericcitizen-meta.micro.blog/2026/03/28/just-finished-compiling-the-list.html</guid>
      <description>&lt;p&gt;Just finished compiling the list of Micro.blog plugins that my blog is using: 31 in total. Then Claude AI analyzed all GitHub repos to see if there were any potential conflicts. Here&amp;rsquo;s the complete impact report for all 31 plugins, organized by risk level:&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;-avoid--direct-conflicts-with-your-theme&#34;&gt;🔴 Avoid / Direct Conflicts with Your Theme&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;micro-blog-cards-theme&lt;/code&gt;&lt;/strong&gt; — This is a full replacement theme, not a plugin. Installing it would completely override your custom theme. Skip it entirely.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;plugin-collections&lt;/code&gt;&lt;/strong&gt; — Injects its own lightbox targeting &lt;code&gt;.e-content img&lt;/code&gt;. This &lt;strong&gt;directly conflicts&lt;/strong&gt; with the custom lightbox you built in v0.9.12 (&lt;code&gt;theme.js&lt;/code&gt;). Both would fight over the same images. Don&amp;rsquo;t install unless you remove your lightbox first.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;plugin-opengraph-basics&lt;/code&gt;&lt;/strong&gt; — Your theme already has full Open Graph meta tags (added in v0.5.9 in &lt;code&gt;head.html&lt;/code&gt;). Installing this would generate &lt;strong&gt;duplicate OG tags&lt;/strong&gt; in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;-caution--needs-attention&#34;&gt;🟡 Caution / Needs Attention&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;plugin-archive-months&lt;/code&gt;&lt;/strong&gt; — Overrides &lt;code&gt;list.archivehtml.html&lt;/code&gt;, which would take over the archive page display with its own year/month grouped layout and inline JS. Replaces the theme&amp;rsquo;s existing compact archive view.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;plugin-photos-months&lt;/code&gt;&lt;/strong&gt; — Overrides &lt;code&gt;list.photoshtml.html&lt;/code&gt;. &lt;strong&gt;Conflicts with your already-installed &lt;code&gt;plugin-all-photos&lt;/code&gt;&lt;/strong&gt;, which also provides that template. Last one installed wins.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;plugin-screenshots&lt;/code&gt;&lt;/strong&gt; — Also overrides &lt;code&gt;list.photoshtml.html&lt;/code&gt;. Same conflict with &lt;code&gt;plugin-all-photos&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;kottkrig/microdotblog-photos-page&lt;/code&gt;&lt;/strong&gt; — Also overrides &lt;code&gt;list.photoshtml.html&lt;/code&gt;. Same three-way conflict. Pick only one photos page plugin.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;stoot&lt;/code&gt;&lt;/strong&gt; — Its CSS expects CSS custom properties (&lt;code&gt;--gray-*&lt;/code&gt;, &lt;code&gt;--white&lt;/code&gt;, &lt;code&gt;--black&lt;/code&gt;, &lt;code&gt;--link&lt;/code&gt;, &lt;code&gt;--text-base&lt;/code&gt;) that your theme doesn&amp;rsquo;t define. Mastodon embeds will render with broken/missing styles. Fixable by adding those variables to &lt;code&gt;style.css&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;plugin-post-stats&lt;/code&gt;&lt;/strong&gt; — No dark mode support in its CSS. The stats page cards and Chart.js charts will look visually inconsistent in dark mode. Fixable with CSS overrides.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;plugin-bigfoot&lt;/code&gt;&lt;/strong&gt; — Already analyzed. jQuery 1.12.4 (~90KB) loaded on every page, and footnote popover CSS won&amp;rsquo;t respect dark mode. Set &lt;code&gt;post_separator = &amp;quot;article.h-entry&amp;quot;&lt;/code&gt; when installing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;plugin-conversation-on-mb&lt;/code&gt;&lt;/strong&gt; — Adds a &amp;ldquo;View conversation on Micro.blog&amp;rdquo; link, but requires manually calling &lt;code&gt;{{ partial &amp;quot;conversation-link.html&amp;quot; . }}&lt;/code&gt; in your theme&amp;rsquo;s single post templates. Your theme already loads Micro.blog&amp;rsquo;s &lt;code&gt;conversation.js&lt;/code&gt; via &lt;code&gt;microhook-after-post.html&lt;/code&gt;, which renders the full conversation box. This plugin adds a different thing (a link vs. a box), so they don&amp;rsquo;t conflict — but it needs a manual theme edit to integrate.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;plugin-reply-by-email&lt;/code&gt;&lt;/strong&gt; — Same situation: requires &lt;code&gt;{{ partial &amp;quot;reply-by-email.html&amp;quot; . }}&lt;/code&gt; manually added to your single post template. Your theme already has &lt;code&gt;reply.js&lt;/code&gt; for native Micro.blog replies. These serve different purposes (email vs. Micro.blog) and can coexist, but need a theme edit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;plugin-reply-on-mastodon&lt;/code&gt;&lt;/strong&gt; — Repository returns 404. &lt;strong&gt;No longer exists.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;plugin-search-space&lt;/code&gt;&lt;/strong&gt; — Creates a &lt;code&gt;/search-space/&lt;/code&gt; page (different URL from your installed &lt;code&gt;/search/&lt;/code&gt; page). Having two search pages could confuse visitors. Pick one.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Bottom line&lt;/strong&gt;: The most important things to avoid are &lt;code&gt;plugin-collections&lt;/code&gt; (lightbox conflict) and &lt;code&gt;plugin-opengraph-basics&lt;/code&gt; (duplicate OG tags). For the photos page, pick only &lt;strong&gt;one&lt;/strong&gt; of: &lt;code&gt;plugin-all-photos&lt;/code&gt;, &lt;code&gt;plugin-photos-months&lt;/code&gt;, &lt;code&gt;plugin-screenshots&lt;/code&gt;, or &lt;code&gt;kottkrig/microdotblog-photos-page&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not sure everything has been analyzed correctly, especially when it comes to photo-related plugins.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Time for a Complete Reset</title>
      <link>https://meta.numericcitizen.me/2026/03/28/time-for-a-complete-reset.html</link>
      <pubDate>Sat, 28 Mar 2026 10:30:06 -0400</pubDate>
      
      <guid>http://numericcitizen-meta.micro.blog/2026/03/28/time-for-a-complete-reset.html</guid>
      <description>&lt;p&gt;It&amp;rsquo;s time for a project status update. I&amp;rsquo;ve been working hard trying to get the Micro.blog/Hugo engine to make this custom theme work the way I want. It&amp;rsquo;s not an easy task. I&amp;rsquo;m starting to think that because Micro.blog is a niche product, even though it is based on Hugo, there are some particularities in the templating system that Claude AI is not well-trained for. Micro.blog plugins make things even more complicated. I&amp;rsquo;m now at a point where I need to feed all plugin GitHub repositories into my conversation so Claude AI can fully understand what each plugin injects into the templates. Finally, due to unpredictable issues with blog posts and image deletions, I had to delete and recreate my test blog, which wiped everything.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m fairly certain that when I&amp;rsquo;m ready to apply my custom theme to my production blog, everything will break. Luckily, we can easily revert to another theme and perform a full rebuild. It&amp;rsquo;s tedious but seems like a reliable process.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://meta.numericcitizen.me/2026/03/27/spent-some-time-building-my.html</link>
      <pubDate>Fri, 27 Mar 2026 18:03:43 -0400</pubDate>
      
      <guid>http://numericcitizen-meta.micro.blog/2026/03/27/spent-some-time-building-my.html</guid>
      <description>&lt;p&gt;Spent some time building my custom theme for Micro.blog with Claude Code. It works but the nature of Hugo processing with different templates complicates the process. What works on the main timeline won’t always work on individual posts. Lot’s of back and forth to test and fix things. The good thing is that I’m building exactly what I want.&lt;/p&gt;
&lt;p&gt;You can see the current design right &lt;a href=&#34;https://numericcitizen-test.micro.blog&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://meta.numericcitizen.me/2026/03/26/another-milestone-accomplished-i-now.html</link>
      <pubDate>Thu, 26 Mar 2026 21:46:15 -0400</pubDate>
      
      <guid>http://numericcitizen-meta.micro.blog/2026/03/26/another-milestone-accomplished-i-now.html</guid>
      <description>&lt;p&gt;Another milestone accomplished: I now have a basic custom theme installed on my Micro.blog test website. It&amp;rsquo;s not a plugin per se; it&amp;rsquo;s a custom theme stored on my GitHub account that Micro.blog fetches. I don&amp;rsquo;t yet know how to turn it into a plugin with version numbering and all that, but I might not need that after all. I just wish Micro.blog would update automatically whenever I push a new change to my repo, like Vercel does.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://meta.numericcitizen.me/2026/03/24/continuing-my-thinking-about-a.html</link>
      <pubDate>Tue, 24 Mar 2026 22:01:13 -0400</pubDate>
      
      <guid>http://numericcitizen-meta.micro.blog/2026/03/24/continuing-my-thinking-about-a.html</guid>
      <description>&lt;p&gt;Continuing &lt;a href=&#34;https://meta.numericcitizen.me/2026/03/23/vibe-coding-a-custombuilt-microblog.html&#34;&gt;my thinking about a custom-built Micro.blog theme plugin&lt;/a&gt;, I wondered why most of Micro.blog themes design card-based? Here&amp;rsquo;s Claude AI&amp;rsquo;s answer:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Cards became the dominant Micro.blog theme style largely by following a broader web design trend from the mid-2010s, when Pinterest and Material Design made cards ubiquitous. Theme authors adopted them because they handle variable-length content safely and look great in screenshots. The fork-from-fork nature of many Micro.blog themes then perpetuated the pattern. The irony is that cards aren&amp;rsquo;t actually the best fit for Micro.blog&amp;rsquo;s typical stream of short title-less posts — making it a real opportunity for anyone wanting a distinctive, better-suited design.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Besides the fact that Micro.blog launched in 2018, the rest of Claude&amp;rsquo;s response makes me think that my design choice of a left sidebar, similar to Micro, is a good one.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Vibe Coding a Custom-Built Micro.blog Theme Plugin</title>
      <link>https://meta.numericcitizen.me/2026/03/23/vibe-coding-a-custombuilt-microblog.html</link>
      <pubDate>Mon, 23 Mar 2026 12:34:00 -0400</pubDate>
      
      <guid>http://numericcitizen-meta.micro.blog/2026/03/23/vibe-coding-a-custombuilt-microblog.html</guid>
      <description>&lt;p&gt;I started working a my next vibe coding project: &lt;strong&gt;building my own Micro.blog theme plugin&lt;/strong&gt;. I wanted to have one even before vibe coding was a thing, but I didn’t have the experience with Hugo and Micro.blog theme creation. With Claude Code, having that knowledge is secondary to the task, but having a clear mind about the desired final product is paramount. I started gathering specifications that will end up in some form in the specs.md file that I’ll feed to Claude Code. I’m also gathering design tidbits that reflect what I like. Ultimately, all my Micro.blog sites will share most of the design elements. Thanks the goal.&lt;/p&gt;
&lt;p&gt;I plan to provide more updates about this little project, here on &lt;a href=&#34;https://meta.numericcitizen.me&#34;&gt;my meta blog&lt;/a&gt;. I’m doing all my work in Craft, a great tool for such project. I might decide to share my document with you dear readers if you show sign of curiosity.&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>