source_monitor 0.13.1 → 0.14.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 +12 -0
- data/CHANGELOG.md +15 -0
- data/Gemfile.lock +1 -1
- 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 +3 -3
- data/docs/upgrade.md +27 -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 +7 -2
- metadata +8 -65
- 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
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Engine Hardening: auth defaults, notification scoping, gem packaging
|
|
2
|
+
|
|
3
|
+
## Objective
|
|
4
|
+
|
|
5
|
+
Implement the three specced engine-hardening issues end-to-end and verified:
|
|
6
|
+
|
|
7
|
+
- #129 — Fail-closed engine access when no auth handler is configured (+ explicit `open_access`/demo opt-in)
|
|
8
|
+
- #130 — Scope notification toasts to prevent cross-user leakage (request-flash path AND background-broadcast path)
|
|
9
|
+
- #131 — Exclude `.claude` internals from the packaged gem, keep `sm-*` skills
|
|
10
|
+
|
|
11
|
+
Each issue's acceptance-criteria checkboxes must be satisfied and the repo gates must be green.
|
|
12
|
+
|
|
13
|
+
## Original Request
|
|
14
|
+
|
|
15
|
+
"these 3 issues" — referring to #129, #130, #131, which were just broken out of umbrella #128 (PRD #127) and given codebase-validated Technical Specs via `issues-to-specs`.
|
|
16
|
+
|
|
17
|
+
## Intake Summary
|
|
18
|
+
|
|
19
|
+
- Input shape: `existing_plan` — each issue already has a `## Technical Spec` with behavior slices, tracer bullet, posture, and acceptance-criteria mapping.
|
|
20
|
+
- Audience: SourceMonitor engine maintainer (dchuk) + host apps consuming the `source_monitor` gem.
|
|
21
|
+
- Authority: `requested` — user wants the work implemented.
|
|
22
|
+
- Proof type: `test`
|
|
23
|
+
- Completion proof: every acceptance-criteria checkbox on #129/#130/#131 checked, and all four gates green.
|
|
24
|
+
- Goal oracle: the four repo gates plus the new behavior-slice tests added per issue, mapped back to each issue's acceptance criteria.
|
|
25
|
+
- Likely misfire: (1) declaring done after planning/discovery only; (2) shipping #129 fail-closed without the `open_access` opt-in + dummy-app update landing together, breaking the dummy app and existing controller tests; (3) entangling all three fixes in one commit instead of one coherent commit per issue; (4) "fixing" #130 by deleting toasts rather than scoping/decontextualizing them.
|
|
26
|
+
- Blind spots considered: #129 is an intentional breaking change needing a CHANGELOG/upgrade note; #130 has TWO global-stream leak paths (`ApplicationController#broadcast_flash_toasts` for request flashes AND `Broadcaster#broadcast_toast` for background events) — both must be addressed; the three issues are independent (no blocker ordering) but each is its own vertical slice + commit; CI diff-coverage gate requires tests for every new rescue/fallback branch.
|
|
27
|
+
- Existing plan facts: the three GitHub issues and their Technical Specs are the authoritative plan. Preserve them; validate against current HEAD before writing.
|
|
28
|
+
|
|
29
|
+
## Goal Oracle
|
|
30
|
+
|
|
31
|
+
The oracle for this goal is:
|
|
32
|
+
|
|
33
|
+
`rbenv exec bin/rails test` (incl. new per-issue behavior tests) + `rbenv exec bin/rubocop` + `rbenv exec bin/brakeman --no-pager` + `cd test/dummy && rbenv exec bundle exec rails zeitwerk:check`, ALL green, with each acceptance-criteria checkbox on #129/#130/#131 satisfied by a named test or evidence.
|
|
34
|
+
|
|
35
|
+
The PM must keep comparing task receipts to this oracle. A passing single issue is not full completion — all three issues must be implemented, verified, and their checkboxes mapped to evidence before `full_outcome_complete: true`.
|
|
36
|
+
|
|
37
|
+
## Goal Kind
|
|
38
|
+
|
|
39
|
+
`existing_plan`
|
|
40
|
+
|
|
41
|
+
## Current Tranche
|
|
42
|
+
|
|
43
|
+
Continuous execution. Validate the three specs against HEAD, then implement one coherent reversible Worker package per issue (suggested order: #131 packaging → #130 notifications → #129 auth, lowest-to-highest blast radius), verifying each package against the gates before advancing. Review only at the final completion boundary unless a package's verification is rejected or its behavior turns out ambiguous. Finish only when all three issues are implemented, verified, and audited complete.
|
|
44
|
+
|
|
45
|
+
## Non-Negotiable Constraints
|
|
46
|
+
|
|
47
|
+
- Follow project conventions in `CLAUDE.md`: Minitest (no RSpec/FactoryBot), `create_source!`/`with_inline_jobs` helpers, `SourceMonitor.reset_configuration!` per test, Tailwind/Hotwire, RuboCop omakase, Brakeman clean.
|
|
48
|
+
- One coherent commit per issue (auto_commit is on); do not entangle the three fixes.
|
|
49
|
+
- #129 fail-closed enforcement and its `open_access`/demo opt-in + dummy-app update + CHANGELOG note must land together in the same package.
|
|
50
|
+
- #130 must address BOTH leak paths; request flashes stay response-local, background operational toasts are scoped or stripped of per-user/request context. Do not delete the toast feature.
|
|
51
|
+
- #131 must only touch gemspec file-selection + one packaging test; do not change gem metadata or dependencies.
|
|
52
|
+
- No new runtime dependency. No schema changes. Preserve existing public config/handler API shape.
|
|
53
|
+
- Run the full local CI equivalent (all four gates) before considering any package done; write tests for every new rescue/fallback branch (CI diff-coverage gate).
|
|
54
|
+
- Never read/output protected files (`config/master.key`, `.env*`, credentials, `*.key`/`*.pem`).
|
|
55
|
+
|
|
56
|
+
## Stop Rule
|
|
57
|
+
|
|
58
|
+
Stop only when a final audit proves all three issues are implemented, verified, and their acceptance criteria are satisfied.
|
|
59
|
+
|
|
60
|
+
Do not stop after plan validation or after a single issue's package passes — advance to the next issue's package.
|
|
61
|
+
|
|
62
|
+
Mark a specific package blocked (with a receipt) only if it genuinely needs owner input (e.g. confirming the `open_access` opt-in name, or whether background toasts should exist at all); keep doing the other safe local packages meanwhile.
|
|
63
|
+
|
|
64
|
+
## Slice Sizing
|
|
65
|
+
|
|
66
|
+
One Worker package per issue is the natural vertical slice — each delivers a verified, independently-committable outcome mapped to a real issue. #131 is small but legitimately isolated (gemspec + one test); it is not a "tiny task" anti-pattern. Each Worker completes its whole issue (code + tests + docs/CHANGELOG where the spec requires) before handing back.
|
|
67
|
+
|
|
68
|
+
## Canonical Board
|
|
69
|
+
|
|
70
|
+
Machine truth lives at:
|
|
71
|
+
|
|
72
|
+
`docs/goals/engine-hardening/state.yaml`
|
|
73
|
+
|
|
74
|
+
If this charter and `state.yaml` disagree, `state.yaml` wins.
|
|
75
|
+
|
|
76
|
+
## Run Command
|
|
77
|
+
|
|
78
|
+
```text
|
|
79
|
+
/goal Follow docs/goals/engine-hardening/goal.md.
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## PM Loop
|
|
83
|
+
|
|
84
|
+
On every `/goal` continuation:
|
|
85
|
+
|
|
86
|
+
1. Read this charter.
|
|
87
|
+
2. Read `state.yaml`.
|
|
88
|
+
3. Run the bundled GoalBuddy update checker when available; mention a newer version without blocking.
|
|
89
|
+
4. Re-check intake: original request, existing-plan facts, authority, proof, blind spots, likely misfire.
|
|
90
|
+
5. Work only on the active board task.
|
|
91
|
+
6. Assign Scout, Judge, Worker, or PM according to the task.
|
|
92
|
+
7. Write a compact task receipt.
|
|
93
|
+
8. Update the board.
|
|
94
|
+
9. If safe local work remains, activate the next Worker package and continue.
|
|
95
|
+
10. Record any operator-escalation issue/PR decision in a receipt; `state.yaml` stays authoritative.
|
|
96
|
+
11. Review only at final completion (or rejected verification / ambiguity), not after every Worker.
|
|
97
|
+
12. Finish only with a Judge/PM audit receipt that maps all three issues' criteria + the four gates back to the original outcome and records `full_outcome_complete: true`.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# T001 — Judge spec validation (full receipt)
|
|
2
|
+
|
|
3
|
+
Decision: **approved_with_notes** · go for T002 · full_outcome_complete: false · sequence #131 -> #130 -> #129 OK · parallel NOT allowed (T003 & T004 share application_controller.rb; safe only serially).
|
|
4
|
+
|
|
5
|
+
## Per-issue
|
|
6
|
+
|
|
7
|
+
### #131 (packaging) — spec_valid: true, no material drift
|
|
8
|
+
- `source_monitor.gemspec:25-31` reject list omits `.claude/`; line 30 `Dir[".claude/skills/sm-*/**/*"]` re-adds 39 sm-* files.
|
|
9
|
+
- Currently **64 non-sm `.claude` files ship** (agents/commands/hooks/agent-memory + ~17 non-sm skills).
|
|
10
|
+
- Adding `.claude/` to reject list is correct + sufficient.
|
|
11
|
+
- `test/integration/release_packaging_test.rb:65` already runs `gem build source_monitor.gemspec`, proving gemspec loads from repo root → new `test/gemspec_packaging_test.rb` is safe.
|
|
12
|
+
|
|
13
|
+
### #130 (notifications) — spec_valid: true, with drift
|
|
14
|
+
- **DRIFT:** spec said "no broadcaster test file"; `test/lib/source_monitor/realtime/broadcaster_test.rb` ALREADY EXISTS (19KB), already stubs `Turbo::StreamsChannel.broadcast_append_to` and tests `broadcast_toast` (lines 151-189). Worker **extends**, not creates.
|
|
15
|
+
- Both leak paths confirmed: (a) `application_controller.rb:63-79` `broadcast_flash_toasts` after_action → `Realtime.broadcast_toast` per flash; (b) `broadcaster.rb:66-85` `broadcast_toast` → `NOTIFICATION_STREAM = "source_monitor_notifications"`.
|
|
16
|
+
- Layout subscribes globally: `application.html.erb:15`. `StreamResponder#toast` (`stream_responder.rb:43-54`) is response-local (good).
|
|
17
|
+
- **Two extra background `broadcast_toast` call sites the spec missed:** `lib/source_monitor/health/source_health_check_orchestrator.rb:57` and `lib/source_monitor/fetching/feed_fetcher/source_updater.rb:196` (auto-pause). Both source-operational → fit the "keep global, context-free" default; Worker should be aware.
|
|
18
|
+
|
|
19
|
+
### #129 (auth) — spec_valid: true, with critical drift
|
|
20
|
+
- **DRIFT (critical):** dummy-initializer-only opt-in does NOT keep suite green. `test/test_helper.rb:85-89` calls `SourceMonitor.reset_configuration!` in EVERY test setup (rebuilds `Configuration.new` at `lib/source_monitor.rb:236`), wiping the dummy initializer's `open_access` per-test. `test/test_helper.rb` was NOT in #129's named files.
|
|
21
|
+
- Silent no-op confirmed: `authentication.rb:42-46` `call_handler` returns early when handler nil. before_actions `application_controller.rb:7-8`. `authentication_settings.rb:39-44` `reset!` present (add `open_access` reset there).
|
|
22
|
+
- ~24 of 26 controller/integration test files hit real engine routes with NO handler and expect 200 → all 403 under fail-closed unless the shared harness opts in.
|
|
23
|
+
- Must flip `test/controllers/source_monitor/application_controller_test.rb:28-31` ("skips authentication when host has not configured it" asserts `:success`) → assert `:forbidden`.
|
|
24
|
+
|
|
25
|
+
Tests relying on implicit open access (must be handled via test_helper open_access enablement):
|
|
26
|
+
sources, dashboard, items, source_fetches, health, bulk_scrape_enablements, fetch_logs, item_scrapes, logs, scrape_logs, source_bulk_scrapes, source_favicon_fetches, source_health_checks, source_health_resets, source_retries, source_scrape_tests, sources_favicon, sources_sort controllers; integration: engine_mounting, navigation, favicon_integration.
|
|
27
|
+
|
|
28
|
+
## Required board updates (PM applied)
|
|
29
|
+
1. T004 allowed_files += `test/test_helper.rb` (enable open_access in per-test setup). **Most important** — without it the suite can't pass.
|
|
30
|
+
2. T004 must flip `application_controller_test.rb:28-31` → `:forbidden`.
|
|
31
|
+
3. T003: extend existing `broadcaster_test.rb`; aware of the two extra background toast sites.
|
|
32
|
+
4. T003 & T004 serial-only (shared application_controller.rb).
|
|
33
|
+
|
|
34
|
+
## Owner questions (all non-blocking, workable defaults)
|
|
35
|
+
- #130: keep background toasts global+context-free (default) vs remove entirely.
|
|
36
|
+
- #129: opt-in name `open_access` (default, adjustable).
|
|
37
|
+
- #129: enable open_access in test_helper (default, lowest churn) vs per-file configure_authentication.
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
# GoalBuddy v2 state.yaml
|
|
2
|
+
# Board truth lives here. goal.md is the editable charter; notes/ holds long receipts only.
|
|
3
|
+
|
|
4
|
+
version: 2
|
|
5
|
+
|
|
6
|
+
goal:
|
|
7
|
+
title: "Engine Hardening: auth defaults, notification scoping, gem packaging"
|
|
8
|
+
slug: "engine-hardening"
|
|
9
|
+
kind: existing_plan # specific | open_ended | existing_plan | recovery | audit
|
|
10
|
+
tranche: "Validate 3 specs against HEAD, then implement one verified Worker package per issue (#131 -> #130 -> #129), then audit all three complete."
|
|
11
|
+
status: done # active | blocked | done
|
|
12
|
+
oracle:
|
|
13
|
+
signal: "rbenv exec bin/rails test + rbenv exec bin/rubocop + rbenv exec bin/brakeman --no-pager + (cd test/dummy && rbenv exec bundle exec rails zeitwerk:check) ALL green, with every acceptance-criteria checkbox on #129/#130/#131 mapped to a named test or evidence."
|
|
14
|
+
cadence: "after each Worker package and at final audit"
|
|
15
|
+
final_proof: "Three done Worker receipts (one per issue) with green gate commands, plus a Judge/PM audit mapping each issue's criteria to evidence."
|
|
16
|
+
intake:
|
|
17
|
+
original_request: "these 3 issues (#129, #130, #131)"
|
|
18
|
+
interpreted_outcome: "Implement and verify all three engine-hardening issues; satisfy each issue's acceptance criteria with the repo gates green."
|
|
19
|
+
input_shape: existing_plan
|
|
20
|
+
audience: "SourceMonitor engine maintainer + host apps consuming the gem"
|
|
21
|
+
authority: requested
|
|
22
|
+
proof_type: test
|
|
23
|
+
completion_proof: "All acceptance-criteria checkboxes on #129/#130/#131 satisfied AND all four repo gates green."
|
|
24
|
+
likely_misfire: "Declaring done after planning; or shipping #129 fail-closed without the open_access opt-in + dummy update, breaking the dummy app/tests; or entangling all 3 in one commit; or deleting toasts instead of scoping them in #130."
|
|
25
|
+
blind_spots_considered:
|
|
26
|
+
- "#129 is an intentional breaking change needing a CHANGELOG/upgrade note + dummy-app opt-in landing together."
|
|
27
|
+
- "#130 has TWO global-stream leak paths: ApplicationController#broadcast_flash_toasts (request flashes) AND Broadcaster#broadcast_toast (background events). Both must be fixed."
|
|
28
|
+
- "Issues are independent (no blocker ordering) but each is its own vertical slice + commit."
|
|
29
|
+
- "CI diff-coverage gate requires a test for every new rescue/fallback branch."
|
|
30
|
+
existing_plan_facts:
|
|
31
|
+
- "GitHub issue #131: exclude .claude/ from gemspec git ls-files filter, keep .claude/skills/sm-*; add test/gemspec_packaging_test.rb. Spec validated against source_monitor.gemspec lines 23-31 (.claude/ NOT in reject list)."
|
|
32
|
+
- "GitHub issue #130: StreamResponder#toast is already response-local; leaks are ApplicationController#broadcast_flash_toasts and Realtime::Broadcaster#broadcast_toast -> global 'source_monitor_notifications' stream subscribed by every tab via layouts/source_monitor/application.html.erb:15. Add test/lib/source_monitor/realtime/broadcaster_test.rb (none exists yet)."
|
|
33
|
+
- "GitHub issue #129: Security::Authentication.authenticate!/authorize! call_handler is a silent no-op when handler nil. authentication_configured? predicate already exists. Add open_access opt-in on Configuration::AuthenticationSettings (reset in reset!), enforce in Security::Authentication, deny via :forbidden following ApplicationController#record_not_found multi-format pattern. Dummy initializer (test/dummy/config/initializers/source_monitor.rb) currently sets NO auth handler -> must opt into open_access. AuthenticationHelpers#configure_authentication covers the handler-honored path."
|
|
34
|
+
- "Source: issues #129/#130/#131 carry full ## Technical Spec sections written by issues-to-specs; parent umbrella #128; PRD #127."
|
|
35
|
+
|
|
36
|
+
rules:
|
|
37
|
+
pm_owns_state: true
|
|
38
|
+
one_active_task: true
|
|
39
|
+
max_write_workers: 1
|
|
40
|
+
no_implementation_without_worker_or_pm_task: true
|
|
41
|
+
no_completion_without_judge_or_pm_audit: true
|
|
42
|
+
planning_is_not_completion: true
|
|
43
|
+
queued_required_worker_blocks_completion: true
|
|
44
|
+
continuous_until_full_outcome: true
|
|
45
|
+
missing_input_or_credentials_do_not_stop_goal: true
|
|
46
|
+
preserve_and_validate_existing_plan: true
|
|
47
|
+
intake_misfire_must_be_audited: true
|
|
48
|
+
goal_pressure_requires_oracle: true
|
|
49
|
+
no_completion_on_weak_proof: true
|
|
50
|
+
slice_policy:
|
|
51
|
+
max_consecutive_tiny_tasks: 2
|
|
52
|
+
prefer_vertical_slices: true
|
|
53
|
+
judge_picks_largest_safe_slice: true
|
|
54
|
+
worker_completes_whole_slice: true
|
|
55
|
+
|
|
56
|
+
agents:
|
|
57
|
+
# installed | bundled_not_installed | missing | unknown
|
|
58
|
+
scout: installed # ~/.claude/agents/goal-scout.md verified
|
|
59
|
+
worker: installed # ~/.claude/agents/goal-worker.md verified
|
|
60
|
+
judge: installed # ~/.claude/agents/goal-judge.md verified
|
|
61
|
+
|
|
62
|
+
visual_board:
|
|
63
|
+
# none | local | unknown
|
|
64
|
+
selected: local
|
|
65
|
+
local:
|
|
66
|
+
status: live # not_requested | starting | live | generated | blocked
|
|
67
|
+
url: "http://goalbuddy.localhost:41737/engine-hardening/"
|
|
68
|
+
command: "npx goalbuddy board docs/goals/engine-hardening"
|
|
69
|
+
|
|
70
|
+
active_task: null
|
|
71
|
+
|
|
72
|
+
tasks:
|
|
73
|
+
- id: T001
|
|
74
|
+
type: judge
|
|
75
|
+
assignee: Judge
|
|
76
|
+
status: done
|
|
77
|
+
reasoning_hint: medium
|
|
78
|
+
objective: "Validate the three issue Technical Specs against current HEAD, confirm the leak paths / fail-open behavior / gemspec filter still match the code, and confirm the Worker package sequence and allowed_files for T002-T004."
|
|
79
|
+
inputs:
|
|
80
|
+
- "GitHub issues #129, #130, #131 (## Technical Spec sections)"
|
|
81
|
+
- "source_monitor.gemspec"
|
|
82
|
+
- "lib/source_monitor/security/authentication.rb"
|
|
83
|
+
- "lib/source_monitor/configuration/authentication_settings.rb"
|
|
84
|
+
- "app/controllers/source_monitor/application_controller.rb"
|
|
85
|
+
- "lib/source_monitor/realtime/broadcaster.rb"
|
|
86
|
+
- "lib/source_monitor/turbo_streams/stream_responder.rb"
|
|
87
|
+
- "app/views/layouts/source_monitor/application.html.erb"
|
|
88
|
+
- "test/dummy/config/initializers/source_monitor.rb"
|
|
89
|
+
constraints:
|
|
90
|
+
- "Read-only. Do not edit implementation files."
|
|
91
|
+
- "Preserve the issue specs as plan facts; only flag conflicts with HEAD."
|
|
92
|
+
- "Do not collapse the three issues into one package; keep one Worker package per issue."
|
|
93
|
+
expected_output:
|
|
94
|
+
- "Per-issue: spec still valid? any drift from HEAD?"
|
|
95
|
+
- "Confirmed allowed_files + verify commands for T002 (#131), T003 (#130), T004 (#129)"
|
|
96
|
+
- "Any owner question needed before a package can proceed (e.g. open_access naming)"
|
|
97
|
+
- "Go/no-go to start T002"
|
|
98
|
+
receipt:
|
|
99
|
+
result: done
|
|
100
|
+
decision: approved_with_notes
|
|
101
|
+
full_outcome_complete: false
|
|
102
|
+
note: notes/T001-spec-validation.md
|
|
103
|
+
summary: "All 3 specs valid vs HEAD (evidence cited). go for T002. Two critical board updates applied: T004 +test/test_helper.rb (reset_configuration! wipes dummy open_access per-test; ~24 tests would 403) and flip application_controller_test.rb:28-31 to :forbidden; T003 extends existing broadcaster_test.rb (spec wrongly said it was missing) + aware of 2 extra background toast sites. Sequence #131->#130->#129 OK; T003/T004 serial-only (shared application_controller.rb)."
|
|
104
|
+
owner_questions_blocking: []
|
|
105
|
+
|
|
106
|
+
- id: T002
|
|
107
|
+
type: worker
|
|
108
|
+
assignee: Worker
|
|
109
|
+
status: done
|
|
110
|
+
reasoning_hint: medium
|
|
111
|
+
objective: "Implement #131: exclude .claude/ internals from the packaged gem while keeping .claude/skills/sm-*; add a packaging test."
|
|
112
|
+
allowed_files:
|
|
113
|
+
- "source_monitor.gemspec"
|
|
114
|
+
- "test/gemspec_packaging_test.rb"
|
|
115
|
+
verify:
|
|
116
|
+
- "rbenv exec bin/rails test test/gemspec_packaging_test.rb"
|
|
117
|
+
- "rbenv exec bin/rubocop"
|
|
118
|
+
stop_if:
|
|
119
|
+
- "Need files outside allowed_files."
|
|
120
|
+
- "git ls-files unavailable in the test env (use stubbed file-list approach per spec risk note)."
|
|
121
|
+
- "Verification fails twice."
|
|
122
|
+
receipt:
|
|
123
|
+
result: done
|
|
124
|
+
changed_files:
|
|
125
|
+
- source_monitor.gemspec
|
|
126
|
+
- test/gemspec_packaging_test.rb
|
|
127
|
+
commands:
|
|
128
|
+
- cmd: "bin/rails test test/gemspec_packaging_test.rb"
|
|
129
|
+
status: pass
|
|
130
|
+
- cmd: "bin/rubocop (505 files)"
|
|
131
|
+
status: pass
|
|
132
|
+
summary: "Added '.claude/' to the gemspec reject-prefix list; existing Dir[.claude/skills/sm-*] re-include keeps only sm-* skills. New test/gemspec_packaging_test.rb asserts no .claude internals ship, only sm-* under .claude/, sm-host-setup present, app/+lib/ intact. spec.files: 427 total, 54 .claude (all sm-*), 0 non-sm .claude. Committed 846d612, Closes #131."
|
|
133
|
+
notes: "IMPORTANT for later tasks: 'rbenv exec bin/rails' fails (rbenv exec resolves via PATH, not relative path). Use 'bin/rails'/'bin/rubocop' directly (already rbenv-shimmed) or 'rbenv exec rails'. Live HEAD has 54 sm-* skill files (T001 said 39); test does not hard-code count."
|
|
134
|
+
|
|
135
|
+
- id: T003
|
|
136
|
+
type: worker
|
|
137
|
+
assignee: Worker
|
|
138
|
+
status: done
|
|
139
|
+
reasoning_hint: medium
|
|
140
|
+
objective: "Implement #130: stop request flashes broadcasting to the global notification stream (keep them response-local) and scope or decontextualize background Broadcaster toasts; EXTEND the existing broadcaster test to prove no cross-user leakage. Do not delete the toast feature."
|
|
141
|
+
constraints:
|
|
142
|
+
- "T001 finding: test/lib/source_monitor/realtime/broadcaster_test.rb ALREADY EXISTS (stubs Turbo::StreamsChannel.broadcast_append_to) -- EXTEND it, do not recreate."
|
|
143
|
+
- "Two leak paths to fix: (a) ApplicationController#broadcast_flash_toasts:63-79 (request flashes -> global) keep response-local; (b) Broadcaster#broadcast_toast:66-85 (background fetch/scrape) keep global but context-free OR scope per spec default."
|
|
144
|
+
- "Be aware of 2 more background broadcast_toast call sites (source_health_check_orchestrator.rb:57, source_updater.rb:196 auto-pause) -- source-operational, context-free is fine."
|
|
145
|
+
- "Serial-only with T004 (shared application_controller.rb); never run in parallel."
|
|
146
|
+
allowed_files:
|
|
147
|
+
- "lib/source_monitor/realtime/broadcaster.rb"
|
|
148
|
+
- "app/controllers/source_monitor/application_controller.rb"
|
|
149
|
+
- "lib/source_monitor/turbo_streams/stream_responder.rb"
|
|
150
|
+
- "app/views/layouts/source_monitor/application.html.erb"
|
|
151
|
+
- "test/lib/source_monitor/realtime/broadcaster_test.rb"
|
|
152
|
+
- "test/controllers/source_monitor/**"
|
|
153
|
+
verify:
|
|
154
|
+
- "rbenv exec bin/rails test test/lib/source_monitor/realtime/broadcaster_test.rb"
|
|
155
|
+
- "rbenv exec bin/rails test"
|
|
156
|
+
- "rbenv exec bin/rubocop"
|
|
157
|
+
stop_if:
|
|
158
|
+
- "Need files outside allowed_files."
|
|
159
|
+
- "Scoped-stream vs context-free-global decision turns out ambiguous beyond the spec default (escalate to owner)."
|
|
160
|
+
- "Verification fails twice."
|
|
161
|
+
receipt:
|
|
162
|
+
result: done
|
|
163
|
+
changed_files:
|
|
164
|
+
- app/controllers/source_monitor/application_controller.rb
|
|
165
|
+
- app/views/layouts/source_monitor/application.html.erb
|
|
166
|
+
- test/controllers/source_monitor/application_controller_test.rb
|
|
167
|
+
- test/lib/source_monitor/realtime/broadcaster_test.rb
|
|
168
|
+
commands:
|
|
169
|
+
- cmd: "bin/rails test test/lib/source_monitor/realtime/broadcaster_test.rb"
|
|
170
|
+
status: pass
|
|
171
|
+
- cmd: "bin/rails test (full suite, 1736 runs)"
|
|
172
|
+
status: pass
|
|
173
|
+
- cmd: "bin/rubocop (505 files)"
|
|
174
|
+
status: pass
|
|
175
|
+
summary: "Removed the after_action broadcasting request flashes to the global ActionCable stream. Request flashes now render inline on HTML loads (source_monitor_flash_toasts helper into existing #source_monitor_notifications container) and append to the turbo_stream response body -- reaching only the requesting tab. Background operational broadcast_toast left global + context-free (decision: source-operational data, not per-user). Fixed a flash.discard timing bug that wiped flashes across redirects. Committed c4e5626, Closes #130."
|
|
176
|
+
notes: "Did not touch broadcaster.rb or stream_responder.rb (in allowed_files but unneeded -- the leak was the controller after_action). Background toast posture = keep global, context-free (documented default; not scoped per-session, avoiding over-engineering)."
|
|
177
|
+
|
|
178
|
+
- id: T004
|
|
179
|
+
type: worker
|
|
180
|
+
assignee: Worker
|
|
181
|
+
status: done
|
|
182
|
+
reasoning_hint: high
|
|
183
|
+
objective: "Implement #129: fail-closed engine access by default when no auth handler is configured; add explicit open_access/demo opt-in; opt the dummy app AND the test harness in; honor configured handlers; update docs/initializer template + CHANGELOG. Land enforcement + opt-in + dummy/test-harness update together."
|
|
184
|
+
constraints:
|
|
185
|
+
- "CRITICAL (T001 finding): dummy initializer alone is NOT enough -- test/test_helper.rb:85-89 runs SourceMonitor.reset_configuration! every test, wiping open_access. MUST enable open_access in test/test_helper.rb setup (or per-file) or ~24 controller/integration tests will 403."
|
|
186
|
+
- "Flip test/controllers/source_monitor/application_controller_test.rb:28-31 (currently asserts :success with no auth) to assert :forbidden -- this is the explicit fail-closed assertion."
|
|
187
|
+
- "Add a test that open_access=true restores 200, and that configured handlers (AuthenticationHelpers#configure_authentication) still allow access."
|
|
188
|
+
- "Reset open_access in AuthenticationSettings#reset! (authentication_settings.rb:39-44)."
|
|
189
|
+
- "Serial-only with T003 (shared application_controller.rb); never run in parallel."
|
|
190
|
+
allowed_files:
|
|
191
|
+
- "lib/source_monitor/security/authentication.rb"
|
|
192
|
+
- "lib/source_monitor/configuration/authentication_settings.rb"
|
|
193
|
+
- "app/controllers/source_monitor/application_controller.rb"
|
|
194
|
+
- "test/dummy/config/initializers/source_monitor.rb"
|
|
195
|
+
- "test/test_helper.rb"
|
|
196
|
+
- "test/lib/source_monitor/configuration/authentication_settings_test.rb"
|
|
197
|
+
- "test/controllers/source_monitor/**"
|
|
198
|
+
- "test/integration/**"
|
|
199
|
+
- "lib/generators/**"
|
|
200
|
+
- "docs/**"
|
|
201
|
+
- "CHANGELOG.md"
|
|
202
|
+
verify:
|
|
203
|
+
- "rbenv exec bin/rails test"
|
|
204
|
+
- "rbenv exec bin/rubocop"
|
|
205
|
+
- "rbenv exec bin/brakeman --no-pager"
|
|
206
|
+
- "cd test/dummy && rbenv exec bundle exec rails zeitwerk:check"
|
|
207
|
+
stop_if:
|
|
208
|
+
- "Need files outside allowed_files."
|
|
209
|
+
- "open_access opt-in naming/posture needs owner confirmation (escalate, do not guess past the spec default)."
|
|
210
|
+
- "Existing tests rely on implicit open access in a way the spec did not anticipate."
|
|
211
|
+
- "Verification fails twice."
|
|
212
|
+
receipt:
|
|
213
|
+
result: done
|
|
214
|
+
changed_files:
|
|
215
|
+
- lib/source_monitor/security/authentication.rb
|
|
216
|
+
- lib/source_monitor/configuration/authentication_settings.rb
|
|
217
|
+
- app/controllers/source_monitor/application_controller.rb
|
|
218
|
+
- test/dummy/config/initializers/source_monitor.rb
|
|
219
|
+
- test/test_helper.rb
|
|
220
|
+
- test/lib/source_monitor/configuration/authentication_settings_test.rb
|
|
221
|
+
- test/controllers/source_monitor/application_controller_test.rb
|
|
222
|
+
- test/controllers/source_monitor/source_favicon_fetches_controller_test.rb
|
|
223
|
+
- test/controllers/source_monitor/sources_controller_favicon_test.rb
|
|
224
|
+
- test/integration/source_monitor/favicon_integration_test.rb
|
|
225
|
+
- lib/generators/source_monitor/install/templates/source_monitor.rb.tt
|
|
226
|
+
- docs/configuration.md
|
|
227
|
+
- docs/deployment.md
|
|
228
|
+
- docs/setup.md
|
|
229
|
+
- CHANGELOG.md
|
|
230
|
+
commands:
|
|
231
|
+
- cmd: "bin/rails test (1741 runs, 0 failures)"
|
|
232
|
+
status: pass
|
|
233
|
+
- cmd: "bin/rubocop (505 files)"
|
|
234
|
+
status: pass
|
|
235
|
+
- cmd: "bin/brakeman --no-pager (0 warnings)"
|
|
236
|
+
status: pass
|
|
237
|
+
- cmd: "cd test/dummy && bin/rails zeitwerk:check"
|
|
238
|
+
status: pass
|
|
239
|
+
summary: "Fail-closed engine access. open_access bool (default false, reset in reset!) on AuthenticationSettings. Security::Authentication.access_denied_by_default? denies when no handler configured AND open_access false; configured handlers short-circuit. ApplicationController enforce_source_monitor_access_default first before_action renders :forbidden (html/turbo_stream/json, mirrors record_not_found) and halts chain. test_helper opts open_access=true after reset_configuration!; 3 self-resetting test files re-opt-in; application_controller_test flips to :forbidden + adds open_access on/off + handler tests. Install template, docs, CHANGELOG (BREAKING) updated. Committed 1dc03fe, Closes #129."
|
|
240
|
+
notes: "REQUIRED FOLLOW-UP -> T005: sm-* skill references (sm-configure, sm-host-setup) document the auth config DSL but live under .claude/skills/ (outside T004 allowed_files). Project CLAUDE.md 'Skills & docs alignment' rule requires them updated in the SAME PR when engine config changes. Created T005 to satisfy this."
|
|
241
|
+
|
|
242
|
+
- id: T005
|
|
243
|
+
type: worker
|
|
244
|
+
assignee: Worker
|
|
245
|
+
status: done
|
|
246
|
+
reasoning_hint: medium
|
|
247
|
+
objective: "Satisfy the project 'Skills & docs alignment' maintenance rule: update the sm-* skill references that document the authentication config DSL to reflect the new fail-closed default and the open_access opt-in (from #129)."
|
|
248
|
+
constraints:
|
|
249
|
+
- "Only update skill content that documents authentication/config defaults; do not alter unrelated skill text."
|
|
250
|
+
- "Match the wording/posture already used in docs/configuration.md + the install template (fail-closed default; open_access = true is demo/non-production only)."
|
|
251
|
+
- "Note: #131 excludes .claude internals from the gem EXCEPT .claude/skills/sm-*, so these skills DO still ship -- the maintenance rule applies."
|
|
252
|
+
allowed_files:
|
|
253
|
+
- ".claude/skills/sm-configure/**"
|
|
254
|
+
- ".claude/skills/sm-host-setup/**"
|
|
255
|
+
verify:
|
|
256
|
+
- "grep -ril 'open_access' .claude/skills/sm-configure .claude/skills/sm-host-setup"
|
|
257
|
+
- "bin/rubocop"
|
|
258
|
+
stop_if:
|
|
259
|
+
- "Need files outside allowed_files."
|
|
260
|
+
- "An sm-* skill has no authentication/config section to update (report which, then proceed with the ones that do)."
|
|
261
|
+
receipt:
|
|
262
|
+
result: done
|
|
263
|
+
changed_files:
|
|
264
|
+
- .claude/skills/sm-configure/SKILL.md
|
|
265
|
+
- .claude/skills/sm-configure/reference/configuration-reference.md
|
|
266
|
+
- .claude/skills/sm-host-setup/SKILL.md
|
|
267
|
+
- .claude/skills/sm-host-setup/reference/initializer-template.md
|
|
268
|
+
- .claude/skills/sm-host-setup/reference/setup-checklist.md
|
|
269
|
+
commands:
|
|
270
|
+
- cmd: "grep -ril open_access .claude/skills/sm-configure .claude/skills/sm-host-setup (5 files)"
|
|
271
|
+
status: pass
|
|
272
|
+
- cmd: "bin/rubocop (505 files)"
|
|
273
|
+
status: pass
|
|
274
|
+
summary: "Updated sm-configure + sm-host-setup skills to document fail-closed default + open_access opt-in, mirroring docs/ and the install template. Auth sections only; unrelated content untouched. Committed 834560c, Refs #129."
|
|
275
|
+
notes: "Satisfies the CLAUDE.md Skills & docs alignment maintenance rule for the #129 config change."
|
|
276
|
+
|
|
277
|
+
- id: T999
|
|
278
|
+
type: judge
|
|
279
|
+
assignee: Judge
|
|
280
|
+
status: done
|
|
281
|
+
reasoning_hint: high
|
|
282
|
+
objective: "Audit whether all three issues (#129, #130, #131) are implemented and verified, mapping each issue's acceptance criteria to test/evidence and confirming all four gates are green."
|
|
283
|
+
inputs:
|
|
284
|
+
- "T002, T003, T004 receipts"
|
|
285
|
+
- "Acceptance criteria on #129, #130, #131"
|
|
286
|
+
- "Last verification (all four gates)"
|
|
287
|
+
- "Current dirty diff"
|
|
288
|
+
constraints:
|
|
289
|
+
- "Do not implement."
|
|
290
|
+
- "Reject completion if any required Worker package is still queued/active or any gate is red."
|
|
291
|
+
- "Reject completion if any acceptance-criteria checkbox lacks evidence."
|
|
292
|
+
- "Map completion back to the original 3-issue outcome."
|
|
293
|
+
expected_output:
|
|
294
|
+
- "complete | not_complete"
|
|
295
|
+
- "full_outcome_complete: true | false"
|
|
296
|
+
- "per-issue criteria -> evidence map"
|
|
297
|
+
- "missing evidence / next task if not complete"
|
|
298
|
+
receipt:
|
|
299
|
+
result: done
|
|
300
|
+
decision: complete
|
|
301
|
+
full_outcome_complete: true
|
|
302
|
+
gates:
|
|
303
|
+
tests: "pass (1741 runs, 0 failures/errors/skips)"
|
|
304
|
+
rubocop: "pass (505 files, 0 offenses)"
|
|
305
|
+
brakeman: "pass (0 warnings)"
|
|
306
|
+
zeitwerk: "pass"
|
|
307
|
+
summary: "Independent audit re-ran 68 targeted tests + evaluated gemspec directly. All acceptance criteria on #131/#130/#129 map to committed code with tests that genuinely assert the behavior. #131: spec.files has 0 non-sm .claude files, 54 sm-* files, sm-host-setup present; packaging test asserts both. #130: global broadcast_flash_toasts after_action removed, replaced with response-local delivery, proven by assert_empty broadcast_calls; background toasts still render. #129: enforce_source_monitor_access_default is FIRST before_action, renders :forbidden and halts chain; tests cover deny-by-default / open_access / configured-handler / default+reset; dummy+harness+docs+template+CHANGELOG+sm-* skills all updated. No gaps."
|
|
308
|
+
missing_evidence: []
|
|
309
|
+
next_task_if_not_complete: null
|
|
310
|
+
|
|
311
|
+
checks:
|
|
312
|
+
dirty_fingerprint: "branch harden-engine-access-129-131; 5 issue commits (846d612 #131, c4e5626 #130, 1dc03fe #129, 834560c skills); work committed, tree clean except board"
|
|
313
|
+
last_verification:
|
|
314
|
+
result: pass
|
|
315
|
+
task: T999-pre-audit (PM ran all four gates on combined branch)
|
|
316
|
+
commands:
|
|
317
|
+
- cmd: "bin/rails test"
|
|
318
|
+
status: "pass — 1741 runs, 5301 assertions, 0 failures, 0 errors, 0 skips"
|
|
319
|
+
- cmd: "bin/rubocop"
|
|
320
|
+
status: "pass — 505 files, 0 offenses"
|
|
321
|
+
- cmd: "bin/brakeman --no-pager"
|
|
322
|
+
status: "pass — 0 warnings"
|
|
323
|
+
- cmd: "cd test/dummy && bin/rails zeitwerk:check"
|
|
324
|
+
status: "pass — All is good!"
|
data/docs/setup.md
CHANGED
|
@@ -18,8 +18,8 @@ This guide consolidates the new guided installer, verification commands, and rol
|
|
|
18
18
|
Run these commands inside your host Rails application before invoking the guided workflow:
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
|
-
bundle add source_monitor --version "~> 0.
|
|
22
|
-
# or add gem "source_monitor", "~> 0.
|
|
21
|
+
bundle add source_monitor --version "~> 0.14.0"
|
|
22
|
+
# or add gem "source_monitor", "~> 0.14.0" to Gemfile manually
|
|
23
23
|
bundle install
|
|
24
24
|
```
|
|
25
25
|
|
|
@@ -109,7 +109,7 @@ Prefer to script each step or plug SourceMonitor into an existing deployment che
|
|
|
109
109
|
6. **Start workers** with `bin/rails solid_queue:start` (or your process manager). The install generator automatically configures recurring jobs in `config/recurring.yml` for fetch scheduling, scraping, and cleanup. They'll run with `bin/dev` or `bin/jobs`.
|
|
110
110
|
- **Procfile.dev:** The generator automatically patches `Procfile.dev` with a `jobs:` entry for Solid Queue. Verify the file contains `jobs: bundle exec rake solid_queue:start` after running the generator.
|
|
111
111
|
- **Recurring schedule:** The generator automatically patches `config/queue.yml` dispatchers with `recurring_schedule: config/recurring.yml`. Verify the key is present after running the generator.
|
|
112
|
-
7. **Review the initializer** and tune queue names, HTTP timeouts, scraping adapters, retention limits, authentication hooks, and Mission Control integration. The [configuration reference](configuration.md) details every option.
|
|
112
|
+
7. **Review the initializer** and tune queue names, HTTP timeouts, scraping adapters, retention limits, authentication hooks, and Mission Control integration. The [configuration reference](configuration.md) details every option. **Important:** the engine is fail-closed by default — configure `config.authentication.authenticate_with` / `authorize_with`, otherwise engine routes return `403 Forbidden`. For non-production demos only, you may set `config.authentication.open_access = true`.
|
|
113
113
|
8. **Verify the install**: run `bin/source_monitor verify` to ensure Solid Queue workers and Action Cable are healthy, then visit the mount path to trigger a fetch manually. Enable telemetry if you want JSON logs recorded for support.
|
|
114
114
|
|
|
115
115
|
### Host Compatibility Matrix
|
data/docs/upgrade.md
CHANGED
|
@@ -46,6 +46,33 @@ If a removed option raises an error (`SourceMonitor::DeprecatedOptionError`), yo
|
|
|
46
46
|
|
|
47
47
|
## Version-Specific Notes
|
|
48
48
|
|
|
49
|
+
### Upgrading to 0.14.0
|
|
50
|
+
|
|
51
|
+
**What changed:**
|
|
52
|
+
- **BREAKING — Fail-closed engine access by default** (#129). SourceMonitor now returns `403 Forbidden` on every mounted route unless the host app has configured an authentication or authorization handler. Previously routes were publicly accessible when no handler was configured.
|
|
53
|
+
- **Request flashes are delivered response-local** (#130). Flash toasts are no longer broadcast to the global `source_monitor_notifications` ActionCable stream (which every connected tab subscribed to); they now render inline on full-page loads and append to the turbo_stream response, reaching only the requesting tab. Background operational toasts remain global but carry no per-user request context.
|
|
54
|
+
- **Gem package excludes `.claude` internals** (#131). Only `.claude/skills/sm-*` SourceMonitor skills are packaged; agents, hooks, agent-memory, `settings.json`, commands, and non-`sm-*` skills are excluded.
|
|
55
|
+
|
|
56
|
+
**Upgrade steps:**
|
|
57
|
+
```bash
|
|
58
|
+
bundle update source_monitor
|
|
59
|
+
bin/rails source_monitor:upgrade
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Action required (BREAKING):**
|
|
63
|
+
Open `config/initializers/source_monitor.rb`. If neither `config.authentication.authenticate_with` nor `config.authentication.authorize_with` is configured, the engine will return `403` on all routes after upgrade. Either:
|
|
64
|
+
- configure a handler to gate the engine with your host auth stack:
|
|
65
|
+
```ruby
|
|
66
|
+
config.authentication.authenticate_with :authenticate_user!
|
|
67
|
+
config.authentication.authorize_with ->(controller) { controller.current_user&.admin? }
|
|
68
|
+
```
|
|
69
|
+
- or, for local demos/sandboxes only, explicitly opt into public access:
|
|
70
|
+
```ruby
|
|
71
|
+
config.authentication.open_access = true # NOT for production
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
No database migrations are required. The flash and packaging changes apply transparently.
|
|
75
|
+
|
|
49
76
|
### Upgrading to 0.13.1
|
|
50
77
|
|
|
51
78
|
**What changed:**
|
|
@@ -52,6 +52,10 @@ SourceMonitor.configure do |config|
|
|
|
52
52
|
config.mission_control_dashboard_path = nil
|
|
53
53
|
|
|
54
54
|
# ---- Authentication -----------------------------------------------------
|
|
55
|
+
# SECURITY: SourceMonitor is FAIL-CLOSED by default. If you do not configure
|
|
56
|
+
# an authentication or authorization handler below, every engine route
|
|
57
|
+
# (including create/update/delete/enqueue actions) returns 403 Forbidden.
|
|
58
|
+
#
|
|
55
59
|
# Hook SourceMonitor into your host application's authentication stack. Each
|
|
56
60
|
# handler can be a Symbol (invoked on the controller) or a callable that will
|
|
57
61
|
# receive the controller instance.
|
|
@@ -59,6 +63,12 @@ SourceMonitor.configure do |config|
|
|
|
59
63
|
# config.authentication.authorize_with -> { authorize!(:manage, :source_monitor) }
|
|
60
64
|
# config.authentication.current_user_method = :current_admin
|
|
61
65
|
# config.authentication.user_signed_in_method = :admin_signed_in?
|
|
66
|
+
#
|
|
67
|
+
# Explicit opt-in for open/unauthenticated access. Leave this commented out
|
|
68
|
+
# in production. Only enable it for local demos or sandboxes where the engine
|
|
69
|
+
# routes are deliberately public.
|
|
70
|
+
# WARNING: non-production / demo only -- this disables the fail-closed guard.
|
|
71
|
+
# config.authentication.open_access = true
|
|
62
72
|
|
|
63
73
|
# ---- HTTP client -------------------------------------------------------
|
|
64
74
|
# Tune the Faraday client SourceMonitor uses for fetches/scrapes.
|
|
@@ -22,7 +22,7 @@ module SourceMonitor
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
attr_reader :authenticate_handler, :authorize_handler
|
|
25
|
-
attr_accessor :current_user_method, :user_signed_in_method
|
|
25
|
+
attr_accessor :current_user_method, :user_signed_in_method, :open_access
|
|
26
26
|
|
|
27
27
|
def initialize
|
|
28
28
|
reset!
|
|
@@ -41,6 +41,10 @@ module SourceMonitor
|
|
|
41
41
|
@authorize_handler = nil
|
|
42
42
|
@current_user_method = nil
|
|
43
43
|
@user_signed_in_method = nil
|
|
44
|
+
# Fail-closed by default: when no handler is configured the engine
|
|
45
|
+
# denies access. Set to true to opt into open/demo access. This is a
|
|
46
|
+
# technical opt-in flag, not business state.
|
|
47
|
+
@open_access = false
|
|
44
48
|
end
|
|
45
49
|
|
|
46
50
|
private
|
|
@@ -31,6 +31,16 @@ module SourceMonitor
|
|
|
31
31
|
config.authenticate_handler.present? || config.authorize_handler.present?
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
+
# Fail-closed predicate. The engine denies access when the host app has
|
|
35
|
+
# configured no authentication/authorization handler AND has not
|
|
36
|
+
# explicitly opted into open access. Configured handlers always win: when
|
|
37
|
+
# a handler is present the handler decides and this returns false.
|
|
38
|
+
def self.access_denied_by_default?(_controller = nil)
|
|
39
|
+
return false if authentication_configured?
|
|
40
|
+
|
|
41
|
+
!SourceMonitor.config.authentication.open_access
|
|
42
|
+
end
|
|
43
|
+
|
|
34
44
|
def self.authorize_configured?
|
|
35
45
|
SourceMonitor.config.authentication.authorize_handler.present?
|
|
36
46
|
end
|
data/source_monitor.gemspec
CHANGED
|
@@ -23,11 +23,16 @@ Gem::Specification.new do |spec|
|
|
|
23
23
|
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
24
24
|
tracked_files = `git ls-files -z`.split("\x0")
|
|
25
25
|
tracked_files.reject do |file|
|
|
26
|
-
file.start_with?(".ai/", ".github/", ".vbw-planning/", "coverage/", "node_modules/", "pkg/", "spec/", "test/", "tmp/", "vendor/", "examples/", "bin/")
|
|
26
|
+
file.start_with?(".ai/", ".github/", ".vbw-planning/", "coverage/", "node_modules/", "pkg/", "spec/", "test/", "tmp/", "vendor/", "examples/", "bin/") ||
|
|
27
|
+
# Exclude all .claude internals (agents, hooks, agent-memory, settings,
|
|
28
|
+
# commands) but keep the intended SourceMonitor sm-* skills. Driven from
|
|
29
|
+
# git ls-files (inside Dir.chdir) so packaging is CWD-independent --- a
|
|
30
|
+
# bare Dir[] glob here returned nothing on CI when another test had
|
|
31
|
+
# changed the process working directory.
|
|
32
|
+
(file.start_with?(".claude/") && !file.start_with?(".claude/skills/sm-"))
|
|
27
33
|
end
|
|
28
34
|
end
|
|
29
35
|
spec.files += [ "CHANGELOG.md" ].select { |path| File.exist?(File.join(__dir__, path)) }
|
|
30
|
-
spec.files += Dir[".claude/skills/sm-*/**/*"]
|
|
31
36
|
spec.files.uniq!
|
|
32
37
|
|
|
33
38
|
spec.require_paths = [ "lib" ]
|