Skip to content

[pull] main from forem:main - #402

Merged
pull[bot] merged 5 commits into
amishakov:mainfrom
forem:main
Sep 26, 2026
Merged

pull[bot] merged 5 commits into
amishakov:mainfrom
forem:main

Conversation

@pull

@pull pull Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

a-abdellatif98 and others added 5 commits September 25, 2026 15:42
* feat(notifications): notify co-authors when a post they are credited on publishes

Closes #23758.

Article has had co-author support for a long time, co_author_ids with
validations, but nothing ever told a co-author their post went live. The
notification architecture was already there too, so this is wiring rather
than new concepts.

- Notifications::CoAuthor::Send creates one notification per credited
  co-author, following the shape of NotifiableAction::Send.
- Notifications::CoAuthor::Remove deletes notifications for co-authors who
  are dropped. Targeted by user, since RemoveAll and RemoveAllByAction
  delete across every user for a notifiable.
- Notifications::CoAuthorWorker runs both, with lock: :until_executing and
  on_conflict: :replace so rapid edits coalesce.
- Article notifies on publish through send_to_mentioned_users_and_followers,
  and on an after_update_commit when co_author_ids changes on an already
  published post. Drafts are skipped.

Avoiding the double notify: co-authors who also follow the author or the
organization would otherwise get a follower notification as well. They are
now excluded from the follower query in NotifiableAction::Send, the same
way already-mentioned users are.

Sending is idempotent. It skips co-authors who already have a notification,
so re-publishing or an unrelated edit does not notify anyone twice, and it
upserts rather than inserts because a unique index covers
(user_id, notifiable_id, notifiable_type, action) and two publishes racing
would otherwise raise.

Web notifications only, no email or push. Locale strings for en, fr and pt.

* Fix co-author notification sorting, nil guards, filter scope, and add regression tests

- Set notified_at in bulk upsert in Notifications::CoAuthor::Send to ensure proper ordering and offset pagination
- Add Array.wrap around co_author_ids to guard against nil in CoAuthor::Send and NotifiableAction::Send
- Include CoAuthor action in Notification.for_published_articles scope so notifications appear under the Posts tab
- Clean up CoAuthor notifications on article unpublish in Articles::Updater and Moderator::UnpublishAllArticles
- Add unit specs for Notifications::CoAuthorWorker, Article#notify_co_author_changes, Notification.send_to_co_authors, and unpublishing cleanup

* Update co-author notifications on article edits and optimize deletion

- Refresh json_data on both Published and CoAuthor notifications in Article#update_notifications
- Replace destroy_all with delete_all in Notifications::CoAuthor::Remove
- Add request specs verifying GET /notifications and GET /notifications/posts
- Add regression model spec in Notification#update_notifications

* Update notification conditions for co-authors

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Refactor(spec): flatten co-author organization request spec and eliminate rubocop disables

* Style(notifications): fix indentation in CoAuthor::Send

* fix(url): prevent double port appending in development environment

---------

Co-authored-by: Ben Halpern <bendhalpern@gmail.com>
Co-authored-by: Mikey <mikeydorje@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Ship production logs to Better Stack when BETTERSTACK_SOURCE_TOKEN is set

Claude-Session: https://claude.ai/code/session_01YL4NMvotZJsvKFri2g2qZa
Entire-Checkpoint: 01M3CN2FH8ABTF7QSP5YXJXJV0

* Document optional Better Stack env vars in .env_sample

Claude-Session: https://claude.ai/code/session_01YL4NMvotZJsvKFri2g2qZa
Entire-Checkpoint: 01M3CN5M7D0HHQ5YCR6D853D5J

* Harden Better Stack log shipping and move it to an initializer

- Attach the Better Stack logger in config/initializers/betterstack.rb with
  Rails.logger.broadcast_to, so it adds to whatever logger Rails built (stdout
  or the default file logger) instead of replacing it when RAILS_LOG_TO_STDOUT
  isn't set. production.rb is back to its original state.
- Add Betterstack::LogDevice, a subclass of logtail's HTTP device that:
  - waits 1s between connection attempts. Upstream reconnects in a tight loop
    when a connection fails fast, which kept a CPU core busy in every process
    that had logged an error.
  - caps the exit-time flush at 5s. Upstream waits up to 20s, and Heroku sends
    SIGKILL 30s after SIGTERM.
  - turns TLS certificate verification back on (upstream sets VERIFY_NONE).
- lib/betterstack is ignored by Zeitwerk, so logtail is still only loaded when
  BETTERSTACK_SOURCE_TOKEN is set.
- BETTERSTACK_INGESTING_HOST is optional and falls back to logtail's default
  host, instead of raising KeyError at boot.
- Pin logtail to ~> 0.1.17, since the subclass overrides private methods.
- Add specs for the initializer wiring (including tagged blocks running once)
  and for the log device fixes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Ben Halpern <bendhalpern@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…g the Fastly client IP (#23882)

Claude-Session: https://claude.ai/code/session_01YL4NMvotZJsvKFri2g2qZa
Entire-Checkpoint: 01M3D5BYND5PPGCK6V07MGZNT0
…g via the Sentry SDK (#23595)

* Add env-gated Better Stack error reporting and sampled OTel tracing (observability pilot)

* Address pilot review: match Honeybadger's ignores, load Sentry only when configured, tolerate missing OTLP host

- Sentry now ignores only the four classes Honeybadger ignores. Rack::Timeout
  and PG::QueryCanceled are fingerprinted into one error each (as Honeybadger
  does) instead of dropped, and SIGHUP SignalExceptions are skipped, so Better
  Stack's counts are comparable.
- sentry-rails/sentry-ruby are require: false and loaded from
  config/application.rb only when BETTER_STACK_ERRORS_DSN is set in
  production, the same way logtail-rails is. Requiring a Railtie from an
  initializer would be too late for its middleware to install.
- BETTER_STACK_OTLP_TOKEN without BETTER_STACK_OTLP_HOST now logs a warning
  and skips tracing instead of raising KeyError at boot.

Claude-Session: https://claude.ai/code/session_01LLhNCkqm5YHohiaT6Z5nQR

* Vendor the pilot's new gems into vendor/cache

CI installs only from vendor/cache, so bundle install failed on the Sentry
and OpenTelemetry gems the pilot added to Gemfile.lock.

Claude-Session: https://claude.ai/code/session_01LLhNCkqm5YHohiaT6Z5nQR

* Trace with the Sentry SDK instead of OpenTelemetry, and report Sidekiq errors

Better Stack stores Sentry SDK transactions as spans (confirmed on MLH
Core's Railway source), so one SDK covers errors and tracing, matching
MLH Core. This drops the OpenTelemetry initializer and its 27 gems.

- traces_sample_rate defaults to 5% (SENTRY_TRACES_SAMPLE_RATE overrides).
- sentry-sidekiq reports worker errors, which sentry-rails alone misses.
  report_after_job_retries mirrors Honeybadger's attempt_threshold by
  skipping failures that will be retried.
- sentry-sidekiq is loaded with sentry-rails in config/application.rb,
  only when BETTER_STACK_ERRORS_DSN is set in production.

Claude-Session: https://claude.ai/code/session_01LLhNCkqm5YHohiaT6Z5nQR
@pull pull Bot locked and limited conversation to collaborators Sep 26, 2026
@pull pull Bot added the ⤵️ pull label Sep 26, 2026
@pull
pull Bot merged commit 18d50cb into amishakov:main Sep 26, 2026

This branch had an error being deployed

1 failed deployment
staging — 18d50cb8 Deployed Sep 26, 2026 by pull[bot] via deploy (staging) #378
production — 18d50cb8 Deployed Sep 26, 2026 by pull[bot] via deploy (production) #378
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants