Skip to content

定期イベント主催者ユーザーが退会/休会/研修終了した際の処理の見直し#9732

Open
yokomaru wants to merge 13 commits intomainfrom
feature/close-regular-event-when-no-organizers
Open

定期イベント主催者ユーザーが退会/休会/研修終了した際の処理の見直し#9732
yokomaru wants to merge 13 commits intomainfrom
feature/close-regular-event-when-no-organizers

Conversation

@yokomaru
Copy link
Copy Markdown
Contributor

@yokomaru yokomaru commented Mar 4, 2026

Issue

概要

  • 退会/休会/研修終了処理時の定期イベント関連の処理を変更
  • 退会/休会/研修終了画面で定期イベント主催者へのワーニング表示に「自分一人しか主催者がいない開催中イベントは終了になる」旨の記載を追加
    • 既存の細かい文言のズレなども修正
    • 以下もリファクタリングも併せて対応
      • ほぼ同じ文章が3箇所に記載されていたためラベル付きでパーシャルへ切り出し(Issueのコメント にて文言など確認済み)
      • viewでクエリを発行していた箇所をcontrollerへ移動
  • イベント作成時の既存バグを修正
    • イベント作成処理後のイベント作成者を主催者に登録する処理をcreateからfind_or_create_byに変更した
      • イベント登録画面にて、自分を主催者を自分1人してイベント作成すると主催者重複エラーとなる
      • 別途Issueは立てているので仕様の見直しや根本的な修正はそちらで行う
      • ローカルで都度エラーになるのは開発体験が悪いためこのPRで併せて対応する

変更確認方法

  1. feature/close-regular-event-when-no-organizersをローカルに取り込む
  2. bin/devでサーバーを起動
  3. 退会時のイベント離脱処理を確認(退会で詳細を確認し、休会・研修終了はざっと確認する)

事前準備

手順
  • hajime退会画面 へ遷移
  • 主催中イベント一覧の注意メッセージに以下が表示されている
    • 「退会後、ご自身のみが主催者である開催中のイベントは自動的に終了となります。」の一文が表示されていること
    • 事前準備で用意した開催中かつ主催者が1人のイベント が表示されていること
    • 開催中かつ主催者が複数人のイベント(チェリー本輪読会 )が表示されていること
  • 必須項目を入力して退会処理を実行
退会後の確認(komagata)
  • komagataでログイン
  • 以下を確認
    • 主催イベント
      • 開催中かつ主催者が1人のイベント
        • イベントが終了していること
      • 開催中かつ主催者が複数人のイベント(チェリー本輪読会 )
        • hajime が主催者から抜けていること
      • 終了済みイベント( 終了イベント)
        • 主催者はhajimeのままになっている
    • 参加イベント
  1. 休会

手順

  • hatsunoなど適当なアカウントでログイン
  • 主催者が1人の開催中イベントを作成
  • 休会画面 へ遷移
  • 「休会後、ご自身のみが主催者である開催中のイベントは自動的に終了となります。」が表示されている
  • 休会画面に主催中イベントが表示されている
  • 休会処理を実行

確認(komagata

  • komagataでログイン
  • イベントが終了になっていることを確認
  1. 研修終了

手順

  • kensyu(研修生)でログイン
  • 主催者が1人の開催中イベントを用意
  • komagataでログインし、適当なイベントにkensyuを主催に追加する
  • 研修終了 へ遷移
  • 研修終了画面に主催中イベントが表示されている
    • 自分が作成したものと、komagataで追加したイベントが両方表示されていること
  • 「研修終了後、ご自身のみが主催者である開催中のイベントは自動的に終了となります。」が表示されている
  • 研修終了処理を実行

確認(komagata

  • komagataでログイン
  • 自分が作成したイベント
    • イベントが終了になっていることを確認
  • komagataで追加したイベントが両方表示されていること
    • kensyuが主催から外れていることを確認

Screenshot

各動線の主催イベント警告文

アクション 変更前 変更後
退会 スクリーンショット 2026-03-05 22 14 33 スクリーンショット 2026-03-05 18 27 28
休会 スクリーンショット 2026-03-05 22 14 24 スクリーンショット 2026-03-05 18 27 37
研修終了 スクリーンショット 2026-03-05 22 14 33 スクリーンショット 2026-03-05 18 29 27

Summary by CodeRabbit

リリースノート

  • リファクタリング
    • 定期イベントのクリーンアップ処理を統合し、オーガナイザー管理の効率を向上
    • 休止・退職・研修終了フローの表示を統一し、ユーザーへの警告メッセージを一貫性のあるデザインで表示
    • 複数箇所に散在していた定期イベント警告表示を共有コンポーネント化

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 4, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

定期イベントの後始末ロジックを整理し、cancel_participation_from_regular_eventsdelete_and_assign_new_organizerを統合したclean_up_regular_eventsメソッドで一元化。複数のコントローラーと関連モデルを更新し、自動再割り当て動作を削除。共有ビューパーシャルと対応テストも追加。

Changes

Cohort / File(s) Summary
Controllers
app/controllers/hibernation_controller.rb, app/controllers/retirement_controller.rb, app/controllers/training_completion_controller.rb, app/controllers/regular_events_controller.rb
各コントローラーの新規・作成アクションで@holding_regular_eventsを初期化し、ユーザー固有の呼び出しをcurrent_user.clean_up_regular_eventsに統合。regular_events_controllerではOrganizer.createregular_event_organizers.find_or_create_byに変更。
Models - User & Cleanup
app/models/user.rb, app/models/retirement.rb
cancel_participation_from_regular_eventsdelete_and_assign_new_organizerをUser から削除し、新しいclean_up_regular_eventsメソッドで統合。Retirement でも同様に2つのメソッドを削除し、新しいclean_up_regular_events委譲メソッドを追加。
Models - Regular Events
app/models/regular_event.rb, app/models/organizer.rb, app/models/regular_event_participation.rb
assign_admin_as_organizer_if_noneを削除、close_or_destroy_organizerメソッドを追加。Organizer からdelete_and_assign_newを削除。RegularEventParticipationfor_holding_eventsスコープを追加。
Views
app/views/hibernation/new.html.slim, app/views/retirement/new.html.slim, app/views/training_completion/new.html.slim, app/views/shared/_regular_events_warning.html.slim
各ビューで定期イベント警告を新しい共有パーシャルshared/regular_events_warningで統一。インライン警告ブロックを削除し、パーシャルへの委譲に変更。
Tests
test/models/organizer_test.rb, test/models/regular_event_test.rb, test/models/retirement_test.rb, test/models/user_test.rb, test/system/retirements/data_deletion_test.rb
削除されたメソッドのテストを削除、新しいclose_or_destroy_organizerテストを追加。cancel_participation_from_regular_eventsdelete_and_assign_new_organizerのテストをclean_up_regular_eventsテストに改名・再構成。
Fixtures
test/fixtures/regular_event_participations.yml
末尾の空白行を削除。

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~35 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers

  • ryufuta
  • komagata

Poem

🐇 ✨ ほんの一つにまとめたら
クリーンアップの流れも澄み渡る
古い再割り当ては卒業して
新しいclose_or_destroyへ🎉
定期イベントも後始末も
スッキリ統一で幸せだ〜!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed タイトルはPRの主要な変更内容(定期イベント主催者の退会/休会/研修終了時の処理見直し)を明確かつ簡潔に表現しており、変更セット全体とよく対応している。
Description check ✅ Passed 説明は必須セクション(Issue、概要、変更確認方法、Screenshot)をすべて含み、詳細な確認手順と期待される振る舞いが記載されている。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/close-regular-event-when-no-organizers

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@yokomaru yokomaru force-pushed the feature/close-regular-event-when-no-organizers branch from c16e368 to f3492c9 Compare March 4, 2026 18:59
@yokomaru yokomaru self-assigned this Mar 5, 2026
@yokomaru yokomaru force-pushed the feature/close-regular-event-when-no-organizers branch 2 times, most recently from fec1e89 to cc8f8cb Compare March 5, 2026 02:15
@yokomaru yokomaru force-pushed the feature/close-regular-event-when-no-organizers branch 4 times, most recently from b0eb509 to ff11d9b Compare March 5, 2026 12:02
end

def set_holding_regular_events
@holding_regular_events = RegularEvent.organizer_event(current_user).holding
Copy link
Copy Markdown
Contributor Author

@yokomaru yokomaru Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

元々slim側でクエリを発行していたロジックをそのままcontollerに移動しています。
ユーザーに紐づく開催中のイベントは、リレーションを使ってcurrent_user.organize_regular_events.holdingと取得できるのでorganizer_event(user)スコープは本来不要なのですが、他にも使っている箇所があったのでこちらがマージされた後別のPRで修正・削除対応しようと思っております。

- holding_regular_events = RegularEvent.organizer_event(current_user).holding
    - if holding_regular_events.any?

end

def set_holding_regular_events
@holding_regular_events = RegularEvent.organizer_event(current_user).holding
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

end

def set_holding_regular_events
@holding_regular_events = RegularEvent.organizer_event(current_user).holding
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

belongs_to :user
has_many :organizers, dependent: :destroy
# TODO: テーブル名を変更したら修正する
has_many :regular_event_organizers, class_name: 'Organizer', dependent: :destroy
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#9640 (comment) にあるように、一時的に変更した関連を使用して修正しています。テーブル名変更時に修正いたします。


class HibernationController < ApplicationController
skip_before_action :require_active_user_login, raise: false, only: %i[show]
before_action :set_holding_regular_events, only: %i[new create]
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@holding_regular_eventsはバリデーションエラー時にもセットしたいので、before_actionを使っています。(他のcontrollerも同様です)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[IMO]
#set_holding_regular_events を実行するのが new と create のみなら、アクションに直接書いてもいいんじゃないのかな〜と思いました!(個人の感想です)
本当に個人の思想なのですが、基本的に Controller に限らずコールバックは避けたいです。
いつどのタイミングでどの処理が呼ばれるのか把握しづらいというのが一番の理由です。全体を把握するために視線の移動が発生するのはなるべく避けたい気持ちがあります。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありがとうございます!
なるべくDRYにした方が良いのかな?と考えていたのですが、
使いたいactionが2つであることと、全体の把握しづらさという点で納得したので
コールバックをやめて直接定義するように修正いたしました!

9484c07

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yokomaru
DRY はいいよね〜
でも DRY に引っ張られて可読性が落ちると本末転倒だったりしちゃうね。
特にコールバックはね、取扱注意だからね、「ここぞ!」というときに使っていきたいね!

@yokomaru yokomaru force-pushed the feature/close-regular-event-when-no-organizers branch from ff11d9b to ce15c91 Compare March 5, 2026 12:28
if @regular_event.save
update_published_at
Organizer.create(user_id: current_user.id, regular_event_id: @regular_event.id)
@regular_event.regular_event_organizers.find_or_create_by(user: current_user)
Copy link
Copy Markdown
Contributor Author

@yokomaru yokomaru Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • イベント登録画面にて、主催者を自分1人にしてイベント作成を行うと、毎回バリデーションエラー(主催者重複違反)となり検証や調査の妨げになるため、すでに登録済みの場合は作成処理を行わないようにfind_or_create_byを使って修正しました
  • 本来であればエラーハンドリングなども含めて処理の見直しが必要だと思われるので、別途立てたIssueで検討になると思います
  • 併せてよりRailsっぽい書き方にするために、外部キー指定ではなく、@regular_eventを起点にしてオブジェクト指定で更新するように修正しています

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[LGTM]
#find_or_create_by にしたのはよいですね!
オブジェクトを渡すのも Rails っぽくてよいですね〜

= form_with model: current_user, local: true, url: training_completion_path, method: :post, class: 'form' do |f|
.form__items
- if current_user.regular_events.any?
- if @holding_regular_events.any?
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

current_user.regular_eventsだと主催イベントではなく作成したイベントのみの取得となっていたので修正しています。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[LGTM]
オォン
こういうところ他にもありそうですね〜

@yokomaru yokomaru marked this pull request as ready for review March 5, 2026 13:20
@github-actions github-actions bot requested a review from okuramasafumi March 5, 2026 13:21
@yokomaru yokomaru requested review from ryufuta and torinoko March 5, 2026 13:22
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/controllers/regular_events_controller.rb`:
- Line 32: `@regular_event.regular_event_organizers.find_or_create_by`(user:
current_user) の戻り値を検証しておらず、unsaved record のまま後続処理を進めてしまう問題があるため、結果を変数(例:
organizer)に代入し organizer.persisted? をチェックして保存に失敗している場合は organizer.save
を試みるかエラーハンドリング(ログ出力と処理中断/レスポンス返却)を行ってください。対象は controller 内の
`@regular_event.regular_event_organizers.find_or_create_by`(user: current_user)
を置き換える箇所で、保存失敗時の分岐を追加して通知や後続処理を行わないようにしてください。

In `@app/models/regular_event.rb`:
- Around line 155-162: The current close_or_destroy_organizer has a race (count
then delete) and can leave an event with zero organizers; fix by performing the
check-and-delete inside a DB-backed lock/transaction to make it atomic: wrap
logic in self.with_lock (or transaction + lock on regular_event_organizers) then
re-check regular_event_organizers.reload.lock.count (or
regular_event_organizers.lock.count) and if count == 1 call update(finished:
true) else locate organizer = regular_event_organizers.find_by(user:) and only
call organizer.destroy if organizer.present? (do not raise if nil).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 77f6429b-06be-4a7f-accf-ea10b92cac8f

📥 Commits

Reviewing files that changed from the base of the PR and between 9f3db88 and ce15c91.

📒 Files selected for processing (26)
  • app/controllers/hibernation_controller.rb
  • app/controllers/regular_events_controller.rb
  • app/controllers/retirement_controller.rb
  • app/controllers/training_completion_controller.rb
  • app/models/organizer.rb
  • app/models/regular_event.rb
  • app/models/retirement.rb
  • app/models/user.rb
  • app/views/hibernation/new.html.slim
  • app/views/retirement/new.html.slim
  • app/views/shared/_regular_events_warning.html.slim
  • app/views/training_completion/new.html.slim
  • test/fixtures/discord_profiles.yml
  • test/fixtures/organizers.yml
  • test/fixtures/regular_event_participations.yml
  • test/fixtures/regular_event_repeat_rules.yml
  • test/fixtures/regular_events.yml
  • test/fixtures/talks.yml
  • test/fixtures/users.yml
  • test/models/organizer_test.rb
  • test/models/regular_event_test.rb
  • test/models/retirement_test.rb
  • test/models/user_test.rb
  • test/system/regular_events_test.rb
  • test/system/retirements/data_deletion_test.rb
  • test/system/user/courses_test.rb
💤 Files with no reviewable changes (2)
  • app/models/organizer.rb
  • test/models/organizer_test.rb

@yokomaru yokomaru force-pushed the feature/close-regular-event-when-no-organizers branch from ce15c91 to fad4ea5 Compare March 6, 2026 04:24
Comment on lines +27 to +23
| 研修を修了される場合、イベントの進行に影響がないよう、任意で他の参加者に主催を引き継ぐことを推奨します。
| 退会手続きを完了する前に、
| 以下のリンク先でイベント設定変更を行なってください。
ul
- current_user.regular_events.each do |event|
li
= link_to edit_regular_event_path(event), target: '_blank', rel: 'noopener' do
| 定期イベント「#{event.title}」の設定変更
= render 'shared/regular_events_warning',
events: @holding_regular_events,
case_text: '研修を終了される',
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

元の文章は研修を修了されるとなっていましたが、git grepしたところ基本的には研修に対しては終了を使っていたので文言を変更しています。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[LGTM]
こういう用語や表現を揃える、とても大事! 👍

end

def clean_up_regular_events
regular_event_participations.joins(:regular_event).merge(RegularEvent.holding).destroy_all
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

削除対象はregular_event_participationsなのですが、開催中イベントという条件を付けるためにjoins(:regular_event) して絞り込んでいます。

RegularEvent起点で削除するか悩んだのですが、最終的に削除するのはparticipationなので今回は regular_event_participations起点で実装しています。

もしより良い書き方があればご意見いただけると嬉しいです 🙇‍♀️

Copy link
Copy Markdown
Contributor

@torinoko torinoko Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[IMO]
コメント漏れしてた!
ユーザーのデータが変更されたことで発生するイベントなので、それでよいと思います。
あと「開催中のイベントの参加者」を取得することが目的であれば、それを RegularEventParticipation に任せちゃうという手もあるのかなと思いました(スコープ名は適当)
これは User から遠い情報をなるべく隠蔽したいという思惑です。
それは User の関心事じゃないからね。

# RegularEventParticipation
scope :currently, -> { joins(:regular_event).merge(RegularEvent.holding) }

そもそも「現在参加中のイベントだけ消す仕様ってどうなんですか?イベントの状態関係なく全部消したらだめなことって何かありますか?」という話もありますが……
そしたらシンプルな実装になるのにね……

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yokomaru @torinoko
どこを起点にするかについては私の方でも後で検討しますが、取り急ぎ仕様について私の意見を書きます。

参加者から離脱するのを開催中のイベントに限定するという現状の仕様は良いと思います。
そもそも非アクティブなユーザーを定期イベントから離脱させたい理由は次のようなことかなと推測しています。

  • 既に活動していないユーザーがまだ活動しているかのように見えると紛らわしい
  • 退会の場合はネガティブな理由もあるので運営としても退会ユーザー自身としても見えやすいところに表示して欲しくない

終了した定期イベントであれば上記の懸念は該当しないので離脱させなくても良さそうです。
逆に終了した定期イベントからも離脱させるのは次の理由で避けた方が良いかなと思います。

  • 主催者からの離脱は開催中に限定することと一貫性がなく仕様がわかりにくい
  • 過去の参加履歴を削除するのはユーザーにとって都合が悪い場合がある
    • コミュニティでどういう活動をしていたかは他者からの評価につながるので残す方が良い(プロフィールから定期イベントの参加情報を確認できる機能を現在実装中)
    • 休会から復帰したときに終了済みイベントに参加していなかったことになるのは不自然
    • いなかったことになるのはかわいそうかも😢

あくまで私の意見なので気になるようならプロダクトオーナーと相談する方が良いとは思います。
(既に相談の上で今の仕様になったのかなと思いますが)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ryufuta
ありがとうございます!
開催中のイベントに限定する仕様はチーム開発MTG時に確認して決定しました!(参加の履歴のためなど概ねryufutaさんが書いてくださったことが理由となります!)
#9258 (comment)

@torinoko
上記仕様については問題ないでしょうか?🧐

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yokomaru
そうですね、 @ryufuta の書いているとおり、終わっているものについては残す方がよいですね。
ミーティングでも方針が決まっているのであれば問題ないです!

@yokomaru
Copy link
Copy Markdown
Contributor Author

yokomaru commented Mar 6, 2026

@ryufuta (CC: @torinoko )

お疲れ様です!
こちらのPR、お手隙でレビューをお願いできますでしょうか。急ぎではありません。
どうぞよろしくお願いいたします。

Copy link
Copy Markdown
Contributor

@torinoko torinoko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yokomaru
コメントしました!


class HibernationController < ApplicationController
skip_before_action :require_active_user_login, raise: false, only: %i[show]
before_action :set_holding_regular_events, only: %i[new create]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[IMO]
#set_holding_regular_events を実行するのが new と create のみなら、アクションに直接書いてもいいんじゃないのかな〜と思いました!(個人の感想です)
本当に個人の思想なのですが、基本的に Controller に限らずコールバックは避けたいです。
いつどのタイミングでどの処理が呼ばれるのか把握しづらいというのが一番の理由です。全体を把握するために視線の移動が発生するのはなるべく避けたい気持ちがあります。

if @regular_event.save
update_published_at
Organizer.create(user_id: current_user.id, regular_event_id: @regular_event.id)
@regular_event.regular_event_organizers.find_or_create_by(user: current_user)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[LGTM]
#find_or_create_by にしたのはよいですね!
オブジェクトを渡すのも Rails っぽくてよいですね〜

= form_with model: current_user, local: true, url: training_completion_path, method: :post, class: 'form' do |f|
.form__items
- if current_user.regular_events.any?
- if @holding_regular_events.any?
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[LGTM]
オォン
こういうところ他にもありそうですね〜

Comment on lines +27 to +23
| 研修を修了される場合、イベントの進行に影響がないよう、任意で他の参加者に主催を引き継ぐことを推奨します。
| 退会手続きを完了する前に、
| 以下のリンク先でイベント設定変更を行なってください。
ul
- current_user.regular_events.each do |event|
li
= link_to edit_regular_event_path(event), target: '_blank', rel: 'noopener' do
| 定期イベント「#{event.title}」の設定変更
= render 'shared/regular_events_warning',
events: @holding_regular_events,
case_text: '研修を終了される',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[LGTM]
こういう用語や表現を揃える、とても大事! 👍

discord_profile_regular_event_organizer_user2:
user: regular_event_organizer_user2
account_name:
times_url: No newline at end of file
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MUST]
最後に改行コード入れてほしい!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コメントありがとうございます!talkの方もなかったので合わせて対応いたしました。

21aca3f

Comment on lines +29 to +39
regular_event_participation8:
user: regular_event_organizer_user1
regular_event: regular_event42

regular_event_participation9:
user: regular_event_organizer_user1
regular_event: regular_event43

regular_event_participation10:
user: regular_event_organizer_user1
regular_event: regular_event44
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[NITS]
今回の RegularEvent 周り fixture はテストのためにいろいろ条件を揃えるため作成されている感じですよね。
であれば regular_event_participation8 のような連番ではなく、目的を表すラベルにした方がよいのかなと思いました。それだと他の fixture との整合性が取れないにゃ〜とかはあるかなとも思いますが、この慣習がそもそもよいとは思えないので。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コメントありがとうございます!

目的を表すラベルにした方がよい

こちら、自分も納得したのでできれば対応したいのですが、
どう命名したら管理しやすいのかわからなくなってしまったので、アドバイスいただけると幸いです!
以下のルールで検討しています。

  • (これは不要かもですが)何のデータかわかるようにprefixをつける
  • 中間テーブルなどは関連の命名に合わせる
  • 目的が同じだけど複数必要だから作ったデータは連番をつける

特に中間テーブルやテスト自体に必要はないけどデータを作るために必要なデータ(userでいうtalkやdiscord-profile、regular_eventでいうとregular_event_repeat_rules)の命名などが難しい&&長くなりがちで逆に読みづらくなっていないだろうか?と不安になりました。

管理のための良い命名アイデアがあればご意見いただきたいです🙏

  • test/fixtures/users.yml
user_regular_event_organizer_1:
user_regular_event_organizer_2:
  • test/fixtures/talks.yml
talk_regular_event_organizer_user_1:
  user: regular_event_organizer_user_1

talk_regular_event_organizer_user_2:
  user: regular_event_organizer_user_2
  • test/fixtures/discord_profiles.yml
discord_profile_regular_event_organizer_user_1:
  user: regular_event_organizer_user_1

discord_profile_regular_event_organizer_user_2:
  user: regular_event_organizer_user_2
  • test/fixtures/regular_events.yml
regular_event_holding_and_one_organizer:
  title: 開催中で主催者が1人の定期イベント
  user: regular_event_organizer_user_1

regular_event_holding_and_multiple_organizers:
  title: 開催中で主催者が複数人の定期イベント
  user: regular_event_organizer_user_1

regular_event_finished_and_one_organizer:
  title: 終了済みで主催者が1人の定期イベント
  user: regular_event_organizer_user_1
  • test/fixtures/regular_event_repeat_rules.yml
repeat_rule_holding_and_one_organizer:
  regular_event: holding_and_one_organizer

repeat_rule_holding_and_multiple_organizers:
  regular_event: holding_and_multiple_organizers

repeat_rule_finished_and_one_organizer:
  regular_event: finished_and_one_organizer
  • test/fixtures/regular_event_participations.yml
participation_holding_and_one_organizer:
  user: regular_event_organizer_user_1
  regular_event: holding_and_one_organizer

participation_holding_and_multiple_organizers:
  user: regular_event_organizer_user_1
  regular_event: holding_and_multiple_organizers

participation_finished_and_one_organizer:
  user: regular_event_organizer_user_1
  regular_event: finished_and_one_organizer
  • test/fixtures/organizers.yml
organizer_holding_and_one_organizer:
  user: regular_event_organizer_user_1
  regular_event: holding_and_one_organizer

organizer_holding_and_multiple_organizer_1:
  user: regular_event_organizer_user_1
  regular_event: holding_and_multiple_organizers

organizer_holding_and_multiple_organizers_2:
  user: regular_event_organizer_user_2
  regular_event: holding_and_multiple_organizers

organizer_finished_and_one_organizer:
  user: regular_event_organizer_user_1
  regular_event: finished_and_one_organizer

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

以下、個人の見解です〜

  • test/fixtures/users.yml

    • user_regular_event_organizer_1 でよさそう
    • あとは regular_event_organize_user1 とか?
      • user を suffix にすることで用途を先に表す形になる
      • と思ったけどアソシエーション見たら元々 suffix にする案だったのかな?
  • test/fixtures/talks.yml

    • ちょっと把握しきれていないのだけど、これって必要なんですか?

ただこれ実はテストケースの分だけ fixture を用意するべきなのか?というお話もあるんですよね。
RSpec だったらやらないかな〜。
Minitest はあまり書いたことないのでわからないけど、たぶん普通だったらここまで fixture が膨らむことはないはず。テストの分だけ fixture を定義するというのがそもそも健全ではないんですよね。たぶんね。

と、思ってコード見直してて今気づいたんですけど、これ RegularEvent 以外のテストで行うことはちょっと違うかも。
RegularEvent の主催者がひとりかどうかをチェックして管理者削除なりイベント終了なり行っています。なのでそこの責務は RegularEvent が負っているので RegularEvent 側でテストする、User 側でテストするとしたら RegularEvent のイベントがちゃんと呼ばれていることな気がする。
Minitest::Mock とか使うのかな。

以下テストコードの方でもコメントしますね。

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
app/controllers/training_completion_controller.rb (1)

29-29: 任意: 重複クエリの抽出を検討

Line 10 と Line 29 で RegularEvent.organizer_event(current_user).holding が重複しています。コミットメッセージにて「アクション内に直接書いた方が処理追跡がしやすい」という意図を理解していますが、before_action を使わずにプライベートメソッドへの抽出という選択肢もあります。

♻️ 提案(任意)
  def new
-   `@holding_regular_events` = RegularEvent.organizer_event(current_user).holding
+   `@holding_regular_events` = holding_regular_events_for_current_user
  end

  # ... in create failure path ...
-     `@holding_regular_events` = RegularEvent.organizer_event(current_user).holding
+     `@holding_regular_events` = holding_regular_events_for_current_user

  private

+ def holding_regular_events_for_current_user
+   RegularEvent.organizer_event(current_user).holding
+ end

現状でも可読性は十分なので、このまま進めても問題ありません。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/controllers/training_completion_controller.rb` at line 29, Duplicate
query RegularEvent.organizer_event(current_user).holding is used twice
(assigning `@holding_regular_events`); extract it into a single private helper
method (e.g., holding_regular_events_for(user)) and call that method from both
places instead of repeating the query, updating assignments to use
holding_regular_events_for(current_user) and leaving behavior unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@app/controllers/training_completion_controller.rb`:
- Line 29: Duplicate query RegularEvent.organizer_event(current_user).holding is
used twice (assigning `@holding_regular_events`); extract it into a single private
helper method (e.g., holding_regular_events_for(user)) and call that method from
both places instead of repeating the query, updating assignments to use
holding_regular_events_for(current_user) and leaving behavior unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: cc0ce7cb-23bd-44d7-83df-f6d2392f3879

📥 Commits

Reviewing files that changed from the base of the PR and between fad4ea5 and 9484c07.

📒 Files selected for processing (5)
  • app/controllers/hibernation_controller.rb
  • app/controllers/retirement_controller.rb
  • app/controllers/training_completion_controller.rb
  • test/fixtures/discord_profiles.yml
  • test/fixtures/talks.yml
🚧 Files skipped from review as they are similar to previous changes (3)
  • app/controllers/retirement_controller.rb
  • test/fixtures/discord_profiles.yml
  • app/controllers/hibernation_controller.rb

@yokomaru yokomaru force-pushed the feature/close-regular-event-when-no-organizers branch from 9484c07 to c4b7111 Compare March 10, 2026 11:34
torinoko
torinoko previously approved these changes Mar 13, 2026
Copy link
Copy Markdown
Contributor

@torinoko torinoko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yokomaru
コメントしましま!
よいと思います〜
丁寧にご対応いただきありがとうございました!

Comment on lines +102 to +103
user = users(:kimura)
regular_event = regular_events(:regular_event5) # kimuraが1人で主催しているイベント
Copy link
Copy Markdown
Contributor

@torinoko torinoko Mar 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[NITS]
ここはコメントを書くでもよいし、以下のように明示しちゃうという手もありますね。
そうすることで fixture に変更が入れられてもこのテストは壊れません。

Suggested change
user = users(:kimura)
regular_event = regular_events(:regular_event5) # kimuraが1人で主催しているイベント
ruser = users(:kimura)
regular_event = regular_events(:regular_event1)
regular_event.regular_event_organizers.destroy_all
regular_event.regular_event_organizers.create(user: user)

でもこれだと可読性が著しく落ちるんですよね。
いや destroy_all するのがお行儀よくないし、organizer がいないイベントの fixture が用意されていれば destroy_all なんてしなくて済むし、そもそもモデルの設計として(以下略)
なので今回は kimura がひとりで主催している regular_event5 を使うでよいと思います!

ちなみにこれも RSpec だともっと簡易に書けるはず……
Minitest でもできるのかもだけどわかりませんでした……

create(:regular_event, regular_event_organizers:  [ create(:kimura) ])

Comment on lines +114 to +115
regular_event = regular_events(:regular_event43)
user = users(:regular_event_organizer_user1)
Copy link
Copy Markdown
Contributor

@torinoko torinoko Mar 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ご対応ありがとうございます!


def close_or_destroy_organizer(user)
if regular_event_organizers.count == 1
update(finished: true)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updateが失敗したときfalseを返しますが、無視しているので失敗に気づけないのは本来であれば良くないかなと思います。
普通ならupdate!として失敗時に例外を投げれば良いのですが、ここの大元の呼び出し元である退会/休会処理が適切に例外処理をしていないように見えるので、今はこのままにしておくのが安全と思います。
organizer.destroyについても同様です。

本PRのスコープ外ですが、退会/休会処理のもとからあるコードが適切でないという点について一応補足。
この処理には

  1. ユーザーのステータス変更
  2. サブスクリプション解約
  3. 定期イベントの後始末を含むその他の些末な処理

があります。
現状では1,2が先に実行されて成功し、3で失敗した場合ここで例外を投げるとユーザーには500エラーページが表示されるだけなので、何が起きたのか、1,2は成功したのかわからない状態になると理解しています。
3はユーザーにとっては不要で管理者が運用でカバーできるため、適切な例外処理がされていない現状では3で例外を発生させないのが良いと思います。
呼び出し元が複数あってそれぞれの書き方もバラバラなので修正するのは難儀しそうです。

@ryufuta
Copy link
Copy Markdown
Contributor

ryufuta commented Mar 13, 2026

@yokomaru
既存の退会/休会/研修終了のコードを改善しないと、本PRで実装した機能だけ綺麗にするのは限界があるなあという印象を持ちました。
(あとfixturesも改善が必要)
現状でできる範囲のことは出来たかなと思うのでApproveします👍
お疲れ様でした。
後のことは今後の課題として必要なら別のメンバーが対応すれば良いと思います。

以下はこうすればもっと改善できるかもしれないというフワッとした私の考えです。
見当違いかもしれず、本PRのスコープを大きく外れる内容も含むので対応不要です。
ご参考までにといった温度感です。

定期イベントの後始末処理をUser起点でなくRegularEvent起点にする

現状ではUser起点にしていますが、RegularEvent起点にした方がいくらか処理がスッキリするかもしれません。
User#clean_up_regular_eventsを削除して代わりにRegularEventのクラスメソッドを定義して退会/休会/研修終了からRegularEvent.clean_up(user)みたいに呼ぶという案です。
退会/休会/研修終了のコードを整理するともっと適切な案が出て来ると思うので今修正する必要はないです。

現状ではUserモデルとRegularEventモデルに処理が分散してわかりにくいです。
定期イベントのことはRegularEventモデルに全て任せる方がわかりやすいと個人的には思いました。
特にRegularEvent#close_or_destroy_organizerUser#clean_up_regular_eventsからしか呼ばれない想定なのに別モデルに分離されているのが気になります。
この分離には次の問題があると思います。

  • それぞれのメソッドで似たようなテストを書く必要がある
    • RegularEventモデルに統一すればRegularEvent#close_or_destroy_organizerに相当する処理はプライベートメソッドにできるのでテスト不要になる
  • わかりにくい想定をおくことになる

退会/休会/研修終了を整理して定期イベントの後始末処理を見直す

退会/休会/研修終了はアクティブユーザーが非アクティブユーザーに変化するという意味で共通のイベントですが、現状は次のようにバラバラの実装になっていて共通の処理を書きにくくなっています。

  • 休会処理は履歴を管理する必要があるのでActiveRecordモデルで実装
  • 退会処理はPOROで実装
  • 研修終了処理はコントローラにビジネスロジックを書いている

Rails的な設計に従って全てイベント型モデルとして実装すると見通しが良くなりそうです。

  • 退会と研修終了の処理は履歴管理不要なのでActiveModelを使って実装しつつ休会(Hibernation)モデルに実装を寄せる
  • 後始末処理の起点をUserモデルではなくこれらイベント型モデルにする
  • 後始末的な意味のモジュールを作ってそれぞれにincludeするか共通のコールバック処理を書く

みたいな感じになるのかなあと。

ryufuta
ryufuta previously approved these changes Mar 13, 2026
@yokomaru
Copy link
Copy Markdown
Contributor Author

yokomaru commented Mar 16, 2026

@ryufuta
コメントありがとうございます!既存の処理についてなど色々調査してくださり本当にありがとうございます🙌

定期イベントの後始末処理をUser起点でなくRegularEvent起点にする

こちらについてもありがとうございます!
自分もどちらを起点にするべきかとても悩ましい気持ちだったのですが、RegularEvent側に置く場合、RegularEvent側がUserの関連構造を知りすぎる(user.regular_event_participations.for_holding_events.destroy_allなど)ことになってしまうのかな?と思い、今回はUser側に置きました。

ただ、おっしゃる通りテストが冗長になってしまうという点や、そもそもRails的には関連をたどって処理を書くのは普通なのと、regular_event_participationsなどはRegularEventを介した関連なのでそこまで違和感はないかな?とも思いはじめ、(今修正する必要はないかもしれないのですが)ここら辺についてもしご意見あればお伺いしたいです!🙏

@ryufuta
Copy link
Copy Markdown
Contributor

ryufuta commented Mar 17, 2026

@yokomaru
RegularEvent起点にする案は実際試してみたところ私が想像していたほど上手くいきませんでした🙇🏻‍♂️
なので現状のままで良いと思います。

以下は実装例です。
(きちんとテストしていないですが、退会時の処理は仕様通り動作しました。)

class RegularEvent < ApplicationRecord
  # ...

  def self.clean_up(user)
    RegularEventParticipation.where(user:, regular_event: holding).destroy_all
    events = holding.joins(:regular_event_organizers).where(regular_event_organizers: { user_id: user.id })
    events.each do |event|
      event.close_or_destroy_organizer(user)
    end
  end
# ...
end

参加者の離脱処理だけならばUser(もしくはRegularEventParticipation)を起点にする方が自然ですね。
主催者の処理の方は定期イベントを終了させるパターンがあるので、RegularEventに全ての処理を書くのが自然かなと思ったのですが、テストが冗長になる問題は解決できませんでした。
よく考えたらクラスメソッドからはプライベートなインスタンスメソッドを呼べないのでRegularEvent#close_or_destroy_organizerはパブリックにせざるを得ないです。
このメソッドを削除してRegularEvent.clean_upに直接処理を書いてしまえばテストが冗長になる問題は解決しますが、可読性が落ちるので今一つです。

@ryufuta
Copy link
Copy Markdown
Contributor

ryufuta commented Mar 17, 2026

@yokomaru
一点漏れていました。
以下は仕様変更に伴って不要になったかと思います。

DEFAULT_REGULAR_EVENT_ORGANIZER = 'komagata'

yokomaru added 11 commits March 18, 2026 15:22
- 参加・主催の離脱処理は現状どの動線からも必ず2つ一緒に呼ばれているため、`clean_up_regular_events`としてまとめた
- 参加・主催ともに離脱するのは開催中(holding)のイベントのみとした
- 離脱時に主催が1人の場合はkomagataに付け替えるではなくイベントを終了するように変更した
- 主催者が1人のイベントは自動的に終了となる旨を追記した
- 退会/休会/研修終了動線でほぼ同じ文言なためパーシャルへ切り出した
- slimファイル内で開催中のイベントを取得していたため、controllerで取得するように修正した
- 研修終了動線のバグと文言修正
- ローカル環境にて主催者にイベント作成者であるユーザーを登録してイベント作成をすると500エラーになり検証や調査の妨げになるため、すでに作成中の場合は作成処理を行わないように修正
- 併せてよりRailsっぽい書き方にするために、外部キー指定ではなく関連を起点にオブジェクト指定で更新するように修正した
- 実行するアクションがnewとcreate のみなのでアクションに直接書いた方が処理が追いやすいため
- Fixturesを新たに作成せず、テスト内で明示的にデータを作成してその結果を確認するようにした
- UserではなくParticipationの関心事として開催中のイベントに紐づく参加情報を取得できるようにした
@yokomaru yokomaru force-pushed the feature/close-regular-event-when-no-organizers branch from c0f92ca to 3cbee86 Compare March 18, 2026 06:24
@yokomaru yokomaru force-pushed the feature/close-regular-event-when-no-organizers branch from 3cbee86 to d5eed00 Compare March 18, 2026 07:06
@yokomaru
Copy link
Copy Markdown
Contributor Author

@ryufuta

RegularEvent起点での検討、ありがとうございます!
クラスを跨った処理をおく場所、本当に悩ましいですね・・!
退会/休会/研修終了を整理して定期イベントの後始末処理を見直す
で書いていただいたように
最終的には別のモジュールにするのが綺麗な気がしつつ、そこまでやるのか的な部分もあると思うのでどこに責務を置くのか本当に難しいなと改めて思いました。

定数についてもありがとうございます!!こちら念の為grepして削除いたしました。
丁寧にご確認いただきありがとうございましたー!

@yokomaru
Copy link
Copy Markdown
Contributor Author

@okuramasafumi
お疲れ様です。
チームレビューが終わったので、メンターレビューをお願いいたします!🙏

wants_announcement? && !wip?
end

def close_or_destroy_organizer(user)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

実装は問題ないと思うのですが、ここの意図としては、

  • 休会や退会にともなって、定期イベントから主催者を削除したい
  • しかし、主催者不在の定期イベントは存在できない
  • 従って、削除される主催者が唯一の主催者である場合、定期イベントを終了状態にする

という感じだと思います。このうち、「主催者不在の定期イベントは存在できない」要件は今回に限った話ではなさそうであり、モデルのバリデーションの一部になるのが本来の姿のように思います。
そうすると、主催者の削除と、イベントの状態確認・終了状態への遷移は異なるレイヤーの出来事として扱いたくなります。主催者を削除した結果、結果的に定期イベントが不正な状態になり、終了状態になる、という流れです。
しかし、不正な状態(バリデーションが通らない状態)なら自動的に終了状態になる、となると影響範囲が大きく、望んでいない挙動になってしまうでしょう。
なので、メソッドとしては、

# これは擬似コードです
event.remove_organizer
if event.invalid? # 主催者がいない!
  event.finished!
end
event.save

のような感じにして、バリデーションとして、

# これは擬似コードです
validate do
  errors.add if organizers.size.zero? && active # activeというのは、終了状態ではない、くらいの意味
end

みたいにしてあげると綺麗かな、という感じです。

ここまでやると少し大掛かりなので、今現状では、メソッドの先頭にコメントを書いておくだけでもよいと思います。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@okuramasafumi
流れ的にはそれが自然ではありますね〜。
今回何でこういう状態になっているかというと、元々の仕様がもっとややこしくて。
その仕様をかなり整理して、今の状態になっています。
なので上記変更は別タスクで対応させていただければと思うのですがいかがでしょうか 🙏

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@okuramasafumi
ご指摘ありがとうございます!

本来はバリデーションで担保すべきという点や主催者の削除とイベントの状態確認・終了状態への遷移を異なるレイヤーの出来事として扱うという考え方につきまして、とても勉強になりました。
この2つが分離できると責務も明確になり設計としてもスッキリするなと感じました!

今回は影響範囲も考慮し、まずはコメントで意図を補足する形にしようと思います。

残すべきコメントとしては、メソッドの意図(主催者が0人にならないようにするための処理)+TODOとして将来的にバリデーションで担保することを記載するイメージで問題ないでしょうか?

# 定期イベントは主催者が1人以上必要なため
# 主催者が1人しかいない場合はイベントを終了状態にし
# それ以外の場合は主催者のみを削除する
#
# TODO: 本来は「主催者が0人のイベントは無効」という制約をバリデーションで担保する形にしたい
# https://github.com/fjordllc/bootcamp/pull/9732#discussion_r2969273381
def close_or_destroy_organizer(user)

@torinoko
たださんもコメントいただきありがとうございます!Issue化もしておきます。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yokomaru コメントそんな感じでよいと思います!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@okuramasafumi
ありがとうございます!
こちらコメント追加いたしました。

5113131

@yokomaru yokomaru dismissed stale reviews from ryufuta and torinoko via d5eed00 March 27, 2026 16:53
@torinoko torinoko requested a review from okuramasafumi March 30, 2026 03:28
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 1, 2026

🚀 Review App

URL: https://bootcamp-pr-9732-fvlfu45apq-an.a.run.app

Basic認証: fjord / (ステージングと同じ)
PR更新時に自動で再デプロイされます。

@yokomaru
Copy link
Copy Markdown
Contributor Author

yokomaru commented Apr 1, 2026

@okuramasafumi

お疲れ様です。
お手隙で再レビューをお願いいたします!🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants