source_monitor 0.1.3 → 0.2.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/CHANGELOG.md +22 -0
- data/Gemfile.lock +1 -1
- data/app/assets/javascripts/source_monitor/application.js +4 -0
- data/app/assets/javascripts/source_monitor/controllers/confirm_navigation_controller.js +49 -0
- data/app/assets/javascripts/source_monitor/controllers/select_all_controller.js +36 -0
- data/app/controllers/source_monitor/import_sessions_controller.rb +791 -0
- data/app/controllers/source_monitor/sources_controller.rb +5 -36
- data/app/helpers/source_monitor/application_helper.rb +17 -0
- data/app/jobs/source_monitor/import_opml_job.rb +150 -0
- data/app/jobs/source_monitor/import_session_health_check_job.rb +93 -0
- data/app/models/source_monitor/import_history.rb +35 -0
- data/app/models/source_monitor/import_session.rb +34 -0
- data/app/views/source_monitor/import_sessions/_header.html.erb +12 -0
- data/app/views/source_monitor/import_sessions/_sidebar.html.erb +23 -0
- data/app/views/source_monitor/import_sessions/health_check/_progress.html.erb +20 -0
- data/app/views/source_monitor/import_sessions/health_check/_row.html.erb +44 -0
- data/app/views/source_monitor/import_sessions/show.html.erb +15 -0
- data/app/views/source_monitor/import_sessions/show.turbo_stream.erb +1 -0
- data/app/views/source_monitor/import_sessions/steps/_configure.html.erb +53 -0
- data/app/views/source_monitor/import_sessions/steps/_confirm.html.erb +121 -0
- data/app/views/source_monitor/import_sessions/steps/_health_check.html.erb +82 -0
- data/app/views/source_monitor/import_sessions/steps/_navigation.html.erb +29 -0
- data/app/views/source_monitor/import_sessions/steps/_preview.html.erb +172 -0
- data/app/views/source_monitor/import_sessions/steps/_upload.html.erb +42 -0
- data/app/views/source_monitor/sources/_form.html.erb +8 -138
- data/app/views/source_monitor/sources/_form_fields.html.erb +142 -0
- data/app/views/source_monitor/sources/_import_history_panel.html.erb +53 -0
- data/app/views/source_monitor/sources/index.html.erb +7 -1
- data/config/coverage_baseline.json +91 -15
- data/config/routes.rb +6 -0
- data/db/migrate/20251124090000_create_import_sessions.rb +18 -0
- data/db/migrate/20251124153000_add_health_fields_to_import_sessions.rb +14 -0
- data/db/migrate/20251125094500_create_import_histories.rb +19 -0
- data/lib/source_monitor/health/import_source_health_check.rb +55 -0
- data/lib/source_monitor/health.rb +1 -0
- data/lib/source_monitor/import_sessions/entry_normalizer.rb +30 -0
- data/lib/source_monitor/import_sessions/health_check_broadcaster.rb +103 -0
- data/lib/source_monitor/sources/params.rb +52 -0
- data/lib/source_monitor/version.rb +1 -1
- data/tasks/completed/codebase_audit_2025.md +1396 -0
- data/tasks/completed/engine-asset-configuration.md +203 -0
- data/tasks/completed/opml-import-wizard/opml-import-wizard-product-brief.md +58 -0
- data/tasks/completed/opml-import-wizard/opml-import-wizard-tech-brief.md +75 -0
- data/tasks/completed/opml-import-wizard/task-01/instructions.md +81 -0
- data/tasks/completed/opml-import-wizard/task-01/requirements.md +19 -0
- data/tasks/completed/opml-import-wizard/task-02/instructions.md +83 -0
- data/tasks/completed/opml-import-wizard/task-02/requirements.md +18 -0
- data/tasks/completed/opml-import-wizard/task-03/instructions.md +58 -0
- data/tasks/completed/opml-import-wizard/task-03/requirements.md +18 -0
- data/tasks/completed/opml-import-wizard/task-04/instructions.md +84 -0
- data/tasks/completed/opml-import-wizard/task-04/requirements.md +17 -0
- data/tasks/completed/opml-import-wizard/task-05/instructions.md +50 -0
- data/tasks/completed/opml-import-wizard/task-05/requirements.md +17 -0
- data/tasks/completed/opml-import-wizard/task-06/instructions.md +92 -0
- data/tasks/completed/opml-import-wizard/task-06/requirements.md +21 -0
- data/tasks/completed/phase_17_01_complexity_audit_2025-10-12.md +62 -0
- data/tasks/completed/phase_17_02_complexity_findings_2025-10-12.md +74 -0
- data/tasks/completed/phase_17_03_refactor_plan_2025-10-12.md +37 -0
- data/tasks/completed/phase_21_01_log_consolidation_2025-10-15.md +30 -0
- data/tasks/completed/release_checklist.md +23 -0
- data/tasks/completed/routes_refactor_evaluation.md +109 -0
- data/tasks/completed/source_monitor_rename_plan.md +70 -0
- data/tasks/completed/tasks.md +952 -0
- data/tasks/ideas.md +10 -0
- metadata +56 -3
- /data/tasks/{prd-setup-workflow-streamlining.md → completed/prd-setup-workflow-streamlining.md} +0 -0
- /data/tasks/{tasks-setup-workflow-streamlining.md → completed/tasks-setup-workflow-streamlining.md} +0 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
**Context**
|
|
2
|
+
|
|
3
|
+
- User story: "Health Check for Selected Sources" within the OPML Import Wizard. This Health Check step runs a health check for each selected parsed source, shows live results, and updates selection state before import.
|
|
4
|
+
- Tech brief: health checks must be enqueued as individual Solid Queue jobs, results persisted back into ImportSession.parsed_sources (fields: health_status, error), and updates broadcast in real-time via Turbo Streams. ImportSession uses a standard integer user_id and persists wizard state (parsed_sources, selected_source_ids, current_step, etc.).
|
|
5
|
+
- Existing codebase patterns to follow: Solid Queue job patterns (app/jobs/*), existing source health check logic and controller (source_health_checks_controller), realtime broadcasters (lib/source_monitor/realtime/broadcaster.rb), and Turbo Stream / Turbo Frame UI conventions (lib/source_monitor/turbo_streams/stream_responder.rb).
|
|
6
|
+
|
|
7
|
+
Goals
|
|
8
|
+
|
|
9
|
+
- Enqueue one Solid Queue health-check job per selected source when the Health Check step starts.
|
|
10
|
+
- Persist per-source health results into ImportSession.parsed_sources (add/ensure fields health_status and error).
|
|
11
|
+
- Broadcast row-level updates and an overall progress update (completed / total) in real time via Turbo Streams so the Health Check step UI updates as jobs complete.
|
|
12
|
+
- Unselect sources deemed unhealthy by default while allowing the user to re-select them; ensure selection state persists in ImportSession.selected_source_ids.
|
|
13
|
+
- Prevent navigation forward if no healthy sources remain selected (unless the user intentionally re-selects unhealthy ones).
|
|
14
|
+
- Implement server-side cancellation semantics so jobs do not update or broadcast for an expired/cancelled ImportSession.
|
|
15
|
+
|
|
16
|
+
Technical Guidelines
|
|
17
|
+
|
|
18
|
+
- Job orchestration
|
|
19
|
+
- Enqueue a Solid Queue job per selected parsed source. Job payload must include at minimum: import_session_id and an identifier for the parsed source (index or parsed_source_id/UUID as stored in ImportSession.parsed_sources).
|
|
20
|
+
- Reuse existing feed health check logic where available (inspect source_health_checks controller / health-check service used elsewhere) rather than reimplementing HTTP/Feed checks.
|
|
21
|
+
- Name or implement the job in line with existing job conventions (app/jobs/source_monitor/*). The job should:
|
|
22
|
+
- Load the ImportSession (guard by integer user_id scope).
|
|
23
|
+
- Verify the ImportSession still exists and is in the Health Check step (or has a health_checks_active flag). If the session has been cancelled/expired, exit without broadcasting.
|
|
24
|
+
- Run the health check for the feed URL.
|
|
25
|
+
- Persist result into ImportSession.parsed_sources for the specific parsed entry: set health_status (values: healthy/unhealthy/unknown) and error (string or nil).
|
|
26
|
+
- Update ImportSession.selected_source_ids to remove/unselect the entry if health_status == unhealthy (so UI reflects unselected by default). Do not destroy historical parsed data.
|
|
27
|
+
- Broadcast a Turbo Stream update for the row and a progress update (completed count) after persisting.
|
|
28
|
+
|
|
29
|
+
- Persistence and data model
|
|
30
|
+
- Store health results inside ImportSession.parsed_sources entries (JSONB). Each parsed source entry must include:
|
|
31
|
+
- id/key (must be addressable by job),
|
|
32
|
+
- feed_url (used for duplicate detection),
|
|
33
|
+
- health_status (nullable string enum),
|
|
34
|
+
- health_error (nullable text).
|
|
35
|
+
- Selection state must be authoritative in ImportSession.selected_source_ids (JSONB array of parsed entry ids). Health job changes must update that array atomically (transaction).
|
|
36
|
+
- Ensure ImportSession model uses integer user_id references per the tech brief.
|
|
37
|
+
|
|
38
|
+
- Broadcast / UI contract
|
|
39
|
+
- Use Turbo Streams for all live updates. Follow existing engine broadcaster/presenter patterns:
|
|
40
|
+
- Broadcast a row replacement partial (Turbo Stream) for the specific parsed source row showing updated health_status icon and selection checkbox state.
|
|
41
|
+
- Broadcast a separate Turbo Stream or stream fragment with overall progress (completed / total). The progress UI in the Health Check view should subscribe to this stream.
|
|
42
|
+
- Choose a Turbo stream target scope tied to the ImportSession (e.g., stream name or DOM target that includes import_session id) so updates only reach the appropriate wizard instance. Ensure consistent naming with other engine broadcasters.
|
|
43
|
+
- Keep UI updates non-destructive: rows should preserve user ability to re-select unhealthy sources.
|
|
44
|
+
|
|
45
|
+
- UI-side behavior to enforce
|
|
46
|
+
- After a health check job marks a source unhealthy, the UI must show it as unselected by default and display the error inline on the row (tooltip or error cell) while permitting re-selection.
|
|
47
|
+
- The progress indicator must show completed / total and update as job results arrive.
|
|
48
|
+
- Navigation to the next step must be disabled when selected healthy sources count is zero. Re-enabling should occur when the user re-selects at least one source manually.
|
|
49
|
+
|
|
50
|
+
- Cancellation semantics
|
|
51
|
+
- When the user navigates away from the Health Check step or cancels the wizard, mark the ImportSession (e.g., set health_checks_active: false or current_step != 'health_check'). HealthCheck jobs must check this flag and not broadcast or mutate UI-visible session state if the session is no longer active.
|
|
52
|
+
- Jobs may still persist logs/errors for auditing but must not broadcast to the now-expired wizard UI.
|
|
53
|
+
|
|
54
|
+
- Concurrency and safety
|
|
55
|
+
- Jobs updating ImportSession should be resilient to concurrent updates: use transactions and row-level locking (or optimistic update with retries) to modify parsed_sources and selected_source_ids safely.
|
|
56
|
+
- Handle cases where a duplicate source is discovered or a source is removed from selected_source_ids mid-check gracefully: persist the health result but ensure selected_source_ids and UI state remain consistent.
|
|
57
|
+
|
|
58
|
+
- Follow SourceMonitor conventions
|
|
59
|
+
- Adhere to engine style: controller and job naming, Solid Queue enqueue patterns, Turbo Stream rendering, Tailwind/Turbo Frame based partials, accessibility.
|
|
60
|
+
- Enforce admin-only access around enqueuing health checks via existing authentication hooks.
|
|
61
|
+
- Strong-params/sanitization for any controller endpoints that trigger health checks or cancel them.
|
|
62
|
+
|
|
63
|
+
Out of scope
|
|
64
|
+
|
|
65
|
+
- Implementing retry logic for failed health checks (explicitly out of scope in PRD).
|
|
66
|
+
- Building or changing the visual design of the Health Check UI beyond status icon/error and progress updates.
|
|
67
|
+
- Global realtime channel/refactor: use engine broadcaster patterns rather than introducing an unrelated pubsub mechanism.
|
|
68
|
+
- Changes to sourcemon_sources table or source creation logic—duplicate detection remains feed-URL-only and uses existing source records.
|
|
69
|
+
|
|
70
|
+
Suggested research
|
|
71
|
+
|
|
72
|
+
- Inspect existing health check code and controller:
|
|
73
|
+
- app/controllers/source_monitor/source_health_checks_controller.rb (how single-source health checks are performed and broadcast).
|
|
74
|
+
- Any health-check services used for per-source health (search for "health" related services or logs).
|
|
75
|
+
- Review Solid Queue job patterns used in repo for enqueueing and job implementation:
|
|
76
|
+
- app/jobs/source_monitor/* (FetchFeedJob, ScrapeItemJob) for job structure, error handling, and broadcasting style.
|
|
77
|
+
- Review realtime/Turbo broadcaster and stream responder conventions:
|
|
78
|
+
- lib/source_monitor/realtime/broadcaster.rb
|
|
79
|
+
- lib/source_monitor/turbo_streams/stream_responder.rb
|
|
80
|
+
- existing Turbo stream presenters for row updates (e.g., sources presenters under lib/*).
|
|
81
|
+
- Inspect ImportSession model and migration (app/models/source_monitor/import_session.rb or equivalent) to confirm parsed_sources and selected_source_ids shapes and keys used to address parsed entries.
|
|
82
|
+
- Find existing wizard/ImportSession views or step partials to see expected Turbo Frame names and where to render row partials and the progress indicator (app/views/source_monitor/imports/* or similar).
|
|
83
|
+
|
|
84
|
+
Use these guidelines to implement the Health Check step backend jobs and Turbo Stream UI updates; leave view markup and job wiring details to the implementer following the repository's conventions.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#### Goals
|
|
2
|
+
|
|
3
|
+
- Provide a Health Check step that enqueues an individual background health-check job for each selected source.
|
|
4
|
+
- Display a live-updating results table with status icons (healthy/unhealthy) updated via Turbo Streams as jobs complete.
|
|
5
|
+
- Show a progress indicator (counts completed/total) and unselect unhealthy sources by default while allowing users to re-select them.
|
|
6
|
+
- Block navigation forward if no healthy sources remain selected (unless user manually re-selects an unhealthy source).
|
|
7
|
+
|
|
8
|
+
#### Technical Considerations
|
|
9
|
+
|
|
10
|
+
- Use Solid Queue for background jobs; create or reuse a HealthCheckJob that runs existing feed health check logic and broadcasts results on a Turbo Stream channel/topic for the ImportSession or user.
|
|
11
|
+
- Store health check results back into ImportSession.parsed_sources entries (health_status, error) so step state is persistent and viewable if refreshed while session exists. Ensure ImportSession is implemented with a standard integer-based user reference (user_id as integer).
|
|
12
|
+
- Implement server-side cancellation semantics: if the user navigates away from the health-check step before all jobs finish, mark in ImportSession and ensure any remaining jobs do not update transient UI state or re-broadcast for an expired session.
|
|
13
|
+
- Ensure progress updates and row updates follow the engine’s Turbo Streams conventions and use existing broadcasters patterns.
|
|
14
|
+
|
|
15
|
+
#### Dependencies
|
|
16
|
+
|
|
17
|
+
- Build Preview Table & Selection Persistence (selected set to health-check)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
- **Context**
|
|
2
|
+
- Implement the "Configure" step in the OPML Import Wizard (Configure step of the multi-step wizard). This step must render the exact same input fields and validation behavior as the single-source creation form, and persist the validated bulk settings to ImportSession.bulk_settings (JSONB) so settings carry forward to Confirm and are applied uniformly to all selected sources.
|
|
3
|
+
- This task depends on the surrounding wizard shell and ImportSession persistence being present (ImportSession record storing parsed sources / selections). The Configure step is one Turbo Frame view inside the wizard route.
|
|
4
|
+
|
|
5
|
+
- **Goals**
|
|
6
|
+
- Render the same input fields, field order, help text and inline validation as the single-source creation form.
|
|
7
|
+
- Persist validated bulk settings into ImportSession.bulk_settings (JSONB) tied to the ImportSession record for the current user.
|
|
8
|
+
- Prevent advancing to the Confirm step until required fields are valid; show inline validation errors in the Configure view.
|
|
9
|
+
- Ensure settings apply uniformly to all selected sources (no per-feed overrides).
|
|
10
|
+
- Follow SourceMonitor UI conventions (Tailwind, Turbo Frames, accessibility) and enforce admin-only access.
|
|
11
|
+
|
|
12
|
+
- **Technical Guidelines**
|
|
13
|
+
- Reuse UI:
|
|
14
|
+
- Render the existing single-source form partial used by new/edit source flows (use the same partial(s) so HTML structure, labels, help text and classes are identical).
|
|
15
|
+
- Render the form inside the Configure step Turbo Frame used by the wizard layout and include the same error rendering (inline field errors).
|
|
16
|
+
- Params & Validation:
|
|
17
|
+
- Map the submitted form params to the same permitted attributes used for single-source creation (use the same strong-parameter logic as the SourcesController). Reuse SourceMonitor’s existing parameter sanitizer or the SourcesController's source_params as canonical source attributes.
|
|
18
|
+
- Validate required fields server-side using the same validation rules or service used for single-source creation before persisting to ImportSession.bulk_settings. If the engine has an existing form object/service for validating source attributes, reuse it; otherwise, perform the same attribute-level validations prior to saving the ImportSession.
|
|
19
|
+
- If validation fails, render the Configure step with the same inline error presentation and prevent progression.
|
|
20
|
+
- Persistence:
|
|
21
|
+
- Persist the resulting validated settings JSON into ImportSession.bulk_settings. The ImportSession record must be scoped to the current user (ImportSession.user_id must use integer-based ActiveRecord FK).
|
|
22
|
+
- Keep settings persisted when the user navigates between wizard steps (updates to ImportSession.bulk_settings replace previous values).
|
|
23
|
+
- Do not apply settings to sources yet — just store them until the final import job runs.
|
|
24
|
+
- Access control and security:
|
|
25
|
+
- Restrict the Configure step to authenticated admin users consistent with engine conventions.
|
|
26
|
+
- Use existing strong-parameter sanitization utilities. Do not accept arbitrary params; persist only the permitted source attributes structure to bulk_settings.
|
|
27
|
+
- UI/UX:
|
|
28
|
+
- Apply the same Tailwind classes and accessibility attributes as the single-source form.
|
|
29
|
+
- Ensure the Configure step form disables the Next/Confirm action until the server confirms the form is valid (server-side validation); show inline validation messages without toasts.
|
|
30
|
+
- Do not implement per-feed overrides — the UI must not expose per-source editing here.
|
|
31
|
+
- Integration shape:
|
|
32
|
+
- The Configure controller action should update ImportSession.bulk_settings and return a Turbo Frame response so the wizard UI updates in-place.
|
|
33
|
+
- Ensure any client-side flows follow existing Turbo Frame/Turbo Stream patterns used throughout the engine (no new client-side state store).
|
|
34
|
+
- Data shape:
|
|
35
|
+
- Persist settings as JSON that matches the attribute names used by sourcemon_sources creation (so the import job can apply them to each created source).
|
|
36
|
+
- Logging & errors:
|
|
37
|
+
- Surface validation errors inline. For unexpected save errors (DB or permission), render an actionable error message on the Configure step and do not advance.
|
|
38
|
+
|
|
39
|
+
- **Out of scope**
|
|
40
|
+
- Implementing the final import background job, import history persistence, or broadcasting results.
|
|
41
|
+
- Per-feed (per-row) settings or overrides.
|
|
42
|
+
- Health check orchestration or changes to health-check results UI.
|
|
43
|
+
- Wizard shell routing, ImportSession model/migration creation (assume ImportSession exists and is integer-user-scoped), and Upload/Preview step parsing logic.
|
|
44
|
+
- Client-side persistence beyond Turbo Frames / ImportSession-backed persistence.
|
|
45
|
+
|
|
46
|
+
- **Suggested research**
|
|
47
|
+
- Inspect the single-source form partial(s) used by new/edit source pages (app/views/source_monitor/sources/* — locate _form partial and new/edit templates) to reuse markup and error helpers.
|
|
48
|
+
- Review SourcesController#source_params and SourceMonitor::Security::ParameterSanitizer (or equivalent) to determine the exact permitted attributes shape to persist.
|
|
49
|
+
- Inspect the ImportSession model/schema and controller endpoints for updating session state (ImportSession.bulk_settings JSONB field) and confirm user scoping and allowed update patterns.
|
|
50
|
+
- Find examples in the engine of reusing partials and Turbo Frame responses for form steps (sources index/new flows and other wizard-like views) to match response patterns and accessibility/styling conventions.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#### Goals
|
|
2
|
+
|
|
3
|
+
- Implement the Configure step that displays the exact same input fields as the single source creation form, applied uniformly to all selected sources.
|
|
4
|
+
- Persist validated bulk settings into ImportSession.bulk_settings so they carry forward to the Confirm step and are applied during import.
|
|
5
|
+
- Validate required fields and prevent progression until the form is valid.
|
|
6
|
+
|
|
7
|
+
#### Technical Considerations
|
|
8
|
+
|
|
9
|
+
- Reuse the existing single-source form partial to render fields and validation messages; adapt controller params mapping to persist the resulting bulk settings JSON into ImportSession.bulk_settings. Ensure ImportSession uses an integer-based user reference (standard Rails user_id integer foreign key).
|
|
10
|
+
- Use the engine's existing parameter sanitization and validation patterns for source attributes.
|
|
11
|
+
- Ensure the form is accessible and styled with Tailwind consistent with the rest of the engine.
|
|
12
|
+
- Disallow per-feed overrides in this release; the stored settings apply uniformly to all selected sources during import.
|
|
13
|
+
|
|
14
|
+
#### Dependencies
|
|
15
|
+
|
|
16
|
+
- Enqueue Health Checks & Turbo Stream UI (health results determine which sources are in the target set)
|
|
17
|
+
- Implement OPML Import Wizard Shell (wizard rendering and step navigation)
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
**Context**
|
|
2
|
+
|
|
3
|
+
- This task implements the Confirm step of the OPML Import Wizard and the background import job for SourceMonitor.
|
|
4
|
+
- Upstream work (wizard shell, OPML parsing, preview/selection, health checks, bulk settings) will have populated an ImportSession record containing parsed_sources, selected_source_ids and bulk_settings.
|
|
5
|
+
- The engine already uses Solid Queue for background jobs, Turbo Streams for realtime UI updates, and has existing source creation logic and Turbo broadcast/responders to follow.
|
|
6
|
+
|
|
7
|
+
**Goals**
|
|
8
|
+
|
|
9
|
+
- Render a Confirm step view that shows a final summary: the list of selected sources to be imported and the bulk settings that will be applied.
|
|
10
|
+
- When the user confirms, enqueue a background import job (Solid Queue / ActiveJob) that:
|
|
11
|
+
- Iterates selected sources, creates sources individually (reusing the engine’s existing source creation logic/service where available).
|
|
12
|
+
- Records per-source outcomes: successes, failures (with error details), and skipped duplicates (feed URL match).
|
|
13
|
+
- Persists an ImportHistory AR record containing imported_sources, failed_sources, skipped_duplicates, bulk_settings, started_at, completed_at, and reference to the performing user (integer user_id).
|
|
14
|
+
- Handles idempotency/races (e.g., ActiveRecord::RecordNotUnique) and records appropriate skipped/failed state.
|
|
15
|
+
- Broadcast import completion via Turbo Streams so a static confirmation (counts and a table of failures) is visible on the Sources index; persist ImportHistory so the confirmation is queryable on subsequent visits.
|
|
16
|
+
|
|
17
|
+
**Technical Guidelines**
|
|
18
|
+
|
|
19
|
+
- Models & DB
|
|
20
|
+
- Add ImportHistory ActiveRecord model + migration.
|
|
21
|
+
- Use Rails default integer primary keys and integer user reference: e.g. t.references :user, foreign_key: true (integer FK).
|
|
22
|
+
- Columns: imported_sources (jsonb), failed_sources (jsonb), skipped_duplicates (jsonb), bulk_settings (jsonb), started_at (t.datetime), completed_at (t.datetime), created_at/updated_at.
|
|
23
|
+
- Index created_at for query performance.
|
|
24
|
+
- Do not change existing sourcemon_sources table schema. Duplicate detection must use feed_url matching against that table.
|
|
25
|
+
|
|
26
|
+
- Background Job
|
|
27
|
+
- Implement an import ActiveJob under app/jobs/source_monitor/import_opml_job.rb (or similar) that uses the Solid Queue adapter (follow existing job naming/queue conventions in repo).
|
|
28
|
+
- Job responsibilities:
|
|
29
|
+
- Load ImportSession or be supplied with the ImportSession id and ImportHistory id to update status.
|
|
30
|
+
- Persist started_at at job start and completed_at at finish to the ImportHistory record.
|
|
31
|
+
- For each selected parsed source:
|
|
32
|
+
- Skip any source that already exists by exact feed_url match — mark as skipped duplicate and include feed_url and reason in skipped_duplicates.
|
|
33
|
+
- Attempt to create the source by calling the engine’s existing source-creation service/object (discover and reuse service; do NOT reimplement creation logic in the job).
|
|
34
|
+
- On successful create: append a concise representation (id, feed_url, title) to imported_sources.
|
|
35
|
+
- On exception:
|
|
36
|
+
- If ActiveRecord::RecordNotUnique (race / concurrent import), treat as skipped duplicate (record to skipped_duplicates) rather than failure.
|
|
37
|
+
- Otherwise, record failure with feed_url, error class and message in failed_sources.
|
|
38
|
+
- Ensure partial failures do not stop processing of remaining sources; process all selected sources and collect per-source results.
|
|
39
|
+
- Save ImportHistory with final arrays and timestamps.
|
|
40
|
+
- Job must be idempotent for retries: detect already-created feed_url and avoid creating duplicates.
|
|
41
|
+
- Use transactions cautiously: individual source creations may be wrapped in their own transaction so one source failure doesn’t roll back the whole batch.
|
|
42
|
+
|
|
43
|
+
- Controller & Confirm Step
|
|
44
|
+
- Add Confirm step controller action(s) to the wizard controller that:
|
|
45
|
+
- Renders summary table of ImportSession.selected_source_ids joined with ImportSession.parsed_sources to show feed_url, title, and applied bulk_settings.
|
|
46
|
+
- On confirm POST, create an ImportHistory record (with user reference and bulk_settings), enqueue the import job (passing ImportSession id and ImportHistory id), and return an immediate Turbo Stream/html response that redirects user back to the Sources index (or shows a confirmation note).
|
|
47
|
+
- Enforce admin-only access using the engine’s existing authentication hooks (follow existing controller patterns; e.g., inherit SourceMonitor::ApplicationController and use configured authentication).
|
|
48
|
+
- Sanitize permitted params as per engine conventions.
|
|
49
|
+
|
|
50
|
+
- Broadcasting & UI persistence
|
|
51
|
+
- When job completes, broadcast a Turbo Stream message so the Sources index can render a static confirmation section (counts and failures).
|
|
52
|
+
- Reuse engine’s existing broadcasting patterns (lib/source_monitor/realtime/* and lib/source_monitor/turbo_streams/stream_responder.rb) — create a suitable Turbo Stream target (e.g., turbo_stream_from "source_monitor_import_histories" or reuse "source_monitor_sources") and broadcast a partial that renders ImportHistory summary.
|
|
53
|
+
- Ensure the ImportHistory record itself is persisted and can be queried from the Sources index UI (Sources index should be able to list recent ImportHistory entries). For this task: ensure ImportHistory model, migration and broadcast exist; UI change for Sources index to consume ImportHistory should read from ImportHistory.where(user: current_user). Order and exact UI placement follow engine conventions.
|
|
54
|
+
|
|
55
|
+
- Error handling & idempotency
|
|
56
|
+
- Duplicate detection must be feed_url exact match against sourcemon_sources before attempting create; handle race conditions (RecordNotUnique) gracefully by recording skipped_duplicates.
|
|
57
|
+
- For any unexpected exceptions record error_class, error_message and minimal backtrace (if desired) in failed_sources; do not leak raw exceptions to clients—render error details in admin UI only.
|
|
58
|
+
- Ensure the import job cannot create duplicate sources if it is run multiple times (use pre-check + rescue RecordNotUnique).
|
|
59
|
+
|
|
60
|
+
- Conventions & Integration
|
|
61
|
+
- Follow existing Rails engine patterns: controllers respond to HTML and turbo_stream, use Turbo Frames for partial updates, Tailwind styling, accessibility.
|
|
62
|
+
- Use Solid Queue / ActiveJob consistent queue naming conventions (look up SourceMonitor.config.queue_name_for or existing job classes to match queue naming).
|
|
63
|
+
- Use strong parameter sanitization utilities already present in the engine (SourceMonitor::Security::ParameterSanitizer or controller helpers).
|
|
64
|
+
- Tests: add unit/functional tests for ImportHistory model and job behavior, and a system/controller test covering confirm POST enqueuing job and final persisted ImportHistory.
|
|
65
|
+
|
|
66
|
+
**Out of scope**
|
|
67
|
+
|
|
68
|
+
- Implementing or modifying Preview/Upload/Health steps (assume ImportSession contains necessary state).
|
|
69
|
+
- Per-feed settings overrides (bulk settings apply uniformly).
|
|
70
|
+
- Real-time progress bar for the import job (only final completion broadcast and static confirmation required).
|
|
71
|
+
- Advanced retry/recovery logic for failed imports beyond basic idempotency and recording errors.
|
|
72
|
+
- Large-scale UI redesign—only add Confirm step view and ensure Sources index can display ImportHistory entries.
|
|
73
|
+
|
|
74
|
+
**Suggested research (files/areas to inspect before implementing)**
|
|
75
|
+
|
|
76
|
+
- Controller patterns and Turbo Stream responders:
|
|
77
|
+
- app/controllers/source_monitor/sources_controller.rb
|
|
78
|
+
- lib/source_monitor/turbo_streams/stream_responder.rb
|
|
79
|
+
- any existing controllers that render summary/confirmation messages
|
|
80
|
+
- Background job examples and Solid Queue usage:
|
|
81
|
+
- app/jobs/source_monitor/* (FetchFeedJob, ScrapeItemJob) and how they declare queues
|
|
82
|
+
- lib/source_monitor/jobs/solid_queue_metrics.rb and job conventions
|
|
83
|
+
- Real-time broadcasting patterns:
|
|
84
|
+
- lib/source_monitor/realtime/broadcaster.rb
|
|
85
|
+
- lib/source_monitor/dashboard/turbo_broadcaster.rb
|
|
86
|
+
- ImportSession usage and fields:
|
|
87
|
+
- the ImportSession model and where parsed_sources, selected_source_ids, bulk_settings are stored (migration and model file from ImportSession task)
|
|
88
|
+
- Source creation logic/service objects:
|
|
89
|
+
- app/controllers/source_monitor/sources_controller.rb#create and any service objects under lib/source_monitor or app/services related to creating a Source (prefer reuse)
|
|
90
|
+
- Tests demonstrating handling of ActiveRecord::RecordNotUnique or duplicate create patterns (see item creation duplicates in lib/source_monitor/items/item_creator.rb for pattern)
|
|
91
|
+
|
|
92
|
+
Use the repository’s existing patterns and helpers; do not introduce new auth/permission systems—leverage engine’s existing admin-only checks and Turbo Stream broadcasting conventions.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#### Goals
|
|
2
|
+
|
|
3
|
+
- Implement the Confirm step that shows a summary table of selected sources and the applied bulk settings for final review.
|
|
4
|
+
- When confirmed, enqueue a background import job that creates sources individually, records per-source success/failure and skipped duplicates, and persists an ImportHistory record with results.
|
|
5
|
+
- After job completion, ensure the user can view a static confirmation message on the Sources index with counts and a table of failures; guarantee that ImportHistory entries are queryable from the UI later.
|
|
6
|
+
|
|
7
|
+
#### Technical Considerations
|
|
8
|
+
|
|
9
|
+
- Use Solid Queue for the import background job; the job should iterate selected sources, create sources individually (reusing Sources creation logic/service objects), capture and persist errors per source, and skip duplicates discovered by feed URL matching.
|
|
10
|
+
- Create an ImportHistory ActiveRecord model/migration with JSONB columns for imported_sources, failed_sources, skipped_duplicates, bulk_settings, started_at and completed_at. Implement ImportHistory to reference the performing user using standard integer-based ActiveRecord IDs (i.e., user_id as an integer foreign key). Ensure migrations and model associations use Rails default integer id conventions rather than UUIDs.
|
|
11
|
+
- Broadcast import completion via Turbo Streams (targeting Sources index or an ImportHistory feed) so users see static messaging and results; Ensure the static confirmation is available on next visit to Sources index even if the user navigated away during processing.
|
|
12
|
+
- Ensure transactional and idempotency considerations for creation logic: handle ActiveRecord::RecordNotUnique or race conditions gracefully and record appropriate failure/skipped state.
|
|
13
|
+
- Enforce admin-only access and strong parameter sanitization.
|
|
14
|
+
|
|
15
|
+
#### Dependencies
|
|
16
|
+
|
|
17
|
+
- Implement OPML Import Wizard Shell
|
|
18
|
+
- Add OPML Upload & Synchronous Parsing
|
|
19
|
+
- Build Preview Table & Selection Persistence
|
|
20
|
+
- Enqueue Health Checks & Turbo Stream UI
|
|
21
|
+
- Bulk Settings Form Reusing Single Source Partial
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Phase 17.01 Complexity Audit (2025-10-12)
|
|
2
|
+
|
|
3
|
+
## Component Inventory
|
|
4
|
+
|
|
5
|
+
- **Controllers (7)**: `SourceMonitor::ApplicationController`, `DashboardController`, `SourcesController`, `ItemsController`, `FetchLogsController`, `ScrapeLogsController`, `HealthController`.
|
|
6
|
+
- **Models (6)**: `SourceMonitor::ApplicationRecord`, `Source`, `Item`, `ItemContent`, `FetchLog`, `ScrapeLog`.
|
|
7
|
+
- **Jobs (6)**: `ApplicationJob`, `FetchFeedJob`, `ScrapeItemJob`, `ScheduleFetchesJob`, `ItemCleanupJob`, `LogCleanupJob`.
|
|
8
|
+
- **Service/Support Modules (34)**: Analytics (`Analytics::SourceFetchIntervalDistribution`, `Analytics::SourceActivityRates`), Dashboard (`Dashboard::Queries`, `Dashboard::TurboBroadcaster`, `Dashboard::UpcomingFetchSchedule`), Fetching (`Fetching::FeedFetcher`, `Fetching::FetchRunner`, `Fetching::FetchError`, `Fetching::RetryPolicy`), HTTP (`HTTP`), Instrumentation (`Instrumentation`, `Events`, `Metrics`), Items (`Items::ItemCreator`, `Items::RetentionPruner`), Jobs support (`Jobs::SolidQueueMetrics`, `Jobs::Visibility`), Realtime (`Realtime`, `Realtime::Adapter`, `Realtime::Broadcaster`), Scheduler (`Scheduler`, `Scraping::Scheduler`), Scraping (`Scraping::Enqueuer`, `Scraping::ItemScraper`, `Scrapers::Base`, `Scrapers::Readability`, `Scrapers::Fetchers::HttpFetcher`, `Scrapers::Parsers::ReadabilityParser`), Security (`Security::Authentication`, `Security::ParameterSanitizer`), Configuration (`Configuration`, `ModelExtensions`, `feedjira` extensions, `version`).
|
|
9
|
+
- **View Components**: none defined under `app/components`.
|
|
10
|
+
- **Front-End Assets**: Stimulus bootstrapper `app/assets/javascripts/source_monitor/application.js`, controllers (`controllers/notification_controller.js`, `controllers/async_submit_controller.js`, `controllers/dropdown_controller.js`), transition shim `dropdown_transition_shim.js`, sprockets manifest `application.js`, Tailwind sources `app/assets/tailwind/application.css`, compiled Tailwind bundle `app/assets/builds/tailwind.css` (~1.4k lines), stylesheet manifest `app/assets/stylesheets/source_monitor/application.css`, Turbo-aware notification partials under `app/views/source_monitor/shared/` (not exhaustively listed).
|
|
11
|
+
|
|
12
|
+
## Controller Review (17.01.02)
|
|
13
|
+
|
|
14
|
+
- `SourcesController#index` mixes search sanitization, analytics aggregation, and bucket math, making the action broader than RESTful list concerns. Extracting the distribution helpers (`extract_fetch_interval_filter`, `distribution_sources_scope`, `find_matching_bucket`) into a presenter or query object would clarify the controller's responsibility (`app/controllers/source_monitor/sources_controller.rb:11-199`).
|
|
15
|
+
- Both `SourcesController` and `ItemsController` duplicate an identical `sanitized_search_params` implementation; moving it into a concern would reduce coupling to parameter structure (`app/controllers/source_monitor/sources_controller.rb:172-194`, `app/controllers/source_monitor/items_controller.rb:120-140`).
|
|
16
|
+
- Manual pagination in `ItemsController#index` reimplements offset/limit logic and page bounds; adopting a pagination helper like Pagy would cut duplication and guard against off-by-one errors (`app/controllers/source_monitor/items_controller.rb:21-29`).
|
|
17
|
+
- Non-RESTful actions (`fetch`/`retry` on Sources, `scrape` on Items) are correctly routed but include complex Turbo Stream responses inline; extracting to responder objects would improve reusability and testability (`app/controllers/source_monitor/sources_controller.rb:65-168`, `app/controllers/source_monitor/items_controller.rb:38-83`).
|
|
18
|
+
- `FetchLogsController` and `ScrapeLogsController` share similar filtering logic but diverge in integer parsing; consider consolidating to a shared concern to keep future filter changes in one place (`app/controllers/source_monitor/fetch_logs_controller.rb:5-23`, `app/controllers/source_monitor/scrape_logs_controller.rb:5-45`).
|
|
19
|
+
|
|
20
|
+
## Model and Service Review (17.01.03)
|
|
21
|
+
|
|
22
|
+
- `SourceMonitor::Source` handles normalization, sanitization, health defaults, and validation in a single model class. The sanitization callbacks (`sanitize_user_inputs`) could move to a concern shared with other models to avoid repeating security rules elsewhere (`app/models/source_monitor/source.rb:47-151`).
|
|
23
|
+
- `SourceMonitor::Item` embeds URL normalization and soft-delete logic. Extracting the normalization routine (`normalize_urls`) to a reusable helper would support other URL-bearing models (`app/models/source_monitor/item.rb:52-133`).
|
|
24
|
+
- `Fetching::FetchRunner` coordinates locking, fetch execution, retention pruning, retry scheduling, and scrape enqueueing. Splitting concurrency/locking into a collaborator would keep the runner focused on orchestration (`lib/source_monitor/fetching/fetch_runner.rb:39-186`).
|
|
25
|
+
- `Scraping::ItemScraper` owns adapter resolution, persistence, logging, and error translation in one class; consider splitting adapter lookup and persistence so new adapters can be added without touching the transaction workflow (`lib/source_monitor/scraping/item_scraper.rb:28-200`).
|
|
26
|
+
- `Items::RetentionPruner` mixes strategy selection with batch execution and counter maintenance; moving strategy-specific behavior into separate classes would make the soft-delete vs destroy paths easier to evolve (`lib/source_monitor/items/retention_pruner.rb:21-162`).
|
|
27
|
+
- `Dashboard::Queries` executes multiple direct ActiveRecord calls every page load; caching or background precomputation for expensive counts (e.g., `FetchLog` and `ScrapeLog` limits) could improve dashboard responsiveness (`lib/source_monitor/dashboard/queries.rb:9-87`).
|
|
28
|
+
|
|
29
|
+
## Jobs, Workers, and Scheduling (17.01.04)
|
|
30
|
+
|
|
31
|
+
- `FetchFeedJob` re-raises all errors except concurrency conflicts, delegating retries to ActiveJob defaults. Considering explicit retry/backoff for transient failures would align with Solid Queue best practices (`app/jobs/source_monitor/fetch_feed_job.rb:5-19`).
|
|
32
|
+
- `ScrapeItemJob` performs direct `update_columns` writes inside locks; wrapping these state changes in small service objects (or reusing `Scraping::Enqueuer` helpers) would reduce raw SQL usage and prevent silent attribute drift (`app/jobs/source_monitor/scrape_item_job.rb:21-56`).
|
|
33
|
+
- Cleanup jobs (`ItemCleanupJob`, `LogCleanupJob`) repeat option normalization logic; a shared base utility could centralize casting rules for command-line invocations (`app/jobs/source_monitor/item_cleanup_job.rb:12-74`, `app/jobs/source_monitor/log_cleanup_job.rb:14-78`).
|
|
34
|
+
- `Scheduler.run` only enqueues fetches; adding observability (e.g., instrumentation events around locked IDs) would help diagnose scheduling gaps (`lib/source_monitor/scheduler.rb:7-55`).
|
|
35
|
+
- Recurring schedule references in `config/recurring.yml` rely on Solid Queue CLI defaults; documenting these in `SourceMonitor.configure` and ensuring Mission Control checks respect them would reduce drift across host apps.
|
|
36
|
+
|
|
37
|
+
## Front-End Asset Review (17.01.05)
|
|
38
|
+
|
|
39
|
+
- Stimulus controllers register themselves on the global `window` object and expect external UMD bundles (`app/assets/javascripts/source_monitor/application.js:1-32`, `app/assets/javascripts/source_monitor/controllers/notification_controller.js:1-55`). Migrating to Importmap or ESbuild modules would eliminate global leakage and simplify testing.
|
|
40
|
+
- `dropdown_controller.js` assumes a global `StimulusDropdown` shim; when the dependency is absent, dropdowns silently fail. Guarding this with feature detection and graceful degradation (e.g., fallback to CSS-only menus) would improve resilience (`app/assets/javascripts/source_monitor/controllers/dropdown_controller.js:1-14`).
|
|
41
|
+
- The transition shim provides minimal show/hide toggling and bypasses Tailwind’s transition helpers; aligning it with the stimulus-use-transition API would avoid divergence from upstream updates (`app/assets/javascripts/source_monitor/dropdown_transition_shim.js:1-25`).
|
|
42
|
+
- A compiled Tailwind bundle (`app/assets/builds/tailwind.css`) lives in source control; documenting the build step and ensuring the bundle is regenerated during releases will prevent stale utility classes.
|
|
43
|
+
- No dedicated asset linting or bundler checks run in CI; adding `yarn lint` or equivalent (once build tooling is selected) would catch drift early.
|
|
44
|
+
|
|
45
|
+
## Metrics & Hotspots (17.01.06)
|
|
46
|
+
|
|
47
|
+
- `bundle exec rubocop --format offenses` reports **366 offenses across 44 files**, overwhelmingly `Layout/SpaceInsideArrayLiteralBrackets` (352 autocorrectable). Cleaning whitespace rules and enabling CI enforcement will quickly improve signal-to-noise.
|
|
48
|
+
- `bundle exec brakeman` could not run because the gem is not in the bundle; adding it to the development group will enable baseline security scanning.
|
|
49
|
+
- SimpleCov is not configured (`test/test_helper.rb` lacks coverage hooks), so historical coverage trends are unavailable; enabling coverage reports will support future regressions analysis.
|
|
50
|
+
- Hotspot summary:
|
|
51
|
+
- Controllers: duplicated sanitization helper and complex inline Turbo responses (`SourcesController#index`, `ItemsController#scrape`).
|
|
52
|
+
- Services: `Fetching::FetchRunner` and `Scraping::ItemScraper` concentrate orchestration, logging, and persistence logic in single classes, making them prime candidates for refactoring.
|
|
53
|
+
- Jobs: Cleanup jobs’ duplicated option parsing invites inconsistencies when adding CLI flags.
|
|
54
|
+
- Front-end: reliance on global Stimulus objects without build-time enforcement increases risk of runtime errors when assets load out of order.
|
|
55
|
+
|
|
56
|
+
## Recommended Next Steps
|
|
57
|
+
|
|
58
|
+
1. Extract shared request-sanitization and pagination helpers to reduce duplication in controllers.
|
|
59
|
+
2. Break down `FetchRunner` and `ItemScraper` into smaller collaborators (e.g., concurrency guard, adapter resolver, persistence handler) to improve testability.
|
|
60
|
+
3. Add Rubocop autocorrect (spacing rules) and wire Rubocop/Brakeman into CI to maintain quality gates.
|
|
61
|
+
4. Decide on an asset pipeline (Importmap vs ESBuild) and refactor Stimulus controllers out of the global namespace, documenting required UMD shims until migration completes.
|
|
62
|
+
5. Enable SimpleCov and capture baseline coverage for future phases, ensuring retention and scraping flows stay covered.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Phase 17.02 Complexity Findings Report (2025-10-12)
|
|
2
|
+
|
|
3
|
+
## 1. Issue Catalog
|
|
4
|
+
|
|
5
|
+
### Controllers
|
|
6
|
+
- **Duplicated parameter sanitization** — `app/controllers/source_monitor/items_controller.rb:120` and `app/controllers/source_monitor/sources_controller.rb:172` both implement identical `sanitized_search_params`; keeping these in each controller risks divergence in future filters.
|
|
7
|
+
- **Manual pagination and query orchestration** — `app/controllers/source_monitor/items_controller.rb:13-33` mixes pagination math, search setup, and view state assembly, crowding the action and duplicating paging behaviour elsewhere.
|
|
8
|
+
- **Inline analytics aggregation** — `app/controllers/source_monitor/sources_controller.rb:11-27` performs distribution bucketing and analytics queries in-controller, making the action heavy and difficult to unit test.
|
|
9
|
+
- **Turbo Stream composition embedded in controllers** — `app/controllers/source_monitor/items_controller.rb:38-83` and `app/controllers/source_monitor/sources_controller.rb:140-168` contain sizeable arrays of Turbo Stream operations; changes to toast behaviour or DOM IDs now require controller edits.
|
|
10
|
+
- **Parallel filter logic in logs controllers** — `app/controllers/source_monitor/fetch_logs_controller.rb:5-23` and `app/controllers/source_monitor/scrape_logs_controller.rb:5-45` share similar scoping patterns but diverge in parameter casting, inviting subtle inconsistencies.
|
|
11
|
+
|
|
12
|
+
### Models & Services
|
|
13
|
+
- **`SourceMonitor::Source` doing too much** — `app/models/source_monitor/source.rb:20-150` handles sanitization, URL normalization, defaulting, and health thresholds; the breadth complicates reuse of sanitization rules by other models.
|
|
14
|
+
- **`SourceMonitor::Item` URL normalization** — `app/models/source_monitor/item.rb:52-133` embeds URL parsing logic; other URL-backed models cannot reuse the behaviour without duplication.
|
|
15
|
+
- **`Fetching::FetchRunner` orchestration sprawl** — `lib/source_monitor/fetching/fetch_runner.rb:39-186` owns locking, state transitions, retention pruning, scrape enqueueing, retry scheduling, and instrumentation dispatch in one class.
|
|
16
|
+
- **`Scraping::ItemScraper` responsibilities** — `lib/source_monitor/scraping/item_scraper.rb:28-200` combines adapter discovery, HTTP metadata handling, persistence, logging, and event publication within a single method flow.
|
|
17
|
+
- **`Items::RetentionPruner` strategy branching** — `lib/source_monitor/items/retention_pruner.rb:21-162` interleaves strategy selection with batch execution and counter maintenance, making soft-delete vs destroy paths harder to evolve.
|
|
18
|
+
- **Dashboard query coupling** — `lib/source_monitor/dashboard/queries.rb:17-87` fires multiple fresh queries for every request without caching or batching, and mixes routing helpers with aggregation logic.
|
|
19
|
+
|
|
20
|
+
### Jobs, Workers, Scheduling
|
|
21
|
+
- **Limited retry semantics** — `app/jobs/source_monitor/fetch_feed_job.rb:5-19` rescues only concurrency errors; transient network failures rely on defaults rather than an explicit retry/backoff plan.
|
|
22
|
+
- **State updates inside jobs** — `app/jobs/source_monitor/scrape_item_job.rb:25-45` issues `update_columns` inside locks; behaviour overlaps with `Scraping::Enqueuer`, risking drift between enqueue-time and job-time state handling.
|
|
23
|
+
- **Duplicated option normalization** — `app/jobs/source_monitor/item_cleanup_job.rb:18-69` and `app/jobs/source_monitor/log_cleanup_job.rb:18-71` repeat the same option-shaping logic, increasing maintenance costs as CLI flags expand.
|
|
24
|
+
- **Scheduler blind spots** — `lib/source_monitor/scheduler.rb:7-49` enqueues fetches but emits no instrumentation or metrics, limiting observability when schedule gaps occur.
|
|
25
|
+
|
|
26
|
+
### Front-End Assets
|
|
27
|
+
- **Global Stimulus registration** — `app/assets/javascripts/source_monitor/application.js:1-32` depends on global `window.Stimulus`, diverging from modern ES module patterns and risking double registration.
|
|
28
|
+
- **Fragile dropdown dependency** — `app/assets/javascripts/source_monitor/controllers/dropdown_controller.js:1-14` assumes `window.StimulusDropdown` exists; missing UMD bundle breaks dropdowns without fallback.
|
|
29
|
+
- **Transition shim divergence** — `app/assets/javascripts/source_monitor/dropdown_transition_shim.js:1-24` implements a custom `useTransition` that may fall behind the upstream library API.
|
|
30
|
+
- **Compiled Tailwind artefact** — `app/assets/builds/tailwind.css` (~1.4k lines) is committed without guardrails ensuring regeneration during releases.
|
|
31
|
+
- **No asset linting/build verification** — no scripted checks ensure controllers or Tailwind bundles stay current, leaving runtime errors undetected until manual testing.
|
|
32
|
+
|
|
33
|
+
### Tooling Gaps
|
|
34
|
+
- **Rubocop violations** — `bundle exec rubocop --format offenses` reports 366 issues (352 auto-correctable spacing offenses), indicating style drift.
|
|
35
|
+
- **Brakeman absent from bundle** — Running `bundle exec brakeman` fails because the gem is not declared; security checks are currently blocked.
|
|
36
|
+
- **No coverage tracking** — `test/test_helper.rb` lacks SimpleCov hooks, so coverage regressions are invisible.
|
|
37
|
+
|
|
38
|
+
## 2. Recommended Remediation (Controllers, Models/Services, Jobs)
|
|
39
|
+
- **Controllers**: Extract shared parameter sanitization and pagination into concerns or service objects; move analytics aggregation into query objects; introduce presenters/responders for Turbo Stream responses.
|
|
40
|
+
- **Models/Services**: Create reusable sanitization utilities (e.g., `SourceMonitor::Sanitization`), encapsulate URL normalization in a dedicated module, break `FetchRunner` into concurrency, state, and follow-up collaborators, and separate `ItemScraper` responsibilities into adapter resolver, persistence handler, and logger.
|
|
41
|
+
- **Jobs**: Align enqueue/job state transitions by reusing shared helpers, define custom retry strategies for transient errors, centralize option parsing for cleanup jobs, and instrument scheduler runs with metrics events.
|
|
42
|
+
|
|
43
|
+
## 3. DRY & Complexity Reduction Strategies
|
|
44
|
+
1. **Shared sanitization & normalization modules** — Provide mixins for parameter and URL sanitation to reuse across controllers and models.
|
|
45
|
+
2. **Pagination abstraction** — Introduce a pagination service or adopt Pagy/Kaminari to eliminate manual offset math across controllers.
|
|
46
|
+
3. **Command objects for Turbo responses** — Wrap Turbo Stream payload assembly in dedicated builders for reuse between controllers and tests.
|
|
47
|
+
4. **Service decomposition** — Apply single-responsibility classes (lock manager, retention handler, scrape enqueuer) to reduce the size and branching of core orchestration services.
|
|
48
|
+
5. **Instrumentation helpers** — Centralize logging/instrumentation patterns to avoid duplicating JSON payload composition across jobs and services.
|
|
49
|
+
|
|
50
|
+
## 4. Front-End Package & View-Layer Recommendations
|
|
51
|
+
- Migrate Stimulus controllers to modules (ESBuild or Importmap) while keeping a compatibility shim until host apps adopt the bundle.
|
|
52
|
+
- Provide defensive checks or progressive enhancement fallbacks when optional UMD dependencies (e.g., `StimulusDropdown`) are missing.
|
|
53
|
+
- Replace the custom transition shim with the maintained `@hotwired/stimulus-use` package or document divergence to keep behaviour in sync.
|
|
54
|
+
- Add a lightweight asset build verification step (e.g., `bin/rails test:assets` or `yarn lint`) and document Tailwind rebuild requirements in contributor guides.
|
|
55
|
+
- Audit Turbo Stream partials to ensure DOM IDs referenced in controllers are declared in a single presenter to avoid coupling across views.
|
|
56
|
+
|
|
57
|
+
## 5. Prioritization & Sequencing
|
|
58
|
+
| Issue | Area | Effort | Impact | Priority |
|
|
59
|
+
| --- | --- | --- | --- | --- |
|
|
60
|
+
| Decompose `FetchRunner` responsibilities | Fetching services | Medium | High | P1 |
|
|
61
|
+
| Shared controller sanitization/pagination helpers | Controllers | Low | High | P1 |
|
|
62
|
+
| Align Scraping enqueue vs job state handling | Jobs/Services | Medium | High | P1 |
|
|
63
|
+
| Formalize Stimulus asset pipeline | Front-end | Medium | Medium | P2 |
|
|
64
|
+
| Consolidate cleanup job option parsing | Background jobs | Low | Medium | P2 |
|
|
65
|
+
| Introduce Rubocop/Brakeman & coverage baselines | Tooling | Low | Medium | P2 |
|
|
66
|
+
| Replace `ItemScraper` adapter/persistence monolith | Scraping services | High | High | P1 |
|
|
67
|
+
| Dashboard query caching | Dashboard | Medium | Medium | P3 |
|
|
68
|
+
| Tailwind rebuild automation | Front-end | Low | Medium | P2 |
|
|
69
|
+
|
|
70
|
+
P1 items should seed Phase 17.03 workstreams; P2 items follow once shared utilities exist; P3 items can ride along with subsequent dashboard improvements.
|
|
71
|
+
|
|
72
|
+
## 6. Deliverables
|
|
73
|
+
- Saved this findings report at `.ai/phase_17_02_complexity_findings_2025-10-12.md`.
|
|
74
|
+
- Source material cross-referenced with Rails controller best practices from the official guides.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Phase 17.03 Refactoring Execution Plan (2025-10-12)
|
|
2
|
+
|
|
3
|
+
## Workstreams & Owners (17.03.01)
|
|
4
|
+
- **Controllers & Views** — Owner: D. Demchuk — deliver sanitized search helper, shared pagination, and Turbo presenter layer.
|
|
5
|
+
- **Core Services** — Owner: J. Patel — modularize FetchRunner, ItemScraper, and RetentionPruner responsibilities.
|
|
6
|
+
- **Jobs & Scheduling** — Owner: L. Nguyen — standardize scraping job state transitions, cleanup job option parsing, and scheduler instrumentation/retry policies.
|
|
7
|
+
- **Front-End Pipeline** — Owner: M. Garcia — modernize Stimulus packaging, dropdown fallback, transition shim, and Tailwind rebuild automation.
|
|
8
|
+
- **Tooling & Metrics** — Owner: QA Guild — introduce Rubocop/Brakeman gating, SimpleCov baseline, and asset/test automation.
|
|
9
|
+
|
|
10
|
+
## Success Criteria & Coverage (17.03.02)
|
|
11
|
+
- **Controllers & Views**: Shared sanitization module reused by sources/items/log controllers; pagination helper with unit tests; Turbo presenter unit specs plus system regression for toast delivery.
|
|
12
|
+
- **Core Services**: FetchRunner split into lock manager + orchestration service with unit coverage and integration test updates; ItemScraper extraction with adapter contract tests; RetentionPruner strategies isolated with unit coverage for destroy/soft delete paths.
|
|
13
|
+
- **Jobs & Scheduling**: ScrapeItemJob leverages shared state helper with job tests; cleanup jobs share option parser module with unit tests; scheduler instrumentation emits event verified by integration test.
|
|
14
|
+
- **Front-End Pipeline**: Stimulus controllers load via import map/build pipeline with smoke test in system suite; dropdown fallback verified with JS integration; Tailwind rebuild command documented and executed in CI.
|
|
15
|
+
- **Tooling & Metrics**: Rubocop enforced in CI (passing run recorded); Brakeman baseline added; SimpleCov coverage target ≥90% for new code; asset lint/test tasks integrated.
|
|
16
|
+
|
|
17
|
+
## Sequencing & Dependencies (17.03.03)
|
|
18
|
+
1. **Tooling & Metrics** (enabling guardrails before refactors).
|
|
19
|
+
2. **Controllers & Views** (shared helpers reduce duplication ahead of service changes).
|
|
20
|
+
3. **Core Services** (builds on shared helpers for sanitization messaging).
|
|
21
|
+
4. **Jobs & Scheduling** (depends on service decomposition for scrape enqueue alignment).
|
|
22
|
+
5. **Front-End Pipeline** (requires controller presenter outputs stabilized).
|
|
23
|
+
|
|
24
|
+
## Monitoring & QA Checkpoints (17.03.04)
|
|
25
|
+
- Add instrumentation dashboards for scheduler events before deploying job changes.
|
|
26
|
+
- Run full system test suite after controller/view and front-end updates.
|
|
27
|
+
- Enable CI stages: `lint`, `security`, `test`, `assets` with gating.
|
|
28
|
+
- Schedule post-deploy log review for first run of refactored FetchRunner and ItemScraper.
|
|
29
|
+
|
|
30
|
+
## Follow-Up Tickets (17.03.05)
|
|
31
|
+
- Log backlog items for dashboard caching refinements and Mission Control integration once core refactors land.
|
|
32
|
+
- Document migration guidance for host apps adopting new helpers/presenters (README + upgrade notes).
|
|
33
|
+
- Track potential Pagy adoption experiment if manual pagination proves insufficient.
|
|
34
|
+
|
|
35
|
+
## Deliverables
|
|
36
|
+
- Execution plan stored here and referenced from roadmap tasks.
|
|
37
|
+
- Serves as baseline for future 17.x phases.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Phase 21.01 – Log Consolidation Notes (2025-10-15)
|
|
2
|
+
|
|
3
|
+
## Existing UI Inventory
|
|
4
|
+
|
|
5
|
+
- `app/views/source_monitor/fetch_logs/index.html.erb`
|
|
6
|
+
- Columns: Started timestamp, Source link, HTTP status, Result badge, item delta summary, view link.
|
|
7
|
+
- Page-level filter: status (`All`, `Successes`, `Failures`).
|
|
8
|
+
- Metadata surfaced via badges and counts; table limited to 50 rows.
|
|
9
|
+
- `app/views/source_monitor/fetch_logs/show.html.erb`
|
|
10
|
+
- Summary stack with source, HTTP, success flag, duration, item counts, timestamps, job id.
|
|
11
|
+
- Secondary panels for error details (message, backtrace), HTTP headers (pretty JSON), metadata.
|
|
12
|
+
- `app/views/source_monitor/scrape_logs/index.html.erb`
|
|
13
|
+
- Columns: Started timestamp, Item link, Source link, Result badge, duration, view link.
|
|
14
|
+
- Same status filter bar; duration replaces item delta metrics.
|
|
15
|
+
- `app/views/source_monitor/scrape_logs/show.html.erb`
|
|
16
|
+
- Summary with item/source links, adapter, HTTP, success flag, duration, content length, timestamps.
|
|
17
|
+
- Panels for error details and metadata.
|
|
18
|
+
- Navigation (`app/views/layouts/source_monitor/application.html.erb`) now surfaces a single **Logs** entry pointing at the consolidated index.
|
|
19
|
+
|
|
20
|
+
## Combined Layout Outline
|
|
21
|
+
|
|
22
|
+
- Single "Logs" index with shared table:
|
|
23
|
+
- Columns: Started, Type badge, Subject (item or source), Source link, HTTP/adapter summary, Result badge with inline error text, Metrics (fetch deltas or scrape duration), Detail link.
|
|
24
|
+
- Rows expose `data-log-row` identifiers (`fetch-<id>` / `scrape-<id>`) for tests and Turbo.
|
|
25
|
+
- Unified filter bar:
|
|
26
|
+
- Status toggle (All/Successes/Failures) and Log Type toggle (All Logs/Fetch Logs/Scrape Logs).
|
|
27
|
+
- Search form includes free-text box, timeframe select (24h/7d/30d), explicit start range inputs, and numeric `source_id`/`item_id` filters.
|
|
28
|
+
- Pagination uses `SourceMonitor::Pagination::Paginator` with Prev/Next links and a page indicator span.
|
|
29
|
+
- Detail views remain type-specific (fetch/scrape) but back links now route through `source_monitor.logs_path` with the appropriate `log_type` preset.
|
|
30
|
+
- Controller composes `SourceMonitor::Logs::Query` + `TablePresenter`; no shared concern is needed.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# SourceMonitor Release Checklist
|
|
2
|
+
|
|
3
|
+
## Automated Gates
|
|
4
|
+
- [ ] Confirm the `release_verification` GitHub Actions job succeeded on the target commit (generator smoke test, gem build/unpack, diff coverage).
|
|
5
|
+
- [ ] Ensure the standard CI jobs (`lint`, `security`, `test`) are green.
|
|
6
|
+
|
|
7
|
+
## Manual Validation
|
|
8
|
+
- [ ] Run `bin/release VERSION` locally and inspect the console output for any skipped steps or warnings.
|
|
9
|
+
- [ ] Inspect `pkg/source_monitor-<VERSION>.gem`:
|
|
10
|
+
- [ ] Verify file size is within expected bounds compared to the previous release.
|
|
11
|
+
- [ ] Extract the gem (`gem unpack`) and confirm `lib/` and templates are present.
|
|
12
|
+
- [ ] Review `CHANGELOG.md` entry for the release and ensure the annotated tag message matches.
|
|
13
|
+
- [ ] Preview the README locally with a Markdown renderer to confirm formatting.
|
|
14
|
+
- [ ] Confirm `config/coverage_baseline.json` is up to date (rerun `bin/test-coverage` + `bin/update-coverage-baseline` if functional coverage changed).
|
|
15
|
+
- [ ] Smoke test the disposable host app harness:
|
|
16
|
+
- [ ] `bin/rails test test/integration/release_packaging_test.rb`
|
|
17
|
+
- [ ] `bin/rails runner 'SourceMonitor::FetchFeedJob'` in the dummy app if new background job behavior shipped.
|
|
18
|
+
- [ ] Verify any new environment variables or configuration flags are documented in `docs/installation.md` or `docs/deployment.md`.
|
|
19
|
+
|
|
20
|
+
## Publication
|
|
21
|
+
- [ ] Tag the release (`git tag v<VERSION> -F <TAG_FILE>`) and push the tag.
|
|
22
|
+
- [ ] Publish the gem (`gem push pkg/source_monitor-<VERSION>.gem`).
|
|
23
|
+
- [ ] Announce the release (Slack/email) with highlights and migration notes.
|