Skip to content

ペアワークのペアもしくは日程変更時の通知とペア取り消し機能を実装#9812

Open
s-tone-gs wants to merge 22 commits intomainfrom
feature/add-notifications-and-cancellation
Open

ペアワークのペアもしくは日程変更時の通知とペア取り消し機能を実装#9812
s-tone-gs wants to merge 22 commits intomainfrom
feature/add-notifications-and-cancellation

Conversation

@s-tone-gs
Copy link
Copy Markdown
Contributor

@s-tone-gs s-tone-gs commented Mar 19, 2026

Issue

概要

ペアワークに以下の機能を追加しました。

  • ペア変更時の通知機能
  • 日程変更時の通知機能
  • ペア取り消し機能
    • ペア取り消し時の通知機能
    • 退会時に自分がバディの未開催のペアワークのペアを解消する
  • カレンダーの変更
    • ペアが確定したペアワークにメンターがアクセスした際、カレンダーにペアワークの確定日を表示させる
    • ペアが確定したペアワークにメンター以外がアクセスした際、確定日のみをテキストで表示させる
    • 終了したペアワークには「終了しました」と表示する
    • デザインを変更
  • ペアが未確定のペアワークの希望日変更機能

変更確認方法

1. カレンダーの変更を確認

  1. feature/add-notifications-and-cancellationをローカルに取り込む
  2. 開発サーバーを起動する
  3. kimuraでログインする
  • kimura
  • testtest
  1. http://localhost:3000/pair_works/290131064 にアクセス
  2. 希望日時の欄に「開催日時は3月26日(木)23:00に確定しました。」という文言が表示されていることを確認する
  3. メンターでログインする
  • mentormentaro
  • testtest
  1. http://localhost:3000/pair_works/290131064 にアクセス
  2. 「ペアを希望する」で確定日が記載されたボタンを押し、カレンダーを表示させる
  3. カレンダーの確定日の欄に「確定」という文言が表示されていることを確認する
  4. http://localhost:3000/pair_works/254777815 にアクセス
  5. 「ペアワークは終了しました」という文言が表示されていることを確認する

2. ペア変更時にペアワーク作成者と変更前のペアに通知が来ることの確認

  1. kimuraでログインする
  2. http://localhost:3000/pair_works/new にアクセスし、新しいペアワークを作成する
  3. メンターでログインする
  • mentormentaro
  • testtest
  1. http://localhost:3000/pair_works?target=not_solved から手順2で作成したペアワークを開く
  2. 希望日を選び、ペアワークを申し込む
  3. 別のメンターでログインする
  • komagata
  • testtest
  1. http://localhost:3000/pair_works?target=solved から手順2で作成したペアワークを開く
  2. 「ペア変更、自分がペアになる」ボタンを押し、希望日のカレンダーを出現させる
  3. 希望日を選んでペアワークを申し込む
  4. kimuraでログインし、ペア変更のアプリ内通知が届いていることを確認する
  5. mentormentaroでログインし、ペア変更のアプリ内通知が届いていることを確認する
  6. http://localhost:3000/letter_opener を開き、kimuraとmentormentaro宛にメール通知が届いていることを確認する。

3. 日程変更時にペアワーク作成者に通知が来ることの確認

  1. kimuraでログインする
  2. http://localhost:3000/pair_works/new にアクセスし、新しいペアワークを作成する(この時希望日時は二つ以上選択する)
  3. komagata でログインする
  4. http://localhost:3000/pair_works?target=not_solved にアクセスし、手順2で作成したペアワークを開く
  5. 希望日を選択し、ペアワークを申し込む
  6. 「日時の変更、ペアのキャンセル」ボタンを押し、希望日のカレンダーを出現させる
  7. 現在の確定日とは別日でペアワークを申し込む
  8. kimuraでログインし、日程変更のアプリ内通知が届いていることを確認する。
  9. http://localhost:3000/letter_opener を開き、kimura宛にメール通知が届いていることを確認する。

4. ペア取り消し機能と通知がペアワーク作成者と各メンターに届いていることの確認

  1. kimuraでログインする
  2. http://localhost:3000/pair_works/new にアクセスし、新しいペアワークを作成する(この時希望日時は二つ以上選択する)
  3. komagata でログインする
  4. http://localhost:3000/pair_works?target=solved にアクセスし、手順2で作成したペアワークを開く
  5. 希望日を選択し、ペアワークを申し込む
  6. 「日時の変更、ペアのキャンセル」ボタンを押し、「ペア確定を取り消す」ボタンを出現させる
  7. 「ペア確定を取り消す」ボタンを押しペア確定を取り消す
  8. ペア確定が取り消されていることを確認する(ペアワークのタイトル横に表示されるステータスが「募集中」になっている)
  9. kimuraでログインし、ペア取り消しのアプリ内通知が届いていることを確認する
  10. mentormentaroでログインし、ペア取り消しのアプリ内通知が届いていることを確認する
  11. http://localhost:3000/letter_opener を開き、kimuraと各メンター宛(new-mentor、pjord、komagata、unadmentor、machidanohimitsu、mentormentaro)にメール通知が届いていることを確認する。

5 退会時に自分がバディの未開催のペアワークのペアが解消されることの確認

  1. kimuraでログインする
  2. http://localhost:3000/pair_works/new にアクセスし、新しいペアワークを作成する(この時希望日時は二つ以上選択する)
  3. mentormentaro でログインする
  4. . http://localhost:3000/pair_works?target=not_solved にアクセスし、手順2で作成したペアワークを開く
  5. 希望日を選び、ペアワークを申し込む
  6. http://localhost:3000/retirement/new にアクセスし、退会する
  7. kimuraでログイン
  8. http://localhost:3000/pair_works?target=not_solved にアクセスし、手順2で作成したペアワークのペアがk死傷されていることを確認する(ステータスが「募集中」になっている)
  9. ペア取り消しのアプリ内通知が届いていることを確認する
  10. komagataでログインし、ペア取り消しのアプリ内通知が届いていることを確認する
  11. http://localhost:3000/letter_opener を開き、kimuraと各メンター宛(new-mentor、pjord、komagata、unadmentor、machidanohimitsu、mentormentaro)にメール通知が届いていることを確認する。

6.ペアが未確定のペアワークの希望日変更

  1. kimuraでログイン
  2. http://localhost:3000/pair_works/new にアクセスし、 新しいペアワークを作成する。
  3. ペアワークを開き、「希望日時変更」のボタンを押す
  4. 希望日時を選択し、「更新」のボタンを押す
  5. 希望日時が更新されていることを確認する

Screenshot

1. カレンダーの変更

変更前

スクリーンショット 2026-03-26 103203

変更後

メンターでアクセス
スクリーンショット 2026-03-26 103405
メンター以外でアクセス
image
終了したペアワーク
image

2. ペア変更の通知

変更後

アプリ内
image
メール
image

3, 日程変更の通知

変更後

アプリ内
image
メール
image

4. ペア取り消し機能

変更前

development._._.FBC.-.Google.Chrome.2026-03-26.10-38-04.mp4

変更後

development._._.FBC.-.Google.Chrome.2026-03-26.10-40-07.mp4
アプリ内通知
image
メール
image
バディが退会した時
development._._.FBC.-.Google.Chrome.2026-03-26.10-45-14.mp4

5. ペア未確定のペアワークの希望日変更

変更後

development._.q._.FBC.-.Google.Chrome.2026-03-26.16-55-28.mp4

Summary by CodeRabbit

  • 新機能

    • ペア確定をキャンセルできる操作と予約の更新機能を追加
  • 通知

    • 再割当(rematch)、日程変更(reschedule)、取消(cancel)に関するメールとアクティビティ通知を追加
  • UI

    • カレンダー上で「確定」表示、終了表示(「ペアワークは終了しました。」)および取消の確認ダイアログを導入
  • テスト

    • 通知/メール/システム/モデル周りのテストを拡充

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 19, 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

ペアワークの予約更新(再スケジュール/リマッチ)、予約キャンセル、対応する通知経路(ActivityNotifier/ActivityMailer/ActiveSupport::Notifications)の追加、退職時のアンマッチ処理、関連するビュー・スタイル・ルーティング・テストの拡張を行いました。

Changes

Cohort / File(s) Summary
予約コントローラー
app/controllers/pair_works/reservations_controller.rb
update を追加(reserve → 成功時に pair_work.reschedule/pair_work.rematch を instrument)。destroy を実装して unmatchpair_work.cancel を instrument。失敗時は pair_works/show を render。
モデル/通知ノーティファイア
app/models/pair_work.rb, app/models/pair_work_rematching_notifier.rb, app/models/pair_work_reschedule_notifier.rb, app/models/pair_work_cancel_notifier.rb
PairWork#unmatch 追加。3つの Notifier クラスを追加し、イベント受信→ActivityDelivery を発行するロジックを実装。
通知定義・配信・メール・ビュー
app/models/notification.rb, app/notifiers/activity_notifier.rb, app/mailers/activity_mailer.rb, app/views/activity_mailer/*.html.slim, test/mailers/*, test/mailers/previews/*
Notification enum に rematching_pair_work/reschedule_pair_work/cancel_pair_work 追加。ActivityNotifier/ActivityMailer に対応メソッドとテンプレート・プレビュー・テストを追加。
退職フロー
app/models/retirement.rb, test/system/retirements/*, test/models/retirement_test.rb
Retirement.execute に unmatch_pair_works ステップを追加し、退職時に該当ペアワークを unmatch して pair_work.cancel を instrument。関連テストを追加/更新。
ビュー・フォーム・スケジュール UI
app/views/pair_works/_body.html.slim, app/views/pair_works/_schedule.html.slim, app/views/pair_works/_form.html.slim, app/views/pair_works/index.html.slim
確定/終了表示、PATCH/DELETE ボタン・確認ダイアログ、スケジュール選択の disabled/confirmed 状態、終了スケジュールトグル等を追加・調整。
ルーティング・通知配線
config/routes.rb, config/initializers/active_support_notifications.rb
reservationsupdate ルートを追加。pair_work.rematch/pair_work.reschedule/pair_work.cancel の ActiveSupport::Notifications 購読を追加し各 Notifier に紐付け。
スタイル
app/assets/stylesheets/...
スケジュール終了トグル用の表示切替と disabled チェック用スタイルを追加。
テスト・フィクスチャ
test/..., test/fixtures/*, db/fixtures/*
メール/配信/モデル/システムテストを多数追加。新 fixtures(pair_work4, schedules 等)を追加。

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant UI as UI / Views
    participant Controller as ReservationsController
    participant PairWork as PairWork (Model)
    participant ASN as ActiveSupport::Notifications
    participant Notifier as PairWork*Notifier
    participant ActivityDelivery as ActivityDelivery
    participant Mailer as ActivityMailer

    User->>UI: PATCH or DELETE /pair_works/:pair_work_id/reservations
    UI->>Controller: request
    Controller->>PairWork: reserve(params) or unmatch()
    alt reserve/unmatch success
        Controller->>ASN: instrument (pair_work.rematch / pair_work.reschedule / pair_work.cancel, payload)
        ASN->>Notifier: call(_name,..., payload)
        Notifier->>ActivityDelivery: with(pair_work, receiver).notify(:kind)
        ActivityDelivery->>Mailer: deliver/enqueue email
        Mailer-->>User: notification email
        Controller-->>UI: redirect (notice)
    else failure
        Controller-->>UI: render pair_works/show (errors)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 分

Possibly related issues

Possibly related PRs

Suggested reviewers

  • machida
  • komagata

Poem

🐰 ピョン、予約が変わって日が巡り、
メールがふわりと届けば嬉しい知らせ、
キャンセルもリマッチもぴょんと決まり、
テストは光り、ビューは整い、
うさぎはぴょんと跳ねて祝う。

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.03% 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 プルリクエストの説明は、リポジトリの説明テンプレートに従う必要がある形式と比較して、大部分は完成しているが、構成が異なっている。

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/add-notifications-and-cancellation

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.

@s-tone-gs s-tone-gs force-pushed the feature/add-notifications-and-cancellation branch from 9af4a4c to 9fb9fb1 Compare March 19, 2026 08:41
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: 8

🧹 Nitpick comments (5)
app/models/pair_work.rb (1)

89-91: メッセージの句読点の一貫性を確認してください。

cancel のメッセージには句点(。)がありませんが、他のメッセージ(createupdatedestroyreserve)には句点があります。

♻️ 修正案
      reserve: 'ペアが確定しました。',
-     cancel: 'ペア確定を取り消しました'
+     cancel: 'ペア確定を取り消しました。'
    }[action_name]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/models/pair_work.rb` around lines 89 - 91, The cancel message in the
action_name mapping in PairWork (the hash that returns messages for keys like
create, update, destroy, reserve, cancel) is missing the Japanese full stop;
update the 'cancel' value to include the trailing '。' so it matches the
punctuation used by create, update, destroy, and reserve and maintains
consistency across PairWork messages.
app/models/pair_work_cancel_notifier.rb (1)

17-22: notify_to_chat の文面が cancel 用になっていません。

現状 call からも使われていませんが、Line 19 が「決定しました」のままだと、後で呼ぶようにした際に cancel 通知と逆の内容を送ってしまいます。使う予定がないなら削除、使うなら取消文面へ直しておきたいです。

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

In `@app/models/pair_work_cancel_notifier.rb` around lines 17 - 22, The
notify_to_chat method in PairWorkCancelNotifier currently sends a "matching pair
decided" message which is incorrect for cancel notifications; either remove the
unused notify_to_chat method if it will never be used, or update its text to a
cancel-specific message: change the ChatNotifier.message payload in
notify_to_chat to indicate the pair matching was cancelled (e.g.,
"マッチングペアが取消されました") and keep the existing pair_work_url link for context so
future callers (including call) will send a cancel notification rather than a
confirmation.
test/system/pair_works_test.rb (1)

128-147: この 2 件は時刻を固定した方が安定します。

pair_work4 の確定日時は 2025-01-02 01:00:00 ですが、現在は 2026-03-19 です。travel_to なしだと、過去日の扱い変更で cancel pair confirmationshow confirmed date on calendar が無関係に壊れやすいので、2025-01-01 前後に固定しておくと意図した挙動だけを検証できます。

修正イメージ
   test 'cancel pair confirmation' do
-    pair_work = pair_works(:pair_work4)
-    visit_with_auth pair_work_path(pair_work), 'komagata'
-    find("label[for='show-schedule-dates']").click
-    accept_alert do
-      click_button 'ペア確定を取り消す'
+    travel_to Time.zone.local(2025, 1, 1, 0, 0, 0) do
+      pair_work = pair_works(:pair_work4)
+      visit_with_auth pair_work_path(pair_work), 'komagata'
+      find("label[for='show-schedule-dates']").click
+      accept_alert do
+        click_button 'ペア確定を取り消す'
+      end
+
+      assert_text 'ペア確定を取り消しました'
     end
-
-    assert_text 'ペア確定を取り消しました'
   end

   test 'show confirmed date on calendar' do
-    pair_work = pair_works(:pair_work4)
-    visit_with_auth pair_work_path(pair_work), 'kimura'
-    assert_selector "[id='2025-01-02T01:00:00+09:00']", text: '確定'
-
-    visit_with_auth pair_work_path(pair_work), 'komagata'
-    find("label[for='show-schedule-dates']").click
-    assert_selector "[id='2025-01-02T01:00:00+09:00']", text: '確定'
+    travel_to Time.zone.local(2025, 1, 1, 0, 0, 0) do
+      pair_work = pair_works(:pair_work4)
+      visit_with_auth pair_work_path(pair_work), 'kimura'
+      assert_selector "[id='2025-01-02T01:00:00+09:00']", text: '確定'
+
+      visit_with_auth pair_work_path(pair_work), 'komagata'
+      find("label[for='show-schedule-dates']").click
+      assert_selector "[id='2025-01-02T01:00:00+09:00']", text: '確定'
+    end
   end
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/system/pair_works_test.rb` around lines 128 - 147, Tests "cancel pair
confirmation" and "show confirmed date on calendar" depend on a past confirmed
datetime (pair_works(:pair_work4) = 2025-01-02 01:00:00) so make them
deterministic by fixing the current time with travel_to; wrap the
actions/assertions in the tests named 'cancel pair confirmation' and 'show
confirmed date on calendar' (or wrap the entire test bodies) with a travel_to
set to a date around 2025-01-01/02 so the confirmed datetime is treated as
intended, then ensure you return time to normal (using travel_back or the
travel_to block) and keep references to pair_works(:pair_work4),
visit_with_auth, and the calendar selector id (2025-01-02T01:00:00+09:00) when
applying the change.
app/models/pair_work_rematching_notifier.rb (1)

17-22: 未使用のプライベートメソッド notify_to_chat

このメソッドは定義されていますが、現在のcallフローでは呼び出されていません。また、メッセージ内容「マッチングペアが決定しました」はrematchingではなくmatchingの文脈のようです。

将来的にチャット通知を追加する予定がない場合は、このコードを削除することを検討してください。追加予定の場合は、メッセージ内容を「ペアが変更されました」などに修正する必要があります。

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

In `@app/models/pair_work_rematching_notifier.rb` around lines 17 - 22, The
private method notify_to_chat is unused and its message wording refers to an
initial matching rather than a rematching; either remove the unused method or
update and wire it into the call flow: if you intend to keep chat notifications,
rename or change the message in notify_to_chat to reflect a rematch (e.g.,
"ペアが変更されました"), and invoke notify_to_chat from the call method where rematching
is handled so the notifier runs; otherwise delete the notify_to_chat method to
avoid dead code (referencing notify_to_chat and the call method to locate the
change).
app/models/pair_work_reschedule_notifier.rb (1)

17-22: 未使用のプライベートメソッド notify_to_chat

PairWorkRematchingNotifierと同様に、このメソッドは未使用です。メッセージ内容も日程変更の文脈と一致していません(「マッチングペアが決定しました」)。

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

In `@app/models/pair_work_reschedule_notifier.rb` around lines 17 - 22, The
private method notify_to_chat in PairWorkRescheduleNotifier is unused and its
message text ("マッチングペアが決定しました") is incorrect for a reschedule notifier; either
remove the unused notify_to_chat method or repurpose it by changing its message
to reflect a rescheduled meeting and ensure it is invoked from the class's
public notification flow (e.g., call notify_to_chat from the main notify
method). If keeping it, update the ChatNotifier.message body to reference
rescheduling and reuse
Rails.application.routes.url_helpers.pair_work_url(pair_work) as currently used;
otherwise delete the notify_to_chat method to match PairWorkRematchingNotifier's
cleanup.
🤖 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/pair_works/reservations_controller.rb`:
- Around line 24-33: The destroy action lacks a server-side authorization check
allowing any authenticated user to cancel a PairWork; update the destroy method
(the controller action that finds PairWork via
PairWork.find(params[:pair_work_id]) and calls `@pair_work.unmatch`) to enforce
the same constraint as the view: either call require_mentor_login at the start
(like create) or explicitly verify current_user == `@pair_work.buddy` before
performing `@pair_work.unmatch`, and handle unauthorized cases by
redirecting/raising or rendering the show with `@comments` as in the else branch.
- Around line 10-11: The reschedule check compares `@pair_work.reserved_at`
(DateTime/TimeWithZone) to reservation_params[:reserved_at] (string), causing a
false positive; inside reserve() before the notification_kinds lines (and before
assign_attributes is called) parse/convert reservation_params[:reserved_at] to a
Time/DateTime using Time.zone.parse or to_datetime and use that parsed value for
the comparison (e.g. compare `@pair_work.reserved_at` to parsed_reserved_at) so
the types match and reschedule notifications are only added when the actual time
changes.

In `@app/models/pair_work_rematching_notifier.rb`:
- Around line 13-15: Rename the misspelled method notify_pair_work_creater to
notify_pair_work_creator in PairWorkRematchingNotifier (update the method
definition) and update all call sites that invoke notify_pair_work_creater
(e.g., the caller referenced around line 8) to use notify_pair_work_creator so
names remain consistent; ensure any tests or references to
ActivityDelivery.with(...).notify(:rematching_pair_work) remain unchanged except
for the method name change.

In `@app/models/pair_work_reschedule_notifier.rb`:
- Around line 13-15: The method name notify_pair_work_creater in
PairWorkRescheduleNotifier is misspelled; rename it to notify_pair_work_creator
and update any callers (including the one referenced at line 8) to use
notify_pair_work_creator; keep the ActivityDelivery.with(pair_work:, receiver:
pair_work.user).notify(:reschedule_pair_work) behavior intact and ensure the
signature and visibility remain the same to match PairWorkRematchingNotifier.

In `@app/views/activity_mailer/reschedule_pair_work.html.slim`:
- Line 5: Remove the hardcoded subject prefix "[FBC]" from the email body
template in the activity_mailer reschedule_pair_work view by editing the line
that currently renders p "[FBC] ペアワーク【 #{`@pair_work.title`} 】の日程が#{I18n.l
`@pair_work.reserved_at`}に変更になりました。"; change it to render the title and
reserved_at only (keep `@pair_work.title` and I18n.l `@pair_work.reserved_at`
intact) so the prefix is not duplicated in the message body while leaving
subject handling unchanged.

In `@test/mailers/previews/activity_mailer_preview.rb`:
- Around line 204-209: The preview method rematching_pair_work uses fixture
pair_work1 which has buddy: null causing ActivityMailer#rematching_pair_work to
raise NoMethodError when accessing `@pair_work.buddy.login_name`; update
rematching_pair_work to load a PairWork fixture that has a buddy (e.g., replace
pair_work1 with pair_work2 or pair_work4) or ensure the fixture sets buddy, so
the preview calls ActivityMailer.with(receiver:,
pair_work:).rematching_pair_work with a pair_work that has a non-nil buddy.

In `@test/system/notification/pair_works_test.rb`:
- Line 91: テスト名のタイポを修正してください: 現在のテスト定義 test 'pair work creater receive
notificationnotify when pair rematched' の文字列中の "notificationnotify" を
"notification when"(または適切な英語表現 "notification when")に置き換え、テストメソッド名を test 'pair
work creater receive notification when pair rematched'
のように修正してスペルと可読性を正してください(対象は該当の test 定義行)。

In `@test/system/pair_works_test.rb`:
- Around line 132-134: Replace the use of accept_alert with accept_confirm
around the click for the 'ペア確定を取り消す' button: locate the block using accept_alert
and click_button 'ペア確定を取り消す' and change it to use accept_confirm so the Capybara
modal helper matches the button's data-confirm behavior; ensure the surrounding
block semantics remain the same and tests continue to click_button 'ペア確定を取り消す'
inside the new accept_confirm block.

---

Nitpick comments:
In `@app/models/pair_work_cancel_notifier.rb`:
- Around line 17-22: The notify_to_chat method in PairWorkCancelNotifier
currently sends a "matching pair decided" message which is incorrect for cancel
notifications; either remove the unused notify_to_chat method if it will never
be used, or update its text to a cancel-specific message: change the
ChatNotifier.message payload in notify_to_chat to indicate the pair matching was
cancelled (e.g., "マッチングペアが取消されました") and keep the existing pair_work_url link for
context so future callers (including call) will send a cancel notification
rather than a confirmation.

In `@app/models/pair_work_rematching_notifier.rb`:
- Around line 17-22: The private method notify_to_chat is unused and its message
wording refers to an initial matching rather than a rematching; either remove
the unused method or update and wire it into the call flow: if you intend to
keep chat notifications, rename or change the message in notify_to_chat to
reflect a rematch (e.g., "ペアが変更されました"), and invoke notify_to_chat from the call
method where rematching is handled so the notifier runs; otherwise delete the
notify_to_chat method to avoid dead code (referencing notify_to_chat and the
call method to locate the change).

In `@app/models/pair_work_reschedule_notifier.rb`:
- Around line 17-22: The private method notify_to_chat in
PairWorkRescheduleNotifier is unused and its message text ("マッチングペアが決定しました") is
incorrect for a reschedule notifier; either remove the unused notify_to_chat
method or repurpose it by changing its message to reflect a rescheduled meeting
and ensure it is invoked from the class's public notification flow (e.g., call
notify_to_chat from the main notify method). If keeping it, update the
ChatNotifier.message body to reference rescheduling and reuse
Rails.application.routes.url_helpers.pair_work_url(pair_work) as currently used;
otherwise delete the notify_to_chat method to match PairWorkRematchingNotifier's
cleanup.

In `@app/models/pair_work.rb`:
- Around line 89-91: The cancel message in the action_name mapping in PairWork
(the hash that returns messages for keys like create, update, destroy, reserve,
cancel) is missing the Japanese full stop; update the 'cancel' value to include
the trailing '。' so it matches the punctuation used by create, update, destroy,
and reserve and maintains consistency across PairWork messages.

In `@test/system/pair_works_test.rb`:
- Around line 128-147: Tests "cancel pair confirmation" and "show confirmed date
on calendar" depend on a past confirmed datetime (pair_works(:pair_work4) =
2025-01-02 01:00:00) so make them deterministic by fixing the current time with
travel_to; wrap the actions/assertions in the tests named 'cancel pair
confirmation' and 'show confirmed date on calendar' (or wrap the entire test
bodies) with a travel_to set to a date around 2025-01-01/02 so the confirmed
datetime is treated as intended, then ensure you return time to normal (using
travel_back or the travel_to block) and keep references to
pair_works(:pair_work4), visit_with_auth, and the calendar selector id
(2025-01-02T01:00:00+09:00) when applying the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5fab15ab-4ea6-4e07-bfd8-de1953a4a3dc

📥 Commits

Reviewing files that changed from the base of the PR and between 0b9aa99 and 9fb9fb1.

📒 Files selected for processing (22)
  • app/controllers/pair_works/reservations_controller.rb
  • app/mailers/activity_mailer.rb
  • app/models/notification.rb
  • app/models/pair_work.rb
  • app/models/pair_work_cancel_notifier.rb
  • app/models/pair_work_rematching_notifier.rb
  • app/models/pair_work_reschedule_notifier.rb
  • app/models/retirement.rb
  • app/notifiers/activity_notifier.rb
  • app/views/activity_mailer/cancel_pair_work.html.slim
  • app/views/activity_mailer/rematching_pair_work.html.slim
  • app/views/activity_mailer/reschedule_pair_work.html.slim
  • app/views/pair_works/_body.html.slim
  • app/views/pair_works/_schedule.html.slim
  • config/initializers/active_support_notifications.rb
  • test/deliveries/activity_delivery_test.rb
  • test/fixtures/pair_work_schedules.yml
  • test/fixtures/pair_works.yml
  • test/mailers/activity_mailer_test.rb
  • test/mailers/previews/activity_mailer_preview.rb
  • test/system/notification/pair_works_test.rb
  • test/system/pair_works_test.rb

@s-tone-gs s-tone-gs force-pushed the feature/add-notifications-and-cancellation branch 3 times, most recently from 326f485 to ef3e2c6 Compare March 23, 2026 09:26
@s-tone-gs s-tone-gs force-pushed the feature/add-notifications-and-cancellation branch from ef3e2c6 to 9fcfa85 Compare March 24, 2026 00:35
@s-tone-gs
Copy link
Copy Markdown
Contributor Author

@machida お疲れ様です!通知とメールの文面を仮で実装したので改めて確認いただきたいです!
加えてapp/views/pair_works/_schedule.html.slim:39において、カレンダーに確定日を表示させる実装をおこなっていますのでデザインをお願いします🙇‍♂️

ペア取り消し通知

アプリ内通知

image

メール

image

ペア変更

アプリ内

image

メール

image

日程変更

アプリ内

image

メール

image

@machida
Copy link
Copy Markdown
Member

machida commented Mar 24, 2026

@s-tone-gs 連絡ありがとうございます!!
確認しました👍これでいきましょう

@s-tone-gs
Copy link
Copy Markdown
Contributor Author

@machida
デザインなのですが、今週の木曜日までにいただきたいです🙏
研修の期限的に3月末までに生徒間レビューに渡したく・・・!急で申し訳ないのですがよろしくお願いいたします🙇‍♂️

@machida
Copy link
Copy Markdown
Member

machida commented Mar 24, 2026

@s-tone-gs 了解です!木曜までに終わらせますー

@machida
Copy link
Copy Markdown
Member

machida commented Mar 25, 2026

@s-tone-gs デザイン入れました!急ぎで入れたので、あとで変更するかもですが、一旦これでテストなどを整えてレビューに進めてください🙏

@s-tone-gs
Copy link
Copy Markdown
Contributor Author

@machida
ありがとうございます~!

@s-tone-gs s-tone-gs force-pushed the feature/add-notifications-and-cancellation branch from 17c13bd to 154c412 Compare March 26, 2026 01:13
@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 26, 2026

🚀 Review App

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

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

@s-tone-gs s-tone-gs marked this pull request as ready for review March 26, 2026 02:08
@github-actions github-actions bot requested a review from okuramasafumi March 26, 2026 02:08
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: 1

🧹 Nitpick comments (3)
test/system/retirements/data_deletion_test.rb (1)

129-138: reserved_at も一緒に検証しておきたいです。

Line 137 だと buddy スコープから外れたことしか見ていないので、buddy_id だけ nil になって reserved_at が残る不整合でも通ってしまいます。pair_works(:pair_work2).reloadbuddy_idreserved_at の両方を nil で確認すると、この回帰を直接捕まえられます。

💡 修正例
-        assert_not_includes PairWork.where(buddy: users(:sotugyou)), pair_works(:pair_work2)
+        pair_work = pair_works(:pair_work2).reload
+        assert_nil pair_work.buddy_id
+        assert_nil pair_work.reserved_at

Based on learnings 「buddy(ペアを申し込んだメンター)ユーザーが削除された場合、buddy_id だけでなく reserved_at も NULL にしてマッチングを解除する必要がある」。

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

In `@test/system/retirements/data_deletion_test.rb` around lines 129 - 138, テストが
buddy スコープから外れたことしか確認していないので、PairWork のレコードが部分的にしか更新されていない不整合を見逃します。テスト "shold
unmatch when buddy has retired" で pair_works(:pair_work2) をそのまま比較する代わりに
pair_works(:pair_work2).reload を使ってレコードを再読み込みし、pair_work.buddy_id と
pair_work.reserved_at がどちらも nil になっていることを assert_nil で検証するように修正してください(モデル:
PairWork、フィクスチャ参照: pair_works(:pair_work2) を参照)。
app/models/pair_work_rematching_notifier.rb (1)

14-17: past_buddy が nil の場合の考慮が必要

コントローラーで User.find_by(id: past_buddy_id) を使用しているため、過去のバディユーザーが削除されている場合 past_buddynil になる可能性があります。nil を含む配列をイテレートすると、ActivityDelivery.with(receiver: nil) が呼ばれてエラーになる可能性があります。

♻️ 修正案: nil をフィルタリング
   def notify_pair_work_creator(pair_work, past_buddy)
-    [pair_work.user, past_buddy].each do |receiver|
+    [pair_work.user, past_buddy].compact.each do |receiver|
       ActivityDelivery.with(pair_work:, receiver:).notify(:rematching_pair_work)
     end
   end
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/models/pair_work_rematching_notifier.rb` around lines 14 - 17,
notify_pair_work_creator currently iterates over [pair_work.user, past_buddy]
and may call ActivityDelivery.with(..., receiver: nil) if past_buddy is nil;
filter out nil receivers (e.g., use .compact or reject(&:nil?)) before the .each
so ActivityDelivery.with is only invoked with valid User objects; update the
notify_pair_work_creator method (referencing ActivityDelivery.with(pair_work:,
receiver:)) to skip nil past_buddy values.
app/controllers/pair_works/reservations_controller.rb (1)

32-43: 認可失敗時のサイレントリターンは UX 上の問題

Line 34 の return if current_user != @pair_work.buddy`` は認可チェックとして正しく動作しますが、bare return はレスポンスなしで終了するため、ユーザーが「ペア確定を取り消す」ボタンをクリックしても何も起こらないように見えます。エラーメッセージ付きでリダイレクトするか、403 エラーを返す方が適切です。

♻️ 修正案: エラーメッセージ付きリダイレクト
   def destroy
     `@pair_work` = PairWork.find(params[:pair_work_id])
-    return if current_user != `@pair_work.buddy`
+    unless current_user == `@pair_work.buddy`
+      redirect_to `@pair_work`, alert: 'ペア確定を取り消す権限がありません。'
+      return
+    end

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

In `@app/controllers/pair_works/reservations_controller.rb` around lines 32 - 43,
The bare `return` in the destroy action causes a silent no-response when
`current_user != `@pair_work.buddy``; replace it with an explicit unauthorized
response: either perform a redirect with an error/alert (e.g. redirect_to
Redirection.determin_url(self, `@pair_work`), alert: 'You are not authorized to
cancel this pairing') or render a 403 (render plain: 'Forbidden', status:
:forbidden), and then return to stop execution; update the `destroy` method
(which finds PairWork via PairWork.find(params[:pair_work_id]) and compares
current_user to `@pair_work.buddy`) to use one of these explicit responses instead
of the bare `return`.
🤖 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/pair_works_controller.rb`:
- Around line 41-44: Currently the code calls `@pair_work.schedules.destroy_all`
before attempting `@pair_work.update` in update (called after set_wip), which can
delete schedules even if update fails and ignores cases where the browser omits
schedules_attributes; change this to perform schedule replacement atomically
inside a transaction: do not call destroy_all up front, instead wrap the update
and schedule-replacement in a PairWork.transaction block (use update! to raise
on failure), apply the new schedules only after a successful update (or use
nested attributes with allow_destroy and handle a missing schedules_attributes
by treating it as an explicit empty selection), and reference the update action,
set_wip, `@pair_work.schedules.destroy_all` and pair_work_params when making the
change.

---

Nitpick comments:
In `@app/controllers/pair_works/reservations_controller.rb`:
- Around line 32-43: The bare `return` in the destroy action causes a silent
no-response when `current_user != `@pair_work.buddy``; replace it with an explicit
unauthorized response: either perform a redirect with an error/alert (e.g.
redirect_to Redirection.determin_url(self, `@pair_work`), alert: 'You are not
authorized to cancel this pairing') or render a 403 (render plain: 'Forbidden',
status: :forbidden), and then return to stop execution; update the `destroy`
method (which finds PairWork via PairWork.find(params[:pair_work_id]) and
compares current_user to `@pair_work.buddy`) to use one of these explicit
responses instead of the bare `return`.

In `@app/models/pair_work_rematching_notifier.rb`:
- Around line 14-17: notify_pair_work_creator currently iterates over
[pair_work.user, past_buddy] and may call ActivityDelivery.with(..., receiver:
nil) if past_buddy is nil; filter out nil receivers (e.g., use .compact or
reject(&:nil?)) before the .each so ActivityDelivery.with is only invoked with
valid User objects; update the notify_pair_work_creator method (referencing
ActivityDelivery.with(pair_work:, receiver:)) to skip nil past_buddy values.

In `@test/system/retirements/data_deletion_test.rb`:
- Around line 129-138: テストが buddy スコープから外れたことしか確認していないので、PairWork
のレコードが部分的にしか更新されていない不整合を見逃します。テスト "shold unmatch when buddy has retired" で
pair_works(:pair_work2) をそのまま比較する代わりに pair_works(:pair_work2).reload
を使ってレコードを再読み込みし、pair_work.buddy_id と pair_work.reserved_at がどちらも nil になっていることを
assert_nil で検証するように修正してください(モデル: PairWork、フィクスチャ参照: pair_works(:pair_work2)
を参照)。
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 44b5a1d6-5125-475f-bee7-c7ebdf4d1390

📥 Commits

Reviewing files that changed from the base of the PR and between 9fb9fb1 and 154c412.

📒 Files selected for processing (31)
  • app/assets/stylesheets/application/blocks/pair-work/_pair-work-schedule-dates.css
  • app/assets/stylesheets/shared/blocks/form/_form-table.css
  • app/controllers/pair_works/reservations_controller.rb
  • app/controllers/pair_works_controller.rb
  • app/mailers/activity_mailer.rb
  • app/models/notification.rb
  • app/models/pair_work.rb
  • app/models/pair_work_cancel_notifier.rb
  • app/models/pair_work_rematching_notifier.rb
  • app/models/pair_work_reschedule_notifier.rb
  • app/models/retirement.rb
  • app/notifiers/activity_notifier.rb
  • app/views/activity_mailer/cancel_pair_work.html.slim
  • app/views/activity_mailer/rematching_pair_work.html.slim
  • app/views/activity_mailer/reschedule_pair_work.html.slim
  • app/views/pair_works/_body.html.slim
  • app/views/pair_works/_form.html.slim
  • app/views/pair_works/_schedule.html.slim
  • app/views/pair_works/index.html.slim
  • config/initializers/active_support_notifications.rb
  • config/routes.rb
  • test/deliveries/activity_delivery_test.rb
  • test/fixtures/pair_work_schedules.yml
  • test/fixtures/pair_works.yml
  • test/mailers/activity_mailer_test.rb
  • test/mailers/previews/activity_mailer_preview.rb
  • test/models/pair_work_test.rb
  • test/models/retirement_test.rb
  • test/system/notification/pair_works_test.rb
  • test/system/pair_works_test.rb
  • test/system/retirements/data_deletion_test.rb
✅ Files skipped from review due to trivial changes (9)
  • app/views/pair_works/index.html.slim
  • app/assets/stylesheets/shared/blocks/form/_form-table.css
  • app/assets/stylesheets/application/blocks/pair-work/_pair-work-schedule-dates.css
  • app/views/activity_mailer/rematching_pair_work.html.slim
  • app/views/activity_mailer/reschedule_pair_work.html.slim
  • test/fixtures/pair_works.yml
  • config/initializers/active_support_notifications.rb
  • app/views/activity_mailer/cancel_pair_work.html.slim
  • test/fixtures/pair_work_schedules.yml
🚧 Files skipped from review as they are similar to previous changes (8)
  • app/models/notification.rb
  • test/deliveries/activity_delivery_test.rb
  • test/system/notification/pair_works_test.rb
  • app/notifiers/activity_notifier.rb
  • test/mailers/activity_mailer_test.rb
  • app/views/pair_works/_body.html.slim
  • app/views/pair_works/_schedule.html.slim
  • test/system/pair_works_test.rb

@s-tone-gs s-tone-gs force-pushed the feature/add-notifications-and-cancellation branch from 34ab5e3 to 4995f4a Compare March 26, 2026 02:21
@s-tone-gs
Copy link
Copy Markdown
Contributor Author

s-tone-gs commented Mar 26, 2026

@coderabbitai
以下の指摘について質問です。

app/controllers/pair_works/reservations_controller.rb (1)
32-43: 認可失敗時のサイレントリターンは UX 上の問題

ペア取り消しボタンはバディにしか表示されないものであり、バディ以外がペア取り消しを行うというのは通常あり得ないです。しかし、悪意あるユーザーによって同操作が行われる場合を危惧してコントローラーで認可処理を書いている状況です。そもそもあり得ない操作なのでUXを考慮する必要もないという考えなのですがいかがでしょうか?

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 26, 2026

Tip

For best results, initiate chat on the files or code changes.

@s-tone-gs ご説明ありがとうございます!おっしゃる通りだと思います。

「ペア取り消し」ボタンはバディにしか表示されない設計であり、バディ以外がこの操作を行うのは通常ありえません。コントローラーの認可チェックはあくまで悪意ある操作(直接リクエスト送信など)に対する多層防御(defense-in-depth)の目的で実装されているわけですね。

そのようなセキュリティガードのケースでは、正規ユーザーがこのコードパスに到達することは想定されていないため、サイレントリターンはむしろ意図的かつ適切な設計です。エラーメッセージを返すことで攻撃者に情報を与える必要もありません。

この指摘は取り下げます。現在の実装で問題ありません!


✏️ Learnings added
Learnt from: s-tone-gs
URL: https://github.com/fjordllc/bootcamp/pull/9812

Timestamp: 2026-03-26T04:13:01.581Z
Learning: fjordllc/bootcamp の `PairWorks::ReservationsController#destroy`(`app/controllers/pair_works/reservations_controller.rb`)において、バディ以外によるペア取り消し操作に対するサイレントリターン(早期 return)は意図的な設計である。「ペア取り消し」ボタンはバディにしか表示されず、通常ありえない操作に対するサーバーサイドの多層防御(defense-in-depth)のため、UX向けのエラーメッセージは不要と判断されている。

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: mousu-a
Repo: fjordllc/bootcamp PR: 9626
File: db/schema.rb:1153-1156
Timestamp: 2026-02-11T02:59:47.770Z
Learning: fjordllc/bootcamp の PairWork 機能において、buddy(ペアを申し込んだメンター)ユーザーが削除された場合、buddy_id だけでなく reserved_at も NULL にしてマッチングを解除する必要がある。buddy_id のみ NULL にすると reserved_at が残り不整合な状態になるため、将来のマッチング解除機能実装時に User の削除時コールバックまたは明示的なアンマッチ処理で両方のカラムをクリアする設計とする。

Learnt from: yokomaru
Repo: fjordllc/bootcamp PR: 9732
File: app/models/user.rb:955-958
Timestamp: 2026-03-16T03:19:51.757Z
Learning: fjordllc/bootcamp の `User#clean_up_regular_events` において、`regular_event_participations.for_holding_events.destroy_all`(参加解除)と `organize_regular_events.holding.each { |event| event.close_or_destroy_organizer(self) }`(主催者側終了/削除)はトランザクションでまとめていないが、これは意図的な設計判断である。理由: (1) 参加者と主催者は別モデルであり厳密な整合性は不要、(2) 呼び出し元(退会・休会・研修終了処理)全体のトランザクション設計を一緒に見直す予定があるため、このメソッド単体での対応は見送られた。

Learnt from: mousu-a
Repo: fjordllc/bootcamp PR: 8566
File: app/notifiers/activity_notifier.rb:357-373
Timestamp: 2025-07-01T04:30:19.565Z
Learning: matching_pair_workメソッドは、ペア確定時に送信される通知のため、pair_work.buddy_idは必ず存在することがビジネスロジックで保証されている。そのため、User.find(pair_work.buddy_id)でのエラーハンドリングは不要。

Learnt from: mousu-a
Repo: fjordllc/bootcamp PR: 8566
File: app/controllers/pair_works_controller.rb:58-76
Timestamp: 2025-11-01T03:15:49.349Z
Learning: PairWork.matching_params?メソッドは、buddy_idとreserved_atのみを含むペア確定リクエストのみを許可するため、title、description、practice_id、channel、schedules_attributesがパラメータに含まれる場合は権限を拒否することで、メンターによる権限昇格を防ぐセキュリティ対策が実装されている。

Learnt from: mousu-a
Repo: fjordllc/bootcamp PR: 8566
File: app/views/application/_global_nav.slim:48-54
Timestamp: 2025-08-28T00:34:28.541Z
Learning: app/views/application/_global_nav.slim のQ&A/ペアワークバッジの環境分岐は、ペアワーク機能の本番リリース後に削除される一時的な実装である。その際、メンター・管理者のみに表示する制限仕様も撤廃される予定のため、現在の実装にはアクセス権限ガードを追加する必要がない。

Learnt from: mousu-a
Repo: fjordllc/bootcamp PR: 8566
File: db/fixtures/pair_works.yml:19-20
Timestamp: 2026-01-15T03:21:32.929Z
Learning: fjordllc/bootcampの db/fixtures/pair_works.yml において、pair_work1とpair_work2は特定の状態(解決済み・募集中)の動作確認用フィクスチャであり、意図的に過去の静的な日付(例: Time.zone.parse('2025-01-01 00:00:00'))を使用している。これはテストの再現性を保証するための設計であり、問題ではない。

Learnt from: mousu-a
Repo: fjordllc/bootcamp PR: 8566
File: app/views/pair_works/_body.html.slim:111-117
Timestamp: 2025-08-31T23:39:23.210Z
Learning: PairWorkモデルにreserved_at_in_schedules?カスタムバリデーション(on: :update)が実装されており、reserved_atが必ずschedules.map(&:proposed_at)に含まれることを検証している。これにより、フォームから任意の日時を送信することを防いでいる。

Learnt from: mousu-a
Repo: fjordllc/bootcamp PR: 8566
File: test/mailers/activity_mailer_test.rb:0-0
Timestamp: 2025-08-01T12:57:10.349Z
Learning: came_pair_work機能は現在テスト運用段階で、メンターと関係者のみに通知される仕様のため、メール通知OFF時の制御テストは現時点では実装不要と判断されている。

Learnt from: yokomaru
Repo: fjordllc/bootcamp PR: 9732
File: app/models/user.rb:955-958
Timestamp: 2026-03-16T03:24:48.283Z
Learning: fjordllc/bootcamp の `Retirement#execute`(`app/models/retirement.rb`)において、`user.clean_up_regular_events` の呼び出しは `ActiveRecord::Base.transaction` ブロックの外(コミット後)に置かれているが、これは意図的な設計判断である。理由: 退会・休会・研修終了処理全体のトランザクション設計を一緒に見直す予定があり、`clean_up_regular_events` 単体をトランザクション内に移動する対応は別途全体見直し時に検討する。

Learnt from: mousu-a
Repo: fjordllc/bootcamp PR: 8566
File: app/views/pair_works/_body.html.slim:85-85
Timestamp: 2025-08-31T03:39:07.792Z
Learning: app/views/pair_works/_body.html.slimテンプレートには2つの独立したif pair_work.solved?条件分岐があり、user変数は最初の分岐でのみ設定されるため、2番目の分岐での参照には注意が必要。

Learnt from: jun-kondo
Repo: fjordllc/bootcamp PR: 0
File: :0-0
Timestamp: 2025-07-23T21:11:21.826Z
Learning: fjordllc/bootcampプロジェクトの`app/models/work_notification_destroyer.rb`において、このクラスは`app/controllers/works_controller.rb`の`destroy`アクションから呼び出され、`before_action :set_my_work`でIDの妥当性チェックが事前に行われるため、payloadのworkオブジェクトは常に有効であることが保証されている。そのためエラーハンドリングは不要。

Learnt from: mousu-a
Repo: fjordllc/bootcamp PR: 8566
File: app/controllers/pair_works_controller.rb:58-61
Timestamp: 2025-09-22T03:12:23.411Z
Learning: PairWork.matching_permission?メソッドでは、titleやdescriptionパラメータが含まれる場合は権限を拒否し、buddy_idとreserved_atのみを含むペア確定リクエストのみを許可することで、権限昇格を防ぐセキュリティ対策が実装されている。

Learnt from: mousu-a
Repo: fjordllc/bootcamp PR: 8566
File: app/views/pair_works/_body.html.slim:3-4
Timestamp: 2025-07-01T04:26:24.636Z
Learning: PairWorkモデルにおいて、pair_work.solved?がtrueを返す場合、buddy_idは必ず存在することがビジネスロジックで保証されている。そのため、solved?の条件分岐内でのUser.find(pair_work.buddy_id)は安全に使用できる。

Learnt from: Miya096jp
Repo: fjordllc/bootcamp PR: 9102
File: app/controllers/mentor/buzzes_controller.rb:0-0
Timestamp: 2025-09-04T01:39:22.261Z
Learning: fjordllc/bootcamp プロジェクトでは、更新処理の成功時の通知メッセージについて、I18n を使用せずに日本語文字列を直接記述する方針で一貫性を保っている。

@coderabbitai

This comment was marked as duplicate.

@coderabbitai

This comment was marked as duplicate.

@s-tone-gs
Copy link
Copy Markdown
Contributor Author

s-tone-gs commented Mar 26, 2026

@coderabbitai
以下のレビューの指摘事項を修正しました
#9812 (review)

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 26, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Fetch the specific review and its comments
gh api repos/fjordllc/bootcamp/pulls/9812/reviews/4011010037 --jq '{state: .state, body: .body, submitted_at: .submitted_at}'

echo "---REVIEW COMMENTS---"
gh api repos/fjordllc/bootcamp/pulls/9812/reviews/4011010037/comments --jq '.[] | {path: .path, line: .original_line, body: .body, diff_hunk: .diff_hunk}'

Length of output: 13428


🏁 Script executed:

#!/bin/bash
echo "=== app/controllers/pair_works_controller.rb (update action) ==="
cat app/controllers/pair_works_controller.rb

echo ""
echo "=== app/models/pair_work_rematching_notifier.rb ==="
cat app/models/pair_work_rematching_notifier.rb

echo ""
echo "=== test/system/retirements/data_deletion_test.rb (last 30 lines) ==="
tail -40 test/system/retirements/data_deletion_test.rb

Length of output: 4139


@s-tone-gs ご確認しました!修正状況は以下の通りです。

✅ 修正済み(2件)

  • app/models/pair_work_rematching_notifier.rb: past_buddy が nil の場合の考慮として .compact が追加されています。
  • test/system/retirements/data_deletion_test.rb: pair_works(:pair_work2).reloadbuddy_idreserved_at の両方を assert_nil で検証するよう修正されています。

🔴 未修正(1件)

app/controllers/pair_works_controller.rbupdate アクション(Criticalとしてマークされていた指摘)は、まだ修正されていないようです。現在も以下のままになっています:

def update
  set_wip
  `@pair_work.schedules.destroy_all` if params[:pair_work][:schedules_attributes].present?
  if `@pair_work.update`(pair_work_params)

destroy_allupdate より先に実行されるため、update が失敗してもスケジュールだけは DB から削除されてしまいます。トランザクション内で一括処理する対応が必要です。例えば以下のような修正が考えられます:

def update
  set_wip
  PairWork.transaction do
    `@pair_work.schedules.destroy_all` if params[:pair_work][:schedules_attributes].present?
    `@pair_work.update`!(pair_work_params)
  end
  ActiveSupport::Notifications.instrument('pair_work.update', pair_work: `@pair_work`)
  redirect_to Redirection.determin_url(self, `@pair_work`), notice: `@pair_work.generate_notice_message`(:update)
rescue ActiveRecord::RecordInvalid
  render :edit
end

こちらの対応もお願いできますか?


🧠 Learnings used
Learnt from: hirokiej
Repo: fjordllc/bootcamp PR: 8740
File: app/helpers/reports_helper.rb:56-64
Timestamp: 2025-06-29T03:44:15.179Z
Learning: このプロジェクト(fjordllc/bootcamp)では、ja.ymlファイルで一部の単語や文章のみI18n対応されているが、多くのテキストは日本語でハードコーディングされており、完全な国際化対応は行われていない。新しい機能でもI18n対応は不要と判断される。

Learnt from: Miya096jp
Repo: fjordllc/bootcamp PR: 9102
File: app/controllers/mentor/buzzes_controller.rb:0-0
Timestamp: 2025-09-04T01:39:22.261Z
Learning: fjordllc/bootcamp プロジェクトでは、更新処理の成功時の通知メッセージについて、I18n を使用せずに日本語文字列を直接記述する方針で一貫性を保っている。

Learnt from: tyrrell-IH
Repo: fjordllc/bootcamp PR: 9306
File: app/javascript/components/Bookmarks.jsx:248-265
Timestamp: 2025-11-17T00:46:30.794Z
Learning: fjordllc/bootcamp プロジェクトでは、Reactからバニラ JavaScript への移行作業が進行中である。そのため、新しいReactファイルの作成は避け、既存のReactコンポーネント内で完結する実装が推奨される。BookmarksInDashboard.jsx は issue `#9045` で削除予定。

Learnt from: ryufuta
Repo: fjordllc/bootcamp PR: 9115
File: db/migrate/20250902024949_add_unique_index_to_correct_answers.rb:1-5
Timestamp: 2025-11-13T09:20:36.030Z
Learning: fjordllc/bootcamp プロジェクトでは、データの整合性を保つためのデータ修正には data-migrate gem を使用し、db/data/ ディレクトリ配下にデータマイグレーションファイルを作成する運用を採用している。スキーマ変更(db/migrate/)とデータ変更(db/data/)を分離することで、より安全で管理しやすい構成を実現している。

Learnt from: jun-kondo
Repo: fjordllc/bootcamp PR: 0
File: :0-0
Timestamp: 2025-07-23T20:42:19.974Z
Learning: fjordllc/bootcampプロジェクトでは、hタグ(見出し)の文言は日本語でハードコーディングする方針が確立されており、I18n対応は行わない。例:app/views/welcome/logo.html.slimでh2、h3タグが日本語でハードコーディングされている。

Learnt from: su-su-su-su
Repo: fjordllc/bootcamp PR: 0
File: :0-0
Timestamp: 2025-07-07T05:28:03.676Z
Learning: fjordllc/bootcampプロジェクトでは、fixtureファイル内のERB構文で改行制御文字(-)は使用せず、標準的な<% %>と<%= %>のみを使用する方針が統一されている。<% -%>や<%- %>を使用するとSyntaxErrorが発生する。

Learnt from: yokomaru
Repo: fjordllc/bootcamp PR: 9338
File: app/helpers/products_helper.rb:0-0
Timestamp: 2026-01-11T01:55:06.202Z
Learning: fjordllc/bootcamp プロジェクトでは、日付から曜日を取得する際、配列参照ではなく I18n.l を使用した日付フォーマット表示を優先する。

Learnt from: su-su-su-su
Repo: fjordllc/bootcamp PR: 0
File: :0-0
Timestamp: 2025-07-26T15:24:14.799Z
Learning: fjordllc/bootcampプロジェクトでは、Rubyの述語メソッドには?を付ける命名規則(例: can_be_checker?)を徹底している。

Learnt from: komagata
Repo: fjordllc/bootcamp PR: 9101
File: app/notifiers/discord_notifier.rb:131-135
Timestamp: 2025-09-12T21:18:00.834Z
Learning: Rails アップグレードPRにおいて、product_review_not_completed メソッドの webhook URL 設定の shared フォールバック追加も、設定システム全体の変更として別PRで対応すべきである。

Learnt from: mousu-a
Repo: fjordllc/bootcamp PR: 9626
File: db/schema.rb:1153-1156
Timestamp: 2026-02-11T02:59:47.770Z
Learning: fjordllc/bootcamp の PairWork 機能において、buddy(ペアを申し込んだメンター)ユーザーが削除された場合、buddy_id だけでなく reserved_at も NULL にしてマッチングを解除する必要がある。buddy_id のみ NULL にすると reserved_at が残り不整合な状態になるため、将来のマッチング解除機能実装時に User の削除時コールバックまたは明示的なアンマッチ処理で両方のカラムをクリアする設計とする。

Learnt from: s-tone-gs
Repo: fjordllc/bootcamp PR: 0
File: :0-0
Timestamp: 2026-03-26T04:13:01.581Z
Learning: fjordllc/bootcamp の `PairWorks::ReservationsController#destroy`(`app/controllers/pair_works/reservations_controller.rb`)において、バディ以外によるペア取り消し操作に対するサイレントリターン(早期 return)は意図的な設計である。「ペア取り消し」ボタンはバディにしか表示されず、通常ありえない操作に対するサーバーサイドの多層防御(defense-in-depth)のため、UX向けのエラーメッセージは不要と判断されている。

Learnt from: s-tone-gs
Repo: fjordllc/bootcamp PR: 0
File: :0-0
Timestamp: 2026-03-26T04:13:22.108Z
Learning: fjordllc/bootcamp の `PairWorks::ReservationsController#destroy`(`app/controllers/pair_works/reservations_controller.rb`)において、`current_user == pair_work.buddy` チェックに失敗した場合のサイレントリターンは意図的な設計である。理由: 「ペア取り消し」ボタンはバディにしか表示されないため、非バディによるアクセスは悪意ある直接リクエストのみであり、その場合にUXを考慮する必要はない。これはセキュリティ多層防御の観点から実装されている。

Learnt from: mousu-a
Repo: fjordllc/bootcamp PR: 8566
File: test/mailers/activity_mailer_test.rb:0-0
Timestamp: 2025-08-01T12:57:10.349Z
Learning: came_pair_work機能は現在テスト運用段階で、メンターと関係者のみに通知される仕様のため、メール通知OFF時の制御テストは現時点では実装不要と判断されている。

Learnt from: mousu-a
Repo: fjordllc/bootcamp PR: 8566
File: db/fixtures/pair_works.yml:19-20
Timestamp: 2026-01-15T03:21:32.929Z
Learning: fjordllc/bootcampの db/fixtures/pair_works.yml において、pair_work1とpair_work2は特定の状態(解決済み・募集中)の動作確認用フィクスチャであり、意図的に過去の静的な日付(例: Time.zone.parse('2025-01-01 00:00:00'))を使用している。これはテストの再現性を保証するための設計であり、問題ではない。

Learnt from: yokomaru
Repo: fjordllc/bootcamp PR: 9732
File: app/models/user.rb:955-958
Timestamp: 2026-03-16T03:19:51.757Z
Learning: fjordllc/bootcamp の `User#clean_up_regular_events` において、`regular_event_participations.for_holding_events.destroy_all`(参加解除)と `organize_regular_events.holding.each { |event| event.close_or_destroy_organizer(self) }`(主催者側終了/削除)はトランザクションでまとめていないが、これは意図的な設計判断である。理由: (1) 参加者と主催者は別モデルであり厳密な整合性は不要、(2) 呼び出し元(退会・休会・研修終了処理)全体のトランザクション設計を一緒に見直す予定があるため、このメソッド単体での対応は見送られた。

Learnt from: mousu-a
Repo: fjordllc/bootcamp PR: 8566
File: app/views/pair_works/_body.html.slim:111-117
Timestamp: 2025-08-31T23:39:23.210Z
Learning: PairWorkモデルにreserved_at_in_schedules?カスタムバリデーション(on: :update)が実装されており、reserved_atが必ずschedules.map(&:proposed_at)に含まれることを検証している。これにより、フォームから任意の日時を送信することを防いでいる。

Learnt from: mousu-a
Repo: fjordllc/bootcamp PR: 8566
File: app/controllers/pair_works_controller.rb:58-76
Timestamp: 2025-11-01T03:15:49.349Z
Learning: PairWork.matching_params?メソッドは、buddy_idとreserved_atのみを含むペア確定リクエストのみを許可するため、title、description、practice_id、channel、schedules_attributesがパラメータに含まれる場合は権限を拒否することで、メンターによる権限昇格を防ぐセキュリティ対策が実装されている。

Learnt from: mousu-a
Repo: fjordllc/bootcamp PR: 8566
File: app/notifiers/activity_notifier.rb:357-373
Timestamp: 2025-07-01T04:30:19.565Z
Learning: matching_pair_workメソッドは、ペア確定時に送信される通知のため、pair_work.buddy_idは必ず存在することがビジネスロジックで保証されている。そのため、User.find(pair_work.buddy_id)でのエラーハンドリングは不要。

@s-tone-gs
Copy link
Copy Markdown
Contributor Author

@coderabbitai
未修正のものに関しては実装者に意図の確認を行ってから取り掛かりますので少々お待ちください

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 26, 2026

Tip

For best results, initiate chat on the files or code changes.

@s-tone-gs 了解しました!実装者への確認が取れましたら、またお知らせください。お待ちしております🙏


🧠 Learnings used
Learnt from: mousu-a
Repo: fjordllc/bootcamp PR: 8566
File: db/fixtures/pair_works.yml:19-20
Timestamp: 2026-01-15T03:21:32.929Z
Learning: fjordllc/bootcampの db/fixtures/pair_works.yml において、pair_work1とpair_work2は特定の状態(解決済み・募集中)の動作確認用フィクスチャであり、意図的に過去の静的な日付(例: Time.zone.parse('2025-01-01 00:00:00'))を使用している。これはテストの再現性を保証するための設計であり、問題ではない。

Learnt from: mousu-a
Repo: fjordllc/bootcamp PR: 9626
File: db/schema.rb:1153-1156
Timestamp: 2026-02-11T02:59:47.770Z
Learning: fjordllc/bootcamp の PairWork 機能において、buddy(ペアを申し込んだメンター)ユーザーが削除された場合、buddy_id だけでなく reserved_at も NULL にしてマッチングを解除する必要がある。buddy_id のみ NULL にすると reserved_at が残り不整合な状態になるため、将来のマッチング解除機能実装時に User の削除時コールバックまたは明示的なアンマッチ処理で両方のカラムをクリアする設計とする。

Learnt from: s-tone-gs
Repo: fjordllc/bootcamp PR: 0
File: :0-0
Timestamp: 2026-03-26T04:13:22.108Z
Learning: fjordllc/bootcamp の `PairWorks::ReservationsController#destroy`(`app/controllers/pair_works/reservations_controller.rb`)において、`current_user == pair_work.buddy` チェックに失敗した場合のサイレントリターンは意図的な設計である。理由: 「ペア取り消し」ボタンはバディにしか表示されないため、非バディによるアクセスは悪意ある直接リクエストのみであり、その場合にUXを考慮する必要はない。これはセキュリティ多層防御の観点から実装されている。

Learnt from: s-tone-gs
Repo: fjordllc/bootcamp PR: 0
File: :0-0
Timestamp: 2026-03-26T04:13:01.581Z
Learning: fjordllc/bootcamp の `PairWorks::ReservationsController#destroy`(`app/controllers/pair_works/reservations_controller.rb`)において、バディ以外によるペア取り消し操作に対するサイレントリターン(早期 return)は意図的な設計である。「ペア取り消し」ボタンはバディにしか表示されず、通常ありえない操作に対するサーバーサイドの多層防御(defense-in-depth)のため、UX向けのエラーメッセージは不要と判断されている。

Learnt from: yokomaru
Repo: fjordllc/bootcamp PR: 9732
File: app/models/user.rb:955-958
Timestamp: 2026-03-16T03:19:51.757Z
Learning: fjordllc/bootcamp の `User#clean_up_regular_events` において、`regular_event_participations.for_holding_events.destroy_all`(参加解除)と `organize_regular_events.holding.each { |event| event.close_or_destroy_organizer(self) }`(主催者側終了/削除)はトランザクションでまとめていないが、これは意図的な設計判断である。理由: (1) 参加者と主催者は別モデルであり厳密な整合性は不要、(2) 呼び出し元(退会・休会・研修終了処理)全体のトランザクション設計を一緒に見直す予定があるため、このメソッド単体での対応は見送られた。

Learnt from: mousu-a
Repo: fjordllc/bootcamp PR: 8566
File: app/helpers/pair_work_helper.rb:31-33
Timestamp: 2026-01-15T03:20:33.388Z
Learning: fjordllc/bootcamp: 時間の加算には Numeric#hours(複数形)を優先して用いるプロジェクト方針。例: app/helpers/pair_work_helper.rb の schedule_time では hour_count.hours を使用する。

Learnt from: mousu-a
Repo: fjordllc/bootcamp PR: 8566
File: app/notifiers/activity_notifier.rb:357-373
Timestamp: 2025-07-01T04:30:19.565Z
Learning: matching_pair_workメソッドは、ペア確定時に送信される通知のため、pair_work.buddy_idは必ず存在することがビジネスロジックで保証されている。そのため、User.find(pair_work.buddy_id)でのエラーハンドリングは不要。

Learnt from: yokomaru
Repo: fjordllc/bootcamp PR: 9732
File: app/models/user.rb:955-958
Timestamp: 2026-03-16T03:24:48.283Z
Learning: fjordllc/bootcamp の `Retirement#execute`(`app/models/retirement.rb`)において、`user.clean_up_regular_events` の呼び出しは `ActiveRecord::Base.transaction` ブロックの外(コミット後)に置かれているが、これは意図的な設計判断である。理由: 退会・休会・研修終了処理全体のトランザクション設計を一緒に見直す予定があり、`clean_up_regular_events` 単体をトランザクション内に移動する対応は別途全体見直し時に検討する。

Learnt from: komagata
Repo: fjordllc/bootcamp PR: 9101
File: app/notifiers/discord_notifier.rb:131-135
Timestamp: 2025-09-12T21:18:00.834Z
Learning: Rails アップグレードPRにおいて、product_review_not_completed メソッドの webhook URL 設定の shared フォールバック追加も、設定システム全体の変更として別PRで対応すべきである。

Learnt from: hirokiej
Repo: fjordllc/bootcamp PR: 8740
File: app/helpers/reports_helper.rb:56-64
Timestamp: 2025-06-29T03:44:15.179Z
Learning: このプロジェクト(fjordllc/bootcamp)では、ja.ymlファイルで一部の単語や文章のみI18n対応されているが、多くのテキストは日本語でハードコーディングされており、完全な国際化対応は行われていない。新しい機能でもI18n対応は不要と判断される。

Learnt from: mousu-a
Repo: fjordllc/bootcamp PR: 8566
File: test/mailers/activity_mailer_test.rb:0-0
Timestamp: 2025-08-01T12:57:10.349Z
Learning: came_pair_work機能は現在テスト運用段階で、メンターと関係者のみに通知される仕様のため、メール通知OFF時の制御テストは現時点では実装不要と判断されている。

Learnt from: mousu-a
Repo: fjordllc/bootcamp PR: 8566
File: app/views/pair_works/_body.html.slim:111-117
Timestamp: 2025-08-31T23:39:23.210Z
Learning: PairWorkモデルにreserved_at_in_schedules?カスタムバリデーション(on: :update)が実装されており、reserved_atが必ずschedules.map(&:proposed_at)に含まれることを検証している。これにより、フォームから任意の日時を送信することを防いでいる。

Learnt from: mousu-a
Repo: fjordllc/bootcamp PR: 8566
File: app/controllers/pair_works_controller.rb:58-76
Timestamp: 2025-11-01T03:15:49.349Z
Learning: PairWork.matching_params?メソッドは、buddy_idとreserved_atのみを含むペア確定リクエストのみを許可するため、title、description、practice_id、channel、schedules_attributesがパラメータに含まれる場合は権限を拒否することで、メンターによる権限昇格を防ぐセキュリティ対策が実装されている。

こちらを利用すると希望日のチェックを全て外して更新した場合にバリデーションエラーになり、よりユーザーフレンドリーな動作になる
@s-tone-gs s-tone-gs force-pushed the feature/add-notifications-and-cancellation branch from 2784349 to c74e4e8 Compare March 26, 2026 07:31
@s-tone-gs
Copy link
Copy Markdown
Contributor Author

@coderabbitai
改めてですが、以下で未対応とされていたものも修正してあります。
#9812 (comment)

@s-tone-gs s-tone-gs requested a review from karlley March 26, 2026 08:41
@s-tone-gs
Copy link
Copy Markdown
Contributor Author

@karlley
お疲れ様です!こちらレビュー依頼をさせていただいています!
変更確認が多いのでお手すきの際にお願いできればと思います。よろしくお願いいたします。

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 26, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== app/controllers/pair_works_controller.rb ==="
cat app/controllers/pair_works_controller.rb

Length of output: 2095


🏁 Script executed:

#!/bin/bash
echo "=== app/models/pair_work.rb (relevant section) ==="
cat app/models/pair_work.rb

Length of output: 3600


@s-tone-gs 確認しました!修正されています✅

以前の問題だった @pair_work.schedules.destroy_all の呼び出しは完全に削除され、現在の update アクションは以下のようにシンプルで安全な実装になっています:

def update
  set_wip
  if `@pair_work.update`(pair_work_params)
    ActiveSupport::Notifications.instrument('pair_work.update', pair_work: `@pair_work`)
    redirect_to Redirection.determin_url(self, `@pair_work`), notice: `@pair_work.generate_notice_message`(:update)
  else
    render :edit
  end
end

スケジュールの削除は accepts_nested_attributes_for :schedules, allow_destroy: true_destroy フラグを通じて update 内でアトミックに処理されるようになっており、update が失敗した場合にスケジュールだけ削除されてしまう問題は解消されています。

対応ありがとうございました!🐇


🧠 Learnings used
Learnt from: mousu-a
Repo: fjordllc/bootcamp PR: 8566
File: db/fixtures/pair_works.yml:19-20
Timestamp: 2026-01-15T03:21:32.929Z
Learning: fjordllc/bootcampの db/fixtures/pair_works.yml において、pair_work1とpair_work2は特定の状態(解決済み・募集中)の動作確認用フィクスチャであり、意図的に過去の静的な日付(例: Time.zone.parse('2025-01-01 00:00:00'))を使用している。これはテストの再現性を保証するための設計であり、問題ではない。

Learnt from: mousu-a
Repo: fjordllc/bootcamp PR: 9626
File: db/schema.rb:1153-1156
Timestamp: 2026-02-11T02:59:47.770Z
Learning: fjordllc/bootcamp の PairWork 機能において、buddy(ペアを申し込んだメンター)ユーザーが削除された場合、buddy_id だけでなく reserved_at も NULL にしてマッチングを解除する必要がある。buddy_id のみ NULL にすると reserved_at が残り不整合な状態になるため、将来のマッチング解除機能実装時に User の削除時コールバックまたは明示的なアンマッチ処理で両方のカラムをクリアする設計とする。

Learnt from: yokomaru
Repo: fjordllc/bootcamp PR: 9732
File: app/models/user.rb:955-958
Timestamp: 2026-03-16T03:19:51.757Z
Learning: fjordllc/bootcamp の `User#clean_up_regular_events` において、`regular_event_participations.for_holding_events.destroy_all`(参加解除)と `organize_regular_events.holding.each { |event| event.close_or_destroy_organizer(self) }`(主催者側終了/削除)はトランザクションでまとめていないが、これは意図的な設計判断である。理由: (1) 参加者と主催者は別モデルであり厳密な整合性は不要、(2) 呼び出し元(退会・休会・研修終了処理)全体のトランザクション設計を一緒に見直す予定があるため、このメソッド単体での対応は見送られた。

Learnt from: yokomaru
Repo: fjordllc/bootcamp PR: 9732
File: app/models/user.rb:955-958
Timestamp: 2026-03-16T03:24:48.283Z
Learning: fjordllc/bootcamp の `Retirement#execute`(`app/models/retirement.rb`)において、`user.clean_up_regular_events` の呼び出しは `ActiveRecord::Base.transaction` ブロックの外(コミット後)に置かれているが、これは意図的な設計判断である。理由: 退会・休会・研修終了処理全体のトランザクション設計を一緒に見直す予定があり、`clean_up_regular_events` 単体をトランザクション内に移動する対応は別途全体見直し時に検討する。

Learnt from: s-tone-gs
Repo: fjordllc/bootcamp PR: 0
File: :0-0
Timestamp: 2026-03-26T04:13:22.108Z
Learning: fjordllc/bootcamp の `PairWorks::ReservationsController#destroy`(`app/controllers/pair_works/reservations_controller.rb`)において、`current_user == pair_work.buddy` チェックに失敗した場合のサイレントリターンは意図的な設計である。理由: 「ペア取り消し」ボタンはバディにしか表示されないため、非バディによるアクセスは悪意ある直接リクエストのみであり、その場合にUXを考慮する必要はない。これはセキュリティ多層防御の観点から実装されている。

Learnt from: komagata
Repo: fjordllc/bootcamp PR: 9101
File: app/notifiers/discord_notifier.rb:131-135
Timestamp: 2025-09-12T21:18:00.834Z
Learning: Rails アップグレードPRにおいて、product_review_not_completed メソッドの webhook URL 設定の shared フォールバック追加も、設定システム全体の変更として別PRで対応すべきである。

Learnt from: s-tone-gs
Repo: fjordllc/bootcamp PR: 0
File: :0-0
Timestamp: 2026-03-26T04:13:01.581Z
Learning: fjordllc/bootcamp の `PairWorks::ReservationsController#destroy`(`app/controllers/pair_works/reservations_controller.rb`)において、バディ以外によるペア取り消し操作に対するサイレントリターン(早期 return)は意図的な設計である。「ペア取り消し」ボタンはバディにしか表示されず、通常ありえない操作に対するサーバーサイドの多層防御(defense-in-depth)のため、UX向けのエラーメッセージは不要と判断されている。

Learnt from: hirokiej
Repo: fjordllc/bootcamp PR: 8740
File: app/helpers/reports_helper.rb:56-64
Timestamp: 2025-06-29T03:44:15.179Z
Learning: このプロジェクト(fjordllc/bootcamp)では、ja.ymlファイルで一部の単語や文章のみI18n対応されているが、多くのテキストは日本語でハードコーディングされており、完全な国際化対応は行われていない。新しい機能でもI18n対応は不要と判断される。

Learnt from: komagata
Repo: fjordllc/bootcamp PR: 9101
File: app/models/graduation_notifier.rb:23-28
Timestamp: 2025-09-12T21:16:47.639Z
Learning: Rails upgrade PRにおいて、configuration systemの変更やwebhook設定の改善提案も、アップグレードに直接関連しない場合は別PRで対応すべきである。PRのスコープ維持が重要。

Learnt from: mousu-a
Repo: fjordllc/bootcamp PR: 8566
File: app/helpers/pair_work_helper.rb:31-33
Timestamp: 2026-01-15T03:20:33.388Z
Learning: fjordllc/bootcamp: 時間の加算には Numeric#hours(複数形)を優先して用いるプロジェクト方針。例: app/helpers/pair_work_helper.rb の schedule_time では hour_count.hours を使用する。

Learnt from: Miya096jp
Repo: fjordllc/bootcamp PR: 9102
File: app/controllers/mentor/buzzes_controller.rb:0-0
Timestamp: 2025-09-04T01:39:22.261Z
Learning: fjordllc/bootcamp プロジェクトでは、更新処理の成功時の通知メッセージについて、I18n を使用せずに日本語文字列を直接記述する方針で一貫性を保っている。

Learnt from: ryufuta
Repo: fjordllc/bootcamp PR: 9115
File: db/migrate/20250902024949_add_unique_index_to_correct_answers.rb:1-5
Timestamp: 2025-11-13T09:20:36.030Z
Learning: fjordllc/bootcamp プロジェクトでは、データの整合性を保つためのデータ修正には data-migrate gem を使用し、db/data/ ディレクトリ配下にデータマイグレーションファイルを作成する運用を採用している。スキーマ変更(db/migrate/)とデータ変更(db/data/)を分離することで、より安全で管理しやすい構成を実現している。

Learnt from: tyrrell-IH
Repo: fjordllc/bootcamp PR: 9306
File: app/javascript/components/Bookmarks.jsx:248-265
Timestamp: 2025-11-17T00:46:30.794Z
Learning: fjordllc/bootcamp プロジェクトでは、Reactからバニラ JavaScript への移行作業が進行中である。そのため、新しいReactファイルの作成は避け、既存のReactコンポーネント内で完結する実装が推奨される。BookmarksInDashboard.jsx は issue `#9045` で削除予定。

Learnt from: yokomaru
Repo: fjordllc/bootcamp PR: 9338
File: app/helpers/products_helper.rb:0-0
Timestamp: 2026-01-11T01:55:06.202Z
Learning: fjordllc/bootcamp プロジェクトでは、日付から曜日を取得する際、配列参照ではなく I18n.l を使用した日付フォーマット表示を優先する。

Learnt from: jun-kondo
Repo: fjordllc/bootcamp PR: 0
File: :0-0
Timestamp: 2025-07-23T20:42:19.974Z
Learning: fjordllc/bootcampプロジェクトでは、hタグ(見出し)の文言は日本語でハードコーディングする方針が確立されており、I18n対応は行わない。例:app/views/welcome/logo.html.slimでh2、h3タグが日本語でハードコーディングされている。

Learnt from: mousu-a
Repo: fjordllc/bootcamp PR: 8566
File: app/views/pair_works/_body.html.slim:111-117
Timestamp: 2025-08-31T23:39:23.210Z
Learning: PairWorkモデルにreserved_at_in_schedules?カスタムバリデーション(on: :update)が実装されており、reserved_atが必ずschedules.map(&:proposed_at)に含まれることを検証している。これにより、フォームから任意の日時を送信することを防いでいる。

Learnt from: mousu-a
Repo: fjordllc/bootcamp PR: 8566
File: app/controllers/pair_works_controller.rb:58-76
Timestamp: 2025-11-01T03:15:49.349Z
Learning: PairWork.matching_params?メソッドは、buddy_idとreserved_atのみを含むペア確定リクエストのみを許可するため、title、description、practice_id、channel、schedules_attributesがパラメータに含まれる場合は権限を拒否することで、メンターによる権限昇格を防ぐセキュリティ対策が実装されている。

Learnt from: mousu-a
Repo: fjordllc/bootcamp PR: 8566
File: test/mailers/activity_mailer_test.rb:0-0
Timestamp: 2025-08-01T12:57:10.349Z
Learning: came_pair_work機能は現在テスト運用段階で、メンターと関係者のみに通知される仕様のため、メール通知OFF時の制御テストは現時点では実装不要と判断されている。

If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

@karlley
Copy link
Copy Markdown
Contributor

karlley commented Mar 26, 2026

@s-tone-gs
お疲れ様です!
レビューに関してですが2週間ほどお時間いただきたいですが問題ないでしょうか?
お急ぎの場合はお手数ですが別の方にお願いしていただきたいです。
お時間に関して問題ないようであればレビュー受けさせていただきます。

@s-tone-gs
Copy link
Copy Markdown
Contributor Author

@karlley
問題ありません!よろしくお願いいたします🙇‍♂️

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.

3 participants