A dropdown looks like a problem solved by many already. But it is not too difficult to build your own and allows to make them exactly how you want them to be. It has to open next to its trigger, flip when there’s no room, stay inside the viewport, move focus into the panel, cycle through its items with the arrow keys, close on Escape and disappear when you click somewhere else.
Then there’s the part I find most useful: the same panel should also work as a right-click context menu, opening where the pointer is instead on a button.
That’s a handful of behaviors together. I keep them in one custom element, <ui-dropdown>.

The click version is a trigger plus a panel marked with data-dropdown-pa…
Hand coding a feature in a Ruby gem
- This year
- The idea
- How often do I quit?
- How early do I quit?
- Adding a test
- Making the CLI output prettier
- Showing the new stats on my website
- Why hand coding?
A full year since my last programming post, recharged by months of being offline LARPing as a normal person, I’m ready to make my grand re-entrance to the Internet.
Ready for it?
Today I’ll walk you through adding a small feature to a little Ruby gem I made a few years ago.
Audience: 【・_・?】
I’m pretty sure I’m the only person who uses my gem.
Audience: ( ̄(エ) ̄)ゞ
Oh, and we’ll do it entirely by hand. No AI!
Audience: (ง ˃ ᗜ ˂)و ̑̑ ヾ(。★ω★。)シ ٩(ˊᗜˋ*)و ♡ ₍₍⚞(˶˃ ᵕ ˂˶)⚟⁾⁾
(At least, that’s how I feel these days…

How Evil Martians moved Uscreen’s MCP server into its Rails app as a remote, multi-tenant service: Doorkeeper OAuth, tenant-scoped tools, and dry-run previews for destructive operations.
Uscreen, the leading all-in-one membership platform for video creators and entrepreneurs, came to Evil Martians with a working Model Context Protocol (MCP) server already tested by early adopters. To use it, a creator installed an NPM package that ran the server on their computer, entered an API key, and plugged it into the AI client of their choice. It worked, but it took a lot of setup. So, to…
How Can You Be Responsible for Code You Don't Understand, Especially When You Can No Longer Program?
If AI is writing all the code and no one is reviewing it, how the heck can anyone be responsible or accountable for code they not only don’t understand and eventually can never understand?
AI's primary purpose is to deskill its operators.The short answer is that programmers become responsible for outcomes, not code. The longer answer is there are no programmers - AI is a replacement, not an assistant, since it’s primary purpose is to deskill its operators.
If AI is writing all the code and no one is reviewing it, how the heck can anyone be responsible or accountable for code they not only don't understand and eventually *can never understand*? The short answer is that programmers…📄 I'm just a meat proxy
I feel like AI-related blog posts should all begin with a disclosure as to how cooked the author is. On a scale of 1 to 10, I'm apparently now at "built a custom software factory and has fooled himself into thinking it actually works." So I guess that makes me an 8 or a 9? Not full-blown psychosis, I hope, but consider yourself duly warned.
My agents are currently shipping 20 to 40 issues a day across the dozen or so iOS apps I'm building. Today, I'm going to show you a bit of my app, MeatProxy, and how it governs my software development process. I'm not going to let you download it (you don't want it, trust me), but at the end of this post, your agent will find instructions for building…
An engineering manager I worked with before asked me how I can deliver good software for lower cost. Am I slow? Is it truly good? Am I losing money?
One should always keep such friends close. And it’s a fair question. The old rule in software is good, fast, cheap: pick two. But I provide all three. The short answer is that I changed how I work. The long answer follows.
I jumped in advance
For most of my career I had it all: a reliable full-time salary as a staff engineer, solving tricky engineering or team problems with people I like. Then AI agents appeared, and got better at writing code faster than anyone expected. And I wanted to build software differently. How long would jobs…
If you run a small business, there’s a good chance your website, your proposals, and the handouts you give clients were made at different times, by different people, in different tools. Each one looks fine on its own. Put them side by side, though, and they don’t quite look like they came from the same company.
I ran into this recently on a side project: setting up the brand and website for a new business that had little more than a few rough documents to its name. I used Claude Design, Anthropic’s design tool, and instead of starting with the website, I started with a design guide (also called a style guide or design book). That’s one document that records the business’s colors, fonts,…
What does it take to make an open source project welcoming to contributors—and manageable for its maintainers—in the age of AI? Rachael Wright-Munn joins David Hill to share what she’s learning through her work on Ruby Events, where supporting new contributors and reviewing AI-assisted pull requests raise many of the same questions about context, documentation, and code quality.
Rachael explains how Ruby Events uses YAML validation to catch problems and provide actionable feedback, along with an evolving Active Record–style interface built around Yerb. The conversation explores how clear rules, useful abstractions, and better development environments can help contributors produce…
Many of the Ruby on Rails apps we work on now have an AI layer of some kind. Sometimes it’s a single RubyLLM chat in a background job, sometimes it’s an MCP client wired into a support tool, and on most teams there’s a coding agent running in somebody’s editor. That layer gets the same security process as the rest of the app, which usually means bundler-audit in CI and a Ruby upgrade when somebody gets to it.
The last few weeks produced four separate problems in that layer. None of them has an advisory against a Ruby gem, so a dependency scanner stays green for all four. Two come from reading the source of ruby_llm-mcp, a community MCP client gem, one is a version constraint, and one is a…
RubyLLM 2.2 is out! I released it on stage at SF Ruby, and it’s the release I’m proudest of.
The code a model writes now runs in a sandbox on your own servers, and skills and file stores build on it. Tools can ask a person and wait for the answer. A chat survives a deploy. And there are context budgets, tests without a provider, and four new providers.
It’s also the first release I built for a new kind of user: the coding agent. More and more of the code that uses RubyLLM is written by Claude Code, Codex and Cursor, so I spent as much time making RubyLLM easy for them to use as I did on features. The talk was about that, and about the idea that you don’t need frameworks anymore.
Here’s…
Limit to 20 minutes of audio for (hosted?) whisper-1 for transcription
We are using the proprietary hosted version of OpenAI whisper-1 to transcribe audio, with timestamps, into timestamped VTT.
The newer gpt-transcribe may reportedly do a better job of transcribing — but it doesn’t provide timestamps. One could probably try to use other tools to then sync the transcript, but we haven’t looked into that, not sure if it would get you anything useful, we’re trying to keep it simple.
The platform-hosted whisper-1 has a limit of 25 MB per audio file input, but no duration limit.
So when we started, when I was new to such things, I figured, great, we can downsample our oral history interview content to 16kbps mono Opus/ogg audio, and fit a lot in 25MB.…
How I Learned to Use Copilot Sessions 400 Million Tokens Later
I recently built a Markdown linter with GitHub Copilot CLI. In between, I reviewed other projects, edited this blog, and generally treated one conversation as a terminal into which I could dump whatever I was doing next. I really loved the fact that Copilot would context switch, do some work, then go back to what it was doing. It’s so smart!
Well, that may have burnt some extra tokens.
Counting Tokens
The local usage logs for the roughly three-and-a-half-day development window showed approximately 4,322 model calls, including delegated agents:
Token Type Millions Non-cached input 11.4 Output 2.4 Cache…Back in August, we tested Anthropic’s promise to watermark Claude’s output and found nothing there yet. Since then, Anthropic has published which models it marks and how, and it’s no longer alone: OpenAI has announced a text watermark of its own, Google has opened its detector to the public, and xAI has taken a different route entirely.
Since August, though, the questions we’ve heard most are about what comes after the watermark: who owns a logo that a model drew, whether code written with AI can be treated as proprietary, and whether a prompt counts as authorship. And behind those sits a newer one: if your AI-assisted marketing turns out to infringe someone’s work, does your insurance…
Continuations 2026/45: Non-stop
It’s been a week of non-stop work.
First, big thing: I merged diagnostics into Hanami Webconsole! With that done, I was able to do a review of the rest of the gem and push up a range of improvements, along with a related change in Hanami Reloader.
I also got to sight some of the design revisions that Max has been putting together for this. It’s going to feel truly us when it ships. Can’t wait.I shipped the new, styled output for Hanami CLI! It feels so much more polished now, and this is just the beginning. Also discovered that fast terminals are fast and pushed up a fix for a flickering spinner.
Katafrakt Korner: Paweł found and fixed a gap in our error handling around actions…
Passenger 6.2.2

