Skip to content

Merge template: replace Avo with Madmin, add teams/billing/MCP/multilingual#142

Merged
newstler merged 110 commits intomainfrom
refactor/merge-template
Apr 10, 2026
Merged

Merge template: replace Avo with Madmin, add teams/billing/MCP/multilingual#142
newstler merged 110 commits intomainfrom
refactor/merge-template

Conversation

@newstler
Copy link
Copy Markdown
Owner

@newstler newstler commented Apr 1, 2026

Summary

Major merge from the template remote, bringing in the new architecture and features while preserving all WhyRuby-specific functionality.

What changed

  • Admin panel: Replaced Avo with Madmin (dark-themed, magic-link admin auth)
  • Multi-tenancy: Added teams with memberships, billing (Stripe), and onboarding
  • AI chat: RubyLLM-powered chat with per-team scoping
  • MCP: Model Context Protocol tools via fast-mcp gem (agent-native architecture)
  • Multilingual: Mobility gem with auto-translation support
  • Articles: New team-scoped content model
  • Code style: 37signals conventions, Claude Code rules
  • Infrastructure: Improved Dockerfile (jemalloc), Litestream env-controlled, Tailwind 4 dark theme

What was preserved

  • GitHub OAuth via Devise + OmniAuth
  • All posts, categories, success stories, testimonials
  • Community profiles, developer map, newsletter
  • Cross-domain routing (whyruby.info + rubycommunity.org)
  • All existing views, layouts, and branding

Migration notes

  • Duplicate CreateUsers and CreateActiveStorageTables migrations removed (already existed)
  • CreateTeamsForExistingUsers migration fixed to use raw SQL (bypasses model callbacks for api_key column ordering)
  • Template fixture user IDs remapped to match existing fixture IDs

Test plan

  • RuboCop passes (0 offenses)
  • Brakeman passes (0 warnings)
  • All original WhyRuby tests pass
  • 12 template test failures remain (onboarding/articles tests need Devise auth adaptation)
  • Madmin admin panel loads and redirects to login
  • Homepage renders with success stories
  • Server starts without errors

🤖 Generated with Claude Code

newstler and others added 30 commits January 7, 2026 16:03
Generated complete favicon set from capedbot.svg including multi-resolution
.ico, PNG variants for different use cases, and SVG for modern browsers.
Updated layout to use proper favicon link tags and web app manifest.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Remove unused config/tailwind.config.js (v3 JS config was dead code)
- Add @custom-variant dark for class-based dark mode
- Add --font-sans to @theme for Inter font family
- Add .claude/rules/tailwind.md documenting v4 configuration

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Only show AI models in the UI whose provider has API credentials configured.
Added Model.enabled method that filters by providers with API keys and
deduplicates by name. Default model name now shown in select dropdown.

- Renamed Model.for_select to Model.enabled
- Added Model.configured_providers and with_configured_provider scope
- Standardized credential key from :open_ai to :openai to match provider name
- Updated bin/configure, initializer, and documentation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
newstler and others added 29 commits April 6, 2026 17:41
…tion

- Upgrade omniauth-rails_csrf_protection 1.0.2 → 2.0.1 (fixes
  ActiveSupport::Configurable deprecation warning on Rails 8.1+)
- Update ruby_llm, lefthook, regexp_parser, parallel, addressable

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ble concern

Merge LocationNormalizer and TimezoneResolver service objects into a
User::Geocodable concern with a single geocode! method. Thin out
NormalizeLocationJob to delegate to user.geocode!. Move tests to
test/models/concerns/user/geocodable_test.rb and delete old service files.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… concern

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rename job

Extracts AI field generation logic from GenerateTestimonialFieldsJob into
Testimonial::AiGeneratable concern using RubyLLM. Renames job to GenerateTestimonialJob.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…use RubyLLM

Extracts AI validation logic from ValidateTestimonialJob into
Testimonial::AiValidatable concern using RubyLLM. Thins out the job to a single
delegation call.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Update the AI agent guide to accurately describe the 37signals vanilla
Rails style: fat models with concerns, no service objects. Add Concern
Catalog, AI Operations section, and thin-delegator job pattern. Remove
all references to deleted services and old gems.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ource controllers

Move map_data to Users::MapDataController#show and og_image to Users::OgImagesController#show, following 37signals REST conventions. UsersController now only contains index and show.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…to nested resource controllers

- Tags#search → Tags::SearchesController#show (GET /tags/search unchanged)
- Sessions#verify → Sessions::VerificationsController#show, adds missing GET /auth/:token route
- Admins::Sessions#verify → Admins::Sessions::VerificationsController#show
- Teams::Settings#regenerate_api_key → Teams::Settings::ApiKeyRegenerationsController#create (PATCH→POST)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ontrollers

Move image, preview, fetch_metadata, check_duplicate_url to:
- Posts::ImagesController#show
- Posts::PreviewsController#create
- Posts::MetadataController#create
- Posts::DuplicateChecksController#create

PostsController now only has REST actions: show, new, create, edit, update, destroy.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…rollers

Replace toggle actions with proper REST resources:
- UserSettings::VisibilitiesController#update (was toggle_public)
- UserSettings::OpenToWorksController#update (was toggle_open_to_work)
- UserSettings::NewslettersController#update (was toggle_newsletter)
- UserSettings::RepositoryHidesController#create/#destroy (was hide_repo/unhide_repo)

Delete UserSettingsController entirely.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Delete tests that only check validates/associations/scopes/formatting
without testing actual business logic. Removed:
- 8 entire model test files (article, chat, membership, star_snapshot,
  price, tool_call, model, admin)
- 8 controller test files (home, articles, profiles, teams CRUD,
  billing, checkouts, pricing, members)
- 4 resource test files (MCP redirect stubs)
- 3 job test files (enqueue-only tests)
- Trimmed team_test, testimonial_test, message_test to keep only
  business logic tests

376 → 278 tests. All remaining tests verify actual app behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add trusted? threshold tests to user_test.rb (scope + instance method)
- Add UsersController tests for public/private profile visibility
- Add Post::SvgSanitizable class method tests for XSS sanitization
- Add Post::MetadataFetchable tests with WebMock for HTTP behavior
- Add Testimonial::AiGeneratable tests for heading_taken? logic
- Fix Project#topics to always return Array (fixture/JSON column mismatch)
- Fix projects.yml fixture topics to use empty JSON arrays

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Posts without a valid category association would crash the homepage with
ActionController::UrlGenerationError. Now falls back to root_path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…oved user views

Add Madmin resources, controllers, and views for Categories, Comments, Posts,
Projects, Reports, Tags, and Testimonials. Enhance user index/show with
content stats, filtering by role/trusted status, and company-team linking.
Add team consolidation rake task and sidebar navigation for all resources.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… and reordered metrics

Replace teams/messages cards with projects and testimonials stats.
Reorder dashboard cards to prioritize content metrics over infrastructure.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…h/receive

The wildcard `get "auth/:token"` route was matching `/auth/receive` before
the explicit route, sending cross-domain auth requests to the wrong controller.
Moved explicit auth routes above the wildcard. Also fixed undefined
`new_session_path` in VerificationsController.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…n Settings

Move all credentials (except maxmind) from Rails encrypted credentials
to the Madmin Settings admin panel, and add per-task AI model configuration.

- Add GitHub OAuth keys (whyruby + rubycommunity) to Settings
- Add GitHub API token to Settings
- Add default_ai_model and per-task models (summary, testimonial, validation, translation)
- Remove hardcoded SMTP from production.rb (Setting#configure_smtp! handles it)
- Fix mask_secret helper by moving to Madmin::ApplicationHelper
- Update omniauth initializer to read from Settings
- Update GithubSyncable to read API token from Settings
- Update all AI concerns and TranslateContentJob to use per-task models
- Update Madmin settings UI with GitHub, AI Models sections
- Update rake tasks to use Settings

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Update URL regex to allow dots mid-path while still excluding trailing dots
- Add break-all to user tile bio to prevent long text from overflowing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
break-words is less aggressive and only breaks at word boundaries when
needed, preserving readability while still preventing overflow.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@newstler newstler merged commit 6a04042 into main Apr 10, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant