source_monitor 0.13.1 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.claude/skills/sm-configuration-setting/reference/settings-catalog.md +1 -0
- data/.claude/skills/sm-configure/SKILL.md +8 -1
- data/.claude/skills/sm-configure/reference/configuration-reference.md +11 -0
- data/.claude/skills/sm-host-setup/SKILL.md +13 -3
- data/.claude/skills/sm-host-setup/reference/initializer-template.md +11 -0
- data/.claude/skills/sm-host-setup/reference/setup-checklist.md +9 -1
- data/.claude/skills/sm-upgrade/reference/version-history.md +26 -0
- data/AGENTS.md +145 -38
- data/CHANGELOG.md +29 -0
- data/CLAUDE.md +6 -116
- data/Gemfile.lock +86 -86
- data/README.md +3 -3
- data/VERSION +1 -1
- data/app/controllers/source_monitor/application_controller.rb +73 -14
- data/app/views/layouts/source_monitor/application.html.erb +6 -0
- data/docs/configuration.md +18 -1
- data/docs/deployment.md +1 -1
- data/docs/goals/engine-hardening/.goalbuddy-board/app.js +543 -0
- data/docs/goals/engine-hardening/.goalbuddy-board/goalbuddy-mark.png +0 -0
- data/docs/goals/engine-hardening/.goalbuddy-board/index.html +111 -0
- data/docs/goals/engine-hardening/.goalbuddy-board/styles.css +991 -0
- data/docs/goals/engine-hardening/goal.md +97 -0
- data/docs/goals/engine-hardening/notes/T001-spec-validation.md +37 -0
- data/docs/goals/engine-hardening/state.yaml +324 -0
- data/docs/setup.md +4 -4
- data/docs/upgrade.md +41 -0
- data/lib/generators/source_monitor/install/templates/source_monitor.rb.tt +10 -0
- data/lib/source_monitor/configuration/authentication_settings.rb +5 -1
- data/lib/source_monitor/security/authentication.rb +10 -0
- data/lib/source_monitor/version.rb +1 -1
- data/source_monitor.gemspec +8 -3
- metadata +10 -67
- data/.claude/agent-memory/vbw-vbw-debugger/MEMORY.md +0 -15
- data/.claude/agent-memory/vbw-vbw-dev/MEMORY.md +0 -34
- data/.claude/agent-memory/vbw-vbw-lead/MEMORY.md +0 -49
- data/.claude/agents/rails-concern.md +0 -464
- data/.claude/agents/rails-controller.md +0 -424
- data/.claude/agents/rails-hotwire.md +0 -446
- data/.claude/agents/rails-implement.md +0 -374
- data/.claude/agents/rails-job.md +0 -334
- data/.claude/agents/rails-lint.md +0 -294
- data/.claude/agents/rails-mailer.md +0 -371
- data/.claude/agents/rails-migration.md +0 -449
- data/.claude/agents/rails-model.md +0 -420
- data/.claude/agents/rails-policy.md +0 -443
- data/.claude/agents/rails-presenter.md +0 -427
- data/.claude/agents/rails-query.md +0 -412
- data/.claude/agents/rails-review.md +0 -490
- data/.claude/agents/rails-service.md +0 -458
- data/.claude/agents/rails-state-records.md +0 -465
- data/.claude/agents/rails-tdd.md +0 -314
- data/.claude/agents/rails-test.md +0 -441
- data/.claude/agents/rails-view-component.md +0 -418
- data/.claude/commands/rails-audit.md +0 -77
- data/.claude/commands/release.md +0 -366
- data/.claude/hooks/block-secrets.sh +0 -52
- data/.claude/settings.json +0 -85
- data/.claude/skills/action-cable-patterns/SKILL.md +0 -296
- data/.claude/skills/action-mailer-patterns/SKILL.md +0 -295
- data/.claude/skills/active-storage-setup/SKILL.md +0 -311
- data/.claude/skills/api-versioning/SKILL.md +0 -294
- data/.claude/skills/authentication-flow/SKILL.md +0 -335
- data/.claude/skills/authentication-flow/reference/current.md +0 -248
- data/.claude/skills/authentication-flow/reference/passwordless.md +0 -253
- data/.claude/skills/authentication-flow/reference/sessions.md +0 -201
- data/.claude/skills/authorization-pundit/SKILL.md +0 -462
- data/.claude/skills/caching-strategies/SKILL.md +0 -350
- data/.claude/skills/database-migrations/SKILL.md +0 -354
- data/.claude/skills/form-object-patterns/SKILL.md +0 -399
- data/.claude/skills/hotwire-patterns/SKILL.md +0 -247
- data/.claude/skills/hotwire-patterns/reference/stimulus.md +0 -307
- data/.claude/skills/hotwire-patterns/reference/tailwind-integration.md +0 -112
- data/.claude/skills/hotwire-patterns/reference/turbo-frames.md +0 -158
- data/.claude/skills/hotwire-patterns/reference/turbo-streams.md +0 -218
- data/.claude/skills/i18n-patterns/SKILL.md +0 -320
- data/.claude/skills/install/SKILL.md +0 -367
- data/.claude/skills/performance-optimization/SKILL.md +0 -311
- data/.claude/skills/rails-architecture/SKILL.md +0 -259
- data/.claude/skills/rails-architecture/reference/error-handling.md +0 -333
- data/.claude/skills/rails-architecture/reference/event-tracking.md +0 -142
- data/.claude/skills/rails-architecture/reference/layer-interactions.md +0 -417
- data/.claude/skills/rails-architecture/reference/multi-tenancy.md +0 -152
- data/.claude/skills/rails-architecture/reference/query-patterns.md +0 -342
- data/.claude/skills/rails-architecture/reference/service-patterns.md +0 -286
- data/.claude/skills/rails-architecture/reference/state-records.md +0 -250
- data/.claude/skills/rails-architecture/reference/testing-strategy.md +0 -326
- data/.claude/skills/rails-concern/SKILL.md +0 -399
- data/.claude/skills/rails-controller/SKILL.md +0 -336
- data/.claude/skills/rails-model-generator/SKILL.md +0 -321
- data/.claude/skills/rails-model-generator/reference/validations.md +0 -298
- data/.claude/skills/rails-presenter/SKILL.md +0 -274
- data/.claude/skills/rails-query-object/SKILL.md +0 -289
- data/.claude/skills/rails-service-object/SKILL.md +0 -349
- data/.claude/skills/solid-queue-setup/SKILL.md +0 -307
- data/.claude/skills/tdd-cycle/SKILL.md +0 -359
- data/.claude/skills/viewcomponent-patterns/SKILL.md +0 -333
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 55bf050a9f74f18ed354d7d751f868bc98dc8bc5da59c3da6ce5e0aa0cedc833
|
|
4
|
+
data.tar.gz: 37a5906b19541de00ef3b860f266a1c7a12e737da58d1a7b80cb806a191936d5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0c0109e46a1ec8691592fd304095c833d5d78b9091ffa216e72011040a8683be820016dd51b4a2185d17b9780761410918297f9f4fdeb2fd31d1e952575fbf99
|
|
7
|
+
data.tar.gz: 9016293bb6d6339026ed811689db2a4f1b87cd90fb60bb7f270113fc698c9714f9935f2aec704c1581b3ef6b61137b2df5c5b21e0ecd8b2bea69be987152057f
|
|
@@ -157,6 +157,7 @@ Has `reset!` method. The `adapter=` setter validates against `VALID_ADAPTERS`.
|
|
|
157
157
|
| `authorize_handler` | Handler/nil | `nil` | Authorization handler |
|
|
158
158
|
| `current_user_method` | Symbol/nil | `nil` | Method name for current user |
|
|
159
159
|
| `user_signed_in_method` | Symbol/nil | `nil` | Method name for signed-in check |
|
|
160
|
+
| `open_access` | Boolean | `false` | Opt out of the fail-closed access guard (demo/non-production only); ignored when a handler is configured |
|
|
160
161
|
|
|
161
162
|
Has `reset!` method.
|
|
162
163
|
|
|
@@ -66,10 +66,17 @@ config.http.retry_max = 3
|
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
### Authentication (Devise)
|
|
69
|
+
SourceMonitor is **fail-closed by default**: with no `authenticate_with`/`authorize_with`
|
|
70
|
+
handler configured, every engine route returns `403 Forbidden`. Configure a handler
|
|
71
|
+
to protect the dashboard (the handler decides access and bypasses the fail-closed guard):
|
|
69
72
|
```ruby
|
|
70
73
|
config.authentication.authenticate_with :authenticate_user!
|
|
71
74
|
config.authentication.authorize_with ->(c) { c.current_user&.admin? }
|
|
72
75
|
```
|
|
76
|
+
For local demos/sandboxes only, opt out of the fail-closed guard (non-production):
|
|
77
|
+
```ruby
|
|
78
|
+
config.authentication.open_access = true # default: false -- demo/non-production only
|
|
79
|
+
```
|
|
73
80
|
|
|
74
81
|
### Image Downloads (Active Storage)
|
|
75
82
|
```ruby
|
|
@@ -167,7 +174,7 @@ end
|
|
|
167
174
|
- [ ] Initializer exists at `config/initializers/source_monitor.rb`
|
|
168
175
|
- [ ] Queue names match `config/queue.yml` (or `config/solid_queue.yml`) entries
|
|
169
176
|
- [x] Dispatcher config includes `recurring_schedule: config/recurring.yml` (handled by install generator)
|
|
170
|
-
- [ ] Authentication
|
|
177
|
+
- [ ] Authentication decision made: configure `authenticate_with`/`authorize_with` (fail-closed default) OR set `config.authentication.open_access = true` for demos only
|
|
171
178
|
- [ ] HTTP timeouts appropriate for target feeds
|
|
172
179
|
- [ ] Retention policy set for production
|
|
173
180
|
- [ ] Workers restarted after configuration changes
|
|
@@ -283,10 +283,17 @@ config.realtime.solid_cable.connects_to = { database: { writing: :cable } }
|
|
|
283
283
|
|
|
284
284
|
Class: `SourceMonitor::Configuration::AuthenticationSettings`
|
|
285
285
|
|
|
286
|
+
**Fail-closed by default.** When no `authenticate_with`/`authorize_with` handler is
|
|
287
|
+
configured and `open_access` is `false`, the engine denies every route with
|
|
288
|
+
`403 Forbidden` (see `SourceMonitor::Security::Authentication.access_denied_by_default?`).
|
|
289
|
+
Configuring either handler makes the handler authoritative and bypasses the
|
|
290
|
+
fail-closed guard.
|
|
291
|
+
|
|
286
292
|
| Setting | Type | Default | Description |
|
|
287
293
|
|---|---|---|---|
|
|
288
294
|
| `current_user_method` | Symbol/nil | `nil` | Controller method to get current user |
|
|
289
295
|
| `user_signed_in_method` | Symbol/nil | `nil` | Controller method to check sign-in status |
|
|
296
|
+
| `open_access` | Boolean | `false` | Opt out of the fail-closed guard so engine routes are public. **Demo/non-production only.** Ignored when a handler is configured. |
|
|
290
297
|
|
|
291
298
|
### Methods
|
|
292
299
|
|
|
@@ -312,6 +319,10 @@ config.authentication.authorize_with ->(controller) {
|
|
|
312
319
|
config.authentication.authorize_with do
|
|
313
320
|
redirect_to root_path unless current_user&.admin?
|
|
314
321
|
end
|
|
322
|
+
|
|
323
|
+
# Open access (demo/non-production only) -- disables the fail-closed guard.
|
|
324
|
+
# Leave commented/false in production. A configured handler always wins.
|
|
325
|
+
config.authentication.open_access = true # default: false
|
|
315
326
|
```
|
|
316
327
|
|
|
317
328
|
---
|
|
@@ -119,7 +119,7 @@ After installation, review and customize the initializer. Key areas:
|
|
|
119
119
|
| Section | Purpose |
|
|
120
120
|
|---|---|
|
|
121
121
|
| Queue settings | Queue names, concurrency, namespace |
|
|
122
|
-
| Authentication | `authenticate_with
|
|
122
|
+
| Authentication | **Fail-closed by default** -- configure `authenticate_with`/`authorize_with` hooks, or set `open_access = true` for demos only |
|
|
123
123
|
| HTTP client | Timeouts, proxy, retries |
|
|
124
124
|
| Fetching | Adaptive scheduling intervals and factors |
|
|
125
125
|
| Health | Auto-pause/resume thresholds |
|
|
@@ -152,7 +152,17 @@ SOURCE_MONITOR_SETUP_TELEMETRY=true bin/source_monitor verify
|
|
|
152
152
|
# Logs to log/source_monitor_setup.log
|
|
153
153
|
```
|
|
154
154
|
|
|
155
|
-
##
|
|
155
|
+
## Authentication (Fail-Closed by Default)
|
|
156
|
+
|
|
157
|
+
SourceMonitor is **fail-closed by default**: with no `authenticate_with`/`authorize_with`
|
|
158
|
+
handler configured, every engine route (including create/update/delete/enqueue actions)
|
|
159
|
+
returns `403 Forbidden`. You must make one of two choices during setup:
|
|
160
|
+
|
|
161
|
+
1. **Configure a handler** (recommended) -- the handler decides access and bypasses the
|
|
162
|
+
fail-closed guard.
|
|
163
|
+
2. **Set `config.authentication.open_access = true`** -- opts out of the guard so routes
|
|
164
|
+
are public. **Demo/non-production only.** A configured handler always takes precedence
|
|
165
|
+
over this flag.
|
|
156
166
|
|
|
157
167
|
When Devise is detected, the guided installer offers to wire authentication hooks:
|
|
158
168
|
|
|
@@ -238,6 +248,6 @@ end
|
|
|
238
248
|
- [x] `Procfile.dev` includes `jobs:` entry for Solid Queue (handled by generator)
|
|
239
249
|
- [x] Dispatcher config includes `recurring_schedule: config/recurring.yml` (handled by generator)
|
|
240
250
|
- [ ] Solid Queue workers started
|
|
241
|
-
- [ ] Authentication
|
|
251
|
+
- [ ] Authentication decision made (engine is fail-closed by default): handler configured via `authenticate_with`/`authorize_with`, OR `config.authentication.open_access = true` for demos only
|
|
242
252
|
- [ ] `bin/source_monitor verify` passes
|
|
243
253
|
- [ ] Dashboard accessible at mount path
|
|
@@ -57,7 +57,12 @@ SourceMonitor.configure do |config|
|
|
|
57
57
|
# ===========================================================================
|
|
58
58
|
# Authentication
|
|
59
59
|
# ===========================================================================
|
|
60
|
+
# SECURITY: SourceMonitor is FAIL-CLOSED by default. If you do not configure
|
|
61
|
+
# an authentication or authorization handler below, every engine route
|
|
62
|
+
# (including create/update/delete/enqueue actions) returns 403 Forbidden.
|
|
60
63
|
# Handlers: Symbol (invoked on controller) or callable (receives controller).
|
|
64
|
+
# As soon as a handler is configured it decides access and the fail-closed
|
|
65
|
+
# guard no longer applies.
|
|
61
66
|
|
|
62
67
|
# Authenticate before accessing any SourceMonitor page.
|
|
63
68
|
# config.authentication.authenticate_with :authenticate_user!
|
|
@@ -71,6 +76,12 @@ SourceMonitor.configure do |config|
|
|
|
71
76
|
# config.authentication.current_user_method = :current_user
|
|
72
77
|
# config.authentication.user_signed_in_method = :user_signed_in?
|
|
73
78
|
|
|
79
|
+
# Explicit opt-in for open/unauthenticated access. Leave commented out in
|
|
80
|
+
# production. Only enable for local demos or sandboxes where engine routes
|
|
81
|
+
# are deliberately public.
|
|
82
|
+
# WARNING: non-production / demo only -- this disables the fail-closed guard.
|
|
83
|
+
# config.authentication.open_access = true
|
|
84
|
+
|
|
74
85
|
# ===========================================================================
|
|
75
86
|
# HTTP Client
|
|
76
87
|
# ===========================================================================
|
|
@@ -57,6 +57,10 @@ bin/rails db:migrate
|
|
|
57
57
|
|
|
58
58
|
## Phase 5: Configure Authentication
|
|
59
59
|
|
|
60
|
+
**Fail-closed by default.** With no handler configured, every engine route returns
|
|
61
|
+
`403 Forbidden`. Make one of two choices: configure a handler (recommended) OR opt into
|
|
62
|
+
open access for demos only.
|
|
63
|
+
|
|
60
64
|
Edit `config/initializers/source_monitor.rb`:
|
|
61
65
|
|
|
62
66
|
```ruby
|
|
@@ -68,10 +72,14 @@ SourceMonitor.configure do |config|
|
|
|
68
72
|
}
|
|
69
73
|
config.authentication.current_user_method = :current_user
|
|
70
74
|
config.authentication.user_signed_in_method = :user_signed_in?
|
|
75
|
+
|
|
76
|
+
# OR, for local demos/sandboxes only (non-production) -- opt out of the
|
|
77
|
+
# fail-closed guard so routes are public. A configured handler always wins.
|
|
78
|
+
# config.authentication.open_access = true
|
|
71
79
|
end
|
|
72
80
|
```
|
|
73
81
|
|
|
74
|
-
- [ ] Authentication
|
|
82
|
+
- [ ] Authentication decision made: handler configured (fail-closed default) OR `open_access = true` set for demos only
|
|
75
83
|
- [ ] Authorization hook configured (if needed)
|
|
76
84
|
|
|
77
85
|
## Phase 6: Configure Workers
|
|
@@ -2,6 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
Version-specific migration notes for each major/minor version transition. Agents should reference this file when guiding users through multi-version upgrades.
|
|
4
4
|
|
|
5
|
+
## 0.14.0 to 0.15.0
|
|
6
|
+
|
|
7
|
+
**Key changes:**
|
|
8
|
+
- **Security:** Rails bumped to 8.1.3 (security release), picking up fixes for five CVEs including XSS in tag/DebugExceptions helpers, an Active Storage path-traversal, and a NumberConverter issue.
|
|
9
|
+
- **ViewComponent 4.x:** the `view_component` dependency widens from `>= 3.0, < 4.0` to `>= 3.0, < 5.0`, allowing host apps to resolve ViewComponent 4.x (engine lockfile resolves to 4.5.0). Engine components use only stable APIs unaffected by v4.
|
|
10
|
+
- **Solid Queue 1.4.0:** bumped from 1.3.1 with race-condition and supervisor stability fixes; dynamic recurring tasks are opt-in.
|
|
11
|
+
- **Documentation:** engine conventions consolidated into `AGENTS.md`; `CLAUDE.md` now points to it.
|
|
12
|
+
|
|
13
|
+
**Action items:**
|
|
14
|
+
1. `bundle update source_monitor`
|
|
15
|
+
2. `bin/rails source_monitor:upgrade`
|
|
16
|
+
3. No migrations or breaking config/API changes.
|
|
17
|
+
4. If the host app has ViewComponent 3.x customizations (custom components, previews), test after upgrading and consult ViewComponent v4 migration guides if needed.
|
|
18
|
+
|
|
19
|
+
## 0.13.1 to 0.14.0
|
|
20
|
+
|
|
21
|
+
**Key changes:**
|
|
22
|
+
- **BREAKING — Fail-closed by default** (#129): engine routes return `403 Forbidden` unless `config.authentication.authenticate_with` or `config.authentication.authorize_with` is configured, OR `config.authentication.open_access = true` is explicitly set (non-production only).
|
|
23
|
+
- **Request flashes response-local** (#130): flash toasts no longer broadcast to the global `source_monitor_notifications` ActionCable stream; rendered inline on HTML loads and appended on turbo_stream responses instead. Background operational toasts stay global but context-free.
|
|
24
|
+
- **Gem package excludes `.claude` internals** (#131): only `.claude/skills/sm-*` shipped; agents, hooks, `settings.json`, commands, and non-`sm-*` skills excluded.
|
|
25
|
+
|
|
26
|
+
**Action items:**
|
|
27
|
+
1. `bundle update source_monitor`
|
|
28
|
+
2. **Action required (auth):** in `config/initializers/source_monitor.rb`, ensure at least one of `authenticate_with`, `authorize_with`, or `open_access = true` (demo/sandbox only). Without one, all engine routes return `403`.
|
|
29
|
+
3. No migrations or other breaking API changes; flash/packaging changes apply transparently.
|
|
30
|
+
|
|
5
31
|
## 0.12.4 to 0.13.0
|
|
6
32
|
|
|
7
33
|
**Key changes:**
|
data/AGENTS.md
CHANGED
|
@@ -1,47 +1,74 @@
|
|
|
1
1
|
# Repository Guidelines
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This file is the **canonical engine convention reference, shared across all AI coding agents** (Claude Code, Codex, etc.). It is the single source of truth for tech stack, architecture, testing, quality gates, CI, security, and commands.
|
|
4
|
+
|
|
5
|
+
`CLAUDE.md` holds only Claude Code-specific context (working-memory header, VBW commands, and the `.claude/` agent/skill catalogs) and references this file for conventions. When a convention changes, edit it **here**, not in `CLAUDE.md`.
|
|
4
6
|
|
|
5
7
|
Use rbenv for all ruby and bundler/gem commands, not the system ruby.
|
|
6
8
|
|
|
7
|
-
##
|
|
9
|
+
## Tech Stack
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
| Layer | Technology |
|
|
12
|
+
|-------|------------|
|
|
13
|
+
| Ruby | 4.0+ |
|
|
14
|
+
| Rails | 8.x |
|
|
15
|
+
| Testing | Minitest (no fixtures -- uses factory helpers + WebMock/VCR) |
|
|
16
|
+
| Authorization | Host app responsibility (mountable engine); **fail-closed by default** (#129) |
|
|
17
|
+
| Jobs | Solid Queue |
|
|
18
|
+
| Frontend | Hotwire (Turbo + Stimulus) + Tailwind CSS |
|
|
19
|
+
| Linting | RuboCop (omakase) + Brakeman |
|
|
20
|
+
| Database | PostgreSQL only |
|
|
16
21
|
|
|
17
|
-
##
|
|
22
|
+
## Project Structure & Module Organization
|
|
18
23
|
|
|
19
|
-
|
|
24
|
+
The engine follows the Rails mountable layout generated by `rails plugin new source_monitor --mountable`. Runtime code lives under `app/` (controllers, jobs, views) and is namespaced as `SourceMonitor`. Long-lived services, adapters, and instrumentation helpers belong in `lib/source_monitor/`. Generator templates and install scripts reside in `lib/generators/source_monitor/`. Tests sit in `test/`, with fixtures under `test/fixtures/feeds/`, and the dummy host app in `test/dummy/` for integration coverage. Keep shared UI assets under `app/assets/` and engine configuration in `config/initializers/`.
|
|
20
25
|
|
|
21
|
-
|
|
26
|
+
## Architecture Conventions
|
|
22
27
|
|
|
23
|
-
|
|
28
|
+
### Models First
|
|
29
|
+
- Business logic lives in models. Use concerns for horizontal sharing.
|
|
30
|
+
- Service objects ONLY for operations spanning 3+ models or external integrations.
|
|
31
|
+
- Query objects for complex queries that don't fit a single scope.
|
|
32
|
+
- Presenters (SimpleDelegator) for view-specific formatting.
|
|
24
33
|
|
|
25
|
-
|
|
34
|
+
### Everything-is-CRUD Routing
|
|
35
|
+
- Prefer creating a new resource over adding custom actions.
|
|
36
|
+
- `POST /posts/:id/publications` over `POST /posts/:id/publish`.
|
|
37
|
+
- RESTful routes only; no `member` or `collection` blocks with custom verbs.
|
|
26
38
|
|
|
27
|
-
|
|
39
|
+
### State as Records
|
|
40
|
+
- Track business state transitions as separate records (who/when/why).
|
|
41
|
+
- Boolean columns ONLY for technical flags (e.g., `email_verified`, `open_access`).
|
|
28
42
|
|
|
29
|
-
|
|
43
|
+
### Jobs
|
|
44
|
+
- Shallow jobs: call `_later` or `_now` methods on models/services.
|
|
45
|
+
- Jobs contain only deserialization + delegation. No business logic.
|
|
46
|
+
- Use Solid Queue recurring jobs for scheduled work.
|
|
30
47
|
|
|
31
|
-
|
|
48
|
+
### Frontend
|
|
49
|
+
- Turbo Frames for partial page updates.
|
|
50
|
+
- Turbo Streams for real-time broadcasts.
|
|
51
|
+
- Stimulus controllers: small, focused, one behavior each.
|
|
52
|
+
- Tailwind CSS utility classes; extract components for repeated patterns.
|
|
32
53
|
|
|
33
|
-
##
|
|
54
|
+
## Coding Style & Naming Conventions
|
|
34
55
|
|
|
35
|
-
-
|
|
36
|
-
|
|
37
|
-
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
56
|
+
Use two-space indentation and Ruby 4.0+ syntax. Keep engine classes under the `SourceMonitor::` namespace; new modules should mirror their directory, e.g., `lib/source_monitor/fetching/pipeline.rb`. Favor service objects ending in `Service`, jobs ending in `Job`, and background channels ending in `Channel`. Rails defaults handle formatting, but run `bin/rubocop` (configured via `.rubocop.yml`) before opening a PR. For views, stick with ERB and Tailwind utility classes.
|
|
57
|
+
|
|
58
|
+
Sub-module extraction pattern: create `module/submodule.rb` with `require_relative`, lazy accessors, and forwarding methods for backward compatibility. The project uses Ruby autoload for `lib/` modules (not Zeitwerk).
|
|
59
|
+
|
|
60
|
+
## Clean Coding Principles
|
|
61
|
+
|
|
62
|
+
- **SRP**: Classes and methods should have a single responsibility.
|
|
63
|
+
- **DRY**: Avoid duplication; changes should only need one edit.
|
|
64
|
+
- **Depend on behaviour, not data**: Wrap instance variables in methods (`attr_reader`); use Struct for data structures.
|
|
65
|
+
- **Minimise dependencies**: Use dependency injection, encapsulate external messages, prefer hash arguments.
|
|
66
|
+
- **Depend on things that change less often than you do.**
|
|
41
67
|
|
|
42
68
|
## Configuration DSL
|
|
43
69
|
|
|
44
|
-
- `SourceMonitor.configure`
|
|
70
|
+
- `SourceMonitor.configure` exposes structured namespaces:
|
|
71
|
+
- `config.authentication` gates the engine. With no `authenticate_with`/`authorize_with` handler configured, the engine is **fail-closed** and returns `403`. Set `config.authentication.open_access = true` (default `false`) only for local demos/sandboxes — never production.
|
|
45
72
|
- `config.http` for Faraday timeouts, retry policy, proxy, and default headers. Per the [Faraday retry docs](https://github.com/lostisland/faraday/blob/main/docs/middleware/index.md), middleware options map 1:1 to the settings we surface (max retries, interval, backoff, statuses).
|
|
46
73
|
- `config.scrapers` registers/overrides adapters by name; adapters must inherit from `SourceMonitor::Scrapers::Base` and are discovered before constant lookup.
|
|
47
74
|
- `config.retention` supplies global defaults for `items_retention_days`, `max_items`, and the pruning strategy (`:destroy` or `:soft_delete`). Runtimes treat blank source fields as “inherit from config”.
|
|
@@ -57,29 +84,109 @@ Run `bin/setup` to install gems, prepare the dummy database, and compile Tailwin
|
|
|
57
84
|
- `SourceMonitor::ItemCleanupJob` batches retention pruning across sources and can soft delete records (`rake source_monitor:cleanup:items` honours `SOFT_DELETE=true`, `SOURCE_IDS=1,2`). `SourceMonitor::LogCleanupJob` prunes old fetch/scrape logs (`rake source_monitor:cleanup:logs`, override `FETCH_LOG_DAYS` / `SCRAPE_LOG_DAYS`).
|
|
58
85
|
- Nightly recurring entries in `config/recurring.yml` enqueue both cleanup jobs by default; adjust schedules or disable via Solid Queue overrides as needed.
|
|
59
86
|
|
|
60
|
-
##
|
|
87
|
+
## Background Job Defaults
|
|
61
88
|
|
|
62
|
-
|
|
89
|
+
- SourceMonitor ensures Solid Queue is the default adapter when the host app is still using the async adapter, but respects any explicit `ActiveJob` configuration already in place. Override queues/concurrency via `SourceMonitor.configure`.
|
|
90
|
+
- Queue names are namespaced (`source_monitor_fetch`/`source_monitor_scrape` by default) and automatically honor host `queue_name_prefix`. Use `SourceMonitor.queue_name(:fetch)` helpers inside jobs.
|
|
91
|
+
- Dashboard queue metrics read directly from Solid Queue tables via `SourceMonitor::Jobs::SolidQueueMetrics`. Host apps must install the Solid Queue migrations (reuse the engine's `20251009140000_create_solid_queue_tables.rb` or run `rails solid_queue:install`) for the card to surface ready/scheduled/failed counts; otherwise the UI falls back to an availability warning. Mission Control remains optional for deeper drill-downs.
|
|
92
|
+
- The dummy host keeps Solid Queue tables in the primary database via `20251009140000_create_solid_queue_tables.rb`. Real apps can either reuse that migration or run `rails solid_queue:install` to manage a dedicated queue database—Mission Control expects one of those setups before it can surface data.
|
|
93
|
+
- Recurring schedules live in `config/recurring.yml`, scheduling `SourceMonitor::ScheduleFetchesJob` each minute plus the scraping scheduler every two minutes. Override the schedule path with `bin/jobs --recurring_schedule_file=...` (or `SOLID_QUEUE_RECURRING_SCHEDULE_FILE`) and disable recurring runners with `SOLID_QUEUE_SKIP_RECURRING=true` or `bin/jobs --skip-recurring`.
|
|
94
|
+
- Hosts that need to wrap Solid Queue command execution can set `config.recurring_command_job_class` in the generated initializer to point at their custom job class.
|
|
95
|
+
|
|
96
|
+
## Build, Test, and Development Commands
|
|
97
|
+
|
|
98
|
+
Run `bin/setup` to install gems, prepare the dummy database, and compile Tailwind. During feature work, `bin/dev` starts the dummy app with Solid Queue workers and Tailwind watcher.
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
bin/dev # Start dev server
|
|
102
|
+
bin/rails test # Run the MiniTest suite (NOT a substitute for CI -- see below)
|
|
103
|
+
bin/rubocop # Check style
|
|
104
|
+
bin/rubocop -a # Auto-fix style
|
|
105
|
+
bin/brakeman --no-pager # Security scan
|
|
106
|
+
bin/rails db:migrate # Run migrations
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The dummy host app runs on **port 3002** (`cd test/dummy && bin/rails server -p 3002`).
|
|
110
|
+
|
|
111
|
+
## Testing
|
|
63
112
|
|
|
64
|
-
|
|
113
|
+
- **Framework:** Minitest. NEVER use RSpec or FactoryBot. Name files with `_test.rb` and wrap suites in `module SourceMonitor`. Tests live in `test/models`, `test/controllers`, `test/system`, `test/lib`, `test/integration`.
|
|
114
|
+
- **Helpers:** `create_source!` factory, `with_inline_jobs`, `with_queue_adapter`.
|
|
115
|
+
- **HTTP:** WebMock disables external HTTP; VCR for recorded cassettes under `test/vcr_cassettes/` (record new fixtures with descriptive names like `source_fetch_success.yml`).
|
|
116
|
+
- **Config isolation:** `test/test_helper.rb` calls `SourceMonitor.reset_configuration!` in every test's setup (rebuilding a fresh `Configuration`), so any config a test relies on must be set in setup, not the dummy initializer.
|
|
117
|
+
- **Coverage:** Target >90% for new services; cover every model validation, scope, public method, and controller action, plus regression tests for bug fixes.
|
|
118
|
+
- **Parallelism:** Coverage runs need `COVERAGE=1 PARALLEL_WORKERS=1` with **threads** (not forks) to avoid a PG segfault and SimpleCov data loss. Scope queries to a specific source/item to prevent cross-test contamination in parallel runs.
|
|
119
|
+
- **Automate what you can:** Anything verifiable programmatically (config defaults, job enqueue behavior, controller responses) should be a test, not a manual checkpoint.
|
|
120
|
+
|
|
121
|
+
## Quality Gates
|
|
122
|
+
|
|
123
|
+
- `bin/rubocop` — zero offenses before commit (omakase: only ~45/775 cops enabled, all Metrics cops disabled — no file-size enforcement).
|
|
124
|
+
- `bin/brakeman --no-pager` — zero warnings before merge.
|
|
125
|
+
- `bin/rails test` — all tests pass.
|
|
126
|
+
- `yarn build` — rebuild JS assets if any `.js` files changed (ESLint runs in CI).
|
|
127
|
+
- No N+1 queries (use `includes`/`preload`).
|
|
128
|
+
- No hardcoded credentials (use Rails credentials or ENV).
|
|
129
|
+
|
|
130
|
+
### Pre-Push CI Checklist (run ALL before pushing to GitHub)
|
|
131
|
+
|
|
132
|
+
Before pushing any branch (especially release branches), run the full CI equivalent locally:
|
|
133
|
+
|
|
134
|
+
1. `bin/rubocop` — catches Ruby lint issues.
|
|
135
|
+
2. `bin/test-coverage` — **this is what CI's `test` job actually runs.** Do NOT rely on `bin/rails test` to predict CI: `bin/rails test` uses a different harness/seed and does NOT run the diff-coverage gate, so it can be green (e.g. 1741/0) while CI fails. `bin/test-coverage` runs the real seed, the second `health_suite` pass, and the host-app-template test that **changes the process CWD mid-suite** (see gemspec note below).
|
|
136
|
+
3. `bundle exec ruby bin/check-diff-coverage` — run AFTER `bin/test-coverage` (it reads `coverage/.resultset.json`). Reproduces the CI diff-coverage gate locally (threshold 90% on changed `app/`/`lib/` lines vs `origin/main`). This is the only way to know the gate passes before pushing.
|
|
137
|
+
4. `bin/brakeman --no-pager` — catches security issues.
|
|
138
|
+
5. `yarn build` — rebuilds JS and catches ESLint issues (CI runs ESLint separately).
|
|
139
|
+
|
|
140
|
+
If legitimate coverage gaps remain after new code paths, refresh the baseline: `bin/test-coverage` then `bin/update-coverage-baseline`, and commit the regenerated `config/coverage_baseline.json`.
|
|
141
|
+
|
|
142
|
+
**Why:** CI failures cost ~5 min per round-trip. Hard-won lessons:
|
|
143
|
+
- **CI runs `bin/test-coverage`, not `bin/rails test`** — replicate the gate locally with steps 2 + 3. (v0.14.0: a green local `bin/rails test` masked a CI `bin/test-coverage` failure.)
|
|
144
|
+
- **Diff coverage covers EVERY changed `app/`/`lib/` line**, including defensive/edge branches with no natural caller. If a branch can't be reached through a normal engine route (e.g. the `#130` turbo_stream flash-append, which only fires when a turbo_stream request carries a Rails flash), add a **test-only probe controller in the dummy app** (pattern: `test/dummy/app/controllers/test_support_controller.rb` + a route in `test/dummy/config/routes.rb`); subclass `SourceMonitor::ApplicationController` if the branch lives in the engine's filter chain.
|
|
145
|
+
- Every `rescue`/fallback/error path in new source code needs test coverage.
|
|
146
|
+
- **Gemspec packaging must be CWD-independent.** Do NOT use a bare `Dir[...]` glob in `source_monitor.gemspec` for file selection — it resolves against the process CWD, and during `bin/test-coverage` a sibling test chdir's into a generated host app, so the glob returns nothing and files silently drop from the package (v0.14.0 `#131`). Drive packaging from `git ls-files` inside the existing `Dir.chdir(File.expand_path(__dir__))` block.
|
|
147
|
+
- JS files need `/* global */` declarations for browser APIs (MutationObserver, requestAnimationFrame, etc.); ESLint `no-undef` rejects them otherwise. Run `yarn build` after JS changes to sync sourcemaps.
|
|
148
|
+
|
|
149
|
+
## Contribution Workflow
|
|
65
150
|
|
|
66
|
-
|
|
151
|
+
- Treat the engine like any external contributor would: no direct commits to `main`.
|
|
152
|
+
- Before writing code, branch off the latest `origin/main` (use a descriptive `feature/` or `bugfix/` prefix) and open a draft PR on `github.com/dchuk/source_monitor`.
|
|
153
|
+
- Push early and often to that branch so history stays visible; keep commits scoped and rebases local to your branch only.
|
|
154
|
+
- Move the PR out of draft once tests pass and the slice is ready for review; request at least one review and wait for all CI jobs (lint, security, test + diff coverage, release_verification) to succeed before merging.
|
|
155
|
+
- Merge via the PR UI (squash or rebase as agreed) after approval; avoid rewriting shared history post-push.
|
|
156
|
+
- Tag releases only after the release PR merges, then follow the checklist in `CHANGELOG.md`. There are TWO version files — `lib/source_monitor/version.rb` AND the top-level `VERSION` — and both must match; run `bundle install` after a bump so `Gemfile.lock` stays in sync (CI runs `--frozen`).
|
|
67
157
|
|
|
68
158
|
## Commit & Pull Request Guidelines
|
|
69
159
|
|
|
70
|
-
|
|
160
|
+
Use Conventional Commit subjects in the format `type(scope): description`, e.g., `fix(fetch): resolve advisory lock contention`. Group unrelated work into separate commits. PRs should describe context, summarise the slice delivered, and list validation steps (`bin/test-coverage`, manual fetch run). Include screenshots or console output when altering UI or background jobs. Request at least one review and ensure CI completes before merge.
|
|
161
|
+
|
|
162
|
+
## Security
|
|
71
163
|
|
|
72
|
-
|
|
164
|
+
### Protected files (NEVER read or output)
|
|
165
|
+
- `.env`, `.env.*`
|
|
166
|
+
- `config/master.key`, `config/credentials.yml.enc`
|
|
167
|
+
- `.kamal/secrets`
|
|
168
|
+
- Any `*.pem` / `*.key` files
|
|
73
169
|
|
|
170
|
+
### Forbidden operations
|
|
171
|
+
- `git push --force` to main/master/production
|
|
172
|
+
- `git reset --hard` without explicit user confirmation
|
|
173
|
+
- `rm -rf` on root, home, or parent directories
|
|
174
|
+
- `chmod 777`
|
|
175
|
+
|
|
176
|
+
### General
|
|
74
177
|
Store secrets (API keys, webhook tokens) in `config/credentials/` and never commit plain-text values. When adding HTTP endpoints or webhooks, default to Solid Queue middleware for retries and respect the allowlist in `config/source_monitor.yml`. Document new environment variables in `config/application.yml.sample` and call out any migrations that impact host apps.
|
|
75
178
|
|
|
76
|
-
##
|
|
179
|
+
## Maintenance: Skills & Docs Alignment
|
|
77
180
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
-
|
|
181
|
+
Whenever engine code changes (models, configuration, pipeline, jobs, migrations, scrapers, events, health rules, or dashboard), the corresponding `sm-*` skill and its `reference/` files MUST be updated **in the same PR** so skills always reflect current engine behavior. Releases must also audit `README.md`, `docs/` (especially `docs/upgrade.md`), and the install initializer template against the source code.
|
|
182
|
+
|
|
183
|
+
## Library Documentation
|
|
184
|
+
|
|
185
|
+
- Use Context7 MCP constantly to look up fresh documentation for any task, especially tasks that rely on libraries or gems.
|
|
186
|
+
|
|
187
|
+
### Project Dependencies & context7 links
|
|
188
|
+
|
|
189
|
+
- Feedjira - https://context7.com/feedjira/feedjira
|
|
83
190
|
|
|
84
191
|
## Claude Code Skills
|
|
85
192
|
|
|
@@ -92,4 +199,4 @@ bin/rails source_monitor:skills:all # All skills
|
|
|
92
199
|
bin/rails source_monitor:skills:remove # Remove all sm-* skills
|
|
93
200
|
```
|
|
94
201
|
|
|
95
|
-
See `CLAUDE.md` for the full skills catalog and
|
|
202
|
+
See `CLAUDE.md` for the full skills catalog (consumer vs. contributor) and the `.claude/` agent catalog.
|
data/CHANGELOG.md
CHANGED
|
@@ -13,6 +13,35 @@ All notable changes to this project are documented below. The format follows [Ke
|
|
|
13
13
|
|
|
14
14
|
## [Unreleased]
|
|
15
15
|
|
|
16
|
+
- No unreleased changes yet.
|
|
17
|
+
|
|
18
|
+
## [0.15.0] - 2026-06-18
|
|
19
|
+
|
|
20
|
+
### Security
|
|
21
|
+
- Bump Rails to 8.1.3 via the 8.1.2.1 security release (#104), picking up fixes for five CVEs including XSS in tag/DebugExceptions helpers, an Active Storage path-traversal, and a NumberConverter issue.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
- **Allow ViewComponent 4.x** (#96). The `view_component` dependency constraint widens from `>= 3.0, < 4.0` to `>= 3.0, < 5.0`, so host apps can now resolve ViewComponent 4.x (the engine's lockfile moves to 4.5.0). The engine's components use only stable ViewComponent APIs and are unaffected by the v4 upgrade — but a host app on its own ViewComponent 3.x customizations should review the v4 upgrade notes before running `bundle update`.
|
|
25
|
+
- Bump Solid Queue to 1.4.0 (#102) — race-condition and supervisor stability fixes; the new dynamic recurring-tasks feature is opt-in and off by default.
|
|
26
|
+
- Bump nokolexbor to 0.6.4 (#103) and json to 2.19.2 (#99).
|
|
27
|
+
- Development/CI dependency bumps: test-prof 1.6.0 (#101), webmock 3.26.2 (#100), brakeman 8.0.4 (#88), selenium-webdriver 4.41.0 (#78), stackprof 0.2.28 (#71), and the GitHub Actions artifact actions (#87, #86).
|
|
28
|
+
|
|
29
|
+
### Documentation
|
|
30
|
+
- Consolidate engine conventions into `AGENTS.md` as the canonical, cross-agent reference; `CLAUDE.md` now points to it instead of duplicating content (#134).
|
|
31
|
+
|
|
32
|
+
## [0.14.0] - 2026-05-28
|
|
33
|
+
|
|
34
|
+
### Security (BREAKING)
|
|
35
|
+
- **Fail-closed engine access by default** (#129). When the host app has configured no authentication/authorization handler, every mounted SourceMonitor route now returns `403 Forbidden` instead of being publicly accessible. Previously `authenticate!`/`authorize!` were silent no-ops when unconfigured, so create/update/delete/enqueue routes were public by omission.
|
|
36
|
+
- **Migration:** Configure a handler — `config.authentication.authenticate_with` and/or `config.authentication.authorize_with` — to gate the engine with your host auth stack. Configured handlers are honored exactly as before.
|
|
37
|
+
- **Demo opt-in:** To intentionally keep routes public (local demos/sandboxes only), set `config.authentication.open_access = true` (default `false`). Do not enable this in production.
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
- **Request flashes no longer leak across users** (#130). Request flash toasts were broadcast to a global ActionCable stream (`source_monitor_notifications`) that every connected browser tab subscribed to, so one user's flash could appear on every user's screen. Flashes are now delivered response-local — rendered inline on full-page HTML loads and appended to the turbo_stream response otherwise — reaching only the requesting tab. Background operational toasts (fetch/scrape completion) remain global but carry no per-user request context.
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
- **Gem package no longer ships `.claude` internals** (#131). The packaged gem now excludes `.claude` agents, hooks, agent-memory, `settings.json`, commands, and non-`sm-*` skills; only the intended `.claude/skills/sm-*` SourceMonitor skills are shipped. Packaging is also now CWD-independent so the skill files are included deterministically.
|
|
44
|
+
|
|
16
45
|
## [0.13.1] - 2026-05-28
|
|
17
46
|
|
|
18
47
|
### Fixed
|