Version 6.2.2 of the Passenger application server has been released. This release is mostly focused on bug-fixes and security hardening.
Other Improvements
- Harden against symlink attacks on improperly secured instance directories. This is a mitigation, and not intended to replace the use of proper filesystem permissions.
- Harden against secure headers containing header delimiters.
- [Enterprise] Fix the hard memory limit not being enforced on a process that already exceeded the soft memory limit and is still finishing its requests.
- [Standalone] Fix Passengerfile.json strings that contain \uXXXX escapes for non-ASCII characters being garbled or making Passenger crash.
Upgraded libraries
- Upgrade llhttp…
Metaprogramming on a Budget
If you generate an empty model class in a Rails application, the attribute methods are automatically available:
class Post < ActiveRecord::Base
end
p = Post.new
p.title = "Metaprogramming on a Budget"
p.title # => "Metaprogramming on a Budget"
It’s one of Rails’ oldest features, as seen in the old Build a Blog in 15 Minutes demo from 2005. But how does it work?
✨ Metaprogramming ✨
If you’ve never used it before, metaprogramming may sound really scary, but its not too bad in practice. All it really means is that we’re writing code that writes more code.
To get a better understanding, let’s try implementing a simple version of this Rails feature ourselves. The entrypoint will be a…
In the previous post, we saw how a large language model like Claude works. Your application can use Claude by sending HTTP requests to the Claude API, which Anthropic runs. The model can't query your database, call your APIs, or run any of your code. All it can do is read the text you send it and write text back. Yet you've used AI features that look up records, search the web, or edit your code in a local repo. They're all built on a feature of the Claude API called tool use. With tool use, Claude doesn't run your code. It asks your app to run it, and your app decides whether to run it or not.
In this post, we'll build that tool exchange to learn what goes back and forth between…
I’ve been using git worktrees a lot lately. Having a couple of branches checked out next to each other (often with an agent working away in one of them) is awesome. Until two of them start running migrations against the same development database and everything gets mixed up.
The obvious fix is to give every worktree its own database. But I like to develop against an anonymized copy of production, and copying gigabytes of data for every new worktree is slow and fills up my disk in no time.
Postgres 18 quietly shipped a new setting that fixes both: file_copy_method = clone. It’s been there since 18.0 (changelog), so if you’re still on an older version, that’s a good reason to upgrade.
What…
NWRUG's (Manchester, UK) November 2026 meeting — (Un)learning programming
The next North West Ruby User Group in Manchester, UK is in a fortnight on 19 November at Colony, One Silk Street. Sam Péchèr will reflect on starting out as a junior last year, just as the industry started to shift to not writing code.
We’ll be at the venue for pizza from 6:30pm with the talk starting at 7pm. Afterwards we’ll retire to The Crown & Kettle for a drink. The venue, pizza, and drinks are kindly provided by NWRUG’s sponsors Fatsoma and thoughtbot.
Register here if you would like to attend. Full details on this (and all NWRUG events) over on NWRUG’s website.
If you enjoyed this post, you might also like:
On the 4th of November the Rails security team shipped 8.0.5.2 and 8.1.4.1, and it is a big one: 21 security issues in a single release. One of them is rated critical, GHSA-f3w6-w445-fwh9, “Possible arbitrary file write and read in Active Storage image analysis”, with a CVSS score of 9.8.
Introducing Project Steadfast
Recently I wrote about an idea that’s been
floating around in my mind for a long time - the ability to do metaprogramming
in Ruby by manipulating Ruby ASTs, similar to the macro capabilities of
languages such as Lisp or Elixir. The use of eval (in its different
variations) is a common metaprogramming technique in Ruby that consists of
generating code at runtime from dynamically constructed strings. This is often
done for performance reasons: ERB compiles templates into optimized code by
generating pieces of Ruby source code then putting them together and calling
eval; In Rails, the same technique is used for implementing features such as
associations, or scaffolding. With eval, we can avoid…
Releasing Ruby gems manually typically involves incrementing a version in a file, creating a tag, pushing a gem to Rubygems and making a GitHub release. These tasks can be automated in many ways, including release-gem or GHA with rubygems/configure-rubygems-credentials and trusted publishing. Moreover, you can build upon these approaches when you need something beyond the plain rake release, such as updating a CHANGELOG.md, by rolling miles of brittle GitHub Actions YAML and shell script AI slop.
As an alternative to both the manual process and scripts, I’ve been asking AI to follow a hand-crafted RELEASING.md. AI can run tests, date CHANGELOG.md, ask me for a RubyGems OTP, make the…
My father is a poetry translator, so perhaps my obsession with prose runs in the family. This started before Markdown: I wrote rubocop-exception_messages after noticing inconsistent exception messages in Grape.
After I replaced this blog’s Python spell checker with a RuboCop plugin I still had a Node installation to check Markdown. Since it’s a Jekyll blog, and the build is Ruby, the spelling checker is Ruby, and my default set of code examples are also Ruby, why was formatting the README Node.js?
I first tried extending rubocop-md, proposing prose checks and parser diagnostics, a reusable Markdown parser, hidden cop-directive fixes, front-matter handling, and preserving prose alongside…
Hi, it’s Greg, bringing you the latest changes in the Rails codebase. It was a busy week, with a lot of changes, so buckle up!
Rails 8.0.5.2 and 8.1.4.1 have been released! These are security releases, addressing 21 security issues, so you should upgrade if you haven’t already.
Allow anonymous subclasses of cache stores with LocalCache
A recent change built the local cache’s registry key from the store’s class name in initialize, and that name is nil for an anonymous class, so Class.new(ActiveSupport::Cache::MemoryStore).new blew up with a NoMethodError. Anonymous stores are handy in tests to record what a request writes to the cache, and they work again.
Garrett and I recorded episode two of The Friday Deploy. We started with personalized interfaces and ended with a basketball app and me casually mentioning that I’d rewritten a chunk of Flare to run on Cloudflare. As one does.
But the thing I keep coming back to is the pull request that makes your stomach hurt.
I’ve made plenty of those lately. Ask AI to build something. Have another model review it. Fix the findings. Review it again. Find more things. Fix those. Pretty soon the little change you wanted is 3,000 lines and you don’t want to merge any of it.
For a while, my response was to keep going. Surely one more review would make me feel better.
Sometimes the better answer is to throw…
https://avdi.codes/192568-2/?utm_source=rss&utm_medium=rss&utm_campaign=192568-2
So who is going to buy Twitter now that he’s throwing his toys?
Vulnerabilites & AI with Nate Berkopec
Nate Berkopec returns to Remote Ruby joining Chris, Andrew, and David for a deep conversation about the latest wave of Ruby security vulnerabilities, AI-powered cybersecurity, and the rapidly changing role of software engineers. Nate shares his firsthand experience investigating a serious Ruby regex vulnerability, explains why developers need to take a more proactive approach to security, and challenges traditional development practices in an AI-driven world. From autonomous coding agents and dramatically faster development workflows to rethinking unit tests and code reviews, this episode explores why the future of programming may be less about writing code and more about building systems…
Code Got Cheap. The Rest Didn't. November 6, 2026 AI has fundamentally changed how we build software. But implementation is only one part of the job. Architecture, business knowledge, testing, and understanding real user workflows still require experienced developers. By Guido Boyke I've been developing software for more than 30 years, including about 20 years … Continue reading Code Got Cheap. The Rest Didn’t.
Lately, I've been trying to learn more about AI, particularly large language models (LLMs). My usual approach to learning a new subject is to read the classic books on the topic. However, since this version of generative and agentic AI is so new and evolving so rapidly, it's been difficult to find good, established books that covers it and explains it so a web developer like me could understand it.
So I decided to try something different by learning AI with AI. That is, I asked Claude to explain how LLMs work over a series of back-and-forth conversations over a few days to build my understanding of the topic. This post is my attempt to explain what I've learned about how LLMs…
I'm a web…
#825 — November 5, 2026
Ruby Weekly
🔒 Rails 8.0.5.2 and 8.1.4.1 Released to Fix 21 Security Issues — A rare 21-advisory release, led by a critical Active Storage bug (when using ImageMagick/mini_magick) where attaching a specially crafted image can let an uploader write (and sometimes read) arbitrary files. A Strong Parameters bypass, remote_ip spoofing in 8.1, and more make this a key update.
Mike Dalessio (Rails Core)
💡 Christine Seeman's walkthrough covers the advisories you may want to give a closer look, what can break on upgrade, and workarounds if you can't upgrade.
JRuby 10.0.8.0 Released
The JRuby community is pleased to announce the release of JRuby 10.0.8.0.
- Homepage: https://www.jruby.org/
- Download: https://www.jruby.org/download
JRuby 10.0.x targets Ruby 3.4 compatibility.
Thank you to our contributors this release, you help keep JRuby moving forward! This cycle we had several new contributors and hundreds of pull requests. Keep it coming, JRubyists! @aminmansuri, @kares, @hiukkanen, @evaniainbrooks, @stefanlew-tractive, @sampokuokkanen, @zev, @matrei, @notsrch, @sferik, @RaphaelFakhri
Notable Changes
Security
- The asm bytecode library was updated to 9.11.1 to fix recent CVEs (none of which affect JRuby). #9858
Standard Library
- The standard library…
JRuby 10.1.3.0 Released
The JRuby community is pleased to announce the release of JRuby 10.1.3.0.
- Homepage: https://www.jruby.org/
- Download: https://www.jruby.org/download
JRuby 10.1.x targets Ruby 4.0 compatibility.
Thank you to our contributors this release, you help keep JRuby moving forward! This cycle we had several new contributors and hundreds of pull requests. This list includes contributors to JRuby 10.0.8.0. Keep it coming, JRubyists! @samuel-williams-shopify, @aminmansuri, @kares, @hiukkanen, @evaniainbrooks, @stefanlew-tractive, @sampokuokkanen, @edipofederle, @samyron, @reginaldalfret, @albertalef, @taxor03, @zev, @matrei, @notsrch, @sferik, @RaphaelFakhri
Notable Changes
See also the JRub…
RubyMine 2026.3 is out!
This release brings JetBrains Air for working with your preferred coding agents, native access to WSL and Dev Container projects, and smarter Ruby and Rails support. You’ll also find easier interpreter setup, improved navigation and refactoring, and updates that make everyday development more comfortable.
Download RubyMine 2026.3 from our website or update via the Toolbox App.
Now, let’s explore the highlights.
JetBrains Air
A Look at Datastar
I have always been interested in frontend solutions for building web applications. Even though I’m more of a backend programmer, I really dig all the different techniques people come up with for creating browser-based apps that are both reactive (reflecting remote state in real-time) and interactive (responding to user actions).
Over the last few years, I’ve been looking at different frontend frameworks and libraries, to see how they work, and what I can learn from them. I adore Svelte, which is really smart and efficient. Its sister framework SvelteKit inspired the file-based routing in my own web framework for Ruby, Syntropy. I also really like the approach of the Phoenix framework…
From One to Five: How We Solved a Real-Time Trading System’s Scaling Crisis
Ask any LLM to style a layout and it uses CSS units like: px, rem, em, % and vh. So do you, out of habit. But CSS has grown its number of units. Ones that fit specific jobs way better than your old defaults. Here are seven your code generator has probably never suggested.
They are all baseline ready (e.g. supported by core browsers for at least 30 months), so there is no fallback excuse left. Let’s get into it.
Logical properties: think in axes (x and y)
A few of these units only make sense when you drop the ideas of “width” and “height” and think in axes instead. CSS now measures along the inline axis (the direction your text flows, left to right in, for example, English) and the block…
4.0.24 Released
RubyGems 4.0.24 includes enhancements, bug fixes and documentation and Bundler 4.0.24 includes enhancements and bug fixes.
To update to the latest RubyGems you can run:
gem update --system [--pre]
To update to the latest Bundler you can run:
gem install bundler [--pre]
bundle update --bundler=4.0.24
RubyGems Release Notes
Enhancements:
- Installs bundler 4.0.24 as a default gem.
Bug fixes:
- Skip executable removal gracefully when the bin directory is missing. Pull request #9963 by Evan Li and Hiroshi SHIBATA
- Fix file locks letting multiple processes in when lock files are removed. Pull request #9930 by Richard Janita and Hiroshi SHIBATA
- Turn off echo before printing the…
Documentation:
- [DOC] Fix link. Pull request #10014 by Burdette Lamar
Bundler Release Notes
Enhancements:
- Read the Ruby version from mise’s inline table form. Pull request #9991 by…

Buildkite runs CI/CD for some of the most demanding engineering teams in the world. Why a team of opinionated developers hired Evil Martians to build a framework-agnostic design system with AI skills that guide coding agents through every component.
Buildkite is a high-scale, hybrid CI/CD platform that lets engineering teams run fast automated build, test, and deployment pipelines.
Ruby AI News - November 4th, 2026
Welcome to the 40th edition of Ruby AI News! This edition features an avalanche of security issues, Ruby’s MCP SDK reaching Tier 1 status, a closer look at building AI evals for Rails, and much more.
Personal…
Newsletter Sponsorship Packages
New Sponsorship Opportunities
Ruby AI News has reached its 40th edition, and to mark it I am opening sponsorship packages that cover everything in the RoboRuby network. It is built for organizations that want in depth…
GHSA-2gff-2mmg-m73p (actionpack): Possible IP spoofing in Action Dispatch RemoteIp
GHSA-2p2f-4rq4-gjwc (actionview): Possible XSS vulnerability in Action View text helpers
GHSA-2w9f-fxwj-w2qf (activesupport): Possible XSS vulnerability in Active Support MessagePack serializer
GHSA-3f4q-8x5m-76v8 (activestorage): Possible local file disclosure in Active Storage video previews and analysis
GHSA-3w93-6hmw-whh4 (activestorage): Unsigned Content-Type in Active Storage GCS direct uploads with cache_control
GHSA-4v2w-w6g6-f8f8 (activestorage): Possible DoS vulnerability in Active Storage proxy mode via storage errors during streaming
GHSA-5957-c7qm-m469 (actionmailbox): Possible signature replay in Action Mailbox Mailgun ingress
GHSA-68p9-83h6-5wpj (actionview): Possible XSS vulnerability in Action View tag helpers
GHSA-8474-xrp6-wh69 (activesupport): Possible DoS vulnerability in Active Support safe_constantize
GHSA-f3w6-w445-fwh9 (activestorage): Possible arbitrary file write and read in Active Storage image analysis
GHSA-gmj8-hvj3-49mw (actionpack): Missing SameSite attribute on session cookies from Action Pack session stores
GHSA-hfr4-8rjc-97m2 (activestorage): Possible circumvention of file size limits in Active Storage direct uploads to Google Cloud Storage
GHSA-p2pm-g2h6-7j5j (activestorage): Possible DoS vulnerability in Active Storage filename handling
GHSA-r9jg-fmfx-2gv6 (actionpack): Possible cache poisoning in Action Pack content negotiation
GHSA-rq8q-p985-fjq7 (actionview): Possible XSS vulnerability in Action View strip_tags
GHSA-rwvh-fw6r-pwvp (actionpack): Possible DoS vulnerability in Action Controller Live streaming
GHSA-v6rj-xm57-3p59 (actionview): Possible XSS vulnerability in Action View highlight helper
GHSA-v924-7rp8-4mxh (activestorage): Possible arbitrary file read and remote code execution in Active Storage video previews, analysis and image processing
GHSA-wm3x-v3qw-4jh3 (actionpack): Possible Strong Parameters bypass in Parameters#each_key in Action Pack
GHSA-x6mj-8jx9-qhqv (actionpack): Possible sensitive session information leak in Active Storage proxy mode
How to Distribute a Rust App with Cargo, Homebrew, MSI, and WinGet
I recently ported my 1996 Pascal/Delphi calculator to Rust. The result is excalc, a cross-platform command-line application and MCP server.
Rust makes it easy to compile an application on every platform. Distributing it via crates.io, MSI, Homebrew or WinGet needed more work. Paste this cookbook into your favorite AI prompt to get it done.
We trigger a distribution from GitHub Releases: a Git tag identifies the new version, CI publishes the crate from that tag, builds portable archives and an MSI from that tag, and attaches everything to the release. Homebrew downloads the tagged source archive. WinGet points at the released MSI.
Prepare the Rust Package
Start with complete package…
Episode 559: From Consulting to Rails Products with Eduardo and Camila Souza
What happens when the tool you rely on disappears? For Eduardo and Camila Souza, the shutdown of Pivotal Tracker became the motivation to build Pivotal Tasker, a Rails-based alternative inspired by the project management workflow they missed. They discuss reconstructing the experience from memory and reference material, adding classic and modern interfaces, and integrating coding agents to help create and work through stories.
The conversation expands into their move from consulting toward building products. Eduardo and Camila describe Chamber Builder, an all-in-one platform for chambers of commerce, and a custom sales CRM that brings calling, recordings, transcripts, and…
So long, RubyTapas.com
Five years ago, I announced that RubyTapas had become Graceful.Dev. But a revenant of that feast was left behind: the rubytapas.com site stuck around to handle billing for legacy customers, and I never got around to fully rolling that aspect into Graceful.Dev.
Until now. As of today, RubyTapas.com is no more. If you have a RubyTapas legacy account, it is now managed at Graceful.Dev. Same plan, same price, same access. Nothing changes except the words on the receipts, and there’s nothing you need to do.
And of course, all RubyTapas episodes are here at graceful.dev (as they have been for years). As well as lots and lots of post-RubyTapas content, organized into courses. The old…
Hello, Rails community!
Rails Versions 8.0.5.2 and 8.1.4.1 have been released. These are security updates addressing 21 security issues falling into a few themes:
- Possible arbitrary file read, file write and remote code execution in Active Storage image and video processing
- Possible Strong Parameters bypass in
Parameters#each_key - Possible IP spoofing in
remote_ipthrough theForwardedheader (8.1 only) - Multiple possible XSS vulnerabilities in Action View helpers, and one in the Active Support MessagePack serializer (8.1 only)
- Possible DoS vulnerabilities in Active Support
safe_constantize, Action Controller Live streaming, and Active Storage proxy mode and filename handling - U…
We strongly recommend upgrading as soon as possible.
Older versions of Rails are unsupported, and users are…
ChatGPT Can Now Use Your Apps
Every business has a list of jobs nobody wants and nobody can automate. Someone logs into a supplier portal every Monday to download invoices, then types them into the accounting software one line at a time. Someone else fills in the same government form every quarter, copying numbers from a spreadsheet into boxes on a website. Neither system offers a way for other software to connect to it (an API), the vendor isn’t going to build an integration for a customer your size, and so the work stays manual.
At the end of October, OpenAI released GPT-6.1 Terra, a model built for what OpenAI calls computer use: it looks at a screen, reads what’s on it, and clicks and types its way through apps and…
We recently moved a client’s Ruby on Rails app off Heroku: a Rails API with Sidekiq workers and several services around it. Before we changed anything, we spent about a week on discovery. We had a kickoff call with the client’s team on a Monday, and then two of us paired for the rest of the week to map the infrastructure and draft a migration plan.
Nothing about that app followed one pattern. Its services were deployed in different ways, and some of the things that mattered most for the migration, like the tools outside Heroku that read from the database and the service that promoted code to production, didn’t show up in the Heroku dashboard at all.
In this article, you will learn what we…
AI can generate a lot of code quickly. There’s a thought that we can catch its mistakes or other issues by reviewing that code. In theory, this may be true; code review can catch bugs. In practice, it only works if the reviewer knows what to look for, has some context and, well, gives a shit.
There's a thought that we can catch mistakes in AI-generated code by reviewing it (or having an AI do that). This is short-sighted thinking, especially as developers lose the skill of code review entirely. Better to use AI to create better tooling.This is all in a professional context, i.e. at a job. I don't think the conclusions here are necessarily relevant to hobby or open source…
Why Review…
The Agents on Rails Files: enter lemans, a Ruby harness for fair agent races

