Skip to content
CAI
Software that uses CAICheck a score

stringer-rss/stringer

59.0

Adequate · 28 September 2026

3.8k

lines of production code

Ruby

with TypeScript

2

measurements over time

CAI band scale
CAI trend line
CAI lens gauges

What this system is

This system is a multi-user RSS/Atom feed reader application built on Rails 8.1, designed to aggregate, display, and manage news stories from various sources. It supports user authentication, feed organization into groups, and state management for reading and starring items, while maintaining compatibility with the Fever API for third-party client integration. The platform handles background processing for feed fetching and cleanup, secure OPML import/export, and provides a modern, responsive web interface for managing subscriptions.

How it got here

2013 — Rails migration and multi-user architecture

18 changes.

The project underwent a comprehensive migration from a Sinatra prototype to a full Rails 8.1 stack, introducing a structured architecture with repositories and GoodJob for background processing. This period established a multi-user system with secure authentication, database isolation, and modern frontend tooling, supported by extensive RSpec test coverage.

2014–2023 — Rails 8 migration and modernization

34 changes.

The project underwent a comprehensive upgrade to Rails 8, involving a complete overhaul of the asset pipeline with esbuild and Bootstrap 5, alongside significant security hardening. This period also focused on restructuring the backend logic into modular command objects and establishing robust test coverage across the application.

2026 — frontend modernization and linting infrastructure

11 changes.

The project modernized its frontend by migrating from jQuery and Backbone to TypeScript, Stimulus, and Turbo, while upgrading the UI framework to Bootstrap 5. Concurrently, it established robust linting infrastructure through autogeneration scripts for ESLint and Stylelint, updated RuboCop configurations, and added comprehensive test coverage for the new JavaScript components.

Features

Add default Rails public asset files

The application now includes standard boilerplate files in the public directory, providing styled error pages for HTTP 400, 404, 406, 422, and 500 status codes, along with essential assets like favicon, touch icons, a web app manifest, and robots.txt.

public · high confidence

Add import feed upload page

Introduces a new view for importing OPML files, allowing users to upload a file and submit it to the /feeds/import endpoint. The page displays the current user's username in a navigation bar and provides options to proceed with the import or skip to a tutorial.

app/views/imports · high confidence

Add interactive tutorial for new users

A new tutorial experience is now available at the tutorials index page, guiding users through core features like marking stories as read, refreshing feeds, and managing subscriptions. The page renders a dedicated action bar with icons for these actions and overlays instructional text for each step. After a 10-second delay, a 'Start' button appears, allowing users to proceed to the main news feed once they have completed the walkthrough.

app/views/tutorials · high confidence

Add internal helpers for boolean casting and Fever API configuration

New internal command modules have been introduced to support application logic: CastBoolean provides a standardized method for validating and converting various input formats (strings, integers, booleans) into a strict boolean value, while FeverAPI defines the API version constant and the set of valid parameters for the Fever API integration. These changes add structural support for data validation and API interaction without altering existing user-facing behavior.

app/commands · high confidence

The application now includes a new login view at app/views/sessions/new.erb. This page uses Bootstrap 5 classes (form-control, btn, float-end) for styling and implements a login form with username and password fields. It also conditionally displays a link to the signup page if user signups are enabled via the Setting::UserSignup configuration.

app/views/sessions · high confidence

Added linting autogeneration scripts for ESLint and Stylelint

New executable scripts in the \exe\ directory (\eslint\_autogen\ and \stylelint\_autogen\) have been added to automatically generate configuration files that suppress existing linting violations. The ESLint script runs the linter, parses the JSON output, and writes a TypeScript file mapping specific rules to the files where they are violated, allowing developers to gradually fix offenses. Similarly, the Stylelint script generates a YAML configuration file that disables specific rules for the affected files, facilitating a phased integration of Stylelint into the codebase.

exe · high confidence

Introduction of CallableJob for generic job execution

The application now includes a new base job class, CallableJob, which allows any object responding to \#call to be executed as a background job. This is achieved by creating a new ApplicationJob base class inheriting from ActiveJob::Base and a CallableJob that delegates its perform method to the callable object passed to it, enabling a more flexible pattern for defining job logic.

app/jobs · high confidence

