# Calvin's Dev Logs > Calvin's dev logs — code snippets, demos, tutorials, and solutions across software topics. Tracking my coding journey and discoveries. Each article below is plain Markdown, with YAML front matter, at its page URL plus .md. ## Articles - [The "json" gem v3.0 upgrade broke Rails App](https://calvin.my/posts/the-json-gem-v3-0-upgrade-broke-rails-app.md): A json gem 3.0 upgrade can cause Rails tests to fail with misleading argument errors originating in session cookie decryption rather than application code. Full backtraces reveal the failure occurs when Rails deserializes JSON metadata. Rails has merged and backported a fix, but no release includes it yet. Teams can use the stable Rails branch or pin json to the 2.x series, while configuring dependency automation to avoid repeated 3.x upgrade proposals. - [Cutting GitHub Actions bill in a Rails app](https://calvin.my/posts/cutting-github-actions-bill-in-a-rails-app.md): The post explains how a Rails team reduced private GitHub Actions costs without losing useful failure reporting. It combines short checks to avoid repeated runner setup, cancels outdated feature-branch runs, and adds job timeouts. It also reduces Dependabot frequency, separates unit and system tests so both results appear after failures while adjusting coverage checks, and explicitly limits workflow token permissions. The changes trade some parallelism for lower billed wall-clock time and more reliable feedback. - [Migrating Rails System Tests from Selenium to Playwright](https://calvin.my/posts/migrating-rails-system-tests-from-selenium-to-playwright.md): The post outlines migrating Rails system tests from Selenium to Playwright through Capybara, often without rewriting the suite. It recommends auditing Selenium-specific calls and subtle behavioral differences, replacing dependencies, installing a version-matched Playwright CLI and Chromium, and updating the system-test driver and CI workflow. Translation work should remove obsolete Selenium flakiness workarounds while preserving application-level waits. Full-suite runs and fixed test seeds help validate the migration. - [Migrate a Single Repository from Bitbucket to GitHub](https://calvin.my/posts/migrate-a-single-repository-from-bitbucket-to-github.md): This guide explains how to migrate a Bitbucket repository to GitHub while preserving its full branch, tag, and commit history. It covers required local tools and access, transferring repository references to an empty or intentionally overwritten destination, cleaning up temporary files, and recloning from GitHub. An optional step renames the default branch from master to main, followed by checks to confirm repository integrity and tracking. - [Chrome on-device AI with Gemma4 (Aug 2026)](https://calvin.my/posts/chrome-on-device-ai-with-gemma4-aug-2026.md): The post evaluates Chrome’s developer-trial on-device Gemma4 summarizer against Nano v3 and a server-side model using a 1,300-word Rails deployment article. Gemma4 better captured the article’s update-from-2017 framing, while Nano often recited its structure. All outputs were usable but Gemma4 showed minor wording errors. Chrome’s API cannot identify or select the active model, so testing requires disabling alternatives. On-device summaries offer privacy, offline use, and no API cost, but remain unsuitable for unattended metadata. - [Deploying Rails with Capistrano in 2026](https://calvin.my/posts/deploying-rails-with-capistrano-in-2026.md): An updated guide to deploying a modern Rails application with Capistrano explains changes since 2017, including encrypted credentials, Propshaft, Solid Queue, and Bundler 4. It covers initial project and server setup, shared files, Passenger, deployment checks, rollbacks, and worker restarts. The post also corrects outdated defaults by configuring Bundler, cleanup, conditional migrations, and Passenger behavior, and recommends pruning Bootsnap caches caused by release-specific paths. - [Few ways to ask an AI provider how much credit you have left](https://calvin.my/posts/few-ways-to-ask-an-ai-provider-how-much-credit-you-have-left.md): Managing prepaid balances across AI providers requires different monitoring strategies because billing interfaces vary widely. DeepSeek and Kimi provide balances directly, while xAI requires a separate management credential and adjustment for its billing format. Anthropic and OpenAI expose spending rather than balances, so manual balance entries can serve as anchors for estimated remaining credit. Gemini offers no usable programmatic billing data, requiring manual console checks. - [Rewriting a web app against a frozen API contract](https://calvin.my/posts/rewriting-a-web-app-against-a-frozen-api-contract.md): Rebuilding a live, unmaintained Rails application required preserving its frozen third-party interface while correcting internals. The team documented existing behavior separately from defects, delivered staged components with tests, and replayed captured traffic and database changes to expose every divergence. Screens were validated against production screenshots, and removals were documented. The rewrite also improved security and rate limiting, though browser test flakiness persisted. The main lesson: modernization was secondary to observing, reproducing, and proving real-world behavior. - [Ruby Bundler’s cooldown Feature for Safer Gem Updates](https://calvin.my/posts/ruby-bundler-s-cooldown-feature-for-safer-gem-updates.md): Ruby Bundler 4.0.13 introduces a cooldown setting that filters out gem versions published within a chosen number of days. The feature aims to reduce supply-chain risk by allowing time for newly released gems to be vetted and for potential account-takeover incidents to emerge. Developers can apply the setting through dependency configuration or Bundler options, and can override it when immediate updates are necessary. - [SQLite3 as Production Database](https://calvin.my/posts/sqlite3-as-production-database.md): The post outlines production SQLite practices for Rails applications. It recommends splitting primary data, jobs, cache, and cable workloads into separate database files to isolate locking and WAL activity. Performance can be tuned through SQLite settings that increase caching, retry lock contention, and keep temporary data in memory, while accounting for memory limits. It also stresses establishing and testing a backup-and-restore process. - [Interesting features in Claude Code CLI](https://calvin.my/posts/interesting-features-in-claude-code-cli.md): The post highlights several Claude Code CLI features aimed at personalization and workflow review. Users can customize the status line with contextual details, enable push-to-talk voice input, and access an official sticker store. An alternate terminal layout can keep the input area fixed at the bottom, reducing visual flicker. The insights feature analyzes the previous month’s sessions, summarizing usage patterns and suggesting areas to improve. - [Warning: connection is not using a post-quantum key exchange algorithm](https://calvin.my/posts/warning-connection-is-not-using-a-post-quantum-key-exchange-algorithm.md): OpenSSH may warn when a connection lacks post-quantum key exchange, highlighting potential future “store now, decrypt later” risks rather than an immediate threat. The post recommends upgrading remote servers to OpenSSH 10 or later. Clients can also require quantum-resistant key exchange methods to check whether a server supports them; failed negotiation indicates the server is not ready. Alternatively, users may suppress the warning, though this does not improve security. - [Warning 'mysql_native_password' authentication type is disabled by default in MySQL 8.4](https://calvin.my/posts/warning-mysql_native_password-authentication-type-is-disabled-by-default-in-mysql-8-4.md): Upgrading from MySQL 8.0 to 8.4 may trigger warnings for accounts using the deprecated mysql_native_password method, which is disabled by default in 8.4 and planned for removal. The post recommends first updating to the latest 8.0 release, identifying affected users, migrating them to caching_sha2_password, notifying users if credentials change, and testing each updated database connection. - [The Chrome DevTool MCP General Availability](https://calvin.my/posts/the-chrome-devtool-mcp-general-availability.md): Chrome DevTools MCP is generally available in Chrome 146’s stable channel, allowing compatible AI command-line tools to connect to a running browser after remote debugging is enabled. The post outlines setup for Gemini CLI and Claude CLI, then demonstrates Claude inspecting a local page through screenshots, identifying unused layout space, modifying the form’s sizing, reloading the page, and verifying the visual improvements. This enables AI-assisted browser debugging and iterative interface refinement. - [Enabling agentic coding in Xcode](https://calvin.my/posts/enabling-agentic-coding-in-xcode.md): The post explains how to enable Xcode’s agentic coding feature in version 26.3. Users open the Coding Agent panel, select a supported provider with an active subscription, link the account, install the provider’s agent tool, and authorize Xcode to use its quota. Once configured, the panel provides a chat interface for questions and tasks, supports feedback reporting, and lets users switch to the Claude Agent for development work. - [Gemini 3.1 Flash image (Nano Banana 2) preview](https://calvin.my/posts/gemini-3-1-flash-image-nano-banana-2-preview.md): The post reviews Google’s February 2026 preview of Gemini 3.1 Flash Image, also called Nano Banana 2. It highlights API additions including 512-pixel output, expanded aspect ratios, search grounding with image search, and combined text-and-image responses. Tests demonstrate small icon generation, extreme panoramic and vertical formats, and a search-grounded currency exchange board. The examples suggest the model can follow detailed composition requests and produce current-rate imagery with reasonable accuracy. - [Rails Solid Queue async mode](https://calvin.my/posts/rails-solid-queue-async-mode.md): Solid Queue version 1.3.0 reintroduces async mode, which runs the supervisor, worker, dispatcher, and scheduler within one process to reduce memory use. This can make deployments more suitable for resource-constrained environments. After upgrading, administrators enable the mode through the jobs startup configuration and can confirm it by checking the supervisor’s reported mode name. - [Adding a catch all black hole blocks in Nginx](https://calvin.my/posts/adding-a-catch-all-black-hole-blocks-in-nginx.md): The post describes using a default Nginx server block as a catch-all “black hole” for requests that do not match configured domains. It pairs the block with a long-lived self-signed certificate for encrypted traffic and silently drops unmatched requests. This helps limit bot scans aimed at server IP addresses, prevents spoofed or unexpected host headers from reaching applications, and avoids exposing legitimate site certificates during HTTPS requests to the IP. - [Using simplecov code coverage with Rails & Minitest](https://calvin.my/posts/using-simplecov-code-coverage-with-rails-minitest.md): The post explains how to add SimpleCov coverage reporting to a Rails 8 application using Minitest. It covers installing the coverage tool, initializing it early in test setup, enabling branch coverage, and assigning distinct names so unit and system test results can be merged. It also describes optional file exclusions and custom coverage groups. Running all test suites together produces a combined report in the generated coverage output. - [A simple Ruby::Box demo](https://calvin.my/posts/a-simple-ruby-box-demo.md): The post demonstrates Ruby 4.0’s experimental Ruby::Box feature as a way to isolate untrusted Ruby code within a single process. In an LLM code-execution scenario, direct evaluation can expose application globals and sensitive credentials, while code run inside a box cannot access those values. Ruby::Box may support safer function calling, but it still needs controls such as dependency whitelists and malicious-code detection, and is not yet recommended for production. - [Litestream 0.5.x updates](https://calvin.my/posts/litestream-0-5-x-updates.md): This post outlines installing and operating Litestream 0.5.x for SQLite database backups. It explains selecting the appropriate release, running Litestream as a system service, and configuring replicas for local storage or Amazon S3 with credentials and synchronization settings. Readers can verify snapshots in an S3 bucket and restore a database by reinstalling Litestream, supplying valid S3 permissions, and using the configured backup source. - [Build a simple app that describes an image and works offline](https://calvin.my/posts/build-a-simple-app-that-describes-an-image-and-works-offline.md): The article explains how to build an Android image-description app with Gemini’s on-device Image Description API. Developers initialize the descriptor, check device support and model availability, prompt users to download the model when needed, and submit a bitmap for inference. After download, descriptions work without an internet connection. The feature requires Android 29 or later, is limited to supported flagship devices, and currently returns English-only output. - [API Error: claude block must have a corresponding `tool_use` block in the previous message](https://calvin.my/posts/api-error-claude-block-must-have-a-corresponding-tool_use-block-in-the-previous-message.md): The post addresses an occasional Claude Code conversation error that prevents further interaction when a prior tool-use message is missing or mismatched. Suggested recovery options are starting a new conversation, compacting history while retaining a summary, or rewinding to an earlier checkpoint. Rewind can restore the conversation, code, or both, removing the problematic recent exchange and allowing work to continue from a stable state. - [Gemini 3 Pro image (Nano Banana Pro) early preview](https://calvin.my/posts/gemini-3-pro-image-nano-banana-pro-early-preview.md): An early preview of Google’s Nano Banana Pro examines its image-generation API and practical results. The model supports up to 4K output, multiple reference images, conversational editing, and blending realistic and illustrative elements; it also produced a correct math solution in an imitated handwriting style. Developers must preserve thought signatures for continued conversations, account for increased token use and large files, and handle response times of two to four minutes, preferably through background streaming. - [Adding sub-resource integrity with Importmap & Rails](https://calvin.my/posts/adding-sub-resource-integrity-with-importmap-rails.md): The post explains how to enable Subresource Integrity for Rails assets built dynamically, where hashes must be generated for each build. It outlines configuring a supported integrity hash algorithm, adding integrity attributes to stylesheet tags, and enabling integrity support in Importmap configuration. With these changes, Rails adds cryptographic hashes to generated stylesheet and module preload resources, helping browsers verify asset integrity. - [Adding sub-resource integrity with Vite build](https://calvin.my/posts/adding-sub-resource-integrity-with-vite-build.md): Vite does not natively generate Subresource Integrity hashes for assets created during builds, which are needed to verify resource integrity in browsers. The post uses an open-source manifest plugin to add hashes during the build process. After configuration, the build manifest includes integrity values and generated HTML references carry them automatically. The plugin also supports selecting a stronger SHA-512 hashing algorithm. - [Install the missing ext-intl PHP extension in MacOS](https://calvin.my/posts/install-the-missing-ext-intl-php-extension-in-macos.md): Homebrew disabled PHP’s intl extension in its PHP formulas, which can break applications that rely on internationalization support after an update. The post outlines two remedies: use a different PHP distribution or compile PHP manually, or install Homebrew’s separate intl extension package alongside PHP. This change means macOS developers may need to explicitly add the extension when upgrading or configuring PHP environments. - [New ways to install Claude Code on macOS](https://calvin.my/posts/new-ways-to-install-claude-code-on-macos.md): Claude Code on macOS can now be installed without NPM through an official shell-based installer or Homebrew, while the existing NPM method remains supported. Users moving from the NPM version should remove it first, then choose one of the new installation options. The tool also supports automatic updates; Homebrew users can update it through their package manager. - [Breaking change in net-http gem to exclude default content type](https://calvin.my/posts/breaking-change-in-net-http-gem-to-exclude-default-content-type.md): Net-http version 0.7.0 removes its default request content type, potentially changing behavior for applications and libraries that rely on it indirectly. Requests that previously sent form data without an explicit header may now be interpreted differently by APIs, as illustrated by a Google reCAPTCHA verification failure. Developers should review affected HTTP calls and explicitly set the appropriate Content-Type, rather than depending on net-http defaults. - [Claude Code's vision - The Chrome DevTool MCP](https://calvin.my/posts/claude-code-s-vision-the-chrome-devtool-mcp.md): Google’s preview Chrome DevTools MCP connects coding agents to browser data, addressing the difficulty of communicating frontend issues through text alone. The post explains configuring it for Claude Code on macOS, including possible permission requirements. Once connected to a local development site, Claude can launch a controlled browser instance and use DevTools capabilities such as screenshots and console error inspection to investigate UI problems directly. - [Gemini Robotics-ER 1.5: High real world accuracy?](https://calvin.my/posts/gemini-robotics-er-1-5-high-real-world-accuracy.md): An informal comparison tests Gemini Robotics-ER 1.5 on physical-world visual tasks: counting overlapping cookies, estimating walking distance from a photo, and reading a wet tire sidewall. The model produced the correct cookie count, included the correct step estimate within its range, and identified the 18-inch rim size. GPT-5 mini and Grok 4 missed each task, suggesting Robotics-ER may offer stronger visual grounding in these examples. - [Error: certificate verify failed (unable to get certificate CRL) (OpenSSL::SSL::SSLError)](https://calvin.my/posts/error-certificate-verify-failed-unable-to-get-certificate-crl-openssl-ssl-sslerror.md): Ruby applications connecting to Google services may encounter an OpenSSL certificate verification failure related to certificate revocation lists. The problem stems from a behavior change in the underlying OpenSSL library rather than the service connection itself. A fix is available in version 3.3.1 of Ruby’s OpenSSL gem. To ensure the patched release is selected, applications should explicitly include the OpenSSL gem in their dependency configuration. - [Creating an AI Assistant That Produces PDF Files](https://calvin.my/posts/creating-an-ai-assistant-that-produces-pdf-files.md): The post explains how to extend a text-generating AI assistant to create PDF documents through function calling. The model generates HTML content, then invokes a Ruby-based conversion function that renders it as a PDF, attaches the file to the conversation, and returns a completion result. It also outlines handling tool-call events and preserving call identifiers. Input validation and HTML sanitization are essential for production use. The pattern can support other document formats by replacing the conversion function. - [Gemini "Nano Banana" generally available](https://calvin.my/posts/gemini-nano-banana-generally-available.md): Gemini Nano Banana is now generally available, with API users required to switch from the preview model identifier to the production version. The image-generation model also adds support for a broader selection of aspect ratios than GPT Image 1. These formats enable outputs tailored to practical uses such as vertical mobile wallpapers and ultrawide banner images, expanding flexibility for developers and content creators. - [API Error: Claude's response exceeded the 32000 output token maximum](https://calvin.my/posts/api-error-claude-s-response-exceeded-the-32000-output-token-maximum.md): Claude Code may fail when a response exceeds its default 32,000-token output limit. The post recommends first simplifying prompts that trigger unnecessary large output, such as generated test data. If more capacity is needed, users can raise the configured output limit and switch to a compatible model such as Sonnet. It also notes that Claude Opus 4.6 supports up to 128,000 output tokens. - [AI assistants in my daily life](https://calvin.my/posts/ai-assistants-in-my-daily-life.md): The post illustrates a day of using specialized AI assistants for system settings, movie research, calendar scheduling, news tracking, document drafting, email review, image editing, contextual translation, voice-based recipe help, shopping lists, and fact-checking. It also includes a lottery-number request and end-of-day usage review. The examples portray AI as a collection of task-focused tools supporting both work and everyday personal activities. - [Gemini "Nano Banana" image editing capability](https://calvin.my/posts/gemini-nano-banana-image-editing-capability.md): The post evaluates Gemini Flash 2.5 Image, nicknamed Nano Banana, through iterative edits to a generated portrait. The model largely preserved the subject’s identity while changing pose, adding a shirt logo that followed fabric texture, and introducing a window whose light altered the scene. A subtle lighting-only request produced little visible effect, suggesting prompt specificity matters. Overall, repeated processing retained recognizable character consistency, expanding potential image-editing uses. - [Allow composer to install private Github repository](https://calvin.my/posts/allow-composer-to-install-private-github-repository.md): The post explains how to let Composer access private GitHub repositories in local and deployment environments. For local use, it recommends creating a fine-grained personal access token limited to the target repository with read-only contents access, then storing it in Composer authentication settings. For remote servers, it uses a separate SSH key as a repository deploy key, plus SSH and Git configuration to route repository access through that key. - [Android 16 KB compatibility](https://calvin.my/posts/android-16-kb-compatibility.md): Android 15 introduced support for 16 KB memory pages, and Google Play now checks uploaded apps for compatibility. Java and Kotlin-only apps are generally unaffected, but apps using native code directly or through dependencies may need updates. Developers can identify native libraries in Android Studio, rebuild direct NDK usage with NDK r28, update affected libraries, and verify alignment through a compatible emulator, Pixel developer settings, or Play Console checks. - [Using the Summarizer API in Google Chrome](https://calvin.my/posts/using-the-summarizer-api-in-google-chrome.md): This update explains how to use Chrome’s on-device Summarizer API, powered by Gemini Nano, to generate headlines, key points, or TLDRs from long text. It outlines requirements: desktop Chrome 138 or later and enabling the relevant experimental feature. Developers should verify browser support and model availability, define summary type, format, length, and context, monitor model downloads, then create a summarizer and process streamed results. Initial use requires a user gesture. - [Claude code AI agent 101](https://calvin.my/posts/claude-code-ai-agent-101.md): An introductory guide to Claude Code explains how to install and launch the coding agent from a project’s root, choose subscription or API billing, and optionally select a model. It covers persistent project instructions, permission controls for sensitive files and actions, and ways to reference files or images. Readers also learn to monitor session costs, access partner pricing, resume prior sessions, and submit bug reports. - [Using git's pre-commit hook](https://calvin.my/posts/using-git-s-pre-commit-hook.md): The post explains how to use Git’s pre-commit hook to enforce checks before changes are committed. It outlines creating an executable hook script that runs RuboCop for linting and Brakeman for security analysis, blocking commits when either check fails. A test demonstrates the hook stopping a problematic commit. It also notes that developers can bypass the checks when necessary using Git’s verification-skip option. - [Building a universal link application with Gemini CLI](https://calvin.my/posts/building-a-universal-link-application-with-gemini-cli.md): The post shows how Gemini CLI, Google’s coding agent with free usage grants, can build a Firebase Dynamic Links alternative from a text prompt. Given requirements for link creation, database storage, mobile app association files, data retrieval, and fallback redirects, it generated a Laravel and SQLite application in about 15 minutes. Tests confirmed link creation, associated-data retrieval, platform association hosting, and redirect behavior. - [Implementing Android App Link](https://calvin.my/posts/implementing-android-app-link.md): The post explains how to implement Android App Links so supported web URLs open directly in an installed app while falling back to the browser otherwise. It outlines configuring an Android activity to handle matching HTTPS paths, reading a URL query parameter in the app, and verifying domain ownership through a publicly hosted association file containing the app package and signing-certificate fingerprint. Testing involves opening the same QR-encoded URL before and after installing a signed app build. - [How much does it cost to build a service website using AI?](https://calvin.my/posts/how-much-does-it-cost-to-build-a-service-website-using-ai.md): The post tests building a static website for an imaginary security company using an AI coding agent, Hugo, a terminal, and a browser. After documenting design, content, responsiveness, and SEO requirements, the author asks the agent to generate the site and later update its body font. The initial build took about five minutes, and the font revision less than a minute. Total AI usage cost was $0.29. - [Good bye Skype (Exporting skype data & histories)](https://calvin.my/posts/good-bye-skype-exporting-skype-data-histories.md): Microsoft’s retirement of Skype makes it important for past users to preserve their account information and conversation history. The post outlines the official export process: sign in through Skype’s data export page, choose the data categories needed, submit a request, and later return to download the prepared archive. Readers should act before the export service closes in January 2026. - [Migrating from system role to developer role in OpenAi messages](https://calvin.my/posts/migrating-from-system-role-to-developer-role-in-openai-messages.md): OpenAI has introduced a developer message role to replace the system role for providing instructions and context to newer reasoning models. Applications using supported models should migrate their top-level prompt from system to developer while leaving user messages unchanged. However, o1-mini accepts neither system nor developer roles and returns an unsupported-role error, so integrations must account for model-specific message-role compatibility. - [Fixing a bug using AI with Google Jules](https://calvin.my/posts/fixing-a-bug-using-ai-with-google-jules.md): The post demonstrates using Google Jules, a cloud-based GitHub-integrated coding agent, to repair a nonfunctional Delete button. After granting repository access, users select a branch, describe the requirements, review Jules’ proposed plan, and inspect its changes before publishing them to a bug-fix branch. The example requires disabling deletion for content older than two hours. Jules also generated tests without prompting and can run multiple repository tasks concurrently. - [Documenting several modern additions of the Ruby language syntax](https://calvin.my/posts/documenting-several-modern-additions-of-the-ruby-language-syntax.md): The article surveys modern Ruby syntax additions that reduce boilerplate. It covers named, numbered, and implicit block parameters; beginless and endless ranges for slicing and query conditions; hash shorthand for matching local variable names and keys; argument forwarding for wrapper methods; and single-line endless method definitions. These features offer more concise alternatives while preserving familiar Ruby behavior. - [Cleanup completed Solid Queue job records](https://calvin.my/posts/cleanup-completed-solid-queue-job-records.md): Solid Queue retains completed job records by default, which can cause database growth over time. The post outlines setting a retention period, with one day as the default, then running the built-in batch cleanup operation to remove older finished jobs. Cleanup can also be scheduled as a recurring Solid Queue task for ongoing maintenance. Restarting Solid Queue applies the updated configuration. - [Collection of less commonly used HTML elements](https://calvin.my/posts/collection-of-less-commonly-used-html-elements.md): The article highlights lesser-used HTML elements that can improve semantics, usability, and accessibility without complex implementation. It covers annotation support for pronunciation and supplementary text, native progress indicators, labeled form groupings, expandable content sections, and grouping related headings. These elements provide built-in structure and interaction for common interface needs, helping developers create clearer forms, compact disclosures, multilingual content, and more meaningful document organization. - [Deploy 2 GitHub repositories on the same server](https://calvin.my/posts/deploy-2-github-repositories-on-the-same-server.md): The post explains how to deploy two GitHub repositories from one server despite GitHub’s restriction on sharing a deploy key across repositories. It recommends creating separate SSH key pairs, loading them into the SSH agent, and defining host aliases that map each repository to its own identity file. After adding each public key to the corresponding repository, deployment scripts should use the aliases, with GitHub added to known hosts if needed.