A tour of lemans, the open source Ruby harness behind Agents on Rails: eval task anatomy, how it runs and grades coding agents for hours, and how cheaters shaped its guardrails.
Benchmarking coding agents in one sentence: give a model a task, let it work, check the result. The hard part is keeping the race fair. Every model must get the same conditions, and no agent should be able to escape its sandbox or fool its grader (and they try). And all of that has to keep running for hours. In the first post of The Agents…
Continuations 2026/44: More filtery
Milestone: this week I called a freeze on new Hanami work. From here we’re focused on cutting down our outstanding items and then switching to release prep.
I cut down a few items myself: three little
pigsthings to finish our Hanami CLI changes for compatibility with Dry CLI. This CLI is just right.After some great feedback from Michael, I made a few adjustments to Dry Logger’s expanded log filtering (like making Regexp filters case insensitive by default, for consistency with string filters), and merged it. Thanks Michael! I also updated Hanami’s default list of filters. Combined with Dry Loggers substring filtering, this will make for a much more filtery out of the box experience.
I’ve been building a new project in my spare time while working through ideas for a few other apps.
Naturally, I found something to get distracted with.
Oh My Scrobbles is a Mac app for scrobbling Apple Music (and Radio!) to Last.fm and exploring your old listening history.
Like most projects I actually follow through on… this started with a huge itch I needed to scratch. I have over 300,000 scrobbles. I take this shit very seriously. 😉
It can sync my whole Last.fm history to my Mac and expose a local MCP server, so I can ask ChatGPT and Claude questions about it.
I used that to find the exact tracks I’d scrobbled just before and after creating my GitHub account in 2008. Apparently,…

Somebody at Gusto asked, during one of our internal AI AMAs, whether /login was costing them tokens. The answer came back yes, relayed into our Claude Code channel a few minutes later: /login "is an LLM call, which will take more time and end up with unnecessary token use," so run claude auth login from the CLI instead, because that one's instant and free. It came with receipts, sort of. Secondhand from someone who'd watched /login run up $3.
That felt off, so I went and checked. It’s off. /login never touches the model, in any config. It's a local command: it runs entirely on your machine and never builds a prompt, so there is nothing to send.
So where did the $3 come from? A leading space.…
GHSA-345x-3rq2-jfrm (loofah): SVG `` and `` bypass local-reference restriction in Loofah
GHSA-3w22-6jfg-527w (nokogiri): Use-After-Free in Nokogiri when replacing an attribute value
GHSA-4p39-36wj-23h4 (nokogiri): Possible Mutation XSS when serializing `noscript` content in `Nokogiri::HTML5` documents
GHSA-cpgf-5mjf-73p7 (rails-html-sanitizer): Possible regular expression denial of service with certain configurations of rails-html-sanitizer
GHSA-h6v6-rg66-fqj4 (loofah): Mutation XSS in Loofah HTML5 scrubbers through unescaped attribute values
GHSA-h7wh-q2qr-458c (rails-html-sanitizer): Possible XSS via shared scrubber state in concurrent rails-html-sanitizer calls
GHSA-jcff-cj73-4w4f (loofah): Loofah allowed_uri? treats data:text/html as safe text/plain
GHSA-m3x9-938q-5767 (loofah): CSS url() sanitization bypass via escaped parentheses in Loofah
GHSA-mf2j-8f84-cc9q (loofah): Regular expression denial of service in Loofah's SVG local-reference check
GHSA-p9wj-w8xq-vjx6 (loofah): Loofah allowed_uri? treats javascript: alert(1) as safe
GHSA-v995-44v6-5fqm (nokogiri): Possible SSRF on JRuby when resolving external DTDs or entities with `NONET` set
Rails main lets ActiveSupport::EncryptedFile try comma-separated keys when decrypting, including keys supplied through RAILS_MASTER_KEY.
This is unreleased Rails 8.2 development functionality; Rails 8.1.4 still expects a single key.
Successful fallback reads do not establish support for multi-key writes or a complete credentials-editing workflow.
Try fallback reads with a disposable file
This standalone example generates its own keys and uses a separate example variable.
It does not load application credentials or read RAILS_MASTER_KEY.
require "active_support"
require "active_support/encrypted_file"
require "tmpdir"
require "fileutils"
variable = "SAELOUN_SYNTHETIC_KEYS"
raise "Use an…Idempotency
GHSA-4rw8-495r-pf6j (graphql): Execution::Next renders objects whose authorized? check failed
GHSA-q3cc-p5hm-55wj (graphql): Interpreter runtime renders results from objects whose `.authorized?` checks had failed
GHSA-4fqr-96q8-xrfx (sqlite3): Possible SQL query truncation when a string is 4 GiB or larger
GHSA-76hq-g43j-f2rr (wasmtime): Data race in WASI when calling functions with `gvl: false`
GHSA-4w5q-wg7v-w6gp (addressable): Regular Expression Denial of Service in Addressable templates with several exploded variables
GHSA-539f-xcgw-2gjg (addressable): Addressable display_uri can show misleading hosts containing bidi and invisible characters
GHSA-8gmh-2hhj-2hv9 (addressable): Addressable URI normalization resolves some dotted path segments to the wrong path
GHSA-9crm-g4mf-wc5j (addressable): Addressable display_uri can show misleading hosts with invalid Punycode A-labels
GHSA-9f42-57jx-vpp3 (addressable): Regular Expression Denial of Service in Addressable templates with multiple variables
GHSA-j82w-28jv-3pmj (addressable): Regular Expression Denial of Service in Addressable templates with certain exploded expressions
GHSA-qq38-m9mg-xjj5 (addressable): Denial of service in Addressable URI path normalization
Avoid Proton
We are, disturbingly, in a Gilded Age 2.0.0 era and, like the corrupt industrial tycoons of years past, we have tech tycoons forcing LLMs and data centers upon us. Software should be used to enhance and enrich the lives of staff and customers instead of extracting all their worth.
Worse, we have to be much more diligent about what we pay for and be quick to cancel when they disregard their engineers and customers. As was — and still is the case — where you must Avoid Zoom, you need to cancel any/all services with Proton as well. To quote from 404 Media:
Proton, the company behind ProtonMail and other privacy-focused products, told staff on Monday it would be…
Two jobs finish successfully. One account update disappears.
Suppose an account has 100 credits. You enqueue two jobs to add 10 and 20. Both jobs finish without an exception, but the account ends with 120 credits.
Each job runs this code:
class AddCreditsJob < ApplicationJob
def perform(account_id, amount)
account = Account.find(account_id)
account.update!(credits: account.credits + amount)
end
endAssume credits is an integer column, with no optimistic locking or custom database rule protecting the update. These are application credits; the example doesn't model a payment system.
Two workers can execute the job in this sequence:
Job A (+10) Job B (+20)
read 100
…The idea for Svet came from a private repository at work, where checking filename case helps catch portability problems like Logo.svg and logo.svg colliding on a case-insensitive Windows filesystem. These checks are implemented in pure JavaScript. That seemed like a useful job for a reusable repository linter.
I went looking for an existing tool.
Tool Why it isn’t enough here ESLint Can check filenames with a processor; byte-level checks and Git snapshots need custom plumbing. Oxlint Arbitrary non-JS/TS files don’t reach its lint rules. Biome Checks supported languages, not arbitrary repository…I had a Raspberry Pi 4B I thought might be dead. It wasn’t. I’m having a bunch of building work done and of course, the thermostats do not work with Apple HomeKit.
So the Pi now runs Homebridge, which puts our underfloor heating thermostats (and also now Samsung TVs) in Apple Home. For good measure it also runs Pi-hole, which blocks ads and trackers for every device in the house.
Gear required
- A Raspberry Pi 4B + USB-C power + micro-HDMI
- A (new) microSD card
- Raspberry Pi Imager on my Mac
- An Argon ONE case and a USB SSD
I thought the Pi was dead
First I checked the cheap parts. SD cards and power supplies fail far more often than the Pis themselves.
- Removed and replaced…

.jpg)

