.github/copilot-instructions.md: update
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled

This commit is contained in:
CroneKorkN 2026-01-18 16:21:47 +01:00
parent a708d89b2e
commit 987f3c08cc
Signed by: cronekorkn
SSH key fingerprint: SHA256:v0410ZKfuO1QHdgKBsdQNF64xmTxOF8osF1LIqwTcVw

View file

@ -1,5 +1,5 @@
--- ---
description: 'L4D Tools - Rails 8.1 application with Solid Queue, Hotwire stack' description: 'L4D Tools - Early-stage Rails 8.1 app with Solid Queue, Hotwire, Kamal deployment'
applyTo: '**/*.rb' applyTo: '**/*.rb'
--- ---
@ -7,70 +7,99 @@ applyTo: '**/*.rb'
## Project Overview ## Project Overview
**l4d_tools** is a Rails 8.1 application using modern Rails conventions. Key architectural decisions: **l4d_tools** is a fresh Rails 8.1 application in early development stage. No domain models exist yet—focus is on establishing clean patterns for future features.
- **Database**: SQLite (development), supports production deployment via Kamal/Docker
- **Job Processing**: Solid Queue (replaces Sidekiq) - background jobs embedded with app via `:solid_queue` plugin in Puma **Core Stack:**
- **Frontend Stack**: Hotwire (Turbo + Stimulus) with importmap-rails and Propshaft - **Framework**: Rails 8.1 (modern defaults: modern browser support, Propshaft asset pipeline)
- **Testing**: Minitest with Capybara for system tests (parallelized workers) - **Database**: SQLite with WAL mode (development); production supports Kamal/Docker deployment
- **Deployment**: Kamal-based Docker containerization with private registry support - **Jobs**: Solid Queue (embedded in Puma via `plugin :solid_queue` when `SOLID_QUEUE_IN_PUMA=true`)
- **Security Scanning**: Brakeman (security), Rubocop-rails-omakase (style), Bundler-audit (gems) - **Frontend**: Hotwire (Turbo + Stimulus) + importmap-rails (no build step)
- **Testing**: Minitest + Capybara with parallel workers
- **Deployment**: Kamal (Docker/container-based) with private registry at `localhost:5555`
- **Security**: Brakeman, Rubocop-rails-omakase, Bundler-audit
## Critical Developer Workflows ## Critical Developer Workflows
### Initial Setup ### Setup & Running
```bash ```bash
bin/setup # Installs gems, prepares DB, starts server bin/setup # Install gems, prepare SQLite DB, start server on :3000
bin/setup --reset # Full reset including DB wipe bin/dev # Start Rails dev server (invokes bin/rails server)
bin/setup --skip-server # Setup without starting server bin/rails console # Interactive REPL with full app context
``` ```
### Development Commands ### Testing
```bash ```bash
bin/dev # Start Rails server (port 3000) bin/rails test # Full test suite (auto-parallelized by CPU cores)
bin/rails console # REPL with app context bin/rails test test/models/post_test.rb # Single test file
bin/rails test # Full test suite (parallel workers) bin/rails test test/models/post_test.rb:42 # Specific test by line number
bin/rails test test/path # Specific test file/directory
bundle exec brakeman # Security vulnerability scan
bundle exec rubocop -a # Auto-fix style violations
``` ```
### Database Management ### Code Quality
```bash
bundle exec brakeman # Security scanning; fix ALL warnings before merge
bundle exec rubocop -a # Auto-fix style issues (rails-omakase rules)
bundle exec bundler-audit # Check gem vulnerabilities; update outdated gems
```
### Database (SQLite WAL mode)
```bash ```bash
bin/rails db:prepare # Create/migrate DB
bin/rails db:reset # Wipe and re-seed
bin/rails db:migrate # Apply pending migrations bin/rails db:migrate # Apply pending migrations
bin/rails db:rollback # Revert last migration bin/rails db:rollback # Revert last migration step
bin/rails db:reset # Wipe dev DB and re-run schema + seeds
``` ```
### Deployment (Kamal/Docker) ### Deployment (Kamal)
- **Config**: `config/deploy.yml` - registry, servers, environment variables - **Config**: `config/deploy.yml` (servers, registry endpoint, environment variables)
- **Secrets**: `.kamal/secrets` file (contains RAILS_MASTER_KEY) - **Build**: Auto-built by Kamal; Dockerfile uses multi-stage build
- **Registry**: Private registry at localhost:5555 for deployment target - **Registry**: Private Docker registry at `localhost:5555` (configured in deploy.yml)
- **Job Processing**: `SOLID_QUEUE_IN_PUMA=true` runs supervisor in web process (single-server only) - **Secrets**: Set `RAILS_MASTER_KEY` env var via Kamal secrets or system env
- **Build**: `docker build -t l4d_tools .` builds production image - **Job Queue**: `SOLID_QUEUE_IN_PUMA=true` embeds queue supervisor in Puma (single-server deployments only)
## Architecture & Key Components ## Architecture & Key Components
### Job Processing (Solid Queue) ### Job Processing (Solid Queue - no separate Redis/Sidekiq)
- **Location**: `app/jobs/` - **Store**: Database-backed; data lives in Solid Queue tables (queue_schema.rb)
- **Pattern**: Inherit from `ApplicationJob < ActiveJob::Base` - **Location**: `app/jobs/` - inherit from `ApplicationJob < ActiveJob::Base`
- **Configuration**: `config/queue.yml` defines adapters and queues - **Configuration**: `config/queue.yml` defines:
- **Execution**: Jobs run synchronously in test mode; async in development/production - `threads: 3` - worker threads per process
- **Puma Integration**: `plugin :solid_queue if ENV["SOLID_QUEUE_IN_PUMA"]` in `config/puma.rb` - `processes: JOB_CONCURRENCY` env var - defaults to 1 (scale via env)
- **Usage**: `MyJob.perform_later(args)` for async; `MyJob.perform_now(args)` for testing - `polling_interval: 0.1` - how frequently workers check for jobs
- **Enqueue**: `MyJob.perform_later(args)` for async; `MyJob.perform_now(args)` for testing/immediate
- **Puma Integration**: `plugin :solid_queue if ENV["SOLID_QUEUE_IN_PUMA"]` in `config/puma.rb` runs supervisor in same process (single-server deployments only)
- **In Tests**: Jobs run synchronously (development mode behavior)
- **Important**: No Sidekiq/Redis—Solid Queue is database-backed and built into Puma
### Frontend (Hotwire) ### Frontend (Hotwire + Importmap, no build step)
- **Turbo**: Rapid page transitions via AJAX without full reloads - **Turbo**: Handles navigation via AJAX (replaces full page loads); auto-loads from `data-turbo=true`
- **Stimulus**: Minimal DOM binding framework; controllers in `app/javascript/controllers/` - **Stimulus**: Zero-config JavaScript binding via `data-controller="name"``NameController` in `app/javascript/controllers/name_controller.js`
- **Asset Pipeline**: Propshaft + importmap-rails; no build step needed - **Asset Pipeline**: Propshaft (modern successor to Sprockets) + importmap-rails
- **Entry Point**: `app/javascript/application.js` loads all controllers - CSS: All files in `app/assets/stylesheets/` auto-loaded via `stylesheet_link_tag :app`
- **Naming**: `data-controller="hello"` maps to `HelloController` in `app/javascript/controllers/hello_controller.js` - JS: Controllers auto-discovered; no webpack/bundler required
- Entry point: `app/javascript/application.js` imports controllers and libraries
- **Example Stimulus binding**:
```javascript
// app/javascript/controllers/search_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ["input"]
search() { console.log(this.inputTarget.value) }
}
```
```erb
<!-- In view: -->
<div data-controller="search">
<input data-search-target="input" data-action="input->search#search">
</div>
```
### Database & Models ### Database & Migrations (SQLite WAL)
- **Schema**: `db/schema.rb` auto-generated from migrations (commit to VCS) - **Schema**: `db/schema.rb` auto-generated; commit to git (tracks schema evolution)
- **Migrations**: Generate via `bin/rails generate migration CreateTableName` - **Migrations**: Generate via `bin/rails generate migration DescriptiveNameHere`
- **Patterns**: Use model scopes for complex queries; keep migrations simple and reversible - Use `change` method for reversible migrations (preferred)
- **Fixtures**: YAML files in `test/fixtures/*.yml` auto-loaded by Minitest - Always add indexes for foreign keys
- Test rollback: `bin/rails db:rollback` verifies reversibility
- **SQLite Limits**: AUTOINCREMENT primary keys, pragma constraints, no ALTER COLUMN
- **Models**: No domain models exist yet; structure will be pattern-driven once first models are created
### Testing Structure ### Testing Structure
- **Unit Tests**: `test/models/`, `test/helpers/`, `test/controllers/` - **Unit Tests**: `test/models/`, `test/helpers/`, `test/controllers/`
@ -81,18 +110,70 @@ bin/rails db:rollback # Revert last migration
## Project-Specific Guidelines ## Project-Specific Guidelines
- Follow RuboCop-rails-omakase style guide strictly; run `rubocop -a` to auto-fix ### Early Stage Patterns (No Domain Models Yet)
- **Start with migrations**: Define database schema first; models follow naturally
- **Keep ApplicationRecord/ApplicationJob clean**: These are templates for future models/jobs
- **Use concerns sparingly**: Extract concerns only after 2+ models need the same behavior
- **Establish directory structure early**: Create `app/services/`, `app/presenters/` if needed for future patterns
### Code Quality & Style
- Follow RuboCop-rails-omakase style guide strictly; run `bundle exec rubocop -a` to auto-fix
- Use snake_case for variables/methods, CamelCase for classes - Use snake_case for variables/methods, CamelCase for classes
- Extract reusable business logic into `app/services/` to keep models lean - Extract reusable business logic into `app/services/` to keep models lean
- Use Solid Queue `perform_later` for background work; never block HTTP requests - Keep `config/routes.rb` simple; add routes only when adding controller actions
- Prefer Turbo Frames for dynamic UI updates over full page reloads
- Write tests using fixtures; auto-loaded into test instance variables - Write tests using fixtures; auto-loaded into test instance variables
- Use `bin/rails test` to run full suite with parallel workers - Use `bin/rails test` to run full suite with parallel workers
- Keep `config/routes.rb` simple; add routes only when adding controller actions
- Store secrets in `Rails.application.credentials` (encrypted via `master.key`)
- Debug with `byebug` or Rails logger; avoid `puts` for production debugging
## Common Patterns ### Performance & Background Jobs
- Use Solid Queue `perform_later` for background work; never block HTTP requests
- Set `JOB_CONCURRENCY` env var to scale job processing beyond default (1 process × 3 threads)
- Test jobs with `MyJob.perform_now` in test env; they run synchronously
- Monitor job failures in `bin/rails console`: `SolidQueue::Job.where(status: :failed)`
### Frontend & UI
- Prefer Turbo Frames (`<turbo-frame>`) for dynamic UI updates over full page reloads
- Always set `data-turbo="false"` on forms that should NOT use Turbo (e.g., file uploads, legacy forms)
- Stimulus controllers are zero-config; name them kebab-case, file them snake_case
- Test JavaScript with system tests; Capybara + Selenium support Turbo navigation
### Secrets & Credentials
- Store all secrets in `Rails.application.credentials` (encrypted via `master.key`)
- Never commit `config/master.key`; set `RAILS_MASTER_KEY` env var in production
- Use `bin/rails credentials:edit` to add new secrets
- Development uses `.key` file automatically; test/production use env variable
### Debugging
- Debug with `byebug` or Rails logger; avoid `puts` for production debugging
- Use `Rails.logger.info("...")` for production-safe logging
- Inspect Solid Queue tables directly: `bin/rails console``SolidQueue::Job.last(10)`
## Integration Points & Data Flows
### HTTP Request Lifecycle
1. **Routing**: `config/routes.rb` → Controller action
2. **Parameter Validation**: Use `strong_parameters` in ApplicationController (already configured)
3. **Business Logic**: Execute in service objects or job queues, not controllers
4. **Response**: Render ERB template (with Turbo) or JSON (if API)
5. **Browser**: Turbo replaces page or frame; Stimulus controllers initialize
### Database Access Patterns
- **Queries**: Use model scopes and class methods (not raw SQL)
- **Write Operations**: Controller delegates to service → model (transaction boundaries)
- **Background Jobs**: Solid Queue tables store jobs; accessed via ActiveJob interface
- **Migrations**: Always reversible; schema changes are version-controlled in `db/schema.rb`
### Job Queue Integration
1. **Enqueue**: `MyJob.perform_later(id)` in controller/service
2. **Polling**: Solid Queue supervisor (in Puma) polls for jobs every 0.1s
3. **Execution**: Worker thread picks job, calls `perform(id)`
4. **Persistence**: Results/errors logged in job records; failures retryable
5. **Monitoring**: Check status via `SolidQueue::Job` model in console
### Asset & Static File Pipeline
- **CSS**: All files in `app/assets/stylesheets/` auto-included via `stylesheet_link_tag :app`
- **JS**: Controllers auto-discovered from `app/javascript/controllers/`; no bundling required
- **Images**: Reference via `image_tag "name"` (fingerprinted in production)
- **PWA**: Service worker template available in `app/views/pwa/` (commented out in routes)
### Job Enqueue (Solid Queue) ### Job Enqueue (Solid Queue)
```ruby ```ruby
@ -164,3 +245,46 @@ end
- SQLite adequate for single-server; use PostgreSQL for multi-server scaling - SQLite adequate for single-server; use PostgreSQL for multi-server scaling
- Content Security Policy configured in `config/initializers/content_security_policy.rb` - Content Security Policy configured in `config/initializers/content_security_policy.rb`
- Never commit `master.key` or `.kamal/secrets`; set RAILS_MASTER_KEY via environment - Never commit `master.key` or `.kamal/secrets`; set RAILS_MASTER_KEY via environment
## Key Directory Structure
- `app/controllers/` - HTTP request handlers; keep thin (validate + delegate to services)
- `app/models/` - ActiveRecord models; use scopes for queries, keep business logic minimal
- `app/views/` - ERB templates with Turbo/Stimulus integration
- `app/javascript/controllers/` - Stimulus controllers for DOM interactivity
- `app/jobs/` - Solid Queue background jobs; use for time-consuming operations
- `config/` - Application configuration; routes, database, queue, deployment settings
- `test/` - Test suite mirroring app structure; fixtures auto-loaded
- `db/migrations/` - Version-controlled database schema changes
## Common Pitfalls to Avoid
### Early-Stage Mistakes
- **Don't skip migrations**: Always create reversible migrations with `change` blocks (or up/down)
- **Don't hard-code job names**: Reference jobs via constants/classes, not strings
- **Don't mix concerns in models**: Business logic belongs in services; models are data containers
- **Don't create routes without controllers**: Routes must map to actual controller actions
### Solid Queue Specifics
- **No Sidekiq/Redis**: Job data is stored in SQLite; don't assume Redis-style operations
- **Single-server default**: `JOB_CONCURRENCY=1` by default; set env var to scale
- **Test mode synchronous**: Jobs run immediately in tests; use `perform_now` explicitly if needed
- **Monitor job failures**: Failed jobs remain in DB; check `SolidQueue::Job.where(status: :failed)`
### Hotwire/Frontend Gotchas
- **Set data-turbo="false" for file uploads**: Turbo doesn't handle multipart forms well
- **Use `data-turbo-method="delete"` for non-GET actions**: Turbo expects method hints
- **Stimulus controllers auto-initialize**: No manual instantiation needed; just use HTML attributes
- **CSS conflicts with Turbo**: Use scoped styles; avoid global `.active` or `.selected` classes
### SQLite Development
- **No concurrent migrations**: SQLite locks during schema changes; don't run multiple devs simultaneously
- **WAL mode enabled**: Provides better concurrency; don't disable without reason
- **No ALTER COLUMN**: Use temporary tables pattern for complex migrations
- **Indexes matter**: SQLite can't optimize without them; always index foreign keys
### Deployment & Secrets
- **RAILS_MASTER_KEY required**: Set env var before Docker startup; app won't boot without it
- **Registry authentication**: Docker registry at localhost:5555 must be running for Kamal builds
- **Single-server assumption**: `SOLID_QUEUE_IN_PUMA=true` only works with one web server
- **Database file location**: SQLite database is in `storage/` directory; ensure volume mounts in Docker