A new \UrlHelpers\ module has been added to provide utilities for processing URLs within content. It includes \expand\_absolute\_urls\ to convert relative links (in anchor, image, and video tags) into absolute URLs based on a base URL, and \normalize\_url\ to resolve relative URIs, enforce allowed schemes (http/https), and percent-encode non-ASCII characters for valid URL formatting. These helpers are designed to sanitize and standardize URLs, preventing issues with malformed links or malicious schemes.

app/helpers · high confidence

New password setup form for first-run user creation

A new view at app/views/passwords/new.html.erb has been added to handle the initial password setup during the first run. This form allows users to create their account by entering a username, password, and password confirmation, utilizing Rails form helpers and Bootstrap styling for the input fields.

app/views/passwords · high confidence

New task to remove old, read stories based on a cutoff date

A new callable module, RemoveOldStories, has been added to allow users to clean up the database by removing unstarred, read stories that are older than a specified number of days. When executed, this task identifies the relevant stories, deletes them, and updates the last\_fetched timestamp on the associated feeds to reflect the pruning.

app/tasks · high confidence

New utility modules for authorization, content sanitization, feed discovery, OPML parsing, and safe fetching

This change introduces several new utility classes and modules in app/utils to support core application features. Authorization.rb provides a centralized mechanism for checking record ownership and scoping queries to the current user. ContentSanitizer.rb handles HTML sanitization using Loofah to remove unsafe elements. FeedDiscovery.rb manages the process of fetching and parsing RSS/Atom feeds, integrating with SafeFetch.rb to prevent Server-Side Request Forgery (SSRF) attacks by validating URLs and restricting connections to public addresses. OpmlParser.rb enables the import of OPML files, correctly handling feed groups and titles. SampleStory.rb defines a sample data structure for testing or demonstration purposes, including an is\_read attribute.

app/utils · high confidence

Architecture

Introduce dedicated repository classes for data access

The application now uses explicit repository classes (FeedRepository, GroupRepository, StoryRepository, UserRepository) to encapsulate data access logic. This change centralizes operations such as fetching, updating, and deleting feeds and stories, managing user setup, and handling story grouping and pagination, replacing previous inline model queries with a structured repository pattern.

app/repositories · high confidence

Behavioural changes

Added TypeScript declaration for eslint-plugin-sort-keys-fix

A new TypeScript declaration file (types/eslint-plugin-sort-keys-fix.d.ts) was added to provide type definitions for the eslint-plugin-sort-keys-fix module, enabling proper type checking for this ESLint plugin within the project.

types · low confidence

Admin access constraint for GoodJob dashboard

A new AdminConstraint class has been added to enforce role-based access control, ensuring that only users with admin privileges can access the GoodJob admin interface. This change restricts visibility of job management tools to authorized personnel, enhancing security for background job operations.

lib · high confidence

Adopts esbuild for JavaScript and CSS asset compilation

The application's build process has shifted from previous tooling to esbuild, introducing dedicated scripts in the bin/ directory to manage asset compilation. Users will now use bin/build to bundle TypeScript files into the app/assets/builds directory and bin/build\_css to process stylesheets, with specific loaders configured for web fonts and external image paths. This change replaces older asset management approaches, streamlining the development workflow by leveraging esbuild's speed for both JavaScript and CSS bundling.

bin · high confidence

Database schema evolution and background job infrastructure updates

The database schema has been updated to support multi-user isolation by adding user references to feeds and groups, enforcing null constraints, and introducing a username field. Story handling now supports grouping, enclosure URLs, and a configurable display order. The application has migrated its background job processing from Delayed::Job to GoodJob, including the creation of necessary tables and indexes, while also adding support for user subscriptions and application settings.

db/migrate · high confidence

Database schema updated to support GoodJob, multi-user feeds, and subscriptions

The database schema has been regenerated to reflect significant structural changes: the background job system has migrated to GoodJob (adding tables for jobs, batches, executions, and processes), and the data model now supports multi-user isolation with user\_id foreign keys on feeds and groups. Additionally, new tables for Stripe subscriptions and user settings have been added, while the stories table now defaults is\_read to false.

db · high confidence

Docker container now precompiles assets and uses supervisord with Puma

The Docker image now precompiles assets before launching the application server, addressing previous startup issues. The container entrypoint has been updated to use supervisord to manage both the Puma web server and a cron process for periodic tasks, replacing the previous startup method. Additionally, a new initialization script ensures required environment variables, including database credentials and secret keys, are set in the .env file if not already provided.

docker · high confidence

Enforce username requirement for user login

The sign-in process now requires a username to authenticate users. The new SignInUser command validates the provided username against the database and verifies the password, ensuring that login attempts cannot proceed without a valid username identifier.

app/commands/user · high confidence

Fever API commands refactored into callable modules with authorization

The Fever API implementation in app/commands/fever\_api has been restructured so that each API action (such as Authentication, ReadItems, WriteMarkItem, etc.) is now a standalone callable module. This change introduces explicit authorization checks for multi-user support, ensuring that operations like reading feeds, marking items as read, or syncing item IDs are scoped to the current user's permissions. The FeverAPI::Response module now orchestrates these callable actions, merging their results into a single JSON response.

_app/commands/fever\api · high confidence

Fix compatibility with ESLint 10 in sort-keys-fix plugin

The patch for the eslint-plugin-sort-keys-fix package has been updated to support ESLint 10. It modifies the sort-keys-fix rule to access the source code via the new context.sourceCode property, falling back to the legacy getSourceCode() method for backward compatibility, ensuring the plugin functions correctly with the latest ESLint version.

patches · high confidence

Introduction of multi-user architecture and security enhancements

The application now supports multiple users, introducing User, Group, and Subscription models to manage accounts, feed organization, and billing. Feed and Story models are scoped to individual users, and a new Setting model allows administrators to control user signups. Security is improved by validating URL schemes to prevent unsafe protocols and encrypting API keys. Additionally, the Fever API compatibility layer has been updated to respect user-specific data and starred status.

app/models · high confidence

Migrate JavaScript stack to TypeScript, Stimulus, and Turbo with Bootstrap 5

The application's frontend has been modernized by introducing TypeScript support, replacing jQuery and Backbone.ajax with native fetch and Stimulus controllers, and upgrading the UI framework to Bootstrap 5. This change also integrates Hotwired Turbo for navigation and switches scroll behavior to 'instant' for improved responsiveness, while maintaining backward compatibility for existing Backbone views via NativeView.

app/javascript · high confidence

Migrate client-side story interactions to Stimulus controllers

The application replaces legacy JavaScript (Backbone) with a new set of Stimulus controllers to manage core user interactions. This introduces dedicated controllers for toggling story stars and 'keep unread' status, refreshing stories, marking all items as read, and updating the browser tab title with the unread count. A new HotkeysController also enables keyboard shortcuts for these actions, while the underlying API helpers now handle these updates via direct fetch requests rather than the previous client-side model layer.

app/javascript/controllers · high confidence

Migrate controllers to ActionController and enforce multi-user authorization

All controllers in app/controllers have been rewritten to inherit from ActionController::Base, replacing the previous lightweight implementation. This change introduces a centralized ApplicationController that enforces user authentication via session lookup and requires initial setup completion. A new Authorization class is integrated into every controller to scope data access by user, ensuring that feeds, stories, and settings are only accessible to their owners. The Fever API controller now authenticates users via API keys, and specific actions (such as login, signup, and debug pages) correctly skip authentication or setup checks.

app/controllers · high confidence

Migrate stories views to Stimulus controllers and inline JavaScript

The stories views have been refactored to replace legacy JavaScript patterns with modern Rails and Stimulus conventions. The action bar now uses Stimulus controllers for keyboard shortcuts (hotkeys) and marking stories as read, while individual story interactions (star, keep unread, refresh) are handled by dedicated Stimulus controllers defined in the new templates. The main story list rendering has moved from server-side HTML generation to client-side JavaScript (StoryList/AppView) loaded via a new partial, which also manages keyboard navigation (j/k/n/p/o/b/v/m/s) using Mousetrap. Pagination links in the starred and archived views now include Stimulus data attributes for keyboard navigation support.

app/views/stories · high confidence

Migrate stylesheets to npm packages and restructure asset pipeline

The application's styling infrastructure has been reorganized to use npm-managed packages for core dependencies. The main stylesheet now imports Bootstrap, Font Awesome, and specific Google Fonts (Lato, Reenie Beanie) directly from their npm modules via @fontsource and local paths, replacing previous bundling methods. Additionally, dedicated CSS files have been created to define custom @font-face declarations for FontAwesome, Lato, and Reenie Beanie, ensuring these fonts are served correctly from the asset pipeline. This change supports the broader migration from sinatra-assetpack to sprockets and eventually propshaft, streamlining how third-party UI assets are resolved and served to the user.

app/assets/stylesheets · high confidence

New RuboCop cop disallows RSpec before hooks

A new RuboCop cop, NoBeforeHook, has been added to the linter configuration to detect and flag the use of RSpec \before\ hooks (such as \before\ or \before(:each)\). This change enforces a style where setup logic is inlined directly within examples rather than using before blocks, helping to improve test readability and reduce hidden dependencies between tests.

linters · high confidence

Placeholder added for JavaScript build output directory

A .keep file has been added to the app/assets/builds directory to ensure the folder is tracked by version control. This change supports the project's transition to esbuild for JavaScript management by providing a designated location for build artifacts.

app/assets/builds · medium confidence

Profile page allows configuring news feed order and changing credentials

Users can now customize their news feed experience by selecting the story order and enabling story grouping directly from the profile settings. Additionally, the profile page provides a centralized location for users to update their username (requiring their existing password for verification) and change their password.

app/views/profiles · high confidence

Rails 8.1 migration and modernized development tooling

The application has been migrated from a Sinatra-based prototype to a full Rails 8.1 stack, introducing a structured architecture with command objects, repositories, and GoodJob for background processing. The development environment is now anchored by a \.tool-versions\ file pinning Ruby 4.0.7, Node.js 26.8.2, and pnpm 12.6.0, with a new Dockerfile and docker-compose setup for containerized deployment. Frontend tooling has been modernized with TypeScript, esbuild, Vitest, ESLint, and Stylelint, replacing the legacy Backbone/Underscore setup with Stimulus controllers and Turbo (disabled). The project also introduces a Code of Conduct, an AGENTS.md guide for coding agents, and standardized linting baselines to enforce code quality.

(repo-wide) · high confidence

Refactor feeds UI to use modern Rails helpers and Stimulus controllers

The feeds interface has been updated to use Rails' \form\_with\ helper for forms and \button\_to\ for delete actions, improving security and consistency. Accessibility has been enhanced by adding ARIA labels to icon-only buttons and ensuring screen-reader-only text for status indicators. The UI now leverages Stimulus controllers for handling keyboard shortcuts (hotkeys) and marking stories as read, replacing previous JavaScript implementations. Additionally, the feed list and action bars have been restructured to use Bootstrap 5 components and data attributes for interactivity.

app/views/feeds · high confidence

Refactored feed operations into modular command objects

Feed-related logic has been reorganized into specific command classes within the \app/commands/feed\ directory to improve code structure and maintainability. This change introduces dedicated modules for fetching (\FetchOne\, \FetchAll\, \FetchAllForUser\), importing and exporting (\ImportFromOpml\, \ExportToOpml\), creating (\Create\), and identifying new stories (\FindNewStories\). Users benefit from a more robust and organized backend structure that supports parallel fetching via thread pools and handles feed discovery and name resolution more consistently during creation and import processes.

app/commands/feed · high confidence

Refactored story state management into dedicated command objects

The story command logic has been reorganized into specific, single-responsibility modules to improve code clarity and maintainability. New command objects now handle individual and bulk operations for marking stories as read, unread, starred, or unstarred, as well as marking entire feeds or groups as read. Additionally, a dedicated command for refreshing story data from the feed source has been introduced, encapsulating the logic for fetching and updating entry details. This structural change isolates these behaviors, making the codebase easier to test and extend without altering the external user-facing capabilities.

app/commands/story · high confidence

Removal of legacy CSS assets

The application no longer includes the Bootstrap v2.3.0 stylesheet, the Flat UI theme (without icons), or the previous custom styles.css file. This change removes the underlying visual styling for the user interface, likely as part of a migration to a new design system or framework.

app/public · high confidence

Removal of legacy index view template

The \app/views/index.erb\ template has been deleted. This file previously rendered the main story list and action bar for the application's index page. Its removal indicates that the index view functionality has been migrated to a different rendering strategy or component, likely as part of the broader frontend restructuring mentioned in the commit history (such as moving to Backbone).

app/views · high confidence

Security hardening and configuration updates for Rails 8

This update applies Rails 8 configuration defaults and introduces several security and behavioral improvements. It mitigates XML External Entity (XXE) vulnerabilities in feed parsing by disabling external entity replacement in SAXMachine, enforces a 2-week session expiration for improved security, and requires specific environment variables for encryption keys. Additionally, it configures sensitive parameter filtering for logs, sets up Content Security Policy and Permissions Policy headers, and disables the preservation of background job records to reduce database storage.

config/initializers · high confidence

Updated Rails environment configurations for development, production, and test

The environment configuration files have been updated to reflect modern Rails defaults and best practices. In development, code reloading is enabled by default, eager loading is disabled, and verbose logging is turned on for database queries, background jobs, and missing translations to aid debugging. Production mode now enforces SSL, uses far-future cache headers for assets, logs to STDOUT with request IDs, and configures GoodJob for async background job execution. The test environment disables reloading, disables forgery protection, and uses a null cache store to ensure isolation and speed during test runs.

config/environments · high confidence

Upgrade to Bootstrap 5 and modernize layout structure

The application layout has been upgraded from Bootstrap 4 to Bootstrap 5, replacing the previous flat-ui and custom stylesheets with the standard Bootstrap 5 asset pipeline. This change updates the HTML structure to use Bootstrap 5 grid classes (e.g., \col-md-12\ instead of \span12\) and introduces new layout partials for flash messages, a keyboard shortcuts modal, and a dynamic footer. The main layout now includes accessibility improvements such as setting the \lang\ attribute on the HTML tag and adding a viewport meta tag for mobile responsiveness. Additionally, the layout now renders a manifest file for PWA support and uses Stimulus-compatible data attributes for the shortcuts modal.

app/views/layouts · high confidence

Upgrade to Rails 8.0 and migrate database to PostgreSQL

The application has been upgraded to Rails 8.0, which updates the default configuration via \config.load\_defaults(8.0)\ and introduces new defaults such as disabling \belongs\_to\ required associations by default. The database adapter has switched from SQLite to PostgreSQL for development and test environments, while production now relies on the \DATABASE\_URL\ environment variable. Additionally, the configuration now uses GoodJob as the active job queue adapter and enables Active Record encryption via environment variables for primary, deterministic, and salt keys.

config · high confidence

Test coverage

1 commit adding/updating tests in spec/linters; Added FactoryBot definitions and specs for core models; Added JavaScript test suite for application and setup; Added JavaScript unit tests for Stimulus controllers and helpers; Added comprehensive test coverage for core models and commands; Added integration tests for feed importing; Added request specs for core application controllers; Added system tests for account setup, feed management, and import/export workflows; Added test coverage for Fever API command modules; Added test coverage for Story model and StoryView; Added test coverage for feed command operations; Added test coverage for repository layer; Added test coverage for story read/unread and refresh commands; Added test coverage for utility modules; Added tests for CallableJob; Added tests for URL helper security and normalization; Added tests for the RemoveOldStories task; Added tests for user sign-in command; Initialize RSpec test configuration; New test support infrastructure for system tests and coverage.

Dependencies

Migrate from Sinatra to Rails 8.1 and adopt pnpm for frontend tooling

The application framework has been upgraded from Sinatra to Rails 8.1, introducing a full suite of Rails components including ActionCable, ActiveStorage, and Turbo, alongside a new background job system using Good Job and asset management via Propshaft and esbuild. Frontend dependencies have been consolidated into a new package.json managed by pnpm 12.6.0, replacing previous asset handling with modern tooling including TypeScript, Vitest, and ESLint.

(dependencies) · high confidence

Housekeeping

Added boilerplate .keep files for Rails directory structure; Added placeholder files to the log directory.

Written by watchdog.canine.dev from the codebase's own history, inside the signed delivery this page is composed from.

How this codebase got here

Score

  • CAI 60 → 59 (-1.1)
  • Rubric changed (rubric-2026.09.8 → rubric-2026.09.16) — scores are not directly comparable.

Lenses

  • Code Health 100 → 100 (+0.0)
  • Architecture 100 → 70 (-29.6)
  • Maturity 63 → 63 (+0.0)
  • Readiness 76 → 62 (-13.6)
  • Security 45 → 71 (+26.4)
  • Domain Modelling 100 → 100 (+0.0)
  • Accessibility 74 → 49 (-24.8)
  • Performance 100 (new)

Resolved (20)

  • Documentation: no architecture or design documentation (README.md)
  • High CVE: [GHSA redacted] (pnpm-lock.yaml)
  • High CVE: [GHSA redacted] (pnpm-lock.yaml)
  • High CVE: [GHSA redacted] (pnpm-lock.yaml)
  • High CVE: [GHSA redacted] (pnpm-lock.yaml)
  • High CVE: [GHSA redacted] (pnpm-lock.yaml)
  • High CVE: [GHSA redacted] (pnpm-lock.yaml)
  • High CVE: [GHSA redacted] (pnpm-lock.yaml)
  • High CVE: [GHSA redacted] (pnpm-lock.yaml)
  • High CVE: [GHSA redacted] (pnpm-lock.yaml)
  • High CVE: [GHSA redacted] (pnpm-lock.yaml)
  • High CVE: [GHSA redacted] (pnpm-lock.yaml)
  • Low vulnerability: [GHSA redacted] (pnpm-lock.yaml)
  • Medium CVE: [GHSA redacted] (pnpm-lock.yaml)
  • Medium CVE: [GHSA redacted] (pnpm-lock.yaml)
  • Medium vulnerability: [GHSA redacted] (pnpm-lock.yaml)
  • Off-boarding risk: anonymized user #1
  • Outdated: rubocop
  • Outdated: selenium-webdriver
  • Outdated: stripe

New (5)

  • Documentation: no contributor guidance (README.md)
  • Documentation: no project overview (README.md)
  • No ADRs found
  • Off-boarding risk: anonymized user #1
  • Projects may be oversized for their cohesion

Changes since last survey

  • 17 commits — 17 feature/other, 0 fixes

By area

  • (root) — 14 commits
  • .circleci/config.yml — 1 commit
  • .github/workflows — 1 commit
  • bin/build — 1 commit

Notable commits

  • change: Deps: update bundler to version 4.0.21 (#1622)
  • change: Deps: update pnpm to version 12.4.1 (#1611)
  • change: Deps: update pnpm to version 12.5.1 (#1621)
  • change: Deps: update pnpm to version 12.6.0 (#1632)
  • change: Rubocop: allow increment!, decrement! methods (#1630)
  • change: Speed up Docker builds with layer caching (#1625)
  • change: Update Node.js to version 26.8.2 (#1613)
  • change: Update Ruby to version 4.0.7 (#1627)
  • change: Update all Bundler dependencies (2026-09-28) (#1631)
  • change: Update all pnpm dependencies (2026-09-12) (#1609)
  • change: Update all pnpm dependencies (2026-09-26) (#1629)
  • change: add ScoutAPM (#1612)
  • change: assign myself to tooling updates (#1618)
  • change: esbuild: remove --public-path=/assets (#1619)
  • change: split up CircleCI config (#1626)
  • change: upgrade Postgres version (#1628)
  • change: 🚨 [security] [js] Update vitest 4.1.11 → 5.0.1 (major) (#1624)

Written by watchdog.canine.dev from the codebase's own history, inside the signed delivery this page is composed from.

Survey your own repository

stringer-rss/stringer was measured the same way every project in this corpus was: the same rubric, at a pinned commit, with the result published in full. Point a surveyor at a repository you know and see whether you agree with it.

About this page

  • The score is its most recent published measurement, taken on 28 September 2026 at a pinned commit. It is not a live figure and does not change until the project is measured again.
  • Measured at commit 2a064da708ef2d24daf28c0c6864ae9c85d5ee41 — the exact code this score is about.
  • Scored under rubric-2026.09.16 — the same rubric and the same method as every other entry in this index.
  • Measured by watchdog.canine.dev using codehealth-analyzer preprod-2d9048c36d26.