<rss version="2.0">
  <channel>
    <title>enumerator.dev</title>
    <link>https://enumerator.dev/</link>
    <description></description>
    
    <language>en-us</language>
    
    <lastBuildDate>Wed, 26 Nov 2025 03:08:00 +0000</lastBuildDate>
    <item>
      <title>Post</title>
      <link>https://enumerator.dev/post/</link>
      <pubDate>Wed, 26 Nov 2025 03:08:00 +0000</pubDate>
      
      <guid>https://enumerator.dev/post/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Safe Claude Code Settings</title>
      <link>https://enumerator.dev/safe-claude-code-settings/</link>
      <pubDate>Wed, 26 Nov 2025 03:08:00 +0000</pubDate>
      
      <guid>https://enumerator.dev/safe-claude-code-settings/</guid>
      <description>&lt;p&gt;Here are a few commands I have in my Claude Code deny-list to prevent bad things from happening. I wish Claude shipped with these by default!&lt;/p&gt;
&lt;p&gt;The following commands disallow Claude from force pushing and skipping pre-commit hooks.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s2&#34;&gt;&amp;#34;permissions&amp;#34;&lt;/span&gt;: &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s2&#34;&gt;&amp;#34;deny&amp;#34;&lt;/span&gt;: &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;Bash(git push -f)&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;Bash(git push --force)&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;Bash(git commit:*-n:*)&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s2&#34;&gt;&amp;#34;Bash(git commit:*--no-verify:*)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To be extra careful, I also have a &lt;a href=&#34;https://github.com/cassiascheffer/dotfiles/blob/main/.gitignore&#34;&gt;pretty thorough gitignore&lt;/a&gt; that I install globally which includes common patterns for secret credentials.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*.key
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*.pem
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*.p12
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*.pfx
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*.cer
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*.crt
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;**/secrets/**
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;**/credentials/**
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;.env
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;.env.*
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;**/config/secrets.toml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>Planning, Building, Reviewing: Why The JJ Workflow Works Better Than Git</title>
      <link>https://enumerator.dev/planning-building-reviewing-why-the-jj-workflow-works-better-than-git/</link>
      <pubDate>Sat, 08 Nov 2025 14:04:00 +0000</pubDate>
      
      <guid>https://enumerator.dev/planning-building-reviewing-why-the-jj-workflow-works-better-than-git/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://enumerator.dev/images/planning-building-reviewing-why-the-jj-workflow-works-better-than-git.jpg&#34; alt=&#34;Photo by Lucas Kepner on Unsplash&#34;&gt;&lt;/p&gt;
&lt;p&gt;After a week of experimenting with using &lt;a href=&#34;https://enumerator.dev/use-git-to-plan-and-build-with-claude&#34;&gt;git&lt;/a&gt; and &lt;a href=&#34;https://enumerator.dev/use-jujutsu-to-plan-and-build-with-claude&#34;&gt;jj&lt;/a&gt; to plan and build, I&amp;rsquo;ve landed on &lt;code&gt;jj&lt;/code&gt; as the winner.&lt;/p&gt;
&lt;h2 id=&#34;rebasing-kills-the-git-workflow&#34;&gt;Rebasing Kills the Git Workflow&lt;/h2&gt;
&lt;p&gt;Automatic rebases are the winning feature here. When I tell Claude to update a change, it will automatically rebase all its descendants. This is a MASSIVE time saver for both me and Claude.&lt;/p&gt;
&lt;p&gt;When I use empty commits in git, Claude performs the same until it has to start editing code. Then things get messy.&lt;/p&gt;
&lt;p&gt;Rebasing in &lt;code&gt;git&lt;/code&gt; is almost always painful. Claude can clunk through the rebase workflow faster than I can, but it still gets lost at times.&lt;/p&gt;
&lt;h2 id=&#34;my-current-workflow&#34;&gt;My Current Workflow&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/cassiascheffer/dotfiles/blob/700f901c3cfedc2ae05866c33cc0a99aabed237b/claude/CLAUDE.md&#34;&gt;Here is the CLAUDE.md that&amp;rsquo;s been working well for me lately&lt;/a&gt;. It teaches Claude how to use the &lt;code&gt;jj&lt;/code&gt; workflow.&lt;/p&gt;
&lt;h3 id=&#34;1-start-plan-mode&#34;&gt;1. Start plan mode&lt;/h3&gt;
&lt;p&gt;I start a new session in plan mode and give Claude Code all the information I can think of. I describe what I need, reference files, and explain the outcome.&lt;/p&gt;
&lt;h3 id=&#34;2-iterate-on-the-plan&#34;&gt;2. Iterate on the Plan&lt;/h3&gt;
&lt;p&gt;I almost always refine the plan with Claude. I verify the code samples it provides and verify that the implementation meets my expectations.&lt;/p&gt;
&lt;h3 id=&#34;3-turn-the-plan-into-commits&#34;&gt;3. Turn the plan into commits&lt;/h3&gt;
&lt;p&gt;I&amp;rsquo;m still in plan mode, and I ask Claude to break the plan down into empty &lt;code&gt;jj&lt;/code&gt; changes.&lt;/p&gt;
&lt;h3 id=&#34;4-review-the-jj-plan&#34;&gt;4. Review the &lt;code&gt;jj&lt;/code&gt; Plan&lt;/h3&gt;
&lt;p&gt;This is key. I am still in plan mode here, and I need to ensure the steps Claude has planned make sense and have no gotchas. Iterate on this step.&lt;/p&gt;
&lt;h3 id=&#34;5-auto-accept-edits&#34;&gt;5. &lt;strong&gt;Auto-accept edits&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Finally, I let Claude rip. Once the plan looks good, I auto-accept edits and allow Claude to use &lt;code&gt;jj&lt;/code&gt; commands as needed.&lt;/p&gt;
&lt;h3 id=&#34;6-do-something-else&#34;&gt;6. Do Something Else&lt;/h3&gt;
&lt;p&gt;While I refined this workflow, I watched Claude work to keep it on track and assess whether we had come up with a good enough plan. But I&amp;rsquo;ve found that with a focused plan, Claude can work away just fine.&lt;/p&gt;
&lt;h3 id=&#34;7-code-review-with-claude&#34;&gt;7. Code review with Claude&lt;/h3&gt;
&lt;p&gt;I use a &lt;a href=&#34;https://github.com/cassiascheffer/dotfiles/blob/7478e2b382376be7972840e3043882073adfad33/claude/commands/code-review.md&#34;&gt;code-review command&lt;/a&gt; to kick off code review with Claude. We walk through one change at a time. I sometimes make manual edits, other times I get Claude to refactor things.&lt;/p&gt;
&lt;h3 id=&#34;8-use-gh-to-create-prs&#34;&gt;8. Use &lt;code&gt;gh&lt;/code&gt; to create PRs&lt;/h3&gt;
&lt;p&gt;Depending on the scope of the change, I&amp;rsquo;ll make a PR stack and ask Claude to manage the stack for me, or I&amp;rsquo;ll create a single PR with a linear history.&lt;/p&gt;
&lt;h3 id=&#34;9-code-review-again&#34;&gt;9. Code Review Again&lt;/h3&gt;
&lt;p&gt;Yes, I do code review again. The code is mine, after all. I go through the GitHub UI and review the code myself. If I want Claude to make a change, I ask it to edit the &lt;code&gt;jj&lt;/code&gt; change set where the code was introduced.&lt;/p&gt;
&lt;p&gt;The result is a clean commit history that implements a feature one change set at a time.&lt;/p&gt;
&lt;h2 id=&#34;why-jj-works-so-well&#34;&gt;Why JJ Works So Well&lt;/h2&gt;
&lt;p&gt;In steps 7 and 9 above, I review code and often want to make changes to specific change sets. In &lt;code&gt;git&lt;/code&gt;, this would involve &lt;code&gt;git rebase -i&lt;/code&gt;, picking the commits to edit, making the edits, then &lt;code&gt;git rebase --continue&lt;/code&gt;. In &lt;code&gt;jj&lt;/code&gt;, all that is done for you.&lt;/p&gt;
&lt;h2 id=&#34;two-takeaways&#34;&gt;Two Takeaways&lt;/h2&gt;
&lt;p&gt;Agent or not. These two things are true in software development. &lt;strong&gt;Planning and reviewing are the most critical steps.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&#34;plan-plan-plan-again&#34;&gt;Plan, Plan, Plan Again&lt;/h3&gt;
&lt;p&gt;Working with an AI agent emphasizes the importance of planning. Planning your work has always been essential for successful feature development. Now with agents, we have really fast researchers who can help us refine a plan.&lt;/p&gt;
&lt;p&gt;The more time you put into planning, the more successful your work will be, regardless of whether you&amp;rsquo;re using an agent.&lt;/p&gt;
&lt;h3 id=&#34;review-review-review-again&#34;&gt;Review, Review, Review Again&lt;/h3&gt;
&lt;p&gt;Like planning, reviewing your work ensures it is accurate, bug-free, and performant. This is YOUR work after all. Even if an agent wrote it, you own the code.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Use Git to Plan and Build With Claude</title>
      <link>https://enumerator.dev/use-git-to-plan-and-build-with-claude/</link>
      <pubDate>Sat, 01 Nov 2025 12:57:00 +0000</pubDate>
      
      <guid>https://enumerator.dev/use-git-to-plan-and-build-with-claude/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://enumerator.dev/images/use-git-to-plan-and-build-with-claude.png&#34; alt=&#34;Git-Icon-1788C.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;When I shared &lt;a href=&#34;https://enumerator.dev/use-jujutsu-to-plan-and-build-with-claude&#34;&gt;my new JJ and Claude workflow last week&lt;/a&gt;, I wondered whether JJ was too much friction for developers. It requires learning a new version-control tool in addition to learning to use Claude. I had an idea in the back of my mind to use plain git for this.&lt;/p&gt;
&lt;p&gt;And then Claude did it for me!&lt;/p&gt;
&lt;p&gt;Towards the end of the week, I was working in a codebase where I hadn&amp;rsquo;t initialized JJ yet. Claude tried to use the JJ workflow, saw that JJ wasn&amp;rsquo;t initialized, and started implementing the same workflow using plain old git. We were working on a relatively simple feature, and Claude created planning commits and implemented it.&lt;/p&gt;
&lt;h2 id=&#34;you-dont-need-to-learn-a-new-tool&#34;&gt;You Don&amp;rsquo;t Need to Learn a New Tool&lt;/h2&gt;
&lt;p&gt;JJ is a good workflow if you need a graph of related features. I find this happens most often when I&amp;rsquo;m building a new feature or working in an old codebase where I find bugs.&lt;/p&gt;
&lt;p&gt;Most day-to-day work can be done in linear branches. Plus, everyone knows git already.&lt;/p&gt;
&lt;p&gt;This morning, I worked with Claude to &lt;a href=&#34;https://github.com/cassiascheffer/dotfiles/blob/3d068ff7413f1b114b035f04f4a194d90cf86656/claude/CLAUDE.md&#34;&gt;refine a CLAUDE.md that uses plain old git to plan and build features&lt;/a&gt;. This looks really promising so far.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Why is Claude Code Different from Cursor if they Both Use Claude?</title>
      <link>https://enumerator.dev/why-is-claude-code-different-from-cursor-if-they-both-use-claude/</link>
      <pubDate>Mon, 27 Oct 2025 13:30:00 +0000</pubDate>
      
      <guid>https://enumerator.dev/why-is-claude-code-different-from-cursor-if-they-both-use-claude/</guid>
      <description>&lt;p&gt;I get this question often. Here&amp;rsquo;s how the story goes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Someone complains that AI is bad because it just does a bunch of stuff for you really quickly and does it wrong.&lt;/li&gt;
&lt;li&gt;I ask what tools they&amp;rsquo;re using, and they say they&amp;rsquo;re using Cursor because it&amp;rsquo;s the most familiar IDE for them.&lt;/li&gt;
&lt;li&gt;I ask them if they start a new chat for each feature, and the answer is usually &amp;ldquo;no&amp;rdquo; because this isn&amp;rsquo;t intuitive.&lt;/li&gt;
&lt;li&gt;I suggest they try a different agent — Claude Code is my preferred tool — and they say, &amp;ldquo;It&amp;rsquo;s all Claude, though, how would that be different?&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Few developers have had time to learn these tools. They&amp;rsquo;ve been forced to use them as productivity enhancers, without the time to learn.&lt;/p&gt;
&lt;p&gt;I have seen performance gains in my work. I shipped the first version of &lt;a href=&#34;https://upliftapp.online&#34;&gt;Uplift&lt;/a&gt; in a few hours! But I&amp;rsquo;ve also taken an enormous amount of time learning, training others, and trying terrible AI tools.&lt;/p&gt;
&lt;p&gt;I work with many developers who use Cursor daily. This is good! Cursor seems to work better with their workflow. While I have a distaste for Cursor, others find it useful. Agents, like IDEs, are a preference, after all.&lt;/p&gt;
&lt;p&gt;Going faster means slowing down to learn your tools. Changing IDEs or using a CLI is a BIG workflow change for most people, and if Cursor is your first brush with AI, I do not blame you for thinking it&amp;rsquo;s a waste of time.&lt;/p&gt;
&lt;p&gt;In this post, I want to demystify &amp;ldquo;It&amp;rsquo;s all Claude in the end&amp;rdquo; to help people understand why some agents are good and others are frustrating.&lt;/p&gt;
&lt;h2 id=&#34;defining-terms&#34;&gt;Defining Terms&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s start with defining three standard terms. I&amp;rsquo;m going to use the common nomenclature in this post, but it&amp;rsquo;s important to note that their meanings are often blended in different contexts.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;AI&lt;/strong&gt;: These two small letters carry the weight of &amp;ldquo;agents&amp;rdquo;, &amp;ldquo;LLMs&amp;rdquo;, &amp;ldquo;automation&amp;rdquo;, &amp;ldquo;autocomplete&amp;rdquo;, and everything else. When someone says &amp;ldquo;AI,&amp;rdquo; they mean any one of these. In this post, &amp;ldquo;AI&amp;rdquo; refers to coding agents that have some autonomy in their work.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Agent&lt;/strong&gt;: This term comes up in phrases like &amp;ldquo;agentic workflow&amp;rdquo; or &amp;ldquo;coding agent&amp;rdquo;. The agent is the engine of AI coding. The agent connects the user&amp;rsquo;s input to the local environment&amp;rsquo;s context (files, available tools) and provides it to the LLM. In this post, &amp;ldquo;agent&amp;rdquo; is a loop that takes user input, provides context to the LLM, and calls tools.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LLM&lt;/strong&gt;: We all know LLMs like Claude, ChatGPT, or Codex. &amp;ldquo;LLM&amp;rdquo; stands for &amp;ldquo;large language model&amp;rdquo;. &amp;ldquo;LLM&amp;rdquo; is a misnomer. Many of these models accept multiple types of input and produce various outputs. A more accurate name is &amp;ldquo;Large Multimodal Model&amp;rdquo;. The models we use for coding are autoregressive, meaning they predict the following sequence based on previous context. For consistency with vernacular usage, I&amp;rsquo;ll refer to them as LLMs.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;ai-distinguishing-the-agent-from-the-llm&#34;&gt;AI: Distinguishing the Agent from the LLM&lt;/h2&gt;
&lt;p&gt;In the diagram below, the &amp;ldquo;environment&amp;rdquo; is your computer and the tools you let your agent use. If you&amp;rsquo;ve given your agent access to &lt;code&gt;find&lt;/code&gt;, for example, it will show up in the list. Depending on the agent, it might also collect some project stats to send to the LLM.&lt;/p&gt;
&lt;p&gt;When you have a &lt;code&gt;CLAUDE.md&lt;/code&gt; or &lt;code&gt;AGENTS.md&lt;/code&gt; file, this will get sent with the context, too.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-mermaid&#34;&gt;sequenceDiagram
    participant U as User
    participant AS as Agent
    participant E as Environment
    participant LLM as LLM (API)
    
    U-&amp;gt;&amp;gt;AS: &amp;#34;Fix bug in auth.js&amp;#34;
    
    box rgba(0,0,0,0.05) Your Computer
      participant AS as Agent
      participant E as Environment
    end
    
    Note over AS,E: Agent Orchestration Layer&amp;lt;br/&amp;gt;Manages loop, provides tools, maintains context
    loop Until task complete
        AS-&amp;gt;&amp;gt;LLM: Context &amp;#43; Available Tools &amp;#43; User Request
        Note over LLM: Parse intent&amp;lt;br/&amp;gt;Reason about next step&amp;lt;br/&amp;gt;Choose tool to call
        LLM-&amp;gt;&amp;gt;AS: Tool call decision
        AS-&amp;gt;&amp;gt;E: Execute tool (view/edit/bash)
        E-&amp;gt;&amp;gt;AS: Return result
        AS-&amp;gt;&amp;gt;AS: Append result to context
    end
    
    AS-&amp;gt;&amp;gt;U: &amp;#34;Fixed: added null check&amp;lt;br/&amp;gt;Tests passing ✓&amp;#34;
    
    Note over U,E: Different agents = different tools, autonomy levels,&amp;lt;br/&amp;gt;and orchestration strategies (even with same LLM)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice that most of the work actually happens on your own machine. The agent interacts with your environment to collect information to send to the LLM, makes edits, and reports back.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;An agent is not intelligent.&lt;/strong&gt; This is really important to understand. An agent without an LLM is a loop with conditionals. Much like the code we write every day.&lt;/p&gt;
&lt;p&gt;Adding an LLM into the loop gives the agent the ability to reason and make decisions beyond pattern matching.&lt;/p&gt;
&lt;p&gt;Every agentic coding company will write its agent differently. The tools available, the actions the agent takes, and the system prompts it sends to the LLM are the product the company builds.&lt;/p&gt;
&lt;p&gt;Cursor is different from Claude Code because Cursor&amp;rsquo;s agent is different, even if they both use a Claude model to reason and make decisions.&lt;/p&gt;
&lt;h2 id=&#34;the-llm-reads-the-entire-conversation-every-time&#34;&gt;The LLM Reads the Entire Conversation Every Time&lt;/h2&gt;
&lt;p&gt;The LLM is an outside actor. If you are using Cursor or Claude code, every message you send and every agent loop involves API calls to the LLM provider you are using. If you are running a model locally, the model is separate from your agent system.&lt;/p&gt;
&lt;p&gt;Every time the agent calls the LLM API, it is the &lt;em&gt;first&lt;/em&gt; time the LLM has ever seen your message. The LLM reads &lt;strong&gt;THE WHOLE MESSAGE THREAD EVERY TIME&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s right. Do you have a long-running conversation where you&amp;rsquo;ve worked on three or four different tasks? When you ask the agent a question, the LLM reads the whole message thread and can easily confuse instructions you previously gave it with your current task.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In my experience, this is the most common reason agentic coding starts as highly accurate and quickly degrades into chaos.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;How do you fix this? Start a new chat. It&amp;rsquo;s that simple. Start a new chat for every task you work on.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.warp.dev/&#34;&gt;Warp&lt;/a&gt; has a great feature in its agent that detects a change in subject and suggests starting a new chat. I wish other agents would do that too.&lt;/p&gt;
&lt;p&gt;This is an important distinction to make. The agent&amp;rsquo;s capabilities and system prompt affect how it works. Cursor tends to be &lt;em&gt;very&lt;/em&gt; ambitious, which means the context window can quickly become bloated with failed attempts and misdirection.&lt;/p&gt;
&lt;p&gt;More cautious agents will confirm actions with the user and detect when the conversation is drifting, keeping the context focused on the task at hand.&lt;/p&gt;
&lt;p&gt;No agent is perfect, which is why I&amp;rsquo;ve explored using the &lt;a href=&#34;https://enumerator.dev/use-jujutsu-to-plan-and-build-with-claude&#34;&gt;Jujutsu VCS to give the agent memory between sessions and narrow the context the LLM receives on each turn&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-mermaid&#34;&gt;sequenceDiagram
    participant CC as Agent
    participant LLM as LLM (API)
    
    box rgba(0,0,0,0.05) Your Computer

    end
    
    Note over CC: Context:&amp;lt;br/&amp;gt;[System prompt]
    
    CC-&amp;gt;&amp;gt;LLM: [System prompt, User: &amp;#34;Fix bug&amp;#34;]
    LLM-&amp;gt;&amp;gt;CC: &amp;#34;I&amp;#39;ll check the file&amp;#34;
    Note over CC: Context:&amp;lt;br/&amp;gt;[System, User,&amp;lt;br/&amp;gt;Assistant]
    
    CC-&amp;gt;&amp;gt;LLM: [System, User, Assistant, Tool: file contents]
    LLM-&amp;gt;&amp;gt;CC: &amp;#34;I&amp;#39;ll edit line 42&amp;#34;
    Note over CC: Context:&amp;lt;br/&amp;gt;[System, User,&amp;lt;br/&amp;gt;Asst, Tool,&amp;lt;br/&amp;gt;Asst]
    
    CC-&amp;gt;&amp;gt;LLM: [System, User, Asst, Tool, Asst, Tool: edit result]
    LLM-&amp;gt;&amp;gt;CC: &amp;#34;Fixed! Tests passing&amp;#34;
    Note over CC: Context:&amp;lt;br/&amp;gt;[System, User,&amp;lt;br/&amp;gt;Asst, Tool,&amp;lt;br/&amp;gt;Asst, Tool,&amp;lt;br/&amp;gt;Asst]
    
    Note over CC,LLM: Each API call sends the ENTIRE context&amp;lt;br/&amp;gt;Context grows with every message&amp;lt;br/&amp;gt;and tool result.&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;key-principles&#34;&gt;Key Principles&lt;/h2&gt;
&lt;p&gt;The diagram below illustrates the capabilities of a coding agent. The Agent Loop is the central capability. This is the part that each company builds (Claude Code, Cursor, Zed, Warp, etc.)&lt;/p&gt;
&lt;p&gt;The LLM is separate from the agent loop. Many different agents can use the same LLM, and most agents let you pick which LLM to interact with.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-mermaid&#34;&gt;flowchart LR    
    AgentLoop[Agent Loop] --&amp;gt; LLM[Calls LLM]
    
    AgentLoop --&amp;gt; AL1[Stateful Orchestration]
    AgentLoop --&amp;gt; AL2[Maintains state and history]
    AgentLoop --&amp;gt; AL3[Executes tools]
    AgentLoop --&amp;gt; AL4[Manages context window]
    
    LLM --&amp;gt; L1[Stateless Reasoning]
    LLM --&amp;gt; L2[Processes full context each turn]
    LLM --&amp;gt; L3[Selects tools &amp;amp; plans actions]
    LLM --&amp;gt; L4[No memory between calls]&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Try lots of agents! I know this can feel slow and frustrating, but when you do this, you are learning how different agents work. You are learning the new tools you can use in your job.&lt;/p&gt;
&lt;p&gt;Take 15 minutes each day to build a small project with an agent. Pick something simple, like a to-do list app. Build the same app with different agents and see how they perform.&lt;/p&gt;
&lt;p&gt;In the future, I think we will standardize on a handful of coding agents that developers can pick to work with their preferred workflow, much as we have standardized on a handful of IDEs for different use cases.&lt;/p&gt;
&lt;p&gt;I prefer Claude Code for its built-in protections and customization options.&lt;/p&gt;
&lt;p&gt;If you want an agent integrated in your IDE, you can run &lt;code&gt;/ide&lt;/code&gt; to hook Claude Code up to VSCode. It&amp;rsquo;s not as deeply integrated as Cursor is, but it works okay. Another good option is to try out &lt;a href=&#34;https://zed.dev/&#34;&gt;Zed&lt;/a&gt;, their agent has similar capabilities to Claude Code and is built into the IDE.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve tried Warp a few times, and the deep integration of chat, CLI, and editing is promising to me, but sometimes confusing.&lt;/p&gt;
&lt;p&gt;In the end, Vim is my home row, so the setup that works for me is Claude Code in one pane and Vim in the other.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Use Jujutsu to Plan and Build with Claude</title>
      <link>https://enumerator.dev/use-jujutsu-to-plan-and-build-with-claude/</link>
      <pubDate>Sun, 26 Oct 2025 12:58:00 +0000</pubDate>
      
      <guid>https://enumerator.dev/use-jujutsu-to-plan-and-build-with-claude/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://enumerator.dev/images/use-jujutsu-to-plan-and-build-with-claude.png&#34; alt=&#34;claude-jj-logo.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;This week, I read about &lt;a href=&#34;https://steve-yegge.medium.com/introducing-beads-a-coding-agent-memory-system-637d7d92514a&#34;&gt;Steve Yegge&amp;rsquo;s Beads&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I was skeptical.&lt;/p&gt;
&lt;p&gt;He writes like someone trying to prompt inject your LLM. But he&amp;rsquo;s always written that way.&lt;/p&gt;
&lt;p&gt;I tried beads, and it was pretty good. I wrote all of &lt;a href=&#34;https://enumerator.dev/uplift&#34;&gt;uplift&lt;/a&gt; using beads.&lt;/p&gt;
&lt;p&gt;But it was also pretty buggy. I noticed I had multiple &lt;code&gt;bd&lt;/code&gt; daemons running and checking git status. Not cool.&lt;/p&gt;
&lt;h2 id=&#34;jujutsu&#34;&gt;Jujutsu&lt;/h2&gt;
&lt;p&gt;I shared beads with &lt;a href=&#34;https://theinternate.com/&#34;&gt;Nate Smith&lt;/a&gt;, and he said, &amp;ldquo;You could do that with Jujutsu.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;We both tried it and it rocks.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/cassiascheffer/dotfiles/blob/436760e954d41458214bc632661fa71af11816a7/claude/CLAUDE.md&#34;&gt;Here is my current CLAUDE.md &lt;/a&gt;, which teaches Claude to use Jujutsu to plan and track issue progress.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/jj-vcs/jj&#34;&gt;Jujutsu&lt;/a&gt; is a Git-compatible version control system. The graph foundation of Jujutsu makes it perfect for this kind of work because Claude can add empty TODO commits along the way and connect them to one or more tasks that also need to be done.&lt;/p&gt;
&lt;p&gt;Here is the gist of the instructions Claude gets:&lt;/p&gt;
&lt;blockquote&gt;
&lt;h2 id=&#34;planning-best-practices&#34;&gt;Planning Best Practices&lt;/h2&gt;
&lt;p&gt;Create Descriptive Empty Commits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Each commit description should fully explain what needs to be done&lt;/li&gt;
&lt;li&gt;Include acceptance criteria in the description&lt;/li&gt;
&lt;li&gt;Note any dependencies or prerequisites&lt;/li&gt;
&lt;li&gt;Use clear, actionable language&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;The steps for you are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Enter plan mode and build a plan with Claude.&lt;/li&gt;
&lt;li&gt;When you&amp;rsquo;re ready, tell Claude to make empty commits for each step in the plan.&lt;/li&gt;
&lt;li&gt;Tell Claude to work through one commit at a time until it is done.&lt;/li&gt;
&lt;li&gt;If at any point Claude struggles or decides to compact memory. Kill your session and start a new one with &amp;ldquo;We are in the middle of working through a to-do list created in &lt;code&gt;jj&lt;/code&gt;. Find the task you were in the middle of and finish it, then move on to the next task until you are done.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;On Friday, I refactored a small Ruby library with Claude and Jujutsu, and it was really nice. I knew what I wanted the end state to look like and described that to Claude. Claude then made a plan with detailed acceptance criteria for 12 distinct steps. Once I approved, Claude turned the plan into &lt;code&gt;jj&lt;/code&gt; commits and worked through them.&lt;/p&gt;
&lt;p&gt;Around step 7, Claude slowed down and suggested that steps 7 through 12 were tightly coupled. I ran &lt;code&gt;/clear&lt;/code&gt; to start a new session and pick up where we left off. Claude still saw that steps 7 through 12 were coupled, but no longer tried to implement them all at once. It methodically worked through the steps.&lt;/p&gt;
&lt;p&gt;At the end, Claude suggested a five-PR stack so developers could review PRs implementing the requirement in separate logical steps.&lt;/p&gt;
&lt;h2 id=&#34;why-this-works-my-theory&#34;&gt;Why This Works (My Theory)&lt;/h2&gt;
&lt;p&gt;Steve Yegge is right about why this works, but more than a little off base about needing a whole buggy go program to do it.&lt;/p&gt;
&lt;p&gt;Claude works better with this workflow because the context you send is focused and clear. A big old markdown doc is fine, but Claude has to read the whole thing multiple times to find the next task.&lt;/p&gt;
&lt;p&gt;With the Jujutsu workflow, Claude can read a summary of what is done with &lt;code&gt;jj log&lt;/code&gt; and pick up the following task with &lt;code&gt;jj edit&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The LLM on the other side of the Claude agent receives a more focused context. As the conversation continues, the thread narrows to specific tasks.&lt;/p&gt;
&lt;p&gt;In past work, when I&amp;rsquo;ve used a &lt;code&gt;plan.md&lt;/code&gt; or Claude&amp;rsquo;s default todo list, Claude struggles because it reads &lt;em&gt;the entire plan every time, multiple times&lt;/em&gt;, since the whole chat is sent to the LLM with each message. This means the context gets bloated with planning and loses focus on accomplishing tasks.&lt;/p&gt;
&lt;p&gt;With the Jujutsu workflow, planning shows up exactly once in the context. Then &lt;code&gt;jj log&lt;/code&gt; shows incremental progress.&lt;/p&gt;
&lt;h2 id=&#34;i-want-this&#34;&gt;I Want This&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s good! You should try it. Get Jujutsu set up and copy my CLAUDE.md.&lt;/p&gt;
&lt;p&gt;If you don&amp;rsquo;t know how to use Jujutsu, Claude can learn by using &lt;code&gt;jj help&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The most important thing to remember is: if you think the agent is lost, you&amp;rsquo;re probably lost too!&lt;/p&gt;
&lt;p&gt;You can &lt;code&gt;/clear&lt;/code&gt; your session, take a break, and start fresh. Claude will pick up where it left off because it knows to use &lt;code&gt;jj log&lt;/code&gt; to find the next task.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>🎈 Uplift</title>
      <link>https://enumerator.dev/uplift/</link>
      <pubDate>Mon, 20 Oct 2025 18:59:00 +0000</pubDate>
      
      <guid>https://enumerator.dev/uplift/</guid>
      <description>&lt;p&gt;I recently read &lt;a href=&#34;https://mxstbr.com/notes/gratitude-circles&#34;&gt;Max Stoiber&lt;/a&gt;&amp;rsquo;s post on Gratitude Circles and was inspired to build an app for it! Why an app? First, you can&amp;rsquo;t see handwriting. I am super embarrassed by my clumsy handwriting. Typing though, thank gosh for auto-correct.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://upliftapp.online/&#34;&gt;🎈 Try Uplift!&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Uplift is an app for running Gratitude Circles. It keeps author names anonymous to emphasize that the whole group lifts one another up. Give it a whirl and let me know what you think.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Navigating to Get Things Done</title>
      <link>https://enumerator.dev/navigating-to-get-things-done/</link>
      <pubDate>Tue, 07 Oct 2025 11:55:00 +0000</pubDate>
      
      <guid>https://enumerator.dev/navigating-to-get-things-done/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://enumerator.dev/images/navigating-to-get-things-done.jpg&#34; alt=&#34;Photo by Ali Kazal on Unsplash&#34;&gt;&lt;/p&gt;
&lt;p&gt;In engineering culture, we sometimes refer to managers and Staff Engineers as &amp;ldquo;shit umbrellas&amp;rdquo; for their team. I&amp;rsquo;ve never liked this idea. It paints a picture that individual leaders create a peaceful oasis for their team while chaos reigns outside.&lt;/p&gt;
&lt;p&gt;Sounds nice to be one of those engineers! Not a care in the world. Just flow. It must feel special to have a shit umbrella manager.&lt;/p&gt;
&lt;p&gt;But the shit umbrella is a lie.&lt;/p&gt;
&lt;p&gt;If every manager believes they create a sphere of deep focus and flow, where is all the shit coming from? One team&amp;rsquo;s flow is another team&amp;rsquo;s dysfunction.&lt;/p&gt;
&lt;p&gt;The &amp;ldquo;shit umbrella&amp;rdquo; metaphor entrenches an &amp;ldquo;us versus them&amp;rdquo; culture and isolates individual teams from the rest of the company. It pits leaders against lowly engineers and ensures communication starts with arguments rather than curiosity.&lt;/p&gt;
&lt;h2 id=&#34;be-a-navigator&#34;&gt;Be a Navigator&lt;/h2&gt;
&lt;p&gt;Effective engineers engage across an organization and know how to navigate its complexities. Effective engineers recognize that people are complex, have diverse emotions, make mistakes, and struggle to communicate effectively. And that&amp;rsquo;s okay because we are all human.&lt;/p&gt;
&lt;p&gt;Teams need to understand how an organization operates to be effective engineers, and people leaders are responsible for helping teams focus, navigate complexities, and achieve their goals.&lt;/p&gt;
&lt;p&gt;Being a navigator for a team means helping them find their way and letting them do the work to reach their destination by helping the team understand who is who in the organization and what these individuals care about. Working with the complexities in an org is about curiosity and learning.&lt;/p&gt;
&lt;p&gt;Helping your team navigate an organization begins with trust and focus: trust that your team can handle what comes their way and that the people they work with understand their part of the organization better than you do.&lt;/p&gt;
&lt;p&gt;In &lt;a href=&#34;https://terriblesoftware.org/2025/10/01/stop-avoiding-politics/&#34;&gt;Matheus Lima&amp;rsquo;s recent post on politics&lt;/a&gt;, he says:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The engineers who refuse to engage with politics often complain that their companies make bad technical decisions. But they’re not willing to do what it takes to influence those decisions. They want a world where technical merit alone determines outcomes. That world doesn’t exist and never has.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The same goes for managers who choose to be shit umbrellas. Protecting your team from politics isolates them from the business and creates an insular culture where &amp;ldquo;everything could be better if&amp;hellip;&amp;rdquo; while not improving anything.&lt;/p&gt;
&lt;h2 id=&#34;protecting-your-team-protects-dysfunction&#34;&gt;Protecting Your Team Protects Dysfunction&lt;/h2&gt;
&lt;p&gt;When people leaders try to protect their team from organizational chaos, they are unintentionally ensuring the dysfunction stays in place.&lt;/p&gt;
&lt;p&gt;The purported &amp;ldquo;shit&amp;rdquo; that a shit umbrella protects people from is just people being human. To quote &lt;a href=&#34;https://lethain.com/extract-the-kernel/&#34;&gt;Will Larson&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[T]he reality is that executives are human. You’ll make much more progress by focusing on improving how you communicate with them than by blaming them for their deficiencies.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Larson&amp;rsquo;s comments apply to anyone you work with in your org. Improving your communication skills ensures that you approach conversations with a clear understanding of the core business needs.&lt;/p&gt;
&lt;p&gt;Blaming others for their deficiencies ensures organizational dysfunction is the norm and prevents the business from growing.&lt;/p&gt;
&lt;p&gt;People at all levels of an organization need to make mistakes and learn from them. The shit-umbrella culture breaks this feedback cycle. It allows new leaders to fail without receiving feedback and support from their peers, isolates senior leaders from the teams they need to work with, and isolates your team from the business.&lt;/p&gt;
&lt;h2 id=&#34;focus&#34;&gt;Focus&lt;/h2&gt;
&lt;p&gt;People who talk about being a shit umbrella want to maintain their team&amp;rsquo;s focus and limit distractions. It comes from a well-meaning place, but creates a culture of isolation instead of focus.&lt;/p&gt;
&lt;p&gt;As a navigator, show your team what to focus on and how to reach their destination. Trust them to raise concerns when things get hard, and trust your peers on other teams to support their work.&lt;/p&gt;
&lt;p&gt;Provide your team with a map and help them to focus on the business impact of their work, so that they can approach complex parts of the org with curiosity and an ambition to build great things for the business.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>willow.camp Updates - October 7, 2025</title>
      <link>https://enumerator.dev/willow-camp-updates-october-7-2025/</link>
      <pubDate>Tue, 07 Oct 2025 11:25:05 +0000</pubDate>
      
      <guid>https://enumerator.dev/willow-camp-updates-october-7-2025/</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve spent most of my willow.camp work on finding a new backend language. But I&amp;rsquo;ve still updated the Rails app! Here&amp;rsquo;s what&amp;rsquo;s changed since my last update.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Moved HTML storage out of the &lt;code&gt;post&lt;/code&gt; table and into &lt;code&gt;ActionText&lt;/code&gt;. It was this deep dive that made me question using Rails. I went down a long rabbit hole, finding a good text editor and then realized this work felt more like building Ikea furniture than I wanted. I&amp;rsquo;m more interested in having access to the application end-to-end than plugging parts together. Anyway, in the meantime, your posts are now stored in an ActionText table.&lt;/li&gt;
&lt;li&gt;Prevented email enumeration attacks by returning an ambiguous error on sign-up. This is my pet peeve with Devise, and I am a bit embarrassed I let this live in production for so long. Previously, the sign-up form would indicate if an email address had already been taken. Since I can&amp;rsquo;t guarantee people use unique passwords, this means an attacker could use a leaked email/password list to access accounts that use poor password hygiene. No more! I&amp;rsquo;ve done my best to give ambiguous, helpful messages when something is wrong on the signup page.&lt;/li&gt;
&lt;li&gt;Updated dependencies! A whole bunch of Ruby Gems and npm packages got minor version bumps.&lt;/li&gt;
&lt;li&gt;Fixed CI errors with vips. This is minor but tests were failing in CI because vips wasn&amp;rsquo;t installed on the machine.&lt;/li&gt;
&lt;li&gt;Cleaned up the Hanami directory. There was a day when I was convinced Hanami was the next framework for willow.camp, and I started a migration in its own directory.&lt;/li&gt;
&lt;li&gt;Cleaned up image things I was trying out with images and media handling.&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>willow.camp: Next</title>
      <link>https://enumerator.dev/willow-camp-next/</link>
      <pubDate>Tue, 07 Oct 2025 01:26:24 +0000</pubDate>
      
      <guid>https://enumerator.dev/willow-camp-next/</guid>
      <description>&lt;p&gt;I have spent the past few weeks figuring out the future of willow.camp.&lt;/p&gt;
&lt;p&gt;It turns out I love making it, and I love that a handful of people are using it.&lt;/p&gt;
&lt;p&gt;I built willow.camp in Rails because I could prototype quickly without having to think about things like auth, logging, or building forms. Rails was great for that.&lt;/p&gt;
&lt;p&gt;Now that I have a version of willow.camp I like, I want to be more hands-on with what I&amp;rsquo;m building, and I find that I&amp;rsquo;ve spent more time working around the framework and various libraries than I have creating the things I want.&lt;/p&gt;
&lt;p&gt;Learning other people&amp;rsquo;s code isn&amp;rsquo;t particularly enjoyable.&lt;/p&gt;
&lt;p&gt;Aside: Rails and Ruby have also had a public meltdown lately, and I&amp;rsquo;m tired of feeling like a puny developer watching giants fight and say evil things.&lt;/p&gt;
&lt;h2 id=&#34;finding-the-next-tech-stack&#34;&gt;Finding the Next Tech Stack&lt;/h2&gt;
&lt;p&gt;This was a long journey.&lt;/p&gt;
&lt;p&gt;I started with &lt;a href=&#34;https://hanamirb.org/&#34;&gt;Hanami&lt;/a&gt;. It is still Ruby, and the community is much friendlier. Hanami didn&amp;rsquo;t work for me. It felt like I kept bumping up against the framework, telling me I was doing it wrong. The community is friendly, but the framework is very rigid about &amp;ldquo;the right way&amp;rdquo; of doing things.&lt;/p&gt;
&lt;p&gt;Then I hopped over to &lt;a href=&#34;https://crystal-lang.org/&#34;&gt;Crystal&lt;/a&gt; and tried &lt;a href=&#34;https://luckyframework.org/&#34;&gt;Lucky&lt;/a&gt;, &lt;a href=&#34;https://amberframework.org/&#34;&gt;Amber&lt;/a&gt;, and &lt;a href=&#34;https://martenframework.com/&#34;&gt;Marten&lt;/a&gt;. They were all okay to work with, but didn&amp;rsquo;t strike my fancy.&lt;/p&gt;
&lt;p&gt;Lucky was the easiest to get going. I had the data models for willow.camp up and running with a couple of rough-looking views in a few hours.&lt;/p&gt;
&lt;p&gt;Marten&amp;rsquo;s &amp;ldquo;apps&amp;rdquo; are nice to work with (think slices in Hanami). But the framework doesn&amp;rsquo;t natively support subdomain routing, and I didn&amp;rsquo;t want to fight that design.&lt;/p&gt;
&lt;p&gt;Crystal is also very slow to compile. A straightforward Lucky app took over 30 seconds on my M1 to compile! Who has that kind of time when you need to compile multiple times an hour?&lt;/p&gt;
&lt;p&gt;Finally, the community. The Lucky devs are really active, which is nice. But Crystal itself seems to have a fragmented community. When I interacted with people, I didn&amp;rsquo;t really see someone like me in any of the channels.&lt;/p&gt;
&lt;h3 id=&#34;what-i-want-in-a-language-and-framework&#34;&gt;What I Want in A Language and Framework&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;An active, welcoming community that builds trust.&lt;/li&gt;
&lt;li&gt;Helpful error messages from the language.&lt;/li&gt;
&lt;li&gt;Good documentation.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That&amp;rsquo;s about it. I don&amp;rsquo;t need huge framework features. I need a language with a helpful design and friendly people.&lt;/p&gt;
&lt;h2 id=&#34;the-next-willowcamp-tech-stack&#34;&gt;The Next willow.camp Tech Stack&lt;/h2&gt;
&lt;p&gt;Once I realized that I valued community and friendly docs more than I valued framework features, I found &lt;a href=&#34;https://gleam.run&#34;&gt;Gleam&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Backend:
&lt;ul&gt;
&lt;li&gt;Gleam with the &lt;a href=&#34;https://github.com/gleam-wisp/wisp&#34;&gt;Wisp Framework&lt;/a&gt; on a Postgres database.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Frontend:
&lt;ul&gt;
&lt;li&gt;So far, I plan to use &lt;a href=&#34;https://hexdocs.pm/nakai/index.html&#34;&gt;Nakai&lt;/a&gt; for server-side rendered HTML and HTMX or a similar library for interactive components.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Oh, and I&amp;rsquo;ve already written a Gleam package! willow.camp needs an accurate domain name parser, so I wrote &lt;a href=&#34;https://github.com/cassiascheffer/psl&#34;&gt;psl&lt;/a&gt; to parse domains using the &lt;a href=&#34;https://publicsuffix.org/&#34;&gt;Public Suffix List&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;the-next-willowcamp-features&#34;&gt;The Next willow.camp Features&lt;/h2&gt;
&lt;p&gt;I have a long list of features I want to build for willow.camp, and it&amp;rsquo;s these features that inspired me to change the language and framework.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A WYSYIG editor that supports markdown. This is the best of both worlds and opens willow.camp up to non-technical writers.&lt;/li&gt;
&lt;li&gt;An image library via &lt;a href=&#34;https://openverse.org/&#34;&gt;openverse&lt;/a&gt; with an image editor UI.&lt;/li&gt;
&lt;li&gt;Email newsletters! I&amp;rsquo;m excited about this one. You will be able to collect a list of subscribers and send blog posts to your distribution list.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://atproto.com/&#34;&gt;ATProto&lt;/a&gt; and &lt;a href=&#34;https://www.w3.org/TR/activitypub/&#34;&gt;ActivityPub&lt;/a&gt; support.&lt;/li&gt;
&lt;li&gt;An RSS reader so you can follow blogs without a social profile.&lt;/li&gt;
&lt;li&gt;Private bookmarks with notes and highlights.&lt;/li&gt;
&lt;/ol&gt;
</description>
    </item>
    
    <item>
      <title>willow.camp Updates September 21, 2025</title>
      <link>https://enumerator.dev/willow-camp-updates-september-21-2025/</link>
      <pubDate>Sun, 21 Sep 2025 12:58:00 +0000</pubDate>
      
      <guid>https://enumerator.dev/willow-camp-updates-september-21-2025/</guid>
      <description>&lt;p&gt;Quiet on the updates this week. I could rename &amp;ldquo;Major Features&amp;rdquo; to &amp;ldquo;Major Worries&amp;rdquo; because I&amp;rsquo;ve spent more time worrying about how images should work than testing them with real users!&lt;/p&gt;
&lt;p&gt;I enabled image uploads on the &lt;a href=&#34;github.com/avo-hq/marksmith&#34;&gt;marksmith&lt;/a&gt; text editor this week, and a few people tested it out for me. Turns out marksmith doesn&amp;rsquo;t work how I thought it would with images. Image uploads are still enabled, but I&amp;rsquo;m going back to the drawing board with the editor UI and media management.&lt;/p&gt;
&lt;h2 id=&#34;major-features&#34;&gt;Major Features&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Image storage and processing functionality with Active Storage integration, direct uploads, and automatic image processing&lt;/li&gt;
&lt;li&gt;I heard you like willow.camp. How about deleting willow.camp? You can now delete your blog and account if you&amp;rsquo;d like. This will delete all your data and is irreversible. The option is there if you need it.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;improvements&#34;&gt;Improvements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Enhanced navigation to only show blog title or subdomain in menu&lt;/li&gt;
&lt;li&gt;Updated testing configuration for quieter pre-commit output&lt;/li&gt;
&lt;li&gt;Removed social_share_image functionality&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;security--dependencies&#34;&gt;Security &amp;amp; Dependencies&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Updated JavaScript dependencies&lt;/li&gt;
&lt;li&gt;Updated Ruby gem dependencies&lt;/li&gt;
&lt;li&gt;Added new license file&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>How Much does it Cost to Run willow.camp?</title>
      <link>https://enumerator.dev/how-much-does-it-cost-to-run-willow-camp/</link>
      <pubDate>Sun, 21 Sep 2025 12:21:00 +0000</pubDate>
      
      <guid>https://enumerator.dev/how-much-does-it-cost-to-run-willow-camp/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://enumerator.dev/images/how-much-does-it-cost-to-run-willow-camp.jpg&#34; alt=&#34;Photo by StellrWeb on Unsplash&#34;&gt;&lt;/p&gt;
&lt;p&gt;I recently enabled image uploads in the marksmith text editor on willow.camp. I&amp;rsquo;ve been hesitant to add media management to willow.camp for two reasons: it is hard to get the user experience right, media management adds extra cost.&lt;/p&gt;
&lt;p&gt;I am being cost-conscious on willow.camp because it doesn&amp;rsquo;t make any money. This is a passion project, and I&amp;rsquo;m having lots of fun doing it. But the more fun I have, the more it might cost.&lt;/p&gt;
&lt;p&gt;It turns out adding storage is &lt;em&gt;way cheaper&lt;/em&gt; than I thought. I would have to have a whole lot of media and a few really busy willow.camp sites to start getting charged more for data transfer fees.&lt;/p&gt;
&lt;p&gt;I will eventually build a pricing plan for willow.camp to cover costs for active sites. In the meantime, if you want to support willow.camp, you can send me a tip on &lt;a href=&#34;https://ko-fi.com/cassiascheffer&#34;&gt;https://ko-fi.com/cassiascheffer&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;claude-code&#34;&gt;Claude Code&lt;/h2&gt;
&lt;p&gt;I include Claude Code in here because it is part of my development process. I&amp;rsquo;m on the Max plan, and I use it enough that it pays off.&lt;/p&gt;
&lt;p&gt;In the past 30 days, I&amp;rsquo;ve used CAD $188.49 of tokens, and I even took a week off in the woods.&lt;/p&gt;
&lt;p&gt;I use Claude Code to scaffold ideas, experiment with features, and improve test coverage. I&amp;rsquo;ve been able to build willow.camp so quickly because Claude Code is pretty good at scaffolding a blog in Rails.&lt;/p&gt;
&lt;h2 id=&#34;august-breakdown&#34;&gt;August Breakdown&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;service&lt;/th&gt;
          &lt;th&gt;fee (CAD)&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;DigitalOcean Droplet (1 vcpu, 1 gb memory)&lt;/td&gt;
          &lt;td&gt;$8.25&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;DigitalOcean Postgres ( 1gb ram, 10 gb disk)&lt;/td&gt;
          &lt;td&gt;$20.82&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Hatchbox.io Deployments&lt;/td&gt;
          &lt;td&gt;$14.13&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Claude Code Max&lt;/td&gt;
          &lt;td&gt;$158.2&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Honeybadger Error Tracking&lt;/td&gt;
          &lt;td&gt;$0&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Total&lt;/td&gt;
          &lt;td&gt;$201.40&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Total without Claude Code&lt;/td&gt;
          &lt;td&gt;$43.20&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;september-estimate&#34;&gt;September Estimate&lt;/h2&gt;
&lt;p&gt;Here is my estimate for September based on today&amp;rsquo;s exchange rate.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;service&lt;/th&gt;
          &lt;th&gt;fee (CAD)&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;DigitalOcean Droplet (1 vCPU, 1 GB memory)&lt;/td&gt;
          &lt;td&gt;$8.26&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;DigitalOcean Postgres ( 1 GB RAM, 10 GB disk)&lt;/td&gt;
          &lt;td&gt;$20.87&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;DigitalOcean Spaces (half a month)&lt;/td&gt;
          &lt;td&gt;$3.44&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Hatchbox.io Deployments&lt;/td&gt;
          &lt;td&gt;$14.13&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Claude Code Max&lt;/td&gt;
          &lt;td&gt;$192.84&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Honeybadger Error Tracking&lt;/td&gt;
          &lt;td&gt;$0&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Total&lt;/td&gt;
          &lt;td&gt;$239.54&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Total without Claude Code&lt;/td&gt;
          &lt;td&gt;$46.70&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://unsplash.com/@stellrweb?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash&#34;&gt;&lt;a href=&#34;https://unsplash.com/@stellrweb?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&#34;&gt;StellrWeb&lt;/a&gt;&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/photos/white-canon-cash-register-djb1whucfBY?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash&#34;&gt;&lt;a href=&#34;https://unsplash.com/photos/white-canon-cash-register-djb1whucfBY?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash&#34;&gt;Unsplash&lt;/a&gt;&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Naming Things for What They Do</title>
      <link>https://enumerator.dev/naming-things-for-what-they-do/</link>
      <pubDate>Tue, 09 Sep 2025 11:23:33 +0000</pubDate>
      
      <guid>https://enumerator.dev/naming-things-for-what-they-do/</guid>
      <description>&lt;p&gt;I just saw the Rails World announcement that flavorjones is working on &lt;a href=&#34;https://github.com/basecamp/activerecord-tenanted&#34;&gt;ActiveRecord::Tenanted&lt;/a&gt; and all I can say is, “What a reasonable name!”&lt;/p&gt;
&lt;p&gt;When I started willow.camp I chose not to use &lt;a href=&#34;https://github.com/influitive/apartment&#34;&gt;apartment&lt;/a&gt; partially because the name and the metaphor were too confusing. I didn&amp;rsquo;t like tho have to get used to the idea of requests taking &amp;ldquo;elevators&amp;rdquo; and all that. The &amp;ldquo;apartment&amp;rdquo; metaphor doesn&amp;rsquo;t work for databases even though &amp;ldquo;tenant&amp;rdquo; does.&lt;/p&gt;
&lt;p&gt;Always choose descriptive names over clever ones. It doesn’t matter how well you think the metaphor fits; you’ll always end up with something confusing in the end.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>willow.camp Updates September 7 2025</title>
      <link>https://enumerator.dev/willow-camp-updates-september-7-2025/</link>
      <pubDate>Mon, 08 Sep 2025 00:20:35 +0000</pubDate>
      
      <guid>https://enumerator.dev/willow-camp-updates-september-7-2025/</guid>
      <description>&lt;p&gt;It&amp;rsquo;s been a busy month! I took some time off for a much-needed vacation and forgot to post about willow.camp. I&amp;rsquo;ve worked on some fun features in the past few weeks.&lt;/p&gt;
&lt;h2 id=&#34;new-features&#34;&gt;New Features&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Favicons now use &lt;a href=&#34;https://openmoji.org/&#34;&gt;OpenMoji&lt;/a&gt; emojis! I think these are adorable icons. I have a few qualms with how they fit in the willow.camp design (they&amp;rsquo;re all a little bottom-heavy), but it&amp;rsquo;s not a deal breaker.&lt;/li&gt;
&lt;li&gt;New favicon picker! You can now search for an emoji by name to pick for your blog.&lt;/li&gt;
&lt;li&gt;Now you can have two blogs! You can click on the profile icon and add a second blog. I&amp;rsquo;m going to use this to document my garden and remind myself what I want to do next year.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I also fiddled around with auto-generating OG images for each blog. I wrote a script that uses your OpenMoji favicon and your blog&amp;rsquo;s colours to create an Open Graph image for your blog.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s what the tiled image looks like for enumerator.dev.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://enumerator.dev/tiled_1F3D5_1200x630.png&#34; alt=&#34;willow.camp tile&#34;&gt;&lt;/p&gt;
&lt;p&gt;Ultimately, the best approach for images is to enable users to upload them instead of doing some clever auto-generation. I&amp;rsquo;m going to put this on my project board and figure out how to do it. Since storage costs me, I might need to change the license on willow.camp and start charging for image uploads.&lt;/p&gt;
&lt;p&gt;Here is the Claude-generated summary of the last few weeks.&lt;/p&gt;
&lt;h2 id=&#34;summary---august-17---september-7-2025&#34;&gt;Summary - August 17 - September 7, 2025&lt;/h2&gt;
&lt;h3 id=&#34;major-features&#34;&gt;Major Features&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Multi-Blog Support&lt;/strong&gt;: Complete implementation of &amp;ldquo;Two Blogs per User&amp;rdquo; feature (&lt;a href=&#34;https://github.com/cassiascheffer/willow_camp/commit/a33fdaa9eec42e4f9e046653886963ce2a986dcc&#34;&gt;#85&lt;/a&gt;) - Major architectural change allowing users to have multiple blogs instead of a single-tenant structure&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OpenMoji Favicon System&lt;/strong&gt;: Implemented comprehensive emoji favicon selector with search functionality using OpenMoji library (&lt;a href=&#34;https://github.com/cassiascheffer/willow_camp/commit/2d13fd2e318339b77cf0068a0dd0b945896e44bd&#34;&gt;2d13fd2e&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;improvements&#34;&gt;Improvements&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;OG Image Generation&lt;/strong&gt;: Added automated Open Graph image generation scripts (&lt;a href=&#34;https://github.com/cassiascheffer/willow_camp/commit/29ba2359fdd5b761d0cf995fbc3f8153e24a5c3f&#34;&gt;29ba2359&lt;/a&gt;, &lt;a href=&#34;https://github.com/cassiascheffer/willow_camp/commit/290f237256be0ff7f1ab7ddc100cbb82eef751d5&#34;&gt;290f2372&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>willow.camp Updates August 16, 2025</title>
      <link>https://enumerator.dev/willow-camp-updates-august-16-2025/</link>
      <pubDate>Sat, 16 Aug 2025 12:32:00 +0000</pubDate>
      
      <guid>https://enumerator.dev/willow-camp-updates-august-16-2025/</guid>
      <description>&lt;p&gt;It felt like a quiet week on willow.camp but somehow I still got lots done.&lt;/p&gt;
&lt;p&gt;Aside from the release notes below, I&amp;rsquo;m focusing my energy on getting proper favicons for willow.camp blogs.&lt;/p&gt;
&lt;p&gt;My first version of this will use &lt;a href=&#34;https://openmoji.org/&#34;&gt;OpenMoji&lt;/a&gt; emojis for favicons. I like their simple design principles and especially like that they are CC-BY-SA 4.0, similar to willow.camp&amp;rsquo;s source code.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s how I think I&amp;rsquo;ll approach this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Include OpenMoji assets in the willow.camp source code.&lt;/li&gt;
&lt;li&gt;Use their &lt;a href=&#34;https://github.com/hfg-gmuend/openmoji/blob/master/data/openmoji.json&#34;&gt;source map&lt;/a&gt; to populate a choices.js dropdown on the settings page.&lt;/li&gt;
&lt;li&gt;Then save the Unicode reference to the database and use that to fetch the correct favicon files when rendering a page.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;OpenMoji provides pngs in two sizes, so I&amp;rsquo;ll need to resize them for the best cross-device compatibility.&lt;/p&gt;
&lt;p&gt;Okay, on to this week&amp;rsquo;s summary!&lt;/p&gt;
&lt;h2 id=&#34;improvements&#34;&gt;Improvements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Migrated system tests from Selenium to Cuprite for better performance and reliability&lt;/li&gt;
&lt;li&gt;Optimized &lt;code&gt;ReservedWords&lt;/code&gt; with &lt;code&gt;Set&lt;/code&gt; data structure for faster lookups&lt;/li&gt;
&lt;li&gt;Added post-deploy script to send deploy messages to Discord&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;security--dependencies&#34;&gt;Security &amp;amp; Dependencies&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Updated Rails from 8.0.2 to 8.0.2.1&lt;/li&gt;
&lt;li&gt;Updated ActiveRecord from 8.0.2 to 8.0.2.1&lt;/li&gt;
&lt;li&gt;Updated Pagy from 9.3.5 to 9.4.0&lt;/li&gt;
&lt;li&gt;Updated solid_cable from 3.0.11 to 3.0.12&lt;/li&gt;
&lt;li&gt;Updated Honeybadger from 6.0.4 to 6.0.5&lt;/li&gt;
&lt;li&gt;Updated Jbuilder from 2.13.0 to 2.14.1&lt;/li&gt;
&lt;li&gt;Updated CommonMarker from 2.3.1 to 2.3.2&lt;/li&gt;
&lt;li&gt;Updated Lefthook from 1.12.2 to 1.12.3&lt;/li&gt;
&lt;li&gt;Updated GitHub Actions dependencies (actions/checkout v4→v5, browser-actions/setup-chrome v1→v2)&lt;/li&gt;
&lt;li&gt;Implemented subdomain validation and bot protection in &lt;code&gt;Rack::Attack&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Added user agent and IP logging to Honeybadger error context to better understand bot behaviour&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;bug-fixes&#34;&gt;Bug Fixes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Fixed deprecated &lt;code&gt;:unprocessable_entity&lt;/code&gt; status code (replaced with &lt;code&gt;:unprocessable_content&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Temporarily disabled auto-save functionality. This isn&amp;rsquo;t a bug fix. Sorry! I want to get to fixing this soon.&lt;/li&gt;
&lt;li&gt;Remove social_share_image JavaScript and HTML. I am going to sit on this a bit. I want this to be rendered server-side instead of in the browser. Once I integrate OpenMoji, this will be much easier because I&amp;rsquo;ll have png assets available on the server.&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>End To End Tests with Minitest and Rails</title>
      <link>https://enumerator.dev/end-to-end-tests-with-minitest-and-rails/</link>
      <pubDate>Wed, 13 Aug 2025 22:27:26 +0000</pubDate>
      
      <guid>https://enumerator.dev/end-to-end-tests-with-minitest-and-rails/</guid>
      <description>&lt;p&gt;willow.camp has a small system test suite that runs on CI. It used to use the out-of-the-box settings that come with Rails: Capybara, Selenium, etc.&lt;/p&gt;
&lt;p&gt;I noticed a flaky test on CI, which launched a journey to replace Selenium with Cuprite.&lt;/p&gt;
&lt;h2 id=&#34;fixing-the-flaky-test&#34;&gt;Fixing the Flaky Test&lt;/h2&gt;
&lt;p&gt;I worked through the flaky test with Claude Code, and as Claude was running the tests, I noticed this pop-up from Chrome.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A data breach on a site or app exposed your password&amp;hellip;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I don&amp;rsquo;t use Chrome very often, so this jumped out at me. Of course, I use the oh-so-secret password &amp;ldquo;password&amp;rdquo; in my test suite, and Chrome flagged it as unsafe.&lt;/p&gt;
&lt;p&gt;Disabling password leak detection fixed this for me.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;browser_options: { &amp;#34;disable-features&amp;#34;: &amp;#34;PasswordLeakDetection&amp;#34; }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But I still got timeouts on CI that looked like the tests were running before the browser was ready.&lt;/p&gt;
&lt;p&gt;Waiting for CI is a bit painful. You can see my hilariously bad Git history with Claude&amp;rsquo;s gleeful overconfidence:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;* 0c514d1 - nosandbox (15 minutes ago) &amp;lt;Cassia Scheffer&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;* 57ac55b - bump timeout to 30 sec for ci (21 minutes ago)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;* 9d6e86e - rewirite cuprite/capybara configs (26 minutes ago)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;* 39f3238 - fix: simplify Cuprite configuration following best practices (10 hours ago)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;* 68bfd53 - fix: simplify Cuprite configuration and fix CI timeout issues (24 hours ago)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;* cebfae7 - fix: resolve Cuprite websocket timeout issues in CI (24 hours ago)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;* dc288b7 - fix: make Cuprite configuration more robust for CI environments (24 hours ago)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;* e5bb4df - fix: increase Cuprite timeout and add CI-specific settings for GitHub Acti&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;* cb36eaa - feat: migrate system tests from Selenium to Cuprite (35 hours ago)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;* 39f0ff1 - disable selenium chrome password detection (35 hours ago)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;that-looks-bad-cassia-why-would-you-let-claude-do-that&#34;&gt;That Looks Bad, Cassia. Why Would You Let Claude Do That!?&lt;/h3&gt;
&lt;p&gt;Because Claude is faster at making mistakes than I am, and I am tired of waiting for CI. Making mistakes quickly means speedier feedback. So I sent Claude off on a fool&amp;rsquo;s errand to help me learn from the mistakes I would otherwise have made myself.&lt;/p&gt;
&lt;p&gt;In the meantime, I read the &lt;a href=&#34;https://evilmartians.com/chronicles/system-of-a-test-setting-up-end-to-end-rails-testing#dockerizing-system-tests&#34;&gt;Evil Martians article&lt;/a&gt; about better system tests.&lt;/p&gt;
&lt;h2 id=&#34;using-cuprite-in-rails-with-minitest&#34;&gt;Using Cuprite in Rails with Minitest&lt;/h2&gt;
&lt;p&gt;The Evil Martians article on system tests uses RSpec, but willow.camp uses Minitest. Here&amp;rsquo;s what I had to change to make it work with Minitest.&lt;/p&gt;
&lt;h3 id=&#34;1-precompileassets-becomes-a-module-and-gets-called-when-setup-runs&#34;&gt;1. PrecompileAssets becomes a module and gets called when setup runs.&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-rb&#34; data-lang=&#34;rb&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Precompile assets before running tests to avoid timeouts.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Do not precompile if webpack-dev-server is running (NOTE: MUST be launched with RAILS_ENV=test)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;PrecompileAssets&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;setup&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# Check if we&amp;#39;re running system tests by looking at the test files being loaded&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;running_system_tests&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;caller&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;any?&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;line&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;line&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;include?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;test/system&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;no&#34;&gt;ARGV&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;any?&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;arg&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;include?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;test/system&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;no&#34;&gt;ENV&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;RAILS_TEST_TYPE&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;system&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;unless&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;running_system_tests&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nb&#34;&gt;puts&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;🚀️️  No system test selected. Skip assets compilation.&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;puts&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;🐢  Precompiling assets.&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;original_stdout&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;vg&#34;&gt;$stdout&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;clone&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;start&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;Time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;current&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;begin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;vg&#34;&gt;$stdout&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;reopen&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;no&#34;&gt;File&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;no&#34;&gt;File&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;no&#34;&gt;Constants&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;no&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;w&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nb&#34;&gt;require&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;rake&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;no&#34;&gt;Rails&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;application&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;load_tasks&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;no&#34;&gt;Rake&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;no&#34;&gt;Task&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;assets:precompile&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;].&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;invoke&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;ensure&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;vg&#34;&gt;$stdout&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;reopen&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;original_stdout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nb&#34;&gt;puts&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Finished in &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;no&#34;&gt;Time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;current&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;round&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; seconds&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Run the setup when this file is loaded&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;no&#34;&gt;PrecompileAssets&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;setup&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;defined?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;no&#34;&gt;Rails&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;2-betterrailssystemtests-becomes-a-moduel&#34;&gt;2. BetterRailsSystemTests Becomes a Moduel&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-rb&#34; data-lang=&#34;rb&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;BetterRailsSystemTests&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c1&#34;&gt;# Make failure screenshots compatible with multi-session setup.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;take_screenshot&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;super&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;unless&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;Capybara&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;last_used_session&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;no&#34;&gt;Capybara&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;using_session&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;no&#34;&gt;Capybara&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;last_used_session&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;super&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;3-modules-are-included-in-applicationsystemtestcase&#34;&gt;3. Modules are included in ApplicationSystemTestCase&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-rb&#34; data-lang=&#34;rb&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;require&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;test_helper&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;require&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;capybara/cuprite&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;require_relative&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;system/system_helper&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;ApplicationSystemTestCase&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;ActionDispatch&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;no&#34;&gt;SystemTestCase&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kp&#34;&gt;include&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;BetterRailsSystemTests&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kp&#34;&gt;include&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;CupriteHelpers&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;driven_by&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;Capybara&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;javascript_driver&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;setup&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;super&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# Use JS driver always&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# Store original host for cleanup&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;vi&#34;&gt;@original_host&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;Rails&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;application&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;default_url_options&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:host&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# Make urls in mailers contain the correct server host.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# This is required for testing links in emails (e.g., via capybara-email).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;no&#34;&gt;Rails&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;application&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;default_url_options&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:host&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;Capybara&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;server_host&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;teardown&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# Restore original host&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;no&#34;&gt;Rails&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;application&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;default_url_options&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:host&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;vi&#34;&gt;@original_host&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;super&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;flakiness-still-included-on-ci&#34;&gt;Flakiness Still Included on CI&lt;/h2&gt;
&lt;p&gt;I still see a bit of system test flakiness on CI, but far less than before. I am running on the default GitHub Actions runner, which is pretty small. So there&amp;rsquo;s a good chance that the flakiness is due to low resources on the machine. So far, tests have consistently passed when I run these on my M1 MacBook Air.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll dig into these flaky tests another day! But this looks like an improvement overall to me!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>willow.camp Release Notes August 9, 2025</title>
      <link>https://enumerator.dev/willow-camp-release-notes-august-9-2025/</link>
      <pubDate>Sat, 09 Aug 2025 20:51:00 +0000</pubDate>
      
      <guid>https://enumerator.dev/willow-camp-release-notes-august-9-2025/</guid>
      <description>&lt;p&gt;Really quiet week for willow.camp this week.&lt;/p&gt;
&lt;p&gt;I updated a few dependencies and upgraded the server it runs on. Everything&amp;rsquo;s been running smoothly!&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve also been working with an LLM to learn &lt;a href=&#34;https://hanamirb.org/&#34;&gt;Hanami&lt;/a&gt; and might eventually move the backend over to Hanami. I&amp;rsquo;d lose a lot of &amp;ldquo;batteries included&amp;rdquo; stuff switching to Hanami, but I like the philosophy they have.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s been nice working on something the LLM doesn&amp;rsquo;t know well. It often drifts off into Rails land, and I have to learn the right pattern to correct the LLM. I think I learn faster this way because it&amp;rsquo;s easier to be skeptical of the LLM than it is of my own mistakes.&lt;/p&gt;
&lt;p&gt;Please have a look at my first pass at the migration at the &lt;a href=&#34;https://github.com/cassiascheffer/willow_camp_2&#34;&gt;willow_camp_2&lt;/a&gt; repo.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>willow.camp Release Notes Week Ending August 2, 2025</title>
      <link>https://enumerator.dev/willow-camp-release-notes-week-ending-august-2-2025/</link>
      <pubDate>Sat, 02 Aug 2025 12:43:00 +0000</pubDate>
      
      <guid>https://enumerator.dev/willow-camp-release-notes-week-ending-august-2-2025/</guid>
      <description>&lt;p&gt;It was a quiet week for willow.camp. The biggest thing I did was to test out generating Open Graph images using a browser canvas. I also improved logging for Honeybadger Insights, which will help me find performance issues.&lt;/p&gt;
&lt;h2 id=&#34;beta-features&#34;&gt;Beta Features&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Added a beta feature for auto-generating Open Graph images for RSS feeds and social media. I&amp;rsquo;m still figuring out how I want this to work before releasing it for general use.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;improvements&#34;&gt;Improvements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Added in-memory caching to the blog index page.&lt;/li&gt;
&lt;li&gt;Site map generation performance optimizations and test coverage.&lt;/li&gt;
&lt;li&gt;Added a few more Rack::Attack configurations for noisy bots.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;infrastructure--monitoring&#34;&gt;Infrastructure &amp;amp; Monitoring&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Logging Refactor to work better with Honeybadger Insights.&lt;/li&gt;
&lt;li&gt;Marksmith Editor: Fixed scroll position issues during autosave.&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Three Memory Leak Patterns</title>
      <link>https://enumerator.dev/three-memory-leak-patterns/</link>
      <pubDate>Thu, 31 Jul 2025 19:51:51 +0000</pubDate>
      
      <guid>https://enumerator.dev/three-memory-leak-patterns/</guid>
      <description>&lt;h2 id=&#34;normal-memory-usage&#34;&gt;Normal Memory Usage&lt;/h2&gt;
&lt;p&gt;In a typical application, memory consumption should increase when the application boots and then stabilize. This pattern will repeat every time the application is deployed.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Memory
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  ^
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  |        ███████████████████████        ████████████████████
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  |  █████████████████████████████  ██████████████████████████
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  |  █████████████████████████████████████████████████████████ &amp;gt; Time
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    Boot         Stable Runtime      Deploy        Stable Runtime
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;gradual-leak&#34;&gt;Gradual Leak&lt;/h2&gt;
&lt;p&gt;In an application with a gradual memory leak, the memory usage will increase sporadically. This is usually due to specific code paths that are only executed sporadically, causing memory leaks. This pattern looks like a very gradual increase in memory over time that never plateaus and gets reset every time an application is deployed. This is a challenging memory leak to identify because frequent deploys can mask it.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Memory
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  ^
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  |                        ███████████                 ████████████
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  |                      █████████████              ███████████████
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  |                 ██████████████████           ██████████████████
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  |     ██████████████████████████████  ███████████████████████████
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  |     ██████████████████████████████  ███████████████████████████
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  | ███████████████████████████████████████████████████████████████ &amp;gt; Time
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    Boot    Gradual Memory Growth      Deploy    Gradual Memory Growth
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;spiky-memory-leak&#34;&gt;Spiky Memory Leak&lt;/h2&gt;
&lt;p&gt;A memory leak that causes a dramatic spike in memory will often cause the application to churn, meaning it boots up, memory increases continually, it hits the limit, it runs out of memory, and reboots. This happens over and over and over. These are very easy to identify memory leaks, although the source may be challenging to locate.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Memory
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  ^
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  |            ████              ████
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  |         ███████           ███████  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  |      ██████████        ██████████
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  |   █████████████     █████████████
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  |████████████████  ████████████████
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  |██████████████████████████████████████████████ &amp;gt; Time
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    Boot |Rapid Growth | Repeat
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                     CRASH!
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>Learn AI Workflows by Stealing .claude Files</title>
      <link>https://enumerator.dev/learn-ai-workflows-by-stealing-claude-files/</link>
      <pubDate>Wed, 30 Jul 2025 14:09:20 +0000</pubDate>
      
      <guid>https://enumerator.dev/learn-ai-workflows-by-stealing-claude-files/</guid>
      <description>&lt;p&gt;When Harper Reed first published &amp;ldquo;&lt;a href=&#34;https://harper.blog/2025/02/16/my-llm-codegen-workflow-atm/&#34;&gt;My LLM codegen workflow atm&lt;/a&gt;&amp;rdquo;, I was hooked.&lt;/p&gt;
&lt;p&gt;I had been working with LLMs for a few months and Harper&amp;rsquo;s post made it all click. &amp;ldquo;THIS! This is the workflow that has been taking shape for me!&amp;rdquo; I dove in head first and now for the past few months, I&amp;rsquo;ve been AI first in all my work. I hardly type code any more. I read, refine, critique, and guide. I only get hands-on-keyboard when I have a very specific preference for how things should work.&lt;/p&gt;
&lt;h2 id=&#34;learning-takes-time&#34;&gt;Learning Takes Time&lt;/h2&gt;
&lt;p&gt;It took me a lot of time to learn this workflow. I was lucky enough to have willow.camp as  a side project so I could get into the nitty-gritty. But not everyone has that opportunity.&lt;/p&gt;
&lt;p&gt;If I had to learn this all today, here&amp;rsquo;s the approach I would take:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/cassiascheffer/dotfiles/tree/main/.claude/commands&#34;&gt;Steal my .claude files&lt;/a&gt;, which I stole from &lt;a href=&#34;https://github.com/harperreed/dotfiles/tree/master/.claude&#34;&gt;Harper&lt;/a&gt;, who borrowed from &lt;a href=&#34;https://github.com/obra/dotfiles/tree/main/.claude&#34;&gt;Jesse&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Install Claude Code&lt;/li&gt;
&lt;li&gt;Pick up a ticket and start with Claude Code. This is TRAINING WHEELS OFF! Try it out. If you need an editor on the side, hook &lt;code&gt;claude&lt;/code&gt; up to Cursor by typing &lt;code&gt;/ide&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Type &lt;code&gt;/brainstorm&lt;/code&gt; into Claude Code.&lt;/li&gt;
&lt;li&gt;Work through the brainstorming steps to generate a spec.md.&lt;/li&gt;
&lt;li&gt;Then kick off &lt;code&gt;/plan&lt;/code&gt;,  which will generate a todo.md.&lt;/li&gt;
&lt;li&gt;Finally, kick off &lt;code&gt;/do-todo&lt;/code&gt;, which will iterate through todos.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Repeat.&lt;/p&gt;
&lt;p&gt;The more you do this, the more it will become second nature. Eventually, you might not even need the &lt;code&gt;commands&lt;/code&gt; anymore because you&amp;rsquo;ll know when to plan and when to act.&lt;/p&gt;
&lt;h2 id=&#34;break-things&#34;&gt;Break Things&lt;/h2&gt;
&lt;p&gt;I learn by breaking things!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Modify your &lt;code&gt;commands&lt;/code&gt; to suit your needs. Come up with your own commands and see how they work for you.&lt;/li&gt;
&lt;li&gt;Try the same thing twice to learn more about keeping the LLM on track and focused.&lt;/li&gt;
&lt;li&gt;Try the same task without doing the planning and see what happens.&lt;/li&gt;
&lt;li&gt;Try a massively complex task with and without planning.&lt;/li&gt;
&lt;li&gt;Try a tiny, simple task with and without planning.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Breaking things is all about finding the boundaries. The best way to know what an AI agent is good at is to find out what it is BAD at!&lt;/p&gt;
&lt;p&gt;Keep it cool. If the LLM wanders off into the woods to build a tree fort when you asked it to do something else, that&amp;rsquo;s okay. We all get off track. Here are a few things I do to learn:&lt;/p&gt;
&lt;p&gt;Ask the LLM what went wrong:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;ldquo;I noticed we struggled to solve this. Analyze our conversation and give me pointers on how to find a solution sooner.&amp;rdquo; This is a good prompt because sometimes, some incorrect context or weird code misdirects the LLM.&lt;/li&gt;
&lt;li&gt;&amp;ldquo;We have struggled to find a solution. Explain to me why you thought this was the right path forward?&amp;rdquo; This is an excellent prompt for getting the LLM to reveal its thinking. I have learned from this prompt that it is &lt;strong&gt;very important&lt;/strong&gt; to &lt;a href=&#34;https://enumerator.dev/what-to-do-when-ai-makes-a-mistake&#34;&gt;correct the LLM early and often&lt;/a&gt;. If you don&amp;rsquo;t, you both might misunderstand something. Once the LLM is off track, I ask for a &lt;a href=&#34;https://github.com/cassiascheffer/dotfiles/blob/main/.claude/commands/session-summary.md&#34;&gt;session summary&lt;/a&gt;, clear the chat and start over.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;takeways&#34;&gt;Takeways&lt;/h2&gt;
&lt;p&gt;Try stuff! Try lots of stuff and try it often. Timebox your work so you don&amp;rsquo;t get stuck in a hole. Check yourself to see if the task you&amp;rsquo;re doing is valuable. Are you learning something? Are you making progress?&lt;/p&gt;
&lt;p&gt;Learning is about making mistakes. Remember to make mistakes safely and adapt to them quickly. AI workflows can accelerate your work, but you have to shift your approach first and know how the tools work.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Testing A New willow.camp Feature</title>
      <link>https://enumerator.dev/testing-a-new-willow-camp-feature/</link>
      <pubDate>Wed, 30 Jul 2025 13:12:00 +0000</pubDate>
      
      <guid>https://enumerator.dev/testing-a-new-willow-camp-feature/</guid>
      <description>&lt;p&gt;I don&amp;rsquo;t like competing for clicks. But I do like people reading my blog.&lt;/p&gt;
&lt;p&gt;If you landed here form LinkedIn, you probably saw a colorful image showing the title of this post. Where is that image?! It&amp;rsquo;s in the Open Graph tags in the &lt;code&gt;head&lt;/code&gt; of this page.&lt;/p&gt;
&lt;p&gt;When I launched willow.camp, I intentionally kept social-media style features out of it. willow.camp doesn&amp;rsquo;t track views (aside from what&amp;rsquo;s needed for performance and bug monitoring), and doesn&amp;rsquo;t have likes or comments.&lt;/p&gt;
&lt;p&gt;But it was missing something.&lt;/p&gt;
&lt;p&gt;When I shared posts to LinkedIn, my posts would show up as plain text. Despite having no analytics, I get the feeling that some people miss my posts because they are overwhelmed by a sea of images.&lt;/p&gt;
&lt;p&gt;So here I am. Writing a social media feature for willow.camp that is in beta (meaning I am the beta tester!) If you are using willow.camp, I&amp;rsquo;ll release this feature once I&amp;rsquo;ve put the right polish on it.&lt;/p&gt;
&lt;p&gt;I added a canvas to the post editing interface that grabs the theme colours, favicon, and title to create an Open Graph image that LinkedIn can see. This is the first time I have image storage.&lt;/p&gt;
&lt;p&gt;The images are stored locally on the server, so I&amp;rsquo;ll run out of space one day. But for now, I&amp;rsquo;m going to test this out and polish up the user experience before making it more broadly available.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Taste</title>
      <link>https://enumerator.dev/taste/</link>
      <pubDate>Sun, 27 Jul 2025 13:02:00 +0000</pubDate>
      
      <guid>https://enumerator.dev/taste/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;I can see really high-skill, high-taste shops like 37signals struggling to adopt [agentic coding] because their very good taste means they are repelled by the slop. Bigger companies and small startups/indies will have an easier time at it, out of necessity.
&amp;ndash; &lt;a href=&#34;https://www.linkedin.com/feed/update/urn:li:activity:7350995561115774977/&#34;&gt;Nate Berkopec on LinkedIn&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;With LLMs making leetcode and other whiteboard problems trivial, software hiring IMO should shift towards establishing taste, which LLMs absolutely do not have.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;What do you hate about ActiveRecord? What would you change about Rails if you could?&amp;rdquo; Review this PR, etc.
&amp;ndash; &lt;a href=&#34;https://www.linkedin.com/feed/update/urn:li:activity:7354194426715365376/&#34;&gt;Nate Berkopec on LinkedIn&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What is &amp;ldquo;taste&amp;rdquo;? Does your taste matter as a developer? Do your customers care about ActiveRecord? (Spoiler alert: they do not).&lt;/p&gt;
&lt;p&gt;I have been working in an exclusively agentic workflow for about six months now. I hardly write code. Does this mean I have poor taste?&lt;/p&gt;
&lt;p&gt;I remember in my early days in software development, I overheard two opinionated Senior Developers argue about white space and wondered why it mattered.&lt;/p&gt;
&lt;h2 id=&#34;taste-does-not-matter-customers-matter&#34;&gt;Taste Does Not Matter, Customers Matter&lt;/h2&gt;
&lt;p&gt;What matters at the end of the day in any business is its customers. Do they like what you&amp;rsquo;ve made? Will they tell people about it?&lt;/p&gt;
&lt;p&gt;You can&amp;rsquo;t run a business on opinions about ActiveRecord, Rails, whitespace, or any of that. You run a business by selling a product that works well and makes people happy.&lt;/p&gt;
&lt;h2 id=&#34;but-insert-thing-here-is-bad&#34;&gt;But &lt;code&gt;insert thing here&lt;/code&gt; is BAD!?&lt;/h2&gt;
&lt;p&gt;It might be! What do we mean by &amp;ldquo;bad&amp;rdquo;? What is the impact on our ability to deliver a reliable product? How does it impact our customers?&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Taste&amp;rdquo; is only important if it affects the customer. Here are the interview questions I&amp;rsquo;d use in this new world:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How does ActiveRecord affect the software development life cycle?&lt;/li&gt;
&lt;li&gt;What impact can the ORM have on the quality of your application?&lt;/li&gt;
&lt;li&gt;When do you choose to set your opinions aside and ship quickly vs weighing your opinions against real-world consequences?&lt;/li&gt;
&lt;li&gt;How do you measure the quality of good software?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The answers to questions like this will inform technical decisions and will avoid banter about taste.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;d much rather focus on building great things than getting mired in debates about ORMs.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I care about your views on building fast, reliable software that makes people happy. Let&amp;rsquo;s leave the bikeshedding to others.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>willow.camp Release Notes Week Ending July 26, 2025</title>
      <link>https://enumerator.dev/willow-camp-release-notes-week-ending-july-26-2025/</link>
      <pubDate>Sat, 26 Jul 2025 12:20:00 +0000</pubDate>
      
      <guid>https://enumerator.dev/willow-camp-release-notes-week-ending-july-26-2025/</guid>
      <description>&lt;p&gt;Here&amp;rsquo;s everything I did on willow.camp this week. It blows my mind how much I can get done when I&amp;rsquo;m having fun! I work on this app for about an hour every morning and evening.&lt;/p&gt;
&lt;h2 id=&#34;major-features-added&#34;&gt;Major Features Added&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Featured posts functionality with toggle UI&lt;/li&gt;
&lt;li&gt;Tap/click to edit a tag on the tags index page&lt;/li&gt;
&lt;li&gt;Terms of Service page&lt;/li&gt;
&lt;li&gt;Rack-attack integration for rate limiting and security (blocking PHP requests, credential probes)&lt;/li&gt;
&lt;li&gt;Added no-index option to settings page for search engine control&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;uiux-improvements&#34;&gt;UI/UX Improvements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Replaced inline SVG icons with heroicons gem&lt;/li&gt;
&lt;li&gt;Enhanced mobile navigation by moving dashboard tabs to the hamburger menu&lt;/li&gt;
&lt;li&gt;Updated tag UI with circular icon buttons&lt;/li&gt;
&lt;li&gt;Replaced default Rails error pages with custom DaisyUI designs&lt;/li&gt;
&lt;li&gt;Improved post actions UI with a dropdown menu and a modal delete confirmation&lt;/li&gt;
&lt;li&gt;Added &amp;ldquo;Save Draft&amp;rdquo; and &amp;ldquo;Publish&amp;rdquo; buttons instead of a &amp;ldquo;Published&amp;rdquo; checkbox&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;performance--testing&#34;&gt;Performance &amp;amp; Testing&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Optimized Dashboard::TokensControllerTest (1.97s → 0.31s)&lt;/li&gt;
&lt;li&gt;Optimized slow mermaid test by reducing markdown content in tests&lt;/li&gt;
&lt;li&gt;Added Bullet gem for N+1 query detection&lt;/li&gt;
&lt;li&gt;Fixed N+1 query in API posts controller&lt;/li&gt;
&lt;li&gt;Re-enabled parallel test runs&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;code-quality&#34;&gt;Code Quality&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Removed RuboCop entirely and only use StandardRB for code style&lt;/li&gt;
&lt;li&gt;Added standard-rails gem for enhanced Rails linting&lt;/li&gt;
&lt;li&gt;Updated Brakeman from 7.0.2 to 7.1.0&lt;/li&gt;
&lt;li&gt;Merged willow_camp_cli history into cli/ subdirectory&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;security--seo&#34;&gt;Security &amp;amp; SEO&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Enhanced request logging with user agent and subdomain info&lt;/li&gt;
&lt;li&gt;Added CC license to GitHub repo&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;bug-fixes&#34;&gt;Bug Fixes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Fixed favicon controller error&lt;/li&gt;
&lt;li&gt;Fixed mobile tag editing card UI updates&lt;/li&gt;
&lt;li&gt;Fixed OG published_time tag for draft posts&lt;/li&gt;
&lt;li&gt;Prevented mobile auto-capitalization for domain inputs&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>What to do When AI Makes a Mistake</title>
      <link>https://enumerator.dev/what-to-do-when-ai-makes-a-mistake/</link>
      <pubDate>Thu, 24 Jul 2025 15:31:00 +0000</pubDate>
      
      <guid>https://enumerator.dev/what-to-do-when-ai-makes-a-mistake/</guid>
      <description>&lt;p&gt;Reading Sean Goedecke&amp;rsquo;s post &amp;ldquo;&lt;a href=&#34;https://www.seangoedecke.com/do-not-yell-at-the-language-model/&#34;&gt;Do not yell at the language model&lt;/a&gt;&amp;rdquo;, my first reaction was, &amp;ldquo;Don&amp;rsquo;t&amp;hellip;what?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t yell at people. Why would I yell at a non-sentient tool? That&amp;rsquo;s like yelling at the sledgehammer you dropped on your foot.&lt;/p&gt;
&lt;p&gt;There is a good bit of truth in his post:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;So what should you do when an AI model makes a big mistake? I recommend correcting it as matter-of-factly as possible and trying to briskly move on. If you haven’t yet built up a lot of context in the conversation, it might be worth starting over entirely. The best approach - only offered by some AI tools - is to go back to the point in the conversation right before the mistake was made and head it off by updating your previous message.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Just start over! Treat mistakes as opportunities to set better guidelines. The quicker you are at catching mistakes, the more accurate the LLM will become.&lt;/p&gt;
&lt;p&gt;Here are a few things I do when working with LLMs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;One small task per chat. I know some people build up extensive context in a long chat, but if I don&amp;rsquo;t catch a mistake, the % of incorrect context can compound over time.&lt;/li&gt;
&lt;li&gt;When the LLM or I are stuck, ask it what we are stuck on and ask it to summarize possible solutions.&lt;/li&gt;
&lt;li&gt;Try one solution at a time. Sometime in the same chat. Sometimes in a new chat. Depending on how long your chat has gone on.&lt;/li&gt;
&lt;li&gt;Verify the proposed solutions and problem space by looking at the code. Sometimes you&amp;rsquo;ll catch something the LLM misunderstood. Other times, you&amp;rsquo;ll learn something you had misunderstood!&lt;/li&gt;
&lt;li&gt;Find docs to help focus the LLM&amp;rsquo;s attention.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Write Docs&lt;/em&gt;. This is so important. Once you&amp;rsquo;ve solved something, write the documentation in a &lt;code&gt;docs/&lt;/code&gt; directory and reference it in later chats for similar problems.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Agents don&amp;rsquo;t have intent. They don&amp;rsquo;t even have all the context. They can miss things. Treat the agents&amp;rsquo; work as directionally correct. Verify implementation details.&lt;/p&gt;
&lt;p&gt;To work quickly with LLMs, you need to understand the language, system, and tools you&amp;rsquo;re working with so that you can get the agent back on track when it inevitably drifts.&lt;/p&gt;
&lt;p&gt;One last note about agents making mistakes. We all make mistakes! The best part of working with an LLM is that it can make mistakes quickly, and you can redirect those mistakes. The faster the LLM makes mistakes, the quicker you can refine the task and complete the work.&lt;/p&gt;
&lt;p&gt;Anticipating errors is as much a part of working with humans as it is working with AI.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Use Precomputed Hashes Instead of BCrypt in Test</title>
      <link>https://enumerator.dev/use-precomputed-hashes-instead-of-bcrypt-in-test/</link>
      <pubDate>Mon, 21 Jul 2025 00:55:00 +0000</pubDate>
      
      <guid>https://enumerator.dev/use-precomputed-hashes-instead-of-bcrypt-in-test/</guid>
      <description>&lt;p&gt;I asked Claude to find the slowest test file again after my success &lt;a href=&#34;https://enumerator.dev/speeding-up-willow-camp-s-post_test-rb-by-99&#34;&gt;earlier today&lt;/a&gt; and Claude found a call to BCrypt in my fixtures that was gobbling up time.&lt;/p&gt;
&lt;p&gt;I need user records all over the place in my test and each new user is a new call to encrypt a password.&lt;/p&gt;
&lt;p&gt;Here is Claude&amp;rsquo;s summary, with my Grammarly edits:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Problem: The &lt;code&gt;PreviewsControllerTest#test_should_show_preview_for_own_unpublished_post&lt;/code&gt; test was taking 2.95 seconds due to BCrypt password hashing being computed dynamically in fixtures with &lt;code&gt;&amp;lt;%= BCrypt::Password.create(&amp;quot;password&amp;quot;) %&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Solution: Replaced the dynamic BCrypt computation with a pre-computed hash &lt;code&gt;$2a$12$dapdHZiOTxAL8OT9yNI6e.hhTjRhWgXq7kx737/xf8tBfQbMD2bi&lt;/code&gt; for all user fixtures.&lt;/p&gt;
&lt;p&gt;Result: The slowest test now runs in ~0.25-0.52 seconds (82% improvement) and all tests pass. The overall test suite performance has been significantly improved, as this optimization affects all tests that use user fixtures.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
    </item>
    
  </channel>
</rss>
