<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Brennan Hitchcock]]></title><description><![CDATA[Problem solving, musings and ideas.]]></description><link>https://hitchcock.dev/</link><image><url>https://hitchcock.dev/favicon.png</url><title>Brennan Hitchcock</title><link>https://hitchcock.dev/</link></image><generator>Ghost 5.75</generator><lastBuildDate>Sun, 19 Apr 2026 05:53:57 GMT</lastBuildDate><atom:link href="https://hitchcock.dev/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[I Spent 3 Hours Fixing a 15-Minute Setup]]></title><description><![CDATA[<h1 id="making-claude-cognitive-actually-work-a-multi-project-setup-guide">Making Claude-Cognitive Actually Work: A Multi-Project Setup Guide</h1><p>I recently tried setting up <a href="https://github.com/GMaN1911/claude-cognitive?ref=hitchcock.dev">claude-cognitive</a>, a working memory system for Claude Code that promises context persistence across sessions. The 15-minute setup guide looked straightforward enough. Three hours later, I finally had it working&#x2014;but only after discovering several assumptions baked</p>]]></description><link>https://hitchcock.dev/i-spent-3-hours-fixing-a-15-minute-setup/</link><guid isPermaLink="false">695fba0d6cab9e0441cf3cfe</guid><dc:creator><![CDATA[Brennan Hitchcock]]></dc:creator><pubDate>Thu, 08 Jan 2026 14:17:08 GMT</pubDate><media:content url="https://hitchcock.dev/content/images/2026/01/claude_cognifitive_header.jpg" medium="image"/><content:encoded><![CDATA[<h1 id="making-claude-cognitive-actually-work-a-multi-project-setup-guide">Making Claude-Cognitive Actually Work: A Multi-Project Setup Guide</h1><img src="https://hitchcock.dev/content/images/2026/01/claude_cognifitive_header.jpg" alt="I Spent 3 Hours Fixing a 15-Minute Setup"><p>I recently tried setting up <a href="https://github.com/GMaN1911/claude-cognitive?ref=hitchcock.dev">claude-cognitive</a>, a working memory system for Claude Code that promises context persistence across sessions. The 15-minute setup guide looked straightforward enough. Three hours later, I finally had it working&#x2014;but only after discovering several assumptions baked into the default configuration that don&apos;t work for most real-world use cases.</p><p>Here&apos;s what I learned and how I fixed it.</p><hr><h2 id="what-claude-cognitive-does">What Claude-Cognitive Does</h2><p>Before diving into the fixes, let&apos;s understand what this tool actually provides:</p><ul><li><strong>Attention-based context routing</strong>: Files you mention become &quot;hot&quot; (fully injected into context), then decay to &quot;warm&quot; (headers only), then &quot;cold&quot; (evicted)</li><li><strong>Co-activation</strong>: Related files boost each other&#x2014;mention authentication, and the multitenancy docs warm up too</li><li><strong>State persistence</strong>: Your attention scores survive across conversation turns</li><li><strong>Pool coordination</strong>: Multiple Claude instances can coordinate via shared state</li></ul><p>The system hooks into Claude Code&apos;s <code>UserPromptSubmit</code> event to inject relevant documentation before each prompt. In theory, this gives Claude &quot;working memory&quot; about your project.</p><hr><h2 id="problem-1-the-scripts-look-in-the-wrong-place">Problem 1: The Scripts Look in the Wrong Place</h2><p>After following the setup guide exactly, I ran a test:</p><pre><code class="language-bash">echo &apos;{&quot;prompt&quot;:&quot;How does authentication work?&quot;}&apos; | python3 ~/.claude/scripts/context-router-v2.py
</code></pre><p>Nothing. No output. No attention state header.</p><p>Digging into the script, I found this:</p><pre><code class="language-python"># Line 452 in the original context-router-v2.py
docs_root = Path(os.environ.get(&quot;CONTEXT_DOCS_ROOT&quot;, str(Path.home() / &quot;.claude&quot;)))
</code></pre><p>The router looks for documentation in <code>~/.claude/systems/</code>, <code>~/.claude/modules/</code>, etc.&#x2014;the <strong>global</strong> Claude directory. But the setup guide has you create documentation in your <strong>project&apos;s</strong> <code>.claude/</code> directory.</p><h3 id="the-fix">The Fix</h3><p>Make the script prefer project-local docs:</p><pre><code class="language-python"># Priority: CONTEXT_DOCS_ROOT env &gt; project .claude/ &gt; global ~/.claude/
if os.environ.get(&quot;CONTEXT_DOCS_ROOT&quot;):
    docs_root = Path(os.environ[&quot;CONTEXT_DOCS_ROOT&quot;])
elif Path(&quot;.claude&quot;).exists():
    docs_root = Path(&quot;.claude&quot;)
else:
    docs_root = Path.home() / &quot;.claude&quot;
</code></pre><p>Now the router automatically uses whichever <code>.claude/</code> directory exists in your current working directory.</p><hr><h2 id="problem-2-hardcoded-keywords-for-someone-elses-project">Problem 2: Hardcoded Keywords for Someone Else&apos;s Project</h2><p>Even after fixing the docs path, nothing activated. Checking the injection log:</p><pre><code>Stats: Hot=0, Warm=0, Cold=4
Activated files: none
</code></pre><p>The script found my files but couldn&apos;t activate them. Why?</p><p>The <code>KEYWORDS</code> dictionary&#x2014;which maps trigger words to documentation files&#x2014;was hardcoded for the author&apos;s specific project:</p><pre><code class="language-python"># Original keywords (lines 79-197)
KEYWORDS: Dict[str, List[str]] = {
    &quot;systems/server-one.md&quot;: [
        &quot;server-one&quot;, &quot;gpu&quot;, &quot;local model&quot;, &quot;inference&quot;,
        &quot;vram&quot;, &quot;cuda&quot;, &quot;nvidia-smi&quot;...
    ],
    &quot;systems/server-two.md&quot;: [
        &quot;server-two&quot;, &quot;edge&quot;, &quot;sensory&quot;, &quot;perception&quot;, &quot;layer 0&quot;...
    ],
    &quot;modules/custom-module.md&quot;: [
        &quot;custom&quot;, &quot;trajectory&quot;, &quot;prediction&quot;, &quot;state machine&quot;...
    ],
    # ... 100+ more project-specific keywords
}
</code></pre><p>The script contained over 100 keywords mapped to files that don&apos;t exist in my codebase. No matter what I typed, nothing activated because my project has completely different files and terminology.</p><h3 id="the-fix-config-driven-keywords">The Fix: Config-Driven Keywords</h3><p>Rather than edit the script every time I switch projects, I refactored it to load keywords from a project-local config file:</p><pre><code class="language-python">def load_project_config() -&gt; Tuple[Dict[str, List[str]], Dict[str, List[str]], List[str]]:
    &quot;&quot;&quot;
    Load keywords, co-activation, and pinned files from project config.
    Returns (keywords, co_activation, pinned_files) tuple.
    &quot;&quot;&quot;
    config_paths = [
        Path(&quot;.claude/keywords.json&quot;),           # Project-local
        Path.home() / &quot;.claude/keywords.json&quot;,   # Global fallback
    ]

    for config_path in config_paths:
        if config_path.exists():
            try:
                with open(config_path) as f:
                    config = json.load(f)
                return (
                    config.get(&quot;keywords&quot;, {}),
                    config.get(&quot;co_activation&quot;, {}),
                    config.get(&quot;pinned&quot;, [])
                )
            except (json.JSONDecodeError, IOError):
                continue

    return ({}, {}, [])

# Load at module level
KEYWORDS, CO_ACTIVATION, PINNED_FILES = load_project_config()
</code></pre><p>Now each project gets its own <code>.claude/keywords.json</code>:</p><pre><code class="language-json">{
  &quot;keywords&quot;: {
    &quot;systems/development.md&quot;: [
      &quot;development&quot;, &quot;dev&quot;, &quot;local&quot;, &quot;localhost&quot;,
      &quot;database&quot;, &quot;migration&quot;, &quot;test&quot;, &quot;debug&quot;
    ],
    &quot;systems/production.md&quot;: [
      &quot;production&quot;, &quot;prod&quot;, &quot;deploy&quot;, &quot;hosting&quot;, &quot;ci/cd&quot;
    ],
    &quot;modules/feature-a.md&quot;: [
      &quot;feature-a&quot;, &quot;component&quot;, &quot;service&quot;, &quot;handler&quot;,
      &quot;your-specific-terms&quot;, &quot;class-names&quot;, &quot;function-names&quot;
    ],
    &quot;integrations/external-api.md&quot;: [
      &quot;external-api&quot;, &quot;webhook&quot;, &quot;api-key&quot;, &quot;integration&quot;
    ]
  },
  &quot;co_activation&quot;: {
    &quot;modules/feature-a.md&quot;: [&quot;systems/development.md&quot;],
    &quot;integrations/external-api.md&quot;: [&quot;modules/feature-a.md&quot;]
  },
  &quot;pinned&quot;: [&quot;systems/development.md&quot;]
}
</code></pre><hr><h2 id="problem-3-silent-failures">Problem 3: Silent Failures</h2><p>The most frustrating part of debugging was that the router fails silently. If no files reach HOT or WARM status, it outputs nothing:</p><pre><code class="language-python"># Line 492 in original
if stats[&quot;hot&quot;] &gt; 0 or stats[&quot;warm&quot;] &gt; 0:
    print(output)
</code></pre><p>This makes sense in production&#x2014;you don&apos;t want noise when there&apos;s nothing relevant. But during setup, you have no idea if the hook is even running.</p><h3 id="debugging-tip">Debugging Tip</h3><p>The script logs everything to <code>~/.claude/context_injection.log</code>. Check it:</p><pre><code class="language-bash">tail -50 ~/.claude/context_injection.log
</code></pre><p>You&apos;ll see exactly what&apos;s happening:</p><pre><code>================================================================================
[2026-01-08T08:41:36.818653] Turn 15
Prompt (first 100 chars): How does authentication work?...
Stats: Hot=0, Warm=0, Cold=4
Total chars: 144
Activated files: none
================================================================================
</code></pre><hr><h2 id="the-final-architecture">The Final Architecture</h2><p>After all fixes, here&apos;s what a properly configured multi-project setup looks like:</p><pre><code>~/.claude/
&#x251C;&#x2500;&#x2500; scripts/                    # Global (shared across all projects)
&#x2502;   &#x251C;&#x2500;&#x2500; context-router-v2.py   # Modified to load project config
&#x2502;   &#x251C;&#x2500;&#x2500; pool-auto-update.py
&#x2502;   &#x251C;&#x2500;&#x2500; pool-loader.py
&#x2502;   &#x251C;&#x2500;&#x2500; pool-extractor.py
&#x2502;   &#x2514;&#x2500;&#x2500; pool-query.py
&#x251C;&#x2500;&#x2500; settings.json              # Global hooks configuration
&#x2514;&#x2500;&#x2500; (empty systems/modules/integrations - not needed)

~/projects/your-project/.claude/
&#x251C;&#x2500;&#x2500; keywords.json              # Project-specific keywords &amp; co-activation
&#x251C;&#x2500;&#x2500; attn_state.json            # Attention state (auto-generated)
&#x251C;&#x2500;&#x2500; CLAUDE.md                  # Project overview
&#x251C;&#x2500;&#x2500; systems/
&#x2502;   &#x251C;&#x2500;&#x2500; development.md         # Dev environment docs
&#x2502;   &#x2514;&#x2500;&#x2500; production.md          # Production infrastructure
&#x251C;&#x2500;&#x2500; modules/
&#x2502;   &#x251C;&#x2500;&#x2500; feature-a.md           # Core feature documentation
&#x2502;   &#x2514;&#x2500;&#x2500; feature-b.md           # Another feature
&#x2514;&#x2500;&#x2500; integrations/
    &#x2514;&#x2500;&#x2500; external-api.md        # Third-party integration docs
</code></pre><hr><h2 id="writing-effective-documentation">Writing Effective Documentation</h2><p>The context router&apos;s value comes from the documentation you feed it. Here&apos;s what makes docs useful for this system:</p><h3 id="keep-files-focused">Keep Files Focused</h3><p>Each file should cover one system, module, or integration. The router injects <strong>entire files</strong> when they&apos;re HOT&#x2014;a 500-line mega-doc will blow your context budget.</p><h3 id="front-load-the-important-stuff">Front-Load the Important Stuff</h3><p>WARM files only get their first ~25 lines injected. Put the most useful information at the top:</p><pre><code class="language-markdown"># Feature Name

**Purpose:** One-line description of what this does
**Entry Point:** `src/services/FeatureService.ts`
**Status:** Active

## Quick Reference

**Key Files:**
- `src/services/FeatureService.ts` - Main logic
- `src/controllers/FeatureController.ts` - API endpoints

**Endpoints:**
- `POST /api/feature/action` - Do the thing
- `GET /api/feature/:id` - Get the thing
</code></pre><h3 id="choose-keywords-carefully">Choose Keywords Carefully</h3><p>Keywords are matched against the <strong>lowercased prompt</strong>. Include:</p><ul><li>Technical terms (<code>api</code>, <code>webhook</code>, <code>middleware</code>, <code>database</code>)</li><li>File/class names (<code>UserService</code>, <code>OrderController</code>)</li><li>Common phrasings (<code>how does X work</code>, <code>set up Y</code>)</li></ul><p>Avoid overly generic words that would trigger false positives.</p><hr><h2 id="is-it-worth-it">Is It Worth It?</h2><p>After all this setup, does claude-cognitive actually help?</p><p><strong>Yes, but with caveats.</strong></p><p>When configured properly, asking &quot;How does authentication work?&quot; now gives Claude immediate context about my JWT implementation, tenant membership model, and test accounts. That&apos;s genuinely useful for complex codebases.</p><p>But the setup cost is significant. You need to:</p><ol><li>Write project documentation (which you should do anyway)</li><li>Curate keywords that match how you actually ask questions</li><li>Maintain the docs as your project evolves</li></ol><p>For small projects, this is overkill. For larger codebases where you&apos;re constantly context-switching between subsystems, the attention-based routing can save real time.</p><hr><h2 id="quick-start-for-your-project">Quick Start for Your Project</h2><p>If you want to try this modified setup:</p><p><strong>1. Clone and install scripts:</strong></p><pre><code class="language-bash">git clone https://github.com/GMaN1911/claude-cognitive.git ~/.claude-cognitive
cp ~/.claude-cognitive/scripts/*.py ~/.claude/scripts/
chmod +x ~/.claude/scripts/*.py
</code></pre><p><strong>2. Apply my patches to <code>~/.claude/scripts/context-router-v2.py</code>:</strong></p><p>Replace the docs_root logic (~line 450):</p><pre><code class="language-python">if os.environ.get(&quot;CONTEXT_DOCS_ROOT&quot;):
    docs_root = Path(os.environ[&quot;CONTEXT_DOCS_ROOT&quot;])
elif Path(&quot;.claude&quot;).exists():
    docs_root = Path(&quot;.claude&quot;)
else:
    docs_root = Path.home() / &quot;.claude&quot;
</code></pre><p>Replace the KEYWORDS/CO_ACTIVATION section (~line 68-174) with the <code>load_project_config()</code> function shown above.</p><p><strong>3. Create project config:</strong></p><pre><code class="language-bash">mkdir -p your-project/.claude/{systems,modules,integrations}
# Create keywords.json with your project&apos;s keywords
# Create documentation files
</code></pre><p><strong>4. Configure hooks in <code>~/.claude/settings.json</code>:</strong></p><pre><code class="language-json">{
  &quot;hooks&quot;: {
    &quot;UserPromptSubmit&quot;: [{
      &quot;hooks&quot;: [
        {&quot;type&quot;: &quot;command&quot;, &quot;command&quot;: &quot;python3 ~/.claude/scripts/context-router-v2.py&quot;}
      ]
    }]
  }
}
</code></pre><p><strong>5. Set your instance ID:</strong></p><pre><code class="language-bash">echo &apos;export CLAUDE_INSTANCE=A&apos; &gt;&gt; ~/.zshrc
</code></pre><hr><h2 id="conclusion">Conclusion</h2><p>Claude-cognitive is a clever system with a flawed default configuration. The core ideas&#x2014;attention decay, co-activation, tiered context injection&#x2014;are sound. But the implementation assumes you&apos;re running a specific project and storing everything globally.</p><p>With a few patches to support project-local configuration, it becomes genuinely useful for managing context across large codebases. The setup investment pays off when you stop needing to re-explain your architecture every few prompts.</p><p>I&apos;ve <a href="https://github.com/GMaN1911/claude-cognitive/pull/7?ref=hitchcock.dev">opened a PR</a> to add per-project keyword configuration to the main repository. Hopefully this makes the tool more accessible to those who routinely work outside of a monorepo.</p>]]></content:encoded></item><item><title><![CDATA[Enable Rails 7.1.2 Health Check Endpoint]]></title><description><![CDATA[<p>I upgrade my rails project and tend to run on the latest stable version. Doing those upgrades tends to wipe out my configs so I will usally do them manually. In this case I missed a really cool feature - the health check endpoint at <code>\up</code>. <br><br>Setting this up is</p>]]></description><link>https://hitchcock.dev/enable-rails-7-1-2-health-check/</link><guid isPermaLink="false">6598248c2a6fad04c7bf3275</guid><dc:creator><![CDATA[Brennan Hitchcock]]></dc:creator><pubDate>Fri, 05 Jan 2024 17:11:51 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1505751172876-fa1923c5c528?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDJ8fGhlYWx0aCUyMGNoZWNrfGVufDB8fHx8MTcwNDQ3NDcyN3ww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1505751172876-fa1923c5c528?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDJ8fGhlYWx0aCUyMGNoZWNrfGVufDB8fHx8MTcwNDQ3NDcyN3ww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Enable Rails 7.1.2 Health Check Endpoint"><p>I upgrade my rails project and tend to run on the latest stable version. Doing those upgrades tends to wipe out my configs so I will usally do them manually. In this case I missed a really cool feature - the health check endpoint at <code>\up</code>. <br><br>Setting this up is fairly easy! Add these lines to the top of your routes file:</p><figure class="kg-card kg-code-card"><pre><code class="language-ruby">Rails.application.routes.draw do
  # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
  # Can be used by load balances and uptime moniors to verify the app is live.
  get &apos;up&apos; =&gt; &apos;rails/health#show&apos;, as: :rails_health_check
...</code></pre><figcaption>Added <code>up</code> route to routes.rb</figcaption></figure><p>Now if you visit your running app&apos;s <code>\up</code> path you should be presented with a green screen for a running app, and a red one for a failed. </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://hitchcock.dev/content/images/2024/01/Screenshot-2024-01-05-at-10.59.10-AM.png" class="kg-image" alt="Enable Rails 7.1.2 Health Check Endpoint" loading="lazy" width="597" height="398"><figcaption>Green status page</figcaption></figure><p>If you host on fly.io you can take advantage of this new endpoint by modifying your <code>fly.toml</code> to include the health checks as shown here:</p><figure class="kg-card kg-code-card"><pre><code class="language-toml">[http_service]
  internal_port = 3000
  force_https = true
  auto_stop_machines = true
  auto_start_machines = true
  min_machines_running = 1
  processes = [&quot;app&quot;]

[checks]
  [checks.status]
    port = 3000
    type = &quot;http&quot;
    interval = &quot;10s&quot;
    timeout = &quot;2s&quot;
    grace_period = &quot;5s&quot;
    method = &quot;GET&quot;
    path = &quot;/up&quot;
    protocol = &quot;http&quot;
    tls_skip_verify = false
    [checks.status.headers]
      X-Forwarded-Proto = &quot;https&quot;</code></pre><figcaption><code>fly.toml</code> added configuration to use health checks</figcaption></figure>]]></content:encoded></item><item><title><![CDATA[DEPRECATION WARNING: Rails.application.secrets is deprecated in favor of Rails.application.credentials Rails 7.1 and Devise]]></title><description><![CDATA[<figure class="kg-card kg-code-card"><pre><code class="language-ruby">DEPRECATION WARNING: Rails.application.secrets is deprecated in favor of Rails.application.credentials</code></pre><figcaption>WUT?!</figcaption></figure><p>I recently upgraded to Rails 7.1 for my project, <a href="https://www.kidcarekit.com/?ref=hitchcock.dev">kidcarekit</a> (a daycare management Saas) and upon running my tests, discovered a dreaded <code>DEPRECATION WARNING</code>. Well, at least we caught it before 7.2 was released!</p>]]></description><link>https://hitchcock.dev/deprecation-warning-rails-application-secrets-is-deprecated-in-favor-of-rails-application-credentials/</link><guid isPermaLink="false">652025baccaf58043b3d3288</guid><dc:creator><![CDATA[Brennan Hitchcock]]></dc:creator><pubDate>Fri, 06 Oct 2023 17:27:12 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1522776851755-3914469f0ca2?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDR8fHJ1YnklMjBvbiUyMHJhaWxzfGVufDB8fHx8MTY5Njk2NDQ5Nnww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<figure class="kg-card kg-code-card"><pre><code class="language-ruby">DEPRECATION WARNING: Rails.application.secrets is deprecated in favor of Rails.application.credentials</code></pre><figcaption>WUT?!</figcaption></figure><img src="https://images.unsplash.com/photo-1522776851755-3914469f0ca2?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDR8fHJ1YnklMjBvbiUyMHJhaWxzfGVufDB8fHx8MTY5Njk2NDQ5Nnww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="DEPRECATION WARNING: Rails.application.secrets is deprecated in favor of Rails.application.credentials Rails 7.1 and Devise"><p>I recently upgraded to Rails 7.1 for my project, <a href="https://www.kidcarekit.com/?ref=hitchcock.dev">kidcarekit</a> (a daycare management Saas) and upon running my tests, discovered a dreaded <code>DEPRECATION WARNING</code>. Well, at least we caught it before 7.2 was released!</p><p>Being relatively new to rails, I wasn&apos;t immediately clear how to update my project to use the new <code>Rails.application.credentials</code> instead of <code>secrets</code>. My <code>application.rb</code> file looked like this, and the error pointed at <code>Bundler.require(*Rails.groups)</code></p><pre><code class="language-ruby">require_relative &quot;boot&quot;

require &quot;rails/all&quot;

# Require the gems listed in Gemfile, including any gems
# you&apos;ve limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module Kidcarekit
...	</code></pre><p>After some digging it looks like this was due to the <code>Devise</code> gem having a <code>SecretKeyFinder</code> that looked like this:</p><figure class="kg-card kg-code-card"><pre><code class="language-ruby"># frozen_string_literal: true

module Devise
  class SecretKeyFinder
    def initialize(application)
      @application = application
    end

    def find
      if @application.respond_to?(:credentials) &amp;&amp; key_exists?(@application.credentials)
        @application.credentials.secret_key_base
      elsif @application.respond_to?(:secrets) &amp;&amp; key_exists?(@application.secrets)
        @application.secrets.secret_key_base
      elsif @application.config.respond_to?(:secret_key_base) &amp;&amp; key_exists?(@application.config)
        @application.config.secret_key_base
      elsif @application.respond_to?(:secret_key_base) &amp;&amp; key_exists?(@application)
        @application.secret_key_base
      end
    end

    private

    def key_exists?(object)
      object.secret_key_base.present?
    end
  end
end
</code></pre><figcaption>devise\secret_key_finder.rb</figcaption></figure><p>The FIX - I am able to mitigate the warning by implementing a Devise-specific secret key in <code>devise.rb</code>, but I&apos;d like to keep it using the <code>secret_key_base</code>. So, in light of Rails 7.1 just being release yesterday, I&apos;ve decided to wait for <code>Devise</code> to catch up!</p>]]></content:encoded></item><item><title><![CDATA[Embracing whimsy: Sprinkling fairy dust on Rails 7 MailerSend integration]]></title><description><![CDATA[<p>Transactional emails are the lifeblood of web applications, delivering crucial information and updates to users. But who said transactional emails have to be boring? In this blog post, we will explore how the powerful combination of Rails Action Mailer and MailerSend can infuse whimsy and enchantment into your transactional email</p>]]></description><link>https://hitchcock.dev/rails-7-and-mailsender/</link><guid isPermaLink="false">651da0435f0f682a0a1b8b48</guid><dc:creator><![CDATA[Brennan Hitchcock]]></dc:creator><pubDate>Thu, 05 Oct 2023 15:26:37 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1596526131083-e8c633c948d2?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDR8fGVtYWlsfGVufDB8fHx8MTY5NjUxNzgxOXww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1596526131083-e8c633c948d2?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDR8fGVtYWlsfGVufDB8fHx8MTY5NjUxNzgxOXww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Embracing whimsy: Sprinkling fairy dust on Rails 7 MailerSend integration"><p>Transactional emails are the lifeblood of web applications, delivering crucial information and updates to users. But who said transactional emails have to be boring? In this blog post, we will explore how the powerful combination of Rails Action Mailer and MailerSend can infuse whimsy and enchantment into your transactional email communications. Not really... but you can get up and delivering production transactional emails <em>without getting out your credit card.</em></p><p>My project, <a href="https://www.kidcarekit.com/?ref=hitchcock.dev">kidcarekit</a>, has reached the point where I want to test things like email verification and provide the ability for users to reset their password via email. As it turns out with Rails setting up SMTP is as simple as adding a few lines to your environment files.</p><p>In development I&apos;m using the <code>letter_opener</code> gem that automatically pops up the email your app would&apos;ve sent in a new browser tab. It&apos;s insaely powerful and easy to setup. In your <code>gemfile</code> add (under development only so avoid loading this gem in your production environment) like this:</p><figure class="kg-card kg-code-card"><pre><code class="language-ruby">group :development do
  # Don&apos;t send emails in development
  gem &apos;letter_opener&apos;
end</code></pre><figcaption>gemfile</figcaption></figure><p>You will also need to modify your <code>development.rb</code> file to use your new gem. </p><figure class="kg-card kg-code-card"><pre><code class="language-ruby">  # Config letter opener for development.
  config.action_mailer.delivery_method = :letter_opener
  config.action_mailer.perform_deliveries = true</code></pre><figcaption>development.rb</figcaption></figure><p>Bundle install and then run your app to the point of an email being generated and you should be presented with your test email in a new browser tab. NEAT! </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://hitchcock.dev/content/images/2023/10/image.png" class="kg-image" alt="Embracing whimsy: Sprinkling fairy dust on Rails 7 MailerSend integration" loading="lazy" width="762" height="339" srcset="https://hitchcock.dev/content/images/size/w600/2023/10/image.png 600w, https://hitchcock.dev/content/images/2023/10/image.png 762w" sizes="(min-width: 720px) 720px"><figcaption>Example of letter_opener gem showing a transaction email in my SaaS dev environment</figcaption></figure><div class="kg-card kg-callout-card kg-callout-card-yellow"><div class="kg-callout-emoji">&#x1F4B0;</div><div class="kg-callout-text">Affiliate link for MailerSend below</div></div><p>Now for your production emails. I&apos;m using the free tier of <a href="https://www.mailersend.com/?ref=7a9mprixhoxc">MailerSend</a> via SMTP which is super easy to configure for your Rails app. As of writing this post they have a super generous free tier; 3000 emails a month then 1$ for each additional thousand emails your app generates. </p><p>I will leave setting up and verifying your account with MailerSend an exercise to the reader, but once you have it is simple to configure Rails via the <code>production.rb</code> environment configuration to use the SMTP server provided by MailerSend. </p><figure class="kg-card kg-code-card"><pre><code class="language-ruby">  # Set default_url_options host
  config.action_mailer.default_url_options = { host: &quot;www.kidcarekit.com&quot; }
  config.action_mailer.delivery_method = :smtp

  # SMTP settings for Mailerlite
  config.action_mailer.smtp_settings = {
  :address =&gt; ENV[&quot;MAILERSEND_SERVER&quot;],
  :port =&gt; 587,
  :user_name =&gt; ENV[&quot;MAILERSEND_USERNAME&quot;],
  :password =&gt; ENV[&quot;MAILERSEND_PASSWORD&quot;],
  }</code></pre><figcaption>production.rb</figcaption></figure><p>Mine looks like this. I host my application on fly.io now that Heroku&apos;s value proposition has decreased. One of the beautiful things about fly.io is the management of secrets via the environment. I don&apos;t recommend storing your actual credentials in your source code. </p><p>Thanks for sticking around and happy building!</p>]]></content:encoded></item><item><title><![CDATA[Rails 7 model generator not including references]]></title><description><![CDATA[<p>I ran into a perplexing issue today with the Rails 7.0.6 model generator not actually generating the references tag. </p><p>Here&apos;s what I was attempting to do:</p><pre><code>rails g model Waitlist name:string --primary-key-type=uuid daycare:references</code></pre><p>The model it created should a string field, name and</p>]]></description><link>https://hitchcock.dev/rails-7-model-generator-not-including-references/</link><guid isPermaLink="false">64b6ed065f0f682a0a1b8b09</guid><dc:creator><![CDATA[Brennan Hitchcock]]></dc:creator><pubDate>Tue, 18 Jul 2023 19:59:23 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1606166245039-ffeba59d83a4?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDE3fHxlcnJvcnxlbnwwfHx8fDE2ODk3MTAzNDJ8MA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1606166245039-ffeba59d83a4?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDE3fHxlcnJvcnxlbnwwfHx8fDE2ODk3MTAzNDJ8MA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Rails 7 model generator not including references"><p>I ran into a perplexing issue today with the Rails 7.0.6 model generator not actually generating the references tag. </p><p>Here&apos;s what I was attempting to do:</p><pre><code>rails g model Waitlist name:string --primary-key-type=uuid daycare:references</code></pre><p>The model it created should a string field, name and references to the daycare table. Or so I thought. No matter how many times I ran this generator and the subsequent <code>db:migrate</code> my reference fields just didn&apos;t exist.</p><p>I proceeded to rubber duck the problem to a friend on Discord and no sooner had I typed it out and hit <code>Send</code> that I realized that the order probably matters. </p><pre><code class="language-rails">rails g model Waitlist name:string daycare:references --primary-key-type=uuid</code></pre><p>MAGIC - everything was generated as expected. Happy rubying!</p>]]></content:encoded></item><item><title><![CDATA[Error deploying Rails 7 app to fly.io with Tailwind and Flowbite]]></title><description><![CDATA[<p>This error had me stumped for a few hours the other night. I&apos;m using fly.io to host a <a href="http://kidcarekit.com/?ref=hitchcock.dev">project</a> that I&apos;m working on to help manage in-home child care providers run their business. </p><figure class="kg-card kg-code-card"><pre><code class="language-bash"> &gt; [build 6/6] RUN SECRET_KEY_BASE=DUMMY ./bin/rails assets:</code></pre></figure>]]></description><link>https://hitchcock.dev/deploying-rails-7-app-to-fly-io/</link><guid isPermaLink="false">64ac49b15f0f682a0a1b8ac6</guid><dc:creator><![CDATA[Brennan Hitchcock]]></dc:creator><pubDate>Mon, 10 Jul 2023 18:23:49 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1621252179027-94459d278660?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDF8fGZydXN0cmF0aW9ufGVufDB8fHx8MTY4OTcxMDQ0M3ww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1621252179027-94459d278660?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDF8fGZydXN0cmF0aW9ufGVufDB8fHx8MTY4OTcxMDQ0M3ww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Error deploying Rails 7 app to fly.io with Tailwind and Flowbite"><p>This error had me stumped for a few hours the other night. I&apos;m using fly.io to host a <a href="http://kidcarekit.com/?ref=hitchcock.dev">project</a> that I&apos;m working on to help manage in-home child care providers run their business. </p><figure class="kg-card kg-code-card"><pre><code class="language-bash"> &gt; [build 6/6] RUN SECRET_KEY_BASE=DUMMY ./bin/rails assets:precompile:                                             
#15 3.687                                                                                                           
#15 3.687 Rebuilding...                                                                                             
#15 3.734 Error: Cannot find module &apos;@tailwindcss/forms&apos;                                                            
#15 3.734 Require stack:
#15 3.734 - /rails/config/tailwind.config.js
#15 3.734     at Function.Module._resolveFilename (node:internal/modules/cjs/loader:933:15)
#15 3.734     at Function._resolveFilename (pkg/prelude/bootstrap.js:1955:46)
#15 3.734     at Function.resolve (node:internal/modules/cjs/helpers:108:19)
#15 3.734     at _resolve (/snapshot/tailwindcss/node_modules/jiti/dist/jiti.js:1:241025)
#15 3.734     at jiti (/snapshot/tailwindcss/node_modules/jiti/dist/jiti.js:1:243309)
#15 3.734     at /rails/config/tailwind.config.js:54:5
#15 3.734     at jiti (/snapshot/tailwindcss/node_modules/jiti/dist/jiti.js:1:245784)
#15 3.734     at /snapshot/tailwindcss/lib/lib/load-config.js:37:30
#15 3.734     at loadConfig (/snapshot/tailwindcss/lib/lib/load-config.js:39:6)
#15 3.734     at Object.loadConfig (/snapshot/tailwindcss/lib/cli/build/plugin.js:135:49) {
#15 3.734   code: &apos;MODULE_NOT_FOUND&apos;,
#15 3.734   requireStack: [ &apos;/rails/config/tailwind.config.js&apos; ]
#15 3.734 }
#15 3.744 rails aborted!
#15 3.744 Command failed with exit 1: /rails/vendor/bundle/ruby/3.2.0/gems/tailwindcss-rails-2.0.29-x86_64-linux/exe/x86_64-linux/tailwindcss
...&lt;removed some unuseful stack trace here&gt;...
#15 3.745 Tasks: TOP =&gt; assets:precompile =&gt; tailwindcss:build
#15 3.745 (See full trace by running task with --trace)
------
Error: failed to fetch an image or build from source: error building: executor failed running [/bin/sh -c SECRET_KEY_BASE=DUMMY ./bin/rails assets:precompile]: exit code: 1</code></pre><figcaption>What the heck?</figcaption></figure><p>If this was my first deploy I&apos;d think that something was going on with fly but this had been working perfectly. Then I realized it happened once I added <a href="http://flowbite.com/?ref=hitchcock.dev">Flowbite</a> / <a href="https://tailwindcss.com/?ref=hitchcock.dev">tailwindcss</a> to the mix. There are some workarounds to <a href="https://flowbite.com/docs/getting-started/rails/?ref=hitchcock.dev#turbo-load-support">get Flowbite working with Turbo</a> but that involved mixing importmap and node_modules a bit. <br><br>So the fix was to delete my <code>DockerFile</code>, but not the <code>fly.toml</code> and regenerate the <code>DockerFile</code> with fly deploy. The let the fly console appropriately add the node build steps and allowed my deployments to work as intended. </p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://hitchcock.dev/content/images/2023/07/image.png" class="kg-image" alt="Error deploying Rails 7 app to fly.io with Tailwind and Flowbite" loading="lazy" width="1121" height="815" srcset="https://hitchcock.dev/content/images/size/w600/2023/07/image.png 600w, https://hitchcock.dev/content/images/size/w1000/2023/07/image.png 1000w, https://hitchcock.dev/content/images/2023/07/image.png 1121w"><figcaption>Diff for newly generated DockerFile for Rails 7.0.* with node</figcaption></figure>]]></content:encoded></item><item><title><![CDATA[2022 in review]]></title><description><![CDATA[As 2022 comes to a close, it's time to look back and reflect on the ups and downs of the past 12 months. For me, this year brought many exciting and memorable experiences, including starting a new job, diving into the world of Ruby on Rails, and welcoming my second child into the world. ]]></description><link>https://hitchcock.dev/2022-in-review/</link><guid isPermaLink="false">64ac444d5f0f682a0a1b8aa5</guid><dc:creator><![CDATA[Brennan Hitchcock]]></dc:creator><pubDate>Sat, 31 Dec 2022 23:04:00 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1514242879996-d7b3bb2dd531?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDJ8fDIwMjIlMjB5ZWFyJTIwaW4lMjByZXZpZXd8ZW58MHx8fHwxNjg5MDEyMjg5fDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1514242879996-d7b3bb2dd531?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDJ8fDIwMjIlMjB5ZWFyJTIwaW4lMjByZXZpZXd8ZW58MHx8fHwxNjg5MDEyMjg5fDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="2022 in review"><p>As 2022 comes to a close, it&apos;s time to look back and reflect on the ups and downs of the past 12 months. For me, this year brought many exciting and memorable experiences, including starting a new job, diving into the world of Ruby on Rails, and welcoming my second child into the world. It also brought its fair share of challenges, including several hospitalizations for my kids due to respiratory syncytial virus (RSV).</p><p>Looking back, it&apos;s clear that 2022 was a year full of growth and change. From learning new skills and adapting to a fully remote work environment, to navigating the joys and challenges of parenthood, it&apos;s been a whirlwind of a year. I want to share some of the highlights and lessons learned from my personal journey in 2022.</p><p>One of the biggest changes for me in 2022 was starting a new job. After spending a decade at my previous company, I decided it was time for a change and took a leap of faith by accepting a new position at a different company. The transition wasn&apos;t always easy, but it ultimately proved to be a great decision.</p><p>In my new role, I have been able to take on more responsibility and challenge myself in new ways. I have also had the opportunity to work with a talented and supportive team, which has been incredibly rewarding.</p><p>One of the biggest adjustments for me has been working in a fully remote environment. While I had worked remotely before, this was the first time I had done so full-time. It took some getting used to, but I have come to appreciate the flexibility and autonomy that comes with remote work.</p><p>One of the biggest challenges I faced in my new job was the requirement to learn a new programming language. After years of working with C#, I was tasked with learning Ruby on Rails, a popular web application framework.</p><p>The learning process was not always easy, and there were certainly moments when I felt overwhelmed or frustrated (and occasionally still do). However, I found that the more I practiced and applied what I was learning, the more confident and competent I became.</p><p>I was also fortunate to have a great support system at my new job, including fellow developers and online resources, who were there to offer guidance and encouragement.</p><p>One of the biggest highlights of 2022 for me was welcoming my second son, Desmond, into the world. As a parent, there is nothing quite like the joy and love that comes with a new child, and Desmond has brought so much happiness and laughter into our lives.</p><p>Having a second child was certainly a different experience than the first time around. I was more confident and prepared in some ways, but there were also new challenges and uncertainties to navigate.</p><p>One of the most memorable moments of the year was watching my older son, Osborne, who is now a big brother, bond with Desmond and take on his new role with pride and joy. It has been a true joy to watch the two of them grow and develop together.</p><p>Unfortunately, 2022 was not all sunshine and rainbows for our family. My kids both ended up hospitalized with respiratory syncytial virus (RSV), a common but highly contagious respiratory illness. It was a scary and stressful time, and seeing my little ones hooked up to machines and struggling to breathe was heart-wrenching.</p><p>However, the hospital staff were amazing and did everything they could to make sure my kids received the best possible care. They were also very supportive of us as parents, helping us navigate the situation and providing resources and guidance.</p><p>While the hospitalizations were certainly a low point of the year, I am grateful for the care my kids received and for the lessons learned from the experience. It reminded me of the importance of taking care of our health and being prepared for the unexpected.</p><p>Overall, the hospitalizations for RSV in 2022 were a challenging experience, and one that I hope to never have to repeat.</p><p>As I look back on the ups and downs of 2022, it&apos;s clear that it was a year of growth and change. From starting a new job and learning Ruby on Rails, to welcoming my second child and facing hospitalizations for RSV, it&apos;s been a whirlwind of a year.</p><p>Looking ahead to 2023, I am focusing on getting my health back on track. I have a new doctor and have taken up cycling with my new Peloton bike, and I am determined to make healthy lifestyle choices and prioritize self-care.</p><p>One of the things I am most excited about in 2023 is watching my children continue to grow and develop. My older son is already showing signs of independence and creativity, and my younger son, Desmond, is growing and changing every day.</p><p>As a parent, there is nothing quite like the joy and pride that comes with watching your children learn and grow. I can&apos;t wait to see what new milestones and achievements my kids will reach in the coming year, and to be there to support and encourage them every step of the way.</p><p>I am also looking forward to continuing my journey with Ruby on Rails and growing in my new job. It&apos;s going to be a great year, and I can&apos;t wait to see what it has in store.</p>]]></content:encoded></item><item><title><![CDATA[Plex Indirect Access on LAN with OPNSense / Unbound DNS]]></title><description><![CDATA[<p>Some devices on my network were unable to access Plex directly since migrating to OPNSense with Unbound DNS.</p><p>After much troubleshooting adding <code>plex.direct</code> to <code>Services-&gt;Unbound DNS-&gt;Advanced-&gt;Private Domains</code> and applying/restarting Unbound those devices now connect directly.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://hitchcock.dev/content/images/2023/07/image-1.png" class="kg-image" alt="opnsense config screenshot showing &quot;plex.direct&quot; as the only entry to the &quot;Private Domains&quot; category" loading="lazy" width="601" height="75" srcset="https://hitchcock.dev/content/images/size/w600/2023/07/image-1.png 600w, https://hitchcock.dev/content/images/2023/07/image-1.png 601w"><figcaption>plex.direct private domain entry</figcaption></figure><p>Happy (local)</p>]]></description><link>https://hitchcock.dev/plex-indirect-access-on-lan-with-opnsense-unbound-dns/</link><guid isPermaLink="false">64c177265f0f682a0a1b8b2f</guid><dc:creator><![CDATA[Brennan Hitchcock]]></dc:creator><pubDate>Mon, 14 Nov 2022 20:45:00 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1548867688-231911e4ba3c?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDMxfHxwb3Bjb3JufGVufDB8fHx8MTY5MDQwMDc5OHww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1548867688-231911e4ba3c?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDMxfHxwb3Bjb3JufGVufDB8fHx8MTY5MDQwMDc5OHww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Plex Indirect Access on LAN with OPNSense / Unbound DNS"><p>Some devices on my network were unable to access Plex directly since migrating to OPNSense with Unbound DNS.</p><p>After much troubleshooting adding <code>plex.direct</code> to <code>Services-&gt;Unbound DNS-&gt;Advanced-&gt;Private Domains</code> and applying/restarting Unbound those devices now connect directly.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://hitchcock.dev/content/images/2023/07/image-1.png" class="kg-image" alt="Plex Indirect Access on LAN with OPNSense / Unbound DNS" loading="lazy" width="601" height="75" srcset="https://hitchcock.dev/content/images/size/w600/2023/07/image-1.png 600w, https://hitchcock.dev/content/images/2023/07/image-1.png 601w"><figcaption>plex.direct private domain entry</figcaption></figure><p>Happy (local) streaming!</p>]]></content:encoded></item></channel></rss>